@moku-labs/game 0.15.14 → 0.15.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/dist/app/page.d.mts +1 -1
  2. package/dist/app/page.mjs +1 -1
  3. package/dist/app/system.d.mts +3 -3
  4. package/dist/app.d.mts +3 -3
  5. package/dist/app.mjs +1 -1
  6. package/dist/assets.d.mts +1 -1
  7. package/dist/{commands-aLTGwuyk.d.mts → commands-DEVJzdsr.d.mts} +5 -5
  8. package/dist/control.d.mts +4 -4
  9. package/dist/{define-DbxBzO-G.d.mts → define-BfWdvNHC.d.mts} +2 -2
  10. package/dist/{headless-DHtavbqk.d.mts → headless-ByFLyHxx.d.mts} +1 -1
  11. package/dist/{index-YUceiUFm.d.mts → index-BYDIzUDh.d.mts} +6 -5
  12. package/dist/index.d.mts +4 -4
  13. package/dist/index.mjs +2 -2
  14. package/dist/inspect.d.mts +6 -6
  15. package/dist/inspect.mjs +1 -1
  16. package/dist/jsx-dev-runtime.d.mts +1 -1
  17. package/dist/jsx-runtime.d.mts +1 -1
  18. package/dist/{runtime-B5OD9JTT.d.mts → runtime-CrF7Cx-b.d.mts} +1 -1
  19. package/dist/{sources-Fbq_PFVH.mjs → sources-BYKXYPhd.mjs} +1 -1
  20. package/dist/{src-Co5sO4z1.mjs → src-sg41xWNr.mjs} +471 -75
  21. package/dist/testing.d.mts +4 -4
  22. package/dist/testing.mjs +1 -1
  23. package/dist/{types-3bEK86Z1.d.mts → types-Bi6hj1OB.d.mts} +2 -53
  24. package/dist/{types-Dm4tM5nH.d.mts → types-D8zAAXLt.d.mts} +4 -4
  25. package/dist/{types-CuEK0Vam.d.mts → types-DOldmhIw.d.mts} +21 -5
  26. package/dist/{types-xmH1X8qd.d.mts → types-ae0jg0r4.d.mts} +133 -14
  27. package/dist/{types-_a6-ALzz.d.mts → types-qsE-I59e.d.mts} +2 -2
  28. package/dist/{visual-CbGKzlj0.mjs → visual-DNM5oFYx.mjs} +40 -9
  29. package/dist/{visual-DExHHx-a.d.mts → visual-Hj0oNqPP.d.mts} +6 -6
  30. package/dist/visual.d.mts +1 -1
  31. package/dist/visual.mjs +1 -1
  32. package/llms.txt +1 -1
  33. package/package.json +1 -1
@@ -1,7 +1,7 @@
1
- import { Qn as AnyFlow, Xn as FeaturePlugin, cr as Descriptor, fr as Hint, gr as ClockSource, ir as NodeTable, pr as NodeFx, vr as FakeClock, wr as Json } from "./types-CuEK0Vam.mjs";
2
- import { t as createApp } from "./index-YUceiUFm.mjs";
3
- import { b as saveOf, v as WithGameState, y as memory } from "./types-Dm4tM5nH.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-DHtavbqk.mjs";
1
+ import { Qn as AnyFlow, Xn as FeaturePlugin, cr as Descriptor, fr as Hint, gr as ClockSource, ir as NodeTable, pr as NodeFx, vr as FakeClock, wr as Json } from "./types-DOldmhIw.mjs";
2
+ import { t as createApp } from "./index-BYDIzUDh.mjs";
3
+ import { b as saveOf, v as WithGameState, y as memory } from "./types-D8zAAXLt.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-ByFLyHxx.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 { $t as exit, Qt as refuseStart, m as flattenFeatures, nn as type, t as createApp } from "./src-Co5sO4z1.mjs";
1
+ import { $t as exit, Qt as refuseStart, m as flattenFeatures, nn as type, t as createApp } from "./src-sg41xWNr.mjs";
2
2
  import { n as saveOf, t as memory } from "./memory-CWlqhS7l.mjs";
3
3
  import { t as fakeClock } from "./fake-6mazmHK3.mjs";
4
4
  import { i as stepFrames, r as runRepro, t as createHeadless } from "./headless-BA6ffzqD.mjs";
@@ -1,8 +1,7 @@
1
- import { $r as Events$2, $t as Api$3, An as ReducedMotion, Br as Events$5, Cn as Ease, Cr as Events$3, D as FilterSlotInputs, Dn as NumericFields, E as FilterSlot, En as MotionHandle, K as Api$4, Mn as TrackSegment, O as SyncApi, Pn as Api$2, Q as PixiTexture, Wr as Api$1, X as PixiModule, Y as PixiFilter, Z as PixiSprite, cr as Descriptor, dn as Entity, ei as Require, in as AnyComponent, jn as TrackOptions, ln as ComponentType, lt as FilterCache, on as AnyComponentValue, s as Events$1, sn as ComponentHandle, sr as TypeTag, st as Point, t as Api$5, v as Events$4, wr as Json, xr as Api$6 } from "./types-CuEK0Vam.mjs";
2
- import { C as TextApi, L as I18nApi, M as InputApi, Y as StylesState, c as UiNode, i as Finding, n as ElementChange, o as JsxState, r as ElementMotion } from "./types-xmH1X8qd.mjs";
1
+ import { $r as Events$2, $t as Api$3, An as ReducedMotion, Br as Events$5, Cn as Ease, Cr as Events$3, D as FilterSlotInputs, Dn as NumericFields, E as FilterSlot, En as MotionHandle, K as Api$4, Mn as TrackSegment, O as SyncApi, Pn as Api$2, Q as PixiTexture, Wr as Api$1, X as PixiModule, Y as PixiFilter, Z as PixiSprite, cr as Descriptor, dn as Entity, ei as Require, in as AnyComponent, jn as TrackOptions, ln as ComponentType, lt as FilterCache, on as AnyComponentValue, s as Events$1, sn as ComponentHandle, sr as TypeTag, st as Point, t as Api$5, v as Events$4, wr as Json, xr as Api$6 } from "./types-DOldmhIw.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-ae0jg0r4.mjs";
3
3
  import { Log } from "@moku-labs/common/browser";
4
4
  import { PluginCtx } from "@moku-labs/core";
5
- import { Node, Yoga } from "yoga-layout/load";
6
5
 
7
6
  //#region src/plugins/debug/types.d.ts
8
7
  declare namespace types_d_exports$4 {
@@ -2807,56 +2806,6 @@ type MusicRequest = {
2807
2806
  key: string | null;
2808
2807
  fadeMs: number;
2809
2808
  };
2810
- //#endregion
2811
- //#region src/plugins/ui/layout/types.d.ts
2812
- /**
2813
- * The patches the scroll step writes with, one object each, filled in place: a frame builds none.
2814
- * Every number in them is a whole reference unit.
2815
- */
2816
- type ScrollPatches = {
2817
- scroll: {
2818
- offset: number;
2819
- min: number;
2820
- };
2821
- state: {
2822
- offset: number;
2823
- max: number;
2824
- moving: boolean;
2825
- };
2826
- scrollable: {
2827
- moving: boolean;
2828
- };
2829
- x: {
2830
- x: number;
2831
- };
2832
- y: {
2833
- y: number;
2834
- };
2835
- };
2836
- /**
2837
- * layout module state. `nodes` is attach minus detach: Yoga 3.2.1 has no instance counter.
2838
- * `scrolling` is the scroll the finger holds; `scrollClock` the ms the scroll step has run, the
2839
- * clock of the samples; `scrollRing` the last pointer samples of the press on its axis, a
2840
- * position and a time each, `scrollHead` the slot the next one takes and `scrollSamples` how
2841
- * many it holds; `scrollMoved` when the finger last moved.
2842
- */
2843
- type LayoutState = {
2844
- yoga: Yoga | undefined;
2845
- byEntity: Map<Entity, Node>;
2846
- nodes: number;
2847
- measured: number;
2848
- solves: number;
2849
- scrolling: Entity | undefined;
2850
- scrollClock: number;
2851
- scrollRing: Float64Array;
2852
- scrollHead: number;
2853
- scrollSamples: number;
2854
- scrollMoved: number;
2855
- scrollPatches: ScrollPatches; /** One line of the `body` style as the icons were last measured with it; none before that. */
2856
- iconLine: number | undefined;
2857
- order: number;
2858
- cleanups: Array<() => void>;
2859
- };
2860
2809
  declare namespace types_d_exports {
2861
2810
  export { Config, Deps, ElementChange, ElementMotion, Finding, FocusRing, KernelSlice, State, TextInputLook, UiApi, UiCtx, UiNode };
2862
2811
  }
@@ -1,7 +1,7 @@
1
- import { Ar as ProviderCall, In as Config$1, Pr as SnapshotOf, Qn as AnyFlow, Sr as Config, X as PixiModule, Xn as FeaturePlugin, a as Config$3, d as Manifest, gr as ClockSource, jr as SaveDoc, kr as PlayerStateProvider, n as AssetsIo, ot as Orientation, q as Config$2, sr as TypeTag, vr as FakeClock, wr as Json } from "./types-CuEK0Vam.mjs";
2
- import { o as AudioContextLike, s as Config$4 } from "./types-3bEK86Z1.mjs";
3
- import { H as StringTable, L as I18nApi, U as TextKey } from "./types-xmH1X8qd.mjs";
4
- import { ft as platformPlugin, i as screen, mt as PlatformProvider, t as createApp, wt as audioPlugin, xt as effectsPlugin } from "./index-YUceiUFm.mjs";
1
+ import { Ar as ProviderCall, In as Config$1, Pr as SnapshotOf, Qn as AnyFlow, Sr as Config, X as PixiModule, Xn as FeaturePlugin, a as Config$3, d as Manifest, gr as ClockSource, jr as SaveDoc, kr as PlayerStateProvider, n as AssetsIo, ot as Orientation, q as Config$2, sr as TypeTag, vr as FakeClock, wr as Json } from "./types-DOldmhIw.mjs";
2
+ import { o as AudioContextLike, s as Config$4 } from "./types-Bi6hj1OB.mjs";
3
+ import { R as I18nApi, U as StringTable, W as TextKey } from "./types-ae0jg0r4.mjs";
4
+ import { ft as platformPlugin, i as screen, mt as PlatformProvider, t as createApp, wt as audioPlugin, xt as effectsPlugin } from "./index-BYDIzUDh.mjs";
5
5
  import { AnyPluginInstance } from "@moku-labs/core";
6
6
 
7
7
  //#region src/plugins/model/store/providers/memory.d.ts
@@ -11712,7 +11712,11 @@ type ManifestBundle = {
11712
11712
  * manifest.
11713
11713
  */
11714
11714
  dir?: string;
11715
- tier: Tier; /** `"leave"`: unloaded at the next rest once its feature left. Absent: `"budget"`. */
11715
+ tier: Tier;
11716
+ /**
11717
+ * `"leave"`: unloaded at the next rest once its feature left, or once the exit of a scene that named it is over.
11718
+ * Absent: `"budget"`.
11719
+ */
11716
11720
  release?: "leave";
11717
11721
  mb: number;
11718
11722
  pages?: readonly AtlasPage[];
@@ -11962,11 +11966,17 @@ type State = {
11962
11966
  queue: PreloadQueue | undefined;
11963
11967
  current: NodeInfo | undefined;
11964
11968
  useCounter: number; /** Keys already reported missing, so one key warns and starts its background load once. */
11965
- warned: Set<string>; /** `onEnter`, the `load` handler and the texture provider. */
11969
+ warned: Set<string>; /** `onEnter`, the `load` handler, the texture provider, and the start and the end of an exit in `world`. */
11966
11970
  removers: Array<() => void>; /** The `files` map of the stamp the dev hot swap applied last. `undefined` until the first swap. */
11967
11971
  stamps: Readonly<Record<string, string>> | undefined; /** The running dev hot swap. The next one waits for it, so two swaps never overlap. */
11968
11972
  swapping: Promise<void> | undefined; /** The list the mounted scene names, from the last `sceneBundles` call. */
11969
- sceneNamed: readonly string[]; /** Registered companions, in registration order. */
11973
+ sceneNamed: readonly string[];
11974
+ /**
11975
+ * The bundles a scene named before and the list names no more: held while anything carries `Exiting`, so a
11976
+ * leaving scene's pictures stay until its elements played their exit (F51).
11977
+ */
11978
+ sceneLeaving: string[]; /** How many entities carry `Exiting` now, counted from the world's `onAdded` and `onRemoved` of the tag. */
11979
+ exiting: number; /** Registered companions, in registration order. */
11970
11980
  companions: BundleCompanion[]; /** A live rest was announced; the arriving node's `load` stage runs the release. */
11971
11981
  restPending: boolean;
11972
11982
  };
@@ -12188,7 +12198,9 @@ type Api = {
12188
12198
  /**
12189
12199
  * Tells assets which bundles the mounted scene names: its `bundle`, then what its `bundles(state)` returned.
12190
12200
  * Each call replaces the list before it. A bundle in the list is never a victim of the budget and is not
12191
- * released by `release: "leave"` while it stays in the list. Called by `scenes`; a game never calls it.
12201
+ * released by `release: "leave"` while it stays in the list. A bundle the list named before and names no more
12202
+ * stays held while anything carries `Exiting`, so a leaving scene's pictures stay until its elements played their
12203
+ * exit; then a `"leave"` one goes at once (F51). Called by `scenes`; a game never calls it.
12192
12204
  *
12193
12205
  * @param named - The bundle names of the mounted scene, the scene bundle first.
12194
12206
  * @example
@@ -12196,6 +12208,8 @@ type Api = {
12196
12208
  * // scenes, right after it mounted the ward for episode 1 (and after a re-read that changed the list).
12197
12209
  * ctx.require(assetsPlugin).sceneBundles(["ui", "ep01"]);
12198
12210
  * // ep01 (release "leave") now stays while the ward shows it, even after the ep01 flow left the stack
12211
+ * ctx.require(assetsPlugin).sceneBundles(["board"]);
12212
+ * // the board is mounted: ep01 stays until the ward's elements played their exit, then goes
12199
12213
  * ```
12200
12214
  */
12201
12215
  sceneBundles(named: readonly string[]): void;
@@ -12248,6 +12262,7 @@ type Deps = {
12248
12262
  renderer: Api$1;
12249
12263
  time: Api$7;
12250
12264
  model: Api$5;
12265
+ world: Api$2;
12251
12266
  };
12252
12267
  /**
12253
12268
  * What the kernel context offers before the deps are attached.
@@ -12282,7 +12297,8 @@ type HotSwap = Events$8["ui:hot-swap"];
12282
12297
  /**
12283
12298
  * When a bundle leaves besides the budget. `"budget"` (default): only the LRU unloads it when memory
12284
12299
  * runs out. `"leave"`: also unloaded at the next rest once no node of its feature is on the stack
12285
- * and no mounted scene names it.
12300
+ * and no mounted scene names it; a scene that named it and left holds it until its elements played
12301
+ * their exit, and then it goes at once.
12286
12302
  *
12287
12303
  * @example
12288
12304
  * ```ts
@@ -1,6 +1,7 @@
1
- import { $t as Api$2, Dn as NumericFields, En as MotionHandle, K as Api$3, Nn as ViewHandle, On as ProjectionMotion, Pn as Api$1, Tn as MotionCause, U as TransformValue, Wr as Api, an as AnyComponentType, bn as TagType, cr as Descriptor, ct as ViewportSize, dn as Entity, ei as Require, fr as Hint, hr as Api$5, in as AnyComponent, ln as ComponentType, mr as Answer, on as AnyComponentValue, s as Events$1, t as Api$4, wn as Motion, wr as Json, xn as ChangeHook } from "./types-CuEK0Vam.mjs";
1
+ import { $t as Api$2, An as ReducedMotion, Cn as Ease, Dn as NumericFields, En as MotionHandle, K as Api$3, Nn as ViewHandle, On as ProjectionMotion, Pn as Api$1, Tn as MotionCause, U as TransformValue, Wr as Api, an as AnyComponentType, bn as TagType, cr as Descriptor, ct as ViewportSize, dn as Entity, ei as Require, fr as Hint, hr as Api$5, in as AnyComponent, ln as ComponentType, mr as Answer, on as AnyComponentValue, s as Events$1, t as Api$4, wn as Motion, wr as Json, xn as ChangeHook } from "./types-DOldmhIw.mjs";
2
2
  import { Log } from "@moku-labs/common/browser";
3
3
  import { PluginCtx } from "@moku-labs/core";
4
+ import { Node, Yoga } from "yoga-layout/load";
4
5
 
5
6
  //#region src/plugins/ui/styles/types.d.ts
6
7
  /**
@@ -1421,7 +1422,10 @@ type CountdownValue = {
1421
1422
  /**
1422
1423
  * What the `Text` component holds. `resolved` is engine-owned: a game writes `content`, `style`,
1423
1424
  * `bind`, `anchor` and `alpha`, and reads `resolved`. `alpha` fades every run, shadow and icon of
1424
- * the block together, 1 by default; a change is written in place and a tween can drive it.
1425
+ * the block together, 1 by default; a change is written in place and a tween can drive it. `box`
1426
+ * is the width a style with `wrap: "box"` wraps at: `ui` writes the laid-out width of the text
1427
+ * element there, only on a text that wrapped at it, so a saved or described value has none; with no
1428
+ * box or 0 it is one line. A style that wraps otherwise ignores it.
1425
1429
  *
1426
1430
  * @example
1427
1431
  * ```ts
@@ -1431,6 +1435,11 @@ type CountdownValue = {
1431
1435
  * };
1432
1436
  * // The hint of an empty name field, at half alpha.
1433
1437
  * const hint: TextValue = { ...value, content: "Your name", alpha: 0.5, resolved: "Your name" };
1438
+ * // A player name in a "card.name" style with wrap "box", laid out 200 wide by ui: two lines.
1439
+ * const name: TextValue = {
1440
+ * ...value, content: "Aleksandra Kucherenko", style: "card.name", box: 200,
1441
+ * resolved: "Aleksandra Kucherenko"
1442
+ * };
1434
1443
  * ```
1435
1444
  */
1436
1445
  type TextValue = {
@@ -1439,6 +1448,7 @@ type TextValue = {
1439
1448
  bind: TextBind | undefined;
1440
1449
  anchor: Point;
1441
1450
  alpha: number;
1451
+ box?: number;
1442
1452
  resolved: string;
1443
1453
  };
1444
1454
  /**
@@ -1451,14 +1461,17 @@ type TextValue = {
1451
1461
  */
1452
1462
  type TextAlign = "left" | "center" | "right";
1453
1463
  /**
1454
- * The wrap width of a style in reference pixels, or `"none"` for one line per `\n` segment.
1464
+ * The wrap width of a style in reference pixels; `"box"` for the width the `ui` layout gave the
1465
+ * text element, one line outside a layout; or `"none"` for one line per `\n` segment.
1455
1466
  *
1456
1467
  * @example
1457
1468
  * ```ts
1458
1469
  * const wrap: TextWrap = 480;
1470
+ * // A name that wraps at the room its row leaves it: the boxes on the way need `shrink: 1`.
1471
+ * const name: TextWrap = "box";
1459
1472
  * ```
1460
1473
  */
1461
- type TextWrap = number | "none";
1474
+ type TextWrap = number | "box" | "none";
1462
1475
  /**
1463
1476
  * The drop shadow of a style as the plugin stores it: a copy of every glyph run in `color`, at
1464
1477
  * `alpha`, moved by `dx` and `dy` reference pixels.
@@ -1662,13 +1675,15 @@ type LayoutOptions = {
1662
1675
  * What was resolved for one entity last, so the frame step knows whether anything moved. `unit`
1663
1676
  * is what the shown string of a bound label was built from: the rounded value for `"int"`, whole
1664
1677
  * seconds for a time format. `bindRef` is the bind object `bind` was keyed from, so the key is
1665
- * built once per attached bind and a frame compares one reference.
1678
+ * built once per attached bind and a frame compares one reference. `box` is the `Text.box` the
1679
+ * label was laid out at: a label of a `"box"` style is laid out again when it moves.
1666
1680
  *
1667
1681
  * @example
1668
1682
  * ```ts
1669
1683
  * const left = bind(Countdown, "left", { format: "mm:ss" });
1670
1684
  * const seen: SeenText = {
1671
- * content: "", style: "digits", bindRef: left, bind: "Countdown.left:mm:ss", locale: "ru", unit: 95
1685
+ * content: "", style: "digits", bindRef: left, bind: "Countdown.left:mm:ss", locale: "ru",
1686
+ * unit: 95, box: undefined
1672
1687
  * };
1673
1688
  * ```
1674
1689
  */
@@ -1679,6 +1694,7 @@ type SeenText = {
1679
1694
  bind: string | undefined;
1680
1695
  locale: string;
1681
1696
  unit: number | undefined;
1697
+ box: number | undefined;
1682
1698
  };
1683
1699
  /**
1684
1700
  * text plugin config.
@@ -1705,7 +1721,7 @@ type State = {
1705
1721
  installed: Set<string>; /** What was resolved for an entity last. */
1706
1722
  seen: Map<Entity, SeenText>; /** `bind.component` to its world type; `undefined` after the one warning. */
1707
1723
  bindTypes: Map<string, AnyComponent | undefined>; /** The measured size of every live label, for `ui`. */
1708
- measured: Map<Entity, Size>; /** Layout per `style + resolved`, oldest dropped first. */
1724
+ measured: Map<Entity, Size>; /** Layout per `style + resolved`, and the box width for a `"box"` style; oldest dropped first. */
1709
1725
  cache: Map<string, TextLayout>; /** What every live label container was last filled from, by container. */
1710
1726
  drawn: WeakMap<object, DrawnLabel>; /** Entities to re-resolve in the next layout phase. */
1711
1727
  dirty: Set<Entity>;
@@ -1743,10 +1759,13 @@ type TextApi = {
1743
1759
  /**
1744
1760
  * The size of a piece of text in reference pixels, from the advance table of the style's font.
1745
1761
  * A `Message` is formatted in the current locale first. Never touches a canvas, so the answer
1746
- * is the same in a browser and in plain Bun. Cached per style and resolved string.
1762
+ * is the same in a browser and in plain Bun. Cached per style and resolved string, and per
1763
+ * width for a `"box"` style.
1747
1764
  *
1748
1765
  * @param content - A plain string, or what `tr` returned.
1749
1766
  * @param style - A registered style name. An unknown one warns once and measures as `body`.
1767
+ * @param width - The width a style with `wrap: "box"` wraps at, in reference pixels. Left out
1768
+ * (or 0) the block is one line. A style that wraps otherwise ignores it.
1750
1769
  * @returns The width and height of the block.
1751
1770
  * @example
1752
1771
  * ```ts
@@ -1754,9 +1773,12 @@ type TextApi = {
1754
1773
  * const text = ctx.require(textPlugin);
1755
1774
  *
1756
1775
  * text.measure("120", "digits"); // { width: 57.6, height: 38.4 } on the 0.6 em fallback
1776
+ * // "card.name" is a 32 px style with wrap "box": the width Yoga offers wraps it.
1777
+ * text.measure("Aleksandra Kucherenko", "card.name", 200); // { width: 192, height: 76.8 }
1778
+ * text.measure("Aleksandra Kucherenko", "card.name"); // { width: 403.2, height: 38.4 }
1757
1779
  * ```
1758
1780
  */
1759
- measure(content: string | Message, style: string): Size;
1781
+ measure(content: string | Message, style: string, width?: number): Size;
1760
1782
  /**
1761
1783
  * Whether the font of a style has a glyph for one character, the lookup Pixi draws by. A
1762
1784
  * character its font lacks is drawn as nothing and `measure` counts it 0 wide. While the font is
@@ -2339,6 +2361,74 @@ declare function popup<Properties extends object, Local extends object, Outcomes
2339
2361
  over?: string;
2340
2362
  }): Descriptor<Answer<keyof Outcomes & string>>;
2341
2363
  //#endregion
2364
+ //#region src/plugins/ui/layout/types.d.ts
2365
+ /**
2366
+ * The patches the scroll step writes with, one object each, filled in place: a frame builds none.
2367
+ * Every number in them is a whole reference unit.
2368
+ */
2369
+ type ScrollPatches = {
2370
+ scroll: {
2371
+ offset: number;
2372
+ min: number;
2373
+ };
2374
+ state: {
2375
+ offset: number;
2376
+ max: number;
2377
+ moving: boolean;
2378
+ };
2379
+ scrollable: {
2380
+ moving: boolean;
2381
+ };
2382
+ x: {
2383
+ x: number;
2384
+ };
2385
+ y: {
2386
+ y: number;
2387
+ };
2388
+ };
2389
+ /**
2390
+ * layout module state. `nodes` is attach minus detach: Yoga 3.2.1 has no instance counter.
2391
+ * `scrolling` is the scroll the finger holds; `scrollClock` the ms the scroll step has run, the
2392
+ * clock of the samples; `scrollRing` the last pointer samples of the press on its axis, a
2393
+ * position and a time each, `scrollHead` the slot the next one takes and `scrollSamples` how
2394
+ * many it holds; `scrollMoved` when the finger last moved.
2395
+ */
2396
+ type LayoutState = {
2397
+ yoga: Yoga | undefined;
2398
+ byEntity: Map<Entity, Node>;
2399
+ nodes: number;
2400
+ measured: number;
2401
+ solves: number;
2402
+ scrolling: Entity | undefined;
2403
+ scrollClock: number;
2404
+ scrollRing: Float64Array;
2405
+ scrollHead: number;
2406
+ scrollSamples: number;
2407
+ scrollMoved: number;
2408
+ scrollPatches: ScrollPatches; /** One line of the `body` style as the icons were last measured with it; none before that. */
2409
+ iconLine: number | undefined;
2410
+ order: number;
2411
+ cleanups: Array<() => void>;
2412
+ };
2413
+ /**
2414
+ * The longest finite track a motion hook started through its view handle: where it ends (delay included), its length,
2415
+ * delay, ease and reduced-motion rule. The level of an overlay follows the root's own enter and exit motion by it
2416
+ * (spec 22, LEAD-FIXES-4 item 5a); `end` 0 means the hook started no timed track.
2417
+ *
2418
+ * @example
2419
+ * ```ts
2420
+ * // A sheet that fades in over 250 ms after 40 ms.
2421
+ * const span: MotionSpan = { end: 290, ms: 250, delayMs: 40, ease: "out", reduced: undefined };
2422
+ * ```
2423
+ */
2424
+ type MotionSpan = {
2425
+ end: number;
2426
+ ms: number;
2427
+ delayMs: number;
2428
+ ease: Ease | undefined;
2429
+ reduced: ReducedMotion | undefined;
2430
+ };
2431
+ //#endregion
2342
2432
  //#region src/plugins/ui/jsx/types.d.ts
2343
2433
  /**
2344
2434
  * One node of an element description, as the JSX runtime builds it. `key` is the third argument
@@ -2534,7 +2624,9 @@ type StyleBasis = {
2534
2624
  * warnings of its props in `warned`; `scrolledIn` marks an element a range change of a windowed
2535
2625
  * scroll created: it plays no `enter` hook. A scroll carries its `scroll` motion from its enter. A text or an icon that sizes itself keeps in
2536
2626
  * `measuredSize` what its measure function last answered, before Yoga's clamp: a bound text whose
2537
- * new string measures the same asks for no solve. `basis` is what `style` was resolved on, set
2627
+ * new string measures the same asks for no solve. `measuredWidth` is the width Yoga offered that
2628
+ * call when the text wrapped at it (only a `"box"` style does), `undefined` otherwise: such a text
2629
+ * is measured again at its laid-out width and gets a `Text.box`, any other gets none. `basis` is what `style` was resolved on, set
2538
2630
  * once the style is on the layout node and the components are written; an element without one is
2539
2631
  * patched in full at its next patch. `index` is its place among the element children of its parent
2540
2632
  * at the last diff (0 for a root element): with its `zIndex` it gives the child its `Order`.
@@ -2592,8 +2684,35 @@ type Element = {
2592
2684
  width: number;
2593
2685
  height: number;
2594
2686
  };
2687
+ measuredWidth?: number | undefined; /** True once the text wrapped at its box: from then on `ui` writes its box, 0 when it fits. */
2688
+ boxed?: boolean;
2595
2689
  basis?: StyleBasis; /** The motions of its hooks that started a `repeat: "forever"` track; made with the first one. */
2596
- endless?: Set<MotionHandle>;
2690
+ endless?: Set<MotionHandle>; /** What its enter hook did at the `Box`, until `ui.enter` keeps the play or plays it again (F49). */
2691
+ posed?: PosedEnter | undefined;
2692
+ };
2693
+ /**
2694
+ * One `set` of an enter hook: the fields it wrote, and the values they had before. A hook may write any component of
2695
+ * the game, so the fields are kept by name, as records.
2696
+ */
2697
+ type PoseWrite = {
2698
+ component: ComponentType<Record<string, unknown>>;
2699
+ wrote: Record<string, unknown>;
2700
+ before: Record<string, unknown>;
2701
+ };
2702
+ /**
2703
+ * What the play of an enter hook at the `Box` kept: the motion it returned, the components it read
2704
+ * (with their fields as they stood after the play), the names of the components it wrote, its
2705
+ * `set` writes, and the span noted for an overlay.
2706
+ */
2707
+ type PosedEnter = {
2708
+ motion: MotionHandle | undefined;
2709
+ reads: Array<{
2710
+ component: ComponentType<Record<string, unknown>>;
2711
+ seen: Record<string, unknown>;
2712
+ }>;
2713
+ written: string[];
2714
+ writes: PoseWrite[];
2715
+ span: MotionSpan | undefined;
2597
2716
  };
2598
2717
  /**
2599
2718
  * The popup side of a root: the handler that shows it now and the root it was opened over.
@@ -2792,8 +2911,8 @@ type JsxState = {
2792
2911
  text: TextState;
2793
2912
  hints: Hint[];
2794
2913
  /**
2795
- * The elements whose `Box` came since the last `sync`: their enter hooks play there, after the game's systems
2796
- * (F48). The first `count` slots are the queue; the array keeps its length, so a frame allocates nothing.
2914
+ * The elements whose `Box` came since the last `sync`: their enter hooks played at the `Box` (F49) and are settled
2915
+ * there, after the game's systems, played again when a system changed what a hook read (F48). The first `count` slots are the queue; the array keeps its length, so a frame allocates nothing.
2797
2916
  */
2798
2917
  entering: {
2799
2918
  list: Entity[];
@@ -2922,4 +3041,4 @@ type CommonProps = {
2922
3041
  components?: ElementComponents;
2923
3042
  };
2924
3043
  //#endregion
2925
- export { defineComponent as A, Part as B, TextApi as C, TextStyles as D, TextStyleInput as E, Argument as F, types_d_exports$2 as G, StringTable as H, Config$2 as I, Style as J, IsFlags as K, I18nApi as L, InputApi as M, State$1 as N, TextValue as O, types_d_exports$1 as P, IntlKit as R, State as S, TextFormat as T, TextKey as U, State$2 as V, TypedTr as W, WhenFlags as X, StylesState as Y, BindField as _, JsxChild as a, CountdownValue as b, UiNode as c, LocalWrite as d, ScrollState as f, UiIntrinsicElements as g, IntrinsicElementsFor as h, Finding as i, Config$1 as j, types_d_exports as k, Box as l, defineTokens as m, ElementChange as n, JsxState as o, popup as p, ResolvedStyle as q, ElementMotion as r, PopupComponent as s, DescriptionNode as t, DebugTrigger as u, BindOptions as v, TextBind as w, Point as x, Config as y, Message as z };
3044
+ export { types_d_exports as A, Message as B, State as C, TextStyleInput as D, TextFormat as E, types_d_exports$1 as F, TypedTr as G, State$2 as H, Argument as I, ResolvedStyle as J, types_d_exports$2 as K, Config$2 as L, Config$1 as M, InputApi as N, TextStyles as O, State$1 as P, I18nApi as R, Point as S, TextBind as T, StringTable as U, Part as V, TextKey as W, StylesState as X, Style as Y, WhenFlags as Z, UiIntrinsicElements as _, JsxChild as a, Config as b, UiNode as c, DebugTrigger as d, LocalWrite as f, IntrinsicElementsFor as g, defineTokens as h, Finding as i, defineComponent as j, TextValue as k, LayoutState as l, popup as m, ElementChange as n, JsxState as o, ScrollState as p, IsFlags as q, ElementMotion as r, PopupComponent as s, DescriptionNode as t, Box as u, BindField as v, TextApi as w, CountdownValue as x, BindOptions as y, IntlKit as z };
@@ -1,5 +1,5 @@
1
- import { Nr as Snapshot, wr as Json } from "./types-CuEK0Vam.mjs";
2
- import { t as HeadlessApp } from "./headless-DHtavbqk.mjs";
1
+ import { Nr as Snapshot, wr as Json } from "./types-DOldmhIw.mjs";
2
+ import { t as HeadlessApp } from "./headless-ByFLyHxx.mjs";
3
3
  import { Log } from "@moku-labs/common/browser";
4
4
 
5
5
  //#region src/plugins/flow/doors/types.d.ts
@@ -618,7 +618,8 @@ function rootPointOf(ecs, entity, point) {
618
618
  * Words on the screen: a string or a message, the style it is drawn in, and the string `text`
619
619
  * resolved out of it. A game writes `content`, `style`, `bind`, `anchor` and `alpha`; `resolved`
620
620
  * is engine-owned and `ui` and the tests read it. `alpha` (1 by default) fades the whole block and
621
- * is patched in place, so a tween on it rebuilds nothing.
621
+ * is patched in place, so a tween on it rebuilds nothing. `box` (none by default) is the width a
622
+ * style with `wrap: "box"` wraps at; `ui` writes the laid-out width of a text element there.
622
623
  */
623
624
  const Text = /*#__PURE__*/ component("Text", {
624
625
  content: "",
@@ -726,16 +727,30 @@ function shadowOf(input) {
726
727
  };
727
728
  }
728
729
  /**
729
- * Fills the defaults of one style and refuses a wrap that is neither a width nor `"none"`.
730
+ * Tells whether a wrap is one a style can carry: a width above zero, `"box"` or `"none"`. A style
731
+ * read back from a feature may carry anything, so a word is checked as well as a number.
732
+ *
733
+ * @param wrap - The wrap a style names, as it was stored.
734
+ * @returns False for a number that is not above zero, and for anything else.
735
+ * @example
736
+ * ```ts
737
+ * isWrap("box"); // true
738
+ * ```
739
+ */
740
+ function isWrap(wrap) {
741
+ return typeof wrap === "number" ? wrap > 0 : wrap === "box" || wrap === "none";
742
+ }
743
+ /**
744
+ * Fills the defaults of one style and refuses a wrap that is not a width, `"box"` or `"none"`.
730
745
  *
731
746
  * @param name - The style name, for the error.
732
747
  * @param input - What the game wrote.
733
748
  * @returns The style with every field filled.
734
- * @throws {Error} When `wrap` is a number that is not greater than zero.
749
+ * @throws {Error} When `wrap` is a number that is not greater than zero, or another word.
735
750
  */
736
751
  function normalizeStyle(name, input) {
737
752
  const wrap = input.wrap ?? "none";
738
- if (wrap !== "none" && wrap <= 0) throw new Error(`[game] Text style "${name}" has wrap ${wrap}.\n Use a width in reference px or "none".`);
753
+ if (!isWrap(wrap)) throw new Error(`[game] Text style "${name}" has wrap ${wrap}.\n Use a width in reference px, "box" or "none".`);
739
754
  return {
740
755
  font: input.font,
741
756
  bold: input.bold,
@@ -757,7 +772,7 @@ function normalizeStyle(name, input) {
757
772
  *
758
773
  * @param map - Style name to what that style looks like.
759
774
  * @returns The registration a feature carries.
760
- * @throws {Error} When a style names a wrap that is not a width and not `"none"`.
775
+ * @throws {Error} When a style names a wrap that is not a width, `"box"` or `"none"`.
761
776
  * @example
762
777
  * ```ts
763
778
  * defineTextStyles({ "hud.digits": { font: "ui.font-digits", size: 40, fill: 0xffe082 } });
@@ -1182,9 +1197,24 @@ function contentOf(props) {
1182
1197
  return props.content ?? "";
1183
1198
  }
1184
1199
  /**
1185
- * The visual component of an element: a sprite for an image or an icon, the text for a text, the
1186
- * nine-slice of its style (outlined when the style sets `debug`), or a rounded rectangle, a
1187
- * triangle with `shape: "triangle"`, its stroke dashed with `dash`, or an arc with `shape: "arc"`
1200
+ * The box a text wraps at: its laid-out width while it wraps at it, 0 once it fits again, so a
1201
+ * text that wrapped draws on one line, and no field on a text that never wrapped.
1202
+ *
1203
+ * @param element - The text element, with its rect known.
1204
+ * @returns `{ box }`, or nothing.
1205
+ */
1206
+ function boxOf(element) {
1207
+ if (element.measuredWidth !== void 0) {
1208
+ element.boxed = true;
1209
+ return { box: element.rect.w };
1210
+ }
1211
+ return element.boxed === true ? { box: 0 } : {};
1212
+ }
1213
+ /**
1214
+ * The visual component of an element: a sprite for an image or an icon, the text for a text (with
1215
+ * its laid-out width as the box a `"box"` style wraps at), the nine-slice of its style (outlined
1216
+ * when the style sets `debug`), or a rounded rectangle, a triangle with `shape: "triangle"`, its
1217
+ * stroke dashed with `dash`, or an arc with `shape: "arc"`
1188
1218
  * over the `sweep` of its style, ended by its `cap`. A clipping element (`scroll`,
1189
1219
  * `overflow: "hidden"`) keeps the rectangle, which carries the clip; a text field's rectangle
1190
1220
  * clips too, and a nine-slice field draws its slices and clips to them. A style with a stroke and
@@ -1221,7 +1251,8 @@ function visualOf(element) {
1221
1251
  anchor: {
1222
1252
  x: 0,
1223
1253
  y: 0
1224
- }
1254
+ },
1255
+ ...boxOf(element)
1225
1256
  })];
1226
1257
  }
1227
1258
  const clip = type === "scroll" || style.overflow === "hidden";
@@ -1,9 +1,9 @@
1
- import { $t as Api$1, K as Api, Rr as Api$3, xr as Api$2 } from "./types-CuEK0Vam.mjs";
2
- import { P as AnimApi, r as UiApi } from "./types-3bEK86Z1.mjs";
3
- import { M as InputApi } from "./types-xmH1X8qd.mjs";
4
- import { r as Repro } from "./headless-DHtavbqk.mjs";
5
- import { a as InputOf, r as ControlApp } from "./types-_a6-ALzz.mjs";
6
- import { t as commands } from "./commands-aLTGwuyk.mjs";
1
+ import { $t as Api$1, K as Api, Rr as Api$3, xr as Api$2 } from "./types-DOldmhIw.mjs";
2
+ import { P as AnimApi, r as UiApi } from "./types-Bi6hj1OB.mjs";
3
+ import { N as InputApi } from "./types-ae0jg0r4.mjs";
4
+ import { r as Repro } from "./headless-ByFLyHxx.mjs";
5
+ import { a as InputOf, r as ControlApp } from "./types-qsE-I59e.mjs";
6
+ import { t as commands } from "./commands-DEVJzdsr.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-DExHHx-a.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-Hj0oNqPP.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/dist/visual.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { n as read, t as sources } from "./sources-Fbq_PFVH.mjs";
1
+ import { n as read, t as sources } from "./sources-BYKXYPhd.mjs";
2
2
  import { n as commands, t as run } from "./run-BqYmcNo7.mjs";
3
3
  import path from "node:path";
4
4
  import { createBrandConsole } from "@moku-labs/common/cli";
package/llms.txt CHANGED
@@ -554,7 +554,7 @@ A screen is a projection whose `view` returns JSX. The game's `tsconfig.json` se
554
554
  - `defineComponent(name, { local?, outcomes?, view(props, local) })`. A component with `outcomes` is a popup: `await fx(popup(Component, props))` opens the gate for its outcomes and resolves with `{ intent, payload }` of the pressed button. A button with `local` writes into the nearest component instance; it never reaches the graph.
555
555
  - `tr(key, params?)` returns a frozen `Message` `{ key, params }`; no locale is read at the call site. Strings live in `strings/<locale>.json` of any feature or sub-feature, as ICU MessageFormat 1 (`{n, plural, one {# order} other {# orders}}`, `select`, `selectordinal`, `number`, `date`, `time`; no skeletons, no `currency`). `bun run assets:keys` compiles them. The keys of the top-level features and the layers go into `generated/strings.<locale>.ts`, which one feature registers; a sub-feature's keys start with `<name>.` (`ep01.title`) and go into `generated/strings.<name>.<locale>.ts`, which the sub-feature registers lazily, `strings: { en: () => import("@generated/strings.ep01.en"), ru: () => import("@generated/strings.ep01.ru") }`. With `assets` composed those modules load and unload with the bundle named like the sub-feature: every way the bundle comes (a node entry, a scene's `bundles`, `load`, a restore, the `safeNode`) brings its strings, a locale switch brings them in the new locale, and while the bundle is not loaded a key of it gives `⟦key⟧` and `has(key)` is false. A compiled module calls the root helpers `messageArgument(value, intl)` (an element stays an element part, a list goes through `Intl.ListFormat`, a number through `Intl.NumberFormat`, else text) and `messageDuration(ms)` (`95_000` gives `{ minutes: 1, seconds: 35 }`). `app.i18n`: `locale()`, `setLocale(locale)` (async, emits `i18n:locale-changed`), `format(message, locale?)` returns `Part[]`, `plain(message)` returns a string, `has(key)`, `isKey(value)`, `locales()`, `replace(locale, messages)` (dev hot swap of a generated strings file). Config: `locale: "en"`, `fallback: "en"`, `locales: {}`.
556
556
  - Keys that come from data: `I18n.TextKey<Strings>` is the keys of the generated `Strings` without parameters (nothing new is generated). A data table types its keys `TextKey` or builds them with the kit's `key("board.hint.red")`; the kit's `tr` never takes a plain `string`. A key read back from a save is checked where the app is (an effect handler, app or page code, loading a save) with one `isKey(value)`: `defineGameApp({ ..., strings: type<Strings>() })` types the game's `app.i18n`, so a true `app.i18n.isKey(value)` narrows the value to `TextKey`; `ctx.require(i18nPlugin).isKey(value)` in a plugin narrows to `string`. `app.i18n.isKey(value)` answers true for a key of the loaded tables of the current locale or the fallback, and for the `<feature>.` prefix of a sub-feature whose strings are not loaded yet; a key that takes parameters answers true too.
557
- - Text: `Text` component, `label({ text, style, at, anchor? })` for board text in a projection view, `defineTextStyles({ "hud.digits": { font, size, fill, bold?, italic?, stroke?, strokeWidth?, letterSpacing?, align?, wrap?, digits? } })` under the feature's `textStyles` key. Tags inside a string: `<b>`, `<i>`, `<color=#rrggbb>`, `<icon=key>`, `\<`, `\n`. Built-in styles `body` and `digits` from config `fonts: { body: "ui.font-body", digits: "ui.font-digits" }`. `app.text.measure(content, style)` gives the same numbers in a browser and in Bun. `app.text.replaceStyles(styles)` is the dev hot swap of a saved `defineTextStyles` value.
557
+ - Text: `Text` component, `label({ text, style, at, anchor? })` for board text in a projection view, `defineTextStyles({ "hud.digits": { font, size, fill, bold?, italic?, stroke?, strokeWidth?, letterSpacing?, align?, wrap? (a width, "none" or "box": wrap at the laid-out width of its ui element), digits? } })` under the feature's `textStyles` key. Tags inside a string: `<b>`, `<i>`, `<color=#rrggbb>`, `<icon=key>`, `\<`, `\n`. Built-in styles `body` and `digits` from config `fonts: { body: "ui.font-body", digits: "ui.font-digits" }`. `app.text.measure(content, style, width?)` gives the same numbers in a browser and in Bun. `app.text.replaceStyles(styles)` is the dev hot swap of a saved `defineTextStyles` value.
558
558
  - Animation is data: `defineAnimation(id, { slots: { name: type<Anim.Target>() }, build: (slots, { at }) => Step })`. Steps: `sequence(...)`, `parallel(...)`, `stagger(items, ms, item => Step)`, `tween(target, Component, to, { ms, ease?, delayMs?, additive?, space?, reduced? })`, `set(target, Component, patch)`, `wait(ms)`, `mark(name)`, `frames(target, { keys, fps, loop? })`, `sfx(key, { bus? })`, `haptic(kind)`, `use(animation, slots, tools?)`, `spawn` (a temporary entity, flying coins, that the timeline despawns when it ends; see the anim README). `Frames({ keys, fps, loop, playing })` is a component that walks a sprite through its textures for the entity's whole life. A target is `{ projection, key }` or an entity. A timeline resolves each `{ projection, key }` target once, when it starts: every step writes the view that held the key then, also after a commit removed it and while its exit plays, until it despawns; a view spawned again under the same key is not followed, and a key nothing held at the start is looked up at its step. `reduced: "keep"` (on a `tween` step, `transition` and `loop` of `defineMotion`, `TweenOptions`, `RestOptions`) plays a track as written under reduced motion, a fade for one; the default `"cut"` lands it at once or holds a loop on its first key. A node plays it with `await fx(play(animation, slots))`; in fast mode it finishes at once. `defineMotion({ states, transition?, on: { enter?, exit?, change? }, when? })` builds the enter, exit and change hooks for a projection or the `motion` prop of a tag; `when: "change"` plays enter and exit only for a commit, so an element that comes or goes with its screen stands at rest and leaves without a motion (default `"always"`). Events `anim:mark` `{ animation, mark }` and `anim:finished` `{ animation }`. `app.anim.replace(definition)` is the dev hot swap: the next `play` builds the new tree. A system cannot `require` a plugin, so it plays through the resource `AnimPlayer`: `res(AnimPlayer).play(animation, slots)` returns the same handle as `app.anim.play`, and the registered animation of `animation.id` wins, so a hot swap reaches the system. Play on a change, never every frame; from phase `input` the tree moves in `animate` of the same frame. Before `anim` starts it throws `[game] AnimPlayer is not ready: anim has not started.` Config `maxTracks: 2000`.
559
559
  - Audio is opt-in: `plugins: [...screen, audioPlugin]`. `fx(sfx("orders.complete"))` resolves when the sound started. `await fx(music("board.theme"))` cross-fades; `music(null)` fades out; a scene's `music` key switches the track on `scenes:changed`. Buses `master`, `music`, `sfx`. Volumes come from the committed player: config `volumes: player => (player as Player).settings.audio`, applied on every `model:committed`. `app.audio`: `setVolume(bus, value)`, `volume(bus)`, `mute(bus, on)`, `muted(bus)`, `unlocked()`, `journal()` (the last started sounds, empty unless config `journal` is above 0). Config: `buses: { master: 1, music: 0.6, sfx: 1 }`, `musicFadeMs: 600`, `volumes`, `context`, `journal: 0`, `music: "decode" | "stream"` default `"decode"` (gapless buffer; `"stream"` is an `<audio>` element, less memory, a gap at every loop), `session: "ambient" | "playback" | "auto"` default `"ambient"` (iOS only: ambient mixes with other apps and obeys the silent switch). The context is asked to run at start: where the shell lets audio play (a native webview with the gesture requirement off) `unlocked()` is true with no touch; a browser that wants a gesture unlocks on the first `pointerdown` or `touchend`. Without `audio` every `sfx` and `music` effect resolves `undefined` at once, so the same node runs in a game with no sound.
560
560
  - `app.ui`: `tree()` the live screen as plain data (a text node carries `content`, the words it draws after `tr()` and `bind`; a text field carries `value`), `find(key)` the entity of a keyed element, `fill(key, value)` types into the text field with that `key`, `scroll(key, to)` puts the `scroll` with that `key` at `to` from its start at once, inside `0..max`, its fling or glide stopped (`false` and `ui:scroll-without-scroll` for no live `scroll`, `false` and `ui:scroll-before-solve` for a `scroll` its first solve has not placed yet), `lint()` tap targets under `tapTargetPt` (default `44`), text that overflows in some locale, absolute elements without `reason`. Config: `tapTargetPt: 44`, `breakpoints: { tall: 2, wide: 1.5 }`. `ui` emits only the dev global event `ui:hot-swap`, see "Hot swap".
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moku-labs/game",
3
- "version": "0.15.14",
3
+ "version": "0.15.17",
4
4
  "description": "2D puzzle game engine on TypeScript and PixiJS, built on @moku-labs/core.",
5
5
  "type": "module",
6
6
  "sideEffects": [