@moku-labs/game 0.15.36 → 0.15.38

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
- import { $n as FeaturePlugin, Dr as Json, dr as Descriptor, gr as NodeFx, hr as Hint, sr as NodeTable, tr as AnyFlow, xr as FakeClock, yr as ClockSource } from "./types-K3qfyTu0.mjs";
2
- import { t as createApp } from "./index-Cs4qaPMq.mjs";
3
- import { b as saveOf, v as WithGameState, y as memory } from "./types-Dh2cyp0H.mjs";
4
- import { a as createHeadless, i as ReproResult, n as HeadlessGame, o as runRepro, r as Repro, s as stepFrames, t as HeadlessApp } from "./headless-BFI7EXHo.mjs";
1
+ import { $n as FeaturePlugin, Dr as Json, dr as Descriptor, gr as NodeFx, hr as Hint, sr as NodeTable, tr as AnyFlow, xr as FakeClock, yr as ClockSource } from "./types-C_dk-lXz.mjs";
2
+ import { t as createApp } from "./index-DFAjLwxi.mjs";
3
+ import { b as saveOf, v as WithGameState, y as memory } from "./types-H1FiWmKa.mjs";
4
+ import { a as createHeadless, i as ReproResult, n as HeadlessGame, o as runRepro, r as Repro, s as stepFrames, t as HeadlessApp } from "./headless-CRqMxzqv.mjs";
5
5
  import { AnyPluginInstance } from "@moku-labs/core";
6
6
 
7
7
  //#region src/plugins/clock/fake.d.ts
package/dist/testing.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { en as refuseStart, in as type, m as flattenFeatures, t as createApp, tn as exit } from "./src-BPEIEsWH.mjs";
1
+ import { en as refuseStart, in as type, m as flattenFeatures, t as createApp, tn as exit } from "./src-B2vblgmb.mjs";
2
2
  import { n as saveOf, t as memory } from "./memory-CWlqhS7l.mjs";
3
3
  import { n as fakeClock } from "./fake-hnJcJeYf.mjs";
4
4
  import { i as stepFrames, r as runRepro, t as createHeadless } from "./headless-Bxxl_mQX.mjs";
@@ -1,4 +1,4 @@
1
- import { An as NumericFields, Cn as TagType, Dn as Motion, Dr as Json, En as Ease, In as ViewHandle, K as TransformValue, Ln as Api$1, Nn as ReducedMotion, On as MotionCause, Y as Api$3, _r as Answer, cn as AnyComponentType, dr as Descriptor, dt as ViewportSize, fn as ComponentType, hr as Hint, jn as ProjectionMotion, kn as MotionHandle, ln as AnyComponentValue, mn as Entity, nn as Api$2, qr as Api, ri as Require, s as Events$1, sn as AnyComponent, t as Api$4, vr as Api$5, wn as ChangeHook } from "./types-K3qfyTu0.mjs";
1
+ import { An as NumericFields, Cn as TagType, Dn as Motion, Dr as Json, En as Ease, In as ViewHandle, K as TransformValue, Ln as Api$1, Nn as ReducedMotion, On as MotionCause, Y as Api$3, _r as Answer, cn as AnyComponentType, dr as Descriptor, dt as ViewportSize, fn as ComponentType, hr as Hint, jn as ProjectionMotion, kn as MotionHandle, ln as AnyComponentValue, mn as Entity, nn as Api$2, qr as Api, ri as Require, s as Events$1, sn as AnyComponent, t as Api$4, vr as Api$5, wn as ChangeHook } from "./types-C_dk-lXz.mjs";
2
2
  import { Log } from "@moku-labs/common/browser";
3
3
  import { PluginCtx } from "@moku-labs/core";
4
4
  import { Node, Yoga } from "yoga-layout/load";
@@ -2779,6 +2779,25 @@ type PopupLink = {
2779
2779
  released?: boolean;
2780
2780
  over?: Entity;
2781
2781
  };
2782
+ /**
2783
+ * One part of a root built aside that waits for a later frame (spec 33 A2): an element that exists
2784
+ * and whose children were not diffed yet, the nodes it would have diffed, the nearest component
2785
+ * instance and the cause its children enter with. Back in the pool it holds no element.
2786
+ */
2787
+ type Parked = {
2788
+ element: Element | undefined;
2789
+ nodes: readonly DescriptionNode[];
2790
+ instance: string | undefined;
2791
+ cause: MotionCause | undefined;
2792
+ };
2793
+ /**
2794
+ * The application of a solved root built aside, in parts (spec 33 A6): the elements still to
2795
+ * apply, the next one on top, and whether a live one takes its rect with no motion.
2796
+ */
2797
+ type ApplyCursor = {
2798
+ stack: Element[];
2799
+ quiet: boolean;
2800
+ };
2782
2801
  /**
2783
2802
  * One reconciled tree: a projection view or a popup, with the flags the frame step reads.
2784
2803
  * `covered` is true while the root is kept under another popup; every element of the root then
@@ -2787,7 +2806,8 @@ type PopupLink = {
2787
2806
  * restore or a load. `slot` is the host element the root is laid out in (`undefined` for the
2788
2807
  * viewport), `slotRect` the content box it was last solved in, in the host's own units.
2789
2808
  * `pending` is true while the solve running now still waits to lay the root out; `mountRoot`
2790
- * creates it `false`.
2809
+ * creates it `false`. A root built aside carries its parked parts in `parked` and its
2810
+ * application in parts in `applying` (spec 33 slice A).
2791
2811
  */
2792
2812
  type Root = {
2793
2813
  entity: Entity;
@@ -2815,6 +2835,12 @@ type Root = {
2815
2835
  * other root.
2816
2836
  */
2817
2837
  aside?: true;
2838
+ /**
2839
+ * The parts of a root built aside whose children wait for a later frame, in the order they were
2840
+ * parked. Made with its first part and kept, empty, after its last: a later part reuses it.
2841
+ */
2842
+ parked?: Parked[]; /** The application of a root built aside in parts, while it is open; absent otherwise. */
2843
+ applying?: ApplyCursor;
2818
2844
  };
2819
2845
  /**
2820
2846
  * Where the focus ring was last drawn: the layer and order of its root, the rect grown by the
@@ -1,5 +1,5 @@
1
- import { $ as PixiModule, A as SyncApi, An as NumericFields, Dr as Json, En as Ease, Er as Events$3, Fn as TrackSegment, Ln as Api$2, Nn as ReducedMotion, O as FilterSlot, Pn as TrackOptions, Q as PixiFilter, Ur as Events$5, Y as Api$4, b as Events$4, dr as Descriptor, et as PixiSprite, fn as ComponentType, ft as FilterCache, k as FilterSlotInputs, kn as MotionHandle, ln as AnyComponentValue, mn as Entity, ni as Events$2, nn as Api$3, qr as Api$1, ri as Require, s as Events$1, sn as AnyComponent, t as Api$5, tt as PixiTexture, un as ComponentHandle, ur as TypeTag, ut as Point, wr as Api$6 } from "./types-K3qfyTu0.mjs";
2
- import { N as InputApi, R as I18nApi, X as StylesState, c as UiNode, i as Finding, l as LayoutState, n as ElementChange, o as JsxState, r as ElementMotion, w as TextApi } from "./types-BAv5qOZX.mjs";
1
+ import { $ as PixiModule, A as SyncApi, An as NumericFields, Dr as Json, En as Ease, Er as Events$3, Fn as TrackSegment, Ln as Api$2, Nn as ReducedMotion, O as FilterSlot, Pn as TrackOptions, Q as PixiFilter, Ur as Events$5, Y as Api$4, b as Events$4, dr as Descriptor, et as PixiSprite, fn as ComponentType, ft as FilterCache, k as FilterSlotInputs, kn as MotionHandle, ln as AnyComponentValue, mn as Entity, ni as Events$2, nn as Api$3, qr as Api$1, ri as Require, s as Events$1, sn as AnyComponent, t as Api$5, tt as PixiTexture, un as ComponentHandle, ur as TypeTag, ut as Point, wr as Api$6 } from "./types-C_dk-lXz.mjs";
2
+ import { N as InputApi, R as I18nApi, X as StylesState, c as UiNode, i as Finding, l as LayoutState, n as ElementChange, o as JsxState, r as ElementMotion, w as TextApi } from "./types-0Br0SjTB.mjs";
3
3
  import { Log } from "@moku-labs/common/browser";
4
4
  import { PluginCtx } from "@moku-labs/core";
5
5
 
@@ -1,5 +1,5 @@
1
- import { Dr as Json, Ir as Snapshot } from "./types-K3qfyTu0.mjs";
2
- import { t as HeadlessApp } from "./headless-BFI7EXHo.mjs";
1
+ import { Dr as Json, Ir as Snapshot } from "./types-C_dk-lXz.mjs";
2
+ import { t as HeadlessApp } from "./headless-CRqMxzqv.mjs";
3
3
  import { Log } from "@moku-labs/common/browser";
4
4
 
5
5
  //#region src/plugins/flow/doors/types.d.ts
@@ -4273,9 +4273,35 @@ type AnyProjectionMotion = {
4273
4273
  readonly change?: ChangeHooks<never>;
4274
4274
  settle?(view: ViewHandle<unknown>, components: readonly string[]): Motion;
4275
4275
  };
4276
+ /**
4277
+ * One pair of `ProjectionSpec.initialOnly`: a component and the fields of it that `view` gives a
4278
+ * view once, when it is born. The field names are checked against the component's own value.
4279
+ *
4280
+ * @example
4281
+ * ```ts
4282
+ * const arc: InitialOnlyPair<ShapeValue> = [Shape, ["sweep"]];
4283
+ * ```
4284
+ */
4285
+ type InitialOnlyPair<Value extends object> = readonly [ComponentHandle<Value>, readonly (keyof Value & string)[]];
4286
+ /**
4287
+ * The `initialOnly` list of a projection: one pair for each value type of `Values`, each pair
4288
+ * checked against its own component. `projection()` infers `Values` from the list, so
4289
+ * `[[Shape, ["swep"]]]` is a type error.
4290
+ *
4291
+ * @example
4292
+ * ```ts
4293
+ * const fields: InitialOnly<[ShapeValue, TransformValue]> = [
4294
+ * [Shape, ["sweep", "alpha"]],
4295
+ * [Transform, ["rotation"]]
4296
+ * ];
4297
+ * ```
4298
+ */
4299
+ type InitialOnly<Values extends readonly object[]> = { readonly [Index in keyof Values]: InitialOnlyPair<Values[Index]> };
4276
4300
  /**
4277
4301
  * One projection: keyed items of the model turned into entities. `Item` is inferred from the
4278
4302
  * return of `from`; `layer` and `lift` keep their literal types so a scene can check them.
4303
+ * `Values` are the value types of the `initialOnly` pairs, inferred by `projection()`; an
4304
+ * annotation that leaves it out accepts any field name.
4279
4305
  *
4280
4306
  * @example
4281
4307
  * ```ts
@@ -4289,7 +4315,7 @@ type AnyProjectionMotion = {
4289
4315
  * };
4290
4316
  * ```
4291
4317
  */
4292
- type ProjectionSpec<Item, LayerName extends string, LiftName extends string, Player, Session> = {
4318
+ type ProjectionSpec<Item, LayerName extends string, LiftName extends string, Player, Session, Values extends readonly object[] = readonly Record<string, unknown>[]> = {
4293
4319
  readonly name: string;
4294
4320
  readonly layer: LayerName;
4295
4321
  readonly lift?: LiftName;
@@ -4297,6 +4323,35 @@ type ProjectionSpec<Item, LayerName extends string, LiftName extends string, Pla
4297
4323
  key?(item: Item): string;
4298
4324
  view(item: Item): ViewOutput;
4299
4325
  readonly motion?: ProjectionMotion<Item>;
4326
+ /**
4327
+ * Fields another writer owns once a view is born. `view` gives them their first value when the
4328
+ * view spawns; from then on nothing of the projection writes or compares them: not a changed
4329
+ * item, not a motion or its finish, not the check at the end of a motion (no
4330
+ * `world:view-corrected`), not a rollback, `settle(entity)`, a restore or a load. These are the
4331
+ * rules of `mute()`, for every view of the projection and for its whole life. A `mute()` on the
4332
+ * view adds to them, and its release keeps them. Left out, the projection writes every field.
4333
+ *
4334
+ * @example
4335
+ * ```ts
4336
+ * // The timer ring of a chest: born full, then the system `board.rings` draws its arc from `Now`.
4337
+ * export const marks = projection({
4338
+ * name: "board.marks",
4339
+ * layer: "marks",
4340
+ * from: (player: Player) => player.board.timers,
4341
+ * key: timer => timer.id,
4342
+ * view: timer => [Shape({ kind: "arc", w: 28, h: 28, strokeWidth: 2.2, sweep: 1 })],
4343
+ * motion: {
4344
+ * enter: view => {
4345
+ * view.set(Shape, { alpha: 0 });
4346
+ * return view.toRest(Shape, { ms: 200 });
4347
+ * }
4348
+ * },
4349
+ * initialOnly: [[Shape, ["sweep"]]]
4350
+ * });
4351
+ * // The fade ends with the 0.42 the system wrote: no full ring, no `world:view-corrected`.
4352
+ * ```
4353
+ */
4354
+ readonly initialOnly?: InitialOnly<Values>;
4300
4355
  };
4301
4356
  /**
4302
4357
  * A projection spec with its item and layer types erased, as the world stores it.
@@ -4319,7 +4374,8 @@ type AnyProjectionSpec = {
4319
4374
  from(player: Json, session: Json): unknown;
4320
4375
  key?(item: unknown): string;
4321
4376
  view(item: unknown): ViewOutput;
4322
- readonly motion?: AnyProjectionMotion;
4377
+ readonly motion?: AnyProjectionMotion; /** The `initialOnly` pairs, their field names no longer tied to the component. */
4378
+ readonly initialOnly?: readonly (readonly [ComponentHandle<object>, readonly string[]])[];
4323
4379
  };
4324
4380
  /**
4325
4381
  * Why a reconcile runs. Merged over the frame; the cause table decides play or direct. A
@@ -4442,6 +4498,12 @@ type Dirty = {
4442
4498
  roots: Set<Root>;
4443
4499
  force: boolean;
4444
4500
  };
4501
+ /**
4502
+ * Component name to the fields another writer owns, of one entity. A frozen table, and a frozen
4503
+ * set in one, is the `initialOnly` seed every view of one projection shares: nothing writes into
4504
+ * it, and `mute()` copies it before it adds a field.
4505
+ */
4506
+ type MuteTable = ReadonlyMap<string, ReadonlySet<string>>;
4445
4507
  /**
4446
4508
  * A mute's fields joined with a type's owned fields, and the owned set they were joined with.
4447
4509
  */
@@ -4471,8 +4533,14 @@ type ProjectionState = {
4471
4533
  * The reverse of `keys`, so `keyOf` answers for a registered element too. The entry of an
4472
4534
  * entity that is gone is dropped with its rest pose, and its side of `keys` with it.
4473
4535
  */
4474
- keysByEntity: Map<Entity, ProjectionKey>; /** Entity to component name to the fields another writer owns. */
4475
- mutes: Map<Entity, Map<string, Set<string>>>; /** The owned fields of each component type as a set, by the type's frozen `owned` list: made once. */
4536
+ keysByEntity: Map<Entity, ProjectionKey>;
4537
+ /**
4538
+ * Entity to component name to the fields another writer owns: what `mute()` handed over, and
4539
+ * the shared `initialOnly` seed a view of such a projection is born with. A view a listener of
4540
+ * its spawn muted keeps that table, and the seed joins it.
4541
+ */
4542
+ mutes: Map<Entity, MuteTable>; /** The `initialOnly` seed of each registered spec that has one, frozen, built at `register` and `replace`. */
4543
+ initialOnly: WeakMap<AnyProjectionSpec, MuteTable>; /** The owned fields of each component type as a set, by the type's frozen `owned` list: made once. */
4476
4544
  ownedSets: WeakMap<readonly string[], ReadonlySet<string>>; /** The muted and the owned fields joined, by the mute's field set: dropped when `mute()` or its release changes it. */
4477
4545
  joinedMutes: WeakMap<ReadonlySet<string>, JoinedFields>; /** The `onConverged` listeners: the owners of elements, told after a restore or a load. */
4478
4546
  converged: Array<() => void>;
@@ -4646,7 +4714,9 @@ type ProjectionApi = {
4646
4714
  */
4647
4715
  settle(entity: Entity): void;
4648
4716
  /**
4649
- * Hands a set of fields to another writer. Tracks never write them, also not on `finish()`.
4717
+ * Hands a set of fields to another writer. Tracks never write them, also not on `finish()`. On
4718
+ * a view whose projection names `initialOnly` fields the mute adds to them, and the remover keeps
4719
+ * them.
4650
4720
  *
4651
4721
  * @param entity - The entity of the view.
4652
4722
  * @param component - The component whose fields are taken over.
@@ -6421,7 +6491,7 @@ type SystemTiming = {
6421
6491
  ms: Float64Array;
6422
6492
  };
6423
6493
  declare namespace types_d_exports$3 {
6424
- export { AnyComponentType, AnyComponentValue, AnyProjectionSpec, AnySystem, Api$2 as Api, Cause, ChangeHook, ComponentHandle, ComponentSchema, ComponentType, ComponentValue, Config$3 as Config, Deps$3 as Deps, DescriptionNode, Ease, EcsApi, EcsModule, Entity, EntityDiff, EntitySnapshot, Events$3 as Events, Explained, FrameDiff, GameSystemDefinition, KernelSlice$3 as KernelSlice, LayerSort, LayerSpec, ModelCommitted, Motion, MotionCause, MotionHandle, Mut, Narrowed, NumericFields, Owner, ProjectionApi, ProjectionKey, ProjectionModule, ProjectionMotion, ProjectionSpec, QueryTerm, QueryTuple, ReducedMotion, ResourceType, RestOptions, State$3 as State, SystemContext, SystemContextOf, SystemDefinition, SystemFor, SystemTiming, TagType, TrackOptions, TrackSegment, TweenDriver, TweenOptions, ViewHandle, ViewOutput, WorldCtx, WorldMode, WorldPhase, WorldSnapshot };
6494
+ export { AnyComponentType, AnyComponentValue, AnyProjectionSpec, AnySystem, Api$2 as Api, Cause, ChangeHook, ComponentHandle, ComponentSchema, ComponentType, ComponentValue, Config$3 as Config, Deps$3 as Deps, DescriptionNode, Ease, EcsApi, EcsModule, Entity, EntityDiff, EntitySnapshot, Events$3 as Events, Explained, FrameDiff, GameSystemDefinition, InitialOnly, InitialOnlyPair, KernelSlice$3 as KernelSlice, LayerSort, LayerSpec, ModelCommitted, Motion, MotionCause, MotionHandle, Mut, Narrowed, NumericFields, Owner, ProjectionApi, ProjectionKey, ProjectionModule, ProjectionMotion, ProjectionSpec, QueryTerm, QueryTuple, ReducedMotion, ResourceType, RestOptions, State$3 as State, SystemContext, SystemContextOf, SystemDefinition, SystemFor, SystemTiming, TagType, TrackOptions, TrackSegment, TweenDriver, TweenOptions, ViewHandle, ViewOutput, WorldCtx, WorldMode, WorldPhase, WorldSnapshot };
6425
6495
  }
6426
6496
  /**
6427
6497
  * world plugin events.
@@ -11725,7 +11795,7 @@ type SceneOwner = Owner & {
11725
11795
  *
11726
11796
  * @example
11727
11797
  * ```ts
11728
- * // The default: half of each frame, read at the start of every build: 8.33… ms at 60 Hz, 4.17 ms at 120 Hz.
11798
+ * // The default: two thirds of each frame, read at the start of every build: 11.1 ms at 60 Hz, 5.56 ms at 120 Hz.
11729
11799
  * const config: Config = { buildMs: "auto" };
11730
11800
  *
11731
11801
  * // A game on a slow phone gives the next scene a fixed 3 ms of each frame: the switch takes more frames.
@@ -11735,8 +11805,8 @@ type SceneOwner = Owner & {
11735
11805
  type Config$1 = {
11736
11806
  /**
11737
11807
  * Milliseconds per frame the next scene's building work may take on a live switch with a
11738
- * drawing renderer: a finite number above 0, or `"auto"` (the default) for half of the frame
11739
- * interval the loop runs at, `500 / time.rate().fps`, read once at the build start.
11808
+ * drawing renderer: a finite number above 0, or `"auto"` (the default) for two thirds of the
11809
+ * frame interval the loop runs at, `(1000 / time.rate().fps) * 2 / 3`, read once at the build start.
11740
11810
  */
11741
11811
  buildMs: number | "auto";
11742
11812
  };
@@ -1,7 +1,7 @@
1
- import { $ as PixiModule, $n as FeaturePlugin, Dr as Json, Lr as SnapshotOf, Mr as PlayerStateProvider, Nr as ProviderCall, Pr as SaveDoc, Tr as Config, X as Config$2, a as Config$3, d as Manifest, lt as Orientation, n as AssetsIo, tr as AnyFlow, ur as TypeTag, xr as FakeClock, yr as ClockSource, zn as Config$1 } from "./types-K3qfyTu0.mjs";
2
- import { o as AudioContextLike, s as Config$4 } from "./types-B1W2CPUu.mjs";
3
- import { R as I18nApi, U as StringTable, W as TextKey } from "./types-BAv5qOZX.mjs";
4
- import { St as effectsPlugin, Tt as audioPlugin, ht as PlatformProvider, i as screen, pt as platformPlugin, t as createApp } from "./index-Cs4qaPMq.mjs";
1
+ import { $ as PixiModule, $n as FeaturePlugin, Dr as Json, Lr as SnapshotOf, Mr as PlayerStateProvider, Nr as ProviderCall, Pr as SaveDoc, Tr as Config, X as Config$2, a as Config$3, d as Manifest, lt as Orientation, n as AssetsIo, tr as AnyFlow, ur as TypeTag, xr as FakeClock, yr as ClockSource, zn as Config$1 } from "./types-C_dk-lXz.mjs";
2
+ import { o as AudioContextLike, s as Config$4 } from "./types-54KzmAA6.mjs";
3
+ import { R as I18nApi, U as StringTable, W as TextKey } from "./types-0Br0SjTB.mjs";
4
+ import { St as effectsPlugin, Tt as audioPlugin, ht as PlatformProvider, i as screen, pt as platformPlugin, t as createApp } from "./index-DFAjLwxi.mjs";
5
5
  import { AnyPluginInstance } from "@moku-labs/core";
6
6
 
7
7
  //#region src/plugins/model/store/providers/memory.d.ts
@@ -1,9 +1,9 @@
1
- import { Vr as Api$3, Y as Api, nn as Api$1, vr as Api$4, wr as Api$2 } from "./types-K3qfyTu0.mjs";
2
- import { P as AnimApi, r as UiApi } from "./types-B1W2CPUu.mjs";
3
- import { N as InputApi } from "./types-BAv5qOZX.mjs";
4
- import { r as Repro } from "./headless-BFI7EXHo.mjs";
5
- import { a as InputOf, r as ControlApp } from "./types-D52biu5v.mjs";
6
- import { t as commands } from "./commands-BGrtxBga.mjs";
1
+ import { Vr as Api$3, Y as Api, nn as Api$1, vr as Api$4, wr as Api$2 } from "./types-C_dk-lXz.mjs";
2
+ import { P as AnimApi, r as UiApi } from "./types-54KzmAA6.mjs";
3
+ import { N as InputApi } from "./types-0Br0SjTB.mjs";
4
+ import { r as Repro } from "./headless-CRqMxzqv.mjs";
5
+ import { a as InputOf, r as ControlApp } from "./types-CGPWYbZO.mjs";
6
+ import { t as commands } from "./commands-CMFdiDcg.mjs";
7
7
 
8
8
  //#region src/visual/types.d.ts
9
9
  /** The base commands of the `/control` door, keyed by their short names. */
package/dist/visual.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- import { a as VisualApp, c as VisualRenderer, d as VisualStart, f as VisualStep, h as VisualTolerance, i as CheckpointResult, l as VisualReport, m as VisualTestResult, n as runVisualTests, o as VisualOptions, p as VisualTest, r as defineVisualTest, s as VisualPage, t as parseVisualArgv, u as VisualSetup } from "./visual-DMx0vv_3.mjs";
1
+ import { a as VisualApp, c as VisualRenderer, d as VisualStart, f as VisualStep, h as VisualTolerance, i as CheckpointResult, l as VisualReport, m as VisualTestResult, n as runVisualTests, o as VisualOptions, p as VisualTest, r as defineVisualTest, s as VisualPage, t as parseVisualArgv, u as VisualSetup } from "./visual-DjTJ02c3.mjs";
2
2
  export { type CheckpointResult, type VisualApp, type VisualOptions, type VisualPage, type VisualRenderer, type VisualReport, type VisualSetup, type VisualStart, type VisualStep, type VisualTest, type VisualTestResult, type VisualTolerance, defineVisualTest, parseVisualArgv, runVisualTests };
package/llms.txt CHANGED
@@ -520,7 +520,7 @@ The graph. Modules `runner`, `gate`, `inbox`, `fx`, `features`. Runner methods s
520
520
  ### Screen plugins (V2, built)
521
521
 
522
522
  `world`, `renderer`, `input`, `assets`, `scenes`. Each has a README at `src/plugins/<name>/README.md`; the root README lists their APIs, events and config. Rendering: WebGPU preferred, with Pixi's WebGL fallback.
523
- - `app.scenes`: `current()` the mounted scene id, `expect(id)` the scene the next node without its own `scene` mounts, a rest node or a transit node in live mode (the restore door calls it; an unknown id throws). An `over` node takes it the same way, also over another mounted scene. A live switch, before it builds the scene, warms the bundles the scene names (`assets.warm`): their textures go to the GPU `renderer.uploads` a drawn frame while the old scene is still up, so the first frame of the new scene uploads none of them; textures already there are not waited for, and a fast walk or a headless app never waits. A bundle the scene does not name (a `core` bundle of portraits the board shows) is not warmed: name it in the scene's `bundles` or call `app.assets.warm`. Then, with a drawing renderer, the next scene is built aside: its logic runs in one block, the renderer makes its Pixi views over the next frames off the stage in a budget per frame, config `scenes.buildMs: number | "auto"` (default `"auto"`, half the frame interval: 8.33 ms at 60 Hz, 4.17 ms at 120 Hz; `0` throws), and it is swapped in one frame. Until the swap `current()` answers the old scene, nothing of the new one is drawn, hit or found by `ui.find`, its `enter` motions wait and the gate stays closed, so the first tap lands on a complete scene; `scenes:changed` is emitted at the swap. A fast walk, a world that is not live and a headless app mount in one block as before.
523
+ - `app.scenes`: `current()` the mounted scene id, `expect(id)` the scene the next node without its own `scene` mounts, a rest node or a transit node in live mode (the restore door calls it; an unknown id throws). An `over` node takes it the same way, also over another mounted scene. A live switch, before it builds the scene, warms the bundles the scene names (`assets.warm`): their textures go to the GPU `renderer.uploads` a drawn frame while the old scene is still up, so the first frame of the new scene uploads none of them; textures already there are not waited for, and a fast walk or a headless app never waits. A bundle the scene does not name (a `core` bundle of portraits the board shows) is not warmed: name it in the scene's `bundles` or call `app.assets.warm`. Then, with a drawing renderer, the next scene is built aside: its ui elements are made by root and by part, laid out once and applied in parts, and the renderer makes its Pixi views, over the next frames off the stage in a budget per frame, config `scenes.buildMs: number | "auto"` (default `"auto"`, two thirds of the frame interval: 11.1 ms at 60 Hz, 5.56 ms at 120 Hz; `0` throws), and it is swapped in one frame. Until the swap `current()` answers the old scene, nothing of the new one is drawn, hit or found by `ui.find`, its `enter` motions wait and the gate stays closed, so the first tap lands on a complete scene; `scenes:changed` is emitted at the swap. A fast walk, a world that is not live and a headless app mount in one block as before.
524
524
  - `Shape({ kind, w, h, fill, fillAlpha, alpha, radius, stroke, strokeWidth, dash, clip, sweep?, cap? })` (`kind: "rect"`; `sweep` and `cap` are optional, `1` and `"round"` when left out; only an arc reads them; a rectangle or a triangle ignores them, also when an earlier arc left them on the `Shape`): a rounded rectangle, `kind: "triangle"` (fills its box pointing right) or `kind: "arc"`: a stroke on the circle inscribed in the box (`min(w, h) − strokeWidth` across, centred) from 12 o'clock clockwise over `sweep` (0..1, clamped; 0 draws nothing, 1 a closed ring), ended by `cap` (`"round" | "butt"`); an arc draws no fill and ignores `radius` and `dash`. The path is built again only when a field changes: a still arc costs nothing a frame. `Shape({ kind: "arc", w: 28, h: 28, stroke: 0xc8a15a, strokeWidth: 2.2, sweep: 0.42 })` is a slot timer's ring with 42 % left.
525
525
  - Overlays: `Overlay({ dim, dimColor, cover, hud, fade, hold })` on the root of an overlay (in `components`) keeps everything drawn before it as one picture, retaken in the frame anything in it changes, and with `cover: "opaque"` draws none of it once landed at alpha 1; `hold: true` (default `false`) holds the endless loops under it while it stands, as under a cover or a blurred picture (their phase follows time), so a dimmed sheet over a sharp scene draws no frame at rest; the engine draws the dim (`dim × level`, right under the root); the level follows the root's own enter and exit motion, `fade` (250 ms) only when it has none; level and landing are read through `app.renderer.sync.debug.overlays()`. A `backdrop: true` filter inside the kept range runs no pass while the picture stands (its look is in the picture) and is drawn again in the frame the overlay is gone. Types: `OverlayValue` (the component's value) and `HudPartValue` (a part of the HUD an overlay keeps shown or lets fade).
526
526
  - HUD: a scene layer `{ above: "overlays" }` is the HUD layer, drawn above every overlay and never in a picture; a scene with a HUD layer and no `ui` gets `ui` right under it. Its roots never take the keyboard: Escape and the Back press reach the top sheet under it. `HudPart({ key })` tags a HUD view; an overlay's `hud` (`"all"`, `"none"` or keys) names the parts that stay shown, the others fade with its level and take no tap. Keys are typed by `defineGame<{ …, hud: "energy" | "gems" }>()`.
@@ -532,6 +532,7 @@ The graph. Modules `runner`, `gate`, `inbox`, `fx`, `features`. Runner methods s
532
532
  - When a system runs: before each run the world checks it. A system with a `query` is skipped while a term of its query has no entity (the smallest store among the terms is empty): no walk, no `mut()` mark, no timing. A system without a query always runs. `system({ ..., scenes: ["board"] })` runs only while one of those scenes is mounted or leaving: `scenes` calls `app.world.ecs.setScene(id)` at every switch, and the scene before leaves until nothing carries `Exiting` (the elements of its JSX views played their exit; the next scene's entities already stand then, so a `scenes` system writes only its own scene's entities). A dev build throws at start for a name in `scenes` no feature declares: `[game] System "board.idle" runs on the scene "bord", which no feature declares.` `ctx.resumed` is true on the first run after a skip (a cache or a remembered cue starts over then). The same for every system, the engine's included; 0 B a frame. A run with no rows does not happen any more: work a system did besides its rows (a hover, a drop or a press that ends, a resource reset, something that outlives its entities) goes to a system with `query: []` and `scenes`, reading its rows with `world.query()`. From 0.15.3: delete `systemsRun` from every feature (the type refuses it), rewrite the systems that work outside their rows, add `scenes` only to a system whose entities stand on every screen; `ecs.hold` and `ecs.isActive` are gone (a test asks `app.scenes.current()` or whether the system ran). World README, "When a system runs".
533
533
  - Motion hooks are told why they play: `enter(view, item, hint, cause)` and `exit(view, item, hint, cause)` get `World.MotionCause`, `"mount"` (the view comes or goes with its screen: a mount, an unmount, a load, a restore, a paused or fast world) or `"change"` (a commit). A world projection plays them only for `"change"`; `ui` plays them for both. `app.world.projection.causeOf(entity)` answers how a view came or how it is leaving, also in a removal hook of its despawn, until the next frame.
534
534
  - `app.world.projection.replace(spec)` swaps the spec of a registered projection; a mounted one runs its new `view` on the next frame. An unknown name throws. The caller is the dev hot swap.
535
+ - `projection({ …, initialOnly: [[Shape, ["sweep"]]] })`: `view` gives those fields once, at the birth of a view; after that they belong to another writer (a system drawing a timer arc from `Now`), and no changed item, motion, end-of-motion check (`world:view-corrected`), rollback, `settle`, restore or load writes or compares them, as with `mute()` for every view's whole life. Field names are checked per component; a pair with no field throws at register. Absent, every field is the projection's.
535
536
  - `input`: one followed pointer. A finger that has not started a gesture (pressed, inside the tap slop, no long press, no trace) gives way to the next finger that goes down, wherever it rests, and its press is cancelled; a finger that started a gesture keeps it. `Pointer.id` is the followed `pointerId` (`-1` at rest): a system that measures the finger's travel starts over when it changes.
536
537
  - `Draggable({ payload, carry?, aim? })`: `aim: { x, y }`, a point of the view in its own units, moves the hover and the drop lookup from the finger to that point of the view in the hand; `null` (default) is the finger.
537
538
  - `pluginConfigs.renderer.onDemand: true` draws a frame only when something visible changed (a written view, a filter value, a resize, a wake, a capture); `app.renderer.stats().drawn` is the frames drawn per second (`fps` without it); a game that writes into its own `Display` object calls `app.renderer.invalidate(entity)` before the write; `renderer.sync.debug.verifyFrames(true)` (dev/debug) checks skipped frames by pixels and logs `renderer:missed-frame`; a dev build warns `renderer:missed-mark` for a write without the call.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moku-labs/game",
3
- "version": "0.15.36",
3
+ "version": "0.15.38",
4
4
  "description": "2D puzzle game engine on TypeScript and PixiJS, built on @moku-labs/core.",
5
5
  "type": "module",
6
6
  "sideEffects": [