@moku-labs/game 0.7.1 → 0.9.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 (40) hide show
  1. package/README.md +12 -8
  2. package/dist/assets.d.mts +97 -33
  3. package/dist/assets.mjs +350 -110
  4. package/dist/{commands-mzE9PX0H.d.mts → commands-DuwCxK5t.d.mts} +5 -4
  5. package/dist/control.d.mts +4 -3
  6. package/dist/control.mjs +2 -2
  7. package/dist/{define-BbgXv4cy.d.mts → define-B6pHpdBi.d.mts} +4 -3
  8. package/dist/headless-BsNdJs9W.mjs +186 -0
  9. package/dist/headless-SCQXJxQf.d.mts +181 -0
  10. package/dist/hot.d.mts +4 -2
  11. package/dist/hot.mjs +8 -6
  12. package/dist/index-BO9kZzY3.d.mts +1611 -0
  13. package/dist/index.d.mts +6 -1596
  14. package/dist/index.mjs +4 -34334
  15. package/dist/{input-CK4nxBO1.mjs → input-z_35Jwfn.mjs} +2 -2
  16. package/dist/inspect.d.mts +7 -6
  17. package/dist/inspect.mjs +2 -2
  18. package/dist/jsx-dev-runtime.d.mts +1 -1
  19. package/dist/jsx-runtime.d.mts +1 -1
  20. package/dist/lint.d.mts +55 -4
  21. package/dist/lint.mjs +993 -40
  22. package/dist/project.d.mts +57 -41
  23. package/dist/project.mjs +994 -505
  24. package/dist/{run-Bcyg32M5.mjs → run-CofJEdYx.mjs} +4 -187
  25. package/dist/{runtime-HTlmRb81.d.mts → runtime-CdNJ0UBm.d.mts} +1 -1
  26. package/dist/{sources-DVWJ8nfr.mjs → sources-DNnzyCYf.mjs} +2 -2
  27. package/dist/src-536arXjV.mjs +34644 -0
  28. package/dist/testing.d.mts +85 -283
  29. package/dist/testing.mjs +158 -2109
  30. package/dist/{types-BMEn0tSC.d.mts → types-B0v5VVa3.d.mts} +10 -9
  31. package/dist/{types-ClF8say0.d.mts → types-CQueODzo.d.mts} +2 -2
  32. package/dist/{types-CYYKjvLn.d.mts → types-DUMoYPYe.d.mts} +5 -182
  33. package/dist/{types-CXyipKhE.d.mts → types-VZVWwqCg.d.mts} +42 -4
  34. package/dist/{types-BaUUcIaN.d.mts → types-xXfyk56Q.d.mts} +1 -1
  35. package/dist/{visual-DUvvPPCt.mjs → visual-dMW8iqAE.mjs} +8 -4
  36. package/dist/visual.d.mts +330 -0
  37. package/dist/visual.mjs +2142 -0
  38. package/llms.txt +59 -21
  39. package/package.json +7 -8
  40. package/dist/memory-CvgdnsQO.mjs +0 -259
@@ -2741,7 +2741,7 @@ declare function to<const TargetName extends string, Payload, Output>(target: Ta
2741
2741
  */
2742
2742
  declare function slot(name: string): SlotNode;
2743
2743
  declare namespace types_d_exports$3 {
2744
- export { Allow, Answer, AnyFlow, AnyNode, AnyNodeContext, Api$3 as Api, Bookmark, BundlesOf, CheckedEdges, Config$3 as Config, Contribution, DefineNode, Deps$3 as Deps, Descriptor, Edges, EmitterIdOf, EnterCallback, Events$3 as Events, Exit, FeatureDescription, FeatureEmitter, FeatureFilter, FeaturePlugin, FeaturesApi, FlowCtx, FlowDefinition, FlowGraph, FlowKit, FlowSpec, FlowState, FxApi, FxHandler, GameState, GameTypes, GateApi, GraphError, GraphNode, GuideOptions, Hint, InboxApi, JournalEntry, KernelSlice$3 as KernelSlice, LifecycleChanged, Mapped, NodeContext, NodeDefinition, NodeFx, NodeInfo, NodeSpec, OutcomeTags, Position, Result, RouteStep, RunnerApi, SceneIdOf, SlotNode, Stage, State$3 as State, StringTableLike, Target, TextStylesOf, TypeTag, WorldEvent };
2744
+ export { Allow, Answer, AnyFlow, AnyNode, AnyNodeContext, Api$3 as Api, Bookmark, BundlesOf, CheckedEdges, Config$3 as Config, Contribution, DefineNode, Deps$3 as Deps, Descriptor, Edges, EmitterIdOf, EnterCallback, Events$3 as Events, Exit, FeatureDescription, FeatureEmitter, FeatureFilter, FeaturePlugin, FeaturesApi, FlowCtx, FlowDefinition, FlowEntry, FlowGraph, FlowKit, FlowSpec, FlowState, FxApi, FxHandler, GameState, GameTypes, GateApi, GraphError, GraphNode, GuideOptions, Hint, InboxApi, JournalEntry, KernelSlice$3 as KernelSlice, LifecycleChanged, Mapped, NodeContext, NodeDefinition, NodeFx, NodeInfo, NodeSpec, NodeTable, OutcomeTags, Position, Result, RouteStep, RunnerApi, SceneIdOf, SlotNode, Stage, State$3 as State, StringTableLike, Target, TextStylesOf, TypeTag, WorldEvent };
2745
2745
  }
2746
2746
  /**
2747
2747
  * flow plugin events.
@@ -3983,8 +3983,8 @@ type ComponentHandle<Value extends object> = {
3983
3983
  */
3984
3984
  type Narrowed<Value extends object, Patch> = Omit<ComponentType<Value>, never> & ((patch?: Patch) => ComponentValue<Value>);
3985
3985
  /**
3986
- * A resource type made by `resource()`: one mutable object per world, created from a deep clone
3987
- * of the defaults on first read.
3986
+ * A resource type made by `resource()`: one mutable object per world, made on first read from a
3987
+ * deep clone of the plain defaults, or by one call of the factory default.
3988
3988
  *
3989
3989
  * @example
3990
3990
  * ```ts
@@ -3993,8 +3993,8 @@ type Narrowed<Value extends object, Patch> = Omit<ComponentType<Value>, never> &
3993
3993
  * ```
3994
3994
  */
3995
3995
  type ResourceType<Value extends object> = {
3996
- readonly resourceName: string;
3997
- readonly defaults: Readonly<Value>;
3996
+ readonly resourceName: string; /** The frozen plain defaults, or the factory a world calls once on first read. */
3997
+ readonly defaults: Readonly<Value> | (() => Value);
3998
3998
  };
3999
3999
  /**
4000
4000
  * A query term marked as written by `mut()`. Its only effect is change detection.
@@ -4061,7 +4061,8 @@ type QueryTuple<Terms extends readonly QueryTerm[]> = [Entity, ...QueryValues<Te
4061
4061
  /**
4062
4062
  * What a system receives next to its entities: the world, the mutable resources, the frozen model
4063
4063
  * snapshot of the frame and the frame's `Time`. A system may read pure game rules; it never
4064
- * writes the model.
4064
+ * writes the model. A system plays animations through `res(AnimPlayer)`, the resource `anim`
4065
+ * writes when it starts.
4065
4066
  *
4066
4067
  * @example
4067
4068
  * ```ts
@@ -4524,8 +4525,8 @@ type EcsApi = {
4524
4525
  */
4525
4526
  query<const Terms extends readonly QueryTerm[]>(...terms: Terms): Iterable<QueryTuple<Terms>>;
4526
4527
  /**
4527
- * Returns the one mutable value of a resource, created from a deep clone of its defaults on
4528
- * first read.
4528
+ * Returns the one mutable value of a resource, made on first read: a deep clone of its plain
4529
+ * defaults, or what its factory default returns, called once per world.
4529
4530
  *
4530
4531
  * @param resourceType - The resource type.
4531
4532
  * @returns The mutable resource value.
@@ -7180,4 +7181,4 @@ type FlowRest = {
7180
7181
  checkpoint: boolean;
7181
7182
  };
7182
7183
  //#endregion
7183
- export { Ease as $, SaveDoc as $t, Config$2 as A, JournalEntry as At, ComponentValue as B, State$4 as Bt, Captured as C, to as Ct, Point as D, DefineNode as Dt, FilterSlot as E, Bookmark as Et, AnyComponentType as F, GuideOptions as Ft, Narrowed as G, Events$4 as Gt, EntitySnapshot as H, types_d_exports$4 as Ht, AnyComponentValue as I, Hint as It, ResourceType as J, State$5 as Jt, Owner as K, Json as Kt, ComponentHandle as L, Answer as Lt, State$2 as M, SceneIdOf as Mt, types_d_exports$2 as N, TypeTag as Nt, ViewportSize as O, FlowGraph as Ot, AnyComponent as P, Descriptor as Pt, DescriptionNode as Q, ProviderCall as Qt, ComponentSchema as R, Api$4 as Rt, types_d_exports$1 as S, slot as St, DebugSwitches as T, FeatureDescription as Tt, FrameDiff as U, Api$5 as Ut, Entity as V, Time as Vt, Mut as W, Config$5 as Wt, TagType as X, Patch as Xt, SystemDefinition as Y, types_d_exports$5 as Yt, ChangeHook as Z, PlayerStateProvider as Zt, PixiModule as _, Events$6 as _n, State$3 as _t, DefineBundles as a, Api$6 as an, ProjectionMotion as at, State$1 as b, defineFlow as bt, LoadReason as c, PauseReason as cn, TrackSegment as ct, Tier as d, Api$7 as dn, BundlesOf as dt, SaveUnreadableError as en, LayerSort as et, Usage as f, Config$7 as fn, Config$3 as ft, PixiFilter as g, Config$8 as gn, Position as gt, Config$1 as h, types_d_exports$7 as hn, GameTypes as ht, Config as i, RngState as in, NumericFields as it, Explained as j, RouteStep as jt, Api$2 as k, FlowState as kt, Manifest as l, State$6 as ln, ViewHandle as lt, Api$1 as m, State$7 as mn, FeaturePlugin as mt, BundleMap as n, StoreApi as nn, Motion as nt, Events as o, Config$6 as on, ProjectionSpec as ot, types_d_exports as p, FakeClock as pn, EmitterIdOf as pt, QueryTerm as q, Root as qt, BundleSpec as r, RngApi as rn, MotionHandle as rt, LoadBundles as s, Events$5 as sn, TrackOptions as st, Api as t, Snapshot as tn, LayerSpec as tt, State as u, types_d_exports$6 as un, Api$3 as ut, PixiSprite as v, Require as vn, TextStylesOf as vt, RenderStats as w, type as wt, Under as x, exit as xt, PixiTexture as y, types_d_exports$3 as yt, ComponentType as z, Config$4 as zt };
7184
+ export { Ease as $, PlayerStateProvider as $t, Config$2 as A, FlowState as At, ComponentValue as B, Api$4 as Bt, Captured as C, to as Ct, Point as D, Bookmark as Dt, FilterSlot as E, AnyFlow as Et, AnyComponentType as F, TypeTag as Ft, Narrowed as G, Api$5 as Gt, EntitySnapshot as H, State$4 as Ht, AnyComponentValue as I, Descriptor as It, ResourceType as J, Json as Jt, Owner as K, Config$5 as Kt, ComponentHandle as L, GuideOptions as Lt, State$2 as M, NodeTable as Mt, types_d_exports$2 as N, RouteStep as Nt, ViewportSize as O, DefineNode as Ot, AnyComponent as P, SceneIdOf as Pt, DescriptionNode as Q, Patch as Qt, ComponentSchema as R, Hint as Rt, types_d_exports$1 as S, slot as St, DebugSwitches as T, FeatureDescription as Tt, FrameDiff as U, Time as Ut, Entity as V, Config$4 as Vt, Mut as W, types_d_exports$4 as Wt, TagType as X, State$5 as Xt, SystemDefinition as Y, Root as Yt, ChangeHook as Z, types_d_exports$5 as Zt, PixiModule as _, State$7 as _n, State$3 as _t, DefineBundles as a, RngApi as an, ProjectionMotion as at, State$1 as b, Events$6 as bn, defineFlow as bt, LoadReason as c, Config$6 as cn, TrackSegment as ct, Tier as d, State$6 as dn, BundlesOf as dt, ProviderCall as en, LayerSort as et, Usage as f, types_d_exports$6 as fn, Config$3 as ft, PixiFilter as g, FakeClock as gn, Position as gt, Config$1 as h, Config$7 as hn, GameTypes as ht, Config as i, StoreApi as in, NumericFields as it, Explained as j, JournalEntry as jt, Api$2 as k, FlowGraph as kt, Manifest as l, Events$5 as ln, ViewHandle as lt, Api$1 as m, ClockSource as mn, FeaturePlugin as mt, BundleMap as n, SaveUnreadableError as nn, Motion as nt, Events as o, RngState as on, ProjectionSpec as ot, types_d_exports as p, Api$7 as pn, EmitterIdOf as pt, QueryTerm as q, Events$4 as qt, BundleSpec as r, Snapshot as rn, MotionHandle as rt, LoadBundles as s, Api$6 as sn, TrackOptions as st, Api as t, SaveDoc as tn, LayerSpec as tt, State as u, PauseReason as un, Api$3 as ut, PixiSprite as v, types_d_exports$7 as vn, TextStylesOf as vt, RenderStats as w, type as wt, Under as x, Require as xn, exit as xt, PixiTexture as y, Config$8 as yn, types_d_exports$3 as yt, ComponentType as z, Answer as zt };
@@ -1,5 +1,5 @@
1
- import { D as Point, E as FilterSlot, L as ComponentHandle, V as Entity, _ as PixiModule, _n as Events$1, g as PixiFilter, k as Api$1, m as Api$2, o as Events, t as Api$3, ut as Api, v as PixiSprite, vn as Require, y as PixiTexture, z as ComponentType } from "./types-BMEn0tSC.mjs";
2
- import { b as AnimApi } from "./types-CXyipKhE.mjs";
1
+ import { D as Point, E as FilterSlot, L as ComponentHandle, V as Entity, _ as PixiModule, bn as Events$1, g as PixiFilter, k as Api$1, m as Api$2, o as Events, t as Api$3, ut as Api, v as PixiSprite, xn as Require, y as PixiTexture, z as ComponentType } from "./types-B0v5VVa3.mjs";
2
+ import { b as AnimApi } from "./types-VZVWwqCg.mjs";
3
3
  import { Log } from "@moku-labs/common/browser";
4
4
  import { PluginCtx } from "@moku-labs/core";
5
5
 
@@ -1,184 +1,7 @@
1
- import { At as JournalEntry, Kt as Json, Lt as Answer, Rt as Api, in as RngState, jt as RouteStep, kt as FlowState, tn as Snapshot, ut as Api$1 } from "./types-BMEn0tSC.mjs";
1
+ import { Jt as Json, rn as Snapshot } from "./types-B0v5VVa3.mjs";
2
+ import { t as HeadlessApp } from "./headless-SCQXJxQf.mjs";
2
3
  import { Log } from "@moku-labs/common/browser";
3
4
 
4
- //#region src/plugins/flow/headless.d.ts
5
- /**
6
- * The part of an app the headless helpers use. Structural on purpose: any app created with the
7
- * default plugins fits.
8
- *
9
- * @example
10
- * ```ts
11
- * const app: HeadlessApp = createApp({ pluginConfigs: { flow: { mainFlow } } });
12
- * ```
13
- */
14
- type HeadlessApp = {
15
- start(): Promise<void>;
16
- stop(): Promise<void>;
17
- time: Api;
18
- flow: Api$1;
19
- };
20
- /**
21
- * A game played without a screen: answers go through the gate, effects resolve instantly.
22
- *
23
- * @example
24
- * ```ts
25
- * // A test of the dice game: two rolls, no screen, no waiting.
26
- * const game = await createHeadless(app);
27
- * const state = await game.walk([{ at: "home", intent: "roll" }, { at: "home", intent: "roll" }]);
28
- *
29
- * state.path; // "home"
30
- * await game.stop();
31
- * ```
32
- */
33
- type HeadlessGame = {
34
- /**
35
- * Walks a route through the running loop.
36
- *
37
- * @param route - The player's answers and substituted sub-flow results, in order.
38
- * @returns The state the walk ended in.
39
- * @throws {Error} When a step is never reached, or the loop failed fatally.
40
- * @example
41
- * ```ts
42
- * // Open the board and tap the generator twice. Every node's logic runs for real.
43
- * const tap = { at: "board/awaitIntent", intent: "tap", payload: { generatorId: "sawmill" } };
44
- * const state = await game.walk([{ at: "home", intent: "play" }, tap, tap]);
45
- *
46
- * state.path; // "board/awaitIntent"
47
- * ```
48
- */
49
- walk(route: readonly RouteStep[]): Promise<FlowState>;
50
- /**
51
- * Gives one answer to the gate, the way a tap does.
52
- *
53
- * @param answer - Intent and optional payload.
54
- * @returns Whether the gate took it.
55
- * @example
56
- * ```ts
57
- * // One tap without a route. The test then waits for the loop by itself.
58
- * game.answer({ intent: "play" }); // true: "home" rests and lists "play"
59
- * ```
60
- */
61
- answer(answer: Answer): boolean;
62
- /**
63
- * Reads where the graph stands.
64
- *
65
- * @returns Whether it runs, the path, the stack, what it waits for and the mode.
66
- * @example
67
- * ```ts
68
- * // Right after createHeadless: the graph rests at its first rest node.
69
- * game.state().path; // "home"
70
- * game.state().pending; // { gate: ["play"] }
71
- * ```
72
- */
73
- state(): FlowState;
74
- /**
75
- * Reads the edges taken since the last checkpoint.
76
- *
77
- * @returns A copy of the journal.
78
- * @example
79
- * ```ts
80
- * // After a merge onto an empty cell: the last edge says why the move was refused.
81
- * const last = game.history().at(-1);
82
- * // last?.path: "board/merge", last?.outcome: "rejected", last?.payload: { reason: "empty" }
83
- * ```
84
- */
85
- history(): readonly JournalEntry[];
86
- /**
87
- * Stops the app, then re-throws a fatal error of the loop.
88
- *
89
- * @throws {Error} When the loop failed fatally.
90
- * @example
91
- * ```ts
92
- * // The last line of a headless test. A fatal error of the loop fails the test here.
93
- * await game.stop();
94
- * ```
95
- */
96
- stop(): Promise<void>;
97
- };
98
- /**
99
- * A reproducible run: a starting state, an optional checkpoint and the route played from it.
100
- *
101
- * @example
102
- * ```ts
103
- * const repro: Repro = { player: { coins: 0 }, checkpoint: "home", route: [{ at: "home", intent: "play" }] };
104
- * ```
105
- */
106
- type Repro = {
107
- player: Json;
108
- session?: Json;
109
- rng?: RngState;
110
- checkpoint?: string;
111
- route: RouteStep[];
112
- };
113
- /**
114
- * What a repro run ends with: the path where the loop rests and the committed state.
115
- *
116
- * @example
117
- * ```ts
118
- * const result: ReproResult = { path: ["home"], player: { coins: 3 }, session: { visits: 1 } };
119
- * ```
120
- */
121
- type ReproResult = {
122
- path: string[];
123
- player: Json;
124
- session: Json;
125
- };
126
- /**
127
- * Starts an app headless: sets flow mode `"fast"`, awaits `app.start()`, starts `flow.run()`
128
- * unless the app's own `onStart` already did, and waits until the loop rests for the first time.
129
- * `game.state().path` therefore names the first rest node of the graph as soon as the call
130
- * resolves. A fatal error of the loop rejects this call, and a later one is re-thrown by `walk`
131
- * and by `stop`, so a headless test never loses it.
132
- *
133
- * @param app - An app that is not started yet.
134
- * @returns The game: walk it, answer it, read it, stop it.
135
- * @throws {Error} When the loop failed fatally before it reached its first rest node.
136
- * @example
137
- * ```ts
138
- * // A test starts the merge game without a screen. The call resolves at the first rest node.
139
- * const game = await createHeadless(app);
140
- *
141
- * game.state().path; // "home": the transit node "boot" was played out
142
- * game.state().mode; // "fast"
143
- * await game.stop();
144
- * ```
145
- */
146
- declare function createHeadless(app: HeadlessApp): Promise<HeadlessGame>;
147
- /**
148
- * Plays a repro: starts the app headless, restores the bookmark built from its state and
149
- * checkpoint, then walks its route. The app keeps running, so the caller can read more than the
150
- * result and stops it itself.
151
- *
152
- * @param app - An app that is not started yet.
153
- * @param repro - Starting state, optional checkpoint and the route.
154
- * @returns The path the run ended at, and the committed state.
155
- * @example
156
- * ```ts
157
- * // A bug report on the dice game: "reset keeps my coins". Replay it from the "home" checkpoint.
158
- * const reset = { at: "home", intent: "reset" };
159
- * const result = await runRepro(app, { player: { coins: 7 }, checkpoint: "home", route: [reset] });
160
- *
161
- * result.player; // { coins: 0 }; result.path is ["home"]
162
- * await app.stop(); // the app keeps running after the run
163
- * ```
164
- */
165
- declare function runRepro(app: HeadlessApp, repro: Repro): Promise<ReproResult>;
166
- /**
167
- * Steps the frame loop by hand: `count` calls of `app.time.step(deltaMs)`. A headless game has no
168
- * frame source, so this is the only thing that moves the frame phases.
169
- *
170
- * @param app - A started app.
171
- * @param count - Number of frames.
172
- * @param deltaMs - Milliseconds per frame.
173
- * @example
174
- * ```ts
175
- * // A headless game has no frame source: the test moves the six frame phases by hand.
176
- * stepFrames(app, 2, 16);
177
- * app.time.snapshot(); // frame: 2, delta: 16, elapsed: 32
178
- * ```
179
- */
180
- declare function stepFrames(app: HeadlessApp, count: number, deltaMs: number): void;
181
- //#endregion
182
5
  //#region src/plugins/flow/doors/types.d.ts
183
6
  /**
184
7
  * The kinds an input field can take. `json` is any JSON value the command checks itself.
@@ -276,7 +99,7 @@ type Effect = "read" | "route" | "cosmetic" | "cheat" | "raw";
276
99
  * ```ts
277
100
  * // A game's .dev module: the coins a panel shows, re-read on every commit.
278
101
  * const coins = defineSource({
279
- * id: "timber.coins",
102
+ * id: "mini.coins",
280
103
  * title: "Coins",
281
104
  * input: {},
282
105
  * changes: "commit",
@@ -300,7 +123,7 @@ type Source<S extends InputSchema, O, App = HeadlessApp> = {
300
123
  * ```ts
301
124
  * // A game's .dev module: jump to a level through the graph, the session stays clean.
302
125
  * const jumpToLevel = defineCommand({
303
- * id: "timber.jumpToLevel",
126
+ * id: "mini.jumpToLevel",
304
127
  * title: "Go to level",
305
128
  * input: { level: "number" },
306
129
  * effect: "route",
@@ -389,4 +212,4 @@ type ControlApp = HeadlessApp & {
389
212
  readonly log: Log.LogApi;
390
213
  };
391
214
  //#endregion
392
- export { stepFrames as _, InputOf as a, Source as c, HeadlessApp as d, HeadlessGame as f, runRepro as g, createHeadless as h, InputArguments as i, WatchApp as l, ReproResult as m, Command as n, InputSchema as o, Repro as p, ControlApp as r, Ran as s, CheatEntry as t, WatchInput as u };
215
+ export { InputOf as a, Source as c, InputArguments as i, WatchApp as l, Command as n, InputSchema as o, ControlApp as r, Ran as s, CheatEntry as t, WatchInput as u };
@@ -1,5 +1,5 @@
1
- import { $ as Ease, Gt as Events$3, I as AnyComponentValue, K as Owner, Kt as Json, L as ComponentHandle, Nt as TypeTag, P as AnyComponent, Pt as Descriptor, Rt as Api, Ut as Api$5, V as Entity, _n as Events$2, ct as TrackSegment, et as LayerSort, it as NumericFields, k as Api$2, m as Api$3, o as Events$5, rt as MotionHandle, sn as Events$4, st as TrackOptions, t as Api$4, tt as LayerSpec, ut as Api$1, vn as Require, z as ComponentType } from "./types-BMEn0tSC.mjs";
2
- import { D as TextApi, H as InputApi, I as I18nApi, c as UiNode, i as Finding, n as ElementChange, o as JsxState, r as ElementMotion, v as StylesState } from "./types-BaUUcIaN.mjs";
1
+ import { $ as Ease, Bt as Api, Ft as TypeTag, Gt as Api$5, I as AnyComponentValue, It as Descriptor, Jt as Json, K as Owner, L as ComponentHandle, P as AnyComponent, V as Entity, bn as Events$2, ct as TrackSegment, et as LayerSort, it as NumericFields, k as Api$2, ln as Events$4, m as Api$3, o as Events$5, qt as Events$3, rt as MotionHandle, st as TrackOptions, t as Api$4, tt as LayerSpec, ut as Api$1, xn as Require, z as ComponentType } from "./types-B0v5VVa3.mjs";
2
+ import { D as TextApi, H as InputApi, I as I18nApi, c as UiNode, i as Finding, n as ElementChange, o as JsxState, r as ElementMotion, v as StylesState } from "./types-xXfyk56Q.mjs";
3
3
  import { Log } from "@moku-labs/common/browser";
4
4
  import { PluginCtx } from "@moku-labs/core";
5
5
  import { Node, Yoga } from "yoga-layout/load";
@@ -632,7 +632,7 @@ type RunningTimeline = {
632
632
  ended: boolean;
633
633
  };
634
634
  declare namespace types_d_exports$3 {
635
- export { AnimApi, AnimCtx, AnimationDefinition, AnimationSpec, AnyAnimationDefinition, BuildTools, Config$3 as Config, Cursor, Deps$3 as Deps, ErasedFields, Events$1 as Events, ExternalPlayer, FrameLoop, FramesValue, FxStep, HapticDescriptor, HapticKind, KernelSlice$3 as KernelSlice, MarkListener, MotionKeyframe, PlayDescriptor, PlayHandle, Pose, RunningTimeline, SfxDescriptor, SlotRecord, SlotTags, SlotValue, SlotValues, State$3 as State, Step, StepMotion, Target, TimelineRuntime, Track };
635
+ export { AnimApi, AnimCtx, AnimPlayerValue, AnimationDefinition, AnimationSpec, AnyAnimationDefinition, BuildTools, Config$3 as Config, Cursor, Deps$3 as Deps, ErasedFields, Events$1 as Events, ExternalPlayer, FrameLoop, FramesValue, FxStep, HapticDescriptor, HapticKind, KernelSlice$3 as KernelSlice, MarkListener, MotionKeyframe, PlayDescriptor, PlayHandle, Pose, RunningTimeline, SfxDescriptor, SlotRecord, SlotTags, SlotValue, SlotValues, State$3 as State, Step, StepMotion, Target, TimelineRuntime, Track };
636
636
  }
637
637
  /**
638
638
  * What a step and a slot are pointed at: the projection key of a live view or of an element a
@@ -1056,6 +1056,44 @@ type AnimApi = {
1056
1056
  */
1057
1057
  replace(definition: AnyAnimationDefinition): void;
1058
1058
  };
1059
+ /**
1060
+ * What a system may do with anim, read with `res(AnimPlayer)`: play. A system that needs more
1061
+ * than play is a plugin. Play on a change, never on every frame: keep the handle in a resource, or
1062
+ * take away the tag that asked for the play.
1063
+ *
1064
+ * @example
1065
+ * ```ts
1066
+ * // A fresh item pops once: the system plays, then takes the Fresh tag away.
1067
+ * const popIn = system({ name: "popIn", phase: "input", query: [Fresh], run: (items, { world, res }) => {
1068
+ * for (const [entity] of items) {
1069
+ * res(AnimPlayer).play(pop, { thing: entity });
1070
+ * world.untag(entity, Fresh);
1071
+ * }
1072
+ * } });
1073
+ * ```
1074
+ */
1075
+ type AnimPlayerValue = {
1076
+ /**
1077
+ * Plays an animation from a system, the way `app.anim.play` does. The registered animation of
1078
+ * `animation.id` wins when a feature brought one, so a hot swap reaches the system. Played from
1079
+ * phase `input`, the tree moves in `animate` of the same frame; from a later phase it moves
1080
+ * from the next frame.
1081
+ *
1082
+ * @param animation - What `defineAnimation` returned.
1083
+ * @param slots - One target, or a list of targets, per declared slot.
1084
+ * @returns The handle: `cancel()`, `finish()`, `active()`, `done`, `marks()`.
1085
+ * @throws {Error} Before `anim` started: `[game] AnimPlayer is not ready: anim has not started.`
1086
+ * @example
1087
+ * ```ts
1088
+ * const lift = defineAnimation("look.hover", { slots: { thing: type<Target>() },
1089
+ * build: ({ thing }) => tween(thing, Transform, { scale: 1.1 }, { ms: 100 }) });
1090
+ * // Inside a system of phase "input": entity 1048576 is the item under the pointer.
1091
+ * const handle = res(AnimPlayer).play(lift, { thing: 1_048_576 });
1092
+ * handle.active(); // true until the lift lands 100 ms later, then false
1093
+ * ```
1094
+ */
1095
+ play<Tags extends SlotTags>(animation: AnimationDefinition<Tags>, slots: SlotValues<Tags>): PlayHandle;
1096
+ };
1059
1097
  /**
1060
1098
  * Resolved dependency APIs. `renderer` is not among them: `anim` writes its `Sprite` and reads
1061
1099
  * its `Transform` through the component objects, which are plain data, composes the root pose
@@ -1995,4 +2033,4 @@ type MusicRequest = {
1995
2033
  fadeMs: number;
1996
2034
  };
1997
2035
  //#endregion
1998
- export { HAPTIC_KINDS as A, sfx as B, MotionKeyframe as C, HapticKind as D, types_d_exports$3 as E, mark as F, use as G, spawned as H, parallel as I, wait as K, play as L, external as M, frames as N, SfxDescriptor as O, haptic as P, sequence as R, FramesValue as S, Target as T, stagger as U, spawn as V, tween as W, State$2 as _, SoundEntry as a, AnimApi as b, AnySceneProjection as c, SceneDefinition as d, SceneSpec as f, Config$2 as g, types_d_exports$1 as h, MusicOptions as i, defineAnimation as j, Step as k, DefineScene as l, State$1 as m, Config as n, State as o, ScenesApi as p, MusicDescriptor as r, types_d_exports as s, AudioApi as t, LayerMap as u, UiApi as v, State$3 as w, Config$3 as x, types_d_exports$2 as y, set as z };
2036
+ export { Step as A, set as B, FramesValue as C, types_d_exports$3 as D, Target as E, haptic as F, tween as G, spawn as H, mark as I, use as K, parallel as L, defineAnimation as M, external as N, HapticKind as O, frames as P, play as R, Config$3 as S, State$3 as T, spawned as U, sfx as V, stagger as W, State$2 as _, SoundEntry as a, AnimApi as b, AnySceneProjection as c, SceneDefinition as d, SceneSpec as f, Config$2 as g, types_d_exports$1 as h, MusicOptions as i, HAPTIC_KINDS as j, SfxDescriptor as k, DefineScene as l, State$1 as m, Config as n, State as o, ScenesApi as p, wait as q, MusicDescriptor as r, types_d_exports as s, AudioApi as t, LayerMap as u, UiApi as v, MotionKeyframe as w, AnimPlayerValue as x, types_d_exports$2 as y, sequence as z };
@@ -1,4 +1,4 @@
1
- import { B as ComponentValue, D as Point$2, F as AnyComponentType, I as AnyComponentValue, It as Hint, Kt as Json, O as ViewportSize, P as AnyComponent, Pt as Descriptor, Rt as Api, V as Entity, Z as ChangeHook, at as ProjectionMotion, dn as Api$5, it as NumericFields, k as Api$2, lt as ViewHandle, m as Api$3, nt as Motion, o as Events$1, rt as MotionHandle, t as Api$4, ut as Api$1, vn as Require, z as ComponentType } from "./types-BMEn0tSC.mjs";
1
+ import { B as ComponentValue, Bt as Api, D as Point$2, F as AnyComponentType, I as AnyComponentValue, It as Descriptor, Jt as Json, O as ViewportSize, P as AnyComponent, Rt as Hint, V as Entity, Z as ChangeHook, at as ProjectionMotion, it as NumericFields, k as Api$2, lt as ViewHandle, m as Api$3, nt as Motion, o as Events$1, pn as Api$5, rt as MotionHandle, t as Api$4, ut as Api$1, xn as Require, z as ComponentType } from "./types-B0v5VVa3.mjs";
2
2
  import { Log } from "@moku-labs/common/browser";
3
3
  import { PluginCtx } from "@moku-labs/core";
4
4
 
@@ -60,22 +60,26 @@ function tag(name) {
60
60
  return tagType;
61
61
  }
62
62
  /**
63
- * Declares a resource: one mutable object per world, created from a deep clone of the defaults
64
- * on first read.
63
+ * Declares a resource: one mutable object per world, made on first read. Plain defaults are
64
+ * deep-cloned. A factory is called instead, once per world, so the value may hold a function or a
65
+ * `Map`.
65
66
  *
66
67
  * @param name - Storage name, unique per world.
67
- * @param defaults - The value a fresh world starts with.
68
+ * @param defaults - The value a fresh world starts with, or the factory that makes it.
68
69
  * @returns The resource type.
69
70
  * @example
70
71
  * ```ts
71
72
  * const Pointer = resource("Pointer", { x: 0, y: 0, down: false });
72
73
  * Pointer.defaults; // { x: 0, y: 0, down: false }
74
+ *
75
+ * // A resource that holds a Map: a factory, called once per world on first read.
76
+ * const Looks = resource("boardLooks", () => ({ shown: new Map<Entity, { look: string }>() }));
73
77
  * ```
74
78
  */
75
79
  function resource(name, defaults) {
76
80
  return {
77
81
  resourceName: name,
78
- defaults: Object.freeze({ ...defaults })
82
+ defaults: typeof defaults === "function" ? defaults : Object.freeze({ ...defaults })
79
83
  };
80
84
  }
81
85
  /**