@moku-labs/game 0.2.0 → 0.4.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.
- package/README.md +2 -0
- package/dist/assets.d.mts +181 -22
- package/dist/assets.mjs +1186 -160
- package/dist/{commands-C0BljsM5.d.mts → commands-DYO22lzi.d.mts} +22 -5
- package/dist/control.d.mts +3 -3
- package/dist/control.mjs +3 -3
- package/dist/{define-CjP_4H0G.d.mts → define-BKWjWgyQ.d.mts} +1 -1
- package/dist/dev-CQYJPmcD.mjs +1183 -0
- package/dist/flatten-BkXuscAV.mjs +100 -0
- package/dist/index.d.mts +85 -31
- package/dist/index.mjs +3208 -490
- package/dist/{session-DmAxY6Ll.mjs → input-CK4nxBO1.mjs} +41 -12
- package/dist/inspect.d.mts +32 -9
- package/dist/inspect.mjs +2 -2
- package/dist/jsx-dev-runtime.d.mts +1 -1
- package/dist/jsx-dev-runtime.mjs +1 -1
- package/dist/jsx-runtime.d.mts +1 -1
- package/dist/jsx-runtime.mjs +1 -1
- package/dist/{run-tlcn1w7y.mjs → run-BfuMLahc.mjs} +489 -213
- package/dist/{runtime-Bn5TXNxP.d.mts → runtime-Bhu442L8.d.mts} +1 -1
- package/dist/{runtime-DRlwxkIv.mjs → runtime-CBZgWgzx.mjs} +1 -57
- package/dist/{sources-BooL2MsX.mjs → sources-C1uGjz4l.mjs} +198 -21
- package/dist/testing.d.mts +5 -5
- package/dist/testing.mjs +5 -4
- package/dist/{types-CxZy7Pi4.d.mts → types-BnWH-75b.d.mts} +95 -21
- package/dist/{types-CqkrEY06.d.mts → types-CVUHPvMB.d.mts} +5 -434
- package/dist/{types-DmhTHRVW.d.mts → types-DtmFVCHf.d.mts} +1 -1
- package/dist/{types-BZ1OLf24.d.mts → types-Dy1xB68L.d.mts} +2318 -1169
- package/dist/{types-X9jXjka3.d.mts → types-VErbiEXy.d.mts} +867 -198
- package/dist/{visual-NPrDkvUk.mjs → visual-DUvvPPCt.mjs} +51 -1
- package/llms.txt +5 -5
- package/package.json +5 -5
- package/dist/component-DGg5DqKK.mjs +0 -44
- package/dist/dev-CcwFrwok.mjs +0 -413
- package/dist/types-C47fKNcn.d.mts +0 -695
|
@@ -1,10 +1,214 @@
|
|
|
1
|
-
import {
|
|
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-Dy1xB68L.mjs";
|
|
2
2
|
import { Log } from "@moku-labs/common/browser";
|
|
3
3
|
import { PluginCtx } from "@moku-labs/core";
|
|
4
4
|
|
|
5
|
-
//#region src/plugins/
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
//#region src/plugins/renderer/components.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* Where a view sits, in reference units and radians. Relative to the `Parent` when there is one.
|
|
8
|
+
* `pivot` is the local point the view turns and scales around, and `x`, `y` is where that point
|
|
9
|
+
* lands; the default pivot `{ x: 0, y: 0 }` is the view's own origin.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* // A 200 x 80 button that grows around its centre.
|
|
14
|
+
* const value: TransformValue = {
|
|
15
|
+
* x: 640, y: 340, rotation: 0, scale: 1.1, pivot: { x: 100, y: 40 }
|
|
16
|
+
* };
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
type TransformValue = {
|
|
20
|
+
x: number;
|
|
21
|
+
y: number;
|
|
22
|
+
rotation: number;
|
|
23
|
+
scale: number;
|
|
24
|
+
pivot: Point$2;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* How a sized sprite fills its box: `"fill"` stretches, `"contain"` scales uniformly inside the
|
|
28
|
+
* box and centres, `"cover"` scales uniformly over the box and crops the overflow.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```ts
|
|
32
|
+
* const fit: SpriteFit = "cover";
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
type SpriteFit = "fill" | "contain" | "cover";
|
|
36
|
+
/**
|
|
37
|
+
* One textured quad. `texture` is an asset key; `defineGame` narrows it to the game's keys.
|
|
38
|
+
* `width` and `height` are the box in reference units; 0 on an axis keeps the texture's own size
|
|
39
|
+
* there. `anchor` is the point of the box that sits on the `Transform`. `alpha` fades the quad
|
|
40
|
+
* and the entity's children.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```ts
|
|
44
|
+
* const value: SpriteValue = {
|
|
45
|
+
* texture: "board.bg-forest-meadow", tint: 0xffffff, alpha: 1, anchor: { x: 0, y: 0 },
|
|
46
|
+
* width: 1080, height: 1920, fit: "cover"
|
|
47
|
+
* };
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
type SpriteValue = {
|
|
51
|
+
texture: string;
|
|
52
|
+
tint: number;
|
|
53
|
+
alpha: number;
|
|
54
|
+
anchor: Point$2;
|
|
55
|
+
width: number;
|
|
56
|
+
height: number;
|
|
57
|
+
fit: SpriteFit;
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* A stretchable panel. The slice borders come with the texture, not with the component. `debug`
|
|
61
|
+
* strokes the bounds and the four cut lines over the panel: cyan, red when the corners overlap or
|
|
62
|
+
* the texture is missing. `clip` masks the children of the entity to the `width × height` box,
|
|
63
|
+
* the same rule as `Shape.clip`: the mask is a filled rectangle and is never drawn, and the panel
|
|
64
|
+
* itself is never masked. `alpha` fades the panel and the entity's children.
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```ts
|
|
68
|
+
* const value: NineSliceValue = {
|
|
69
|
+
* texture: "ui.panel", width: 600, height: 320, alpha: 1, tint: 0xffffff, debug: false,
|
|
70
|
+
* clip: false
|
|
71
|
+
* };
|
|
72
|
+
* // The board tray, outlined while its insets are checked.
|
|
73
|
+
* const tray: NineSliceValue = {
|
|
74
|
+
* ...value, texture: "board.board-tray", width: 1000, height: 1040, debug: true
|
|
75
|
+
* };
|
|
76
|
+
* // A text field: a long value never draws outside the frame.
|
|
77
|
+
* const field: NineSliceValue = {
|
|
78
|
+
* ...value, texture: "ui.field", width: 480, height: 96, clip: true
|
|
79
|
+
* };
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
82
|
+
type NineSliceValue = {
|
|
83
|
+
texture: string;
|
|
84
|
+
width: number;
|
|
85
|
+
height: number;
|
|
86
|
+
alpha: number;
|
|
87
|
+
tint: number;
|
|
88
|
+
debug: boolean;
|
|
89
|
+
clip: boolean;
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* The escape hatch: the game owns a Pixi object and `sync` only attaches and detaches it.
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* ```ts
|
|
96
|
+
* const value: DisplayValue = { object: spineAnimation };
|
|
97
|
+
* ```
|
|
98
|
+
*/
|
|
99
|
+
type DisplayValue = {
|
|
100
|
+
object: unknown;
|
|
101
|
+
};
|
|
102
|
+
/**
|
|
103
|
+
* A filled rounded rectangle or a triangle, anchored at the top left of the `Transform`. `kind`
|
|
104
|
+
* picks the outline: `"rect"` (the default) or `"triangle"`, which fills its `w × h` box pointing
|
|
105
|
+
* right (rotate the element for another direction) and ignores `radius`. `fillAlpha` is the alpha
|
|
106
|
+
* of the fill alone: 0 draws only the stroke, a ring. `alpha` fades the whole shape and the
|
|
107
|
+
* entity's children: a disabled button at 0.6 draws its icon at 0.6 too. `dash` is the
|
|
108
|
+
* dash length of the stroke in reference units, with gaps of half a dash; 0 strokes a solid line.
|
|
109
|
+
* `clip` masks the children of the entity to the shape, always solid, which is how `ui` draws a
|
|
110
|
+
* scroll and an overflow. The shape itself is never masked, so its stroke is drawn whole.
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* ```ts
|
|
114
|
+
* const value: ShapeValue = {
|
|
115
|
+
* kind: "rect", w: 320, h: 96, fill: 0x101018, fillAlpha: 1, alpha: 1, radius: 16,
|
|
116
|
+
* stroke: 0x000000, strokeWidth: 0, dash: 0, clip: false
|
|
117
|
+
* };
|
|
118
|
+
* // The honey ring on a selected cell: a stroke and no fill.
|
|
119
|
+
* const ring: ShapeValue = {
|
|
120
|
+
* kind: "rect", w: 140, h: 140, fill: 0xffffff, fillAlpha: 0, alpha: 1, radius: 24,
|
|
121
|
+
* stroke: 0xffc233, strokeWidth: 6, dash: 0, clip: false
|
|
122
|
+
* };
|
|
123
|
+
* // The play glyph of a watch button, and a dashed focus ring: 10 u dashes, 5 u gaps.
|
|
124
|
+
* const play: ShapeValue = { ...ring, kind: "triangle", w: 36, h: 40, fillAlpha: 1, strokeWidth: 0 };
|
|
125
|
+
* const focus: ShapeValue = { ...ring, w: 200, h: 80, stroke: 0x3a2212, strokeWidth: 4, dash: 10 };
|
|
126
|
+
* ```
|
|
127
|
+
*/
|
|
128
|
+
type ShapeValue = {
|
|
129
|
+
kind: "rect" | "triangle";
|
|
130
|
+
w: number;
|
|
131
|
+
h: number;
|
|
132
|
+
fill: number;
|
|
133
|
+
fillAlpha: number;
|
|
134
|
+
alpha: number;
|
|
135
|
+
radius: number;
|
|
136
|
+
stroke: number;
|
|
137
|
+
strokeWidth: number;
|
|
138
|
+
dash: number;
|
|
139
|
+
clip: boolean;
|
|
140
|
+
};
|
|
141
|
+
/**
|
|
142
|
+
* What `sprite()` takes: the two things every visual needs, and the four that have defaults.
|
|
143
|
+
*
|
|
144
|
+
* @example
|
|
145
|
+
* ```ts
|
|
146
|
+
* const options: SpriteOptions = { texture: "board.cell", at: { x: 540, y: 300 } };
|
|
147
|
+
* ```
|
|
148
|
+
*/
|
|
149
|
+
type SpriteOptions = {
|
|
150
|
+
texture: string;
|
|
151
|
+
at: Point$2;
|
|
152
|
+
tint?: number;
|
|
153
|
+
alpha?: number;
|
|
154
|
+
anchor?: Point$2;
|
|
155
|
+
scale?: number;
|
|
156
|
+
};
|
|
157
|
+
/**
|
|
158
|
+
* Where a view sits: reference units, radians, uniform scale, and the local point it turns
|
|
159
|
+
* around.
|
|
160
|
+
*/
|
|
161
|
+
declare const Transform: ComponentType<TransformValue>;
|
|
162
|
+
/**
|
|
163
|
+
* One textured quad. `texture` is an asset key, resolved through the texture providers; a size
|
|
164
|
+
* and a `fit` draw it into a box.
|
|
165
|
+
*/
|
|
166
|
+
declare const Sprite: ComponentType<SpriteValue>;
|
|
167
|
+
/**
|
|
168
|
+
* A stretchable panel, sized in reference units, with its own alpha and tint. `debug: true` draws
|
|
169
|
+
* the slice outline over it. `clip: true` masks the children of the entity to the panel box.
|
|
170
|
+
*/
|
|
171
|
+
declare const NineSlice: ComponentType<{
|
|
172
|
+
texture: string;
|
|
173
|
+
width: number;
|
|
174
|
+
height: number;
|
|
175
|
+
alpha: number;
|
|
176
|
+
tint: number;
|
|
177
|
+
debug: boolean;
|
|
178
|
+
clip: boolean;
|
|
179
|
+
}>;
|
|
180
|
+
/**
|
|
181
|
+
* The entity this view moves with. `0` means no parent.
|
|
182
|
+
*/
|
|
183
|
+
declare const Parent: ComponentType<{
|
|
184
|
+
entity: number;
|
|
185
|
+
}>;
|
|
186
|
+
/**
|
|
187
|
+
* A display object the game owns. Never pooled, never destroyed by `sync`.
|
|
188
|
+
*/
|
|
189
|
+
declare const Display: ComponentType<DisplayValue>;
|
|
190
|
+
/**
|
|
191
|
+
* A filled rounded rectangle or a right-pointing triangle, drawn with Pixi `Graphics` and redrawn
|
|
192
|
+
* only when a field changed. `fillAlpha: 0` draws only the stroke, `dash` above 0 dashes it.
|
|
193
|
+
* `clip: true` masks the children of the entity to the shape.
|
|
194
|
+
*/
|
|
195
|
+
declare const Shape: ComponentType<ShapeValue>;
|
|
196
|
+
/**
|
|
197
|
+
* Bundles the two components every visual needs, so a projection `view` reads as one line.
|
|
198
|
+
*
|
|
199
|
+
* @param options - Texture key, position, and the four values that have defaults.
|
|
200
|
+
* @returns The `Sprite` and the `Transform` value, in that order.
|
|
201
|
+
* @example
|
|
202
|
+
* ```ts
|
|
203
|
+
* sprite({ texture: "board.cell", at: { x: 540, y: 300 } });
|
|
204
|
+
* // [Sprite({ texture: "board.cell", tint: 0xffffff, alpha: 1, anchor: { x: 0.5, y: 0.5 },
|
|
205
|
+
* // width: 0, height: 0, fit: "fill" }),
|
|
206
|
+
* // Transform({ x: 540, y: 300, rotation: 0, scale: 1, pivot: { x: 0, y: 0 } })]
|
|
207
|
+
* ```
|
|
208
|
+
*/
|
|
209
|
+
declare function sprite(options: SpriteOptions): [ComponentValue<SpriteValue>, ComponentValue<TransformValue>];
|
|
210
|
+
declare namespace types_d_exports$2 {
|
|
211
|
+
export { Carried, Config$2 as Config, Deps$1 as Deps, Direction, GesturePhase, InputApi, InputCtx, KernelSlice$1 as KernelSlice, KeyInput, KeyListener, Point$1 as Point, PointerListener, RawSample, State$2 as State, TapListener, Target };
|
|
8
212
|
}
|
|
9
213
|
/**
|
|
10
214
|
* A point in reference units, as `renderer.viewport.toReference` answers it.
|
|
@@ -14,7 +218,7 @@ declare namespace types_d_exports$1 {
|
|
|
14
218
|
* const point: Point = { x: 540, y: 960 };
|
|
15
219
|
* ```
|
|
16
220
|
*/
|
|
17
|
-
type Point = {
|
|
221
|
+
type Point$1 = {
|
|
18
222
|
x: number;
|
|
19
223
|
y: number;
|
|
20
224
|
};
|
|
@@ -67,7 +271,7 @@ type RawSample = {
|
|
|
67
271
|
* const phase: GesturePhase = "dragging";
|
|
68
272
|
* ```
|
|
69
273
|
*/
|
|
70
|
-
type GesturePhase = "idle" | "pressed" | "longPressed" | "dragging";
|
|
274
|
+
type GesturePhase = "idle" | "pressed" | "longPressed" | "dragging" | "tracing";
|
|
71
275
|
/**
|
|
72
276
|
* What `onTap` hands a listener: the view the finger let go on, inside the tap slop.
|
|
73
277
|
*
|
|
@@ -127,9 +331,12 @@ type KeyListener = (key: KeyInput) => boolean | void;
|
|
|
127
331
|
* createApp({ plugins: [...screen], pluginConfigs: { input: { heldScale: 1.08 } } });
|
|
128
332
|
* // A game with its own cursors: the config merges shallowly, so both values are given.
|
|
129
333
|
* createApp({ plugins: [...screen], pluginConfigs: { input: { cursor: { control: "grab", idle: "default" } } } });
|
|
334
|
+
* // A word grid with 40 px cells: the finger's path is hit-tested every 16 px, below the
|
|
335
|
+
* // shortest cell span, so a fast finger skips no letter.
|
|
336
|
+
* createApp({ plugins: [...screen], pluginConfigs: { input: { traceStepPx: 16 } } });
|
|
130
337
|
* ```
|
|
131
338
|
*/
|
|
132
|
-
type Config$
|
|
339
|
+
type Config$2 = {
|
|
133
340
|
/** Largest move, in reference px, that still counts as a tap or a long press. */tapSlopPx: number; /** Hold time, in ms of `time`, after which a `Pressable` answers. */
|
|
134
341
|
longPressMs: number; /** Move, in reference px, that turns a press on a `Draggable` into a drag. */
|
|
135
342
|
dragStartPx: number; /** Shortest swipe, in reference px. */
|
|
@@ -152,11 +359,30 @@ type Config$1 = {
|
|
|
152
359
|
control: string;
|
|
153
360
|
idle: string;
|
|
154
361
|
};
|
|
362
|
+
/**
|
|
363
|
+
* Reference px between two hit tests along the finger's path inside one frame of a trace. Keep
|
|
364
|
+
* it below the shortest cell span: a longer step jumps over a cell.
|
|
365
|
+
*/
|
|
366
|
+
traceStepPx: number;
|
|
367
|
+
/**
|
|
368
|
+
* The hit circle of a trace cell: its radius is this times the short side of the cell's hit
|
|
369
|
+
* box, around the box centre. A diagonal then crosses no corner of a neighbour.
|
|
370
|
+
*/
|
|
371
|
+
traceInset: number;
|
|
372
|
+
};
|
|
373
|
+
/**
|
|
374
|
+
* One view a drag carries on top of the held one: hung under the held view from the grab to the
|
|
375
|
+
* release.
|
|
376
|
+
*/
|
|
377
|
+
type Carried = {
|
|
378
|
+
/** The carried view. */entity: Entity; /** The `Parent` it had at the grab, hung back under it on the release. */
|
|
379
|
+
parent: Entity | undefined; /** The remover `world.projection.mute` returned for its root pose. */
|
|
380
|
+
unmute: () => void;
|
|
155
381
|
};
|
|
156
382
|
/**
|
|
157
383
|
* input plugin state: the raw queue the DOM handlers write, and the one gesture in progress.
|
|
158
384
|
*/
|
|
159
|
-
type State$
|
|
385
|
+
type State$2 = {
|
|
160
386
|
/** Written by the DOM handlers, drained by the frame step. */samples: RawSample[];
|
|
161
387
|
phase: GesturePhase; /** The one active pointer. A sample of any other pointer is dropped while this is set. */
|
|
162
388
|
pointerId: number | undefined; /** The pressed or held entity. */
|
|
@@ -165,14 +391,24 @@ type State$1 = {
|
|
|
165
391
|
projection: string;
|
|
166
392
|
key: string;
|
|
167
393
|
} | undefined; /** Reference px at pointer down. */
|
|
168
|
-
start: Point; /** `Transform` minus the pointer at the moment of the grab. */
|
|
169
|
-
grabOffset: Point; /** Sum of `time.delta` since pointer down. No device clock is ever read. */
|
|
394
|
+
start: Point$1; /** `Transform` minus the pointer at the moment of the grab. */
|
|
395
|
+
grabOffset: Point$1; /** Sum of `time.delta` since pointer down. No device clock is ever read. */
|
|
170
396
|
pressedMs: number; /** The drop target that carries `Hovered` during a drag. */
|
|
171
397
|
hovered: Entity | undefined; /** The view that carries `PointerOver`: a press would take it, and a mouse or a pen is over it. */
|
|
172
398
|
pointerOver: Entity | undefined; /** The `Parent` the held view had at the grab. It is hung back under it on the release. */
|
|
173
399
|
parent: Entity | undefined; /** The rest scale of the held view in root space: the base of `heldScale`, set back on drop. */
|
|
174
|
-
restScale: number | undefined;
|
|
175
|
-
|
|
400
|
+
restScale: number | undefined;
|
|
401
|
+
/**
|
|
402
|
+
* The root pose of the held view, written at the grab and at every move. A carried view is
|
|
403
|
+
* placed from it when the held view despawns before the release and takes its `Transform`.
|
|
404
|
+
*/
|
|
405
|
+
heldPose: TransformValue | undefined; /** The remover `world.projection.mute` returned, while a drag runs. */
|
|
406
|
+
unmute: (() => void) | undefined; /** The views a drag carries on top of the held one, in `carry` order; empty outside a drag. */
|
|
407
|
+
carried: Carried[]; /** The cells of the trace in progress, in path order; empty outside a trace. */
|
|
408
|
+
path: Entity[]; /** The intent of the first cell of the trace; a cell of another intent closes the path. */
|
|
409
|
+
traceIntent: string | undefined; /** True once a cell of another intent was crossed: the path neither grows nor pops. */
|
|
410
|
+
traceClosed: boolean; /** Where the finger was at the last frame of the trace: the segment starts there. */
|
|
411
|
+
lastPoint: Point$1 | undefined;
|
|
176
412
|
canvas: HTMLCanvasElement | undefined;
|
|
177
413
|
offFrame: (() => void) | undefined;
|
|
178
414
|
detach: (() => void) | undefined; /** Registered through `onTap`, called in this order on every tap. */
|
|
@@ -252,6 +488,26 @@ type InputApi = {
|
|
|
252
488
|
* ```
|
|
253
489
|
*/
|
|
254
490
|
swipe(target: Target, direction: Direction): boolean;
|
|
491
|
+
/**
|
|
492
|
+
* Reads the `Traceable` of every cell and answers one path, the way a finger drawn through the
|
|
493
|
+
* cells would: the intent of the cells and `{ path }`, the cell payloads in order. Every cell
|
|
494
|
+
* must name the same intent. An empty list, a cell with no `Traceable` and two intents warn and
|
|
495
|
+
* answer `false` without calling the gate. Nothing is tagged `Traced`.
|
|
496
|
+
*
|
|
497
|
+
* @param path - The cells in the order the finger goes through them.
|
|
498
|
+
* @returns What `flow.gate.answer` returned: true when the gate took the answer.
|
|
499
|
+
* @example
|
|
500
|
+
* ```ts
|
|
501
|
+
* // A word-game test spells a word without a finger.
|
|
502
|
+
* app.input.trace([
|
|
503
|
+
* { projection: "board.cells", key: "b3" },
|
|
504
|
+
* { projection: "board.cells", key: "c3" },
|
|
505
|
+
* { projection: "board.cells", key: "d3" }
|
|
506
|
+
* ]);
|
|
507
|
+
* // true: answers { intent: "word", payload: { path: [{ cell: "b3" }, { cell: "c3" }, { cell: "d3" }] } }
|
|
508
|
+
* ```
|
|
509
|
+
*/
|
|
510
|
+
trace(path: readonly Target[]): boolean;
|
|
255
511
|
/**
|
|
256
512
|
* Registers a listener called on every tap — a press and a release inside `tapSlopPx` — on the
|
|
257
513
|
* topmost view the hit test accepted, before the `Tappable` answer. A view that carries only
|
|
@@ -375,7 +631,7 @@ type InputApi = {
|
|
|
375
631
|
/**
|
|
376
632
|
* Resolved dependency APIs.
|
|
377
633
|
*/
|
|
378
|
-
type Deps = {
|
|
634
|
+
type Deps$1 = {
|
|
379
635
|
time: Api;
|
|
380
636
|
flow: Api$1;
|
|
381
637
|
world: Api$2;
|
|
@@ -386,7 +642,7 @@ type Deps = {
|
|
|
386
642
|
*
|
|
387
643
|
* `input` owns no event, so `emit` is the kernel's and never called here.
|
|
388
644
|
*/
|
|
389
|
-
type KernelSlice = PluginCtx<Config$
|
|
645
|
+
type KernelSlice$1 = PluginCtx<Config$2, State$2> & {
|
|
390
646
|
readonly global: object;
|
|
391
647
|
readonly log: Log.LogApi;
|
|
392
648
|
readonly require: Require;
|
|
@@ -394,11 +650,11 @@ type KernelSlice = PluginCtx<Config$1, State$1> & {
|
|
|
394
650
|
/**
|
|
395
651
|
* Domain context of the input plugin: the kernel slice plus the four resolved dependencies.
|
|
396
652
|
*/
|
|
397
|
-
type InputCtx = KernelSlice & {
|
|
398
|
-
readonly deps: Deps;
|
|
653
|
+
type InputCtx = KernelSlice$1 & {
|
|
654
|
+
readonly deps: Deps$1;
|
|
399
655
|
};
|
|
400
|
-
declare namespace types_d_exports {
|
|
401
|
-
export { Argument, CompiledMessage, CompiledMessages, Config, ElementNode, Events, I18nApi, I18nCtx, I18nKit, IntlKit, KeysWithoutParameters, Message, Part, RegisteredModule, ResolvedModule, State, StringTable, StringsLoader, TypedTr };
|
|
656
|
+
declare namespace types_d_exports$1 {
|
|
657
|
+
export { Argument, CompiledMessage, CompiledMessages, Config$1 as Config, DurationStyle, ElementNode, Events, I18nApi, I18nCtx, I18nKit, IntlKit, KeysWithoutParameters, Message, Part, RegisteredModule, ResolvedModule, State$1 as State, StringTable, StringsLoader, TypedTr };
|
|
402
658
|
}
|
|
403
659
|
/**
|
|
404
660
|
* One interface element handed to a message as a parameter. Structural on purpose: it mirrors
|
|
@@ -475,7 +731,31 @@ type IntlKit = {
|
|
|
475
731
|
number(options?: Intl.NumberFormatOptions): Intl.NumberFormat; /** The list format of the locale, which ICU MessageFormat 1 has no syntax for. */
|
|
476
732
|
list(options?: Intl.ListFormatOptions): Intl.ListFormat; /** The date and time format of the locale. */
|
|
477
733
|
date(options?: Intl.DateTimeFormatOptions): Intl.DateTimeFormat;
|
|
734
|
+
/**
|
|
735
|
+
* The duration format of the locale. A compiled `{left, duration}` reads it with
|
|
736
|
+
* `secondsDisplay: "always"`, so a countdown at zero still says "0 sec".
|
|
737
|
+
*
|
|
738
|
+
* @param options - The `Intl.DurationFormat` options.
|
|
739
|
+
* @returns The memoised formatter for these options.
|
|
740
|
+
* @throws {Error} When the runtime has no `Intl.DurationFormat`.
|
|
741
|
+
* @example
|
|
742
|
+
* ```ts
|
|
743
|
+
* const intl = createIntlKit("en");
|
|
744
|
+
* intl.duration({ style: "short" }).format({ minutes: 1, seconds: 35 }); // "1 min, 35 sec"
|
|
745
|
+
* ```
|
|
746
|
+
*/
|
|
747
|
+
duration(options?: Intl.DurationFormatOptions): Intl.DurationFormat;
|
|
478
748
|
};
|
|
749
|
+
/**
|
|
750
|
+
* How a duration reads: `"long"` (1 minute, 35 seconds), `"short"` (1 min, 35 sec), `"narrow"`
|
|
751
|
+
* (1m 35s) or `"digital"` (0:01:35). A string file names one in `{left, duration, short}`.
|
|
752
|
+
*
|
|
753
|
+
* @example
|
|
754
|
+
* ```ts
|
|
755
|
+
* const style: DurationStyle = "digital"; // app.i18n.duration(95_000, style) is "0:01:35"
|
|
756
|
+
* ```
|
|
757
|
+
*/
|
|
758
|
+
type DurationStyle = "long" | "short" | "narrow" | "digital";
|
|
479
759
|
/**
|
|
480
760
|
* One message as the build wrote it: a plain function of the parameters and the locale's kit.
|
|
481
761
|
* There is no ICU parser at run time.
|
|
@@ -515,7 +795,7 @@ type StringsLoader = () => Promise<CompiledMessages | {
|
|
|
515
795
|
* createApp({ pluginConfigs: { i18n: { locale: "ru", fallback: "en", locales: {} } } });
|
|
516
796
|
* ```
|
|
517
797
|
*/
|
|
518
|
-
type Config = {
|
|
798
|
+
type Config$1 = {
|
|
519
799
|
/** The locale at start. */locale: string; /** The locale a missing key is read from before it is reported missing. */
|
|
520
800
|
fallback: string;
|
|
521
801
|
/**
|
|
@@ -542,7 +822,7 @@ type ResolvedModule = {
|
|
|
542
822
|
/**
|
|
543
823
|
* i18n plugin state.
|
|
544
824
|
*/
|
|
545
|
-
type State = {
|
|
825
|
+
type State$1 = {
|
|
546
826
|
/** The current locale; `config.locale` until `setLocale`. */locale: string; /** Per locale, one entry per feature and one for the config, filled in `onStart`. */
|
|
547
827
|
registered: Map<string, RegisteredModule[]>; /** Merged and resolved modules per locale. */
|
|
548
828
|
loaded: Map<string, CompiledMessages>; /** One kit per locale, built on first use. */
|
|
@@ -650,6 +930,25 @@ type I18nApi = {
|
|
|
650
930
|
* ```
|
|
651
931
|
*/
|
|
652
932
|
has(key: string): boolean;
|
|
933
|
+
/**
|
|
934
|
+
* Formats milliseconds in the current locale through `Intl.DurationFormat`. The value is rounded
|
|
935
|
+
* up to whole seconds, so a countdown never reads zero while time is left; a negative or
|
|
936
|
+
* non-finite value reads as zero; the seconds are always shown. `text` formats a field bound
|
|
937
|
+
* with `format: "duration"` through this call.
|
|
938
|
+
*
|
|
939
|
+
* @param ms - The duration in milliseconds.
|
|
940
|
+
* @param style - How the duration reads. Default `"short"`.
|
|
941
|
+
* @returns The duration as text.
|
|
942
|
+
* @throws {Error} When the runtime has no `Intl.DurationFormat`.
|
|
943
|
+
* @example
|
|
944
|
+
* ```ts
|
|
945
|
+
* // The out-of-energy popup shows when the next refill comes, locale "en".
|
|
946
|
+
* app.i18n.duration(95_000); // "1 min, 35 sec"
|
|
947
|
+
* app.i18n.duration(95_000, "digital"); // "0:01:35"
|
|
948
|
+
* app.i18n.duration(500); // "1 sec": rounded up
|
|
949
|
+
* ```
|
|
950
|
+
*/
|
|
951
|
+
duration(ms: number, style?: DurationStyle): string;
|
|
653
952
|
/**
|
|
654
953
|
* Every locale at least one feature or the config brought a module for, sorted.
|
|
655
954
|
*
|
|
@@ -669,264 +968,543 @@ type I18nApi = {
|
|
|
669
968
|
* row 8), so the own event reaches the context the kernel hands the factories and `emit` is the
|
|
670
969
|
* kernel's own, i18n-typed one. No member of the context is cast.
|
|
671
970
|
*/
|
|
672
|
-
type I18nCtx = PluginCtx<Config, State, Events> & {
|
|
971
|
+
type I18nCtx = PluginCtx<Config$1, State$1, Events> & {
|
|
673
972
|
readonly global: object;
|
|
674
973
|
readonly log: Log.LogApi;
|
|
675
974
|
readonly require: Require;
|
|
676
975
|
};
|
|
677
976
|
/**
|
|
678
|
-
* The key-to-parameters table a game generates: `generated/strings.ts` exports it as `Strings`.
|
|
679
|
-
* A key with no parameters carries `Record<never, never>`.
|
|
977
|
+
* The key-to-parameters table a game generates: `generated/strings.ts` exports it as `Strings`.
|
|
978
|
+
* A key with no parameters carries `Record<never, never>`.
|
|
979
|
+
*
|
|
980
|
+
* @example
|
|
981
|
+
* ```ts
|
|
982
|
+
* const table: StringTable = { "hud.orders": { n: 3 }, "orders.complete": {} };
|
|
983
|
+
* ```
|
|
984
|
+
*/
|
|
985
|
+
type StringTable = Record<string, unknown>;
|
|
986
|
+
/**
|
|
987
|
+
* The keys of a table whose message takes no parameters, so `tr` may be called with the key
|
|
988
|
+
* alone.
|
|
989
|
+
*
|
|
990
|
+
* @example
|
|
991
|
+
* ```ts
|
|
992
|
+
* type Keys = KeysWithoutParameters<{ "home.play": Record<never, never>; "hud.orders": { n: number } }>;
|
|
993
|
+
* // "home.play"
|
|
994
|
+
* ```
|
|
995
|
+
*/
|
|
996
|
+
type KeysWithoutParameters<Table extends StringTable> = { [Key in keyof Table]: Record<never, never> extends Table[Key] ? Key : never }[keyof Table] & string;
|
|
997
|
+
/**
|
|
998
|
+
* `tr` as one game reads it: the key is checked against the generated table, and the parameters
|
|
999
|
+
* are the ones that key declares. A key with no parameters takes no second argument.
|
|
1000
|
+
*
|
|
1001
|
+
* @example
|
|
1002
|
+
* ```ts
|
|
1003
|
+
* const { tr } = i18nFor<{ "hud.orders": { n: number } }>();
|
|
1004
|
+
* tr("hud.orders", { n: 3 }); // { key: "hud.orders", params: { n: 3 } }
|
|
1005
|
+
* ```
|
|
1006
|
+
*/
|
|
1007
|
+
type TypedTr<Table extends StringTable> = {
|
|
1008
|
+
<Key extends KeysWithoutParameters<Table>>(key: Key): Message<Key, Table[Key]>;
|
|
1009
|
+
<Key extends keyof Table & string>(key: Key, params: Table[Key]): Message<Key, Table[Key]>;
|
|
1010
|
+
};
|
|
1011
|
+
/**
|
|
1012
|
+
* What `defineGame` spreads from this plugin.
|
|
1013
|
+
*
|
|
1014
|
+
* @example
|
|
1015
|
+
* ```ts
|
|
1016
|
+
* const kit: I18nKit<{ "home.play": Record<never, never> }> = i18nFor();
|
|
1017
|
+
* kit.tr("home.play"); // { key: "home.play" }
|
|
1018
|
+
* ```
|
|
1019
|
+
*/
|
|
1020
|
+
type I18nKit<Table extends StringTable> = {
|
|
1021
|
+
tr: TypedTr<Table>;
|
|
1022
|
+
};
|
|
1023
|
+
declare namespace types_d_exports {
|
|
1024
|
+
export { AdvanceTable, BindField, BindOptions, BundleLoaded, BundleUnloaded, Config, CountdownValue, Deps, DrawnLabel, IconRun, KernelSlice, LayoutOptions, Line, LocaleChanged, Point, Run, SeenText, Size, State, TextAlign, TextApi, TextBind, TextCtx, TextFormat, TextLayout, TextRun, TextShadow, TextStyle, TextStyleInput, TextStyles, TextValue, TextWrap, Warn };
|
|
1025
|
+
}
|
|
1026
|
+
/**
|
|
1027
|
+
* A point in reference units: where a label sits, and which point of the block sits there.
|
|
1028
|
+
*
|
|
1029
|
+
* @example
|
|
1030
|
+
* ```ts
|
|
1031
|
+
* const anchor: Point = { x: 0.5, y: 0.5 };
|
|
1032
|
+
* ```
|
|
1033
|
+
*/
|
|
1034
|
+
type Point = {
|
|
1035
|
+
x: number;
|
|
1036
|
+
y: number;
|
|
1037
|
+
};
|
|
1038
|
+
/**
|
|
1039
|
+
* The size of a measured block, in reference pixels.
|
|
1040
|
+
*
|
|
1041
|
+
* @example
|
|
1042
|
+
* ```ts
|
|
1043
|
+
* const size: Size = { width: 57.6, height: 38.4 };
|
|
1044
|
+
* ```
|
|
1045
|
+
*/
|
|
1046
|
+
type Size = {
|
|
1047
|
+
width: number;
|
|
1048
|
+
height: number;
|
|
1049
|
+
};
|
|
1050
|
+
/**
|
|
1051
|
+
* How a bound number is shown. `"int"` rounds it; the three time formats take milliseconds and
|
|
1052
|
+
* show whole seconds, rounded up, so a timer never reads zero while time is left.
|
|
1053
|
+
*
|
|
1054
|
+
* @example
|
|
1055
|
+
* ```ts
|
|
1056
|
+
* const format: TextFormat = "mm:ss"; // 95_000 ms shows "01:35"
|
|
1057
|
+
* ```
|
|
1058
|
+
*/
|
|
1059
|
+
type TextFormat = "int" | "mm:ss" | "h:mm:ss" | "duration";
|
|
1060
|
+
/** The brand only `bind()` puts on a `TextBind`. Type-only: nothing is emitted for it. */
|
|
1061
|
+
declare const textBind: unique symbol;
|
|
1062
|
+
/**
|
|
1063
|
+
* Which numeric component field a label shows, and how. Plain data in the component, so a
|
|
1064
|
+
* snapshot lists it; nominal, so only `bind()` makes one and a hand-written literal does not
|
|
1065
|
+
* compile.
|
|
1066
|
+
*
|
|
1067
|
+
* @example
|
|
1068
|
+
* ```ts
|
|
1069
|
+
* const Counter = component("Counter", { value: 0 });
|
|
1070
|
+
* const coins: TextBind = bind(Counter, "value");
|
|
1071
|
+
* // { component: "Counter", field: "value", format: "int" }
|
|
1072
|
+
* ```
|
|
1073
|
+
*/
|
|
1074
|
+
type TextBind = {
|
|
1075
|
+
readonly component: string;
|
|
1076
|
+
readonly field: string;
|
|
1077
|
+
readonly format: TextFormat;
|
|
1078
|
+
} & {
|
|
1079
|
+
readonly [textBind]: true;
|
|
1080
|
+
};
|
|
1081
|
+
/**
|
|
1082
|
+
* The numeric fields of a component value, by name: what `bind()` takes as its field.
|
|
1083
|
+
*
|
|
1084
|
+
* @example
|
|
1085
|
+
* ```ts
|
|
1086
|
+
* type Fields = BindField<{ value: number; name: string }>; // "value"
|
|
1087
|
+
* ```
|
|
1088
|
+
*/
|
|
1089
|
+
type BindField<Value extends object> = keyof NumericFields<Value> & string;
|
|
1090
|
+
/**
|
|
1091
|
+
* What `bind()` takes after the field. The format is `"int"` when left out.
|
|
1092
|
+
*
|
|
1093
|
+
* @example
|
|
1094
|
+
* ```ts
|
|
1095
|
+
* const options: BindOptions = { format: "mm:ss" };
|
|
1096
|
+
* ```
|
|
1097
|
+
*/
|
|
1098
|
+
type BindOptions = {
|
|
1099
|
+
format?: TextFormat;
|
|
1100
|
+
};
|
|
1101
|
+
/**
|
|
1102
|
+
* A moment to count down to, and how much is left. A game writes `until`, a moment of `clock` in
|
|
1103
|
+
* epoch milliseconds; `left` is engine-owned and follows what the label shows.
|
|
1104
|
+
*
|
|
1105
|
+
* @example
|
|
1106
|
+
* ```ts
|
|
1107
|
+
* // clock.now() is 1_790_000_000_000: the chest opens in 95 s.
|
|
1108
|
+
* const chest: CountdownValue = { until: 1_790_000_095_000, left: 95_000 };
|
|
1109
|
+
* ```
|
|
1110
|
+
*/
|
|
1111
|
+
type CountdownValue = {
|
|
1112
|
+
until: number;
|
|
1113
|
+
left: number;
|
|
1114
|
+
};
|
|
1115
|
+
/**
|
|
1116
|
+
* What the `Text` component holds. `resolved` is engine-owned: a game writes `content`, `style`,
|
|
1117
|
+
* `bind`, `anchor` and `alpha`, and reads `resolved`. `alpha` fades every run, shadow and icon of
|
|
1118
|
+
* the block together, 1 by default; a change is written in place and a tween can drive it.
|
|
1119
|
+
*
|
|
1120
|
+
* @example
|
|
1121
|
+
* ```ts
|
|
1122
|
+
* const value: TextValue = {
|
|
1123
|
+
* content: "+5", style: "body", bind: undefined, anchor: { x: 0.5, y: 0.5 }, alpha: 1,
|
|
1124
|
+
* resolved: "+5"
|
|
1125
|
+
* };
|
|
1126
|
+
* // The hint of an empty name field, at half alpha.
|
|
1127
|
+
* const hint: TextValue = { ...value, content: "Your name", alpha: 0.5, resolved: "Your name" };
|
|
1128
|
+
* ```
|
|
1129
|
+
*/
|
|
1130
|
+
type TextValue = {
|
|
1131
|
+
content: string | Message;
|
|
1132
|
+
style: string;
|
|
1133
|
+
bind: TextBind | undefined;
|
|
1134
|
+
anchor: Point;
|
|
1135
|
+
alpha: number;
|
|
1136
|
+
resolved: string;
|
|
1137
|
+
};
|
|
1138
|
+
/**
|
|
1139
|
+
* Where the lines of a block sit inside the widest of them.
|
|
1140
|
+
*
|
|
1141
|
+
* @example
|
|
1142
|
+
* ```ts
|
|
1143
|
+
* const align: TextAlign = "center";
|
|
1144
|
+
* ```
|
|
1145
|
+
*/
|
|
1146
|
+
type TextAlign = "left" | "center" | "right";
|
|
1147
|
+
/**
|
|
1148
|
+
* The wrap width of a style in reference pixels, or `"none"` for one line per `\n` segment.
|
|
1149
|
+
*
|
|
1150
|
+
* @example
|
|
1151
|
+
* ```ts
|
|
1152
|
+
* const wrap: TextWrap = 480;
|
|
1153
|
+
* ```
|
|
1154
|
+
*/
|
|
1155
|
+
type TextWrap = number | "none";
|
|
1156
|
+
/**
|
|
1157
|
+
* The drop shadow of a style as the plugin stores it: a copy of every glyph run in `color`, at
|
|
1158
|
+
* `alpha`, moved by `dx` and `dy` reference pixels.
|
|
1159
|
+
*
|
|
1160
|
+
* @example
|
|
1161
|
+
* ```ts
|
|
1162
|
+
* const shadow: TextShadow = { color: 0x5b3a1e, dx: 0, dy: 4, alpha: 1 };
|
|
1163
|
+
* ```
|
|
1164
|
+
*/
|
|
1165
|
+
type TextShadow = {
|
|
1166
|
+
color: number;
|
|
1167
|
+
dx: number;
|
|
1168
|
+
dy: number;
|
|
1169
|
+
alpha: number;
|
|
1170
|
+
};
|
|
1171
|
+
/**
|
|
1172
|
+
* One style as a game writes it: the font and the size are required, the rest has defaults.
|
|
1173
|
+
*
|
|
1174
|
+
* @example
|
|
1175
|
+
* ```ts
|
|
1176
|
+
* // A popup title: cream display glyphs over a brown shadow 4 px down.
|
|
1177
|
+
* const title: TextStyleInput = {
|
|
1178
|
+
* font: "ui.font-display", size: 56, fill: 0xfff3d6, shadow: { color: 0x5b3a1e, dx: 0, dy: 4 }
|
|
1179
|
+
* };
|
|
1180
|
+
*
|
|
1181
|
+
* defineTextStyles({ "popup.title": title }).map["popup.title"]?.shadow;
|
|
1182
|
+
* // { color: 0x5b3a1e, dx: 0, dy: 4, alpha: 1 }
|
|
1183
|
+
* ```
|
|
1184
|
+
*/
|
|
1185
|
+
type TextStyleInput = {
|
|
1186
|
+
font: string;
|
|
1187
|
+
bold?: string;
|
|
1188
|
+
italic?: string;
|
|
1189
|
+
size: number;
|
|
1190
|
+
fill: number; /** The outline colour. Drawn only when `strokeWidth` is above 0. */
|
|
1191
|
+
stroke?: number;
|
|
1192
|
+
/**
|
|
1193
|
+
* The outline width in reference px: 8 copies of each glyph run on a circle this wide (12 from 6
|
|
1194
|
+
* on), tinted `stroke`. 0 draws none. It never changes what `measure` answers.
|
|
1195
|
+
*/
|
|
1196
|
+
strokeWidth?: number;
|
|
1197
|
+
letterSpacing?: number;
|
|
1198
|
+
align?: TextAlign;
|
|
1199
|
+
wrap?: TextWrap;
|
|
1200
|
+
digits?: boolean;
|
|
1201
|
+
/**
|
|
1202
|
+
* A drop shadow under every glyph run, in reference px at the style size. `alpha` is 1 when
|
|
1203
|
+
* left out. It never changes what `measure` answers.
|
|
1204
|
+
*/
|
|
1205
|
+
shadow?: {
|
|
1206
|
+
color: number;
|
|
1207
|
+
dx: number;
|
|
1208
|
+
dy: number;
|
|
1209
|
+
alpha?: number;
|
|
1210
|
+
};
|
|
1211
|
+
};
|
|
1212
|
+
/**
|
|
1213
|
+
* One style as the plugin stores it: every field filled, `bold` and `italic` only when the game
|
|
1214
|
+
* shipped those MSDF fonts, `shadow` only when the style has one.
|
|
680
1215
|
*
|
|
681
1216
|
* @example
|
|
682
1217
|
* ```ts
|
|
683
|
-
* const
|
|
1218
|
+
* const style: TextStyle = {
|
|
1219
|
+
* font: "ui.font-body", bold: undefined, italic: undefined, size: 32, fill: 0xffffff,
|
|
1220
|
+
* stroke: 0x000000, strokeWidth: 0, letterSpacing: 0, align: "left", wrap: "none",
|
|
1221
|
+
* digits: false, shadow: undefined
|
|
1222
|
+
* };
|
|
684
1223
|
* ```
|
|
685
1224
|
*/
|
|
686
|
-
type
|
|
1225
|
+
type TextStyle = {
|
|
1226
|
+
font: string;
|
|
1227
|
+
bold: string | undefined;
|
|
1228
|
+
italic: string | undefined;
|
|
1229
|
+
size: number;
|
|
1230
|
+
fill: number;
|
|
1231
|
+
stroke: number;
|
|
1232
|
+
strokeWidth: number;
|
|
1233
|
+
letterSpacing: number;
|
|
1234
|
+
align: TextAlign;
|
|
1235
|
+
wrap: TextWrap;
|
|
1236
|
+
digits: boolean;
|
|
1237
|
+
shadow: TextShadow | undefined;
|
|
1238
|
+
};
|
|
687
1239
|
/**
|
|
688
|
-
*
|
|
689
|
-
*
|
|
1240
|
+
* What `defineTextStyles` returns and a feature registers under its `textStyles` key. Structural
|
|
1241
|
+
* on purpose: `flow` carries the map, `text` reads it.
|
|
690
1242
|
*
|
|
691
1243
|
* @example
|
|
692
1244
|
* ```ts
|
|
693
|
-
*
|
|
694
|
-
* // "home.play"
|
|
1245
|
+
* const styles: TextStyles = { kind: "textStyles", map: { "hud.title": bodyStyle } };
|
|
695
1246
|
* ```
|
|
696
1247
|
*/
|
|
697
|
-
type
|
|
1248
|
+
type TextStyles = {
|
|
1249
|
+
readonly kind: "textStyles";
|
|
1250
|
+
readonly map: Record<string, TextStyle>;
|
|
1251
|
+
};
|
|
698
1252
|
/**
|
|
699
|
-
*
|
|
700
|
-
*
|
|
1253
|
+
* The advances of one font at the size the `.fnt` was exported with. Widths scale by
|
|
1254
|
+
* `style.size / size`; kerning pairs are ignored.
|
|
701
1255
|
*
|
|
702
1256
|
* @example
|
|
703
1257
|
* ```ts
|
|
704
|
-
* const
|
|
705
|
-
* tr("hud.orders", { n: 3 }); // { key: "hud.orders", params: { n: 3 } }
|
|
1258
|
+
* const table: AdvanceTable = { size: 32, lineHeight: 40, advances: new Map([["1", 18]]) };
|
|
706
1259
|
* ```
|
|
707
1260
|
*/
|
|
708
|
-
type
|
|
709
|
-
|
|
710
|
-
|
|
1261
|
+
type AdvanceTable = {
|
|
1262
|
+
size: number;
|
|
1263
|
+
lineHeight: number;
|
|
1264
|
+
advances: Map<string, number>;
|
|
711
1265
|
};
|
|
712
1266
|
/**
|
|
713
|
-
*
|
|
1267
|
+
* One run of glyphs: the text and the flags every glyph in it shares.
|
|
714
1268
|
*
|
|
715
1269
|
* @example
|
|
716
1270
|
* ```ts
|
|
717
|
-
* const
|
|
718
|
-
* kit.tr("home.play"); // { key: "home.play" }
|
|
1271
|
+
* const run: TextRun = { kind: "text", text: "+5", bold: true, italic: false, color: 0xffe082 };
|
|
719
1272
|
* ```
|
|
720
1273
|
*/
|
|
721
|
-
type
|
|
722
|
-
|
|
1274
|
+
type TextRun = {
|
|
1275
|
+
kind: "text";
|
|
1276
|
+
text: string;
|
|
1277
|
+
bold: boolean;
|
|
1278
|
+
italic: boolean;
|
|
1279
|
+
color: number | undefined;
|
|
723
1280
|
};
|
|
724
|
-
//#endregion
|
|
725
|
-
//#region src/plugins/renderer/components.d.ts
|
|
726
1281
|
/**
|
|
727
|
-
*
|
|
728
|
-
* `pivot` is the local point the view turns and scales around, and `x`, `y` is where that point
|
|
729
|
-
* lands; the default pivot `{ x: 0, y: 0 }` is the view's own origin.
|
|
1282
|
+
* One inline icon: an asset key drawn square at the line height.
|
|
730
1283
|
*
|
|
731
1284
|
* @example
|
|
732
1285
|
* ```ts
|
|
733
|
-
*
|
|
734
|
-
* const value: TransformValue = {
|
|
735
|
-
* x: 640, y: 340, rotation: 0, scale: 1.1, pivot: { x: 100, y: 40 }
|
|
736
|
-
* };
|
|
1286
|
+
* const run: IconRun = { kind: "icon", key: "hud.coin" };
|
|
737
1287
|
* ```
|
|
738
1288
|
*/
|
|
739
|
-
type
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
rotation: number;
|
|
743
|
-
scale: number;
|
|
744
|
-
pivot: Point$1;
|
|
1289
|
+
type IconRun = {
|
|
1290
|
+
kind: "icon";
|
|
1291
|
+
key: string;
|
|
745
1292
|
};
|
|
746
1293
|
/**
|
|
747
|
-
*
|
|
748
|
-
* box and centres, `"cover"` scales uniformly over the box and crops the overflow.
|
|
1294
|
+
* A piece of a line: glyphs or one inline icon.
|
|
749
1295
|
*
|
|
750
1296
|
* @example
|
|
751
1297
|
* ```ts
|
|
752
|
-
* const
|
|
1298
|
+
* const runs: Run[] = [{ kind: "icon", key: "hud.coin" }];
|
|
753
1299
|
* ```
|
|
754
1300
|
*/
|
|
755
|
-
type
|
|
1301
|
+
type Run = TextRun | IconRun;
|
|
756
1302
|
/**
|
|
757
|
-
* One
|
|
758
|
-
* `width` and `height` are the box in reference units; 0 on an axis keeps the texture's own size
|
|
759
|
-
* there. `anchor` is the point of the box that sits on the `Transform`. `alpha` fades the quad
|
|
760
|
-
* and the entity's children.
|
|
1303
|
+
* One laid-out line and how wide it is.
|
|
761
1304
|
*
|
|
762
1305
|
* @example
|
|
763
1306
|
* ```ts
|
|
764
|
-
* const
|
|
765
|
-
* texture: "board.bg-forest-meadow", tint: 0xffffff, alpha: 1, anchor: { x: 0, y: 0 },
|
|
766
|
-
* width: 1080, height: 1920, fit: "cover"
|
|
767
|
-
* };
|
|
1307
|
+
* const line: Line = { runs: [{ kind: "icon", key: "hud.coin" }], width: 38.4 };
|
|
768
1308
|
* ```
|
|
769
1309
|
*/
|
|
770
|
-
type
|
|
771
|
-
|
|
772
|
-
tint: number;
|
|
773
|
-
alpha: number;
|
|
774
|
-
anchor: Point$1;
|
|
1310
|
+
type Line = {
|
|
1311
|
+
runs: Run[];
|
|
775
1312
|
width: number;
|
|
776
|
-
height: number;
|
|
777
|
-
fit: SpriteFit;
|
|
778
1313
|
};
|
|
779
1314
|
/**
|
|
780
|
-
* A
|
|
781
|
-
* strokes the bounds and the four cut lines over the panel: cyan, red when the corners overlap or
|
|
782
|
-
* the texture is missing. `clip` masks the children of the entity to the `width × height` box,
|
|
783
|
-
* the same rule as `Shape.clip`: the mask is a filled rectangle and is never drawn, and the panel
|
|
784
|
-
* itself is never masked. `alpha` fades the panel and the entity's children.
|
|
1315
|
+
* A laid-out block: its lines and the size `measure` answers with.
|
|
785
1316
|
*
|
|
786
1317
|
* @example
|
|
787
1318
|
* ```ts
|
|
788
|
-
* const
|
|
789
|
-
* texture: "ui.panel", width: 600, height: 320, alpha: 1, tint: 0xffffff, debug: false,
|
|
790
|
-
* clip: false
|
|
791
|
-
* };
|
|
792
|
-
* // The board tray, outlined while its insets are checked.
|
|
793
|
-
* const tray: NineSliceValue = {
|
|
794
|
-
* ...value, texture: "board.board-tray", width: 1000, height: 1040, debug: true
|
|
795
|
-
* };
|
|
796
|
-
* // A text field: a long value never draws outside the frame.
|
|
797
|
-
* const field: NineSliceValue = {
|
|
798
|
-
* ...value, texture: "ui.field", width: 480, height: 96, clip: true
|
|
799
|
-
* };
|
|
1319
|
+
* const layout: TextLayout = { lines: [], width: 0, height: 0 };
|
|
800
1320
|
* ```
|
|
801
1321
|
*/
|
|
802
|
-
type
|
|
803
|
-
|
|
1322
|
+
type TextLayout = {
|
|
1323
|
+
lines: Line[];
|
|
804
1324
|
width: number;
|
|
805
1325
|
height: number;
|
|
806
|
-
alpha: number;
|
|
807
|
-
tint: number;
|
|
808
|
-
debug: boolean;
|
|
809
|
-
clip: boolean;
|
|
810
1326
|
};
|
|
811
1327
|
/**
|
|
812
|
-
*
|
|
1328
|
+
* What one label container was last filled from: the style object and the laid-out block. An
|
|
1329
|
+
* update with the same style object, the same anchor and the same lines and runs writes the new
|
|
1330
|
+
* text and positions into the objects already there.
|
|
1331
|
+
*/
|
|
1332
|
+
type DrawnLabel = {
|
|
1333
|
+
style: TextStyle;
|
|
1334
|
+
layout: TextLayout;
|
|
1335
|
+
};
|
|
1336
|
+
/**
|
|
1337
|
+
* A dev warning that is written once per key. Pure modules take it as an argument, so nothing
|
|
1338
|
+
* below the plugin context reaches the log itself.
|
|
813
1339
|
*
|
|
814
1340
|
* @example
|
|
815
1341
|
* ```ts
|
|
816
|
-
* const
|
|
1342
|
+
* const warn: Warn = (key, message) => seen.has(key) || log.warn(message);
|
|
817
1343
|
* ```
|
|
818
1344
|
*/
|
|
819
|
-
type
|
|
820
|
-
|
|
1345
|
+
type Warn = (key: string, message: string, data?: Record<string, unknown>) => void;
|
|
1346
|
+
/**
|
|
1347
|
+
* What the pure layout needs next to the runs, the style and the tables.
|
|
1348
|
+
*
|
|
1349
|
+
* @example
|
|
1350
|
+
* ```ts
|
|
1351
|
+
* const options: LayoutOptions = { warn: () => undefined };
|
|
1352
|
+
* ```
|
|
1353
|
+
*/
|
|
1354
|
+
type LayoutOptions = {
|
|
1355
|
+
warn: Warn;
|
|
821
1356
|
};
|
|
822
1357
|
/**
|
|
823
|
-
*
|
|
824
|
-
*
|
|
825
|
-
*
|
|
826
|
-
* of the fill alone: 0 draws only the stroke, a ring. `alpha` fades the whole shape and the
|
|
827
|
-
* entity's children: a disabled button at 0.6 draws its icon at 0.6 too. `dash` is the
|
|
828
|
-
* dash length of the stroke in reference units, with gaps of half a dash; 0 strokes a solid line.
|
|
829
|
-
* `clip` masks the children of the entity to the shape, always solid, which is how `ui` draws a
|
|
830
|
-
* scroll and an overflow. The shape itself is never masked, so its stroke is drawn whole.
|
|
1358
|
+
* What was resolved for one entity last, so the frame step knows whether anything moved. `unit`
|
|
1359
|
+
* is what the shown string of a bound label was built from: the rounded value for `"int"`, whole
|
|
1360
|
+
* seconds for a time format.
|
|
831
1361
|
*
|
|
832
1362
|
* @example
|
|
833
1363
|
* ```ts
|
|
834
|
-
* const
|
|
835
|
-
*
|
|
836
|
-
* stroke: 0x000000, strokeWidth: 0, dash: 0, clip: false
|
|
837
|
-
* };
|
|
838
|
-
* // The honey ring on a selected cell: a stroke and no fill.
|
|
839
|
-
* const ring: ShapeValue = {
|
|
840
|
-
* kind: "rect", w: 140, h: 140, fill: 0xffffff, fillAlpha: 0, alpha: 1, radius: 24,
|
|
841
|
-
* stroke: 0xffc233, strokeWidth: 6, dash: 0, clip: false
|
|
1364
|
+
* const seen: SeenText = {
|
|
1365
|
+
* content: "", style: "digits", bind: "Countdown.left:mm:ss", locale: "ru", unit: 95
|
|
842
1366
|
* };
|
|
843
|
-
* // The play glyph of a watch button, and a dashed focus ring: 10 u dashes, 5 u gaps.
|
|
844
|
-
* const play: ShapeValue = { ...ring, kind: "triangle", w: 36, h: 40, fillAlpha: 1, strokeWidth: 0 };
|
|
845
|
-
* const focus: ShapeValue = { ...ring, w: 200, h: 80, stroke: 0x3a2212, strokeWidth: 4, dash: 10 };
|
|
846
1367
|
* ```
|
|
847
1368
|
*/
|
|
848
|
-
type
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
alpha: number;
|
|
855
|
-
radius: number;
|
|
856
|
-
stroke: number;
|
|
857
|
-
strokeWidth: number;
|
|
858
|
-
dash: number;
|
|
859
|
-
clip: boolean;
|
|
1369
|
+
type SeenText = {
|
|
1370
|
+
content: unknown;
|
|
1371
|
+
style: string;
|
|
1372
|
+
bind: string | undefined;
|
|
1373
|
+
locale: string;
|
|
1374
|
+
unit: number | undefined;
|
|
860
1375
|
};
|
|
861
1376
|
/**
|
|
862
|
-
*
|
|
1377
|
+
* text plugin config.
|
|
863
1378
|
*
|
|
864
1379
|
* @example
|
|
865
1380
|
* ```ts
|
|
866
|
-
*
|
|
1381
|
+
* createApp({ pluginConfigs: { text: { fonts: { body: "ui.body", digits: "ui.digits" } } } });
|
|
867
1382
|
* ```
|
|
868
1383
|
*/
|
|
869
|
-
type
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
anchor?: Point$1;
|
|
875
|
-
scale?: number;
|
|
1384
|
+
type Config = {
|
|
1385
|
+
/** The two fonts every game ships in its boot bundle. They back the built-in styles. */fonts: {
|
|
1386
|
+
body: string;
|
|
1387
|
+
digits: string;
|
|
1388
|
+
};
|
|
876
1389
|
};
|
|
877
1390
|
/**
|
|
878
|
-
*
|
|
879
|
-
* around.
|
|
1391
|
+
* text plugin state.
|
|
880
1392
|
*/
|
|
881
|
-
|
|
1393
|
+
type State = {
|
|
1394
|
+
/** Built-in styles first, then the styles of every feature in feature order. */styles: Map<string, TextStyle>; /** Style name to the feature that brought it, for the duplicate error. */
|
|
1395
|
+
styleOwner: Map<string, string>; /** The config fonts plus every `font`, `bold` and `italic` of every style. */
|
|
1396
|
+
fontKeys: Set<string>; /** The parsed advance table per font key, filled when `assets` has the font. */
|
|
1397
|
+
tables: Map<string, AdvanceTable>; /** The font keys handed to `renderer.sync.fonts.install`. */
|
|
1398
|
+
installed: Set<string>; /** What was resolved for an entity last. */
|
|
1399
|
+
seen: Map<Entity, SeenText>; /** `bind.component` to its world type; `undefined` after the one warning. */
|
|
1400
|
+
bindTypes: Map<string, AnyComponent | undefined>; /** The measured size of every live label, for `ui`. */
|
|
1401
|
+
measured: Map<Entity, Size>; /** Layout per `style + resolved`, oldest dropped first. */
|
|
1402
|
+
cache: Map<string, TextLayout>; /** What every live label container was last filled from, by container. */
|
|
1403
|
+
drawn: WeakMap<object, DrawnLabel>; /** Entities to re-resolve in the next layout phase. */
|
|
1404
|
+
dirty: Set<Entity>; /** Warning keys already written, so a style, a tag, a glyph or a font warns once. */
|
|
1405
|
+
warned: Set<string>; /** The system, the two world hooks and the display adapter. */
|
|
1406
|
+
removers: Array<() => void>;
|
|
1407
|
+
};
|
|
882
1408
|
/**
|
|
883
|
-
*
|
|
884
|
-
*
|
|
1409
|
+
* text plugin API, `app.text`. Two questions: how big a piece of text is, and which styles the
|
|
1410
|
+
* game registered. Everything else happens in the frame.
|
|
1411
|
+
*
|
|
1412
|
+
* @example
|
|
1413
|
+
* ```ts
|
|
1414
|
+
* app.text.measure("120", "digits"); // { width: 57.6, height: 38.4 }
|
|
1415
|
+
* app.text.styles(); // ["body", "digits"]
|
|
1416
|
+
* ```
|
|
885
1417
|
*/
|
|
886
|
-
|
|
1418
|
+
type TextApi = {
|
|
1419
|
+
/**
|
|
1420
|
+
* The size of a piece of text in reference pixels, from the advance table of the style's font.
|
|
1421
|
+
* A `Message` is formatted in the current locale first. Never touches a canvas, so the answer
|
|
1422
|
+
* is the same in a browser and in plain Bun. Cached per style and resolved string.
|
|
1423
|
+
*
|
|
1424
|
+
* @param content - A plain string, or what `tr` returned.
|
|
1425
|
+
* @param style - A registered style name. An unknown one warns once and measures as `body`.
|
|
1426
|
+
* @returns The width and height of the block.
|
|
1427
|
+
* @example
|
|
1428
|
+
* ```ts
|
|
1429
|
+
* // `ui.layout` asks once per invalidation, from the Yoga measure function of a text element.
|
|
1430
|
+
* const text = ctx.require(textPlugin);
|
|
1431
|
+
*
|
|
1432
|
+
* text.measure("120", "digits"); // { width: 57.6, height: 38.4 } on the 0.6 em fallback
|
|
1433
|
+
* ```
|
|
1434
|
+
*/
|
|
1435
|
+
measure(content: string | Message, style: string): Size;
|
|
1436
|
+
/**
|
|
1437
|
+
* Whether the font of a style has a glyph for one character, the lookup Pixi draws by. A
|
|
1438
|
+
* character its font lacks is drawn as nothing and `measure` counts it 0 wide. While the font is
|
|
1439
|
+
* not loaded every character counts, as the 0.6 em fallback measures it. An unknown style warns
|
|
1440
|
+
* once and looks up `body`.
|
|
1441
|
+
*
|
|
1442
|
+
* @param char - One character, a whole code point.
|
|
1443
|
+
* @param style - A registered style name.
|
|
1444
|
+
* @returns False only for a character a loaded font has no glyph for.
|
|
1445
|
+
* @example
|
|
1446
|
+
* ```ts
|
|
1447
|
+
* // `ui` measures the caret of a text field over the characters the font draws.
|
|
1448
|
+
* const text = ctx.require(textPlugin);
|
|
1449
|
+
*
|
|
1450
|
+
* text.hasGlyph("A", "body"); // true
|
|
1451
|
+
* text.hasGlyph("😀", "body"); // false once the body font is loaded: it has no emoji glyph
|
|
1452
|
+
* ```
|
|
1453
|
+
*/
|
|
1454
|
+
hasGlyph(char: string, style: string): boolean;
|
|
1455
|
+
/**
|
|
1456
|
+
* The registered style names: the two built-ins first, then the styles of every feature in
|
|
1457
|
+
* feature order.
|
|
1458
|
+
*
|
|
1459
|
+
* @returns A frozen list of style names.
|
|
1460
|
+
* @example
|
|
1461
|
+
* ```ts
|
|
1462
|
+
* // A dev overlay lists what a game may write in a `style` prop.
|
|
1463
|
+
* app.text.styles(); // ["body", "digits", "hud.digits"]
|
|
1464
|
+
* ```
|
|
1465
|
+
*/
|
|
1466
|
+
styles(): readonly string[];
|
|
1467
|
+
};
|
|
887
1468
|
/**
|
|
888
|
-
*
|
|
889
|
-
* the slice outline over it. `clip: true` masks the children of the entity to the panel box.
|
|
1469
|
+
* Resolved dependency APIs.
|
|
890
1470
|
*/
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
}
|
|
1471
|
+
type Deps = {
|
|
1472
|
+
time: Api;
|
|
1473
|
+
flow: Api$1;
|
|
1474
|
+
world: Api$2;
|
|
1475
|
+
renderer: Api$3;
|
|
1476
|
+
assets: Api$4;
|
|
1477
|
+
i18n: I18nApi;
|
|
1478
|
+
clock: Api$5;
|
|
1479
|
+
};
|
|
900
1480
|
/**
|
|
901
|
-
*
|
|
1481
|
+
* What the kernel context offers before the deps are attached.
|
|
1482
|
+
*
|
|
1483
|
+
* `text` owns no event, so `emit` is the kernel's and never called here.
|
|
902
1484
|
*/
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
1485
|
+
type KernelSlice = PluginCtx<Config, State> & {
|
|
1486
|
+
readonly global: object;
|
|
1487
|
+
readonly log: Log.LogApi;
|
|
1488
|
+
readonly require: Require;
|
|
1489
|
+
};
|
|
906
1490
|
/**
|
|
907
|
-
*
|
|
1491
|
+
* Domain context shared by the files of the plugin: the kernel slice plus the resolved deps.
|
|
908
1492
|
*/
|
|
909
|
-
|
|
1493
|
+
type TextCtx = KernelSlice & {
|
|
1494
|
+
readonly deps: Deps;
|
|
1495
|
+
};
|
|
910
1496
|
/**
|
|
911
|
-
*
|
|
912
|
-
* only when a field changed. `fillAlpha: 0` draws only the stroke, `dash` above 0 dashes it.
|
|
913
|
-
* `clip: true` masks the children of the entity to the shape.
|
|
1497
|
+
* Payload of the `assets:bundle-loaded` hook: a bundle landed, so a font may be readable now.
|
|
914
1498
|
*/
|
|
915
|
-
|
|
1499
|
+
type BundleLoaded = Events$1["assets:bundle-loaded"];
|
|
916
1500
|
/**
|
|
917
|
-
*
|
|
918
|
-
*
|
|
919
|
-
* @param options - Texture key, position, and the four values that have defaults.
|
|
920
|
-
* @returns The `Sprite` and the `Transform` value, in that order.
|
|
921
|
-
* @example
|
|
922
|
-
* ```ts
|
|
923
|
-
* sprite({ texture: "board.cell", at: { x: 540, y: 300 } });
|
|
924
|
-
* // [Sprite({ texture: "board.cell", tint: 0xffffff, alpha: 1, anchor: { x: 0.5, y: 0.5 },
|
|
925
|
-
* // width: 0, height: 0, fit: "fill" }),
|
|
926
|
-
* // Transform({ x: 540, y: 300, rotation: 0, scale: 1, pivot: { x: 0, y: 0 } })]
|
|
927
|
-
* ```
|
|
1501
|
+
* Payload of the `assets:bundle-unloaded` hook: `keys` names the assets that left.
|
|
928
1502
|
*/
|
|
929
|
-
|
|
1503
|
+
type BundleUnloaded = Events$1["assets:bundle-unloaded"];
|
|
1504
|
+
/**
|
|
1505
|
+
* Payload of the `i18n:locale-changed` hook: every message has to be resolved again.
|
|
1506
|
+
*/
|
|
1507
|
+
type LocaleChanged = Events["i18n:locale-changed"];
|
|
930
1508
|
//#endregion
|
|
931
1509
|
//#region src/plugins/ui/jsx/component.d.ts
|
|
932
1510
|
/**
|
|
@@ -1314,11 +1892,6 @@ type ButtonLocal = Record<string, unknown> & Brand<"ButtonLocal">;
|
|
|
1314
1892
|
type ButtonEscape = boolean & Brand<"ButtonEscape">;
|
|
1315
1893
|
/** What a text draws: a plain string, or a message of the game's string table. */
|
|
1316
1894
|
type TextContent = (string | Message) & Brand<"TextContent">;
|
|
1317
|
-
/** The component field a text follows instead of a content. */
|
|
1318
|
-
type TextBind = {
|
|
1319
|
-
component: string;
|
|
1320
|
-
field: string;
|
|
1321
|
-
} & Brand<"TextBind">;
|
|
1322
1895
|
/** The text style key, or a layout style when the built-in `body` is meant. */
|
|
1323
1896
|
type TextStyleProp = (string | Style) & Brand<"TextStyleProp">;
|
|
1324
1897
|
/** The asset key of an image. */
|
|
@@ -1329,6 +1902,23 @@ type IconName = string & Brand<"IconName">;
|
|
|
1329
1902
|
type ImageFit = ("contain" | "cover" | "fill") & Brand<"ImageFit">;
|
|
1330
1903
|
/** The axis a scroll container moves on. `"x"` throws until V4. */
|
|
1331
1904
|
type ScrollAxis = ("x" | "y") & Brand<"ScrollAxis">;
|
|
1905
|
+
/** How many rows a windowed scroll has. */
|
|
1906
|
+
type ScrollRows = number & Brand<"ScrollRows">;
|
|
1907
|
+
/** The height of every row, in reference units; above 0. */
|
|
1908
|
+
type ScrollRowHeight = number & Brand<"ScrollRowHeight">;
|
|
1909
|
+
/** Rows kept alive beyond each edge of the viewport; 5 when left out. */
|
|
1910
|
+
type ScrollOverscan = number & Brand<"ScrollOverscan">;
|
|
1911
|
+
/**
|
|
1912
|
+
* Builds the row at an index; called at reconcile for the rows in the window, never at build time.
|
|
1913
|
+
*
|
|
1914
|
+
* @example
|
|
1915
|
+
* ```tsx
|
|
1916
|
+
* <scroll key="shop" style={list} rows={items.length} rowHeight={80} overscan={5}
|
|
1917
|
+
* row={index => <ShopRow key={items[index].id} item={items[index]} />} />;
|
|
1918
|
+
* // at offset 0: rows 0 to 24 exist, 25 of the 1000, for 80 u rows in a 1600 u list with overscan 5
|
|
1919
|
+
* ```
|
|
1920
|
+
*/
|
|
1921
|
+
type ScrollRow = ((index: number) => DescriptionNode) & Brand<"ScrollRow">;
|
|
1332
1922
|
/** The world projections whose live views a container draws inside itself. */
|
|
1333
1923
|
type HostedProjections = readonly string[] & Brand<"HostedProjections">;
|
|
1334
1924
|
/** The local field of the nearest component instance a text field writes on every keystroke. */
|
|
@@ -1409,11 +1999,15 @@ type ButtonTagProps = CommonProps & {
|
|
|
1409
1999
|
local?: ButtonLocal;
|
|
1410
2000
|
});
|
|
1411
2001
|
/**
|
|
1412
|
-
* What a text takes: a content or a bind, and the style key that carries its size.
|
|
2002
|
+
* What a text takes: a content or a bind, and the style key that carries its size. Only `bind()`
|
|
2003
|
+
* makes a bind; the text shows the bound field of a component of its own entity, so the bound
|
|
2004
|
+
* component goes in `components`.
|
|
1413
2005
|
*
|
|
1414
2006
|
* @example
|
|
1415
2007
|
* ```ts
|
|
1416
2008
|
* const props: TextTagProps = { content: "120", style: "digits" };
|
|
2009
|
+
* // The coin counter: Counter is an extra component, the text shows Math.round of its value.
|
|
2010
|
+
* const coins: TextTagProps = { bind: bind(Counter, "value"), components: [Counter({ value: 25 })] };
|
|
1417
2011
|
* ```
|
|
1418
2012
|
*/
|
|
1419
2013
|
type TextTagProps = Omit<CommonProps, "style"> & {
|
|
@@ -1460,17 +2054,40 @@ type IconProps = CommonProps & {
|
|
|
1460
2054
|
* ```
|
|
1461
2055
|
*/
|
|
1462
2056
|
type PanelProps = CommonProps;
|
|
2057
|
+
/** The child form of a scroll: its children are the content, laid out whole. */
|
|
2058
|
+
type ScrollChildForm = {
|
|
2059
|
+
rows?: never;
|
|
2060
|
+
rowHeight?: never;
|
|
2061
|
+
overscan?: never;
|
|
2062
|
+
row?: never;
|
|
2063
|
+
};
|
|
2064
|
+
/** The windowed form of a scroll: only the rows in view plus `overscan` exist. */
|
|
2065
|
+
type ScrollWindowForm = {
|
|
2066
|
+
rows: ScrollRows;
|
|
2067
|
+
rowHeight: ScrollRowHeight;
|
|
2068
|
+
overscan?: ScrollOverscan;
|
|
2069
|
+
row: ScrollRow;
|
|
2070
|
+
children?: never;
|
|
2071
|
+
};
|
|
1463
2072
|
/**
|
|
1464
|
-
* What a scroll container takes
|
|
2073
|
+
* What a scroll container takes: children for a short list, or `rows`, `rowHeight` and `row` for
|
|
2074
|
+
* a long one, whose rows exist only in the window.
|
|
1465
2075
|
*
|
|
1466
2076
|
* @example
|
|
1467
2077
|
* ```ts
|
|
1468
2078
|
* const props: ScrollProps = { axis: "y" };
|
|
2079
|
+
* // 1000 orders of 120 u in a 1600 u list: rows 0 to 18 exist at offset 0.
|
|
2080
|
+
* const orders: ScrollProps = {
|
|
2081
|
+
* style: { height: 1600 },
|
|
2082
|
+
* rows: 1000,
|
|
2083
|
+
* rowHeight: 120,
|
|
2084
|
+
* row: index => ({ type: "row", key: `order${index}`, props: {}, children: [] })
|
|
2085
|
+
* };
|
|
1469
2086
|
* ```
|
|
1470
2087
|
*/
|
|
1471
2088
|
type ScrollProps = CommonProps & {
|
|
1472
2089
|
axis?: ScrollAxis;
|
|
1473
|
-
};
|
|
2090
|
+
} & (ScrollChildForm | ScrollWindowForm);
|
|
1474
2091
|
/**
|
|
1475
2092
|
* What a text field takes. `local` is required: every keystroke writes that field of the local
|
|
1476
2093
|
* state of the nearest component, and Enter answers `submit` with `{ [local]: value }`. `style` is
|
|
@@ -1686,9 +2303,10 @@ type JsxChild = DescriptionNode | string | number | boolean | null | undefined |
|
|
|
1686
2303
|
type ElementChange<Value> = (view: ViewHandle<unknown>, previous: Value, next: Value, hint?: Hint) => Motion;
|
|
1687
2304
|
/**
|
|
1688
2305
|
* The motion hooks of one element: the hook triple of `world.projection`, which `defineMotion`
|
|
1689
|
-
* of `anim` returns. `ui` plays
|
|
1690
|
-
*
|
|
1691
|
-
* `defineMotion` result fits, and `ui`
|
|
2306
|
+
* of `anim` returns. `ui` plays the `change` hooks of `Box` (the rects), of `Transform` (the rest
|
|
2307
|
+
* poses) and of every extra component of the `components` prop (its values before and after). A
|
|
2308
|
+
* hook for any other component name is accepted, so every `defineMotion` result fits, and `ui`
|
|
2309
|
+
* never plays it. Every hook gets the hint routed to the element's key in that frame step.
|
|
1692
2310
|
*
|
|
1693
2311
|
* @example
|
|
1694
2312
|
* ```ts
|
|
@@ -1773,14 +2391,37 @@ type Instance = {
|
|
|
1773
2391
|
props: object;
|
|
1774
2392
|
dirty: boolean;
|
|
1775
2393
|
};
|
|
2394
|
+
/**
|
|
2395
|
+
* The range a windowed scroll holds and what it was cut with: `rows` rows of `rowHeight`, the
|
|
2396
|
+
* indexes `first` to `last` alive, `overscan` of them beyond each edge of the viewport. `last` is
|
|
2397
|
+
* `first - 1` for an empty list.
|
|
2398
|
+
*
|
|
2399
|
+
* @example
|
|
2400
|
+
* ```ts
|
|
2401
|
+
* // P18's list scrolled to -3840 u: 24 of its 1000 rows exist.
|
|
2402
|
+
* const window: ScrollWindow = { rows: 1000, rowHeight: 120, overscan: 5, first: 27, last: 50 };
|
|
2403
|
+
* ```
|
|
2404
|
+
*/
|
|
2405
|
+
type ScrollWindow = {
|
|
2406
|
+
rows: number;
|
|
2407
|
+
rowHeight: number;
|
|
2408
|
+
overscan: number;
|
|
2409
|
+
first: number;
|
|
2410
|
+
last: number;
|
|
2411
|
+
};
|
|
1776
2412
|
/**
|
|
1777
2413
|
* One live element: the entity, the Yoga node, the resolved style and the place in the tree.
|
|
1778
2414
|
* `rect` is natural: under a `fit` ancestor it is the rect before that ancestor's scale. `fit`
|
|
1779
2415
|
* is the element's own fit scale (1 without `fit: "contain"`), and `rest` the rest `Transform`
|
|
1780
2416
|
* last written for it. `loop` is the motion of the running `loop` hook, kept apart from
|
|
1781
2417
|
* `handles`: it never ends, so the exit sweep must not wait for it. `extras` holds the values of
|
|
1782
|
-
* the `components` prop added last time, by component name; `
|
|
1783
|
-
* `
|
|
2418
|
+
* the `components` prop added last time, by component name; `extraHandles` the motion the last
|
|
2419
|
+
* `change` hook of each of them returned; `warnedOwned` the names of the `components` values the
|
|
2420
|
+
* element owns that were already logged. A windowed scroll carries its `window` and the one-time
|
|
2421
|
+
* warnings of its props in `warned`; `scrolledIn` marks an element a range change of a windowed
|
|
2422
|
+
* scroll created: it plays no `enter` hook. A text or an icon that sizes itself keeps in
|
|
2423
|
+
* `measuredSize` what its measure function last answered, before Yoga's clamp: a bound text whose
|
|
2424
|
+
* new string measures the same asks for no solve.
|
|
1784
2425
|
*/
|
|
1785
2426
|
type Element = {
|
|
1786
2427
|
entity: Entity;
|
|
@@ -1817,7 +2458,15 @@ type Element = {
|
|
|
1817
2458
|
entered: boolean;
|
|
1818
2459
|
dropKey: (() => void) | undefined;
|
|
1819
2460
|
extras: ReadonlyMap<string, AnyComponentValue>;
|
|
2461
|
+
extraHandles: Map<string, MotionHandle>;
|
|
1820
2462
|
warnedOwned: Set<string>;
|
|
2463
|
+
warned: Set<string>;
|
|
2464
|
+
scrolledIn: boolean;
|
|
2465
|
+
window?: ScrollWindow;
|
|
2466
|
+
measuredSize?: {
|
|
2467
|
+
width: number;
|
|
2468
|
+
height: number;
|
|
2469
|
+
};
|
|
1821
2470
|
};
|
|
1822
2471
|
/**
|
|
1823
2472
|
* The popup side of a root: the handler that shows it now and the root it was opened over.
|
|
@@ -1971,7 +2620,9 @@ type TextState = {
|
|
|
1971
2620
|
};
|
|
1972
2621
|
/**
|
|
1973
2622
|
* jsx module state. `hosts` holds the elements with a `hosts` prop; `hosted` maps a world view to
|
|
1974
|
-
* the element that hosts it; `fields` holds the text fields by entity.
|
|
2623
|
+
* the element that hosts it; `fields` holds the text fields by entity. `hints` buffers the hints
|
|
2624
|
+
* released since the last frame step; `scrolls` holds the scroll containers; `windowRenders`
|
|
2625
|
+
* counts the re-renders of a windowed list its range made.
|
|
1975
2626
|
*/
|
|
1976
2627
|
type JsxState = {
|
|
1977
2628
|
components: Map<string, AnyComponentDefinition>;
|
|
@@ -1988,12 +2639,16 @@ type JsxState = {
|
|
|
1988
2639
|
focus: FocusState;
|
|
1989
2640
|
fields: Map<Entity, Field>;
|
|
1990
2641
|
text: TextState;
|
|
2642
|
+
hints: Hint[];
|
|
2643
|
+
scrolls: Set<Entity>;
|
|
2644
|
+
windowRenders: number;
|
|
1991
2645
|
};
|
|
1992
2646
|
/**
|
|
1993
2647
|
* One node of the snapshot `tree()` answers with. `rect` is natural: under a `fit` ancestor it
|
|
1994
2648
|
* is the rect before that ancestor's scale. An element with `fit: "contain"` adds `fitScale`,
|
|
1995
2649
|
* the scale it is drawn at. An `input` adds `value`: the text being typed while it is edited,
|
|
1996
|
-
* else the local field it writes.
|
|
2650
|
+
* else the local field it writes. A windowed `scroll` adds `window`: the rows that exist, which
|
|
2651
|
+
* are the children of its content, and how many the list has.
|
|
1997
2652
|
*
|
|
1998
2653
|
* @example
|
|
1999
2654
|
* ```ts
|
|
@@ -2019,6 +2674,20 @@ type UiNode = {
|
|
|
2019
2674
|
local?: Record<string, unknown>;
|
|
2020
2675
|
fitScale?: number;
|
|
2021
2676
|
value?: string;
|
|
2677
|
+
/**
|
|
2678
|
+
* The rows of a windowed scroll that exist and how many the list has.
|
|
2679
|
+
*
|
|
2680
|
+
* @example
|
|
2681
|
+
* ```ts
|
|
2682
|
+
* // P18's shop list of 1000 rows, 120 u each in 1600 u, scrolled to -3840 u: 24 rows exist.
|
|
2683
|
+
* const window: UiNode["window"] = { first: 27, last: 50, rows: 1000 };
|
|
2684
|
+
* ```
|
|
2685
|
+
*/
|
|
2686
|
+
window?: {
|
|
2687
|
+
first: number;
|
|
2688
|
+
last: number;
|
|
2689
|
+
rows: number;
|
|
2690
|
+
};
|
|
2022
2691
|
children: UiNode[];
|
|
2023
2692
|
};
|
|
2024
2693
|
/**
|
|
@@ -2055,4 +2724,4 @@ type CommonProps = {
|
|
|
2055
2724
|
components?: ElementComponents;
|
|
2056
2725
|
};
|
|
2057
2726
|
//#endregion
|
|
2058
|
-
export {
|
|
2727
|
+
export { SpriteValue as $, TextStyleInput as A, types_d_exports$1 as B, Config as C, TextApi as D, State as E, Config$1 as F, Display as G, InputApi as H, I18nApi as I, Parent as J, NineSlice as K, Message as L, TextValue as M, types_d_exports as N, TextBind as O, Argument as P, SpriteOptions as Q, State$1 as R, BindOptions as S, Point as T, State$2 as U, Config$2 as V, types_d_exports$2 as W, ShapeValue as X, Shape as Y, Sprite as Z, Style as _, JsxChild as a, defineComponent as b, UiNode as c, popup as d, Transform as et, defineTokens as f, ResolvedStyle as g, IsFlags as h, Finding as i, TextStyles as j, TextFormat as k, Box as l, UiIntrinsicElements as m, ElementChange as n, sprite as nt, JsxState as o, IntrinsicElementsFor as p, NineSliceValue as q, ElementMotion as r, PopupComponent as s, DescriptionNode as t, TransformValue as tt, LocalWrite as u, StylesState as v, CountdownValue as w, BindField as x, WhenFlags as y, TypedTr as z };
|