@moku-labs/game 0.0.2 → 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 (35) hide show
  1. package/README.md +107 -31
  2. package/dist/assets.d.mts +58 -6
  3. package/dist/assets.mjs +1630 -234
  4. package/dist/commands-C0BljsM5.d.mts +99 -0
  5. package/dist/control.d.mts +3 -92
  6. package/dist/control.mjs +3 -757
  7. package/dist/define-CjP_4H0G.d.mts +51 -0
  8. package/dist/{registry-DlpRCibU.mjs → dev-CcwFrwok.mjs} +30 -1
  9. package/dist/index.d.mts +362 -303
  10. package/dist/index.mjs +12717 -6654
  11. package/dist/inspect.d.mts +10 -8
  12. package/dist/inspect.mjs +2 -518
  13. package/dist/jsx-dev-runtime.d.mts +1 -1
  14. package/dist/jsx-runtime.d.mts +1 -1
  15. package/dist/{rolldown-runtime-D7D4PA-g.mjs → rolldown-runtime-8H4AJuhK.mjs} +1 -0
  16. package/dist/run-tlcn1w7y.mjs +944 -0
  17. package/dist/{runtime-DiOTkDZz.d.mts → runtime-Bn5TXNxP.d.mts} +1 -1
  18. package/dist/sources-BooL2MsX.mjs +545 -0
  19. package/dist/testing.d.mts +328 -3
  20. package/dist/testing.mjs +2126 -2
  21. package/dist/types-BZ1OLf24.d.mts +5999 -0
  22. package/dist/{types-DWILGrPn.d.mts → types-C47fKNcn.d.mts} +93 -20
  23. package/dist/types-CqkrEY06.d.mts +1734 -0
  24. package/dist/types-CxZy7Pi4.d.mts +1210 -0
  25. package/dist/{define-sFoO3y6X.d.mts → types-DmhTHRVW.d.mts} +180 -51
  26. package/dist/{types-yg_ywtT-.d.mts → types-X9jXjka3.d.mts} +230 -31
  27. package/dist/{visual-BDUHSRvf.mjs → visual-NPrDkvUk.mjs} +19 -13
  28. package/package.json +22 -2
  29. package/dist/headless-CvamCUcR.mjs +0 -186
  30. package/dist/headless-KaSWcd0s.d.mts +0 -181
  31. package/dist/types-BxkNNYul.d.mts +0 -2840
  32. package/dist/types-CTPS9GBu.d.mts +0 -742
  33. package/dist/types-DD-QrG_z.d.mts +0 -588
  34. package/dist/types-DYLnSgMI.d.mts +0 -570
  35. package/dist/types-JNc_UQBo.d.mts +0 -2896
@@ -0,0 +1,1734 @@
1
+ import { A as TrackSegment, At as Json, E as NumericFields, F as ComponentHandle, I as ComponentType, M as AnyComponent, P as AnyComponentValue, R as Entity, St as Api, T as MotionHandle, _t as TypeTag, h as Api$2, k as TrackOptions, nn as Require, q as Api$1, t as Api$3, x as Ease } from "./types-BZ1OLf24.mjs";
2
+ import { o as Events$1, t as Api$4 } from "./types-C47fKNcn.mjs";
3
+ import { F as Events$2, H as InputApi, I as I18nApi, L as Message, c as UiNode, i as Finding, n as ElementChange, o as JsxState, r as ElementMotion, v as StylesState } from "./types-X9jXjka3.mjs";
4
+ import { Log } from "@moku-labs/common/browser";
5
+ import { PluginCtx } from "@moku-labs/core";
6
+ import { Node, Yoga } from "yoga-layout/load";
7
+
8
+ //#region src/plugins/text/types.d.ts
9
+ declare namespace types_d_exports$2 {
10
+ export { AdvanceTable, BundleLoaded, BundleUnloaded, Config$2 as Config, Deps$2 as Deps, DrawnLabel, IconRun, KernelSlice$2 as KernelSlice, LayoutOptions, Line, LocaleChanged, Point, Run, SeenText, Size, State$2 as State, TextAlign, TextApi, TextBind, TextCtx, TextLayout, TextRun, TextShadow, TextStyle, TextStyleInput, TextStyles, TextValue, TextWrap, Warn };
11
+ }
12
+ /**
13
+ * A point in reference units: where a label sits, and which point of the block sits there.
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * const anchor: Point = { x: 0.5, y: 0.5 };
18
+ * ```
19
+ */
20
+ type Point = {
21
+ x: number;
22
+ y: number;
23
+ };
24
+ /**
25
+ * The size of a measured block, in reference pixels.
26
+ *
27
+ * @example
28
+ * ```ts
29
+ * const size: Size = { width: 57.6, height: 38.4 };
30
+ * ```
31
+ */
32
+ type Size = {
33
+ width: number;
34
+ height: number;
35
+ };
36
+ /**
37
+ * Which numeric component field a label shows. The component is named, not imported: a HUD is
38
+ * written before the game's components are in scope.
39
+ *
40
+ * @example
41
+ * ```ts
42
+ * const bind: TextBind = { component: "Counter", field: "value" };
43
+ * ```
44
+ */
45
+ type TextBind = {
46
+ component: string;
47
+ field: string;
48
+ };
49
+ /**
50
+ * What the `Text` component holds. `resolved` is engine-owned: a game writes `content`, `style`,
51
+ * `bind`, `anchor` and `alpha`, and reads `resolved`. `alpha` fades every run, shadow and icon of
52
+ * the block together, 1 by default; a change is written in place and a tween can drive it.
53
+ *
54
+ * @example
55
+ * ```ts
56
+ * const value: TextValue = {
57
+ * content: "+5", style: "body", bind: undefined, anchor: { x: 0.5, y: 0.5 }, alpha: 1,
58
+ * resolved: "+5"
59
+ * };
60
+ * // The hint of an empty name field, at half alpha.
61
+ * const hint: TextValue = { ...value, content: "Your name", alpha: 0.5, resolved: "Your name" };
62
+ * ```
63
+ */
64
+ type TextValue = {
65
+ content: string | Message;
66
+ style: string;
67
+ bind: TextBind | undefined;
68
+ anchor: Point;
69
+ alpha: number;
70
+ resolved: string;
71
+ };
72
+ /**
73
+ * Where the lines of a block sit inside the widest of them.
74
+ *
75
+ * @example
76
+ * ```ts
77
+ * const align: TextAlign = "center";
78
+ * ```
79
+ */
80
+ type TextAlign = "left" | "center" | "right";
81
+ /**
82
+ * The wrap width of a style in reference pixels, or `"none"` for one line per `\n` segment.
83
+ *
84
+ * @example
85
+ * ```ts
86
+ * const wrap: TextWrap = 480;
87
+ * ```
88
+ */
89
+ type TextWrap = number | "none";
90
+ /**
91
+ * The drop shadow of a style as the plugin stores it: a copy of every glyph run in `color`, at
92
+ * `alpha`, moved by `dx` and `dy` reference pixels.
93
+ *
94
+ * @example
95
+ * ```ts
96
+ * const shadow: TextShadow = { color: 0x5b3a1e, dx: 0, dy: 4, alpha: 1 };
97
+ * ```
98
+ */
99
+ type TextShadow = {
100
+ color: number;
101
+ dx: number;
102
+ dy: number;
103
+ alpha: number;
104
+ };
105
+ /**
106
+ * One style as a game writes it: the font and the size are required, the rest has defaults.
107
+ *
108
+ * @example
109
+ * ```ts
110
+ * // A popup title: cream display glyphs over a brown shadow 4 px down.
111
+ * const title: TextStyleInput = {
112
+ * font: "ui.font-display", size: 56, fill: 0xfff3d6, shadow: { color: 0x5b3a1e, dx: 0, dy: 4 }
113
+ * };
114
+ *
115
+ * defineTextStyles({ "popup.title": title }).map["popup.title"]?.shadow;
116
+ * // { color: 0x5b3a1e, dx: 0, dy: 4, alpha: 1 }
117
+ * ```
118
+ */
119
+ type TextStyleInput = {
120
+ font: string;
121
+ bold?: string;
122
+ italic?: string;
123
+ size: number;
124
+ fill: number; /** The outline colour. Drawn only when `strokeWidth` is above 0. */
125
+ stroke?: number;
126
+ /**
127
+ * The outline width in reference px: 8 copies of each glyph run on a circle this wide (12 from 6
128
+ * on), tinted `stroke`. 0 draws none. It never changes what `measure` answers.
129
+ */
130
+ strokeWidth?: number;
131
+ letterSpacing?: number;
132
+ align?: TextAlign;
133
+ wrap?: TextWrap;
134
+ digits?: boolean;
135
+ /**
136
+ * A drop shadow under every glyph run, in reference px at the style size. `alpha` is 1 when
137
+ * left out. It never changes what `measure` answers.
138
+ */
139
+ shadow?: {
140
+ color: number;
141
+ dx: number;
142
+ dy: number;
143
+ alpha?: number;
144
+ };
145
+ };
146
+ /**
147
+ * One style as the plugin stores it: every field filled, `bold` and `italic` only when the game
148
+ * shipped those MSDF fonts, `shadow` only when the style has one.
149
+ *
150
+ * @example
151
+ * ```ts
152
+ * const style: TextStyle = {
153
+ * font: "ui.font-body", bold: undefined, italic: undefined, size: 32, fill: 0xffffff,
154
+ * stroke: 0x000000, strokeWidth: 0, letterSpacing: 0, align: "left", wrap: "none",
155
+ * digits: false, shadow: undefined
156
+ * };
157
+ * ```
158
+ */
159
+ type TextStyle = {
160
+ font: string;
161
+ bold: string | undefined;
162
+ italic: string | undefined;
163
+ size: number;
164
+ fill: number;
165
+ stroke: number;
166
+ strokeWidth: number;
167
+ letterSpacing: number;
168
+ align: TextAlign;
169
+ wrap: TextWrap;
170
+ digits: boolean;
171
+ shadow: TextShadow | undefined;
172
+ };
173
+ /**
174
+ * What `defineTextStyles` returns and a feature registers under its `textStyles` key. Structural
175
+ * on purpose: `flow` carries the map, `text` reads it.
176
+ *
177
+ * @example
178
+ * ```ts
179
+ * const styles: TextStyles = { kind: "textStyles", map: { "hud.title": bodyStyle } };
180
+ * ```
181
+ */
182
+ type TextStyles = {
183
+ readonly kind: "textStyles";
184
+ readonly map: Record<string, TextStyle>;
185
+ };
186
+ /**
187
+ * The advances of one font at the size the `.fnt` was exported with. Widths scale by
188
+ * `style.size / size`; kerning pairs are ignored.
189
+ *
190
+ * @example
191
+ * ```ts
192
+ * const table: AdvanceTable = { size: 32, lineHeight: 40, advances: new Map([["1", 18]]) };
193
+ * ```
194
+ */
195
+ type AdvanceTable = {
196
+ size: number;
197
+ lineHeight: number;
198
+ advances: Map<string, number>;
199
+ };
200
+ /**
201
+ * One run of glyphs: the text and the flags every glyph in it shares.
202
+ *
203
+ * @example
204
+ * ```ts
205
+ * const run: TextRun = { kind: "text", text: "+5", bold: true, italic: false, color: 0xffe082 };
206
+ * ```
207
+ */
208
+ type TextRun = {
209
+ kind: "text";
210
+ text: string;
211
+ bold: boolean;
212
+ italic: boolean;
213
+ color: number | undefined;
214
+ };
215
+ /**
216
+ * One inline icon: an asset key drawn square at the line height.
217
+ *
218
+ * @example
219
+ * ```ts
220
+ * const run: IconRun = { kind: "icon", key: "hud.coin" };
221
+ * ```
222
+ */
223
+ type IconRun = {
224
+ kind: "icon";
225
+ key: string;
226
+ };
227
+ /**
228
+ * A piece of a line: glyphs or one inline icon.
229
+ *
230
+ * @example
231
+ * ```ts
232
+ * const runs: Run[] = [{ kind: "icon", key: "hud.coin" }];
233
+ * ```
234
+ */
235
+ type Run = TextRun | IconRun;
236
+ /**
237
+ * One laid-out line and how wide it is.
238
+ *
239
+ * @example
240
+ * ```ts
241
+ * const line: Line = { runs: [{ kind: "icon", key: "hud.coin" }], width: 38.4 };
242
+ * ```
243
+ */
244
+ type Line = {
245
+ runs: Run[];
246
+ width: number;
247
+ };
248
+ /**
249
+ * A laid-out block: its lines and the size `measure` answers with.
250
+ *
251
+ * @example
252
+ * ```ts
253
+ * const layout: TextLayout = { lines: [], width: 0, height: 0 };
254
+ * ```
255
+ */
256
+ type TextLayout = {
257
+ lines: Line[];
258
+ width: number;
259
+ height: number;
260
+ };
261
+ /**
262
+ * What one label container was last filled from: the style object and the laid-out block. An
263
+ * update with the same style object, the same anchor and the same lines and runs writes the new
264
+ * text and positions into the objects already there.
265
+ */
266
+ type DrawnLabel = {
267
+ style: TextStyle;
268
+ layout: TextLayout;
269
+ };
270
+ /**
271
+ * A dev warning that is written once per key. Pure modules take it as an argument, so nothing
272
+ * below the plugin context reaches the log itself.
273
+ *
274
+ * @example
275
+ * ```ts
276
+ * const warn: Warn = (key, message) => seen.has(key) || log.warn(message);
277
+ * ```
278
+ */
279
+ type Warn = (key: string, message: string, data?: Record<string, unknown>) => void;
280
+ /**
281
+ * What the pure layout needs next to the runs, the style and the tables.
282
+ *
283
+ * @example
284
+ * ```ts
285
+ * const options: LayoutOptions = { warn: () => undefined };
286
+ * ```
287
+ */
288
+ type LayoutOptions = {
289
+ warn: Warn;
290
+ };
291
+ /**
292
+ * What was resolved for one entity last, so the frame step knows whether anything moved.
293
+ *
294
+ * @example
295
+ * ```ts
296
+ * const seen: SeenText = { content: "+5", style: "body", bind: undefined, locale: "ru" };
297
+ * ```
298
+ */
299
+ type SeenText = {
300
+ content: unknown;
301
+ style: string;
302
+ bind: string | undefined;
303
+ locale: string;
304
+ };
305
+ /**
306
+ * text plugin config.
307
+ *
308
+ * @example
309
+ * ```ts
310
+ * createApp({ pluginConfigs: { text: { fonts: { body: "ui.body", digits: "ui.digits" } } } });
311
+ * ```
312
+ */
313
+ type Config$2 = {
314
+ /** The two fonts every game ships in its boot bundle. They back the built-in styles. */fonts: {
315
+ body: string;
316
+ digits: string;
317
+ };
318
+ };
319
+ /**
320
+ * text plugin state.
321
+ */
322
+ type State$2 = {
323
+ /** Built-in styles first, then the styles of every feature in feature order. */styles: Map<string, TextStyle>; /** Style name to the feature that brought it, for the duplicate error. */
324
+ styleOwner: Map<string, string>; /** The config fonts plus every `font`, `bold` and `italic` of every style. */
325
+ fontKeys: Set<string>; /** The parsed advance table per font key, filled when `assets` has the font. */
326
+ tables: Map<string, AdvanceTable>; /** The font keys handed to `renderer.sync.fonts.install`. */
327
+ installed: Set<string>; /** What was resolved for an entity last. */
328
+ seen: Map<Entity, SeenText>; /** `bind.component` to its world type; `undefined` after the one warning. */
329
+ bindTypes: Map<string, AnyComponent | undefined>; /** The measured size of every live label, for `ui`. */
330
+ measured: Map<Entity, Size>; /** Layout per `style + resolved`, oldest dropped first. */
331
+ cache: Map<string, TextLayout>; /** What every live label container was last filled from, by container. */
332
+ drawn: WeakMap<object, DrawnLabel>; /** Entities to re-resolve in the next layout phase. */
333
+ dirty: Set<Entity>; /** Warning keys already written, so a style, a tag, a glyph or a font warns once. */
334
+ warned: Set<string>; /** The system, the two world hooks and the display adapter. */
335
+ removers: Array<() => void>;
336
+ };
337
+ /**
338
+ * text plugin API, `app.text`. Two questions: how big a piece of text is, and which styles the
339
+ * game registered. Everything else happens in the frame.
340
+ *
341
+ * @example
342
+ * ```ts
343
+ * app.text.measure("120", "digits"); // { width: 57.6, height: 38.4 }
344
+ * app.text.styles(); // ["body", "digits"]
345
+ * ```
346
+ */
347
+ type TextApi = {
348
+ /**
349
+ * The size of a piece of text in reference pixels, from the advance table of the style's font.
350
+ * A `Message` is formatted in the current locale first. Never touches a canvas, so the answer
351
+ * is the same in a browser and in plain Bun. Cached per style and resolved string.
352
+ *
353
+ * @param content - A plain string, or what `tr` returned.
354
+ * @param style - A registered style name. An unknown one warns once and measures as `body`.
355
+ * @returns The width and height of the block.
356
+ * @example
357
+ * ```ts
358
+ * // `ui.layout` asks once per invalidation, from the Yoga measure function of a text element.
359
+ * const text = ctx.require(textPlugin);
360
+ *
361
+ * text.measure("120", "digits"); // { width: 57.6, height: 38.4 } on the 0.6 em fallback
362
+ * ```
363
+ */
364
+ measure(content: string | Message, style: string): Size;
365
+ /**
366
+ * Whether the font of a style has a glyph for one character, the lookup Pixi draws by. A
367
+ * character its font lacks is drawn as nothing and `measure` counts it 0 wide. While the font is
368
+ * not loaded every character counts, as the 0.6 em fallback measures it. An unknown style warns
369
+ * once and looks up `body`.
370
+ *
371
+ * @param char - One character, a whole code point.
372
+ * @param style - A registered style name.
373
+ * @returns False only for a character a loaded font has no glyph for.
374
+ * @example
375
+ * ```ts
376
+ * // `ui` measures the caret of a text field over the characters the font draws.
377
+ * const text = ctx.require(textPlugin);
378
+ *
379
+ * text.hasGlyph("A", "body"); // true
380
+ * text.hasGlyph("😀", "body"); // false once the body font is loaded: it has no emoji glyph
381
+ * ```
382
+ */
383
+ hasGlyph(char: string, style: string): boolean;
384
+ /**
385
+ * The registered style names: the two built-ins first, then the styles of every feature in
386
+ * feature order.
387
+ *
388
+ * @returns A frozen list of style names.
389
+ * @example
390
+ * ```ts
391
+ * // A dev overlay lists what a game may write in a `style` prop.
392
+ * app.text.styles(); // ["body", "digits", "hud.digits"]
393
+ * ```
394
+ */
395
+ styles(): readonly string[];
396
+ };
397
+ /**
398
+ * Resolved dependency APIs.
399
+ */
400
+ type Deps$2 = {
401
+ time: Api;
402
+ flow: Api$1;
403
+ world: Api$2;
404
+ renderer: Api$3;
405
+ assets: Api$4;
406
+ i18n: I18nApi;
407
+ };
408
+ /**
409
+ * What the kernel context offers before the deps are attached.
410
+ *
411
+ * `text` owns no event, so `emit` is the kernel's and never called here.
412
+ */
413
+ type KernelSlice$2 = PluginCtx<Config$2, State$2> & {
414
+ readonly global: object;
415
+ readonly log: Log.LogApi;
416
+ readonly require: Require;
417
+ };
418
+ /**
419
+ * Domain context shared by the files of the plugin: the kernel slice plus the resolved deps.
420
+ */
421
+ type TextCtx = KernelSlice$2 & {
422
+ readonly deps: Deps$2;
423
+ };
424
+ /**
425
+ * Payload of the `assets:bundle-loaded` hook: a bundle landed, so a font may be readable now.
426
+ */
427
+ type BundleLoaded = Events$1["assets:bundle-loaded"];
428
+ /**
429
+ * Payload of the `assets:bundle-unloaded` hook: `keys` names the assets that left.
430
+ */
431
+ type BundleUnloaded = Events$1["assets:bundle-unloaded"];
432
+ /**
433
+ * Payload of the `i18n:locale-changed` hook: every message has to be resolved again.
434
+ */
435
+ type LocaleChanged = Events$2["i18n:locale-changed"];
436
+ //#endregion
437
+ //#region src/plugins/ui/layout/types.d.ts
438
+ /**
439
+ * layout module state. `nodes` is attach minus detach: Yoga 3.2.1 has no instance counter.
440
+ */
441
+ type LayoutState = {
442
+ yoga: Yoga | undefined;
443
+ byEntity: Map<Entity, Node>;
444
+ nodes: number;
445
+ measured: number;
446
+ solves: number;
447
+ scrolling: Entity | undefined;
448
+ scrollStart: {
449
+ pointerY: number;
450
+ offset: number;
451
+ };
452
+ order: number;
453
+ cleanups: Array<() => void>;
454
+ };
455
+ declare namespace types_d_exports$1 {
456
+ export { Config$1 as Config, Deps$1 as Deps, ElementChange, ElementMotion, Finding, FocusRing, KernelSlice$1 as KernelSlice, State$1 as State, TextInputLook, UiApi, UiCtx, UiNode };
457
+ }
458
+ /**
459
+ * The look of the keyboard focus ring: a dashed ring with a solid halo under it, drawn around the
460
+ * focused control. Colours are `0xRRGGBB`, lengths reference units.
461
+ *
462
+ * @example
463
+ * ```ts
464
+ * // A dashed ink ring with a cream halo, 9 u outside the control.
465
+ * const ring: FocusRing = {
466
+ * stroke: 0x3a2212, strokeWidth: 4, dash: 10, offset: 9, halo: 0xfff3d6, haloWidth: 12
467
+ * };
468
+ * ```
469
+ */
470
+ type FocusRing = {
471
+ /** The colour of the dashed ring. */stroke: number; /** The stroke width of the dashed ring. */
472
+ strokeWidth: number; /** The dash length of the ring, with gaps of half a dash; 0 draws it solid. */
473
+ dash: number; /** How far outside the control's rect the ring runs, on every side. */
474
+ offset: number; /** The colour of the solid halo drawn under the ring. */
475
+ halo: number; /** The stroke width of the halo, centred on the same path as the ring. */
476
+ haloWidth: number;
477
+ };
478
+ /**
479
+ * The look of a text field while it is edited: the caret, the selection box and the IME
480
+ * underline. Colours are `0xRRGGBB`, lengths reference units, `keyboardMargin` CSS px. The caret
481
+ * does not blink.
482
+ *
483
+ * @example
484
+ * ```ts
485
+ * // A thick ink caret, a pale blue selection, the field 16 px above the keyboard.
486
+ * const look: TextInputLook = {
487
+ * caretWidth: 3, caret: 0x3a2212, selection: 0x3390ff, selectionAlpha: 0.35,
488
+ * composingUnderline: 3, keyboardMargin: 16
489
+ * };
490
+ * ```
491
+ */
492
+ type TextInputLook = {
493
+ /** The width of the caret. */caretWidth: number; /** The colour of the caret and of the underline under an IME composing range. */
494
+ caret: number; /** The colour of the selection box. */
495
+ selection: number; /** The alpha of the selection box. */
496
+ selectionAlpha: number; /** The height of the underline under an IME composing range. */
497
+ composingUnderline: number; /** How far above the keyboard the bottom of the edited field is lifted, in CSS px. */
498
+ keyboardMargin: number;
499
+ };
500
+ /**
501
+ * ui plugin config.
502
+ *
503
+ * @example
504
+ * ```ts
505
+ * createApp({ pluginConfigs: { ui: { tapTargetPt: 48, breakpoints: { tall: 2, wide: 1.5 } } } });
506
+ * // A game with a white focus ring on a dark board.
507
+ * createApp({
508
+ * pluginConfigs: {
509
+ * ui: {
510
+ * focusRing: {
511
+ * stroke: 0xffffff, strokeWidth: 4, dash: 10, offset: 9, halo: 0x000000, haloWidth: 12
512
+ * }
513
+ * }
514
+ * }
515
+ * });
516
+ * // A game whose text fields have an ink caret.
517
+ * createApp({
518
+ * pluginConfigs: {
519
+ * ui: {
520
+ * textInput: {
521
+ * caretWidth: 3, caret: 0x3a2212, selection: 0x3390ff, selectionAlpha: 0.35,
522
+ * composingUnderline: 3, keyboardMargin: 16
523
+ * }
524
+ * }
525
+ * }
526
+ * });
527
+ * ```
528
+ */
529
+ type Config$1 = {
530
+ /** Smallest tap target `lint()` accepts, in CSS px (points). */tapTargetPt: number; /** `when` flags: `tall` when height / width is at least `tall`, `wide` the other way round. */
531
+ breakpoints: {
532
+ tall: number;
533
+ wide: number;
534
+ };
535
+ /**
536
+ * The ring drawn around the control the keyboard focused. Shallow merge: a game that sets
537
+ * `focusRing` gives all six fields.
538
+ */
539
+ focusRing: FocusRing;
540
+ /**
541
+ * The caret, the selection and the IME underline of a text field. Shallow merge: a game that
542
+ * sets `textInput` gives all six fields.
543
+ */
544
+ textInput: TextInputLook;
545
+ };
546
+ /**
547
+ * ui plugin state: one branch per module.
548
+ */
549
+ type State$1 = {
550
+ jsx: JsxState;
551
+ styles: StylesState;
552
+ layout: LayoutState;
553
+ };
554
+ /**
555
+ * Resolved dependency APIs. `anim` is not here: its edge is validation only, and its work
556
+ * reaches `ui` through the tween driver behind every `ViewHandle`.
557
+ */
558
+ type Deps$1 = {
559
+ time: Api;
560
+ flow: Api$1;
561
+ world: Api$2;
562
+ renderer: Api$3;
563
+ input: InputApi;
564
+ i18n: I18nApi;
565
+ text: TextApi;
566
+ };
567
+ /**
568
+ * What the kernel context offers before the deps are attached.
569
+ *
570
+ * `ui` owns no event, so `emit` is the kernel's and never called here.
571
+ */
572
+ type KernelSlice$1 = PluginCtx<Config$1, State$1> & {
573
+ readonly global: object;
574
+ readonly log: Log.LogApi;
575
+ readonly require: Require;
576
+ };
577
+ /**
578
+ * Domain context shared by the three modules: the kernel slice plus the resolved deps.
579
+ */
580
+ type UiCtx = KernelSlice$1 & {
581
+ readonly deps: Deps$1;
582
+ };
583
+ /**
584
+ * ui plugin API, `app.ui`. The screen is one more projection, so three members read it the way a
585
+ * test reads the board: a snapshot, a key lookup and a list of findings; the fourth types into a
586
+ * text field.
587
+ *
588
+ * @example
589
+ * ```ts
590
+ * // A headless scenario taps the claim button of the reward popup.
591
+ * app.ui.tree().type; // "row", the root element of the HUD
592
+ * app.input.tap(app.ui.find("claim") ?? 0);
593
+ * app.ui.lint(); // []
594
+ * ```
595
+ */
596
+ type UiApi = {
597
+ /**
598
+ * The live screen as plain data: every root in layer order, every element in child order, with
599
+ * its rect in root coordinates, its resolved style and its seven state flags. A rect is natural:
600
+ * under a `fit: "contain"` element it is the rect before that scale, and the fitted element
601
+ * adds `fitScale`. Works headless.
602
+ *
603
+ * @returns The root node; several roots come back under one `screen` node.
604
+ * @example
605
+ * ```ts
606
+ * // On an iPhone SE the 970 u board is drawn smaller to fit the slot between the bars.
607
+ * const screen = app.ui.tree();
608
+ * screen.children.map(child => child.key); // ["fitTop", "slot", "fitBottom"]
609
+ * const board = screen.children[1]?.children[0];
610
+ * board?.rect.w; // 970, the natural width
611
+ * board?.fitScale; // 0.805, the 781 u the slot leaves over 970 u
612
+ * ```
613
+ */
614
+ tree(): UiNode;
615
+ /**
616
+ * The entity of a keyed element, so a test can tap it and `anim` can aim at it. Popup roots
617
+ * come first, then the projection roots in layer order.
618
+ *
619
+ * @param key - The `key` prop of the element.
620
+ * @returns The entity, or `undefined` for an unknown or exiting element.
621
+ * @example
622
+ * ```ts
623
+ * // A headless scenario answers the gate through the button of the popup.
624
+ * app.input.tap(app.ui.find("claim") ?? 0); // true
625
+ * app.ui.find("nothing"); // undefined
626
+ * ```
627
+ */
628
+ find(key: string): Entity | undefined;
629
+ /**
630
+ * Reads the live screen against the five rules: a tap target under `tapTargetPt` at the size
631
+ * it is drawn (a `fit: "contain"` on it or above it shrinks it), a text that does not fit its
632
+ * box in some registered locale, an absolute element with no `reason`, a clipping element
633
+ * (`scroll`, `overflow: "hidden"`) whose style names a nine-slice it never draws, and a root
634
+ * element whose style sets a `zIndex` it ignores. Never throws; empty when nothing is mounted.
635
+ *
636
+ * @returns One finding per rule and element.
637
+ * @example
638
+ * ```ts
639
+ * // A game test keeps the board honest on an iPhone SE: a 140 u cell in the 0.8 slot.
640
+ * app.ui.lint(); // [{ rule: "tap-target", key: "cell", detail: "39 x 39 pt" }]
641
+ * // A list styled with a nine-slice: the clip keeps it from drawing.
642
+ * app.ui.lint(); // [{ rule: "nine-slice-clipped", key: "orders", detail: "scroll" }]
643
+ * // A screen root that asks to be drawn over the others: the layer decides that.
644
+ * app.ui.lint(); // [{ rule: "z-index-on-root", key: "board", detail: "zIndex 2" }]
645
+ * ```
646
+ */
647
+ lint(): readonly Finding[];
648
+ /**
649
+ * Types into a text field the way a player does, for a headless test or an agent: the field
650
+ * becomes the one being edited, `value` cut to its `maxLength` is written into it, and the
651
+ * local field it names is written, so the component re-renders on the next frame. Nothing
652
+ * reaches the gate: Enter submits.
653
+ *
654
+ * @param key - The `key` prop of the `input` element.
655
+ * @param value - The text to type.
656
+ * @returns True when the field took the text; false with one warning when no live `input` has
657
+ * the key.
658
+ * @example
659
+ * ```ts
660
+ * // A headless test renames the player through the Rename popup.
661
+ * app.ui.fill("nameField", "Alex"); // true
662
+ * app.time.step(16);
663
+ * app.input.pressKey("Enter"); // true: the gate took { intent: "save", payload: { name: "Alex" } }
664
+ * app.ui.fill("nothing", "Alex"); // false, with the warning "ui:fill-without-input"
665
+ * ```
666
+ */
667
+ fill(key: string, value: string): boolean;
668
+ };
669
+ //#endregion
670
+ //#region src/plugins/anim/tween/types.d.ts
671
+ /**
672
+ * One running track: the numeric fields of one component of one entity moving from the values
673
+ * read at the end of the delay to the exact target, straight or through keyframe segments. A
674
+ * track with repeats walks again from its first segment each time a run ends.
675
+ *
676
+ * @example
677
+ * ```ts
678
+ * const track: Track = {
679
+ * id: 1, entity: 1_048_576, component: Transform, to: { x: 200 },
680
+ * keys: { x: "1048576:Transform:x" }, ms: 350, ease: "out", delayMs: 0, elapsed: 0,
681
+ * from: undefined, muted: () => new Set(), additive: false, driven: false, bornFrame: 4,
682
+ * ended: false, segments: undefined, repeatsLeft: 0, held: false
683
+ * };
684
+ * ```
685
+ */
686
+ type Track = {
687
+ readonly id: number;
688
+ readonly entity: Entity;
689
+ readonly component: AnyComponent; /** The target fields. Retarget deletes the fields a newer absolute track took over. */
690
+ to: Record<string, number>;
691
+ /**
692
+ * The key of the owner, bases and offsets tables per field, built once when the track starts.
693
+ * It keeps a field `to` lost, which nothing reads again.
694
+ */
695
+ readonly keys: Readonly<Record<string, string>>;
696
+ readonly ms: number;
697
+ readonly ease: Ease;
698
+ readonly delayMs: number;
699
+ elapsed: number; /** Read when the delay ends, never earlier. */
700
+ from: Record<string, number> | undefined;
701
+ readonly muted: () => ReadonlySet<string>;
702
+ readonly additive: boolean; /** True for a track a timeline step advances by hand; the frame step leaves it alone. */
703
+ readonly driven: boolean; /** The frame step the track was born in. That step does not advance it a second time. */
704
+ readonly bornFrame: number;
705
+ ended: boolean;
706
+ /**
707
+ * The keyframe segments the track walks over `ms`, or `undefined` for a straight track. A
708
+ * segment without an ease runs on the track's `ease`.
709
+ */
710
+ readonly segments: readonly TrackSegment[] | undefined;
711
+ /**
712
+ * The runs still to go after the one that plays: `0` for a track that runs once, `Infinity`
713
+ * for a loop (`repeat: "forever"`), which never ends by itself.
714
+ */
715
+ repeatsLeft: number;
716
+ /**
717
+ * True while a loop stands on its first key and has written it: the hold writes once, and the
718
+ * walk clears it, so the next hold writes the first key again.
719
+ */
720
+ held: boolean;
721
+ };
722
+ /**
723
+ * A motion handle that can also be advanced by hand: what the timeline cursor needs, so a track
724
+ * born inside a frame step still consumes the remainder of that step's delta.
725
+ *
726
+ * @example
727
+ * ```ts
728
+ * const motion: StepMotion = { ...inert, advance: () => 0 };
729
+ * motion.advance(16); // 0: nothing is left over
730
+ * ```
731
+ */
732
+ type StepMotion = MotionHandle & {
733
+ /**
734
+ * Advances the track by one delta.
735
+ *
736
+ * @param deltaMs - Milliseconds of game time to consume.
737
+ * @returns The milliseconds left over past the end of the track.
738
+ */
739
+ advance(deltaMs: number): number;
740
+ };
741
+ //#endregion
742
+ //#region src/plugins/anim/timeline/steps.d.ts
743
+ /**
744
+ * Every haptic tick `haptic(kind)` takes, in one frozen list: the three impacts, the selection
745
+ * tick and the three notifications. `platform` checks a kind against it before it plays one.
746
+ *
747
+ * @example
748
+ * ```ts
749
+ * HAPTIC_KINDS.includes("light"); // true
750
+ * ```
751
+ */
752
+ declare const HAPTIC_KINDS: readonly ["light", "medium", "heavy", "selection", "success", "warning", "error"];
753
+ /**
754
+ * Runs the steps one after another. The remainder past a step's end reaches the next step in the
755
+ * same frame, so the tree takes exactly the sum of its durations.
756
+ *
757
+ * @param steps - The steps, in order.
758
+ * @returns The sequence step.
759
+ * @example
760
+ * ```ts
761
+ * sequence(wait(100), mark("done"));
762
+ * // { kind: "sequence", steps: [{ kind: "wait", ms: 100 }, { kind: "mark", name: "done" }] }
763
+ * ```
764
+ */
765
+ declare function sequence(...steps: readonly Step[]): Step;
766
+ /**
767
+ * Runs every step at once and ends with the longest one.
768
+ *
769
+ * @param steps - The steps.
770
+ * @returns The parallel step.
771
+ * @example
772
+ * ```ts
773
+ * parallel(wait(100), wait(300)); // ends after 300 ms
774
+ * ```
775
+ */
776
+ declare function parallel(...steps: readonly Step[]): Step;
777
+ /**
778
+ * Builds one step per item and starts each one `staggerMs` later than the one before. The build
779
+ * function runs here, so no function survives in the data.
780
+ *
781
+ * @param items - What to build a step for.
782
+ * @param staggerMs - Delay between two neighbouring items, in game milliseconds.
783
+ * @param build - Builds the step of one item.
784
+ * @returns The parallel of delayed sequences.
785
+ * @example
786
+ * ```ts
787
+ * stagger(["a", "b"], 60, item => mark(item));
788
+ * // parallel(sequence(wait(0), mark("a")), sequence(wait(60), mark("b")))
789
+ * ```
790
+ */
791
+ declare function stagger<Item>(items: readonly Item[], staggerMs: number, build: (item: Item, index: number) => Step): Step;
792
+ /**
793
+ * Holds still for a while.
794
+ *
795
+ * @param durationMs - How long to wait, in game milliseconds.
796
+ * @returns The wait step.
797
+ * @example
798
+ * ```ts
799
+ * wait(120); // { kind: "wait", ms: 120 }
800
+ * ```
801
+ */
802
+ declare function wait(durationMs: number): Step;
803
+ /**
804
+ * Names a point in the choreography. Reaching it emits `anim:mark`, calls the `onMark` listeners
805
+ * and appends the name to `marks()`. It is the only way out of a timeline.
806
+ *
807
+ * @param name - Name of the mark.
808
+ * @returns The mark step.
809
+ * @example
810
+ * ```ts
811
+ * mark("landed"); // { kind: "mark", name: "landed" }
812
+ * ```
813
+ */
814
+ declare function mark(name: string): Step;
815
+ /**
816
+ * Moves the numeric fields of one component of one target to an exact target value. The component
817
+ * is any handle `world.ecs` takes, so the kit's `Sprite` and `NineSlice` of `defineGame` pass as
818
+ * they are.
819
+ *
820
+ * By default the fields are local: they are written into the target's own `Transform`. With
821
+ * `space: "root"` the `x`, `y`, `rotation` and `scale` of a `Transform` are a root pose, the space
822
+ * `at()` answers in, and are turned into the space of the target's `Parent` when the track starts.
823
+ * That aims a hosted view, such as a board item inside the board slot, at another element. A
824
+ * field the step does not name stays where it is, so under a turned parent name `x` and `y`
825
+ * together. A target without a parent, and a component other than `Transform`, move as in the
826
+ * local space.
827
+ *
828
+ * @param target - The projection key or the entity to animate.
829
+ * @param component - The component to animate.
830
+ * @param to - The numeric target fields.
831
+ * @param options - Duration, easing, delay, the additive flag and the space of the fields.
832
+ * @param options.ms - Duration in game milliseconds.
833
+ * @param options.ease - Easing curve; `"out"` when omitted.
834
+ * @param options.delayMs - How long the track waits before it reads its start values.
835
+ * @param options.additive - `true` adds an offset instead of owning the fields.
836
+ * @param options.space - `"root"` reads the `Transform` fields as a root pose; `"local"` when
837
+ * omitted.
838
+ * @returns The tween step.
839
+ * @example
840
+ * ```ts
841
+ * // An order is delivered: the board item flies out of the scaled board slot onto the order card.
842
+ * const deliverFly = defineAnimation("orders.deliverFly", {
843
+ * slots: { item: type<Target>(), card: type<Target>() },
844
+ * build: ({ item, card }, { at }) =>
845
+ * tween(item, Transform, { x: at(card).x, y: at(card).y, scale: at(card).scale }, {
846
+ * ms: 400, ease: "inCubic", space: "root"
847
+ * })
848
+ * });
849
+ * app.anim.play(deliverFly, {
850
+ * item: { projection: "board.items", key: "i1" },
851
+ * card: { projection: "hud", key: "card0" }
852
+ * });
853
+ * // 400 ms later the item covers the card at the card's size; its Transform stays slot-local
854
+ * ```
855
+ */
856
+ declare function tween<Value extends object>(target: Target, component: ComponentHandle<Value>, to: Partial<NumericFields<Value>>, options: {
857
+ ms: number;
858
+ ease?: Ease;
859
+ delayMs?: number;
860
+ additive?: boolean;
861
+ space?: "local" | "root";
862
+ }): Step;
863
+ /**
864
+ * Writes a component patch at once. Any field may be written, not only the numeric ones. The
865
+ * component is any handle `world.ecs` takes, the kit's `Sprite` and `NineSlice` included.
866
+ *
867
+ * @param target - The projection key or the entity to write.
868
+ * @param component - The component to write.
869
+ * @param patch - The fields to overwrite.
870
+ * @returns The set step.
871
+ * @example
872
+ * ```ts
873
+ * set({ projection: "hud", key: "coins" }, Sprite, { texture: "hud.coin-gold" });
874
+ * // { kind: "set", patch: { texture: "hud.coin-gold" }, ... }
875
+ * ```
876
+ */
877
+ declare function set<Value extends object>(target: Target, component: ComponentHandle<Value>, patch: Partial<Value>): Step;
878
+ /**
879
+ * Plays a frame sprite: one texture key of the list per frame of `fps`, written into `Sprite`.
880
+ *
881
+ * @param target - The projection key or the entity to animate.
882
+ * @param options - The keys, the frame rate and whether the list repeats.
883
+ * @param options.keys - The texture keys, in play order.
884
+ * @param options.fps - Frames per second of the list.
885
+ * @param options.loop - `true` repeats the list until the step is finished.
886
+ * @returns The frames step.
887
+ * @example
888
+ * ```ts
889
+ * frames({ projection: "board", key: "c3" }, { keys: ["fx.pop-1", "fx.pop-2"], fps: 12 });
890
+ * // { kind: "frames", keys: ["fx.pop-1", "fx.pop-2"], fps: 12, loop: false, ... }
891
+ * ```
892
+ */
893
+ declare function frames(target: Target, options: {
894
+ keys: readonly string[];
895
+ fps: number;
896
+ loop?: boolean;
897
+ }): Step;
898
+ /**
899
+ * Makes a temporary entity when the step is reached: a flying coin, a toast sign, a sparkle. The
900
+ * entity is owned by `anim`, drawn in `layer` at `order`, and despawned when the timeline ends, is
901
+ * finished or is cancelled. Later steps of the same timeline aim at it with `spawned(id)`.
902
+ *
903
+ * @param id - Name of the entity inside this timeline; one timeline spawns each id once.
904
+ * @param components - The component values the entity starts with, as `world.ecs.spawn` takes.
905
+ * @param options - Where the entity is drawn.
906
+ * @param options.layer - The layer; `"ui"` when omitted.
907
+ * @param options.order - The draw order inside the layer; `0` when omitted.
908
+ * @returns The spawn step.
909
+ * @example
910
+ * ```ts
911
+ * spawn("coin1", [Sprite({ texture: "ui.icon-coin" }), Transform({ x: 540, y: 900 })], { order: 50 });
912
+ * // { kind: "spawn", id: "coin1", components: [...], layer: "ui", order: 50 }
913
+ * ```
914
+ */
915
+ declare function spawn(id: string, components: readonly AnyComponentValue[], options?: {
916
+ layer?: string;
917
+ order?: number;
918
+ }): Step;
919
+ /**
920
+ * Aims a later step at the entity a `spawn` step of the same timeline made. Before that step is
921
+ * reached nothing answers the id, so a step aimed at it ends silently.
922
+ *
923
+ * @param id - The id the `spawn` step was given.
924
+ * @returns The target.
925
+ * @example
926
+ * ```ts
927
+ * tween(spawned("coin1"), Transform, { x: 40, y: 120 }, { ms: 600, ease: "inCubic" });
928
+ * // { kind: "tween", target: { spawned: "coin1" }, ... }
929
+ * ```
930
+ */
931
+ declare function spawned(id: string): {
932
+ spawned: string;
933
+ };
934
+ /**
935
+ * A sound. `anim` owns the descriptor because it sits below `audio`; `audio` owns the handler,
936
+ * and a game without `audio` plays nothing and hears no error.
937
+ *
938
+ * @param key - Asset key of the sound.
939
+ * @param options - The bus to play it on.
940
+ * @param options.bus - `"sfx"` when omitted.
941
+ * @returns The descriptor, usable as a timeline step and as `fx(...)` in a node.
942
+ * @example
943
+ * ```ts
944
+ * sfx("board.merge"); // { kind: "sfx", payload: { key: "board.merge", bus: "sfx" }, cosmetic: true }
945
+ * ```
946
+ */
947
+ declare function sfx(key: string, options?: {
948
+ bus?: string;
949
+ }): SfxDescriptor;
950
+ /**
951
+ * A haptic tick. `platform` owns the handler; a device without one stays still.
952
+ *
953
+ * @param kind - Which tick to play, one of `HAPTIC_KINDS`, for example `"light"`.
954
+ * @returns The descriptor, usable as a timeline step and as `fx(...)` in a node.
955
+ * @example
956
+ * ```ts
957
+ * haptic("light"); // { kind: "haptic", payload: { kind: "light" }, cosmetic: true }
958
+ * ```
959
+ */
960
+ declare function haptic(kind: HapticKind): HapticDescriptor;
961
+ /**
962
+ * Reserved for Spine and the other external players. It throws until one arrives, so nobody
963
+ * builds a choreography on a door that is not open yet.
964
+ *
965
+ * @param _player - The player that would run the clip.
966
+ * @param _clip - Name of the clip.
967
+ * @throws {Error} Always.
968
+ * @example
969
+ * ```ts
970
+ * external(spine, "idle"); // throws: External players arrive with Spine.
971
+ * ```
972
+ */
973
+ declare function external(_player: ExternalPlayer, _clip: string): never;
974
+ /**
975
+ * Nests one animation inside another. The nested tree is built here, so its marks are reported
976
+ * under the nested id and no function survives in the data. Pass the outer `tools` when the
977
+ * nested `build` reads `at`.
978
+ *
979
+ * @param animation - The animation to nest.
980
+ * @param slots - One target, or a list of targets, per slot of the nested animation.
981
+ * @param tools - The build tools of the outer animation; without them `at` is the identity pose.
982
+ * @returns The use step.
983
+ * @example
984
+ * ```ts
985
+ * use(popCard, { card: { projection: "hud", key: "order" } });
986
+ * // { kind: "use", id: "hud.popCard", step: { kind: "sequence", ... } }
987
+ * ```
988
+ */
989
+ declare function use<Tags extends SlotTags>(animation: AnimationDefinition<Tags>, slots: SlotValues<Tags>, tools?: BuildTools): Step;
990
+ /**
991
+ * The effect descriptor a node awaits to play a choreography. The payload names the animation,
992
+ * so the handler resolves the definition from the registry the features filled.
993
+ *
994
+ * @param animation - The animation to play.
995
+ * @param slots - One target, or a list of targets, per slot.
996
+ * @returns The descriptor, cosmetic, so a failing build never breaks the node.
997
+ * @example
998
+ * ```ts
999
+ * play(coinsFly, { from: { projection: "hud", key: "purse" } });
1000
+ * // { kind: "play", payload: { animation: "hud.coinsFly", slots: { from: { ... } } }, cosmetic: true }
1001
+ * ```
1002
+ */
1003
+ declare function play<Tags extends SlotTags>(animation: AnimationDefinition<Tags>, slots: SlotValues<Tags>): PlayDescriptor;
1004
+ /**
1005
+ * Declares one animation: an id, the slots it takes and the pure function that builds its step
1006
+ * tree. `build` runs once per play, so the same animation may play twice at once.
1007
+ *
1008
+ * @param id - Animation id, unique across the features of a game.
1009
+ * @param spec - The slots and the build function.
1010
+ * @returns The animation as frozen plain data.
1011
+ * @example
1012
+ * ```ts
1013
+ * const coinsFly = defineAnimation("hud.coinsFly", {
1014
+ * slots: { from: type<Target>() },
1015
+ * build: ({ from }) => tween(from, Transform, { scale: 0.4 }, { ms: 600 })
1016
+ * });
1017
+ * coinsFly.id; // "hud.coinsFly"
1018
+ * ```
1019
+ */
1020
+ declare function defineAnimation<Tags extends SlotTags>(id: string, spec: AnimationSpec<Tags>): AnimationDefinition<Tags>;
1021
+ //#endregion
1022
+ //#region src/plugins/anim/timeline/types.d.ts
1023
+ /**
1024
+ * The value of the component a step names, with its shape erased: the value type of
1025
+ * `AnyComponent`. A step is built against the real component, so `set(target, Sprite, patch)`
1026
+ * checks the patch; the step tree and the runtime then carry the component erased, which lets one
1027
+ * choreography mix components.
1028
+ *
1029
+ * @example
1030
+ * ```ts
1031
+ * const patch: ErasedFields = { texture: "hud.coin-gold" };
1032
+ * ```
1033
+ */
1034
+ type ErasedFields = AnyComponent extends ComponentType<infer Value> ? Value : never;
1035
+ /**
1036
+ * A sound, owned by `anim` and handled by `audio`. It is a `flow` descriptor and a timeline step
1037
+ * at once: `fx(sfx("board.merge"))` in a node, or `sfx("board.merge")` inside a `sequence`.
1038
+ *
1039
+ * @example
1040
+ * ```ts
1041
+ * const descriptor: SfxDescriptor = {
1042
+ * kind: "sfx", payload: { key: "board.merge", bus: "sfx" }, cosmetic: true
1043
+ * };
1044
+ * ```
1045
+ */
1046
+ type SfxDescriptor = {
1047
+ readonly kind: "sfx";
1048
+ readonly payload: {
1049
+ readonly key: string;
1050
+ readonly bus: string;
1051
+ };
1052
+ readonly cosmetic: true;
1053
+ };
1054
+ /**
1055
+ * One haptic tick a device can play: the impact, selection and notification families of Tauri.
1056
+ * `HAPTIC_KINDS` lists the same seven at runtime.
1057
+ *
1058
+ * @example
1059
+ * ```ts
1060
+ * const kind: HapticKind = "success";
1061
+ * ```
1062
+ */
1063
+ type HapticKind = (typeof HAPTIC_KINDS)[number];
1064
+ /**
1065
+ * A haptic tick, owned by `anim` and handled by `platform`. Like `sfx` it is a descriptor and a
1066
+ * timeline step at once.
1067
+ *
1068
+ * @example
1069
+ * ```ts
1070
+ * const descriptor: HapticDescriptor = {
1071
+ * kind: "haptic", payload: { kind: "light" }, cosmetic: true
1072
+ * };
1073
+ * ```
1074
+ */
1075
+ type HapticDescriptor = {
1076
+ readonly kind: "haptic";
1077
+ readonly payload: {
1078
+ readonly kind: HapticKind;
1079
+ };
1080
+ readonly cosmetic: true;
1081
+ };
1082
+ /**
1083
+ * The two descriptors `anim` owns. As a step, the descriptor is dispatched through
1084
+ * `flow.fx.dispatch` when the step is reached; a kind nobody handles is silent.
1085
+ *
1086
+ * @example
1087
+ * ```ts
1088
+ * const step: FxStep = { kind: "haptic", payload: { kind: "light" }, cosmetic: true };
1089
+ * ```
1090
+ */
1091
+ type FxStep = SfxDescriptor | HapticDescriptor;
1092
+ /**
1093
+ * The descriptor `play(animation, slots)` builds. The payload names the animation, so the
1094
+ * handler resolves the definition from the registry instead of carrying a function.
1095
+ *
1096
+ * @example
1097
+ * ```ts
1098
+ * const descriptor: PlayDescriptor = {
1099
+ * kind: "play",
1100
+ * payload: { animation: "hud.coinsFly", slots: { from: { projection: "hud", key: "coins" } } },
1101
+ * cosmetic: true
1102
+ * };
1103
+ * ```
1104
+ */
1105
+ type PlayDescriptor = {
1106
+ readonly kind: "play";
1107
+ readonly payload: {
1108
+ readonly animation: string;
1109
+ readonly slots: Json;
1110
+ };
1111
+ readonly cosmetic: true;
1112
+ };
1113
+ /**
1114
+ * One step of a choreography: plain frozen data with no callback inside, so a step tree can be
1115
+ * read, logged and compared. A `spawn` step carries the component values of a temporary entity,
1116
+ * made when the step is reached and despawned when the timeline ends.
1117
+ *
1118
+ * @example
1119
+ * ```ts
1120
+ * const step: Step = { kind: "wait", ms: 120 };
1121
+ * ```
1122
+ */
1123
+ type Step = {
1124
+ readonly kind: "sequence";
1125
+ readonly steps: readonly Step[];
1126
+ } | {
1127
+ readonly kind: "parallel";
1128
+ readonly steps: readonly Step[];
1129
+ } | {
1130
+ readonly kind: "wait";
1131
+ readonly ms: number;
1132
+ } | {
1133
+ readonly kind: "mark";
1134
+ readonly name: string;
1135
+ } | {
1136
+ readonly kind: "tween";
1137
+ readonly target: Target;
1138
+ readonly component: AnyComponent;
1139
+ readonly to: Readonly<Record<string, number>>;
1140
+ readonly ms: number;
1141
+ readonly ease: Ease;
1142
+ readonly delayMs: number;
1143
+ readonly additive: boolean; /** `"root"`: the `Transform` fields are a root pose, turned local when the track starts. */
1144
+ readonly space: "local" | "root";
1145
+ } | {
1146
+ readonly kind: "set";
1147
+ readonly target: Target;
1148
+ readonly component: AnyComponent;
1149
+ readonly patch: Readonly<ErasedFields>;
1150
+ } | {
1151
+ readonly kind: "frames";
1152
+ readonly target: Target;
1153
+ readonly keys: readonly string[];
1154
+ readonly fps: number;
1155
+ readonly loop: boolean;
1156
+ } | {
1157
+ readonly kind: "spawn";
1158
+ readonly id: string;
1159
+ readonly components: readonly AnyComponentValue[];
1160
+ readonly layer: string;
1161
+ readonly order: number;
1162
+ } | FxStep | {
1163
+ readonly kind: "use";
1164
+ readonly id: string;
1165
+ readonly step: Step;
1166
+ };
1167
+ /**
1168
+ * Reserved for Spine and the other external players: `external` throws, and this is the shape it
1169
+ * would take. Exported as a type only.
1170
+ *
1171
+ * @example
1172
+ * ```ts
1173
+ * const player: ExternalPlayer = { name: "spine", play: () => undefined };
1174
+ * ```
1175
+ */
1176
+ type ExternalPlayer = {
1177
+ readonly name: string;
1178
+ play(clip: string): unknown;
1179
+ };
1180
+ /**
1181
+ * Where one step of a running timeline stands. One mutable record per step of the tree, built
1182
+ * when the timeline starts.
1183
+ */
1184
+ type Cursor = {
1185
+ readonly step: Step;
1186
+ readonly children: readonly Cursor[]; /** Position in a `sequence`, and the last written key of a `frames` step. */
1187
+ index: number; /** Consumed milliseconds of a `wait` or a `frames` step. */
1188
+ elapsed: number;
1189
+ motion: StepMotion | undefined;
1190
+ started: boolean;
1191
+ ended: boolean; /** The entity a running `frames` step holds against its `Frames` loop, until the step ends. */
1192
+ held: Entity | undefined;
1193
+ };
1194
+ /**
1195
+ * What the cursor reaches the rest of the engine through. Injected, so `timeline` never imports
1196
+ * the run-time code of `tween` or of another plugin.
1197
+ */
1198
+ type TimelineRuntime = {
1199
+ /**
1200
+ * Resolves a target to an entity.
1201
+ *
1202
+ * @param target - The projection key, the entity or the spawned id a step names.
1203
+ * @param spawned - The spawn table of the timeline that asks; a spawned id resolves only there.
1204
+ * @returns The entity, or `undefined` when no live view, element or spawned entity holds it.
1205
+ */
1206
+ entityOf(target: Target, spawned?: ReadonlyMap<string, Entity>): Entity | undefined;
1207
+ /**
1208
+ * Spawns a temporary entity owned by `anim`. The timeline that spawned it despawns it when it
1209
+ * ends, whichever way it ends.
1210
+ *
1211
+ * @param components - The component values the entity starts with, `Layer` and `Order` included.
1212
+ * @returns The new entity.
1213
+ */
1214
+ spawn(components: readonly AnyComponentValue[]): Entity;
1215
+ /**
1216
+ * Reads a component of an entity, which is also the liveness check of a step.
1217
+ *
1218
+ * @param entity - The entity to read.
1219
+ * @param component - The component to read.
1220
+ * @returns The stored value, or `undefined`.
1221
+ */
1222
+ read(entity: Entity, component: AnyComponent): Readonly<ErasedFields> | undefined;
1223
+ /**
1224
+ * Writes a patch through `ecs.set`, so `changed()` sees it.
1225
+ *
1226
+ * @param entity - The entity to write.
1227
+ * @param component - The component to write.
1228
+ * @param patch - The fields to overwrite.
1229
+ */
1230
+ write(entity: Entity, component: AnyComponent, patch: Partial<ErasedFields>): void;
1231
+ /**
1232
+ * Turns the root-space target of a `tween` step into the local target under the entity's
1233
+ * parent, through `localPoseOf` of `renderer`.
1234
+ *
1235
+ * @param entity - The entity the step moves.
1236
+ * @param component - The component the step drives; only `Transform` is converted.
1237
+ * @param to - The numeric target fields, in root space.
1238
+ * @returns The target fields in the space of the entity's parent.
1239
+ */
1240
+ toLocal(entity: Entity, component: AnyComponent, to: Readonly<Record<string, number>>): Record<string, number>;
1241
+ /**
1242
+ * Starts one track on the tween core.
1243
+ *
1244
+ * @param entity - The entity to animate.
1245
+ * @param component - The component to animate.
1246
+ * @param to - The numeric target fields.
1247
+ * @param options - Duration, easing, delay and the additive flag.
1248
+ * @returns The motion of the track, advanceable by hand.
1249
+ */
1250
+ start(entity: Entity, component: AnyComponent, to: Record<string, number>, options: TrackOptions): StepMotion;
1251
+ /**
1252
+ * Hands a descriptor step to `flow.fx.dispatch`.
1253
+ *
1254
+ * @param descriptor - The `sfx` or `haptic` step that was reached.
1255
+ */
1256
+ dispatch(descriptor: FxStep): void;
1257
+ /**
1258
+ * Reports a mark: the event, the `onMark` listeners and the timeline's own list.
1259
+ *
1260
+ * @param animation - Id of the animation the mark belongs to.
1261
+ * @param name - Name of the mark.
1262
+ */
1263
+ mark(animation: string, name: string): void;
1264
+ /**
1265
+ * Lifts the idle frame rate, because something started to move.
1266
+ */
1267
+ wake(): void;
1268
+ /**
1269
+ * Claims the sprite of an entity for a running `frames` step: the `Frames` loop of the entity
1270
+ * writes nothing while any claim is open.
1271
+ *
1272
+ * @param entity - The entity the step writes.
1273
+ */
1274
+ holdFrames(entity: Entity): void;
1275
+ /**
1276
+ * Gives one claim back. At the last one the `Frames` loop writes its current key again on its
1277
+ * next step, so the sprite does not stay on the step's last key.
1278
+ *
1279
+ * @param entity - The entity the step wrote.
1280
+ */
1281
+ releaseFrames(entity: Entity): void;
1282
+ };
1283
+ /**
1284
+ * One running timeline: its cursor, the marks it reached, the entities it counts on, the entities
1285
+ * it spawned and despawns at its end, and the resolver of its `done` promise.
1286
+ */
1287
+ type RunningTimeline = {
1288
+ readonly id: number;
1289
+ readonly animation: string;
1290
+ readonly cursor: Cursor;
1291
+ readonly marks: string[];
1292
+ readonly entities: readonly Entity[]; /** The entities its `spawn` steps made, by spawn id. Despawned when the timeline ends. */
1293
+ readonly spawned: Map<string, Entity>;
1294
+ readonly resolve: () => void;
1295
+ ended: boolean;
1296
+ };
1297
+ declare namespace types_d_exports {
1298
+ export { AnimApi, AnimCtx, AnimationDefinition, AnimationSpec, AnyAnimationDefinition, BuildTools, Config, Cursor, Deps, ErasedFields, Events, ExternalPlayer, FrameLoop, FramesValue, FxStep, HapticDescriptor, HapticKind, KernelSlice, MarkListener, MotionKeyframe, PlayDescriptor, PlayHandle, Pose, RunningTimeline, SfxDescriptor, SlotRecord, SlotTags, SlotValue, SlotValues, State, Step, StepMotion, Target, TimelineRuntime, Track };
1299
+ }
1300
+ /**
1301
+ * What a step and a slot are pointed at: the projection key of a live view or of an element a
1302
+ * plugin above registered with `registerKey`, an entity, or the id of an entity a `spawn` step of
1303
+ * the same timeline made (`spawned(id)`). A node passes keys, never entities.
1304
+ *
1305
+ * @example
1306
+ * ```ts
1307
+ * const counter: Target = { projection: "hud", key: "coins" };
1308
+ * const flyingCoin: Target = spawned("coin1"); // { spawned: "coin1" }
1309
+ * ```
1310
+ */
1311
+ type Target = {
1312
+ projection: string;
1313
+ key: string;
1314
+ } | Entity | {
1315
+ spawned: string;
1316
+ };
1317
+ /**
1318
+ * The pose `at(target)` answers with: where the target rests in root (reference) space, its rest
1319
+ * `Transform` composed through the `Parent` chain, or the identity pose when nothing is there.
1320
+ *
1321
+ * @example
1322
+ * ```ts
1323
+ * const pose: Pose = { x: 540, y: 960, rotation: 0, scale: 1 };
1324
+ * ```
1325
+ */
1326
+ type Pose = {
1327
+ x: number;
1328
+ y: number;
1329
+ rotation: number;
1330
+ scale: number;
1331
+ };
1332
+ /**
1333
+ * The slot declaration of an animation: one type tag per slot, each carrying one target or a
1334
+ * list of targets.
1335
+ *
1336
+ * @example
1337
+ * ```ts
1338
+ * const slots: SlotTags = { items: type<Target[]>(), card: type<Target>() };
1339
+ * ```
1340
+ */
1341
+ type SlotTags = Readonly<Record<string, TypeTag<Target> | TypeTag<Target[]>>>;
1342
+ /**
1343
+ * The targets `play` and `use` take, read off the slot tags of the animation.
1344
+ *
1345
+ * @example
1346
+ * ```ts
1347
+ * type Slots = SlotValues<{ card: TypeTag<Target> }>; // { card: Target }
1348
+ * ```
1349
+ */
1350
+ type SlotValues<Tags extends SlotTags> = { [Name in keyof Tags]: SlotValue<Tags[Name]> };
1351
+ /**
1352
+ * What one slot tag stands for: a list of targets when the tag declared one, a single target
1353
+ * otherwise. Distributive on purpose, so the erased `SlotTags` widens to both.
1354
+ *
1355
+ * @example
1356
+ * ```ts
1357
+ * type One = SlotValue<TypeTag<Target>>; // Target
1358
+ * ```
1359
+ */
1360
+ type SlotValue<Tag> = Tag extends TypeTag<Target[]> ? Target[] : Target;
1361
+ /**
1362
+ * The slots of a running animation with their tags erased, as `build` and the `play` handler
1363
+ * read them.
1364
+ *
1365
+ * @example
1366
+ * ```ts
1367
+ * const slots: SlotRecord = { card: { projection: "hud", key: "order" } };
1368
+ * ```
1369
+ */
1370
+ type SlotRecord = SlotValues<SlotTags>;
1371
+ /**
1372
+ * What `build` is handed next to the slots: `at` reads where a target rests at play time, in root
1373
+ * space (its rest `Transform` composed through the `Parent` chain, every pivot applied), so a
1374
+ * choreography can fly one element to where another one rests, even inside a scaled slot. A view
1375
+ * that has a `Parent` itself is aimed at that pose with a `tween` in `space: "root"`.
1376
+ *
1377
+ * @example
1378
+ * ```ts
1379
+ * const tools: BuildTools = { at: () => ({ x: 0, y: 0, rotation: 0, scale: 1 }) };
1380
+ * ```
1381
+ */
1382
+ type BuildTools = {
1383
+ at(target: Target): Pose;
1384
+ };
1385
+ /**
1386
+ * One animation: an id, the slots it takes and the pure function that builds its step tree. The
1387
+ * same definition may play twice at once, because `build` runs per play.
1388
+ *
1389
+ * @example
1390
+ * ```ts
1391
+ * const coinsFly: AnimationDefinition<{ from: TypeTag<Target> }> = defineAnimation("hud.coinsFly", {
1392
+ * slots: { from: type<Target>() },
1393
+ * build: ({ from }) => tween(from, Transform, { scale: 0.4 }, { ms: 600 })
1394
+ * });
1395
+ * coinsFly.id; // "hud.coinsFly"
1396
+ * ```
1397
+ */
1398
+ type AnimationDefinition<Tags extends SlotTags = SlotTags> = {
1399
+ readonly id: string;
1400
+ readonly slots: Tags;
1401
+ build(slots: SlotValues<Tags>, tools: BuildTools): Step;
1402
+ };
1403
+ /**
1404
+ * What `defineAnimation` is given: the slots the animation takes and the pure function that
1405
+ * builds its step tree.
1406
+ *
1407
+ * @example
1408
+ * ```ts
1409
+ * const spec: AnimationSpec<{ card: TypeTag<Target> }> = {
1410
+ * slots: { card: type<Target>() },
1411
+ * build: ({ card }) => tween(card, Transform, { scale: 1.2 }, { ms: 120 })
1412
+ * };
1413
+ * ```
1414
+ */
1415
+ type AnimationSpec<Tags extends SlotTags> = {
1416
+ readonly slots: Tags;
1417
+ build(slots: SlotValues<Tags>, tools: BuildTools): Step;
1418
+ };
1419
+ /**
1420
+ * An animation with its slot tags erased, as the registry stores it.
1421
+ *
1422
+ * @example
1423
+ * ```ts
1424
+ * const stored: AnyAnimationDefinition = { id: "hud.coinsFly", slots: {}, build: () => wait(0) };
1425
+ * ```
1426
+ */
1427
+ type AnyAnimationDefinition = AnimationDefinition<SlotTags>;
1428
+ /**
1429
+ * What `play` hands back: the motion contract over the whole step tree, the promise of its end
1430
+ * and the marks it reached so far.
1431
+ *
1432
+ * @example
1433
+ * ```ts
1434
+ * const deliverOrder = defineAnimation("orders.deliver", { slots: { card: type<Target>() },
1435
+ * build: ({ card }) => sequence(tween(card, Transform, { x: 100 }, { ms: 100 }), mark("done")) });
1436
+ * const handle: PlayHandle = app.anim.play(deliverOrder, { card: { projection: "board.items", key: "a" } });
1437
+ * handle.marks(); // []: nothing reached yet
1438
+ * ```
1439
+ */
1440
+ type PlayHandle = MotionHandle & {
1441
+ readonly done: Promise<void>;
1442
+ marks(): readonly string[];
1443
+ };
1444
+ /**
1445
+ * A listener of `onMark`: the animation id and the name of the mark that was reached.
1446
+ *
1447
+ * @example
1448
+ * ```ts
1449
+ * const reached: string[] = [];
1450
+ * const listener: MarkListener = (_animation, mark) => reached.push(mark);
1451
+ * ```
1452
+ */
1453
+ type MarkListener = (animation: string, mark: string) => void;
1454
+ /**
1455
+ * anim plugin events.
1456
+ *
1457
+ * @example
1458
+ * ```ts
1459
+ * // A feature plugin logs every mark a choreography reaches.
1460
+ * createPlugin("rewardLog", {
1461
+ * depends: [animPlugin],
1462
+ * hooks: ctx => ({ "anim:mark": ({ mark }) => ctx.log.info("mark", { mark }) })
1463
+ * }); // a reached mark("done") step logs { mark: "done" }
1464
+ * ```
1465
+ */
1466
+ type Events = {
1467
+ /** A `mark` step was reached, or jumped by `finish()`. */"anim:mark": {
1468
+ animation: string;
1469
+ mark: string;
1470
+ }; /** A timeline ended or was finished. Not emitted on `cancel()`. */
1471
+ "anim:finished": {
1472
+ animation: string;
1473
+ };
1474
+ };
1475
+ /**
1476
+ * One key of a keyframe track in `defineMotion`: where on the track it sits (`at`, 0..1 of
1477
+ * `transition.ms`), the curve of the segment that ends on it (default `"inOut"`) and the pose.
1478
+ * `dx` and `dy` are offsets from the rest pose in reference units; `rotation` (radians), `scale`
1479
+ * and `alpha` are absolute. A field the key leaves out holds the previous key's value.
1480
+ *
1481
+ * An enter walk starts on the first key and ends on the rest pose at `at: 1`; an exit walk starts
1482
+ * where the view is and ends on the last key, the pose the element leaves in.
1483
+ *
1484
+ * In a loop (`loop: { track }`) every Transform field is an offset added to the rest pose: `dx`, `dy`, `rotation`
1485
+ * and `scale` (`scale: 0.05` grows a scale-1 view to 1.05). `alpha` stays absolute. The loop
1486
+ * stands on its first key until that key's `at`, and its last key repeats its first one.
1487
+ *
1488
+ * @example
1489
+ * ```ts
1490
+ * // A popup board at 42 % of its swing: 14 u below its rest, tilted 5°, slightly larger.
1491
+ * const key: MotionKeyframe = { at: 0.42, ease: "out", Transform: { dy: 14, rotation: 0.087, scale: 1.04 } };
1492
+ * ```
1493
+ */
1494
+ type MotionKeyframe = {
1495
+ readonly at: number;
1496
+ readonly ease?: Ease;
1497
+ readonly Transform?: {
1498
+ readonly dx?: number;
1499
+ readonly dy?: number;
1500
+ readonly rotation?: number;
1501
+ readonly scale?: number;
1502
+ };
1503
+ readonly Shape?: {
1504
+ readonly alpha?: number;
1505
+ };
1506
+ readonly Sprite?: {
1507
+ readonly alpha?: number;
1508
+ };
1509
+ readonly NineSlice?: {
1510
+ readonly alpha?: number;
1511
+ };
1512
+ };
1513
+ /**
1514
+ * The value of the `Frames` component: a frame loop that lives as long as its entity. `keys` and
1515
+ * `fps` mean what they mean on the `frames` step; `loop: false` stops on the last key and
1516
+ * `playing: false` holds the key on the screen. A new `keys` array restarts at key 0, the same
1517
+ * array keeps its phase.
1518
+ *
1519
+ * @example
1520
+ * ```ts
1521
+ * const spin: FramesValue = { keys: ["items.coin-0", "items.coin-1"], fps: 12, loop: true, playing: true };
1522
+ * ```
1523
+ */
1524
+ type FramesValue = {
1525
+ keys: readonly string[];
1526
+ fps: number;
1527
+ loop: boolean;
1528
+ playing: boolean;
1529
+ };
1530
+ /**
1531
+ * Where the `Frames` loop of one entity stands. `keys` is the array last seen, for the restart
1532
+ * check; `index` is the key the clock last landed on, `-1` before the first one; `written` is
1533
+ * false while that key still has to reach the sprite.
1534
+ */
1535
+ type FrameLoop = {
1536
+ keys: readonly string[];
1537
+ elapsed: number;
1538
+ index: number;
1539
+ written: boolean;
1540
+ };
1541
+ /**
1542
+ * anim plugin config. The timings live in the steps and in `defineMotion`, not here: a
1543
+ * choreography that reads its duration from a config cannot be read as text.
1544
+ *
1545
+ * @example
1546
+ * ```ts
1547
+ * createApp({ plugins: [...screen], pluginConfigs: { anim: { maxTracks: 500, reducedMotion: true } } });
1548
+ * ```
1549
+ */
1550
+ type Config = {
1551
+ /** Dev guard: one warning each time the running track count rises past this number. */maxTracks: number;
1552
+ /**
1553
+ * Start value of reduced motion: every track but a loop takes 0 ms and every loop stands on
1554
+ * its first key. `app.anim.setReducedMotion(on)` switches it while the game runs.
1555
+ */
1556
+ reducedMotion: boolean;
1557
+ };
1558
+ /**
1559
+ * anim plugin state: the one track table, the field bookkeeping of the retarget policy and the
1560
+ * offsets accumulator, the running timelines and the animation registry.
1561
+ */
1562
+ type State = {
1563
+ /** The one track table. Insertion order is advance order. */tracks: Map<number, Track>; /** Ids of tracks and timelines. */
1564
+ nextId: number; /** `"<entity>:<component>:<field>"` to the id of the absolute track that drives the field. */
1565
+ owner: Map<string, number>; /** Per field key, the delta each additive track contributes. */
1566
+ offsets: Map<string, Map<number, number>>; /** Per field key, the value the offsets are added to. Dropped when no track drives the field. */
1567
+ bases: Map<string, number>; /** The running timelines, by id. */
1568
+ timelines: Map<number, RunningTimeline>; /** The `animations` of every feature, read in `onStart`. */
1569
+ registry: Map<string, AnyAnimationDefinition>; /** `onMark` subscribers, in subscription order. */
1570
+ markListeners: Set<MarkListener>; /** Counter of the frame steps. A track born in the running step is not advanced by it. */
1571
+ frame: number; /** True while the track count is over `maxTracks`, so the warning is one per crossing. */
1572
+ overMaxTracks: boolean; /** The live reduced-motion switch. The frozen `Config.reducedMotion` only seeds it. */
1573
+ reducedMotion: boolean; /** Remover of `world.projection.setDriver`. */
1574
+ removeDriver: (() => void) | undefined; /** Remover of `time.onFrame("animate")`. */
1575
+ offFrame: (() => void) | undefined; /** Remover of `flow.fx.handle("play")`. */
1576
+ offPlay: (() => void) | undefined; /** Bound in `onInit`, so the teardown context can end everything with the state alone. */
1577
+ finishAll: (() => void) | undefined; /** One loop per entity that carries `Frames`, kept by the two world hooks. */
1578
+ frameLoops: Map<Entity, FrameLoop>; /** How many running `frames` steps write this entity now. Its loop writes nothing above 0. */
1579
+ framesHeld: Map<Entity, number>;
1580
+ /**
1581
+ * Per entity that carries `Frames`, the remover of its `world.projection.mute` on
1582
+ * `Sprite.texture`: the loop owns that field, so a projection never corrects it back.
1583
+ */
1584
+ frameMutes: Map<Entity, () => void>; /** Removers of the `onAdded` and `onRemoved` hooks on `Frames`. */
1585
+ offFrames: Array<() => void>;
1586
+ };
1587
+ /**
1588
+ * anim plugin API, `app.anim`: play a choreography, end everything, count what runs and listen
1589
+ * to the marks. A choreography may spawn temporary entities; they live exactly as long as its
1590
+ * timeline.
1591
+ *
1592
+ * @example
1593
+ * ```ts
1594
+ * // A choreography runs in game time: two frames of 50 ms play the 100 ms slide of a card.
1595
+ * const deliverOrder = defineAnimation("orders.deliver", { slots: { card: type<Target>() },
1596
+ * build: ({ card }) => sequence(tween(card, Transform, { x: 100 }, { ms: 100 }), mark("done")) });
1597
+ * app.anim.play(deliverOrder, { card: { projection: "board.items", key: "a" } });
1598
+ * app.time.step(50); // app.anim.active() is 1: the card slides
1599
+ * app.time.step(50); // app.anim.active() is 0: the card rests at x 100
1600
+ * ```
1601
+ */
1602
+ type AnimApi = {
1603
+ /**
1604
+ * Builds the step tree of an animation with the given targets and starts it. The tree runs
1605
+ * from the next frame step on, in game time, so `time.setScale` and a pause apply to it. `at`
1606
+ * inside `build` answers where a target rests in root space, even inside a scaled slot. A
1607
+ * `tween` with `space: "root"` lands a hosted view on such a pose: a board item inside the board
1608
+ * slot flies onto an order card of the HUD.
1609
+ *
1610
+ * @param animation - What `defineAnimation` returned.
1611
+ * @param slots - One target, or a list of targets, per declared slot.
1612
+ * @returns The handle of the running timeline.
1613
+ * @throws {Error} When the tree spawns one id twice.
1614
+ * @example
1615
+ * ```ts
1616
+ * // The player delivers order card "a": it slides to x 100 in 100 ms, then reaches "done".
1617
+ * const deliverOrder = defineAnimation("orders.deliver", { slots: { card: type<Target>() },
1618
+ * build: ({ card }) => sequence(tween(card, Transform, { x: 100 }, { ms: 100 }), mark("done")) });
1619
+ * const handle = app.anim.play(deliverOrder, { card: { projection: "board.items", key: "a" } });
1620
+ * app.time.step(100);
1621
+ * handle.marks(); // ["done"], and handle.done resolves
1622
+ * ```
1623
+ */
1624
+ play<Tags extends SlotTags>(animation: AnimationDefinition<Tags>, slots: SlotValues<Tags>): PlayHandle;
1625
+ /**
1626
+ * Ends every track and every timeline now: each writes its exact target, the marks of a
1627
+ * running timeline are jumped in tree order, every entity a timeline spawned is despawned and
1628
+ * every pending `done` resolves.
1629
+ *
1630
+ * @example
1631
+ * ```ts
1632
+ * // A /control tool skips the delivery mid-flight: the card lands on x 100 at once.
1633
+ * const deliverOrder = defineAnimation("orders.deliver", { slots: { card: type<Target>() },
1634
+ * build: ({ card }) => sequence(tween(card, Transform, { x: 100 }, { ms: 100 }), mark("done")) });
1635
+ * app.anim.play(deliverOrder, { card: { projection: "board.items", key: "a" } });
1636
+ * app.time.step(50);
1637
+ * app.anim.finishAll(); // app.anim.active() is 0
1638
+ * ```
1639
+ */
1640
+ finishAll(): void;
1641
+ /**
1642
+ * How many tracks are in the table, the delayed ones and the running loops included. A loop
1643
+ * never ends by itself, so a screen at rest counts one track per loop lane it shows. A `Frames`
1644
+ * component is not a track: a spinning coin counts nothing.
1645
+ *
1646
+ * @returns The number of running tracks.
1647
+ * @example
1648
+ * ```ts
1649
+ * // The assertion every motion test ends with: the screen came to rest.
1650
+ * const deliverOrder = defineAnimation("orders.deliver", { slots: { card: type<Target>() },
1651
+ * build: ({ card }) => sequence(tween(card, Transform, { x: 100 }, { ms: 100 }), mark("done")) });
1652
+ * app.anim.play(deliverOrder, { card: { projection: "board.items", key: "a" } });
1653
+ * app.time.step(100);
1654
+ * app.anim.active(); // 0
1655
+ * ```
1656
+ */
1657
+ active(): number;
1658
+ /**
1659
+ * Whether reduced motion is on. It starts at `Config.reducedMotion` and `setReducedMotion`
1660
+ * switches it.
1661
+ *
1662
+ * @returns True while every new track takes 0 ms and every loop stands on its first key.
1663
+ * @example
1664
+ * ```ts
1665
+ * // A settings popup shows the switch as the player left it.
1666
+ * app.anim.reducedMotion(); // false: the start value of Config.reducedMotion
1667
+ * ```
1668
+ */
1669
+ reducedMotion(): boolean;
1670
+ /**
1671
+ * Switches reduced motion. While it is on, every track started from then on takes 0 ms: enter
1672
+ * and exit, state changes, change and settle motions, drag returns and timeline tweens land on
1673
+ * their target at the next frame, and their marks and sounds still fire. Every loop stands on
1674
+ * its first key, a running one at once. Tracks already running keep their length. A `Frames`
1675
+ * loop stops on the frame it shows and walks on from that frame when the switch goes off.
1676
+ *
1677
+ * @param on - True for less motion, false for the full motion.
1678
+ * @example
1679
+ * ```ts
1680
+ * // The player turns on "Less motion" in the settings popup.
1681
+ * app.anim.setReducedMotion(true);
1682
+ * app.anim.reducedMotion(); // true: a popup opened now stands in its rest pose next frame
1683
+ * ```
1684
+ */
1685
+ setReducedMotion(on: boolean): void;
1686
+ /**
1687
+ * Registers a listener called for every mark a timeline reaches, next to the `anim:mark`
1688
+ * event. A listener that throws is logged and the listeners after it still run.
1689
+ *
1690
+ * @param fn - Called with the animation id and the mark name.
1691
+ * @returns The remover; calling it twice is a no-op.
1692
+ * @example
1693
+ * ```ts
1694
+ * // A test waits for the "done" mark instead of counting frames.
1695
+ * const ping = defineAnimation("fx.ping", { slots: {}, build: () => sequence(wait(100), mark("done")) });
1696
+ * const reached: string[] = [];
1697
+ * app.anim.onMark((animation, name) => reached.push(`${animation}:${name}`));
1698
+ * app.anim.play(ping, {});
1699
+ * app.time.step(100); // reached: ["fx.ping:done"]
1700
+ * ```
1701
+ */
1702
+ onMark(fn: MarkListener): () => void;
1703
+ };
1704
+ /**
1705
+ * Resolved dependency APIs. `renderer` is not among them: `anim` writes its `Sprite` and reads
1706
+ * its `Transform` through the component objects, which are plain data, composes the root pose
1707
+ * of `at` with the pure `rootPoseOf` of `renderer/sync/pose.ts`, and turns the target of a
1708
+ * `tween` in `space: "root"` local with the pure `localPoseOf` of the same module.
1709
+ */
1710
+ type Deps = {
1711
+ time: Api;
1712
+ flow: Api$1;
1713
+ world: Api$2;
1714
+ };
1715
+ /**
1716
+ * What the kernel context offers before the deps are attached.
1717
+ *
1718
+ * `index.ts` writes `events` with an annotated `register` (core spec `14-EVENT-REGISTRATION.md`
1719
+ * row 8), so the own events reach the context the kernel hands the factories and `emit` is the
1720
+ * kernel's own, anim-typed one. No member of the context is cast.
1721
+ */
1722
+ type KernelSlice = PluginCtx<Config, State, Events> & {
1723
+ readonly global: object;
1724
+ readonly log: Log.LogApi;
1725
+ readonly require: Require;
1726
+ };
1727
+ /**
1728
+ * Domain context of the anim plugin: the kernel slice and the three resolved dependencies.
1729
+ */
1730
+ type AnimCtx = KernelSlice & {
1731
+ readonly deps: Deps;
1732
+ };
1733
+ //#endregion
1734
+ export { UiApi as A, spawned as C, wait as D, use as E, TextApi as F, TextStyleInput as I, TextStyles as L, Config$2 as M, Point as N, Config$1 as O, State$2 as P, TextValue as R, spawn as S, tween as T, parallel as _, State as a, set as b, HapticKind as c, HAPTIC_KINDS as d, defineAnimation as f, mark as g, haptic as h, MotionKeyframe as i, types_d_exports$1 as j, State$1 as k, SfxDescriptor as l, frames as m, Config as n, Target as o, external as p, FramesValue as r, types_d_exports as s, AnimApi as t, Step as u, play as v, stagger as w, sfx as x, sequence as y, types_d_exports$2 as z };