@moku-labs/game 0.15.32 → 0.15.34

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,6 +1,6 @@
1
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-D27I4Q7b.mjs";
2
- import { t as createApp } from "./index-DMeqKGSu.mjs";
3
- import { b as saveOf, v as WithGameState, y as memory } from "./types-DmU_qlvA.mjs";
2
+ import { t as createApp } from "./index-ZPmKRr0j.mjs";
3
+ import { b as saveOf, v as WithGameState, y as memory } from "./types-DrtsLNnT.mjs";
4
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-f31cEZ7f.mjs";
5
5
  import { AnyPluginInstance } from "@moku-labs/core";
6
6
 
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-5qdNjLKN.mjs";
1
+ import { en as refuseStart, in as type, m as flattenFeatures, t as createApp, tn as exit } from "./src-DFhuDrm7.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,5 +1,5 @@
1
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-D27I4Q7b.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-BAp9HdR4.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-DGHVGP5i.mjs";
3
3
  import { Log } from "@moku-labs/common/browser";
4
4
  import { PluginCtx } from "@moku-labs/core";
5
5
 
@@ -1151,7 +1151,8 @@ type State$3 = {
1151
1151
  registry: Map<string, AnyAnimationDefinition>; /** `onMark` subscribers, in subscription order. */
1152
1152
  markListeners: Set<MarkListener>; /** Counter of the frame steps. A track born in the running step is not advanced by it. */
1153
1153
  frame: number; /** True while the track count is over `maxTracks`, so the warning is one per crossing. */
1154
- overMaxTracks: boolean; /** The live reduced-motion switch. The frozen `Config.reducedMotion` only seeds it. */
1154
+ overMaxTracks: boolean; /** The animations already warned for a looping `frames` step that blocks the rest of a `sequence` (dev). */
1155
+ warnedLoops: Set<string>; /** The live reduced-motion switch. The frozen `Config.reducedMotion` only seeds it. */
1155
1156
  reducedMotion: boolean; /** Remover of `world.projection.setDriver`. */
1156
1157
  removeDriver: (() => void) | undefined; /** Remover of `time.onFrame("animate")`. */
1157
1158
  offFrame: (() => void) | undefined; /** Remover of `flow.fx.handle("play")`. */
@@ -2502,9 +2502,12 @@ type ElementChange<Value> = (view: ViewHandle<unknown>, previous: Value, next: V
2502
2502
  /**
2503
2503
  * The motion hooks of one element: the hook triple of `world.projection`, which `defineMotion`
2504
2504
  * of `anim` returns. `ui` plays the `change` hooks of `Box` (the rects), of `Transform` (the rest
2505
- * poses) and of every extra component of the `components` prop (its values before and after). A
2506
- * hook for any other component name is accepted, so every `defineMotion` result fits, and `ui`
2507
- * never plays it. Every hook gets the hint routed to the element's key in that frame step.
2505
+ * poses), of the visual the element is drawn with (`Shape`, `Sprite`, `NineSlice` or `Text`: its
2506
+ * values before and after, when a render or a restyle moves a number of it) and of every extra
2507
+ * component of the `components` prop (its values before and after). A hook for any other
2508
+ * component name is accepted, so every `defineMotion` result fits; `ui` never plays it, and a dev
2509
+ * build names it once (`ui:change-never-plays`). Every hook gets the hint routed to the element's
2510
+ * key in that frame step.
2508
2511
  *
2509
2512
  * @example
2510
2513
  * ```ts
@@ -2648,27 +2651,29 @@ type StyleBasis = {
2648
2651
  };
2649
2652
  /**
2650
2653
  * One live element: the entity, the Yoga node, the resolved style and the place in the tree.
2651
- * `rect` is natural: under a `fit` ancestor it is the rect before that ancestor's scale. `fit`
2652
- * is the element's own fit scale (1 without `fit: "contain"`), and `rest` the rest `Transform`
2653
- * last written for it. `loop` is the motion of the running `loop` hook, kept apart from
2654
- * `handles`: it never ends, so the exit sweep must not wait for it. `extras` holds the values of
2655
- * the `components` prop added last time, by component name; `extraHandles` the motion the last
2656
- * `change` hook of each of them returned; `warnedOwned` the names of the `components` values the
2657
- * element owns that were already logged. A windowed scroll carries its `window` and the one-time
2658
- * warnings of its props in `warned`; `scrolledIn` marks an element a range change of a windowed
2659
- * 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
2660
- * `measuredSize` what its measure function last answered, before Yoga's clamp: a bound text whose
2661
- * new string measures the same asks for no solve. `measuredWidth` is the width Yoga offered that
2662
- * call when the text wrapped at it (only a `"box"` style does), `undefined` otherwise: such a text
2663
- * 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
2664
- * once the style is on the layout node and the components are written; an element without one is
2665
- * patched in full at its next patch. `index` is its place among the element children of its parent
2666
- * at the last diff (0 for a root element): with its `zIndex` it gives the child its `Order`.
2667
- * `cause` is why the element appeared: `"mount"` when it came with the first build of its root and
2668
- * the root came by a mount, `"change"` otherwise; its `enter` hook is told. `rebuilds` counts the
2669
- * patches in a row that brought a loop of another reference that is not really another loop: a
2670
- * dev build names the element once it reaches three. `endless` holds the motions a hook returned
2671
- * after it started a track that repeats forever: they never end, so no exit waits for them (F42).
2654
+ * `rect` is natural: under a `fit` ancestor it is the rect before that ancestor's scale. `fit` is
2655
+ * the element's own fit scale (1 without `fit: "contain"`), and `rest` the rest `Transform` last
2656
+ * written for it. `loop` is the motion of the running `loop` hook, kept apart from `handles`: it
2657
+ * never ends, so the exit sweep must not wait for it. `extras` holds the values of the
2658
+ * `components` prop added last time, by component name; `extraHandles` the motion the last
2659
+ * `change` hook of each of them and of the visual returned; `warnedOwned` the names of the
2660
+ * `components` values the element owns that were already logged. A windowed scroll carries its
2661
+ * `window` and the one-time warnings of its props in `warned`; `scrolledIn` marks an element a
2662
+ * range change of a windowed scroll created: it plays no `enter` hook. A scroll carries its
2663
+ * `scroll` motion from its enter. A text or an icon that sizes itself keeps in `measuredSize` what
2664
+ * its measure function last answered, before Yoga's clamp: a bound text whose new string measures
2665
+ * the same asks for no solve. `measuredWidth` is the width Yoga offered that call when the text
2666
+ * wrapped at it (only a `"box"` style does), `undefined` otherwise: such a text is measured again
2667
+ * at its laid-out width and gets a `Text.box`, any other gets none. `basis` is what `style` was
2668
+ * resolved on, set once the style is on the layout node and the components are written; an
2669
+ * element without one is patched in full at its next patch. `index` is its place among the
2670
+ * element children of its parent at the last diff (0 for a root element): with its `zIndex` it
2671
+ * gives the child its `Order`. `cause` is why the element appeared: `"mount"` when it came with
2672
+ * the first build of its root and the root came by a mount, `"change"` otherwise; its `enter` hook
2673
+ * is told. `rebuilds` counts the patches in a row that brought a loop of another reference that is
2674
+ * not really another loop: a dev build names the element once it reaches three. `endless` holds
2675
+ * the motions a hook returned after it started a track that repeats forever: they never end, so
2676
+ * no exit waits for them (F42).
2672
2677
  */
2673
2678
  type Element = {
2674
2679
  entity: Entity;
@@ -2724,7 +2729,10 @@ type Element = {
2724
2729
  blended?: boolean;
2725
2730
  basis?: StyleBasis; /** The motions of its hooks that started a `repeat: "forever"` track; made with the first one. */
2726
2731
  endless?: Set<MotionHandle>; /** What its enter hook did at the `Box`, until `ui.enter` keeps the play or plays it again (F49). */
2727
- posed?: PosedEnter | undefined;
2732
+ posed?: PosedEnter | undefined; /** The value of its visual last written: what a `change` hook of the visual gets as the value before (F85). */
2733
+ look?: AnyComponentValue | undefined; /** The write pass in which a `change` hook of its visual last returned a motion. */
2734
+ lookPass?: number | undefined; /** The value of its visual before that pass: a second write in the pass plays the hook from it. */
2735
+ lookFrom?: object | undefined;
2728
2736
  };
2729
2737
  /**
2730
2738
  * One `set` of an enter hook: the fields it wrote, and the values they had before. A hook may write any component of
@@ -1,7 +1,7 @@
1
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-D27I4Q7b.mjs";
2
- import { o as AudioContextLike, s as Config$4 } from "./types-DHamyZq1.mjs";
3
- import { R as I18nApi, U as StringTable, W as TextKey } from "./types-BAp9HdR4.mjs";
4
- import { St as effectsPlugin, Tt as audioPlugin, ht as PlatformProvider, i as screen, pt as platformPlugin, t as createApp } from "./index-DMeqKGSu.mjs";
2
+ import { o as AudioContextLike, s as Config$4 } from "./types-CrZvIBB9.mjs";
3
+ import { R as I18nApi, U as StringTable, W as TextKey } from "./types-DGHVGP5i.mjs";
4
+ import { St as effectsPlugin, Tt as audioPlugin, ht as PlatformProvider, i as screen, pt as platformPlugin, t as createApp } from "./index-ZPmKRr0j.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
1
  import { Vr as Api$3, Y as Api, nn as Api$1, vr as Api$4, wr as Api$2 } from "./types-D27I4Q7b.mjs";
2
- import { P as AnimApi, r as UiApi } from "./types-DHamyZq1.mjs";
3
- import { N as InputApi } from "./types-BAp9HdR4.mjs";
2
+ import { P as AnimApi, r as UiApi } from "./types-CrZvIBB9.mjs";
3
+ import { N as InputApi } from "./types-DGHVGP5i.mjs";
4
4
  import { r as Repro } from "./headless-f31cEZ7f.mjs";
5
5
  import { a as InputOf, r as ControlApp } from "./types-CgE5L_7f.mjs";
6
- import { t as commands } from "./commands-C8N_rpwt.mjs";
6
+ import { t as commands } from "./commands-DSYOsn8r.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-DEo5Hndx.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-CYxR9E2v.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,6 +1,6 @@
1
1
  import { t as VISUAL_CLOCK } from "./fake-hnJcJeYf.mjs";
2
- import { n as read, t as sources } from "./sources-DM5Zcl4o.mjs";
3
- import { n as commands, t as run } from "./run-BnriyKUu.mjs";
2
+ import { n as read, t as sources } from "./sources-MfRpM8sV.mjs";
3
+ import { n as commands, t as run } from "./run-G4m1APMv.mjs";
4
4
  import path from "node:path";
5
5
  import { createBrandConsole } from "@moku-labs/common/cli";
6
6
  import { mkdir, readFile, writeFile } from "node:fs/promises";
package/llms.txt CHANGED
@@ -550,7 +550,7 @@ The graph. Modules `runner`, `gate`, `inbox`, `fx`, `features`. Runner methods s
550
550
 
551
551
  A screen is a projection whose `view` returns JSX. The game's `tsconfig.json` sets `"jsx": "react-jsx"` and `"jsxImportSource": "@moku-labs/game"`; screens are `.tsx` files. There is no DOM and no React.
552
552
 
553
- - Tags: `screen`, `layer`, `row`, `column`, `stack`, `spacer`, `panel` (swallows taps), `image` (`texture`, `fit?: "contain" | "cover" | "fill"`), `icon` (`name`, `fit?`), `text` (`content: string | Message`, or `bind={bind(Counter, "value")}` with `components={[Counter({ value })]}`; formats `int`, `mm:ss`, `h:mm:ss`, `duration`; `Countdown({ until })` with `bind(Countdown, "left", { format: "mm:ss" })` counts down to a `clock` moment; `style` is a text style key), `button` (`intent` + `payload`, or `local`, never both), `scroll` (`axis?: "x" | "y"` (`"y"`), fixed at spawn, another one later warns `ui:scroll-axis-fixed`; children for a short list, laid out as a column on `y` and a row on `x`, or the windowed form `rows`, `rowHeight`, `overscan?` (5), `row: index => node`, on `y` only: `axis: "x"` with `rows` throws `[game] A windowed scroll moves on y only.`; row state belongs in the model, a row that leaves the window loses its local state; `snap?: number[]` stops in offsets, kept inside `0..max`: a release glides to the stop nearest `offset + v × 260` (v in units a ms) on `1 − (1 − p)³` over `min(620, 260 + distance × 1.1)` ms; without `snap` a release flings, `v ×= 0.998` a ms until under 0.02, 0 when the finger rested over 50 ms; `offset?: number` (0) where it stands from the start, drawn there in its first frame, a new value glides there, the same value does nothing; hard edges, nothing passes on to a parent scroll; a press on a child that leaves the tap slop along the scroll's axis goes to the scroll and the child answers nothing, a finger on a moving scroll only stops it; every number in `time.delta` ms, so a time scale applies; the public component `ScrollState { offset, max, moving }` on its entity, whole units `0..max`, written when it changes and once with `moving: false` when it stops, read by a system and by `game.explain`; `app.ui.scroll(key, to)` and the visual step `{ scroll: { key, to } }` set it at once), `input` (a text field; its text lives in the component's `local`, see the ui README). Common props: `key`, `style`, `state: { active?, disabled?, selected? }`, `motion`, `components` (extra component values for the element's entity, such as a filter: `components={[Glow({ strength: 2 })]}`, or `DebugTrigger()`: five taps within 3000 ms of wall time on that view call `app.debug.overlay.open()` on a page with the agent flag, nothing elsewhere; put it on a `button` naming nothing); containers take `hosts?: string[]` (projections drawn inside the element: world views hang under it from the slot's first frame; a JSX projection is laid out in the slot's content box). Children draw and are hit in markup order at equal `zIndex`; the roots of a scene draw in the order of its `projections` (the `ui` layer sorts by `Order`; `ui: { sort: "none" }` keeps insertion order). `defineMotion({ when: "change" })` plays its `enter` and `exit` only for a commit or a popup, never when its screen mounts or unmounts; declare a motion at module level (`ui:motion-rebuilt` names a loop rebuilt in a view). Style adds `nineSlice`, `tint`, `offsetX`, `offsetY`, `scale`, `origin`, `fit: "contain"`; state variants `is: { hover, pressed, disabled, active, selected, covered }`. `popup(Component, props, { over })` stacks a popup over a kept one.
553
+ - Tags: `screen`, `layer`, `row`, `column`, `stack`, `spacer`, `panel` (swallows taps), `image` (`texture`, `fit?: "contain" | "cover" | "fill"`), `icon` (`name`, `fit?`), `text` (`content: string | Message`, or `bind={bind(Counter, "value")}` with `components={[Counter({ value })]}`; formats `int`, `mm:ss`, `h:mm:ss`, `duration`; `Countdown({ until })` with `bind(Countdown, "left", { format: "mm:ss" })` counts down to a `clock` moment; `style` is a text style key), `button` (`intent` + `payload`, or `local`, never both), `scroll` (`axis?: "x" | "y"` (`"y"`), fixed at spawn, another one later warns `ui:scroll-axis-fixed`; children for a short list, laid out as a column on `y` and a row on `x`, or the windowed form `rows`, `rowHeight`, `overscan?` (5), `row: index => node`, on `y` only: `axis: "x"` with `rows` throws `[game] A windowed scroll moves on y only.`; row state belongs in the model, a row that leaves the window loses its local state; `snap?: number[]` stops in offsets, kept inside `0..max`: a release glides to the stop nearest `offset + v × 260` (v in units a ms) on `1 − (1 − p)³` over `min(620, 260 + distance × 1.1)` ms; without `snap` a release flings, `v ×= 0.998` a ms until under 0.02, 0 when the finger rested over 50 ms; `offset?: number` (0) where it stands from the start, drawn there in its first frame, a new value glides there, the same value does nothing; hard edges, nothing passes on to a parent scroll; a press on a child that leaves the tap slop along the scroll's axis goes to the scroll and the child answers nothing, a finger on a moving scroll only stops it; every number in `time.delta` ms, so a time scale applies; the public component `ScrollState { offset, max, moving }` on its entity, whole units `0..max`, written when it changes and once with `moving: false` when it stops, read by a system and by `game.explain`; `app.ui.scroll(key, to)` and the visual step `{ scroll: { key, to } }` set it at once), `input` (a text field; its text lives in the component's `local`, see the ui README). Common props: `key`, `style`, `state: { active?, disabled?, selected? }`, `motion`, `components` (extra component values for the element's entity, such as a filter: `components={[Glow({ strength: 2 })]}`, or `DebugTrigger()`: five taps within 3000 ms of wall time on that view call `app.debug.overlay.open()` on a page with the agent flag, nothing elsewhere; put it on a `button` naming nothing); containers take `hosts?: string[]` (projections drawn inside the element: world views hang under it from the slot's first frame; a JSX projection is laid out in the slot's content box). Children draw and are hit in markup order at equal `zIndex`; the roots of a scene draw in the order of its `projections` (the `ui` layer sorts by `Order`; `ui: { sort: "none" }` keeps insertion order). `defineMotion({ when: "change" })` plays its `enter` and `exit` only for a commit or a popup, never when its screen mounts or unmounts; declare a motion at module level (`ui:motion-rebuilt` names a loop rebuilt in a view). `motion.change` plays `Box` (the rects), `Transform` (the rest poses), the visual the element is drawn with (`Shape`, `Sprite`, `NineSlice`, `Text`, when a render or a restyle moves a number of it: `sweep`, `alpha`, `w`; a returned motion brings the numbers home from where they stand and the other fields land at once; the last motion of that name is cancelled first) and each extra of `components`, each as `(view, previous, next, hint)`; a dev build names any other `change` hook once, `ui:change-never-plays` `{ key, name }`. An element a render replaces (a root whose view returns another key or tag, a child of another key or tag) plays its exit with `"change"`, everything under it plays its own, and it leaves `find` at once, so the new element takes the keys they share. Style adds `nineSlice`, `tint`, `offsetX`, `offsetY`, `scale`, `origin`, `fit: "contain"`; state variants `is: { hover, pressed, disabled, active, selected, covered }`. `popup(Component, props, { over })` stacks a popup over a kept one.
554
554
  - Style vocabulary (`defineStyle`, plain data): flow `direction`, `wrap`, `justify`, `align`, `alignSelf`, `gap`, `grow`, `shrink`; box `padding`, `margin`, `width`, `height`, `minWidth`, `minHeight`, `maxWidth`, `maxHeight`, `aspect`, `overflow`; position `position`, `left`, `top`, `right`, `bottom`, `reason`; visual `fill`, `stroke`, `strokeWidth`, `radius`, `alpha`, `shape: "rect" | "triangle" | "arc"` (`"rect"`; a triangle fills the box pointing right; an arc is only a `stroke` of `strokeWidth` on the circle inscribed in the box, `min(width, height) − strokeWidth` across, from 12 o'clock clockwise over `sweep`), `dash` (dash length of the stroke, gaps of half a dash; not on an arc), `sweep` (0..1, default 1, clamped; 0 draws nothing, 1 a closed ring), `cap: "round" | "butt"` (`"round"`; the ends of an open arc); variants `is: { pressed?, disabled?, active?, selected? }` and `when: { portrait?, landscape?, tall?, wide? }`, each a partial style merged in when the flag holds. Lengths are reference units or `"safeArea.top" | "safeArea.right" | "safeArea.bottom" | "safeArea.left"`. No font size here: it comes from the text style.
555
555
  - `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.
556
556
  - `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: {}`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moku-labs/game",
3
- "version": "0.15.32",
3
+ "version": "0.15.34",
4
4
  "description": "2D puzzle game engine on TypeScript and PixiJS, built on @moku-labs/core.",
5
5
  "type": "module",
6
6
  "sideEffects": [