@tawaship/pixi-animate-runtime 0.0.0-stage → 0.1.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.
Files changed (38) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +452 -2
  3. package/bin/pixi-animate-runtime.js +27 -0
  4. package/dist/generator.cjs.js +1056 -0
  5. package/dist/generator.cjs.js.map +1 -0
  6. package/dist/pixi-animate-runtime.cjs.js +3678 -0
  7. package/dist/pixi-animate-runtime.cjs.js.map +1 -0
  8. package/dist/pixi-animate-runtime.esm.js +3613 -0
  9. package/dist/pixi-animate-runtime.esm.js.map +1 -0
  10. package/dist/pixi-animate-runtime.js +3680 -0
  11. package/dist/pixi-animate-runtime.js.map +1 -0
  12. package/dist-skills/animate-data-converter-guide/SKILL.md +86 -0
  13. package/dist-skills/pixi-animate-runtime-guide/SKILL.md +146 -0
  14. package/package.json +70 -4
  15. package/tools/pixi-animate-runtime.bat +8 -0
  16. package/types/generator/generator/cli.d.ts +17 -0
  17. package/types/generator/generator/converter-version.d.ts +7 -0
  18. package/types/generator/generator/generate.d.ts +33 -0
  19. package/types/generator/generator/index.d.ts +6 -0
  20. package/types/generator/generator/plan.d.ts +87 -0
  21. package/types/generator/player/data.d.ts +141 -0
  22. package/types/generator/player/ease.d.ts +11 -0
  23. package/types/generator/player/semver.d.ts +27 -0
  24. package/types/generator/player/version.d.ts +2 -0
  25. package/types/player/animate-container.d.ts +71 -0
  26. package/types/player/content.d.ts +170 -0
  27. package/types/player/data.d.ts +141 -0
  28. package/types/player/display.d.ts +186 -0
  29. package/types/player/ease.d.ts +11 -0
  30. package/types/player/events.d.ts +60 -0
  31. package/types/player/geom.d.ts +19 -0
  32. package/types/player/index.d.ts +20 -0
  33. package/types/player/loader.d.ts +53 -0
  34. package/types/player/movieclip.d.ts +191 -0
  35. package/types/player/objects.d.ts +320 -0
  36. package/types/player/semver.d.ts +27 -0
  37. package/types/player/transform.d.ts +32 -0
  38. package/types/player/version.d.ts +2 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 tawaship
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,453 @@
1
- # Temporary Holding Version
1
+ # @tawaship/pixi-animate-runtime
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Plays content made in Adobe Animate (published for HTML5 Canvas) on PixiJS, without CreateJS.
4
+
5
+ - **Convert once, at development time**: a command turns the files Animate publishes into an optimized
6
+ *runtime content* file
7
+ - **Play the runtime content in your PixiJS application**: the player plays it as CreateJS would, without
8
+ CreateJS and without the published files
9
+ - **What it brings**: creating many instances at once stays light (each symbol is prepared once and shared),
10
+ playback stays even when frame times vary, and what is played is checked frame by frame against CreateJS
11
+
12
+ Coming from [@tawaship/pixi-animate-container](https://github.com/tawaship/pixi-animate-container)? It plays the
13
+ same content and is used the same way; see [Coming from pixi-animate-container](#coming-from-pixi-animate-container).
14
+
15
+ **Status: under development.** What does not work yet is listed under [Current limits](#current-limits).
16
+
17
+ API reference: https://tawaship.github.io/pixi-animate-runtime/docs/
18
+
19
+ Samples (content published from Animate, played by this package or by CreateJS): https://tawaship.github.io/pixi-animate-runtime/samples/
20
+
21
+ **Convert only content you trust.** Converting runs the published JavaScript, frame scripts included, to read
22
+ it. It runs in a sandbox (QuickJS, a separate JavaScript engine compiled to WebAssembly) that is given nothing
23
+ of your machine: no files, no commands, no network. Running code is never without risk all the same: a flaw in
24
+ the sandbox or the engines could let code in the file reach further. Convert content published by you or by
25
+ people you trust, and treat content you receive (from a contractor, an asset store and so on) as code you would
26
+ run. In the browser, the runtime content is as safe as the published content would be.
27
+
28
+ ## Quick start
29
+
30
+ The published files are needed only to convert. Your application ships the runtime content (and the images it
31
+ uses), not the published files. This example keeps them apart:
32
+
33
+ ```
34
+ my-project/
35
+ ├─ animate/ what Animate publishes (read when converting; not shipped)
36
+ └─ public/ what the application ships
37
+ ├─ animates/ the runtime content, written by the command
38
+ ├─ lib/ PixiJS and the player
39
+ └─ index.html
40
+ ```
41
+
42
+ 1. **Publish from Animate.** In Adobe Animate, publish the document for HTML5 Canvas into `animate/` (you get
43
+ `game.js`, `game.html` and an `images/` directory).
44
+
45
+ 2. **Install.** In the project (Node.js 18 or later):
46
+
47
+ ```
48
+ npm install pixi.js@5 @tawaship/pixi-animate-runtime
49
+ ```
50
+
51
+ Copy `node_modules/pixi.js/dist/pixi.min.js` and
52
+ `node_modules/@tawaship/pixi-animate-runtime/dist/pixi-animate-runtime.js` into `public/lib/` (or import the
53
+ player with a bundler).
54
+
55
+ 3. **Convert.** From the project's root:
56
+
57
+ ```
58
+ npx --no-install @tawaship/pixi-animate-runtime animate --out-dir public/animates
59
+ ```
60
+
61
+ It lists what it will write and asks; answer `y`. This writes `public/animates/game.js` (the runtime
62
+ content) and copies the images it uses into `public/animates/images/`. The list shows the content's
63
+ **composition id**, which the next step needs.
64
+
65
+ 4. **Play.** In `public/index.html`, load PixiJS, the player and the runtime content, and put the symbol the
66
+ published HTML showed (named after the published file, here `game`) on an `AnimateContainer`:
67
+
68
+ ```html
69
+ <script src="lib/pixi.min.js"></script>
70
+ <script src="lib/pixi-animate-runtime.js"></script>
71
+ <script src="animates/game.js"></script>
72
+ <script>
73
+ const app = new PIXI.Application({ width: 550, height: 400 });
74
+ document.body.appendChild(app.view);
75
+
76
+ const { loadContentAsync, AnimateContainer } = PIXI.animateRuntime;
77
+ loadContentAsync({ id: '<composition id>', basepath: 'animates/' }).then(content => {
78
+ const container = new AnimateContainer();
79
+ app.stage.addChild(container);
80
+ app.ticker.add(container.handleTick, container);
81
+ container.addCreatejs(content.create('game'));
82
+ });
83
+ </script>
84
+ ```
85
+
86
+ The content plays at the frame rate it was published with.
87
+
88
+ 5. **After publishing again from Animate, run step 3 again.** The player plays the runtime content, never the
89
+ published files: a change made in Animate shows only once the runtime content is made again.
90
+
91
+ The sections below explain each step: [Converting published content](#converting-published-content) (options,
92
+ the Windows batch file, the Node API) and [Playing content](#playing-content) (loading options, the container,
93
+ the objects).
94
+
95
+ ## What you need
96
+
97
+ - PixiJS 5.3 (`pixi.js` ^5.3.2) in your application
98
+ - Node.js 18 or later to convert the published file (the conversion does not run in the browser)
99
+
100
+ ## Supported versions
101
+
102
+ - Content published by **Adobe Animate 24.0.15** for HTML5 Canvas. What this package reads is the form of the published
103
+ JavaScript, which depends on the version of Animate; content published by other versions has not been checked
104
+ - PixiJS 5.3 (`pixi.js` ^5.3.2). Checked with 5.3.9
105
+
106
+ ## Converting published content
107
+
108
+ Turn the published content into runtime content with the command line tool, giving it the directory Animate
109
+ published into (the one with the published `.js` files; `.fla` files and an `images/` directory there are
110
+ optional):
111
+
112
+ ```
113
+ npx --no-install @tawaship/pixi-animate-runtime assets/animates
114
+ ```
115
+
116
+ `--no-install` runs only the copy installed in your project: without it, `npx` would download and run a package from the npm registry when the command is not installed. In the `scripts` of your `package.json`, the command name alone is enough (`"animate": "pixi-animate-runtime animate -o public/animates"`): npm runs scripts with the commands installed in the project and downloads nothing.
117
+
118
+ To keep `npx` from downloading anything in your project at all (also when `--no-install` is forgotten), put
119
+ `yes=false` in the project's `.npmrc`. `npx` then stops with an error when a command is not installed, instead
120
+ of asking or, where nobody can answer (CI, scripts), downloading it without asking.
121
+
122
+ This writes, into `assets/animates/generated/`, one runtime content file for every published JavaScript file
123
+ in the directory, under the same name (`game.js` to `generated/game.js`), and copies the images each content
124
+ uses next to it at the same relative paths (`generated/images/...`). Images left over from earlier publishes
125
+ that no content uses are not copied, and scripts that are not Animate output are left alone. Copy the whole
126
+ `generated/` directory into your application's assets.
127
+
128
+ | Argument / option | Effect |
129
+ |---|---|
130
+ | a published `.js` file instead of a directory | Converts that file only |
131
+ | `-o, --out-dir <directory>` | Writes straight into that directory instead of `generated/` |
132
+ | `-r, --recursive` | Also converts the published files in the directories below (not in `node_modules`, nor in directories whose name starts with `.`). With `-o`, each is written below that directory at the same relative path (`title/title.js` to `<directory>/title/title.js`); without it, into `generated/` next to its published file |
133
+ | `-y, --yes` | Writes without asking anything (directories not given take their defaults). Needed when run from a script or CI |
134
+ | `-f, --force` | Writes runtime content even when the content uses features that are not supported |
135
+ | `-h, --help` | Shows the usage |
136
+
137
+ Before writing anything, the tool lists where it reads from and writes to, then a row for each published file:
138
+ its composition id, the number of images it uses, and whether it is new, replaces a file already there, or is
139
+ not written (and why):
140
+
141
+ ```
142
+ From: assets/animates
143
+ To: generated/ next to each published file
144
+
145
+ published composition id images
146
+ game.js 2FA8E0C7... 3 new
147
+ title.js D6D50362... 1 replace
148
+ old.js B3DEAFD3... not written: images it uses are missing: images/old_atlas_1.png
149
+
150
+ 2 to write, 1 not written.
151
+ Write these? (y/N):
152
+ ```
153
+
154
+ Composition ids are shortened in this list; the whole id is in the published file (`lib.properties.id`) and at
155
+ the top of the runtime content file.
156
+
157
+ Nothing is written unless you answer `y`. Run from a script or CI, where nobody can answer (not a terminal),
158
+ it only lists and writes nothing unless `-y` is given; with `-y`, it asks nothing and writes. Run in a terminal
159
+ without a directory (and without `-y`), the tool first asks for it, lists the published files it found, and
160
+ asks where to write (empty: `generated/`).
161
+
162
+ Nothing is ever deleted: files already in the output directory that the run did not write are listed, for you
163
+ to remove when they are no longer needed.
164
+
165
+ A content is not written when it uses unsupported features (each one is listed), when an image it uses is
166
+ missing, or when the path of an image leads out of the published directory (Animate writes them below it);
167
+ the other contents still are, and the command then exits with `1`. It exits with `2` when the directory or
168
+ file cannot be read.
169
+
170
+ On Windows, `tools/pixi-animate-runtime.bat` (in the installed package) does the same: copy it into your
171
+ project, then drop the published directory on it, or double-click it to be asked.
172
+
173
+ The same is available from Node, in two steps, so that a script of your own can show the plan, ask its own
174
+ questions or choose what to write in between:
175
+
176
+ ```js
177
+ const { planGeneration, writeGeneration } = require('@tawaship/pixi-animate-runtime/generator');
178
+
179
+ planGeneration({ input: 'assets/animates', outDir: 'public/animates', recursive: true }).then(plan => {
180
+ plan.contents.forEach(content => {
181
+ console.log(content.name, '->', content.out, content.problem || '');
182
+ });
183
+ const result = writeGeneration(plan);
184
+ console.log(result.written.length + ' file(s) written');
185
+ });
186
+ ```
187
+
188
+ `planGeneration(options)` takes what the command line takes (`input`, `outDir`, `recursive`, `force`, `cwd`),
189
+ makes the runtime content and works out where everything goes, but writes nothing. Each of `plan.contents` is
190
+ one published file: `name`, `id` (composition id), `out` (where it goes), `exists` (a file is already there),
191
+ `images` (`from` and `to`), `issues`, `warnings`, and `problem`, the reason it is not written (`null` when it
192
+ is). `writeGeneration(plan)` writes the runtime content as the plan made it (a published file changed in
193
+ between does not change what is written), copies the images, and returns `written` (the files it wrote) and
194
+ `left` (the files already in the output directories that it did not write). It deletes nothing. To write only some of the contents, give it a plan with
195
+ the others removed from `contents`.
196
+
197
+ For one published file without writing anything:
198
+
199
+ ```js
200
+ const { generateFromPublishedFile } = require('@tawaship/pixi-animate-runtime/generator');
201
+
202
+ generateFromPublishedFile('assets/animates/game.js').then(generated => {
203
+ require('fs').writeFileSync('game.js', generated.source);
204
+ });
205
+ ```
206
+
207
+ `generated.id` is the content's composition id, `generated.data` its data, `generated.issues` lists
208
+ unsupported features and `generated.warnings` what to look at (see [Versions](#versions)). Images are not
209
+ copied by this function.
210
+
211
+ `generate(definition)` makes the runtime content from a definition you already have, in the form
212
+ `@tawaship/animate-data-converter` extracts it (its compact form). An expanded definition (written with
213
+ `--expanded`, or made with `expand()`) is refused: turn it back with the converter's `compact()` first.
214
+
215
+ Frame scripts are copied into the runtime content as they were published, as code. Nothing is rewritten
216
+ (a `console.log` left in a script stays), and the runtime content needs no `eval` at run time.
217
+
218
+ ### Composition ids
219
+
220
+ A runtime content file registers itself, when it is loaded, under the content's **composition id**: the
221
+ `lib.properties.id` of the published content, the id Animate gives a document when the `.fla` is created. The
222
+ player takes the content by this id.
223
+
224
+ - Copying a `.fla` keeps its composition id, so two contents made that way share one id. Create a new document
225
+ for a new content (the id can also be changed by editing the XML inside the `.fla`)
226
+ - Loading a runtime content file whose id is already registered replaces the earlier one, with a warning on the
227
+ console. Contents already built from the earlier one keep working; contents loaded afterwards use the new one.
228
+ This lets an application load contents of the same make (the same symbols, different looks) under one id in
229
+ turn. Two different contents with one id are not told apart: if their symbols share names, the later content
230
+ is shown where the earlier was meant
231
+
232
+ ## Playing content
233
+
234
+ Load the runtime content file, load the content by its composition id, and put an instance of a symbol on an
235
+ `AnimateContainer`:
236
+
237
+ ```html
238
+ <script src="pixi.js"></script>
239
+ <script src="pixi-animate-runtime.js"></script>
240
+ <script src="assets/animates/game.js"></script> <!-- a runtime content file -->
241
+ ```
242
+
243
+ ```js
244
+ const { loadContentAsync, AnimateContainer } = PIXI.animateRuntime;
245
+
246
+ loadContentAsync({ id: '2FA8E0C7230941478CE2CA3DB82DBEDF', basepath: 'assets/animates/' })
247
+ .then(content => {
248
+ const container = new AnimateContainer();
249
+ app.stage.addChild(container);
250
+ app.ticker.add(container.handleTick, container);
251
+
252
+ container.addCreatejs(content.create('game')); // the symbol the published HTML used as its root
253
+ });
254
+ ```
255
+
256
+ This loads the content's images from `basepath` and shows the `game` symbol, playing it at the frame rate it
257
+ was published with, whatever the display's refresh rate: `handleTick` turns the ticker's time into the
258
+ content's frames.
259
+
260
+ - Runtime content files are loaded by you, before `loadContentAsync`: with script tags as above, or by adding
261
+ script elements at run time. They register into `window.PixiAnimateRuntimeContents` and can be loaded
262
+ before or after the player
263
+ - `id` is the composition id (the command line tool prints it, and it is written at the top of the runtime
264
+ content file). When no content is registered under it, the error lists the ones that are
265
+ - `basepath` is the directory the content's image paths are relative to: where the runtime content file was
266
+ written, ending with `/`. `options: { crossOrigin: false }` loads the images without CORS (requested with
267
+ CORS, anonymous, by default)
268
+ - `options: { handleManifest(manifest) { ... } }` is called with the content's images (`{ id, src }`, `src` as
269
+ published) before they are loaded. Changing an entry's `src` changes what this load reads: a `data:` or
270
+ `blob:` URL is used as it is, another path is resolved against `basepath`. It may return a promise to make the
271
+ loading wait
272
+ - The second argument, `{ version }`, adds `_fv=<version>` to every image URL to bypass caches; the query
273
+ Animate adds to each image path on every publish stays
274
+ - An array of targets loads several contents, giving an array of contents
275
+ - `create(id)` creates an instance of any symbol, set up the way the published HTML sets up the root it
276
+ creates: use it for the instances your application places. `content.lib` holds the symbol classes;
277
+ `new content.lib.game()` creates an instance the way a frame script does, without that setup (it only
278
+ matters for color effects, which the player draws the same either way)
279
+ - `content.id` is the composition id, and `content.properties` holds the values of the published
280
+ `lib.properties` (`width`, `height`, `fps`, `color` and so on; read-only)
281
+ - To use an image of the content elsewhere, create the symbol that shows it and take its Pixi node:
282
+ `content.create('Bitmap1').pixi.texture`
283
+ - `container.speed` changes the playback speed (`1` is the published frame rate, `0` stops). When the
284
+ display runs slower than the content, one call plays several frames; set `container.overSpeed = false`
285
+ to play at most one frame per call
286
+ - `container.smooth` (`true` by default) keeps the playing even when the time between frames of the display
287
+ varies a little: the frames follow a steady clock (the usual time between frames of the display), so a
288
+ display frame coming a little early or late does not leave one call with no frame and the next with two.
289
+ The clock keeps to the real time, and frames the display misses are still caught up at once. `false`
290
+ plays by the time of each call as it is, as pixi-animate-container does
291
+ - A movie clip starts from its first frame when it is put on the container. `removeCreatejs(instance)`
292
+ takes it off, and it stops playing
293
+ - `tick(instance)` plays exactly one frame, for stepping through content by hand
294
+ - To play roots on a container of your own (of another framework built on PixiJS), give it an
295
+ `AnimateController`: `new AnimateController(container)` has the `addCreatejs`, `handleTick`, `speed`,
296
+ `overSpeed` and `smooth` of `AnimateContainer`, putting the instances on that container
297
+
298
+ For Pixim.js applications, use [@tawaship/pixim-animate-runtime](https://github.com/tawaship/Pixim-animate-runtime): it
299
+ includes this player and loads contents as assets of a Pixim content.
300
+
301
+ With a bundler, import the player instead (`import { loadContentAsync } from '@tawaship/pixi-animate-runtime'`);
302
+ the runtime content files are still loaded as scripts.
303
+
304
+ ### Objects and their Pixi nodes
305
+
306
+ Every instance is a CreateJS-compatible object (what frame scripts see as `this`), shown by a PixiJS node
307
+ in its `pixi` property. Values the two have in common are kept in one place: `instance.x = 100` and
308
+ `instance.pixi.x = 100` are the same write, and what the timeline does with it afterwards is what
309
+ CreateJS would do.
310
+
311
+ Members whose meaning differs between CreateJS and PixiJS keep the CreateJS meaning on the instance and
312
+ the PixiJS meaning on its node:
313
+
314
+ | Member | On the instance | On `instance.pixi` |
315
+ |---|---|---|
316
+ | `rotation` | Degrees | Radians (`angle` is degrees) |
317
+ | `skewX` / `skewY` | Degrees, CreateJS skew | `skew` in radians |
318
+ | `mask`, `filters`, `parent`, `children` | CreateJS objects | PixiJS objects |
319
+
320
+ Shapes that show the same drawing share one PixiJS geometry, even across symbols. Do not draw into the
321
+ `pixi` node of a shape (`graphics.clear()`, `lineTo()` and so on): the change would show in every shape that
322
+ shares the drawing.
323
+
324
+ Rotation and skew combine the way CreateJS combines them (the object is rotated first and then skewed),
325
+ which matches what the published content shows in a browser.
326
+
327
+ Dynamic text breaks into lines the way the published content does: a line wider than the text box is broken
328
+ at spaces only, so a long word, or text without spaces such as Japanese, runs past the box. Animate's own
329
+ stage breaks such text inside words, so the two can look different; the player follows the published
330
+ content.
331
+
332
+ Frame scripts can use the names published code uses: `this`, `cjs` / `createjs` (the player's
333
+ CreateJS-compatible classes), and `lib` (the symbol classes). `exportRoot` and `stage` are undefined.
334
+ `playSound()` plays nothing.
335
+
336
+ ## Coming from pixi-animate-container
337
+
338
+ pixi-animate-container loads CreateJS and the published JavaScript in the browser, runs them, and shows the
339
+ CreateJS display objects through PixiJS. This package does the CreateJS part once, at development time:
340
+
341
+ - In the browser, only PixiJS, the player and the runtime content files are loaded. CreateJS and the
342
+ published JavaScript are not
343
+ - After publishing again from Animate, generate the runtime content again: the player does not read the
344
+ published file
345
+ - Each symbol is defined once and shared by every instance, instead of each instance building its own
346
+ timeline when it is created
347
+
348
+ The way content is put on the screen stays the same, under new names:
349
+
350
+ | pixi-animate-container | pixi-animate-runtime |
351
+ |---|---|
352
+ | `<script>` tags for CreateJS and the published `.js` | A `<script>` tag for the runtime content file (no CreateJS) |
353
+ | `PIXI.animate` | `PIXI.animateRuntime` |
354
+ | `loadAssetAsync(targets)`, giving `lib` | `loadContentAsync(targets)`, giving a content (its classes are in `content.lib`) |
355
+ | `new lib.game()` | `content.create('game')` (`new content.lib.game()` also works, see [Playing content](#playing-content)) |
356
+ | `PIXI.animate.Container` | `AnimateContainer` |
357
+ | `addCreatejs`, `addCreatejsAt`, `removeCreatejs`, `handleTick` | The same |
358
+ | `createjsSpeed` | `speed` |
359
+ | `createjsOverSpeed` | `overSpeed` |
360
+ | (none) | `smooth`: even playing when the time between frames varies (on by default; `false` plays as pixi-animate-container does) |
361
+ | `options: { crossOrigin }` | The same |
362
+ | `CreatejsMovieClip`, `CreatejsShape` and the other wrapper classes | `MovieClip`, `Shape` and the other CreateJS-compatible classes; `instance.pixi` is still the PixiJS node |
363
+
364
+ What behaves differently:
365
+
366
+ - `instance.pixi` is always current. The instance and its node share their values (see
367
+ [Objects and their Pixi nodes](#objects-and-their-pixi-nodes)), so a frame script reading `instance.pixi.x`
368
+ gets the value just written, not the value of the previous frame
369
+ - Not available: `options.listenFrameEvents` (`endAnimation`, `reachLabel`), `options.useSound` (sound is not
370
+ played), and CreateJS mouse events on instances (see [Current limits](#current-limits))
371
+
372
+ ## Current limits
373
+
374
+ - Buttons do not react to the pointer: a button shows its idle ("up") frame and stays there. Listeners for
375
+ CreateJS mouse events (`mousedown`, `click`, `rollover` and so on) are not called. To react to the
376
+ pointer, listen to PixiJS events on the instance's `pixi` node (`pointerdown`, `pointertap`,
377
+ `pointerover` and so on, with `interactive = true`). Masks are never hit by the pointer
378
+ - Dynamic text is provisional. Lines break as in the published content (see above). Depending on the font,
379
+ text sits slightly higher than in the published content. Paragraph indents and margins set in Animate are not
380
+ in the published content and have no effect. `textBaseline` other than `top`, `maxWidth` and `outline` are
381
+ not checked yet
382
+ - Sound is not played
383
+ - Content that uses the Animate camera ("Advanced Layers") is not supported: delete the camera layer and
384
+ publish again
385
+
386
+ ## How it works
387
+
388
+ Published content defines each symbol as a class whose constructor builds its children and its whole timeline,
389
+ for every instance. The command runs the published code once, at development time (in its sandbox, with
390
+ `@tawaship/animate-data-converter`), and records each symbol as data: the display objects it owns and the
391
+ value of every property its timeline sets, at every frame. The runtime content holds that data, written
392
+ compactly, and the frame scripts as code.
393
+
394
+ The player builds every instance of a symbol from the same data, so creating many instances at once does not
395
+ build a timeline for each, and it needs neither CreateJS nor the published code. It plays the frames as CreateJS
396
+ does: tests play every test content on both, frame by frame (see
397
+ [How correctness is checked](#how-correctness-is-checked)).
398
+
399
+ ## Versions
400
+
401
+ Runtime content records the version of this package that generated it, and the player checks it when the
402
+ content is loaded (`loadContentAsync`):
403
+
404
+ | Runtime content generated by | What the player does |
405
+ |---|---|
406
+ | A version in `RUNTIME_CONTENT_RANGE`, not newer than the player | Plays it |
407
+ | A version in `RUNTIME_CONTENT_RANGE`, newer than the player | Plays it and warns on the console: what the newer version added may not play. Update the player |
408
+ | A version outside `RUNTIME_CONTENT_RANGE` (or none) | Fails to load (the promise is rejected). The message says whether to update the player or to generate the runtime content again |
409
+
410
+ The generator checks the same way the definitions it is given: a definition records the version of
411
+ `@tawaship/animate-data-converter` that extracted it. One from a version outside the range this package
412
+ depends on is refused, and one from a version newer than the generator was built with is generated with a
413
+ warning (in `generated.warnings`; the command line tool prints it and still writes the runtime content).
414
+ This matters when definitions are kept as files (`animate-data-converter --out`) and generated later.
415
+
416
+ `VERSION` is the version of the player, and `satisfies(version, range)` tests a version against a range
417
+ written as npm writes it (`0.x || 1.x`, `^1.2.0`, `>=1.0.0 <2.0.0`).
418
+
419
+ ## Claude Code skills
420
+
421
+ The package comes with skills for Claude Code (Anthropic's AI coding tool) in `dist-skills/`: `pixi-animate-runtime-guide`, which tells it how to generate runtime content and play it, and `animate-data-converter-guide` (the converter the generator runs). To
422
+ let Claude Code use them in your project, copy them into the project's `.claude/skills/`:
423
+
424
+ ```
425
+ mkdir -p .claude/skills
426
+ cp -r node_modules/@tawaship/pixi-animate-runtime/dist-skills/* .claude/skills/
427
+ ```
428
+
429
+ Copy them again after updating the package. Each skill names the versions it was written for; with another
430
+ version, it tells Claude Code to follow the type definitions in `node_modules` where they disagree.
431
+
432
+ ## How correctness is checked
433
+
434
+ The player was developed with Claude (Anthropic's AI assistant). Its correctness rests on testing rather
435
+ than on trust in the author: every test content is played both by CreateJS and by this player, and their
436
+ states are compared at every frame (positions, playback, display lists, drawing commands, text, filters,
437
+ masks, buttons), together with what the PixiJS nodes show (their matrices, masks, filters, textures).
438
+ What is drawn is compared too: every test content is drawn by CreateJS and by this player in the same
439
+ headless browser, frame by frame, and the pixels are compared, with differences in antialiasing allowed for.
440
+ Known faults are put into the player on purpose to confirm that the comparisons notice them.
441
+
442
+ ## Change log
443
+
444
+ ### 0.1.0
445
+
446
+ - First release: generates runtime content from content published by Adobe Animate for HTML5 Canvas (the command
447
+ line tool and the API), and plays it on PixiJS 5.3 without CreateJS (`loadContentAsync`, `AnimateContainer`,
448
+ `AnimateController`, with `speed`, `overSpeed` and `smooth`)
449
+ - Claude Code skills in `dist-skills/`, published with the package
450
+
451
+ ## License
452
+
453
+ MIT
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ var readline = require('readline');
5
+ var generator = require('../dist/generator.cjs.js');
6
+
7
+ // Questions are asked only in a terminal: run from a script or CI, the defaults are used
8
+ var ask = process.stdin.isTTY ? function (question) {
9
+ return new Promise(function (resolve) {
10
+ var rl = readline.createInterface({ input: process.stdin, output: process.stdout });
11
+ rl.question(question, function (answer) {
12
+ rl.close();
13
+ resolve(answer);
14
+ });
15
+ });
16
+ } : undefined;
17
+
18
+ generator.runCli(process.argv.slice(2), {
19
+ stdout: function (text) { process.stdout.write(text); },
20
+ stderr: function (text) { process.stderr.write(text); },
21
+ ask: ask
22
+ }).then(function (code) {
23
+ process.exitCode = code;
24
+ }, function (error) {
25
+ process.stderr.write((error && error.stack ? error.stack : String(error)) + '\n');
26
+ process.exitCode = 2;
27
+ });