@moku-labs/game 0.2.0 → 0.3.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,4 +1,4 @@
|
|
|
1
|
-
import { a as JsxChild, m as UiIntrinsicElements, t as DescriptionNode } from "./types-
|
|
1
|
+
import { a as JsxChild, m as UiIntrinsicElements, t as DescriptionNode } from "./types-VErbiEXy.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/plugins/ui/jsx/runtime.d.ts
|
|
4
4
|
/** What the compiler passes as props: the children plus whatever the tag declared. */
|
|
@@ -1,60 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
//#region src/plugins/ui/jsx/flatten.ts
|
|
3
|
-
/** The type of the node `Fragment` builds. It never reaches a reconcile: `flatten` lifts it. */
|
|
4
|
-
const FRAGMENT = "#fragment";
|
|
5
|
-
/**
|
|
6
|
-
* Builds the node a bare string or number child becomes.
|
|
7
|
-
*
|
|
8
|
-
* @param content - What stood between the tags.
|
|
9
|
-
* @returns A text node carrying it.
|
|
10
|
-
* @example
|
|
11
|
-
* ```ts
|
|
12
|
-
* textNode(5); // { type: "text", props: { content: "5" }, children: [] }
|
|
13
|
-
* ```
|
|
14
|
-
*/
|
|
15
|
-
function textNode(content) {
|
|
16
|
-
return {
|
|
17
|
-
type: "text",
|
|
18
|
-
props: { content: String(content) },
|
|
19
|
-
children: []
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* Tells a description node from the other things a child may be.
|
|
24
|
-
*
|
|
25
|
-
* @param child - One child of a tag.
|
|
26
|
-
* @returns True when it is a node.
|
|
27
|
-
* @example
|
|
28
|
-
* ```ts
|
|
29
|
-
* isNode({ type: "row", props: {}, children: [] }); // true
|
|
30
|
-
* ```
|
|
31
|
-
*/
|
|
32
|
-
function isNode(child) {
|
|
33
|
-
return typeof child === "object" && child !== null && !Array.isArray(child) && "type" in child;
|
|
34
|
-
}
|
|
35
|
-
/**
|
|
36
|
-
* Turns whatever stood between two tags into the children of that tag.
|
|
37
|
-
*
|
|
38
|
-
* @param child - One child, an array of children, or nothing.
|
|
39
|
-
* @returns The children as nodes, fragments lifted and empty values dropped.
|
|
40
|
-
* @example
|
|
41
|
-
* ```ts
|
|
42
|
-
* flatten(["a", undefined, 2]).map(node => node.props.content); // ["a", "2"]
|
|
43
|
-
* ```
|
|
44
|
-
*/
|
|
45
|
-
function flatten(child) {
|
|
46
|
-
if (child === null || child === void 0 || typeof child === "boolean") return [];
|
|
47
|
-
if (typeof child === "string" || typeof child === "number") return [textNode(child)];
|
|
48
|
-
if (Array.isArray(child)) {
|
|
49
|
-
const nodes = [];
|
|
50
|
-
for (const item of child) nodes.push(...flatten(item));
|
|
51
|
-
return nodes;
|
|
52
|
-
}
|
|
53
|
-
if (!isNode(child)) return [];
|
|
54
|
-
if (child.type === "#fragment") return [...child.children];
|
|
55
|
-
return [child];
|
|
56
|
-
}
|
|
57
|
-
//#endregion
|
|
1
|
+
import { i as isComponentDefinition, n as flatten, t as FRAGMENT } from "./flatten-BkXuscAV.mjs";
|
|
58
2
|
//#region src/plugins/ui/jsx/runtime.ts
|
|
59
3
|
/**
|
|
60
4
|
* @file ui/jsx — the JSX runtime itself: the three factories a TypeScript build calls, the
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { i as scaleByFits } from "./visual-
|
|
2
|
-
import {
|
|
1
|
+
import { i as scaleByFits, w as Layer } from "./visual-DUvvPPCt.mjs";
|
|
2
|
+
import { c as defineSource, n as cheatsOf, o as readTarget, r as isTainted, t as inputOrEmpty } from "./input-CK4nxBO1.mjs";
|
|
3
3
|
//#region src/plugins/flow/doors/read.ts
|
|
4
4
|
/**
|
|
5
5
|
* @file flow/doors — `read` and `watch`, the `/inspect` side. Neither changes the game.
|
|
@@ -171,9 +171,27 @@ const modelSource = defineSource({
|
|
|
171
171
|
//#endregion
|
|
172
172
|
//#region src/plugins/renderer/inspect.ts
|
|
173
173
|
/**
|
|
174
|
-
* @file renderer plugin — the renderer
|
|
175
|
-
* Production-safe:
|
|
174
|
+
* @file renderer plugin — the renderer sources of the `/inspect` door: the frame counters, and what
|
|
175
|
+
* lies under a point of the page. Production-safe: both only read.
|
|
176
|
+
*/
|
|
177
|
+
/**
|
|
178
|
+
* Names one entity under a point: its owner, the projection and key that address it, and the
|
|
179
|
+
* layer its own `Layer` names.
|
|
180
|
+
*
|
|
181
|
+
* @param world - The world API.
|
|
182
|
+
* @param entity - The entity hit.
|
|
183
|
+
* @returns What lies there, or `undefined` for an entity that despawned this frame.
|
|
176
184
|
*/
|
|
185
|
+
function underOf(world, entity) {
|
|
186
|
+
const owner = world.ecs.ownerOf(entity);
|
|
187
|
+
if (owner === void 0) return void 0;
|
|
188
|
+
return {
|
|
189
|
+
entity,
|
|
190
|
+
owner,
|
|
191
|
+
key: world.projection.keyOf(entity),
|
|
192
|
+
layer: world.ecs.get(entity, Layer)?.name
|
|
193
|
+
};
|
|
194
|
+
}
|
|
177
195
|
/**
|
|
178
196
|
* The counters of the renderer: frames per second, frame work, GPU textures and their memory,
|
|
179
197
|
* views and pooled display objects, render passes, and in a dev build the draw calls.
|
|
@@ -192,11 +210,44 @@ const renderSource = defineSource({
|
|
|
192
210
|
changes: "frame",
|
|
193
211
|
read: (app) => app.renderer.stats()
|
|
194
212
|
});
|
|
213
|
+
/**
|
|
214
|
+
* What lies under a point of the page, topmost first: the point in client CSS pixels, the
|
|
215
|
+
* coordinates `game.locate` answers and Playwright clicks, mapped through
|
|
216
|
+
* `viewport.toReference`, and every view whose hit box holds it, with the skip rules of a tap.
|
|
217
|
+
* `[]` while the renderer is inert and where nothing is drawn.
|
|
218
|
+
*
|
|
219
|
+
* @example
|
|
220
|
+
* ```ts
|
|
221
|
+
* // An agent asks what lies under the pointer on a 390 px wide phone: a shine over a board cell.
|
|
222
|
+
* read(app, sources.at, { x: 195, y: 108.33 });
|
|
223
|
+
* // [{ entity: 1048577, owner: { kind: "plugin", name: "fx" }, key: undefined, layer: undefined },
|
|
224
|
+
* // { entity: 1048576, owner: { kind: "projection", name: "board.items" },
|
|
225
|
+
* // key: { projection: "board.items", key: "c7" }, layer: "items" }]
|
|
226
|
+
* ```
|
|
227
|
+
*/
|
|
228
|
+
const atSource = defineSource({
|
|
229
|
+
id: "game.at",
|
|
230
|
+
title: "Under a point",
|
|
231
|
+
input: {
|
|
232
|
+
x: "number",
|
|
233
|
+
y: "number"
|
|
234
|
+
},
|
|
235
|
+
changes: "frame",
|
|
236
|
+
read: (app, { x, y }) => {
|
|
237
|
+
const point = app.renderer.viewport.toReference(x, y);
|
|
238
|
+
const found = [];
|
|
239
|
+
for (const entity of app.renderer.sync.hitAll(point.x, point.y)) {
|
|
240
|
+
const under = underOf(app.world, entity);
|
|
241
|
+
if (under !== void 0) found.push(under);
|
|
242
|
+
}
|
|
243
|
+
return found;
|
|
244
|
+
}
|
|
245
|
+
});
|
|
195
246
|
//#endregion
|
|
196
247
|
//#region src/plugins/ui/inspect.ts
|
|
197
248
|
/**
|
|
198
|
-
* @file ui plugin — the ui sources of the `/inspect` door: the live screen as data and
|
|
199
|
-
*
|
|
249
|
+
* @file ui plugin — the ui sources of the `/inspect` door: the live screen as data, and where an
|
|
250
|
+
* element or a view is on the page. Production-safe: every one only reads.
|
|
200
251
|
*/
|
|
201
252
|
/**
|
|
202
253
|
* Finds a keyed node and the nodes above it.
|
|
@@ -253,6 +304,37 @@ function onPage(renderer, rect) {
|
|
|
253
304
|
};
|
|
254
305
|
}
|
|
255
306
|
/**
|
|
307
|
+
* Where a live ui element is on the page: its layout box, scaled by every fitted element above
|
|
308
|
+
* it, through the viewport.
|
|
309
|
+
*
|
|
310
|
+
* @param app - The app with ui and renderer.
|
|
311
|
+
* @param key - The `key` prop of the element.
|
|
312
|
+
* @returns The rect in CSS px, or `undefined` when no live element has the key.
|
|
313
|
+
*/
|
|
314
|
+
function locateKey(app, key) {
|
|
315
|
+
const chain = app.ui.find(key) !== void 0 ? chainTo(app.ui.tree(), key) : void 0;
|
|
316
|
+
return chain === void 0 ? void 0 : onPage(app.renderer, drawnRect(chain));
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Where a view is on the page: the box the renderer draws for the entity of its projection key,
|
|
320
|
+
* through the viewport.
|
|
321
|
+
*
|
|
322
|
+
* @param app - The app with ui, renderer and world.
|
|
323
|
+
* @param target - The projection key of the view.
|
|
324
|
+
* @returns The rect in CSS px, or `undefined` when the view is not live or has no drawn box.
|
|
325
|
+
*/
|
|
326
|
+
function locateTarget(app, target) {
|
|
327
|
+
const entity = app.world.projection.entityOf(target.projection, target.key);
|
|
328
|
+
const bounds = entity === void 0 ? void 0 : app.renderer.sync.boundsOf(entity);
|
|
329
|
+
if (bounds === void 0) return void 0;
|
|
330
|
+
return onPage(app.renderer, {
|
|
331
|
+
x: bounds.x,
|
|
332
|
+
y: bounds.y,
|
|
333
|
+
w: bounds.width,
|
|
334
|
+
h: bounds.height
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
256
338
|
* The live screen as plain data: every root, every element with its natural rect, its style,
|
|
257
339
|
* its state flags and the fit scale of a fitted one.
|
|
258
340
|
*
|
|
@@ -270,32 +352,42 @@ const uiSource = defineSource({
|
|
|
270
352
|
read: (app) => app.ui.tree()
|
|
271
353
|
});
|
|
272
354
|
/**
|
|
273
|
-
* Where
|
|
274
|
-
*
|
|
275
|
-
* the renderer
|
|
355
|
+
* Where something is on the page, in CSS px, by either of the two addresses the doors use: a ui
|
|
356
|
+
* element by its `key` (its layout box, scaled by every fitted element above it, so a bare button
|
|
357
|
+
* with no fill has one), or a view by its `target` projection key (the box the renderer draws for
|
|
358
|
+
* it). Both go through the viewport, so the numbers are what Playwright clicks. In reference units
|
|
359
|
+
* while the renderer is inert; a view has no drawn box then.
|
|
276
360
|
*
|
|
277
361
|
* @example
|
|
278
362
|
* ```ts
|
|
279
363
|
* // An e2e script clicks Home's Play plank on a 390 px wide phone.
|
|
280
|
-
* read(app, sources.
|
|
281
|
-
*
|
|
364
|
+
* read(app, sources.locate, { key: "play" }); // { x: 101.9, y: 469.2, w: 189.2, h: 54.6 }
|
|
365
|
+
* // An agent finds the coin c7 before it drags it: 64 x 64 u, drawn at (540, 300).
|
|
366
|
+
* read(app, sources.locate, { target: { projection: "board.items", key: "c7" } });
|
|
367
|
+
* // { x: 183.4, y: 96.8, w: 23.1, h: 23.1 }; undefined while nothing draws it
|
|
282
368
|
* ```
|
|
283
369
|
*/
|
|
284
|
-
const
|
|
285
|
-
id: "game.
|
|
286
|
-
title: "
|
|
287
|
-
input: {
|
|
370
|
+
const locateSource = defineSource({
|
|
371
|
+
id: "game.locate",
|
|
372
|
+
title: "Locate",
|
|
373
|
+
input: {
|
|
374
|
+
key: "string?",
|
|
375
|
+
target: "json?"
|
|
376
|
+
},
|
|
288
377
|
changes: "frame",
|
|
289
|
-
read: (app, { key }) => {
|
|
290
|
-
|
|
291
|
-
|
|
378
|
+
read: (app, { key, target }) => {
|
|
379
|
+
if (key !== void 0 && target === void 0) return locateKey(app, key);
|
|
380
|
+
if (target !== void 0 && key === void 0) return locateTarget(app, readTarget(target));
|
|
381
|
+
throw new Error("[game] game.locate takes a key or a target.\n Pass exactly one of { key } and { target }.");
|
|
292
382
|
}
|
|
293
383
|
});
|
|
294
384
|
//#endregion
|
|
295
385
|
//#region src/plugins/world/inspect.ts
|
|
296
386
|
/**
|
|
297
387
|
* @file world plugin — the world sources of the `/inspect` door: the entities, filtered by owner
|
|
298
|
-
* and component,
|
|
388
|
+
* and component, the projection keys, one entity in full, what changed between two frames and
|
|
389
|
+
* the component schema. Every source only reads; `game.diff` answers in a dev build only, because
|
|
390
|
+
* only a dev build keeps the frame history.
|
|
299
391
|
*/
|
|
300
392
|
/**
|
|
301
393
|
* Tells whether an entity carries a component, as a JSON value or as a skipped one.
|
|
@@ -356,6 +448,87 @@ const projectionsSource = defineSource({
|
|
|
356
448
|
return keys;
|
|
357
449
|
}
|
|
358
450
|
});
|
|
451
|
+
/**
|
|
452
|
+
* One entity in full: its owner, its projection key, every JSON component, the names of the
|
|
453
|
+
* skipped ones and the components a motion still drives. `undefined` for a stale id. The source
|
|
454
|
+
* reads the whole snapshot once per read and picks the row.
|
|
455
|
+
*
|
|
456
|
+
* @example
|
|
457
|
+
* ```ts
|
|
458
|
+
* // The editor explains item i7 while it slides to its new cell.
|
|
459
|
+
* read(app, sources.explain, { entity: 1048577 });
|
|
460
|
+
* // { id: 1048577, owner: { kind: "projection", name: "board.items" },
|
|
461
|
+
* // key: { projection: "board.items", key: "i7" },
|
|
462
|
+
* // components: { Transform: { x: 64, y: 0, rotation: 0, scale: 1, pivot: { x: 0, y: 0 } },
|
|
463
|
+
* // Layer: { name: "items" } }, skipped: [], motions: ["Transform"] }
|
|
464
|
+
* read(app, sources.explain, { entity: 42 }); // undefined: a stale id
|
|
465
|
+
* ```
|
|
466
|
+
*/
|
|
467
|
+
const explainSource = defineSource({
|
|
468
|
+
id: "game.explain",
|
|
469
|
+
title: "Entity",
|
|
470
|
+
input: { entity: "number" },
|
|
471
|
+
changes: "frame",
|
|
472
|
+
read: (app, { entity }) => {
|
|
473
|
+
const row = app.world.ecs.snapshot().entities.find((candidate) => candidate.id === entity);
|
|
474
|
+
if (row === void 0) return void 0;
|
|
475
|
+
return {
|
|
476
|
+
id: row.id,
|
|
477
|
+
owner: row.owner,
|
|
478
|
+
key: app.world.projection.keyOf(row.id),
|
|
479
|
+
components: row.components,
|
|
480
|
+
skipped: row.skipped,
|
|
481
|
+
motions: app.world.projection.motionsOf(row.id)
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
});
|
|
485
|
+
/**
|
|
486
|
+
* What changed between the world at the end of frame `from` and at the end of frame `to`, from
|
|
487
|
+
* the frame history of a dev build: one entry per entity, sorted by id. Throws in a production
|
|
488
|
+
* build and for a frame outside the last 120.
|
|
489
|
+
*
|
|
490
|
+
* @example
|
|
491
|
+
* ```ts
|
|
492
|
+
* // The editor asks what the last second of play changed: item i7 merged up to level 3.
|
|
493
|
+
* read(app, sources.diff, { from: 120, to: 180 });
|
|
494
|
+
* // { from: 120, to: 180, entities: [{ id: 1048577,
|
|
495
|
+
* // owner: { kind: "projection", name: "board.items" },
|
|
496
|
+
* // key: { projection: "board.items", key: "i7" }, change: "changed",
|
|
497
|
+
* // components: { Level: { from: { level: 2 }, to: { level: 3 } } } }] }
|
|
498
|
+
* ```
|
|
499
|
+
*/
|
|
500
|
+
const diffSource = defineSource({
|
|
501
|
+
id: "game.diff",
|
|
502
|
+
title: "Frame diff",
|
|
503
|
+
input: {
|
|
504
|
+
from: "number",
|
|
505
|
+
to: "number"
|
|
506
|
+
},
|
|
507
|
+
changes: "frame",
|
|
508
|
+
read: (app, { from, to }) => app.world.ecs.diff(from, to)
|
|
509
|
+
});
|
|
510
|
+
/**
|
|
511
|
+
* Every component and tag type the world met so far, sorted by name, with the JSON kind of each
|
|
512
|
+
* field: the editor's component palette. A system may write a new component on any frame, so it
|
|
513
|
+
* is read again every frame.
|
|
514
|
+
*
|
|
515
|
+
* @example
|
|
516
|
+
* ```ts
|
|
517
|
+
* // The editor's component palette, in a world that has met Transform and the tag Held.
|
|
518
|
+
* read(app, sources.schema);
|
|
519
|
+
* // [{ name: "Held", kind: "tag", json: true, fields: {}, defaults: true, owned: [] },
|
|
520
|
+
* // { name: "Transform", kind: "component", json: true,
|
|
521
|
+
* // fields: { x: "number", y: "number", rotation: "number", scale: "number", pivot: "object" },
|
|
522
|
+
* // defaults: { x: 0, y: 0, rotation: 0, scale: 1, pivot: { x: 0, y: 0 } }, owned: [] }]
|
|
523
|
+
* ```
|
|
524
|
+
*/
|
|
525
|
+
const schemaSource = defineSource({
|
|
526
|
+
id: "game.schema",
|
|
527
|
+
title: "Components",
|
|
528
|
+
input: {},
|
|
529
|
+
changes: "frame",
|
|
530
|
+
read: (app) => app.world.ecs.schema()
|
|
531
|
+
});
|
|
359
532
|
//#endregion
|
|
360
533
|
//#region src/plugins/flow/inspect.ts
|
|
361
534
|
/** The levels of `Log.LogLevel`, least severe first, as the names a caller passes. */
|
|
@@ -534,12 +707,16 @@ const sources = Object.freeze({
|
|
|
534
707
|
entities: entitiesSource,
|
|
535
708
|
projections: projectionsSource,
|
|
536
709
|
ui: uiSource,
|
|
537
|
-
|
|
710
|
+
locate: locateSource,
|
|
538
711
|
render: renderSource,
|
|
539
712
|
sounds: soundsSource,
|
|
540
713
|
effects: effectsSource,
|
|
541
714
|
assets: assetsSource,
|
|
542
|
-
log: logSource
|
|
715
|
+
log: logSource,
|
|
716
|
+
explain: explainSource,
|
|
717
|
+
diff: diffSource,
|
|
718
|
+
schema: schemaSource,
|
|
719
|
+
at: atSource
|
|
543
720
|
});
|
|
544
721
|
//#endregion
|
|
545
722
|
export { read as n, watch as r, sources as t };
|
package/dist/testing.d.mts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { _ as stepFrames, a as InputOf, d as HeadlessApp, f as HeadlessGame, g as runRepro, h as createHeadless, m as ReproResult, p as Repro, r as ControlApp } from "./types-
|
|
3
|
-
import { H as InputApi } from "./types-
|
|
4
|
-
import { A as UiApi, t as AnimApi } from "./types-
|
|
5
|
-
import { t as commands } from "./commands-
|
|
1
|
+
import { $t as SaveDoc, Kt as Json, Qt as ProviderCall, Ut as Api$2, Zt as PlayerStateProvider, an as Api$3, k as Api$1, m as Api, pn as FakeClock } from "./types-Dy1xB68L.mjs";
|
|
2
|
+
import { _ as stepFrames, a as InputOf, d as HeadlessApp, f as HeadlessGame, g as runRepro, h as createHeadless, m as ReproResult, p as Repro, r as ControlApp } from "./types-DtmFVCHf.mjs";
|
|
3
|
+
import { H as InputApi } from "./types-VErbiEXy.mjs";
|
|
4
|
+
import { A as UiApi, t as AnimApi } from "./types-CVUHPvMB.mjs";
|
|
5
|
+
import { t as commands } from "./commands-DYO22lzi.mjs";
|
|
6
6
|
|
|
7
7
|
//#region src/plugins/clock/fake.d.ts
|
|
8
8
|
/**
|
package/dist/testing.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { n as saveOf, t as memory } from "./memory-CvgdnsQO.mjs";
|
|
2
|
-
import { a as stepFrames, i as runRepro, n as commands, r as createHeadless, t as run } from "./run-
|
|
3
|
-
import { n as read, t as sources } from "./sources-
|
|
2
|
+
import { a as stepFrames, i as runRepro, n as commands, r as createHeadless, t as run } from "./run-BfuMLahc.mjs";
|
|
3
|
+
import { n as read, t as sources } from "./sources-C1uGjz4l.mjs";
|
|
4
4
|
import { createBrandConsole } from "@moku-labs/common/cli";
|
|
5
5
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
6
6
|
import path from "node:path";
|
|
@@ -1242,7 +1242,7 @@ async function pageCheckpoint(argument) {
|
|
|
1242
1242
|
await pageTick(handles);
|
|
1243
1243
|
await pageTick(handles);
|
|
1244
1244
|
const captured = await game.renderer.capture();
|
|
1245
|
-
const screen = captured === void 0 ? void 0 : await pageWebp(captured);
|
|
1245
|
+
const screen = captured === void 0 ? void 0 : await pageWebp(captured.png);
|
|
1246
1246
|
return {
|
|
1247
1247
|
ok: true,
|
|
1248
1248
|
value: {
|
|
@@ -1385,7 +1385,8 @@ async function pageEncode(pixels) {
|
|
|
1385
1385
|
return `data:image/webp;base64,${btoa(binary)}`;
|
|
1386
1386
|
}
|
|
1387
1387
|
/**
|
|
1388
|
-
* Turns the PNG data URL of `renderer.capture()` into the lossless WebP of the
|
|
1388
|
+
* Turns the PNG data URL of `renderer.capture()`, its `png`, into the lossless WebP of the
|
|
1389
|
+
* baselines.
|
|
1389
1390
|
*
|
|
1390
1391
|
* @param url - The PNG data URL.
|
|
1391
1392
|
* @returns The WebP data URL of the same pixels.
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { t as AnimApi } from "./types-CqkrEY06.mjs";
|
|
1
|
+
import { D as Point, E as FilterSlot, Gt as Events$2, K as Owner, Kt as Json, L as ComponentHandle, Pt as Descriptor, Rt as Api$4, Ut as Api$5, V as Entity, _ as PixiModule, et as LayerSort, g as PixiFilter, k as Api$1, m as Api$2, o as Events$1, sn as Events$3, t as Api$3, tt as LayerSpec, ut as Api, v as PixiSprite, vn as Require, y as PixiTexture, z as ComponentType } from "./types-Dy1xB68L.mjs";
|
|
2
|
+
import { t as AnimApi } from "./types-CVUHPvMB.mjs";
|
|
4
3
|
import { Log } from "@moku-labs/common/browser";
|
|
5
4
|
import { PluginCtx } from "@moku-labs/core";
|
|
6
5
|
|
|
@@ -623,7 +622,7 @@ type Deps$2 = {
|
|
|
623
622
|
flow: Api;
|
|
624
623
|
world: Api$1;
|
|
625
624
|
renderer: Api$2;
|
|
626
|
-
assets: Api$
|
|
625
|
+
assets: Api$3;
|
|
627
626
|
anim: AnimApi;
|
|
628
627
|
};
|
|
629
628
|
/**
|
|
@@ -644,7 +643,7 @@ type EffectsCtx = KernelSlice$2 & {
|
|
|
644
643
|
/**
|
|
645
644
|
* Payload of the `assets:bundle-unloaded` hook: `keys` names every texture that is gone.
|
|
646
645
|
*/
|
|
647
|
-
type BundleUnloaded$1 = Events$
|
|
646
|
+
type BundleUnloaded$1 = Events$1["assets:bundle-unloaded"];
|
|
648
647
|
declare namespace types_d_exports$1 {
|
|
649
648
|
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 };
|
|
650
649
|
}
|
|
@@ -871,8 +870,8 @@ type ScenesApi = {
|
|
|
871
870
|
type Deps$1 = {
|
|
872
871
|
flow: Api;
|
|
873
872
|
world: Api$1;
|
|
874
|
-
assets: Api$
|
|
875
|
-
time: Api$
|
|
873
|
+
assets: Api$3;
|
|
874
|
+
time: Api$4;
|
|
876
875
|
};
|
|
877
876
|
/**
|
|
878
877
|
* What the kernel context offers before the deps are attached.
|
|
@@ -905,7 +904,7 @@ type RunContext = {
|
|
|
905
904
|
signal: AbortSignal;
|
|
906
905
|
};
|
|
907
906
|
declare namespace types_d_exports {
|
|
908
|
-
export { AudioApi, AudioContextLike, AudioCtx, BundleUnloaded, Bus, BusState, Config, Deps, KernelSlice, LifecycleChanged, ModelCommitted, MusicDescriptor, MusicOptions, MusicRequest, MusicSwitch, MusicTrack, SceneChanged, SfxRequest, SoundEntry, State, Volumes };
|
|
907
|
+
export { AudioApi, AudioContextLike, AudioCtx, AudioSessionLike, AudioSessionType, BundleUnloaded, Bus, BusState, Config, Deps, KernelSlice, LifecycleChanged, ModelCommitted, MusicDescriptor, MusicMode, MusicOptions, MusicRequest, MusicSwitch, MusicTrack, RetiringTrack, SceneChanged, SfxRequest, SoundEntry, State, StreamTrack, Volumes };
|
|
909
908
|
}
|
|
910
909
|
/**
|
|
911
910
|
* The three volume buses. `master` carries the whole game, `music` the scene track, `sfx` every
|
|
@@ -929,7 +928,7 @@ type Bus = "master" | "music" | "sfx";
|
|
|
929
928
|
* createApp({ plugins: [...screen, audioPlugin], pluginConfigs: { audio: { context: () => context } } });
|
|
930
929
|
* ```
|
|
931
930
|
*/
|
|
932
|
-
type AudioContextLike = Pick<AudioContext, "destination" | "currentTime" | "createGain" | "createBufferSource" | "decodeAudioData" | "resume" | "close"> & {
|
|
931
|
+
type AudioContextLike = Pick<AudioContext, "destination" | "currentTime" | "createGain" | "createBufferSource" | "decodeAudioData" | "createMediaElementSource" | "resume" | "close"> & {
|
|
933
932
|
readonly state: AudioContext["state"] | "interrupted";
|
|
934
933
|
};
|
|
935
934
|
/**
|
|
@@ -963,6 +962,73 @@ type MusicDescriptor<Asset extends string> = (key: Asset | null, options?: Music
|
|
|
963
962
|
* ```
|
|
964
963
|
*/
|
|
965
964
|
type Volumes = (player: Json) => Partial<Record<Bus, number>>;
|
|
965
|
+
/**
|
|
966
|
+
* How a music track is played. `"decode"`: one AudioBuffer per track, gapless, about 58 MB per
|
|
967
|
+
* 150 s at 48 kHz. `"stream"`: an `<audio>` element on a `blob:` URL of the bytes `assets` holds,
|
|
968
|
+
* about 12 MB, and a gap at every loop: 5 to 49 ms in Chromium, about 0.4 s in WebKit. A tight
|
|
969
|
+
* loop keeps `"decode"`; a long track that tolerates a seam takes `"stream"`.
|
|
970
|
+
*
|
|
971
|
+
* @example
|
|
972
|
+
* ```ts
|
|
973
|
+
* const mode: MusicMode = "stream";
|
|
974
|
+
* ```
|
|
975
|
+
*/
|
|
976
|
+
type MusicMode = "decode" | "stream";
|
|
977
|
+
/**
|
|
978
|
+
* What the page asks iOS for. `"ambient"`: mixes with other apps, the silent switch mutes it.
|
|
979
|
+
* `"playback"`: stops other apps, plays through the switch. `"auto"`: WebKit picks.
|
|
980
|
+
*
|
|
981
|
+
* @example
|
|
982
|
+
* ```ts
|
|
983
|
+
* const session: AudioSessionType = "playback";
|
|
984
|
+
* ```
|
|
985
|
+
*/
|
|
986
|
+
type AudioSessionType = "ambient" | "playback" | "auto";
|
|
987
|
+
/**
|
|
988
|
+
* The part of `navigator.audioSession` the plugin writes. `lib.dom` has no type for it, and only
|
|
989
|
+
* WebKit has the object at all.
|
|
990
|
+
*
|
|
991
|
+
* @example
|
|
992
|
+
* ```ts
|
|
993
|
+
* // What Safari answers before the plugin started: no override.
|
|
994
|
+
* const session: AudioSessionLike = { type: "auto" };
|
|
995
|
+
* ```
|
|
996
|
+
*/
|
|
997
|
+
type AudioSessionLike = {
|
|
998
|
+
type: string;
|
|
999
|
+
};
|
|
1000
|
+
/**
|
|
1001
|
+
* One music track played by an element at `music: "stream"`: the element, its source node in the
|
|
1002
|
+
* graph, the track gain every level goes through, and the `blob:` URL the element reads.
|
|
1003
|
+
*
|
|
1004
|
+
* @example
|
|
1005
|
+
* ```ts
|
|
1006
|
+
* // `state.music.stream` once "board.theme" plays at `music: "stream"`.
|
|
1007
|
+
* const { element, url }: StreamTrack = track;
|
|
1008
|
+
* element.loop; // true
|
|
1009
|
+
* url.startsWith("blob:"); // true
|
|
1010
|
+
* ```
|
|
1011
|
+
*/
|
|
1012
|
+
type StreamTrack = {
|
|
1013
|
+
element: HTMLAudioElement;
|
|
1014
|
+
node: MediaElementAudioSourceNode;
|
|
1015
|
+
gain: GainNode;
|
|
1016
|
+
url: string;
|
|
1017
|
+
};
|
|
1018
|
+
/**
|
|
1019
|
+
* A streamed track that fades out. Its timer frees the element when the fade ends: an element has
|
|
1020
|
+
* no `stop(when)` on the context clock.
|
|
1021
|
+
*
|
|
1022
|
+
* @example
|
|
1023
|
+
* ```ts
|
|
1024
|
+
* // A switch at `musicFadeMs: 600` keeps the old track here for 600 ms.
|
|
1025
|
+
* const entry: RetiringTrack = { stream: oldTrack, timer: setTimeout(free, 600) };
|
|
1026
|
+
* ```
|
|
1027
|
+
*/
|
|
1028
|
+
type RetiringTrack = {
|
|
1029
|
+
stream: StreamTrack;
|
|
1030
|
+
timer: ReturnType<typeof setTimeout>;
|
|
1031
|
+
};
|
|
966
1032
|
/**
|
|
967
1033
|
* audio plugin config.
|
|
968
1034
|
*
|
|
@@ -970,7 +1036,9 @@ type Volumes = (player: Json) => Partial<Record<Bus, number>>;
|
|
|
970
1036
|
* ```ts
|
|
971
1037
|
* createApp({
|
|
972
1038
|
* plugins: [...screen, audioPlugin],
|
|
973
|
-
* pluginConfigs: {
|
|
1039
|
+
* pluginConfigs: {
|
|
1040
|
+
* audio: { musicFadeMs: 400, music: "stream", session: "playback", volumes: player => player.settings.audio }
|
|
1041
|
+
* }
|
|
974
1042
|
* });
|
|
975
1043
|
* ```
|
|
976
1044
|
*/
|
|
@@ -983,7 +1051,9 @@ type Config = {
|
|
|
983
1051
|
musicFadeMs: number; /** Reads the player's choice from the committed player. `undefined`: the buses stay at `buses`. */
|
|
984
1052
|
volumes: Volumes | undefined; /** Context factory, the test seam. `undefined`: `new AudioContext()` when the global exists, otherwise headless. */
|
|
985
1053
|
context: (() => AudioContextLike) | undefined; /** How many started sounds `journal()` keeps, newest last. `0` turns the journal off. */
|
|
986
|
-
journal: number;
|
|
1054
|
+
journal: number; /** How music plays. `"stream"` needs MP3 or AAC: WebM Opus is silent through a media element in WebKit (bug 276813). Sounds always decode. */
|
|
1055
|
+
music: MusicMode; /** Set on `navigator.audioSession.type` in `onStart`, before the context exists. No-op where the API is missing (every Chromium, Firefox). */
|
|
1056
|
+
session: AudioSessionType;
|
|
987
1057
|
};
|
|
988
1058
|
/**
|
|
989
1059
|
* One sound that started: a play of an `sfx` or a music track that began. `at` is the elapsed
|
|
@@ -1016,17 +1086,19 @@ type BusState = {
|
|
|
1016
1086
|
};
|
|
1017
1087
|
/**
|
|
1018
1088
|
* The music that is playing, or the key remembered until the first touch unlocks the context.
|
|
1019
|
-
* `source`
|
|
1089
|
+
* `source` plays a decoded track, `stream` a streamed one; they are never both set. Both
|
|
1090
|
+
* `undefined`: the key is only remembered (a locked context, or a refused `play()`).
|
|
1020
1091
|
*
|
|
1021
1092
|
* @example
|
|
1022
1093
|
* ```ts
|
|
1023
|
-
* const track: MusicTrack = { key: "board.theme", source: undefined,
|
|
1094
|
+
* const track: MusicTrack = { key: "board.theme", gain: undefined, source: undefined, stream: undefined };
|
|
1024
1095
|
* ```
|
|
1025
1096
|
*/
|
|
1026
1097
|
type MusicTrack = {
|
|
1027
1098
|
key: string;
|
|
1028
|
-
source: AudioBufferSourceNode | undefined;
|
|
1029
1099
|
gain: GainNode | undefined;
|
|
1100
|
+
source: AudioBufferSourceNode | undefined;
|
|
1101
|
+
stream: StreamTrack | undefined;
|
|
1030
1102
|
};
|
|
1031
1103
|
/**
|
|
1032
1104
|
* A music switch that waits for its buffer to decode. Its identity is the token: a later request
|
|
@@ -1056,7 +1128,9 @@ type State = {
|
|
|
1056
1128
|
music: MusicTrack | undefined; /** The latest switch still decoding; `undefined` once it started or a later request replaced it. */
|
|
1057
1129
|
musicPending: MusicSwitch | undefined; /** Removes the two window listeners of the unlock. */
|
|
1058
1130
|
unlock: (() => void) | undefined; /** The removers of the two fx handlers. */
|
|
1059
|
-
removers: Array<() => void>;
|
|
1131
|
+
removers: Array<() => void>; /** The `navigator.audioSession` that took `config.session`; `stop` writes `"auto"` back to it. */
|
|
1132
|
+
session: AudioSessionLike | undefined; /** Streamed tracks that fade out, each freed by its timer. */
|
|
1133
|
+
retiring: Set<RetiringTrack>;
|
|
1060
1134
|
/**
|
|
1061
1135
|
* The sounds that started, oldest first, at most `config.journal` of them. Frozen: a write
|
|
1062
1136
|
* replaces the whole list, so `journal()` hands it out without a copy.
|
|
@@ -1156,9 +1230,9 @@ type AudioApi = {
|
|
|
1156
1230
|
*/
|
|
1157
1231
|
type Deps = {
|
|
1158
1232
|
flow: Api;
|
|
1159
|
-
assets: Api$
|
|
1160
|
-
model: Api$
|
|
1161
|
-
time: Api$
|
|
1233
|
+
assets: Api$3;
|
|
1234
|
+
model: Api$5;
|
|
1235
|
+
time: Api$4;
|
|
1162
1236
|
};
|
|
1163
1237
|
/**
|
|
1164
1238
|
* What the kernel context offers before the deps are attached.
|
|
@@ -1179,7 +1253,7 @@ type AudioCtx = KernelSlice & {
|
|
|
1179
1253
|
/**
|
|
1180
1254
|
* Payload of the `model:committed` hook: the source of every volume a game sets.
|
|
1181
1255
|
*/
|
|
1182
|
-
type ModelCommitted = Events$
|
|
1256
|
+
type ModelCommitted = Events$2["model:committed"];
|
|
1183
1257
|
/**
|
|
1184
1258
|
* Payload of the `scenes:changed` hook: `music` is the track the next scene declared.
|
|
1185
1259
|
*/
|
|
@@ -1187,11 +1261,11 @@ type SceneChanged = Events["scenes:changed"];
|
|
|
1187
1261
|
/**
|
|
1188
1262
|
* Payload of the `lifecycle:changed` hook: the pause that holds every bus at zero.
|
|
1189
1263
|
*/
|
|
1190
|
-
type LifecycleChanged = Events$
|
|
1264
|
+
type LifecycleChanged = Events$3["lifecycle:changed"];
|
|
1191
1265
|
/**
|
|
1192
1266
|
* Payload of the `assets:bundle-unloaded` hook: `keys` names every decoded sound that is gone.
|
|
1193
1267
|
*/
|
|
1194
|
-
type BundleUnloaded = Events$
|
|
1268
|
+
type BundleUnloaded = Events$1["assets:bundle-unloaded"];
|
|
1195
1269
|
/**
|
|
1196
1270
|
* What one play of a sound asks for: the key and the bus the sound goes to.
|
|
1197
1271
|
*/
|