@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.
- package/dist/app/page.d.mts +1 -1
- package/dist/app/page.mjs +1 -1
- package/dist/app/system.d.mts +3 -3
- package/dist/app.d.mts +3 -3
- package/dist/app.mjs +1 -1
- package/dist/assets.d.mts +1 -1
- package/dist/{commands-aLTGwuyk.d.mts → commands-DEVJzdsr.d.mts} +5 -5
- package/dist/control.d.mts +4 -4
- package/dist/{define-DbxBzO-G.d.mts → define-BfWdvNHC.d.mts} +2 -2
- package/dist/{headless-DHtavbqk.d.mts → headless-ByFLyHxx.d.mts} +1 -1
- package/dist/{index-YUceiUFm.d.mts → index-BYDIzUDh.d.mts} +6 -5
- package/dist/index.d.mts +4 -4
- package/dist/index.mjs +2 -2
- package/dist/inspect.d.mts +6 -6
- package/dist/inspect.mjs +1 -1
- package/dist/jsx-dev-runtime.d.mts +1 -1
- package/dist/jsx-runtime.d.mts +1 -1
- package/dist/{runtime-B5OD9JTT.d.mts → runtime-CrF7Cx-b.d.mts} +1 -1
- package/dist/{sources-Fbq_PFVH.mjs → sources-BYKXYPhd.mjs} +1 -1
- package/dist/{src-Co5sO4z1.mjs → src-sg41xWNr.mjs} +471 -75
- package/dist/testing.d.mts +4 -4
- package/dist/testing.mjs +1 -1
- package/dist/{types-3bEK86Z1.d.mts → types-Bi6hj1OB.d.mts} +2 -53
- package/dist/{types-Dm4tM5nH.d.mts → types-D8zAAXLt.d.mts} +4 -4
- package/dist/{types-CuEK0Vam.d.mts → types-DOldmhIw.d.mts} +21 -5
- package/dist/{types-xmH1X8qd.d.mts → types-ae0jg0r4.d.mts} +133 -14
- package/dist/{types-_a6-ALzz.d.mts → types-qsE-I59e.d.mts} +2 -2
- package/dist/{visual-CbGKzlj0.mjs → visual-DNM5oFYx.mjs} +40 -9
- package/dist/{visual-DExHHx-a.d.mts → visual-Hj0oNqPP.d.mts} +6 -6
- package/dist/visual.d.mts +1 -1
- package/dist/visual.mjs +1 -1
- package/llms.txt +1 -1
- package/package.json +1 -1
package/dist/testing.d.mts
CHANGED
|
@@ -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-
|
|
2
|
-
import { t as createApp } from "./index-
|
|
3
|
-
import { b as saveOf, v as WithGameState, y as memory } from "./types-
|
|
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-
|
|
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-
|
|
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-
|
|
2
|
-
import {
|
|
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-
|
|
2
|
-
import { o as AudioContextLike, s as Config$4 } from "./types-
|
|
3
|
-
import {
|
|
4
|
-
import { ft as platformPlugin, i as screen, mt as PlatformProvider, t as createApp, wt as audioPlugin, xt as effectsPlugin } from "./index-
|
|
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;
|
|
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
|
|
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[];
|
|
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.
|
|
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-
|
|
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
|
|
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",
|
|
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. `
|
|
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
|
|
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 {
|
|
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-
|
|
2
|
-
import { t as HeadlessApp } from "./headless-
|
|
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
|
-
*
|
|
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
|
|
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
|
|
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
|
|
1186
|
-
*
|
|
1187
|
-
*
|
|
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-
|
|
2
|
-
import { P as AnimApi, r as UiApi } from "./types-
|
|
3
|
-
import {
|
|
4
|
-
import { r as Repro } from "./headless-
|
|
5
|
-
import { a as InputOf, r as ControlApp } from "./types-
|
|
6
|
-
import { t as commands } from "./commands-
|
|
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-
|
|
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-
|
|
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
|
|
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".
|