@moku-labs/game 0.0.1 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -1,23 +1,224 @@
1
- import { A as Patch, B as PauseReason, C as State, D as Root, E as Json, F as StoreApi, G as State$4, H as types_d_exports$2, I as RngApi, J as Events, K as types_d_exports$4, O as State$3, P as SaveUnreadableError, R as Api$2, T as Config$3, U as Api$3, V as State$2, W as Config$4, _ as GuideOptions, a as Kit, b as Api, c as exit, d as type, f as FeatureDescription, g as Descriptor, i as GameTypes, k as types_d_exports$3, l as slot, n as Config$1, o as State$1, q as Config$5, r as FeaturePlugin, s as types_d_exports$1, t as Api$1, u as to, v as Hint, w as types_d_exports, x as Config, z as Config$2 } from "./types-BfsmUzLC.mjs";
1
+ import { $t as types_d_exports$3, At as Json, B as Mut, Bt as StoreApi, Ct as Config$5, D as ProjectionMotion, E as NumericFields, Et as types_d_exports$13, F as ComponentHandle, G as SystemDefinition, I as ComponentType, J as BundlesOf, Jt as types_d_exports$8, K as TagType, Kt as PauseReason, L as ComponentValue, Mt as State$3, Nt as types_d_exports$9, O as ProjectionSpec, Ot as Config$3, P as AnyComponentValue, Pt as Patch, Q as GameTypes, Qt as State, Rt as SaveUnreadableError, St as Api$4, U as QueryTerm, Ut as Api$2, V as Narrowed, Vt as RngApi, W as ResourceType, Wt as Config$2, X as EmitterIdOf, Xt as Config, Y as Config$1, Yt as Api, Z as FeaturePlugin, _ as State$6, at as slot, b as DescriptionNode, bt as Hint, c as types_d_exports$10, ct as FeatureDescription, en as Config$7, et as State$1, g as Config$6, gt as SceneIdOf, h as Api$5, it as exit, jt as Root, m as ViewportSize, n as Config$4, nt as types_d_exports$5, ot as to, q as Api$1, qt as State$2, rt as defineFlow, s as State$4, st as type, t as Api$3, tn as Events, tt as TextStylesOf, ut as DefineNode, v as types_d_exports$15, vt as Descriptor, wt as State$5, x as Ease, yt as GuideOptions } from "./types-Cjf3ywai.mjs";
2
+ import { a as DefineBundles, c as LoadReason, d as Tier, i as Config$8, n as BundleMap, p as types_d_exports$1, r as BundleSpec, s as LoadBundles, t as Api$6, u as State$7 } from "./types-DgwbzDLP.mjs";
3
+ import { A as Transform, B as types_d_exports$6, C as NineSliceValue, D as Sprite, E as ShapeValue, H as InputApi, I as I18nApi, L as Message, M as sprite, N as Argument, O as SpriteOptions, P as Config$9, R as State$8, S as NineSlice, T as Shape, U as State$9, V as Config$10, W as types_d_exports$7, _ as Style, b as defineComponent, d as popup, f as defineTokens, g as ResolvedStyle, h as IsFlags, j as TransformValue, k as SpriteValue, l as Box, p as IntrinsicElementsFor, s as PopupComponent, u as LocalWrite, w as Parent, x as Display, y as WhenFlags, z as TypedTr } from "./types-C0SLQsx0.mjs";
4
+ import { C as Point, D as TextStyles, E as TextStyleInput, O as TextValue, S as Config$12, T as TextApi, _ as Step, a as Config$11, b as UiApi, c as SlotTags, d as Target, f as types_d_exports, g as SfxDescriptor, h as PlayDescriptor, i as BuildTools, k as types_d_exports$12, l as SlotValues, m as HapticDescriptor, n as AnimationDefinition, o as FramesValue, p as ExternalPlayer, r as AnimationSpec, s as MotionKeyframe, t as AnimApi, u as State$10, v as Config$13, w as State$11, x as types_d_exports$14, y as State$12 } from "./types-KlE_GL1z.mjs";
5
+ import { A as FilterValueOf, C as EmitterValue, D as DisplacementValue, E as ColorMatrixValue, M as NoiseValue, N as OutlineValue, O as FilterComponent, P as UniformDeclaration, S as EmitterDefinition, T as BlurValue, _ as EffectsApi, b as types_d_exports$4, c as AnySceneProjection, d as SceneDefinition, f as SceneSpec, g as Config$15, h as types_d_exports$11, i as MusicOptions, j as GlowValue, k as FilterSpec, l as DefineScene, m as State$15, n as Config$14, o as State$13, p as ScenesApi, r as MusicDescriptor, s as types_d_exports$2, t as AudioApi, u as LayerMap, w as AlphaValue, x as EmitterConfig, y as State$14 } from "./types-BTycq8_Z.mjs";
2
6
 
7
+ //#region src/plugins/text/components.d.ts
8
+ /**
9
+ * Words on the screen: a string or a message, the style it is drawn in, and the string `text`
10
+ * resolved out of it. A game writes `content`, `style`, `bind`, `anchor` and `alpha`; `resolved`
11
+ * is engine-owned and `ui` and the tests read it. `alpha` (1 by default) fades the whole block and
12
+ * is patched in place, so a tween on it rebuilds nothing.
13
+ */
14
+ declare const Text: ComponentType<TextValue>;
15
+ /**
16
+ * What `label` takes: the two things every label needs, and the anchor that has a default.
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * const options: LabelOptions = { text: "+5", style: "board.float", at: { x: 90, y: 180 } };
21
+ * ```
22
+ */
23
+ type LabelOptions = {
24
+ text: string | Message;
25
+ style: string;
26
+ at: Point;
27
+ anchor?: Point;
28
+ };
29
+ /**
30
+ * Bundles the two components a label needs, so a projection `view` reads as one line. The anchor
31
+ * is the point of the block that sits on the transform.
32
+ *
33
+ * @param options - The text, its style, where it sits, and which point of it sits there.
34
+ * @returns The `Text` and the `Transform` value, in that order.
35
+ * @example
36
+ * ```ts
37
+ * label({ text: "+5", style: "board.float", at: { x: 90, y: 180 } });
38
+ * // [Text({ content: "+5", style: "board.float", bind: undefined,
39
+ * // anchor: { x: 0.5, y: 0.5 }, alpha: 1, resolved: "" }),
40
+ * // Transform({ x: 90, y: 180, rotation: 0, scale: 1 })]
41
+ * ```
42
+ */
43
+ declare function label(options: LabelOptions): [ComponentValue<TextValue>, ComponentValue<TransformValue>];
44
+ /**
45
+ * Declares the text styles of a feature. The result goes under the `textStyles` key of the
46
+ * feature description, and `text` reads it in `onStart`.
47
+ *
48
+ * @param map - Style name to what that style looks like.
49
+ * @returns The registration a feature carries.
50
+ * @throws {Error} When a style names a wrap that is not a width and not `"none"`.
51
+ * @example
52
+ * ```ts
53
+ * defineTextStyles({ "hud.digits": { font: "ui.font-digits", size: 40, fill: 0xffe082 } });
54
+ * // { kind: "textStyles", map: { "hud.digits": { font: "ui.font-digits", bold: undefined,
55
+ * // italic: undefined, size: 40, fill: 0xffe082, stroke: 0x000000, strokeWidth: 0,
56
+ * // letterSpacing: 0, align: "left", wrap: "none", digits: false, shadow: undefined } } }
57
+ * ```
58
+ */
59
+ declare function defineTextStyles(map: Record<string, TextStyleInput>): TextStyles;
60
+ //#endregion
61
+ //#region src/plugins/world/projection/define.d.ts
62
+ /**
63
+ * Types a projection: keyed items of the model turned into entities. `Item` is inferred from the
64
+ * return of `from` and reaches `key`, `view` and every motion hook; `layer` and `lift` stay
65
+ * literal, so `defineScene` can check them against the layers of the scene.
66
+ *
67
+ * @param spec - Name, layer, optional lift layer, `from`, `key`, `view` and the motion hooks.
68
+ * @returns The same spec, typed.
69
+ * @example
70
+ * ```ts
71
+ * const boardItems = projection({
72
+ * name: "board.items",
73
+ * layer: "items",
74
+ * from: (player: { items: { id: string }[] }) => player.items,
75
+ * key: item => item.id,
76
+ * view: () => []
77
+ * });
78
+ * boardItems.layer; // "items", the literal type, not string
79
+ * ```
80
+ */
81
+ declare function projection<const LayerName extends string, const LiftName extends string, Item, Player = Json, Session = Json>(spec: ProjectionSpec<Item, LayerName, LiftName, Player, Session>): ProjectionSpec<Item, LayerName, LiftName, Player, Session>;
82
+ /**
83
+ * The projection helper bound to one game's `player` and `session`.
84
+ *
85
+ * @example
86
+ * ```ts
87
+ * const bound: ProjectionFor<{ coins: number }, {}> = projection;
88
+ * ```
89
+ */
90
+ type ProjectionFor<Player, Session> = <const LayerName extends string, const LiftName extends string, Item>(spec: ProjectionSpec<Item, LayerName, LiftName, Player, Session>) => ProjectionSpec<Item, LayerName, LiftName, Player, Session>;
91
+ //#endregion
92
+ //#region src/plugins/anim/index.d.ts
93
+ /**
94
+ * Anim plugin: `app.anim.play(animation, slots)`, `app.anim.finishAll()`,
95
+ * `app.anim.setReducedMotion(on)`.
96
+ *
97
+ * @example
98
+ * ```ts
99
+ * // Animation is text: a module next to the feature, played by a node or by a test.
100
+ * const coinsFly = defineAnimation("hud.coinsFly", {
101
+ * slots: { from: type<Target>(), to: type<Target>() },
102
+ * build: ({ from, to }, { at }) =>
103
+ * sequence(
104
+ * tween(from, Transform, { x: at(to).x, y: at(to).y }, { ms: 600, ease: "inCubic" }),
105
+ * mark("landed")
106
+ * )
107
+ * });
108
+ *
109
+ * const app = createApp({ plugins: [...screen, animPlugin, hudFeature] });
110
+ *
111
+ * app.anim.play(coinsFly, { from: purse, to: counter }).marks(); // []: the first frame is next
112
+ * ```
113
+ */
114
+ declare const animPlugin: import("@moku-labs/core").PluginInstance<"anim", Config$11, State$10, AnimApi, {
115
+ "anim:mark": {
116
+ animation: string;
117
+ mark: string;
118
+ };
119
+ "anim:finished": {
120
+ animation: string;
121
+ };
122
+ }> & Record<never, never>;
123
+ //#endregion
124
+ //#region src/plugins/assets/index.d.ts
125
+ /**
126
+ * Assets plugin: `app.assets.load(bundle)`, `app.assets.texture(key)`, `app.assets.usage()`.
127
+ *
128
+ * @example
129
+ * ```ts
130
+ * // A game names its manifest; the graph decides from there what is loaded and when.
131
+ * const app = createApp({
132
+ * plugins: [...screen, boardFeature],
133
+ * pluginConfigs: { assets: { manifest: "/assets/manifest.json", textureBudgetMb: 192 } }
134
+ * });
135
+ *
136
+ * await app.start();
137
+ * app.assets.usage().textureMb; // 0.188, the boot tier
138
+ * ```
139
+ */
140
+ declare const assetsPlugin: import("@moku-labs/core").PluginInstance<"assets", Config$8, State$7, Api$6, {
141
+ "assets:bundle-loaded": {
142
+ bundle: string;
143
+ tier: Tier;
144
+ mb: number;
145
+ reason: LoadReason;
146
+ };
147
+ "assets:bundle-progress": {
148
+ bundle: string;
149
+ loaded: number;
150
+ total: number;
151
+ };
152
+ "assets:bundle-unloaded": {
153
+ bundle: string;
154
+ tier: Tier;
155
+ mb: number;
156
+ reason: "budget" | "request";
157
+ keys: readonly string[];
158
+ };
159
+ }> & Record<never, never>;
160
+ //#endregion
161
+ //#region src/plugins/audio/index.d.ts
162
+ /**
163
+ * Audio plugin: `app.audio`, the handlers of the effect kinds `sfx` and `music`, and the three
164
+ * buses a game drives from its player state.
165
+ *
166
+ * @example
167
+ * ```ts
168
+ * // A game composes it next to the screen set and points it at the player's settings.
169
+ * createApp({
170
+ * plugins: [...screen, audioPlugin, settingsFeature],
171
+ * pluginConfigs: { audio: { volumes: player => player.settings.audio } }
172
+ * });
173
+ * ```
174
+ */
175
+ declare const audioPlugin: import("@moku-labs/core").PluginInstance<"audio", Config$14, State$13, AudioApi, {}> & Record<never, never>;
176
+ //#endregion
3
177
  //#region src/plugins/clock/index.d.ts
4
178
  /**
5
179
  * Clock plugin: `app.clock.now()`, `app.clock.scheduleAt(moment)`, `app.clock.onElapsed(fn)`.
6
180
  *
7
181
  * @example
8
182
  * ```ts
9
- * ctx.require(clockPlugin).scheduleAt(nextDue);
183
+ * // A game plugin that needs trusted time declares the dependency and asks for the API.
184
+ * const energyPlugin = createPlugin("energy", {
185
+ * depends: [clockPlugin],
186
+ * api: ctx => ({ fullAt: (missing: number) => ctx.require(clockPlugin).now() + missing * 60_000 })
187
+ * });
10
188
  * ```
11
189
  */
12
190
  declare const clockPlugin: import("@moku-labs/core").PluginInstance<"clock", Config, State, Api, {}> & Record<never, never>;
13
191
  //#endregion
192
+ //#region src/plugins/effects/index.d.ts
193
+ /**
194
+ * Effects plugin: `app.effects.stats()`; the `Emitter` component and every filter component do
195
+ * the rest as data on entities.
196
+ *
197
+ * @example
198
+ * ```ts
199
+ * // A game composes it next to the screen set; its features bring the emitters and filters.
200
+ * const app = createApp({
201
+ * plugins: [...screen, effectsPlugin, boardFeature],
202
+ * pluginConfigs: { renderer: { mount: "#game" }, effects: { maxPasses: 16 } }
203
+ * });
204
+ * app.effects.stats(); // { particles: 0, emitters: 0, filters: 0, renderPasses: 0 }: nothing runs before start
205
+ * ```
206
+ */
207
+ declare const effectsPlugin: import("@moku-labs/core").PluginInstance<"effects", Config$15, State$14, EffectsApi, {}> & Record<never, never>;
208
+ //#endregion
14
209
  //#region src/plugins/flow/index.d.ts
15
210
  /**
16
211
  * Flow plugin: `app.flow.run()`, `app.flow.gate.answer(...)`, `app.flow.fx.handle(...)`.
17
212
  *
18
213
  * @example
19
214
  * ```ts
20
- * createApp({ pluginConfigs: { flow: { mainFlow } }, onStart: startGraph });
215
+ * // The game starts the graph itself: an awaited loop in onStart would never let start() resolve.
216
+ * createApp({
217
+ * pluginConfigs: { flow: { mainFlow, safeNode: "home" } },
218
+ * onStart: ctx => {
219
+ * ctx.flow.run().catch(showFatal);
220
+ * }
221
+ * });
21
222
  * ```
22
223
  */
23
224
  declare const flowPlugin: import("@moku-labs/core").PluginInstance<"flow", Config$1, State$1, Api$1, {
@@ -46,13 +247,59 @@ declare const flowPlugin: import("@moku-labs/core").PluginInstance<"flow", Confi
46
247
  };
47
248
  }> & Record<never, never>;
48
249
  //#endregion
250
+ //#region src/plugins/i18n/index.d.ts
251
+ /**
252
+ * I18n plugin: `app.i18n.format(tr("hud.orders", { n: 3 }))`, and `tr` for the game.
253
+ *
254
+ * @example
255
+ * ```ts
256
+ * // A feature ships its compiled modules; the settings screen switches between them.
257
+ * export const hudFeature = defineFeature("hud", {
258
+ * projections: [hud],
259
+ * strings: { ru: ruStrings, en: () => import("./generated/strings.en") }
260
+ * });
261
+ *
262
+ * await app.i18n.setLocale("en"); // text re-resolves every message on i18n:locale-changed
263
+ * ```
264
+ */
265
+ declare const i18nPlugin: import("@moku-labs/core").PluginInstance<"i18n", Config$9, State$8, I18nApi, {
266
+ "i18n:locale-changed": {
267
+ locale: string;
268
+ };
269
+ }> & Record<never, never>;
270
+ //#endregion
271
+ //#region src/plugins/input/index.d.ts
272
+ /**
273
+ * Input plugin: `app.input.tap(target)`, `app.input.drag(from, to)`.
274
+ *
275
+ * @example
276
+ * ```ts
277
+ * // A view declares its behaviour; the engine owns the drag and the way home.
278
+ * const boardItems = projection({
279
+ * name: "board.items",
280
+ * layer: "items",
281
+ * from: (player: Player) => player.items,
282
+ * key: (item: Item) => item.id,
283
+ * view: (item: Item) => [
284
+ * Draggable({ payload: { from: item.cell } }),
285
+ * DropTarget({ intent: "merge", payload: { to: item.cell } })
286
+ * ]
287
+ * });
288
+ * ```
289
+ */
290
+ declare const inputPlugin: import("@moku-labs/core").PluginInstance<"input", Config$10, State$9, InputApi, {}> & Record<never, never>;
291
+ //#endregion
49
292
  //#region src/plugins/lifecycle/index.d.ts
50
293
  /**
51
294
  * Lifecycle plugin: `app.lifecycle.push(reason)`, `app.lifecycle.pop(reason)`.
52
295
  *
53
296
  * @example
54
297
  * ```ts
55
- * ctx.require(lifecyclePlugin).push("background");
298
+ * // A game plugin that reacts to the pause declares the dependency and hooks the event.
299
+ * const musicPlugin = createPlugin("music", {
300
+ * depends: [lifecyclePlugin],
301
+ * hooks: () => ({ "lifecycle:changed": ({ paused }) => music.setMuted(paused) })
302
+ * });
56
303
  * ```
57
304
  */
58
305
  declare const lifecyclePlugin: import("@moku-labs/core").PluginInstance<"lifecycle", Config$2, State$2, Api$2, {
@@ -71,7 +318,13 @@ declare const lifecyclePlugin: import("@moku-labs/core").PluginInstance<"lifecyc
71
318
  *
72
319
  * @example
73
320
  * ```ts
74
- * const transaction = ctx.require(modelPlugin).store.begin();
321
+ * // A game plugin that draws from committed state declares the dependency and reads the snapshot.
322
+ * const hudPlugin = createPlugin("hud", {
323
+ * depends: [modelPlugin],
324
+ * hooks: ctx => ({
325
+ * "model:committed": () => drawCoins(ctx.require(modelPlugin).store.snapshot().player)
326
+ * })
327
+ * });
75
328
  * ```
76
329
  */
77
330
  declare const modelPlugin: import("@moku-labs/core").PluginInstance<"model", Config$3, State$3, {
@@ -84,16 +337,132 @@ declare const modelPlugin: import("@moku-labs/core").PluginInstance<"model", Con
84
337
  };
85
338
  }> & Record<never, never>;
86
339
  //#endregion
340
+ //#region src/plugins/renderer/index.d.ts
341
+ /**
342
+ * Renderer plugin: `app.renderer.host.ready()`, `app.renderer.viewport.size()`,
343
+ * `app.renderer.sync.hitTest(...)`.
344
+ *
345
+ * @example
346
+ * ```ts
347
+ * // A game with a screen names the element the canvas goes into. Nothing else is configured.
348
+ * const app = createApp({
349
+ * plugins: [...screen, boardFeature],
350
+ * pluginConfigs: { renderer: { mount: "#game" } }
351
+ * });
352
+ *
353
+ * await app.start();
354
+ * app.renderer.host.kind(); // "webgpu" in Chrome, "webgl" on an older device
355
+ * ```
356
+ */
357
+ declare const rendererPlugin: import("@moku-labs/core").PluginInstance<"renderer", Config$4, State$4, Api$3, {
358
+ "renderer:device-lost": {
359
+ kind: "webgpu" | "webgl";
360
+ reason: string;
361
+ };
362
+ }> & Record<never, never>;
363
+ //#endregion
364
+ //#region src/plugins/scenes/index.d.ts
365
+ /**
366
+ * Scenes plugin: `app.scenes.current()`, and `defineScene` for the game.
367
+ *
368
+ * @example
369
+ * ```ts
370
+ * // features/board/view/scene.ts, and the node that shows it.
371
+ * export const boardScene = defineScene("board", {
372
+ * bundle: "board",
373
+ * layers: { cells: {}, items: { sort: "y" }, lifted: {} },
374
+ * projections: [boardCells, boardItems]
375
+ * });
376
+ *
377
+ * export const awaitIntent = defineNode({ scene: "board", rest: true, outcomes: { merge: type() } });
378
+ * ```
379
+ */
380
+ declare const scenesPlugin: import("@moku-labs/core").PluginInstance<"scenes", Record<string, never>, State$15, ScenesApi, {
381
+ "scenes:changed": {
382
+ from: string | undefined;
383
+ to: string;
384
+ music: string | undefined;
385
+ };
386
+ }> & Record<never, never>;
387
+ //#endregion
388
+ //#region src/plugins/text/index.d.ts
389
+ /**
390
+ * Text plugin: `app.text.measure(...)`, the `Text` component and the helpers `label` and
391
+ * `defineTextStyles` a game writes.
392
+ *
393
+ * @example
394
+ * ```ts
395
+ * // A feature brings its styles; a projection puts a label over an item.
396
+ * export const hudStyles = defineTextStyles({
397
+ * "hud.digits": { font: "ui.font-digits", size: 40, fill: 0xffe082, digits: true }
398
+ * });
399
+ *
400
+ * const app = createApp({ plugins: [...screen, hudFeature] });
401
+ *
402
+ * app.text.styles(); // ["body", "digits", "hud.digits"]
403
+ * ```
404
+ */
405
+ declare const textPlugin: import("@moku-labs/core").PluginInstance<"text", Config$12, State$11, TextApi, {}> & Record<never, never>;
406
+ //#endregion
87
407
  //#region src/plugins/time/index.d.ts
88
408
  /**
89
409
  * Time plugin: `app.time.onFrame(phase, fn)`, `app.time.step(dt)`.
90
410
  *
91
411
  * @example
92
412
  * ```ts
93
- * ctx.require(timePlugin).onFrame("animate", advanceTweens);
413
+ * // A game plugin with frame work declares the dependency and registers a callback on start.
414
+ * const sparklePlugin = createPlugin("sparkle", {
415
+ * depends: [timePlugin],
416
+ * onStart: ctx => void ctx.require(timePlugin).onFrame("animate", time => advance(time.delta))
417
+ * });
94
418
  * ```
95
419
  */
96
- declare const timePlugin: import("@moku-labs/core").PluginInstance<"time", Config$4, State$4, Api$3, {}> & Record<never, never>;
420
+ declare const timePlugin: import("@moku-labs/core").PluginInstance<"time", Config$5, State$5, Api$4, {}> & Record<never, never>;
421
+ //#endregion
422
+ //#region src/plugins/ui/index.d.ts
423
+ /**
424
+ * Ui plugin: `app.ui.tree()`, `app.ui.find(...)`, `app.ui.lint()`, `app.ui.fill(...)`, the
425
+ * text field `<input local>`, the components `Box` and
426
+ * `LocalWrite`, and the helpers `defineComponent`, `defineStyle`, `defineTokens` and `popup`.
427
+ *
428
+ * @example
429
+ * ```ts
430
+ * // A headless test reads the HUD and taps the button the popup opened.
431
+ * const app = createApp({ plugins: [...screen, uiPlugin, hudFeature] });
432
+ *
433
+ * app.ui.tree().type; // "row"
434
+ * app.input.tap(app.ui.find("claim") ?? 0);
435
+ * ```
436
+ */
437
+ declare const uiPlugin: import("@moku-labs/core").PluginInstance<"ui", Config$13, State$12, UiApi, {}> & Record<never, never>;
438
+ //#endregion
439
+ //#region src/plugins/world/index.d.ts
440
+ /**
441
+ * World plugin: `app.world.ecs.query(...)`, `app.world.projection.mount(...)`.
442
+ *
443
+ * @example
444
+ * ```ts
445
+ * // A test drives the screen without a browser: mount, commit, step frames.
446
+ * const app = createApp({ plugins: [worldPlugin, boardFeature] });
447
+ *
448
+ * app.world.projection.setLayers([{ name: "items", sort: "y" }]);
449
+ * app.world.projection.mount(["board.items"], { kind: "plugin", name: "test" });
450
+ * app.world.projection.entityOf("board.items", "i7"); // 1048576
451
+ * ```
452
+ */
453
+ declare const worldPlugin: import("@moku-labs/core").PluginInstance<"world", Config$6, State$6, Api$5, {
454
+ "world:reconciled": {
455
+ mode: "play" | "direct";
456
+ projections: number;
457
+ entered: number;
458
+ changed: number;
459
+ exited: number;
460
+ revived: number;
461
+ queued: number;
462
+ hintsRouted: number;
463
+ hintsDropped: number;
464
+ };
465
+ }> & Record<never, never>;
97
466
  //#endregion
98
467
  //#region src/plugins/flow/feature.d.ts
99
468
  /**
@@ -107,10 +476,12 @@ declare const timePlugin: import("@moku-labs/core").PluginInstance<"time", Confi
107
476
  * @throws {Error} When the name is reserved or belongs to an engine plugin.
108
477
  * @example
109
478
  * ```ts
110
- * export const boardFeature = defineFeature("board", {
111
- * flows: [boardFlow],
112
- * contribute: { afterWin: { flow: rewardFlow, order: 10 } }
479
+ * // The reward popup is a feature: the main flow only declares slot("afterOrder").
480
+ * export const rewardFeature = defineFeature("reward", {
481
+ * flows: [rewardFlow],
482
+ * contribute: { afterOrder: { flow: rewardFlow, order: 10 } }
113
483
  * });
484
+ * createApp({ plugins: [rewardFeature] }); // a headless test passes rewardFeature.logicOnly
114
485
  * ```
115
486
  */
116
487
  declare function defineFeature(name: string, description: FeatureDescription): FeaturePlugin;
@@ -125,7 +496,9 @@ declare function defineFeature(name: string, description: FeatureDescription): F
125
496
  * @returns The hint as plain data.
126
497
  * @example
127
498
  * ```ts
499
+ * // Inside a node body: sparkle on the merged cell, shown once the edge has committed.
128
500
  * fx.emit(hint("sparkle", { cell: "c3" }));
501
+ * // hint("sparkle", { cell: "c3" }) is { kind: "sparkle", payload: { cell: "c3" }, hint: true }
129
502
  * ```
130
503
  */
131
504
  declare function hint(kind: string, payload?: Json): Hint;
@@ -138,74 +511,910 @@ declare function hint(kind: string, payload?: Json): Hint;
138
511
  * @returns The descriptor a node awaits.
139
512
  * @example
140
513
  * ```ts
141
- * await fx(schedule(rules.nextDue(player, tables)));
514
+ * // Inside a node body, after the rules changed a timer: ask for the next `elapsed`.
515
+ * await fx(schedule(1_790_000_060_000)); // payload: { moment: 1790000060000 }
516
+ * await fx(schedule(undefined)); // payload: {}, the pending timer is cancelled
142
517
  * ```
143
518
  */
144
519
  declare function schedule(moment: number | undefined): Descriptor;
145
520
  /**
146
521
  * Creates the tutorial descriptor: it narrows the gate to one answer until the node exits. Its
147
- * visual part (highlight, hand, text) is handled by `ui` later. The highlight list is copied, so
148
- * a later change of the caller's array cannot reach the descriptor.
522
+ * visual part (`target`, highlight, hand, text) is drawn by `ui`, which dims the screen and cuts
523
+ * a hole over the target element. The highlight list and the target are copied, so a later change
524
+ * of the caller's objects cannot reach the descriptor.
149
525
  *
150
526
  * @param options - The allowed answer and the visual hints.
151
527
  * @returns The descriptor a node awaits.
152
528
  * @example
153
529
  * ```ts
154
- * await fx(guide({ allow: { intent: "merge", payload: { from: "c2", to: "c3" } }, hand: "drag" }));
530
+ * // A tutorial node: the popup lists two intents, the guide lets only "ok" through.
531
+ * await fx(guide({ allow: { intent: "ok" }, target: { projection: "hud", key: "play" } }));
532
+ * await fx({ kind: "popup", answers: ["ok", "cancel"] });
533
+ * // app.flow.gate.answer({ intent: "cancel" }) is false until the node exits
155
534
  * ```
156
535
  */
157
536
  declare function guide(options: GuideOptions): Descriptor;
158
537
  //#endregion
159
- //#region src/teardown.d.ts
538
+ //#region src/plugins/world/ecs/define.d.ts
539
+ /**
540
+ * Declares a component type. The defaults give the TypeScript type, the JSON shape and the
541
+ * inspector schema; the name is the storage key and the key of `motion.change`.
542
+ *
543
+ * @param name - Storage name, unique per world.
544
+ * @param defaults - Every field with its default value. Plain JSON only.
545
+ * @param options - The fields a plugin owns: a projection view neither writes nor corrects them.
546
+ * @param options.owned - Names of the owned fields.
547
+ * @returns A callable component type.
548
+ * @example
549
+ * ```ts
550
+ * const Item = component("Item", { kind: "", level: 1 });
551
+ * Item({ level: 2 }); // { type: Item, value: { kind: "", level: 2 } }
552
+ * Item({ level: 2 }).type.componentName; // "Item", the storage key
553
+ *
554
+ * // A caption whose drawn string is written by the plugin that draws it.
555
+ * const Caption = component("Caption", { text: "", shown: "" }, { owned: ["shown"] });
556
+ * Caption.owned; // ["shown"]
557
+ * ```
558
+ */
559
+ declare function component<Value extends object>(name: string, defaults: Value, options?: {
560
+ owned?: ReadonlyArray<keyof Value & string>;
561
+ }): ComponentType<Value>;
160
562
  /**
161
- * @file Teardown registry — the single allowlisted module-scope state of the package.
563
+ * Declares a tag: a component with no data, stored as `true`.
564
+ *
565
+ * @param name - Storage name, unique per world.
566
+ * @returns A callable tag type.
567
+ * @example
568
+ * ```ts
569
+ * const Held = tag("Held");
570
+ * Held(); // { type: Held, value: true }
571
+ * Held().type.componentName; // "Held"
572
+ * ```
162
573
  */
574
+ declare function tag(name: string): TagType;
163
575
  /**
164
- * Function that frees one plugin's resources.
576
+ * Declares a resource: one mutable object per world, created from a deep clone of the defaults
577
+ * on first read.
165
578
  *
579
+ * @param name - Storage name, unique per world.
580
+ * @param defaults - The value a fresh world starts with.
581
+ * @returns The resource type.
166
582
  * @example
167
583
  * ```ts
168
- * const dispose: Disposer = () => cancelAnimationFrame(handle);
584
+ * const Pointer = resource("Pointer", { x: 0, y: 0, down: false });
585
+ * Pointer.defaults; // { x: 0, y: 0, down: false }
169
586
  * ```
170
587
  */
171
- type Disposer = () => void | Promise<void>;
588
+ declare function resource<Value extends object>(name: string, defaults: Value): ResourceType<Value>;
172
589
  /**
173
- * Registers a disposer for one plugin of one app. A second call with the same key replaces the first.
590
+ * Marks a query term as written. The only effect is change detection: every entity the query
591
+ * yields is marked changed for this component.
174
592
  *
175
- * @param global - The app's frozen global config object, used as identity.
176
- * @param key - Plugin name.
177
- * @param dispose - Function that frees the plugin's resources.
593
+ * @param componentType - The component the system writes.
594
+ * @returns The marked term.
178
595
  * @example
179
596
  * ```ts
180
- * teardown.register(ctx.global, "time", stopLoop);
597
+ * const Transform = component("Transform", { x: 0, y: 0 });
598
+ * mut(Transform).kind; // "mut"
181
599
  * ```
182
600
  */
183
- declare function register(global: object, key: string, dispose: Disposer): void;
601
+ declare function mut<Value extends object>(componentType: ComponentHandle<Value>): Mut<Value>;
184
602
  /**
185
- * Runs and removes the disposer of one plugin. A missing key is a no-op.
186
- * A throwing disposer is reported and never rethrown, so one plugin cannot block the stop of the others.
603
+ * Types a system definition. It returns the same plain object; the work is the type, which reads
604
+ * the query tuple and types the first argument of `run`.
187
605
  *
188
- * @param global - The app's global config object.
189
- * @param key - Plugin name.
190
- * @returns Resolves when the disposer has finished.
606
+ * @param definition - Name, phase, query and the run function.
607
+ * @returns The same definition, typed.
191
608
  * @example
192
609
  * ```ts
193
- * await teardown.run(global, "time");
610
+ * const Transform = component("Transform", { x: 0, y: 0 });
611
+ * const drift = system({
612
+ * name: "drift",
613
+ * phase: "animate",
614
+ * query: [mut(Transform)],
615
+ * run: entities => {
616
+ * for (const [, transform] of entities) transform.x += 1;
617
+ * }
618
+ * });
619
+ * drift.phase; // "animate"
194
620
  * ```
195
621
  */
196
- declare function run(global: object, key: string): Promise<void>;
622
+ declare function system<const Terms extends readonly QueryTerm[]>(definition: SystemDefinition<Terms>): SystemDefinition<Terms>;
623
+ /**
624
+ * The layer a view is drawn in. Written only by the projection; a `view` that returns it is
625
+ * logged and the value ignored.
626
+ */
627
+ declare const Layer: ComponentType<{
628
+ name: string;
629
+ }>;
630
+ /**
631
+ * Explicit draw order inside a layer sorted by `"order"`.
632
+ */
633
+ declare const Order: ComponentType<{
634
+ value: number;
635
+ }>;
636
+ /**
637
+ * The view is in the despawn queue: still drawn, never hit-tested.
638
+ */
639
+ declare const Exiting: TagType;
197
640
  /**
198
- * Teardown registry keyed by `ctx.global`.
641
+ * The element description of a screen: a projection whose `view` returns one node gets it wrapped
642
+ * as this component, and `ui` reconciles the node into child entities. The node is a foreign
643
+ * object, so the world diffs it by identity and `snapshot()` leaves it out.
644
+ */
645
+ declare const Tree: ComponentType<{
646
+ node: DescriptionNode;
647
+ }>;
648
+ //#endregion
649
+ //#region src/plugins/input/components.d.ts
650
+ /**
651
+ * A gesture that names an intent. `payload` is a plain JSON object of model keys, never an
652
+ * entity id: the flow graph reads it as the payload of the answer.
199
653
  *
200
654
  * @example
201
655
  * ```ts
202
- * onStop: ({ global }) => teardown.run(global, "time")
656
+ * const value: IntentValue = { intent: "merge", payload: { to: "c3" } };
203
657
  * ```
204
658
  */
205
- declare const teardown: {
206
- register: typeof register;
207
- run: typeof run;
659
+ type IntentValue = {
660
+ intent: string;
661
+ payload: Json;
208
662
  };
663
+ /**
664
+ * A view that can be carried. It names no intent: the drop target does.
665
+ *
666
+ * @example
667
+ * ```ts
668
+ * const value: CarryValue = { payload: { from: "c2" } };
669
+ * ```
670
+ */
671
+ type CarryValue = {
672
+ payload: Json;
673
+ };
674
+ /**
675
+ * Where the one pointer is, in reference units. `justPressed` and `justReleased` are true for
676
+ * exactly one frame.
677
+ *
678
+ * @example
679
+ * ```ts
680
+ * const value: PointerValue = {
681
+ * x: 540, y: 960, down: true, justPressed: false, justReleased: false
682
+ * };
683
+ * ```
684
+ */
685
+ type PointerValue = {
686
+ x: number;
687
+ y: number;
688
+ down: boolean;
689
+ justPressed: boolean;
690
+ justReleased: boolean;
691
+ };
692
+ /**
693
+ * A tap on this view answers `{ intent, payload }`.
694
+ */
695
+ declare const Tappable: ComponentType<IntentValue>;
696
+ /**
697
+ * A long press on this view answers `{ intent, payload }`.
698
+ */
699
+ declare const Pressable: ComponentType<IntentValue>;
700
+ /**
701
+ * This view can be carried by the finger. It names no intent; the drop target does.
702
+ */
703
+ declare const Draggable: ComponentType<CarryValue>;
704
+ /**
705
+ * A drop on this view answers THIS intent, with the carried payload merged under it.
706
+ */
707
+ declare const DropTarget: ComponentType<IntentValue>;
708
+ /**
709
+ * A swipe on this view answers `{ intent, payload: { ...payload, direction } }`.
710
+ */
711
+ declare const Swipeable: ComponentType<IntentValue>;
712
+ /**
713
+ * Takes a press without naming a gesture: the hit test accepts a view that carries only this tag,
714
+ * and a tap on it runs the `onTap` listeners and answers nothing. `ui` tags the buttons that
715
+ * write local state and name no intent.
716
+ */
717
+ declare const Touchable: TagType;
718
+ /**
719
+ * On the carried view, from grab to release.
720
+ */
721
+ declare const Held: TagType;
722
+ /**
723
+ * On the topmost drop target under the finger during a drag. At most one, never the held view.
724
+ */
725
+ declare const Hovered: TagType;
726
+ /**
727
+ * On the pressed view, from pointer down until a tap, a long press, a grab, a swipe or a cancel.
728
+ */
729
+ declare const Pressed: TagType;
730
+ /**
731
+ * On the topmost view a press would take, while a mouse or a pen moves over it with no press. At
732
+ * most one view carries it, and a touch never hovers. A touch sample, a pointer cancel, the pointer
733
+ * leaving the canvas and a paused world take it away. `ui` reads it as `is.hover`. It is not
734
+ * `Hovered`, which marks the drop target under a drag.
735
+ */
736
+ declare const PointerOver: TagType;
737
+ /**
738
+ * Where the one pointer is, in reference units. Written once per frame by the frame step.
739
+ */
740
+ declare const Pointer: ResourceType<PointerValue>;
741
+ //#endregion
742
+ //#region src/plugins/assets/bundles.d.ts
743
+ /**
744
+ * Declares the bundles of one feature. The map is copied, so a later change of the caller's
745
+ * object cannot reach the scanner or the plugin. `defineGame` returns this helper typed by the
746
+ * game's `BundleKey`, so a name the scanner never saw does not compile.
747
+ *
748
+ * @param map - Bundle name to its tier and, when it is a split bundle, its globs.
749
+ * @returns The bundle map as plain data.
750
+ * @example
751
+ * ```ts
752
+ * // features/board/assets.ts of a game: the board is a scene bundle, the chains load on demand.
753
+ * export const boardAssets = defineBundles({
754
+ * board: { tier: "scene" },
755
+ * "board.chains": { tier: "lazy", files: ["chains/*.png"] }
756
+ * });
757
+ * ```
758
+ */
759
+ declare function defineBundles<Key extends string>(map: Partial<Record<Key, BundleSpec>>): BundleMap<Key>;
760
+ /**
761
+ * Creates the awaited effect that loads one or more bundles. The handler of the kind `"load"` is
762
+ * registered by this plugin and runs in fast mode too, so a fast walk really loads.
763
+ *
764
+ * @param bundle - One bundle name, or a list of them in load order.
765
+ * @returns The descriptor a loading node awaits.
766
+ * @example
767
+ * ```ts
768
+ * // A loading node of a game: one bundle per turn, so the screen can draw progress.
769
+ * await fx(load("board.chains")); // { loaded: ["board.chains"], mb: 1.25 }
770
+ * await fx(load(["board", "ui"])); // both, in that order
771
+ * ```
772
+ */
773
+ declare function load(bundle: string | readonly string[]): Descriptor;
774
+ //#endregion
775
+ //#region src/plugins/scenes/define.d.ts
776
+ /**
777
+ * Declares a scene: a bundle, named layers in draw order, the projections it mounts and optional
778
+ * music. A layer `ui` is appended for the HUD and the popups unless the scene declares one. The
779
+ * layer names come from the keys of `layers` and that `ui`, so a projection whose `layer` or
780
+ * `lift` is not among them does not compile — and throws here for a caller without types.
781
+ *
782
+ * @param id - Id of the scene. A node names it with `defineNode({ scene: id })`.
783
+ * @param scene - The bundle, the layers, the projections and the optional music key.
784
+ * @returns The scene as frozen data, ready for the `scenes` key of a feature.
785
+ * @throws {Error} For an integer-like layer name, and for a `layer` or `lift` that is not declared.
786
+ * @example
787
+ * ```ts
788
+ * // features/board/view/scene.ts of a game.
789
+ * export const boardScene = defineScene("board", {
790
+ * bundle: "board",
791
+ * layers: { background: {}, cells: {}, items: { sort: "y" }, lifted: {} },
792
+ * projections: [boardCells, boardItems]
793
+ * });
794
+ *
795
+ * boardScene.layers[2]; // { name: "items", sort: "y" }
796
+ * boardScene.layers[4]; // { name: "ui", sort: "none" }, appended for the HUD
797
+ * ```
798
+ */
799
+ declare function defineScene<Layers extends LayerMap, const Projections extends readonly AnySceneProjection[]>(id: string, scene: SceneSpec<Layers, Projections, string, string>): SceneDefinition;
800
+ //#endregion
801
+ //#region src/plugins/anim/components.d.ts
802
+ /**
803
+ * How many tracks and running timelines name this entity. Written by `anim` while something
804
+ * moves and removed at zero; the inspector and `ui.lint` read it, `anim` itself never does.
805
+ */
806
+ declare const Animation: ComponentType<{
807
+ playing: number;
808
+ }>;
809
+ /**
810
+ * A frame loop on an entity, for as long as the entity carries it: every `1000 / fps` ms
811
+ * `Sprite.texture` becomes the next key of `keys`, after the last key it wraps to the first. No
812
+ * timeline around it, not counted by `Animation` or `app.anim.active()`. A running `frames` step on
813
+ * the same entity owns the sprite until it ends; the loop goes on from where its clock stands.
814
+ * Paused and fast world modes and reduced motion stop the clock on the frame it shows.
815
+ *
816
+ * @example
817
+ * ```ts
818
+ * // A coin spins on the board for its whole life: a projection view returns both components.
819
+ * const coinSpin = ["items.coin-0", "items.coin-1", "items.coin-2", "items.coin-3"] as const;
820
+ * const coin = [Sprite({ texture: "items.coin-0" }), Frames({ keys: coinSpin, fps: 12 })];
821
+ * Frames({ keys: coinSpin, fps: 12 }).value.loop; // true: the default
822
+ * ```
823
+ */
824
+ declare const Frames: ComponentType<FramesValue>;
825
+ //#endregion
826
+ //#region src/plugins/anim/motion.d.ts
827
+ /**
828
+ * The display components a pure `defineMotion` can name. A hook has no context, so it can only
829
+ * reach the components `anim` itself imports: the four of `renderer` that carry numeric fields.
830
+ *
831
+ * @example
832
+ * ```ts
833
+ * type Fields = MotionComponents["Transform"]; // { x: number; y: number; rotation: number; scale: number }
834
+ * ```
835
+ */
836
+ type MotionComponents = {
837
+ Transform: TransformValue;
838
+ Sprite: SpriteValue;
839
+ NineSlice: NineSliceValue;
840
+ Shape: ShapeValue;
841
+ };
842
+ /**
843
+ * One named pose: the numeric fields it gives each component it names.
844
+ *
845
+ * @example
846
+ * ```ts
847
+ * const hidden: MotionState = { Transform: { scale: 0.8 }, Shape: { alpha: 0 } };
848
+ * ```
849
+ */
850
+ type MotionState = { readonly [Name in keyof MotionComponents]?: Readonly<Partial<NumericFields<MotionComponents[Name]>>> };
851
+ /**
852
+ * What `defineMotion` is given: the named poses, the keyframe tracks, how long the way takes, and
853
+ * which hooks to build. `on.enter` and `on.exit` name a state or a track. For a track,
854
+ * `transition.ms` is the whole walk and each segment eases by its key, not by `transition.ease`.
855
+ *
856
+ * `loop.track` names a keyframe track that plays from the moment the element enters, forever,
857
+ * added over whatever else moves it: one cycle is `loop.ms` (default `transition.ms`), every
858
+ * Transform key is an offset from rest, and the last key repeats the first one. It is not part of
859
+ * the motion `enter` returns.
860
+ *
861
+ * @example
862
+ * ```ts
863
+ * const spec: MotionSpec = {
864
+ * states: { hidden: { Transform: { scale: 0.8 } } },
865
+ * keyframes: { dropIn: [{ at: 0, Transform: { dy: -780, scale: 0.8 } }, { at: 0.42, Transform: { dy: 14 } }] },
866
+ * transition: { ms: 1000 },
867
+ * on: { enter: "dropIn", exit: "hidden", change: ["Transform"] }
868
+ * };
869
+ * // An order card pops in over 250 ms and sways on its pin, one swing every 2400 ms.
870
+ * const orderCard: MotionSpec = {
871
+ * states: { small: { Transform: { scale: 0.8 } } },
872
+ * keyframes: { sway: [{ at: 0, Transform: { rotation: 0 } }, { at: 0.5, Transform: { rotation: 0.03 } }, { at: 1, Transform: { rotation: 0 } }] },
873
+ * transition: { ms: 250 },
874
+ * loop: { track: "sway", ms: 2400 },
875
+ * on: { enter: "small" }
876
+ * };
877
+ * ```
878
+ */
879
+ type MotionSpec = {
880
+ readonly states?: Readonly<Record<string, MotionState>>;
881
+ readonly keyframes?: Readonly<Record<string, readonly MotionKeyframe[]>>;
882
+ readonly transition?: {
883
+ readonly ms?: number;
884
+ readonly ease?: Ease;
885
+ };
886
+ readonly loop?: {
887
+ readonly track: string;
888
+ readonly ms?: number;
889
+ };
890
+ readonly on: {
891
+ readonly enter?: string;
892
+ readonly exit?: string;
893
+ readonly change?: readonly (keyof MotionComponents & string)[];
894
+ };
895
+ };
896
+ /**
897
+ * Builds the projection motion hooks of an element from named poses and keyframe tracks. The
898
+ * transition is resolved and every name is checked here, at definition time, so every track it
899
+ * starts carries a concrete duration and a wrong name fails where it is written. A `loop` builds
900
+ * the `loop` hook: `world` and `ui` play it wherever they play `enter`, so a projection view loops
901
+ * once it entered with motion, never after a direct reconcile, and `ui` swaps it when the motion
902
+ * prop of an element changes. One cycle of the loop is `loop.ms`, or `transition.ms` when it is
903
+ * left out.
904
+ *
905
+ * @param spec - The named poses, the keyframe tracks, the transition, the loop and the hooks to
906
+ * build.
907
+ * @returns The `enter`, `loop`, `exit` and `change` hooks. `settle` is left out: the default of
908
+ * `world` applies.
909
+ * @throws {Error} When a track is invalid, a name is both a state and a track, `on` names
910
+ * neither, `loop.track` names no track, `loop.ms` is not a finite number above 0, or the loop
911
+ * ends somewhere else than it starts.
912
+ * @example
913
+ * ```ts
914
+ * const buttonMotion = defineMotion({
915
+ * states: { hidden: { Transform: { scale: 0.8 }, Shape: { alpha: 0 } } },
916
+ * transition: { ms: 150, ease: "out" },
917
+ * on: { enter: "hidden", exit: "hidden", change: ["Transform"] }
918
+ * });
919
+ * buttonMotion.settle; // undefined
920
+ * ```
921
+ */
922
+ declare function defineMotion(spec: MotionSpec): ProjectionMotion<unknown>;
923
+ //#endregion
924
+ //#region src/plugins/anim/timeline/steps.d.ts
925
+ /**
926
+ * Runs the steps one after another. The remainder past a step's end reaches the next step in the
927
+ * same frame, so the tree takes exactly the sum of its durations.
928
+ *
929
+ * @param steps - The steps, in order.
930
+ * @returns The sequence step.
931
+ * @example
932
+ * ```ts
933
+ * sequence(wait(100), mark("done"));
934
+ * // { kind: "sequence", steps: [{ kind: "wait", ms: 100 }, { kind: "mark", name: "done" }] }
935
+ * ```
936
+ */
937
+ declare function sequence(...steps: readonly Step[]): Step;
938
+ /**
939
+ * Runs every step at once and ends with the longest one.
940
+ *
941
+ * @param steps - The steps.
942
+ * @returns The parallel step.
943
+ * @example
944
+ * ```ts
945
+ * parallel(wait(100), wait(300)); // ends after 300 ms
946
+ * ```
947
+ */
948
+ declare function parallel(...steps: readonly Step[]): Step;
949
+ /**
950
+ * Builds one step per item and starts each one `staggerMs` later than the one before. The build
951
+ * function runs here, so no function survives in the data.
952
+ *
953
+ * @param items - What to build a step for.
954
+ * @param staggerMs - Delay between two neighbouring items, in game milliseconds.
955
+ * @param build - Builds the step of one item.
956
+ * @returns The parallel of delayed sequences.
957
+ * @example
958
+ * ```ts
959
+ * stagger(["a", "b"], 60, item => mark(item));
960
+ * // parallel(sequence(wait(0), mark("a")), sequence(wait(60), mark("b")))
961
+ * ```
962
+ */
963
+ declare function stagger<Item>(items: readonly Item[], staggerMs: number, build: (item: Item, index: number) => Step): Step;
964
+ /**
965
+ * Holds still for a while.
966
+ *
967
+ * @param durationMs - How long to wait, in game milliseconds.
968
+ * @returns The wait step.
969
+ * @example
970
+ * ```ts
971
+ * wait(120); // { kind: "wait", ms: 120 }
972
+ * ```
973
+ */
974
+ declare function wait(durationMs: number): Step;
975
+ /**
976
+ * Names a point in the choreography. Reaching it emits `anim:mark`, calls the `onMark` listeners
977
+ * and appends the name to `marks()`. It is the only way out of a timeline.
978
+ *
979
+ * @param name - Name of the mark.
980
+ * @returns The mark step.
981
+ * @example
982
+ * ```ts
983
+ * mark("landed"); // { kind: "mark", name: "landed" }
984
+ * ```
985
+ */
986
+ declare function mark(name: string): Step;
987
+ /**
988
+ * Moves the numeric fields of one component of one target to an exact target value. The component
989
+ * is any handle `world.ecs` takes, so the kit's `Sprite` and `NineSlice` of `defineGame` pass as
990
+ * they are.
991
+ *
992
+ * By default the fields are local: they are written into the target's own `Transform`. With
993
+ * `space: "root"` the `x`, `y`, `rotation` and `scale` of a `Transform` are a root pose, the space
994
+ * `at()` answers in, and are turned into the space of the target's `Parent` when the track starts.
995
+ * That aims a hosted view, such as a board item inside the board slot, at another element. A
996
+ * field the step does not name stays where it is, so under a turned parent name `x` and `y`
997
+ * together. A target without a parent, and a component other than `Transform`, move as in the
998
+ * local space.
999
+ *
1000
+ * @param target - The projection key or the entity to animate.
1001
+ * @param component - The component to animate.
1002
+ * @param to - The numeric target fields.
1003
+ * @param options - Duration, easing, delay, the additive flag and the space of the fields.
1004
+ * @param options.ms - Duration in game milliseconds.
1005
+ * @param options.ease - Easing curve; `"out"` when omitted.
1006
+ * @param options.delayMs - How long the track waits before it reads its start values.
1007
+ * @param options.additive - `true` adds an offset instead of owning the fields.
1008
+ * @param options.space - `"root"` reads the `Transform` fields as a root pose; `"local"` when
1009
+ * omitted.
1010
+ * @returns The tween step.
1011
+ * @example
1012
+ * ```ts
1013
+ * // An order is delivered: the board item flies out of the scaled board slot onto the order card.
1014
+ * const deliverFly = defineAnimation("orders.deliverFly", {
1015
+ * slots: { item: type<Target>(), card: type<Target>() },
1016
+ * build: ({ item, card }, { at }) =>
1017
+ * tween(item, Transform, { x: at(card).x, y: at(card).y, scale: at(card).scale }, {
1018
+ * ms: 400, ease: "inCubic", space: "root"
1019
+ * })
1020
+ * });
1021
+ * app.anim.play(deliverFly, {
1022
+ * item: { projection: "board.items", key: "i1" },
1023
+ * card: { projection: "hud", key: "card0" }
1024
+ * });
1025
+ * // 400 ms later the item covers the card at the card's size; its Transform stays slot-local
1026
+ * ```
1027
+ */
1028
+ declare function tween<Value extends object>(target: Target, component: ComponentHandle<Value>, to: Partial<NumericFields<Value>>, options: {
1029
+ ms: number;
1030
+ ease?: Ease;
1031
+ delayMs?: number;
1032
+ additive?: boolean;
1033
+ space?: "local" | "root";
1034
+ }): Step;
1035
+ /**
1036
+ * Writes a component patch at once. Any field may be written, not only the numeric ones. The
1037
+ * component is any handle `world.ecs` takes, the kit's `Sprite` and `NineSlice` included.
1038
+ *
1039
+ * @param target - The projection key or the entity to write.
1040
+ * @param component - The component to write.
1041
+ * @param patch - The fields to overwrite.
1042
+ * @returns The set step.
1043
+ * @example
1044
+ * ```ts
1045
+ * set({ projection: "hud", key: "coins" }, Sprite, { texture: "hud.coin-gold" });
1046
+ * // { kind: "set", patch: { texture: "hud.coin-gold" }, ... }
1047
+ * ```
1048
+ */
1049
+ declare function set<Value extends object>(target: Target, component: ComponentHandle<Value>, patch: Partial<Value>): Step;
1050
+ /**
1051
+ * Plays a frame sprite: one texture key of the list per frame of `fps`, written into `Sprite`.
1052
+ *
1053
+ * @param target - The projection key or the entity to animate.
1054
+ * @param options - The keys, the frame rate and whether the list repeats.
1055
+ * @param options.keys - The texture keys, in play order.
1056
+ * @param options.fps - Frames per second of the list.
1057
+ * @param options.loop - `true` repeats the list until the step is finished.
1058
+ * @returns The frames step.
1059
+ * @example
1060
+ * ```ts
1061
+ * frames({ projection: "board", key: "c3" }, { keys: ["fx.pop-1", "fx.pop-2"], fps: 12 });
1062
+ * // { kind: "frames", keys: ["fx.pop-1", "fx.pop-2"], fps: 12, loop: false, ... }
1063
+ * ```
1064
+ */
1065
+ declare function frames(target: Target, options: {
1066
+ keys: readonly string[];
1067
+ fps: number;
1068
+ loop?: boolean;
1069
+ }): Step;
1070
+ /**
1071
+ * Makes a temporary entity when the step is reached: a flying coin, a toast sign, a sparkle. The
1072
+ * entity is owned by `anim`, drawn in `layer` at `order`, and despawned when the timeline ends, is
1073
+ * finished or is cancelled. Later steps of the same timeline aim at it with `spawned(id)`.
1074
+ *
1075
+ * @param id - Name of the entity inside this timeline; one timeline spawns each id once.
1076
+ * @param components - The component values the entity starts with, as `world.ecs.spawn` takes.
1077
+ * @param options - Where the entity is drawn.
1078
+ * @param options.layer - The layer; `"ui"` when omitted.
1079
+ * @param options.order - The draw order inside the layer; `0` when omitted.
1080
+ * @returns The spawn step.
1081
+ * @example
1082
+ * ```ts
1083
+ * spawn("coin1", [Sprite({ texture: "ui.icon-coin" }), Transform({ x: 540, y: 900 })], { order: 50 });
1084
+ * // { kind: "spawn", id: "coin1", components: [...], layer: "ui", order: 50 }
1085
+ * ```
1086
+ */
1087
+ declare function spawn(id: string, components: readonly AnyComponentValue[], options?: {
1088
+ layer?: string;
1089
+ order?: number;
1090
+ }): Step;
1091
+ /**
1092
+ * Aims a later step at the entity a `spawn` step of the same timeline made. Before that step is
1093
+ * reached nothing answers the id, so a step aimed at it ends silently.
1094
+ *
1095
+ * @param id - The id the `spawn` step was given.
1096
+ * @returns The target.
1097
+ * @example
1098
+ * ```ts
1099
+ * tween(spawned("coin1"), Transform, { x: 40, y: 120 }, { ms: 600, ease: "inCubic" });
1100
+ * // { kind: "tween", target: { spawned: "coin1" }, ... }
1101
+ * ```
1102
+ */
1103
+ declare function spawned(id: string): {
1104
+ spawned: string;
1105
+ };
1106
+ /**
1107
+ * A sound. `anim` owns the descriptor because it sits below `audio`; `audio` owns the handler,
1108
+ * and a game without `audio` plays nothing and hears no error.
1109
+ *
1110
+ * @param key - Asset key of the sound.
1111
+ * @param options - The bus to play it on.
1112
+ * @param options.bus - `"sfx"` when omitted.
1113
+ * @returns The descriptor, usable as a timeline step and as `fx(...)` in a node.
1114
+ * @example
1115
+ * ```ts
1116
+ * sfx("board.merge"); // { kind: "sfx", payload: { key: "board.merge", bus: "sfx" }, cosmetic: true }
1117
+ * ```
1118
+ */
1119
+ declare function sfx(key: string, options?: {
1120
+ bus?: string;
1121
+ }): SfxDescriptor;
1122
+ /**
1123
+ * A haptic tick. `platform` owns the handler; a device without one stays still.
1124
+ *
1125
+ * @param kind - Which tick to play, for example `"light"`.
1126
+ * @returns The descriptor, usable as a timeline step and as `fx(...)` in a node.
1127
+ * @example
1128
+ * ```ts
1129
+ * haptic("light"); // { kind: "haptic", payload: { kind: "light" }, cosmetic: true }
1130
+ * ```
1131
+ */
1132
+ declare function haptic(kind: string): HapticDescriptor;
1133
+ /**
1134
+ * Reserved for Spine and the other external players. It throws until one arrives, so nobody
1135
+ * builds a choreography on a door that is not open yet.
1136
+ *
1137
+ * @param _player - The player that would run the clip.
1138
+ * @param _clip - Name of the clip.
1139
+ * @throws {Error} Always.
1140
+ * @example
1141
+ * ```ts
1142
+ * external(spine, "idle"); // throws: External players arrive with Spine.
1143
+ * ```
1144
+ */
1145
+ declare function external(_player: ExternalPlayer, _clip: string): never;
1146
+ /**
1147
+ * Nests one animation inside another. The nested tree is built here, so its marks are reported
1148
+ * under the nested id and no function survives in the data. Pass the outer `tools` when the
1149
+ * nested `build` reads `at`.
1150
+ *
1151
+ * @param animation - The animation to nest.
1152
+ * @param slots - One target, or a list of targets, per slot of the nested animation.
1153
+ * @param tools - The build tools of the outer animation; without them `at` is the identity pose.
1154
+ * @returns The use step.
1155
+ * @example
1156
+ * ```ts
1157
+ * use(popCard, { card: { projection: "hud", key: "order" } });
1158
+ * // { kind: "use", id: "hud.popCard", step: { kind: "sequence", ... } }
1159
+ * ```
1160
+ */
1161
+ declare function use<Tags extends SlotTags>(animation: AnimationDefinition<Tags>, slots: SlotValues<Tags>, tools?: BuildTools): Step;
1162
+ /**
1163
+ * The effect descriptor a node awaits to play a choreography. The payload names the animation,
1164
+ * so the handler resolves the definition from the registry the features filled.
1165
+ *
1166
+ * @param animation - The animation to play.
1167
+ * @param slots - One target, or a list of targets, per slot.
1168
+ * @returns The descriptor, cosmetic, so a failing build never breaks the node.
1169
+ * @example
1170
+ * ```ts
1171
+ * play(coinsFly, { from: { projection: "hud", key: "purse" } });
1172
+ * // { kind: "play", payload: { animation: "hud.coinsFly", slots: { from: { ... } } }, cosmetic: true }
1173
+ * ```
1174
+ */
1175
+ declare function play<Tags extends SlotTags>(animation: AnimationDefinition<Tags>, slots: SlotValues<Tags>): PlayDescriptor;
1176
+ /**
1177
+ * Declares one animation: an id, the slots it takes and the pure function that builds its step
1178
+ * tree. `build` runs once per play, so the same animation may play twice at once.
1179
+ *
1180
+ * @param id - Animation id, unique across the features of a game.
1181
+ * @param spec - The slots and the build function.
1182
+ * @returns The animation as frozen plain data.
1183
+ * @example
1184
+ * ```ts
1185
+ * const coinsFly = defineAnimation("hud.coinsFly", {
1186
+ * slots: { from: type<Target>() },
1187
+ * build: ({ from }) => tween(from, Transform, { scale: 0.4 }, { ms: 600 })
1188
+ * });
1189
+ * coinsFly.id; // "hud.coinsFly"
1190
+ * ```
1191
+ */
1192
+ declare function defineAnimation<Tags extends SlotTags>(id: string, spec: AnimationSpec<Tags>): AnimationDefinition<Tags>;
1193
+ //#endregion
1194
+ //#region src/plugins/i18n/tr.d.ts
1195
+ /**
1196
+ * Builds a message. The result is frozen and the parameters are copied, so the caller's object
1197
+ * stays its own and nothing downstream can rewrite a label in place.
1198
+ *
1199
+ * @param key - The message key, one of the keys `compileStrings` generated.
1200
+ * @param params - The parameters the message declares. An element parameter is kept as it is.
1201
+ * @returns The message, frozen.
1202
+ * @example
1203
+ * ```ts
1204
+ * tr("hud.orders", { n: 3 }); // { key: "hud.orders", params: { n: 3 } }
1205
+ * tr("orders.complete"); // { key: "orders.complete" }
1206
+ * ```
1207
+ */
1208
+ declare function tr(key: string, params?: Record<string, Argument>): Message;
1209
+ //#endregion
1210
+ //#region src/plugins/ui/styles/define.d.ts
1211
+ /**
1212
+ * Declares one style. The work is the type: a field outside the vocabulary, or a value outside
1213
+ * its union, is an error where the style is written, not where it is used.
1214
+ *
1215
+ * @param style - The style, with its `is` and `when` variants.
1216
+ * @returns The same object, frozen.
1217
+ * @example
1218
+ * ```ts
1219
+ * defineStyle({ direction: "row", gap: 12 }).gap; // 12
1220
+ * ```
1221
+ */
1222
+ declare function defineStyle<const Given extends Style>(style: Given): Readonly<Given>;
1223
+ //#endregion
1224
+ //#region src/plugins/ui/styles/resolve.d.ts
1225
+ /**
1226
+ * The flags one element is resolved against: the viewport flags of the frame plus its own state.
1227
+ *
1228
+ * @example
1229
+ * ```ts
1230
+ * const flags: StyleFlags = {
1231
+ * portrait: true, landscape: false, tall: false, wide: false,
1232
+ * pressed: false, hover: false, focus: false, disabled: false, active: true, selected: false,
1233
+ * covered: false
1234
+ * };
1235
+ * ```
1236
+ */
1237
+ type StyleFlags = WhenFlags & IsFlags;
1238
+ /**
1239
+ * Resolves one style against the flags of the frame and the viewport.
1240
+ *
1241
+ * @param style - The style a game wrote, or nothing.
1242
+ * @param flags - The four viewport flags and the seven state flags. While `disabled` is true the
1243
+ * `hover` and `pressed` variants are skipped.
1244
+ * @param viewport - What `renderer.viewport.size()` answered, for the safe-area tokens.
1245
+ * @returns The frozen style the layout and the visual are written from.
1246
+ * @example
1247
+ * ```ts
1248
+ * resolve(
1249
+ * { gap: 8, is: { pressed: { gap: 4 } } },
1250
+ * { portrait: true, landscape: false, tall: false, wide: false,
1251
+ * pressed: true, hover: false, focus: false, disabled: false, active: false, selected: false,
1252
+ * covered: false },
1253
+ * { width: 1080, height: 1920, scale: 1, orientation: "portrait",
1254
+ * safeArea: { top: 0, right: 0, bottom: 0, left: 0 } }
1255
+ * ).gap; // 4
1256
+ * ```
1257
+ */
1258
+ declare function resolve(style: Style | undefined, flags: StyleFlags, viewport: ViewportSize): ResolvedStyle;
1259
+ //#endregion
1260
+ //#region src/plugins/audio/descriptors.d.ts
1261
+ /**
1262
+ * Creates the awaited effect that switches the music track. The same key as the one playing does
1263
+ * nothing, and `null` fades the current track out. An absent `fadeMs` leaves the key out of the
1264
+ * payload, so the descriptor stays plain JSON and the plugin's `musicFadeMs` decides at play time.
1265
+ *
1266
+ * @param key - Asset key of an `.mp3`, or `null` to stop the music.
1267
+ * @param options - `fadeMs` overrides the configured cross-fade for this switch.
1268
+ * @returns The descriptor a node awaits, frozen.
1269
+ * @example
1270
+ * ```ts
1271
+ * // A node of a game: the menu goes quiet before the ending cinematic starts.
1272
+ * await fx(music(null, { fadeMs: 1500 }));
1273
+ * // music("board.theme") is { kind: "music", payload: { key: "board.theme" }, cosmetic: true }
1274
+ * ```
1275
+ */
1276
+ declare function music(key: string | null, options?: MusicOptions): Descriptor;
1277
+ //#endregion
1278
+ //#region src/plugins/effects/filters/builtins.d.ts
1279
+ /**
1280
+ * A soft halo of `color` around the opaque pixels of a view and its subtree, `distance` pixels
1281
+ * wide, that follows the shape: rounded corners stay rounded. Its alpha is
1282
+ * `strength × alpha ×` the share of the disc of radius `distance` around the pixel the view
1283
+ * covers, so `strength: 2` is full next to a straight edge and fades to nothing at `distance`.
1284
+ * The view is padded by its live `distance`. One render pass per apply.
1285
+ *
1286
+ * @example
1287
+ * ```ts
1288
+ * // A merged item glows, and a motion hook tweens the glow away.
1289
+ * Glow({ strength: 4 }).value; // { strength: 4, distance: 10, color: 0xffffff, alpha: 1, enabled: true, order: 0 }
1290
+ * view.tween(Glow, { strength: 0 }, { ms: 200 });
1291
+ * ```
1292
+ */
1293
+ declare const Glow: FilterComponent<GlowValue>;
1294
+ /**
1295
+ * A ring of `thickness` pixels in `color` around the opaque pixels of a view and its subtree; the
1296
+ * view is padded by its live `thickness`. One render pass per apply.
1297
+ *
1298
+ * @example
1299
+ * ```ts
1300
+ * // The selected card is outlined in honey.
1301
+ * Outline({ thickness: 4, color: 0xffc233 }).value; // { thickness: 4, color: 0xffc233, alpha: 1, enabled: true, order: 0 }
1302
+ * ```
1303
+ */
1304
+ declare const Outline: FilterComponent<OutlineValue>;
1305
+ /**
1306
+ * Pixi's blur over a view and its subtree. `quality: 0` means `config.blur.quality`;
1307
+ * `resolution: 0` means `config.blur.phoneResolution` on a phone, otherwise 1. `repeatEdgePixels`
1308
+ * keeps the padding at 0. Costs `2 × quality` render passes per apply.
1309
+ *
1310
+ * @example
1311
+ * ```ts
1312
+ * // The board behind a popup: the filter sits on the ui slot that hosts the board.
1313
+ * Blur({ strength: 6 }).value; // { strength: 6, quality: 0, resolution: 0, repeatEdgePixels: true, enabled: true, order: 0 }
1314
+ * ```
1315
+ */
1316
+ declare const Blur: ComponentType<BlurValue>;
1317
+ /**
1318
+ * Pixi's colour matrix: brightness, saturation, contrast, hue in degrees and grayscale, composed
1319
+ * in that order from the identity on every change. Every default is the identity.
1320
+ *
1321
+ * @example
1322
+ * ```ts
1323
+ * // A locked card goes gray.
1324
+ * ColorMatrix({ grayscale: 1 }).value.grayscale; // 1
1325
+ * ```
1326
+ */
1327
+ declare const ColorMatrix: ComponentType<ColorMatrixValue>;
1328
+ /**
1329
+ * Pixi's noise. The seed is always given, never Pixi's random default, so two runs draw the same
1330
+ * grain.
1331
+ *
1332
+ * @example
1333
+ * ```ts
1334
+ * Noise({ amount: 0.2, seed: 7 }).value; // { amount: 0.2, seed: 7, enabled: true, order: 0 }
1335
+ * ```
1336
+ */
1337
+ declare const Noise: ComponentType<NoiseValue>;
1338
+ /**
1339
+ * Pixi's displacement over the texture of the asset key `map`. Nothing is drawn while the map is
1340
+ * not loaded; a missing key warns once.
1341
+ *
1342
+ * @example
1343
+ * ```ts
1344
+ * // Heat haze over the furnace.
1345
+ * Displacement({ map: "fx.ripple", scaleX: 8, scaleY: 8 }).value.map; // "fx.ripple"
1346
+ * ```
1347
+ */
1348
+ declare const Displacement: ComponentType<DisplacementValue>;
1349
+ /**
1350
+ * Pixi's alpha over a view and its subtree as one layer, so overlapping children do not show
1351
+ * through each other.
1352
+ *
1353
+ * @example
1354
+ * ```ts
1355
+ * Alpha({ alpha: 0.5 }).value; // { alpha: 0.5, enabled: true, order: 0 }
1356
+ * ```
1357
+ */
1358
+ declare const Alpha: ComponentType<AlphaValue>;
1359
+ //#endregion
1360
+ //#region src/plugins/effects/filters/define.d.ts
1361
+ /**
1362
+ * Turns a WGSL fragment body into a filter component type. The engine prepends the vertex stage,
1363
+ * the input bindings and a `FilterUniforms` struct in declaration order, bound as `fu`. Every
1364
+ * number uniform is a number field, so the existing `tween` drives it; a colour is one hex field
1365
+ * sent as a `vec3<f32>`; a tuple is an array field and not tweenable. Every filter component also
1366
+ * carries `enabled: true` and `order: 0`. Register it through the `filters` key of a feature.
1367
+ *
1368
+ * @param id - The filter id, also the component name; unique, and not one of the built-ins.
1369
+ * @param spec - The body, the uniforms, the passes one apply costs and the padding.
1370
+ * @returns The component type, with the frozen `filter` definition attached.
1371
+ * @throws {Error} When the id is empty, the body has no `mainFragment`, a uniform is reserved,
1372
+ * badly named or of no known form, or the passes or the padding break their rules.
1373
+ * @example
1374
+ * ```ts
1375
+ * // features/board/effects.ts: a gold tint a motion hook tweens on a merge.
1376
+ * const Tint = defineFilter("fx.tint", { wgsl: tintBody, uniforms: { amount: 0, color: { color: 0xffd700 } } });
1377
+ * Tint({ amount: 1 }).value; // { amount: 1, color: 0xffd700, enabled: true, order: 0 }
1378
+ * Tint.filter.passes; // 1
1379
+ * ```
1380
+ */
1381
+ declare function defineFilter<Uniforms extends Record<string, UniformDeclaration> = Record<never, never>>(id: string, spec: FilterSpec<Uniforms>): FilterComponent<FilterValueOf<Uniforms>>;
1382
+ //#endregion
1383
+ //#region src/plugins/effects/particles/component.d.ts
1384
+ /**
1385
+ * A particle effect on an entity. A `burst` effect emits when the component appears or its
1386
+ * `effect` changes; a `rate` effect emits while `active` is true and the entity lives. An effect in
1387
+ * `"world"` space outlives the entity, one in `"local"` space moves and dies with it. `""` draws
1388
+ * nothing. Plain JSON: it shows in `snapshot()`.
1389
+ *
1390
+ * @example
1391
+ * ```ts
1392
+ * // A merged item bursts stars from its enter hook; the generator steams for its whole life.
1393
+ * view.set(Emitter, { effect: "fx.starsBurst" });
1394
+ * Emitter({ effect: "fx.steam" }).value; // { effect: "fx.steam", active: true }
1395
+ * ```
1396
+ */
1397
+ declare const Emitter: ComponentType<EmitterValue>;
1398
+ //#endregion
1399
+ //#region src/plugins/effects/particles/define.d.ts
1400
+ /**
1401
+ * Describes a particle effect. Every field is optional except `textures` and exactly one of
1402
+ * `burst` and `rate`; the definition is frozen, registered through the `emitters` key of a feature
1403
+ * and started by an `Emitter` component naming its id.
1404
+ *
1405
+ * @param id - The effect id, unique across the features of the game.
1406
+ * @param config - The effect as data.
1407
+ * @returns The frozen definition `{ id, config }`, the config with every default filled in.
1408
+ * @throws {Error} When the id is empty or a field breaks its rule.
1409
+ * @example
1410
+ * ```ts
1411
+ * // features/board/effects.ts: the steam over the generator.
1412
+ * const steam = defineEmitter("fx.steam", { textures: ["fx.puff"], rate: 12, space: "local" });
1413
+ * steam.config.rate; // 12
1414
+ * steam.config.lifeMs; // [600, 1000], the default
1415
+ * ```
1416
+ */
1417
+ declare function defineEmitter(id: string, config: EmitterConfig): EmitterDefinition;
209
1418
  //#endregion
210
1419
  //#region src/index.d.ts
211
1420
  /**
@@ -213,10 +1422,14 @@ declare const teardown: {
213
1422
  *
214
1423
  * @example
215
1424
  * ```ts
216
- * const app = createApp({ plugins: [boardFeature] });
1425
+ * // The entry point of a game. The graph is started by the game, never by the plugin.
1426
+ * const app = createApp({
1427
+ * pluginConfigs: { flow: { mainFlow, safeNode: "home" } },
1428
+ * onStart: ctx => void ctx.flow.run().catch(error => ctx.log.error("game: failed", { error }))
1429
+ * });
217
1430
  * ```
218
1431
  */
219
- declare const createApp: <const ExtraPlugins extends readonly import("@moku-labs/core").AnyPluginInstance[] = readonly []>(options?: import("@moku-labs/core").CreateAppOptions<Config$5, Events, (import("@moku-labs/core").PluginInstance<"time", Config$4, State$4, Api$3, {}> & Record<never, never>) | (import("@moku-labs/core").PluginInstance<"lifecycle", Config$2, State$2, Api$2, {
1432
+ declare const createApp: <const ExtraPlugins extends readonly import("@moku-labs/core").AnyPluginInstance[] = readonly []>(options?: import("@moku-labs/core").CreateAppOptions<Config$7, Events, (import("@moku-labs/core").PluginInstance<"time", Config$5, State$5, Api$4, {}> & Record<never, never>) | (import("@moku-labs/core").PluginInstance<"lifecycle", Config$2, State$2, Api$2, {
220
1433
  "lifecycle:changed": {
221
1434
  reason: PauseReason;
222
1435
  action: "push" | "pop";
@@ -256,7 +1469,7 @@ declare const createApp: <const ExtraPlugins extends readonly import("@moku-labs
256
1469
  rolledBackTo: string;
257
1470
  retry: boolean;
258
1471
  };
259
- }> & Record<never, never>) | ExtraPlugins[number], [...ExtraPlugins], import("@moku-labs/core").CoreApisFromTuple<[import("@moku-labs/core").CorePluginInstance<"log", import("@moku-labs/common/browser").LogConfig, import("@moku-labs/common/browser").LogState, import("@moku-labs/common/browser").LogApi>, import("@moku-labs/core").CorePluginInstance<"env", import("@moku-labs/common/browser").EnvConfig, import("@moku-labs/common/browser").EnvState, import("@moku-labs/common/browser").EnvApi>]>> | undefined) => import("@moku-labs/core").App<Config$5, Events, (import("@moku-labs/core").PluginInstance<"time", Config$4, State$4, Api$3, {}> & Record<never, never>) | (import("@moku-labs/core").PluginInstance<"lifecycle", Config$2, State$2, Api$2, {
1472
+ }> & Record<never, never>) | ExtraPlugins[number], [...ExtraPlugins], import("@moku-labs/core").CoreApisFromTuple<[import("@moku-labs/core").CorePluginInstance<"log", import("@moku-labs/common/browser").LogConfig, import("@moku-labs/common/browser").LogState, import("@moku-labs/common/browser").LogApi>, import("@moku-labs/core").CorePluginInstance<"env", import("@moku-labs/common/browser").EnvConfig, import("@moku-labs/common/browser").EnvState, import("@moku-labs/common/browser").EnvApi>]>> | undefined) => import("@moku-labs/core").App<Config$7, Events, (import("@moku-labs/core").PluginInstance<"time", Config$5, State$5, Api$4, {}> & Record<never, never>) | (import("@moku-labs/core").PluginInstance<"lifecycle", Config$2, State$2, Api$2, {
260
1473
  "lifecycle:changed": {
261
1474
  reason: PauseReason;
262
1475
  action: "push" | "pop";
@@ -302,26 +1515,155 @@ declare const createApp: <const ExtraPlugins extends readonly import("@moku-labs
302
1515
  *
303
1516
  * @example
304
1517
  * ```ts
305
- * export const scorePlugin = createPlugin("score", { api: createScoreApi });
1518
+ * // A game plugin that writes every edge of the graph to the log.
1519
+ * export const edgeLog = createPlugin("edgeLog", {
1520
+ * depends: [flowPlugin],
1521
+ * hooks: ctx => ({ "flow:edge": ({ node, outcome }) => ctx.log.info("edge", { node, outcome }) })
1522
+ * });
1523
+ * ```
1524
+ */
1525
+ declare const createPlugin: import("@moku-labs/core").BoundCreatePluginFunction<Config$7, Events, import("@moku-labs/core").CoreApisFromTuple<[import("@moku-labs/core").CorePluginInstance<"log", import("@moku-labs/common/browser").LogConfig, import("@moku-labs/common/browser").LogState, import("@moku-labs/common/browser").LogApi>, import("@moku-labs/core").CorePluginInstance<"env", import("@moku-labs/common/browser").EnvConfig, import("@moku-labs/common/browser").EnvState, import("@moku-labs/common/browser").EnvApi>]>>;
1526
+ /**
1527
+ * Binds the authoring helpers to the types of one game: "createApp for a game". Each plugin binds
1528
+ * its own helpers (`flowFor`, `projectionFor`, `componentsFor`, `bundlesFor`, `scenesFor`); this
1529
+ * only spreads them, so the kit's type is inferred and never written by hand. At run time these
1530
+ * are the same functions and component objects the plugins export.
1531
+ *
1532
+ * @returns The helpers typed with the game's `player`, `session`, `assets`, `bundles`, `strings`
1533
+ * and `emitters`.
1534
+ * @example
1535
+ * ```ts
1536
+ * // kit.ts of a game: bound once, imported by every node, flow and view file.
1537
+ * export const { defineNode, defineFlow, defineFeature, projection, sprite, Sprite, defineBundles, load, defineScene } =
1538
+ * defineGame<{ player: Player; session: Session; assets: AssetKey; bundles: BundleKey; strings: StringTable }>();
306
1539
  * ```
307
1540
  */
308
- declare const createPlugin: import("@moku-labs/core").BoundCreatePluginFunction<Config$5, Events, import("@moku-labs/core").CoreApisFromTuple<[import("@moku-labs/core").CorePluginInstance<"log", import("@moku-labs/common/browser").LogConfig, import("@moku-labs/common/browser").LogState, import("@moku-labs/common/browser").LogApi>, import("@moku-labs/core").CorePluginInstance<"env", import("@moku-labs/common/browser").EnvConfig, import("@moku-labs/common/browser").EnvState, import("@moku-labs/common/browser").EnvApi>]>>;
1541
+ declare function defineGame<Types extends GameTypes>(): {
1542
+ defineEmitter: (id: EmitterIdOf<Types>, config: Omit<EmitterConfig, "textures"> & {
1543
+ readonly textures: readonly Types["assets"][];
1544
+ }) => EmitterDefinition;
1545
+ Emitter: Narrowed<EmitterValue, {
1546
+ effect?: EmitterIdOf<Types>;
1547
+ active?: boolean;
1548
+ }>;
1549
+ Displacement: Narrowed<DisplacementValue, Partial<Omit<DisplacementValue, "map">> & {
1550
+ map?: Types["assets"];
1551
+ }>;
1552
+ music: MusicDescriptor<Types["assets"]>;
1553
+ defineComponent: typeof defineComponent;
1554
+ defineStyle: <const Given extends Style<Types["assets"]>>(style: Given) => Readonly<Given>;
1555
+ defineTokens: typeof defineTokens;
1556
+ popup: <Properties extends object, Local extends object>(component: PopupComponent<Properties, Local>, props: Properties, options?: {
1557
+ over?: string;
1558
+ }) => Descriptor;
1559
+ intrinsics: IntrinsicElementsFor<Types["assets"], TextStylesOf<Types>, Extract<keyof Types["strings"], string>>;
1560
+ label: (options: Omit<LabelOptions, "style"> & {
1561
+ style: TextStylesOf<Types>;
1562
+ }) => ReturnType<typeof label>;
1563
+ defineTextStyles: (map: Record<string, Omit<TextStyleInput, "bold" | "font" | "italic"> & {
1564
+ font: Types["assets"];
1565
+ } & {
1566
+ bold?: Types["assets"];
1567
+ italic?: Types["assets"];
1568
+ }>) => TextStyles;
1569
+ tr: TypedTr<Types["strings"]>;
1570
+ defineAnimation: typeof defineAnimation;
1571
+ frames: (target: Target, options: {
1572
+ keys: readonly Types["assets"][];
1573
+ fps: number;
1574
+ loop?: boolean;
1575
+ }) => Step;
1576
+ sfx: (key: Types["assets"], options?: {
1577
+ bus?: string;
1578
+ }) => SfxDescriptor;
1579
+ play: typeof play;
1580
+ Frames: Narrowed<FramesValue, Partial<Omit<FramesValue, "keys">> & {
1581
+ keys?: readonly Types["assets"][];
1582
+ }>;
1583
+ defineScene: DefineScene<Types["assets"], BundlesOf<Types>>;
1584
+ defineBundles: DefineBundles<BundlesOf<Types>>;
1585
+ load: LoadBundles<BundlesOf<Types>>;
1586
+ sprite: (options: Omit<SpriteOptions, "texture"> & {
1587
+ texture: Types["assets"];
1588
+ }) => ReturnType<typeof sprite>;
1589
+ Sprite: Narrowed<SpriteValue, Partial<Omit<SpriteValue, "texture">> & {
1590
+ texture?: Types["assets"];
1591
+ }>;
1592
+ NineSlice: Narrowed<NineSliceValue, Partial<Omit<NineSliceValue, "texture">> & {
1593
+ texture?: Types["assets"];
1594
+ }>;
1595
+ projection: ProjectionFor<Types["player"], Types["session"]>;
1596
+ defineNode: DefineNode<{
1597
+ player: Types["player"];
1598
+ session: Types["session"];
1599
+ scenes: SceneIdOf<Types>;
1600
+ }>;
1601
+ defineFlow: typeof defineFlow;
1602
+ defineFeature: (name: string, description: FeatureDescription) => FeaturePlugin;
1603
+ };
309
1604
  /**
310
- * Binds the authoring helpers to the types of one game: "createApp for a game". The binding is
311
- * type-only; at run time these are the same functions the plugins export. One line per helper,
312
- * no logic: anything a single plugin can own lives in that plugin.
1605
+ * The screen plugins, in dependency order. A game with a screen spreads them into `plugins`;
1606
+ * a headless test leaves them out. V2: `world`, `renderer`, `input`, `assets`, `scenes`. V3 appends `anim`, `i18n`, `text`, `ui`; `audio` and `effects` stay opt-in: `[...screen, audioPlugin, effectsPlugin]`.
313
1607
  *
314
- * @returns The helpers typed with the game's `player` and `session`.
315
1608
  * @example
316
1609
  * ```ts
317
- * export const { defineNode, defineFlow, defineFeature } = defineGame<{
318
- * player: Player;
319
- * session: Session;
320
- * assets: AssetKey;
321
- * strings: StringTable;
322
- * }>();
1610
+ * createApp({ plugins: [...screen, boardFeature], pluginConfigs: { renderer: { mount: "#game" } } });
323
1611
  * ```
324
1612
  */
325
- declare function defineGame<Types extends GameTypes>(): Kit<Types>;
1613
+ declare const screen: readonly [import("@moku-labs/core").PluginInstance<"world", Config$6, State$6, Api$5, {
1614
+ "world:reconciled": {
1615
+ mode: "play" | "direct";
1616
+ projections: number;
1617
+ entered: number;
1618
+ changed: number;
1619
+ exited: number;
1620
+ revived: number;
1621
+ queued: number;
1622
+ hintsRouted: number;
1623
+ hintsDropped: number;
1624
+ };
1625
+ }> & Record<never, never>, import("@moku-labs/core").PluginInstance<"renderer", Config$4, State$4, Api$3, {
1626
+ "renderer:device-lost": {
1627
+ kind: "webgpu" | "webgl";
1628
+ reason: string;
1629
+ };
1630
+ }> & Record<never, never>, import("@moku-labs/core").PluginInstance<"input", Config$10, State$9, InputApi, {}> & Record<never, never>, import("@moku-labs/core").PluginInstance<"assets", Config$8, State$7, Api$6, {
1631
+ "assets:bundle-loaded": {
1632
+ bundle: string;
1633
+ tier: Tier;
1634
+ mb: number;
1635
+ reason: LoadReason;
1636
+ };
1637
+ "assets:bundle-progress": {
1638
+ bundle: string;
1639
+ loaded: number;
1640
+ total: number;
1641
+ };
1642
+ "assets:bundle-unloaded": {
1643
+ bundle: string;
1644
+ tier: Tier;
1645
+ mb: number;
1646
+ reason: "budget" | "request";
1647
+ keys: readonly string[];
1648
+ };
1649
+ }> & Record<never, never>, import("@moku-labs/core").PluginInstance<"scenes", Record<string, never>, State$15, ScenesApi, {
1650
+ "scenes:changed": {
1651
+ from: string | undefined;
1652
+ to: string;
1653
+ music: string | undefined;
1654
+ };
1655
+ }> & Record<never, never>, import("@moku-labs/core").PluginInstance<"anim", Config$11, State$10, AnimApi, {
1656
+ "anim:mark": {
1657
+ animation: string;
1658
+ mark: string;
1659
+ };
1660
+ "anim:finished": {
1661
+ animation: string;
1662
+ };
1663
+ }> & Record<never, never>, import("@moku-labs/core").PluginInstance<"i18n", Config$9, State$8, I18nApi, {
1664
+ "i18n:locale-changed": {
1665
+ locale: string;
1666
+ };
1667
+ }> & Record<never, never>, import("@moku-labs/core").PluginInstance<"text", Config$12, State$11, TextApi, {}> & Record<never, never>, import("@moku-labs/core").PluginInstance<"ui", Config$13, State$12, UiApi, {}> & Record<never, never>];
326
1668
  //#endregion
327
- export { types_d_exports as Clock, types_d_exports$1 as Flow, types_d_exports$2 as Lifecycle, types_d_exports$3 as Model, SaveUnreadableError, types_d_exports$4 as Time, clockPlugin, createApp, createPlugin, defineFeature, defineGame, exit, flowPlugin, guide, hint, lifecyclePlugin, modelPlugin, schedule, slot, teardown, timePlugin, to, type };
1669
+ export { Alpha, types_d_exports as Anim, Animation, types_d_exports$1 as Assets, types_d_exports$2 as Audio, Blur, Box, types_d_exports$3 as Clock, ColorMatrix, type DescriptionNode, Displacement, Display, Draggable, DropTarget, types_d_exports$4 as Effects, Emitter, Exiting, types_d_exports$5 as Flow, Frames, Glow, Held, Hovered, types_d_exports$6 as I18n, types_d_exports$7 as Input, Layer, types_d_exports$8 as Lifecycle, LocalWrite, types_d_exports$9 as Model, NineSlice, Noise, Order, Outline, Parent, Pointer, PointerOver, Pressable, Pressed, types_d_exports$10 as Renderer, SaveUnreadableError, types_d_exports$11 as Scenes, Shape, Sprite, Swipeable, Tappable, Text, types_d_exports$12 as TextTypes, types_d_exports$13 as Time, Touchable, Transform, Tree, types_d_exports$14 as Ui, types_d_exports$15 as World, animPlugin, assetsPlugin, audioPlugin, clockPlugin, component, createApp, createPlugin, defineAnimation, defineBundles, defineComponent, defineEmitter, defineFeature, defineFilter, defineGame, defineMotion, defineScene, defineStyle, defineTextStyles, defineTokens, effectsPlugin, exit, external, flowPlugin, frames, guide, haptic, hint, i18nPlugin, inputPlugin, label, lifecyclePlugin, load, mark, modelPlugin, music, mut, parallel, play, popup, projection, rendererPlugin, resolve, resource, scenesPlugin, schedule, screen, sequence, set, sfx, slot, spawn, spawned, sprite, stagger, system, tag, textPlugin, timePlugin, to, tr, tween, type, uiPlugin, use, wait, worldPlugin };