@zylem/game-lib 0.8.2 → 0.8.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/dist/api/game-lib.api.json +1310 -704
  2. package/dist/api/game-lib.api.md +125 -88
  3. package/dist/api/game-lib.d.ts +255 -92
  4. package/dist/api/game-lib.typedoc.json +127368 -201231
  5. package/dist/bridge.d.ts +97 -0
  6. package/dist/bridge.js +1 -0
  7. package/dist/chunk-2VSVOPQN.js +19 -0
  8. package/dist/{chunk-KVSJE4XD.js → chunk-3I537JFL.js} +1 -1
  9. package/dist/chunk-AIQ6IVT5.js +1 -0
  10. package/dist/chunk-C5NEGC3S.js +1 -0
  11. package/dist/{chunk-CD7ZOZ35.js → chunk-CV75NSES.js} +1 -1
  12. package/dist/chunk-GKHAJ3MW.js +1 -0
  13. package/dist/chunk-HNCTOJP7.js +1 -0
  14. package/dist/{chunk-MZZCDXFB.js → chunk-LGS5N5M4.js} +1 -1
  15. package/dist/chunk-LKFF6XJC.js +1 -0
  16. package/dist/chunk-Q27ULMXY.js +24 -0
  17. package/dist/chunk-TV3AZKT4.js +9 -0
  18. package/dist/chunk-W5SXED5T.js +1 -0
  19. package/dist/chunk-WVSYUWTT.js +1 -0
  20. package/dist/core.d.ts +2 -2
  21. package/dist/core.js +1 -1
  22. package/dist/debug-state-Dp9CQQZ4.d.ts +49 -0
  23. package/dist/debug.d.ts +22 -3
  24. package/dist/debug.js +1 -1
  25. package/dist/entity.js +1 -1
  26. package/dist/events.d.ts +1 -64
  27. package/dist/events.js +1 -1
  28. package/dist/{game-DbqBQiFC.d.ts → game-NHkubIfI.d.ts} +49 -21
  29. package/dist/game-default-5TB3T5N7.js +1 -0
  30. package/dist/public.d.ts +5 -3
  31. package/dist/public.js +1 -1
  32. package/dist/web-components.d.ts +2 -2
  33. package/dist/web-components.js +1 -1
  34. package/package.json +8 -3
  35. package/dist/chunk-3PRBSSTS.js +0 -24
  36. package/dist/chunk-CFVRRRZZ.js +0 -1
  37. package/dist/chunk-COM6HJ2L.js +0 -1
  38. package/dist/chunk-JQKS42G4.js +0 -1
  39. package/dist/chunk-TAUWDQZF.js +0 -9
  40. package/dist/chunk-VS2D4B3W.js +0 -1
  41. package/dist/chunk-YHEWKN7N.js +0 -1
  42. package/dist/debug-state-DRoeasG2.d.ts +0 -17
  43. package/dist/game-default-6AZZCNST.js +0 -1
@@ -1,4 +1,5 @@
1
1
  import { abs } from 'three/tsl';
2
+ import { announceBridgeReady } from '@zylem/bridge';
2
3
  import { asin } from 'three/tsl';
3
4
  import { atan } from 'three/tsl';
4
5
  import { BehaviorDescriptor } from '@zylem/behaviors';
@@ -20,6 +21,14 @@ import { BoundaryRicochet3DEntity } from '@zylem/behaviors';
20
21
  import { BoundaryRicochetCoordinator } from '@zylem/behaviors';
21
22
  import { BoundaryRicochetEntity } from '@zylem/behaviors';
22
23
  import { BoundaryRicochetUpdate } from '@zylem/behaviors';
24
+ import { BRIDGE_READY_EVENT } from '@zylem/bridge';
25
+ import { BridgeChannel } from '@zylem/bridge';
26
+ import { BridgeDebugTool } from '@zylem/bridge';
27
+ import { BridgeHandler } from '@zylem/bridge';
28
+ import { GameLoadingPayload as BridgeLoadingPayload } from '@zylem/bridge';
29
+ import { BridgeMessages } from '@zylem/bridge';
30
+ import { BridgeMessageType } from '@zylem/bridge';
31
+ import { BridgeVec3 } from '@zylem/bridge';
23
32
  import { BufferGeometry } from 'three';
24
33
  import { BurstParticlePresetOptions } from '@zylem/behaviors';
25
34
  import { Camera } from 'three';
@@ -71,11 +80,14 @@ import { Destructible3DHandle } from '@zylem/behaviors';
71
80
  import { DestructibleMesh } from '@zylem/behaviors';
72
81
  import { DestructiblePrebakeWorkerAPI } from '@zylem/behaviors';
73
82
  import { dot } from 'three/tsl';
83
+ import { EditorToGameMessages } from '@zylem/bridge';
74
84
  import { emitRuntimeGoal } from '@zylem/behaviors/runtime-2d';
75
85
  import { emitRuntimeRicochet } from '@zylem/behaviors/runtime-2d';
76
86
  import { emitRuntimeTriggerRegionEnter } from '@zylem/behaviors/runtime-2d';
77
87
  import { EntityDefinition } from '@zylem/behaviors/core';
78
88
  import { EntityHandle } from '@zylem/behaviors/core';
89
+ import { EntitySummaryPayload } from '@zylem/bridge';
90
+ import { EntityThumbnailPayload } from '@zylem/bridge';
79
91
  import { exp } from 'three/tsl';
80
92
  import { FacingAngle2DEntity } from '@zylem/behaviors';
81
93
  import { fireCooldown } from '@zylem/behaviors';
@@ -102,11 +114,16 @@ import { FogExp2 } from 'three';
102
114
  import { fract } from 'three/tsl';
103
115
  import { FractureOptions } from '@zylem/behaviors';
104
116
  import { FractureOptionsInput } from '@zylem/behaviors';
117
+ import { GameConfigPayload } from '@zylem/bridge';
118
+ import { GameNoticePayload } from '@zylem/bridge';
119
+ import { GameToEditorMessages } from '@zylem/bridge';
120
+ import { GameVariablePayload } from '@zylem/bridge';
105
121
  import { getCooldown } from '@zylem/behaviors';
106
122
  import { getCooldownStore } from '@zylem/behaviors';
107
123
  import { getEntityFacingAngle2D } from '@zylem/behaviors';
108
124
  import { getPosition } from '@zylem/behaviors/core';
109
125
  import { getVelocity } from '@zylem/behaviors/core';
126
+ import { getZylemBridge } from '@zylem/bridge';
110
127
  import { Group } from 'three';
111
128
  import { hasAnyWorldBoundary2DHit } from '@zylem/behaviors';
112
129
  import { hasAnyWorldBoundary3DHit } from '@zylem/behaviors';
@@ -302,6 +319,7 @@ import { StageBodyKindValue } from '@zylem/behaviors/core';
302
319
  import { StageBoundaryDim } from '@zylem/behaviors/core';
303
320
  import { StageColliderConfig } from '@zylem/behaviors/core';
304
321
  import { StageColliderShape } from '@zylem/behaviors/core';
322
+ import { StageConfigPayload } from '@zylem/bridge';
305
323
  import { StageDebugRender } from '@zylem/behaviors/core';
306
324
  import { StageEvent } from '@zylem/behaviors/core';
307
325
  import { StageEventType } from '@zylem/behaviors/core';
@@ -310,6 +328,7 @@ import { StageRaycastHit } from '@zylem/behaviors/core';
310
328
  import { StageRenderSlot } from '@zylem/behaviors/core';
311
329
  import { StageRicochetDim } from '@zylem/behaviors/core';
312
330
  import { StageRicochetReflection } from '@zylem/behaviors/core';
331
+ import { StageSnapshotPayload } from '@zylem/bridge';
313
332
  import { StageTopDownPlane } from '@zylem/behaviors/core';
314
333
  import { Static } from '@sinclair/typebox';
315
334
  import { step } from 'three/tsl';
@@ -374,6 +393,7 @@ import { WorldBoundary3DPosition } from '@zylem/behaviors';
374
393
  import { WorldBoundary3DState } from '@zylem/behaviors';
375
394
  import { wrapAround3D } from '@zylem/behaviors/core';
376
395
  import { wrapAroundXY } from '@zylem/behaviors/core';
396
+ import { ZylemBridge } from '@zylem/bridge';
377
397
 
378
398
  export { abs }
379
399
 
@@ -439,6 +459,8 @@ declare type AnimationOptions = {
439
459
  fadeDuration?: number;
440
460
  };
441
461
 
462
+ export { announceBridgeReady }
463
+
442
464
  declare type AnyNode = NodeLike | Promise<NodeLike>;
443
465
 
444
466
  /**
@@ -680,6 +702,22 @@ declare interface BoxMeshOptions extends BaseMeshOptions {
680
702
 
681
703
  declare type BoxOptions = BaseNode | Partial<ZylemBoxOptions>;
682
704
 
705
+ export { BRIDGE_READY_EVENT }
706
+
707
+ export { BridgeChannel }
708
+
709
+ export { BridgeDebugTool }
710
+
711
+ export { BridgeHandler }
712
+
713
+ export { BridgeLoadingPayload }
714
+
715
+ export { BridgeMessages }
716
+
717
+ export { BridgeMessageType }
718
+
719
+ export { BridgeVec3 }
720
+
683
721
  /** Cuboid collider sized as `size.xyz` (full extents — half-extents are computed). */
684
722
  export declare function buildBoxCollider(size: Vec3Input, options?: RuntimeColliderOptions): StageColliderConfig;
685
723
 
@@ -1662,6 +1700,9 @@ declare interface CylinderMeshOptions extends BaseMeshOptions {
1662
1700
 
1663
1701
  declare type CylinderOptions = BaseNode | Partial<ZylemCylinderOptions>;
1664
1702
 
1703
+ /** Resolves a UUID to the live entity owned by the active stage. */
1704
+ export declare type DebugEntityResolver = (uuid: string) => GameEntity<any> | null;
1705
+
1665
1706
  /**
1666
1707
  * Interface for entities that provide custom debug information
1667
1708
  */
@@ -1673,14 +1714,22 @@ declare interface DebugState {
1673
1714
  enabled: boolean;
1674
1715
  paused: boolean;
1675
1716
  tool: DebugTools;
1676
- selectedEntity: GameEntity<any> | null;
1677
- hoveredEntity: GameEntity<any> | null;
1717
+ /**
1718
+ * UUID of the selected entity, never the entity itself. Valtio deep-proxies
1719
+ * any object assigned into a proxy, so storing a live `GameEntity` here
1720
+ * would walk `group.parent` into the entire THREE scene graph and
1721
+ * `physicsWorldRef` into the Rapier world, freezing the tab and handing
1722
+ * Proxy wrappers to engine code that expects real instances.
1723
+ */
1724
+ selectedEntityId: string | null;
1725
+ /** UUID of the hovered entity. Same constraint as `selectedEntityId`. */
1726
+ hoveredEntityId: string | null;
1678
1727
  flags: Set<string>;
1679
1728
  }
1680
1729
 
1681
1730
  export declare const debugState: DebugState;
1682
1731
 
1683
- export declare type DebugTools = 'select' | 'translate' | 'rotate' | 'scale' | 'delete' | 'none';
1732
+ export declare type DebugTools = 'select' | 'translate' | 'rotate' | 'scale' | 'delete' | 'add' | 'none';
1684
1733
 
1685
1734
  /**
1686
1735
  * Builds a ready-to-use {@link GameInputConfig} that wires up themed
@@ -1804,6 +1853,8 @@ export declare function dispatchStageState(): void;
1804
1853
 
1805
1854
  export { dot }
1806
1855
 
1856
+ export { EditorToGameMessages }
1857
+
1807
1858
  export { emitRuntimeGoal }
1808
1859
 
1809
1860
  export { emitRuntimeRicochet }
@@ -1841,36 +1892,6 @@ declare abstract class EntityCollisionBuilder extends CollisionBuilder {
1841
1892
  abstract collider(options: GameEntityOptions): SimulationColliderDefinition;
1842
1893
  }
1843
1894
 
1844
- /** Payload for entity configuration sent to editor. */
1845
- export declare interface EntityConfigPayload {
1846
- uuid: string;
1847
- name: string;
1848
- type: string;
1849
- position: {
1850
- x: number;
1851
- y: number;
1852
- z: number;
1853
- };
1854
- rotation: {
1855
- x: number;
1856
- y: number;
1857
- z: number;
1858
- };
1859
- scale: {
1860
- x: number;
1861
- y: number;
1862
- z: number;
1863
- };
1864
- /** PNG data URL of a framed entity preview, when available. */
1865
- thumbnail?: string | null;
1866
- /** World-space AABB size used for thumbnail rulers. */
1867
- bounds?: {
1868
- width: number;
1869
- height: number;
1870
- depth: number;
1871
- };
1872
- }
1873
-
1874
1895
  declare interface EntityDebugInfo {
1875
1896
  buildInfo: () => Record<string, string>;
1876
1897
  }
@@ -2029,6 +2050,8 @@ declare interface EntitySpawner {
2029
2050
 
2030
2051
  export declare function entitySpawner(factory: (x: number, y: number) => Promise<any> | GameEntity<any>): EntitySpawner;
2031
2052
 
2053
+ export { EntitySummaryPayload }
2054
+
2032
2055
  /**
2033
2056
  * Generates and caches entity thumbnail previews for the editor entity list.
2034
2057
  * Renders a cloned Object3D into an offscreen RenderTarget on the shared WebGPU renderer.
@@ -2038,15 +2061,30 @@ export declare class EntityThumbnailCache {
2038
2061
  private inFlight;
2039
2062
  private renderer;
2040
2063
  private size;
2041
- constructor(size?: number);
2064
+ private maxEntries;
2065
+ private queue;
2066
+ private activeRenders;
2067
+ constructor(size?: number, maxEntries?: number);
2042
2068
  setRenderer(renderer: WebGPURenderer | null): void;
2043
2069
  get(uuid: string): EntityThumbnailCacheEntry | null;
2044
2070
  invalidate(uuid: string): void;
2045
2071
  clear(): void;
2072
+ /** Number of thumbnails currently cached. */
2073
+ get cacheSize(): number;
2074
+ /** Entities waiting for a render slot. */
2075
+ get pendingCount(): number;
2046
2076
  /**
2047
- * Return a cached thumbnail or generate one asynchronously.
2077
+ * Return a cached thumbnail, or schedule one. Renders are throttled to
2078
+ * {@link MAX_CONCURRENT_RENDERS} at a time behind a bounded queue, so a
2079
+ * burst of spawns cannot flood the renderer with pixel readbacks.
2048
2080
  */
2049
2081
  ensure(uuid: string, object: Object3D | null | undefined): Promise<EntityThumbnailCacheEntry | null>;
2082
+ /** Start queued renders until the concurrency budget is spent. */
2083
+ private drainQueue;
2084
+ /** Move an entry to the end of the Map so it is evicted last. */
2085
+ private touch;
2086
+ /** Evict least-recently-used entries down to the cap. */
2087
+ private evictOverflow;
2050
2088
  private render;
2051
2089
  }
2052
2090
 
@@ -2057,7 +2095,13 @@ export declare interface EntityThumbnailCacheEntry extends EntityThumbnailResult
2057
2095
  uuid: string;
2058
2096
  }
2059
2097
 
2098
+ export { EntityThumbnailPayload }
2099
+
2060
2100
  export declare interface EntityThumbnailResult {
2101
+ /**
2102
+ * Preview image URL. Blob URL when the payload worker is available
2103
+ * (encoded off-thread), PNG data URL on the synchronous fallback path.
2104
+ */
2061
2105
  dataUrl: string;
2062
2106
  bounds: Pick<ObjectBounds, 'width' | 'height' | 'depth'>;
2063
2107
  }
@@ -2458,6 +2502,89 @@ export declare class Game<TGlobals extends BaseGlobals> implements IGame<TGlobal
2458
2502
  onLoading(callback: (event: GameLoadingEvent) => void): () => void;
2459
2503
  }
2460
2504
 
2505
+ export declare class GameBridge {
2506
+ private channel;
2507
+ private unsubscribes;
2508
+ private host;
2509
+ /**
2510
+ * Values the editor is already known to hold, either because it commanded
2511
+ * them or because we published them. Anything matching here is skipped, so
2512
+ * an editor command can never echo back as a game update.
2513
+ */
2514
+ private editorKnown;
2515
+ constructor();
2516
+ /**
2517
+ * Subscribe to editor commands and announce the game side as ready.
2518
+ * @param host Live-game context for entity/stage lookups
2519
+ * @param announceTarget DOM node the `zylem:bridge:ready` event bubbles from
2520
+ */
2521
+ connect(host: GameBridgeHost, announceTarget?: EventTarget): void;
2522
+ disconnect(): void;
2523
+ /**
2524
+ * Publish only the parts of `debugState` the editor does not already know.
2525
+ * Values that arrived as editor commands are recorded on `editorKnown`
2526
+ * first, so they are never echoed back.
2527
+ */
2528
+ private publishDebugStateChanges;
2529
+ /**
2530
+ * Whether anything is listening for entity list updates. Building entity
2531
+ * summaries is pure editor overhead, so publishers skip it when no editor
2532
+ * is attached.
2533
+ */
2534
+ wantsEntityUpdates(): boolean;
2535
+ /**
2536
+ * Whether anything is listening for thumbnails. Each thumbnail costs an
2537
+ * offscreen render plus a pixel readback on the renderer the game draws
2538
+ * with, so this gate matters far more than the others.
2539
+ */
2540
+ wantsThumbnails(): boolean;
2541
+ /**
2542
+ * Run a callback when an editor first subscribes to entity data, so the
2543
+ * game can backfill state produced while nothing was listening.
2544
+ *
2545
+ * @returns An unsubscribe function.
2546
+ */
2547
+ onEditorAttached(callback: () => void): () => void;
2548
+ publishConfig(config: GameConfigPayload): void;
2549
+ publishLoading(payload: BridgeLoadingPayload): void;
2550
+ publishStatus(status: {
2551
+ paused?: boolean;
2552
+ debug?: boolean;
2553
+ }): void;
2554
+ publishVariable(payload: GameVariablePayload): void;
2555
+ /** Surface a diagnostic message in the editor console. */
2556
+ publishNotice(level: GameNoticePayload['level'], message: string): void;
2557
+ publishStageSnapshot(snapshot: StageSnapshotPayload): void;
2558
+ /** RAF-coalesced entity add/update stream; merged by uuid per frame. */
2559
+ queueEntityUpsert(entities: EntitySummaryPayload[]): void;
2560
+ /** RAF-coalesced entity removal stream. */
2561
+ queueEntityRemoved(uuids: string[]): void;
2562
+ /** RAF-coalesced thumbnail stream; merged by uuid per frame. */
2563
+ queueThumbnails(thumbnails: EntityThumbnailPayload[]): void;
2564
+ }
2565
+
2566
+ /**
2567
+ * Game-side adapter for the shared editor ↔ game bridge.
2568
+ *
2569
+ * Owns the editor→game command subscriptions (debug/tool/playback, entity
2570
+ * select/focus/transform, stage variables) and exposes typed publish helpers
2571
+ * the running game uses to stream config, stage snapshots, entity upserts,
2572
+ * and thumbnails to the editor. High-frequency publishes go through the
2573
+ * channel's `queue`, which coalesces to at most one message per animation
2574
+ * frame.
2575
+ */
2576
+
2577
+ /**
2578
+ * Host context supplied by the running game so bridge commands can act on
2579
+ * live stage state without the bridge importing game internals.
2580
+ */
2581
+ export declare interface GameBridgeHost {
2582
+ /** Resolve a live entity by UUID from the current stage. */
2583
+ resolveEntity(uuid: string): GameEntity<any> | null;
2584
+ /** Write a stage variable (editor `stage:variable:set`). */
2585
+ setStageVariable?(key: string, value: unknown): void;
2586
+ }
2587
+
2461
2588
  /**
2462
2589
  * GameCanvas is a DOM delegate that owns:
2463
2590
  * - container lookup/creation and styling (including fullscreen centering)
@@ -2668,6 +2795,8 @@ export declare type GameConfigLike = Partial<{
2668
2795
  canvas: HTMLCanvasElement;
2669
2796
  }>;
2670
2797
 
2798
+ export { GameConfigPayload }
2799
+
2671
2800
  export declare type GameDeviceConfig = Partial<{
2672
2801
  /** numeric value or key in AspectRatio */
2673
2802
  aspectRatio: AspectRatioValue | keyof typeof AspectRatio;
@@ -2994,7 +3123,6 @@ export declare type GameEvents = {
2994
3123
  'debug': {
2995
3124
  enabled: boolean;
2996
3125
  };
2997
- 'state:dispatch': StateDispatchPayload;
2998
3126
  };
2999
3127
 
3000
3128
  export declare interface GameInputConfig {
@@ -3212,6 +3340,10 @@ export declare interface GameLoadingPayload extends LoadingEvent {
3212
3340
 
3213
3341
  declare type GameOptions<TGlobals extends BaseGlobals> = Array<ZylemGameConfig<Stage, any, TGlobals> | GameConfigLike | Stage | GameEntityLifeCycle | BaseNode>;
3214
3342
 
3343
+ export { GameToEditorMessages }
3344
+
3345
+ export { GameVariablePayload }
3346
+
3215
3347
  export { getCooldown }
3216
3348
 
3217
3349
  export { getCooldownStore }
@@ -3232,6 +3364,11 @@ export declare function getGlobal<T = unknown>(path: string): T | undefined;
3232
3364
  */
3233
3365
  export declare function getGlobals<T = Record<string, unknown>>(): T;
3234
3366
 
3367
+ /** The live hovered entity, resolved on demand through the active stage. */
3368
+ export declare function getHoveredEntity(): GameEntity<any> | null;
3369
+
3370
+ export declare function getHoveredEntityId(): string | null;
3371
+
3235
3372
  export { getPosition }
3236
3373
 
3237
3374
  export declare function getPresetResolution(preset: RetroPresetKey, key?: string): RetroResolution | undefined;
@@ -3241,6 +3378,11 @@ export declare function getPresetResolution(preset: RetroPresetKey, key?: string
3241
3378
  */
3242
3379
  export declare function getRotation(entity: RotatableEntity): any;
3243
3380
 
3381
+ /** The live selected entity, resolved on demand through the active stage. */
3382
+ export declare function getSelectedEntity(): GameEntity<any> | null;
3383
+
3384
+ export declare function getSelectedEntityId(): string | null;
3385
+
3244
3386
  /**
3245
3387
  * Get a variable from an object by path.
3246
3388
  * @example getVariable(stage1, 'totalAngle') // 0.5
@@ -3250,6 +3392,8 @@ export declare function getVariable<T = unknown>(target: object, path: string):
3250
3392
 
3251
3393
  export { getVelocity }
3252
3394
 
3395
+ export { getZylemBridge }
3396
+
3253
3397
  /**
3254
3398
  * Listen for a single global key change inside an onUpdate pipeline.
3255
3399
  * Usage: onUpdate(globalChange('p1Score', (value) => { ... }))
@@ -4322,6 +4466,15 @@ declare type RectOptions = BaseNode | Partial<ZylemRectOptions>;
4322
4466
 
4323
4467
  export { registerCooldown }
4324
4468
 
4469
+ /**
4470
+ * Register the active stage's entity resolver so `getSelectedEntity` /
4471
+ * `getHoveredEntity` can hand back live entities without the reactive store
4472
+ * ever holding one.
4473
+ *
4474
+ * @returns An unregister function.
4475
+ */
4476
+ export declare function registerDebugEntityResolver(resolver: DebugEntityResolver): () => void;
4477
+
4325
4478
  /**
4326
4479
  * Register the active stage's focus context. Returns an unregister function.
4327
4480
  */
@@ -4600,6 +4753,9 @@ export declare function resolveButtonAccent(theme: TouchTheme, slot: VirtualTouc
4600
4753
 
4601
4754
  export { ResolvedAim2D }
4602
4755
 
4756
+ /** Resolve a UUID through the active stage, or null when unavailable. */
4757
+ export declare function resolveDebugEntity(uuid: string | null): GameEntity<any> | null;
4758
+
4603
4759
  /** `StageTransitionConfig` with all defaults applied. */
4604
4760
  declare interface ResolvedStageTransition {
4605
4761
  duration: number;
@@ -5038,6 +5194,8 @@ export declare function setDebugTool(tool: DebugTools): void;
5038
5194
  */
5039
5195
  export declare function setGlobal(path: string, value: unknown): void;
5040
5196
 
5197
+ export declare function setHoveredEntityId(uuid: string | null): void;
5198
+
5041
5199
  export declare function setPaused(paused: boolean): void;
5042
5200
 
5043
5201
  export { setPosition }
@@ -5088,7 +5246,13 @@ export declare function setRotationY(entity: RotatableEntity, y: number): void;
5088
5246
  */
5089
5247
  export declare function setRotationZ(entity: RotatableEntity, z: number): void;
5090
5248
 
5091
- export declare function setSelectedEntity(entity: GameEntity<any> | null): void;
5249
+ /**
5250
+ * @deprecated Pass a UUID to {@link setSelectedEntityId} instead. Accepts a
5251
+ * live entity only to keep the previous public API working.
5252
+ */
5253
+ export declare function setSelectedEntity(entity: GameEntity<any> | string | null): void;
5254
+
5255
+ export declare function setSelectedEntityId(uuid: string | null): void;
5092
5256
 
5093
5257
  export declare function setStageFrameProfilerEnabled(value: boolean): void;
5094
5258
 
@@ -5500,19 +5664,7 @@ export declare type StageConfigLike = Partial<{
5500
5664
  postProcessingEffects: ZylemPostEffect[];
5501
5665
  }>;
5502
5666
 
5503
- /** Payload for stage configuration sent to editor. */
5504
- export declare interface StageConfigPayload {
5505
- id: string;
5506
- backgroundColor: string;
5507
- backgroundImage: string | null;
5508
- gravity: {
5509
- x: number;
5510
- y: number;
5511
- z: number;
5512
- };
5513
- inputs: Record<string, string[]>;
5514
- variables: Record<string, unknown>;
5515
- }
5667
+ export { StageConfigPayload }
5516
5668
 
5517
5669
  /**
5518
5670
  * Self-managing debug delegate that subscribes to `debugState` internally.
@@ -5534,13 +5686,27 @@ declare class StageDebugDelegate {
5534
5686
  private cameraDebugDelegate;
5535
5687
  private debugStateUnsubscribe;
5536
5688
  private focusContextUnregister;
5689
+ private entityResolverUnregister;
5690
+ private lastDebugEnabled;
5691
+ private warnedMissingAddFactory;
5537
5692
  constructor(stage: ZylemStage, options?: StageDebugDelegateOptions);
5693
+ /**
5694
+ * Supply the factory the `add` tool uses to spawn entities at the clicked
5695
+ * point. Without one the tool warns instead of silently doing nothing.
5696
+ */
5697
+ setAddEntityFactory(factory: AddEntityFactory | null): void;
5538
5698
  /** Camera used for debug orbit / raycasts (active primary, usually `__debug__`). */
5539
5699
  private getDebugViewCamera;
5540
5700
  private resolveEntity;
5541
5701
  /** Copy all stage children into debugMap so raycast/select tools work after enabling debug mid-session. */
5542
5702
  private populateDebugMap;
5543
5703
  private ensureCameraDebugDelegate;
5704
+ /**
5705
+ * Activate/deactivate only when `debugState.enabled` actually flips.
5706
+ * Hover and selection write to `debugState` every frame, and `activate()`
5707
+ * rebuilds post-processing, so reacting to every notification would
5708
+ * rebuild the render pipeline once per frame.
5709
+ */
5544
5710
  private syncWithDebugState;
5545
5711
  /** Initialize DOM listeners and switch rendering to the debug orbit camera. */
5546
5712
  private activate;
@@ -5939,6 +6105,8 @@ export declare const StageSchema: _sinclair_typebox.TObject<{
5939
6105
  assets: _sinclair_typebox.TOptional<_sinclair_typebox.TArray<_sinclair_typebox.TString>>;
5940
6106
  }>;
5941
6107
 
6108
+ export { StageSnapshotPayload }
6109
+
5942
6110
  declare type StageState = ZylemStageConfig & {
5943
6111
  entities: GameEntityInterface[];
5944
6112
  };
@@ -6006,27 +6174,6 @@ export declare const standardShader: {
6006
6174
  fragment: string;
6007
6175
  };
6008
6176
 
6009
- /** Payload for state dispatch events from game to editor. */
6010
- export declare interface StateDispatchPayload {
6011
- scope: 'game' | 'stage' | 'entity';
6012
- path: string;
6013
- value: unknown;
6014
- previousValue?: unknown;
6015
- config?: {
6016
- id: string;
6017
- aspectRatio: number;
6018
- fullscreen: boolean;
6019
- bodyBackground: string | undefined;
6020
- internalResolution: {
6021
- width: number;
6022
- height: number;
6023
- } | undefined;
6024
- debug: boolean;
6025
- } | null;
6026
- stageConfig?: StageConfigPayload | null;
6027
- entities?: EntityConfigPayload[] | null;
6028
- }
6029
-
6030
6177
  export { step }
6031
6178
 
6032
6179
  /**
@@ -7085,6 +7232,8 @@ export declare class ZylemBox extends GameEntity<ZylemBoxOptions> {
7085
7232
 
7086
7233
  declare type ZylemBoxOptions = GameEntityOptions;
7087
7234
 
7235
+ export { ZylemBridge }
7236
+
7088
7237
  /**
7089
7238
  * ZylemCamera is a lightweight camera wrapper that manages the Three.js camera,
7090
7239
  * the camera pose pipeline, and viewport configuration. Rendering is handled
@@ -7575,16 +7724,10 @@ declare class ZylemGame<TGlobals extends BaseGlobals> {
7575
7724
  private rendererObserver;
7576
7725
  private eventBusUnsubscribes;
7577
7726
  private thumbnailUnsubscribes;
7578
- /**
7579
- * Trailing-debounce timer for entity-driven state dispatches. Building a
7580
- * `state:dispatch` payload is O(entities) (and downstream editor stores
7581
- * re-proxy/reconcile every entity), so bursts of thumbnail/destroy events
7582
- * must coalesce into a single dispatch instead of one per entity.
7583
- */
7584
- private entityDispatchTimer;
7585
- private pendingEntityDispatchPath;
7586
- /** Debounce window (ms) for coalescing entity-driven state dispatches. */
7587
- private static readonly ENTITY_DISPATCH_DEBOUNCE_MS;
7727
+ /** Serialized last-published game config, for dirty-checking resize churn. */
7728
+ private lastPublishedGameConfig;
7729
+ /** Editor ↔ game bridge adapter (RAF-coalesced publishes, command intake). */
7730
+ private gameBridge;
7588
7731
  private readonly gameUpdateParams;
7589
7732
  private readonly stageSetupParams;
7590
7733
  private readonly stageUpdateParams;
@@ -7638,27 +7781,47 @@ declare class ZylemGame<TGlobals extends BaseGlobals> {
7638
7781
  */
7639
7782
  private buildStageConfigPayload;
7640
7783
  /**
7641
- * Build the entities payload for the current stage.
7784
+ * Build a bridge entity summary for one entity, or null for entities on
7785
+ * the managed render path (instanced packs/bundles), which can number in
7786
+ * the thousands and are not useful in the editor panel.
7787
+ */
7788
+ private buildEntitySummary;
7789
+ /**
7790
+ * Build the full entities payload for the current stage.
7642
7791
  */
7643
7792
  private buildEntitiesPayload;
7644
7793
  /**
7645
- * Generate entity thumbnails as entities become available, then refresh editor state.
7794
+ * Generate entity thumbnails as entities become available, streaming
7795
+ * upserts/thumbnails/removals to the editor through the bridge
7796
+ * (RAF-coalesced, merged by uuid).
7797
+ *
7798
+ * All of this work exists only to feed the editor, and thumbnails in
7799
+ * particular cost an offscreen render plus a pixel readback on the shared
7800
+ * renderer. Each publisher therefore checks the bridge for a live listener
7801
+ * first, and a late-connecting editor is backfilled from
7802
+ * {@link wireEditorBackfill}.
7646
7803
  */
7647
7804
  private wireEntityThumbnails;
7648
- private clearEntityThumbnailWiring;
7649
7805
  /**
7650
- * Coalesce entity-driven state dispatches (thumbnail resolutions, entity
7651
- * destruction) into one trailing `emitStateDispatch` per burst.
7806
+ * Republish the current stage when an editor subscribes after the game
7807
+ * started. Without this, entities added while nothing was listening would
7808
+ * never appear, since the per-entity publishers were skipped.
7652
7809
  */
7653
- private queueEntityStateDispatch;
7810
+ private wireEditorBackfill;
7811
+ private clearEntityThumbnailWiring;
7812
+ private buildGameConfigPayload;
7813
+ /** Publish the resolved display config over the bridge. */
7654
7814
  /**
7655
- * Emit a state:dispatch event to the zylemEventBus.
7656
- * Called after stage load and on global state changes.
7815
+ * Publish the display config, skipping unchanged payloads. The
7816
+ * `ResizeObserver` on `<zylem-game>` funnels into `setDisplayRuntime`, which
7817
+ * fires on every layout tick even when nothing about the config moved.
7657
7818
  */
7658
- private emitStateDispatch;
7819
+ private publishGameConfig;
7820
+ /** Publish a full stage snapshot (config + entity list) over the bridge. */
7821
+ private publishStageSnapshot;
7659
7822
  /**
7660
- * Subscribe to the game event bus for stage loading and state events.
7661
- * Emits events to zylemEventBus for cross-package communication.
7823
+ * Subscribe to the game event bus for stage loading and state events,
7824
+ * mirroring global-variable updates to the editor over the bridge.
7662
7825
  */
7663
7826
  private subscribeToEventBus;
7664
7827
  }