@jgengine/shell 0.18.1 → 0.21.0

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 (261) hide show
  1. package/CHANGELOG.md +504 -7
  2. package/dist/GameHost.d.ts +12 -1
  3. package/dist/GameHost.js +14 -1
  4. package/dist/GamePlayerShell.js +132 -17
  5. package/dist/Shell3dPresentation.d.ts +5 -11
  6. package/dist/Shell3dPresentation.js +73 -11
  7. package/dist/ShellChrome.d.ts +2 -1
  8. package/dist/ShellChrome.js +22 -6
  9. package/dist/ShellHudPresentation.d.ts +20 -2
  10. package/dist/ShellHudPresentation.js +41 -6
  11. package/dist/audio/AudioComponents.js +6 -2
  12. package/dist/audio/audioEngine.d.ts +24 -0
  13. package/dist/audio/audioEngine.js +231 -83
  14. package/dist/audio/audioWire.d.ts +3 -0
  15. package/dist/audio/audioWire.js +9 -3
  16. package/dist/audio/loopParams.d.ts +39 -0
  17. package/dist/audio/loopParams.js +43 -0
  18. package/dist/audio/synthEngine.d.ts +7 -1
  19. package/dist/audio/synthEngine.js +103 -38
  20. package/dist/camera/GameCameraRig.js +3 -2
  21. package/dist/camera/GameFirstPersonCamera.d.ts +6 -4
  22. package/dist/camera/GameFirstPersonCamera.js +68 -27
  23. package/dist/camera/Viewports.d.ts +34 -0
  24. package/dist/camera/Viewports.js +123 -0
  25. package/dist/camera/cameraRigs.js +76 -62
  26. package/dist/camera/chaseHeading.d.ts +9 -0
  27. package/dist/camera/chaseHeading.js +10 -0
  28. package/dist/camera/customMuzzle.d.ts +14 -0
  29. package/dist/camera/customMuzzle.js +68 -0
  30. package/dist/camera/index.d.ts +1 -0
  31. package/dist/camera/index.js +1 -0
  32. package/dist/camera/orbitCameraMath.js +2 -2
  33. package/dist/camera/rigMath.d.ts +82 -4
  34. package/dist/camera/rigMath.js +133 -15
  35. package/dist/camera/rigResolve.d.ts +3 -0
  36. package/dist/camera/rigResolve.js +11 -0
  37. package/dist/combat/ProjectileModels.d.ts +31 -0
  38. package/dist/combat/ProjectileModels.js +70 -0
  39. package/dist/commandSink.d.ts +1 -2
  40. package/dist/commandSink.js +3 -2
  41. package/dist/defineGame.d.ts +19 -3
  42. package/dist/defineGame.js +39 -7
  43. package/dist/devtools/DevtoolsOverlay.d.ts +2 -69
  44. package/dist/devtools/DevtoolsOverlay.js +15 -167
  45. package/dist/devtools/DevtoolsRuntime.d.ts +7 -0
  46. package/dist/devtools/DevtoolsRuntime.js +61 -0
  47. package/dist/devtools/agentBridge.d.ts +6 -0
  48. package/dist/devtools/agentBridge.js +16 -2
  49. package/dist/devtools/devtoolsReports.d.ts +68 -0
  50. package/dist/devtools/devtoolsReports.js +100 -0
  51. package/dist/devtools/latencyInstrumentation.d.ts +3 -0
  52. package/dist/devtools/latencyInstrumentation.js +11 -0
  53. package/dist/devtools/playtestProbe.d.ts +4 -0
  54. package/dist/devtools/playtestProbe.js +22 -0
  55. package/dist/diagnostics/FpsCounter.d.ts +17 -0
  56. package/dist/diagnostics/FpsCounter.js +32 -0
  57. package/dist/drivers/FrameDriver.js +35 -20
  58. package/dist/drivers/FrameRateLimiter.d.ts +9 -0
  59. package/dist/drivers/FrameRateLimiter.js +39 -0
  60. package/dist/drivers/HudOnlyDriver.d.ts +6 -1
  61. package/dist/drivers/HudOnlyDriver.js +32 -14
  62. package/dist/environment/DayNightSky.js +5 -5
  63. package/dist/environment/Daylight.d.ts +35 -1
  64. package/dist/environment/Daylight.js +98 -28
  65. package/dist/environment/EnvironmentScene.js +11 -10
  66. package/dist/environment/VolumetricClouds.js +20 -2
  67. package/dist/environment/daylightCycle.d.ts +20 -0
  68. package/dist/environment/daylightCycle.js +59 -7
  69. package/dist/environment/sunShadowMath.d.ts +45 -0
  70. package/dist/environment/sunShadowMath.js +50 -0
  71. package/dist/input/gamepadPoll.d.ts +47 -0
  72. package/dist/input/gamepadPoll.js +142 -0
  73. package/dist/input/gamepadSource.d.ts +11 -1
  74. package/dist/input/gamepadSource.js +117 -71
  75. package/dist/input/mouseLook.js +2 -1
  76. package/dist/input/padHaptics.d.ts +17 -0
  77. package/dist/input/padHaptics.js +32 -0
  78. package/dist/input/pointerLock.d.ts +11 -0
  79. package/dist/input/pointerLock.js +19 -0
  80. package/dist/inputSink.d.ts +6 -6
  81. package/dist/inputSink.js +60 -21
  82. package/dist/lazyShell3dPresentation.d.ts +9 -0
  83. package/dist/lazyShell3dPresentation.js +24 -0
  84. package/dist/materialOverride.d.ts +42 -0
  85. package/dist/materialOverride.js +95 -36
  86. package/dist/multiplayer.d.ts +33 -3
  87. package/dist/multiplayer.js +105 -22
  88. package/dist/pointer/pointerService.d.ts +2 -0
  89. package/dist/pointer/pointerService.js +12 -2
  90. package/dist/postfx/PostProcessing.d.ts +11 -1
  91. package/dist/postfx/PostProcessing.js +14 -42
  92. package/dist/postfx/gradeShader.d.ts +1 -1
  93. package/dist/postfx/gradeShader.js +30 -2
  94. package/dist/postfx/shadowPrepass.d.ts +3 -0
  95. package/dist/postfx/shadowPrepass.js +18 -0
  96. package/dist/postfx/stylizePass.d.ts +23 -0
  97. package/dist/postfx/stylizePass.js +216 -0
  98. package/dist/presentationRecovery.d.ts +75 -0
  99. package/dist/presentationRecovery.js +204 -0
  100. package/dist/presentationRecovery3d.d.ts +4 -0
  101. package/dist/presentationRecovery3d.js +32 -0
  102. package/dist/presentationResolve.d.ts +8 -1
  103. package/dist/presentationResolve.js +5 -2
  104. package/dist/render/CascadedShadows.d.ts +7 -2
  105. package/dist/render/CascadedShadows.js +98 -20
  106. package/dist/render/EnvironmentLighting.js +74 -35
  107. package/dist/render/HairCards.d.ts +7 -0
  108. package/dist/render/HairCards.js +23 -0
  109. package/dist/render/SceneModels.js +52 -78
  110. package/dist/render/SkinnedInstances.d.ts +91 -0
  111. package/dist/render/SkinnedInstances.js +249 -0
  112. package/dist/render/TextureFiltering.d.ts +16 -0
  113. package/dist/render/TextureFiltering.js +64 -0
  114. package/dist/render/authoredSurfaceMaterial.d.ts +43 -0
  115. package/dist/render/authoredSurfaceMaterial.js +108 -0
  116. package/dist/render/displayColor.d.ts +33 -0
  117. package/dist/render/displayColor.js +138 -0
  118. package/dist/render/materialAppearanceSignals.d.ts +23 -0
  119. package/dist/render/materialAppearanceSignals.js +58 -0
  120. package/dist/render/materialAsset.d.ts +98 -0
  121. package/dist/render/materialAsset.js +454 -0
  122. package/dist/render/measureBounds.js +16 -3
  123. package/dist/render/measureCollisionMesh.js +8 -1
  124. package/dist/render/modelAssets.d.ts +8 -0
  125. package/dist/render/modelAssets.js +43 -0
  126. package/dist/render/modelBindPose.d.ts +5 -0
  127. package/dist/render/modelBindPose.js +35 -0
  128. package/dist/render/modelLoad.d.ts +32 -0
  129. package/dist/render/modelLoad.js +70 -1
  130. package/dist/render/modelNodeVisibility.d.ts +6 -0
  131. package/dist/render/modelNodeVisibility.js +18 -0
  132. package/dist/render/modelRender.d.ts +18 -1
  133. package/dist/render/modelRender.js +81 -3
  134. package/dist/render/projectedOverdraw.d.ts +35 -0
  135. package/dist/render/projectedOverdraw.js +204 -0
  136. package/dist/render/resolveModel.d.ts +12 -2
  137. package/dist/render/resolveModel.js +25 -4
  138. package/dist/render/rigNode.d.ts +26 -0
  139. package/dist/render/rigNode.js +32 -0
  140. package/dist/render/spriteRender.d.ts +4 -0
  141. package/dist/render/spriteRender.js +5 -0
  142. package/dist/render/staticShapeInstances.d.ts +34 -0
  143. package/dist/render/staticShapeInstances.js +78 -0
  144. package/dist/render/useFootIk.d.ts +53 -4
  145. package/dist/render/useFootIk.js +262 -70
  146. package/dist/render/useModelAnimation.d.ts +50 -0
  147. package/dist/render/useModelAnimation.js +245 -99
  148. package/dist/render/useModelInstance.d.ts +25 -0
  149. package/dist/render/useModelInstance.js +43 -0
  150. package/dist/scatter/scatterModels.d.ts +3 -2
  151. package/dist/scatter/scatterModels.js +15 -7
  152. package/dist/scene/AuthoredScene.d.ts +23 -6
  153. package/dist/scene/AuthoredScene.js +46 -9
  154. package/dist/scene/CityRenderer.js +6 -6
  155. package/dist/scene/GeneratedAssetRenderer.js +11 -42
  156. package/dist/settings/QuickControls.js +1 -1
  157. package/dist/settings/SettingsMenu.js +11 -18
  158. package/dist/settings/SettingsRuntime.d.ts +5 -0
  159. package/dist/settings/SettingsRuntime.js +29 -2
  160. package/dist/settings/appliedSettings.d.ts +9 -3
  161. package/dist/settings/appliedSettings.js +6 -5
  162. package/dist/settings/settingsController.d.ts +3 -0
  163. package/dist/settings/settingsController.js +47 -3
  164. package/dist/shellControlSuspension.d.ts +18 -0
  165. package/dist/shellControlSuspension.js +28 -0
  166. package/dist/shellInputPublication.d.ts +28 -0
  167. package/dist/shellInputPublication.js +49 -0
  168. package/dist/shellPointerInput.d.ts +18 -0
  169. package/dist/shellPointerInput.js +49 -0
  170. package/dist/structures/BuildingKitBatch.d.ts +3 -1
  171. package/dist/structures/BuildingKitBatch.js +35 -56
  172. package/dist/structures/BuildingRenderBudget.d.ts +23 -0
  173. package/dist/structures/BuildingRenderBudget.js +54 -0
  174. package/dist/structures/GeneratedBuilding.d.ts +3 -15
  175. package/dist/structures/GeneratedBuilding.js +33 -22
  176. package/dist/structures/buildingKitMaterials.d.ts +20 -0
  177. package/dist/structures/buildingKitMaterials.js +70 -0
  178. package/dist/structures/buildingSpatialBatch.d.ts +11 -0
  179. package/dist/structures/buildingSpatialBatch.js +45 -0
  180. package/dist/structures/buildingSurface.d.ts +21 -0
  181. package/dist/structures/buildingSurface.js +134 -0
  182. package/dist/terrain/GrassField.d.ts +2 -1
  183. package/dist/terrain/GrassField.js +59 -5
  184. package/dist/terrain/grassGeometry.d.ts +10 -0
  185. package/dist/terrain/grassGeometry.js +55 -0
  186. package/dist/terrain/grassMaterial.js +2 -2
  187. package/dist/terrain/terrainDetailMaterial.d.ts +3 -3
  188. package/dist/terrain/terrainDetailMaterial.js +7 -7
  189. package/dist/touch/TouchControlsOverlay.js +4 -4
  190. package/dist/useShellMultiplayerSync.d.ts +1 -1
  191. package/dist/useShellMultiplayerSync.js +9 -3
  192. package/dist/vfx/ParticleField.d.ts +23 -20
  193. package/dist/vfx/ParticleField.js +168 -50
  194. package/dist/visibility/CullingProvider.js +2 -62
  195. package/dist/visibility/cullingDriver.d.ts +9 -0
  196. package/dist/visibility/cullingDriver.js +68 -0
  197. package/dist/weather/AuthoredWeatherLayer.d.ts +16 -0
  198. package/dist/weather/AuthoredWeatherLayer.js +31 -0
  199. package/dist/weather/DustField.d.ts +2 -1
  200. package/dist/weather/DustField.js +17 -3
  201. package/dist/weather/FireSpreadLayer.d.ts +4 -2
  202. package/dist/weather/FireSpreadLayer.js +5 -11
  203. package/dist/weather/LightningStrike.d.ts +4 -1
  204. package/dist/weather/LightningStrike.js +13 -17
  205. package/dist/weather/RainField.d.ts +2 -1
  206. package/dist/weather/RainField.js +17 -3
  207. package/dist/weather/RainImpactField.d.ts +16 -0
  208. package/dist/weather/RainImpactField.js +89 -0
  209. package/dist/weather/SnowField.d.ts +2 -1
  210. package/dist/weather/SnowField.js +17 -3
  211. package/dist/weather/WeatherLayer.d.ts +11 -2
  212. package/dist/weather/WeatherLayer.js +30 -6
  213. package/dist/weather/index.d.ts +2 -0
  214. package/dist/weather/index.js +2 -0
  215. package/dist/weather/weatherMath.d.ts +2 -0
  216. package/dist/weather/weatherMath.js +8 -0
  217. package/dist/weather/weatherShelter.d.ts +18 -0
  218. package/dist/weather/weatherShelter.js +75 -0
  219. package/dist/weather/weatherUniforms.d.ts +40 -1
  220. package/dist/weather/weatherUniforms.js +29 -9
  221. package/dist/world/AuthoredFlocks.d.ts +22 -0
  222. package/dist/world/AuthoredFlocks.js +87 -0
  223. package/dist/world/StaticObjectModels.d.ts +8 -0
  224. package/dist/world/StaticObjectModels.js +75 -0
  225. package/dist/world/WorldHud.d.ts +14 -6
  226. package/dist/world/WorldHud.js +25 -12
  227. package/dist/world/WorldParticles.d.ts +10 -23
  228. package/dist/world/WorldParticles.js +210 -62
  229. package/dist/world/WorldScene.d.ts +3 -1
  230. package/dist/world/WorldScene.js +41 -26
  231. package/dist/world/staticObjectBatches.d.ts +35 -0
  232. package/dist/world/staticObjectBatches.js +163 -0
  233. package/dist/world/worldBarSamples.d.ts +37 -33
  234. package/dist/world/worldBarSamples.js +117 -75
  235. package/package.json +4 -4
  236. package/skills/ce-handoff/SKILL.md +2 -2
  237. package/skills/game-design/SKILL.md +2 -2
  238. package/skills/jgengine/SKILL.md +28 -6
  239. package/skills/jgengine/api.md +139 -99
  240. package/skills/jgengine/capabilities.md +28 -0
  241. package/skills/jgengine/recipes/minimal-game.md +10 -2
  242. package/skills/jgengine-ui/SKILL.md +16 -17
  243. package/skills/jgengine-ui/api.md +603 -365
  244. package/skills/jgengine-ui/capabilities.md +131 -1
  245. package/skills/jgengine-ui/reference-primitives.md +179 -0
  246. package/skills/jgengine-ui/reference.md +4 -81
  247. package/skills/jgengine-world/SKILL.md +25 -4
  248. package/skills/jgengine-world/api.md +1045 -655
  249. package/skills/jgengine-world/capabilities.md +246 -9
  250. package/skills/jgengine-world/recipes/character-animation.md +62 -0
  251. package/skills/jgengine-world/recipes/controls.md +48 -0
  252. package/skills/jgengine-world/recipes/flight-feel.md +76 -0
  253. package/skills/jgengine-world/recipes/npc-senses.md +55 -0
  254. package/skills/jgengine-world/recipes/vehicle-feel.md +87 -0
  255. package/skills/jgengine-world/reference.md +180 -4
  256. package/skills/jgengine-world/visibility.md +139 -0
  257. package/skills/level-design/SKILL.md +1 -1
  258. package/dist/render/testFixtures/proceduralSpriteAtlas.d.ts +0 -15
  259. package/dist/render/testFixtures/proceduralSpriteAtlas.js +0 -30
  260. package/dist/render/testFixtures/proceduralTileLayer.d.ts +0 -13
  261. package/dist/render/testFixtures/proceduralTileLayer.js +0 -30
@@ -41,10 +41,13 @@
41
41
 
42
42
  - `CINEMATIC_POST_PROCESSING` (const): const CINEMATIC_POST_PROCESSING: PostProcessingConfig — Tuned tone-map + bloom + gentle SSAO + vignette/grade stack — the shipped-game post look.
43
43
  - `CINEMATIC_SKY` (const): const CINEMATIC_SKY: SkyEnvironmentConfig — A static day sky: a real dome plus the shell's view-following shadow-casting sun and hemisphere fill.
44
+ - `COMIC_POST_PROCESSING` (const): const COMIC_POST_PROCESSING: PostProcessingConfig — Ink outlines, four cel bands and a punchy grade — a hand-inked comic look on any model.
44
45
  - `GameLookInput` (interface): interface GameLookInput — Explicit graphics knobs a game may set; the preset only fills the ones left undefined.
45
- - `LookPreset` (type): type LookPreset = "neutral" | "photoreal" | "toon" | "cinematic" | "flat" — Named look preset composing the existing lighting/sky/fog/post knobs into one field. `"neutral"` (the default when unset) keeps presentation unstyled; `"cinematic"` draws a scene lit like a shipped game — a real day sky with a view-following shadow-casting sun + hemisphere fill, a network-free image-based-lighting environment so PBR surfaces catch soft reflections, and a tuned tone-map/bloom/AO/vignette post stack. `"flat"` opts out of the sky/IBL/post rig to the bare ambient+directional default (pre-#773). The upgraded default primitive materials — tuned roughness/metalness plus subtle procedural surface detail so un-modeled boxes/capsules stop reading as flat plastic — apply under both presets.
46
+ - `LOOK_PRESETS` (const): const LOOK_PRESETS: readonly LookPreset[] — Every look preset name, for pickers and validating a preset read from a URL or file.
47
+ - `LookPreset` (type): type LookPreset = "neutral" | "photoreal" | "toon" | "comic" | "retro" | "cinematic" | "flat" — Named look preset composing the existing lighting/sky/fog/post knobs into one field. `"neutral"` (the default when unset) keeps presentation unstyled; `"cinematic"` draws a scene lit like a shipped game — a real day sky with a view-following shadow-casting sun + hemisphere fill, a network-free image-based-lighting environment so PBR surfaces catch soft reflections, and a tuned tone-map/bloom/AO/vignette post stack. `"comic"` adds ink outlines and cel bands over a saturated grade (Borderlands-style); `"retro"` pixelates and posterizes the frame. `"flat"` opts out of the sky/IBL/post rig to the bare ambient+directional default (pre-#773). The upgraded default primitive materials — tuned roughness/metalness plus subtle procedural surface detail so un-modeled boxes/capsules stop reading as flat plastic — apply under both presets.
46
48
  - `NEUTRAL_POST_PROCESSING` (const): const NEUTRAL_POST_PROCESSING: PostProcessingConfig — Unstyled baseline: a stable linear presentation with no genre-specific grading.
47
49
  - `PHOTOREAL_POST_PROCESSING` (const): const PHOTOREAL_POST_PROCESSING: PostProcessingConfig — High-contrast physically based presentation for authored HDR environments.
50
+ - `RETRO_POST_PROCESSING` (const): const RETRO_POST_PROCESSING: PostProcessingConfig — Chunky pixels and posterized colour with no smoothing — a low-resolution retro look.
48
51
  - `ResolvedGameLook` (interface): interface ResolvedGameLook — Concrete lighting/backdrop/post the shell renders after the preset has filled unset knobs.
49
52
  - `TOON_POST_PROCESSING` (const): const TOON_POST_PROCESSING: PostProcessingConfig — Clean graphic presentation; toon materials remain game-owned while this sets the grade.
50
53
  - `resolveGameLook` (function): function resolveGameLook(input: GameLookInput): ResolvedGameLook — Expand a game's `look` into concrete lighting/backdrop/post. The default is `"neutral"`; `"flat"` passes the explicit knobs through untouched. Anything the game authored wins — the preset only fills unset knobs, and it never adds a sky when the world already owns one (so the sky's tuned sun/hemisphere serve as the lighting rig).
@@ -55,9 +58,11 @@
55
58
  - `BloomConfig` (interface): interface BloomConfig — UnrealBloom stage — soft HDR glow around bright pixels (sun, glints, emissive).
56
59
  - `DofConfig` (interface): interface DofConfig — Depth-of-field (bokeh) stage — throws the fore/background out of focus around a focus distance.
57
60
  - `GradeConfig` (interface): interface GradeConfig — Final colour-grade stage: lift/gain/gamma, saturation, vignette, film grain — applied in display space after tone mapping.
61
+ - `OutlineConfig` (interface): interface OutlineConfig — Ink outlines traced from depth and surface-normal edges — silhouettes plus interior creases.
58
62
  - `PostAaMode` (type): type PostAaMode = "smaa" | "msaa" | false — Edge antialiasing for the post chain. SMAA runs in linear-sRGB before the output tone-map and cleans alpha-tested foliage/particles that MSAA samples alone leave crawling. `"msaa"` keeps only the render-target multisample resolve; `false` disables AA stages.
59
63
  - `PostProcessingConfig` (interface): interface PostProcessingConfig — Declarative post-processing chain (RenderPass → AO → Bloom → SMAA → tone-map output → Grade). Present on a game means the shell mounts an `EffectComposer` and owns the render; absent means the renderer draws directly (unchanged). Each stage is a config object, `false` to skip, or omitted for its default. Pure data — no three.js types leak into core.
60
64
  - `STUDIO_STAGE_POST` (const): const STUDIO_STAGE_POST: PostProcessingConfig — A cinematic "product shot" post preset — the full chain on (contact-AO, soft bloom, a warm film grade with vignette + a touch of grain + chromatic aberration). Meant for a `StudioStage` where a single parametric asset is framed on a backdrop, so every studio reads shipped, not intern-tier. DoF is left off by default (it needs a per-scene focus distance); set `dof` to enable it.
65
+ - `StylizeConfig` (interface): interface StylizeConfig — Art-style stage that turns the same models and lighting into a different look without touching materials: ink outlines (comic / Borderlands), cel bands (cartoon), pixelation (retro). Runs after tone mapping, before the grade.
61
66
  - `ToneMappingMode` (type): type ToneMappingMode = "aces" | "agx" | "reinhard" | "cineon" | "linear" | "none" — Renderer tone-mapping curve applied by the post chain's output stage.
62
67
 
63
68
  ## @jgengine/core/render/sprite2d
@@ -67,12 +72,35 @@
67
72
  - `createSpriteClipPlayer` (function): function createSpriteClipPlayer(atlas: SpriteAtlas): { play(name: string): void; advance(dt: number): void; frame: () => { x: number; y: number; w: number; h: number; pivot?: [number, number] | undefined; } | undefined; snapshot: () => { animation: string; frameIndex: number; elapsed: number; done: … — Create a renderer-independent sprite animation player.
68
73
  - `sortingOrder` (function): function sortingOrder(layers: readonly string[], layer: string, offset = 0): number — Resolve a layer name and local offset into a stable render order.
69
74
 
75
+ ## @jgengine/core/settings/frameRateLimit
76
+
77
+ - `FRAME_RATE_LIMIT_DISPLAY` (const): const FRAME_RATE_LIMIT_DISPLAY: "display" — Stored value for "render every display refresh" — the browser's vsync-locked rate.
78
+ - `FRAME_RATE_LIMIT_OPTIONS` (const): const FRAME_RATE_LIMIT_OPTIONS: readonly SettingOption[] — Frame-rate limit choices. Browsers always present on the display's vsync, so the uncapped option runs at the monitor's refresh rate; the numeric caps skip refreshes to save GPU and battery.
79
+ - `paceFrame` (function): function paceFrame(now: number, anchor: number, fps: number): number | null — Frame pacing for a capped render loop driven by display refreshes. Returns the new frame anchor when a frame should render at `now`, or `null` to skip this refresh. The anchor keeps the remainder so a 60 cap on a 144 Hz display averages 60 instead of drifting to 48.
80
+ - `readFrameRateLimit` (function): function readFrameRateLimit(store: Pick<SettingsStore, "get">): number — The player's frame-rate cap in frames per second, or 0 for the display rate.
81
+
70
82
  ## @jgengine/core/settings/graphicsProfile
71
83
 
72
84
  - `DEFAULT_GRAPHICS_PROFILES` (const): const DEFAULT_GRAPHICS_PROFILES: Record<GraphicsQuality, GraphicsProfile> — Conservative defaults that preserve the existing high-quality shell behavior.
73
85
  - `GraphicsProfile` (interface): interface GraphicsProfile — Serializable renderer budget selected by the player's graphics tier.
74
86
  - `resolveGraphicsProfile` (function): function resolveGraphicsProfile(quality: GraphicsQuality, overrides?: Partial<GraphicsProfile>): GraphicsProfile — Resolve one tier and apply game-authored field and post-stage overrides.
75
87
 
88
+ ## @jgengine/core/settings/graphicsSettings
89
+
90
+ - `ANISOTROPY_OPTIONS` (const): const ANISOTROPY_OPTIONS: readonly { value: string; label: string }[] — Texture-filtering choices offered to the player; the value is the anisotropy sample count.
91
+ - `GRAPHICS_POST_STAGES` (const): const GRAPHICS_POST_STAGES: readonly GraphicsPostStage[] — Ordered stage list used to build settings rows.
92
+ - `GRAPHICS_POST_STAGE_LABELS` (const): const GRAPHICS_POST_STAGE_LABELS: Record<GraphicsPostStage, string> — Player-facing labels for each post stage.
93
+ - `GraphicsPostStage` (type): type GraphicsPostStage = keyof GraphicsProfile["postStages"] — The post-processing stages a player can toggle individually on top of the quality tier.
94
+ - `GraphicsProfileOverrides` (type): type GraphicsProfileOverrides = Partial<Record<GraphicsQuality, Partial<GraphicsProfile>>> — Per-tier profile overrides a game authors with `defineGame({ graphics })`.
95
+ - `GraphicsSettingsState` (interface): interface GraphicsSettingsState — The resolved player graphics choice: the tier plus the profile after per-stage and render-scale overrides.
96
+ - `RENDER_SCALE_MAX` (const): const RENDER_SCALE_MAX: 2 — Highest player render scale.
97
+ - `RENDER_SCALE_MIN` (const): const RENDER_SCALE_MIN: 0.5 — Lowest player render scale; the value is the device-pixel-ratio cap handed to the canvas.
98
+ - `RENDER_SCALE_STEP` (const): const RENDER_SCALE_STEP: 0.05 — Render-scale slider increment.
99
+ - `applyGraphicsQuality` (function): function applyGraphicsQuality(store: Pick<SettingsStore, "set">, quality: GraphicsQuality, overrides?: GraphicsProfileOverrides): GraphicsProfile — Select a quality tier and re-apply its render scale, texture filtering and stage defaults, so picking a preset resets the individual overrides instead of leaving stale toggles behind.
100
+ - `graphicsPostStageSettingId` (function): function graphicsPostStageSettingId(stage: GraphicsPostStage): string — Setting id that stores one stage toggle (`graphics.post.<stage>`).
101
+ - `readGraphicsQuality` (function): function readGraphicsQuality(store: Pick<SettingsStore, "get">): GraphicsQuality — The stored quality tier, falling back to the default when the value is missing or unknown.
102
+ - `readGraphicsSettings` (function): function readGraphicsSettings(store: Pick<SettingsStore, "get">, overrides?: GraphicsProfileOverrides): GraphicsSettingsState — Resolve the player's graphics state from a settings store. The tier profile (with the game's overrides) supplies every default; a stored render scale or stage toggle replaces its field.
103
+
76
104
  ## @jgengine/core/settings/settingsModel
77
105
 
78
106
  - `BUILT_IN_SETTING_CATEGORIES` (const): const BUILT_IN_SETTING_CATEGORIES: readonly BuiltInSettingCategory[] — ⚠ undocumented
@@ -84,17 +112,17 @@
84
112
  - `GRAPHICS_QUALITY_DPR` (const): const GRAPHICS_QUALITY_DPR: Record<GraphicsQuality, number> — Device-pixel-ratio ceiling per quality tier — the shell's `Canvas` dpr cap.
85
113
  - `GRAPHICS_QUALITY_OPTIONS` (const): const GRAPHICS_QUALITY_OPTIONS: readonly SettingOption[] — ⚠ undocumented
86
114
  - `GameSettingDef` (interface): interface GameSettingDef — Extra setting a game appends to a built-in category via `defineGame({ settings: { extra } })`.
87
- - `GameSettingsConfig` (interface): interface GameSettingsConfig — ⚠ undocumented
88
- - `GraphicsQuality` (type): type GraphicsQuality = "low" | "medium" | "high" — ⚠ undocumented
115
+ - `GameSettingsConfig` (interface): interface GameSettingsConfig { variant?: SettingsVariant; surface?: SettingsSurface | false; extra?: readonly GameSettingDef[]; categories?: readonly SettingCategoryDef[]; hide?: readonly SettingCategory[]; actions?: readonly SettingsActionDef[]; hideBindings?: readonl… — ⚠ undocumented
116
+ - `GraphicsQuality` (type): type GraphicsQuality = "low" | "medium" | "high" | "ultra" — ⚠ undocumented · used by `GRAPHICS_QUALITY_DPR`: Device-pixel-ratio ceiling per quality tier — the shell's `Canvas` dpr cap.
89
117
  - `SETTINGS_STORAGE_PREFIX` (const): const SETTINGS_STORAGE_PREFIX: "jgengine:setting:" — ⚠ undocumented
90
- - `SETTING_IDS` (const): const SETTING_IDS: { readonly masterVolume: "sound.master"; readonly graphicsQuality: "graphics.quality"; readonly graphicsShadows: "graphics.shadows"; readonly graphicsUiScale: "graphics.uiScale"; readonly touchStyle: "controls.touchStyle"; readonly touchJoystick: "controls.touchJoystick"; } — ⚠ undocumented
118
+ - `SETTING_IDS` (const): const SETTING_IDS: { readonly masterVolume: "sound.master"; readonly graphicsQuality: "graphics.quality"; readonly graphicsShadows: "graphics.shadows"; readonly graphicsUiScale: "graphics.uiScale"; readonly graphicsRenderScale: "graphics.renderScale"; readonly graphicsPostAo: "graphics.post.ao"; rea… — ⚠ undocumented
91
119
  - `SettingCategory` (type): type SettingCategory = BuiltInSettingCategory | (string & {}) — Built-in category ids keep autocomplete; any other string makes a fresh category.
92
120
  - `SettingCategoryDef` (interface): interface SettingCategoryDef — Declares or relabels/reorders a category tab; use it for a custom category or to reshape the built-ins.
93
121
  - `SettingKind` (type): type SettingKind = "slider" | "toggle" | "select" — ⚠ undocumented
94
- - `SettingOption` (interface): interface SettingOption — ⚠ undocumented
95
- - `SettingValue` (type): type SettingValue = number | boolean | string — ⚠ undocumented
122
+ - `SettingOption` (interface): interface SettingOption { value: string; label: string } — ⚠ undocumented · used by `FRAME_RATE_LIMIT_OPTIONS` (@jgengine/core/settings/frameRateLimit): Frame-rate limit choices.
123
+ - `SettingValue` (type): type SettingValue = number | boolean | string — ⚠ undocumented · used by `useSetting` (@jgengine/react): Read + write one persisted setting; re-renders when the value changes anywhere.
96
124
  - `SettingsActionDef` (interface): interface SettingsActionDef — A game-state action (Restart, Quit to menu, …) shown as rows in the first "Game" settings tab — never a floating button or a rebindable key.
97
- - `SettingsStore` (interface): interface SettingsStore — ⚠ undocumented
125
+ - `SettingsStore` (interface): interface SettingsStore { get<T extends SettingValue>(id: string, fallback: T): T; set(id: string, value: SettingValue): void; subscribe(listener: () => void): () => void } — ⚠ undocumented · used by `createSettingsStore`: Reactive, localStorage-backed settings store shared by the shell wiring and React hooks.
98
126
  - `SettingsSurface` (type): type SettingsSurface = "quick" — `quick` shows compact on-screen volume/graphics buttons; `false` (default) mounts no engine trigger — open the menu from your own UI with `<SettingsTrigger>` or `useSettings().open()`.
99
127
  - `SettingsVariant` (type): type SettingsVariant = "panel" | "sheet" | "sidebar" | "fullscreen" — The four themed settings layouts, chosen with `defineGame({ settings: { variant } })`. All read the game's `--jg-*` theme tokens.
100
128
  - `UI_SCALE_MAX` (const): const UI_SCALE_MAX: 1.5 — ⚠ undocumented
@@ -138,19 +166,19 @@
138
166
  - `GRAPHICS_QUALITY_OPTIONS` (const): const GRAPHICS_QUALITY_OPTIONS: readonly SettingOption[] — ⚠ undocumented
139
167
  - `GameLayoutMode` (type): type GameLayoutMode = "desktop-wide" | "desktop-compact" | "mobile-landscape" | "mobile-portrait" — The explicit composition mode a game renders for — not a scaled desktop layout.
140
168
  - `GameSettingDef` (interface): interface GameSettingDef — Extra setting a game appends to a built-in category via `defineGame({ settings: { extra } })`.
141
- - `GameSettingsConfig` (interface): interface GameSettingsConfig — ⚠ undocumented
169
+ - `GameSettingsConfig` (interface): interface GameSettingsConfig { variant?: SettingsVariant; surface?: SettingsSurface | false; extra?: readonly GameSettingDef[]; categories?: readonly SettingCategoryDef[]; hide?: readonly SettingCategory[]; actions?: readonly SettingsActionDef[]; hideBindings?: readonl… — ⚠ undocumented
142
170
  - `GameViewportLayout` (interface): interface GameViewportLayout — The shared live geometry the engine allocates once and every UI subsystem reads.
143
171
  - `GradeConfig` (interface): interface GradeConfig — Final colour-grade stage: lift/gain/gamma, saturation, vignette, film grain — applied in display space after tone mapping.
144
- - `GraphicsQuality` (type): type GraphicsQuality = "low" | "medium" | "high" — ⚠ undocumented
172
+ - `GraphicsQuality` (type): type GraphicsQuality = "low" | "medium" | "high" | "ultra" — ⚠ undocumented · used by `GRAPHICS_QUALITY_DPR`: Device-pixel-ratio ceiling per quality tier — the shell's `Canvas` dpr cap.
145
173
  - `GridFocusOptions` (interface): interface GridFocusOptions — Options for {@link moveGridFocus}.
146
174
  - `HUD_ANCHOR_FRACTIONS` (const): const HUD_ANCHOR_FRACTIONS: Record<HudAnchor, { fx: number; fy: number }> — ⚠ undocumented
147
- - `HudAnchor` (type): type HudAnchor = | "top-left" | "top" | "top-right" | "left" | "center" | "right" | "bottom-left" | "bottom" | "bottom-right" — ⚠ undocumented
148
- - `HudLayoutStore` (interface): interface HudLayoutStore — ⚠ undocumented
149
- - `HudPlacement` (interface): interface HudPlacement — ⚠ undocumented
175
+ - `HudAnchor` (type): type HudAnchor = | "top-left" | "top" | "top-right" | "left" | "center" | "right" | "bottom-left" | "bottom" | "bottom-right" — ⚠ undocumented · used by `HudPanel` (@jgengine/shell/gameKit): A HUD block that lives in one of the nine anchor regions.
176
+ - `HudLayoutStore` (interface): interface HudLayoutStore { getState(): HudLayoutState; subscribe(listener: (state: HudLayoutState) => void): () => void; register(id: string, placement: HudPlacement, options?: {width?: number; height?: number; visible?: boolean; type?: string}): void; move(id: str… — ⚠ undocumented · used by `HudCanvas` (@jgengine/shell/gameKit): Full-viewport HUD surface.
177
+ - `HudPlacement` (interface): interface HudPlacement { anchor: HudAnchor; dx: number; dy: number } — ⚠ undocumented
150
178
  - `HudPlatform` (type): type HudPlatform = "web" | "mobile" — Where a game is meant to be played. `"web"` alone keeps today's desktop-first HUD; adding `"mobile"` turns on design-resolution fit scaling on compact displays.
151
179
  - `HudPriority` (type): type HudPriority = "critical" | "secondary" | "tertiary" — Gameplay-importance tier of a HUD element.
152
180
  - `HudResizeAxes` (type): type HudResizeAxes = "none" | "x" | "y" | "both" — Which axes a panel type may grow when resized in canvas mode. Resize is semantic — content reflows (longer track, more rows) — never a CSS scale of the whole panel.
153
- - `HudSize` (interface): interface HudSize — ⚠ undocumented
181
+ - `HudSize` (interface): interface HudSize { width: number; height: number } — ⚠ undocumented · used by `hudScaleForViewport`: The one scaling rule for every display: the ratio of the live viewport to the authored design size along the limiting axis, clamped.
154
182
  - `HudViewportConfig` (interface): interface HudViewportConfig extends HudFitConfig — Per-game HUD viewport declaration carried on `PlayableGame.hudFit`; `mobile` overrides the fit on compact displays so the owner can tune the phone layout separately.
155
183
  - `I18n` (interface): interface I18n — Runtime translator over a message {@link Catalog} — active locale, lookup with fallback, interpolation, and pluralization.
156
184
  - `I18nOptions` (interface): interface I18nOptions — Configuration for {@link createI18n}.
@@ -161,7 +189,7 @@
161
189
  - `LayoutRect` (interface): interface LayoutRect — Axis-aligned rectangle in CSS pixels (origin top-left). Structurally compatible with a `DOMRect`'s edge fields.
162
190
  - `LayoutRegion` (interface): interface LayoutRegion — A physical rectangle a UI subsystem occupies, published to the shared registry.
163
191
  - `Locale` (type): type Locale = string — A locale tag (e.g. `"en"`, `"es"`, `"ja"`) — a key into a {@link Catalog}.
164
- - `LookPreset` (type): type LookPreset = "neutral" | "photoreal" | "toon" | "cinematic" | "flat" — Named look preset composing the existing lighting/sky/fog/post knobs into one field. `"neutral"` (the default when unset) keeps presentation unstyled; `"cinematic"` draws a scene lit like a shipped game — a real day sky with a view-following shadow-casting sun + hemisphere fill, a network-free image-based-lighting environment so PBR surfaces catch soft reflections, and a tuned tone-map/bloom/AO/vignette post stack. `"flat"` opts out of the sky/IBL/post rig to the bare ambient+directional default (pre-#773). The upgraded default primitive materials — tuned roughness/metalness plus subtle procedural surface detail so un-modeled boxes/capsules stop reading as flat plastic — apply under both presets.
192
+ - `LookPreset` (type): type LookPreset = "neutral" | "photoreal" | "toon" | "comic" | "retro" | "cinematic" | "flat" — Named look preset composing the existing lighting/sky/fog/post knobs into one field. `"neutral"` (the default when unset) keeps presentation unstyled; `"cinematic"` draws a scene lit like a shipped game — a real day sky with a view-following shadow-casting sun + hemisphere fill, a network-free image-based-lighting environment so PBR surfaces catch soft reflections, and a tuned tone-map/bloom/AO/vignette post stack. `"comic"` adds ink outlines and cel bands over a saturated grade (Borderlands-style); `"retro"` pixelates and posterizes the frame. `"flat"` opts out of the sky/IBL/post rig to the bare ambient+directional default (pre-#773). The upgraded default primitive materials — tuned roughness/metalness plus subtle procedural surface detail so un-modeled boxes/capsules stop reading as flat plastic — apply under both presets.
165
193
  - `MODAL_CANCEL` (const): const MODAL_CANCEL: "cancel" — Conventional cancel/dismiss result (also the default auto-dismiss and pop result).
166
194
  - `MODAL_CONFIRM` (const): const MODAL_CONFIRM: "confirm" — Conventional confirm result for a two-button dialog — a shared string, not model behavior.
167
195
  - `Messages` (type): type Messages = Readonly<Record<string, string>> — Flat message table for one locale: key → template string with `{param}` slots.
@@ -192,7 +220,7 @@
192
220
  - `RadialSlice` (interface): interface RadialSlice — One wedge of a radial menu — geometry for rendering a slice. Angles are radians from "up" (−Y), clockwise.
193
221
  - `RadialVectorOptions` (interface): interface RadialVectorOptions extends RadialArc — Options for {@link radialIndexFromVector}.
194
222
  - `ResolvedAction` (interface): interface ResolvedAction — A resolved action view model — availability computed once from cooldown, cost, `disabled`, and any caller reasons. `enabled` is the single truth a renderer gates interaction on; `reasons` is the ordered explanation (cooldown, then unmet costs, then caller reasons, then a generic disable).
195
- - `SETTING_IDS` (const): const SETTING_IDS: { readonly masterVolume: "sound.master"; readonly graphicsQuality: "graphics.quality"; readonly graphicsShadows: "graphics.shadows"; readonly graphicsUiScale: "graphics.uiScale"; readonly touchStyle: "controls.touchStyle"; readonly touchJoystick: "controls.touchJoystick"; } — ⚠ undocumented
223
+ - `SETTING_IDS` (const): const SETTING_IDS: { readonly masterVolume: "sound.master"; readonly graphicsQuality: "graphics.quality"; readonly graphicsShadows: "graphics.shadows"; readonly graphicsUiScale: "graphics.uiScale"; readonly graphicsRenderScale: "graphics.renderScale"; readonly graphicsPostAo: "graphics.post.ao"; rea… — ⚠ undocumented
196
224
  - `STUDIO_STAGE_POST` (const): const STUDIO_STAGE_POST: PostProcessingConfig — A cinematic "product shot" post preset — the full chain on (contact-AO, soft bloom, a warm film grade with vignette + a touch of grain + chromatic aberration). Meant for a `StudioStage` where a single parametric asset is framed on a backdrop, so every studio reads shipped, not intern-tier. DoF is left off by default (it needs a per-scene focus distance); set `dof` to enable it.
197
225
  - `ScreenMarkerLayout` (interface): interface ScreenMarkerLayout — Where and how to draw one marker, from {@link layoutScreenMarker}.
198
226
  - `ScreenMarkerOptions` (interface): interface ScreenMarkerOptions — Options for {@link layoutScreenMarker}.
@@ -205,10 +233,10 @@
205
233
  - `SettingCategory` (type): type SettingCategory = BuiltInSettingCategory | (string & {}) — Built-in category ids keep autocomplete; any other string makes a fresh category.
206
234
  - `SettingCategoryDef` (interface): interface SettingCategoryDef — Declares or relabels/reorders a category tab; use it for a custom category or to reshape the built-ins.
207
235
  - `SettingKind` (type): type SettingKind = "slider" | "toggle" | "select" — ⚠ undocumented
208
- - `SettingOption` (interface): interface SettingOption — ⚠ undocumented
209
- - `SettingValue` (type): type SettingValue = number | boolean | string — ⚠ undocumented
236
+ - `SettingOption` (interface): interface SettingOption { value: string; label: string } — ⚠ undocumented · used by `FRAME_RATE_LIMIT_OPTIONS` (@jgengine/core/settings/frameRateLimit): Frame-rate limit choices.
237
+ - `SettingValue` (type): type SettingValue = number | boolean | string — ⚠ undocumented · used by `useSetting` (@jgengine/react): Read + write one persisted setting; re-renders when the value changes anywhere.
210
238
  - `SettingsActionDef` (interface): interface SettingsActionDef — A game-state action (Restart, Quit to menu, …) shown as rows in the first "Game" settings tab — never a floating button or a rebindable key.
211
- - `SettingsStore` (interface): interface SettingsStore — ⚠ undocumented
239
+ - `SettingsStore` (interface): interface SettingsStore { get<T extends SettingValue>(id: string, fallback: T): T; set(id: string, value: SettingValue): void; subscribe(listener: () => void): () => void } — ⚠ undocumented · used by `createSettingsStore`: Reactive, localStorage-backed settings store shared by the shell wiring and React hooks.
212
240
  - `SettingsSurface` (type): type SettingsSurface = "quick" — `quick` shows compact on-screen volume/graphics buttons; `false` (default) mounts no engine trigger — open the menu from your own UI with `<SettingsTrigger>` or `useSettings().open()`.
213
241
  - `SettingsVariant` (type): type SettingsVariant = "panel" | "sheet" | "sidebar" | "fullscreen" — The four themed settings layouts, chosen with `defineGame({ settings: { variant } })`. All read the game's `--jg-*` theme tokens.
214
242
  - `StoredObjectiveBanner` (interface): interface StoredObjectiveBanner — A persisted banner record — all announcement fields resolved, plus id and schedule.
@@ -365,14 +393,14 @@
365
393
  ## @jgengine/core/ui/hudLayout
366
394
 
367
395
  - `HUD_ANCHOR_FRACTIONS` (const): const HUD_ANCHOR_FRACTIONS: Record<HudAnchor, { fx: number; fy: number }> — ⚠ undocumented
368
- - `HudAnchor` (type): type HudAnchor = | "top-left" | "top" | "top-right" | "left" | "center" | "right" | "bottom-left" | "bottom" | "bottom-right" — ⚠ undocumented
369
- - `HudLayoutOptions` (interface): interface HudLayoutOptions — ⚠ undocumented
370
- - `HudLayoutState` (interface): interface HudLayoutState — ⚠ undocumented
371
- - `HudLayoutStore` (interface): interface HudLayoutStore — ⚠ undocumented
372
- - `HudPanelState` (interface): interface HudPanelState — ⚠ undocumented
373
- - `HudPlacement` (interface): interface HudPlacement — ⚠ undocumented
374
- - `HudRect` (interface): interface HudRect — ⚠ undocumented
375
- - `HudSize` (interface): interface HudSize — ⚠ undocumented
396
+ - `HudAnchor` (type): type HudAnchor = | "top-left" | "top" | "top-right" | "left" | "center" | "right" | "bottom-left" | "bottom" | "bottom-right" — ⚠ undocumented · used by `HudPanel` (@jgengine/shell/gameKit): A HUD block that lives in one of the nine anchor regions.
397
+ - `HudLayoutOptions` (interface): interface HudLayoutOptions { snap?: number; locked?: boolean; onDocumentPatch?: (id: string, panel: import("./hudDocument").EditorUiPanelLayout) => void } — ⚠ undocumented
398
+ - `HudLayoutState` (interface): interface HudLayoutState { panels: Record<string, HudPanelState>; locked: boolean; editing: boolean } — ⚠ undocumented
399
+ - `HudLayoutStore` (interface): interface HudLayoutStore { getState(): HudLayoutState; subscribe(listener: (state: HudLayoutState) => void): () => void; register(id: string, placement: HudPlacement, options?: {width?: number; height?: number; visible?: boolean; type?: string}): void; move(id: str… — ⚠ undocumented · used by `HudCanvas` (@jgengine/shell/gameKit): Full-viewport HUD surface.
400
+ - `HudPanelState` (interface): interface HudPanelState { id: string; placement: HudPlacement; z: number; moved: boolean; width?: number; height?: number; visible: boolean; type?: string } — ⚠ undocumented
401
+ - `HudPlacement` (interface): interface HudPlacement { anchor: HudAnchor; dx: number; dy: number } — ⚠ undocumented
402
+ - `HudRect` (interface): interface HudRect { x: number; y: number; width: number; height: number } — ⚠ undocumented · used by `overflowingPanels` (@jgengine/core/ui): Every panel rect that escapes the viewport — the data behind the HUD overflow gate.
403
+ - `HudSize` (interface): interface HudSize { width: number; height: number } — ⚠ undocumented · used by `hudScaleForViewport` (@jgengine/core/ui): The one scaling rule for every display: the ratio of the live viewport to the authored design size along the limiting axis, clamped.
376
404
 
377
405
  ## @jgengine/core/ui/hudScale
378
406
 
@@ -380,7 +408,7 @@
380
408
  - `DEFAULT_HUD_MAX_SCALE` (const): const DEFAULT_HUD_MAX_SCALE: 1 — ⚠ undocumented
381
409
  - `DEFAULT_HUD_MIN_SCALE` (const): const DEFAULT_HUD_MIN_SCALE: 0.4 — ⚠ undocumented
382
410
  - `HudFitConfig` (interface): interface HudFitConfig — Design-resolution fit for the HUD surface. The game authors its UI against `designSize`; on smaller viewports the whole HUD scales down by the limiting axis ratio, clamped to `[minScale, maxScale]`, so panels keep their layout and simply shrink instead of overflowing.
383
- - `HudOverflow` (interface): interface HudOverflow — ⚠ undocumented
411
+ - `HudOverflow` (interface): interface HudOverflow { id: string; left: number; top: number; right: number; bottom: number } — ⚠ undocumented · used by `overflowingPanels`: Every panel rect that escapes the viewport — the data behind the HUD overflow gate.
384
412
  - `HudPlatform` (type): type HudPlatform = "web" | "mobile" — Where a game is meant to be played. `"web"` alone keeps today's desktop-first HUD; adding `"mobile"` turns on design-resolution fit scaling on compact displays.
385
413
  - `HudViewportConfig` (interface): interface HudViewportConfig extends HudFitConfig — Per-game HUD viewport declaration carried on `PlayableGame.hudFit`; `mobile` overrides the fit on compact displays so the owner can tune the phone layout separately.
386
414
  - `hudScaleForViewport` (function): function hudScaleForViewport(fit: Required<HudFitConfig>, viewport: HudSize): number — The one scaling rule for every display: the ratio of the live viewport to the authored design size along the limiting axis, clamped. 1 on a viewport at or above design size; smoothly below 1 down to `minScale` on phones.
@@ -504,7 +532,7 @@
504
532
 
505
533
  ## @jgengine/react
506
534
 
507
- - `AbilitySlotBindingOptions` (interface): interface AbilitySlotBindingOptions — ⚠ undocumented
535
+ - `AbilitySlotBindingOptions` (interface): interface AbilitySlotBindingOptions { intervalMs?: number } — ⚠ undocumented · used by `useEventMeter`: Bind a `createEventMeter` (`@jgengine/core/stats/eventMeter`) heat/streak gauge to a component — the react-render half of the ult/adrenaline…
508
536
  - `AccessibilityProvider` (function): function AccessibilityProvider({ store, children, className, style, }: { store: AccessibilityStore; children: ReactNode; className?: string; style?: CSSProperties; }): ReactNode — Apply accessibility preferences to a subtree: exposes `--jg-text-scale` for text to scale off, sets `data-reduced-motion` / `data-high-contrast` / `data-colorblind` / `data-captions` for CSS to respond to, and wraps the tree in the selected colorblind `feColorMatrix` filter. Reads a `createAccessibilityStore` and re-renders on change; provides the state to `useAccessibility`.
509
537
  - `AchievementGallery` (function): function AchievementGallery({ achievements, title = "Achievements", maskSecrets = true, renderIcon, columns = 2, emptyLabel = "No achievements yet.", className, style, }: AchievementGalleryProps): ReactNode — Achievement/trophy gallery — a responsive grid of cards showing unlocked vs. locked state, a progress bar for counter achievements, and a header summary of completion and score. Secret+locked entries mask their name/description. Feed it `useAchievements(tracker)`.
510
538
  - `AchievementGalleryProps` (interface): interface AchievementGalleryProps — Props for {@link AchievementGallery}.
@@ -524,10 +552,13 @@
524
552
  - `AtomicBarProps` (interface): interface AtomicBarProps — Shared props for every atomic bar — bound to a stat (`statId`/`entityId`) or explicit (`value`).
525
553
  - `BarShape` (type): type BarShape = "rect" | "pill" | "skew" | "chamfer" — Trough shape language — `rect` (default), `pill` (fully round), `skew` (parallelogram, upright label), or `chamfer` (cut corners). Matches a game's skin without hand-rolling a local bar.
526
554
  - `BarTokens` (interface): interface BarTokens — Atomic, purpose-named vitals — one component per readout (`HealthBar`, `ShieldBar`, …), never a `tone`-switched umbrella and never a bundled combo. Every bar reads the same shared CSS custom properties (`--jg-health`, `--jg-shield`, `--jg-xp`, plus frame/shape tokens). Default token colors are scaffolding only: games own layout, framing, and art direction, and must set tokens (or replace the markup) so the HUD is unique to the pitch — the engine ships parts, not a face.
527
- - `BarsPreview` (function): function BarsPreview({ className }: { className?: string }): React.JSX.Element — Renders the atomic bar matrix twice under different token blocks to prove global re-theming.
528
- - `BetterAuthSessionState` (interface): interface BetterAuthSessionState — ⚠ undocumented
529
- - `BetterAuthUserShape` (interface): interface BetterAuthUserShape — ⚠ undocumented
555
+ - `BarsPreview` (function): function BarsPreview({ className }: { className?: string }): React.JSX.Element — Renders atomic bars under token blocks and nested themes to prove fill and skin precedence.
556
+ - `BetterAuthSessionState` (interface): interface BetterAuthSessionState { data: {user: BetterAuthUserShape} | null | undefined; isPending: boolean } — ⚠ undocumented
557
+ - `BetterAuthUserShape` (interface): interface BetterAuthUserShape { id: string; name?: string | null; email?: string | null; image?: string | null } — ⚠ undocumented
530
558
  - `BossBar` (function): function BossBar(props: AtomicBarProps & { name?: string }): React.JSX.Element — A wide encounter/boss health bar with the boss name at the start.
559
+ - `BrowserSuspensionOptions` (interface): interface BrowserSuspensionOptions — Select suspension events; all are observed by default. No callback runs on attachment.
560
+ - `BrowserSuspensionReason` (type): type BrowserSuspensionReason = "blur" | "hidden" | "pointer-lock-lost" — Browser events that can interrupt active play. The caller decides whether to pause.
561
+ - `BrowserSuspensionSources` (interface): interface BrowserSuspensionSources — Event sources for a browser window, an iframe, or a test.
531
562
  - `CameraShakeMeter` (function): function CameraShakeMeter({ controller, animate = false, title = "CAMERA SHAKE", kindLabels, variation = "glass", className, style, }: CameraShakeMeterProps): ReactNode — A drop-in HUD panel that visualizes a core {@link CameraShakeController}: a trauma meter (reusing the shared {@link StaminaBar} building block) plus a live percentage and the current impact `kind` label. Presentation only — it reads the controller and never interprets `kind`, mapping it through `kindLabels` for display. A shell component applies the same controller's `offset()` to the camera; this panel is the on-screen readout of how much shake is in flight.
532
563
  - `CameraShakeMeterProps` (interface): interface CameraShakeMeterProps — Props for {@link CameraShakeMeter}.
533
564
  - `CameraShakeReadout` (interface): interface CameraShakeReadout — A live read of a camera-shake controller: current trauma `0..1` and the last game-owned `kind`.
@@ -537,13 +568,13 @@
537
568
  - `ChannelTabs` (function): function ChannelTabs({ channels, active, onSelect, className, tabClassName, activeTabClassName, renderTab, }: { channels?: readonly string[]; active: string; onSelect: (channelId: string) => void; className?: string; tabClassName?: string; activeTabClassName?: string; renderTab?: (channelId: string,… — ⚠ undocumented
538
569
  - `CharacterSheet` (function): function CharacterSheet({ slots, stats, name, subtitle, portrait, header, title = "Character", variation = "themed", slotSize, onSlotActivate, className, style, }: CharacterSheetProps): ReactNode — Drop-in character sheet: a {@link HudFrame} wrapping an optional name/subtitle/portrait header, the equip-slot {@link Paperdoll}, and the derived-stat {@link StatList}. The game supplies slots, stats, header content, and the activation callback, and owns the window that toggles it (this component does not manage its own open/close). Pure composition over existing primitives and HudTheme tokens.
539
570
  - `CharacterSheetProps` (interface): interface CharacterSheetProps — Props for {@link CharacterSheet}.
540
- - `ChatBubble` (interface): interface ChatBubble — ⚠ undocumented
541
- - `ChatBubblesOptions` (interface): interface ChatBubblesOptions — ⚠ undocumented
571
+ - `ChatBubble` (interface): interface ChatBubble { id: string; fromUserId: string; body: string; at: number } — ⚠ undocumented
572
+ - `ChatBubblesOptions` (interface): interface ChatBubblesOptions { channelId?: string; ttlMs?: number; limit?: number } — ⚠ undocumented
542
573
  - `ChatInput` (function): function ChatInput({ channelId, className, inputClassName, buttonClassName, placeholder, sendLabel, onSent, onRejected, }: { channelId: string; className?: string; inputClassName?: string; buttonClassName?: string; placeholder?: string; sendLabel?: ReactNode; onSent?: (message: ChatMessage) => void;… — ⚠ undocumented
543
574
  - `ChatLog` (function): function ChatLog({ channelId, limit, className, messageClassName, ownMessageClassName, renderMessage, }: { channelId: string; limit?: number; className?: string; messageClassName?: string; ownMessageClassName?: string; renderMessage?: (message: ChatMessage) => ReactNode; }): React.JSX.Element — ⚠ undocumented
544
575
  - `ChatPanel` (function): function ChatPanel<T extends ChatMessage = ChatMessage>(props: Parameters<typeof ContextChatPanel>[0] | StandaloneChatPanelProps<T>): React.JSX.Element — Chat behavior over a game context or externally supplied server messages.
545
- - `ClerkUserShape` (interface): interface ClerkUserShape — ⚠ undocumented
546
- - `ClerkUserState` (interface): interface ClerkUserState — ⚠ undocumented
576
+ - `ClerkUserShape` (interface): interface ClerkUserShape { id: string; fullName?: string | null; username?: string | null; imageUrl?: string | null; primaryEmailAddress?: {emailAddress: string} | null; createdAt?: Date | null; lastSignInAt?: Date | null } — ⚠ undocumented
577
+ - `ClerkUserState` (interface): interface ClerkUserState { isLoaded: boolean; isSignedIn: boolean | undefined; user: ClerkUserShape | null | undefined } — ⚠ undocumented
547
578
  - `Clock` (function): function Clock({ format = "24h", showDay = true, controls = false, style, className, }: { format?: "24h" | "12h"; showDay?: boolean; controls?: boolean; style?: CSSProperties; className?: string; }): React.JSX.Element — A time-of-day clock reading the sim calendar — `Day N · HH:MM`, 24h or 12h. `controls` adds pause + the game's speed multipliers as clickable pills (the "fast-forward" bar), off by default so a game opts into letting the player scrub time.
548
579
  - `CoachMark` (function): function CoachMark({ view, positionStyle, onNext, onSkip, nextLabel = "Next", doneLabel = "Got it", skipLabel = "Skip tour", theme, className, style, }: CoachMarkProps): ReactNode — A single coach-mark callout — title, body, an "N of M" counter, a Next/Got-it button, and a Skip-tour link. Presentation-only; position it with `positionStyle` or drop it into {@link CoachMarkHost}. Reskin via {@link CoachMarkTheme}.
549
580
  - `CoachMarkHost` (function): function CoachMarkHost({ sequence, resolveAnchor, showBackdrop = true, onComplete, nextLabel, doneLabel, skipLabel, theme, className, style, }: CoachMarkHostProps): ReactNode — Full-screen coach-mark host: watches a sequence, positions the current step's callout beside its anchored element (or centered when unanchored) via `getBoundingClientRect`, and draws an optional dimmed backdrop with a spotlight cutout around the anchor. Next advances, Skip ends the tour — both persist so hints never re-show. Reskin with {@link CoachMarkTheme}.
@@ -558,7 +589,7 @@
558
589
  - `ComboMeterHudProps` (interface): interface ComboMeterHudProps — Props for {@link ComboMeterHud}.
559
590
  - `ComboMeterTheme` (interface): interface ComboMeterTheme — Reskin tokens for {@link ComboMeterHud}.
560
591
  - `Compass` (function): function Compass({ facingYaw, center, markers, width = 340, fov = (Math.PI * 2) / 3, kindStyles = DEFAULT_MARKER_KINDS, className, }: CompassProps): ReactNode — Horizontal compass strip centered on the player's facing direction, with the eight cardinals and optional marker pips from static views, an external source, or a native `MarkerSet`.
561
- - `CompassProps` (interface): interface CompassProps — ⚠ undocumented
592
+ - `CompassProps` (interface): interface CompassProps { facingYaw: number; center?: WorldXZ; markers?: MarkerCollection; width?: number; fov?: number; kindStyles?: Record<string, MarkerKindStyle>; className?: string } — ⚠ undocumented · used by `Compass`: Horizontal compass strip centered on the player's facing direction, with the eight cardinals and optional marker pips from static views, an …
562
593
  - `ConfirmDialog` (function): function ConfirmDialog({ title, body, confirmLabel = "Confirm", cancelLabel = "Cancel", onConfirm, onCancel, danger = false, width = 360, className, style, }: ConfirmDialogProps): ReactNode — A generic two-button confirmation dialog — title, optional body, and Cancel / Confirm buttons over a `HudTheme`-token-driven {@link HudFrame}. Presentation-only: wire `onConfirm`/`onCancel` to a modal stack's `resolve` (typically inside a {@link ModalHost} render callback) and reskin via `HudTheme` tokens or `danger` for a destructive action. It interprets nothing — the game supplies the copy.
563
594
  - `ConfirmDialogProps` (interface): interface ConfirmDialogProps — Props for {@link ConfirmDialog}.
564
595
  - `ControlHint` (interface): interface ControlHint — One row of a control legend. Name the game action(s) whose bound key(s) to show (`action`) so the glyphs come straight from the keybind map — never re-typed — or give literal `keys` for controls that live outside the map (`"Mouse"`, `"LMB"`). `label` says what the control does.
@@ -576,22 +607,23 @@
576
607
  - `DebounceTimer` (interface): interface DebounceTimer — Injectable timer seam so the controller is testable with a manual clock.
577
608
  - `DebouncedCommit` (interface): interface DebouncedCommit<T> — Live-mirrored, trailing-debounced commit binding for a single control value.
578
609
  - `DefaultEquipSlotId` (type): type DefaultEquipSlotId = | "head" | "chest" | "hands" | "legs" | "feet" | "mainHand" | "offHand" | "ring1" | "ring2" | "trinket" — Slot ids used by {@link defaultEquipLayout}'s convenience arrangement.
610
+ - `DialogBehaviorOptions` (interface): interface DialogBehaviorOptions — Options for controlled dialog focus behavior; callers own markup, state, and close policy.
579
611
  - `DialogueBox` (function): function DialogueBox({ dialogue, onChoice, rng, className, lineClassName, speakerClassName, choicesClassName, choiceClassName, checkClassName, }: { dialogue: DialogueDef; onChoice?: (choice: DialogueChoice, result: CheckResult | null) => void; rng?: () => number; className?: string; lineClassName?: … — ⚠ undocumented
580
- - `DialogueCheck` (interface): interface DialogueCheck — ⚠ undocumented
581
- - `DialogueChoice` (interface): interface DialogueChoice — ⚠ undocumented
582
- - `DialogueDef` (interface): interface DialogueDef — ⚠ undocumented
612
+ - `DialogueCheck` (interface): interface DialogueCheck { label?: string; modifier: number; dc: number; advantage?: CheckAdvantage } — ⚠ undocumented
613
+ - `DialogueChoice` (interface): interface DialogueChoice { label: string; invoke: {command: string; args?: unknown} | null; check?: DialogueCheck; onSuccess?: {command: string; args?: unknown} | null; onFailure?: {command: string; args?: unknown} | null } — ⚠ undocumented · used by `runDialogueChoice`: Route a {@link DialogueBox} choice through the `features.dialogue` bridge: resolve the choice's invoke (honoring a skill-check `result`), ru…
614
+ - `DialogueDef` (interface): interface DialogueDef { id: string; lines: readonly DialogueLine[] } — ⚠ undocumented
583
615
  - `DialogueLine` (type): type DialogueLine = { speaker: string; text: string } | { choices: readonly DialogueChoice[] } — ⚠ undocumented
584
616
  - `DialogueView` (function): function DialogueView({ run, renderPortrait, onChoose, onClose, closeLabel = "Close", theme, className, style, speakerClassName, textClassName, choicesClassName, choiceClassName, }: DialogueViewProps): ReactNode — A drop-in branching-conversation view over a {@link DialogueRun}: renders the current node's speaker name (+ an optional portrait slot), the line, and a list of clickable response choices that advance the conversation — the game passes its dialogue graph and gets working node traversal and choice state with no hand-rolled walk. A choice with a destination advances the run; a terminal choice (or the Close control on a choiceless node) fires `onClose`. Speaker/choice `kind` are free strings surfaced as `data-*` attributes for the game to style; the view never interprets them. HudTheme-skinned via `--jg-*` tokens; reskin further with {@link DialogueViewTheme}.
585
617
  - `DialogueViewProps` (interface): interface DialogueViewProps — Props for {@link DialogueView}.
586
618
  - `DialogueViewTheme` (interface): interface DialogueViewTheme — Reskin tokens for {@link DialogueView}. Each defaults to a HudTheme `--jg-*` token.
587
- - `DisplayProfile` (interface): interface DisplayProfile — ⚠ undocumented
619
+ - `DisplayProfile` (interface): interface DisplayProfile { coarsePointer: boolean; compact: boolean; portrait: boolean } — ⚠ undocumented
588
620
  - `DomEventTarget` (interface): interface DomEventTarget — Minimal add/removeEventListener surface accepted by useDomEvent (window, document, elements).
589
621
  - `DragGhost` (function): function DragGhost<T>({ layer, className, style, children, }: { layer: DragLayer<T>; className?: string; style?: CSSProperties; children?: (payload: DragPayload<T>) => ReactNode; }): React.JSX.Element | null — ⚠ undocumented
590
- - `DragLayer` (interface): interface DragLayer<T> — ⚠ undocumented
591
- - `DragPayload` (interface): interface DragPayload<T> — ⚠ undocumented
592
- - `DragState` (interface): interface DragState<T> — ⚠ undocumented
622
+ - `DragLayer` (interface): interface DragLayer<T> { state: DragState<T>; dragging: boolean; pointRef: RefObject<{x: number; y: number}>; beginDrag(payload: {id: string; value: T; rotation?: Rotation}, event: ReactPointerEvent): void; rotate(quarterTurns?: number): void; setTarget(target: s… — ⚠ undocumented · used by `useDragLayer`: Pointer-owned drag state shared by cards and inventory grids.
623
+ - `DragPayload` (interface): interface DragPayload<T> { id: string; value: T; rotation: Rotation } — ⚠ undocumented
624
+ - `DragState` (interface): interface DragState<T> { payload: DragPayload<T> | null; point: {x: number; y: number}; origin: {x: number; y: number}; overTarget: string | null; overCell: Cell | null } — ⚠ undocumented
593
625
  - `DraggableCard` (function): function DraggableCard<T>({ id, value, layer, className, children, onRotate, }: { id: string; value: T; layer: DragLayer<T>; className?: string; children?: ReactNode; onRotate?: boolean; }): React.JSX.Element — ⚠ undocumented
594
- - `DropInfo` (interface): interface DropInfo<T> — ⚠ undocumented
626
+ - `DropInfo` (interface): interface DropInfo<T> { payload: DragPayload<T>; target: string | null; cell: Cell | null; point: {x: number; y: number} } — ⚠ undocumented · used by `useDragLayer`: Pointer-owned drag state shared by cards and inventory grids.
595
627
  - `DropZone` (function): function DropZone<T>({ id, layer, className, activeClassName, cellSize, children, }: { id: string; layer: DragLayer<T>; className?: string; activeClassName?: string; cellSize?: number; children?: ReactNode; }): React.JSX.Element — ⚠ undocumented
596
628
  - `EmoteWheel` (function): function EmoteWheel({ emotes, radius, open = true, className, emoteClassName, onPlayed, onRejected, renderEmote, }: { emotes: readonly string[]; radius?: number; open?: boolean; className?: string; emoteClassName?: string; onPlayed?: (emoteId: string) => void; onRejected?: (reason: string) => void; … — ⚠ undocumented
597
629
  - `EntityFrameEntry` (interface): interface EntityFrameEntry — The minimum an entry must carry; games extend it with their own display data.
@@ -607,7 +639,7 @@
607
639
  - `EquipSlotPosition` (type): type EquipSlotPosition = "left" | "right" | "bottom" — Character-sheet / paperdoll composition (#1033 follow-up): a drop-in `<CharacterSheet>` that arranges caller-supplied equip slots as a paperdoll around a portrait and lists derived stats, all inside a {@link HudFrame}. It reuses the atomic slot rendering ({@link EquipmentSlots}) and the shared `--jg-slot-*` / `--jg-accent` HudTheme tokens (#1034) — the game owns which slots and which stats matter (both are props), the skin, and the window that toggles it open/closed. No genre list is baked in; {@link defaultEquipLayout} is a convenience arrangement the game can override wholesale.
608
640
  - `EquipmentSlots` (function): function EquipmentSlots(props: SlotGridProps): React.JSX.Element — Worn-gear slots (head/chest/hands/…): one atomic grid, painted icons, themed from tokens.
609
641
  - `EventMeterView` (interface): interface EventMeterView — A rendered snapshot of an {@link EventMeter}: current value, fill fraction, active tier, and ready-to-consume flag.
610
- - `EventfulEngineStore` (interface): interface EventfulEngineStore<TEventMap extends object> — ⚠ undocumented
642
+ - `EventfulEngineStore` (interface): interface EventfulEngineStore<TEventMap extends object> { on<K extends keyof TEventMap>(eventName: K, listener: (payload: TEventMap[K]) => void): () => void } — ⚠ undocumented
611
643
  - `ExperienceBar` (const): const ExperienceBar: (props: AtomicBarProps) => React.JSX.Element — Level progress / experience.
612
644
  - `FastTravelMenu` (function): function FastTravelMenu<TMeta = unknown>({ network, from, onTravel, currentId, title = "Fast Travel", emptyLabel = "No destinations discovered yet.", onClose, className, style, }: FastTravelMenuProps<TMeta>): ReactNode — Fast-travel menu — the discovered-destinations list players pick from: points grouped by region, distance-sorted from `from`, each showing icon, name, and distance, with a header counting discovered vs. total. Selecting a row calls `onTravel`; the current location is flagged and not travelable. Bind it to a `createFastTravelNetwork`.
613
645
  - `FastTravelMenuProps` (interface): interface FastTravelMenuProps<TMeta = unknown> — Props for {@link FastTravelMenu}.
@@ -632,7 +664,7 @@
632
664
  - `Hotbar` (function): function Hotbar({ inventoryId, activeSlot, keys, slotSize = 46, itemIcon, style, className, }: { inventoryId: string; activeSlot?: number; keys?: readonly string[]; slotSize?: number; /** Caller-supplied item id → icon registry; return null/undefined to fall back to the default glyph. */ itemIcon?: … — A numbered hotbar bound to an inventory — painted iconed slots (a `GameIcon` glyph over a school-keyed gradient with a count badge, #1035), an active-slot highlight, and a keycap per slot. Place it and pass the `inventoryId`; `activeSlot` highlights the equipped one. Supply `itemIcon` to map your item ids to your own glyphs — the default resolves a `GameIcon` from the item id.
633
665
  - `HudCanvas` (function): function HudCanvas({ layout, editChord, compactScale, showDuring, className, style, children, }: { layout: HudLayoutStore; editChord?: HudEditChord | false; /** Zoom applied to the whole HUD on compact displays. Default 0.85. */ compactScale?: number; /** Opt-in play-phase gate: render the HUD only … — Full-viewport HUD surface. Panels declared with `HudPanel` flow into nine anchor regions and stack automatically with a gap — no per-panel pixel offsets, no manual clearance for sibling panels, the touch-control dock (`--jg-hud-dock-clearance`), or device safe areas. On compact displays the whole surface scales down and each panel applies its `compact` behavior.
634
666
  - `HudCompactMode` (type): type HudCompactMode = "keep" | "chip" | "hide" — How a panel behaves on compact (phone-scale) displays. `keep` stays visible at the global compact scale, `chip` collapses to a small tap-to-expand pill, `hide` unmounts entirely.
635
- - `HudEditChord` (interface): interface HudEditChord — ⚠ undocumented
667
+ - `HudEditChord` (interface): interface HudEditChord { hold: string; press: string } — ⚠ undocumented · used by `HudCanvas`: Full-viewport HUD surface.
636
668
  - `HudFrame` (function): function HudFrame({ variation = "glass", shape = "rounded", title, aside, padding, width, interactive, className, style, children, }: HudFrameProps): ReactNode — Optional framed panel primitive — a single `<div data-hud-frame>` with a `glass`, `plate`, `retro`, or `themed` skin, optional `title`/`aside` header, and caller `style` merged last. Shared building block for demos and token-themed widgets; games own product chrome and art direction (see AGENTS.md).
637
669
  - `HudFrameProps` (interface): interface HudFrameProps — Props for {@link HudFrame}.
638
670
  - `HudFrameShape` (type): type HudFrameShape = "rounded" | "circle" | "square" — Frame corner shape — `rounded` (the variation's default radius), `circle` (fully round), or `square` (no radius).
@@ -656,14 +688,14 @@
656
688
  - `HudThemeSlot` (interface): interface HudThemeSlot — Action/inventory slot tokens.
657
689
  - `HudThemeStatus` (interface): interface HudThemeStatus — Semantic status colors — outcome, threat, and relationship coloring.
658
690
  - `HudThemeSurface` (interface): interface HudThemeSurface — Chrome surfaces, edges, and text — what panels, overlays, and menus are built out of.
659
- - `HudViewportContextValue` (interface): interface HudViewportContextValue — ⚠ undocumented
691
+ - `HudViewportContextValue` (interface): interface HudViewportContextValue { fitEnabled: boolean; config: HudViewportConfig | undefined; userScale: number } — ⚠ undocumented
660
692
  - `HudViewportProvider` (function): function HudViewportProvider({ platforms, config, userScale, children, }: { platforms: readonly HudPlatform[] | undefined; config: HudViewportConfig | undefined; userScale?: number; children?: ReactNode; }): React.JSX.Element — Mounted by the shell around `GameUI` so every `HudCanvas` inside the game picks up the game's `platforms`/`hudFit` declaration and the player's UI scale setting without any game-side wiring.
661
693
  - `I18nProvider` (function): function I18nProvider({ i18n, children }: { i18n: I18n; children: ReactNode }): ReactNode — Provide an {@link I18n} instance to the tree so `useT`/`useLocale` can read it.
662
694
  - `IconSchool` (type): type IconSchool = | "fire" | "frost" | "arcane" | "nature" | "holy" | "tech" | "shadow" | "steel" | "neutral" — `IconTreatment` (#1035): a procedural painted-icon face — a glyph over a school-keyed radial gradient, with a vignette inset shadow, a top gloss highlight, and optional count/keycap badges. Turns the engine's flat white `GameIcon` glyphs (and game-icons.net SVGs) into painted ability / item icons that read as AAA slots, not debug UI. Theme-aware: the frame reads the `--jg-slot-*` and `--jg-accent` `HudTheme` tokens (#1034), so a theme change re-skins every treated icon.
663
695
  - `IconTreatment` (function): function IconTreatment({ icon, glyph, school = "neutral", size = 44, count, keycap, active = false, className, style, }: IconTreatmentProps): React.JSX.Element — A painted icon face — gradient + vignette + gloss behind a glyph, with count/keycap badges.
664
696
  - `IconTreatmentProps` (interface): interface IconTreatmentProps — Props for {@link IconTreatment}.
665
697
  - `IconsPreview` (function): function IconsPreview({ className }: { className?: string }): React.JSX.Element — Renders the school-gradient row and a treated-icon hotbar — the deterministic #1035 fixture.
666
- - `IdentitySource` (interface): interface IdentitySource — ⚠ undocumented
698
+ - `IdentitySource` (interface): interface IdentitySource { session: AuthSession | null; isLoading: boolean; signOut?: () => void } — ⚠ undocumented
667
699
  - `InteractionPrompt` (function): function InteractionPrompt({ registry, playerPosition, theme, accentFor, keyFor, gaugeProgress, anchor = "bottom-center", zIndex = 40, className, style, }: InteractionPromptProps): ReactNode — A screen-anchored "Press E to …" interaction callout that renders the active prompt of a {@link PromptRegistry}. It subscribes to the registry and — when given `playerPosition` — re-resolves as the hero moves, showing the nearest in-range interactable and switching to a closer/higher-priority one as they cross radii. Each display kind renders as itself: a `keybind` shows a key cap plus its label ("Press [E] Open"), a `gauge` shows a hold bar filling with `gaugeProgress`, and a `label` shows plain text. Presentation only — the registry never branches on a prompt's meaning; the game colors each via `accentFor` and maps action ids to key glyphs via `keyFor`. HudTheme-skinnable through `--jg-accent` and {@link InteractionPromptTheme}.
668
700
  - `InteractionPromptAnchor` (type): type InteractionPromptAnchor = "bottom-center" | "top-center" | "center" — Where the callout sits over the scene.
669
701
  - `InteractionPromptProps` (interface): interface InteractionPromptProps — Props for {@link InteractionPrompt}.
@@ -687,7 +719,7 @@
687
719
  - `LeavePartyButton` (function): function LeavePartyButton({ className, children, }: { className?: string; children?: ReactNode; }): React.JSX.Element | null — ⚠ undocumented
688
720
  - `LevelUpFlash` (function): function LevelUpFlash({ stat, durationMs = 1600, className, children, renderFlash, }: { stat?: string; durationMs?: number; className?: string; children?: ReactNode; renderFlash?: (event: StatLevelUpEvent) => ReactNode; }): React.JSX.Element | null — ⚠ undocumented
689
721
  - `ManaBar` (const): const ManaBar: (props: AtomicBarProps) => React.JSX.Element — Spell/energy resource pool.
690
- - `MapBounds` (interface): interface MapBounds — ⚠ undocumented
722
+ - `MapBounds` (interface): interface MapBounds { minX: number; minZ: number; maxX: number; maxZ: number } — ⚠ undocumented
691
723
  - `MapLegend` (function): function MapLegend({ kinds, labels, kindStyles = DEFAULT_MARKER_KINDS, title = "Legend", className, style, }: MapLegendProps): ReactNode — Marker-kind key for a map/minimap — glyph + color swatch per kind, labelled. Reads the same `kindStyles` the map renders with, so the legend can never drift from the pins.
692
724
  - `MapLegendProps` (interface): interface MapLegendProps — Props for {@link MapLegend}.
693
725
  - `MapViewport` (interface): interface MapViewport — Pan/zoom transform applied to a {@link WorldMapSurface}'s content group.
@@ -699,7 +731,7 @@
699
731
  - `MinimapChromeProps` (interface): interface MinimapChromeProps — Props for `MinimapChrome`.
700
732
  - `MinimapPanel` (function): function MinimapPanel({ zoneLabel, clock, showCompass = true, compassProps, headerClassName, zoneLabelClassName, clockClassName, compassClassName, children, ...minimapProps }: MinimapPanelProps): ReactNode — Composed circular-minimap chrome: optional zone-label + clock header above the `Minimap`, optional `Compass` strip below. Purely a wiring layer over the existing primitives — the game supplies the zone name and clock text, this only places them; omit either slot and the header disappears.
701
733
  - `MinimapPanelProps` (interface): interface MinimapPanelProps extends MinimapProps — Props for `MinimapPanel` — all `MinimapProps` plus the zone-label/clock header and compass slots.
702
- - `MinimapProps` (interface): interface MinimapProps — ⚠ undocumented
734
+ - `MinimapProps` (interface): interface MinimapProps { markers: MarkerCollection; center: WorldXZ; worldRadius: number; fog?: FogField; size?: number; facingYaw?: number; rotate?: boolean; kindStyles?: Record<string, MarkerKindStyle>; background?: string; mapBounds?: MapBounds; routes?: reado… — ⚠ undocumented · used by `Minimap`: Framed circular minimap: optional baked terrain background, reveal-on-event fog overlay, categorized marker icons, and a facing arrow.
703
735
  - `MinimapTrack` (function): function MinimapTrack({ spans = [], pips = [], width = "100%", height = 12, railColor = "rgba(255,255,255,0.10)", className, style, children, }: MinimapTrackProps): ReactNode — Horizontal linear track minimap — a rounded progress rail with colored zone `spans` and gate/exit/player `pips` positioned by 0..1 fraction. The structural counterpart to the radial {@link Minimap} for corridor/route games: fractions are supplied by the caller (via core `trackFraction`), so it reads no store and stays presentation-only, sharing chrome with the radial minimap.
704
736
  - `MinimapTrackPip` (interface): interface MinimapTrackPip — A point marker on the {@link MinimapTrack} rail at a 0..1 fraction (e.g. a gate, the exit, or the player).
705
737
  - `MinimapTrackProps` (interface): interface MinimapTrackProps — Props for {@link MinimapTrack}.
@@ -750,7 +782,7 @@
750
782
  - `RadialMenuOption` (interface): interface RadialMenuOption — One selectable entry on a radial/quick menu (weapon or emote wheel).
751
783
  - `RadialMenuProps` (interface): interface RadialMenuProps — Props for {@link RadialMenu}.
752
784
  - `Rank` (type): type Rank = "A" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" | "10" | "J" | "Q" | "K" — A playing-card rank, ace through king.
753
- - `ReadableEngineStore` (interface): interface ReadableEngineStore<TState> — ⚠ undocumented
785
+ - `ReadableEngineStore` (interface): interface ReadableEngineStore<TState> { getState(): TState; subscribe(listener: (state: TState) => void): () => void } — ⚠ undocumented
754
786
  - `RegionRecord` (interface): interface RegionRecord extends LayoutRegion — A region registration: the full `LayoutRegion` plus the live element (dev outlining), rect measured by the shell/react side.
755
787
  - `RequireSession` (function): function RequireSession({ fallback, loading, children, }: { fallback?: ReactNode; loading?: ReactNode; children?: ReactNode; }): React.JSX.Element — ⚠ undocumented
756
788
  - `RotateDeviceScreen` (function): function RotateDeviceScreen({ title = "Turn your device", description, requiredOrientation = "landscape", accent = "var(--jg-accent, #8ea2ff)", icon, className, style, }: { /** Short headline. */ title?: string; /** One concise explanatory line. Defaults from `requiredOrientation`. */ description?: … — Full-viewport, non-dismissible rotate-device gate shown when a game requires an orientation the device isn't in.
@@ -764,6 +796,8 @@
764
796
  - `ScoreboardProps` (interface): interface ScoreboardProps — Props for {@link Scoreboard}. Pass either `entries` (already ranked) or `rows` + `options`.
765
797
  - `ScoreboardTheme` (interface): interface ScoreboardTheme — Reskin tokens for {@link Scoreboard}. Every color/spacing value reads a `--jg-*` token first.
766
798
  - `Screen` (function): function Screen({ id, open = true, className, children, }: { id: string; open?: boolean; className?: string; children?: ReactNode; }): React.JSX.Element | null — ⚠ undocumented
799
+ - `ScrollRail` (function): function ScrollRail({ children, label, className, style, gap = 6 }: ScrollRailProps): React.JSX.Element — Native horizontal scrolling with overflow-only navigation and automatic focus reveal. Callers own item markup, selection, keyboard semantics, and skin through HudTheme or style.
800
+ - `ScrollRailProps` (interface): interface ScrollRailProps — Props for caller-composed horizontally scrolling controls.
767
801
  - `SelectionCollectionChrome` (function): function SelectionCollectionChrome({ model, itemSize = 44, gap = 4, windowSize, scroll = 0, ariaLabel = "Selection", renderMember, renderGroup, onGroupSelect, className, style, }: { model: SelectionModel; itemSize?: number; gap?: number; /** Cap the mounted portraits; the model virtualizes past this… — The composable RENDERER for the member collection: a portrait strip for a small selection, or the same-kind group chips once the model is `grouped`. Roving tabindex + arrow-key focus. Swap `renderMember`/`renderGroup` for custom chrome, or read the model and lay it out yourself. No panel skin — a behavior/accessibility layer only.
768
802
  - `SelectionGroupChip` (function): function SelectionGroupChip({ group, onSelect, renderIcon, className, style, }: { group: SelectionGroup; onSelect?: (group: SelectionGroup) => void; renderIcon?: (group: SelectionGroup) => ReactNode; className?: string; style?: CSSProperties; }): React.JSX.Element — A same-kind selection chip — icon plus count — for the grouped large-selection view. The default chrome {@link SelectionCollectionChrome} renders per group; swap it via `renderGroup`.
769
803
  - `SelectionKeyEvent` (interface): interface SelectionKeyEvent — A minimal keyboard-event shape — a real `KeyboardEvent`/React synthetic satisfies it.
@@ -817,15 +851,15 @@
817
851
  - `ToastStack` (function): function ToastStack({ action, limit = 4, className, renderToast, }: { action: string; limit?: number; className?: string; renderToast?: (entry: FeedEntry, index: number) => ReactNode; }): React.JSX.Element | null — Render `ctx.game.feed`'s entries for `action` as a newest-first toast stack — the feed-backed sibling of `@jgengine/core/game/toasts`' `createToastQueue`. Reach for this when the message source is already an engine event bound onto `ctx.game.feed` (kill feed, quest updates, loot log); reach for a `createToastQueue` when the game raises ad-hoc messages that need their own TTL independent of the feed's ring buffer.
818
852
  - `Trans` (function): function Trans({ k, params }: { k: string; params?: TParams }): ReactNode — Render a translated message inline: `<Trans k="hud.score" params={{ value }} />`.
819
853
  - `UseActionBarOptions` (interface): interface UseActionBarOptions — Options for {@link useActionBar}.
820
- - `UseAxisChannelResult` (interface): interface UseAxisChannelResult — ⚠ undocumented
854
+ - `UseAxisChannelResult` (interface): interface UseAxisChannelResult { channel: AxisChannel; isDown: (code: string) => boolean } — ⚠ undocumented · used by `useAxisChannel`: Wires useHeldKeys into a fresh AxisChannel, ready for a per-frame `channel.sample(dt, isDown)`.
821
855
  - `UsePanelsOptions` (interface): interface UsePanelsOptions — Options for {@link usePanels}.
822
856
  - `UseSelectionViewOptions` (interface): interface UseSelectionViewOptions — Options for {@link useSelectionView}.
823
- - `UseVoiceOptions` (interface): interface UseVoiceOptions — ⚠ undocumented
857
+ - `UseVoiceOptions` (interface): interface UseVoiceOptions { transport?: VoiceTransport; channelId?: string; mode?: PushToTalkMode; resolveRoutes?: () => readonly VoiceRoute[]; getUserMedia?: (constraints: MediaStreamConstraints) => Promise<MediaStream> } — ⚠ undocumented · used by `useVoice`: Mic capture + push-to-talk + channel roster over the VoiceTransport signaling seam.
824
858
  - `UserBadge` (function): function UserBadge({ className, avatarClassName, nameClassName, renderBadge, }: { className?: string; avatarClassName?: string; nameClassName?: string; renderBadge?: (session: AuthSession) => ReactNode; }): React.JSX.Element | null — ⚠ undocumented
825
859
  - `ViewportMetrics` (interface): interface ViewportMetrics — Live viewport rectangles: the layout viewport and the visible `visualViewport`.
826
860
  - `VitalBar` (function): function VitalBar({ vital, width = 120, showLabel = true, className, style, }: { vital: EntityVital; width?: number; showLabel?: boolean; className?: string; style?: CSSProperties; }): React.JSX.Element — A compact current/max vital meter for an entity summary; tone maps to a colour ramp. Reuse it as the vital row inside {@link EntitySummary} or any custom entity panel.
827
861
  - `VoiceRoster` (function): function VoiceRoster({ voice, className, participantClassName, renderParticipant, }: { voice: VoiceState; className?: string; participantClassName?: string; renderParticipant?: (participant: VoiceParticipant, gain: number) => ReactNode; }): React.JSX.Element — ⚠ undocumented
828
- - `VoiceState` (interface): interface VoiceState — ⚠ undocumented
862
+ - `VoiceState` (interface): interface VoiceState { supported: boolean; micStream: MediaStream | null; micError: string | null; requestMic(): Promise<boolean>; transmitting: boolean; status: PushToTalkStatus; mode: PushToTalkMode; setMode(mode: PushToTalkMode): void; muted: boolean; setMut… — ⚠ undocumented · used by `useVoice`: Mic capture + push-to-talk + channel roster over the VoiceTransport signaling seam.
829
863
  - `WaveBanner` (function): function WaveBanner({ wave, label = "Wave", subtitle, style, className, }: { wave: number | string; label?: string; subtitle?: ReactNode; style?: CSSProperties; className?: string; }): React.JSX.Element — A wave / round banner — a bold centered pill for "WAVE 3" style callouts, with an optional subtitle (enemies remaining, timer). Pure display: pass the current `wave` and whatever subtitle you track.
830
864
  - `WaveHud` (function): function WaveHud({ runner, theme, variation = "plate", width = 260, className, style }: WaveHudProps): ReactNode — A drop-in wave/spawn HUD panel over a {@link WaveRunner}: a big "WAVE N" label, a wave-progress bar (the shared {@link ExperienceBar}), and the live spawned / total, budget, and alert readouts — the visible face of "the brain behind WAVE 3". It subscribes to the runner and re-renders on change; the game drives the runner's clock. Reskin with {@link WaveHudTheme} and the shared HudTheme bar tokens. When the final wave finishes it shows a "WAVES CLEARED" state. Presentation only: the schedule, budget, and RNG live in the core model, and spawn `kind`s are never read.
831
865
  - `WaveHudProps` (interface): interface WaveHudProps — Props for {@link WaveHud}.
@@ -838,10 +872,10 @@
838
872
  - `WeaponSlots` (function): function WeaponSlots(props: SlotGridProps): React.JSX.Element — Weapon loadout slots.
839
873
  - `Window` (function): function Window({ title, ariaLabel, closable = true, onClose, x, y, onMove, z, width, variation, shape, className, style, bodyStyle, children, }: { title: ReactNode; ariaLabel?: string; closable?: boolean; onClose?: () => void; /** Initial (uncontrolled) or current (controlled, with `onMove`) x posi… — A single standalone window — title bar, close button, and title-bar drag over {@link HudFrame} chrome — usable without the {@link usePanels} manager for a one-off dialog. Position is uncontrolled (seeded by `x`/`y`) unless both `x`/`y` and `onMove` are supplied, in which case the caller owns placement. Accessible: `role="dialog"` with an `aria-label` and a focusable close button.
840
874
  - `WorldBrowser` (function): function WorldBrowser({ listings, onJoin, className, rowClassName, joinClassName, emptyState, renderListing, }: { listings: readonly SessionListing[]; onJoin: (listing: SessionListing) => void; className?: string; rowClassName?: string; joinClassName?: string; emptyState?: ReactNode; renderListing?:… — ⚠ undocumented
841
- - `WorldBrowserState` (interface): interface WorldBrowserState — ⚠ undocumented
875
+ - `WorldBrowserState` (interface): interface WorldBrowserState { listings: SessionListing[]; loading: boolean; error: string | null; refresh(): void } — ⚠ undocumented · used by `useWorldBrowser`: Polls a host-supplied session fetcher (e.g.
842
876
  - `WorldInviteToast` (function): function WorldInviteToast({ className, acceptClassName, declineClassName, onAccepted, renderInvite, }: { className?: string; acceptClassName?: string; declineClassName?: string; onAccepted: (target: WorldInviteTarget) => void; renderInvite?: (invite: WorldInvite) => ReactNode; }): React.JSX.Element … — ⚠ undocumented
843
877
  - `WorldMap` (function): function WorldMap({ markers, bounds, player, facingYaw = 0, fog, background, width = 520, height, kindStyles = DEFAULT_MARKER_KINDS, routes, zones, cellStates, onWorldClick, className, title = "World Map", onClose, }: WorldMapProps): ReactNode — Full-bounds top-down world map (the "press M" overlay): baked terrain background, reveal-on-event fog, all markers with labels, and the player. Rectangular linear projection over the supplied world `bounds`. Framed panel wrapper over {@link WorldMapSurface}.
844
- - `WorldMapProps` (interface): interface WorldMapProps — ⚠ undocumented
878
+ - `WorldMapProps` (interface): interface WorldMapProps { markers: MarkerCollection; bounds: MapBounds; player?: WorldXZ; facingYaw?: number; fog?: FogField; background?: string; width?: number; height?: number; kindStyles?: Record<string, MarkerKindStyle>; routes?: readonly MapRoute[]; zones?: … — ⚠ undocumented · used by `WorldMap`: Full-bounds top-down world map (the "press M" overlay): baked terrain background, reveal-on-event fog, all markers with labels, and the play…
845
879
  - `WorldMapSurface` (function): function WorldMapSurface({ markers, bounds, player, facingYaw = 0, fog, background, width = 520, height, kindStyles = DEFAULT_MARKER_KINDS, routes, zones, cellStates, onWorldClick, canvasWidth, canvasHeight, viewport = IDENTITY_VIEWPORT, style, }: WorldMapSurfaceProps): ReactNode — The bare top-down map `<svg>` shared by {@link WorldMap} (framed panel) and {@link FullscreenMap} (pan/zoom overlay): baked terrain background, fog, map layers, markers with labels, and the player arrow, drawn under an optional viewport transform. Rectangular linear projection over the world `bounds`.
846
880
  - `WorldMapSurfaceProps` (interface): interface WorldMapSurfaceProps — Props for {@link WorldMapSurface}.
847
881
  - `abilityKitNeedsHeartbeat` (function): function abilityKitNeedsHeartbeat(kit: AbilityKit, resourceAvailable?: number): boolean — ⚠ undocumented
@@ -860,13 +894,14 @@
860
894
  - `guestIdentity` (function): function guestIdentity(seed?: string): IdentitySource — ⚠ undocumented
861
895
  - `hudSkinVars` (function): function hudSkinVars(skin?: HudSkin): CSSProperties — Converts a skin into the CSS variables consumed by shared HUD components.
862
896
  - `hudTheme` (function): function hudTheme(overrides: HudThemeOverrides): HudTheme — Builds a full {@link HudTheme} from per-group partials, filling the rest from {@link defaultHudTheme}. Reach for this when a game wants to name exact values; use {@link deriveHudTheme} when a seed color pair is enough.
863
- - `hudThemeVars` (function): function hudThemeVars(theme: HudTheme): CSSProperties — Emits the CSS custom properties for a theme — spread onto any HUD ancestor to re-skin the subtree.
897
+ - `hudThemeVars` (function): function hudThemeVars(theme: HudTheme): CSSProperties — Emits theme CSS properties, resetting inherited bar fills when absent. Spread onto a HUD ancestor.
864
898
  - `hudVisibleInPhase` (function): function hudVisibleInPhase(showDuring: readonly GamePhase[] | undefined, phase: GamePhase): boolean — Whether a HUD element opted into `showDuring` is visible in the current phase; `undefined` = always visible (default).
865
899
  - `isRedSuit` (function): function isRedSuit(suit: Suit): boolean — True for the red suits (hearts, diamonds).
866
900
  - `latestChatBubbles` (function): function latestChatBubbles(messages: readonly ChatMessage[], nowMs: number, ttlMs: number): ChatBubble[] — ⚠ undocumented
867
901
  - `layoutEntityFrames` (function): function layoutEntityFrames<E extends EntityFrameEntry>(entries: readonly E[], project: ProjectEntity, options: EntityFrameLayoutOptions = {}): EntityFramePlacement<E>[] — Pure layout core: projects each entry, drops the ones behind the camera, off-screen (when `viewport` is set), or beyond `maxCount`, and returns the survivors sorted farthest-first so nearer frames render last (on top). Stable for equal depths via `id`. No React, no DOM — unit-testable in isolation.
868
902
  - `layoutFloatingText` (function): function layoutFloatingText(views: readonly FloatingTextView[], project: ProjectEntity, options: FloatingTextLayoutOptions = {}): FloatingTextPlacement[] — Pure layout core: projects each view, drops the ones behind the camera or (when `viewport` is set) off-screen, and returns the survivors sorted farthest-first so nearer text paints on top. No React, no DOM — unit-testable.
869
903
  - `localPlayerEntity` (function): function localPlayerEntity(ctx: GameContext): SceneEntity | null — ⚠ undocumented
904
+ - `observeBrowserSuspension` (function): function observeBrowserSuspension(onSuspend: (reason: BrowserSuspensionReason) => void, options: BrowserSuspensionOptions = {}, sources: BrowserSuspensionSources | null = typeof window === "undefined" || typeof document === "undefined" ? null : { window, document }): () => void — Observe interruption events until detached. Lock loss requires a preceding lock; SSR attaches nothing.
870
905
  - `panelKeyAction` (function): function panelKeyAction(defs: readonly PanelDef[], state: PanelState, event: { code?: string; key?: string }): PanelKeyResult — Pure keybind resolver for a panel set: ESC resolves to `closeTop` when a closable window is open, and any other key routes through {@link panelByHotkey} (trying `code` then `key`) to a `toggle`. Returns the intent without touching the DOM, so it unit-tests headless and `usePanels` is a thin shell over it.
871
906
  - `previewFixtureNames` (function): function previewFixtureNames(): string[] — Sorted list of registered fixture names, for discovery/listing.
872
907
  - `relativeSavedAt` (function): function relativeSavedAt(savedAt: number, nowMs = Date.now()): string — Turn a `savedAt` timestamp into a compact relative label ("just now", "4m ago", "2d ago"). Presentation-only helper — the model stores raw ms.
@@ -887,6 +922,7 @@
887
922
  - `useAuthedPlayer` (function): function useAuthedPlayer(options?: { guestSeed?: string }): PlayerIdentity | null — ⚠ undocumented
888
923
  - `useAutoScroll` (function): function useAutoScroll<T extends HTMLElement>(dep: unknown): React.RefObject<T | null> — Pin a scrollable element to its bottom whenever `dep` changes (typically a length or the list itself). Attach the returned ref to the scroll container. Owns the log/chat/console scroll-to-bottom effect so panels don't hand-roll it.
889
924
  - `useAxisChannel` (function): function useAxisChannel(config: AxisChannelConfig): UseAxisChannelResult — Wires useHeldKeys into a fresh AxisChannel, ready for a per-frame `channel.sample(dt, isDown)`. The channel is recreated when `config` identity changes, so pass a stable config (useMemo/module constant at the call site) unless a rebind is intended.
925
+ - `useBrowserSuspension` (function): function useBrowserSuspension(onSuspend: (reason: BrowserSuspensionReason) => void, options: BrowserSuspensionOptions = {}): void — Observe browser interruptions while mounted, invoking the latest callback without replacing listeners.
890
926
  - `useCameraShake` (function): function useCameraShake(controller: CameraShakeController, animate = true): CameraShakeReadout — Subscribe to a camera-shake controller and re-render on every change (add / decay / clear). Optionally drives `update()` on an animation frame so the trauma meter bleeds down live even when no game loop ticks the controller. Returns the current trauma + kind for a HUD readout.
891
927
  - `useChat` (function): function useChat(channelId: string, options?: { limit?: number }): ChatMessage[] — ⚠ undocumented
892
928
  - `useChatBubbles` (function): function useChatBubbles(options?: ChatBubblesOptions): readonly ChatBubble[] — ⚠ undocumented
@@ -895,9 +931,10 @@
895
931
  - `useCurrency` (function): function useCurrency(currencyId: string): number — ⚠ undocumented
896
932
  - `useDamageDirection` (function): function useDamageDirection(tracker: DamageDirectionTracker): readonly DamageIndicator[] — Subscribe to a damage-direction tracker and animate. Because indicators fade continuously on a clock (not just on discrete events), this drives a `requestAnimationFrame` loop while any indicator is live and stops when the screen is clear, then wakes again on the next hit. Returns the tracker's current `active()` array (reused between frames — copy if you retain it).
897
933
  - `useDebouncedCommit` (function): function useDebouncedCommit<T>(value: T, commit: (value: T) => void, delayMs = 180): DebouncedCommit<T> — See {@link DebouncedCommit}. `commit` and `delayMs` may change between renders (kept in refs); the binding identity stays stable except when `value` (the local mirror) changes.
934
+ - `useDialogBehavior` (function): function useDialogBehavior<T extends HTMLElement = HTMLDivElement>(options: DialogBehaviorOptions): RefObject<T | null> — Focus entry, Tab trapping, Escape requests, and focus restoration for a caller-owned dialog. Attach the returned ref to the dialog root and supply `role="dialog"`, `aria-modal`, an accessible name, and `tabIndex={-1}`. The most recently opened dialog handles keys, including portal roots. Closing an inner dialog restores its opener; cleanup never steals focus from another active dialog. Replacing the root while open keeps its position and original return target.
898
935
  - `useDialogueRun` (function): function useDialogueRun(run: DialogueRun): DialogueGraphView | null — Subscribe to a {@link DialogueRun} and re-render on every advance / jump / reset, returning the current node view (or `null` if the run's node id is unknown).
899
936
  - `useDomEvent` (function): function useDomEvent<E>(resolveTarget: () => DomEventTarget | null, type: string, handler: (event: E) => void, options?: { capture?: boolean; passive?: boolean }): void — Attach a DOM event listener with automatic cleanup. `resolveTarget` runs inside the effect, so `() => window` and ref-reading resolvers are SSR-safe; return null to skip attaching. The handler is kept in a ref, so a fresh closure per render never re-binds the listener. Re-binds only when `type` or `options.capture`/`options.passive` change.
900
- - `useDragLayer` (function): function useDragLayer<T>(options?: { onDrop?: (info: DropInfo<T>) => void; }): DragLayer<T> — ⚠ undocumented
937
+ - `useDragLayer` (function): function useDragLayer<T>(options?: { onDrop?: (info: DropInfo<T>) => void; }): DragLayer<T> — Pointer-owned drag state shared by cards and inventory grids. Registered DropZones are hit-tested through pointer capture; release finishes once, while cancellation, blur and unmount release ownership. Callers own drop policy, rotation and presentation; pointer movement paints the ghost without rerendering it.
901
938
  - `useEntityChatBubble` (function): function useEntityChatBubble(instanceId: string, options?: ChatBubblesOptions): ChatBubble | null — ⚠ undocumented
902
939
  - `useEntityStat` (function): function useEntityStat(instanceId: string, statId: string): StatValue | null — ⚠ undocumented
903
940
  - `useEventMeter` (function): function useEventMeter(meter: EventMeter, options?: AbilitySlotBindingOptions): EventMeterView — Bind a `createEventMeter` (`@jgengine/core/stats/eventMeter`) heat/streak gauge to a component — the react-render half of the ult/adrenaline and streak/combo meters (`event-meter` capability) that lets a HUD gauge re-render on tick without the game hand-rolling a `useEffect`/`setInterval` heartbeat around `meter.value()`.
@@ -907,7 +944,7 @@
907
944
  - `useFog` (function): function useFog(fog: FogField): ReturnType<FogField["cells"]> — ⚠ undocumented
908
945
  - `useFriendRequests` (function): function useFriendRequests(): FriendRequestEntry[] — ⚠ undocumented
909
946
  - `useFriends` (function): function useFriends(): FriendEntry[] — ⚠ undocumented
910
- - `useGame` (function): function useGame(): { commands: GameContext["game"]["commands"]; events: GameEvents } — ⚠ undocumented
947
+ - `useGame` (function): function useGame(): { commands: Omit<GameContext["game"]["commands"], "run"> & { run(name: string, input: unknown): DispatchedCommandResult }; events: GameEvents } — UI command surface. Hosted commands return a promise; await it to display acknowledgement or rejection. Registration and runAs remain trusted local APIs.
911
948
  - `useGameClock` (function): function useGameClock(): ClockSnapshot & { controls: SimClock } — ⚠ undocumented
912
949
  - `useGameContext` (function): function useGameContext(): GameContext — ⚠ undocumented
913
950
  - `useGameLayoutMode` (function): function useGameLayoutMode(): GameLayoutMode — The resolved explicit composition mode (`desktop-wide` … `mobile-portrait`).
@@ -969,7 +1006,7 @@
969
1006
  - `useShopStock` (function): function useShopStock(shop: ShopStock): ShopStockEntry[] — Subscribe to a shop's stock and re-render whenever it changes (buy, sell, restock, price, add, remove, restore). Returns the current entry list — a detached snapshot safe to map over.
970
1007
  - `useStore` (function): function useStore<T>(handle: StoreHandle<T>): T — Subscribe a component to a typed store slot defined with `defineStore`. Returns the current value (or the definition's initial before any write), re-rendering only when the slot changes — the cast-free, boilerplate-free replacement for a hand-written `useGameStore((ctx) => ctx.game.store.get(KEY) as T)`.
971
1008
  - `useT` (function): function useT(): (key: string, params?: TParams) => string — Return the translator bound to the current locale; the component re-renders when `setLocale` is called, so returned strings stay live.
972
- - `useTarget` (function): function useTarget(fromInstanceId: string): string | null — ⚠ undocumented
1009
+ - `useTarget` (function): function useTarget(fromInstanceId: string): string | null — The entity `fromInstanceId` currently targets (set by `ctx.scene.entity.setTarget` or `cycleTarget`), for a target frame.
973
1010
  - `useTicker` (function): function useTicker(hz = 10): number — Re-render at a steady rate. Returns a monotonically increasing tick count driven by a setInterval, for HUD elements that display wall-clock-derived values (cooldowns, cast bars, swing timers) without an engine subscription to hang off. `hz <= 0` disables the ticker.
974
1011
  - `useTimerRead` (function): function useTimerRead(timer: TimerSet, id: string, active = true): TimerRead | null — Subscribe to a single timer and re-read it every animation frame while mounted, so a HUD readout stays live without the game hand-rolling interval math. Reuses one read object (allocation-aware). Returns `null` for an unknown id. Pass `active={false}` to freeze the per-frame tick (e.g. an off-screen HUD).
975
1012
  - `useViewportMetrics` (function): function useViewportMetrics(): ViewportMetrics — Live visible viewport, tracking `window.visualViewport` (mobile browser chrome, pinch-zoom) with a layout-viewport fallback.
@@ -1029,7 +1066,15 @@
1029
1066
 
1030
1067
  ## @jgengine/react/barsPreview
1031
1068
 
1032
- - `BarsPreview` (function): function BarsPreview({ className }: { className?: string }): React.JSX.Element — Renders the atomic bar matrix twice under different token blocks to prove global re-theming.
1069
+ - `BarsPreview` (function): function BarsPreview({ className }: { className?: string }): React.JSX.Element — Renders atomic bars under token blocks and nested themes to prove fill and skin precedence.
1070
+
1071
+ ## @jgengine/react/browserLifecycle
1072
+
1073
+ - `BrowserSuspensionOptions` (interface): interface BrowserSuspensionOptions — Select suspension events; all are observed by default. No callback runs on attachment.
1074
+ - `BrowserSuspensionReason` (type): type BrowserSuspensionReason = "blur" | "hidden" | "pointer-lock-lost" — Browser events that can interrupt active play. The caller decides whether to pause.
1075
+ - `BrowserSuspensionSources` (interface): interface BrowserSuspensionSources — Event sources for a browser window, an iframe, or a test.
1076
+ - `observeBrowserSuspension` (function): function observeBrowserSuspension(onSuspend: (reason: BrowserSuspensionReason) => void, options: BrowserSuspensionOptions = {}, sources: BrowserSuspensionSources | null = typeof window === "undefined" || typeof document === "undefined" ? null : { window, document }): () => void — Observe interruption events until detached. Lock loss requires a preceding lock; SSR attaches nothing.
1077
+ - `useBrowserSuspension` (function): function useBrowserSuspension(onSuspend: (reason: BrowserSuspensionReason) => void, options: BrowserSuspensionOptions = {}): void — Observe browser interruptions while mounted, invoking the latest callback without replacing listeners.
1033
1078
 
1034
1079
  ## @jgengine/react/cameraShake
1035
1080
 
@@ -1075,8 +1120,8 @@
1075
1120
 
1076
1121
  ## @jgengine/react/chatBubbles
1077
1122
 
1078
- - `ChatBubble` (interface): interface ChatBubble — ⚠ undocumented
1079
- - `ChatBubblesOptions` (interface): interface ChatBubblesOptions — ⚠ undocumented
1123
+ - `ChatBubble` (interface): interface ChatBubble { id: string; fromUserId: string; body: string; at: number } — ⚠ undocumented
1124
+ - `ChatBubblesOptions` (interface): interface ChatBubblesOptions { channelId?: string; ttlMs?: number; limit?: number } — ⚠ undocumented
1080
1125
  - `latestChatBubbles` (function): function latestChatBubbles(messages: readonly ChatMessage[], nowMs: number, ttlMs: number): ChatBubble[] — ⚠ undocumented
1081
1126
  - `useChatBubbles` (function): function useChatBubbles(options?: ChatBubblesOptions): readonly ChatBubble[] — ⚠ undocumented
1082
1127
  - `useEntityChatBubble` (function): function useEntityChatBubble(instanceId: string, options?: ChatBubblesOptions): ChatBubble | null — ⚠ undocumented
@@ -1108,9 +1153,9 @@
1108
1153
  - `CurrencyPill` (function): function CurrencyPill({ currencyId, className }: { currencyId: string; className?: string }): React.JSX.Element — ⚠ undocumented
1109
1154
  - `DeathScreen` (function): function DeathScreen({ statId = "health", open, className, children, }: { statId?: string; open?: boolean; className?: string; children?: ReactNode; }): React.JSX.Element — ⚠ undocumented
1110
1155
  - `DialogueBox` (function): function DialogueBox({ dialogue, onChoice, rng, className, lineClassName, speakerClassName, choicesClassName, choiceClassName, checkClassName, }: { dialogue: DialogueDef; onChoice?: (choice: DialogueChoice, result: CheckResult | null) => void; rng?: () => number; className?: string; lineClassName?: … — ⚠ undocumented
1111
- - `DialogueCheck` (interface): interface DialogueCheck — ⚠ undocumented
1112
- - `DialogueChoice` (interface): interface DialogueChoice — ⚠ undocumented
1113
- - `DialogueDef` (interface): interface DialogueDef — ⚠ undocumented
1156
+ - `DialogueCheck` (interface): interface DialogueCheck { label?: string; modifier: number; dc: number; advantage?: CheckAdvantage } — ⚠ undocumented
1157
+ - `DialogueChoice` (interface): interface DialogueChoice { label: string; invoke: {command: string; args?: unknown} | null; check?: DialogueCheck; onSuccess?: {command: string; args?: unknown} | null; onFailure?: {command: string; args?: unknown} | null } — ⚠ undocumented · used by `runDialogueChoice`: Route a {@link DialogueBox} choice through the `features.dialogue` bridge: resolve the choice's invoke (honoring a skill-check `result`), ru…
1158
+ - `DialogueDef` (interface): interface DialogueDef { id: string; lines: readonly DialogueLine[] } — ⚠ undocumented
1114
1159
  - `DialogueLine` (type): type DialogueLine = { speaker: string; text: string } | { choices: readonly DialogueChoice[] } — ⚠ undocumented
1115
1160
  - `KeybindRow` (function): function KeybindRow({ action, keys, className, }: { action: string; keys: readonly string[]; className?: string; }): React.JSX.Element — ⚠ undocumented
1116
1161
  - `LevelUpFlash` (function): function LevelUpFlash({ stat, durationMs = 1600, className, children, renderFlash, }: { stat?: string; durationMs?: number; className?: string; children?: ReactNode; renderFlash?: (event: StatLevelUpEvent) => ReactNode; }): React.JSX.Element | null — ⚠ undocumented
@@ -1144,6 +1189,11 @@
1144
1189
  - `DamageDirectionTheme` (interface): interface DamageDirectionTheme — Reskin tokens for {@link DamageDirectionOverlay}.
1145
1190
  - `useDamageDirection` (function): function useDamageDirection(tracker: DamageDirectionTracker): readonly DamageIndicator[] — Subscribe to a damage-direction tracker and animate. Because indicators fade continuously on a clock (not just on discrete events), this drives a `requestAnimationFrame` loop while any indicator is live and stops when the screen is clear, then wakes again on the next hit. Returns the tracker's current `active()` array (reused between frames — copy if you retain it).
1146
1191
 
1192
+ ## @jgengine/react/dialogBehavior
1193
+
1194
+ - `DialogBehaviorOptions` (interface): interface DialogBehaviorOptions — Options for controlled dialog focus behavior; callers own markup, state, and close policy.
1195
+ - `useDialogBehavior` (function): function useDialogBehavior<T extends HTMLElement = HTMLDivElement>(options: DialogBehaviorOptions): RefObject<T | null> — Focus entry, Tab trapping, Escape requests, and focus restoration for a caller-owned dialog. Attach the returned ref to the dialog root and supply `role="dialog"`, `aria-modal`, an accessible name, and `tabIndex={-1}`. The most recently opened dialog handles keys, including portal roots. Closing an inner dialog restores its opener; cleanup never steals focus from another active dialog. Replacing the root while open keeps its position and original return target.
1196
+
1147
1197
  ## @jgengine/react/dialogueView
1148
1198
 
1149
1199
  - `DialogueView` (function): function DialogueView({ run, renderPortrait, onChoose, onClose, closeLabel = "Close", theme, className, style, speakerClassName, textClassName, choicesClassName, choiceClassName, }: DialogueViewProps): ReactNode — A drop-in branching-conversation view over a {@link DialogueRun}: renders the current node's speaker name (+ an optional portrait slot), the line, and a list of clickable response choices that advance the conversation — the game passes its dialogue graph and gets working node traversal and choice state with no hand-rolled walk. A choice with a destination advances the run; a terminal choice (or the Close control on a choiceless node) fires `onClose`. Speaker/choice `kind` are free strings surfaced as `data-*` attributes for the game to style; the view never interprets them. HudTheme-skinned via `--jg-*` tokens; reskin further with {@link DialogueViewTheme}.
@@ -1153,23 +1203,23 @@
1153
1203
 
1154
1204
  ## @jgengine/react/display
1155
1205
 
1156
- - `DisplayProfile` (interface): interface DisplayProfile — ⚠ undocumented
1206
+ - `DisplayProfile` (interface): interface DisplayProfile { coarsePointer: boolean; compact: boolean; portrait: boolean } — ⚠ undocumented
1157
1207
 
1158
1208
  ## @jgengine/react/dragLayer
1159
1209
 
1160
1210
  - `DragGhost` (function): function DragGhost<T>({ layer, className, style, children, }: { layer: DragLayer<T>; className?: string; style?: CSSProperties; children?: (payload: DragPayload<T>) => ReactNode; }): React.JSX.Element | null — ⚠ undocumented
1161
- - `DragLayer` (interface): interface DragLayer<T> — ⚠ undocumented
1162
- - `DragPayload` (interface): interface DragPayload<T> — ⚠ undocumented
1163
- - `DragState` (interface): interface DragState<T> — ⚠ undocumented
1211
+ - `DragLayer` (interface): interface DragLayer<T> { state: DragState<T>; dragging: boolean; pointRef: RefObject<{x: number; y: number}>; beginDrag(payload: {id: string; value: T; rotation?: Rotation}, event: ReactPointerEvent): void; rotate(quarterTurns?: number): void; setTarget(target: s… — ⚠ undocumented · used by `useDragLayer`: Pointer-owned drag state shared by cards and inventory grids.
1212
+ - `DragPayload` (interface): interface DragPayload<T> { id: string; value: T; rotation: Rotation } — ⚠ undocumented
1213
+ - `DragState` (interface): interface DragState<T> { payload: DragPayload<T> | null; point: {x: number; y: number}; origin: {x: number; y: number}; overTarget: string | null; overCell: Cell | null } — ⚠ undocumented
1164
1214
  - `DraggableCard` (function): function DraggableCard<T>({ id, value, layer, className, children, onRotate, }: { id: string; value: T; layer: DragLayer<T>; className?: string; children?: ReactNode; onRotate?: boolean; }): React.JSX.Element — ⚠ undocumented
1165
- - `DropInfo` (interface): interface DropInfo<T> — ⚠ undocumented
1215
+ - `DropInfo` (interface): interface DropInfo<T> { payload: DragPayload<T>; target: string | null; cell: Cell | null; point: {x: number; y: number} } — ⚠ undocumented · used by `useDragLayer`: Pointer-owned drag state shared by cards and inventory grids.
1166
1216
  - `DropZone` (function): function DropZone<T>({ id, layer, className, activeClassName, cellSize, children, }: { id: string; layer: DragLayer<T>; className?: string; activeClassName?: string; cellSize?: number; children?: ReactNode; }): React.JSX.Element — ⚠ undocumented
1167
- - `useDragLayer` (function): function useDragLayer<T>(options?: { onDrop?: (info: DropInfo<T>) => void; }): DragLayer<T> — ⚠ undocumented
1217
+ - `useDragLayer` (function): function useDragLayer<T>(options?: { onDrop?: (info: DropInfo<T>) => void; }): DragLayer<T> — Pointer-owned drag state shared by cards and inventory grids. Registered DropZones are hit-tested through pointer capture; release finishes once, while cancellation, blur and unmount release ownership. Callers own drop policy, rotation and presentation; pointer movement paints the ghost without rerendering it.
1168
1218
 
1169
1219
  ## @jgengine/react/engineStore
1170
1220
 
1171
- - `EventfulEngineStore` (interface): interface EventfulEngineStore<TEventMap extends object> — ⚠ undocumented
1172
- - `ReadableEngineStore` (interface): interface ReadableEngineStore<TState> — ⚠ undocumented
1221
+ - `EventfulEngineStore` (interface): interface EventfulEngineStore<TEventMap extends object> { on<K extends keyof TEventMap>(eventName: K, listener: (payload: TEventMap[K]) => void): () => void } — ⚠ undocumented
1222
+ - `ReadableEngineStore` (interface): interface ReadableEngineStore<TState> { getState(): TState; subscribe(listener: (state: TState) => void): () => void } — ⚠ undocumented
1173
1223
 
1174
1224
  ## @jgengine/react/entityFrames
1175
1225
 
@@ -1198,14 +1248,14 @@
1198
1248
 
1199
1249
  ## @jgengine/react/fogOverlay
1200
1250
 
1201
- - `FogCellRect` (interface): interface FogCellRect — ⚠ undocumented
1202
- - `FogPaintSurface` (interface): interface FogPaintSurface — ⚠ undocumented
1251
+ - `FogCellRect` (interface): interface FogCellRect { x: number; y: number; width: number; height: number } — ⚠ undocumented
1252
+ - `FogPaintSurface` (interface): interface FogPaintSurface { fillStyle: string | CanvasGradient | CanvasPattern; fillRect(x: number, y: number, width: number, height: number): void; clearRect(x: number, y: number, width: number, height: number): void } — ⚠ undocumented
1203
1253
 
1204
1254
  ## @jgengine/react/gameIcons
1205
1255
 
1206
1256
  - `GAME_ICON_NAMES` (const): const GAME_ICON_NAMES: readonly ["sword", "dagger", "axe", "hammer", "bow", "arrow", "staff", "wand", "spear", "crossbow", "gun", "bomb", "shield", "helmet", "chestplate", "boots", "gauntlet", "ring", "amulet", "cloak", "backpack", "torch", "potionRed", "potionBlue", "scroll", "tome", "meat", "bread… — ⚠ undocumented
1207
1257
  - `GameIcon` (function): function GameIcon({ name, size = 24, color, className, }: { name: GameIconName; size?: number; color?: string; className?: string; }): React.JSX.Element — ⚠ undocumented
1208
- - `GameIconName` (type): type GameIconName = (typeof GAME_ICON_NAMES)[number] — ⚠ undocumented
1258
+ - `GameIconName` (type): type GameIconName = (typeof GAME_ICON_NAMES)[number] — ⚠ undocumented · used by `iconForAction`: Control glyph for a semantic action name (`hardDrop`, `sprint`, `shiftLeft`), or null when no rule matches.
1209
1259
  - `iconForAction` (function): function iconForAction(action: string): GameIconName | null — Control glyph for a semantic action name (`hardDrop`, `sprint`, `shiftLeft`), or null when no rule matches.
1210
1260
  - `iconForItemId` (function): function iconForItemId(itemId: string): GameIconName | null — ⚠ undocumented
1211
1261
  - `isGameIconName` (function): function isGameIconName(value: string): value is GameIconName — ⚠ undocumented
@@ -1226,12 +1276,12 @@
1226
1276
 
1227
1277
  ## @jgengine/react/hooks
1228
1278
 
1229
- - `AbilitySlotBindingOptions` (interface): interface AbilitySlotBindingOptions — ⚠ undocumented
1279
+ - `AbilitySlotBindingOptions` (interface): interface AbilitySlotBindingOptions { intervalMs?: number } — ⚠ undocumented · used by `useEventMeter`: Bind a `createEventMeter` (`@jgengine/core/stats/eventMeter`) heat/streak gauge to a component — the react-render half of the ult/adrenaline…
1230
1280
  - `DomEventTarget` (interface): interface DomEventTarget — Minimal add/removeEventListener surface accepted by useDomEvent (window, document, elements).
1231
1281
  - `EventMeterView` (interface): interface EventMeterView — A rendered snapshot of an {@link EventMeter}: current value, fill fraction, active tier, and ready-to-consume flag.
1232
1282
  - `InventoryGridBinding` (interface): interface InventoryGridBinding — Live slots plus `move`/`split` actions bound to `inventoryId`, routed through the notifying `inventory.move`/`inventory.split` commands so React re-renders.
1233
- - `UseAxisChannelResult` (interface): interface UseAxisChannelResult — ⚠ undocumented
1234
- - `WorldBrowserState` (interface): interface WorldBrowserState — ⚠ undocumented
1283
+ - `UseAxisChannelResult` (interface): interface UseAxisChannelResult { channel: AxisChannel; isDown: (code: string) => boolean } — ⚠ undocumented · used by `useAxisChannel`: Wires useHeldKeys into a fresh AxisChannel, ready for a per-frame `channel.sample(dt, isDown)`.
1284
+ - `WorldBrowserState` (interface): interface WorldBrowserState { listings: SessionListing[]; loading: boolean; error: string | null; refresh(): void } — ⚠ undocumented · used by `useWorldBrowser`: Polls a host-supplied session fetcher (e.g.
1235
1285
  - `abilityKitNeedsHeartbeat` (function): function abilityKitNeedsHeartbeat(kit: AbilityKit, resourceAvailable?: number): boolean — ⚠ undocumented
1236
1286
  - `createHeldKeyTracker` (function): function createHeldKeyTracker(target: HeldKeyEventTarget): { isDown: (code: string) => boolean; dispose: () => void; } — ⚠ undocumented
1237
1287
  - `eventMeterNeedsHeartbeat` (function): function eventMeterNeedsHeartbeat(meter: EventMeter, previous: EventMeterView | null): boolean — True when `meter`'s value/fraction/tier/ready diverge from `previous` — the re-render check `useEventMeter` polls on its heartbeat.
@@ -1249,7 +1299,7 @@
1249
1299
  - `useFeed` (function): function useFeed({ action, limit }: { action: string; limit?: number }): FeedEntry[] — ⚠ undocumented
1250
1300
  - `useFriendRequests` (function): function useFriendRequests(): FriendRequestEntry[] — ⚠ undocumented
1251
1301
  - `useFriends` (function): function useFriends(): FriendEntry[] — ⚠ undocumented
1252
- - `useGame` (function): function useGame(): { commands: GameContext["game"]["commands"]; events: GameEvents } — ⚠ undocumented
1302
+ - `useGame` (function): function useGame(): { commands: Omit<GameContext["game"]["commands"], "run"> & { run(name: string, input: unknown): DispatchedCommandResult }; events: GameEvents } — UI command surface. Hosted commands return a promise; await it to display acknowledgement or rejection. Registration and runAs remain trusted local APIs.
1253
1303
  - `useGameClock` (function): function useGameClock(): ClockSnapshot & { controls: SimClock } — ⚠ undocumented
1254
1304
  - `useGamePhase` (function): function useGamePhase(): { phase: GamePhase; setPhase: (phase: GamePhase) => void } — Live run phase + a setter that also gates the shell's touch controls. `menu`/`paused`/`ended` hide the touch dock; `playing` shows it.
1255
1305
  - `useGameStore` (function): function useGameStore<T>(selector: (ctx: GameContext) => T, isEqual: (previous: T, next: T) => boolean = Object.is): T — ⚠ undocumented
@@ -1273,7 +1323,7 @@
1273
1323
  - `useSceneEntityIds` (function): function useSceneEntityIds(): readonly string[] — Membership-only entity id list: the returned array keeps a stable identity across per-frame pose writes and only changes when an entity spawns, despawns, or the store is hydrated (#625). A marker mapped from these ids reads its own live pose imperatively (useFrame), so the actor tree no longer re-reconciles every frame. Prefer this over {@link useSceneEntities} for large scenes.
1274
1324
  - `useSceneObjectIds` (function): function useSceneObjectIds(): readonly string[] — Membership-only object id list — the object counterpart of {@link useSceneEntityIds}; stable across move/rotate/setVisual, changes only on place/remove.
1275
1325
  - `useSceneObjects` (function): function useSceneObjects(): readonly SceneObject[] — ⚠ undocumented
1276
- - `useTarget` (function): function useTarget(fromInstanceId: string): string | null — ⚠ undocumented
1326
+ - `useTarget` (function): function useTarget(fromInstanceId: string): string | null — The entity `fromInstanceId` currently targets (set by `ctx.scene.entity.setTarget` or `cycleTarget`), for a target frame.
1277
1327
  - `useTicker` (function): function useTicker(hz = 10): number — Re-render at a steady rate. Returns a monotonically increasing tick count driven by a setInterval, for HUD elements that display wall-clock-derived values (cooldowns, cast bars, swing timers) without an engine subscription to hang off. `hz <= 0` disables the ticker.
1278
1328
  - `useWorldBrowser` (function): function useWorldBrowser(options: { fetchSessions: () => Promise<readonly SessionListing[]>; filter?: MatchFilter; limit?: number; refreshMs?: number; }): WorldBrowserState — Polls a host-supplied session fetcher (e.g. createWsBackend().browse) and filters through matchmaking's browseSessions. fetchSessions must be identity-stable (wrap in useCallback at the call site) or every render refetches.
1279
1329
  - `useWorldInvites` (function): function useWorldInvites(): WorldInvite[] — ⚠ undocumented
@@ -1301,7 +1351,7 @@
1301
1351
 
1302
1352
  - `HudCanvas` (function): function HudCanvas({ layout, editChord, compactScale, showDuring, className, style, children, }: { layout: HudLayoutStore; editChord?: HudEditChord | false; /** Zoom applied to the whole HUD on compact displays. Default 0.85. */ compactScale?: number; /** Opt-in play-phase gate: render the HUD only … — Full-viewport HUD surface. Panels declared with `HudPanel` flow into nine anchor regions and stack automatically with a gap — no per-panel pixel offsets, no manual clearance for sibling panels, the touch-control dock (`--jg-hud-dock-clearance`), or device safe areas. On compact displays the whole surface scales down and each panel applies its `compact` behavior.
1303
1353
  - `HudCompactMode` (type): type HudCompactMode = "keep" | "chip" | "hide" — How a panel behaves on compact (phone-scale) displays. `keep` stays visible at the global compact scale, `chip` collapses to a small tap-to-expand pill, `hide` unmounts entirely.
1304
- - `HudEditChord` (interface): interface HudEditChord — ⚠ undocumented
1354
+ - `HudEditChord` (interface): interface HudEditChord { hold: string; press: string } — ⚠ undocumented · used by `HudCanvas`: Full-viewport HUD surface.
1305
1355
  - `HudPanel` (function): function HudPanel({ id, anchor = "top-left", order, compact: compactMode = "keep", chip, interactive, inset, locked, showDuring, priority, mobileBehavior, allowOverlapWith, collisionGroup, region = true, width, height, type, className, style, children, }: { id: string; anchor?: HudAnchor; /** Stack … — A HUD block that lives in one of the nine anchor regions. Panels sharing a region stack outward from the screen edge in ascending `order`. On fine pointers panels stay draggable through the edit chord; a dragged panel leaves the flow and keeps its custom placement. On compact displays custom placements are ignored and the `compact` behavior applies.
1306
1356
  - `hudVisibleInPhase` (function): function hudVisibleInPhase(showDuring: readonly GamePhase[] | undefined, phase: GamePhase): boolean — Whether a HUD element opted into `showDuring` is visible in the current phase; `undefined` = always visible (default).
1307
1357
  - `useHudLayout` (function): function useHudLayout(options?: { storageKey?: string; snap?: number; locked?: boolean; /** * Scene-document `ui` section — source of truth for panel placement/size. * When provided, hydrates the layout store (and wins over legacy localStorage). */ documentUi?: EditorUiDocument; /** * When true (def… — Layout state for `HudCanvas` — panel placements, edit-mode drag/resize, and per-game persistence.
@@ -1343,7 +1393,7 @@
1343
1393
  - `deriveHudTheme` (function): function deriveHudTheme(seed: HudThemeSeed): HudTheme — Derives a coherent full theme from an accent/surface pair — shades, edges, dim text, frames, bar troughs, and slots all fall out of the seed, so a game gets one consistent look across the `packages/react` primitives and the `registry/jgengine` blocks without naming 50 tokens. Hex seeds only (`#rgb` or `#rrggbb`).
1344
1394
  - `hudSkinVars` (function): function hudSkinVars(skin?: HudSkin): CSSProperties — Converts a skin into the CSS variables consumed by shared HUD components.
1345
1395
  - `hudTheme` (function): function hudTheme(overrides: HudThemeOverrides): HudTheme — Builds a full {@link HudTheme} from per-group partials, filling the rest from {@link defaultHudTheme}. Reach for this when a game wants to name exact values; use {@link deriveHudTheme} when a seed color pair is enough.
1346
- - `hudThemeVars` (function): function hudThemeVars(theme: HudTheme): CSSProperties — Emits the CSS custom properties for a theme — spread onto any HUD ancestor to re-skin the subtree.
1396
+ - `hudThemeVars` (function): function hudThemeVars(theme: HudTheme): CSSProperties — Emits theme CSS properties, resetting inherited bar fills when absent. Spread onto a HUD ancestor.
1347
1397
  - `resolveHudTheme` (function): function resolveHudTheme(theme?: HudThemePreset | HudTheme): HudTheme — Resolves a preset name (or a full theme) to a `HudTheme`; falls back to the default theme.
1348
1398
 
1349
1399
  ## @jgengine/react/hudThemePreview
@@ -1352,7 +1402,7 @@
1352
1402
 
1353
1403
  ## @jgengine/react/hudViewport
1354
1404
 
1355
- - `HudViewportContextValue` (interface): interface HudViewportContextValue — ⚠ undocumented
1405
+ - `HudViewportContextValue` (interface): interface HudViewportContextValue { fitEnabled: boolean; config: HudViewportConfig | undefined; userScale: number } — ⚠ undocumented
1356
1406
  - `HudViewportProvider` (function): function HudViewportProvider({ platforms, config, userScale, children, }: { platforms: readonly HudPlatform[] | undefined; config: HudViewportConfig | undefined; userScale?: number; children?: ReactNode; }): React.JSX.Element — Mounted by the shell around `GameUI` so every `HudCanvas` inside the game picks up the game's `platforms`/`hudFit` declaration and the player's UI scale setting without any game-side wiring.
1357
1407
  - `useHudViewport` (function): function useHudViewport(): HudViewportContextValue | null — ⚠ undocumented
1358
1408
 
@@ -1381,12 +1431,12 @@
1381
1431
 
1382
1432
  ## @jgengine/react/identity
1383
1433
 
1384
- - `BetterAuthSessionState` (interface): interface BetterAuthSessionState — ⚠ undocumented
1385
- - `BetterAuthUserShape` (interface): interface BetterAuthUserShape — ⚠ undocumented
1386
- - `ClerkUserShape` (interface): interface ClerkUserShape — ⚠ undocumented
1387
- - `ClerkUserState` (interface): interface ClerkUserState — ⚠ undocumented
1434
+ - `BetterAuthSessionState` (interface): interface BetterAuthSessionState { data: {user: BetterAuthUserShape} | null | undefined; isPending: boolean } — ⚠ undocumented
1435
+ - `BetterAuthUserShape` (interface): interface BetterAuthUserShape { id: string; name?: string | null; email?: string | null; image?: string | null } — ⚠ undocumented
1436
+ - `ClerkUserShape` (interface): interface ClerkUserShape { id: string; fullName?: string | null; username?: string | null; imageUrl?: string | null; primaryEmailAddress?: {emailAddress: string} | null; createdAt?: Date | null; lastSignInAt?: Date | null } — ⚠ undocumented
1437
+ - `ClerkUserState` (interface): interface ClerkUserState { isLoaded: boolean; isSignedIn: boolean | undefined; user: ClerkUserShape | null | undefined } — ⚠ undocumented
1388
1438
  - `GameIdentityProvider` (function): function GameIdentityProvider({ source, children, }: { source: IdentitySource; children?: ReactNode; }): React.JSX.Element — ⚠ undocumented
1389
- - `IdentitySource` (interface): interface IdentitySource — ⚠ undocumented
1439
+ - `IdentitySource` (interface): interface IdentitySource { session: AuthSession | null; isLoading: boolean; signOut?: () => void } — ⚠ undocumented
1390
1440
  - `RequireSession` (function): function RequireSession({ fallback, loading, children, }: { fallback?: ReactNode; loading?: ReactNode; children?: ReactNode; }): React.JSX.Element — ⚠ undocumented
1391
1441
  - `SignOutButton` (function): function SignOutButton({ className, children, }: { className?: string; children?: ReactNode; }): React.JSX.Element | null — ⚠ undocumented
1392
1442
  - `UserBadge` (function): function UserBadge({ className, avatarClassName, nameClassName, renderBadge, }: { className?: string; avatarClassName?: string; nameClassName?: string; renderBadge?: (session: AuthSession) => ReactNode; }): React.JSX.Element | null — ⚠ undocumented
@@ -1439,10 +1489,10 @@
1439
1489
  ## @jgengine/react/map
1440
1490
 
1441
1491
  - `Compass` (function): function Compass({ facingYaw, center, markers, width = 340, fov = (Math.PI * 2) / 3, kindStyles = DEFAULT_MARKER_KINDS, className, }: CompassProps): ReactNode — Horizontal compass strip centered on the player's facing direction, with the eight cardinals and optional marker pips from static views, an external source, or a native `MarkerSet`.
1442
- - `CompassProps` (interface): interface CompassProps — ⚠ undocumented
1492
+ - `CompassProps` (interface): interface CompassProps { facingYaw: number; center?: WorldXZ; markers?: MarkerCollection; width?: number; fov?: number; kindStyles?: Record<string, MarkerKindStyle>; className?: string } — ⚠ undocumented · used by `Compass`: Horizontal compass strip centered on the player's facing direction, with the eight cardinals and optional marker pips from static views, an …
1443
1493
  - `FullscreenMap` (function): function FullscreenMap({ open = true, bounds, contentWidth = 900, minScale = 0.5, maxScale = 8, title = "Map", onClose, onWorldClick, tool = "pan", onStrokeComplete, drawTone = "info", drawWidth = 3, children, overlayClassName, overlayStyle, ...surfaceProps }: FullscreenMapProps): ReactNode — Fullscreen pan/zoom world-map overlay — the enlarged "press M" map. Wheel to zoom (toward the cursor), drag to pan, and click to place (wire `onWorldClick` to a waypoint/annotation store). Switch `tool` to `"draw"` to freehand-draw strokes committed to `onStrokeComplete` (wire to `createAnnotationLayer`). Reuses {@link WorldMapSurface} for rendering, so terrain bake, fog, routes, zones, markers, and the player arrow all appear; a drag never fires `onWorldClick`. Compose a {@link MapLegend} or tool palette via `children`.
1444
1494
  - `FullscreenMapProps` (interface): interface FullscreenMapProps extends Omit<WorldMapSurfaceProps, "canvasWidth" | "canvasHeight" | "viewport" | "width" | "height" | "style"> — Props for {@link FullscreenMap}.
1445
- - `MapBounds` (interface): interface MapBounds — ⚠ undocumented
1495
+ - `MapBounds` (interface): interface MapBounds { minX: number; minZ: number; maxX: number; maxZ: number } — ⚠ undocumented
1446
1496
  - `MapLegend` (function): function MapLegend({ kinds, labels, kindStyles = DEFAULT_MARKER_KINDS, title = "Legend", className, style, }: MapLegendProps): ReactNode — Marker-kind key for a map/minimap — glyph + color swatch per kind, labelled. Reads the same `kindStyles` the map renders with, so the legend can never drift from the pins.
1447
1497
  - `MapLegendProps` (interface): interface MapLegendProps — Props for {@link MapLegend}.
1448
1498
  - `MapViewport` (interface): interface MapViewport — Pan/zoom transform applied to a {@link WorldMapSurface}'s content group.
@@ -1452,7 +1502,7 @@
1452
1502
  - `MinimapChromeProps` (interface): interface MinimapChromeProps — Props for `MinimapChrome`.
1453
1503
  - `MinimapPanel` (function): function MinimapPanel({ zoneLabel, clock, showCompass = true, compassProps, headerClassName, zoneLabelClassName, clockClassName, compassClassName, children, ...minimapProps }: MinimapPanelProps): ReactNode — Composed circular-minimap chrome: optional zone-label + clock header above the `Minimap`, optional `Compass` strip below. Purely a wiring layer over the existing primitives — the game supplies the zone name and clock text, this only places them; omit either slot and the header disappears.
1454
1504
  - `MinimapPanelProps` (interface): interface MinimapPanelProps extends MinimapProps — Props for `MinimapPanel` — all `MinimapProps` plus the zone-label/clock header and compass slots.
1455
- - `MinimapProps` (interface): interface MinimapProps — ⚠ undocumented
1505
+ - `MinimapProps` (interface): interface MinimapProps { markers: MarkerCollection; center: WorldXZ; worldRadius: number; fog?: FogField; size?: number; facingYaw?: number; rotate?: boolean; kindStyles?: Record<string, MarkerKindStyle>; background?: string; mapBounds?: MapBounds; routes?: reado… — ⚠ undocumented · used by `Minimap`: Framed circular minimap: optional baked terrain background, reveal-on-event fog overlay, categorized marker icons, and a facing arrow.
1456
1506
  - `MinimapTrack` (function): function MinimapTrack({ spans = [], pips = [], width = "100%", height = 12, railColor = "rgba(255,255,255,0.10)", className, style, children, }: MinimapTrackProps): ReactNode — Horizontal linear track minimap — a rounded progress rail with colored zone `spans` and gate/exit/player `pips` positioned by 0..1 fraction. The structural counterpart to the radial {@link Minimap} for corridor/route games: fractions are supplied by the caller (via core `trackFraction`), so it reads no store and stays presentation-only, sharing chrome with the radial minimap.
1457
1507
  - `MinimapTrackPip` (interface): interface MinimapTrackPip — A point marker on the {@link MinimapTrack} rail at a 0..1 fraction (e.g. a gate, the exit, or the player).
1458
1508
  - `MinimapTrackProps` (interface): interface MinimapTrackProps — Props for {@link MinimapTrack}.
@@ -1460,7 +1510,7 @@
1460
1510
  - `WaypointArrow` (function): function WaypointArrow({ relative, distance, label, formatDistance = (value) => `${Math.round(value)}m`, size = 44, color = "#f59e0b", className, style, }: WaypointArrowProps): ReactNode — On-screen guide arrow to the tracked waypoint — a HUD compass needle rotated by the facing-relative bearing, with an optional label and distance readout. Pair with `WaypointStore.guidance(playerXZ, facingYaw)`.
1461
1511
  - `WaypointArrowProps` (interface): interface WaypointArrowProps — Props for {@link WaypointArrow}.
1462
1512
  - `WorldMap` (function): function WorldMap({ markers, bounds, player, facingYaw = 0, fog, background, width = 520, height, kindStyles = DEFAULT_MARKER_KINDS, routes, zones, cellStates, onWorldClick, className, title = "World Map", onClose, }: WorldMapProps): ReactNode — Full-bounds top-down world map (the "press M" overlay): baked terrain background, reveal-on-event fog, all markers with labels, and the player. Rectangular linear projection over the supplied world `bounds`. Framed panel wrapper over {@link WorldMapSurface}.
1463
- - `WorldMapProps` (interface): interface WorldMapProps — ⚠ undocumented
1513
+ - `WorldMapProps` (interface): interface WorldMapProps { markers: MarkerCollection; bounds: MapBounds; player?: WorldXZ; facingYaw?: number; fog?: FogField; background?: string; width?: number; height?: number; kindStyles?: Record<string, MarkerKindStyle>; routes?: readonly MapRoute[]; zones?: … — ⚠ undocumented · used by `WorldMap`: Full-bounds top-down world map (the "press M" overlay): baked terrain background, reveal-on-event fog, all markers with labels, and the play…
1464
1514
  - `WorldMapSurface` (function): function WorldMapSurface({ markers, bounds, player, facingYaw = 0, fog, background, width = 520, height, kindStyles = DEFAULT_MARKER_KINDS, routes, zones, cellStates, onWorldClick, canvasWidth, canvasHeight, viewport = IDENTITY_VIEWPORT, style, }: WorldMapSurfaceProps): ReactNode — The bare top-down map `<svg>` shared by {@link WorldMap} (framed panel) and {@link FullscreenMap} (pan/zoom overlay): baked terrain background, fog, map layers, markers with labels, and the player arrow, drawn under an optional viewport transform. Rectangular linear projection over the world `bounds`.
1465
1515
  - `WorldMapSurfaceProps` (interface): interface WorldMapSurfaceProps — Props for {@link WorldMapSurface}.
1466
1516
  - `useFog` (function): function useFog(fog: FogField): ReturnType<FogField["cells"]> — ⚠ undocumented
@@ -1579,6 +1629,11 @@
1579
1629
  - `ScoreboardProps` (interface): interface ScoreboardProps — Props for {@link Scoreboard}. Pass either `entries` (already ranked) or `rows` + `options`.
1580
1630
  - `ScoreboardTheme` (interface): interface ScoreboardTheme — Reskin tokens for {@link Scoreboard}. Every color/spacing value reads a `--jg-*` token first.
1581
1631
 
1632
+ ## @jgengine/react/scrollRail
1633
+
1634
+ - `ScrollRail` (function): function ScrollRail({ children, label, className, style, gap = 6 }: ScrollRailProps): React.JSX.Element — Native horizontal scrolling with overflow-only navigation and automatic focus reveal. Callers own item markup, selection, keyboard semantics, and skin through HudTheme or style.
1635
+ - `ScrollRailProps` (interface): interface ScrollRailProps — Props for caller-composed horizontally scrolling controls.
1636
+
1582
1637
  ## @jgengine/react/selectSnapshot
1583
1638
 
1584
1639
  - `SelectCache` (type): type SelectCache<TSnapshot, TSelected> = { ready: boolean; snapshot: TSnapshot; value: TSelected; } — ⚠ undocumented
@@ -1709,9 +1764,9 @@
1709
1764
  - `MicToggle` (function): function MicToggle({ voice, className, mutedLabel, unmutedLabel, }: { voice: VoiceState; className?: string; mutedLabel?: ReactNode; unmutedLabel?: ReactNode; }): React.JSX.Element — ⚠ undocumented
1710
1765
  - `PushToTalkButton` (function): function PushToTalkButton({ voice, className, children, }: { voice: VoiceState; className?: string; children?: ReactNode; }): React.JSX.Element — ⚠ undocumented
1711
1766
  - `SpeakingIndicator` (function): function SpeakingIndicator({ voice, userId, className, threshold = 0.01, children, }: { voice: VoiceState; userId: string; className?: string; threshold?: number; children?: ReactNode; }): React.JSX.Element — ⚠ undocumented
1712
- - `UseVoiceOptions` (interface): interface UseVoiceOptions — ⚠ undocumented
1767
+ - `UseVoiceOptions` (interface): interface UseVoiceOptions { transport?: VoiceTransport; channelId?: string; mode?: PushToTalkMode; resolveRoutes?: () => readonly VoiceRoute[]; getUserMedia?: (constraints: MediaStreamConstraints) => Promise<MediaStream> } — ⚠ undocumented · used by `useVoice`: Mic capture + push-to-talk + channel roster over the VoiceTransport signaling seam.
1713
1768
  - `VoiceRoster` (function): function VoiceRoster({ voice, className, participantClassName, renderParticipant, }: { voice: VoiceState; className?: string; participantClassName?: string; renderParticipant?: (participant: VoiceParticipant, gain: number) => ReactNode; }): React.JSX.Element — ⚠ undocumented
1714
- - `VoiceState` (interface): interface VoiceState — ⚠ undocumented
1769
+ - `VoiceState` (interface): interface VoiceState { supported: boolean; micStream: MediaStream | null; micError: string | null; requestMic(): Promise<boolean>; transmitting: boolean; status: PushToTalkStatus; mode: PushToTalkMode; setMode(mode: PushToTalkMode): void; muted: boolean; setMut… — ⚠ undocumented · used by `useVoice`: Mic capture + push-to-talk + channel roster over the VoiceTransport signaling seam.
1715
1770
  - `useVoice` (function): function useVoice(options?: UseVoiceOptions): VoiceState — Mic capture + push-to-talk + channel roster over the VoiceTransport signaling seam. Transmission gates the captured tracks' `enabled` flag; the media plane that actually moves audio bytes (WebRTC/SFU) stays behind the transport, host-supplied. Call once per voice channel and hand the returned state to the voice components.
1716
1771
 
1717
1772
  ## @jgengine/react/waveHud
@@ -1763,16 +1818,18 @@
1763
1818
 
1764
1819
  ## @jgengine/shell/audio/audioEngine
1765
1820
 
1766
- - `AudioEmitterHandle` (interface): interface AudioEmitterHandle — ⚠ undocumented
1767
- - `AudioEngine` (interface): interface AudioEngine — ⚠ undocumented
1768
- - `AudioSceneConfig` (interface): interface AudioSceneConfig — ⚠ undocumented
1821
+ - `AudioEmitterHandle` (interface): interface AudioEmitterHandle { isPlaying?(): boolean; setPosition(position: Vec3): void; setRate(rate: number): void; setGain(gain: number): void; setLowpass(hz: number): void; setHighpass(hz: number): void; setVelocity(velocity: Vec3): void; stop(): void } — ⚠ undocumented
1822
+ - `AudioEngine` (interface): interface AudioEngine { setListenerPose(pose: ListenerPose | Vec3): void; setVoiceLimit(maxTotal: number, overflow?: VoiceAllocatorConfig["overflow"]): void; playOneShot(soundId: string, position?: Vec3): void; playLoop(soundId: string, position?: Vec3): AudioEm… — ⚠ undocumented · used by `createAudioEngine`: Game-owned sound catalog playback with bounded SFX reservations, spatial panning, and shared buses.
1823
+ - `AudioSceneConfig` (interface): interface AudioSceneConfig { sounds?: Record<string, SoundDef>; maxVoices?: number; voiceOverflow?: VoiceAllocatorConfig["overflow"]; voiceAllocator?: VoiceAllocator; buses?: Record<string, AudioBusDef>; music?: Record<string, MusicTheme>; musicBus?: string } — ⚠ undocumented · used by `createAudioEngine`: Game-owned sound catalog playback with bounded SFX reservations, spatial panning, and shared buses.
1769
1824
  - `ListenerPose` (interface): interface ListenerPose — Position and orientation for a spatial-audio listener.
1770
- - `Vec3` (interface): interface Vec3 — ⚠ undocumented
1771
- - `createAudioEngine` (function): function createAudioEngine(config: AudioSceneConfig = {}): AudioEngine — ⚠ undocumented
1825
+ - `Vec3` (interface): interface Vec3 { x: number; y: number; z: number } — ⚠ undocumented · used by `coverPoints` (@jgengine/core/ai/tacticalQueries): Returns boundary locations that are hidden from a threat.
1826
+ - `createAudioEngine` (function): function createAudioEngine(config: AudioSceneConfig = {}): AudioEngine — Game-owned sound catalog playback with bounded SFX reservations, spatial panning, and shared buses.
1772
1827
 
1773
1828
  ## @jgengine/shell/audio/loopParams
1774
1829
 
1830
+ - `MAX_LOOP_CUTOFF` (const): const MAX_LOOP_CUTOFF: 22050 — Highest live filter cutoff, Hz; the engine also caps it at the context's Nyquist frequency.
1775
1831
  - `MAX_LOOP_RATE` (const): const MAX_LOOP_RATE: 4 — Highest live playback-rate multiplier (two octaves above authored pitch).
1832
+ - `MIN_LOOP_CUTOFF` (const): const MIN_LOOP_CUTOFF: 10 — Lowest live filter cutoff, Hz.
1776
1833
  - `MIN_LOOP_RATE` (const): const MIN_LOOP_RATE: 0.25 — Pure clamps for retained-loop live control (#1051). Kept dependency-free so the playback-rate/gain windows are one testable source of truth the audio engine reads.
1777
1834
 
1778
1835
  ## @jgengine/shell/audio/musicDirector
@@ -1786,8 +1843,9 @@
1786
1843
 
1787
1844
  ## @jgengine/shell/audio/synthEngine
1788
1845
 
1846
+ - `SynthPlayback` (interface): interface SynthPlayback — Scheduled procedural sources; stopping releases their source/filter/envelope graph.
1789
1847
  - `createNoiseBuffer` (function): function createNoiseBuffer(ctx: BaseAudioContext): AudioBuffer — Build the shared 1-second mono white-noise buffer every noise voice samples from.
1790
- - `realizeSynthPatch` (function): function realizeSynthPatch(ctx: BaseAudioContext, out: AudioNode, noiseBuf: AudioBuffer, patch: SynthPatch): void — Realise a procedural cue on Web Audio: every voice is scheduled at `ctx.currentTime + delay` into `out`, summed into one one-shot. `noiseBuf` is the shared buffer from {@link createNoiseBuffer}.
1848
+ - `realizeSynthPatch` (function): function realizeSynthPatch(ctx: BaseAudioContext, out: AudioNode, noiseBuf: AudioBuffer, patch: SynthPatch): SynthPlayback — Realise a procedural cue on Web Audio: every voice is scheduled at `ctx.currentTime + delay` into `out`, summed into one one-shot. `noiseBuf` is the shared buffer from {@link createNoiseBuffer}.
1791
1849
 
1792
1850
  ## @jgengine/shell/behaviour
1793
1851
 
@@ -1802,26 +1860,28 @@
1802
1860
  - `CAMERA_TRANSPARENT_USERDATA` (const): const CAMERA_TRANSPARENT_USERDATA: { readonly jgCameraTransparent: true } — userData applied to the group wrapping author `WorldOverlay` decor so the spring-arm ignores it by default.
1803
1861
  - `CameraFollowListener` (type): type CameraFollowListener = (state: CameraFollowState) => void — ⚠ undocumented
1804
1862
  - `CameraOccluder` (interface): interface CameraOccluder — Camera spring-arm occlusion filtering.
1805
- - `CameraShakeChannel` (interface): interface CameraShakeChannel — ⚠ undocumented
1863
+ - `CameraShakeChannel` (interface): interface CameraShakeChannel { shake(amplitude: number, decayPerSecond?: number): void; step(dt: number): void; sample(config?: Parameters<typeof shakeOffset>[1]): ShakeOffset; trauma(): number } — ⚠ undocumented · used by `defaultCameraShakeChannel`: Process-wide default channel.
1806
1864
  - `CameraShakeContext` (const): const CameraShakeContext: React.Context<CameraShakeChannel> — ⚠ undocumented
1807
1865
  - `ControllerCameraShake` (function): function ControllerCameraShake({ controller, priority = GAME_SIM_FRAME_PRIORITY, }: ControllerCameraShakeProps): ReactNode — The shell-side consumer of a core {@link CameraShakeController}: each frame it calls `controller.update(delta)` to bleed trauma, reads the pooled `controller.offset()`, and applies it additively to the active camera — a positional kick plus a pitch/yaw/roll rotation — so the view VISIBLY shakes on impacts. It runs after the camera rig (which re-poses the camera to its base every frame), so the shake composes with any rig without a manual save/restore and without fighting the built-in `shakeChannel`. Renders nothing.
1808
1866
  - `ControllerCameraShakeProps` (interface): interface ControllerCameraShakeProps — Props for {@link ControllerCameraShake}.
1867
+ - `FirstPersonMuzzleReader` (type): type FirstPersonMuzzleReader = (target: Vector3) => boolean — Writes a live presentation muzzle in world space; false means this rig has no muzzle available.
1809
1868
  - `GAME_SIM_FRAME_PRIORITY` (const): const GAME_SIM_FRAME_PRIORITY: 0 — Run simulation/movement before orbit follow so poses are current.
1810
1869
  - `GameCameraRig` (function): function GameCameraRig({ yawRef, pitchRef, config, onDragChange, pointerControls, panKeysEnabled, director, viewmodel, }: GameCameraRigProps): React.JSX.Element — ⚠ undocumented
1811
- - `GameCameraRigProps` (interface): interface GameCameraRigProps — ⚠ undocumented
1812
- - `GameFirstPersonCamera` (function): function GameFirstPersonCamera({ yawRef, pitchRef, config, followEntityId, viewmodel, }: GameFirstPersonCameraProps): React.JSX.Element | null — ⚠ undocumented
1813
- - `GameFirstPersonCameraProps` (interface): interface GameFirstPersonCameraProps — ⚠ undocumented
1870
+ - `GameCameraRigProps` (interface): interface GameCameraRigProps { yawRef: MutableRefObject<number>; pitchRef: MutableRefObject<number>; config?: GameCameraConfig; onDragChange?: (dragging: boolean) => void; pointerControls?: boolean; panKeysEnabled?: boolean; director?: CameraDirector; viewmodel?: Compo… — ⚠ undocumented
1871
+ - `GameFirstPersonCamera` (function): function GameFirstPersonCamera({ yawRef, pitchRef, config, followEntityId, viewmodel, weapon, }: GameFirstPersonCameraProps): React.JSX.Element | null — ⚠ undocumented
1872
+ - `GameFirstPersonCameraProps` (interface): interface GameFirstPersonCameraProps { yawRef: MutableRefObject<number>; pitchRef: MutableRefObject<number>; config?: FirstPersonCameraConfig; followEntityId?: string; viewmodel?: ComponentType<ViewmodelProps>; weapon?: (entityId: string) => CameraWeaponView | null } — ⚠ undocumented
1814
1873
  - `GameInspectionCamera` (function): function GameInspectionCamera({ config: configPatch }: GameInspectionCameraProps): React.JSX.Element — Model-viewer / editor rig (#207.7, #866): left-click selects (editor), middle-drag pans, right-drag orbits, scroll zooms toward a configurable anchor. Orbits a fixed `target`; never reads player/entity state.
1815
- - `GameInspectionCameraProps` (interface): interface GameInspectionCameraProps — ⚠ undocumented
1874
+ - `GameInspectionCameraProps` (interface): interface GameInspectionCameraProps { config?: InspectionCameraConfig } — ⚠ undocumented · used by `GameInspectionCamera`: Model-viewer / editor rig (#207.7, #866): left-click selects (editor), middle-drag pans, right-drag orbits, scroll zooms toward a configurab…
1816
1875
  - `GameOrbitCamera` (function): function GameOrbitCamera({ yawRef, pitchRef, config: configPatch, followEntityId, resolveFollowTarget, onDragChange, onCameraFollow, pointerControls = false, }: GameOrbitCameraProps): React.JSX.Element — ⚠ undocumented
1817
- - `GameOrbitCameraProps` (interface): interface GameOrbitCameraProps — ⚠ undocumented
1876
+ - `GameOrbitCameraProps` (interface): interface GameOrbitCameraProps { yawRef: MutableRefObject<number>; pitchRef?: MutableRefObject<number>; config?: Partial<OrbitCameraConfig>; followEntityId?: string; resolveFollowTarget?: (entity: SceneEntity) => Vec3; onDragChange?: (dragging: boolean) => void; onCamera… — ⚠ undocumented
1818
1877
  - `PlayerFovProvider` (function): function PlayerFovProvider({ config, orthographic, children, }: { config?: GameCameraConfig; orthographic: boolean; children: ReactNode; }): React.JSX.Element — ⚠ undocumented
1819
1878
  - `PlayerFovSlider` (function): function PlayerFovSlider(): React.JSX.Element | null — ⚠ undocumented
1820
- - `PlayerFovState` (interface): interface PlayerFovState — ⚠ undocumented
1879
+ - `PlayerFovState` (interface): interface PlayerFovState { fov: number; bounds: PlayerFovBounds; enabled: boolean; persist: boolean; setFov: (value: number) => void; compose: (poseFov: number, mode?: "relative" | "absolute") => number } — ⚠ undocumented
1821
1880
  - `ViewmodelProps` (interface): interface ViewmodelProps — Props handed to a custom viewmodel component (#542): a live cue ref (velocity/bob/firing/reloading/recoil/hit) for the followed entity, driven from your own `useFrame` — read `cuesRef.current` there rather than storing it as render state.
1822
1881
  - `defaultCameraShakeChannel` (const): const defaultCameraShakeChannel: CameraShakeChannel — Process-wide default channel. A shell mounts its own channel via `CameraShakeContext`, but game systems that have no React context (e.g. a `loop.onTick` reacting to `entity.died`) can import `cameraShake` and feed the default channel directly.
1823
1882
  - `isCameraOccluderTransparent` (function): function isCameraOccluderTransparent(object: CameraOccluder | null | undefined): boolean — Should the camera spring-arm ignore this raycast hit? Walks the object up its `.parent` chain and honors the nearest camera tag: `jgCameraCollide === true` blocks (opt back in), `jgCameraTransparent === true` passes through. Untagged geometry blocks as before, so engine-owned ground/entities are unaffected.
1824
- - `readFirstPersonMuzzle` (function): function readFirstPersonMuzzle(target: THREE.Vector3): boolean — World position of the first-person weapon muzzle, or false when no viewmodel is mounted.
1883
+ - `readFirstPersonMuzzle` (function): function readFirstPersonMuzzle(target: THREE.Vector3, camera?: THREE.Camera): boolean — Presentation muzzle in world space; camera-scoped when supplied, false leaves target unchanged.
1884
+ - `registerFirstPersonMuzzle` (function): function registerFirstPersonMuzzle(camera: Camera, reader: FirstPersonMuzzleReader): () => void — Register a custom rig's presentation muzzle for its Three.js camera. Newest available custom reader wins over that camera's stock muzzle; call the returned cleanup on unmount.
1825
1885
  - `usePlayerFov` (function): function usePlayerFov(): PlayerFovState — ⚠ undocumented
1826
1886
 
1827
1887
  ## @jgengine/shell/camera/ControllerCameraShake
@@ -1832,37 +1892,41 @@
1832
1892
  ## @jgengine/shell/camera/GameCameraRig
1833
1893
 
1834
1894
  - `GameCameraRig` (function): function GameCameraRig({ yawRef, pitchRef, config, onDragChange, pointerControls, panKeysEnabled, director, viewmodel, }: GameCameraRigProps): React.JSX.Element — ⚠ undocumented
1835
- - `GameCameraRigProps` (interface): interface GameCameraRigProps — ⚠ undocumented
1895
+ - `GameCameraRigProps` (interface): interface GameCameraRigProps { yawRef: MutableRefObject<number>; pitchRef: MutableRefObject<number>; config?: GameCameraConfig; onDragChange?: (dragging: boolean) => void; pointerControls?: boolean; panKeysEnabled?: boolean; director?: CameraDirector; viewmodel?: Compo… — ⚠ undocumented
1836
1896
 
1837
1897
  ## @jgengine/shell/camera/GameFirstPersonCamera
1838
1898
 
1839
- - `GameFirstPersonCamera` (function): function GameFirstPersonCamera({ yawRef, pitchRef, config, followEntityId, viewmodel, }: GameFirstPersonCameraProps): React.JSX.Element | null — ⚠ undocumented
1840
- - `GameFirstPersonCameraProps` (interface): interface GameFirstPersonCameraProps — ⚠ undocumented
1899
+ - `GameFirstPersonCamera` (function): function GameFirstPersonCamera({ yawRef, pitchRef, config, followEntityId, viewmodel, weapon, }: GameFirstPersonCameraProps): React.JSX.Element | null — ⚠ undocumented
1900
+ - `GameFirstPersonCameraProps` (interface): interface GameFirstPersonCameraProps { yawRef: MutableRefObject<number>; pitchRef: MutableRefObject<number>; config?: FirstPersonCameraConfig; followEntityId?: string; viewmodel?: ComponentType<ViewmodelProps>; weapon?: (entityId: string) => CameraWeaponView | null } — ⚠ undocumented
1841
1901
  - `ViewmodelProps` (interface): interface ViewmodelProps — Props handed to a custom viewmodel component (#542): a live cue ref (velocity/bob/firing/reloading/recoil/hit) for the followed entity, driven from your own `useFrame` — read `cuesRef.current` there rather than storing it as render state.
1842
- - `readFirstPersonMuzzle` (function): function readFirstPersonMuzzle(target: THREE.Vector3): boolean — World position of the first-person weapon muzzle, or false when no viewmodel is mounted.
1902
+ - `readFirstPersonMuzzle` (function): function readFirstPersonMuzzle(target: THREE.Vector3, camera?: THREE.Camera): boolean — Presentation muzzle in world space; camera-scoped when supplied, false leaves target unchanged.
1843
1903
 
1844
1904
  ## @jgengine/shell/camera/GameInspectionCamera
1845
1905
 
1846
1906
  - `GameInspectionCamera` (function): function GameInspectionCamera({ config: configPatch }: GameInspectionCameraProps): React.JSX.Element — Model-viewer / editor rig (#207.7, #866): left-click selects (editor), middle-drag pans, right-drag orbits, scroll zooms toward a configurable anchor. Orbits a fixed `target`; never reads player/entity state.
1847
- - `GameInspectionCameraProps` (interface): interface GameInspectionCameraProps — ⚠ undocumented
1907
+ - `GameInspectionCameraProps` (interface): interface GameInspectionCameraProps { config?: InspectionCameraConfig } — ⚠ undocumented · used by `GameInspectionCamera`: Model-viewer / editor rig (#207.7, #866): left-click selects (editor), middle-drag pans, right-drag orbits, scroll zooms toward a configurab…
1848
1908
 
1849
1909
  ## @jgengine/shell/camera/GameOrbitCamera
1850
1910
 
1851
1911
  - `CameraFollowListener` (type): type CameraFollowListener = (state: CameraFollowState) => void — ⚠ undocumented
1852
1912
  - `GameOrbitCamera` (function): function GameOrbitCamera({ yawRef, pitchRef, config: configPatch, followEntityId, resolveFollowTarget, onDragChange, onCameraFollow, pointerControls = false, }: GameOrbitCameraProps): React.JSX.Element — ⚠ undocumented
1853
- - `GameOrbitCameraProps` (interface): interface GameOrbitCameraProps — ⚠ undocumented
1913
+ - `GameOrbitCameraProps` (interface): interface GameOrbitCameraProps { yawRef: MutableRefObject<number>; pitchRef?: MutableRefObject<number>; config?: Partial<OrbitCameraConfig>; followEntityId?: string; resolveFollowTarget?: (entity: SceneEntity) => Vec3; onDragChange?: (dragging: boolean) => void; onCamera… — ⚠ undocumented
1854
1914
  - `seedOrbitCameraTarget` (function): function seedOrbitCameraTarget(camera: Camera, target: Vector3, distance: number, height: number): void — Seed orbit target before controls mount (demo spawn at origin).
1855
1915
 
1856
1916
  ## @jgengine/shell/camera/PlayerFov
1857
1917
 
1858
1918
  - `PlayerFovProvider` (function): function PlayerFovProvider({ config, orthographic, children, }: { config?: GameCameraConfig; orthographic: boolean; children: ReactNode; }): React.JSX.Element — ⚠ undocumented
1859
1919
  - `PlayerFovSlider` (function): function PlayerFovSlider(): React.JSX.Element | null — ⚠ undocumented
1860
- - `PlayerFovState` (interface): interface PlayerFovState — ⚠ undocumented
1920
+ - `PlayerFovState` (interface): interface PlayerFovState { fov: number; bounds: PlayerFovBounds; enabled: boolean; persist: boolean; setFov: (value: number) => void; compose: (poseFov: number, mode?: "relative" | "absolute") => number } — ⚠ undocumented
1861
1921
  - `usePlayerFov` (function): function usePlayerFov(): PlayerFovState — ⚠ undocumented
1862
1922
 
1923
+ ## @jgengine/shell/camera/Viewports
1924
+
1925
+ - `ViewportHuds` (function): function ViewportHuds({ ctx, config, children, }: { ctx: GameContext; config?: ViewportsConfig; children: (slot: LocalPlayerSlot, viewport: ViewportDef) => ReactNode; }): React.JSX.Element — One absolutely-positioned HUD root per viewport, laid over the canvas: render each seat's own health, prompts or score with `children(slot)`. Pass the same `viewports` config the game gave `defineGame`.
1926
+
1863
1927
  ## @jgengine/shell/camera/cameraBlendMath
1864
1928
 
1865
- - `CameraBlendScratch` (interface): interface CameraBlendScratch — ⚠ undocumented
1929
+ - `CameraBlendScratch` (interface): interface CameraBlendScratch { fromPos: Vector3; fromQuat: Quaternion; toPos: Vector3; toQuat: Quaternion; fov: number; elapsed: number; duration: number } — ⚠ undocumented
1866
1930
 
1867
1931
  ## @jgengine/shell/camera/cameraCollision
1868
1932
 
@@ -1874,43 +1938,52 @@
1874
1938
 
1875
1939
  - `CAMERA_POST_FRAME_PRIORITY` (const): const CAMERA_POST_FRAME_PRIORITY: number — ⚠ undocumented
1876
1940
  - `CAMERA_RIG_FRAME_PRIORITY` (const): const CAMERA_RIG_FRAME_PRIORITY: -1 — ⚠ undocumented
1877
- - `CameraBlendScratch` (interface): interface CameraBlendScratch — ⚠ undocumented
1941
+ - `CameraBlendScratch` (interface): interface CameraBlendScratch { fromPos: Vector3; fromQuat: Quaternion; toPos: Vector3; toQuat: Quaternion; fov: number; elapsed: number; duration: number } — ⚠ undocumented
1878
1942
  - `ChaseRig` (function): function ChaseRig(props: RigProps): null — ⚠ undocumented
1879
1943
  - `CinematicRig` (function): function CinematicRig(props: RigProps & { onComplete?: () => void }): null — ⚠ undocumented
1880
1944
  - `LockOnRig` (function): function LockOnRig(props: RigProps): null — ⚠ undocumented
1881
1945
  - `ObserverRig` (function): function ObserverRig(props: RigProps): null — Detached spectator/photo cam (#120): binds to any entity or fixed point and auto-orbits it, reading no player input at all — the van CCTV / photo-mode / kill-cam rig. Distinct from every other rig, which drives from mouse/keys.
1882
- - `RigProps` (interface): interface RigProps — ⚠ undocumented
1946
+ - `RigProps` (interface): interface RigProps { yawRef: MutableRefObject<number>; pitchRef: MutableRefObject<number>; config?: GameCameraConfig; followEntityId?: string | null; absoluteFov?: boolean } — ⚠ undocumented · used by `ObserverRig`: Detached spectator/photo cam (#120): binds to any entity or fixed point and auto-orbits it, reading no player input at all — the van CCTV / …
1883
1947
  - `RtsRig` (function): function RtsRig(props: RigProps & { panKeysEnabled?: boolean }): null — ⚠ undocumented
1884
1948
  - `ShoulderRig` (function): function ShoulderRig(props: RigProps): null — ⚠ undocumented
1885
1949
  - `SideScrollRig` (function): function SideScrollRig(props: RigProps): null — Fixed side-on 2.5D follow rig: watches the followed entity from the perpendicular axis, never reading WASD/mouse-look.
1886
1950
  - `TopDownRig` (function): function TopDownRig(props: RigProps): null — ⚠ undocumented
1887
1951
 
1952
+ ## @jgengine/shell/camera/chaseHeading
1953
+
1954
+ - `resolveChaseHeading` (function): function resolveChaseHeading(config: Pick<ChaseCameraConfig, "headingSource" | "view"> | undefined, bodyYaw: number, inputYaw: number | undefined, initialYaw?: number): number — Select a chase heading without feeding target facing or camera smoothing back into input-owned yaw. An undefined input yaw seeds from `initialYaw`, or the acquired target's body heading. Seat views always use body heading; omitting `headingSource` preserves body follow.
1955
+
1956
+ ## @jgengine/shell/camera/customMuzzle
1957
+
1958
+ - `FirstPersonMuzzleReader` (type): type FirstPersonMuzzleReader = (target: Vector3) => boolean — Writes a live presentation muzzle in world space; false means this rig has no muzzle available.
1959
+ - `registerFirstPersonMuzzle` (function): function registerFirstPersonMuzzle(camera: Camera, reader: FirstPersonMuzzleReader): () => void — Register a custom rig's presentation muzzle for its Three.js camera. Newest available custom reader wins over that camera's stock muzzle; call the returned cleanup on unmount.
1960
+
1888
1961
  ## @jgengine/shell/camera/fovPreference
1889
1962
 
1890
1963
  - `PLAYER_FOV_DEFAULT` (const): const PLAYER_FOV_DEFAULT: 55 — ⚠ undocumented
1891
1964
  - `PLAYER_FOV_MAX` (const): const PLAYER_FOV_MAX: 120 — ⚠ undocumented
1892
1965
  - `PLAYER_FOV_MIN` (const): const PLAYER_FOV_MIN: 40 — ⚠ undocumented
1893
1966
  - `PLAYER_FOV_STORAGE_KEY` (const): const PLAYER_FOV_STORAGE_KEY: "jgengine:player-fov" — ⚠ undocumented
1894
- - `PlayerFovBounds` (interface): interface PlayerFovBounds — ⚠ undocumented
1967
+ - `PlayerFovBounds` (interface): interface PlayerFovBounds { min: number; max: number; defaultFov: number } — ⚠ undocumented
1895
1968
 
1896
1969
  ## @jgengine/shell/camera/inspectionCameraMath
1897
1970
 
1898
1971
  - `InspectionCameraConfig` (interface): interface InspectionCameraConfig — Model-viewer / inspection rig (#207.7) — orbit + pan + anchored zoom around a fixed point, never reads player input.
1899
1972
  - `InspectionZoomAnchor` (type): type InspectionZoomAnchor = "target" | "cursor" | "center" — How scroll-zoom re-anchors the view for the inspection rig (#207.7): - `target` — dolly toward the orbit target (classic OrbitControls behavior). - `cursor` — dolly toward the point under the pointer. - `center` — dolly toward the viewport center; equivalent to `target` for an OrbitControls-driven rig, since the camera always faces `target` and that point already projects to the exact center of the viewport.
1900
- - `ResolvedInspectionCameraConfig` (interface): interface ResolvedInspectionCameraConfig — ⚠ undocumented
1973
+ - `ResolvedInspectionCameraConfig` (interface): interface ResolvedInspectionCameraConfig { anchor: InspectionZoomAnchor; target: Vec3; initialDistance: number; initialPosition: Vec3 | null; minDistance: number; maxDistance: number; minPolarAngle: number; maxPolarAngle: number; pan: boolean; rotateSpeed: number; zoomSpeed: numbe… — ⚠ undocumented
1901
1974
 
1902
1975
  ## @jgengine/shell/camera/orbitCameraMath
1903
1976
 
1904
- - `CameraFollowState` (interface): interface CameraFollowState — ⚠ undocumented
1977
+ - `CameraFollowState` (interface): interface CameraFollowState { entityId: string; target: Vec3; camera: Vec3; distance: number } — ⚠ undocumented
1905
1978
  - `DEFAULT_ORBIT_CAMERA` (const): const DEFAULT_ORBIT_CAMERA: ResolvedOrbitCameraConfig — ⚠ undocumented
1906
1979
  - `GAME_SIM_FRAME_PRIORITY` (const): const GAME_SIM_FRAME_PRIORITY: 0 — Run simulation/movement before orbit follow so poses are current.
1907
1980
  - `ORBIT_CAMERA_FRAME_PRIORITY` (const): const ORBIT_CAMERA_FRAME_PRIORITY: -1 — Orbit follow reads the latest entity pose after GAME_SIM_FRAME_PRIORITY.
1908
- - `OrbitCameraConfig` (interface): interface OrbitCameraConfig — ⚠ undocumented
1981
+ - `OrbitCameraConfig` (interface): interface OrbitCameraConfig { minDistance?: number; maxDistance?: number; targetHeight?: number; initialDistance?: number; initialHeight?: number; initialYaw?: number; initialPitch?: number; pitchClamp?: readonly [number, number]; targetOffset?: Partial<Vec3>; followL… — ⚠ undocumented
1909
1982
  - `OrbitCollisionConfig` (interface): interface OrbitCollisionConfig — Spring-arm occlusion config for the orbit rig.
1910
- - `OrbitFollowRuntimeState` (interface): interface OrbitFollowRuntimeState — ⚠ undocumented
1983
+ - `OrbitFollowRuntimeState` (interface): interface OrbitFollowRuntimeState { target: Vec3; camera: Vec3; lockedDistance: number | null } — ⚠ undocumented
1911
1984
  - `ResolvedOrbitCameraConfig` (interface): interface ResolvedOrbitCameraConfig — Fully resolved shell config after merging with DEFAULT_ORBIT_CAMERA.
1912
1985
  - `ResolvedOrbitCollision` (interface): interface ResolvedOrbitCollision — An {@link OrbitCollisionConfig} with every field resolved to a concrete value.
1913
- - `Vec3` (interface): interface Vec3 — ⚠ undocumented
1986
+ - `Vec3` (interface): interface Vec3 { x: number; y: number; z: number } — ⚠ undocumented · used by `coverPoints` (@jgengine/core/ai/tacticalQueries): Returns boundary locations that are hidden from a threat.
1914
1987
 
1915
1988
  ## @jgengine/shell/camera/rigMath
1916
1989
 
@@ -1918,28 +1991,38 @@
1918
1991
  - `CALIBRATED_TRAUMA_SHAKE_FREQUENCY` (const): const CALIBRATED_TRAUMA_SHAKE_FREQUENCY: 32 — Calibrated shake noise frequency (Hz) for `traumaShake`.
1919
1992
  - `CALIBRATED_TRAUMA_SHAKE_MAX_OFFSET` (const): const CALIBRATED_TRAUMA_SHAKE_MAX_OFFSET: 0.55 — Calibrated positional shake amplitude at full trauma (world units) for `traumaShake`.
1920
1993
  - `CALIBRATED_TRAUMA_SHAKE_MAX_ROLL` (const): const CALIBRATED_TRAUMA_SHAKE_MAX_ROLL: 0.1 — Calibrated rotational shake amplitude at full trauma (radians) for `traumaShake`.
1921
- - `CameraPose` (interface): interface CameraPose — ⚠ undocumented
1922
- - `CinematicSample` (interface): interface CinematicSample — ⚠ undocumented
1923
- - `DirectorCameraValues` (interface): interface DirectorCameraValues — ⚠ undocumented
1924
- - `ResolvedChase` (interface): interface ResolvedChase — ⚠ undocumented
1925
- - `ResolvedDirectedCamera` (interface): interface ResolvedDirectedCamera — ⚠ undocumented
1926
- - `ResolvedObserver` (interface): interface ResolvedObserver — ⚠ undocumented
1927
- - `ResolvedShoulder` (interface): interface ResolvedShoulder — ⚠ undocumented
1928
- - `ResolvedSideScroll` (interface): interface ResolvedSideScroll — ⚠ undocumented
1929
- - `ResolvedTopDown` (interface): interface ResolvedTopDown — ⚠ undocumented
1930
- - `ShakeOffset` (interface): interface ShakeOffset — ⚠ undocumented
1931
- - `StaticCameraValues` (interface): interface StaticCameraValues — ⚠ undocumented
1932
- - `TraumaState` (interface): interface TraumaState — ⚠ undocumented
1994
+ - `CameraPose` (interface): interface CameraPose { position: Vec3; lookAt: Vec3; fov: number } — ⚠ undocumented
1995
+ - `ChaseBoomClamp` (type): type ChaseBoomClamp = (pivot: Vec3, desired: Vec3) => Vec3 — Pulls a desired camera position in toward `pivot` when something blocks the boom.
1996
+ - `ChaseRigState` (interface): interface ChaseRigState — Mutable per-rig chase state; {@link stepChase} advances it in place.
1997
+ - `ChaseSample` (interface): interface ChaseSample — What the chase rig reads from its target each frame.
1998
+ - `ChaseStepResult` (interface): interface ChaseStepResult — One frame of chase output from {@link stepChase}.
1999
+ - `CinematicSample` (interface): interface CinematicSample { pose: CameraPose; done: boolean } — ⚠ undocumented
2000
+ - `DirectorCameraValues` (interface): interface DirectorCameraValues { followEntityId?: string | null; cinematic?: CinematicCameraConfig | null } — ⚠ undocumented
2001
+ - `ResolvedChase` (interface): interface ResolvedChase { distance: number; height: number; lookHeight: number; springDamping: number; shakePerSpeed: number; leadTime: number; leadMax: number; bankPerYawRate: number; bankMax: number; bankDamping: number; velocityYawBlend: number; velocityYawMinS… — ⚠ undocumented
2002
+ - `ResolvedDirectedCamera` (interface): interface ResolvedDirectedCamera { followEntityId: string | null | undefined; cinematic: CinematicCameraConfig | undefined } — ⚠ undocumented
2003
+ - `ResolvedObserver` (interface): interface ResolvedObserver { distance: number; height: number; lookHeight: number; orbitSpeed: number } — ⚠ undocumented
2004
+ - `ResolvedShoulder` (interface): interface ResolvedShoulder { shoulderOffset: number; heightOffset: number; distance: number; fov: number } — ⚠ undocumented
2005
+ - `ResolvedSideScroll` (interface): interface ResolvedSideScroll { axis: "x" | "z"; distance: number; height: number; lookHeight: number; followSmoothing: number } — ⚠ undocumented
2006
+ - `ResolvedTopDown` (interface): interface ResolvedTopDown { height: number; pitch: number; yaw: number; offset: Vec3; followSmoothing: number } — ⚠ undocumented
2007
+ - `ShakeOffset` (interface): interface ShakeOffset { x: number; y: number; roll: number } — ⚠ undocumented
2008
+ - `StaticCameraValues` (interface): interface StaticCameraValues { followEntityId?: string | null; cinematic?: CinematicCameraConfig } — ⚠ undocumented
2009
+ - `TraumaState` (interface): interface TraumaState { trauma: number; time: number } — ⚠ undocumented
1933
2010
 
1934
2011
  ## @jgengine/shell/camera/shakeChannel
1935
2012
 
1936
- - `CameraShakeChannel` (interface): interface CameraShakeChannel — ⚠ undocumented
2013
+ - `CameraShakeChannel` (interface): interface CameraShakeChannel { shake(amplitude: number, decayPerSecond?: number): void; step(dt: number): void; sample(config?: Parameters<typeof shakeOffset>[1]): ShakeOffset; trauma(): number } — ⚠ undocumented · used by `defaultCameraShakeChannel`: Process-wide default channel.
1937
2014
  - `CameraShakeContext` (const): const CameraShakeContext: React.Context<CameraShakeChannel> — ⚠ undocumented
1938
2015
  - `defaultCameraShakeChannel` (const): const defaultCameraShakeChannel: CameraShakeChannel — Process-wide default channel. A shell mounts its own channel via `CameraShakeContext`, but game systems that have no React context (e.g. a `loop.onTick` reacting to `entity.died`) can import `cameraShake` and feed the default channel directly.
1939
2016
 
1940
2017
  ## @jgengine/shell/camera/shakeChannelMath
1941
2018
 
1942
- - `CameraShakeChannel` (interface): interface CameraShakeChannel — ⚠ undocumented
2019
+ - `CameraShakeChannel` (interface): interface CameraShakeChannel { shake(amplitude: number, decayPerSecond?: number): void; step(dt: number): void; sample(config?: Parameters<typeof shakeOffset>[1]): ShakeOffset; trauma(): number } — ⚠ undocumented · used by `defaultCameraShakeChannel` (@jgengine/shell/camera): Process-wide default channel.
2020
+
2021
+ ## @jgengine/shell/combat/ProjectileModels
2022
+
2023
+ - `ProjectileModels` (function): function ProjectileModels({ model, fallback, forward = [0, 1, 0], maxInstances = 256, source }: ProjectileModelsProps): ReactElement — Mount caller-owned models at authoritative projectile positions. Flight ids determine lifetime; each frame only updates transforms, and settlement removes the model. Optional shaft/head geometry is enabled only by the caller.
2024
+ - `ProjectileModelsProps` (interface): interface ProjectileModelsProps — Render simulated flights without owning their travel or settlement.
2025
+ - `ProjectileShaft` (interface): interface ProjectileShaft — Explicit opt-in shaft/head geometry when a game has no projectile model.
1943
2026
 
1944
2027
  ## @jgengine/shell/commandSink
1945
2028
 
@@ -1980,14 +2063,14 @@
1980
2063
 
1981
2064
  ## @jgengine/shell/devtools/collisionDebug
1982
2065
 
1983
- - `AimProbeConfig` (interface): interface AimProbeConfig — ⚠ undocumented
2066
+ - `AimProbeConfig` (interface): interface AimProbeConfig { from: string; aim: Aim; originPolicy?: ShotOriginPolicy; maxDistance?: number } — ⚠ undocumented
1984
2067
  - `COLLISION_DEBUG_LAYERS` (const): const COLLISION_DEBUG_LAYERS: readonly CollisionDebugLayer[] — ⚠ undocumented
1985
- - `CollisionDebugController` (interface): interface CollisionDebugController — ⚠ undocumented
2068
+ - `CollisionDebugController` (interface): interface CollisionDebugController { getState(): CollisionDebugState; subscribe(listener: CollisionDebugListener): () => void; setLayer(layer: CollisionDebugLayer, on: boolean): void; toggleLayer(layer: CollisionDebugLayer): void; setLayers(partial: Partial<CollisionDebugLay… — ⚠ undocumented
1986
2069
  - `CollisionDebugLayer` (type): type CollisionDebugLayer = | "hitboxes" | "bodies" | "projectiles" | "muzzles" | "aimLaser" — ⚠ undocumented
1987
2070
  - `CollisionDebugLayers` (type): type CollisionDebugLayers = Record<CollisionDebugLayer, boolean> — ⚠ undocumented
1988
2071
  - `CollisionDebugListener` (type): type CollisionDebugListener = () => void — ⚠ undocumented
1989
- - `CollisionDebugState` (interface): interface CollisionDebugState — ⚠ undocumented
1990
- - `ProjectileDebugTrace` (interface): interface ProjectileDebugTrace — ⚠ undocumented
2072
+ - `CollisionDebugState` (interface): interface CollisionDebugState { layers: CollisionDebugLayers; aimProbe: AimProbeConfig | null; projectileTraces: readonly ProjectileDebugTrace[]; maxProjectileTraces: number; projectileTraceLifeMs: number } — ⚠ undocumented
2073
+ - `ProjectileDebugTrace` (interface): interface ProjectileDebugTrace { id: number; origin: EntityPosition; at: EntityPosition; hit: boolean; bornMs: number } — ⚠ undocumented
1991
2074
  - `collisionDebug` (const): const collisionDebug: CollisionDebugController — ⚠ undocumented
1992
2075
 
1993
2076
  ## @jgengine/shell/devtools/collisionDebugMath
@@ -1997,11 +2080,11 @@
1997
2080
  - `AIM_MISS_COLOR` (const): const AIM_MISS_COLOR: "#94a3b8" — ⚠ undocumented
1998
2081
  - `AIM_SOLID_COLOR` (const): const AIM_SOLID_COLOR: "#fbbf24" — ⚠ undocumented
1999
2082
  - `AimEndpointKind` (type): type AimEndpointKind = "damage" | "solid" | "miss" — ⚠ undocumented
2000
- - `AimLaserDebug` (interface): interface AimLaserDebug — ⚠ undocumented
2083
+ - `AimLaserDebug` (interface): interface AimLaserDebug { origin: EntityPosition; direction: EntityPosition; end: EntityPosition; maxDistance: number; kind: AimEndpointKind; nearest: SceneRaycastHit | null; firstImpact: SceneRaycastHit | null; queryCount: number } — ⚠ undocumented
2001
2084
  - `BODY_WIRE_COLOR` (const): const BODY_WIRE_COLOR: "#38bdf8" — ⚠ undocumented
2002
- - `CollectDebugShapesInput` (interface): interface CollectDebugShapesInput — ⚠ undocumented
2003
- - `ComputeAimLaserInput` (interface): interface ComputeAimLaserInput — ⚠ undocumented
2004
- - `DebugShapeEntry` (interface): interface DebugShapeEntry — ⚠ undocumented
2085
+ - `CollectDebugShapesInput` (interface): interface CollectDebugShapesInput { layers: CollisionDebugLayers; entities: readonly {id: string; position: EntityPosition; rotationY: number; name?: string;}[]; objects?: readonly {instanceId: string; catalogId: string; position: EntityPosition; rotationY: number;}[]; enti… — ⚠ undocumented
2086
+ - `ComputeAimLaserInput` (interface): interface ComputeAimLaserInput { layers: CollisionDebugLayers; sceneRaycast: SceneRaycastApi; positionOf(instanceId: string): EntityPosition | undefined; rotationYOf?(instanceId: string): number | undefined; collidersOf?(instanceId: string): EntityColliderSet | null | un… — ⚠ undocumented
2087
+ - `DebugShapeEntry` (interface): interface DebugShapeEntry { key: string; targetKind: "entity" | "object"; instanceId: string; catalogId?: string; name: string; purpose: ColliderPurpose; damageEligible: boolean; blocks: boolean; position: EntityPosition; rotationY: number; shape: | {kind: "sphere";… — ⚠ undocumented
2005
2088
  - `HITBOX_WIRE_COLOR` (const): const HITBOX_WIRE_COLOR: "#f472b6" — ⚠ undocumented
2006
2089
  - `PROJECTILE_PATH_COLOR` (const): const PROJECTILE_PATH_COLOR: "#fde68a" — ⚠ undocumented
2007
2090
 
@@ -2019,12 +2102,16 @@
2019
2102
 
2020
2103
  - `diagnose` (function): function diagnose(frame: NonNullable<ReturnType<typeof devtools.frame.stats>>, longs: readonly LongFrameEvent[]): string | null — ⚠ undocumented
2021
2104
 
2105
+ ## @jgengine/shell/diagnostics/FpsCounter
2106
+
2107
+ - `FrameTally` (interface): interface FrameTally — Mutable frame tally shared between the in-Canvas probe and the DOM readout.
2108
+
2022
2109
  ## @jgengine/shell/diagnostics/RuntimeDiagnostics
2023
2110
 
2024
2111
  - `DiagnosticOverlay` (function): function DiagnosticOverlay({ diagnostics, gameName }: { diagnostics: RuntimeDiagnostic[]; gameName: string }): React.JSX.Element | null — ⚠ undocumented
2025
2112
  - `ErrorReportActions` (function): function ErrorReportActions({ report, issueTitle }: { report: string; issueTitle: string }): React.JSX.Element — "Copy error" + "File issue" buttons for any error surface. `report` is the full text to copy / prefill.
2026
2113
  - `GameUiErrorBoundary` (class): class GameUiErrorBoundary extends Component< { children: ReactNode; onRuntimeError: (error: unknown, phase: string, componentStack?: string) => void }, { failed: boolean } > — ⚠ undocumented
2027
- - `RuntimeDiagnostic` (interface): interface RuntimeDiagnostic — ⚠ undocumented
2114
+ - `RuntimeDiagnostic` (interface): interface RuntimeDiagnostic { id: number; phase: string; message: string; stack?: string; componentStack?: string; capturedAt: string } — ⚠ undocumented
2028
2115
  - `errorReportContext` (function): function errorReportContext(): string — Standalone error footer text (page, browser, engine) shared by every error surface.
2029
2116
  - `logRuntimeError` (function): function logRuntimeError(error: unknown, phase: string, componentStack?: string): Omit<RuntimeDiagnostic, "id"> — ⚠ undocumented
2030
2117
 
@@ -2035,38 +2122,38 @@
2035
2122
 
2036
2123
  ## @jgengine/shell/drivers/HudOnlyDriver
2037
2124
 
2038
- - `HudOnlyDriver` (function): function HudOnlyDriver({ ctx, playable, tracker, pointerAxisRef, gateRef, onRuntimeError, }: { ctx: GameContext; playable: PlayableGame; tracker: ActionStateTracker<string>; pointerAxisRef: { current: PointerAxisState | null }; gateRef: { current: boolean }; onRuntimeError: (error: unknown, phase: s… — ⚠ undocumented
2125
+ - `HudOnlyDriver` (function): function HudOnlyDriver({ ctx, multiplayer, serverIdRef, playable, tracker, pointerAxisRef, gateRef, onRuntimeError, }: { ctx: GameContext; multiplayer: ShellMultiplayer | null; serverIdRef: { current: string | null }; playable: PlayableGame; tracker: ActionStateTracker<string>; pointerAxisRef: { cur… — ⚠ undocumented
2039
2126
 
2040
2127
  ## @jgengine/shell/environment
2041
2128
 
2042
2129
  - `DayNightSky` (function): function DayNightSky({ cycle, radius, hazeStrength, sunGlowStrength, lights = true, keyLightIntensity = 1.1, ambientIntensity = 0.6, }: DayNightSkyProps): React.JSX.Element — Binds a {@link DayNightCycle} to the engine's existing `SkyDome` shader and a pair of lights: each frame it reads `cycle.sample()` and writes the blended tint into the dome's top/horizon/sun uniforms while the sun's arc comes from the shared `daylightStateAt` geometry. This is the turnkey presentation seam — a game mounts this one component in its scene and gets a moving, color-graded day-night sky from the serializable model, with no hand-rolled per-frame lerp and no new renderer. The cycle's `phase`/color values are free-form and never interpreted here.
2043
2130
  - `DayNightSkyProps` (interface): interface DayNightSkyProps — Props for {@link DayNightSky}: the cycle to bind plus optional dome shape and light-strength knobs.
2044
- - `DaylightCycleConfig` (interface): interface DaylightCycleConfig — ⚠ undocumented
2045
- - `DaylightProps` (interface): interface DaylightProps — ⚠ undocumented
2046
- - `DaylightState` (interface): interface DaylightState — ⚠ undocumented
2131
+ - `DaylightCycleConfig` (interface): interface DaylightCycleConfig { horizonColor?: string; zenithColor?: string; sunIntensity?: number; ambientIntensity?: number; sun?: SkySunConfig } — ⚠ undocumented
2132
+ - `DaylightProps` (interface): interface DaylightProps { sky?: SkyDomeProps | false; fog?: {color?: string; near?: number; far?: number} | false; sun?: {position?: readonly [number, number, number]; intensity?: number; color?: string}; ambient?: {skyColor?: string; groundColor?: string; intensi… — ⚠ undocumented
2133
+ - `DaylightState` (interface): interface DaylightState { sunPosition: [number, number, number]; sunIntensity: number; ambientIntensity: number; skyTop: string; skyBottom: string; background: string } — ⚠ undocumented
2047
2134
  - `EnvironmentScene` (function): function EnvironmentScene({ feature }: EnvironmentSceneProps): React.JSX.Element — ⚠ undocumented
2048
- - `EnvironmentSceneProps` (interface): interface EnvironmentSceneProps — ⚠ undocumented
2135
+ - `EnvironmentSceneProps` (interface): interface EnvironmentSceneProps { feature: EnvironmentWorldFeature } — ⚠ undocumented
2049
2136
  - `SKY_PRESET_DAY_FRACTION` (const): const SKY_PRESET_DAY_FRACTION: Record<"day" | "dusk" | "night", number> — ⚠ undocumented
2050
- - `SkyDaylightProps` (interface): interface SkyDaylightProps — ⚠ undocumented
2051
- - `SkyDomeProps` (interface): interface SkyDomeProps — ⚠ undocumented
2137
+ - `SkyDaylightProps` (interface): interface SkyDaylightProps { sky: SkyEnvironmentDescriptor; lights?: boolean; bands?: readonly BiomeBand[] } — ⚠ undocumented
2138
+ - `SkyDomeProps` (interface): interface SkyDomeProps { topColor?: string; horizonColor?: string; radius?: number; offset?: number; exponent?: number; sunDirection?: readonly [number, number, number]; sunColor?: string; sunIntensity?: number; hazeStrength?: number; cloudiness?: number; sunGlow… — ⚠ undocumented
2052
2139
  - `SkyLightOwnership` (type): type SkyLightOwnership = "authored" | "sky-default" — Policy for composing sky backdrops with `PlayableGame.lighting`: - authored lighting present → sky renders dome + fog only; lights stay game-owned - no authored lighting → sky may emit its default sun/hemisphere with the dome Time-of-day never rewrites configured lights; it only drives sky colors/fog (and sky-owned lights when the game did not author lighting).
2053
- - `TimeOfDayDaylightProps` (interface): interface TimeOfDayDaylightProps — ⚠ undocumented
2140
+ - `TimeOfDayDaylightProps` (interface): interface TimeOfDayDaylightProps { sky: SkyEnvironmentDescriptor; clock?: {calendar(): {dayFraction: number}}; lights?: boolean; bands?: readonly BiomeBand[] } — ⚠ undocumented
2054
2141
  - `VolumetricCloudsProps` (interface): interface VolumetricCloudsProps — Props for {@link VolumetricClouds} — fully-resolved cloud rules plus the shared sun direction.
2055
2142
 
2056
2143
  ## @jgengine/shell/environment
2057
2144
 
2058
2145
  - `DayNightSky` (function): function DayNightSky({ cycle, radius, hazeStrength, sunGlowStrength, lights = true, keyLightIntensity = 1.1, ambientIntensity = 0.6, }: DayNightSkyProps): React.JSX.Element — Binds a {@link DayNightCycle} to the engine's existing `SkyDome` shader and a pair of lights: each frame it reads `cycle.sample()` and writes the blended tint into the dome's top/horizon/sun uniforms while the sun's arc comes from the shared `daylightStateAt` geometry. This is the turnkey presentation seam — a game mounts this one component in its scene and gets a moving, color-graded day-night sky from the serializable model, with no hand-rolled per-frame lerp and no new renderer. The cycle's `phase`/color values are free-form and never interpreted here.
2059
2146
  - `DayNightSkyProps` (interface): interface DayNightSkyProps — Props for {@link DayNightSky}: the cycle to bind plus optional dome shape and light-strength knobs.
2060
- - `DaylightCycleConfig` (interface): interface DaylightCycleConfig — ⚠ undocumented
2061
- - `DaylightProps` (interface): interface DaylightProps — ⚠ undocumented
2062
- - `DaylightState` (interface): interface DaylightState — ⚠ undocumented
2147
+ - `DaylightCycleConfig` (interface): interface DaylightCycleConfig { horizonColor?: string; zenithColor?: string; sunIntensity?: number; ambientIntensity?: number; sun?: SkySunConfig } — ⚠ undocumented
2148
+ - `DaylightProps` (interface): interface DaylightProps { sky?: SkyDomeProps | false; fog?: {color?: string; near?: number; far?: number} | false; sun?: {position?: readonly [number, number, number]; intensity?: number; color?: string}; ambient?: {skyColor?: string; groundColor?: string; intensi… — ⚠ undocumented
2149
+ - `DaylightState` (interface): interface DaylightState { sunPosition: [number, number, number]; sunIntensity: number; ambientIntensity: number; skyTop: string; skyBottom: string; background: string } — ⚠ undocumented
2063
2150
  - `EnvironmentScene` (function): function EnvironmentScene({ feature }: EnvironmentSceneProps): React.JSX.Element — ⚠ undocumented
2064
- - `EnvironmentSceneProps` (interface): interface EnvironmentSceneProps — ⚠ undocumented
2151
+ - `EnvironmentSceneProps` (interface): interface EnvironmentSceneProps { feature: EnvironmentWorldFeature } — ⚠ undocumented
2065
2152
  - `SKY_PRESET_DAY_FRACTION` (const): const SKY_PRESET_DAY_FRACTION: Record<"day" | "dusk" | "night", number> — ⚠ undocumented
2066
- - `SkyDaylightProps` (interface): interface SkyDaylightProps — ⚠ undocumented
2067
- - `SkyDomeProps` (interface): interface SkyDomeProps — ⚠ undocumented
2153
+ - `SkyDaylightProps` (interface): interface SkyDaylightProps { sky: SkyEnvironmentDescriptor; lights?: boolean; bands?: readonly BiomeBand[] } — ⚠ undocumented
2154
+ - `SkyDomeProps` (interface): interface SkyDomeProps { topColor?: string; horizonColor?: string; radius?: number; offset?: number; exponent?: number; sunDirection?: readonly [number, number, number]; sunColor?: string; sunIntensity?: number; hazeStrength?: number; cloudiness?: number; sunGlow… — ⚠ undocumented
2068
2155
  - `SkyLightOwnership` (type): type SkyLightOwnership = "authored" | "sky-default" — Policy for composing sky backdrops with `PlayableGame.lighting`: - authored lighting present → sky renders dome + fog only; lights stay game-owned - no authored lighting → sky may emit its default sun/hemisphere with the dome Time-of-day never rewrites configured lights; it only drives sky colors/fog (and sky-owned lights when the game did not author lighting).
2069
- - `TimeOfDayDaylightProps` (interface): interface TimeOfDayDaylightProps — ⚠ undocumented
2156
+ - `TimeOfDayDaylightProps` (interface): interface TimeOfDayDaylightProps { sky: SkyEnvironmentDescriptor; clock?: {calendar(): {dayFraction: number}}; lights?: boolean; bands?: readonly BiomeBand[] } — ⚠ undocumented
2070
2157
  - `VolumetricCloudsProps` (interface): interface VolumetricCloudsProps — Props for {@link VolumetricClouds} — fully-resolved cloud rules plus the shared sun direction.
2071
2158
 
2072
2159
  ## @jgengine/shell/environment/DayNightSky
@@ -2076,20 +2163,20 @@
2076
2163
 
2077
2164
  ## @jgengine/shell/environment/Daylight
2078
2165
 
2079
- - `DaylightProps` (interface): interface DaylightProps — ⚠ undocumented
2080
- - `SkyDaylightProps` (interface): interface SkyDaylightProps — ⚠ undocumented
2081
- - `SkyDomeProps` (interface): interface SkyDomeProps — ⚠ undocumented
2082
- - `TimeOfDayDaylightProps` (interface): interface TimeOfDayDaylightProps — ⚠ undocumented
2166
+ - `DaylightProps` (interface): interface DaylightProps { sky?: SkyDomeProps | false; fog?: {color?: string; near?: number; far?: number} | false; sun?: {position?: readonly [number, number, number]; intensity?: number; color?: string}; ambient?: {skyColor?: string; groundColor?: string; intensi… — ⚠ undocumented
2167
+ - `SkyDaylightProps` (interface): interface SkyDaylightProps { sky: SkyEnvironmentDescriptor; lights?: boolean; bands?: readonly BiomeBand[] } — ⚠ undocumented
2168
+ - `SkyDomeProps` (interface): interface SkyDomeProps { topColor?: string; horizonColor?: string; radius?: number; offset?: number; exponent?: number; sunDirection?: readonly [number, number, number]; sunColor?: string; sunIntensity?: number; hazeStrength?: number; cloudiness?: number; sunGlow… — ⚠ undocumented
2169
+ - `TimeOfDayDaylightProps` (interface): interface TimeOfDayDaylightProps { sky: SkyEnvironmentDescriptor; clock?: {calendar(): {dayFraction: number}}; lights?: boolean; bands?: readonly BiomeBand[] } — ⚠ undocumented
2083
2170
 
2084
2171
  ## @jgengine/shell/environment/EnvironmentScene
2085
2172
 
2086
2173
  - `EnvironmentScene` (function): function EnvironmentScene({ feature }: EnvironmentSceneProps): React.JSX.Element — ⚠ undocumented
2087
- - `EnvironmentSceneProps` (interface): interface EnvironmentSceneProps — ⚠ undocumented
2174
+ - `EnvironmentSceneProps` (interface): interface EnvironmentSceneProps { feature: EnvironmentWorldFeature } — ⚠ undocumented
2088
2175
 
2089
2176
  ## @jgengine/shell/environment/GroundPad
2090
2177
 
2091
2178
  - `GroundPad` (function): function GroundPad({ pad, field }: GroundPadProps): React.JSX.Element — ⚠ undocumented
2092
- - `GroundPadProps` (interface): interface GroundPadProps — ⚠ undocumented
2179
+ - `GroundPadProps` (interface): interface GroundPadProps { pad: PadEnvironmentDescriptor; field: TerrainField } — ⚠ undocumented
2093
2180
 
2094
2181
  ## @jgengine/shell/environment/RoadRibbons
2095
2182
 
@@ -2107,8 +2194,9 @@
2107
2194
  - `DEFAULT_DAY_SKY_BOTTOM` (const): const DEFAULT_DAY_SKY_BOTTOM: "#e3f4ff" — ⚠ undocumented
2108
2195
  - `DEFAULT_DAY_SKY_TOP` (const): const DEFAULT_DAY_SKY_TOP: "#3fa4f2" — ⚠ undocumented
2109
2196
  - `DEFAULT_DAY_SUN_INTENSITY` (const): const DEFAULT_DAY_SUN_INTENSITY: 1 — ⚠ undocumented
2110
- - `DaylightCycleConfig` (interface): interface DaylightCycleConfig — ⚠ undocumented
2111
- - `DaylightState` (interface): interface DaylightState — ⚠ undocumented
2197
+ - `DEFAULT_SUN_ELEVATION_DEG` (const): const DEFAULT_SUN_ELEVATION_DEG: number — The engine arc's noon height: `atan(1 / SUN_DEPTH_RATIO)`, ~68 degrees.
2198
+ - `DaylightCycleConfig` (interface): interface DaylightCycleConfig { horizonColor?: string; zenithColor?: string; sunIntensity?: number; ambientIntensity?: number; sun?: SkySunConfig } — ⚠ undocumented
2199
+ - `DaylightState` (interface): interface DaylightState { sunPosition: [number, number, number]; sunIntensity: number; ambientIntensity: number; skyTop: string; skyBottom: string; background: string } — ⚠ undocumented
2112
2200
  - `SKY_PRESET_DAY_FRACTION` (const): const SKY_PRESET_DAY_FRACTION: Record<"day" | "dusk" | "night", number> — ⚠ undocumented
2113
2201
 
2114
2202
  ## @jgengine/shell/environment/groundPadMath
@@ -2126,56 +2214,80 @@
2126
2214
  - `mergeGamepadFrame` (function): function mergeGamepadFrame(base: GamepadInputFrame, gamepad: GamepadFrame): GamepadInputFrame — Merge one resolved gamepad frame with another input source's semantic state.
2127
2215
  - `mergeGamepadInput` (function): function mergeGamepadInput(frames: readonly GamepadFrame[], base: GamepadInputFrame = { held: [], analog: {} }): GamepadInputFrame — Pure reducer used by the shell and synthetic gamepad tests.
2128
2216
 
2217
+ ## @jgengine/shell/input/gamepadPoll
2218
+
2219
+ - `GamepadPoll` (interface): interface GamepadPoll — Buffers one shell gamepad poll reuses across frames.
2220
+ - `GamepadRoute` (interface): interface GamepadRoute — Where each connected pad's input goes this frame; buffers are reused across frames.
2221
+ - `emptyGamepadPoll` (function): function emptyGamepadPoll(): GamepadPoll — Fresh buffers for {@link stepGamepadPoll}.
2222
+ - `emptyGamepadRoute` (function): function emptyGamepadRoute(): GamepadRoute — Fresh buffers for {@link routeGamepads}.
2223
+ - `gamepadCodes` (function): function gamepadCodes(bindings: ActionCodesMap): GamepadBindings — The pad-only slice of an action binding map, in the shape {@link resolveGamepadFrame} reads.
2224
+ - `rebindGamepadPoll` (function): function rebindGamepadPoll(poll: GamepadPoll, previous: GamepadBindings, next: GamepadBindings, tracker: ActionStateTracker<string>): void — Swap the pad bindings mid-game (a context push) without touching keyboard state: release the codes of pad-held actions whose pad codes changed (the next poll presses them under the new map), and keep the rest held.
2225
+ - `routeGamepads` (function): function routeGamepads(pads: ArrayLike<GamepadSample | null | undefined>, seats: Pick<LocalPlayers, "assign" | "slotForDevice" | "config">, route: GamepadRoute): GamepadRoute — Route pads to local seats: a claimed pad goes to its seat, an unclaimed pad hot-joins on a button press (stick drift never opens a seat), and with one seat every pad drives the primary player.
2226
+ - `stepGamepadPoll` (function): function stepGamepadPoll(poll: GamepadPoll, pads: ArrayLike<GamepadSample | null | undefined>, bindings: GamepadBindings, options: ResolveGamepadFrameOptions, tracker: ActionStateTracker<string>, analogIn: Readonly<Record<string, number>> | null): Readonly<Record<string, number>> | null — One poll: resolve every connected pad, press/release tracker codes for actions whose pad state changed, and return the analog map to publish (pad values max-merged over the other source's). Allocation-free after the first frame.
2227
+
2129
2228
  ## @jgengine/shell/input/gamepadSource
2130
2229
 
2131
- - `GamepadSource` (function): function GamepadSource({ tracker, bindings, analogRef, input, }: { tracker: ActionStateTracker<string>; bindings: ActionCodesMap; analogRef: { current: Readonly<Record<string, number>> | null }; input: InputSnapshot; }): null — Poll browser gamepads and feed semantic actions into the shell tracker.
2230
+ - `GamepadSource` (function): function GamepadSource({ tracker, bindings, analogRef, input, feel, seats, onSeatJoin, seatsActive, }: { tracker: ActionStateTracker<string>; bindings: ActionCodesMap; analogRef: { current: Readonly<Record<string, number>> | null }; input: InputSnapshot; /** Game-level pad feel (`defineGame({ gamepa… — Poll browser gamepads and feed semantic actions into the shell tracker.
2132
2231
  - `mergeGamepadFrame` (function): function mergeGamepadFrame(base: GamepadInputFrame, gamepad: GamepadFrame): GamepadInputFrame — Merge one resolved gamepad frame with another input source's semantic state.
2133
2232
  - `mergeGamepadInput` (function): function mergeGamepadInput(frames: readonly GamepadFrame[], base: GamepadInputFrame = { held: [], analog: {} }): GamepadInputFrame — Pure reducer used by the shell and synthetic gamepad tests.
2134
2233
 
2135
2234
  ## @jgengine/shell/input/mouseLook
2136
2235
 
2137
- - `MouseLookAim` (interface): interface MouseLookAim — ⚠ undocumented
2138
- - `MouseLookOptions` (interface): interface MouseLookOptions — ⚠ undocumented
2236
+ - `MouseLookAim` (interface): interface MouseLookAim { yaw: number; pitch: number } — ⚠ undocumented
2237
+ - `MouseLookOptions` (interface): interface MouseLookOptions { sensitivity?: number; maxPitch?: number; pointerLock?: boolean; initialYaw?: number; initialPitch?: number } — ⚠ undocumented
2139
2238
  - `MouseLookTracker` (interface): interface MouseLookTracker — The analog mouse-look service chase/orbit-cam games hand-rolled (#282.8) — pointer-lock lifecycle plus delta accumulation into a yaw/pitch aim, decoupled from the first-person rig. Attach it to the canvas, read `aim()` from `onTick`/`useFrame`, dispose on unmount.
2140
2239
  - `createMouseLookTracker` (function): function createMouseLookTracker(element: HTMLElement, options: MouseLookOptions = {}): MouseLookTracker — ⚠ undocumented
2141
2240
 
2241
+ ## @jgengine/shell/input/padHaptics
2242
+
2243
+ - `PAD_HAPTIC_EFFECT_MS` (const): const PAD_HAPTIC_EFFECT_MS: 120 — How long each rumble command lasts; refreshed before it runs out so a held level feels continuous.
2244
+ - `PadHapticState` (interface): interface PadHapticState — Last command sent to one pad.
2245
+ - `emptyPadHapticState` (function): function emptyPadHapticState(): PadHapticState — Fresh state for {@link stepPadHaptics}.
2246
+ - `stepPadHaptics` (function): function stepPadHaptics(state: PadHapticState, level: HapticLevel, nowMs: number): "play" | "reset" | null — Decide what to send a pad this frame for a mixed level: `"play"` when the level changed or the last effect is about to run out, `"reset"` when it fell silent, `null` otherwise.
2247
+
2248
+ ## @jgengine/shell/input/pointerLock
2249
+
2250
+ - `requestRawPointerLock` (function): function requestRawPointerLock(element: LockableElement): void — Request pointer lock with raw (unaccelerated) mouse deltas, falling back to a plain lock when the browser or OS rejects `unadjustedMovement`.
2251
+
2142
2252
  ## @jgengine/shell/inputSink
2143
2253
 
2144
2254
  - `InputSink` (interface): interface InputSink — Where the local player's per-frame input goes: discarded in single-player, sent to the authoritative host under `authority: "server"`.
2145
2255
 
2146
2256
  ## @jgengine/shell/map
2147
2257
 
2148
- - `BakeTerrainMapOptions` (interface): interface BakeTerrainMapOptions — ⚠ undocumented
2149
- - `BakedMap` (interface): interface BakedMap — ⚠ undocumented
2150
- - `MapBakeBounds` (interface): interface MapBakeBounds — ⚠ undocumented
2258
+ - `BakeTerrainMapOptions` (interface): interface BakeTerrainMapOptions { resolution?: number; landLow?: readonly [number, number, number]; landHigh?: readonly [number, number, number]; water?: readonly [number, number, number] } — ⚠ undocumented · used by `bakeTerrainMap`: Bake a top-down image of a `TerrainField` (or `RegionField`) over `bounds` for the react `Minimap` / `WorldMap` background.
2259
+ - `BakedMap` (interface): interface BakedMap { url: string; bounds: MapBakeBounds } — ⚠ undocumented · used by `bakeTerrainMap`: Bake a top-down image of a `TerrainField` (or `RegionField`) over `bounds` for the react `Minimap` / `WorldMap` background.
2260
+ - `MapBakeBounds` (interface): interface MapBakeBounds { minX: number; minZ: number; maxX: number; maxZ: number } — ⚠ undocumented · used by `bakeTerrainMap`: Bake a top-down image of a `TerrainField` (or `RegionField`) over `bounds` for the react `Minimap` / `WorldMap` background.
2151
2261
  - `MapMarkerBeacons` (function): function MapMarkerBeacons({ markers, kindStyles, height = 5 }: MapMarkerBeaconsProps): React.JSX.Element — World-space beacons for map markers (the visible in-world side of a ping): a floating diamond over a soft light beam, colored by marker kind. Wire it through `PlayableGame.WorldOverlay`.
2152
- - `MapMarkerBeaconsProps` (interface): interface MapMarkerBeaconsProps — ⚠ undocumented
2262
+ - `MapMarkerBeaconsProps` (interface): interface MapMarkerBeaconsProps { markers: MarkerCollection; kindStyles?: Record<string, MarkerKindStyle>; height?: number } — ⚠ undocumented · used by `MapMarkerBeacons`: World-space beacons for map markers (the visible in-world side of a ping): a floating diamond over a soft light beam, colored by marker kind…
2153
2263
  - `bakeTerrainMap` (function): function bakeTerrainMap(field: TerrainField, bounds: MapBakeBounds, options: BakeTerrainMapOptions = {}): BakedMap | null — Bake a top-down image of a `TerrainField` (or `RegionField`) over `bounds` for the react `Minimap` / `WorldMap` background. Runs in the browser via a 2D canvas; renderer-side, so it lives in the shell.
2154
2264
 
2155
2265
  ## @jgengine/shell/map/MapMarkerBeacons
2156
2266
 
2157
2267
  - `MapMarkerBeacons` (function): function MapMarkerBeacons({ markers, kindStyles, height = 5 }: MapMarkerBeaconsProps): React.JSX.Element — World-space beacons for map markers (the visible in-world side of a ping): a floating diamond over a soft light beam, colored by marker kind. Wire it through `PlayableGame.WorldOverlay`.
2158
- - `MapMarkerBeaconsProps` (interface): interface MapMarkerBeaconsProps — ⚠ undocumented
2268
+ - `MapMarkerBeaconsProps` (interface): interface MapMarkerBeaconsProps { markers: MarkerCollection; kindStyles?: Record<string, MarkerKindStyle>; height?: number } — ⚠ undocumented · used by `MapMarkerBeacons`: World-space beacons for map markers (the visible in-world side of a ping): a floating diamond over a soft light beam, colored by marker kind…
2159
2269
 
2160
2270
  ## @jgengine/shell/map/terrainMap
2161
2271
 
2162
- - `BakeTerrainMapOptions` (interface): interface BakeTerrainMapOptions — ⚠ undocumented
2163
- - `BakedMap` (interface): interface BakedMap — ⚠ undocumented
2164
- - `MapBakeBounds` (interface): interface MapBakeBounds — ⚠ undocumented
2272
+ - `BakeTerrainMapOptions` (interface): interface BakeTerrainMapOptions { resolution?: number; landLow?: readonly [number, number, number]; landHigh?: readonly [number, number, number]; water?: readonly [number, number, number] } — ⚠ undocumented · used by `bakeTerrainMap`: Bake a top-down image of a `TerrainField` (or `RegionField`) over `bounds` for the react `Minimap` / `WorldMap` background.
2273
+ - `BakedMap` (interface): interface BakedMap { url: string; bounds: MapBakeBounds } — ⚠ undocumented · used by `bakeTerrainMap`: Bake a top-down image of a `TerrainField` (or `RegionField`) over `bounds` for the react `Minimap` / `WorldMap` background.
2274
+ - `MapBakeBounds` (interface): interface MapBakeBounds { minX: number; minZ: number; maxX: number; maxZ: number } — ⚠ undocumented · used by `bakeTerrainMap`: Bake a top-down image of a `TerrainField` (or `RegionField`) over `bounds` for the react `Minimap` / `WorldMap` background.
2165
2275
  - `bakeTerrainMap` (function): function bakeTerrainMap(field: TerrainField, bounds: MapBakeBounds, options: BakeTerrainMapOptions = {}): BakedMap | null — Bake a top-down image of a `TerrainField` (or `RegionField`) over `bounds` for the react `Minimap` / `WorldMap` background. Runs in the browser via a 2D canvas; renderer-side, so it lives in the shell.
2166
2276
 
2167
2277
  ## @jgengine/shell/materialOverride
2168
2278
 
2169
- - `MaterialOverrideOptions` (interface): interface MaterialOverrideOptions — ⚠ undocumented
2279
+ - `MaterialOverrideOptions` (interface): interface MaterialOverrideOptions { clone?: boolean; textures?: MaterialOverrideTextures; ownMaterial?: (material: THREE.Material) => void } — ⚠ undocumented
2170
2280
  - `MaterialOverrideTextures` (interface): interface MaterialOverrideTextures — Loaded PBR textures for `applyMaterialOverride`'s `textures` option — matches `ModelMaterialMaps`' roles.
2171
2281
 
2172
2282
  ## @jgengine/shell/multiplayer
2173
2283
 
2174
2284
  - `DEFAULT_FEED_ACTIONS` (const): const DEFAULT_FEED_ACTIONS: string[] — ⚠ undocumented
2285
+ - `PeerShellFactories` (interface): interface PeerShellFactories — Alternate peer factories for hosts that provide their own WebRTC implementation or transport pipe.
2286
+ - `ResolvePeerShellMultiplayerArgs` (interface): interface ResolvePeerShellMultiplayerArgs — Peer bootstrap options; passing a playable hosts its authoritative GameContext world.
2175
2287
  - `ResolveShellMultiplayerArgs` (type): type ResolveShellMultiplayerArgs = { game: GameDefinition; gameId: string; url?: string; userId?: string; force?: boolean; feedActions?: string[]; } — ⚠ undocumented
2176
- - `ShellMultiplayer` (type): type ShellMultiplayer = MultiplayerSession — ⚠ undocumented
2288
+ - `ShellMultiplayer` (type): type ShellMultiplayer = MultiplayerSession — ⚠ undocumented · used by `resolvePeerShellMultiplayer`: Opens a peer session.
2177
2289
  - `randomPlayerId` (function): function randomPlayerId(): string — ⚠ undocumented
2178
- - `resolvePeerShellMultiplayer` (function): function resolvePeerShellMultiplayer(args: { gameId: string; role: "host" | "join"; room?: string; userId?: string; feedActions?: string[]; }): Promise<ShellMultiplayer & { close: () => void }> — ⚠ undocumented
2290
+ - `resolvePeerShellMultiplayer` (function): function resolvePeerShellMultiplayer(args: ResolvePeerShellMultiplayerArgs): Promise<ShellMultiplayer & { close: () => Promise<void> }> — Opens a peer session. With `playable`, one host runs the game loop and guests mirror its shared world. Await `close()` to finish authoritative persistence. Same-origin rooms use BroadcastChannel signaling.
2179
2291
  - `resolveShellMultiplayer` (function): function resolveShellMultiplayer(args: ResolveShellMultiplayerArgs): ShellMultiplayer | null — ⚠ undocumented
2180
2292
 
2181
2293
  ## @jgengine/shell/pointer/PointerOverlays
@@ -2192,12 +2304,12 @@
2192
2304
  - `POINTER_ENTITY_KEY` (const): const POINTER_ENTITY_KEY: "jgEntityId" — ⚠ undocumented
2193
2305
  - `POINTER_OBJECT_KEY` (const): const POINTER_OBJECT_KEY: "jgObjectId" — ⚠ undocumented
2194
2306
  - `PointerHitFilter` (type): type PointerHitFilter = (object: THREE.Object3D) => boolean — ⚠ undocumented
2195
- - `PointerService` (interface): interface PointerService — ⚠ undocumented
2307
+ - `PointerService` (interface): interface PointerService { worldHit(): PointerHit | null; worldHitCenter(): PointerHit | null; screenOf(world: PointerVec3): {x: number; y: number} | null; hasCursor(): boolean; bind(deps: PointerDeps | null): void; setCursor(ndcX: number, ndcY: number, present: bo… — ⚠ undocumented
2196
2308
  - `createPointerService` (function): function createPointerService(): PointerService — ⚠ undocumented
2197
2309
 
2198
2310
  ## @jgengine/shell/postfx/PostProcessing
2199
2311
 
2200
- - `PostProcessing` (function): function PostProcessing({ config, quality = "high", stages }: { config: PostProcessingConfig; quality?: GraphicsQuality; stages?: { ao: boolean; bloom: boolean; dof: boolean; smaa: boolean } }): null — Mounts an `EffectComposer` inside the shell Canvas and takes over rendering (priority-1 `useFrame`, which disables R3F auto-render) to run the configured post chain: RenderPass → GTAO → UnrealBloom → SMAA → OutputPass → Grade. Rendered only when `PlayableGame.postProcessing` is set, so games without it draw unchanged.
2312
+ - `PostProcessing` (function): function PostProcessing({ config, quality = "high", stages }: { config: PostProcessingConfig; quality?: GraphicsQuality; stages?: { ao: boolean; bloom: boolean; dof: boolean; smaa: boolean } }): null — Mounts an `EffectComposer` inside the shell Canvas and takes over rendering (priority-1 `useFrame`, which disables R3F auto-render) to run the configured post chain: RenderPass → GTAO → UnrealBloom → SMAA → OutputPass → Stylize → Grade. Rendered only when `PlayableGame.postProcessing` is set, so games without it draw unchanged.
2201
2313
 
2202
2314
  ## @jgengine/shell/postfx/ScreenEffectsOverlay
2203
2315
 
@@ -2206,7 +2318,7 @@
2206
2318
 
2207
2319
  ## @jgengine/shell/postfx/gradeShader
2208
2320
 
2209
- - `createGradePass` (function): function createGradePass(config: GradeConfig = {}): ShaderPass — Build the display-space colour-grade pass (lift/gain/gamma, saturation, vignette, grain). Advance `uniforms.uTime.value` each frame to animate the grain.
2321
+ - `createGradePass` (function): function createGradePass(config: GradeConfig = {}): ShaderPass — Build the display-space colour-grade pass and load its optional LUT. Dispose the pass to release its LUT; advance `uniforms.uTime.value` each frame to animate grain.
2210
2322
 
2211
2323
  ## @jgengine/shell/postfx/postfxOverlay
2212
2324
 
@@ -2215,6 +2327,10 @@
2215
2327
  - `isPostfxOverlay` (function): function isPostfxOverlay(object: { userData?: Record<string, unknown> }): boolean — True when the object opted out of postfx scene prepasses via {@link POSTFX_OVERLAY_USERDATA}.
2216
2328
  - `restorePostfxOverlays` (function): function restorePostfxOverlays(hidden: THREE.Object3D[]): void — Restore visibility for the objects hidden by {@link hidePostfxOverlays} and clear the list.
2217
2329
 
2330
+ ## @jgengine/shell/postfx/stylizePass
2331
+
2332
+ - `StylizePass` (class): class StylizePass extends Pass — Art-style post pass: ink outlines from a normal+depth prepass, cel luminance bands, and pixelation. Runs on the display-space frame after `OutputPass`. The prepass renders only when outlines are on, and reuses the frame's shadow maps instead of re-rendering them.
2333
+
2218
2334
  ## @jgengine/shell/registry
2219
2335
 
2220
2336
  - `GameRegistry` (type): type GameRegistry = Record<string, () => Promise<PlayableGame>> — ⚠ undocumented
@@ -2226,13 +2342,17 @@
2226
2342
  ## @jgengine/shell/render/CascadedShadows
2227
2343
 
2228
2344
  - `CsmMaterialSetup` (interface): interface CsmMaterialSetup — Structural slice of CSM so the patcher is testable without a renderer.
2229
- - `patchSceneMaterials` (function): function patchSceneMaterials(scene: THREE.Scene, csm: CsmMaterialSetup, patched: WeakSet<THREE.Material>): void — Run CSM's `setupMaterial` over every unpatched standard material in the scene, chaining rather than clobbering a material's own `onBeforeCompile`. Called every frame (WeakSet-guarded) so streamed meshes never render lit by all cascades at once.
2345
+ - `patchSceneMaterials` (function): function patchSceneMaterials(scene: THREE.Scene, csm: CsmMaterialSetup, patched: WeakSet<THREE.Material>): void — Run CSM's `setupMaterial` over every unpatched standard material in the scene, chaining rather than clobbering a material's own `onBeforeCompile`.
2230
2346
 
2231
2347
  ## @jgengine/shell/render/EntityPreview
2232
2348
 
2233
2349
  - `EntityPreview` (function): function EntityPreview({ children, model, instanceId, mood = "studio", backdrop, turntable = 0, faceCamera = false, forward, environment = true, cameraPosition = [0, 1, 3], fov = 35, dpr = [1, 2], className, style, }: EntityPreviewProps): React.JSX.Element — Drop-in live 3D preview of a single entity — the character-screen portrait, unit inspector, or loadout viewer. It owns all the plumbing a game would otherwise re-derive by hand: a nested `<Canvas>`, the {@link GameContextBridge} that carries the running `GameContext` across the R3F reconciler boundary (so the subject's `useEntityRenderCues` / `useGameContext` hooks resolve and it mirrors the live game), a {@link StudioStage} 3-point lighting rig with optional turntable / face-camera, and a capture-friendly GL surface. The game keeps ownership of its `renderEntity`: pass it as `children`, or hand a native `model` (+ `instanceId`) for an engine-composed body. Bound to a live `instanceId`, the portrait walks, flinches, and topples in lockstep with the world instead of a canned spin.
2234
2350
  - `EntityPreviewProps` (interface): interface EntityPreviewProps — Props for {@link EntityPreview}.
2235
2351
 
2352
+ ## @jgengine/shell/render/HairCards
2353
+
2354
+ - `HairCards` (function): function HairCards({ cards, material }: { cards: HairCardAuthoring; material: MaterialAsset }): React.JSX.Element — Render authored guide ribbons with explicit UVs and strand tangents; motion and collision remain caller-owned.
2355
+
2236
2356
  ## @jgengine/shell/render/PartMotion
2237
2357
 
2238
2358
  - `PartMotionRig` (function): function PartMotionRig({ parts, model, instanceId, renderPart, children, }: { parts: readonly ModelPart[]; model: ModelConfig; instanceId?: string; /** Renders one part's model content (the shell passes its part renderer to avoid an import cycle). */ renderPart: (part: ModelPart, index: number) => R… — Procedural motion rig for a rig-less part-composed character (`ModelPart.role` — see `@jgengine/core/game/partAnimation`). Wraps the whole composition in a root group that bobs, breathes, flinches on `combat.hitReaction`, and topples on `entity.died`, while each role-tagged part swings around its authored transform — legs/arms counter-phase from the entity's live movement speed, head counter-sway, tail wag, wing flap. Untagged parts render as static kit pieces. Children are the base model content (primitive, attachments).
@@ -2251,11 +2371,54 @@
2251
2371
  - `IsolatedEntityModel` (function): function IsolatedEntityModel({ model, instanceId, measure, fallback, }: { model: ModelConfig; instanceId?: string; measure?: MeasureTarget; fallback?: ReactNode; }): React.JSX.Element — ⚠ undocumented
2252
2372
  - `MeasureTarget` (interface): interface MeasureTarget — Where a measured model reports its rendered bounds: an entity kind or an object catalog id.
2253
2373
 
2374
+ ## @jgengine/shell/render/SkinnedInstances
2375
+
2376
+ - `BakeSkinnedCrowdOptions` (interface): interface BakeSkinnedCrowdOptions — Options for {@link bakeSkinnedCrowd}.
2377
+ - `BakedSkinnedCrowd` (interface): interface BakedSkinnedCrowd — A rig baked for instancing: one merged geometry, the bone-matrix texture and its layout.
2378
+ - `CrowdInstance` (interface): interface CrowdInstance — One crowd member: where it stands and what it plays.
2379
+ - `CrowdUniforms` (interface): interface CrowdUniforms — Uniforms shared by every material of one crowd; `crowdTime` advances once per frame.
2380
+ - `SkinnedCrowdSource` (interface): interface SkinnedCrowdSource — A rig to bake: a loaded scene with skinned meshes and its clips.
2381
+ - `SkinnedInstances` (function): function SkinnedInstances({ url, instances, clips, once, fps, modelScale = 1, castShadow = true, receiveShadow = true, timeScale = 1, }: SkinnedInstancesProps): React.JSX.Element — Renders many animated copies of one rigged model in a single instanced draw per material: the rig's clips are baked to a bone-matrix texture once, and each instance samples its own clip, time offset and speed on the GPU. For crowds, spectators and distant NPCs; a character that needs blending, IK or attachments stays an entity model.
2382
+ - `SkinnedInstancesProps` (interface): interface SkinnedInstancesProps — Props for {@link SkinnedInstances}.
2383
+ - `bakeSkinnedCrowd` (function): function bakeSkinnedCrowd(source: SkinnedCrowdSource, options: BakeSkinnedCrowdOptions = {}): BakedSkinnedCrowd — Bakes a rig for {@link SkinnedInstances}: merges its skinned meshes into one geometry in bind space, samples every requested clip at `fps`, and writes each bone's model-space skinning matrix into a float texture laid out by `planBoneTexture`. Meshes with several materials keep only the first. Exported for tests and custom crowd renderers.
2384
+ - `createCrowdUniforms` (function): function createCrowdUniforms(baked: BakedSkinnedCrowd): CrowdUniforms — Builds the uniforms a crowd material reads: the texture and per-clip start, frames, fps and loop.
2385
+ - `patchCrowdMaterial` (function): function patchCrowdMaterial<T extends THREE.Material>(material: T, uniforms: CrowdUniforms): T — Patches a material so each instance skins itself from the crowd texture: per-instance `crowdPlay` is (clip index, time offset, speed). Works on any built-in material, the depth material for shadows included.
2386
+
2254
2387
  ## @jgengine/shell/render/assetBase
2255
2388
 
2256
2389
  - `installAssetBase` (function): function installAssetBase(base: string): void — Installs the app base URL (pass `import.meta.env.BASE_URL`) so root-absolute asset paths load from under it. Call once at app startup, before any game loads. Bases that are not root-absolute (`/`, `./`) reset to the pass-through default. Also registers {@link resolveAssetBaseUrl} on `THREE.DefaultLoadingManager`, covering `TextureLoader`, drei's `useTexture` / `useGLTF`, and every other loader on the default manager.
2257
2390
  - `resolveAssetBaseUrl` (function): function resolveAssetBaseUrl(url: string): string — Resolves a root-absolute asset URL (`/models/…`, `/materials/…`) against the installed app base. Relative, protocol-relative, absolute-scheme (`https:`, `blob:`, `data:`), and already-based URLs pass through unchanged. Installed as the URL modifier on THREE loading managers so every model and texture load resolves correctly wherever the app is mounted.
2258
2391
 
2392
+ ## @jgengine/shell/render/authoredSurfaceMaterial
2393
+
2394
+ - `AuthoredSurfaceConfig` (interface): interface AuthoredSurfaceConfig extends Omit<ModelMaterialOverride, "anisotropy" | "normalScale"> — Serializable PBR settings for an authored primitive surface. Map roles use the model-material contract.
2395
+ - `AuthoredSurfaceMaterial` (function): function AuthoredSurfaceMaterial({ surface, shape = "box" }: { surface: AuthoredSurfaceConfig; shape?: SurfaceShape }): React.JSX.Element — Mount an owned PBR surface material on a mesh.
2396
+ - `SurfaceShape` (type): type SurfaceShape = "box" | "cylinder" — Primitive UV layout whose physical dimensions drive `repeatMetres`.
2397
+ - `useAuthoredSurfaceMaterial` (function): function useAuthoredSurfaceMaterial(config: AuthoredSurfaceConfig, shape: SurfaceShape = "box"): THREE.MeshStandardMaterial — Load declared surface-map roles and own the material/map clones (cached textures remain untouched). Colour, emissive, sheen-colour and specular-colour maps are sRGB; numeric maps are linear. Cylinder side metres use the RMS ellipse circumference approximation. Static batches and custom meshes share this lifecycle.
2398
+
2399
+ ## @jgengine/shell/render/materialAppearanceSignals
2400
+
2401
+ - `RenderMaterialAppearanceBaseline` (interface): interface RenderMaterialAppearanceBaseline extends MaterialAppearanceBaseline — Immutable resolved baseline for one caller-owned assigned material, including its original card exposure.
2402
+ - `applyMaterialAppearanceSignals` (function): function applyMaterialAppearanceSignals(material: THREE.MeshStandardMaterial, baseline: MaterialAppearanceBaseline, sample: EnvironmentSample, policy: MaterialAppearancePolicy = {}): MaterialAppearanceResponse — Apply one sample to an already owned material. Imported/cache materials must be cloned before use. Allocates no textures or materials; clearcoat/sheen require an existing physical material and opt-in policy.
2403
+ - `captureMaterialAppearanceBaseline` (function): function captureMaterialAppearanceBaseline(material: THREE.MeshStandardMaterial): RenderMaterialAppearanceBaseline — Capture once after assignment; recapture after an authored change. Never capture an already wet material.
2404
+ - `restoreMaterialAppearanceBaseline` (function): function restoreMaterialAppearanceBaseline(material: THREE.MeshStandardMaterial, baseline: RenderMaterialAppearanceBaseline): void — Restore the dry baseline when removing an appearance binding; maps and unrelated imported features remain borrowed.
2405
+
2406
+ ## @jgengine/shell/render/materialAsset
2407
+
2408
+ - `MaterialAssetSurface` (function): function MaterialAssetSurface({ asset, ...options }: { asset: MaterialAsset } & MaterialAssetSurfaceOptions): React.JSX.Element — Mount a serializable material asset on an authored primitive, sharing model-slot rendering and owned resource cleanup.
2409
+ - `MaterialAssetSurfaceOptions` (interface): interface MaterialAssetSurfaceOptions — Reusable primitive surface options; physical metre UVs reuse the instanced surface renderer.
2410
+ - `MaterialAssignmentTargetOptions` (interface): interface MaterialAssignmentTargetOptions extends MaterialAssignmentValidationOptions — Read-only target validation policy; source coverage can reflect an earlier all-slot override.
2411
+ - `ModelMaterialSlotInfo` (interface): interface ModelMaterialSlotInfo — Runtime imported-slot inventory for precise authoring selectors; indices address the mesh material array.
2412
+ - `applyMaterialAsset` (function): function applyMaterialAsset(material: THREE.Material, asset: MaterialAsset, textures?: MaterialOverrideTextures, overrides?: MaterialSurfaceParameters): THREE.MeshStandardMaterial — Apply one reusable material's parameters, map views and construction to an imported PBR slot.
2413
+ - `applyMaterialAssignments` (function): function applyMaterialAssignments(root: THREE.Object3D, assets: readonly MaterialAsset[], assignments: readonly MaterialAssignment[], textures: ReadonlyMap<string, MaterialOverrideTextures> = new Map()): THREE.Material[] — Apply intersecting mesh/slot selectors without changing omitted imported slots. Returns caller-owned replacements; model clones register them only after success.
2414
+ - `configureMaterialTexture` (function): function configureMaterialTexture(role: MaterialTextureRole, metadata: MaterialTextureMetadata, source: THREE.Texture): THREE.Texture — Validate and configure an owned texture view while retaining the loader's immutable source.
2415
+ - `inspectModelMaterialSlots` (function): function inspectModelMaterialSlots(root: THREE.Object3D): ModelMaterialSlotInfo[] — Inspect imported names, local slot indices and directional/map prerequisites without mutating the source.
2416
+ - `materialAssetSurface` (function): function materialAssetSurface(asset: MaterialAsset, overrides: MaterialSurfaceParameters = {}): MaterialSurfaceParameters — Physical defaults adapt a declared construction; explicit authored parameters remain authoritative.
2417
+ - `materialResourceMetrics` (function): function materialResourceMetrics(materials: readonly THREE.Material[]): { materials: number; textures: number; textureBytes: number; unknownTextureSizes: number; blendedMaterials: number; maskedMaterials: number; transmissionMaterials: number; doubleSidedMaterials: number; physicalMaterials: number … — Estimate texture allocation from loaded dimensions/mips and report pass/coverage risks. Compressed maps count actual mip bytes; uncompressed bytes bound texture views that may share uploads. This does not measure pixel overdraw.
2418
+ - `setHairCardLightExposure` (function): function setHairCardLightExposure(material: THREE.Material, exposure: number): void — Gate the existing thin-sheet hair-card scatter by sampled local light exposure, without changing scene exposure.
2419
+ - `useMaterialAssetMaterial` (function): function useMaterialAssetMaterial(asset: MaterialAsset, options: MaterialAssetSurfaceOptions = {}): THREE.MeshStandardMaterial — Load map views, own their cleanup and build the same physical shader used by model assignments.
2420
+ - `validateMaterialAssignmentTargets` (function): function validateMaterialAssignmentTargets(root: THREE.Object3D, assets: readonly MaterialAsset[], assignments: readonly MaterialAssignment[], options: MaterialAssignmentTargetOptions = {}): void — Check selectors, PBR slots, UVs and construction prerequisites before allocating material or texture views. Optional role restrictions apply only to referenced assets; imported maps remain untouched.
2421
+
2259
2422
  ## @jgengine/shell/render/measureBounds
2260
2423
 
2261
2424
  - `MEASURE_EXCLUDE_KEY` (const): const MEASURE_EXCLUDE_KEY: "jgMeasureExclude" — Subtrees flagged with this userData key are excluded from bounds measurement — debug gizmos, selection quads, effect billboards that should not inflate the hitbox. Sprites and invisible subtrees are always excluded.
@@ -2272,23 +2435,41 @@
2272
2435
 
2273
2436
  ## @jgengine/shell/render/modelLoad
2274
2437
 
2438
+ - `DEFAULT_MODEL_TRIANGLE_BUDGET` (const): const DEFAULT_MODEL_TRIANGLE_BUDGET: 100000 — Per-model triangle ceiling before the loader warns. A prop is a few thousand triangles; a hero rig tens of thousands.
2439
+ - `HeavyModelRecord` (interface): interface HeavyModelRecord — One over-budget model as {@link heavyModels} reports it: the URL the loader fetched, its triangle count, and the budget it exceeded.
2275
2440
  - `ModelLoaderConfig` (interface): interface ModelLoaderConfig — CDN-backed decoder configuration for compressed model and texture assets.
2276
2441
  - `configureModelLoaders` (function): function configureModelLoaders(options: ModelLoaderConfig = {}): Readonly<Required<ModelLoaderConfig>> — Configures shared Draco and KTX2 loaders. Repeated calls with the same paths are no-ops.
2277
2442
  - `detectKtx2Support` (function): function detectKtx2Support(renderer: THREE.WebGLRenderer): void — Detects GPU support for the configured KTX2 transcoder after a renderer exists.
2443
+ - `heavyModels` (function): function heavyModels(): readonly HeavyModelRecord[] — Every model the shared loader resolved whose triangle count exceeds the budget, in load order. A heavy prop is invisible in a screenshot and only shows up as a slow frame, so the loader measures each GLB as it lands and this exposes the verdict for `debug_snapshot` probes and smoke tests to assert against.
2278
2444
  - `modelLoadFallbacks` (function): function modelLoadFallbacks(): readonly { url: string; message: string }[] — Every model the shared loader diagnosed as broken and replaced with a magenta placeholder, in load order.
2279
2445
  - `modelLoadIdleMs` (function): function modelLoadIdleMs(): number — How long the shared GLB loader has been idle, in ms — `0` while any model is still in flight. A capture host reads this to wait for streaming to finish instead of guessing a settle delay: models that pop in after the shot are why an establishing capture used to come back half-empty until someone hand-tuned `--settle`.
2446
+ - `setModelTriangleBudget` (function): function setModelTriangleBudget(triangles: number): void — Retune the per-model triangle budget (a cinematic game may raise it; a mobile target lowers it). Non-finite or non-positive values restore the default.
2280
2447
 
2281
2448
  ## @jgengine/shell/render/modelRender
2282
2449
 
2283
- - `MaterialCache` (interface): interface MaterialCache — ⚠ undocumented
2450
+ - `MaterialCache` (interface): interface MaterialCache { materials: THREE.MeshStandardMaterial[]; seedColor: THREE.Color } — ⚠ undocumented
2284
2451
  - `ModelShadowMode` (type): type ModelShadowMode = "cast" | "receive" | "both" | "none" — Shadow participation applied to every mesh of a cloned model; mirrors `ModelConfig.shadows`.
2285
2452
  - `PAINT_TEXTURE_SIZE` (const): const PAINT_TEXTURE_SIZE: 512 — ⚠ undocumented
2286
- - `PaintCanvas` (interface): interface PaintCanvas — ⚠ undocumented
2453
+ - `PaintCanvas` (interface): interface PaintCanvas { canvas: HTMLCanvasElement; context: CanvasRenderingContext2D; texture: THREE.CanvasTexture } — ⚠ undocumented
2454
+ - `cloneModelScene` (function): function cloneModelScene(source: THREE.Object3D, options?: { cloneMaterials?: boolean; shadows?: ModelShadowMode }): THREE.Object3D — Clone a model with independent pose and materials, retaining shared geometry and textures.
2455
+ - `disposeModelScene` (function): function disposeModelScene(root: THREE.Object3D): void — Release materials and bone textures owned by `cloneModelScene`; shared assets and attached models remain owned by their callers.
2456
+
2457
+ ## @jgengine/shell/render/projectedOverdraw
2458
+
2459
+ - `ProjectedOverdrawOptions` (interface): interface ProjectedOverdrawOptions — Sampling resolution; lower resolutions change thin geometry and alpha-mask raster coverage.
2460
+ - `ProjectedOverdrawReport` (interface): interface ProjectedOverdrawReport — Projected mesh-fragment overlap, with depth rejection disabled; this is not production early-Z overdraw or GPU timing.
2461
+ - `measureProjectedOverdraw` (function): function measureProjectedOverdraw(renderer: THREE.WebGLRenderer, scene: THREE.Scene, camera: THREE.Camera, options: ProjectedOverdrawOptions = {}): ProjectedOverdrawReport — Opt in to a synchronous offscreen count of projected mesh fragments. Native PBR/unlit vertex transforms and alpha discard remain; depth rejection, shadow/refraction passes and postprocessing do not participate. Blended texels count equally, including zero opacity when native alphaTest/alphaHash does not discard them. Pure shader-modifying onBeforeCompile callbacks are retained; render callbacks, custom shader materials, stencil and alpha-to-coverage are rejected. The float readback stalls the GPU. Temporary materials/target are disposed and renderer draw counters/state restored; borrowed assets may become resident while measured.
2287
2462
 
2288
2463
  ## @jgengine/shell/render/resolveModel
2289
2464
 
2290
2465
  - `ModelPick` (type): type ModelPick = { model?: string; fallbackModel?: string; style?: Omit<ModelConfig, "url" | "dims">; } — Preferred + optional fallback catalog ids for a single entity/object slot. Soft-resolves through the catalog: when neither id is live (pack not pulled/ reindexed yet), the mapping is omitted and the shell keeps its primitive. Re-home later by fixing ids / pulling packs — no Kenney, no hard throws.
2291
- - `ModelResolveContext` (interface): interface ModelResolveContext — ⚠ undocumented
2466
+ - `ModelResolveContext` (interface): interface ModelResolveContext { seam: "entityModels" | "objectModels" | "scatterModels"; key: string } — ⚠ undocumented
2467
+
2468
+ ## @jgengine/shell/render/rigNode
2469
+
2470
+ - `RigNodeDiagnostic` (interface): interface RigNodeDiagnostic — A missing or nonunique node reference that a rig author can repair.
2471
+ - `RigNodeResolution` (type): type RigNodeResolution = | { node: Object3D; matchedBy: "runtime" | "authored"; diagnostic?: undefined } | { node: undefined; matchedBy?: undefined; diagnostic: RigNodeDiagnostic } — A node on this model instance, or a diagnostic instead of an arbitrary attachment target.
2472
+ - `resolveRigNode` (function): function resolveRigNode(rig: Object3D, name: string): RigNodeResolution — Resolves a bone or attachment slot by its exact runtime name first, then by the original imported name preserved as `userData.name`. GLTFLoader sanitizes names such as `handslot.r` to `handslotr`; authored references remain usable without renaming the animated hierarchy. Both lookups require a unique target. Call on model load or slot changes, not each frame, and keep the result with that model instance rather than reusing a cached asset's node.
2292
2473
 
2293
2474
  ## @jgengine/shell/render/sceneCapture
2294
2475
 
@@ -2297,6 +2478,11 @@
2297
2478
  - `captureCanvas` (function): function captureCanvas(gl: CaptureRenderer): string | null — Read the current frame to a PNG data URL. Requires the R3F `<Canvas>` to have been created with `gl={{ preserveDrawingBuffer: true }}` (the shell's game canvas already is); returns null if the backing canvas can't be read.
2298
2479
  - `downloadImage` (function): function downloadImage(dataUrl: string, filename = "screenshot.png"): void — Trigger a browser download of an image data URL (the photo-mode "save" action).
2299
2480
 
2481
+ ## @jgengine/shell/render/staticShapeInstances
2482
+
2483
+ - `StaticShapeInstance` (interface): interface StaticShapeInstance — One editor-derived static primitive. A box spans one unit; a cylinder has unit radius and height before scale.
2484
+ - `StaticShapeInstances` (function): function StaticShapeInstances({ instances }: { instances: readonly StaticShapeInstance[] }): React.JSX.Element — Batch editor-derived boxes/cylinders by shape, PBR metadata and shadows. Uploads and bounds run only when instances change; map loading stays isolated per draw group.
2485
+
2300
2486
  ## @jgengine/shell/render/testFixtures/proceduralTileLayer
2301
2487
 
2302
2488
  - `drawProceduralTileLayer` (function): function drawProceduralTileLayer(canvas: CanvasFixture): TilemapWorldConfig — Draw the deterministic tile atlas used by textured tile-layer captures and tests.
@@ -2313,16 +2499,32 @@
2313
2499
 
2314
2500
  ## @jgengine/shell/render/useFootIk
2315
2501
 
2316
- - `applyFootIk` (function): function applyFootIk(scene: THREE.Object3D, config: FootIkConfig, raycast: GameContext["scene"]["raycast"], weight: number, cameraTarget?: readonly [number, number, number]): boolean — Applies one frame of foot IK to a loaded rig. Exported for renderer tests and custom model hosts.
2317
- - `useFootIk` (function): function useFootIk(scene: THREE.Object3D, config: FootIkConfig | undefined, ctx: GameContext | null, instanceId?: string): void — Runs foot IK after the model animation mixer, fading the correction out while the entity is airborne.
2502
+ - `FootIkRig` (interface): interface FootIkRig — Bones resolved once per loaded scene for {@link applyFootIk}.
2503
+ - `FootIkState` (interface): interface FootIkState — Per-model state carried between frames: the faded weight and the smoothed pelvis drop.
2504
+ - `applyFootIk` (function): function applyFootIk(rig: FootIkRig, originY: number, probe: GroundProbe, state: FootIkState, delta: number, cameraTarget?: readonly [number, number, number]): boolean — Applies one frame of foot IK after the animation mixer: probes the ground under each foot, resolves targets with `placeFeet`, lowers the pelvis, solves each leg with `solveTwoBone` bending toward the animated knee, and tilts planted feet to the ground. Returns whether the feet are grounded; `state` carries the faded weight between frames. Exported for tests and custom hosts.
2505
+ - `resolveFootIkRig` (function): function resolveFootIkRig(scene: THREE.Object3D, config: FootIkConfig): FootIkRig | null — Resolves foot-IK bones on a loaded rig: explicit chains, or legs found by bone name for `"auto"` and configs without `feet`. Returns `null` when no leg resolves.
2506
+ - `useFootIk` (function): function useFootIk(scene: THREE.Object3D, config: FootIkConfig | undefined, ctx: GameContext | null, instanceId?: string, groundOffset = 0): void — Runs foot IK after the model's animation mixer for `ModelConfig.ik`. Ground probes hit terrain and blocking physical objects, never the model's own entity. `groundOffset` is the model-space height its soles rest on (`ModelConfig.y`).
2318
2507
 
2319
2508
  ## @jgengine/shell/render/useModelAnimation
2320
2509
 
2321
- - `useModelAnimation` (function): function useModelAnimation(scene: THREE.Object3D, clips: THREE.AnimationClip[], animationInput: ModelAnimationConfig | "auto" | "none" | undefined, instanceId?: string): void — The engine's model animation driver as a standalone hook — the same mixer `EntityModel` runs, for games that render a cloned scene themselves (custom materials, procedural composition). Handles `"auto"` derivation from the GLB's clip names, speed-driven idle/walk/run crossfades read from the entity's live position when `instanceId` is set, one-shots fired from `entity.animation` / `combat.hitReaction` / `entity.died`, held poses, and the death clamp. With `animation.graph` set, the headless `AnimGraph` runtime owns every clip's time and weight and the mixer only applies them; clip events surface as `animation.event`.
2510
+ - `GraphPose` (interface): interface GraphPose — A mixer set up to show {@link AnimGraph} output on a rig; see {@link createGraphPose}.
2511
+ - `ModelAnimationDiagnostic` (interface): interface ModelAnimationDiagnostic — A repairable mismatch between a model animation config and its imported rig.
2512
+ - `createGraphPose` (function): function createGraphPose(scene: THREE.Object3D, graph: AnimGraph, clips: THREE.AnimationClip[]): GraphPose — Binds a graph's clips to a rig exactly as `useModelAnimation` does (masked layers get filtered clips, additive layers additive ones) so a host that runs its own `createAnimGraphRuntime`, such as the editor's graph preview, poses the rig from the runtime's output.
2513
+ - `diagnoseModelAnimation` (function): function diagnoseModelAnimation(scene: THREE.Object3D, animation: ModelAnimationConfig, clips: readonly THREE.AnimationClip[]): readonly ModelAnimationDiagnostic[] — Checks clip mappings, masked layers and root-motion assumptions without starting a mixer.
2514
+ - `readModelAnimationParams` (function): function readModelAnimationParams(ctx: GameContext, instanceId: string, speed: number, out: Record<string, AnimParamValue>): Record<string, AnimParamValue> — Refreshes a caller-owned graph parameter dictionary from authored extras and known physical movement. Grounded, verticalSpeed and crouched use the shared motor's resolved state when available; custom movers retain their authored values. Speed always uses the supplied measured ground speed. Does not write game state.
2515
+ - `takeRootMotion` (function): function takeRootMotion(rootBone: THREE.Object3D, bind: THREE.Vector3, localDelta: readonly [number, number, number] | undefined, out: THREE.Vector3): THREE.Vector3 — For a frame where a `rootMotion` state is current: pins the root bone's horizontal translation to its bind pose, so the clip plays in place, and returns that step's root travel as a world-space horizontal delta (through the rig's parent transform, so the entity's facing and the model's scale apply). The root bone's vertical motion stays in the clip.
2516
+ - `useModelAnimation` (function): function useModelAnimation(scene: THREE.Object3D, clips: THREE.AnimationClip[], animationInput: ModelAnimationConfig | "auto" | "none" | undefined, instanceId?: string): void — The engine's model animation driver as a standalone hook — the same mixer `EntityModel` runs, for games that render a cloned scene themselves (custom materials, procedural composition). Handles `"auto"` derivation from the GLB's clip names, speed-driven idle/walk/run crossfades read from the entity's live position when `instanceId` is set, one-shots fired from `entity.animation` / `combat.hitReaction` / `entity.died`, held poses, and the death clamp. With `animation.graph` set, the headless `AnimGraph` runtime owns every clip's time and weight and the mixer only applies them; clip events surface as `animation.event`. Equivalent same-order config data retains playback across rerenders; actual data edits reset it. Entity-bound playback holds while context game time is frozen; unbound previews keep playing.
2517
+
2518
+ ## @jgengine/shell/render/useModelInstance
2519
+
2520
+ - `ModelInstance` (interface): interface ModelInstance — Render `scene` with `position` and uniform `scale`; animate or deform `content` inside that placement frame.
2521
+ - `ModelInstanceConfig` (type): type ModelInstanceConfig = Pick<ModelConfig, "url" | "scale" | "targetHeight" | "y" | "anchor" | "dims" | "shadows" | "animation" | "hiddenNodes" > — Model loading, placement, shadows and animation for a custom renderer.
2522
+ - `ModelInstanceOptions` (interface): interface ModelInstanceOptions — Caller-owned styling runs on the isolated clone, never the loader cache.
2523
+ - `useModelInstance` (function): function useModelInstance(model: ModelInstanceConfig, options: ModelInstanceOptions = {}): ModelInstance — Load and own an isolated model instance, preserving imported transforms and measuring placement at bind pose. Apply hiddenNodes to the configured clone before measurement; equivalent name lists retain the instance. Mount `scene` with the returned position/scale. Custom materials, deformation and surrounding geometry stay caller-owned. Must run inside an R3F canvas and Suspense boundary; a game provider is optional.
2322
2524
 
2323
2525
  ## @jgengine/shell/replay/useSessionRecorder
2324
2526
 
2325
- - `RecordedPose` (interface): interface RecordedPose — ⚠ undocumented
2527
+ - `RecordedPose` (interface): interface RecordedPose { position: EntityPosition; rotationY: number } — ⚠ undocumented · used by `useSessionRecorder`: Session-recording buffer (#120) for replay / photo mode / kill-cam: records an entity's pose on game-time every frame into a `RecordingBuffe…
2326
2528
  - `useSessionRecorder` (function): function useSessionRecorder(entityId: string, options?: RecordingBufferOptions): RecordingBuffer<RecordedPose> — Session-recording buffer (#120) for replay / photo mode / kill-cam: records an entity's pose on game-time every frame into a `RecordingBuffer`, which a game can then `seek()` to scrub, drive an observer cam ghost, or export a kill-cam clip. Recording rides on `ctx.time.now()`, so pause/fast-forward scrub the recording exactly like the live sim.
2327
2529
 
2328
2530
  ## @jgengine/shell/scatter
@@ -2341,21 +2543,23 @@
2341
2543
 
2342
2544
  ## @jgengine/shell/scene
2343
2545
 
2344
- - `AuthoredObjects` (function): function AuthoredObjects({ document, field, verticalOffset = 0, onExisting = "keep", }: AuthoredObjectsProps): null — Places every editor marker with a catalog id into `ctx.scene.object`, grounded on `field`. WorldScene renders them through the game's `objectModels` seam — no bespoke place loop. Pure headless twin: `resolveAuthoredObjects` / `placeAuthoredObjects`.
2345
- - `AuthoredObjectsProps` (interface): interface AuthoredObjectsProps — Props for {@link AuthoredObjects}: document, ground field, and optional lift / onExisting.
2546
+ - `AuthoredObjects` (function): function AuthoredObjects({ document, field, verticalOffset = 0, onExisting = "keep", synchronize = false, excludeKinds, }: AuthoredObjectsProps): null — Places every editor marker with a catalog id into `ctx.scene.object`, grounded on `field`. WorldScene renders them through the game's `objectModels` seam — no bespoke place loop. Pure headless twin: `resolveAuthoredObjects` / `placeAuthoredObjects`.
2547
+ - `AuthoredObjectsProps` (interface): interface AuthoredObjectsProps extends ResolveAuthoredObjectsOptions — Props for {@link AuthoredObjects}: document, ground field, and optional lift / onExisting.
2346
2548
  - `AuthoredPaths` (function): function AuthoredPaths({ document, field, kinds }: AuthoredPathsProps): React.JSX.Element — Renders a document's non-scatter paths (roads, routes, corridors) as ground-draped ribbons — the editor authors the polyline, the engine drapes it over the live terrain at runtime. Width comes from `path.width`, color from `path.meta.color`/`path.color`. A game never hand-rolls path meshes.
2347
2549
  - `AuthoredPathsProps` (interface): interface AuthoredPathsProps — Props for {@link AuthoredPaths}: the document, the ground field to drape over, and a kind filter.
2348
- - `AuthoredScene` (function): function AuthoredScene({ document, field, pathKinds, scatterModels, assets, live = true, placeObjects, groundColorAt, }: AuthoredSceneProps): React.JSX.Element — Renders an editor document's scene content — draped paths plus GPU-instanced foliage — from one mount, grounded on the live `field`. The runtime counterpart to authoring a scene in the editor: drag paths and foliage regions, save `editor.scene.json`, and the game plays them with no bespoke render code. When a live-sync bus is installed (editor host), document patches stream in and re-render automatically — document is authoritative; runtime overrides stay ephemeral unless written back. Terrain/collision come from the world's ground field (`environment({ sculpt })`); place markers with your own entity spawns. Pass `scatterModels`+`assets` to resolve palette items to real catalog GLBs; unmapped items keep the stylized proxy.
2550
+ - `AuthoredScene` (function): function AuthoredScene({ document, diagnostics = false, field, pathKinds, scatterModels, flockModels, assets, live = true, placeObjects, groundColorAt, }: AuthoredSceneProps): React.JSX.Element — Renders an editor document's scene content — draped paths plus GPU-instanced foliage — from one mount, grounded on the live `field`. The runtime counterpart to authoring a scene in the editor: drag paths and foliage regions, save `editor.scene.json`, and the game plays them with no bespoke render code. When a live-sync bus is installed (editor host), document patches stream in and re-render automatically — document is authoritative; runtime overrides stay ephemeral unless written back. Terrain/collision come from the world's ground field (`environment({ sculpt })`); place markers with your own entity spawns. Pass `scatterModels`+`assets` to resolve palette items to real catalog GLBs; unmapped items keep the stylized proxy.
2349
2551
  - `AuthoredSceneProps` (interface): interface AuthoredSceneProps — Props for {@link AuthoredScene}: the document to render and the ground field to drape/ground on.
2552
+ - `AuthoredSolids` (function): function AuthoredSolids({ document, field }: { document: EditorDocument; field: TerrainField }): null — Writes the document's studio solids (city buildings and any kind with a `solids` hook) into `ctx.world.solids`, so what the studios draw also blocks the player, NPCs and physics.
2350
2553
 
2351
2554
  ## @jgengine/shell/scene/AuthoredScene
2352
2555
 
2353
- - `AuthoredObjects` (function): function AuthoredObjects({ document, field, verticalOffset = 0, onExisting = "keep", }: AuthoredObjectsProps): null — Places every editor marker with a catalog id into `ctx.scene.object`, grounded on `field`. WorldScene renders them through the game's `objectModels` seam — no bespoke place loop. Pure headless twin: `resolveAuthoredObjects` / `placeAuthoredObjects`.
2354
- - `AuthoredObjectsProps` (interface): interface AuthoredObjectsProps — Props for {@link AuthoredObjects}: document, ground field, and optional lift / onExisting.
2556
+ - `AuthoredObjects` (function): function AuthoredObjects({ document, field, verticalOffset = 0, onExisting = "keep", synchronize = false, excludeKinds, }: AuthoredObjectsProps): null — Places every editor marker with a catalog id into `ctx.scene.object`, grounded on `field`. WorldScene renders them through the game's `objectModels` seam — no bespoke place loop. Pure headless twin: `resolveAuthoredObjects` / `placeAuthoredObjects`.
2557
+ - `AuthoredObjectsProps` (interface): interface AuthoredObjectsProps extends ResolveAuthoredObjectsOptions — Props for {@link AuthoredObjects}: document, ground field, and optional lift / onExisting.
2355
2558
  - `AuthoredPaths` (function): function AuthoredPaths({ document, field, kinds }: AuthoredPathsProps): React.JSX.Element — Renders a document's non-scatter paths (roads, routes, corridors) as ground-draped ribbons — the editor authors the polyline, the engine drapes it over the live terrain at runtime. Width comes from `path.width`, color from `path.meta.color`/`path.color`. A game never hand-rolls path meshes.
2356
2559
  - `AuthoredPathsProps` (interface): interface AuthoredPathsProps — Props for {@link AuthoredPaths}: the document, the ground field to drape over, and a kind filter.
2357
- - `AuthoredScene` (function): function AuthoredScene({ document, field, pathKinds, scatterModels, assets, live = true, placeObjects, groundColorAt, }: AuthoredSceneProps): React.JSX.Element — Renders an editor document's scene content — draped paths plus GPU-instanced foliage — from one mount, grounded on the live `field`. The runtime counterpart to authoring a scene in the editor: drag paths and foliage regions, save `editor.scene.json`, and the game plays them with no bespoke render code. When a live-sync bus is installed (editor host), document patches stream in and re-render automatically — document is authoritative; runtime overrides stay ephemeral unless written back. Terrain/collision come from the world's ground field (`environment({ sculpt })`); place markers with your own entity spawns. Pass `scatterModels`+`assets` to resolve palette items to real catalog GLBs; unmapped items keep the stylized proxy.
2560
+ - `AuthoredScene` (function): function AuthoredScene({ document, diagnostics = false, field, pathKinds, scatterModels, flockModels, assets, live = true, placeObjects, groundColorAt, }: AuthoredSceneProps): React.JSX.Element — Renders an editor document's scene content — draped paths plus GPU-instanced foliage — from one mount, grounded on the live `field`. The runtime counterpart to authoring a scene in the editor: drag paths and foliage regions, save `editor.scene.json`, and the game plays them with no bespoke render code. When a live-sync bus is installed (editor host), document patches stream in and re-render automatically — document is authoritative; runtime overrides stay ephemeral unless written back. Terrain/collision come from the world's ground field (`environment({ sculpt })`); place markers with your own entity spawns. Pass `scatterModels`+`assets` to resolve palette items to real catalog GLBs; unmapped items keep the stylized proxy.
2358
2561
  - `AuthoredSceneProps` (interface): interface AuthoredSceneProps — Props for {@link AuthoredScene}: the document to render and the ground field to drape/ground on.
2562
+ - `AuthoredSolids` (function): function AuthoredSolids({ document, field }: { document: EditorDocument; field: TerrainField }): null — Writes the document's studio solids (city buildings and any kind with a `solids` hook) into `ctx.world.solids`, so what the studios draw also blocks the player, NPCs and physics.
2359
2563
 
2360
2564
  ## @jgengine/shell/scene/GeneratedAssetRenderer
2361
2565
 
@@ -2388,13 +2592,14 @@
2388
2592
  ## @jgengine/shell/settings/SettingsRuntime
2389
2593
 
2390
2594
  - `SettingsRuntime` (function): function SettingsRuntime({ variant, surface, actions, children, ...input }: SettingsRuntimeProps): React.JSX.Element — ⚠ undocumented
2391
- - `SettingsRuntimeProps` (interface): interface SettingsRuntimeProps extends SettingsControllerInput — ⚠ undocumented
2595
+ - `SettingsRuntimeProps` (interface): interface SettingsRuntimeProps extends SettingsControllerInput { variant: SettingsVariant; surface: SettingsSurface | false; actions: readonly SettingsActionView[]; children: ReactNode } — ⚠ undocumented
2392
2596
 
2393
2597
  ## @jgengine/shell/settings/appliedSettings
2394
2598
 
2599
+ - `AppliedGraphicsSettings` (interface): interface AppliedGraphicsSettings — The player's graphics choices as the shell applies them.
2395
2600
  - `AudioSettingsBridge` (function): function AudioSettingsBridge({ store, engine, buses, }: { store: SettingsStore; engine: AudioEngine; buses: Record<string, AudioBusDef> | undefined; }): null — ⚠ undocumented
2396
2601
  - `TOUCH_STYLE_AUTO` (const): const TOUCH_STYLE_AUTO: "auto" — Sentinel Controls value meaning "defer to the game's suggested touch skin".
2397
- - `useGraphicsSettings` (function): function useGraphicsSettings(store: SettingsStore, shadowsDefault: boolean, overrides?: Partial<Record<GraphicsQuality, Partial<GraphicsProfile>>>): { shadows: boolean; dpr: number; uiScale: number; quality: GraphicsQuality; profile: GraphicsProfile } — ⚠ undocumented
2602
+ - `useGraphicsSettings` (function): function useGraphicsSettings(store: SettingsStore, shadowsDefault: boolean, overrides?: GraphicsProfileOverrides): AppliedGraphicsSettings — ⚠ undocumented
2398
2603
  - `useSettingsRevision` (function): function useSettingsRevision(store: SettingsStore): number — ⚠ undocumented
2399
2604
 
2400
2605
  ## @jgengine/shell/settings/settingsController
@@ -2402,7 +2607,7 @@
2402
2607
  - `SettingsActionView` (interface): interface SettingsActionView — A resolved game-state action — `run` is already bound to the game context and closes the menu.
2403
2608
  - `SettingsCategoryView` (interface): interface SettingsCategoryView — A settings menu category with its rows and keybinds, ready to render.
2404
2609
  - `SettingsController` (interface): interface SettingsController — The live settings controller — every category/row/keybind/action plus open-state. Render it any way you like or drive the engine menu.
2405
- - `SettingsControllerInput` (interface): interface SettingsControllerInput — ⚠ undocumented
2610
+ - `SettingsControllerInput` (interface): interface SettingsControllerInput { input: ActionCodesMap; buses: Record<string, AudioBusDef> | undefined; extra: readonly GameSettingDef[]; categories: readonly SettingCategoryDef[]; hide: readonly SettingCategory[]; fovEnabled: boolean; graphics?: GraphicsProfileOverrides… — ⚠ undocumented
2406
2611
  - `SettingsKeybindRow` (interface): interface SettingsKeybindRow — One rebindable action row rendered in the controls settings category.
2407
2612
  - `SettingsRow` (interface): interface SettingsRow — One editable setting rendered in a settings menu category.
2408
2613
  - `useSettingsCategories` (function): function useSettingsCategories(config: SettingsControllerInput): SettingsCategoryView[] — ⚠ undocumented
@@ -2420,14 +2625,14 @@
2420
2625
  ## @jgengine/shell/structures
2421
2626
 
2422
2627
  - `BuildingBlock` (function): function BuildingBlock({ part, palette }: BuildingBlockProps): React.JSX.Element — ⚠ undocumented
2423
- - `BuildingBlockProps` (interface): interface BuildingBlockProps — ⚠ undocumented
2628
+ - `BuildingBlockProps` (interface): interface BuildingBlockProps { part: BuildingPartPlacement; palette?: BuildingMaterialPalette } — ⚠ undocumented
2424
2629
  - `GeneratedBuilding` (function): function GeneratedBuilding({ building, palette, partRenderer, kit, resolveModelUrl, visibleKinds, }: GeneratedBuildingProps): React.JSX.Element — ⚠ undocumented
2425
- - `GeneratedBuildingProps` (interface): interface GeneratedBuildingProps — ⚠ undocumented
2426
- - `InstancedBuildingPlacement` (interface): interface InstancedBuildingPlacement — ⚠ undocumented
2630
+ - `GeneratedBuildingProps` (interface): interface GeneratedBuildingProps { building: GeneratedBuildingData; palette?: BuildingMaterialPalette; partRenderer?: BuildingKitRenderer; kit?: BuildingKit; resolveModelUrl?: (model: string) => string; visibleKinds?: readonly BuildingPartKind[] } — ⚠ undocumented
2631
+ - `InstancedBuildingPlacement` (interface): interface InstancedBuildingPlacement { building: GeneratedBuildingData; position?: readonly [number, number, number]; rotationY?: number; pivot?: readonly [number, number] } — ⚠ undocumented
2427
2632
  - `InstancedBuildings` (function): function InstancedBuildings({ buildings, palette, kit, resolveModelUrl, visibleKinds, }: InstancedBuildingsProps): React.JSX.Element | null — ⚠ undocumented
2428
- - `InstancedBuildingsProps` (interface): interface InstancedBuildingsProps — ⚠ undocumented
2633
+ - `InstancedBuildingsProps` (interface): interface InstancedBuildingsProps { buildings: readonly InstancedBuildingPlacement[]; palette?: BuildingMaterialPalette; kit?: BuildingKit; resolveModelUrl?: (model: string) => string; visibleKinds?: readonly BuildingPartKind[] } — ⚠ undocumented
2429
2634
  - `PlacementGhost` (function): function PlacementGhost({ preview, height = 1, validColor = "#34d399", invalidColor = "#f87171", }: PlacementGhostProps): React.JSX.Element | null — Cursor-following build ghost: valid/invalid tint from a placement controller preview.
2430
- - `PlacementGhostProps` (interface): interface PlacementGhostProps — ⚠ undocumented
2635
+ - `PlacementGhostProps` (interface): interface PlacementGhostProps { preview: PlacementPreview | null; height?: number; validColor?: string; invalidColor?: string } — ⚠ undocumented · used by `PlacementGhost`: Cursor-following build ghost: valid/invalid tint from a placement controller preview.
2431
2636
  - `TransformGizmo` (const): const TransformGizmo: React.MemoExoticComponent<({ position, rotationY, mode, snapMode, gridSize, space, rotationSnap, scaleSnap, lift, size, groundSnap, onDraggingChange, onRelease, }: TransformGizmoProps) => React.JSX.Element> — Runtime selection/move gizmo shared by games and the editor. Wraps TransformControls; callers own selection and commit side-effects.
2432
2637
  - `TransformGizmoMode` (type): type TransformGizmoMode = "translate" | "rotate" | "scale" — Active TransformControls mode for the runtime selection gizmo.
2433
2638
  - `TransformGizmoPose` (interface): interface TransformGizmoPose — Pose reported by {@link TransformGizmo} when the user releases a drag.
@@ -2437,14 +2642,14 @@
2437
2642
  ## @jgengine/shell/structures
2438
2643
 
2439
2644
  - `BuildingBlock` (function): function BuildingBlock({ part, palette }: BuildingBlockProps): React.JSX.Element — ⚠ undocumented
2440
- - `BuildingBlockProps` (interface): interface BuildingBlockProps — ⚠ undocumented
2645
+ - `BuildingBlockProps` (interface): interface BuildingBlockProps { part: BuildingPartPlacement; palette?: BuildingMaterialPalette } — ⚠ undocumented
2441
2646
  - `GeneratedBuilding` (function): function GeneratedBuilding({ building, palette, partRenderer, kit, resolveModelUrl, visibleKinds, }: GeneratedBuildingProps): React.JSX.Element — ⚠ undocumented
2442
- - `GeneratedBuildingProps` (interface): interface GeneratedBuildingProps — ⚠ undocumented
2443
- - `InstancedBuildingPlacement` (interface): interface InstancedBuildingPlacement — ⚠ undocumented
2647
+ - `GeneratedBuildingProps` (interface): interface GeneratedBuildingProps { building: GeneratedBuildingData; palette?: BuildingMaterialPalette; partRenderer?: BuildingKitRenderer; kit?: BuildingKit; resolveModelUrl?: (model: string) => string; visibleKinds?: readonly BuildingPartKind[] } — ⚠ undocumented
2648
+ - `InstancedBuildingPlacement` (interface): interface InstancedBuildingPlacement { building: GeneratedBuildingData; position?: readonly [number, number, number]; rotationY?: number; pivot?: readonly [number, number] } — ⚠ undocumented
2444
2649
  - `InstancedBuildings` (function): function InstancedBuildings({ buildings, palette, kit, resolveModelUrl, visibleKinds, }: InstancedBuildingsProps): React.JSX.Element | null — ⚠ undocumented
2445
- - `InstancedBuildingsProps` (interface): interface InstancedBuildingsProps — ⚠ undocumented
2650
+ - `InstancedBuildingsProps` (interface): interface InstancedBuildingsProps { buildings: readonly InstancedBuildingPlacement[]; palette?: BuildingMaterialPalette; kit?: BuildingKit; resolveModelUrl?: (model: string) => string; visibleKinds?: readonly BuildingPartKind[] } — ⚠ undocumented
2446
2651
  - `PlacementGhost` (function): function PlacementGhost({ preview, height = 1, validColor = "#34d399", invalidColor = "#f87171", }: PlacementGhostProps): React.JSX.Element | null — Cursor-following build ghost: valid/invalid tint from a placement controller preview.
2447
- - `PlacementGhostProps` (interface): interface PlacementGhostProps — ⚠ undocumented
2652
+ - `PlacementGhostProps` (interface): interface PlacementGhostProps { preview: PlacementPreview | null; height?: number; validColor?: string; invalidColor?: string } — ⚠ undocumented · used by `PlacementGhost`: Cursor-following build ghost: valid/invalid tint from a placement controller preview.
2448
2653
  - `TransformGizmo` (const): const TransformGizmo: React.MemoExoticComponent<({ position, rotationY, mode, snapMode, gridSize, space, rotationSnap, scaleSnap, lift, size, groundSnap, onDraggingChange, onRelease, }: TransformGizmoProps) => React.JSX.Element> — Runtime selection/move gizmo shared by games and the editor. Wraps TransformControls; callers own selection and commit side-effects.
2449
2654
  - `TransformGizmoMode` (type): type TransformGizmoMode = "translate" | "rotate" | "scale" — Active TransformControls mode for the runtime selection gizmo.
2450
2655
  - `TransformGizmoPose` (interface): interface TransformGizmoPose — Pose reported by {@link TransformGizmo} when the user releases a drag.
@@ -2454,23 +2659,23 @@
2454
2659
  ## @jgengine/shell/structures/GeneratedBuilding
2455
2660
 
2456
2661
  - `BuildingBlock` (function): function BuildingBlock({ part, palette }: BuildingBlockProps): React.JSX.Element — ⚠ undocumented
2457
- - `BuildingBlockProps` (interface): interface BuildingBlockProps — ⚠ undocumented
2662
+ - `BuildingBlockProps` (interface): interface BuildingBlockProps { part: BuildingPartPlacement; palette?: BuildingMaterialPalette } — ⚠ undocumented
2458
2663
  - `BuildingFacade` (type): type BuildingFacade = "front" | "back" | "left" | "right" | "roof" — ⚠ undocumented
2459
- - `BuildingKitRenderer` (interface): interface BuildingKitRenderer — ⚠ undocumented
2460
- - `BuildingMaterialPalette` (interface): interface BuildingMaterialPalette — ⚠ undocumented
2461
- - `BuildingPartKind` (type): type BuildingPartKind = | "wall" | "window" | "awning" | "airConditioner" | "clothesline" | "storefront" | "shutter" | "storeSign" | "roof" | "roofProp" | "guardrail" | "corner" — ⚠ undocumented
2462
- - `BuildingPartPlacement` (interface): interface BuildingPartPlacement — ⚠ undocumented
2664
+ - `BuildingKitRenderer` (interface): interface BuildingKitRenderer { renderPart?: (part: BuildingPartPlacement) => ReactNode | undefined } — ⚠ undocumented
2665
+ - `BuildingMaterialPalette` (type): type BuildingMaterialPalette = Partial<Record<BuildingPartKind, BuildingSurface>> — Per part kind: a hex colour, or a colour plus tiled PBR maps (`BuildingSurfaceMaterial`).
2666
+ - `BuildingPartKind` (type): type BuildingPartKind = | "wall" | "window" | "awning" | "airConditioner" | "clothesline" | "storefront" | "shutter" | "storeSign" | "roof" | "roofProp" | "guardrail" | "corner" — ⚠ undocumented · used by `resolveBuildingKitPart` (@jgengine/core/world/buildingKit): Resolves one generated part against a kit.
2667
+ - `BuildingPartPlacement` (interface): interface BuildingPartPlacement { id: string; kind: BuildingPartKind; facade: BuildingFacade; position: readonly [number, number, number]; rotationY: number; scale: readonly [number, number, number]; kit?: BuildingKitSlot } — ⚠ undocumented
2463
2668
  - `GeneratedBuilding` (function): function GeneratedBuilding({ building, palette, partRenderer, kit, resolveModelUrl, visibleKinds, }: GeneratedBuildingProps): React.JSX.Element — ⚠ undocumented
2464
- - `GeneratedBuildingData` (interface): interface GeneratedBuildingData — ⚠ undocumented
2465
- - `GeneratedBuildingProps` (interface): interface GeneratedBuildingProps — ⚠ undocumented
2466
- - `InstancedBuildingPlacement` (interface): interface InstancedBuildingPlacement — ⚠ undocumented
2669
+ - `GeneratedBuildingData` (interface): interface GeneratedBuildingData { id: string; parts: readonly BuildingPartPlacement[] } — ⚠ undocumented
2670
+ - `GeneratedBuildingProps` (interface): interface GeneratedBuildingProps { building: GeneratedBuildingData; palette?: BuildingMaterialPalette; partRenderer?: BuildingKitRenderer; kit?: BuildingKit; resolveModelUrl?: (model: string) => string; visibleKinds?: readonly BuildingPartKind[] } — ⚠ undocumented
2671
+ - `InstancedBuildingPlacement` (interface): interface InstancedBuildingPlacement { building: GeneratedBuildingData; position?: readonly [number, number, number]; rotationY?: number; pivot?: readonly [number, number] } — ⚠ undocumented
2467
2672
  - `InstancedBuildings` (function): function InstancedBuildings({ buildings, palette, kit, resolveModelUrl, visibleKinds, }: InstancedBuildingsProps): React.JSX.Element | null — ⚠ undocumented
2468
- - `InstancedBuildingsProps` (interface): interface InstancedBuildingsProps — ⚠ undocumented
2673
+ - `InstancedBuildingsProps` (interface): interface InstancedBuildingsProps { buildings: readonly InstancedBuildingPlacement[]; palette?: BuildingMaterialPalette; kit?: BuildingKit; resolveModelUrl?: (model: string) => string; visibleKinds?: readonly BuildingPartKind[] } — ⚠ undocumented
2469
2674
 
2470
2675
  ## @jgengine/shell/structures/PlacementGhost
2471
2676
 
2472
2677
  - `PlacementGhost` (function): function PlacementGhost({ preview, height = 1, validColor = "#34d399", invalidColor = "#f87171", }: PlacementGhostProps): React.JSX.Element | null — Cursor-following build ghost: valid/invalid tint from a placement controller preview.
2473
- - `PlacementGhostProps` (interface): interface PlacementGhostProps — ⚠ undocumented
2678
+ - `PlacementGhostProps` (interface): interface PlacementGhostProps { preview: PlacementPreview | null; height?: number; validColor?: string; invalidColor?: string } — ⚠ undocumented · used by `PlacementGhost`: Cursor-following build ghost: valid/invalid tint from a placement controller preview.
2474
2679
 
2475
2680
  ## @jgengine/shell/structures/TransformGizmo
2476
2681
 
@@ -2489,109 +2694,115 @@
2489
2694
  - `listBuildingKits` (function): function listBuildingKits(): string[] — Every registered kit id — the editor's kit picker and diagnostics read this.
2490
2695
  - `registerBuildingKit` (function): function registerBuildingKit(kit: BuildingKit): void — Registers a kit under its own `id`, replacing any kit already registered under that name.
2491
2696
 
2697
+ ## @jgengine/shell/structures/buildingSurface
2698
+
2699
+ - `TexturedBuildingSurface` (type): type TexturedBuildingSurface = BuildingSurfaceMaterial & { maps: BuildingSurfaceMaps } — A surface that names at least one map URL.
2700
+ - `surfaceHasMaps` (function): function surfaceHasMaps(surface: BuildingSurfaceMaterial | undefined): surface is TexturedBuildingSurface — True when the surface names at least one map URL, so the batch needs the textured path.
2701
+ - `surfaceKey` (function): function surfaceKey(surface: BuildingSurfaceMaterial | undefined): string — A stable key for bucketing instances that share one surface (same colour, maps, tiling, and response).
2702
+
2492
2703
  ## @jgengine/shell/terrain
2493
2704
 
2494
2705
  - `CarvedTerrain` (function): function CarvedTerrain({ field, size, segments, center, colors, heightRange, paletteAt, roughness = 0.95, metalness = 0, surfaceMaterial, receiveShadow = true, epoch = 0, ...meshProps }: CarvedTerrainProps): React.JSX.Element — Renders a `TerrainField` as a deformed ground mesh — the crater/mound view for destructible terrain. Because the geometry samples `field.sampleHeight`, a `CarvableField.carve(...)` shows as a real bowl once `epoch` changes. Pair with `InstancedBodies` to see debris resting in the crater it blasted.
2495
- - `CarvedTerrainProps` (interface): interface CarvedTerrainProps extends Omit<ThreeElements["mesh"], "args" | "children" | "geometry" | "material"> — ⚠ undocumented
2706
+ - `CarvedTerrainProps` (interface): interface CarvedTerrainProps extends Omit<ThreeElements["mesh"], "args" | "children" | "geometry" | "material"> { field: TerrainField; size?: FieldGroundOptions["size"]; segments?: FieldGroundOptions["segments"]; center?: FieldGroundOptions["center"]; colors?: FieldGroundOptions["colors"]; heightRange?: FieldGroundOptions["heightRange"]; paletteAt?: … — ⚠ undocumented · used by `CarvedTerrain`: Renders a `TerrainField` as a deformed ground mesh — the crater/mound view for destructible terrain.
2496
2707
  - `DEFAULT_GRASS_WIND` (const): const DEFAULT_GRASS_WIND: Required<GrassWindOptions> — ⚠ undocumented
2497
2708
  - `EditableGround` (function): function EditableGround({ terrain, bounds, segments = 96, version = 0, baseColor = "#3f6b3a", surfaceColors = DEFAULT_SURFACE_COLORS, }: EditableGroundProps): React.JSX.Element — ⚠ undocumented
2498
- - `EditableGroundProps` (interface): interface EditableGroundProps — ⚠ undocumented
2499
- - `FieldGroundOptions` (interface): interface FieldGroundOptions — ⚠ undocumented
2709
+ - `EditableGroundProps` (interface): interface EditableGroundProps { terrain: Pick<EditableTerrain, "sampleHeight" | "surfaceAt">; bounds: Aabb; segments?: number; version?: number; baseColor?: string; surfaceColors?: Record<string, string> } — ⚠ undocumented
2710
+ - `FieldGroundOptions` (interface): interface FieldGroundOptions { size?: TerrainArea; segments?: number | readonly [x: number, z: number]; center?: readonly [x: number, z: number]; colors?: TerrainVertexColorOptions; heightRange?: readonly [min: number, max: number]; paletteAt?: TerrainPaletteSampler; s… — ⚠ undocumented
2500
2711
  - `FractalNoiseConfig` (interface): interface FractalNoiseConfig — Octave settings for {@link fractalNoise}: frequency, layering, and optional ridged shaping.
2501
- - `GrassBladeGeometryOptions` (interface): interface GrassBladeGeometryOptions — ⚠ undocumented
2502
- - `GrassField` (function): function GrassField({ count = DEFAULT_GRASS_COUNT, density = DEFAULT_GRASS_DENSITY, budget, area = 40, seed = 1, segments = 4, bladeHeight, bladeWidth, bladeBend, tuftBlades = GRASS_TUFT_BLADES, tuftRadius, edgeFeather, exclude, heightAt, colorBase, colorTip, colorGround, colorVariation, wind, dista… — ⚠ undocumented
2503
- - `GrassFieldProps` (interface): interface GrassFieldProps extends Omit<ThreeElements["mesh"], "args" | "children" | "geometry" | "material"> — ⚠ undocumented
2504
- - `GrassMaterialHandle` (interface): interface GrassMaterialHandle — ⚠ undocumented
2505
- - `GrassMaterialOptions` (interface): interface GrassMaterialOptions — ⚠ undocumented
2712
+ - `GrassBladeGeometryOptions` (interface): interface GrassBladeGeometryOptions { count?: number; area?: TerrainArea; seed?: TerrainSeed; segments?: number; height?: GrassRange; width?: GrassRange; bend?: GrassRange; tuftBlades?: number; tuftRadius?: number; edgeFeather?: number; exclude?: readonly GrassExclusion[]; he… — ⚠ undocumented
2713
+ - `GrassField` (function): function GrassField({ count = DEFAULT_GRASS_COUNT, density = DEFAULT_GRASS_DENSITY, budget, area = 40, seed = 1, segments = 4, bladeHeight, bladeWidth, bladeBend, tuftBlades = GRASS_TUFT_BLADES, tuftRadius, edgeFeather, exclude, heightAt, colorBase, colorTip, colorGround, colorVariation, wind, dista… — Seeded grass tufts with bounded per-camera chunk submission; density and budgets stay in blades.
2714
+ - `GrassFieldProps` (interface): interface GrassFieldProps extends Omit<ThreeElements["mesh"], "args" | "children" | "geometry" | "material"> { count?: number; density?: number; budget?: number; area?: TerrainArea; seed?: GrassBladeGeometryOptions["seed"]; segments?: number; bladeHeight?: GrassRange; bladeWidth?: GrassRange; bladeBend?: GrassRange; tuftBlades?: number; tuftRadius… — ⚠ undocumented
2715
+ - `GrassMaterialHandle` (interface): interface GrassMaterialHandle { material: THREE.MeshStandardMaterial; uniforms: GrassShaderUniforms } — ⚠ undocumented
2716
+ - `GrassMaterialOptions` (interface): interface GrassMaterialOptions { colorBase?: THREE.ColorRepresentation; colorTip?: THREE.ColorRepresentation; colorGround?: THREE.ColorRepresentation; colorVariation?: number; wind?: GrassWindOptions | false; distanceFade?: GrassDistanceFadeOptions | false; normalLift?: … — ⚠ undocumented
2506
2717
  - `GrassRange` (type): type GrassRange = number | readonly [min: number, max: number] — ⚠ undocumented
2507
- - `GrassShaderUniforms` (interface): interface GrassShaderUniforms — ⚠ undocumented
2508
- - `GrassWindOptions` (interface): interface GrassWindOptions — ⚠ undocumented
2718
+ - `GrassShaderUniforms` (interface): interface GrassShaderUniforms { uTime: THREE.IUniform<number>; uWindDirection: THREE.IUniform<THREE.Vector2>; uWindStrength: THREE.IUniform<number>; uWindSpeed: THREE.IUniform<number>; uWindGustScale: THREE.IUniform<number>; uWindFlutter: THREE.IUniform<number>; uColorB… — ⚠ undocumented
2719
+ - `GrassWindOptions` (interface): interface GrassWindOptions { direction?: readonly [x: number, z: number]; strength?: number; speed?: number; gustScale?: number; flutter?: number; layered?: boolean } — ⚠ undocumented
2509
2720
  - `HeightfieldColorFn` (type): type HeightfieldColorFn = (x: number, z: number, height: number, out: THREE.Color) => void — Per-vertex color hook for {@link displaceHeightfieldGeometry}; write the tone into `out`.
2510
2721
  - `HeightfieldDisplaceOptions` (interface): interface HeightfieldDisplaceOptions — Options for {@link displaceHeightfieldGeometry}.
2511
2722
  - `HeightfieldRect` (interface): interface HeightfieldRect — XZ rectangle in world units — matches the shape of a terraform snapshot's `bounds`.
2512
2723
  - `NoiseFieldConfig` (interface): interface NoiseFieldConfig — Configuration for {@link noiseField}: seed, amplitude, and fractal noise shaping.
2513
2724
  - `ProceduralGround` (function): function ProceduralGround({ terrain, colors, roughness = 0.94, metalness = 0, receiveShadow = true, ...meshProps }: ProceduralGroundProps): React.JSX.Element — ⚠ undocumented
2514
- - `ProceduralGroundProps` (interface): interface ProceduralGroundProps extends Omit<ThreeElements["mesh"], "args" | "children" | "geometry" | "material"> — ⚠ undocumented
2515
- - `ProceduralTerrainConfig` (interface): interface ProceduralTerrainConfig — ⚠ undocumented
2516
- - `ResolvedGrassBladeGeometryOptions` (interface): interface ResolvedGrassBladeGeometryOptions — ⚠ undocumented
2517
- - `ResolvedTerrainSegments` (interface): interface ResolvedTerrainSegments — ⚠ undocumented
2518
- - `ResolvedTerrainSize` (interface): interface ResolvedTerrainSize — ⚠ undocumented
2725
+ - `ProceduralGroundProps` (interface): interface ProceduralGroundProps extends Omit<ThreeElements["mesh"], "args" | "children" | "geometry" | "material"> { terrain?: ProceduralTerrainConfig; colors?: TerrainVertexColorOptions; roughness?: number; metalness?: number } — ⚠ undocumented
2726
+ - `ProceduralTerrainConfig` (interface): interface ProceduralTerrainConfig { size?: TerrainArea; segments?: number | readonly [x: number, z: number]; seed?: TerrainSeed; height?: number; moundScale?: number; octaves?: number; ridged?: boolean; baseOffset?: number } — ⚠ undocumented
2727
+ - `ResolvedGrassBladeGeometryOptions` (interface): interface ResolvedGrassBladeGeometryOptions { count: number; area: TerrainArea; seed: TerrainSeed; segments: number; height: readonly [min: number, max: number]; width: readonly [min: number, max: number]; bend: readonly [min: number, max: number]; tuftBlades: number; tuftRadius: num… — ⚠ undocumented
2728
+ - `ResolvedTerrainSegments` (interface): interface ResolvedTerrainSegments { x: number; z: number } — ⚠ undocumented
2729
+ - `ResolvedTerrainSize` (interface): interface ResolvedTerrainSize { width: number; depth: number } — ⚠ undocumented
2519
2730
  - `TerraformBrushCursor` (function): function TerraformBrushCursor({ center, y = 0.05, radius, mode }: TerraformBrushCursorProps): React.JSX.Element | null — ⚠ undocumented
2520
- - `TerraformBrushCursorProps` (interface): interface TerraformBrushCursorProps — ⚠ undocumented
2731
+ - `TerraformBrushCursorProps` (interface): interface TerraformBrushCursorProps { center: readonly [number, number] | null; y?: number; radius: number; mode: TerraformMode } — ⚠ undocumented
2521
2732
  - `TerrainArea` (type): type TerrainArea = number | readonly [width: number, depth: number] — ⚠ undocumented
2522
2733
  - `TerrainField` (interface): interface TerrainField — A sampleable ground surface: height and normal at any x/z, with optional bounds and water level.
2523
2734
  - `TerrainHeightSampler` (type): type TerrainHeightSampler = (x: number, z: number) => number — ⚠ undocumented
2524
2735
  - `TerrainNormal` (type): type TerrainNormal = readonly [number, number, number] — A surface normal vector at a terrain sample point.
2525
2736
  - `TerrainSeed` (type): type TerrainSeed = number | string — ⚠ undocumented
2526
2737
  - `TerrainSurfaceColorOptions` (interface): interface TerrainSurfaceColorOptions — Slope/noise surface shading laid over the height lerp so untextured ground reads like terrain instead of a smooth two-tone gradient. All knobs are optional with sane defaults; every field is a pure function of vertex position (+ `seed`), so the coloring is stable across rebuilds and tests.
2527
- - `TerrainVertexColorOptions` (interface): interface TerrainVertexColorOptions — ⚠ undocumented
2738
+ - `TerrainVertexColorOptions` (interface): interface TerrainVertexColorOptions { low?: THREE.ColorRepresentation; high?: THREE.ColorRepresentation; waterline?: THREE.ColorRepresentation; waterlineHeight?: number } — ⚠ undocumented
2528
2739
  - `displaceHeightfieldGeometry` (function): function displaceHeightfieldGeometry(geometry: THREE.BufferGeometry, sampleHeight: (x: number, z: number) => number, options: HeightfieldDisplaceOptions): void — Re-samples a `PlaneGeometry(width, depth, segments, segments).rotateX(-π/2)` heightfield mesh from a `sampleHeight` field in place: vertex Y, optional vertex color, grid central-difference normals, and an analytically maintained bounding sphere. With a dirty `region`, the work is O(region vertices) — heights/colors update inside the covering vertex window, normals inside the window plus a one-vertex ring — never a whole-mesh `computeVertexNormals`/`computeBoundingSphere` pass, which is what keeps per-frame brush stamps inside the editor's frame budget. The bounding sphere derives from the fixed plane extents plus a running height range kept on `geometry.userData`; partial passes only expand it, a full pass resets it exactly.
2529
2740
 
2530
2741
  ## @jgengine/shell/terrain
2531
2742
 
2532
2743
  - `CarvedTerrain` (function): function CarvedTerrain({ field, size, segments, center, colors, heightRange, paletteAt, roughness = 0.95, metalness = 0, surfaceMaterial, receiveShadow = true, epoch = 0, ...meshProps }: CarvedTerrainProps): React.JSX.Element — Renders a `TerrainField` as a deformed ground mesh — the crater/mound view for destructible terrain. Because the geometry samples `field.sampleHeight`, a `CarvableField.carve(...)` shows as a real bowl once `epoch` changes. Pair with `InstancedBodies` to see debris resting in the crater it blasted.
2533
- - `CarvedTerrainProps` (interface): interface CarvedTerrainProps extends Omit<ThreeElements["mesh"], "args" | "children" | "geometry" | "material"> — ⚠ undocumented
2744
+ - `CarvedTerrainProps` (interface): interface CarvedTerrainProps extends Omit<ThreeElements["mesh"], "args" | "children" | "geometry" | "material"> { field: TerrainField; size?: FieldGroundOptions["size"]; segments?: FieldGroundOptions["segments"]; center?: FieldGroundOptions["center"]; colors?: FieldGroundOptions["colors"]; heightRange?: FieldGroundOptions["heightRange"]; paletteAt?: … — ⚠ undocumented · used by `CarvedTerrain`: Renders a `TerrainField` as a deformed ground mesh — the crater/mound view for destructible terrain.
2534
2745
  - `DEFAULT_GRASS_WIND` (const): const DEFAULT_GRASS_WIND: Required<GrassWindOptions> — ⚠ undocumented
2535
2746
  - `EditableGround` (function): function EditableGround({ terrain, bounds, segments = 96, version = 0, baseColor = "#3f6b3a", surfaceColors = DEFAULT_SURFACE_COLORS, }: EditableGroundProps): React.JSX.Element — ⚠ undocumented
2536
- - `EditableGroundProps` (interface): interface EditableGroundProps — ⚠ undocumented
2537
- - `FieldGroundOptions` (interface): interface FieldGroundOptions — ⚠ undocumented
2747
+ - `EditableGroundProps` (interface): interface EditableGroundProps { terrain: Pick<EditableTerrain, "sampleHeight" | "surfaceAt">; bounds: Aabb; segments?: number; version?: number; baseColor?: string; surfaceColors?: Record<string, string> } — ⚠ undocumented
2748
+ - `FieldGroundOptions` (interface): interface FieldGroundOptions { size?: TerrainArea; segments?: number | readonly [x: number, z: number]; center?: readonly [x: number, z: number]; colors?: TerrainVertexColorOptions; heightRange?: readonly [min: number, max: number]; paletteAt?: TerrainPaletteSampler; s… — ⚠ undocumented
2538
2749
  - `FractalNoiseConfig` (interface): interface FractalNoiseConfig — Octave settings for {@link fractalNoise}: frequency, layering, and optional ridged shaping.
2539
- - `GrassBladeGeometryOptions` (interface): interface GrassBladeGeometryOptions — ⚠ undocumented
2540
- - `GrassField` (function): function GrassField({ count = DEFAULT_GRASS_COUNT, density = DEFAULT_GRASS_DENSITY, budget, area = 40, seed = 1, segments = 4, bladeHeight, bladeWidth, bladeBend, tuftBlades = GRASS_TUFT_BLADES, tuftRadius, edgeFeather, exclude, heightAt, colorBase, colorTip, colorGround, colorVariation, wind, dista… — ⚠ undocumented
2541
- - `GrassFieldProps` (interface): interface GrassFieldProps extends Omit<ThreeElements["mesh"], "args" | "children" | "geometry" | "material"> — ⚠ undocumented
2542
- - `GrassMaterialHandle` (interface): interface GrassMaterialHandle — ⚠ undocumented
2543
- - `GrassMaterialOptions` (interface): interface GrassMaterialOptions — ⚠ undocumented
2750
+ - `GrassBladeGeometryOptions` (interface): interface GrassBladeGeometryOptions { count?: number; area?: TerrainArea; seed?: TerrainSeed; segments?: number; height?: GrassRange; width?: GrassRange; bend?: GrassRange; tuftBlades?: number; tuftRadius?: number; edgeFeather?: number; exclude?: readonly GrassExclusion[]; he… — ⚠ undocumented
2751
+ - `GrassField` (function): function GrassField({ count = DEFAULT_GRASS_COUNT, density = DEFAULT_GRASS_DENSITY, budget, area = 40, seed = 1, segments = 4, bladeHeight, bladeWidth, bladeBend, tuftBlades = GRASS_TUFT_BLADES, tuftRadius, edgeFeather, exclude, heightAt, colorBase, colorTip, colorGround, colorVariation, wind, dista… — Seeded grass tufts with bounded per-camera chunk submission; density and budgets stay in blades.
2752
+ - `GrassFieldProps` (interface): interface GrassFieldProps extends Omit<ThreeElements["mesh"], "args" | "children" | "geometry" | "material"> { count?: number; density?: number; budget?: number; area?: TerrainArea; seed?: GrassBladeGeometryOptions["seed"]; segments?: number; bladeHeight?: GrassRange; bladeWidth?: GrassRange; bladeBend?: GrassRange; tuftBlades?: number; tuftRadius… — ⚠ undocumented
2753
+ - `GrassMaterialHandle` (interface): interface GrassMaterialHandle { material: THREE.MeshStandardMaterial; uniforms: GrassShaderUniforms } — ⚠ undocumented
2754
+ - `GrassMaterialOptions` (interface): interface GrassMaterialOptions { colorBase?: THREE.ColorRepresentation; colorTip?: THREE.ColorRepresentation; colorGround?: THREE.ColorRepresentation; colorVariation?: number; wind?: GrassWindOptions | false; distanceFade?: GrassDistanceFadeOptions | false; normalLift?: … — ⚠ undocumented
2544
2755
  - `GrassRange` (type): type GrassRange = number | readonly [min: number, max: number] — ⚠ undocumented
2545
- - `GrassShaderUniforms` (interface): interface GrassShaderUniforms — ⚠ undocumented
2546
- - `GrassWindOptions` (interface): interface GrassWindOptions — ⚠ undocumented
2756
+ - `GrassShaderUniforms` (interface): interface GrassShaderUniforms { uTime: THREE.IUniform<number>; uWindDirection: THREE.IUniform<THREE.Vector2>; uWindStrength: THREE.IUniform<number>; uWindSpeed: THREE.IUniform<number>; uWindGustScale: THREE.IUniform<number>; uWindFlutter: THREE.IUniform<number>; uColorB… — ⚠ undocumented
2757
+ - `GrassWindOptions` (interface): interface GrassWindOptions { direction?: readonly [x: number, z: number]; strength?: number; speed?: number; gustScale?: number; flutter?: number; layered?: boolean } — ⚠ undocumented
2547
2758
  - `HeightfieldColorFn` (type): type HeightfieldColorFn = (x: number, z: number, height: number, out: THREE.Color) => void — Per-vertex color hook for {@link displaceHeightfieldGeometry}; write the tone into `out`.
2548
2759
  - `HeightfieldDisplaceOptions` (interface): interface HeightfieldDisplaceOptions — Options for {@link displaceHeightfieldGeometry}.
2549
2760
  - `HeightfieldRect` (interface): interface HeightfieldRect — XZ rectangle in world units — matches the shape of a terraform snapshot's `bounds`.
2550
2761
  - `NoiseFieldConfig` (interface): interface NoiseFieldConfig — Configuration for {@link noiseField}: seed, amplitude, and fractal noise shaping.
2551
2762
  - `ProceduralGround` (function): function ProceduralGround({ terrain, colors, roughness = 0.94, metalness = 0, receiveShadow = true, ...meshProps }: ProceduralGroundProps): React.JSX.Element — ⚠ undocumented
2552
- - `ProceduralGroundProps` (interface): interface ProceduralGroundProps extends Omit<ThreeElements["mesh"], "args" | "children" | "geometry" | "material"> — ⚠ undocumented
2553
- - `ProceduralTerrainConfig` (interface): interface ProceduralTerrainConfig — ⚠ undocumented
2554
- - `ResolvedGrassBladeGeometryOptions` (interface): interface ResolvedGrassBladeGeometryOptions — ⚠ undocumented
2555
- - `ResolvedTerrainSegments` (interface): interface ResolvedTerrainSegments — ⚠ undocumented
2556
- - `ResolvedTerrainSize` (interface): interface ResolvedTerrainSize — ⚠ undocumented
2763
+ - `ProceduralGroundProps` (interface): interface ProceduralGroundProps extends Omit<ThreeElements["mesh"], "args" | "children" | "geometry" | "material"> { terrain?: ProceduralTerrainConfig; colors?: TerrainVertexColorOptions; roughness?: number; metalness?: number } — ⚠ undocumented
2764
+ - `ProceduralTerrainConfig` (interface): interface ProceduralTerrainConfig { size?: TerrainArea; segments?: number | readonly [x: number, z: number]; seed?: TerrainSeed; height?: number; moundScale?: number; octaves?: number; ridged?: boolean; baseOffset?: number } — ⚠ undocumented
2765
+ - `ResolvedGrassBladeGeometryOptions` (interface): interface ResolvedGrassBladeGeometryOptions { count: number; area: TerrainArea; seed: TerrainSeed; segments: number; height: readonly [min: number, max: number]; width: readonly [min: number, max: number]; bend: readonly [min: number, max: number]; tuftBlades: number; tuftRadius: num… — ⚠ undocumented
2766
+ - `ResolvedTerrainSegments` (interface): interface ResolvedTerrainSegments { x: number; z: number } — ⚠ undocumented
2767
+ - `ResolvedTerrainSize` (interface): interface ResolvedTerrainSize { width: number; depth: number } — ⚠ undocumented
2557
2768
  - `TerraformBrushCursor` (function): function TerraformBrushCursor({ center, y = 0.05, radius, mode }: TerraformBrushCursorProps): React.JSX.Element | null — ⚠ undocumented
2558
- - `TerraformBrushCursorProps` (interface): interface TerraformBrushCursorProps — ⚠ undocumented
2769
+ - `TerraformBrushCursorProps` (interface): interface TerraformBrushCursorProps { center: readonly [number, number] | null; y?: number; radius: number; mode: TerraformMode } — ⚠ undocumented
2559
2770
  - `TerrainArea` (type): type TerrainArea = number | readonly [width: number, depth: number] — ⚠ undocumented
2560
2771
  - `TerrainField` (interface): interface TerrainField — A sampleable ground surface: height and normal at any x/z, with optional bounds and water level.
2561
2772
  - `TerrainHeightSampler` (type): type TerrainHeightSampler = (x: number, z: number) => number — ⚠ undocumented
2562
2773
  - `TerrainNormal` (type): type TerrainNormal = readonly [number, number, number] — A surface normal vector at a terrain sample point.
2563
2774
  - `TerrainSeed` (type): type TerrainSeed = number | string — ⚠ undocumented
2564
2775
  - `TerrainSurfaceColorOptions` (interface): interface TerrainSurfaceColorOptions — Slope/noise surface shading laid over the height lerp so untextured ground reads like terrain instead of a smooth two-tone gradient. All knobs are optional with sane defaults; every field is a pure function of vertex position (+ `seed`), so the coloring is stable across rebuilds and tests.
2565
- - `TerrainVertexColorOptions` (interface): interface TerrainVertexColorOptions — ⚠ undocumented
2776
+ - `TerrainVertexColorOptions` (interface): interface TerrainVertexColorOptions { low?: THREE.ColorRepresentation; high?: THREE.ColorRepresentation; waterline?: THREE.ColorRepresentation; waterlineHeight?: number } — ⚠ undocumented
2566
2777
  - `displaceHeightfieldGeometry` (function): function displaceHeightfieldGeometry(geometry: THREE.BufferGeometry, sampleHeight: (x: number, z: number) => number, options: HeightfieldDisplaceOptions): void — Re-samples a `PlaneGeometry(width, depth, segments, segments).rotateX(-π/2)` heightfield mesh from a `sampleHeight` field in place: vertex Y, optional vertex color, grid central-difference normals, and an analytically maintained bounding sphere. With a dirty `region`, the work is O(region vertices) — heights/colors update inside the covering vertex window, normals inside the window plus a one-vertex ring — never a whole-mesh `computeVertexNormals`/`computeBoundingSphere` pass, which is what keeps per-frame brush stamps inside the editor's frame budget. The bounding sphere derives from the fixed plane extents plus a running height range kept on `geometry.userData`; partial passes only expand it, a full pass resets it exactly.
2567
2778
 
2568
2779
  ## @jgengine/shell/terrain/CarvedTerrain
2569
2780
 
2570
2781
  - `CarvedTerrain` (function): function CarvedTerrain({ field, size, segments, center, colors, heightRange, paletteAt, roughness = 0.95, metalness = 0, surfaceMaterial, receiveShadow = true, epoch = 0, ...meshProps }: CarvedTerrainProps): React.JSX.Element — Renders a `TerrainField` as a deformed ground mesh — the crater/mound view for destructible terrain. Because the geometry samples `field.sampleHeight`, a `CarvableField.carve(...)` shows as a real bowl once `epoch` changes. Pair with `InstancedBodies` to see debris resting in the crater it blasted.
2571
- - `CarvedTerrainProps` (interface): interface CarvedTerrainProps extends Omit<ThreeElements["mesh"], "args" | "children" | "geometry" | "material"> — ⚠ undocumented
2782
+ - `CarvedTerrainProps` (interface): interface CarvedTerrainProps extends Omit<ThreeElements["mesh"], "args" | "children" | "geometry" | "material"> { field: TerrainField; size?: FieldGroundOptions["size"]; segments?: FieldGroundOptions["segments"]; center?: FieldGroundOptions["center"]; colors?: FieldGroundOptions["colors"]; heightRange?: FieldGroundOptions["heightRange"]; paletteAt?: … — ⚠ undocumented · used by `CarvedTerrain`: Renders a `TerrainField` as a deformed ground mesh — the crater/mound view for destructible terrain.
2572
2783
 
2573
2784
  ## @jgengine/shell/terrain/EditableGround
2574
2785
 
2575
2786
  - `EditableGround` (function): function EditableGround({ terrain, bounds, segments = 96, version = 0, baseColor = "#3f6b3a", surfaceColors = DEFAULT_SURFACE_COLORS, }: EditableGroundProps): React.JSX.Element — ⚠ undocumented
2576
- - `EditableGroundProps` (interface): interface EditableGroundProps — ⚠ undocumented
2787
+ - `EditableGroundProps` (interface): interface EditableGroundProps { terrain: Pick<EditableTerrain, "sampleHeight" | "surfaceAt">; bounds: Aabb; segments?: number; version?: number; baseColor?: string; surfaceColors?: Record<string, string> } — ⚠ undocumented
2577
2788
 
2578
2789
  ## @jgengine/shell/terrain/GrassField
2579
2790
 
2580
2791
  - `DEFAULT_GRASS_COUNT` (const): const DEFAULT_GRASS_COUNT: 1500 — ⚠ undocumented
2581
2792
  - `DEFAULT_GRASS_DENSITY` (const): const DEFAULT_GRASS_DENSITY: 4 — Blades per square meter — matches `@jgengine/core/world/vegetation`'s `VEGETATION_DEFAULTS.density`, so an editor-placed grass volume and a bare `<GrassField>` agree on what "4" means.
2582
2793
  - `GRASS_TUFT_BLADES` (const): const GRASS_TUFT_BLADES: 5 — Blades baked into each tuft instance — one instance reads as a clump, not a lone blade.
2583
- - `GrassField` (function): function GrassField({ count = DEFAULT_GRASS_COUNT, density = DEFAULT_GRASS_DENSITY, budget, area = 40, seed = 1, segments = 4, bladeHeight, bladeWidth, bladeBend, tuftBlades = GRASS_TUFT_BLADES, tuftRadius, edgeFeather, exclude, heightAt, colorBase, colorTip, colorGround, colorVariation, wind, dista… — ⚠ undocumented
2584
- - `GrassFieldProps` (interface): interface GrassFieldProps extends Omit<ThreeElements["mesh"], "args" | "children" | "geometry" | "material"> — ⚠ undocumented
2794
+ - `GrassField` (function): function GrassField({ count = DEFAULT_GRASS_COUNT, density = DEFAULT_GRASS_DENSITY, budget, area = 40, seed = 1, segments = 4, bladeHeight, bladeWidth, bladeBend, tuftBlades = GRASS_TUFT_BLADES, tuftRadius, edgeFeather, exclude, heightAt, colorBase, colorTip, colorGround, colorVariation, wind, dista… — Seeded grass tufts with bounded per-camera chunk submission; density and budgets stay in blades.
2795
+ - `GrassFieldProps` (interface): interface GrassFieldProps extends Omit<ThreeElements["mesh"], "args" | "children" | "geometry" | "material"> { count?: number; density?: number; budget?: number; area?: TerrainArea; seed?: GrassBladeGeometryOptions["seed"]; segments?: number; bladeHeight?: GrassRange; bladeWidth?: GrassRange; bladeBend?: GrassRange; tuftBlades?: number; tuftRadius… — ⚠ undocumented
2585
2796
 
2586
2797
  ## @jgengine/shell/terrain/ProceduralGround
2587
2798
 
2588
2799
  - `ProceduralGround` (function): function ProceduralGround({ terrain, colors, roughness = 0.94, metalness = 0, receiveShadow = true, ...meshProps }: ProceduralGroundProps): React.JSX.Element — ⚠ undocumented
2589
- - `ProceduralGroundProps` (interface): interface ProceduralGroundProps extends Omit<ThreeElements["mesh"], "args" | "children" | "geometry" | "material"> — ⚠ undocumented
2800
+ - `ProceduralGroundProps` (interface): interface ProceduralGroundProps extends Omit<ThreeElements["mesh"], "args" | "children" | "geometry" | "material"> { terrain?: ProceduralTerrainConfig; colors?: TerrainVertexColorOptions; roughness?: number; metalness?: number } — ⚠ undocumented
2590
2801
 
2591
2802
  ## @jgengine/shell/terrain/TerraformBrushCursor
2592
2803
 
2593
2804
  - `TerraformBrushCursor` (function): function TerraformBrushCursor({ center, y = 0.05, radius, mode }: TerraformBrushCursorProps): React.JSX.Element | null — ⚠ undocumented
2594
- - `TerraformBrushCursorProps` (interface): interface TerraformBrushCursorProps — ⚠ undocumented
2805
+ - `TerraformBrushCursorProps` (interface): interface TerraformBrushCursorProps { center: readonly [number, number] | null; y?: number; radius: number; mode: TerraformMode } — ⚠ undocumented
2595
2806
 
2596
2807
  ## @jgengine/shell/terrain/grassBudget
2597
2808
 
@@ -2601,20 +2812,20 @@
2601
2812
  ## @jgengine/shell/terrain/grassGeometry
2602
2813
 
2603
2814
  - `GRASS_TUFT_BLADES` (const): const GRASS_TUFT_BLADES: 5 — Blades baked into each tuft instance — one instance reads as a clump, not a lone blade.
2604
- - `GrassBladeGeometryOptions` (interface): interface GrassBladeGeometryOptions — ⚠ undocumented
2815
+ - `GrassBladeGeometryOptions` (interface): interface GrassBladeGeometryOptions { count?: number; area?: TerrainArea; seed?: TerrainSeed; segments?: number; height?: GrassRange; width?: GrassRange; bend?: GrassRange; tuftBlades?: number; tuftRadius?: number; edgeFeather?: number; exclude?: readonly GrassExclusion[]; he… — ⚠ undocumented
2605
2816
  - `GrassExclusion` (interface): interface GrassExclusion — A rectangle (patch-local XZ) the grass bake keeps clear — soil patches, water, plazas.
2606
2817
  - `GrassRange` (type): type GrassRange = number | readonly [min: number, max: number] — ⚠ undocumented
2607
- - `ResolvedGrassBladeGeometryOptions` (interface): interface ResolvedGrassBladeGeometryOptions — ⚠ undocumented
2818
+ - `ResolvedGrassBladeGeometryOptions` (interface): interface ResolvedGrassBladeGeometryOptions { count: number; area: TerrainArea; seed: TerrainSeed; segments: number; height: readonly [min: number, max: number]; width: readonly [min: number, max: number]; bend: readonly [min: number, max: number]; tuftBlades: number; tuftRadius: num… — ⚠ undocumented
2608
2819
 
2609
2820
  ## @jgengine/shell/terrain/grassMaterial
2610
2821
 
2611
2822
  - `DEFAULT_GRASS_DISTANCE_FADE` (const): const DEFAULT_GRASS_DISTANCE_FADE: Required<GrassDistanceFadeOptions> — Default camera-distance fade band: tufts start thinning at 55 m and are gone by 150 m so the meadow reads deep.
2612
2823
  - `DEFAULT_GRASS_WIND` (const): const DEFAULT_GRASS_WIND: Required<GrassWindOptions> — ⚠ undocumented
2613
2824
  - `GrassDistanceFadeOptions` (interface): interface GrassDistanceFadeOptions — Camera-distance fade band: tufts thin out between `start` and `end` meters, so the instance budget spends where the camera lives.
2614
- - `GrassMaterialHandle` (interface): interface GrassMaterialHandle — ⚠ undocumented
2615
- - `GrassMaterialOptions` (interface): interface GrassMaterialOptions — ⚠ undocumented
2616
- - `GrassShaderUniforms` (interface): interface GrassShaderUniforms — ⚠ undocumented
2617
- - `GrassWindOptions` (interface): interface GrassWindOptions — ⚠ undocumented
2825
+ - `GrassMaterialHandle` (interface): interface GrassMaterialHandle { material: THREE.MeshStandardMaterial; uniforms: GrassShaderUniforms } — ⚠ undocumented
2826
+ - `GrassMaterialOptions` (interface): interface GrassMaterialOptions { colorBase?: THREE.ColorRepresentation; colorTip?: THREE.ColorRepresentation; colorGround?: THREE.ColorRepresentation; colorVariation?: number; wind?: GrassWindOptions | false; distanceFade?: GrassDistanceFadeOptions | false; normalLift?: … — ⚠ undocumented
2827
+ - `GrassShaderUniforms` (interface): interface GrassShaderUniforms { uTime: THREE.IUniform<number>; uWindDirection: THREE.IUniform<THREE.Vector2>; uWindStrength: THREE.IUniform<number>; uWindSpeed: THREE.IUniform<number>; uWindGustScale: THREE.IUniform<number>; uWindFlutter: THREE.IUniform<number>; uColorB… — ⚠ undocumented
2828
+ - `GrassWindOptions` (interface): interface GrassWindOptions { direction?: readonly [x: number, z: number]; strength?: number; speed?: number; gustScale?: number; flutter?: number; layered?: boolean } — ⚠ undocumented
2618
2829
 
2619
2830
  ## @jgengine/shell/terrain/heightfieldGeometry
2620
2831
 
@@ -2634,19 +2845,19 @@
2634
2845
  ## @jgengine/shell/terrain/terrainDetailMaterial
2635
2846
 
2636
2847
  - `TerrainDetailMaterialHandle` (interface): interface TerrainDetailMaterialHandle — The built procedural detail terrain material, ready to mount on the ground mesh.
2637
- - `TerrainDetailMaterialTextures` (interface): interface TerrainDetailMaterialTextures — Loaded PBR textures matching a resolved `ResolvedTerrainDetailMaterial.maps`' roles. The caller (a React component, via `useTexture`/`useLoader`) owns loading and disposal; `core` never touches `THREE.Texture` and this shell function never fetches a URL itself.
2848
+ - `TerrainDetailMaterialTextures` (interface): interface TerrainDetailMaterialTextures — Loaded PBR textures matching a resolved `ResolvedTerrainDetailMaterial.maps`' roles. The caller (a React component, via `useTexture`/`useLoader`) owns loading and disposal; `core` never touches `THREE.Texture` and this shell function never fetches a URL itself. Omitted AO/roughness skip sampling.
2638
2849
 
2639
2850
  ## @jgengine/shell/terrain/terrainMath
2640
2851
 
2641
- - `FieldGroundOptions` (interface): interface FieldGroundOptions — ⚠ undocumented
2642
- - `ProceduralTerrainConfig` (interface): interface ProceduralTerrainConfig — ⚠ undocumented
2643
- - `ResolvedTerrainSegments` (interface): interface ResolvedTerrainSegments — ⚠ undocumented
2644
- - `ResolvedTerrainSize` (interface): interface ResolvedTerrainSize — ⚠ undocumented
2852
+ - `FieldGroundOptions` (interface): interface FieldGroundOptions { size?: TerrainArea; segments?: number | readonly [x: number, z: number]; center?: readonly [x: number, z: number]; colors?: TerrainVertexColorOptions; heightRange?: readonly [min: number, max: number]; paletteAt?: TerrainPaletteSampler; s… — ⚠ undocumented
2853
+ - `ProceduralTerrainConfig` (interface): interface ProceduralTerrainConfig { size?: TerrainArea; segments?: number | readonly [x: number, z: number]; seed?: TerrainSeed; height?: number; moundScale?: number; octaves?: number; ridged?: boolean; baseOffset?: number } — ⚠ undocumented
2854
+ - `ResolvedTerrainSegments` (interface): interface ResolvedTerrainSegments { x: number; z: number } — ⚠ undocumented
2855
+ - `ResolvedTerrainSize` (interface): interface ResolvedTerrainSize { width: number; depth: number } — ⚠ undocumented
2645
2856
  - `TerrainArea` (type): type TerrainArea = number | readonly [width: number, depth: number] — ⚠ undocumented
2646
2857
  - `TerrainHeightSampler` (type): type TerrainHeightSampler = (x: number, z: number) => number — ⚠ undocumented
2647
2858
  - `TerrainPaletteSampler` (type): type TerrainPaletteSampler = (x: number, z: number) => { low: string; high: string; waterline?: string } — Per-position palette override for multi-biome ground coloring — `createTerrainPaletteSampler` from `@jgengine/core/world/terrain` returns exactly this shape.
2648
2859
  - `TerrainSurfaceColorOptions` (interface): interface TerrainSurfaceColorOptions — Slope/noise surface shading laid over the height lerp so untextured ground reads like terrain instead of a smooth two-tone gradient. All knobs are optional with sane defaults; every field is a pure function of vertex position (+ `seed`), so the coloring is stable across rebuilds and tests.
2649
- - `TerrainVertexColorOptions` (interface): interface TerrainVertexColorOptions — ⚠ undocumented
2860
+ - `TerrainVertexColorOptions` (interface): interface TerrainVertexColorOptions { low?: THREE.ColorRepresentation; high?: THREE.ColorRepresentation; waterline?: THREE.ColorRepresentation; waterlineHeight?: number } — ⚠ undocumented
2650
2861
 
2651
2862
  ## @jgengine/shell/touch/OrientationHint
2652
2863
 
@@ -2654,7 +2865,7 @@
2654
2865
 
2655
2866
  ## @jgengine/shell/touch/TouchControlsOverlay
2656
2867
 
2657
- - `TouchCodeSink` (interface): interface TouchCodeSink — ⚠ undocumented
2868
+ - `TouchCodeSink` (interface): interface TouchCodeSink { onCodeDown(code: string): void; onCodeUp(code: string): void; onAnalog?(values: Readonly<Record<string, number>> | null): void } — ⚠ undocumented
2658
2869
  - `TouchControlsDock` (function): function TouchControlsDock({ scheme, sink, style, scale = 1, joystickVariant = "floating", }: { scheme: TouchScheme; sink: TouchCodeSink; /** Player-selected skin; falls back to the scheme's game default. */ style?: TouchStyle; scale?: number; /** Player-selected joystick behavior (Settings → Contro… — ⚠ undocumented
2659
2870
  - `TouchPlaySurface` (function): function TouchPlaySurface({ scheme, sink, yawRef, pitchRef, maxPitch, onPrimaryTap, }: { scheme: TouchScheme; sink: TouchCodeSink; yawRef: MutableRefObject<number>; pitchRef: MutableRefObject<number>; maxPitch: number; onPrimaryTap: () => void; }): React.JSX.Element — ⚠ undocumented
2660
2871
  - `primaryButtonOffsets` (function): function primaryButtonOffsets(count: number, scale = 1): { right: number; bottom: number }[] | null — Thumb-arc placement for primary buttons around the bottom-right corner: up to three on an inner ring, the rest on an outer ring. Null means too many buttons for an arc — the dock falls back to a wrapping grid.
@@ -2666,9 +2877,9 @@
2666
2877
 
2667
2878
  ## @jgengine/shell/vfx/ParticleField
2668
2879
 
2669
- - `ParticleBlending` (type): type ParticleBlending = "additive" | "normal" — How particle fragments composite: `additive` for fire/sparks/magic glow, `normal` for smoke/dust.
2670
- - `ParticleField` (function): function ParticleField({ system, advance = true, blending = "additive", scale = 300, depthWrite = false, }: ParticleFieldProps): ReactElement — Renders a core `ParticleSystem` as a GPU point cloud: one draw call, per-particle size / color / alpha via a soft-round shader, and a `drawRange` clamped to the live count so dead particles cost nothing. The simulation stays engine-side and genre-agnostic; this is purely how it reaches the screen. By default it advances the sim with the frame delta — pass `advance={false}` to drive it from your own fixed loop instead.
2671
- - `ParticleFieldProps` (interface): interface ParticleFieldProps — Props for {@link ParticleField}.
2880
+ - `ParticleBlending` (type): type ParticleBlending = "additive" | "normal" — How particle fragments composite on screen.
2881
+ - `ParticleField` (function): function ParticleField({ system, advance = true, blending = "additive", scale = 300, depthWrite = false, render, metrics }: ParticleFieldProps): ReactElement — Render one pooled simulation through a point cloud or instanced quads. Output selection does not replace spawn, forces, collisions, or deterministic state.
2882
+ - `ParticleFieldProps` (interface): interface ParticleFieldProps — Props for the existing pooled particle renderer.
2672
2883
 
2673
2884
  ## @jgengine/shell/vfx/WorldFloatingText
2674
2885
 
@@ -2682,7 +2893,7 @@
2682
2893
 
2683
2894
  ## @jgengine/shell/vision/FrustumSensorHud
2684
2895
 
2685
- - `FrustumSensorProbeOptions` (interface): interface FrustumSensorProbeOptions extends FramingConfig — ⚠ undocumented
2896
+ - `FrustumSensorProbeOptions` (interface): interface FrustumSensorProbeOptions extends FramingConfig { subjectIds: readonly string[]; subjectRadius?: number } — ⚠ undocumented · used by `FrustumSensorReadout`: Renders inside the Canvas (needs the live camera via `useFrame`/`useThree`) but portals a real HTML readout via drei's `Html fullscreen` — a…
2686
2897
  - `FrustumSensorReadout` (function): function FrustumSensorReadout(props: FrustumSensorProbeOptions & { wrapperClassName?: string; className?: string }): React.JSX.Element — Renders inside the Canvas (needs the live camera via `useFrame`/`useThree`) but portals a real HTML readout via drei's `Html fullscreen` — a photo-mode "is this subject framed" HUD.
2687
2898
  - `frustumSampleDisplayEqual` (function): function frustumSampleDisplayEqual(a: FrustumSample | null, b: FrustumSample | null): boolean — ⚠ undocumented
2688
2899
  - `useFrustumSensor` (function): function useFrustumSensor(options: FrustumSensorProbeOptions): FrustumSample | null — ⚠ undocumented
@@ -2690,16 +2901,16 @@
2690
2901
  ## @jgengine/shell/vision/HiddenStateProbeHud
2691
2902
 
2692
2903
  - `SensorReadoutMeter` (function): function SensorReadoutMeter({ label, reading, className }: SensorReadoutMeterProps): React.JSX.Element — A handheld-sensor readout: needle strength bar + the raw reading, or a "no signal" idle state.
2693
- - `SensorReadoutMeterProps` (interface): interface SensorReadoutMeterProps — ⚠ undocumented
2904
+ - `SensorReadoutMeterProps` (interface): interface SensorReadoutMeterProps { label: string; reading: ReturnType<typeof probeHiddenState>; className?: string } — ⚠ undocumented · used by `SensorReadoutMeter`: A handheld-sensor readout: needle strength bar + the raw reading, or a "no signal" idle state.
2694
2905
  - `useHiddenStateProbe` (function): function useHiddenStateProbe(origin: EntityPosition, sources: readonly HiddenStateSource[], options: SensorProbeOptions): SensorReading | null — Reads a hidden zone/entity state variable in range (EMF / thermometer / geiger style sensor verb, #116).
2695
2906
 
2696
2907
  ## @jgengine/shell/vision/RevealVision
2697
2908
 
2698
2909
  - `RevealHighlights` (function): function RevealHighlights(props: RevealHighlightsProps): React.JSX.Element | null — Screen-space reveal effect (#115) — highlights tagged entities through occluders (Dark Sight / detective-vision / wallhack style). Renders with `depthTest: false` so the highlight draws over any wall standing between the origin and the revealed entity, rather than the usual depth-sorted scene.
2699
- - `RevealHighlightsProps` (interface): interface RevealHighlightsProps extends RevealVisionOptions — ⚠ undocumented
2910
+ - `RevealHighlightsProps` (interface): interface RevealHighlightsProps extends RevealVisionOptions { enabled: boolean; color?: string } — ⚠ undocumented · used by `RevealHighlights`: Screen-space reveal effect (#115) — highlights tagged entities through occluders (Dark Sight / detective-vision / wallhack style).
2700
2911
  - `RevealScreenTint` (function): function RevealScreenTint({ enabled, color = "rgba(56, 189, 248, 0.16)", className }: RevealScreenTintProps): React.JSX.Element | null — Full-screen desaturating tint that reads as "vision mode is on" (Dark Sight / thermal / detective vision).
2701
- - `RevealScreenTintProps` (interface): interface RevealScreenTintProps — ⚠ undocumented
2702
- - `RevealVisionOptions` (interface): interface RevealVisionOptions — ⚠ undocumented
2912
+ - `RevealScreenTintProps` (interface): interface RevealScreenTintProps { enabled: boolean; color?: string; className?: string } — ⚠ undocumented · used by `RevealScreenTint`: Full-screen desaturating tint that reads as "vision mode is on" (Dark Sight / thermal / detective vision).
2913
+ - `RevealVisionOptions` (interface): interface RevealVisionOptions { originEntityId?: string; radius: number; tags: readonly string[]; resolveTags: (entity: SceneEntity) => readonly string[] } — ⚠ undocumented · used by `useRevealHits`: Occlusion-ignoring tagged-entity radius query (#115), bound to the live scene.
2703
2914
  - `useRevealHits` (function): function useRevealHits(options: RevealVisionOptions): readonly RevealHit[] — Occlusion-ignoring tagged-entity radius query (#115), bound to the live scene.
2704
2915
 
2705
2916
  ## @jgengine/shell/vision/frustumSampleEqual
@@ -2713,20 +2924,20 @@
2713
2924
  - `MAX_OCEAN_WAVES` (const): const MAX_OCEAN_WAVES: 6 — ⚠ undocumented
2714
2925
  - `OCEAN_QUALITY_PRESETS` (const): const OCEAN_QUALITY_PRESETS: Record<OceanQualityPreset, { size: number; resolution: number }> — ⚠ undocumented
2715
2926
  - `Ocean` (function): function Ocean({ config, depthAt, ...meshProps }: OceanProps): React.JSX.Element — ⚠ undocumented
2716
- - `OceanColorConfig` (interface): interface OceanColorConfig — ⚠ undocumented
2717
- - `OceanConfig` (interface): interface OceanConfig — ⚠ undocumented
2718
- - `OceanDirectionVector` (interface): interface OceanDirectionVector — ⚠ undocumented
2719
- - `OceanFoamConfig` (interface): interface OceanFoamConfig — ⚠ undocumented
2720
- - `OceanMaterialUniforms` (interface): interface OceanMaterialUniforms — ⚠ undocumented
2721
- - `OceanProps` (interface): interface OceanProps extends Omit<ThreeElements["mesh"], "args" | "children" | "geometry" | "material"> — ⚠ undocumented
2927
+ - `OceanColorConfig` (interface): interface OceanColorConfig { shallow?: THREE.ColorRepresentation; deep?: THREE.ColorRepresentation; crest?: THREE.ColorRepresentation; foam?: THREE.ColorRepresentation; opacity?: number; fresnelStrength?: number; horizonBlend?: number; depthRange?: number; sparkle?: … — ⚠ undocumented
2928
+ - `OceanConfig` (interface): interface OceanConfig { quality?: OceanQualityPreset; size?: number; depth?: number; resolution?: number; amplitude?: number; speed?: number; direction?: OceanWaveDirection; choppiness?: number; steepness?: number; timeScale?: number; waveScale?: number; color?:… — ⚠ undocumented
2929
+ - `OceanDirectionVector` (interface): interface OceanDirectionVector { x: number; z: number } — ⚠ undocumented
2930
+ - `OceanFoamConfig` (interface): interface OceanFoamConfig { crestThreshold?: number; softness?: number; intensity?: number; coverage?: number; shoreWidth?: number } — ⚠ undocumented
2931
+ - `OceanMaterialUniforms` (interface): interface OceanMaterialUniforms { uTime: {value: number}; uWaveDirections: {value: THREE.Vector2[]}; uWaveParams: {value: THREE.Vector4[]}; uChoppiness: {value: number}; uShallowColor: {value: THREE.Color}; uDeepColor: {value: THREE.Color}; uCrestColor: {value: THREE.Colo… — ⚠ undocumented
2932
+ - `OceanProps` (interface): interface OceanProps extends Omit<ThreeElements["mesh"], "args" | "children" | "geometry" | "material"> { config?: OceanConfig; depthAt?: (x: number, z: number) => number } — ⚠ undocumented
2722
2933
  - `OceanQualityPreset` (type): type OceanQualityPreset = "low" | "medium" | "high" | "ultra" — ⚠ undocumented
2723
2934
  - `OceanShaderMaterial` (type): type OceanShaderMaterial = THREE.ShaderMaterial & { uniforms: OceanMaterialUniforms } — ⚠ undocumented
2724
- - `OceanWaveConfig` (interface): interface OceanWaveConfig — ⚠ undocumented
2935
+ - `OceanWaveConfig` (interface): interface OceanWaveConfig { amplitude?: number; wavelength?: number; speed?: number; direction?: OceanWaveDirection; steepness?: number } — ⚠ undocumented
2725
2936
  - `OceanWaveDirection` (type): type OceanWaveDirection = number | OceanDirectionVector — ⚠ undocumented
2726
- - `ResolvedOceanColorConfig` (interface): interface ResolvedOceanColorConfig — ⚠ undocumented
2727
- - `ResolvedOceanConfig` (interface): interface ResolvedOceanConfig — ⚠ undocumented
2728
- - `ResolvedOceanFoamConfig` (interface): interface ResolvedOceanFoamConfig — ⚠ undocumented
2729
- - `ResolvedOceanWaveConfig` (interface): interface ResolvedOceanWaveConfig — ⚠ undocumented
2937
+ - `ResolvedOceanColorConfig` (interface): interface ResolvedOceanColorConfig { shallow: THREE.ColorRepresentation; deep: THREE.ColorRepresentation; crest: THREE.ColorRepresentation; foam: THREE.ColorRepresentation; opacity: number; fresnelStrength: number; horizonBlend: number; depthRange: number; sparkle: number } — ⚠ undocumented
2938
+ - `ResolvedOceanConfig` (interface): interface ResolvedOceanConfig { quality: OceanQualityPreset; size: number; depth: number; resolution: number; amplitude: number; speed: number; direction: OceanDirectionVector; choppiness: number; steepness: number; timeScale: number; waveScale: number; color: ResolvedO… — ⚠ undocumented
2939
+ - `ResolvedOceanFoamConfig` (interface): interface ResolvedOceanFoamConfig { crestThreshold: number; softness: number; intensity: number; coverage: number; shoreWidth: number } — ⚠ undocumented
2940
+ - `ResolvedOceanWaveConfig` (interface): interface ResolvedOceanWaveConfig { amplitude: number; wavelength: number; speed: number; direction: OceanDirectionVector; steepness: number } — ⚠ undocumented
2730
2941
  - `createOceanMaterial` (function): function createOceanMaterial(config: ResolvedOceanConfig): OceanShaderMaterial — ⚠ undocumented
2731
2942
  - `syncOceanMaterial` (function): function syncOceanMaterial(material: OceanShaderMaterial, config: ResolvedOceanConfig, elapsedSeconds: number): void — ⚠ undocumented
2732
2943
 
@@ -2737,27 +2948,27 @@
2737
2948
  - `MAX_OCEAN_WAVES` (const): const MAX_OCEAN_WAVES: 6 — ⚠ undocumented
2738
2949
  - `OCEAN_QUALITY_PRESETS` (const): const OCEAN_QUALITY_PRESETS: Record<OceanQualityPreset, { size: number; resolution: number }> — ⚠ undocumented
2739
2950
  - `Ocean` (function): function Ocean({ config, depthAt, ...meshProps }: OceanProps): React.JSX.Element — ⚠ undocumented
2740
- - `OceanColorConfig` (interface): interface OceanColorConfig — ⚠ undocumented
2741
- - `OceanConfig` (interface): interface OceanConfig — ⚠ undocumented
2742
- - `OceanDirectionVector` (interface): interface OceanDirectionVector — ⚠ undocumented
2743
- - `OceanFoamConfig` (interface): interface OceanFoamConfig — ⚠ undocumented
2744
- - `OceanMaterialUniforms` (interface): interface OceanMaterialUniforms — ⚠ undocumented
2745
- - `OceanProps` (interface): interface OceanProps extends Omit<ThreeElements["mesh"], "args" | "children" | "geometry" | "material"> — ⚠ undocumented
2951
+ - `OceanColorConfig` (interface): interface OceanColorConfig { shallow?: THREE.ColorRepresentation; deep?: THREE.ColorRepresentation; crest?: THREE.ColorRepresentation; foam?: THREE.ColorRepresentation; opacity?: number; fresnelStrength?: number; horizonBlend?: number; depthRange?: number; sparkle?: … — ⚠ undocumented
2952
+ - `OceanConfig` (interface): interface OceanConfig { quality?: OceanQualityPreset; size?: number; depth?: number; resolution?: number; amplitude?: number; speed?: number; direction?: OceanWaveDirection; choppiness?: number; steepness?: number; timeScale?: number; waveScale?: number; color?:… — ⚠ undocumented
2953
+ - `OceanDirectionVector` (interface): interface OceanDirectionVector { x: number; z: number } — ⚠ undocumented
2954
+ - `OceanFoamConfig` (interface): interface OceanFoamConfig { crestThreshold?: number; softness?: number; intensity?: number; coverage?: number; shoreWidth?: number } — ⚠ undocumented
2955
+ - `OceanMaterialUniforms` (interface): interface OceanMaterialUniforms { uTime: {value: number}; uWaveDirections: {value: THREE.Vector2[]}; uWaveParams: {value: THREE.Vector4[]}; uChoppiness: {value: number}; uShallowColor: {value: THREE.Color}; uDeepColor: {value: THREE.Color}; uCrestColor: {value: THREE.Colo… — ⚠ undocumented
2956
+ - `OceanProps` (interface): interface OceanProps extends Omit<ThreeElements["mesh"], "args" | "children" | "geometry" | "material"> { config?: OceanConfig; depthAt?: (x: number, z: number) => number } — ⚠ undocumented
2746
2957
  - `OceanQualityPreset` (type): type OceanQualityPreset = "low" | "medium" | "high" | "ultra" — ⚠ undocumented
2747
2958
  - `OceanShaderMaterial` (type): type OceanShaderMaterial = THREE.ShaderMaterial & { uniforms: OceanMaterialUniforms } — ⚠ undocumented
2748
- - `OceanWaveConfig` (interface): interface OceanWaveConfig — ⚠ undocumented
2959
+ - `OceanWaveConfig` (interface): interface OceanWaveConfig { amplitude?: number; wavelength?: number; speed?: number; direction?: OceanWaveDirection; steepness?: number } — ⚠ undocumented
2749
2960
  - `OceanWaveDirection` (type): type OceanWaveDirection = number | OceanDirectionVector — ⚠ undocumented
2750
- - `ResolvedOceanColorConfig` (interface): interface ResolvedOceanColorConfig — ⚠ undocumented
2751
- - `ResolvedOceanConfig` (interface): interface ResolvedOceanConfig — ⚠ undocumented
2752
- - `ResolvedOceanFoamConfig` (interface): interface ResolvedOceanFoamConfig — ⚠ undocumented
2753
- - `ResolvedOceanWaveConfig` (interface): interface ResolvedOceanWaveConfig — ⚠ undocumented
2961
+ - `ResolvedOceanColorConfig` (interface): interface ResolvedOceanColorConfig { shallow: THREE.ColorRepresentation; deep: THREE.ColorRepresentation; crest: THREE.ColorRepresentation; foam: THREE.ColorRepresentation; opacity: number; fresnelStrength: number; horizonBlend: number; depthRange: number; sparkle: number } — ⚠ undocumented
2962
+ - `ResolvedOceanConfig` (interface): interface ResolvedOceanConfig { quality: OceanQualityPreset; size: number; depth: number; resolution: number; amplitude: number; speed: number; direction: OceanDirectionVector; choppiness: number; steepness: number; timeScale: number; waveScale: number; color: ResolvedO… — ⚠ undocumented
2963
+ - `ResolvedOceanFoamConfig` (interface): interface ResolvedOceanFoamConfig { crestThreshold: number; softness: number; intensity: number; coverage: number; shoreWidth: number } — ⚠ undocumented
2964
+ - `ResolvedOceanWaveConfig` (interface): interface ResolvedOceanWaveConfig { amplitude: number; wavelength: number; speed: number; direction: OceanDirectionVector; steepness: number } — ⚠ undocumented
2754
2965
  - `createOceanMaterial` (function): function createOceanMaterial(config: ResolvedOceanConfig): OceanShaderMaterial — ⚠ undocumented
2755
2966
  - `syncOceanMaterial` (function): function syncOceanMaterial(material: OceanShaderMaterial, config: ResolvedOceanConfig, elapsedSeconds: number): void — ⚠ undocumented
2756
2967
 
2757
2968
  ## @jgengine/shell/water/Ocean
2758
2969
 
2759
2970
  - `Ocean` (function): function Ocean({ config, depthAt, ...meshProps }: OceanProps): React.JSX.Element — ⚠ undocumented
2760
- - `OceanProps` (interface): interface OceanProps extends Omit<ThreeElements["mesh"], "args" | "children" | "geometry" | "material"> — ⚠ undocumented
2971
+ - `OceanProps` (interface): interface OceanProps extends Omit<ThreeElements["mesh"], "args" | "children" | "geometry" | "material"> { config?: OceanConfig; depthAt?: (x: number, z: number) => number } — ⚠ undocumented
2761
2972
 
2762
2973
  ## @jgengine/shell/water/OceanConfig
2763
2974
 
@@ -2765,21 +2976,21 @@
2765
2976
  - `DEFAULT_OCEAN_WAVE_SCALE` (const): const DEFAULT_OCEAN_WAVE_SCALE: 18 — Shared with `@jgengine/core/world/water` — primary wavelength in world units.
2766
2977
  - `MAX_OCEAN_WAVES` (const): const MAX_OCEAN_WAVES: 6 — ⚠ undocumented
2767
2978
  - `OCEAN_QUALITY_PRESETS` (const): const OCEAN_QUALITY_PRESETS: Record<OceanQualityPreset, { size: number; resolution: number }> — ⚠ undocumented
2768
- - `OceanColorConfig` (interface): interface OceanColorConfig — ⚠ undocumented
2769
- - `OceanConfig` (interface): interface OceanConfig — ⚠ undocumented
2770
- - `OceanDirectionVector` (interface): interface OceanDirectionVector — ⚠ undocumented
2771
- - `OceanFoamConfig` (interface): interface OceanFoamConfig — ⚠ undocumented
2979
+ - `OceanColorConfig` (interface): interface OceanColorConfig { shallow?: THREE.ColorRepresentation; deep?: THREE.ColorRepresentation; crest?: THREE.ColorRepresentation; foam?: THREE.ColorRepresentation; opacity?: number; fresnelStrength?: number; horizonBlend?: number; depthRange?: number; sparkle?: … — ⚠ undocumented
2980
+ - `OceanConfig` (interface): interface OceanConfig { quality?: OceanQualityPreset; size?: number; depth?: number; resolution?: number; amplitude?: number; speed?: number; direction?: OceanWaveDirection; choppiness?: number; steepness?: number; timeScale?: number; waveScale?: number; color?:… — ⚠ undocumented
2981
+ - `OceanDirectionVector` (interface): interface OceanDirectionVector { x: number; z: number } — ⚠ undocumented
2982
+ - `OceanFoamConfig` (interface): interface OceanFoamConfig { crestThreshold?: number; softness?: number; intensity?: number; coverage?: number; shoreWidth?: number } — ⚠ undocumented
2772
2983
  - `OceanQualityPreset` (type): type OceanQualityPreset = "low" | "medium" | "high" | "ultra" — ⚠ undocumented
2773
- - `OceanWaveConfig` (interface): interface OceanWaveConfig — ⚠ undocumented
2984
+ - `OceanWaveConfig` (interface): interface OceanWaveConfig { amplitude?: number; wavelength?: number; speed?: number; direction?: OceanWaveDirection; steepness?: number } — ⚠ undocumented
2774
2985
  - `OceanWaveDirection` (type): type OceanWaveDirection = number | OceanDirectionVector — ⚠ undocumented
2775
- - `ResolvedOceanColorConfig` (interface): interface ResolvedOceanColorConfig — ⚠ undocumented
2776
- - `ResolvedOceanConfig` (interface): interface ResolvedOceanConfig — ⚠ undocumented
2777
- - `ResolvedOceanFoamConfig` (interface): interface ResolvedOceanFoamConfig — ⚠ undocumented
2778
- - `ResolvedOceanWaveConfig` (interface): interface ResolvedOceanWaveConfig — ⚠ undocumented
2986
+ - `ResolvedOceanColorConfig` (interface): interface ResolvedOceanColorConfig { shallow: THREE.ColorRepresentation; deep: THREE.ColorRepresentation; crest: THREE.ColorRepresentation; foam: THREE.ColorRepresentation; opacity: number; fresnelStrength: number; horizonBlend: number; depthRange: number; sparkle: number } — ⚠ undocumented
2987
+ - `ResolvedOceanConfig` (interface): interface ResolvedOceanConfig { quality: OceanQualityPreset; size: number; depth: number; resolution: number; amplitude: number; speed: number; direction: OceanDirectionVector; choppiness: number; steepness: number; timeScale: number; waveScale: number; color: ResolvedO… — ⚠ undocumented
2988
+ - `ResolvedOceanFoamConfig` (interface): interface ResolvedOceanFoamConfig { crestThreshold: number; softness: number; intensity: number; coverage: number; shoreWidth: number } — ⚠ undocumented
2989
+ - `ResolvedOceanWaveConfig` (interface): interface ResolvedOceanWaveConfig { amplitude: number; wavelength: number; speed: number; direction: OceanDirectionVector; steepness: number } — ⚠ undocumented
2779
2990
 
2780
2991
  ## @jgengine/shell/water/OceanMaterial
2781
2992
 
2782
- - `OceanMaterialUniforms` (interface): interface OceanMaterialUniforms — ⚠ undocumented
2993
+ - `OceanMaterialUniforms` (interface): interface OceanMaterialUniforms { uTime: {value: number}; uWaveDirections: {value: THREE.Vector2[]}; uWaveParams: {value: THREE.Vector4[]}; uChoppiness: {value: number}; uShallowColor: {value: THREE.Color}; uDeepColor: {value: THREE.Color}; uCrestColor: {value: THREE.Colo… — ⚠ undocumented
2783
2994
  - `OceanShaderMaterial` (type): type OceanShaderMaterial = THREE.ShaderMaterial & { uniforms: OceanMaterialUniforms } — ⚠ undocumented
2784
2995
  - `createOceanMaterial` (function): function createOceanMaterial(config: ResolvedOceanConfig): OceanShaderMaterial — ⚠ undocumented
2785
2996
  - `syncOceanMaterial` (function): function syncOceanMaterial(material: OceanShaderMaterial, config: ResolvedOceanConfig, elapsedSeconds: number): void — ⚠ undocumented
@@ -2791,53 +3002,67 @@
2791
3002
 
2792
3003
  ## @jgengine/shell/weather
2793
3004
 
2794
- - `DustField` (function): function DustField({ count = DEFAULT_DUST_COUNT, density = DEFAULT_DUST_DENSITY, budget, volume = DEFAULT_VOLUME, wind, origin = DEFAULT_ORIGIN, followCamera = true, speed = 2.4, size = 0.16, sway = 0.5, opacity = 0.3, color = DEFAULT_DUST_COLOR, groundBias = 0.65, timeScale, seed = 51407, renderOrd… — Renders wind-borne particulate as camera-facing motes. Reach for it whenever the air itself should read as weather — a desert gale, ash off a volcano, pollen in a meadow, grain dust in a silo. Compose it through {@link WeatherLayer}'s `"dust"` mode (or `dustAlways` alongside rain and snow) rather than mounting it directly, so it shares one wind and time source with the other layers.
3005
+ - `AuthoredWeatherLayer` (function): function AuthoredWeatherLayer({ document, rain, snow, dust, heightAt, diagnostics = false }: AuthoredWeatherLayerProps): React.JSX.Element | null — Render pooled precipitation from the same authored document and clock as gameplay.
3006
+ - `AuthoredWeatherLayerProps` (interface): interface AuthoredWeatherLayerProps — Authored weather document and optional precipitation appearance and ground sampling.
3007
+ - `DustField` (function): function DustField({ count = DEFAULT_DUST_COUNT, density = DEFAULT_DUST_DENSITY, budget, volume = DEFAULT_VOLUME, wind, origin = DEFAULT_ORIGIN, followCamera = true, speed = 2.4, size = 0.16, sway = 0.5, opacity = 0.3, color = DEFAULT_DUST_COLOR, groundBias = 0.65, timeScale, timeSeconds, seed = 514… — Renders wind-borne particulate as camera-facing motes. Reach for it whenever the air itself should read as weather — a desert gale, ash off a volcano, pollen in a meadow, grain dust in a silo. Compose it through {@link WeatherLayer}'s `"dust"` mode (or `dustAlways` alongside rain and snow) rather than mounting it directly, so it shares one wind and time source with the other layers.
2795
3008
  - `DustFieldProps` (interface): interface DustFieldProps — Wind-borne particulate: dust, sand, ash, pollen, spores. Unlike rain and snow this is not falling — motion is dominated by horizontal wind with a slow vertical bob, and density thins with height so the air near the ground carries the most grit. Any arid, volcanic, or blown-out world needs it.
2796
- - `FireSpreadLayer` (function): function FireSpreadLayer({ grid, cellSize, origin = [0, 0], heightAt, flameHeight = 1.6, burningColor = "#ff6a1a", emberColor = "#4a1206", }: FireSpreadLayerProps): React.JSX.Element — ⚠ undocumented
2797
- - `FireSpreadLayerProps` (interface): interface FireSpreadLayerProps — ⚠ undocumented
2798
- - `LightningStrike` (function): function LightningStrike({ origin, target, strikeKey = 0, seed = 451, visible = true, duration = 0.18, color = DEFAULT_COLOR, glow = 2.4, branches = 5, jaggedness = 0.08, impactLight = 26, renderOrder = 20, }: LightningStrikeProps): React.JSX.Element — ⚠ undocumented
2799
- - `LightningStrikeProps` (interface): interface LightningStrikeProps — ⚠ undocumented
2800
- - `RainField` (function): function RainField({ count = DEFAULT_RAIN_COUNT, density = DEFAULT_RAIN_DENSITY, budget, volume = DEFAULT_VOLUME, wind, origin = DEFAULT_ORIGIN, followCamera = true, speed = 22, length = 1.35, width = 0.018, opacity = 0.48, color = DEFAULT_RAIN_COLOR, lightning, timeScale, seed = 11939, renderOrder … — ⚠ undocumented
2801
- - `RainFieldProps` (interface): interface RainFieldProps — ⚠ undocumented
2802
- - `SnowField` (function): function SnowField({ count = DEFAULT_SNOW_COUNT, density = DEFAULT_SNOW_DENSITY, budget, volume = DEFAULT_VOLUME, wind, origin = DEFAULT_ORIGIN, followCamera = true, speed = 3.2, size = 0.11, sway = 0.62, opacity = 0.86, color = DEFAULT_SNOW_COLOR, timeScale, seed = 72931, renderOrder = 11, frustumC… — ⚠ undocumented
2803
- - `SnowFieldProps` (interface): interface SnowFieldProps — ⚠ undocumented
2804
- - `WeatherLayer` (function): function WeatherLayer({ mode = "clear", intensity = 1, wind, lightning, timeScale, rain, snow, dust, dustAlways = false, enabled = true, children, }: WeatherLayerProps): React.JSX.Element | null — ⚠ undocumented
3009
+ - `FireSpreadLayer` (function): function FireSpreadLayer({ grid, cellSize = grid.cellSize, origin = grid.origin, heightAt, flameHeight = 1.6, burningColor = "#ff6a1a", emberColor = "#4a1206", timeSeconds, }: FireSpreadLayerProps): React.JSX.Element — ⚠ undocumented
3010
+ - `FireSpreadLayerProps` (interface): interface FireSpreadLayerProps { grid: FireGrid; cellSize?: number; origin?: readonly [number, number]; heightAt?: (x: number, z: number) => number; flameHeight?: number; burningColor?: THREE.ColorRepresentation; emberColor?: THREE.ColorRepresentation; timeSeconds?: () =… — ⚠ undocumented
3011
+ - `LightningStrike` (function): function LightningStrike({ origin, target, strikeKey = 0, seed = 451, visible = true, duration = 0.18, color = DEFAULT_COLOR, glow = 2.4, branches = 5, jaggedness = 0.08, impactLight = 26, renderOrder = 20, timeSeconds, startedAt, }: LightningStrikeProps): React.JSX.Element — ⚠ undocumented
3012
+ - `LightningStrikeProps` (interface): interface LightningStrikeProps { origin: WeatherVector; target: WeatherVector; strikeKey?: string | number; seed?: number; visible?: boolean; duration?: number; color?: THREE.ColorRepresentation; glow?: number; branches?: number; jaggedness?: number; impactLight?: number… — ⚠ undocumented
3013
+ - `RainField` (function): function RainField({ count = DEFAULT_RAIN_COUNT, density = DEFAULT_RAIN_DENSITY, budget, volume = DEFAULT_VOLUME, wind, origin = DEFAULT_ORIGIN, followCamera = true, speed = 22, length = 1.35, width = 0.018, opacity = 0.48, color = DEFAULT_RAIN_COLOR, lightning, timeScale, timeSeconds, seed = 11939,… — ⚠ undocumented
3014
+ - `RainFieldProps` (interface): interface RainFieldProps { count?: number; density?: number; budget?: number; volume?: WeatherVector; wind?: WeatherVector; origin?: WeatherVector; followCamera?: boolean; speed?: number; length?: number; width?: number; opacity?: number; color?: THREE.ColorReprese… — ⚠ undocumented
3015
+ - `RainImpactField` (function): function RainImpactField({ count = 64, extent = 48, heightAt, exposureAt, seed = 11939, color = "#b8c4d8" }: RainImpactFieldProps): React.JSX.Element — Bounded ground ripples sharing precipitation intensity and authoritative animation time.
3016
+ - `RainImpactFieldProps` (interface): interface RainImpactFieldProps — Bounded ripple pool with terrain height and sky exposure query adapters.
3017
+ - `SnowField` (function): function SnowField({ count = DEFAULT_SNOW_COUNT, density = DEFAULT_SNOW_DENSITY, budget, volume = DEFAULT_VOLUME, wind, origin = DEFAULT_ORIGIN, followCamera = true, speed = 3.2, size = 0.11, sway = 0.62, opacity = 0.86, color = DEFAULT_SNOW_COLOR, timeScale, timeSeconds, seed = 72931, renderOrder =… — ⚠ undocumented
3018
+ - `SnowFieldProps` (interface): interface SnowFieldProps { count?: number; density?: number; budget?: number; volume?: WeatherVector; wind?: WeatherVector; origin?: WeatherVector; followCamera?: boolean; speed?: number; size?: number; sway?: number; opacity?: number; color?: THREE.ColorRepresenta… — ⚠ undocumented
3019
+ - `WeatherLayer` (function): function WeatherLayer({ mode = "clear", intensity = 1, wind, lightning, timeScale, timeSeconds, sample, shelters, metrics, rain, snow, dust, dustAlways = false, impacts, enabled = true, children, }: WeatherLayerProps): React.JSX.Element | null — ⚠ undocumented
2805
3020
  - `WeatherLayerMode` (type): type WeatherLayerMode = "clear" | "rain" | "snow" | "mixed" | "dust" — `"mixed"` runs rain and snow together; `"dust"` is airborne particulate and composes with either via `dust`.
2806
- - `WeatherLayerProps` (interface): interface WeatherLayerProps — ⚠ undocumented
2807
- - `WeatherUniformOptions` (interface): interface WeatherUniformOptions — ⚠ undocumented
2808
- - `WeatherUniformSet` (interface): interface WeatherUniformSet — ⚠ undocumented
3021
+ - `WeatherLayerProps` (interface): interface WeatherLayerProps { mode?: WeatherLayerMode; intensity?: number; wind?: WeatherVector; lightning?: number; timeScale?: number; timeSeconds?: number | (() => number); sample?: (x: number, z: number) => AuthoredWeatherSample; shelters?: readonly EditorVolume[]… — ⚠ undocumented
3022
+ - `WeatherUniformOptions` (interface): interface WeatherUniformOptions { wind?: WeatherVector; lightning?: number; timeScale?: number; timeSeconds?: number | (() => number); sample?: (x: number, z: number) => AuthoredWeatherSample; shelters?: readonly EditorVolume[]; metrics?: WeatherParticleMetrics } — ⚠ undocumented
3023
+ - `WeatherUniformSet` (interface): interface WeatherUniformSet extends WeatherShelterUniforms { time: THREE.IUniform<number>; wind: THREE.IUniform<THREE.Vector3>; lightning: THREE.IUniform<number>; rain: THREE.IUniform<number>; snow: THREE.IUniform<number>; dust: THREE.IUniform<number>; controlsWind: boolean; metrics?: WeatherPartic… — ⚠ undocumented
2809
3024
  - `WeatherVector` (type): type WeatherVector = readonly [number, number, number] — ⚠ undocumented
2810
3025
 
3026
+ ## @jgengine/shell/weather/AuthoredWeatherLayer
3027
+
3028
+ - `AuthoredWeatherLayer` (function): function AuthoredWeatherLayer({ document, rain, snow, dust, heightAt, diagnostics = false }: AuthoredWeatherLayerProps): React.JSX.Element | null — Render pooled precipitation from the same authored document and clock as gameplay.
3029
+ - `AuthoredWeatherLayerProps` (interface): interface AuthoredWeatherLayerProps — Authored weather document and optional precipitation appearance and ground sampling.
3030
+
2811
3031
  ## @jgengine/shell/weather/DustField
2812
3032
 
2813
- - `DustField` (function): function DustField({ count = DEFAULT_DUST_COUNT, density = DEFAULT_DUST_DENSITY, budget, volume = DEFAULT_VOLUME, wind, origin = DEFAULT_ORIGIN, followCamera = true, speed = 2.4, size = 0.16, sway = 0.5, opacity = 0.3, color = DEFAULT_DUST_COLOR, groundBias = 0.65, timeScale, seed = 51407, renderOrd… — Renders wind-borne particulate as camera-facing motes. Reach for it whenever the air itself should read as weather — a desert gale, ash off a volcano, pollen in a meadow, grain dust in a silo. Compose it through {@link WeatherLayer}'s `"dust"` mode (or `dustAlways` alongside rain and snow) rather than mounting it directly, so it shares one wind and time source with the other layers.
3033
+ - `DustField` (function): function DustField({ count = DEFAULT_DUST_COUNT, density = DEFAULT_DUST_DENSITY, budget, volume = DEFAULT_VOLUME, wind, origin = DEFAULT_ORIGIN, followCamera = true, speed = 2.4, size = 0.16, sway = 0.5, opacity = 0.3, color = DEFAULT_DUST_COLOR, groundBias = 0.65, timeScale, timeSeconds, seed = 514… — Renders wind-borne particulate as camera-facing motes. Reach for it whenever the air itself should read as weather — a desert gale, ash off a volcano, pollen in a meadow, grain dust in a silo. Compose it through {@link WeatherLayer}'s `"dust"` mode (or `dustAlways` alongside rain and snow) rather than mounting it directly, so it shares one wind and time source with the other layers.
2814
3034
  - `DustFieldProps` (interface): interface DustFieldProps — Wind-borne particulate: dust, sand, ash, pollen, spores. Unlike rain and snow this is not falling — motion is dominated by horizontal wind with a slow vertical bob, and density thins with height so the air near the ground carries the most grit. Any arid, volcanic, or blown-out world needs it.
2815
3035
 
2816
3036
  ## @jgengine/shell/weather/FireSpreadLayer
2817
3037
 
2818
- - `FireSpreadLayer` (function): function FireSpreadLayer({ grid, cellSize, origin = [0, 0], heightAt, flameHeight = 1.6, burningColor = "#ff6a1a", emberColor = "#4a1206", }: FireSpreadLayerProps): React.JSX.Element — ⚠ undocumented
2819
- - `FireSpreadLayerProps` (interface): interface FireSpreadLayerProps — ⚠ undocumented
3038
+ - `FireSpreadLayer` (function): function FireSpreadLayer({ grid, cellSize = grid.cellSize, origin = grid.origin, heightAt, flameHeight = 1.6, burningColor = "#ff6a1a", emberColor = "#4a1206", timeSeconds, }: FireSpreadLayerProps): React.JSX.Element — ⚠ undocumented
3039
+ - `FireSpreadLayerProps` (interface): interface FireSpreadLayerProps { grid: FireGrid; cellSize?: number; origin?: readonly [number, number]; heightAt?: (x: number, z: number) => number; flameHeight?: number; burningColor?: THREE.ColorRepresentation; emberColor?: THREE.ColorRepresentation; timeSeconds?: () =… — ⚠ undocumented
2820
3040
 
2821
3041
  ## @jgengine/shell/weather/LightningStrike
2822
3042
 
2823
- - `LightningStrike` (function): function LightningStrike({ origin, target, strikeKey = 0, seed = 451, visible = true, duration = 0.18, color = DEFAULT_COLOR, glow = 2.4, branches = 5, jaggedness = 0.08, impactLight = 26, renderOrder = 20, }: LightningStrikeProps): React.JSX.Element — ⚠ undocumented
2824
- - `LightningStrikeProps` (interface): interface LightningStrikeProps — ⚠ undocumented
3043
+ - `LightningStrike` (function): function LightningStrike({ origin, target, strikeKey = 0, seed = 451, visible = true, duration = 0.18, color = DEFAULT_COLOR, glow = 2.4, branches = 5, jaggedness = 0.08, impactLight = 26, renderOrder = 20, timeSeconds, startedAt, }: LightningStrikeProps): React.JSX.Element — ⚠ undocumented
3044
+ - `LightningStrikeProps` (interface): interface LightningStrikeProps { origin: WeatherVector; target: WeatherVector; strikeKey?: string | number; seed?: number; visible?: boolean; duration?: number; color?: THREE.ColorRepresentation; glow?: number; branches?: number; jaggedness?: number; impactLight?: number… — ⚠ undocumented
2825
3045
 
2826
3046
  ## @jgengine/shell/weather/RainField
2827
3047
 
2828
- - `RainField` (function): function RainField({ count = DEFAULT_RAIN_COUNT, density = DEFAULT_RAIN_DENSITY, budget, volume = DEFAULT_VOLUME, wind, origin = DEFAULT_ORIGIN, followCamera = true, speed = 22, length = 1.35, width = 0.018, opacity = 0.48, color = DEFAULT_RAIN_COLOR, lightning, timeScale, seed = 11939, renderOrder … — ⚠ undocumented
2829
- - `RainFieldProps` (interface): interface RainFieldProps — ⚠ undocumented
3048
+ - `RainField` (function): function RainField({ count = DEFAULT_RAIN_COUNT, density = DEFAULT_RAIN_DENSITY, budget, volume = DEFAULT_VOLUME, wind, origin = DEFAULT_ORIGIN, followCamera = true, speed = 22, length = 1.35, width = 0.018, opacity = 0.48, color = DEFAULT_RAIN_COLOR, lightning, timeScale, timeSeconds, seed = 11939,… — ⚠ undocumented
3049
+ - `RainFieldProps` (interface): interface RainFieldProps { count?: number; density?: number; budget?: number; volume?: WeatherVector; wind?: WeatherVector; origin?: WeatherVector; followCamera?: boolean; speed?: number; length?: number; width?: number; opacity?: number; color?: THREE.ColorReprese… — ⚠ undocumented
3050
+
3051
+ ## @jgengine/shell/weather/RainImpactField
3052
+
3053
+ - `RainImpactField` (function): function RainImpactField({ count = 64, extent = 48, heightAt, exposureAt, seed = 11939, color = "#b8c4d8" }: RainImpactFieldProps): React.JSX.Element — Bounded ground ripples sharing precipitation intensity and authoritative animation time.
3054
+ - `RainImpactFieldProps` (interface): interface RainImpactFieldProps — Bounded ripple pool with terrain height and sky exposure query adapters.
2830
3055
 
2831
3056
  ## @jgengine/shell/weather/SnowField
2832
3057
 
2833
- - `SnowField` (function): function SnowField({ count = DEFAULT_SNOW_COUNT, density = DEFAULT_SNOW_DENSITY, budget, volume = DEFAULT_VOLUME, wind, origin = DEFAULT_ORIGIN, followCamera = true, speed = 3.2, size = 0.11, sway = 0.62, opacity = 0.86, color = DEFAULT_SNOW_COLOR, timeScale, seed = 72931, renderOrder = 11, frustumC… — ⚠ undocumented
2834
- - `SnowFieldProps` (interface): interface SnowFieldProps — ⚠ undocumented
3058
+ - `SnowField` (function): function SnowField({ count = DEFAULT_SNOW_COUNT, density = DEFAULT_SNOW_DENSITY, budget, volume = DEFAULT_VOLUME, wind, origin = DEFAULT_ORIGIN, followCamera = true, speed = 3.2, size = 0.11, sway = 0.62, opacity = 0.86, color = DEFAULT_SNOW_COLOR, timeScale, timeSeconds, seed = 72931, renderOrder =… — ⚠ undocumented
3059
+ - `SnowFieldProps` (interface): interface SnowFieldProps { count?: number; density?: number; budget?: number; volume?: WeatherVector; wind?: WeatherVector; origin?: WeatherVector; followCamera?: boolean; speed?: number; size?: number; sway?: number; opacity?: number; color?: THREE.ColorRepresenta… — ⚠ undocumented
2835
3060
 
2836
3061
  ## @jgengine/shell/weather/WeatherLayer
2837
3062
 
2838
- - `WeatherLayer` (function): function WeatherLayer({ mode = "clear", intensity = 1, wind, lightning, timeScale, rain, snow, dust, dustAlways = false, enabled = true, children, }: WeatherLayerProps): React.JSX.Element | null — ⚠ undocumented
3063
+ - `WeatherLayer` (function): function WeatherLayer({ mode = "clear", intensity = 1, wind, lightning, timeScale, timeSeconds, sample, shelters, metrics, rain, snow, dust, dustAlways = false, impacts, enabled = true, children, }: WeatherLayerProps): React.JSX.Element | null — ⚠ undocumented
2839
3064
  - `WeatherLayerMode` (type): type WeatherLayerMode = "clear" | "rain" | "snow" | "mixed" | "dust" — `"mixed"` runs rain and snow together; `"dust"` is airborne particulate and composes with either via `dust`.
2840
- - `WeatherLayerProps` (interface): interface WeatherLayerProps — ⚠ undocumented
3065
+ - `WeatherLayerProps` (interface): interface WeatherLayerProps { mode?: WeatherLayerMode; intensity?: number; wind?: WeatherVector; lightning?: number; timeScale?: number; timeSeconds?: number | (() => number); sample?: (x: number, z: number) => AuthoredWeatherSample; shelters?: readonly EditorVolume[]… — ⚠ undocumented
2841
3066
 
2842
3067
  ## @jgengine/shell/weather/weatherMath
2843
3068
 
@@ -2847,14 +3072,25 @@
2847
3072
  - `DEFAULT_RAIN_DENSITY` (const): const DEFAULT_RAIN_DENSITY: 0.45 — ⚠ undocumented
2848
3073
  - `DEFAULT_SNOW_COUNT` (const): const DEFAULT_SNOW_COUNT: 1500 — ⚠ undocumented
2849
3074
  - `DEFAULT_SNOW_DENSITY` (const): const DEFAULT_SNOW_DENSITY: 0.5 — ⚠ undocumented
2850
- - `WeatherSeedAttributes` (interface): interface WeatherSeedAttributes — ⚠ undocumented
3075
+ - `WeatherSeedAttributes` (interface): interface WeatherSeedAttributes { spawn: Float32Array; drift: Float32Array } — ⚠ undocumented
3076
+
3077
+ ## @jgengine/shell/weather/weatherShelter
3078
+
3079
+ - `WEATHER_SHELTER_BUDGET` (const): const WEATHER_SHELTER_BUDGET: 32 — Cosmetic roof clipping is bounded independently of authoritative shelter queries.
3080
+ - `WeatherShelterUniforms` (interface): interface WeatherShelterUniforms — Reusable buffers for the nearest authored shelters submitted to weather shaders.
2851
3081
 
2852
3082
  ## @jgengine/shell/weather/weatherUniforms
2853
3083
 
2854
- - `WeatherUniformOptions` (interface): interface WeatherUniformOptions — ⚠ undocumented
2855
- - `WeatherUniformSet` (interface): interface WeatherUniformSet — ⚠ undocumented
3084
+ - `WeatherParticleMetrics` (interface): interface WeatherParticleMetrics — Actual procedural weather submissions and per-frame impact query work.
3085
+ - `WeatherUniformOptions` (interface): interface WeatherUniformOptions { wind?: WeatherVector; lightning?: number; timeScale?: number; timeSeconds?: number | (() => number); sample?: (x: number, z: number) => AuthoredWeatherSample; shelters?: readonly EditorVolume[]; metrics?: WeatherParticleMetrics } — ⚠ undocumented
3086
+ - `WeatherUniformSet` (interface): interface WeatherUniformSet extends WeatherShelterUniforms { time: THREE.IUniform<number>; wind: THREE.IUniform<THREE.Vector3>; lightning: THREE.IUniform<number>; rain: THREE.IUniform<number>; snow: THREE.IUniform<number>; dust: THREE.IUniform<number>; controlsWind: boolean; metrics?: WeatherPartic… — ⚠ undocumented
2856
3087
  - `WeatherVector` (type): type WeatherVector = readonly [number, number, number] — ⚠ undocumented
2857
3088
 
3089
+ ## @jgengine/shell/world/AuthoredFlocks
3090
+
3091
+ - `AuthoredFlocks` (function): function AuthoredFlocks({ models, assets, forward = [0, 0, -1], maxInstances = 256 }: AuthoredFlocksProps): ReactElement — Render bounded authored habitat agents with game-selected species models. The environment owns movement and lifetime; existing model owners load, animate, and dispose the native GLB. Unmapped species have no substitute geometry.
3092
+ - `AuthoredFlocksProps` (interface): interface AuthoredFlocksProps — Caller-owned species models for authored habitat agents.
3093
+
2858
3094
  ## @jgengine/shell/world/DataObjects
2859
3095
 
2860
3096
  - `DataObjects` (function): function DataObjects<T>({ data, position, height, color, cellSize = 0.28, hovered = null, hoverColor = "#ffffff", onHover, grow, castShadow = true, receiveShadow = true, renderItem, }: DataObjectsProps<T>): React.JSX.Element | null — ⚠ undocumented
@@ -2863,23 +3099,23 @@
2863
3099
  ## @jgengine/shell/world/GridWorldScene
2864
3100
 
2865
3101
  - `GridWorldScene` (function): function GridWorldScene({ feature }: GridWorldSceneProps): React.JSX.Element | null — Data-driven renderer for the `biomes()`/`voxel()`/`plots()`/`tilemap()` world-feature kinds (#207.1): one `InstancedMesh` of extruded, colored boxes built from each feature's declared `cells`, following the same direct-buffer pattern as `InstancedBodies`.
2866
- - `GridWorldSceneProps` (interface): interface GridWorldSceneProps — ⚠ undocumented
3102
+ - `GridWorldSceneProps` (interface): interface GridWorldSceneProps { feature: WorldFeature } — ⚠ undocumented · used by `GridWorldScene`: Data-driven renderer for the `biomes()`/`voxel()`/`plots()`/`tilemap()` world-feature kinds (#207.1): one `InstancedMesh` of extruded, color…
2867
3103
 
2868
3104
  ## @jgengine/shell/world/InstancedBodies
2869
3105
 
2870
3106
  - `InstancedBodies` (function): function InstancedBodies({ world, debugTint = false, baseColors, epoch = 0 }: InstancedBodiesProps): React.JSX.Element — Renders a PhysicsWorld's box bodies as a single InstancedMesh — one draw call per batch. Transforms are written directly into `instanceMatrix.array` each frame (bodies never touch the per-entity React path). Reusable by any game: hand it a physics world and go.
2871
- - `InstancedBodiesProps` (interface): interface InstancedBodiesProps — ⚠ undocumented
3107
+ - `InstancedBodiesProps` (interface): interface InstancedBodiesProps { world: PhysicsWorld; debugTint?: boolean; baseColors?: Float32Array; epoch?: number } — ⚠ undocumented · used by `InstancedBodies`: Renders a PhysicsWorld's box bodies as a single InstancedMesh — one draw call per batch.
2872
3108
 
2873
3109
  ## @jgengine/shell/world/InstancedJoints
2874
3110
 
2875
3111
  - `InstancedJoints` (function): function InstancedJoints({ world, color = "#f5c542" }: InstancedJointsProps): React.JSX.Element — Debug overlay drawing a PhysicsWorld's joints (suspension, ragdoll links, carry tethers) as one LineSegments batch. Endpoints are streamed each frame from `world.readJointSegments`; pair with `InstancedBodies` to see the constraint structure over the bodies.
2876
- - `InstancedJointsProps` (interface): interface InstancedJointsProps — ⚠ undocumented
3112
+ - `InstancedJointsProps` (interface): interface InstancedJointsProps { world: PhysicsWorld; color?: THREE.ColorRepresentation } — ⚠ undocumented · used by `InstancedJoints`: Debug overlay drawing a PhysicsWorld's joints (suspension, ragdoll links, carry tethers) as one LineSegments batch.
2877
3113
 
2878
3114
  ## @jgengine/shell/world/SpriteBatch
2879
3115
 
2880
3116
  - `SpriteBatch` (function): function SpriteBatch({ url, columns = 1, rows = 1, capacity = DEFAULT_CAPACITY, instances, plane = "xy", billboard = false, pixelated = true, alphaTest = DEFAULT_ALPHA_TEST, opacity = 1, }: SpriteBatchProps): React.JSX.Element — Renders a sprite sheet / tile atlas as a single InstancedMesh — one draw call for the whole batch. Each instance picks its atlas frame via a per-instance UV offset attribute patched into the material's vertex shader, so platformer/puzzle-grid presentation never needs one draw call per sprite. Transforms and UV offsets are written directly into the mesh's typed arrays each frame from a plain instance list (bodies never touch the per-entity React path).
2881
- - `SpriteBatchInstance` (interface): interface SpriteBatchInstance — ⚠ undocumented
2882
- - `SpriteBatchProps` (interface): interface SpriteBatchProps — ⚠ undocumented
3117
+ - `SpriteBatchInstance` (interface): interface SpriteBatchInstance { x: number; y: number; z?: number; frame?: number; scale?: number; rotation?: number } — ⚠ undocumented
3118
+ - `SpriteBatchProps` (interface): interface SpriteBatchProps { url: string; columns?: number; rows?: number; capacity?: number; instances: () => readonly SpriteBatchInstance[]; plane?: "xy" | "xz"; billboard?: boolean; pixelated?: boolean; alphaTest?: number; opacity?: number } — ⚠ undocumented · used by `SpriteBatch`: Renders a sprite sheet / tile atlas as a single InstancedMesh — one draw call for the whole batch.
2883
3119
 
2884
3120
  ## @jgengine/shell/world/TileLayerRenderer
2885
3121
 
@@ -2896,13 +3132,13 @@
2896
3132
  ## @jgengine/shell/world/WorldHud
2897
3133
 
2898
3134
  - `CombatCameraShake` (function): function CombatCameraShake(): null — ⚠ undocumented
2899
- - `NameplateSample` (interface): interface NameplateSample — One entity's projected nameplate: screen `x`/`y`, display `name`, health `percent` (or `null` when statless), and world `distance` from the player.
3135
+ - `NameplateSample` (interface): interface NameplateSample — Projected nameplate with optional health and distance from the render camera.
2900
3136
  - `ProjectileTracers` (function): function ProjectileTracers({ lifeMs = 130 }: { lifeMs?: number }): React.JSX.Element — ⚠ undocumented
2901
3137
  - `Reticle` (function): function Reticle({ className }: { className?: string }): React.JSX.Element — ⚠ undocumented
2902
- - `WorldBarSample` (interface): interface WorldBarSample — ⚠ undocumented
2903
- - `WorldEntityBars` (function): function WorldEntityBars({ statId, height = 2.2, roles, resolveRole, maxDistance = 60, occlude = false, }: { statId: string; height?: number; roles?: readonly CatalogEntityRole[]; resolveRole?: (entity: SceneEntity) => CatalogEntityRole | undefined; /** Hide bars for entities farther than this from … — ⚠ undocumented
3138
+ - `WorldBarSample` (interface): interface WorldBarSample { entityId?: string; x: number; y: number; percent: number } — ⚠ undocumented
3139
+ - `WorldEntityBars` (function): function WorldEntityBars({ statId, height = 2.2, roles, resolveRole, maxDistance = 60, occlude = true, tickMs = 80, maxSamples = 64, }: { statId: string; height?: number; roles?: readonly CatalogEntityRole[]; resolveRole?: (entity: SceneEntity) => CatalogEntityRole | undefined; /** Hide bars beyond … — ⚠ undocumented
2904
3140
  - `WorldFloatText` (function): function WorldFloatText({ height = 1.9, lifeMs = 950 }: { height?: number; lifeMs?: number }): React.JSX.Element — ⚠ undocumented
2905
- - `WorldNameplates` (function): function WorldNameplates({ statId = "health", height = 2.3, roles, resolveRole, maxDistance = 40, occlude = false, tickMs = 120, showHealth = true, className, nameplateClassName, nameClassName, barClassName, fillClassName, renderNameplate, }: WorldNameplatesProps): React.JSX.Element — Billboarded name + 78×6px HP bar over every nearby non-local entity that passes `roles`/`maxDistance` — headless (className/data-* slots on every part, `renderNameplate` for a full swap), turned on declaratively via `defineGame({ nameplates })` rather than mounted by hand.
3141
+ - `WorldNameplates` (function): function WorldNameplates({ statId = "health", height = 2.3, roles, resolveRole, maxDistance = 40, occlude = true, tickMs = 120, maxSamples = 64, resolveName, showHealth = true, className, nameplateClassName, nameClassName, barClassName, fillClassName, renderNameplate, }: WorldNameplatesProps): React… — Billboarded name + 78×6px HP bar over every nearby non-local entity that passes `roles`/`maxDistance` — headless (className/data-* slots on every part, `renderNameplate` for a full swap), turned on declaratively via `defineGame({ nameplates })` rather than mounted by hand.
2906
3142
  - `WorldNameplatesProps` (interface): interface WorldNameplatesProps — Props for `WorldNameplates` — entity filter, refresh rate, and headless className/render hooks.
2907
3143
  - `WorldObjectHighlights` (function): function WorldObjectHighlights({ color = "#facc15", radius, y = 0.05 }: WorldObjectHighlightsProps): React.JSX.Element — Ground ring over every `ctx.scene.object.selection`-ed placed object — the object-layer counterpart to `WorldEntityBars`/`WorldNameplates`. Mount it once in the game's scene (headless: no defaults are imposed beyond a visible ring) instead of hand-rolling a selection highlight through `WorldOverlay` against external state.
2908
3144
  - `WorldObjectHighlightsProps` (interface): interface WorldObjectHighlightsProps — Props for {@link WorldObjectHighlights}.
@@ -2914,7 +3150,7 @@
2914
3150
 
2915
3151
  ## @jgengine/shell/world/WorldParticles
2916
3152
 
2917
- - `resolveParticleBudget` (function): function resolveParticleBudget(quality: GraphicsQuality, requested: number | undefined, particleCap?: number): number — The per-effect particle pool a graphics tier allows: the requested `max` clamped to the tier cap, so lower tiers degrade density instead of dropping effects entirely.
3153
+ - `resolveParticleBudget` (function): function resolveParticleBudget(quality: GraphicsQuality, requested: number | undefined, particleCap?: number): number — The requested visual pool clamped to the graphics tier.
2918
3154
 
2919
3155
  ## @jgengine/shell/world/WorldPings
2920
3156
 
@@ -2924,17 +3160,17 @@
2924
3160
  ## @jgengine/shell/world/WorldScene
2925
3161
 
2926
3162
  - `RemotePlayers` (function): function RemotePlayers({ rows, presenceInterpolationMs = 100 }: { rows: PresencePoseRow[]; presenceInterpolationMs?: number }): React.JSX.Element — ⚠ undocumented
2927
- - `WorldView` (function): function WorldView({ entitySprites, entityModels, objectModels, objectStyles, environment, assets, renderEntity, renderObject, selectedIds, hideLocalActor, }: { entitySprites: Record<string, EntitySpriteConfig> | undefined; entityModels: Record<string, string | ModelConfig> | undefined; objectModels… — ⚠ undocumented
3163
+ - `WorldView` (function): function WorldView({ entitySprites, entityModels, objectModels, objectStyles, environment, assets, renderEntity, renderObject, selectedIds, hideLocalActor, editorLayers, }: { entitySprites: Record<string, EntitySpriteConfig> | undefined; entityModels: Record<string, string | ModelConfig> | undefined… — ⚠ undocumented
2928
3164
 
2929
3165
  ## @jgengine/shell/world/entityPose
2930
3166
 
2931
- - `PoseSource` (interface): interface PoseSource — ⚠ undocumented
2932
- - `PoseWritable` (interface): interface PoseWritable — ⚠ undocumented
3167
+ - `PoseSource` (interface): interface PoseSource { position: readonly [number, number, number]; rotationY: number } — ⚠ undocumented
3168
+ - `PoseWritable` (interface): interface PoseWritable { position: {set(x: number, y: number, z: number): void}; rotation: {y: number} } — ⚠ undocumented
2933
3169
 
2934
3170
  ## @jgengine/shell/world/floatTextStyle
2935
3171
 
2936
- - `FloatTextInfo` (interface): interface FloatTextInfo — ⚠ undocumented
2937
- - `FloatTextStyle` (interface): interface FloatTextStyle — ⚠ undocumented
3172
+ - `FloatTextInfo` (interface): interface FloatTextInfo { kind: string; hitType?: string; element?: string; crit?: boolean; scale?: number } — ⚠ undocumented
3173
+ - `FloatTextStyle` (interface): interface FloatTextStyle { color: string; fontSizePx: number; fontWeight: number; glow: string } — ⚠ undocumented
2938
3174
 
2939
3175
  ## @jgengine/shell/world/pingPulse
2940
3176
 
@@ -2949,9 +3185,11 @@
2949
3185
 
2950
3186
  ## @jgengine/shell/world/worldBarSamples
2951
3187
 
2952
- - `NameplateSample` (interface): interface NameplateSample — One entity's projected nameplate: screen `x`/`y`, display `name`, health `percent` (or `null` when statless), and world `distance` from the player.
2953
- - `Projectable` (interface): interface Projectable — ⚠ undocumented
2954
- - `WorldBarSample` (interface): interface WorldBarSample — ⚠ undocumented
3188
+ - `NameplateSample` (interface): interface NameplateSample — Projected nameplate with optional health and distance from the render camera.
3189
+ - `Projectable` (interface): interface Projectable { set(x: number, y: number, z: number): this; project(camera: {matrixWorldInverse: unknown; projectionMatrix: unknown}): this; x: number; y: number; z: number } — ⚠ undocumented
3190
+ - `WorldBarSample` (interface): interface WorldBarSample { entityId?: string; x: number; y: number; percent: number } — ⚠ undocumented
3191
+ - `WorldOverlayCamera` (interface): interface WorldOverlayCamera — Render camera matrices; world translation supplies the overlay's viewpoint.
3192
+ - `WorldOverlaySampleOptions` (interface): interface WorldOverlaySampleOptions — Sampling policy shared by health bars and nameplates.
2955
3193
 
2956
3194
  ## @jgengine/shell/worldSync
2957
3195