aispritejs 0.5.8 → 0.6.0
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 +10 -7
- package/README_ZHTW.md +11 -7
- package/dist/atlas/index.cjs +42 -48
- package/dist/atlas/index.cjs.map +1 -1
- package/dist/atlas/index.d.cts +6 -3
- package/dist/atlas/index.d.ts +6 -3
- package/dist/atlas/index.js +35 -41
- package/dist/atlas/index.js.map +1 -1
- package/dist/{chunk-QF5N7LOI.cjs → chunk-GEQWS7FY.cjs} +122 -96
- package/dist/chunk-GEQWS7FY.cjs.map +1 -0
- package/dist/{chunk-6E4ILDBY.js → chunk-NBMU2UNJ.js} +122 -97
- package/dist/chunk-NBMU2UNJ.js.map +1 -0
- package/dist/index.cjs +6 -6
- package/dist/index.d.cts +6 -4
- package/dist/index.d.ts +6 -4
- package/dist/index.js +1 -1
- package/dist/pixi/index.cjs +20 -12
- package/dist/pixi/index.cjs.map +1 -1
- package/dist/pixi/index.d.cts +17 -7
- package/dist/pixi/index.d.ts +17 -7
- package/dist/pixi/index.js +19 -11
- package/dist/pixi/index.js.map +1 -1
- package/dist/{types-BS72pefJ.d.cts → types-Dr7yXTRQ.d.cts} +29 -8
- package/dist/{types-BS72pefJ.d.ts → types-Dr7yXTRQ.d.ts} +29 -8
- package/llms-full.txt +50 -14
- package/package.json +25 -10
- package/dist/chunk-6E4ILDBY.js.map +0 -1
- package/dist/chunk-QF5N7LOI.cjs.map +0 -1
package/dist/pixi/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/pixi/animator.ts"],"names":["createSpriteAnimator","options"],"mappings":"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","file":"index.cjs","sourcesContent":["// aispritejs/pixi — the PixiJS v8 renderer adapter. It binds the\n// renderer-agnostic core to a `PIXI.Sprite`: on each update it swaps the\n// sprite's texture to the core's active frame and (by default) applies that\n// frame's atlas anchor.\n//\n// `pixi.js` is imported **type-only**, so the built subpath contains no runtime\n// `pixi.js` require — the peer is needed only by the consumer who passes real\n// Sprite / Spritesheet instances. `pixi.js` is declared as an OPTIONAL\n// peerDependency; the core (`aispritejs`) never imports this module.\n\nimport type { Sprite, Spritesheet, Texture } from \"pixi.js\";\nimport {\n type CompleteHandler,\n type ListenerOptions,\n type SpriteGraph,\n type StateChangeHandler,\n type Unsubscribe,\n createSpriteAnimator,\n} from \"../sprite/index.js\";\n\n/**\n * Thrown by {@link createPixiSpriteAnimator} when the supplied textures are\n * missing one or more frame keys the graph's animations reference. Fail-fast at\n * construction, so `update()` never has to guard.\n *\n * @public\n */\nexport class MissingTextureError extends Error {\n readonly keys: readonly string[];\n constructor(keys: readonly string[]) {\n super(`aispritejs/pixi: no texture for frame key(s): ${keys.join(\", \")}`);\n this.name = \"MissingTextureError\";\n this.keys = keys;\n }\n}\n\n/** Frame-key → texture lookup. A PixiJS `Spritesheet` exposes one as `.textures`. */\nexport type TextureMap = Record<string, Texture>;\n\n/**\n * Options for {@link createPixiSpriteAnimator}.\n *\n * @public\n */\nexport interface PixiSpriteAnimatorOptions {\n /**\n * Apply each frame's atlas anchor (`texture.defaultAnchor`) to the sprite when\n * the frame changes — preserving non-centre / foot pivots. Default `true`.\n * Set `false` to manage the anchor yourself.\n */\n readonly applyAnchor?: boolean;\n}\n\n/**\n * A PixiJS-bound animator: the core machine plus a sprite whose texture tracks\n * the active frame.\n *\n * @public\n */\nexport interface PixiSpriteAnimator {\n /** The bound sprite, updated in place. */\n readonly sprite: Sprite;\n /** Run the core machine for `deltaMs`, then sync the sprite's texture. */\n update(deltaMs: number): void;\n /** Set a Number / Boolean input on the core machine. */\n setInput(name: string, value: number | boolean): void;\n /** Fire a Trigger on the core machine. */\n fireTrigger(name: string): void;\n /** Reset the core machine and re-sync the sprite. */\n reset(): void;\n /** Dispose the core machine. Idempotent. Does not destroy the sprite. */\n dispose(): void;\n /** Current state name. */\n readonly activeState: string;\n /** Current frame key. */\n readonly activeFrameKey: string;\n /** `true` once disposed. */\n readonly disposed: boolean;\n /** Subscribe to non-looping clip completions. Returns an unsubscribe. */\n onComplete(handler: CompleteHandler, options?: ListenerOptions): Unsubscribe;\n /** Subscribe to state changes. Returns an unsubscribe. */\n onStateChange(handler: StateChangeHandler, options?: ListenerOptions): Unsubscribe;\n}\n\nfunction toTextureMap(src: Spritesheet | TextureMap): TextureMap {\n // A Spritesheet exposes its frame textures under `.textures`; a plain map is\n // used directly. (If you have a frame literally named \"textures\", pass\n // `spritesheet.textures` instead of the spritesheet.)\n const maybe = src as { textures?: unknown };\n if (maybe.textures && typeof maybe.textures === \"object\") {\n return maybe.textures as TextureMap;\n }\n return src as TextureMap;\n}\n\n/**\n * Bind an input-driven {@link SpriteGraph} to a PixiJS `Sprite`.\n *\n * @param sprite - a plain `Sprite` to drive; its `texture` (and, by default,\n * `anchor`) are updated in place. The adapter owns frame selection, so if a\n * *playing* `AnimatedSprite` is passed (it extends `Sprite`), its internal\n * playback is stopped to stop it fighting the adapter for the texture.\n * @param graph - the input-driven graph (same shape the core consumes).\n * @param textures - a `Spritesheet` or a frame-key → `Texture` map covering\n * every frame the graph references.\n * @param options - see {@link PixiSpriteAnimatorOptions}.\n * @returns a {@link PixiSpriteAnimator}.\n * @throws {@link MissingTextureError} if a referenced frame key has no texture.\n * @throws {@link InvalidGraphError} if the graph is invalid.\n *\n * @public\n */\nexport function createPixiSpriteAnimator(\n sprite: Sprite,\n graph: SpriteGraph,\n textures: Spritesheet | TextureMap,\n options?: PixiSpriteAnimatorOptions,\n): PixiSpriteAnimator {\n const map = toTextureMap(textures);\n const applyAnchor = options?.applyAnchor !== false;\n\n // The adapter owns the sprite's texture/anchor. An AnimatedSprite (which\n // extends Sprite, so the type permits it) drives its own texture from an\n // internal ticker; if it is playing it would fight our frame swaps. Stop it.\n // Structural check — pixi.js is type-only here, so no `instanceof`.\n const playable = sprite as { stop?: () => void };\n if (typeof playable.stop === \"function\") playable.stop();\n\n // Fail-fast: every frame key reachable from the graph must have a texture.\n // Use Object.hasOwn rather than `in` so that Object.prototype keys such as\n // \"constructor\" / \"toString\" are correctly rejected (mirroring APPLY-1 in\n // compile.ts which fixed the same class).\n const missing = new Set<string>();\n for (const frameKeys of Object.values(graph.animations)) {\n for (const key of frameKeys) {\n if (!Object.hasOwn(map, key)) missing.add(key);\n }\n }\n if (missing.size > 0) throw new MissingTextureError([...missing]);\n\n const core = createSpriteAnimator(graph);\n\n // Swap the sprite's texture (and anchor) only when the active frame changes.\n let boundKey = \"\";\n function sync(): void {\n const key = core.activeFrameKey;\n if (key === boundKey) return;\n boundKey = key;\n const tex = map[key]!; // verified present above\n sprite.texture = tex;\n if (applyAnchor && tex.defaultAnchor) {\n sprite.anchor.set(tex.defaultAnchor.x, tex.defaultAnchor.y);\n }\n }\n sync(); // bind the initial frame before the first update\n\n return {\n sprite,\n update(deltaMs) {\n core.update(deltaMs);\n sync();\n },\n setInput(name, value) {\n core.setInput(name, value);\n },\n fireTrigger(name) {\n core.fireTrigger(name);\n },\n reset() {\n core.reset();\n sync();\n },\n dispose() {\n core.dispose();\n },\n get activeState() {\n return core.activeState;\n },\n get activeFrameKey() {\n return core.activeFrameKey;\n },\n get disposed() {\n return core.disposed;\n },\n onComplete(handler, options) {\n return core.onComplete(handler, options);\n },\n onStateChange(handler, options) {\n return core.onStateChange(handler, options);\n },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/pixi/animator.ts"],"names":["assertGraphShape","createSpriteAnimator","options"],"mappings":"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","file":"index.cjs","sourcesContent":["// aispritejs/pixi — the PixiJS v8 renderer adapter. It binds the\n// renderer-agnostic core to a `PIXI.Sprite`: on each update it swaps the\n// sprite's texture to the core's active frame and (by default) applies that\n// frame's atlas anchor.\n//\n// `pixi.js` is imported **type-only**, so the built subpath contains no runtime\n// `pixi.js` require — the peer is needed only by the consumer who passes real\n// Sprite / Spritesheet instances. `pixi.js` is declared as an OPTIONAL\n// peerDependency; the core (`aispritejs`) never imports this module.\n\nimport type { Sprite, Spritesheet, Texture } from \"pixi.js\";\nimport { assertGraphShape } from \"../sprite/compile.js\";\nimport {\n type CompleteHandler,\n type ListenerOptions,\n type SpriteGraph,\n type StateChangeHandler,\n type Unsubscribe,\n createSpriteAnimator,\n} from \"../sprite/index.js\";\n\n/**\n * Thrown by {@link createPixiSpriteAnimator} when the supplied textures are\n * missing one or more frame keys the graph's animations reference (an own entry\n * whose value is `null` / `undefined` counts as missing, and so does every key\n * when `textures` itself is nullish). Fail-fast at construction, so `update()`\n * never has to guard.\n *\n * @public\n */\nexport class MissingTextureError extends Error {\n readonly keys: readonly string[];\n constructor(keys: readonly string[]) {\n super(`aispritejs/pixi: no texture for frame key(s): ${keys.join(\", \")}`);\n this.name = \"MissingTextureError\";\n this.keys = keys;\n }\n}\n\n/** Frame-key → texture lookup. A PixiJS `Spritesheet` exposes one as `.textures`. */\nexport type TextureMap = Record<string, Texture>;\n\n/**\n * Options for {@link createPixiSpriteAnimator}.\n *\n * @public\n */\nexport interface PixiSpriteAnimatorOptions {\n /**\n * Apply each frame's atlas anchor (`texture.defaultAnchor`) to the sprite when\n * the frame changes — preserving non-centre / foot pivots. Default `true`.\n * Set `false` to manage the anchor yourself.\n */\n readonly applyAnchor?: boolean;\n}\n\n/**\n * A PixiJS-bound animator: the core machine plus a sprite whose texture tracks\n * the active frame.\n *\n * @public\n */\nexport interface PixiSpriteAnimator {\n /** The bound sprite, updated in place. */\n readonly sprite: Sprite;\n /**\n * Run the core machine for `deltaMs`, then sync the sprite's texture (also\n * when a listener throws, before the error propagates).\n */\n update(deltaMs: number): void;\n /** Set a Number / Boolean input on the core machine. */\n setInput(name: string, value: number | boolean): void;\n /** Fire a Trigger on the core machine. */\n fireTrigger(name: string): void;\n /** Reset the core machine and re-sync the sprite. */\n reset(): void;\n /** Dispose the core machine. Idempotent. Does not destroy the sprite. */\n dispose(): void;\n /** Current state name. */\n readonly activeState: string;\n /** Current frame key. */\n readonly activeFrameKey: string;\n /** `true` once disposed. */\n readonly disposed: boolean;\n /** Subscribe to non-looping clip completions. Returns an unsubscribe. */\n onComplete(handler: CompleteHandler, options?: ListenerOptions): Unsubscribe;\n /** Subscribe to state changes. Returns an unsubscribe. */\n onStateChange(handler: StateChangeHandler, options?: ListenerOptions): Unsubscribe;\n}\n\nfunction toTextureMap(src: Spritesheet | TextureMap): TextureMap {\n // A Spritesheet exposes its frame textures under `.textures`; a plain map is\n // used directly. The check is structural (pixi.js is type-only here), so a\n // plain map with a frame literally named \"textures\" reads as a Spritesheet:\n // pass the Spritesheet, or wrap the map as `{ textures: map }`. A nullish\n // `src` (untyped callers) reads as an empty map, so every frame key is\n // reported missing instead of a bare TypeError.\n const maybe = src as { textures?: unknown } | null | undefined;\n if (maybe?.textures && typeof maybe.textures === \"object\") {\n return maybe.textures as TextureMap;\n }\n return (src ?? {}) as TextureMap;\n}\n\n/**\n * Bind an input-driven {@link SpriteGraph} to a PixiJS `Sprite`.\n *\n * @param sprite - a plain `Sprite` to drive; its `texture` (and, by default,\n * `anchor`) are updated in place. The adapter owns frame selection, so if a\n * *playing* `AnimatedSprite` is passed (it extends `Sprite`), its internal\n * playback is stopped to stop it fighting the adapter for the texture.\n * @param graph - the input-driven graph (same shape the core consumes).\n * @param textures - a `Spritesheet` or a frame-key → `Texture` map covering\n * every frame of every declared animation. Any object with an object-valued\n * `textures` property is read as a Spritesheet, so if a frame is literally\n * named `textures`, pass the Spritesheet (or `{ textures: map }`), not the\n * bare map.\n * @param options - see {@link PixiSpriteAnimatorOptions}.\n * @returns a {@link PixiSpriteAnimator}.\n * @throws {@link InvalidGraphError} if the graph is not an object or its\n * containers are malformed (checked before textures), or is otherwise invalid.\n * @throws {@link MissingTextureError} if a frame key has no texture, or a\n * `null` / `undefined` one.\n *\n * @public\n */\nexport function createPixiSpriteAnimator(\n sprite: Sprite,\n graph: SpriteGraph,\n textures: Spritesheet | TextureMap,\n options?: PixiSpriteAnimatorOptions,\n): PixiSpriteAnimator {\n // The texture scan below iterates `graph.animations`; check its shape first\n // so a malformed graph is an InvalidGraphError, not a bare TypeError.\n assertGraphShape(graph);\n const map = toTextureMap(textures);\n const applyAnchor = options?.applyAnchor !== false;\n\n // Fail-fast: every frame key of every declared animation must have a\n // texture. Use Object.hasOwn rather than `in` so that Object.prototype keys\n // such as \"constructor\" / \"toString\" are correctly rejected (mirroring\n // APPLY-1 in compile.ts which fixed the same class); an own entry holding\n // null / undefined is missing too, or sync() would blank the sprite.\n const missing = new Set<string>();\n for (const frameKeys of Object.values(graph.animations)) {\n for (const key of frameKeys) {\n if (!Object.hasOwn(map, key) || map[key] == null) missing.add(key);\n }\n }\n if (missing.size > 0) throw new MissingTextureError([...missing]);\n\n const core = createSpriteAnimator(graph);\n\n // The adapter owns the sprite's texture/anchor. An AnimatedSprite (which\n // extends Sprite, so the type permits it) drives its own texture from an\n // internal ticker; if it is playing it would fight our frame swaps. Stop it.\n // Structural check — pixi.js is type-only here, so no `instanceof`. Done\n // only after the checks above pass, so a failed bind has no side effect on\n // the caller's sprite.\n const playable = sprite as { stop?: () => void };\n if (typeof playable.stop === \"function\") playable.stop();\n\n // Swap the sprite's texture (and anchor) only when the active frame changes.\n // `undefined`, not `\"\"`, is the sentinel: `\"\"` is a legal frame key (the\n // schema has no minLength), so using it here would skip the initial bind\n // for a graph whose first frame key is the empty string.\n let boundKey: string | undefined;\n function sync(): void {\n // A listener may dispose() (and destroy the sprite) during core.update() /\n // core.reset(); the sprite is no longer ours to write to after that.\n if (core.disposed) return;\n const key = core.activeFrameKey;\n if (key === boundKey) return;\n boundKey = key;\n const tex = map[key]!; // verified present above\n sprite.texture = tex;\n if (applyAnchor && tex.defaultAnchor) {\n sprite.anchor.set(tex.defaultAnchor.x, tex.defaultAnchor.y);\n }\n }\n sync(); // bind the initial frame before the first update\n\n return {\n sprite,\n update(deltaMs) {\n // `finally`: a throwing listener must not leave the sprite on a frame\n // the core has already left.\n try {\n core.update(deltaMs);\n } finally {\n sync();\n }\n },\n setInput(name, value) {\n core.setInput(name, value);\n },\n fireTrigger(name) {\n core.fireTrigger(name);\n },\n reset() {\n try {\n core.reset();\n } finally {\n sync();\n }\n },\n dispose() {\n core.dispose();\n },\n get activeState() {\n return core.activeState;\n },\n get activeFrameKey() {\n return core.activeFrameKey;\n },\n get disposed() {\n return core.disposed;\n },\n onComplete(handler, options) {\n return core.onComplete(handler, options);\n },\n onStateChange(handler, options) {\n return core.onStateChange(handler, options);\n },\n };\n}\n"]}
|
package/dist/pixi/index.d.cts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { Sprite, Texture, Spritesheet } from 'pixi.js';
|
|
2
|
-
import { C as CompleteHandler, L as ListenerOptions, U as Unsubscribe, d as StateChangeHandler, b as SpriteGraph } from '../types-
|
|
2
|
+
import { C as CompleteHandler, L as ListenerOptions, U as Unsubscribe, d as StateChangeHandler, b as SpriteGraph } from '../types-Dr7yXTRQ.cjs';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Thrown by {@link createPixiSpriteAnimator} when the supplied textures are
|
|
6
|
-
* missing one or more frame keys the graph's animations reference
|
|
7
|
-
*
|
|
6
|
+
* missing one or more frame keys the graph's animations reference (an own entry
|
|
7
|
+
* whose value is `null` / `undefined` counts as missing, and so does every key
|
|
8
|
+
* when `textures` itself is nullish). Fail-fast at construction, so `update()`
|
|
9
|
+
* never has to guard.
|
|
8
10
|
*
|
|
9
11
|
* @public
|
|
10
12
|
*/
|
|
@@ -36,7 +38,10 @@ interface PixiSpriteAnimatorOptions {
|
|
|
36
38
|
interface PixiSpriteAnimator {
|
|
37
39
|
/** The bound sprite, updated in place. */
|
|
38
40
|
readonly sprite: Sprite;
|
|
39
|
-
/**
|
|
41
|
+
/**
|
|
42
|
+
* Run the core machine for `deltaMs`, then sync the sprite's texture (also
|
|
43
|
+
* when a listener throws, before the error propagates).
|
|
44
|
+
*/
|
|
40
45
|
update(deltaMs: number): void;
|
|
41
46
|
/** Set a Number / Boolean input on the core machine. */
|
|
42
47
|
setInput(name: string, value: number | boolean): void;
|
|
@@ -66,11 +71,16 @@ interface PixiSpriteAnimator {
|
|
|
66
71
|
* playback is stopped to stop it fighting the adapter for the texture.
|
|
67
72
|
* @param graph - the input-driven graph (same shape the core consumes).
|
|
68
73
|
* @param textures - a `Spritesheet` or a frame-key → `Texture` map covering
|
|
69
|
-
* every frame
|
|
74
|
+
* every frame of every declared animation. Any object with an object-valued
|
|
75
|
+
* `textures` property is read as a Spritesheet, so if a frame is literally
|
|
76
|
+
* named `textures`, pass the Spritesheet (or `{ textures: map }`), not the
|
|
77
|
+
* bare map.
|
|
70
78
|
* @param options - see {@link PixiSpriteAnimatorOptions}.
|
|
71
79
|
* @returns a {@link PixiSpriteAnimator}.
|
|
72
|
-
* @throws {@link
|
|
73
|
-
*
|
|
80
|
+
* @throws {@link InvalidGraphError} if the graph is not an object or its
|
|
81
|
+
* containers are malformed (checked before textures), or is otherwise invalid.
|
|
82
|
+
* @throws {@link MissingTextureError} if a frame key has no texture, or a
|
|
83
|
+
* `null` / `undefined` one.
|
|
74
84
|
*
|
|
75
85
|
* @public
|
|
76
86
|
*/
|
package/dist/pixi/index.d.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { Sprite, Texture, Spritesheet } from 'pixi.js';
|
|
2
|
-
import { C as CompleteHandler, L as ListenerOptions, U as Unsubscribe, d as StateChangeHandler, b as SpriteGraph } from '../types-
|
|
2
|
+
import { C as CompleteHandler, L as ListenerOptions, U as Unsubscribe, d as StateChangeHandler, b as SpriteGraph } from '../types-Dr7yXTRQ.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Thrown by {@link createPixiSpriteAnimator} when the supplied textures are
|
|
6
|
-
* missing one or more frame keys the graph's animations reference
|
|
7
|
-
*
|
|
6
|
+
* missing one or more frame keys the graph's animations reference (an own entry
|
|
7
|
+
* whose value is `null` / `undefined` counts as missing, and so does every key
|
|
8
|
+
* when `textures` itself is nullish). Fail-fast at construction, so `update()`
|
|
9
|
+
* never has to guard.
|
|
8
10
|
*
|
|
9
11
|
* @public
|
|
10
12
|
*/
|
|
@@ -36,7 +38,10 @@ interface PixiSpriteAnimatorOptions {
|
|
|
36
38
|
interface PixiSpriteAnimator {
|
|
37
39
|
/** The bound sprite, updated in place. */
|
|
38
40
|
readonly sprite: Sprite;
|
|
39
|
-
/**
|
|
41
|
+
/**
|
|
42
|
+
* Run the core machine for `deltaMs`, then sync the sprite's texture (also
|
|
43
|
+
* when a listener throws, before the error propagates).
|
|
44
|
+
*/
|
|
40
45
|
update(deltaMs: number): void;
|
|
41
46
|
/** Set a Number / Boolean input on the core machine. */
|
|
42
47
|
setInput(name: string, value: number | boolean): void;
|
|
@@ -66,11 +71,16 @@ interface PixiSpriteAnimator {
|
|
|
66
71
|
* playback is stopped to stop it fighting the adapter for the texture.
|
|
67
72
|
* @param graph - the input-driven graph (same shape the core consumes).
|
|
68
73
|
* @param textures - a `Spritesheet` or a frame-key → `Texture` map covering
|
|
69
|
-
* every frame
|
|
74
|
+
* every frame of every declared animation. Any object with an object-valued
|
|
75
|
+
* `textures` property is read as a Spritesheet, so if a frame is literally
|
|
76
|
+
* named `textures`, pass the Spritesheet (or `{ textures: map }`), not the
|
|
77
|
+
* bare map.
|
|
70
78
|
* @param options - see {@link PixiSpriteAnimatorOptions}.
|
|
71
79
|
* @returns a {@link PixiSpriteAnimator}.
|
|
72
|
-
* @throws {@link
|
|
73
|
-
*
|
|
80
|
+
* @throws {@link InvalidGraphError} if the graph is not an object or its
|
|
81
|
+
* containers are malformed (checked before textures), or is otherwise invalid.
|
|
82
|
+
* @throws {@link MissingTextureError} if a frame key has no texture, or a
|
|
83
|
+
* `null` / `undefined` one.
|
|
74
84
|
*
|
|
75
85
|
* @public
|
|
76
86
|
*/
|
package/dist/pixi/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createSpriteAnimator } from '../chunk-
|
|
1
|
+
import { assertGraphShape, createSpriteAnimator } from '../chunk-NBMU2UNJ.js';
|
|
2
2
|
|
|
3
3
|
// src/pixi/animator.ts
|
|
4
4
|
var MissingTextureError = class extends Error {
|
|
@@ -11,26 +11,28 @@ var MissingTextureError = class extends Error {
|
|
|
11
11
|
};
|
|
12
12
|
function toTextureMap(src) {
|
|
13
13
|
const maybe = src;
|
|
14
|
-
if (maybe
|
|
14
|
+
if (maybe?.textures && typeof maybe.textures === "object") {
|
|
15
15
|
return maybe.textures;
|
|
16
16
|
}
|
|
17
|
-
return src;
|
|
17
|
+
return src ?? {};
|
|
18
18
|
}
|
|
19
19
|
function createPixiSpriteAnimator(sprite, graph, textures, options) {
|
|
20
|
+
assertGraphShape(graph);
|
|
20
21
|
const map = toTextureMap(textures);
|
|
21
22
|
const applyAnchor = options?.applyAnchor !== false;
|
|
22
|
-
const playable = sprite;
|
|
23
|
-
if (typeof playable.stop === "function") playable.stop();
|
|
24
23
|
const missing = /* @__PURE__ */ new Set();
|
|
25
24
|
for (const frameKeys of Object.values(graph.animations)) {
|
|
26
25
|
for (const key of frameKeys) {
|
|
27
|
-
if (!Object.hasOwn(map, key)) missing.add(key);
|
|
26
|
+
if (!Object.hasOwn(map, key) || map[key] == null) missing.add(key);
|
|
28
27
|
}
|
|
29
28
|
}
|
|
30
29
|
if (missing.size > 0) throw new MissingTextureError([...missing]);
|
|
31
30
|
const core = createSpriteAnimator(graph);
|
|
32
|
-
|
|
31
|
+
const playable = sprite;
|
|
32
|
+
if (typeof playable.stop === "function") playable.stop();
|
|
33
|
+
let boundKey;
|
|
33
34
|
function sync() {
|
|
35
|
+
if (core.disposed) return;
|
|
34
36
|
const key = core.activeFrameKey;
|
|
35
37
|
if (key === boundKey) return;
|
|
36
38
|
boundKey = key;
|
|
@@ -44,8 +46,11 @@ function createPixiSpriteAnimator(sprite, graph, textures, options) {
|
|
|
44
46
|
return {
|
|
45
47
|
sprite,
|
|
46
48
|
update(deltaMs) {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
+
try {
|
|
50
|
+
core.update(deltaMs);
|
|
51
|
+
} finally {
|
|
52
|
+
sync();
|
|
53
|
+
}
|
|
49
54
|
},
|
|
50
55
|
setInput(name, value) {
|
|
51
56
|
core.setInput(name, value);
|
|
@@ -54,8 +59,11 @@ function createPixiSpriteAnimator(sprite, graph, textures, options) {
|
|
|
54
59
|
core.fireTrigger(name);
|
|
55
60
|
},
|
|
56
61
|
reset() {
|
|
57
|
-
|
|
58
|
-
|
|
62
|
+
try {
|
|
63
|
+
core.reset();
|
|
64
|
+
} finally {
|
|
65
|
+
sync();
|
|
66
|
+
}
|
|
59
67
|
},
|
|
60
68
|
dispose() {
|
|
61
69
|
core.dispose();
|
package/dist/pixi/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/pixi/animator.ts"],"names":["options"],"mappings":"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","file":"index.js","sourcesContent":["// aispritejs/pixi — the PixiJS v8 renderer adapter. It binds the\n// renderer-agnostic core to a `PIXI.Sprite`: on each update it swaps the\n// sprite's texture to the core's active frame and (by default) applies that\n// frame's atlas anchor.\n//\n// `pixi.js` is imported **type-only**, so the built subpath contains no runtime\n// `pixi.js` require — the peer is needed only by the consumer who passes real\n// Sprite / Spritesheet instances. `pixi.js` is declared as an OPTIONAL\n// peerDependency; the core (`aispritejs`) never imports this module.\n\nimport type { Sprite, Spritesheet, Texture } from \"pixi.js\";\nimport {\n type CompleteHandler,\n type ListenerOptions,\n type SpriteGraph,\n type StateChangeHandler,\n type Unsubscribe,\n createSpriteAnimator,\n} from \"../sprite/index.js\";\n\n/**\n * Thrown by {@link createPixiSpriteAnimator} when the supplied textures are\n * missing one or more frame keys the graph's animations reference. Fail-fast at\n * construction, so `update()` never has to guard.\n *\n * @public\n */\nexport class MissingTextureError extends Error {\n readonly keys: readonly string[];\n constructor(keys: readonly string[]) {\n super(`aispritejs/pixi: no texture for frame key(s): ${keys.join(\", \")}`);\n this.name = \"MissingTextureError\";\n this.keys = keys;\n }\n}\n\n/** Frame-key → texture lookup. A PixiJS `Spritesheet` exposes one as `.textures`. */\nexport type TextureMap = Record<string, Texture>;\n\n/**\n * Options for {@link createPixiSpriteAnimator}.\n *\n * @public\n */\nexport interface PixiSpriteAnimatorOptions {\n /**\n * Apply each frame's atlas anchor (`texture.defaultAnchor`) to the sprite when\n * the frame changes — preserving non-centre / foot pivots. Default `true`.\n * Set `false` to manage the anchor yourself.\n */\n readonly applyAnchor?: boolean;\n}\n\n/**\n * A PixiJS-bound animator: the core machine plus a sprite whose texture tracks\n * the active frame.\n *\n * @public\n */\nexport interface PixiSpriteAnimator {\n /** The bound sprite, updated in place. */\n readonly sprite: Sprite;\n /** Run the core machine for `deltaMs`, then sync the sprite's texture. */\n update(deltaMs: number): void;\n /** Set a Number / Boolean input on the core machine. */\n setInput(name: string, value: number | boolean): void;\n /** Fire a Trigger on the core machine. */\n fireTrigger(name: string): void;\n /** Reset the core machine and re-sync the sprite. */\n reset(): void;\n /** Dispose the core machine. Idempotent. Does not destroy the sprite. */\n dispose(): void;\n /** Current state name. */\n readonly activeState: string;\n /** Current frame key. */\n readonly activeFrameKey: string;\n /** `true` once disposed. */\n readonly disposed: boolean;\n /** Subscribe to non-looping clip completions. Returns an unsubscribe. */\n onComplete(handler: CompleteHandler, options?: ListenerOptions): Unsubscribe;\n /** Subscribe to state changes. Returns an unsubscribe. */\n onStateChange(handler: StateChangeHandler, options?: ListenerOptions): Unsubscribe;\n}\n\nfunction toTextureMap(src: Spritesheet | TextureMap): TextureMap {\n // A Spritesheet exposes its frame textures under `.textures`; a plain map is\n // used directly. (If you have a frame literally named \"textures\", pass\n // `spritesheet.textures` instead of the spritesheet.)\n const maybe = src as { textures?: unknown };\n if (maybe.textures && typeof maybe.textures === \"object\") {\n return maybe.textures as TextureMap;\n }\n return src as TextureMap;\n}\n\n/**\n * Bind an input-driven {@link SpriteGraph} to a PixiJS `Sprite`.\n *\n * @param sprite - a plain `Sprite` to drive; its `texture` (and, by default,\n * `anchor`) are updated in place. The adapter owns frame selection, so if a\n * *playing* `AnimatedSprite` is passed (it extends `Sprite`), its internal\n * playback is stopped to stop it fighting the adapter for the texture.\n * @param graph - the input-driven graph (same shape the core consumes).\n * @param textures - a `Spritesheet` or a frame-key → `Texture` map covering\n * every frame the graph references.\n * @param options - see {@link PixiSpriteAnimatorOptions}.\n * @returns a {@link PixiSpriteAnimator}.\n * @throws {@link MissingTextureError} if a referenced frame key has no texture.\n * @throws {@link InvalidGraphError} if the graph is invalid.\n *\n * @public\n */\nexport function createPixiSpriteAnimator(\n sprite: Sprite,\n graph: SpriteGraph,\n textures: Spritesheet | TextureMap,\n options?: PixiSpriteAnimatorOptions,\n): PixiSpriteAnimator {\n const map = toTextureMap(textures);\n const applyAnchor = options?.applyAnchor !== false;\n\n // The adapter owns the sprite's texture/anchor. An AnimatedSprite (which\n // extends Sprite, so the type permits it) drives its own texture from an\n // internal ticker; if it is playing it would fight our frame swaps. Stop it.\n // Structural check — pixi.js is type-only here, so no `instanceof`.\n const playable = sprite as { stop?: () => void };\n if (typeof playable.stop === \"function\") playable.stop();\n\n // Fail-fast: every frame key reachable from the graph must have a texture.\n // Use Object.hasOwn rather than `in` so that Object.prototype keys such as\n // \"constructor\" / \"toString\" are correctly rejected (mirroring APPLY-1 in\n // compile.ts which fixed the same class).\n const missing = new Set<string>();\n for (const frameKeys of Object.values(graph.animations)) {\n for (const key of frameKeys) {\n if (!Object.hasOwn(map, key)) missing.add(key);\n }\n }\n if (missing.size > 0) throw new MissingTextureError([...missing]);\n\n const core = createSpriteAnimator(graph);\n\n // Swap the sprite's texture (and anchor) only when the active frame changes.\n let boundKey = \"\";\n function sync(): void {\n const key = core.activeFrameKey;\n if (key === boundKey) return;\n boundKey = key;\n const tex = map[key]!; // verified present above\n sprite.texture = tex;\n if (applyAnchor && tex.defaultAnchor) {\n sprite.anchor.set(tex.defaultAnchor.x, tex.defaultAnchor.y);\n }\n }\n sync(); // bind the initial frame before the first update\n\n return {\n sprite,\n update(deltaMs) {\n core.update(deltaMs);\n sync();\n },\n setInput(name, value) {\n core.setInput(name, value);\n },\n fireTrigger(name) {\n core.fireTrigger(name);\n },\n reset() {\n core.reset();\n sync();\n },\n dispose() {\n core.dispose();\n },\n get activeState() {\n return core.activeState;\n },\n get activeFrameKey() {\n return core.activeFrameKey;\n },\n get disposed() {\n return core.disposed;\n },\n onComplete(handler, options) {\n return core.onComplete(handler, options);\n },\n onStateChange(handler, options) {\n return core.onStateChange(handler, options);\n },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/pixi/animator.ts"],"names":["options"],"mappings":"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","file":"index.js","sourcesContent":["// aispritejs/pixi — the PixiJS v8 renderer adapter. It binds the\n// renderer-agnostic core to a `PIXI.Sprite`: on each update it swaps the\n// sprite's texture to the core's active frame and (by default) applies that\n// frame's atlas anchor.\n//\n// `pixi.js` is imported **type-only**, so the built subpath contains no runtime\n// `pixi.js` require — the peer is needed only by the consumer who passes real\n// Sprite / Spritesheet instances. `pixi.js` is declared as an OPTIONAL\n// peerDependency; the core (`aispritejs`) never imports this module.\n\nimport type { Sprite, Spritesheet, Texture } from \"pixi.js\";\nimport { assertGraphShape } from \"../sprite/compile.js\";\nimport {\n type CompleteHandler,\n type ListenerOptions,\n type SpriteGraph,\n type StateChangeHandler,\n type Unsubscribe,\n createSpriteAnimator,\n} from \"../sprite/index.js\";\n\n/**\n * Thrown by {@link createPixiSpriteAnimator} when the supplied textures are\n * missing one or more frame keys the graph's animations reference (an own entry\n * whose value is `null` / `undefined` counts as missing, and so does every key\n * when `textures` itself is nullish). Fail-fast at construction, so `update()`\n * never has to guard.\n *\n * @public\n */\nexport class MissingTextureError extends Error {\n readonly keys: readonly string[];\n constructor(keys: readonly string[]) {\n super(`aispritejs/pixi: no texture for frame key(s): ${keys.join(\", \")}`);\n this.name = \"MissingTextureError\";\n this.keys = keys;\n }\n}\n\n/** Frame-key → texture lookup. A PixiJS `Spritesheet` exposes one as `.textures`. */\nexport type TextureMap = Record<string, Texture>;\n\n/**\n * Options for {@link createPixiSpriteAnimator}.\n *\n * @public\n */\nexport interface PixiSpriteAnimatorOptions {\n /**\n * Apply each frame's atlas anchor (`texture.defaultAnchor`) to the sprite when\n * the frame changes — preserving non-centre / foot pivots. Default `true`.\n * Set `false` to manage the anchor yourself.\n */\n readonly applyAnchor?: boolean;\n}\n\n/**\n * A PixiJS-bound animator: the core machine plus a sprite whose texture tracks\n * the active frame.\n *\n * @public\n */\nexport interface PixiSpriteAnimator {\n /** The bound sprite, updated in place. */\n readonly sprite: Sprite;\n /**\n * Run the core machine for `deltaMs`, then sync the sprite's texture (also\n * when a listener throws, before the error propagates).\n */\n update(deltaMs: number): void;\n /** Set a Number / Boolean input on the core machine. */\n setInput(name: string, value: number | boolean): void;\n /** Fire a Trigger on the core machine. */\n fireTrigger(name: string): void;\n /** Reset the core machine and re-sync the sprite. */\n reset(): void;\n /** Dispose the core machine. Idempotent. Does not destroy the sprite. */\n dispose(): void;\n /** Current state name. */\n readonly activeState: string;\n /** Current frame key. */\n readonly activeFrameKey: string;\n /** `true` once disposed. */\n readonly disposed: boolean;\n /** Subscribe to non-looping clip completions. Returns an unsubscribe. */\n onComplete(handler: CompleteHandler, options?: ListenerOptions): Unsubscribe;\n /** Subscribe to state changes. Returns an unsubscribe. */\n onStateChange(handler: StateChangeHandler, options?: ListenerOptions): Unsubscribe;\n}\n\nfunction toTextureMap(src: Spritesheet | TextureMap): TextureMap {\n // A Spritesheet exposes its frame textures under `.textures`; a plain map is\n // used directly. The check is structural (pixi.js is type-only here), so a\n // plain map with a frame literally named \"textures\" reads as a Spritesheet:\n // pass the Spritesheet, or wrap the map as `{ textures: map }`. A nullish\n // `src` (untyped callers) reads as an empty map, so every frame key is\n // reported missing instead of a bare TypeError.\n const maybe = src as { textures?: unknown } | null | undefined;\n if (maybe?.textures && typeof maybe.textures === \"object\") {\n return maybe.textures as TextureMap;\n }\n return (src ?? {}) as TextureMap;\n}\n\n/**\n * Bind an input-driven {@link SpriteGraph} to a PixiJS `Sprite`.\n *\n * @param sprite - a plain `Sprite` to drive; its `texture` (and, by default,\n * `anchor`) are updated in place. The adapter owns frame selection, so if a\n * *playing* `AnimatedSprite` is passed (it extends `Sprite`), its internal\n * playback is stopped to stop it fighting the adapter for the texture.\n * @param graph - the input-driven graph (same shape the core consumes).\n * @param textures - a `Spritesheet` or a frame-key → `Texture` map covering\n * every frame of every declared animation. Any object with an object-valued\n * `textures` property is read as a Spritesheet, so if a frame is literally\n * named `textures`, pass the Spritesheet (or `{ textures: map }`), not the\n * bare map.\n * @param options - see {@link PixiSpriteAnimatorOptions}.\n * @returns a {@link PixiSpriteAnimator}.\n * @throws {@link InvalidGraphError} if the graph is not an object or its\n * containers are malformed (checked before textures), or is otherwise invalid.\n * @throws {@link MissingTextureError} if a frame key has no texture, or a\n * `null` / `undefined` one.\n *\n * @public\n */\nexport function createPixiSpriteAnimator(\n sprite: Sprite,\n graph: SpriteGraph,\n textures: Spritesheet | TextureMap,\n options?: PixiSpriteAnimatorOptions,\n): PixiSpriteAnimator {\n // The texture scan below iterates `graph.animations`; check its shape first\n // so a malformed graph is an InvalidGraphError, not a bare TypeError.\n assertGraphShape(graph);\n const map = toTextureMap(textures);\n const applyAnchor = options?.applyAnchor !== false;\n\n // Fail-fast: every frame key of every declared animation must have a\n // texture. Use Object.hasOwn rather than `in` so that Object.prototype keys\n // such as \"constructor\" / \"toString\" are correctly rejected (mirroring\n // APPLY-1 in compile.ts which fixed the same class); an own entry holding\n // null / undefined is missing too, or sync() would blank the sprite.\n const missing = new Set<string>();\n for (const frameKeys of Object.values(graph.animations)) {\n for (const key of frameKeys) {\n if (!Object.hasOwn(map, key) || map[key] == null) missing.add(key);\n }\n }\n if (missing.size > 0) throw new MissingTextureError([...missing]);\n\n const core = createSpriteAnimator(graph);\n\n // The adapter owns the sprite's texture/anchor. An AnimatedSprite (which\n // extends Sprite, so the type permits it) drives its own texture from an\n // internal ticker; if it is playing it would fight our frame swaps. Stop it.\n // Structural check — pixi.js is type-only here, so no `instanceof`. Done\n // only after the checks above pass, so a failed bind has no side effect on\n // the caller's sprite.\n const playable = sprite as { stop?: () => void };\n if (typeof playable.stop === \"function\") playable.stop();\n\n // Swap the sprite's texture (and anchor) only when the active frame changes.\n // `undefined`, not `\"\"`, is the sentinel: `\"\"` is a legal frame key (the\n // schema has no minLength), so using it here would skip the initial bind\n // for a graph whose first frame key is the empty string.\n let boundKey: string | undefined;\n function sync(): void {\n // A listener may dispose() (and destroy the sprite) during core.update() /\n // core.reset(); the sprite is no longer ours to write to after that.\n if (core.disposed) return;\n const key = core.activeFrameKey;\n if (key === boundKey) return;\n boundKey = key;\n const tex = map[key]!; // verified present above\n sprite.texture = tex;\n if (applyAnchor && tex.defaultAnchor) {\n sprite.anchor.set(tex.defaultAnchor.x, tex.defaultAnchor.y);\n }\n }\n sync(); // bind the initial frame before the first update\n\n return {\n sprite,\n update(deltaMs) {\n // `finally`: a throwing listener must not leave the sprite on a frame\n // the core has already left.\n try {\n core.update(deltaMs);\n } finally {\n sync();\n }\n },\n setInput(name, value) {\n core.setInput(name, value);\n },\n fireTrigger(name) {\n core.fireTrigger(name);\n },\n reset() {\n try {\n core.reset();\n } finally {\n sync();\n }\n },\n dispose() {\n core.dispose();\n },\n get activeState() {\n return core.activeState;\n },\n get activeFrameKey() {\n return core.activeFrameKey;\n },\n get disposed() {\n return core.disposed;\n },\n onComplete(handler, options) {\n return core.onComplete(handler, options);\n },\n onStateChange(handler, options) {\n return core.onStateChange(handler, options);\n },\n };\n}\n"]}
|
|
@@ -115,9 +115,16 @@ interface TransitionDef {
|
|
|
115
115
|
readonly when?: readonly TransitionCondition[];
|
|
116
116
|
/**
|
|
117
117
|
* Higher wins. Among satisfied transitions, the highest priority is taken;
|
|
118
|
-
* ties break by declared order (earliest first). An integer
|
|
119
|
-
*
|
|
120
|
-
*
|
|
118
|
+
* ties break by declared order (earliest first). An integer (negative
|
|
119
|
+
* allowed); defaults to `0`. A non-integer (`1.5`, `NaN`, `Infinity`, a
|
|
120
|
+
* string) throws {@link InvalidGraphError} at load. (The JSON Schema
|
|
121
|
+
* constrains it to `integer`; TypeScript widens it to `number`.)
|
|
122
|
+
*
|
|
123
|
+
* Exception: a satisfied Number/Boolean self-transition (`to === from`)
|
|
124
|
+
* that consumes no Trigger is skipped regardless of its priority, so it
|
|
125
|
+
* cannot hold the state against other exits — resolution keeps scanning
|
|
126
|
+
* lower-priority candidates instead of stopping there. A self-transition
|
|
127
|
+
* that consumes a Trigger is not exempt and still wins on priority.
|
|
121
128
|
*/
|
|
122
129
|
readonly priority?: number;
|
|
123
130
|
}
|
|
@@ -220,19 +227,33 @@ interface SpriteAnimator {
|
|
|
220
227
|
*/
|
|
221
228
|
fireTrigger(name: string): void;
|
|
222
229
|
/**
|
|
223
|
-
* Advance by `deltaMs
|
|
224
|
-
*
|
|
230
|
+
* Advance by `deltaMs`: evaluate transitions, recompute the active frame,
|
|
231
|
+
* and fire `onComplete` for a finished non-looping clip. A negative,
|
|
232
|
+
* non-finite, or non-number `deltaMs` is clamped to no progress, and so is a
|
|
233
|
+
* step that would overflow the playback clock to `Infinity`.
|
|
225
234
|
* Deterministic — identical inputs and `dt` sequences yield identical frames.
|
|
226
|
-
*
|
|
235
|
+
*
|
|
236
|
+
* Run-to-completion: an `update()` / `reset()` called from inside an
|
|
237
|
+
* `onStateChange` / `onComplete` listener is queued and runs after the
|
|
238
|
+
* current call finishes (including its `onEnd` auto-transition), in call
|
|
239
|
+
* order; the nested call returns at once. A listener error drops the queued
|
|
240
|
+
* calls and propagates from the outermost call.
|
|
241
|
+
* Throws {@link SpriteAnimatorDisposedError} after `dispose` (also when
|
|
242
|
+
* called from a listener).
|
|
227
243
|
*/
|
|
228
244
|
update(deltaMs: number): void;
|
|
229
245
|
/**
|
|
230
246
|
* Return to the initial state and reset every input to its default, without
|
|
231
247
|
* releasing buffers. Fires `onStateChange` only if the state actually
|
|
232
|
-
* changed.
|
|
248
|
+
* changed. Queued like {@link SpriteAnimator.update} when called from a
|
|
249
|
+
* listener. Throws {@link SpriteAnimatorDisposedError} after `dispose`.
|
|
233
250
|
*/
|
|
234
251
|
reset(): void;
|
|
235
|
-
/**
|
|
252
|
+
/**
|
|
253
|
+
* Release all listeners and drop any queued `update()` / `reset()` calls.
|
|
254
|
+
* Never queued: from a listener it stops the current update at once.
|
|
255
|
+
* Idempotent; subsequent mutators throw.
|
|
256
|
+
*/
|
|
236
257
|
dispose(): void;
|
|
237
258
|
/**
|
|
238
259
|
* Subscribe to state changes. Returns an unsubscribe.
|
|
@@ -115,9 +115,16 @@ interface TransitionDef {
|
|
|
115
115
|
readonly when?: readonly TransitionCondition[];
|
|
116
116
|
/**
|
|
117
117
|
* Higher wins. Among satisfied transitions, the highest priority is taken;
|
|
118
|
-
* ties break by declared order (earliest first). An integer
|
|
119
|
-
*
|
|
120
|
-
*
|
|
118
|
+
* ties break by declared order (earliest first). An integer (negative
|
|
119
|
+
* allowed); defaults to `0`. A non-integer (`1.5`, `NaN`, `Infinity`, a
|
|
120
|
+
* string) throws {@link InvalidGraphError} at load. (The JSON Schema
|
|
121
|
+
* constrains it to `integer`; TypeScript widens it to `number`.)
|
|
122
|
+
*
|
|
123
|
+
* Exception: a satisfied Number/Boolean self-transition (`to === from`)
|
|
124
|
+
* that consumes no Trigger is skipped regardless of its priority, so it
|
|
125
|
+
* cannot hold the state against other exits — resolution keeps scanning
|
|
126
|
+
* lower-priority candidates instead of stopping there. A self-transition
|
|
127
|
+
* that consumes a Trigger is not exempt and still wins on priority.
|
|
121
128
|
*/
|
|
122
129
|
readonly priority?: number;
|
|
123
130
|
}
|
|
@@ -220,19 +227,33 @@ interface SpriteAnimator {
|
|
|
220
227
|
*/
|
|
221
228
|
fireTrigger(name: string): void;
|
|
222
229
|
/**
|
|
223
|
-
* Advance by `deltaMs
|
|
224
|
-
*
|
|
230
|
+
* Advance by `deltaMs`: evaluate transitions, recompute the active frame,
|
|
231
|
+
* and fire `onComplete` for a finished non-looping clip. A negative,
|
|
232
|
+
* non-finite, or non-number `deltaMs` is clamped to no progress, and so is a
|
|
233
|
+
* step that would overflow the playback clock to `Infinity`.
|
|
225
234
|
* Deterministic — identical inputs and `dt` sequences yield identical frames.
|
|
226
|
-
*
|
|
235
|
+
*
|
|
236
|
+
* Run-to-completion: an `update()` / `reset()` called from inside an
|
|
237
|
+
* `onStateChange` / `onComplete` listener is queued and runs after the
|
|
238
|
+
* current call finishes (including its `onEnd` auto-transition), in call
|
|
239
|
+
* order; the nested call returns at once. A listener error drops the queued
|
|
240
|
+
* calls and propagates from the outermost call.
|
|
241
|
+
* Throws {@link SpriteAnimatorDisposedError} after `dispose` (also when
|
|
242
|
+
* called from a listener).
|
|
227
243
|
*/
|
|
228
244
|
update(deltaMs: number): void;
|
|
229
245
|
/**
|
|
230
246
|
* Return to the initial state and reset every input to its default, without
|
|
231
247
|
* releasing buffers. Fires `onStateChange` only if the state actually
|
|
232
|
-
* changed.
|
|
248
|
+
* changed. Queued like {@link SpriteAnimator.update} when called from a
|
|
249
|
+
* listener. Throws {@link SpriteAnimatorDisposedError} after `dispose`.
|
|
233
250
|
*/
|
|
234
251
|
reset(): void;
|
|
235
|
-
/**
|
|
252
|
+
/**
|
|
253
|
+
* Release all listeners and drop any queued `update()` / `reset()` calls.
|
|
254
|
+
* Never queued: from a listener it stops the current update at once.
|
|
255
|
+
* Idempotent; subsequent mutators throw.
|
|
256
|
+
*/
|
|
236
257
|
dispose(): void;
|
|
237
258
|
/**
|
|
238
259
|
* Subscribe to state changes. Returns an unsubscribe.
|
package/llms-full.txt
CHANGED
|
@@ -15,7 +15,7 @@ The short index lives at `llms.txt` (see https://llmstxt.org/).
|
|
|
15
15
|
|
|
16
16
|
Input-driven, renderer-agnostic 2D sprite animation runtime. A JSON graph maps Number/Boolean/Trigger inputs to visual states and frames; adapters bind the chosen frame to a renderer.
|
|
17
17
|
|
|
18
|
-
> **Status: 0.
|
|
18
|
+
> **Status: 0.6.0 - stable family-aligned surface.** Core, PixiJS adapter, atlas parser, and JSON Schema subpath are shipped.
|
|
19
19
|
|
|
20
20
|
## Install
|
|
21
21
|
|
|
@@ -38,8 +38,8 @@ const anim = createSpriteAnimator({
|
|
|
38
38
|
},
|
|
39
39
|
initial: "idle",
|
|
40
40
|
states: {
|
|
41
|
-
idle: { animation: "idle" },
|
|
42
|
-
run: { animation: "run", speed: 1 },
|
|
41
|
+
idle: { animation: "idle", loop: true },
|
|
42
|
+
run: { animation: "run", loop: true, speed: 1 },
|
|
43
43
|
jump: { animation: "jump", loop: false, onEnd: "idle" },
|
|
44
44
|
},
|
|
45
45
|
transitions: [
|
|
@@ -77,13 +77,14 @@ view.dispose(); // disposes core animator; does not destroy the Pixi sprite
|
|
|
77
77
|
- `loadAtlas(atlas, control?)` parses and creates a `SpriteAnimator`.
|
|
78
78
|
- `aispritejs/schema` exports `schemas/aispritejs-graph.schema.json` for editor/CI validation.
|
|
79
79
|
- Parser validation is structural (`InvalidAtlasError`); compiler validation is semantic (`InvalidGraphError`).
|
|
80
|
+
- An explicit `control` gets the same structural checks as a control block inside the atlas, and its `initial` / `defaultFrameDuration` must be a string / number (an atlas's own block drops wrong-typed ones).
|
|
80
81
|
|
|
81
82
|
## Core API
|
|
82
83
|
|
|
83
|
-
- `createSpriteAnimator(graph)` returns a `SpriteAnimator
|
|
84
|
+
- `createSpriteAnimator(graph)` returns a `SpriteAnimator`; a malformed or invalid graph throws `InvalidGraphError` and builds nothing.
|
|
84
85
|
- `setInput(name, value)` accepts Number/Boolean inputs.
|
|
85
86
|
- `fireTrigger(name)` consumes Trigger inputs on transition.
|
|
86
|
-
- `update(deltaMs)` advances time, transitions, frame index, and `onEnd`.
|
|
87
|
+
- `update(deltaMs)` advances time, transitions, frame index, and `onEnd`. A negative, non-finite, or overflowing step is clamped to no progress.
|
|
87
88
|
- `reset()` returns to the initial state.
|
|
88
89
|
- `dispose()` is idempotent; mutators throw `SpriteAnimatorDisposedError` afterward.
|
|
89
90
|
- `onStateChange(handler, options?)` and `onComplete(handler, options?)` support `once` and `signal`.
|
|
@@ -91,10 +92,12 @@ view.dispose(); // disposes core animator; does not destroy the Pixi sprite
|
|
|
91
92
|
## Sharp Edges
|
|
92
93
|
|
|
93
94
|
- Non-looping states with `onEnd` transition during the same `update()` tick that completes the clip.
|
|
95
|
+
- `reset()`/`update()` called from inside `onStateChange`/`onComplete` run after the current update finishes (including its `onEnd` auto-transition); a `reset()` from `onComplete` therefore yields `onStateChange(onEndTarget, from)` then `onStateChange(initial, onEndTarget)`, ending in the initial state; `dispose()` from a listener stops the update immediately.
|
|
94
96
|
- `AnimatedSprite` is accepted by the Pixi adapter because it extends `Sprite`, but playback is stopped on bind so it cannot fight the adapter.
|
|
95
|
-
- Every
|
|
97
|
+
- Every frame key in every declared animation must exist in the texture map/spritesheet — including an animation no state references — or the adapter throws `MissingTextureError` at construction. A `null` / `undefined` entry counts as missing.
|
|
98
|
+
- A texture map with an object under the key `textures` is read as a `Spritesheet`; if a frame is literally named `textures`, pass the `Spritesheet` (or `{ textures: map }`) instead of the bare map.
|
|
96
99
|
- `duration`, `defaultFrameDuration`, and state `speed` must be finite numbers greater than zero.
|
|
97
|
-
-
|
|
100
|
+
- `initial`, state `animation` / `onEnd`, transition `from` / `to`, and condition `input` / `op` must be strings, and a transition `priority` must be an integer.
|
|
98
101
|
|
|
99
102
|
## AI Context
|
|
100
103
|
|
|
@@ -117,7 +120,37 @@ MIT
|
|
|
117
120
|
|
|
118
121
|
All notable changes to aispritejs are summarized here.
|
|
119
122
|
|
|
120
|
-
## [
|
|
123
|
+
## [0.6.0] - 2026-09-29
|
|
124
|
+
|
|
125
|
+
### Breaking
|
|
126
|
+
|
|
127
|
+
- `SpriteAnimator.update()` / `SpriteAnimator.reset()` (and the Pixi adapter's `update()` / `reset()`): a call made from inside an `onStateChange` / `onComplete` listener is now queued and runs after the current call finishes, including its `onEnd` auto-transition, instead of running inside the listener, because nested dispatch let the outer call's later listeners see a stale transition (run-to-completion, the ai*js rule for state-owning dispatchers); a `reset()` from `onComplete` therefore yields `onStateChange(onEndTarget, from)` then `onStateChange(initial, onEndTarget)`, ending in the initial state. Migration: listeners that inspected `activeState` (or `activeFrameKey` / `activeFrameIndex`) right after a nested `update()` / `reset()` must read it after the outer `update()` returns, code that counts `onStateChange` events around a `reset()` from `onComplete` must expect the extra `onEndTarget` pair, and errors thrown by a queued call must be caught around the outermost `update()` / `reset()`.
|
|
128
|
+
- `TransitionDef.priority`: a priority that is not an integer (`NaN`, `Infinity`, `1.5`, or a string such as `"1"`) now makes `createSpriteAnimator()` / `loadAtlas()` / `createPixiSpriteAnimator()` throw `InvalidGraphError` (`transition #<n> priority must be an integer, got <value>`), because such values made the candidate sort inconsistent and could flip which transition wins; the JSON Schema already required `integer`. Migration: use integer priorities (negative integers and `0` stay valid), for example convert JSON strings with `Number(p)` and round fractional values before building the graph.
|
|
129
|
+
|
|
130
|
+
### Changes
|
|
131
|
+
|
|
132
|
+
- Changed: size budgets in `scripts/check-size.mjs` (maintainer-approved for the 0.6.0 minor): `dist/pixi/index.js` 5,100 -> 5,200 B and `dist/atlas/index.js` 5,400 -> 5,500 B (`dist/index.js` stays at 4,400 B) for the run-to-completion mailbox, the identifier / priority / graph-shape / texture / control checks and the overflow guard, itemised in the script; after trimming duplicated gates, a dead guard and field, and comments kept in the bundle, the measured gzip closures are 4,358 -> 4,400, 5,042 -> 5,135 and 5,397 -> 5,486 B.
|
|
133
|
+
- Fixed: `emit()` re-checks listener membership on every call, so a `{ once }` listener can no longer fire twice when an earlier listener re-enters `update()`, and a listener removed mid-dispatch (by `dispose()` or an aborted `signal`) is no longer invoked.
|
|
134
|
+
- Fixed: `onComplete` gives each subscription its own identity instead of sharing a registry entry keyed by the raw handler, so subscribing the same handler twice no longer collides.
|
|
135
|
+
- Fixed: a throwing `onComplete`/`onStateChange` listener no longer wedges the state machine on its last frame — dispatch keeps calling the remaining listeners and `update()` still enters `onEnd`.
|
|
136
|
+
- Fixed: the Pixi adapter no longer writes to the sprite after `dispose()` is called from inside a listener mid-`update()`/`reset()`.
|
|
137
|
+
- Fixed: the Pixi adapter binds the initial frame correctly when its key is the empty string.
|
|
138
|
+
- Fixed: the Pixi adapter only stops a playable sprite's own playback after a bind succeeds, so a failed call has no side effect on the caller's sprite.
|
|
139
|
+
- Fixed: `createSpriteAnimator()` throws `InvalidGraphError` naming the field when `initial`, a state's `animation` / `onEnd`, a transition's `from` / `to`, or a condition's `input` / `op` is not a string (e.g. `transition #0 "from" must be a string, got object`); `Object.hasOwn` used to stringify a number or an array into a declared name, which could brick the animator or mis-evaluate conditions.
|
|
140
|
+
- Fixed: `createSpriteAnimator()` throws `InvalidGraphError` for a malformed graph shape (a non-object graph, `animations`, `inputs` or `states`, a non-array `transitions` or animation frame list, or a `null` input, state or condition entry) instead of a bare `TypeError`, or, for a string frame list, splitting it into one-character frame keys.
|
|
141
|
+
- Fixed: `update()` drops a step that would overflow the playback clock to `Infinity` (clamped to no progress, like an invalid `dt`), so a huge finite `dt` no longer freezes a looping clip on frame 0.
|
|
142
|
+
- Fixed: `createPixiSpriteAnimator()` treats an own `null` / `undefined` texture entry, or a nullish `textures` argument, as missing (`MissingTextureError`) instead of blanking the sprite or throwing a bare `TypeError`, and checks the graph's shape (`InvalidGraphError`) before scanning textures.
|
|
143
|
+
- Fixed: the Pixi adapter's `update()` / `reset()` sync the sprite even when a listener throws, so the sprite no longer stays on a frame the core has already left.
|
|
144
|
+
- Fixed: `parseAtlas()` / `loadAtlas()` run the embedded control block's structural checks on an explicit `control` as well (messages prefixed `control.`, e.g. `control.transitions[2].when[0] must be an object, got null`) and reject a non-string `control.initial` or a non-number `control.defaultFrameDuration`, all with `InvalidAtlasError`, instead of a bare `TypeError` from the compiler or a silently forwarded value.
|
|
145
|
+
- Fixed: `package.json` `exports` nests `types` under `import` and `require` (`require.types` points at the `.d.cts` files) for every code subpath, so `node16` / `nodenext` CommonJS consumers no longer hit TS1479; `verify-exports` walks nested conditions.
|
|
146
|
+
- Docs: the Pixi adapter's `textures` JSDoc and README Sharp Edges give the working workaround for a frame literally named `textures` (pass the `Spritesheet` or `{ textures: map }`); the old code comment's `spritesheet.textures` advice hit the same misdetection.
|
|
147
|
+
- Docs: STABILITY.md's Behavioral Contract states the run-to-completion clause, listener fan-out, the argument-validation boundary and the overflow clamp; `TransitionDef.priority`, `SpriteAnimator.update()` / `reset()` / `dispose()` and `InvalidGraphError` JSDoc match.
|
|
148
|
+
|
|
149
|
+
## [0.5.9] - 2026-06-29
|
|
150
|
+
|
|
151
|
+
- Fixed: a `reset()` / `dispose()` called from inside an `onComplete` handler is no longer clobbered by the state's `onEnd` auto-transition.
|
|
152
|
+
- Fixed: `clear()` fully clears its listener set even if an abort cleanup throws.
|
|
153
|
+
- Docs: schema hardening (non-empty `animations` + finite numeric maxima) is documented as shipped (was listed as backlog).
|
|
121
154
|
|
|
122
155
|
## [0.5.8] - 2026-06-14
|
|
123
156
|
|
|
@@ -155,16 +188,19 @@ All notable changes to aispritejs are summarized here.
|
|
|
155
188
|
## Behavioral Contract
|
|
156
189
|
|
|
157
190
|
- Core is renderer-agnostic and has zero runtime dependencies.
|
|
158
|
-
- Graph validation is fail-fast; invalid graphs do not produce half-built animators.
|
|
191
|
+
- Graph validation is fail-fast; invalid graphs do not produce half-built animators. A malformed graph shape (a non-object graph, `animations`, `inputs` or `states`, a non-array `transitions` or frame list, a `null` entry), a non-string identifier (`initial`, state `animation` / `onEnd`, transition `from` / `to`, condition `input` / `op`), or a non-integer `priority` throws `InvalidGraphError` naming the field, never a bare `TypeError`.
|
|
159
192
|
- Trigger inputs are consumed when a transition uses them.
|
|
160
|
-
- `update(dt)` clamps invalid/non-positive elapsed time to no progress.
|
|
161
|
-
- `
|
|
162
|
-
-
|
|
193
|
+
- `update(dt)` clamps invalid/non-positive elapsed time to no progress, and drops a step that would overflow the playback clock to `Infinity` (also no progress).
|
|
194
|
+
- `update()` / `reset()` are run-to-completion. A call made while the same animator is already dispatching (from an `onStateChange` / `onComplete` listener, or any callback the dispatch invokes) is appended to a FIFO mailbox and returns at once; after the outer call's last notification (and its `onEnd` auto-transition) the mailbox drains in order, each entry running the full update or reset before the next. `setInput()` / `fireTrigger()` only write the input store and are never queued. `dispose()` is never queued: it runs at once, clears the mailbox, and the drain stops. If a listener or a queued entry throws, the mailbox is cleared, the dispatching flag is reset, and the error propagates from the outermost call; the state stays at the last committed transition. Two animators have independent mailboxes. A listener that queues a call on every notification of a graph that always transitions keeps the drain running.
|
|
195
|
+
- Listener fan-out is synchronous over a snapshot of the listeners taken when the notification starts: a listener added meanwhile first fires on the next notification, one removed meanwhile (its unsubscribe, `once`, its `signal`, or `dispose()`) is skipped for the rest of that round, a `once` listener is removed before it is called, and a throwing listener does not stop the others (the first error is rethrown once all have run).
|
|
196
|
+
- `dispose()` is idempotent; post-dispose mutators throw (also from a listener).
|
|
197
|
+
- Pixi adapter owns sprite texture/anchor while bound and does not destroy the sprite on dispose. It syncs the sprite after every `update()` / `reset()`, also when a listener throws, and treats a `null` / `undefined` texture entry (or a nullish texture map) as missing.
|
|
198
|
+
- Argument misuse: `createSpriteAnimator` and `createPixiSpriteAnimator` report a malformed graph with `InvalidGraphError`; `parseAtlas` / `loadAtlas` report a malformed atlas or explicit `control` with `InvalidAtlasError`. Listener handlers, `ListenerOptions.signal`, and the Pixi `sprite` are trusted typed arguments; misuse there can still surface as a bare `TypeError` (REVIEW.md backlog).
|
|
163
199
|
|
|
164
200
|
## Caveats
|
|
165
201
|
|
|
166
|
-
- Atlas parser validates shape; compiler validates semantic graph correctness.
|
|
167
|
-
-
|
|
202
|
+
- Atlas parser validates shape (for the atlas's own control block and an explicit `control` alike); compiler validates semantic graph correctness.
|
|
203
|
+
- Every frame key in every declared animation needs a texture in the Pixi adapter, not just frames reachable from the graph's states.
|
|
168
204
|
- Schema hardening can improve editor feedback but does not replace runtime validation.
|
|
169
205
|
|
|
170
206
|
---
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aispritejs",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Input-driven, renderer-agnostic 2D sprite animation runtime — a tiny, Rive-like visual state machine driven by Number / Boolean / Trigger inputs. JSON transition graph, deterministic update(dt), zero runtime dependencies. Browser / Node / Bun / Deno / WebView / Worker friendly.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"sprite",
|
|
@@ -35,19 +35,34 @@
|
|
|
35
35
|
"types": "./dist/index.d.ts",
|
|
36
36
|
"exports": {
|
|
37
37
|
".": {
|
|
38
|
-
"
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
"import": {
|
|
39
|
+
"types": "./dist/index.d.ts",
|
|
40
|
+
"default": "./dist/index.js"
|
|
41
|
+
},
|
|
42
|
+
"require": {
|
|
43
|
+
"types": "./dist/index.d.cts",
|
|
44
|
+
"default": "./dist/index.cjs"
|
|
45
|
+
}
|
|
41
46
|
},
|
|
42
47
|
"./pixi": {
|
|
43
|
-
"
|
|
44
|
-
|
|
45
|
-
|
|
48
|
+
"import": {
|
|
49
|
+
"types": "./dist/pixi/index.d.ts",
|
|
50
|
+
"default": "./dist/pixi/index.js"
|
|
51
|
+
},
|
|
52
|
+
"require": {
|
|
53
|
+
"types": "./dist/pixi/index.d.cts",
|
|
54
|
+
"default": "./dist/pixi/index.cjs"
|
|
55
|
+
}
|
|
46
56
|
},
|
|
47
57
|
"./atlas": {
|
|
48
|
-
"
|
|
49
|
-
|
|
50
|
-
|
|
58
|
+
"import": {
|
|
59
|
+
"types": "./dist/atlas/index.d.ts",
|
|
60
|
+
"default": "./dist/atlas/index.js"
|
|
61
|
+
},
|
|
62
|
+
"require": {
|
|
63
|
+
"types": "./dist/atlas/index.d.cts",
|
|
64
|
+
"default": "./dist/atlas/index.cjs"
|
|
65
|
+
}
|
|
51
66
|
},
|
|
52
67
|
"./schema": "./schemas/aispritejs-graph.schema.json"
|
|
53
68
|
},
|