@moku-labs/game 0.4.6 → 0.5.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.
@@ -1,243 +1,9 @@
1
- import { $ as Ease, Gt as Events$2, 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, ct as TrackSegment, et as LayerSort, it as NumericFields, k as Api$2, m as Api$3, o as Events$4, rt as MotionHandle, sn as Events$3, st as TrackOptions, t as Api$4, tt as LayerSpec, ut as Api$1, vn as Require, z as ComponentType } from "./types-Dy1xB68L.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-VErbiEXy.mjs";
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";
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";
6
6
 
7
- //#region src/plugins/ui/layout/types.d.ts
8
- /**
9
- * layout module state. `nodes` is attach minus detach: Yoga 3.2.1 has no instance counter.
10
- */
11
- type LayoutState = {
12
- yoga: Yoga | undefined;
13
- byEntity: Map<Entity, Node>;
14
- nodes: number;
15
- measured: number;
16
- solves: number;
17
- scrolling: Entity | undefined;
18
- scrollStart: {
19
- pointerY: number;
20
- offset: number;
21
- };
22
- order: number;
23
- cleanups: Array<() => void>;
24
- };
25
- declare namespace types_d_exports$3 {
26
- export { Config$3 as Config, Deps$3 as Deps, ElementChange, ElementMotion, Finding, FocusRing, KernelSlice$3 as KernelSlice, State$3 as State, TextInputLook, UiApi, UiCtx, UiNode };
27
- }
28
- /**
29
- * The look of the keyboard focus ring: a dashed ring with a solid halo under it, drawn around the
30
- * focused control. Colours are `0xRRGGBB`, lengths reference units.
31
- *
32
- * @example
33
- * ```ts
34
- * // A dashed ink ring with a cream halo, 9 u outside the control.
35
- * const ring: FocusRing = {
36
- * stroke: 0x3a2212, strokeWidth: 4, dash: 10, offset: 9, halo: 0xfff3d6, haloWidth: 12
37
- * };
38
- * ```
39
- */
40
- type FocusRing = {
41
- /** The colour of the dashed ring. */stroke: number; /** The stroke width of the dashed ring. */
42
- strokeWidth: number; /** The dash length of the ring, with gaps of half a dash; 0 draws it solid. */
43
- dash: number; /** How far outside the control's rect the ring runs, on every side. */
44
- offset: number; /** The colour of the solid halo drawn under the ring. */
45
- halo: number; /** The stroke width of the halo, centred on the same path as the ring. */
46
- haloWidth: number;
47
- };
48
- /**
49
- * The look of a text field while it is edited: the caret, the selection box and the IME
50
- * underline. Colours are `0xRRGGBB`, lengths reference units, `keyboardMargin` CSS px. The caret
51
- * does not blink.
52
- *
53
- * @example
54
- * ```ts
55
- * // A thick ink caret, a pale blue selection, the field 16 px above the keyboard.
56
- * const look: TextInputLook = {
57
- * caretWidth: 3, caret: 0x3a2212, selection: 0x3390ff, selectionAlpha: 0.35,
58
- * composingUnderline: 3, keyboardMargin: 16
59
- * };
60
- * ```
61
- */
62
- type TextInputLook = {
63
- /** The width of the caret. */caretWidth: number; /** The colour of the caret and of the underline under an IME composing range. */
64
- caret: number; /** The colour of the selection box. */
65
- selection: number; /** The alpha of the selection box. */
66
- selectionAlpha: number; /** The height of the underline under an IME composing range. */
67
- composingUnderline: number; /** How far above the keyboard the bottom of the edited field is lifted, in CSS px. */
68
- keyboardMargin: number;
69
- };
70
- /**
71
- * ui plugin config.
72
- *
73
- * @example
74
- * ```ts
75
- * createApp({ pluginConfigs: { ui: { tapTargetPt: 48, breakpoints: { tall: 2, wide: 1.5 } } } });
76
- * // A game with a white focus ring on a dark board.
77
- * createApp({
78
- * pluginConfigs: {
79
- * ui: {
80
- * focusRing: {
81
- * stroke: 0xffffff, strokeWidth: 4, dash: 10, offset: 9, halo: 0x000000, haloWidth: 12
82
- * }
83
- * }
84
- * }
85
- * });
86
- * // A game whose text fields have an ink caret.
87
- * createApp({
88
- * pluginConfigs: {
89
- * ui: {
90
- * textInput: {
91
- * caretWidth: 3, caret: 0x3a2212, selection: 0x3390ff, selectionAlpha: 0.35,
92
- * composingUnderline: 3, keyboardMargin: 16
93
- * }
94
- * }
95
- * }
96
- * });
97
- * ```
98
- */
99
- type Config$3 = {
100
- /** Smallest tap target `lint()` accepts, in CSS px (points). */tapTargetPt: number; /** `when` flags: `tall` when height / width is at least `tall`, `wide` the other way round. */
101
- breakpoints: {
102
- tall: number;
103
- wide: number;
104
- };
105
- /**
106
- * The ring drawn around the control the keyboard focused. Shallow merge: a game that sets
107
- * `focusRing` gives all six fields.
108
- */
109
- focusRing: FocusRing;
110
- /**
111
- * The caret, the selection and the IME underline of a text field. Shallow merge: a game that
112
- * sets `textInput` gives all six fields.
113
- */
114
- textInput: TextInputLook;
115
- };
116
- /**
117
- * ui plugin state: one branch per module.
118
- */
119
- type State$3 = {
120
- jsx: JsxState;
121
- styles: StylesState;
122
- layout: LayoutState;
123
- };
124
- /**
125
- * Resolved dependency APIs. `anim` is not here: its edge is validation only, and its work
126
- * reaches `ui` through the tween driver behind every `ViewHandle`.
127
- */
128
- type Deps$3 = {
129
- time: Api;
130
- flow: Api$1;
131
- world: Api$2;
132
- renderer: Api$3;
133
- input: InputApi;
134
- i18n: I18nApi;
135
- text: TextApi;
136
- };
137
- /**
138
- * What the kernel context offers before the deps are attached.
139
- *
140
- * `ui` owns no event, so `emit` is the kernel's and never called here.
141
- */
142
- type KernelSlice$3 = PluginCtx<Config$3, State$3> & {
143
- readonly global: object;
144
- readonly log: Log.LogApi;
145
- readonly require: Require;
146
- };
147
- /**
148
- * Domain context shared by the three modules: the kernel slice plus the resolved deps.
149
- */
150
- type UiCtx = KernelSlice$3 & {
151
- readonly deps: Deps$3;
152
- };
153
- /**
154
- * ui plugin API, `app.ui`. The screen is one more projection, so three members read it the way a
155
- * test reads the board: a snapshot, a key lookup and a list of findings; the fourth types into a
156
- * text field.
157
- *
158
- * @example
159
- * ```ts
160
- * // A headless scenario taps the claim button of the reward popup.
161
- * app.ui.tree().type; // "row", the root element of the HUD
162
- * app.input.tap(app.ui.find("claim") ?? 0);
163
- * app.ui.lint(); // []
164
- * ```
165
- */
166
- type UiApi = {
167
- /**
168
- * The live screen as plain data: every root in layer order, every element in child order, with
169
- * its rect in root coordinates, its resolved style and its seven state flags. A rect is natural:
170
- * under a `fit: "contain"` element it is the rect before that scale, and the fitted element
171
- * adds `fitScale`. A windowed scroll adds `window`, and its content lists the live rows only.
172
- * Works headless.
173
- *
174
- * @returns The root node; several roots come back under one `screen` node.
175
- * @example
176
- * ```ts
177
- * // On an iPhone SE the 970 u board is drawn smaller to fit the slot between the bars.
178
- * const screen = app.ui.tree();
179
- * screen.children.map(child => child.key); // ["fitTop", "slot", "fitBottom"]
180
- * const board = screen.children[1]?.children[0];
181
- * board?.rect.w; // 970, the natural width
182
- * board?.fitScale; // 0.805, the 781 u the slot leaves over 970 u
183
- * ```
184
- */
185
- tree(): UiNode;
186
- /**
187
- * The entity of a keyed element, so a test can tap it and `anim` can aim at it. Popup roots
188
- * come first, then the projection roots in layer order.
189
- *
190
- * @param key - The `key` prop of the element.
191
- * @returns The entity, or `undefined` for an unknown or exiting element.
192
- * @example
193
- * ```ts
194
- * // A headless scenario answers the gate through the button of the popup.
195
- * app.input.tap(app.ui.find("claim") ?? 0); // true
196
- * app.ui.find("nothing"); // undefined
197
- * ```
198
- */
199
- find(key: string): Entity | undefined;
200
- /**
201
- * Reads the live screen against the five rules: a tap target under `tapTargetPt` at the size
202
- * it is drawn (a `fit: "contain"` on it or above it shrinks it), a text that does not fit its
203
- * box in some registered locale, an absolute element with no `reason`, a clipping element
204
- * (`scroll`, `overflow: "hidden"`) whose style names a nine-slice it never draws, and a root
205
- * element whose style sets a `zIndex` it ignores. Never throws; empty when nothing is mounted.
206
- *
207
- * @returns One finding per rule and element.
208
- * @example
209
- * ```ts
210
- * // A game test keeps the board honest on an iPhone SE: a 140 u cell in the 0.8 slot.
211
- * app.ui.lint(); // [{ rule: "tap-target", key: "cell", detail: "39 x 39 pt" }]
212
- * // A list styled with a nine-slice: the clip keeps it from drawing.
213
- * app.ui.lint(); // [{ rule: "nine-slice-clipped", key: "orders", detail: "scroll" }]
214
- * // A screen root that asks to be drawn over the others: the layer decides that.
215
- * app.ui.lint(); // [{ rule: "z-index-on-root", key: "board", detail: "zIndex 2" }]
216
- * ```
217
- */
218
- lint(): readonly Finding[];
219
- /**
220
- * Types into a text field the way a player does, for a headless test or an agent: the field
221
- * becomes the one being edited, `value` cut to its `maxLength` is written into it, and the
222
- * local field it names is written, so the component re-renders on the next frame. Nothing
223
- * reaches the gate: Enter submits.
224
- *
225
- * @param key - The `key` prop of the `input` element.
226
- * @param value - The text to type.
227
- * @returns True when the field took the text; false with one warning when no live `input` has
228
- * the key.
229
- * @example
230
- * ```ts
231
- * // A headless test renames the player through the Rename popup.
232
- * app.ui.fill("nameField", "Alex"); // true
233
- * app.time.step(16);
234
- * app.input.pressKey("Enter"); // true: the gate took { intent: "save", payload: { name: "Alex" } }
235
- * app.ui.fill("nothing", "Alex"); // false, with the warning "ui:fill-without-input"
236
- * ```
237
- */
238
- fill(key: string, value: string): boolean;
239
- };
240
- //#endregion
241
7
  //#region src/plugins/anim/tween/types.d.ts
242
8
  /**
243
9
  * One running track: the numeric fields of one component of one entity moving from the values
@@ -865,8 +631,8 @@ type RunningTimeline = {
865
631
  readonly resolve: () => void;
866
632
  ended: boolean;
867
633
  };
868
- declare namespace types_d_exports$2 {
869
- export { AnimApi, AnimCtx, AnimationDefinition, AnimationSpec, AnyAnimationDefinition, BuildTools, Config$2 as Config, Cursor, Deps$2 as Deps, ErasedFields, Events$1 as Events, ExternalPlayer, FrameLoop, FramesValue, FxStep, HapticDescriptor, HapticKind, KernelSlice$2 as KernelSlice, MarkListener, MotionKeyframe, PlayDescriptor, PlayHandle, Pose, RunningTimeline, SfxDescriptor, SlotRecord, SlotTags, SlotValue, SlotValues, State$2 as State, Step, StepMotion, Target, TimelineRuntime, Track };
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 };
870
636
  }
871
637
  /**
872
638
  * What a step and a slot are pointed at: the projection key of a live view or of an element a
@@ -1118,7 +884,7 @@ type FrameLoop = {
1118
884
  * createApp({ plugins: [...screen], pluginConfigs: { anim: { maxTracks: 500, reducedMotion: true } } });
1119
885
  * ```
1120
886
  */
1121
- type Config$2 = {
887
+ type Config$3 = {
1122
888
  /** Dev guard: one warning each time the running track count rises past this number. */maxTracks: number;
1123
889
  /**
1124
890
  * Start value of reduced motion: every track but a loop takes 0 ms and every loop stands on
@@ -1130,7 +896,7 @@ type Config$2 = {
1130
896
  * anim plugin state: the one track table, the field bookkeeping of the retarget policy and the
1131
897
  * offsets accumulator, the running timelines and the animation registry.
1132
898
  */
1133
- type State$2 = {
899
+ type State$3 = {
1134
900
  /** The one track table. Insertion order is advance order. */tracks: Map<number, Track>; /** Ids of tracks and timelines. */
1135
901
  nextId: number; /** `"<entity>:<component>:<field>"` to the id of the absolute track that drives the field. */
1136
902
  owner: Map<string, number>; /** Per field key, the delta each additive track contributes. */
@@ -1271,6 +1037,24 @@ type AnimApi = {
1271
1037
  * ```
1272
1038
  */
1273
1039
  onMark(fn: MarkListener): () => void;
1040
+ /**
1041
+ * Swaps a registered animation for a new definition of the same id. The dev hot swap of `ui`
1042
+ * calls it when a saved `animations.ts` exports one. The next `play` effect of a node builds
1043
+ * the new definition; a timeline that runs now keeps the tree it was built with.
1044
+ *
1045
+ * @param definition - The new definition; its id names the animation it replaces.
1046
+ * @throws {Error} When no feature registered an animation with that id.
1047
+ * @example
1048
+ * ```ts
1049
+ * // The ui hot swap: a save of `board/animations.ts` brings a bigger merge burst.
1050
+ * const mergeBurst = defineAnimation("board.mergeBurst", { slots: { cell: type<Target>() },
1051
+ * build: ({ cell }) => tween(cell, Transform, { scale: 1.4 }, { ms: 300 }) });
1052
+ * ctx.require(animPlugin).replace(mergeBurst);
1053
+ * // A node then runs fx(play(mergeBurst, { cell: { projection: "board.items", key: "i3" } })):
1054
+ * // the play effect builds the new tree, and item i3 grows to scale 1.4 in 300 ms.
1055
+ * ```
1056
+ */
1057
+ replace(definition: AnyAnimationDefinition): void;
1274
1058
  };
1275
1059
  /**
1276
1060
  * Resolved dependency APIs. `renderer` is not among them: `anim` writes its `Sprite` and reads
@@ -1278,7 +1062,7 @@ type AnimApi = {
1278
1062
  * of `at` with the pure `rootPoseOf` of `renderer/sync/pose.ts`, and turns the target of a
1279
1063
  * `tween` in `space: "root"` local with the pure `localPoseOf` of the same module.
1280
1064
  */
1281
- type Deps$2 = {
1065
+ type Deps$3 = {
1282
1066
  time: Api;
1283
1067
  flow: Api$1;
1284
1068
  world: Api$2;
@@ -1290,7 +1074,7 @@ type Deps$2 = {
1290
1074
  * row 8), so the own events reach the context the kernel hands the factories and `emit` is the
1291
1075
  * kernel's own, anim-typed one. No member of the context is cast.
1292
1076
  */
1293
- type KernelSlice$2 = PluginCtx<Config$2, State$2, Events$1> & {
1077
+ type KernelSlice$3 = PluginCtx<Config$3, State$3, Events$1> & {
1294
1078
  readonly global: object;
1295
1079
  readonly log: Log.LogApi;
1296
1080
  readonly require: Require;
@@ -1298,9 +1082,244 @@ type KernelSlice$2 = PluginCtx<Config$2, State$2, Events$1> & {
1298
1082
  /**
1299
1083
  * Domain context of the anim plugin: the kernel slice and the three resolved dependencies.
1300
1084
  */
1301
- type AnimCtx = KernelSlice$2 & {
1085
+ type AnimCtx = KernelSlice$3 & {
1086
+ readonly deps: Deps$3;
1087
+ };
1088
+ //#endregion
1089
+ //#region src/plugins/ui/layout/types.d.ts
1090
+ /**
1091
+ * layout module state. `nodes` is attach minus detach: Yoga 3.2.1 has no instance counter.
1092
+ */
1093
+ type LayoutState = {
1094
+ yoga: Yoga | undefined;
1095
+ byEntity: Map<Entity, Node>;
1096
+ nodes: number;
1097
+ measured: number;
1098
+ solves: number;
1099
+ scrolling: Entity | undefined;
1100
+ scrollStart: {
1101
+ pointerY: number;
1102
+ offset: number;
1103
+ };
1104
+ order: number;
1105
+ cleanups: Array<() => void>;
1106
+ };
1107
+ declare namespace types_d_exports$2 {
1108
+ export { Config$2 as Config, Deps$2 as Deps, ElementChange, ElementMotion, Finding, FocusRing, KernelSlice$2 as KernelSlice, State$2 as State, TextInputLook, UiApi, UiCtx, UiNode };
1109
+ }
1110
+ /**
1111
+ * The look of the keyboard focus ring: a dashed ring with a solid halo under it, drawn around the
1112
+ * focused control. Colours are `0xRRGGBB`, lengths reference units.
1113
+ *
1114
+ * @example
1115
+ * ```ts
1116
+ * // A dashed ink ring with a cream halo, 9 u outside the control.
1117
+ * const ring: FocusRing = {
1118
+ * stroke: 0x3a2212, strokeWidth: 4, dash: 10, offset: 9, halo: 0xfff3d6, haloWidth: 12
1119
+ * };
1120
+ * ```
1121
+ */
1122
+ type FocusRing = {
1123
+ /** The colour of the dashed ring. */stroke: number; /** The stroke width of the dashed ring. */
1124
+ strokeWidth: number; /** The dash length of the ring, with gaps of half a dash; 0 draws it solid. */
1125
+ dash: number; /** How far outside the control's rect the ring runs, on every side. */
1126
+ offset: number; /** The colour of the solid halo drawn under the ring. */
1127
+ halo: number; /** The stroke width of the halo, centred on the same path as the ring. */
1128
+ haloWidth: number;
1129
+ };
1130
+ /**
1131
+ * The look of a text field while it is edited: the caret, the selection box and the IME
1132
+ * underline. Colours are `0xRRGGBB`, lengths reference units, `keyboardMargin` CSS px. The caret
1133
+ * does not blink.
1134
+ *
1135
+ * @example
1136
+ * ```ts
1137
+ * // A thick ink caret, a pale blue selection, the field 16 px above the keyboard.
1138
+ * const look: TextInputLook = {
1139
+ * caretWidth: 3, caret: 0x3a2212, selection: 0x3390ff, selectionAlpha: 0.35,
1140
+ * composingUnderline: 3, keyboardMargin: 16
1141
+ * };
1142
+ * ```
1143
+ */
1144
+ type TextInputLook = {
1145
+ /** The width of the caret. */caretWidth: number; /** The colour of the caret and of the underline under an IME composing range. */
1146
+ caret: number; /** The colour of the selection box. */
1147
+ selection: number; /** The alpha of the selection box. */
1148
+ selectionAlpha: number; /** The height of the underline under an IME composing range. */
1149
+ composingUnderline: number; /** How far above the keyboard the bottom of the edited field is lifted, in CSS px. */
1150
+ keyboardMargin: number;
1151
+ };
1152
+ /**
1153
+ * ui plugin config.
1154
+ *
1155
+ * @example
1156
+ * ```ts
1157
+ * createApp({ pluginConfigs: { ui: { tapTargetPt: 48, breakpoints: { tall: 2, wide: 1.5 } } } });
1158
+ * // A game with a white focus ring on a dark board.
1159
+ * createApp({
1160
+ * pluginConfigs: {
1161
+ * ui: {
1162
+ * focusRing: {
1163
+ * stroke: 0xffffff, strokeWidth: 4, dash: 10, offset: 9, halo: 0x000000, haloWidth: 12
1164
+ * }
1165
+ * }
1166
+ * }
1167
+ * });
1168
+ * // A game whose text fields have an ink caret.
1169
+ * createApp({
1170
+ * pluginConfigs: {
1171
+ * ui: {
1172
+ * textInput: {
1173
+ * caretWidth: 3, caret: 0x3a2212, selection: 0x3390ff, selectionAlpha: 0.35,
1174
+ * composingUnderline: 3, keyboardMargin: 16
1175
+ * }
1176
+ * }
1177
+ * }
1178
+ * });
1179
+ * ```
1180
+ */
1181
+ type Config$2 = {
1182
+ /** Smallest tap target `lint()` accepts, in CSS px (points). */tapTargetPt: number; /** `when` flags: `tall` when height / width is at least `tall`, `wide` the other way round. */
1183
+ breakpoints: {
1184
+ tall: number;
1185
+ wide: number;
1186
+ };
1187
+ /**
1188
+ * The ring drawn around the control the keyboard focused. Shallow merge: a game that sets
1189
+ * `focusRing` gives all six fields.
1190
+ */
1191
+ focusRing: FocusRing;
1192
+ /**
1193
+ * The caret, the selection and the IME underline of a text field. Shallow merge: a game that
1194
+ * sets `textInput` gives all six fields.
1195
+ */
1196
+ textInput: TextInputLook;
1197
+ };
1198
+ /**
1199
+ * ui plugin state: one branch per module.
1200
+ */
1201
+ type State$2 = {
1202
+ jsx: JsxState;
1203
+ styles: StylesState;
1204
+ layout: LayoutState;
1205
+ };
1206
+ /**
1207
+ * Resolved dependency APIs. `anim` is here for the dev hot swap, which replaces animations; the
1208
+ * motions of `ui` reach `anim` through the tween driver behind every `ViewHandle`.
1209
+ */
1210
+ type Deps$2 = {
1211
+ anim: AnimApi;
1212
+ time: Api;
1213
+ flow: Api$1;
1214
+ world: Api$2;
1215
+ renderer: Api$3;
1216
+ input: InputApi;
1217
+ i18n: I18nApi;
1218
+ text: TextApi;
1219
+ };
1220
+ /**
1221
+ * What the kernel context offers before the deps are attached.
1222
+ *
1223
+ * `ui` owns no event of its own; `emit` sends the global `ui:hot-swap` of the dev hot swap.
1224
+ */
1225
+ type KernelSlice$2 = PluginCtx<Config$2, State$2, Events$2> & {
1226
+ readonly global: object;
1227
+ readonly log: Log.LogApi;
1228
+ readonly require: Require;
1229
+ };
1230
+ /**
1231
+ * Domain context shared by the three modules: the kernel slice plus the resolved deps.
1232
+ */
1233
+ type UiCtx = KernelSlice$2 & {
1302
1234
  readonly deps: Deps$2;
1303
1235
  };
1236
+ /**
1237
+ * ui plugin API, `app.ui`. The screen is one more projection, so three members read it the way a
1238
+ * test reads the board: a snapshot, a key lookup and a list of findings; the fourth types into a
1239
+ * text field.
1240
+ *
1241
+ * @example
1242
+ * ```ts
1243
+ * // A headless scenario taps the claim button of the reward popup.
1244
+ * app.ui.tree().type; // "row", the root element of the HUD
1245
+ * app.input.tap(app.ui.find("claim") ?? 0);
1246
+ * app.ui.lint(); // []
1247
+ * ```
1248
+ */
1249
+ type UiApi = {
1250
+ /**
1251
+ * The live screen as plain data: every root in layer order, every element in child order, with
1252
+ * its rect in root coordinates, its resolved style and its seven state flags. A rect is natural:
1253
+ * under a `fit: "contain"` element it is the rect before that scale, and the fitted element
1254
+ * adds `fitScale`. A windowed scroll adds `window`, and its content lists the live rows only.
1255
+ * Works headless.
1256
+ *
1257
+ * @returns The root node; several roots come back under one `screen` node.
1258
+ * @example
1259
+ * ```ts
1260
+ * // On an iPhone SE the 970 u board is drawn smaller to fit the slot between the bars.
1261
+ * const screen = app.ui.tree();
1262
+ * screen.children.map(child => child.key); // ["fitTop", "slot", "fitBottom"]
1263
+ * const board = screen.children[1]?.children[0];
1264
+ * board?.rect.w; // 970, the natural width
1265
+ * board?.fitScale; // 0.805, the 781 u the slot leaves over 970 u
1266
+ * ```
1267
+ */
1268
+ tree(): UiNode;
1269
+ /**
1270
+ * The entity of a keyed element, so a test can tap it and `anim` can aim at it. Popup roots
1271
+ * come first, then the projection roots in layer order.
1272
+ *
1273
+ * @param key - The `key` prop of the element.
1274
+ * @returns The entity, or `undefined` for an unknown or exiting element.
1275
+ * @example
1276
+ * ```ts
1277
+ * // A headless scenario answers the gate through the button of the popup.
1278
+ * app.input.tap(app.ui.find("claim") ?? 0); // true
1279
+ * app.ui.find("nothing"); // undefined
1280
+ * ```
1281
+ */
1282
+ find(key: string): Entity | undefined;
1283
+ /**
1284
+ * Reads the live screen against the five rules: a tap target under `tapTargetPt` at the size
1285
+ * it is drawn (a `fit: "contain"` on it or above it shrinks it), a text that does not fit its
1286
+ * box in some registered locale, an absolute element with no `reason`, a clipping element
1287
+ * (`scroll`, `overflow: "hidden"`) whose style names a nine-slice it never draws, and a root
1288
+ * element whose style sets a `zIndex` it ignores. Never throws; empty when nothing is mounted.
1289
+ *
1290
+ * @returns One finding per rule and element.
1291
+ * @example
1292
+ * ```ts
1293
+ * // A game test keeps the board honest on an iPhone SE: a 140 u cell in the 0.8 slot.
1294
+ * app.ui.lint(); // [{ rule: "tap-target", key: "cell", detail: "39 x 39 pt" }]
1295
+ * // A list styled with a nine-slice: the clip keeps it from drawing.
1296
+ * app.ui.lint(); // [{ rule: "nine-slice-clipped", key: "orders", detail: "scroll" }]
1297
+ * // A screen root that asks to be drawn over the others: the layer decides that.
1298
+ * app.ui.lint(); // [{ rule: "z-index-on-root", key: "board", detail: "zIndex 2" }]
1299
+ * ```
1300
+ */
1301
+ lint(): readonly Finding[];
1302
+ /**
1303
+ * Types into a text field the way a player does, for a headless test or an agent: the field
1304
+ * becomes the one being edited, `value` cut to its `maxLength` is written into it, and the
1305
+ * local field it names is written, so the component re-renders on the next frame. Nothing
1306
+ * reaches the gate: Enter submits.
1307
+ *
1308
+ * @param key - The `key` prop of the `input` element.
1309
+ * @param value - The text to type.
1310
+ * @returns True when the field took the text; false with one warning when no live `input` has
1311
+ * the key.
1312
+ * @example
1313
+ * ```ts
1314
+ * // A headless test renames the player through the Rename popup.
1315
+ * app.ui.fill("nameField", "Alex"); // true
1316
+ * app.time.step(16);
1317
+ * app.input.pressKey("Enter"); // true: the gate took { intent: "save", payload: { name: "Alex" } }
1318
+ * app.ui.fill("nothing", "Alex"); // false, with the warning "ui:fill-without-input"
1319
+ * ```
1320
+ */
1321
+ fill(key: string, value: string): boolean;
1322
+ };
1304
1323
  declare namespace types_d_exports$1 {
1305
1324
  export { AnySceneProjection, CheckedProjections, Config$1 as Config, DefineScene, Deps$1 as Deps, Events, KernelSlice$1 as KernelSlice, LayerMap, RunContext, SceneDefinition, SceneLayer, SceneOwner, SceneProjection, SceneSpec, ScenesApi, ScenesCtx, SortRule, State$1 as State, UiLayer };
1306
1325
  }
@@ -1473,7 +1492,12 @@ type Config$1 = Record<string, never>;
1473
1492
  */
1474
1493
  type State$1 = {
1475
1494
  /** Scene id to its declaration, filled in `onStart` from `flow.features`. */scenes: Map<string, SceneDefinition>; /** Id of the scene that is mounted now. */
1476
- current: string | undefined; /** Fast mode: the scene a transit node named, applied at the next rest node. */
1495
+ current: string | undefined;
1496
+ /**
1497
+ * The scene the next rest node without its own `scene` mounts: the one a transit node named in
1498
+ * fast mode, or the one the restore door expected with `expect`. A rest node clears it; an over
1499
+ * rest node takes it only while nothing is mounted.
1500
+ */
1477
1501
  pending: string | undefined;
1478
1502
  owner: SceneOwner; /** The remover of the `onEnter` callback: set in `onStart`, run in `onStop`. */
1479
1503
  teardown: (() => void) | undefined;
@@ -1499,7 +1523,7 @@ type Events = {
1499
1523
  };
1500
1524
  /**
1501
1525
  * scenes plugin API, `app.scenes`. A scene is switched by the graph, never by a call, so the
1502
- * plugin answers one question and takes no orders.
1526
+ * plugin answers one question and takes one order, for the restore door.
1503
1527
  *
1504
1528
  * @example
1505
1529
  * ```ts
@@ -1520,6 +1544,24 @@ type ScenesApi = {
1520
1544
  * ```
1521
1545
  */
1522
1546
  current(): string | undefined;
1547
+ /**
1548
+ * Names the scene the next rest node without its own `scene` mounts. The restore door calls it
1549
+ * with the scene of a bookmark, so a bookmark at a popup comes back with the scene under it.
1550
+ * An over rest node takes the expected scene only while nothing is mounted; it never switches a
1551
+ * mounted one.
1552
+ *
1553
+ * @param id - Id of a scene a feature declared.
1554
+ * @throws {Error} For an id no feature declared, also before `onStart` filled the registry.
1555
+ * @example
1556
+ * ```ts
1557
+ * // A fresh page restores a bookmark taken at the Settings popup over Home.
1558
+ * app.scenes.expect("home");
1559
+ * await app.flow.restore(bookmark);
1560
+ * await app.flow.walk([]); // the scene stage runs after restore resolves
1561
+ * app.scenes.current(); // "home"
1562
+ * ```
1563
+ */
1564
+ expect(id: string): void;
1523
1565
  };
1524
1566
  /**
1525
1567
  * Resolved dependency APIs.
@@ -1925,7 +1967,7 @@ type AudioCtx = KernelSlice & {
1925
1967
  /**
1926
1968
  * Payload of the `model:committed` hook: the source of every volume a game sets.
1927
1969
  */
1928
- type ModelCommitted = Events$2["model:committed"];
1970
+ type ModelCommitted = Events$3["model:committed"];
1929
1971
  /**
1930
1972
  * Payload of the `scenes:changed` hook: `music` is the track the next scene declared.
1931
1973
  */
@@ -1933,11 +1975,11 @@ type SceneChanged = Events["scenes:changed"];
1933
1975
  /**
1934
1976
  * Payload of the `lifecycle:changed` hook: the pause that holds every bus at zero.
1935
1977
  */
1936
- type LifecycleChanged = Events$3["lifecycle:changed"];
1978
+ type LifecycleChanged = Events$4["lifecycle:changed"];
1937
1979
  /**
1938
1980
  * Payload of the `assets:bundle-unloaded` hook: `keys` names every decoded sound that is gone.
1939
1981
  */
1940
- type BundleUnloaded = Events$4["assets:bundle-unloaded"];
1982
+ type BundleUnloaded = Events$5["assets:bundle-unloaded"];
1941
1983
  /**
1942
1984
  * What one play of a sound asks for: the key and the bus the sound goes to.
1943
1985
  */
@@ -1953,4 +1995,4 @@ type MusicRequest = {
1953
1995
  fadeMs: number;
1954
1996
  };
1955
1997
  //#endregion
1956
- export { haptic as A, tween as B, HapticKind as C, defineAnimation as D, HAPTIC_KINDS as E, set as F, UiApi as G, wait as H, sfx as I, types_d_exports$3 as K, spawn as L, parallel as M, play as N, external as O, sequence as P, spawned as R, types_d_exports$2 as S, Step as T, Config$3 as U, use as V, State$3 as W, Config$2 as _, SoundEntry as a, State$2 as b, AnySceneProjection as c, SceneDefinition as d, SceneSpec as f, AnimApi as g, types_d_exports$1 as h, MusicOptions as i, mark as j, frames 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, FramesValue as v, SfxDescriptor as w, Target as x, MotionKeyframe as y, stagger as z };
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 };
@@ -1,4 +1,4 @@
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-Dy1xB68L.mjs";
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";
2
2
  import { Log } from "@moku-labs/common/browser";
3
3
 
4
4
  //#region src/plugins/flow/headless.d.ts
@@ -1,5 +1,5 @@
1
- import { D as Point, E as FilterSlot, L as ComponentHandle, V as Entity, _ as PixiModule, 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-Dy1xB68L.mjs";
2
- import { g as AnimApi } from "./types-C-4f2jD_.mjs";
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";
3
3
  import { Log } from "@moku-labs/common/browser";
4
4
  import { PluginCtx } from "@moku-labs/core";
5
5
 
@@ -523,7 +523,7 @@ type EmitterInstance = {
523
523
  readonly particleClass: PixiModule["Particle"];
524
524
  };
525
525
  declare namespace types_d_exports {
526
- export { AlphaValue, BlurValue, BundleUnloaded, ColorMatrixValue, Config, CoreKind, Curve, Deps, DisplacementValue, EffectsApi, EffectsCtx, EffectsStats, EmitterConfig, EmitterDefinition, EmitterValue, FilterComponent, FilterDefinition, FilterSpec, FilterValueOf, GlowValue, KernelSlice, NoiseValue, OutlineValue, Range, ResolvedEmitterConfig, Shape, State, UniformDeclaration, UniformSpec, UniformValue };
526
+ export { AlphaValue, BlurValue, BundleUnloaded, ColorMatrixValue, Config, CoreKind, Curve, Deps, DisplacementValue, EffectsApi, EffectsCtx, EffectsStats, EmitterConfig, EmitterDefinition, EmitterValue, FilterComponent, FilterDefinition, FilterSpec, FilterValueOf, GlowValue, HotSwap, KernelSlice, NoiseValue, OutlineValue, Range, ResolvedEmitterConfig, Shape, State, UniformDeclaration, UniformSpec, UniformValue };
527
527
  }
528
528
  /**
529
529
  * effects plugin config. `maxParticles` here is the global warning line; the `maxParticles` of an
@@ -644,5 +644,9 @@ type EffectsCtx = KernelSlice & {
644
644
  * Payload of the `assets:bundle-unloaded` hook: `keys` names every texture that is gone.
645
645
  */
646
646
  type BundleUnloaded = Events["assets:bundle-unloaded"];
647
+ /**
648
+ * Payload of the global `ui:hot-swap` hook: the saved file and its new exports.
649
+ */
650
+ type HotSwap = Events$1["ui:hot-swap"];
647
651
  //#endregion
648
652
  export { NoiseValue as _, types_d_exports as a, EmitterValue as c, ColorMatrixValue as d, DisplacementValue as f, GlowValue as g, FilterValueOf as h, State as i, AlphaValue as l, FilterSpec as m, EffectsApi as n, EmitterConfig as o, FilterComponent as p, EffectsStats as r, EmitterDefinition as s, Config as t, BlurValue as u, OutlineValue as v, UniformDeclaration as y };