@moku-labs/game 0.15.23 → 0.15.25

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,4 +1,4 @@
1
- import { m as StartedPage, r as GameConfig, t as AnyGameApp, u as PageOptions } from "../types-DwREs5vb.mjs";
1
+ import { m as StartedPage, r as GameConfig, t as AnyGameApp, u as PageOptions } from "../types-BdLJ4lTA.mjs";
2
2
 
3
3
  //#region src/app/page.d.ts
4
4
  /**
@@ -1,6 +1,6 @@
1
- import { Ar as PlayerStateProvider } from "../types-CHmCFReJ.mjs";
2
- import { ht as PlatformProvider } from "../index-CHI7dTc9.mjs";
3
- import { _ as SystemShellFactory, g as SystemName, h as SystemModules } from "../types-DwREs5vb.mjs";
1
+ import { Ar as PlayerStateProvider } from "../types-ChZ4Ej7O.mjs";
2
+ import { ht as PlatformProvider } from "../index-CM1goGHf.mjs";
3
+ import { _ as SystemShellFactory, g as SystemName, h as SystemModules } from "../types-BdLJ4lTA.mjs";
4
4
  import { Back, Haptics, KeepAwake, Lifecycle, Store } from "@moku-labs/system";
5
5
 
6
6
  //#region src/app/system.d.ts
package/dist/app.d.mts CHANGED
@@ -1,6 +1,6 @@
1
- import { Tr as Json } from "./types-CHmCFReJ.mjs";
2
- import { U as StringTable } from "./types-DmGs6x0c.mjs";
3
- import { a as GameHandle, c as MemoryProvider, d as SaveKind, f as Scenario, g as SystemName, i as GameDefinition, l as PageAgent, n as GameApp, o as GamePluginConfigs, p as ScreenSeams, r as GameConfig, s as HeadlessSeams, v as WithGameState } from "./types-DwREs5vb.mjs";
1
+ import { Tr as Json } from "./types-ChZ4Ej7O.mjs";
2
+ import { U as StringTable } from "./types-_OQ6H8WL.mjs";
3
+ import { a as GameHandle, c as MemoryProvider, d as SaveKind, f as Scenario, g as SystemName, i as GameDefinition, l as PageAgent, n as GameApp, o as GamePluginConfigs, p as ScreenSeams, r as GameConfig, s as HeadlessSeams, v as WithGameState } from "./types-BdLJ4lTA.mjs";
4
4
  import { AnyPluginInstance } from "@moku-labs/core";
5
5
 
6
6
  //#region src/app/define.d.ts
package/dist/app.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { Ct as effectsPlugin, H as platformPlugin, Pt as audioPlugin, i as screen, m as flattenFeatures, t as createApp } from "./src-hVe3f8GV.mjs";
1
+ import { Ct as effectsPlugin, H as platformPlugin, Pt as audioPlugin, i as screen, m as flattenFeatures, t as createApp } from "./src-DlKMD4sW.mjs";
2
2
  import { t as memory } from "./memory-CWlqhS7l.mjs";
3
3
  import { n as isFakeClock, t as fakeClock } from "./fake-6mazmHK3.mjs";
4
4
  //#region src/app/define.ts
@@ -2174,17 +2174,20 @@ function isRecord(value) {
2174
2174
  }
2175
2175
  /**
2176
2176
  * Imports the optional `assets.ts` of a feature. Node strips the types of a `.ts` file, so the
2177
- * description a game writes runs as it stands.
2177
+ * description a game writes runs as it stands. The URL carries the file's size and mtime: a
2178
+ * long-lived process (the keys watch of `dev` and the editor) reads a saved description again
2179
+ * instead of the module it cached, while one scan, or scans with nothing saved, import it once.
2178
2180
  *
2179
2181
  * @param file - Absolute path of the description.
2180
2182
  * @returns Its exports, or `undefined` when the feature brought none.
2181
2183
  * @throws {Error} When the file exists but cannot be imported.
2182
2184
  */
2183
2185
  async function importDescription(file) {
2184
- if (!await stat(file).then((entry) => entry.isFile(), () => false)) return void 0;
2186
+ const entry = await stat(file).catch(() => {});
2187
+ if (entry === void 0 || !entry.isFile()) return void 0;
2185
2188
  return await import(
2186
2189
  /* @vite-ignore */
2187
- pathToFileURL(file).href
2190
+ `${pathToFileURL(file).href}?saved=${entry.size}-${Math.round(entry.mtimeMs * 1e3)}`
2188
2191
  );
2189
2192
  }
2190
2193
  /**
package/dist/assets.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { d as Manifest } from "./types-CHmCFReJ.mjs";
1
+ import { d as Manifest } from "./types-ChZ4Ej7O.mjs";
2
2
 
3
3
  //#region src/plugins/assets/scan/cli.d.ts
4
4
  /**
package/dist/assets.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import { a as runCli, c as emitKeys, i as compileStrings, l as emitManifest, n as importStrings, o as scanAssets, r as checkStrings, s as packAssets, t as exportStrings } from "./assets-CEMfBogh.mjs";
1
+ import { a as runCli, c as emitKeys, i as compileStrings, l as emitManifest, n as importStrings, o as scanAssets, r as checkStrings, s as packAssets, t as exportStrings } from "./assets-B8_Uc6_n.mjs";
2
2
  export { checkStrings, compileStrings, emitKeys, emitManifest, exportStrings, importStrings, packAssets, runCli, scanAssets };
package/dist/cli.d.mts CHANGED
@@ -35,8 +35,8 @@ type KeysWatcher = {
35
35
  close(): void;
36
36
  };
37
37
  /**
38
- * What `watchKeys` is started with. The watch scans when an asset file or a strings file changes;
39
- * a folder that becomes or stops being a sub-feature scans too.
38
+ * What `watchKeys` is started with. The watch scans when an asset file, a strings file or a bundle
39
+ * description changes; a folder that becomes or stops being a sub-feature scans too.
40
40
  */
41
41
  type WatchKeysOptions = {
42
42
  /**
package/dist/cli.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { t as resolveConfig } from "./config-B87aFTW-.mjs";
2
2
  import { t as findFeatureOwners } from "./features-DowEilZm.mjs";
3
3
  import { r as watchTree, t as createBatcher, x as isSkippedFolder$1 } from "./watch-BSgptowj.mjs";
4
- import { a as runCli$1, i as compileStrings, n as importStrings, t as exportStrings } from "./assets-CEMfBogh.mjs";
4
+ import { a as runCli$1, i as compileStrings, n as importStrings, t as exportStrings } from "./assets-B8_Uc6_n.mjs";
5
5
  import { constants, copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, rmSync, statSync, watch, writeFileSync } from "node:fs";
6
6
  import path from "node:path";
7
7
  import { createBrandConsole } from "@moku-labs/common/cli";
@@ -2663,10 +2663,11 @@ async function runCommand(argv, deps) {
2663
2663
  * @file The keys watch of a game folder, behind `moku-game dev` and the editor's `watchKeys`: it
2664
2664
  * keeps `generated/` fresh while a dev page is open. A file event is only a trigger. When the
2665
2665
  * events stop for a quiet period, a batch walks the game and reads the size and the time stamp of
2666
- * every asset file and every `strings/<locale>.json`; it runs the scan of `moku-game keys` only
2667
- * when they differ from the batch before. A folder that becomes or stops being a sub-feature scans
2668
- * too: the keys and bundles of its files move between owners. After a scan it writes `.moku/assets-stamp.ts` from the
2669
- * asset files: every file with its stamp, and the ones this batch found changed, so the dev page
2666
+ * every asset file, every `strings/<locale>.json` and every bundle description (`assets.ts`); it
2667
+ * runs the scan of `moku-game keys` only when they differ from the batch before. A folder that
2668
+ * becomes or stops being a sub-feature scans too: the keys and bundles of its files move between
2669
+ * owners. After a scan it writes `.moku/assets-stamp.ts` from the asset files: every file with its
2670
+ * stamp, and the ones this batch found changed, so the dev page
2670
2671
  * reads only those again. One scan runs at a time, and a failed scan never stops the watch. A
2671
2672
  * batch that scanned is followed by one more walk: the platform reports only the first path of a
2672
2673
  * burst of writes, so a save made in the same instant as the scan's own writes under `generated/`
@@ -2679,6 +2680,8 @@ const QUIET_MS = 100;
2679
2680
  const ASSET_FILE = /\.(?:png|webp|fnt|mp3|m4a)$/i;
2680
2681
  /** A strings file: a `.json` directly in a `strings/` folder. */
2681
2682
  const STRINGS_FILE = /(?:^|\/)strings\/[^/]+\.json$/;
2683
+ /** The bundle description of a feature or a layer: its `assets.ts` (`defineBundles`). */
2684
+ const DESCRIPTION_FILE = /(?:^|\/)assets\.ts$/;
2682
2685
  /**
2683
2686
  * Lists one folder of the walk.
2684
2687
  *
@@ -2693,7 +2696,8 @@ function entriesOf(folder) {
2693
2696
  }
2694
2697
  }
2695
2698
  /**
2696
- * Tells whether a file is an input of the scan: an asset file, or a strings file.
2699
+ * Tells whether a file is an input of the scan: an asset file, a strings file, or a bundle
2700
+ * description (`assets.ts`), whose tiers and releases go into the manifest.
2697
2701
  *
2698
2702
  * @param file - The path relative to the game, with `/`.
2699
2703
  * @returns True for a file the watch stamps.
@@ -2703,11 +2707,12 @@ function entriesOf(folder) {
2703
2707
  * ```
2704
2708
  */
2705
2709
  function isInput(file) {
2706
- return ASSET_FILE.test(file) || STRINGS_FILE.test(file);
2710
+ return ASSET_FILE.test(file) || STRINGS_FILE.test(file) || DESCRIPTION_FILE.test(file);
2707
2711
  }
2708
2712
  /**
2709
2713
  * Reads one folder of the walk and the folders below it, outside the skipped ones: every asset
2710
- * file and every strings file joins the inputs with its stamp. Symlinks are not followed.
2714
+ * file, strings file and bundle description joins the inputs with its stamp. Symlinks are not
2715
+ * followed.
2711
2716
  *
2712
2717
  * @param root - The real game folder.
2713
2718
  * @param folder - The folder, relative to the game; `""` is the game folder.
@@ -2911,8 +2916,8 @@ function serialized(watch) {
2911
2916
  * Starts the keys watch of a checked game: watches the folder, runs the scan of `moku-game keys`
2912
2917
  * once, and resolves after it, so `generated/` is fresh before a page is served. The watcher
2913
2918
  * starts first: a save the platform reports during the first scan starts a batch after it. Then
2914
- * every batch of file events outside the skipped folders scans again when an asset file or a
2915
- * strings file changed. A failed scan, the first one too, goes to `onError` and the watch goes on.
2919
+ * every batch of file events outside the skipped folders scans again when an asset file, a
2920
+ * strings file or a bundle description changed. A failed scan, the first one too, goes to `onError` and the watch goes on.
2916
2921
  *
2917
2922
  * @param root - The game folder, absolute.
2918
2923
  * @param settings - The resolved config.
@@ -1,8 +1,8 @@
1
- import { C as ScenesApi, E as DebugSwitches, Ir as StoreApi, Pr as Snapshot, Zr as Time, en as Api$2, er as Bookmark, it as Captured, q as Api$1, qr as FrameRate, rr as FlowState, zr as Api } from "./types-CHmCFReJ.mjs";
2
- import { P as AnimApi, a as AudioApi, h as EffectsApi, r as UiApi } from "./types-9xAh1wGm.mjs";
3
- import { N as InputApi } from "./types-DmGs6x0c.mjs";
4
- import { t as HeadlessApp } from "./headless-DIbfzWkr.mjs";
5
- import { n as Command, r as ControlApp } from "./types-L4nVF9Ny.mjs";
1
+ import { C as ScenesApi, E as DebugSwitches, Ir as StoreApi, Pr as Snapshot, Zr as Time, en as Api$2, er as Bookmark, it as Captured, q as Api$1, qr as FrameRate, rr as FlowState, zr as Api } from "./types-ChZ4Ej7O.mjs";
2
+ import { P as AnimApi, a as AudioApi, h as EffectsApi, r as UiApi } from "./types-C-wAHgpD.mjs";
3
+ import { N as InputApi } from "./types-_OQ6H8WL.mjs";
4
+ import { t as HeadlessApp } from "./headless-PssyKRFN.mjs";
5
+ import { n as Command, r as ControlApp } from "./types-sfaYGg9l.mjs";
6
6
 
7
7
  //#region src/plugins/flow/doors/commands.d.ts
8
8
  /**
@@ -1,7 +1,7 @@
1
- import { t as HeadlessApp } from "./headless-DIbfzWkr.mjs";
2
- import { i as InputArguments, n as Command, o as InputSchema, s as Ran } from "./types-L4nVF9Ny.mjs";
3
- import { t as commands } from "./commands-CV6Deuaj.mjs";
4
- import { t as defineCommand } from "./define-42KUe-al.mjs";
1
+ import { t as HeadlessApp } from "./headless-PssyKRFN.mjs";
2
+ import { i as InputArguments, n as Command, o as InputSchema, s as Ran } from "./types-sfaYGg9l.mjs";
3
+ import { t as commands } from "./commands-KWKfdywA.mjs";
4
+ import { t as defineCommand } from "./define-uiyy88hF.mjs";
5
5
 
6
6
  //#region src/plugins/flow/doors/dev.d.ts
7
7
  /**
@@ -1,5 +1,5 @@
1
- import { t as HeadlessApp } from "./headless-DIbfzWkr.mjs";
2
- import { c as Source, n as Command, o as InputSchema } from "./types-L4nVF9Ny.mjs";
1
+ import { t as HeadlessApp } from "./headless-PssyKRFN.mjs";
2
+ import { c as Source, n as Command, o as InputSchema } from "./types-sfaYGg9l.mjs";
3
3
 
4
4
  //#region src/plugins/flow/doors/define.d.ts
5
5
  /**
@@ -1,4 +1,4 @@
1
- import { Fn as Api$1, Gr as Api, Rr as RngState, Tr as Json, hr as Answer, ir as JournalEntry, or as RouteStep, rr as FlowState } from "./types-CHmCFReJ.mjs";
1
+ import { Fn as Api$1, Gr as Api, Rr as RngState, Tr as Json, hr as Answer, ir as JournalEntry, or as RouteStep, rr as FlowState } from "./types-ChZ4Ej7O.mjs";
2
2
 
3
3
  //#region src/plugins/flow/headless.d.ts
4
4
  /**
@@ -1,6 +1,6 @@
1
- import { $r as Config$9, An as ProjectionSpec, Bn as HudKeysOf, Br as Config$4, C as ScenesApi, Cn as DescriptionNode, Cr as Config$5, Dr as State$5, Er as Root, Fn as Api$2, G as sprite, Gn as defineFlow, Gr as Api$5, H as SpriteValue, Hn as State$3, Hr as PauseReason, I as OverlayValue, In as BundlesOf, Ir as StoreApi, J as Config$6, Jr as RateChange, Kr as Config$7, Ln as Config$3, Lr as RngApi, M as HudPartValue, On as NumericFields, P as NineSliceValue, Qn as FeatureTree, Rn as EmitterIdOf, S as SceneSpec, Tr as Json, Tt as PassKind, Un as TextStylesOf, Ur as State$4, V as SpriteOptions, Vr as Events, W as TransformValue, Xn as FeatureDescription, Xr as ThermalReading, Yr as State$8, Zn as FeaturePlugin, _ as AnySceneProjection, _n as QueryTerm, a as Config$1, b as LayerMap, bn as SystemFor, br as State$2, c as LoadBundles, cn as ComponentHandle, dn as ComponentValue, dr as EffectOptions, ei as Events$1, en as Api$6, f as ReadyBundles, fr as GuideOptions, gn as Narrowed, gr as Api$1, hn as Mut, hr as Answer, ht as FrameRing, i as BundleSpec, jn as ReducedMotion, kn as ProjectionMotion, kr as Patch, l as LoadReason, lr as Descriptor, lt as ViewportSize, m as Tier, mt as FrameRecord, o as DefineBundles, p as State$1, pr as Hint, q as Api$4, r as BundleMap, rn as State$9, sr as SceneIdOf, t as Api, ti as Require, tn as Config$8, tr as DefineNode, tt as State$6, u as LoadResult, un as ComponentType, ur as Effect, v as DefineScene, vn as ResourceType, vr as Config$2, w as State$7, wn as Ease, x as SceneDefinition, xn as TagType, yn as SystemDefinition, z as ShapeValue, zn as GameTypes, zr as Api$3 } from "./types-CHmCFReJ.mjs";
2
- import { A as GlowValue, B as Target, C as BlurValue, D as FilterComponent, F as AnimPlayerValue, H as HapticKind, I as Config$10, K as defineAnimation, L as FramesValue, M as OutlineValue, N as UniformDeclaration, O as FilterSpec, P as AnimApi, Q as play, R as MotionKeyframe, S as AlphaValue, T as DisplacementValue, U as SfxDescriptor, W as Step, _ as State$12, a as AudioApi, b as EmitterDefinition, c as MusicDescriptor, ct as OverlayApi, d as State$11, f as VolumesFor, h as EffectsApi, j as NoiseValue, k as FilterValueOf, l as MusicOptions, m as Config$12, n as State$13, r as UiApi, s as Config$11, t as Config$13, w as ColorMatrixValue, x as EmitterValue, y as EmitterConfig, z as State$10 } from "./types-9xAh1wGm.mjs";
3
- import { B as Message, C as State$16, D as TextStyleInput, G as TypedTr, H as State$14, I as Argument, J as ResolvedStyle, L as Config$14, M as Config$15, N as InputApi, O as TextStyles, P as State$15, R as I18nApi, S as Point, T as TextBind, V as Part, W as TextKey, Y as Style, Z as WhenFlags, b as Config$16, g as IntrinsicElementsFor, h as defineTokens, j as defineComponent, k as TextValue, q as IsFlags, s as PopupComponent, v as BindField, w as TextApi, x as CountdownValue, y as BindOptions, z as IntlKit } from "./types-DmGs6x0c.mjs";
1
+ import { $r as Config$9, An as ProjectionSpec, Bn as HudKeysOf, Br as Config$4, C as ScenesApi, Cn as DescriptionNode, Cr as Config$5, Dr as State$5, Er as Root, Fn as Api$2, G as sprite, Gn as defineFlow, Gr as Api$5, H as SpriteValue, Hn as State$3, Hr as PauseReason, I as OverlayValue, In as BundlesOf, Ir as StoreApi, J as Config$6, Jr as RateChange, Kr as Config$7, Ln as Config$3, Lr as RngApi, M as HudPartValue, On as NumericFields, P as NineSliceValue, Qn as FeatureTree, Rn as EmitterIdOf, S as SceneSpec, Tr as Json, Tt as PassKind, Un as TextStylesOf, Ur as State$4, V as SpriteOptions, Vr as Events, W as TransformValue, Xn as FeatureDescription, Xr as ThermalReading, Yr as State$8, Zn as FeaturePlugin, _ as AnySceneProjection, _n as QueryTerm, a as Config$1, b as LayerMap, bn as SystemFor, br as State$2, c as LoadBundles, cn as ComponentHandle, dn as ComponentValue, dr as EffectOptions, ei as Events$1, en as Api$6, f as ReadyBundles, fr as GuideOptions, gn as Narrowed, gr as Api$1, hn as Mut, hr as Answer, ht as FrameRing, i as BundleSpec, jn as ReducedMotion, kn as ProjectionMotion, kr as Patch, l as LoadReason, lr as Descriptor, lt as ViewportSize, m as Tier, mt as FrameRecord, o as DefineBundles, p as State$1, pr as Hint, q as Api$4, r as BundleMap, rn as State$9, sr as SceneIdOf, t as Api, ti as Require, tn as Config$8, tr as DefineNode, tt as State$6, u as LoadResult, un as ComponentType, ur as Effect, v as DefineScene, vn as ResourceType, vr as Config$2, w as State$7, wn as Ease, x as SceneDefinition, xn as TagType, yn as SystemDefinition, z as ShapeValue, zn as GameTypes, zr as Api$3 } from "./types-ChZ4Ej7O.mjs";
2
+ import { A as GlowValue, B as Target, C as BlurValue, D as FilterComponent, F as AnimPlayerValue, H as HapticKind, I as Config$10, K as defineAnimation, L as FramesValue, M as OutlineValue, N as UniformDeclaration, O as FilterSpec, P as AnimApi, Q as play, R as MotionKeyframe, S as AlphaValue, T as DisplacementValue, U as SfxDescriptor, W as Step, _ as State$12, a as AudioApi, b as EmitterDefinition, c as MusicDescriptor, ct as OverlayApi, d as State$11, f as VolumesFor, h as EffectsApi, j as NoiseValue, k as FilterValueOf, l as MusicOptions, m as Config$12, n as State$13, r as UiApi, s as Config$11, t as Config$13, w as ColorMatrixValue, x as EmitterValue, y as EmitterConfig, z as State$10 } from "./types-C-wAHgpD.mjs";
3
+ import { B as Message, C as State$16, D as TextStyleInput, G as TypedTr, H as State$14, I as Argument, J as ResolvedStyle, L as Config$14, M as Config$15, N as InputApi, O as TextStyles, P as State$15, R as I18nApi, S as Point, T as TextBind, V as Part, W as TextKey, Y as Style, Z as WhenFlags, b as Config$16, g as IntrinsicElementsFor, h as defineTokens, j as defineComponent, k as TextValue, q as IsFlags, s as PopupComponent, v as BindField, w as TextApi, x as CountdownValue, y as BindOptions, z as IntlKit } from "./types-_OQ6H8WL.mjs";
4
4
  import { Log } from "@moku-labs/common/browser";
5
5
  import { PluginCtx } from "@moku-labs/core";
6
6
 
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { $t as DEMAND_REASONS, A as Display, At as PerfCollectorLine, B as Sprite, Bt as PerfMeasured, Cn as DescriptionNode, Ct as PERF_LOG_VERSION, Dt as PerfCause, Et as PassRecord, F as Overlay, Ft as PerfGapLine, G as sprite, Gt as PerfThermalLine, Ht as PerfSpikeLine, I as OverlayValue, It as PerfHeaderLine, Jn as to, Jt as ProblemRule, K as AdaptiveScale, Kn as exit, Kt as PoolTrimDetail, L as Parent, Lt as PerfLogLine, M as HudPartValue, Mt as PerfDisplay, N as NineSlice, Nr as SaveUnreadableError, Nt as PerfEventLine, Or as types_d_exports$10, Ot as PerfChange, Pt as PerfEventName, Qr as types_d_exports$15, Qt as WarmDetail, R as Shape, Rt as PerfLongFrameLine, St as PERF_GAP_BUCKETS, T as types_d_exports$13, Tt as PassKind, U as Transform, Ut as PerfStartupLine, Vt as PerfSampleLine, Wn as types_d_exports$6, Wr as types_d_exports$9, Wt as PerfTelemetryLine, Xt as ShaderEventDetail, Y as MonitorConfig, Yn as type, Yt as SHADER_WHATS, Zt as ShaderWhat, _t as MONITOR_SECTIONS, at as MonitorCounts, bt as OverlayWhat, dt as FrameColumns, et as PoolTrim, ft as FrameReadResult, g as types_d_exports$1, gt as LongFrameEntry, ht as FrameRing, in as types_d_exports$17, j as HudPart, jt as PerfCounts, kt as PerfChangeEntry, mt as FrameRecord, pt as FrameReadout, qn as slot, qt as ProblemEventDetail, rt as types_d_exports$12, vt as MonitorSections, wt as PROBLEM_RULES, xr as types_d_exports$3, xt as PERF_CAUSES, yt as OverlayEventDetail, zt as PerfMarkLine } from "./types-CHmCFReJ.mjs";
2
- import { $ as sequence, E as FilterArea, G as HAPTIC_KINDS, H as HapticKind, J as frames, K as defineAnimation, Q as play, V as types_d_exports, X as mark, Y as haptic, Z as parallel, at as tween, et as set, i as types_d_exports$16, it as stagger, lt as types_d_exports$4, nt as spawn, ot as use, p as types_d_exports$2, q as external, rt as spawned, st as wait, tt as sfx, v as types_d_exports$5 } from "./types-9xAh1wGm.mjs";
3
- import { A as types_d_exports$14, E as TextFormat, F as types_d_exports$8, K as types_d_exports$7, d as DebugTrigger, f as LocalWrite, h as defineTokens, j as defineComponent, m as popup, p as ScrollState, u as Box, x as CountdownValue, y as BindOptions } from "./types-DmGs6x0c.mjs";
4
- import { $ as system, A as Draggable, At as Text, B as Touchable, C as AnimPlayer, Ct as debugPlugin, D as defineBundles, Dt as animPlugin, E as defineScene, Et as assetsPlugin, F as PointerOver, G as Exiting, H as Traced, I as Pressable, J as Order, K as Layer, L as Pressed, M as Held, Mt as defineTextStyles, N as Hovered, Nt as label, O as load, Ot as projection, P as Pointer, Q as resource, R as Swipeable, S as defineMotion, St as effectsPlugin, T as Frames, Tt as audioPlugin, U as PASS_KINDS, V as Traceable, W as frameAt, X as component, Y as Tree, Z as mut, _ as defineStyle, _t as modelPlugin, a as defineEmitter, at as defineFeature, b as messageDuration, bt as i18nPlugin, c as Alpha, ct as uiPlugin, d as Displacement, dt as scenesPlugin, et as tag, f as Glow, ft as rendererPlugin, g as resolve, gt as types_d_exports$11, h as music, ht as PlatformProvider, i as screen, it as schedule, j as DropTarget, jt as bind, k as ready, kt as Countdown, l as Blur, lt as timePlugin, m as Outline, mt as BackResult, n as createPlugin, nt as guide, o as Emitter, ot as flattenFeatures, p as Noise, pt as platformPlugin, q as Now, r as defineGame, rt as hint, s as defineFilter, st as worldPlugin, t as createApp, tt as defineEffect, u as ColorMatrix, ut as textPlugin, v as tr, vt as lifecyclePlugin, w as Animation, wt as clockPlugin, x as cubicBezier, xt as flowPlugin, y as messageArgument, yt as inputPlugin, z as Tappable } from "./index-CHI7dTc9.mjs";
1
+ import { $t as DEMAND_REASONS, A as Display, At as PerfCollectorLine, B as Sprite, Bt as PerfMeasured, Cn as DescriptionNode, Ct as PERF_LOG_VERSION, Dt as PerfCause, Et as PassRecord, F as Overlay, Ft as PerfGapLine, G as sprite, Gt as PerfThermalLine, Ht as PerfSpikeLine, I as OverlayValue, It as PerfHeaderLine, Jn as to, Jt as ProblemRule, K as AdaptiveScale, Kn as exit, Kt as PoolTrimDetail, L as Parent, Lt as PerfLogLine, M as HudPartValue, Mt as PerfDisplay, N as NineSlice, Nr as SaveUnreadableError, Nt as PerfEventLine, Or as types_d_exports$10, Ot as PerfChange, Pt as PerfEventName, Qr as types_d_exports$15, Qt as WarmDetail, R as Shape, Rt as PerfLongFrameLine, St as PERF_GAP_BUCKETS, T as types_d_exports$13, Tt as PassKind, U as Transform, Ut as PerfStartupLine, Vt as PerfSampleLine, Wn as types_d_exports$6, Wr as types_d_exports$9, Wt as PerfTelemetryLine, Xt as ShaderEventDetail, Y as MonitorConfig, Yn as type, Yt as SHADER_WHATS, Zt as ShaderWhat, _t as MONITOR_SECTIONS, at as MonitorCounts, bt as OverlayWhat, dt as FrameColumns, et as PoolTrim, ft as FrameReadResult, g as types_d_exports$1, gt as LongFrameEntry, ht as FrameRing, in as types_d_exports$17, j as HudPart, jt as PerfCounts, kt as PerfChangeEntry, mt as FrameRecord, pt as FrameReadout, qn as slot, qt as ProblemEventDetail, rt as types_d_exports$12, vt as MonitorSections, wt as PROBLEM_RULES, xr as types_d_exports$3, xt as PERF_CAUSES, yt as OverlayEventDetail, zt as PerfMarkLine } from "./types-ChZ4Ej7O.mjs";
2
+ import { $ as sequence, E as FilterArea, G as HAPTIC_KINDS, H as HapticKind, J as frames, K as defineAnimation, Q as play, V as types_d_exports, X as mark, Y as haptic, Z as parallel, at as tween, et as set, i as types_d_exports$16, it as stagger, lt as types_d_exports$4, nt as spawn, ot as use, p as types_d_exports$2, q as external, rt as spawned, st as wait, tt as sfx, v as types_d_exports$5 } from "./types-C-wAHgpD.mjs";
3
+ import { A as types_d_exports$14, E as TextFormat, F as types_d_exports$8, K as types_d_exports$7, d as DebugTrigger, f as LocalWrite, h as defineTokens, j as defineComponent, m as popup, p as ScrollState, u as Box, x as CountdownValue, y as BindOptions } from "./types-_OQ6H8WL.mjs";
4
+ import { $ as system, A as Draggable, At as Text, B as Touchable, C as AnimPlayer, Ct as debugPlugin, D as defineBundles, Dt as animPlugin, E as defineScene, Et as assetsPlugin, F as PointerOver, G as Exiting, H as Traced, I as Pressable, J as Order, K as Layer, L as Pressed, M as Held, Mt as defineTextStyles, N as Hovered, Nt as label, O as load, Ot as projection, P as Pointer, Q as resource, R as Swipeable, S as defineMotion, St as effectsPlugin, T as Frames, Tt as audioPlugin, U as PASS_KINDS, V as Traceable, W as frameAt, X as component, Y as Tree, Z as mut, _ as defineStyle, _t as modelPlugin, a as defineEmitter, at as defineFeature, b as messageDuration, bt as i18nPlugin, c as Alpha, ct as uiPlugin, d as Displacement, dt as scenesPlugin, et as tag, f as Glow, ft as rendererPlugin, g as resolve, gt as types_d_exports$11, h as music, ht as PlatformProvider, i as screen, it as schedule, j as DropTarget, jt as bind, k as ready, kt as Countdown, l as Blur, lt as timePlugin, m as Outline, mt as BackResult, n as createPlugin, nt as guide, o as Emitter, ot as flattenFeatures, p as Noise, pt as platformPlugin, q as Now, r as defineGame, rt as hint, s as defineFilter, st as worldPlugin, t as createApp, tt as defineEffect, u as ColorMatrix, ut as textPlugin, v as tr, vt as lifecyclePlugin, w as Animation, wt as clockPlugin, x as cubicBezier, xt as flowPlugin, y as messageArgument, yt as inputPlugin, z as Tappable } from "./index-CM1goGHf.mjs";
5
5
  export { type AdaptiveScale, Alpha, types_d_exports as Anim, AnimPlayer, Animation, types_d_exports$1 as Assets, types_d_exports$2 as Audio, type BackResult, type BindOptions, Blur, Box, types_d_exports$3 as Clock, ColorMatrix, Countdown, type CountdownValue, DEMAND_REASONS, types_d_exports$4 as Debug, DebugTrigger, type DescriptionNode, Displacement, Display, Draggable, DropTarget, types_d_exports$5 as Effects, Emitter, Exiting, type FilterArea, types_d_exports$6 as Flow, type FrameColumns, type FrameReadResult, type FrameReadout, type FrameRecord, type FrameRing, Frames, Glow, HAPTIC_KINDS, type HapticKind, Held, Hovered, HudPart, type HudPartValue, types_d_exports$7 as I18n, types_d_exports$8 as Input, Layer, types_d_exports$9 as Lifecycle, LocalWrite, type LongFrameEntry, MONITOR_SECTIONS, types_d_exports$10 as Model, type MonitorConfig, type MonitorCounts, type MonitorSections, NineSlice, Noise, Now, Order, Outline, Overlay, type OverlayEventDetail, type OverlayValue, type OverlayWhat, PASS_KINDS, PERF_CAUSES, PERF_GAP_BUCKETS, PERF_LOG_VERSION, PROBLEM_RULES, Parent, type PassKind, type PassRecord, type PerfCause, type PerfChange, type PerfChangeEntry, type PerfCollectorLine, type PerfCounts, type PerfDisplay, type PerfEventLine, type PerfEventName, type PerfGapLine, type PerfHeaderLine, type PerfLogLine, type PerfLongFrameLine, type PerfMarkLine, type PerfMeasured, type PerfSampleLine, type PerfSpikeLine, type PerfStartupLine, type PerfTelemetryLine, type PerfThermalLine, types_d_exports$11 as Platform, type PlatformProvider, Pointer, PointerOver, type PoolTrim, type PoolTrimDetail, Pressable, Pressed, type ProblemEventDetail, type ProblemRule, types_d_exports$12 as Renderer, SHADER_WHATS, SaveUnreadableError, types_d_exports$13 as Scenes, ScrollState, type ShaderEventDetail, type ShaderWhat, Shape, Sprite, Swipeable, Tappable, Text, type TextFormat, types_d_exports$14 as TextTypes, types_d_exports$15 as Time, Touchable, Traceable, Traced, Transform, Tree, types_d_exports$16 as Ui, type WarmDetail, types_d_exports$17 as World, animPlugin, assetsPlugin, audioPlugin, bind, clockPlugin, component, createApp, createPlugin, cubicBezier, debugPlugin, defineAnimation, defineBundles, defineComponent, defineEffect, defineEmitter, defineFeature, defineFilter, defineGame, defineMotion, defineScene, defineStyle, defineTextStyles, defineTokens, effectsPlugin, exit, external, flattenFeatures, flowPlugin, frameAt, frames, guide, haptic, hint, i18nPlugin, inputPlugin, label, lifecyclePlugin, load, mark, messageArgument, messageDuration, modelPlugin, music, mut, parallel, platformPlugin, play, popup, projection, ready, rendererPlugin, resolve, resource, scenesPlugin, schedule, screen, sequence, set, sfx, slot, spawn, spawned, sprite, stagger, system, tag, textPlugin, timePlugin, to, tr, tween, type, uiPlugin, use, wait, worldPlugin };
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { $ as sfx, A as types_exports$7, At as Noise, B as defineMotion, Bt as Frames, C as types_exports$14, Ct as effectsPlugin, D as types_exports$10, Dt as ColorMatrix, E as types_exports$11, Et as Blur, F as types_exports$2, Ft as scenesPlugin, G as external, Gt as PERF_CAUSES, H as platformPlugin, Ht as PASS_KINDS, I as types_exports$1, It as assetsPlugin, J as mark, Jt as PROBLEM_RULES, K as frames, Kt as PERF_GAP_BUCKETS, L as types_exports, Lt as animPlugin, M as types_exports$5, Mt as defineFilter, N as types_exports$4, Nt as debugPlugin, O as types_exports$9, Ot as Displacement, P as types_exports$3, Pt as audioPlugin, Q as set, Qt as flowPlugin, R as uiPlugin, Rt as AnimPlayer, S as types_exports$15, St as messageDuration, T as types_exports$12, Tt as Alpha, U as HAPTIC_KINDS, Ut as frameAt, V as textPlugin, Vt as rendererPlugin, W as defineAnimation, Wt as MONITOR_SECTIONS, X as play, Xt as DEMAND_REASONS, Y as parallel, Yt as SHADER_WHATS, Z as sequence, Zt as worldPlugin, _ as defineBundles, _t as Touchable, a as cubicBezier, an as SaveUnreadableError, at as wait, b as types_exports$17, bt as i18nPlugin, c as hint, cn as clockPlugin, ct as DropTarget, d as defineScene, dt as Pointer, en as exit, et as spawn, f as tr, ft as PointerOver, g as music, gt as Tappable, h as defineEmitter, ht as Swipeable, i as screen, in as modelPlugin, it as use, j as types_exports$6, jt as Outline, k as types_exports$8, kt as Glow, l as schedule, lt as Held, m as flattenFeatures, mt as Pressed, n as createPlugin, nn as to, nt as stagger, o as defineEffect, on as lifecyclePlugin, ot as inputPlugin, p as defineFeature, pt as Pressable, q as haptic, qt as PERF_LOG_VERSION, r as defineGame, rn as type, rt as tween, s as guide, sn as timePlugin, st as Draggable, t as createApp, tn as slot, tt as spawned, u as projection, ut as Hovered, v as load, vt as Traceable, w as types_exports$13, wt as Emitter, x as types_exports$16, xt as messageArgument, y as ready, yt as Traced, z as resolve, zt as Animation } from "./src-hVe3f8GV.mjs";
1
+ import { $ as sfx, A as types_exports$7, At as Noise, B as defineMotion, Bt as Frames, C as types_exports$14, Ct as effectsPlugin, D as types_exports$10, Dt as ColorMatrix, E as types_exports$11, Et as Blur, F as types_exports$2, Ft as scenesPlugin, G as external, Gt as PERF_CAUSES, H as platformPlugin, Ht as PASS_KINDS, I as types_exports$1, It as assetsPlugin, J as mark, Jt as PROBLEM_RULES, K as frames, Kt as PERF_GAP_BUCKETS, L as types_exports, Lt as animPlugin, M as types_exports$5, Mt as defineFilter, N as types_exports$4, Nt as debugPlugin, O as types_exports$9, Ot as Displacement, P as types_exports$3, Pt as audioPlugin, Q as set, Qt as flowPlugin, R as uiPlugin, Rt as AnimPlayer, S as types_exports$15, St as messageDuration, T as types_exports$12, Tt as Alpha, U as HAPTIC_KINDS, Ut as frameAt, V as textPlugin, Vt as rendererPlugin, W as defineAnimation, Wt as MONITOR_SECTIONS, X as play, Xt as DEMAND_REASONS, Y as parallel, Yt as SHADER_WHATS, Z as sequence, Zt as worldPlugin, _ as defineBundles, _t as Touchable, a as cubicBezier, an as SaveUnreadableError, at as wait, b as types_exports$17, bt as i18nPlugin, c as hint, cn as clockPlugin, ct as DropTarget, d as defineScene, dt as Pointer, en as exit, et as spawn, f as tr, ft as PointerOver, g as music, gt as Tappable, h as defineEmitter, ht as Swipeable, i as screen, in as modelPlugin, it as use, j as types_exports$6, jt as Outline, k as types_exports$8, kt as Glow, l as schedule, lt as Held, m as flattenFeatures, mt as Pressed, n as createPlugin, nn as to, nt as stagger, o as defineEffect, on as lifecyclePlugin, ot as inputPlugin, p as defineFeature, pt as Pressable, q as haptic, qt as PERF_LOG_VERSION, r as defineGame, rn as type, rt as tween, s as guide, sn as timePlugin, st as Draggable, t as createApp, tn as slot, tt as spawned, u as projection, ut as Hovered, v as load, vt as Traceable, w as types_exports$13, wt as Emitter, x as types_exports$16, xt as messageArgument, y as ready, yt as Traced, z as resolve, zt as Animation } from "./src-DlKMD4sW.mjs";
2
2
  import { $ as component, H as Shape, I as Display, J as Exiting, L as HudPart, Q as Tree, R as NineSlice, S as bind, T as label, U as Sprite, V as Parent, W as Transform, X as Now, Y as Layer, Z as Order, b as Countdown, d as LocalWrite, et as mut, g as popup, it as tag, l as DebugTrigger, nt as system, p as ScrollState, q as sprite, s as Box, tt as resource, v as defineTokens, w as defineTextStyles, x as Text, y as defineStyle, z as Overlay } from "./visual-kEC9nb-o.mjs";
3
3
  import { t as defineComponent } from "./component-DGg5DqKK.mjs";
4
4
  export { Alpha, types_exports as Anim, AnimPlayer, Animation, types_exports$1 as Assets, types_exports$2 as Audio, Blur, Box, types_exports$3 as Clock, ColorMatrix, Countdown, DEMAND_REASONS, types_exports$4 as Debug, DebugTrigger, Displacement, Display, Draggable, DropTarget, types_exports$5 as Effects, Emitter, Exiting, types_exports$6 as Flow, Frames, Glow, HAPTIC_KINDS, Held, Hovered, HudPart, types_exports$7 as I18n, types_exports$8 as Input, Layer, types_exports$9 as Lifecycle, LocalWrite, MONITOR_SECTIONS, types_exports$10 as Model, NineSlice, Noise, Now, Order, Outline, Overlay, PASS_KINDS, PERF_CAUSES, PERF_GAP_BUCKETS, PERF_LOG_VERSION, PROBLEM_RULES, Parent, types_exports$11 as Platform, Pointer, PointerOver, Pressable, Pressed, types_exports$12 as Renderer, SHADER_WHATS, SaveUnreadableError, types_exports$13 as Scenes, ScrollState, Shape, Sprite, Swipeable, Tappable, Text, types_exports$14 as TextTypes, types_exports$15 as Time, Touchable, Traceable, Traced, Transform, Tree, types_exports$16 as Ui, types_exports$17 as World, animPlugin, assetsPlugin, audioPlugin, bind, clockPlugin, component, createApp, createPlugin, cubicBezier, debugPlugin, defineAnimation, defineBundles, defineComponent, defineEffect, defineEmitter, defineFeature, defineFilter, defineGame, defineMotion, defineScene, defineStyle, defineTextStyles, defineTokens, effectsPlugin, exit, external, flattenFeatures, flowPlugin, frameAt, frames, guide, haptic, hint, i18nPlugin, inputPlugin, label, lifecyclePlugin, load, mark, messageArgument, messageDuration, modelPlugin, music, mut, parallel, platformPlugin, play, popup, projection, ready, rendererPlugin, resolve, resource, scenesPlugin, schedule, screen, sequence, set, sfx, slot, spawn, spawned, sprite, stagger, system, tag, textPlugin, timePlugin, to, tr, tween, type, uiPlugin, use, wait, worldPlugin };
@@ -1,9 +1,9 @@
1
- import { Pr as Snapshot, Sr as Api, Vn as Position, en as Api$1, h as Usage, ir as JournalEntry, ln as ComponentSchema, mn as FrameDiff, nn as Explained, nr as FlowGraph, nt as Under, ot as RenderStats, pn as EntitySnapshot, q as Api$2, t as Api$3 } from "./types-CHmCFReJ.mjs";
2
- import { a as AudioApi, g as EffectsStats, h as EffectsApi, r as UiApi, u as SoundEntry } from "./types-9xAh1wGm.mjs";
3
- import { c as UiNode } from "./types-DmGs6x0c.mjs";
4
- import { t as HeadlessApp } from "./headless-DIbfzWkr.mjs";
5
- import { a as InputOf, c as Source, i as InputArguments, l as WatchApp, o as InputSchema, t as CheatEntry, u as WatchInput } from "./types-L4nVF9Ny.mjs";
6
- import { n as defineSource } from "./define-42KUe-al.mjs";
1
+ import { Pr as Snapshot, Sr as Api, Vn as Position, en as Api$1, h as Usage, ir as JournalEntry, ln as ComponentSchema, mn as FrameDiff, nn as Explained, nr as FlowGraph, nt as Under, ot as RenderStats, pn as EntitySnapshot, q as Api$2, t as Api$3 } from "./types-ChZ4Ej7O.mjs";
2
+ import { a as AudioApi, g as EffectsStats, h as EffectsApi, r as UiApi, u as SoundEntry } from "./types-C-wAHgpD.mjs";
3
+ import { c as UiNode } from "./types-_OQ6H8WL.mjs";
4
+ import { t as HeadlessApp } from "./headless-PssyKRFN.mjs";
5
+ import { a as InputOf, c as Source, i as InputArguments, l as WatchApp, o as InputSchema, t as CheatEntry, u as WatchInput } from "./types-sfaYGg9l.mjs";
6
+ import { n as defineSource } from "./define-uiyy88hF.mjs";
7
7
 
8
8
  //#region src/plugins/flow/doors/read.d.ts
9
9
  /**
@@ -1,2 +1,2 @@
1
- import { a as jsxs, i as jsxDEV, n as JSX, r as jsx, t as Fragment } from "./runtime-BcMjHiib.mjs";
1
+ import { a as jsxs, i as jsxDEV, n as JSX, r as jsx, t as Fragment } from "./runtime-C0YaA9fC.mjs";
2
2
  export { Fragment, type JSX, jsx, jsxDEV, jsxs };
@@ -1,2 +1,2 @@
1
- import { a as jsxs, n as JSX, r as jsx, t as Fragment } from "./runtime-BcMjHiib.mjs";
1
+ import { a as jsxs, n as JSX, r as jsx, t as Fragment } from "./runtime-C0YaA9fC.mjs";
2
2
  export { Fragment, type JSX, jsx, jsxs };
@@ -1,4 +1,4 @@
1
- import { _ as UiIntrinsicElements, a as JsxChild, t as DescriptionNode } from "./types-DmGs6x0c.mjs";
1
+ import { _ as UiIntrinsicElements, a as JsxChild, t as DescriptionNode } from "./types-_OQ6H8WL.mjs";
2
2
 
3
3
  //#region src/plugins/ui/jsx/runtime.d.ts
4
4
  /** What the compiler passes as props: the children plus whatever the tag declared. */
@@ -7029,10 +7029,26 @@ function releaseEntity(state, entity) {
7029
7029
  function isJson(value) {
7030
7030
  if (value === null) return true;
7031
7031
  if (typeof value === "boolean" || typeof value === "number" || typeof value === "string") return true;
7032
- if (Array.isArray(value)) return value.every((entry) => isJson(entry));
7032
+ if (Array.isArray(value)) return isJsonArray(value);
7033
7033
  if (typeof value !== "object") return false;
7034
7034
  if (Object.getPrototypeOf(value) !== Object.prototype) return false;
7035
- return Object.values(value).every((entry) => isJson(entry));
7035
+ for (const key in value) if (Object.hasOwn(value, key) && !isJson(Reflect.get(value, key))) return false;
7036
+ return true;
7037
+ }
7038
+ /**
7039
+ * Tells whether every entry of an array is plain JSON. A hole is skipped, as `every` skips it.
7040
+ *
7041
+ * @param value - The array to check.
7042
+ * @returns True when every entry is plain JSON.
7043
+ * @example
7044
+ * ```ts
7045
+ * isJsonArray([1, "a", [true]]); // true
7046
+ * ```
7047
+ */
7048
+ function isJsonArray(value) {
7049
+ const count = value.length;
7050
+ for (let index = 0; index < count; index += 1) if (index in value && !isJson(value[index])) return false;
7051
+ return true;
7036
7052
  }
7037
7053
  /**
7038
7054
  * Builds the component map of one entity and names the values that are not JSON. The frame
@@ -7112,6 +7128,10 @@ function snapshotWorld(state, mode) {
7112
7128
  const MARKED_ROWS = 32;
7113
7129
  /** The share of changed rows at or under which marked rows count as wasted: one in twenty. */
7114
7130
  const CHANGED_SHARE = 1 / 20;
7131
+ /** The rounding of the rows a frame `world:mut-unchanged` reports: one decimal. */
7132
+ const TENTHS = 10;
7133
+ /** The rounding of the changed share it reports: two decimals. */
7134
+ const HUNDREDTHS = 100;
7115
7135
  const absent = null;
7116
7136
  /**
7117
7137
  * Creates an empty ring, one empty slot per kept frame.
@@ -7225,7 +7245,63 @@ function sameObject(left, right) {
7225
7245
  return true;
7226
7246
  }
7227
7247
  /**
7228
- * The slot of one frame, written fresh when the ring still holds an older frame at its index.
7248
+ * Copies a JSON value into the object or array it replaces, when that one has the same kind and,
7249
+ * for an array, the same length; otherwise into a new one. A primitive is its own copy.
7250
+ *
7251
+ * @param source - The value to copy.
7252
+ * @param into - The earlier copy it replaces, if there is one.
7253
+ * @returns The copy: `into` itself when it could take the value.
7254
+ * @example
7255
+ * ```ts
7256
+ * copyJson({ x: 2 }, { x: 1 }); // { x: 2 }: the second object, written in place
7257
+ * ```
7258
+ */
7259
+ function copyJson(source, into) {
7260
+ if (typeof source !== "object" || source === null) return source;
7261
+ if (Array.isArray(source)) {
7262
+ const count = source.length;
7263
+ const target = Array.isArray(into) && into.length === count ? into : Array.from({ length: count }, () => absent);
7264
+ for (let index = 0; index < count; index += 1) target[index] = copyJson(source[index] ?? absent, target[index]);
7265
+ return target;
7266
+ }
7267
+ const target = typeof into === "object" && into !== null && !Array.isArray(into) ? into : {};
7268
+ for (const key in source) target[key] = copyJson(source[key] ?? absent, target[key]);
7269
+ for (const key in target) if (!Object.hasOwn(source, key)) delete target[key];
7270
+ return target;
7271
+ }
7272
+ /**
7273
+ * Writes one record into the next record object of a slot: the spare one left by the frame that
7274
+ * held the slot before, or a new one. `from` and `to` are copied into the values that record held.
7275
+ *
7276
+ * @param slot - The slot of this frame.
7277
+ * @param id - The entity.
7278
+ * @param owner - Its owner.
7279
+ * @param component - The component name.
7280
+ * @param from - The value before, `absent` for none.
7281
+ * @param to - The value after, `absent` for none.
7282
+ */
7283
+ function writeChange(slot, id, owner, component, from, to) {
7284
+ const spare = slot.changes[slot.count];
7285
+ slot.count += 1;
7286
+ if (spare === void 0) {
7287
+ slot.changes.push({
7288
+ id,
7289
+ owner,
7290
+ component,
7291
+ from: copyJson(from),
7292
+ to: copyJson(to)
7293
+ });
7294
+ return;
7295
+ }
7296
+ spare.id = id;
7297
+ spare.owner = owner;
7298
+ spare.component = component;
7299
+ spare.from = copyJson(from, spare.from);
7300
+ spare.to = copyJson(to, spare.to);
7301
+ }
7302
+ /**
7303
+ * The slot of one frame, emptied for it when the ring still holds an older frame at its index:
7304
+ * its record objects stay, as spares.
7229
7305
  *
7230
7306
  * @param history - The frame history.
7231
7307
  * @param frame - The frame number.
@@ -7235,9 +7311,17 @@ function slotFor$1(history, frame) {
7235
7311
  const index = frame % 120;
7236
7312
  const known = history.slots[index];
7237
7313
  if (known?.frame === frame) return known;
7314
+ if (known !== void 0) {
7315
+ known.frame = frame;
7316
+ known.count = 0;
7317
+ if (known.spawned.length > 0) known.spawned.length = 0;
7318
+ if (known.despawned.length > 0) known.despawned.length = 0;
7319
+ return known;
7320
+ }
7238
7321
  const fresh = {
7239
7322
  frame,
7240
7323
  changes: [],
7324
+ count: 0,
7241
7325
  spawned: [],
7242
7326
  despawned: []
7243
7327
  };
@@ -7266,7 +7350,7 @@ function openFrame$1(ctx, history) {
7266
7350
  */
7267
7351
  function copiedComponents(state, entity) {
7268
7352
  const copies = emptyComponents();
7269
- for (const [name, value] of Object.entries(componentsOf$1(state, entity).components)) copies.set(name, structuredClone(value));
7353
+ for (const [name, value] of Object.entries(componentsOf$1(state, entity).components)) copies.set(name, copyJson(value));
7270
7354
  return copies;
7271
7355
  }
7272
7356
  /**
@@ -7307,13 +7391,7 @@ function recordSpawn(state, history, slot, entity, owner) {
7307
7391
  },
7308
7392
  components: copiedComponents(state, entity)
7309
7393
  };
7310
- for (const [component, value] of entry.components) slot.changes.push({
7311
- id: entity,
7312
- owner: entry.owner,
7313
- component,
7314
- from: absent,
7315
- to: value
7316
- });
7394
+ for (const [component, value] of entry.components) writeChange(slot, entity, entry.owner, component, absent, value);
7317
7395
  slot.spawned.push(entity);
7318
7396
  history.shadow.set(entity, entry);
7319
7397
  }
@@ -7334,26 +7412,14 @@ function recordComponent(state, slot, entity, entry, component) {
7334
7412
  const value = jsonOf(component, state.stores.get(component)?.get(entity));
7335
7413
  if (value === void 0) {
7336
7414
  if (before === void 0) return false;
7337
- slot.changes.push({
7338
- id: entity,
7339
- owner: entry.owner,
7340
- component,
7341
- from: before,
7342
- to: absent
7343
- });
7415
+ writeChange(slot, entity, entry.owner, component, before, absent);
7344
7416
  entry.components.delete(component);
7345
7417
  return true;
7346
7418
  }
7347
7419
  if (before !== void 0 && sameJson(before, value)) return false;
7348
- const copy = structuredClone(value);
7349
- slot.changes.push({
7350
- id: entity,
7351
- owner: entry.owner,
7352
- component,
7353
- from: before ?? absent,
7354
- to: copy
7355
- });
7356
- entry.components.set(component, copy);
7420
+ writeChange(slot, entity, entry.owner, component, before ?? absent, value);
7421
+ const copy = copyJson(value, before);
7422
+ if (copy !== before) entry.components.set(component, copy);
7357
7423
  return true;
7358
7424
  }
7359
7425
  /**
@@ -7367,10 +7433,12 @@ function markCountOf(history, component) {
7367
7433
  const known = history.marks.get(component);
7368
7434
  if (known !== void 0) return known;
7369
7435
  const count = {
7436
+ component,
7370
7437
  marked: 0,
7371
7438
  changed: 0
7372
7439
  };
7373
7440
  history.marks.set(component, count);
7441
+ history.markList.push(count);
7374
7442
  return count;
7375
7443
  }
7376
7444
  /**
@@ -7413,26 +7481,38 @@ function recordChangedEntities(ctx, history, frame) {
7413
7481
  * @param history - The frame history.
7414
7482
  */
7415
7483
  function judgeMarks(ctx, history) {
7416
- for (const [component, count] of history.marks) {
7484
+ for (let index = 0; index < history.markList.length; index += 1) {
7485
+ const count = history.markList[index];
7486
+ if (count === void 0) continue;
7487
+ const component = count.component;
7417
7488
  const rowsPerFrame = count.marked / history.markFrames;
7418
- if (rowsPerFrame >= MARKED_ROWS && count.changed <= count.marked * CHANGED_SHARE && !history.warned.has(component)) {
7419
- const systems = mutSystemsOf(ctx.state.ecs, component);
7420
- if (systems.length > 0) {
7421
- history.warned.add(component);
7422
- ctx.log.warn("world:mut-unchanged", {
7423
- component,
7424
- rowsPerFrame: Math.round(rowsPerFrame * 10) / 10,
7425
- changedShare: Math.round(count.changed / count.marked * 100) / 100,
7426
- systems
7427
- });
7428
- }
7429
- }
7489
+ if (rowsPerFrame >= MARKED_ROWS && count.changed <= count.marked * CHANGED_SHARE && !history.warned.has(component)) warnWasted(ctx, history, count, rowsPerFrame);
7430
7490
  count.marked = 0;
7431
7491
  count.changed = 0;
7432
7492
  }
7433
7493
  history.markFrames = 0;
7434
7494
  }
7435
7495
  /**
7496
+ * Names one wasteful component once, when a registered system writes it with `mut()`: its rows a frame to one decimal,
7497
+ * its changed share to two.
7498
+ *
7499
+ * @param ctx - Domain context of the world plugin.
7500
+ * @param history - The frame history.
7501
+ * @param count - The component's counts over the window.
7502
+ * @param rowsPerFrame - Its marked rows a frame.
7503
+ */
7504
+ function warnWasted(ctx, history, count, rowsPerFrame) {
7505
+ const systems = mutSystemsOf(ctx.state.ecs, count.component);
7506
+ if (systems.length === 0) return;
7507
+ history.warned.add(count.component);
7508
+ ctx.log.warn("world:mut-unchanged", {
7509
+ component: count.component,
7510
+ rowsPerFrame: Math.round(rowsPerFrame * TENTHS) / TENTHS,
7511
+ changedShare: Math.round(count.changed / count.marked * HUNDREDTHS) / HUNDREDTHS,
7512
+ systems
7513
+ });
7514
+ }
7515
+ /**
7436
7516
  * The names of the registered systems whose query writes a component with `mut()`.
7437
7517
  *
7438
7518
  * @param state - ecs module state.
@@ -7465,6 +7545,7 @@ function recordFrame(ctx, frame) {
7465
7545
  newest: void 0,
7466
7546
  started: void 0,
7467
7547
  marks: emptyMarks(),
7548
+ markList: [],
7468
7549
  markFrames: 0,
7469
7550
  warned: emptyNames$1()
7470
7551
  };
@@ -7486,13 +7567,7 @@ function recordDespawn(ctx, entity) {
7486
7567
  const entry = history?.shadow.get(entity);
7487
7568
  if (history === void 0 || entry === void 0) return;
7488
7569
  const slot = slotFor$1(history, openFrame$1(ctx, history));
7489
- for (const [component, value] of entry.components) slot.changes.push({
7490
- id: entity,
7491
- owner: entry.owner,
7492
- component,
7493
- from: value,
7494
- to: absent
7495
- });
7570
+ for (const [component, value] of entry.components) writeChange(slot, entity, entry.owner, component, value, absent);
7496
7571
  slot.despawned.push(entity);
7497
7572
  history.shadow.delete(entity);
7498
7573
  }
@@ -7509,13 +7584,7 @@ function recordRemoval(ctx, entity, component) {
7509
7584
  const entry = history?.shadow.get(entity);
7510
7585
  const before = entry?.components.get(component);
7511
7586
  if (history === void 0 || entry === void 0 || before === void 0) return;
7512
- slotFor$1(history, openFrame$1(ctx, history)).changes.push({
7513
- id: entity,
7514
- owner: entry.owner,
7515
- component,
7516
- from: before,
7517
- to: absent
7518
- });
7587
+ writeChange(slotFor$1(history, openFrame$1(ctx, history)), entity, entry.owner, component, before, absent);
7519
7588
  entry.components.delete(component);
7520
7589
  }
7521
7590
  /**
@@ -7532,6 +7601,7 @@ function resetHistory(state) {
7532
7601
  history.newest = void 0;
7533
7602
  history.started = void 0;
7534
7603
  history.marks.clear();
7604
+ history.markList.length = 0;
7535
7605
  history.markFrames = 0;
7536
7606
  history.warned.clear();
7537
7607
  }
@@ -7543,7 +7613,9 @@ function resetHistory(state) {
7543
7613
  * @param slot - The next slot of the window.
7544
7614
  */
7545
7615
  function foldSlot(folded, slot) {
7546
- for (const change of slot.changes) {
7616
+ for (let index = 0; index < slot.count; index += 1) {
7617
+ const change = slot.changes[index];
7618
+ if (change === void 0) continue;
7547
7619
  const entry = folded.get(change.id) ?? {
7548
7620
  id: change.id,
7549
7621
  owner: change.owner,
@@ -7578,7 +7650,10 @@ function foldSlot(folded, slot) {
7578
7650
  function entityDiffOf(entry) {
7579
7651
  if (entry.spawned && entry.despawned) return [];
7580
7652
  const components = {};
7581
- for (const [component, pair] of entry.pairs) if (!sameJson(pair.from, pair.to)) components[component] = pair;
7653
+ for (const [component, pair] of entry.pairs) if (!sameJson(pair.from, pair.to)) components[component] = {
7654
+ from: copyJson(pair.from),
7655
+ to: copyJson(pair.to)
7656
+ };
7582
7657
  if (Object.keys(components).length === 0) return [];
7583
7658
  let change = "changed";
7584
7659
  if (entry.spawned) change = "spawned";
@@ -12111,6 +12186,11 @@ function changeBelow(state, node) {
12111
12186
  }
12112
12187
  //#endregion
12113
12188
  //#region src/plugins/renderer/demand/check.ts
12189
+ /**
12190
+ * The deepest level inside one render group whose transform list the check reads. Pixi keys the lists by depth in an
12191
+ * object, and a `for…in` over it allocates its keys on every pass; the levels are read by number instead.
12192
+ */
12193
+ const DEEPEST = 128;
12114
12194
  /** Why the check switched itself off, for its one debug line. */
12115
12195
  const NO_LISTS = "Pixi's render group has no change lists";
12116
12196
  /**
@@ -12123,116 +12203,109 @@ function isChangeList(value) {
12123
12203
  return typeof value === "object" && value !== null && Array.isArray(Reflect.get(value, "list")) && typeof Reflect.get(value, "index") === "number";
12124
12204
  }
12125
12205
  /**
12126
- * Reads the change lists of a render group, when it has the shape Pixi 8 gives it.
12206
+ * Whether a render group and every group nested in it have the change lists Pixi 8 gives them. Reads, never allocates.
12127
12207
  *
12128
- * @param value - The render group, or what stands in its place.
12129
- * @returns The lists, or `undefined` when the shape is not Pixi's.
12208
+ * @param group - The render group, or what stands in its place.
12209
+ * @returns False when one of them has no lists of Pixi's shape.
12130
12210
  */
12131
- function groupOf(value) {
12132
- if (typeof value !== "object" || value === null) return void 0;
12133
- const depths = Reflect.get(value, "childrenToUpdate");
12134
- const renderables = Reflect.get(value, "childrenRenderablesToUpdate");
12135
- const nested = Reflect.get(value, "renderGroupChildren");
12136
- if (typeof depths !== "object" || depths === null || !isChangeList(renderables)) return;
12137
- return {
12138
- depths: Object.values(depths),
12139
- renderables,
12140
- nested: Array.isArray(nested) ? nested : []
12141
- };
12211
+ function hasLists(group) {
12212
+ if (typeof group !== "object" || group === null) return false;
12213
+ const depths = Reflect.get(group, "childrenToUpdate");
12214
+ if (typeof depths !== "object" || depths === null) return false;
12215
+ if (!isChangeList(Reflect.get(group, "childrenRenderablesToUpdate"))) return false;
12216
+ const nested = Reflect.get(group, "renderGroupChildren");
12217
+ if (!Array.isArray(nested)) return true;
12218
+ for (const child of nested) if (!hasLists(child)) return false;
12219
+ return true;
12142
12220
  }
12143
12221
  /**
12144
- * Collects the live entries of one change list.
12222
+ * The entity a listed node belongs to: the node itself or the nearest parent that is a view's object or top, read
12223
+ * from sync's back references. A masked container and a mask hang in their view's wrapper, so the walk meets the top.
12224
+ * The stage, the root and a layer belong to none.
12145
12225
  *
12146
- * @param listed - The list.
12147
- * @param into - Where the nodes go.
12226
+ * @param node - The listed node.
12227
+ * @param sync - Sync's state.
12228
+ * @returns The entity, or `undefined`.
12148
12229
  */
12149
- function collect$1(listed, into) {
12150
- for (let at = 0; at < listed.index; at += 1) {
12151
- const node = listed.list[at];
12152
- if (typeof node === "object" && node !== null) into.push(node);
12230
+ function ownerOf$2(node, sync) {
12231
+ for (let at = node; typeof at === "object" && at !== null;) {
12232
+ const entity = sync.entityOf.get(at);
12233
+ if (entity !== void 0) return entity;
12234
+ const top = sync.viewOfTop.get(at);
12235
+ if (top !== void 0) return sync.entityOf.get(top.object);
12236
+ at = Reflect.get(at, "parent");
12153
12237
  }
12154
12238
  }
12155
12239
  /**
12156
- * Collects every node Pixi listed as changed in a render group and the groups nested in it.
12240
+ * Warns about one listed node when it belongs to a shown view nobody marked since the last drawn frame.
12157
12241
  *
12158
- * @param group - The render group.
12159
- * @param into - Where the nodes go.
12160
- * @returns False when a nested group has no lists of Pixi's shape.
12242
+ * @param ctx - Domain context of the renderer plugin.
12243
+ * @param state - The demand state.
12244
+ * @param shown - The shown predicate.
12245
+ * @param node - The listed node.
12161
12246
  */
12162
- function listedNodes(group, into) {
12163
- for (const depth of group.depths) if (isChangeList(depth)) collect$1(depth, into);
12164
- collect$1(group.renderables, into);
12165
- for (const child of group.nested) {
12166
- const nested = groupOf(child);
12167
- if (nested === void 0 || !listedNodes(nested, into)) return false;
12168
- }
12169
- return true;
12247
+ function visit(ctx, state, shown, node) {
12248
+ if (typeof node !== "object" || node === null) return;
12249
+ const entity = ownerOf$2(node, ctx.state.sync);
12250
+ if (entity === void 0) return;
12251
+ if (state.sinceDraw.has(entity) || state.check.warned.has(entity)) return;
12252
+ if (!shown(entity)) return;
12253
+ state.check.warned.add(entity);
12254
+ const label = Reflect.get(node, "label");
12255
+ ctx.log.warn("renderer:missed-mark", {
12256
+ entity,
12257
+ label: typeof label === "string" ? label : ""
12258
+ });
12170
12259
  }
12171
12260
  /**
12172
- * The entity behind every display object the renderer owns for it: the object, the wrapper, the masked container and
12173
- * the mask. Built once per check, dev builds only.
12261
+ * Visits the live entries of one change list.
12174
12262
  *
12175
- * @param views - The views by entity.
12176
- * @returns The reverse map.
12263
+ * @param ctx - Domain context of the renderer plugin.
12264
+ * @param state - The demand state.
12265
+ * @param shown - The shown predicate.
12266
+ * @param listed - The list.
12177
12267
  */
12178
- function ownersOf(views) {
12179
- const owners = /* @__PURE__ */ new Map();
12180
- for (const [entity, view] of views) {
12181
- owners.set(view.object, entity);
12182
- if (view.wrapper !== void 0) owners.set(view.wrapper, entity);
12183
- if (view.clipped !== void 0) owners.set(view.clipped, entity);
12184
- if (view.mask !== void 0) owners.set(view.mask, entity);
12185
- }
12186
- return owners;
12268
+ function visitList(ctx, state, shown, listed) {
12269
+ if (!isChangeList(listed)) return;
12270
+ for (let at = 0; at < listed.index; at += 1) visit(ctx, state, shown, listed.list[at]);
12187
12271
  }
12188
12272
  /**
12189
- * The entity a listed node belongs to: the node itself or the nearest parent that is a view's object, wrapper, masked
12190
- * container or mask. The stage, the root and a layer belong to none.
12273
+ * Visits every node Pixi listed as changed in a render group and the groups nested in it, whose shapes `hasLists`
12274
+ * checked.
12191
12275
  *
12192
- * @param node - The listed node.
12193
- * @param owners - The reverse map of `ownersOf`.
12194
- * @returns The entity, or `undefined`.
12276
+ * @param ctx - Domain context of the renderer plugin.
12277
+ * @param state - The demand state.
12278
+ * @param shown - The shown predicate.
12279
+ * @param group - The render group.
12195
12280
  */
12196
- function ownerOf$2(node, owners) {
12197
- for (let at = node; typeof at === "object" && at !== null;) {
12198
- const entity = owners.get(at);
12199
- if (entity !== void 0) return entity;
12200
- at = Reflect.get(at, "parent");
12201
- }
12281
+ function visitGroup(ctx, state, shown, group) {
12282
+ const depths = Reflect.get(group, "childrenToUpdate");
12283
+ for (let depth = 0; depth <= DEEPEST; depth += 1) visitList(ctx, state, shown, depths[depth]);
12284
+ visitList(ctx, state, shown, Reflect.get(group, "childrenRenderablesToUpdate"));
12285
+ const nested = Reflect.get(group, "renderGroupChildren");
12286
+ if (!Array.isArray(nested)) return;
12287
+ for (const child of nested) visitGroup(ctx, state, shown, child);
12202
12288
  }
12203
12289
  /**
12204
12290
  * Warns `renderer:missed-mark` once per listed, shown, unmarked view of a skipped frame: something wrote into a
12205
12291
  * display object the renderer never heard of. A view marked since the last drawn frame is skipped, and so is a view
12206
12292
  * that is not shown (Pixi lists the changes of hidden views too). Lists keep their entries across skipped frames until
12207
- * Pixi renders, and an entity warns once per run, so one stray write warns once.
12293
+ * Pixi renders, and an entity warns once per run, so one stray write warns once. The owner of a node comes from sync's
12294
+ * back references, so no map of every view is built per frame (F69), and a pass that warns nothing allocates nothing.
12208
12295
  *
12209
12296
  * @param ctx - Domain context of the renderer plugin.
12210
12297
  * @param state - The demand state.
12211
12298
  * @param stage - The stage root.
12212
- * @param views - The views by entity.
12213
12299
  * @param shown - The shown predicate.
12214
12300
  */
12215
- function checkMissedMarks(ctx, state, stage, views, shown) {
12216
- const group = groupOf(Reflect.get(stage, "renderGroup"));
12217
- const nodes = [];
12218
- if (group === void 0 || !listedNodes(group, nodes)) {
12301
+ function checkMissedMarks(ctx, state, stage, shown) {
12302
+ const group = Reflect.get(stage, "renderGroup");
12303
+ if (!hasLists(group)) {
12219
12304
  state.check.off = true;
12220
12305
  ctx.log.debug("renderer:demand-check-off", { reason: NO_LISTS });
12221
12306
  return;
12222
12307
  }
12223
- if (nodes.length === 0) return;
12224
- const owners = ownersOf(views);
12225
- for (const node of nodes) {
12226
- const entity = ownerOf$2(node, owners);
12227
- if (entity === void 0 || state.sinceDraw.has(entity) || state.check.warned.has(entity)) continue;
12228
- if (!shown(entity)) continue;
12229
- state.check.warned.add(entity);
12230
- const label = Reflect.get(node, "label");
12231
- ctx.log.warn("renderer:missed-mark", {
12232
- entity,
12233
- label: typeof label === "string" ? label : ""
12234
- });
12235
- }
12308
+ visitGroup(ctx, state, shown, group);
12236
12309
  }
12237
12310
  //#endregion
12238
12311
  //#region src/plugins/renderer/demand/shown.ts
@@ -12702,7 +12775,7 @@ function createDemandApi(ctx, deps) {
12702
12775
  if (!host.ready()) return;
12703
12776
  if (typeof __MOKU_GAME_DEV__ !== "undefined" && __MOKU_GAME_DEV__ && !drawn && ctx.config.onDemand && !state.check.off) {
12704
12777
  const stage = host.stage();
12705
- if (stage !== void 0) checkMissedMarks(ctx, state, stage, views, shown);
12778
+ if (stage !== void 0) checkMissedMarks(ctx, state, stage, shown);
12706
12779
  }
12707
12780
  if ((typeof __MOKU_GAME_DEV__ !== "undefined" && __MOKU_GAME_DEV__ || typeof __MOKU_GAME_AGENT__ !== "undefined" && __MOKU_GAME_AGENT__) && state.verify.on) {
12708
12781
  verifyFrame(ctx, state, host, state.frame);
@@ -16880,9 +16953,57 @@ function account(kctx, record) {
16880
16953
  record.bytes = bytes;
16881
16954
  }
16882
16955
  /**
16956
+ * The quad of a picture whose top's world transform has another linear part than at the take (F67): the picture is
16957
+ * drawn through `W · W₀⁻¹`, this frame's world transform after the inverse of the one it was taken at, so it scales,
16958
+ * turns and moves with an ancestor's zoom as the live subtree would.
16959
+ *
16960
+ * @param key - The record's key.
16961
+ * @param Ps - The padding of the picture the quad shows.
16962
+ * @param inverse - The inverse of the parent's world transform.
16963
+ * @param local - Where the quad's local transform goes.
16964
+ */
16965
+ function scaledQuad(key, Ps, inverse, local) {
16966
+ const a0 = key[KEY.a] ?? 1;
16967
+ const b0 = key[KEY.b] ?? 0;
16968
+ const c0 = key[KEY.c] ?? 0;
16969
+ const d0 = key[KEY.d] ?? 1;
16970
+ const det = a0 * d0 - b0 * c0;
16971
+ const la = (NOW.a * d0 - NOW.c * b0) / det;
16972
+ const lb = (NOW.b * d0 - NOW.d * b0) / det;
16973
+ const lc = (NOW.c * a0 - NOW.a * c0) / det;
16974
+ const ld = (NOW.d * a0 - NOW.b * c0) / det;
16975
+ const px = (key[KEY.X0] ?? 0) - Ps - (key[KEY.tx] ?? 0);
16976
+ const py = (key[KEY.Y0] ?? 0) - Ps - (key[KEY.ty] ?? 0);
16977
+ const x = la * px + lc * py + NOW.tx;
16978
+ const y = lb * px + ld * py + NOW.ty;
16979
+ local.a = inverse.a * la + inverse.c * lb;
16980
+ local.b = inverse.b * la + inverse.d * lb;
16981
+ local.c = inverse.a * lc + inverse.c * ld;
16982
+ local.d = inverse.b * lc + inverse.d * ld;
16983
+ local.tx = inverse.a * x + inverse.c * y + inverse.tx;
16984
+ local.ty = inverse.b * x + inverse.d * y + inverse.ty;
16985
+ }
16986
+ /**
16987
+ * Tells whether a kept picture may be drawn scaled to this frame's linear part instead of taken again (F67): its
16988
+ * subtree and its stack resolution are as at the take, its filters follow the view (not its canvas place), it was not
16989
+ * cut to the canvas, and the area scale stays within `SCALED_LIMIT` of the take's either way.
16990
+ *
16991
+ * @param record - The record, kept.
16992
+ * @returns True when the quad may scale.
16993
+ */
16994
+ function scalable(record) {
16995
+ const { key } = record;
16996
+ if (record.dirtyS1 || record.cut || record.category === "placed" || key[KEY.rs] !== NOW.rs) return false;
16997
+ const taken = Math.abs((key[KEY.a] ?? 1) * (key[KEY.d] ?? 1) - (key[KEY.b] ?? 0) * (key[KEY.c] ?? 0));
16998
+ const now = Math.abs(NOW.a * NOW.d - NOW.b * NOW.c);
16999
+ const limit = 4;
17000
+ const withinLimit = now <= taken * limit && now * limit >= taken;
17001
+ return taken > 0 && now > 0 && withinLimit;
17002
+ }
17003
+ /**
16883
17004
  * Places the quad of a kept record (P3.5, P2.4): beside its top in the same parent, at the top's depth, drawing the
16884
- * filter output at its canvas place `O + Δ` with an identity world scale; shown while the top is visible and no
16885
- * cover or alpha hides it. Every value is compared before it is written.
17005
+ * filter output at its canvas place `O + Δ` with an identity world scale, or scaled to this frame's linear part when
17006
+ * it differs from the take's (F67); shown while the top is visible and no cover or alpha hides it. Every value is compared before it is written.
16886
17007
  *
16887
17008
  * @param sync - The writers of `sync`: its reader of the hidden reasons.
16888
17009
  * @param pixi - The module that draws.
@@ -16911,15 +17032,18 @@ function placeQuad(sync, pixi, matrices, record, view) {
16911
17032
  if (quad.alpha !== alpha) quad.alpha = alpha;
16912
17033
  const inverse = parent.getGlobalTransform(matrices.parent, false).invert();
16913
17034
  const Ps = record.direct ? 0 : key[KEY.Ps] ?? 0;
16914
- const x = (key[KEY.X0] ?? 0) - Ps + NOW.tx - (key[KEY.tx] ?? 0);
16915
- const y = (key[KEY.Y0] ?? 0) - Ps + NOW.ty - (key[KEY.ty] ?? 0);
16916
17035
  const { local } = matrices;
16917
- local.a = inverse.a;
16918
- local.b = inverse.b;
16919
- local.c = inverse.c;
16920
- local.d = inverse.d;
16921
- local.tx = inverse.a * x + inverse.c * y + inverse.tx;
16922
- local.ty = inverse.b * x + inverse.d * y + inverse.ty;
17036
+ if (linearDiffers(key, NOW, KEY.a, KEY.rs)) scaledQuad(key, Ps, inverse, local);
17037
+ else {
17038
+ const x = (key[KEY.X0] ?? 0) - Ps + NOW.tx - (key[KEY.tx] ?? 0);
17039
+ const y = (key[KEY.Y0] ?? 0) - Ps + NOW.ty - (key[KEY.ty] ?? 0);
17040
+ local.a = inverse.a;
17041
+ local.b = inverse.b;
17042
+ local.c = inverse.c;
17043
+ local.d = inverse.d;
17044
+ local.tx = inverse.a * x + inverse.c * y + inverse.tx;
17045
+ local.ty = inverse.b * x + inverse.d * y + inverse.ty;
17046
+ }
16923
17047
  if (storeMatrix(key, KEY.qa, local)) quad.setFromMatrix(local);
16924
17048
  const visible = top.visible && !sync.hiddenBy(view, "alpha") && !sync.hiddenBy(view, "cover");
16925
17049
  if (quad.visible !== visible) quad.visible = visible;
@@ -17077,11 +17201,14 @@ function canvasChanged(kctx, key) {
17077
17201
  */
17078
17202
  function stepKept$1(kctx, pixi, matrices, record, view) {
17079
17203
  const { key } = record;
17080
- if (record.dirtyS1 || linearDiffers(key, NOW, KEY.a, KEY.rs)) {
17204
+ const turned = linearDiffers(key, NOW, KEY.seenA, KEY.seenRs);
17205
+ const scaled = linearDiffers(key, NOW, KEY.a, KEY.rs);
17206
+ writeSeen(record, NOW);
17207
+ if (record.dirtyS1 || scaled && !scalable(record)) {
17081
17208
  churn(kctx, pixi, matrices, record, view);
17082
17209
  return;
17083
17210
  }
17084
- const moved = NOW.tx !== key[KEY.lastTx] || NOW.ty !== key[KEY.lastTy];
17211
+ const moved = turned || NOW.tx !== key[KEY.lastTx] || NOW.ty !== key[KEY.lastTy];
17085
17212
  if (record.cut && (moved || canvasChanged(kctx, key))) {
17086
17213
  churn(kctx, pixi, matrices, record, view);
17087
17214
  return;
@@ -17093,6 +17220,10 @@ function stepKept$1(kctx, pixi, matrices, record, view) {
17093
17220
  }
17094
17221
  refresh$1(kctx, pixi, matrices, record);
17095
17222
  if (moved) noteMove(kctx, record);
17223
+ if (scaled && !record.settle) {
17224
+ record.settle = true;
17225
+ kctx.demand.request();
17226
+ }
17096
17227
  }
17097
17228
  /**
17098
17229
  * Redoes stage 2 and 3, or stage 3 alone, when their keys changed (P4.3): a refresh never counts as churn.
@@ -17294,7 +17425,8 @@ function bypassRecord(kctx, record, view, live) {
17294
17425
  * it inline, so a production bundle drops this module.
17295
17426
  */
17296
17427
  /**
17297
- * Tells whether a kept record's quad stands off its picture's grid: it is moving, and sampled linearly (P2.5).
17428
+ * Tells whether a kept record's quad stands off its picture's grid: it is moving, or drawn scaled to another linear part
17429
+ * than at the take (F67), and sampled linearly (P2.5).
17298
17430
  *
17299
17431
  * @param kctx - The kept context.
17300
17432
  * @param record - The record, kept.
@@ -17305,7 +17437,7 @@ function offGrid(kctx, record) {
17305
17437
  const world = record.top.getGlobalTransform(record.matrix, false);
17306
17438
  const rs = key[KEY.rs] ?? 1;
17307
17439
  const r = kctx.host.resolution();
17308
- return !onGrid(world.tx - (key[KEY.tx] ?? 0), rs, r) || !onGrid(world.ty - (key[KEY.ty] ?? 0), rs, r);
17440
+ return world.a !== key[KEY.a] || world.b !== key[KEY.b] || world.c !== key[KEY.c] || world.d !== key[KEY.d] || !onGrid(world.tx - (key[KEY.tx] ?? 0), rs, r) || !onGrid(world.ty - (key[KEY.ty] ?? 0), rs, r);
17309
17441
  }
17310
17442
  /**
17311
17443
  * Tells whether the padded picture of a kept record reaches past the canvas edge, where Pixi clips the live filter.
@@ -30731,11 +30863,14 @@ const NONE = {
30731
30863
  bottom: 0,
30732
30864
  left: 0
30733
30865
  };
30734
- /** The sizes of the probes: the public contract a page, a game or the native shell sets. */
30735
- const TOP = "var(--moku-safe-top, env(safe-area-inset-top, 0px))";
30736
- const RIGHT = "var(--moku-safe-right, env(safe-area-inset-right, 0px))";
30737
- const BOTTOM = "var(--moku-safe-bottom, env(safe-area-inset-bottom, 0px))";
30738
- const LEFT = "var(--moku-safe-left, env(safe-area-inset-left, 0px))";
30866
+ /**
30867
+ * The sizes of the probes: the public contract. A page, a game or the native shell sets `--moku-safe-*`; the editor sets
30868
+ * `--moku-device-safe-*` from its device preset, which a page's own value still overrides.
30869
+ */
30870
+ const TOP = "var(--moku-safe-top, var(--moku-device-safe-top, env(safe-area-inset-top, 0px)))";
30871
+ const RIGHT = "var(--moku-safe-right, var(--moku-device-safe-right, env(safe-area-inset-right, 0px)))";
30872
+ const BOTTOM = "var(--moku-safe-bottom, var(--moku-device-safe-bottom, env(safe-area-inset-bottom, 0px)))";
30873
+ const LEFT = "var(--moku-safe-left, var(--moku-device-safe-left, env(safe-area-inset-left, 0px)))";
30739
30874
  /**
30740
30875
  * Reads one CSS length in pixels.
30741
30876
  *
@@ -1,7 +1,7 @@
1
- import { $n as AnyFlow, Tr as Json, Zn as FeaturePlugin, _r as ClockSource, ar as NodeTable, lr as Descriptor, mr as NodeFx, pr as Hint, yr as FakeClock } from "./types-CHmCFReJ.mjs";
2
- import { t as createApp } from "./index-CHI7dTc9.mjs";
3
- import { b as saveOf, v as WithGameState, y as memory } from "./types-DwREs5vb.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-DIbfzWkr.mjs";
1
+ import { $n as AnyFlow, Tr as Json, Zn as FeaturePlugin, _r as ClockSource, ar as NodeTable, lr as Descriptor, mr as NodeFx, pr as Hint, yr as FakeClock } from "./types-ChZ4Ej7O.mjs";
2
+ import { t as createApp } from "./index-CM1goGHf.mjs";
3
+ import { b as saveOf, v as WithGameState, y as memory } from "./types-BdLJ4lTA.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-PssyKRFN.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 refuseStart, en as exit, m as flattenFeatures, rn as type, t as createApp } from "./src-hVe3f8GV.mjs";
1
+ import { $t as refuseStart, en as exit, m as flattenFeatures, rn as type, t as createApp } from "./src-DlKMD4sW.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-Bxxl_mQX.mjs";
@@ -1,7 +1,7 @@
1
- import { $n as AnyFlow, Ar as PlayerStateProvider, Cr as Config, Fr as SnapshotOf, J as Config$2, Ln as Config$1, Mr as SaveDoc, Tr as Json, Z as PixiModule, Zn as FeaturePlugin, _r as ClockSource, a as Config$3, cr as TypeTag, d as Manifest, jr as ProviderCall, n as AssetsIo, st as Orientation, yr as FakeClock } from "./types-CHmCFReJ.mjs";
2
- import { o as AudioContextLike, s as Config$4 } from "./types-9xAh1wGm.mjs";
3
- import { R as I18nApi, U as StringTable, W as TextKey } from "./types-DmGs6x0c.mjs";
4
- import { St as effectsPlugin, Tt as audioPlugin, ht as PlatformProvider, i as screen, pt as platformPlugin, t as createApp } from "./index-CHI7dTc9.mjs";
1
+ import { $n as AnyFlow, Ar as PlayerStateProvider, Cr as Config, Fr as SnapshotOf, J as Config$2, Ln as Config$1, Mr as SaveDoc, Tr as Json, Z as PixiModule, Zn as FeaturePlugin, _r as ClockSource, a as Config$3, cr as TypeTag, d as Manifest, jr as ProviderCall, n as AssetsIo, st as Orientation, yr as FakeClock } from "./types-ChZ4Ej7O.mjs";
2
+ import { o as AudioContextLike, s as Config$4 } from "./types-C-wAHgpD.mjs";
3
+ import { R as I18nApi, U as StringTable, W as TextKey } from "./types-_OQ6H8WL.mjs";
4
+ import { St as effectsPlugin, Tt as audioPlugin, ht as PlatformProvider, i as screen, pt as platformPlugin, t as createApp } from "./index-CM1goGHf.mjs";
5
5
  import { AnyPluginInstance } from "@moku-labs/core";
6
6
 
7
7
  //#region src/plugins/model/store/providers/memory.d.ts
@@ -1,5 +1,5 @@
1
- import { $ as PixiTexture, D as FilterSlot, Dn as MotionHandle, Fn as Api$2, Gr as Api$1, Mn as TrackOptions, Nn as TrackSegment, O as FilterSlotInputs, On as NumericFields, Q as PixiSprite, Sr as Api$6, Tr as Json, Vr as Events$5, X as PixiFilter, Z as PixiModule, an as AnyComponent, cn as ComponentHandle, cr as TypeTag, ct as Point, ei as Events$2, en as Api$3, fn as Entity, jn as ReducedMotion, k as SyncApi, lr as Descriptor, q as Api$4, s as Events$1, sn as AnyComponentValue, t as Api$5, ti as Require, un as ComponentType, ut as FilterCache, wn as Ease, wr as Events$3, y as Events$4 } from "./types-CHmCFReJ.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-DmGs6x0c.mjs";
1
+ import { $ as PixiTexture, D as FilterSlot, Dn as MotionHandle, Fn as Api$2, Gr as Api$1, Mn as TrackOptions, Nn as TrackSegment, O as FilterSlotInputs, On as NumericFields, Q as PixiSprite, Sr as Api$6, Tr as Json, Vr as Events$5, X as PixiFilter, Z as PixiModule, an as AnyComponent, cn as ComponentHandle, cr as TypeTag, ct as Point, ei as Events$2, en as Api$3, fn as Entity, jn as ReducedMotion, k as SyncApi, lr as Descriptor, q as Api$4, s as Events$1, sn as AnyComponentValue, t as Api$5, ti as Require, un as ComponentType, ut as FilterCache, wn as Ease, wr as Events$3, y as Events$4 } from "./types-ChZ4Ej7O.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-_OQ6H8WL.mjs";
3
3
  import { Log } from "@moku-labs/common/browser";
4
4
  import { PluginCtx } from "@moku-labs/core";
5
5
 
@@ -5614,12 +5614,13 @@ type HistoryChange = {
5614
5614
  *
5615
5615
  * @example
5616
5616
  * ```ts
5617
- * const quiet: HistorySlot = { frame: 96, changes: [], spawned: [], despawned: [] };
5617
+ * const quiet: HistorySlot = { frame: 96, changes: [], count: 0, spawned: [], despawned: [] };
5618
5618
  * ```
5619
5619
  */
5620
5620
  type HistorySlot = {
5621
- frame: number;
5622
- changes: HistoryChange[];
5621
+ frame: number; /** The records of this frame are the first `count`; the ones after them are spares kept for reuse. */
5622
+ changes: HistoryChange[]; /** How many of `changes` belong to this frame. */
5623
+ count: number;
5623
5624
  spawned: Entity[];
5624
5625
  despawned: Entity[];
5625
5626
  };
@@ -5639,7 +5640,8 @@ type History = {
5639
5640
  slots: Array<HistorySlot | undefined>; /** The last frame recorded, `undefined` before the first one. */
5640
5641
  newest: number | undefined; /** The frame the recording started on: the shadow is the world at its end. */
5641
5642
  started: number | undefined; /** Rows marked and rows that changed, per component, over the current window of frames. */
5642
- marks: Map<string, MarkCount>; /** Recorded frames in the current window; the counts are judged and zeroed at `HISTORY_FRAMES`. */
5643
+ marks: Map<string, MarkCount>; /** The same counts in the order they were made, walked when a window closes: no iterator a frame. */
5644
+ markList: MarkCount[]; /** Recorded frames in the current window; the counts are judged and zeroed at `HISTORY_FRAMES`. */
5643
5645
  markFrames: number; /** The components `world:mut-unchanged` already named: each is named once. */
5644
5646
  warned: Set<string>;
5645
5647
  };
@@ -5647,6 +5649,7 @@ type History = {
5647
5649
  * Rows of one component marked changed in a window of frames, and how many of them changed.
5648
5650
  */
5649
5651
  type MarkCount = {
5652
+ component: string;
5650
5653
  marked: number;
5651
5654
  changed: number;
5652
5655
  };
@@ -1,4 +1,4 @@
1
- import { Dn as MotionHandle, En as MotionCause, Fn as Api$1, Gr as Api, On as NumericFields, Pn as ViewHandle, Sn as ChangeHook, Tn as Motion, Tr as Json, W as TransformValue, an as AnyComponent, en as Api$2, fn as Entity, gr as Api$5, hr as Answer, jn as ReducedMotion, kn as ProjectionMotion, lr as Descriptor, lt as ViewportSize, on as AnyComponentType, pr as Hint, q as Api$3, s as Events$1, sn as AnyComponentValue, t as Api$4, ti as Require, un as ComponentType, wn as Ease, xn as TagType } from "./types-CHmCFReJ.mjs";
1
+ import { Dn as MotionHandle, En as MotionCause, Fn as Api$1, Gr as Api, On as NumericFields, Pn as ViewHandle, Sn as ChangeHook, Tn as Motion, Tr as Json, W as TransformValue, an as AnyComponent, en as Api$2, fn as Entity, gr as Api$5, hr as Answer, jn as ReducedMotion, kn as ProjectionMotion, lr as Descriptor, lt as ViewportSize, on as AnyComponentType, pr as Hint, q as Api$3, s as Events$1, sn as AnyComponentValue, t as Api$4, ti as Require, un as ComponentType, wn as Ease, xn as TagType } from "./types-ChZ4Ej7O.mjs";
2
2
  import { Log } from "@moku-labs/common/browser";
3
3
  import { PluginCtx } from "@moku-labs/core";
4
4
  import { Node, Yoga } from "yoga-layout/load";
@@ -1,5 +1,5 @@
1
- import { Pr as Snapshot, Tr as Json } from "./types-CHmCFReJ.mjs";
2
- import { t as HeadlessApp } from "./headless-DIbfzWkr.mjs";
1
+ import { Pr as Snapshot, Tr as Json } from "./types-ChZ4Ej7O.mjs";
2
+ import { t as HeadlessApp } from "./headless-PssyKRFN.mjs";
3
3
  import { Log } from "@moku-labs/common/browser";
4
4
 
5
5
  //#region src/plugins/flow/doors/types.d.ts
@@ -1,9 +1,9 @@
1
- import { Sr as Api$2, en as Api$1, q as Api, zr as Api$3 } from "./types-CHmCFReJ.mjs";
2
- import { P as AnimApi, r as UiApi } from "./types-9xAh1wGm.mjs";
3
- import { N as InputApi } from "./types-DmGs6x0c.mjs";
4
- import { r as Repro } from "./headless-DIbfzWkr.mjs";
5
- import { a as InputOf, r as ControlApp } from "./types-L4nVF9Ny.mjs";
6
- import { t as commands } from "./commands-CV6Deuaj.mjs";
1
+ import { Sr as Api$2, en as Api$1, q as Api, zr as Api$3 } from "./types-ChZ4Ej7O.mjs";
2
+ import { P as AnimApi, r as UiApi } from "./types-C-wAHgpD.mjs";
3
+ import { N as InputApi } from "./types-_OQ6H8WL.mjs";
4
+ import { r as Repro } from "./headless-PssyKRFN.mjs";
5
+ import { a as InputOf, r as ControlApp } from "./types-sfaYGg9l.mjs";
6
+ import { t as commands } from "./commands-KWKfdywA.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-CjXAp0DW.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-DmDWJEzs.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moku-labs/game",
3
- "version": "0.15.23",
3
+ "version": "0.15.25",
4
4
  "description": "2D puzzle game engine on TypeScript and PixiJS, built on @moku-labs/core.",
5
5
  "type": "module",
6
6
  "sideEffects": [