@jgengine/shell 0.18.1 → 0.19.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 (112) hide show
  1. package/CHANGELOG.md +158 -7
  2. package/dist/GamePlayerShell.js +20 -3
  3. package/dist/Shell3dPresentation.d.ts +3 -9
  4. package/dist/Shell3dPresentation.js +26 -6
  5. package/dist/ShellHudPresentation.d.ts +9 -1
  6. package/dist/ShellHudPresentation.js +21 -0
  7. package/dist/audio/AudioComponents.js +6 -2
  8. package/dist/audio/audioEngine.d.ts +8 -0
  9. package/dist/audio/audioEngine.js +65 -6
  10. package/dist/audio/audioWire.d.ts +3 -0
  11. package/dist/audio/audioWire.js +7 -1
  12. package/dist/audio/loopParams.d.ts +39 -0
  13. package/dist/audio/loopParams.js +43 -0
  14. package/dist/camera/GameCameraRig.js +1 -1
  15. package/dist/camera/GameFirstPersonCamera.d.ts +4 -2
  16. package/dist/camera/GameFirstPersonCamera.js +52 -15
  17. package/dist/camera/Viewports.d.ts +34 -0
  18. package/dist/camera/Viewports.js +123 -0
  19. package/dist/camera/cameraRigs.js +70 -62
  20. package/dist/camera/orbitCameraMath.js +2 -2
  21. package/dist/camera/rigMath.d.ts +82 -4
  22. package/dist/camera/rigMath.js +133 -15
  23. package/dist/defineGame.d.ts +9 -0
  24. package/dist/defineGame.js +20 -3
  25. package/dist/devtools/DevtoolsOverlay.js +2 -0
  26. package/dist/devtools/agentBridge.d.ts +6 -0
  27. package/dist/devtools/agentBridge.js +15 -1
  28. package/dist/diagnostics/FpsCounter.d.ts +17 -0
  29. package/dist/diagnostics/FpsCounter.js +32 -0
  30. package/dist/drivers/FrameDriver.js +13 -2
  31. package/dist/drivers/FrameRateLimiter.d.ts +9 -0
  32. package/dist/drivers/FrameRateLimiter.js +39 -0
  33. package/dist/drivers/HudOnlyDriver.js +1 -1
  34. package/dist/environment/DayNightSky.js +5 -5
  35. package/dist/environment/Daylight.d.ts +35 -1
  36. package/dist/environment/Daylight.js +98 -28
  37. package/dist/environment/VolumetricClouds.js +20 -2
  38. package/dist/environment/daylightCycle.d.ts +20 -0
  39. package/dist/environment/daylightCycle.js +59 -7
  40. package/dist/environment/sunShadowMath.d.ts +45 -0
  41. package/dist/environment/sunShadowMath.js +50 -0
  42. package/dist/input/gamepadPoll.d.ts +47 -0
  43. package/dist/input/gamepadPoll.js +142 -0
  44. package/dist/input/gamepadSource.d.ts +11 -1
  45. package/dist/input/gamepadSource.js +117 -71
  46. package/dist/input/mouseLook.js +2 -1
  47. package/dist/input/padHaptics.d.ts +17 -0
  48. package/dist/input/padHaptics.js +32 -0
  49. package/dist/input/pointerLock.d.ts +11 -0
  50. package/dist/input/pointerLock.js +19 -0
  51. package/dist/postfx/PostProcessing.d.ts +1 -1
  52. package/dist/postfx/PostProcessing.js +5 -1
  53. package/dist/postfx/stylizePass.d.ts +23 -0
  54. package/dist/postfx/stylizePass.js +216 -0
  55. package/dist/render/SceneModels.js +1 -1
  56. package/dist/render/SkinnedInstances.d.ts +91 -0
  57. package/dist/render/SkinnedInstances.js +249 -0
  58. package/dist/render/TextureFiltering.d.ts +16 -0
  59. package/dist/render/TextureFiltering.js +64 -0
  60. package/dist/render/displayColor.d.ts +33 -0
  61. package/dist/render/displayColor.js +138 -0
  62. package/dist/render/modelLoad.d.ts +29 -0
  63. package/dist/render/modelLoad.js +61 -1
  64. package/dist/render/resolveModel.js +4 -0
  65. package/dist/render/useFootIk.d.ts +53 -4
  66. package/dist/render/useFootIk.js +197 -70
  67. package/dist/render/useModelAnimation.d.ts +24 -0
  68. package/dist/render/useModelAnimation.js +61 -40
  69. package/dist/scene/AuthoredScene.d.ts +9 -0
  70. package/dist/scene/AuthoredScene.js +17 -1
  71. package/dist/scene/CityRenderer.js +6 -6
  72. package/dist/settings/QuickControls.js +1 -1
  73. package/dist/settings/SettingsMenu.js +1 -1
  74. package/dist/settings/appliedSettings.d.ts +9 -3
  75. package/dist/settings/appliedSettings.js +6 -5
  76. package/dist/settings/settingsController.d.ts +3 -0
  77. package/dist/settings/settingsController.js +47 -3
  78. package/dist/structures/BuildingKitBatch.js +26 -22
  79. package/dist/structures/GeneratedBuilding.d.ts +3 -15
  80. package/dist/structures/GeneratedBuilding.js +21 -15
  81. package/dist/structures/buildingSurface.d.ts +21 -0
  82. package/dist/structures/buildingSurface.js +134 -0
  83. package/dist/useShellMultiplayerSync.d.ts +1 -1
  84. package/dist/useShellMultiplayerSync.js +5 -3
  85. package/dist/world/WorldParticles.js +1 -1
  86. package/package.json +4 -4
  87. package/skills/ce-handoff/SKILL.md +1 -1
  88. package/skills/game-design/SKILL.md +2 -2
  89. package/skills/jgengine/SKILL.md +7 -5
  90. package/skills/jgengine/api.md +118 -89
  91. package/skills/jgengine/capabilities.md +28 -0
  92. package/skills/jgengine/recipes/minimal-game.md +3 -0
  93. package/skills/jgengine-ui/SKILL.md +13 -16
  94. package/skills/jgengine-ui/api.md +418 -317
  95. package/skills/jgengine-ui/capabilities.md +16 -0
  96. package/skills/jgengine-ui/reference-primitives.md +89 -0
  97. package/skills/jgengine-ui/reference.md +2 -81
  98. package/skills/jgengine-world/SKILL.md +15 -3
  99. package/skills/jgengine-world/api.md +873 -626
  100. package/skills/jgengine-world/capabilities.md +130 -6
  101. package/skills/jgengine-world/recipes/character-animation.md +39 -0
  102. package/skills/jgengine-world/recipes/controls.md +44 -0
  103. package/skills/jgengine-world/recipes/flight-feel.md +76 -0
  104. package/skills/jgengine-world/recipes/npc-senses.md +43 -0
  105. package/skills/jgengine-world/recipes/vehicle-feel.md +85 -0
  106. package/skills/jgengine-world/reference.md +31 -2
  107. package/skills/jgengine-world/visibility.md +112 -0
  108. package/skills/level-design/SKILL.md +1 -1
  109. package/dist/render/testFixtures/proceduralSpriteAtlas.d.ts +0 -15
  110. package/dist/render/testFixtures/proceduralSpriteAtlas.js +0 -30
  111. package/dist/render/testFixtures/proceduralTileLayer.d.ts +0 -13
  112. 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}.
@@ -525,8 +553,8 @@
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
555
  - `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
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.
531
559
  - `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
560
  - `CameraShakeMeterProps` (interface): interface CameraShakeMeterProps — Props for {@link CameraShakeMeter}.
@@ -537,13 +565,13 @@
537
565
  - `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
566
  - `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
567
  - `CharacterSheetProps` (interface): interface CharacterSheetProps — Props for {@link CharacterSheet}.
540
- - `ChatBubble` (interface): interface ChatBubble — ⚠ undocumented
541
- - `ChatBubblesOptions` (interface): interface ChatBubblesOptions — ⚠ undocumented
568
+ - `ChatBubble` (interface): interface ChatBubble { id: string; fromUserId: string; body: string; at: number } — ⚠ undocumented
569
+ - `ChatBubblesOptions` (interface): interface ChatBubblesOptions { channelId?: string; ttlMs?: number; limit?: number } — ⚠ undocumented
542
570
  - `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
571
  - `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
572
  - `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
573
+ - `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
574
+ - `ClerkUserState` (interface): interface ClerkUserState { isLoaded: boolean; isSignedIn: boolean | undefined; user: ClerkUserShape | null | undefined } — ⚠ undocumented
547
575
  - `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
576
  - `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
577
  - `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 +586,7 @@
558
586
  - `ComboMeterHudProps` (interface): interface ComboMeterHudProps — Props for {@link ComboMeterHud}.
559
587
  - `ComboMeterTheme` (interface): interface ComboMeterTheme — Reskin tokens for {@link ComboMeterHud}.
560
588
  - `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
589
+ - `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
590
  - `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
591
  - `ConfirmDialogProps` (interface): interface ConfirmDialogProps — Props for {@link ConfirmDialog}.
564
592
  - `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.
@@ -577,21 +605,21 @@
577
605
  - `DebouncedCommit` (interface): interface DebouncedCommit<T> — Live-mirrored, trailing-debounced commit binding for a single control value.
578
606
  - `DefaultEquipSlotId` (type): type DefaultEquipSlotId = | "head" | "chest" | "hands" | "legs" | "feet" | "mainHand" | "offHand" | "ring1" | "ring2" | "trinket" — Slot ids used by {@link defaultEquipLayout}'s convenience arrangement.
579
607
  - `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
608
+ - `DialogueCheck` (interface): interface DialogueCheck { label?: string; modifier: number; dc: number; advantage?: CheckAdvantage } — ⚠ undocumented
609
+ - `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…
610
+ - `DialogueDef` (interface): interface DialogueDef { id: string; lines: readonly DialogueLine[] } — ⚠ undocumented
583
611
  - `DialogueLine` (type): type DialogueLine = { speaker: string; text: string } | { choices: readonly DialogueChoice[] } — ⚠ undocumented
584
612
  - `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
613
  - `DialogueViewProps` (interface): interface DialogueViewProps — Props for {@link DialogueView}.
586
614
  - `DialogueViewTheme` (interface): interface DialogueViewTheme — Reskin tokens for {@link DialogueView}. Each defaults to a HudTheme `--jg-*` token.
587
- - `DisplayProfile` (interface): interface DisplayProfile — ⚠ undocumented
615
+ - `DisplayProfile` (interface): interface DisplayProfile { coarsePointer: boolean; compact: boolean; portrait: boolean } — ⚠ undocumented
588
616
  - `DomEventTarget` (interface): interface DomEventTarget — Minimal add/removeEventListener surface accepted by useDomEvent (window, document, elements).
589
617
  - `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
618
+ - `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
619
+ - `DragPayload` (interface): interface DragPayload<T> { id: string; value: T; rotation: Rotation } — ⚠ undocumented
620
+ - `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
621
  - `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
622
+ - `DropInfo` (interface): interface DropInfo<T> { payload: DragPayload<T>; target: string | null; cell: Cell | null; point: {x: number; y: number} } — ⚠ undocumented
595
623
  - `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
624
  - `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
625
  - `EntityFrameEntry` (interface): interface EntityFrameEntry — The minimum an entry must carry; games extend it with their own display data.
@@ -607,7 +635,7 @@
607
635
  - `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
636
  - `EquipmentSlots` (function): function EquipmentSlots(props: SlotGridProps): React.JSX.Element — Worn-gear slots (head/chest/hands/…): one atomic grid, painted icons, themed from tokens.
609
637
  - `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
638
+ - `EventfulEngineStore` (interface): interface EventfulEngineStore<TEventMap extends object> { on<K extends keyof TEventMap>(eventName: K, listener: (payload: TEventMap[K]) => void): () => void } — ⚠ undocumented
611
639
  - `ExperienceBar` (const): const ExperienceBar: (props: AtomicBarProps) => React.JSX.Element — Level progress / experience.
612
640
  - `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
641
  - `FastTravelMenuProps` (interface): interface FastTravelMenuProps<TMeta = unknown> — Props for {@link FastTravelMenu}.
@@ -632,7 +660,7 @@
632
660
  - `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
661
  - `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
662
  - `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
663
+ - `HudEditChord` (interface): interface HudEditChord { hold: string; press: string } — ⚠ undocumented · used by `HudCanvas`: Full-viewport HUD surface.
636
664
  - `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
665
  - `HudFrameProps` (interface): interface HudFrameProps — Props for {@link HudFrame}.
638
666
  - `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 +684,14 @@
656
684
  - `HudThemeSlot` (interface): interface HudThemeSlot — Action/inventory slot tokens.
657
685
  - `HudThemeStatus` (interface): interface HudThemeStatus — Semantic status colors — outcome, threat, and relationship coloring.
658
686
  - `HudThemeSurface` (interface): interface HudThemeSurface — Chrome surfaces, edges, and text — what panels, overlays, and menus are built out of.
659
- - `HudViewportContextValue` (interface): interface HudViewportContextValue — ⚠ undocumented
687
+ - `HudViewportContextValue` (interface): interface HudViewportContextValue { fitEnabled: boolean; config: HudViewportConfig | undefined; userScale: number } — ⚠ undocumented
660
688
  - `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
689
  - `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
690
  - `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
691
  - `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
692
  - `IconTreatmentProps` (interface): interface IconTreatmentProps — Props for {@link IconTreatment}.
665
693
  - `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
694
+ - `IdentitySource` (interface): interface IdentitySource { session: AuthSession | null; isLoading: boolean; signOut?: () => void } — ⚠ undocumented
667
695
  - `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
696
  - `InteractionPromptAnchor` (type): type InteractionPromptAnchor = "bottom-center" | "top-center" | "center" — Where the callout sits over the scene.
669
697
  - `InteractionPromptProps` (interface): interface InteractionPromptProps — Props for {@link InteractionPrompt}.
@@ -687,7 +715,7 @@
687
715
  - `LeavePartyButton` (function): function LeavePartyButton({ className, children, }: { className?: string; children?: ReactNode; }): React.JSX.Element | null — ⚠ undocumented
688
716
  - `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
717
  - `ManaBar` (const): const ManaBar: (props: AtomicBarProps) => React.JSX.Element — Spell/energy resource pool.
690
- - `MapBounds` (interface): interface MapBounds — ⚠ undocumented
718
+ - `MapBounds` (interface): interface MapBounds { minX: number; minZ: number; maxX: number; maxZ: number } — ⚠ undocumented
691
719
  - `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
720
  - `MapLegendProps` (interface): interface MapLegendProps — Props for {@link MapLegend}.
693
721
  - `MapViewport` (interface): interface MapViewport — Pan/zoom transform applied to a {@link WorldMapSurface}'s content group.
@@ -699,7 +727,7 @@
699
727
  - `MinimapChromeProps` (interface): interface MinimapChromeProps — Props for `MinimapChrome`.
700
728
  - `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
729
  - `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
730
+ - `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
731
  - `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
732
  - `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
733
  - `MinimapTrackProps` (interface): interface MinimapTrackProps — Props for {@link MinimapTrack}.
@@ -750,7 +778,7 @@
750
778
  - `RadialMenuOption` (interface): interface RadialMenuOption — One selectable entry on a radial/quick menu (weapon or emote wheel).
751
779
  - `RadialMenuProps` (interface): interface RadialMenuProps — Props for {@link RadialMenu}.
752
780
  - `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
781
+ - `ReadableEngineStore` (interface): interface ReadableEngineStore<TState> { getState(): TState; subscribe(listener: (state: TState) => void): () => void } — ⚠ undocumented
754
782
  - `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
783
  - `RequireSession` (function): function RequireSession({ fallback, loading, children, }: { fallback?: ReactNode; loading?: ReactNode; children?: ReactNode; }): React.JSX.Element — ⚠ undocumented
756
784
  - `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.
@@ -817,15 +845,15 @@
817
845
  - `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
846
  - `Trans` (function): function Trans({ k, params }: { k: string; params?: TParams }): ReactNode — Render a translated message inline: `<Trans k="hud.score" params={{ value }} />`.
819
847
  - `UseActionBarOptions` (interface): interface UseActionBarOptions — Options for {@link useActionBar}.
820
- - `UseAxisChannelResult` (interface): interface UseAxisChannelResult — ⚠ undocumented
848
+ - `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
849
  - `UsePanelsOptions` (interface): interface UsePanelsOptions — Options for {@link usePanels}.
822
850
  - `UseSelectionViewOptions` (interface): interface UseSelectionViewOptions — Options for {@link useSelectionView}.
823
- - `UseVoiceOptions` (interface): interface UseVoiceOptions — ⚠ undocumented
851
+ - `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
852
  - `UserBadge` (function): function UserBadge({ className, avatarClassName, nameClassName, renderBadge, }: { className?: string; avatarClassName?: string; nameClassName?: string; renderBadge?: (session: AuthSession) => ReactNode; }): React.JSX.Element | null — ⚠ undocumented
825
853
  - `ViewportMetrics` (interface): interface ViewportMetrics — Live viewport rectangles: the layout viewport and the visible `visualViewport`.
826
854
  - `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
855
  - `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
856
+ - `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
857
  - `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
858
  - `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
859
  - `WaveHudProps` (interface): interface WaveHudProps — Props for {@link WaveHud}.
@@ -838,10 +866,10 @@
838
866
  - `WeaponSlots` (function): function WeaponSlots(props: SlotGridProps): React.JSX.Element — Weapon loadout slots.
839
867
  - `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
868
  - `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
869
+ - `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
870
  - `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
871
  - `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
872
+ - `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
873
  - `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
874
  - `WorldMapSurfaceProps` (interface): interface WorldMapSurfaceProps — Props for {@link WorldMapSurface}.
847
875
  - `abilityKitNeedsHeartbeat` (function): function abilityKitNeedsHeartbeat(kit: AbilityKit, resourceAvailable?: number): boolean — ⚠ undocumented
@@ -969,7 +997,7 @@
969
997
  - `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
998
  - `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
999
  - `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
1000
+ - `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
1001
  - `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
1002
  - `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
1003
  - `useViewportMetrics` (function): function useViewportMetrics(): ViewportMetrics — Live visible viewport, tracking `window.visualViewport` (mobile browser chrome, pinch-zoom) with a layout-viewport fallback.
@@ -1075,8 +1103,8 @@
1075
1103
 
1076
1104
  ## @jgengine/react/chatBubbles
1077
1105
 
1078
- - `ChatBubble` (interface): interface ChatBubble — ⚠ undocumented
1079
- - `ChatBubblesOptions` (interface): interface ChatBubblesOptions — ⚠ undocumented
1106
+ - `ChatBubble` (interface): interface ChatBubble { id: string; fromUserId: string; body: string; at: number } — ⚠ undocumented
1107
+ - `ChatBubblesOptions` (interface): interface ChatBubblesOptions { channelId?: string; ttlMs?: number; limit?: number } — ⚠ undocumented
1080
1108
  - `latestChatBubbles` (function): function latestChatBubbles(messages: readonly ChatMessage[], nowMs: number, ttlMs: number): ChatBubble[] — ⚠ undocumented
1081
1109
  - `useChatBubbles` (function): function useChatBubbles(options?: ChatBubblesOptions): readonly ChatBubble[] — ⚠ undocumented
1082
1110
  - `useEntityChatBubble` (function): function useEntityChatBubble(instanceId: string, options?: ChatBubblesOptions): ChatBubble | null — ⚠ undocumented
@@ -1108,9 +1136,9 @@
1108
1136
  - `CurrencyPill` (function): function CurrencyPill({ currencyId, className }: { currencyId: string; className?: string }): React.JSX.Element — ⚠ undocumented
1109
1137
  - `DeathScreen` (function): function DeathScreen({ statId = "health", open, className, children, }: { statId?: string; open?: boolean; className?: string; children?: ReactNode; }): React.JSX.Element — ⚠ undocumented
1110
1138
  - `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
1139
+ - `DialogueCheck` (interface): interface DialogueCheck { label?: string; modifier: number; dc: number; advantage?: CheckAdvantage } — ⚠ undocumented
1140
+ - `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…
1141
+ - `DialogueDef` (interface): interface DialogueDef { id: string; lines: readonly DialogueLine[] } — ⚠ undocumented
1114
1142
  - `DialogueLine` (type): type DialogueLine = { speaker: string; text: string } | { choices: readonly DialogueChoice[] } — ⚠ undocumented
1115
1143
  - `KeybindRow` (function): function KeybindRow({ action, keys, className, }: { action: string; keys: readonly string[]; className?: string; }): React.JSX.Element — ⚠ undocumented
1116
1144
  - `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
@@ -1153,23 +1181,23 @@
1153
1181
 
1154
1182
  ## @jgengine/react/display
1155
1183
 
1156
- - `DisplayProfile` (interface): interface DisplayProfile — ⚠ undocumented
1184
+ - `DisplayProfile` (interface): interface DisplayProfile { coarsePointer: boolean; compact: boolean; portrait: boolean } — ⚠ undocumented
1157
1185
 
1158
1186
  ## @jgengine/react/dragLayer
1159
1187
 
1160
1188
  - `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
1189
+ - `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
1190
+ - `DragPayload` (interface): interface DragPayload<T> { id: string; value: T; rotation: Rotation } — ⚠ undocumented
1191
+ - `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
1192
  - `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
1193
+ - `DropInfo` (interface): interface DropInfo<T> { payload: DragPayload<T>; target: string | null; cell: Cell | null; point: {x: number; y: number} } — ⚠ undocumented
1166
1194
  - `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
1195
  - `useDragLayer` (function): function useDragLayer<T>(options?: { onDrop?: (info: DropInfo<T>) => void; }): DragLayer<T> — ⚠ undocumented
1168
1196
 
1169
1197
  ## @jgengine/react/engineStore
1170
1198
 
1171
- - `EventfulEngineStore` (interface): interface EventfulEngineStore<TEventMap extends object> — ⚠ undocumented
1172
- - `ReadableEngineStore` (interface): interface ReadableEngineStore<TState> — ⚠ undocumented
1199
+ - `EventfulEngineStore` (interface): interface EventfulEngineStore<TEventMap extends object> { on<K extends keyof TEventMap>(eventName: K, listener: (payload: TEventMap[K]) => void): () => void } — ⚠ undocumented
1200
+ - `ReadableEngineStore` (interface): interface ReadableEngineStore<TState> { getState(): TState; subscribe(listener: (state: TState) => void): () => void } — ⚠ undocumented
1173
1201
 
1174
1202
  ## @jgengine/react/entityFrames
1175
1203
 
@@ -1198,14 +1226,14 @@
1198
1226
 
1199
1227
  ## @jgengine/react/fogOverlay
1200
1228
 
1201
- - `FogCellRect` (interface): interface FogCellRect — ⚠ undocumented
1202
- - `FogPaintSurface` (interface): interface FogPaintSurface — ⚠ undocumented
1229
+ - `FogCellRect` (interface): interface FogCellRect { x: number; y: number; width: number; height: number } — ⚠ undocumented
1230
+ - `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
1231
 
1204
1232
  ## @jgengine/react/gameIcons
1205
1233
 
1206
1234
  - `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
1235
  - `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
1236
+ - `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
1237
  - `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
1238
  - `iconForItemId` (function): function iconForItemId(itemId: string): GameIconName | null — ⚠ undocumented
1211
1239
  - `isGameIconName` (function): function isGameIconName(value: string): value is GameIconName — ⚠ undocumented
@@ -1226,12 +1254,12 @@
1226
1254
 
1227
1255
  ## @jgengine/react/hooks
1228
1256
 
1229
- - `AbilitySlotBindingOptions` (interface): interface AbilitySlotBindingOptions — ⚠ undocumented
1257
+ - `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
1258
  - `DomEventTarget` (interface): interface DomEventTarget — Minimal add/removeEventListener surface accepted by useDomEvent (window, document, elements).
1231
1259
  - `EventMeterView` (interface): interface EventMeterView — A rendered snapshot of an {@link EventMeter}: current value, fill fraction, active tier, and ready-to-consume flag.
1232
1260
  - `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
1261
+ - `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)`.
1262
+ - `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
1263
  - `abilityKitNeedsHeartbeat` (function): function abilityKitNeedsHeartbeat(kit: AbilityKit, resourceAvailable?: number): boolean — ⚠ undocumented
1236
1264
  - `createHeldKeyTracker` (function): function createHeldKeyTracker(target: HeldKeyEventTarget): { isDown: (code: string) => boolean; dispose: () => void; } — ⚠ undocumented
1237
1265
  - `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.
@@ -1273,7 +1301,7 @@
1273
1301
  - `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
1302
  - `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
1303
  - `useSceneObjects` (function): function useSceneObjects(): readonly SceneObject[] — ⚠ undocumented
1276
- - `useTarget` (function): function useTarget(fromInstanceId: string): string | null — ⚠ undocumented
1304
+ - `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
1305
  - `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
1306
  - `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
1307
  - `useWorldInvites` (function): function useWorldInvites(): WorldInvite[] — ⚠ undocumented
@@ -1301,7 +1329,7 @@
1301
1329
 
1302
1330
  - `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
1331
  - `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
1332
+ - `HudEditChord` (interface): interface HudEditChord { hold: string; press: string } — ⚠ undocumented · used by `HudCanvas`: Full-viewport HUD surface.
1305
1333
  - `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
1334
  - `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
1335
  - `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.
@@ -1352,7 +1380,7 @@
1352
1380
 
1353
1381
  ## @jgengine/react/hudViewport
1354
1382
 
1355
- - `HudViewportContextValue` (interface): interface HudViewportContextValue — ⚠ undocumented
1383
+ - `HudViewportContextValue` (interface): interface HudViewportContextValue { fitEnabled: boolean; config: HudViewportConfig | undefined; userScale: number } — ⚠ undocumented
1356
1384
  - `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
1385
  - `useHudViewport` (function): function useHudViewport(): HudViewportContextValue | null — ⚠ undocumented
1358
1386
 
@@ -1381,12 +1409,12 @@
1381
1409
 
1382
1410
  ## @jgengine/react/identity
1383
1411
 
1384
- - `BetterAuthSessionState` (interface): interface BetterAuthSessionState — ⚠ undocumented
1385
- - `BetterAuthUserShape` (interface): interface BetterAuthUserShape — ⚠ undocumented
1386
- - `ClerkUserShape` (interface): interface ClerkUserShape — ⚠ undocumented
1387
- - `ClerkUserState` (interface): interface ClerkUserState — ⚠ undocumented
1412
+ - `BetterAuthSessionState` (interface): interface BetterAuthSessionState { data: {user: BetterAuthUserShape} | null | undefined; isPending: boolean } — ⚠ undocumented
1413
+ - `BetterAuthUserShape` (interface): interface BetterAuthUserShape { id: string; name?: string | null; email?: string | null; image?: string | null } — ⚠ undocumented
1414
+ - `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
1415
+ - `ClerkUserState` (interface): interface ClerkUserState { isLoaded: boolean; isSignedIn: boolean | undefined; user: ClerkUserShape | null | undefined } — ⚠ undocumented
1388
1416
  - `GameIdentityProvider` (function): function GameIdentityProvider({ source, children, }: { source: IdentitySource; children?: ReactNode; }): React.JSX.Element — ⚠ undocumented
1389
- - `IdentitySource` (interface): interface IdentitySource — ⚠ undocumented
1417
+ - `IdentitySource` (interface): interface IdentitySource { session: AuthSession | null; isLoading: boolean; signOut?: () => void } — ⚠ undocumented
1390
1418
  - `RequireSession` (function): function RequireSession({ fallback, loading, children, }: { fallback?: ReactNode; loading?: ReactNode; children?: ReactNode; }): React.JSX.Element — ⚠ undocumented
1391
1419
  - `SignOutButton` (function): function SignOutButton({ className, children, }: { className?: string; children?: ReactNode; }): React.JSX.Element | null — ⚠ undocumented
1392
1420
  - `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 +1467,10 @@
1439
1467
  ## @jgengine/react/map
1440
1468
 
1441
1469
  - `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
1470
+ - `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
1471
  - `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
1472
  - `FullscreenMapProps` (interface): interface FullscreenMapProps extends Omit<WorldMapSurfaceProps, "canvasWidth" | "canvasHeight" | "viewport" | "width" | "height" | "style"> — Props for {@link FullscreenMap}.
1445
- - `MapBounds` (interface): interface MapBounds — ⚠ undocumented
1473
+ - `MapBounds` (interface): interface MapBounds { minX: number; minZ: number; maxX: number; maxZ: number } — ⚠ undocumented
1446
1474
  - `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
1475
  - `MapLegendProps` (interface): interface MapLegendProps — Props for {@link MapLegend}.
1448
1476
  - `MapViewport` (interface): interface MapViewport — Pan/zoom transform applied to a {@link WorldMapSurface}'s content group.
@@ -1452,7 +1480,7 @@
1452
1480
  - `MinimapChromeProps` (interface): interface MinimapChromeProps — Props for `MinimapChrome`.
1453
1481
  - `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
1482
  - `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
1483
+ - `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
1484
  - `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
1485
  - `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
1486
  - `MinimapTrackProps` (interface): interface MinimapTrackProps — Props for {@link MinimapTrack}.
@@ -1460,7 +1488,7 @@
1460
1488
  - `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
1489
  - `WaypointArrowProps` (interface): interface WaypointArrowProps — Props for {@link WaypointArrow}.
1462
1490
  - `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
1491
+ - `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
1492
  - `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
1493
  - `WorldMapSurfaceProps` (interface): interface WorldMapSurfaceProps — Props for {@link WorldMapSurface}.
1466
1494
  - `useFog` (function): function useFog(fog: FogField): ReturnType<FogField["cells"]> — ⚠ undocumented
@@ -1709,9 +1737,9 @@
1709
1737
  - `MicToggle` (function): function MicToggle({ voice, className, mutedLabel, unmutedLabel, }: { voice: VoiceState; className?: string; mutedLabel?: ReactNode; unmutedLabel?: ReactNode; }): React.JSX.Element — ⚠ undocumented
1710
1738
  - `PushToTalkButton` (function): function PushToTalkButton({ voice, className, children, }: { voice: VoiceState; className?: string; children?: ReactNode; }): React.JSX.Element — ⚠ undocumented
1711
1739
  - `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
1740
+ - `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
1741
  - `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
1742
+ - `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
1743
  - `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
1744
 
1717
1745
  ## @jgengine/react/waveHud
@@ -1763,16 +1791,18 @@
1763
1791
 
1764
1792
  ## @jgengine/shell/audio/audioEngine
1765
1793
 
1766
- - `AudioEmitterHandle` (interface): interface AudioEmitterHandle — ⚠ undocumented
1767
- - `AudioEngine` (interface): interface AudioEngine — ⚠ undocumented
1768
- - `AudioSceneConfig` (interface): interface AudioSceneConfig — ⚠ undocumented
1794
+ - `AudioEmitterHandle` (interface): interface AudioEmitterHandle { 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
1795
+ - `AudioEngine` (interface): interface AudioEngine { setListenerPose(pose: ListenerPose | Vec3): void; playOneShot(soundId: string, position?: Vec3): void; playLoop(soundId: string, position?: Vec3): AudioEmitterHandle | null; playMusic(themeId: string | null, options?: CrossfadeOptions): v… — ⚠ undocumented
1796
+ - `AudioSceneConfig` (interface): interface AudioSceneConfig { sounds?: Record<string, SoundDef>; buses?: Record<string, AudioBusDef>; music?: Record<string, MusicTheme>; musicBus?: string } — ⚠ undocumented
1769
1797
  - `ListenerPose` (interface): interface ListenerPose — Position and orientation for a spatial-audio listener.
1770
- - `Vec3` (interface): interface Vec3 — ⚠ undocumented
1798
+ - `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.
1771
1799
  - `createAudioEngine` (function): function createAudioEngine(config: AudioSceneConfig = {}): AudioEngine — ⚠ undocumented
1772
1800
 
1773
1801
  ## @jgengine/shell/audio/loopParams
1774
1802
 
1803
+ - `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
1804
  - `MAX_LOOP_RATE` (const): const MAX_LOOP_RATE: 4 — Highest live playback-rate multiplier (two octaves above authored pitch).
1805
+ - `MIN_LOOP_CUTOFF` (const): const MIN_LOOP_CUTOFF: 10 — Lowest live filter cutoff, Hz.
1776
1806
  - `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
1807
 
1778
1808
  ## @jgengine/shell/audio/musicDirector
@@ -1802,22 +1832,22 @@
1802
1832
  - `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
1833
  - `CameraFollowListener` (type): type CameraFollowListener = (state: CameraFollowState) => void — ⚠ undocumented
1804
1834
  - `CameraOccluder` (interface): interface CameraOccluder — Camera spring-arm occlusion filtering.
1805
- - `CameraShakeChannel` (interface): interface CameraShakeChannel — ⚠ undocumented
1835
+ - `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
1836
  - `CameraShakeContext` (const): const CameraShakeContext: React.Context<CameraShakeChannel> — ⚠ undocumented
1807
1837
  - `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
1838
  - `ControllerCameraShakeProps` (interface): interface ControllerCameraShakeProps — Props for {@link ControllerCameraShake}.
1809
1839
  - `GAME_SIM_FRAME_PRIORITY` (const): const GAME_SIM_FRAME_PRIORITY: 0 — Run simulation/movement before orbit follow so poses are current.
1810
1840
  - `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
1841
+ - `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
1842
+ - `GameFirstPersonCamera` (function): function GameFirstPersonCamera({ yawRef, pitchRef, config, followEntityId, viewmodel, weapon, }: GameFirstPersonCameraProps): React.JSX.Element | null — ⚠ undocumented
1843
+ - `GameFirstPersonCameraProps` (interface): interface GameFirstPersonCameraProps { yawRef: MutableRefObject<number>; pitchRef: MutableRefObject<number>; config?: FirstPersonCameraConfig; followEntityId?: string; viewmodel?: ComponentType<ViewmodelProps>; weapon?: (entityId: string) => CameraWeaponView | null } — ⚠ undocumented
1814
1844
  - `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
1845
+ - `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
1846
  - `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
1847
+ - `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
1848
  - `PlayerFovProvider` (function): function PlayerFovProvider({ config, orthographic, children, }: { config?: GameCameraConfig; orthographic: boolean; children: ReactNode; }): React.JSX.Element — ⚠ undocumented
1819
1849
  - `PlayerFovSlider` (function): function PlayerFovSlider(): React.JSX.Element | null — ⚠ undocumented
1820
- - `PlayerFovState` (interface): interface PlayerFovState — ⚠ undocumented
1850
+ - `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
1851
  - `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
1852
  - `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
1853
  - `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.
@@ -1832,37 +1862,41 @@
1832
1862
  ## @jgengine/shell/camera/GameCameraRig
1833
1863
 
1834
1864
  - `GameCameraRig` (function): function GameCameraRig({ yawRef, pitchRef, config, onDragChange, pointerControls, panKeysEnabled, director, viewmodel, }: GameCameraRigProps): React.JSX.Element — ⚠ undocumented
1835
- - `GameCameraRigProps` (interface): interface GameCameraRigProps — ⚠ undocumented
1865
+ - `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
1866
 
1837
1867
  ## @jgengine/shell/camera/GameFirstPersonCamera
1838
1868
 
1839
- - `GameFirstPersonCamera` (function): function GameFirstPersonCamera({ yawRef, pitchRef, config, followEntityId, viewmodel, }: GameFirstPersonCameraProps): React.JSX.Element | null — ⚠ undocumented
1840
- - `GameFirstPersonCameraProps` (interface): interface GameFirstPersonCameraProps — ⚠ undocumented
1869
+ - `GameFirstPersonCamera` (function): function GameFirstPersonCamera({ yawRef, pitchRef, config, followEntityId, viewmodel, weapon, }: GameFirstPersonCameraProps): React.JSX.Element | null — ⚠ undocumented
1870
+ - `GameFirstPersonCameraProps` (interface): interface GameFirstPersonCameraProps { yawRef: MutableRefObject<number>; pitchRef: MutableRefObject<number>; config?: FirstPersonCameraConfig; followEntityId?: string; viewmodel?: ComponentType<ViewmodelProps>; weapon?: (entityId: string) => CameraWeaponView | null } — ⚠ undocumented
1841
1871
  - `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
1872
  - `readFirstPersonMuzzle` (function): function readFirstPersonMuzzle(target: THREE.Vector3): boolean — World position of the first-person weapon muzzle, or false when no viewmodel is mounted.
1843
1873
 
1844
1874
  ## @jgengine/shell/camera/GameInspectionCamera
1845
1875
 
1846
1876
  - `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
1877
+ - `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
1878
 
1849
1879
  ## @jgengine/shell/camera/GameOrbitCamera
1850
1880
 
1851
1881
  - `CameraFollowListener` (type): type CameraFollowListener = (state: CameraFollowState) => void — ⚠ undocumented
1852
1882
  - `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
1883
+ - `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
1884
  - `seedOrbitCameraTarget` (function): function seedOrbitCameraTarget(camera: Camera, target: Vector3, distance: number, height: number): void — Seed orbit target before controls mount (demo spawn at origin).
1855
1885
 
1856
1886
  ## @jgengine/shell/camera/PlayerFov
1857
1887
 
1858
1888
  - `PlayerFovProvider` (function): function PlayerFovProvider({ config, orthographic, children, }: { config?: GameCameraConfig; orthographic: boolean; children: ReactNode; }): React.JSX.Element — ⚠ undocumented
1859
1889
  - `PlayerFovSlider` (function): function PlayerFovSlider(): React.JSX.Element | null — ⚠ undocumented
1860
- - `PlayerFovState` (interface): interface PlayerFovState — ⚠ undocumented
1890
+ - `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
1891
  - `usePlayerFov` (function): function usePlayerFov(): PlayerFovState — ⚠ undocumented
1862
1892
 
1893
+ ## @jgengine/shell/camera/Viewports
1894
+
1895
+ - `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`.
1896
+
1863
1897
  ## @jgengine/shell/camera/cameraBlendMath
1864
1898
 
1865
- - `CameraBlendScratch` (interface): interface CameraBlendScratch — ⚠ undocumented
1899
+ - `CameraBlendScratch` (interface): interface CameraBlendScratch { fromPos: Vector3; fromQuat: Quaternion; toPos: Vector3; toQuat: Quaternion; fov: number; elapsed: number; duration: number } — ⚠ undocumented
1866
1900
 
1867
1901
  ## @jgengine/shell/camera/cameraCollision
1868
1902
 
@@ -1874,12 +1908,12 @@
1874
1908
 
1875
1909
  - `CAMERA_POST_FRAME_PRIORITY` (const): const CAMERA_POST_FRAME_PRIORITY: number — ⚠ undocumented
1876
1910
  - `CAMERA_RIG_FRAME_PRIORITY` (const): const CAMERA_RIG_FRAME_PRIORITY: -1 — ⚠ undocumented
1877
- - `CameraBlendScratch` (interface): interface CameraBlendScratch — ⚠ undocumented
1911
+ - `CameraBlendScratch` (interface): interface CameraBlendScratch { fromPos: Vector3; fromQuat: Quaternion; toPos: Vector3; toQuat: Quaternion; fov: number; elapsed: number; duration: number } — ⚠ undocumented
1878
1912
  - `ChaseRig` (function): function ChaseRig(props: RigProps): null — ⚠ undocumented
1879
1913
  - `CinematicRig` (function): function CinematicRig(props: RigProps & { onComplete?: () => void }): null — ⚠ undocumented
1880
1914
  - `LockOnRig` (function): function LockOnRig(props: RigProps): null — ⚠ undocumented
1881
1915
  - `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
1916
+ - `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
1917
  - `RtsRig` (function): function RtsRig(props: RigProps & { panKeysEnabled?: boolean }): null — ⚠ undocumented
1884
1918
  - `ShoulderRig` (function): function ShoulderRig(props: RigProps): null — ⚠ undocumented
1885
1919
  - `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.
@@ -1891,26 +1925,26 @@
1891
1925
  - `PLAYER_FOV_MAX` (const): const PLAYER_FOV_MAX: 120 — ⚠ undocumented
1892
1926
  - `PLAYER_FOV_MIN` (const): const PLAYER_FOV_MIN: 40 — ⚠ undocumented
1893
1927
  - `PLAYER_FOV_STORAGE_KEY` (const): const PLAYER_FOV_STORAGE_KEY: "jgengine:player-fov" — ⚠ undocumented
1894
- - `PlayerFovBounds` (interface): interface PlayerFovBounds — ⚠ undocumented
1928
+ - `PlayerFovBounds` (interface): interface PlayerFovBounds { min: number; max: number; defaultFov: number } — ⚠ undocumented
1895
1929
 
1896
1930
  ## @jgengine/shell/camera/inspectionCameraMath
1897
1931
 
1898
1932
  - `InspectionCameraConfig` (interface): interface InspectionCameraConfig — Model-viewer / inspection rig (#207.7) — orbit + pan + anchored zoom around a fixed point, never reads player input.
1899
1933
  - `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
1934
+ - `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
1935
 
1902
1936
  ## @jgengine/shell/camera/orbitCameraMath
1903
1937
 
1904
- - `CameraFollowState` (interface): interface CameraFollowState — ⚠ undocumented
1938
+ - `CameraFollowState` (interface): interface CameraFollowState { entityId: string; target: Vec3; camera: Vec3; distance: number } — ⚠ undocumented
1905
1939
  - `DEFAULT_ORBIT_CAMERA` (const): const DEFAULT_ORBIT_CAMERA: ResolvedOrbitCameraConfig — ⚠ undocumented
1906
1940
  - `GAME_SIM_FRAME_PRIORITY` (const): const GAME_SIM_FRAME_PRIORITY: 0 — Run simulation/movement before orbit follow so poses are current.
1907
1941
  - `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
1942
+ - `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
1943
  - `OrbitCollisionConfig` (interface): interface OrbitCollisionConfig — Spring-arm occlusion config for the orbit rig.
1910
- - `OrbitFollowRuntimeState` (interface): interface OrbitFollowRuntimeState — ⚠ undocumented
1944
+ - `OrbitFollowRuntimeState` (interface): interface OrbitFollowRuntimeState { target: Vec3; camera: Vec3; lockedDistance: number | null } — ⚠ undocumented
1911
1945
  - `ResolvedOrbitCameraConfig` (interface): interface ResolvedOrbitCameraConfig — Fully resolved shell config after merging with DEFAULT_ORBIT_CAMERA.
1912
1946
  - `ResolvedOrbitCollision` (interface): interface ResolvedOrbitCollision — An {@link OrbitCollisionConfig} with every field resolved to a concrete value.
1913
- - `Vec3` (interface): interface Vec3 — ⚠ undocumented
1947
+ - `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
1948
 
1915
1949
  ## @jgengine/shell/camera/rigMath
1916
1950
 
@@ -1918,28 +1952,32 @@
1918
1952
  - `CALIBRATED_TRAUMA_SHAKE_FREQUENCY` (const): const CALIBRATED_TRAUMA_SHAKE_FREQUENCY: 32 — Calibrated shake noise frequency (Hz) for `traumaShake`.
1919
1953
  - `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
1954
  - `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
1955
+ - `CameraPose` (interface): interface CameraPose { position: Vec3; lookAt: Vec3; fov: number } — ⚠ undocumented
1956
+ - `ChaseBoomClamp` (type): type ChaseBoomClamp = (pivot: Vec3, desired: Vec3) => Vec3 — Pulls a desired camera position in toward `pivot` when something blocks the boom.
1957
+ - `ChaseRigState` (interface): interface ChaseRigState — Mutable per-rig chase state; {@link stepChase} advances it in place.
1958
+ - `ChaseSample` (interface): interface ChaseSample — What the chase rig reads from its target each frame.
1959
+ - `ChaseStepResult` (interface): interface ChaseStepResult — One frame of chase output from {@link stepChase}.
1960
+ - `CinematicSample` (interface): interface CinematicSample { pose: CameraPose; done: boolean } — ⚠ undocumented
1961
+ - `DirectorCameraValues` (interface): interface DirectorCameraValues { followEntityId?: string | null; cinematic?: CinematicCameraConfig | null } — ⚠ undocumented
1962
+ - `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
1963
+ - `ResolvedDirectedCamera` (interface): interface ResolvedDirectedCamera { followEntityId: string | null | undefined; cinematic: CinematicCameraConfig | undefined } — ⚠ undocumented
1964
+ - `ResolvedObserver` (interface): interface ResolvedObserver { distance: number; height: number; lookHeight: number; orbitSpeed: number } — ⚠ undocumented
1965
+ - `ResolvedShoulder` (interface): interface ResolvedShoulder { shoulderOffset: number; heightOffset: number; distance: number; fov: number } — ⚠ undocumented
1966
+ - `ResolvedSideScroll` (interface): interface ResolvedSideScroll { axis: "x" | "z"; distance: number; height: number; lookHeight: number; followSmoothing: number } — ⚠ undocumented
1967
+ - `ResolvedTopDown` (interface): interface ResolvedTopDown { height: number; pitch: number; yaw: number; offset: Vec3; followSmoothing: number } — ⚠ undocumented
1968
+ - `ShakeOffset` (interface): interface ShakeOffset { x: number; y: number; roll: number } — ⚠ undocumented
1969
+ - `StaticCameraValues` (interface): interface StaticCameraValues { followEntityId?: string | null; cinematic?: CinematicCameraConfig } — ⚠ undocumented
1970
+ - `TraumaState` (interface): interface TraumaState { trauma: number; time: number } — ⚠ undocumented
1933
1971
 
1934
1972
  ## @jgengine/shell/camera/shakeChannel
1935
1973
 
1936
- - `CameraShakeChannel` (interface): interface CameraShakeChannel — ⚠ undocumented
1974
+ - `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
1975
  - `CameraShakeContext` (const): const CameraShakeContext: React.Context<CameraShakeChannel> — ⚠ undocumented
1938
1976
  - `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
1977
 
1940
1978
  ## @jgengine/shell/camera/shakeChannelMath
1941
1979
 
1942
- - `CameraShakeChannel` (interface): interface CameraShakeChannel — ⚠ undocumented
1980
+ - `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.
1943
1981
 
1944
1982
  ## @jgengine/shell/commandSink
1945
1983
 
@@ -1980,14 +2018,14 @@
1980
2018
 
1981
2019
  ## @jgengine/shell/devtools/collisionDebug
1982
2020
 
1983
- - `AimProbeConfig` (interface): interface AimProbeConfig — ⚠ undocumented
2021
+ - `AimProbeConfig` (interface): interface AimProbeConfig { from: string; aim: Aim; originPolicy?: ShotOriginPolicy; maxDistance?: number } — ⚠ undocumented
1984
2022
  - `COLLISION_DEBUG_LAYERS` (const): const COLLISION_DEBUG_LAYERS: readonly CollisionDebugLayer[] — ⚠ undocumented
1985
- - `CollisionDebugController` (interface): interface CollisionDebugController — ⚠ undocumented
2023
+ - `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
2024
  - `CollisionDebugLayer` (type): type CollisionDebugLayer = | "hitboxes" | "bodies" | "projectiles" | "muzzles" | "aimLaser" — ⚠ undocumented
1987
2025
  - `CollisionDebugLayers` (type): type CollisionDebugLayers = Record<CollisionDebugLayer, boolean> — ⚠ undocumented
1988
2026
  - `CollisionDebugListener` (type): type CollisionDebugListener = () => void — ⚠ undocumented
1989
- - `CollisionDebugState` (interface): interface CollisionDebugState — ⚠ undocumented
1990
- - `ProjectileDebugTrace` (interface): interface ProjectileDebugTrace — ⚠ undocumented
2027
+ - `CollisionDebugState` (interface): interface CollisionDebugState { layers: CollisionDebugLayers; aimProbe: AimProbeConfig | null; projectileTraces: readonly ProjectileDebugTrace[]; maxProjectileTraces: number; projectileTraceLifeMs: number } — ⚠ undocumented
2028
+ - `ProjectileDebugTrace` (interface): interface ProjectileDebugTrace { id: number; origin: EntityPosition; at: EntityPosition; hit: boolean; bornMs: number } — ⚠ undocumented
1991
2029
  - `collisionDebug` (const): const collisionDebug: CollisionDebugController — ⚠ undocumented
1992
2030
 
1993
2031
  ## @jgengine/shell/devtools/collisionDebugMath
@@ -1997,11 +2035,11 @@
1997
2035
  - `AIM_MISS_COLOR` (const): const AIM_MISS_COLOR: "#94a3b8" — ⚠ undocumented
1998
2036
  - `AIM_SOLID_COLOR` (const): const AIM_SOLID_COLOR: "#fbbf24" — ⚠ undocumented
1999
2037
  - `AimEndpointKind` (type): type AimEndpointKind = "damage" | "solid" | "miss" — ⚠ undocumented
2000
- - `AimLaserDebug` (interface): interface AimLaserDebug — ⚠ undocumented
2038
+ - `AimLaserDebug` (interface): interface AimLaserDebug { origin: EntityPosition; direction: EntityPosition; end: EntityPosition; maxDistance: number; kind: AimEndpointKind; nearest: SceneRaycastHit | null; firstImpact: SceneRaycastHit | null; queryCount: number } — ⚠ undocumented
2001
2039
  - `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
2040
+ - `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
2041
+ - `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
2042
+ - `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
2043
  - `HITBOX_WIRE_COLOR` (const): const HITBOX_WIRE_COLOR: "#f472b6" — ⚠ undocumented
2006
2044
  - `PROJECTILE_PATH_COLOR` (const): const PROJECTILE_PATH_COLOR: "#fde68a" — ⚠ undocumented
2007
2045
 
@@ -2019,12 +2057,16 @@
2019
2057
 
2020
2058
  - `diagnose` (function): function diagnose(frame: NonNullable<ReturnType<typeof devtools.frame.stats>>, longs: readonly LongFrameEvent[]): string | null — ⚠ undocumented
2021
2059
 
2060
+ ## @jgengine/shell/diagnostics/FpsCounter
2061
+
2062
+ - `FrameTally` (interface): interface FrameTally — Mutable frame tally shared between the in-Canvas probe and the DOM readout.
2063
+
2022
2064
  ## @jgengine/shell/diagnostics/RuntimeDiagnostics
2023
2065
 
2024
2066
  - `DiagnosticOverlay` (function): function DiagnosticOverlay({ diagnostics, gameName }: { diagnostics: RuntimeDiagnostic[]; gameName: string }): React.JSX.Element | null — ⚠ undocumented
2025
2067
  - `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
2068
  - `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
2069
+ - `RuntimeDiagnostic` (interface): interface RuntimeDiagnostic { id: number; phase: string; message: string; stack?: string; componentStack?: string; capturedAt: string } — ⚠ undocumented
2028
2070
  - `errorReportContext` (function): function errorReportContext(): string — Standalone error footer text (page, browser, engine) shared by every error surface.
2029
2071
  - `logRuntimeError` (function): function logRuntimeError(error: unknown, phase: string, componentStack?: string): Omit<RuntimeDiagnostic, "id"> — ⚠ undocumented
2030
2072
 
@@ -2041,32 +2083,32 @@
2041
2083
 
2042
2084
  - `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
2085
  - `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
2086
+ - `DaylightCycleConfig` (interface): interface DaylightCycleConfig { horizonColor?: string; zenithColor?: string; sunIntensity?: number; ambientIntensity?: number; sun?: SkySunConfig } — ⚠ undocumented
2087
+ - `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
2088
+ - `DaylightState` (interface): interface DaylightState { sunPosition: [number, number, number]; sunIntensity: number; ambientIntensity: number; skyTop: string; skyBottom: string; background: string } — ⚠ undocumented
2047
2089
  - `EnvironmentScene` (function): function EnvironmentScene({ feature }: EnvironmentSceneProps): React.JSX.Element — ⚠ undocumented
2048
- - `EnvironmentSceneProps` (interface): interface EnvironmentSceneProps — ⚠ undocumented
2090
+ - `EnvironmentSceneProps` (interface): interface EnvironmentSceneProps { feature: EnvironmentWorldFeature } — ⚠ undocumented
2049
2091
  - `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
2092
+ - `SkyDaylightProps` (interface): interface SkyDaylightProps { sky: SkyEnvironmentDescriptor; lights?: boolean; bands?: readonly BiomeBand[] } — ⚠ undocumented
2093
+ - `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
2094
  - `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
2095
+ - `TimeOfDayDaylightProps` (interface): interface TimeOfDayDaylightProps { sky: SkyEnvironmentDescriptor; clock?: {calendar(): {dayFraction: number}}; lights?: boolean; bands?: readonly BiomeBand[] } — ⚠ undocumented
2054
2096
  - `VolumetricCloudsProps` (interface): interface VolumetricCloudsProps — Props for {@link VolumetricClouds} — fully-resolved cloud rules plus the shared sun direction.
2055
2097
 
2056
2098
  ## @jgengine/shell/environment
2057
2099
 
2058
2100
  - `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
2101
  - `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
2102
+ - `DaylightCycleConfig` (interface): interface DaylightCycleConfig { horizonColor?: string; zenithColor?: string; sunIntensity?: number; ambientIntensity?: number; sun?: SkySunConfig } — ⚠ undocumented
2103
+ - `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
2104
+ - `DaylightState` (interface): interface DaylightState { sunPosition: [number, number, number]; sunIntensity: number; ambientIntensity: number; skyTop: string; skyBottom: string; background: string } — ⚠ undocumented
2063
2105
  - `EnvironmentScene` (function): function EnvironmentScene({ feature }: EnvironmentSceneProps): React.JSX.Element — ⚠ undocumented
2064
- - `EnvironmentSceneProps` (interface): interface EnvironmentSceneProps — ⚠ undocumented
2106
+ - `EnvironmentSceneProps` (interface): interface EnvironmentSceneProps { feature: EnvironmentWorldFeature } — ⚠ undocumented
2065
2107
  - `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
2108
+ - `SkyDaylightProps` (interface): interface SkyDaylightProps { sky: SkyEnvironmentDescriptor; lights?: boolean; bands?: readonly BiomeBand[] } — ⚠ undocumented
2109
+ - `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
2110
  - `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
2111
+ - `TimeOfDayDaylightProps` (interface): interface TimeOfDayDaylightProps { sky: SkyEnvironmentDescriptor; clock?: {calendar(): {dayFraction: number}}; lights?: boolean; bands?: readonly BiomeBand[] } — ⚠ undocumented
2070
2112
  - `VolumetricCloudsProps` (interface): interface VolumetricCloudsProps — Props for {@link VolumetricClouds} — fully-resolved cloud rules plus the shared sun direction.
2071
2113
 
2072
2114
  ## @jgengine/shell/environment/DayNightSky
@@ -2076,20 +2118,20 @@
2076
2118
 
2077
2119
  ## @jgengine/shell/environment/Daylight
2078
2120
 
2079
- - `DaylightProps` (interface): interface DaylightProps — ⚠ undocumented
2080
- - `SkyDaylightProps` (interface): interface SkyDaylightProps — ⚠ undocumented
2081
- - `SkyDomeProps` (interface): interface SkyDomeProps — ⚠ undocumented
2082
- - `TimeOfDayDaylightProps` (interface): interface TimeOfDayDaylightProps — ⚠ undocumented
2121
+ - `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
2122
+ - `SkyDaylightProps` (interface): interface SkyDaylightProps { sky: SkyEnvironmentDescriptor; lights?: boolean; bands?: readonly BiomeBand[] } — ⚠ undocumented
2123
+ - `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
2124
+ - `TimeOfDayDaylightProps` (interface): interface TimeOfDayDaylightProps { sky: SkyEnvironmentDescriptor; clock?: {calendar(): {dayFraction: number}}; lights?: boolean; bands?: readonly BiomeBand[] } — ⚠ undocumented
2083
2125
 
2084
2126
  ## @jgengine/shell/environment/EnvironmentScene
2085
2127
 
2086
2128
  - `EnvironmentScene` (function): function EnvironmentScene({ feature }: EnvironmentSceneProps): React.JSX.Element — ⚠ undocumented
2087
- - `EnvironmentSceneProps` (interface): interface EnvironmentSceneProps — ⚠ undocumented
2129
+ - `EnvironmentSceneProps` (interface): interface EnvironmentSceneProps { feature: EnvironmentWorldFeature } — ⚠ undocumented
2088
2130
 
2089
2131
  ## @jgengine/shell/environment/GroundPad
2090
2132
 
2091
2133
  - `GroundPad` (function): function GroundPad({ pad, field }: GroundPadProps): React.JSX.Element — ⚠ undocumented
2092
- - `GroundPadProps` (interface): interface GroundPadProps — ⚠ undocumented
2134
+ - `GroundPadProps` (interface): interface GroundPadProps { pad: PadEnvironmentDescriptor; field: TerrainField } — ⚠ undocumented
2093
2135
 
2094
2136
  ## @jgengine/shell/environment/RoadRibbons
2095
2137
 
@@ -2107,8 +2149,9 @@
2107
2149
  - `DEFAULT_DAY_SKY_BOTTOM` (const): const DEFAULT_DAY_SKY_BOTTOM: "#e3f4ff" — ⚠ undocumented
2108
2150
  - `DEFAULT_DAY_SKY_TOP` (const): const DEFAULT_DAY_SKY_TOP: "#3fa4f2" — ⚠ undocumented
2109
2151
  - `DEFAULT_DAY_SUN_INTENSITY` (const): const DEFAULT_DAY_SUN_INTENSITY: 1 — ⚠ undocumented
2110
- - `DaylightCycleConfig` (interface): interface DaylightCycleConfig — ⚠ undocumented
2111
- - `DaylightState` (interface): interface DaylightState — ⚠ undocumented
2152
+ - `DEFAULT_SUN_ELEVATION_DEG` (const): const DEFAULT_SUN_ELEVATION_DEG: number — The engine arc's noon height: `atan(1 / SUN_DEPTH_RATIO)`, ~68 degrees.
2153
+ - `DaylightCycleConfig` (interface): interface DaylightCycleConfig { horizonColor?: string; zenithColor?: string; sunIntensity?: number; ambientIntensity?: number; sun?: SkySunConfig } — ⚠ undocumented
2154
+ - `DaylightState` (interface): interface DaylightState { sunPosition: [number, number, number]; sunIntensity: number; ambientIntensity: number; skyTop: string; skyBottom: string; background: string } — ⚠ undocumented
2112
2155
  - `SKY_PRESET_DAY_FRACTION` (const): const SKY_PRESET_DAY_FRACTION: Record<"day" | "dusk" | "night", number> — ⚠ undocumented
2113
2156
 
2114
2157
  ## @jgengine/shell/environment/groundPadMath
@@ -2126,54 +2169,76 @@
2126
2169
  - `mergeGamepadFrame` (function): function mergeGamepadFrame(base: GamepadInputFrame, gamepad: GamepadFrame): GamepadInputFrame — Merge one resolved gamepad frame with another input source's semantic state.
2127
2170
  - `mergeGamepadInput` (function): function mergeGamepadInput(frames: readonly GamepadFrame[], base: GamepadInputFrame = { held: [], analog: {} }): GamepadInputFrame — Pure reducer used by the shell and synthetic gamepad tests.
2128
2171
 
2172
+ ## @jgengine/shell/input/gamepadPoll
2173
+
2174
+ - `GamepadPoll` (interface): interface GamepadPoll — Buffers one shell gamepad poll reuses across frames.
2175
+ - `GamepadRoute` (interface): interface GamepadRoute — Where each connected pad's input goes this frame; buffers are reused across frames.
2176
+ - `emptyGamepadPoll` (function): function emptyGamepadPoll(): GamepadPoll — Fresh buffers for {@link stepGamepadPoll}.
2177
+ - `emptyGamepadRoute` (function): function emptyGamepadRoute(): GamepadRoute — Fresh buffers for {@link routeGamepads}.
2178
+ - `gamepadCodes` (function): function gamepadCodes(bindings: ActionCodesMap): GamepadBindings — The pad-only slice of an action binding map, in the shape {@link resolveGamepadFrame} reads.
2179
+ - `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.
2180
+ - `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.
2181
+ - `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.
2182
+
2129
2183
  ## @jgengine/shell/input/gamepadSource
2130
2184
 
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.
2185
+ - `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
2186
  - `mergeGamepadFrame` (function): function mergeGamepadFrame(base: GamepadInputFrame, gamepad: GamepadFrame): GamepadInputFrame — Merge one resolved gamepad frame with another input source's semantic state.
2133
2187
  - `mergeGamepadInput` (function): function mergeGamepadInput(frames: readonly GamepadFrame[], base: GamepadInputFrame = { held: [], analog: {} }): GamepadInputFrame — Pure reducer used by the shell and synthetic gamepad tests.
2134
2188
 
2135
2189
  ## @jgengine/shell/input/mouseLook
2136
2190
 
2137
- - `MouseLookAim` (interface): interface MouseLookAim — ⚠ undocumented
2138
- - `MouseLookOptions` (interface): interface MouseLookOptions — ⚠ undocumented
2191
+ - `MouseLookAim` (interface): interface MouseLookAim { yaw: number; pitch: number } — ⚠ undocumented
2192
+ - `MouseLookOptions` (interface): interface MouseLookOptions { sensitivity?: number; maxPitch?: number; pointerLock?: boolean; initialYaw?: number; initialPitch?: number } — ⚠ undocumented
2139
2193
  - `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
2194
  - `createMouseLookTracker` (function): function createMouseLookTracker(element: HTMLElement, options: MouseLookOptions = {}): MouseLookTracker — ⚠ undocumented
2141
2195
 
2196
+ ## @jgengine/shell/input/padHaptics
2197
+
2198
+ - `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.
2199
+ - `PadHapticState` (interface): interface PadHapticState — Last command sent to one pad.
2200
+ - `emptyPadHapticState` (function): function emptyPadHapticState(): PadHapticState — Fresh state for {@link stepPadHaptics}.
2201
+ - `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.
2202
+
2203
+ ## @jgengine/shell/input/pointerLock
2204
+
2205
+ - `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`.
2206
+
2142
2207
  ## @jgengine/shell/inputSink
2143
2208
 
2144
2209
  - `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
2210
 
2146
2211
  ## @jgengine/shell/map
2147
2212
 
2148
- - `BakeTerrainMapOptions` (interface): interface BakeTerrainMapOptions — ⚠ undocumented
2149
- - `BakedMap` (interface): interface BakedMap — ⚠ undocumented
2150
- - `MapBakeBounds` (interface): interface MapBakeBounds — ⚠ undocumented
2213
+ - `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.
2214
+ - `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.
2215
+ - `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
2216
  - `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
2217
+ - `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
2218
  - `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
2219
 
2155
2220
  ## @jgengine/shell/map/MapMarkerBeacons
2156
2221
 
2157
2222
  - `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
2223
+ - `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
2224
 
2160
2225
  ## @jgengine/shell/map/terrainMap
2161
2226
 
2162
- - `BakeTerrainMapOptions` (interface): interface BakeTerrainMapOptions — ⚠ undocumented
2163
- - `BakedMap` (interface): interface BakedMap — ⚠ undocumented
2164
- - `MapBakeBounds` (interface): interface MapBakeBounds — ⚠ undocumented
2227
+ - `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.
2228
+ - `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.
2229
+ - `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
2230
  - `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
2231
 
2167
2232
  ## @jgengine/shell/materialOverride
2168
2233
 
2169
- - `MaterialOverrideOptions` (interface): interface MaterialOverrideOptions — ⚠ undocumented
2234
+ - `MaterialOverrideOptions` (interface): interface MaterialOverrideOptions { clone?: boolean; textures?: MaterialOverrideTextures } — ⚠ undocumented
2170
2235
  - `MaterialOverrideTextures` (interface): interface MaterialOverrideTextures — Loaded PBR textures for `applyMaterialOverride`'s `textures` option — matches `ModelMaterialMaps`' roles.
2171
2236
 
2172
2237
  ## @jgengine/shell/multiplayer
2173
2238
 
2174
2239
  - `DEFAULT_FEED_ACTIONS` (const): const DEFAULT_FEED_ACTIONS: string[] — ⚠ undocumented
2175
2240
  - `ResolveShellMultiplayerArgs` (type): type ResolveShellMultiplayerArgs = { game: GameDefinition; gameId: string; url?: string; userId?: string; force?: boolean; feedActions?: string[]; } — ⚠ undocumented
2176
- - `ShellMultiplayer` (type): type ShellMultiplayer = MultiplayerSession — ⚠ undocumented
2241
+ - `ShellMultiplayer` (type): type ShellMultiplayer = MultiplayerSession — ⚠ undocumented · used by `useShellMultiplayerSync` (@jgengine/shell/useShellMultiplayerSync): Joins the multiplayer server for the live context and wires presence, feed relay, and chat sync until teardown.
2177
2242
  - `randomPlayerId` (function): function randomPlayerId(): string — ⚠ undocumented
2178
2243
  - `resolvePeerShellMultiplayer` (function): function resolvePeerShellMultiplayer(args: { gameId: string; role: "host" | "join"; room?: string; userId?: string; feedActions?: string[]; }): Promise<ShellMultiplayer & { close: () => void }> — ⚠ undocumented
2179
2244
  - `resolveShellMultiplayer` (function): function resolveShellMultiplayer(args: ResolveShellMultiplayerArgs): ShellMultiplayer | null — ⚠ undocumented
@@ -2192,12 +2257,12 @@
2192
2257
  - `POINTER_ENTITY_KEY` (const): const POINTER_ENTITY_KEY: "jgEntityId" — ⚠ undocumented
2193
2258
  - `POINTER_OBJECT_KEY` (const): const POINTER_OBJECT_KEY: "jgObjectId" — ⚠ undocumented
2194
2259
  - `PointerHitFilter` (type): type PointerHitFilter = (object: THREE.Object3D) => boolean — ⚠ undocumented
2195
- - `PointerService` (interface): interface PointerService — ⚠ undocumented
2260
+ - `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
2261
  - `createPointerService` (function): function createPointerService(): PointerService — ⚠ undocumented
2197
2262
 
2198
2263
  ## @jgengine/shell/postfx/PostProcessing
2199
2264
 
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.
2265
+ - `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
2266
 
2202
2267
  ## @jgengine/shell/postfx/ScreenEffectsOverlay
2203
2268
 
@@ -2215,6 +2280,10 @@
2215
2280
  - `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
2281
  - `restorePostfxOverlays` (function): function restorePostfxOverlays(hidden: THREE.Object3D[]): void — Restore visibility for the objects hidden by {@link hidePostfxOverlays} and clear the list.
2217
2282
 
2283
+ ## @jgengine/shell/postfx/stylizePass
2284
+
2285
+ - `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.
2286
+
2218
2287
  ## @jgengine/shell/registry
2219
2288
 
2220
2289
  - `GameRegistry` (type): type GameRegistry = Record<string, () => Promise<PlayableGame>> — ⚠ undocumented
@@ -2251,6 +2320,19 @@
2251
2320
  - `IsolatedEntityModel` (function): function IsolatedEntityModel({ model, instanceId, measure, fallback, }: { model: ModelConfig; instanceId?: string; measure?: MeasureTarget; fallback?: ReactNode; }): React.JSX.Element — ⚠ undocumented
2252
2321
  - `MeasureTarget` (interface): interface MeasureTarget — Where a measured model reports its rendered bounds: an entity kind or an object catalog id.
2253
2322
 
2323
+ ## @jgengine/shell/render/SkinnedInstances
2324
+
2325
+ - `BakeSkinnedCrowdOptions` (interface): interface BakeSkinnedCrowdOptions — Options for {@link bakeSkinnedCrowd}.
2326
+ - `BakedSkinnedCrowd` (interface): interface BakedSkinnedCrowd — A rig baked for instancing: one merged geometry, the bone-matrix texture and its layout.
2327
+ - `CrowdInstance` (interface): interface CrowdInstance — One crowd member: where it stands and what it plays.
2328
+ - `CrowdUniforms` (interface): interface CrowdUniforms — Uniforms shared by every material of one crowd; `crowdTime` advances once per frame.
2329
+ - `SkinnedCrowdSource` (interface): interface SkinnedCrowdSource — A rig to bake: a loaded scene with skinned meshes and its clips.
2330
+ - `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.
2331
+ - `SkinnedInstancesProps` (interface): interface SkinnedInstancesProps — Props for {@link SkinnedInstances}.
2332
+ - `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.
2333
+ - `createCrowdUniforms` (function): function createCrowdUniforms(baked: BakedSkinnedCrowd): CrowdUniforms — Builds the uniforms a crowd material reads: the texture and per-clip start, frames, fps and loop.
2334
+ - `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.
2335
+
2254
2336
  ## @jgengine/shell/render/assetBase
2255
2337
 
2256
2338
  - `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.
@@ -2272,23 +2354,27 @@
2272
2354
 
2273
2355
  ## @jgengine/shell/render/modelLoad
2274
2356
 
2357
+ - `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.
2358
+ - `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
2359
  - `ModelLoaderConfig` (interface): interface ModelLoaderConfig — CDN-backed decoder configuration for compressed model and texture assets.
2276
2360
  - `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
2361
  - `detectKtx2Support` (function): function detectKtx2Support(renderer: THREE.WebGLRenderer): void — Detects GPU support for the configured KTX2 transcoder after a renderer exists.
2362
+ - `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
2363
  - `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
2364
  - `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`.
2365
+ - `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
2366
 
2281
2367
  ## @jgengine/shell/render/modelRender
2282
2368
 
2283
- - `MaterialCache` (interface): interface MaterialCache — ⚠ undocumented
2369
+ - `MaterialCache` (interface): interface MaterialCache { materials: THREE.MeshStandardMaterial[]; seedColor: THREE.Color } — ⚠ undocumented
2284
2370
  - `ModelShadowMode` (type): type ModelShadowMode = "cast" | "receive" | "both" | "none" — Shadow participation applied to every mesh of a cloned model; mirrors `ModelConfig.shadows`.
2285
2371
  - `PAINT_TEXTURE_SIZE` (const): const PAINT_TEXTURE_SIZE: 512 — ⚠ undocumented
2286
- - `PaintCanvas` (interface): interface PaintCanvas — ⚠ undocumented
2372
+ - `PaintCanvas` (interface): interface PaintCanvas { canvas: HTMLCanvasElement; context: CanvasRenderingContext2D; texture: THREE.CanvasTexture } — ⚠ undocumented
2287
2373
 
2288
2374
  ## @jgengine/shell/render/resolveModel
2289
2375
 
2290
2376
  - `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
2377
+ - `ModelResolveContext` (interface): interface ModelResolveContext { seam: "entityModels" | "objectModels" | "scatterModels"; key: string } — ⚠ undocumented
2292
2378
 
2293
2379
  ## @jgengine/shell/render/sceneCapture
2294
2380
 
@@ -2313,16 +2399,22 @@
2313
2399
 
2314
2400
  ## @jgengine/shell/render/useFootIk
2315
2401
 
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.
2402
+ - `FootIkRig` (interface): interface FootIkRig — Bones resolved once per loaded scene for {@link applyFootIk}.
2403
+ - `FootIkState` (interface): interface FootIkState — Per-model state carried between frames: the faded weight and the smoothed pelvis drop.
2404
+ - `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.
2405
+ - `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.
2406
+ - `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
2407
 
2319
2408
  ## @jgengine/shell/render/useModelAnimation
2320
2409
 
2410
+ - `GraphPose` (interface): interface GraphPose — A mixer set up to show {@link AnimGraph} output on a rig; see {@link createGraphPose}.
2411
+ - `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.
2412
+ - `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.
2321
2413
  - `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`.
2322
2414
 
2323
2415
  ## @jgengine/shell/replay/useSessionRecorder
2324
2416
 
2325
- - `RecordedPose` (interface): interface RecordedPose — ⚠ undocumented
2417
+ - `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
2418
  - `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
2419
 
2328
2420
  ## @jgengine/shell/scatter
@@ -2347,6 +2439,7 @@
2347
2439
  - `AuthoredPathsProps` (interface): interface AuthoredPathsProps — Props for {@link AuthoredPaths}: the document, the ground field to drape over, and a kind filter.
2348
2440
  - `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.
2349
2441
  - `AuthoredSceneProps` (interface): interface AuthoredSceneProps — Props for {@link AuthoredScene}: the document to render and the ground field to drape/ground on.
2442
+ - `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
2443
 
2351
2444
  ## @jgengine/shell/scene/AuthoredScene
2352
2445
 
@@ -2356,6 +2449,7 @@
2356
2449
  - `AuthoredPathsProps` (interface): interface AuthoredPathsProps — Props for {@link AuthoredPaths}: the document, the ground field to drape over, and a kind filter.
2357
2450
  - `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.
2358
2451
  - `AuthoredSceneProps` (interface): interface AuthoredSceneProps — Props for {@link AuthoredScene}: the document to render and the ground field to drape/ground on.
2452
+ - `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
2453
 
2360
2454
  ## @jgengine/shell/scene/GeneratedAssetRenderer
2361
2455
 
@@ -2388,13 +2482,14 @@
2388
2482
  ## @jgengine/shell/settings/SettingsRuntime
2389
2483
 
2390
2484
  - `SettingsRuntime` (function): function SettingsRuntime({ variant, surface, actions, children, ...input }: SettingsRuntimeProps): React.JSX.Element — ⚠ undocumented
2391
- - `SettingsRuntimeProps` (interface): interface SettingsRuntimeProps extends SettingsControllerInput — ⚠ undocumented
2485
+ - `SettingsRuntimeProps` (interface): interface SettingsRuntimeProps extends SettingsControllerInput { variant: SettingsVariant; surface: SettingsSurface | false; actions: readonly SettingsActionView[]; children: ReactNode } — ⚠ undocumented
2392
2486
 
2393
2487
  ## @jgengine/shell/settings/appliedSettings
2394
2488
 
2489
+ - `AppliedGraphicsSettings` (interface): interface AppliedGraphicsSettings — The player's graphics choices as the shell applies them.
2395
2490
  - `AudioSettingsBridge` (function): function AudioSettingsBridge({ store, engine, buses, }: { store: SettingsStore; engine: AudioEngine; buses: Record<string, AudioBusDef> | undefined; }): null — ⚠ undocumented
2396
2491
  - `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
2492
+ - `useGraphicsSettings` (function): function useGraphicsSettings(store: SettingsStore, shadowsDefault: boolean, overrides?: GraphicsProfileOverrides): AppliedGraphicsSettings — ⚠ undocumented
2398
2493
  - `useSettingsRevision` (function): function useSettingsRevision(store: SettingsStore): number — ⚠ undocumented
2399
2494
 
2400
2495
  ## @jgengine/shell/settings/settingsController
@@ -2402,7 +2497,7 @@
2402
2497
  - `SettingsActionView` (interface): interface SettingsActionView — A resolved game-state action — `run` is already bound to the game context and closes the menu.
2403
2498
  - `SettingsCategoryView` (interface): interface SettingsCategoryView — A settings menu category with its rows and keybinds, ready to render.
2404
2499
  - `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
2500
+ - `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
2501
  - `SettingsKeybindRow` (interface): interface SettingsKeybindRow — One rebindable action row rendered in the controls settings category.
2407
2502
  - `SettingsRow` (interface): interface SettingsRow — One editable setting rendered in a settings menu category.
2408
2503
  - `useSettingsCategories` (function): function useSettingsCategories(config: SettingsControllerInput): SettingsCategoryView[] — ⚠ undocumented
@@ -2420,14 +2515,14 @@
2420
2515
  ## @jgengine/shell/structures
2421
2516
 
2422
2517
  - `BuildingBlock` (function): function BuildingBlock({ part, palette }: BuildingBlockProps): React.JSX.Element — ⚠ undocumented
2423
- - `BuildingBlockProps` (interface): interface BuildingBlockProps — ⚠ undocumented
2518
+ - `BuildingBlockProps` (interface): interface BuildingBlockProps { part: BuildingPartPlacement; palette?: BuildingMaterialPalette } — ⚠ undocumented
2424
2519
  - `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
2520
+ - `GeneratedBuildingProps` (interface): interface GeneratedBuildingProps { building: GeneratedBuildingData; palette?: BuildingMaterialPalette; partRenderer?: BuildingKitRenderer; kit?: BuildingKit; resolveModelUrl?: (model: string) => string; visibleKinds?: readonly BuildingPartKind[] } — ⚠ undocumented
2521
+ - `InstancedBuildingPlacement` (interface): interface InstancedBuildingPlacement { building: GeneratedBuildingData; position?: readonly [number, number, number]; rotationY?: number; pivot?: readonly [number, number] } — ⚠ undocumented
2427
2522
  - `InstancedBuildings` (function): function InstancedBuildings({ buildings, palette, kit, resolveModelUrl, visibleKinds, }: InstancedBuildingsProps): React.JSX.Element | null — ⚠ undocumented
2428
- - `InstancedBuildingsProps` (interface): interface InstancedBuildingsProps — ⚠ undocumented
2523
+ - `InstancedBuildingsProps` (interface): interface InstancedBuildingsProps { buildings: readonly InstancedBuildingPlacement[]; palette?: BuildingMaterialPalette; kit?: BuildingKit; resolveModelUrl?: (model: string) => string; visibleKinds?: readonly BuildingPartKind[] } — ⚠ undocumented
2429
2524
  - `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
2525
+ - `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
2526
  - `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
2527
  - `TransformGizmoMode` (type): type TransformGizmoMode = "translate" | "rotate" | "scale" — Active TransformControls mode for the runtime selection gizmo.
2433
2528
  - `TransformGizmoPose` (interface): interface TransformGizmoPose — Pose reported by {@link TransformGizmo} when the user releases a drag.
@@ -2437,14 +2532,14 @@
2437
2532
  ## @jgengine/shell/structures
2438
2533
 
2439
2534
  - `BuildingBlock` (function): function BuildingBlock({ part, palette }: BuildingBlockProps): React.JSX.Element — ⚠ undocumented
2440
- - `BuildingBlockProps` (interface): interface BuildingBlockProps — ⚠ undocumented
2535
+ - `BuildingBlockProps` (interface): interface BuildingBlockProps { part: BuildingPartPlacement; palette?: BuildingMaterialPalette } — ⚠ undocumented
2441
2536
  - `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
2537
+ - `GeneratedBuildingProps` (interface): interface GeneratedBuildingProps { building: GeneratedBuildingData; palette?: BuildingMaterialPalette; partRenderer?: BuildingKitRenderer; kit?: BuildingKit; resolveModelUrl?: (model: string) => string; visibleKinds?: readonly BuildingPartKind[] } — ⚠ undocumented
2538
+ - `InstancedBuildingPlacement` (interface): interface InstancedBuildingPlacement { building: GeneratedBuildingData; position?: readonly [number, number, number]; rotationY?: number; pivot?: readonly [number, number] } — ⚠ undocumented
2444
2539
  - `InstancedBuildings` (function): function InstancedBuildings({ buildings, palette, kit, resolveModelUrl, visibleKinds, }: InstancedBuildingsProps): React.JSX.Element | null — ⚠ undocumented
2445
- - `InstancedBuildingsProps` (interface): interface InstancedBuildingsProps — ⚠ undocumented
2540
+ - `InstancedBuildingsProps` (interface): interface InstancedBuildingsProps { buildings: readonly InstancedBuildingPlacement[]; palette?: BuildingMaterialPalette; kit?: BuildingKit; resolveModelUrl?: (model: string) => string; visibleKinds?: readonly BuildingPartKind[] } — ⚠ undocumented
2446
2541
  - `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
2542
+ - `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
2543
  - `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
2544
  - `TransformGizmoMode` (type): type TransformGizmoMode = "translate" | "rotate" | "scale" — Active TransformControls mode for the runtime selection gizmo.
2450
2545
  - `TransformGizmoPose` (interface): interface TransformGizmoPose — Pose reported by {@link TransformGizmo} when the user releases a drag.
@@ -2454,23 +2549,23 @@
2454
2549
  ## @jgengine/shell/structures/GeneratedBuilding
2455
2550
 
2456
2551
  - `BuildingBlock` (function): function BuildingBlock({ part, palette }: BuildingBlockProps): React.JSX.Element — ⚠ undocumented
2457
- - `BuildingBlockProps` (interface): interface BuildingBlockProps — ⚠ undocumented
2552
+ - `BuildingBlockProps` (interface): interface BuildingBlockProps { part: BuildingPartPlacement; palette?: BuildingMaterialPalette } — ⚠ undocumented
2458
2553
  - `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
2554
+ - `BuildingKitRenderer` (interface): interface BuildingKitRenderer { renderPart?: (part: BuildingPartPlacement) => ReactNode | undefined } — ⚠ undocumented
2555
+ - `BuildingMaterialPalette` (type): type BuildingMaterialPalette = Partial<Record<BuildingPartKind, BuildingSurface>> — Per part kind: a hex colour, or a colour plus tiled PBR maps (`BuildingSurfaceMaterial`).
2556
+ - `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.
2557
+ - `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
2558
  - `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
2559
+ - `GeneratedBuildingData` (interface): interface GeneratedBuildingData { id: string; parts: readonly BuildingPartPlacement[] } — ⚠ undocumented
2560
+ - `GeneratedBuildingProps` (interface): interface GeneratedBuildingProps { building: GeneratedBuildingData; palette?: BuildingMaterialPalette; partRenderer?: BuildingKitRenderer; kit?: BuildingKit; resolveModelUrl?: (model: string) => string; visibleKinds?: readonly BuildingPartKind[] } — ⚠ undocumented
2561
+ - `InstancedBuildingPlacement` (interface): interface InstancedBuildingPlacement { building: GeneratedBuildingData; position?: readonly [number, number, number]; rotationY?: number; pivot?: readonly [number, number] } — ⚠ undocumented
2467
2562
  - `InstancedBuildings` (function): function InstancedBuildings({ buildings, palette, kit, resolveModelUrl, visibleKinds, }: InstancedBuildingsProps): React.JSX.Element | null — ⚠ undocumented
2468
- - `InstancedBuildingsProps` (interface): interface InstancedBuildingsProps — ⚠ undocumented
2563
+ - `InstancedBuildingsProps` (interface): interface InstancedBuildingsProps { buildings: readonly InstancedBuildingPlacement[]; palette?: BuildingMaterialPalette; kit?: BuildingKit; resolveModelUrl?: (model: string) => string; visibleKinds?: readonly BuildingPartKind[] } — ⚠ undocumented
2469
2564
 
2470
2565
  ## @jgengine/shell/structures/PlacementGhost
2471
2566
 
2472
2567
  - `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
2568
+ - `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
2569
 
2475
2570
  ## @jgengine/shell/structures/TransformGizmo
2476
2571
 
@@ -2489,91 +2584,97 @@
2489
2584
  - `listBuildingKits` (function): function listBuildingKits(): string[] — Every registered kit id — the editor's kit picker and diagnostics read this.
2490
2585
  - `registerBuildingKit` (function): function registerBuildingKit(kit: BuildingKit): void — Registers a kit under its own `id`, replacing any kit already registered under that name.
2491
2586
 
2587
+ ## @jgengine/shell/structures/buildingSurface
2588
+
2589
+ - `TexturedBuildingSurface` (type): type TexturedBuildingSurface = BuildingSurfaceMaterial & { maps: BuildingSurfaceMaps } — A surface that names at least one map URL.
2590
+ - `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.
2591
+ - `surfaceKey` (function): function surfaceKey(surface: BuildingSurfaceMaterial | undefined): string — A stable key for bucketing instances that share one surface (same colour, maps, tiling, and response).
2592
+
2492
2593
  ## @jgengine/shell/terrain
2493
2594
 
2494
2595
  - `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
2596
+ - `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
2597
  - `DEFAULT_GRASS_WIND` (const): const DEFAULT_GRASS_WIND: Required<GrassWindOptions> — ⚠ undocumented
2497
2598
  - `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
2599
+ - `EditableGroundProps` (interface): interface EditableGroundProps { terrain: Pick<EditableTerrain, "sampleHeight" | "surfaceAt">; bounds: Aabb; segments?: number; version?: number; baseColor?: string; surfaceColors?: Record<string, string> } — ⚠ undocumented
2600
+ - `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
2601
  - `FractalNoiseConfig` (interface): interface FractalNoiseConfig — Octave settings for {@link fractalNoise}: frequency, layering, and optional ridged shaping.
2501
- - `GrassBladeGeometryOptions` (interface): interface GrassBladeGeometryOptions — ⚠ undocumented
2602
+ - `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
2502
2603
  - `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
2604
+ - `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
2605
+ - `GrassMaterialHandle` (interface): interface GrassMaterialHandle { material: THREE.MeshStandardMaterial; uniforms: GrassShaderUniforms } — ⚠ undocumented
2606
+ - `GrassMaterialOptions` (interface): interface GrassMaterialOptions { colorBase?: THREE.ColorRepresentation; colorTip?: THREE.ColorRepresentation; colorGround?: THREE.ColorRepresentation; colorVariation?: number; wind?: GrassWindOptions | false; distanceFade?: GrassDistanceFadeOptions | false; normalLift?: … — ⚠ undocumented
2506
2607
  - `GrassRange` (type): type GrassRange = number | readonly [min: number, max: number] — ⚠ undocumented
2507
- - `GrassShaderUniforms` (interface): interface GrassShaderUniforms — ⚠ undocumented
2508
- - `GrassWindOptions` (interface): interface GrassWindOptions — ⚠ undocumented
2608
+ - `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
2609
+ - `GrassWindOptions` (interface): interface GrassWindOptions { direction?: readonly [x: number, z: number]; strength?: number; speed?: number; gustScale?: number; flutter?: number; layered?: boolean } — ⚠ undocumented
2509
2610
  - `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
2611
  - `HeightfieldDisplaceOptions` (interface): interface HeightfieldDisplaceOptions — Options for {@link displaceHeightfieldGeometry}.
2511
2612
  - `HeightfieldRect` (interface): interface HeightfieldRect — XZ rectangle in world units — matches the shape of a terraform snapshot's `bounds`.
2512
2613
  - `NoiseFieldConfig` (interface): interface NoiseFieldConfig — Configuration for {@link noiseField}: seed, amplitude, and fractal noise shaping.
2513
2614
  - `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
2615
+ - `ProceduralGroundProps` (interface): interface ProceduralGroundProps extends Omit<ThreeElements["mesh"], "args" | "children" | "geometry" | "material"> { terrain?: ProceduralTerrainConfig; colors?: TerrainVertexColorOptions; roughness?: number; metalness?: number } — ⚠ undocumented
2616
+ - `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
2617
+ - `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
2618
+ - `ResolvedTerrainSegments` (interface): interface ResolvedTerrainSegments { x: number; z: number } — ⚠ undocumented
2619
+ - `ResolvedTerrainSize` (interface): interface ResolvedTerrainSize { width: number; depth: number } — ⚠ undocumented
2519
2620
  - `TerraformBrushCursor` (function): function TerraformBrushCursor({ center, y = 0.05, radius, mode }: TerraformBrushCursorProps): React.JSX.Element | null — ⚠ undocumented
2520
- - `TerraformBrushCursorProps` (interface): interface TerraformBrushCursorProps — ⚠ undocumented
2621
+ - `TerraformBrushCursorProps` (interface): interface TerraformBrushCursorProps { center: readonly [number, number] | null; y?: number; radius: number; mode: TerraformMode } — ⚠ undocumented
2521
2622
  - `TerrainArea` (type): type TerrainArea = number | readonly [width: number, depth: number] — ⚠ undocumented
2522
2623
  - `TerrainField` (interface): interface TerrainField — A sampleable ground surface: height and normal at any x/z, with optional bounds and water level.
2523
2624
  - `TerrainHeightSampler` (type): type TerrainHeightSampler = (x: number, z: number) => number — ⚠ undocumented
2524
2625
  - `TerrainNormal` (type): type TerrainNormal = readonly [number, number, number] — A surface normal vector at a terrain sample point.
2525
2626
  - `TerrainSeed` (type): type TerrainSeed = number | string — ⚠ undocumented
2526
2627
  - `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
2628
+ - `TerrainVertexColorOptions` (interface): interface TerrainVertexColorOptions { low?: THREE.ColorRepresentation; high?: THREE.ColorRepresentation; waterline?: THREE.ColorRepresentation; waterlineHeight?: number } — ⚠ undocumented
2528
2629
  - `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
2630
 
2530
2631
  ## @jgengine/shell/terrain
2531
2632
 
2532
2633
  - `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
2634
+ - `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
2635
  - `DEFAULT_GRASS_WIND` (const): const DEFAULT_GRASS_WIND: Required<GrassWindOptions> — ⚠ undocumented
2535
2636
  - `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
2637
+ - `EditableGroundProps` (interface): interface EditableGroundProps { terrain: Pick<EditableTerrain, "sampleHeight" | "surfaceAt">; bounds: Aabb; segments?: number; version?: number; baseColor?: string; surfaceColors?: Record<string, string> } — ⚠ undocumented
2638
+ - `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
2639
  - `FractalNoiseConfig` (interface): interface FractalNoiseConfig — Octave settings for {@link fractalNoise}: frequency, layering, and optional ridged shaping.
2539
- - `GrassBladeGeometryOptions` (interface): interface GrassBladeGeometryOptions — ⚠ undocumented
2640
+ - `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
2540
2641
  - `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
2642
+ - `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
2643
+ - `GrassMaterialHandle` (interface): interface GrassMaterialHandle { material: THREE.MeshStandardMaterial; uniforms: GrassShaderUniforms } — ⚠ undocumented
2644
+ - `GrassMaterialOptions` (interface): interface GrassMaterialOptions { colorBase?: THREE.ColorRepresentation; colorTip?: THREE.ColorRepresentation; colorGround?: THREE.ColorRepresentation; colorVariation?: number; wind?: GrassWindOptions | false; distanceFade?: GrassDistanceFadeOptions | false; normalLift?: … — ⚠ undocumented
2544
2645
  - `GrassRange` (type): type GrassRange = number | readonly [min: number, max: number] — ⚠ undocumented
2545
- - `GrassShaderUniforms` (interface): interface GrassShaderUniforms — ⚠ undocumented
2546
- - `GrassWindOptions` (interface): interface GrassWindOptions — ⚠ undocumented
2646
+ - `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
2647
+ - `GrassWindOptions` (interface): interface GrassWindOptions { direction?: readonly [x: number, z: number]; strength?: number; speed?: number; gustScale?: number; flutter?: number; layered?: boolean } — ⚠ undocumented
2547
2648
  - `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
2649
  - `HeightfieldDisplaceOptions` (interface): interface HeightfieldDisplaceOptions — Options for {@link displaceHeightfieldGeometry}.
2549
2650
  - `HeightfieldRect` (interface): interface HeightfieldRect — XZ rectangle in world units — matches the shape of a terraform snapshot's `bounds`.
2550
2651
  - `NoiseFieldConfig` (interface): interface NoiseFieldConfig — Configuration for {@link noiseField}: seed, amplitude, and fractal noise shaping.
2551
2652
  - `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
2653
+ - `ProceduralGroundProps` (interface): interface ProceduralGroundProps extends Omit<ThreeElements["mesh"], "args" | "children" | "geometry" | "material"> { terrain?: ProceduralTerrainConfig; colors?: TerrainVertexColorOptions; roughness?: number; metalness?: number } — ⚠ undocumented
2654
+ - `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
2655
+ - `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
2656
+ - `ResolvedTerrainSegments` (interface): interface ResolvedTerrainSegments { x: number; z: number } — ⚠ undocumented
2657
+ - `ResolvedTerrainSize` (interface): interface ResolvedTerrainSize { width: number; depth: number } — ⚠ undocumented
2557
2658
  - `TerraformBrushCursor` (function): function TerraformBrushCursor({ center, y = 0.05, radius, mode }: TerraformBrushCursorProps): React.JSX.Element | null — ⚠ undocumented
2558
- - `TerraformBrushCursorProps` (interface): interface TerraformBrushCursorProps — ⚠ undocumented
2659
+ - `TerraformBrushCursorProps` (interface): interface TerraformBrushCursorProps { center: readonly [number, number] | null; y?: number; radius: number; mode: TerraformMode } — ⚠ undocumented
2559
2660
  - `TerrainArea` (type): type TerrainArea = number | readonly [width: number, depth: number] — ⚠ undocumented
2560
2661
  - `TerrainField` (interface): interface TerrainField — A sampleable ground surface: height and normal at any x/z, with optional bounds and water level.
2561
2662
  - `TerrainHeightSampler` (type): type TerrainHeightSampler = (x: number, z: number) => number — ⚠ undocumented
2562
2663
  - `TerrainNormal` (type): type TerrainNormal = readonly [number, number, number] — A surface normal vector at a terrain sample point.
2563
2664
  - `TerrainSeed` (type): type TerrainSeed = number | string — ⚠ undocumented
2564
2665
  - `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
2666
+ - `TerrainVertexColorOptions` (interface): interface TerrainVertexColorOptions { low?: THREE.ColorRepresentation; high?: THREE.ColorRepresentation; waterline?: THREE.ColorRepresentation; waterlineHeight?: number } — ⚠ undocumented
2566
2667
  - `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
2668
 
2568
2669
  ## @jgengine/shell/terrain/CarvedTerrain
2569
2670
 
2570
2671
  - `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
2672
+ - `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
2673
 
2573
2674
  ## @jgengine/shell/terrain/EditableGround
2574
2675
 
2575
2676
  - `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
2677
+ - `EditableGroundProps` (interface): interface EditableGroundProps { terrain: Pick<EditableTerrain, "sampleHeight" | "surfaceAt">; bounds: Aabb; segments?: number; version?: number; baseColor?: string; surfaceColors?: Record<string, string> } — ⚠ undocumented
2577
2678
 
2578
2679
  ## @jgengine/shell/terrain/GrassField
2579
2680
 
@@ -2581,17 +2682,17 @@
2581
2682
  - `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
2683
  - `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
2684
  - `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
2685
+ - `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
2686
 
2586
2687
  ## @jgengine/shell/terrain/ProceduralGround
2587
2688
 
2588
2689
  - `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
2690
+ - `ProceduralGroundProps` (interface): interface ProceduralGroundProps extends Omit<ThreeElements["mesh"], "args" | "children" | "geometry" | "material"> { terrain?: ProceduralTerrainConfig; colors?: TerrainVertexColorOptions; roughness?: number; metalness?: number } — ⚠ undocumented
2590
2691
 
2591
2692
  ## @jgengine/shell/terrain/TerraformBrushCursor
2592
2693
 
2593
2694
  - `TerraformBrushCursor` (function): function TerraformBrushCursor({ center, y = 0.05, radius, mode }: TerraformBrushCursorProps): React.JSX.Element | null — ⚠ undocumented
2594
- - `TerraformBrushCursorProps` (interface): interface TerraformBrushCursorProps — ⚠ undocumented
2695
+ - `TerraformBrushCursorProps` (interface): interface TerraformBrushCursorProps { center: readonly [number, number] | null; y?: number; radius: number; mode: TerraformMode } — ⚠ undocumented
2595
2696
 
2596
2697
  ## @jgengine/shell/terrain/grassBudget
2597
2698
 
@@ -2601,20 +2702,20 @@
2601
2702
  ## @jgengine/shell/terrain/grassGeometry
2602
2703
 
2603
2704
  - `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
2705
+ - `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
2706
  - `GrassExclusion` (interface): interface GrassExclusion — A rectangle (patch-local XZ) the grass bake keeps clear — soil patches, water, plazas.
2606
2707
  - `GrassRange` (type): type GrassRange = number | readonly [min: number, max: number] — ⚠ undocumented
2607
- - `ResolvedGrassBladeGeometryOptions` (interface): interface ResolvedGrassBladeGeometryOptions — ⚠ undocumented
2708
+ - `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
2709
 
2609
2710
  ## @jgengine/shell/terrain/grassMaterial
2610
2711
 
2611
2712
  - `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
2713
  - `DEFAULT_GRASS_WIND` (const): const DEFAULT_GRASS_WIND: Required<GrassWindOptions> — ⚠ undocumented
2613
2714
  - `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
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
2717
+ - `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
2718
+ - `GrassWindOptions` (interface): interface GrassWindOptions { direction?: readonly [x: number, z: number]; strength?: number; speed?: number; gustScale?: number; flutter?: number; layered?: boolean } — ⚠ undocumented
2618
2719
 
2619
2720
  ## @jgengine/shell/terrain/heightfieldGeometry
2620
2721
 
@@ -2638,15 +2739,15 @@
2638
2739
 
2639
2740
  ## @jgengine/shell/terrain/terrainMath
2640
2741
 
2641
- - `FieldGroundOptions` (interface): interface FieldGroundOptions — ⚠ undocumented
2642
- - `ProceduralTerrainConfig` (interface): interface ProceduralTerrainConfig — ⚠ undocumented
2643
- - `ResolvedTerrainSegments` (interface): interface ResolvedTerrainSegments — ⚠ undocumented
2644
- - `ResolvedTerrainSize` (interface): interface ResolvedTerrainSize — ⚠ undocumented
2742
+ - `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
2743
+ - `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
2744
+ - `ResolvedTerrainSegments` (interface): interface ResolvedTerrainSegments { x: number; z: number } — ⚠ undocumented
2745
+ - `ResolvedTerrainSize` (interface): interface ResolvedTerrainSize { width: number; depth: number } — ⚠ undocumented
2645
2746
  - `TerrainArea` (type): type TerrainArea = number | readonly [width: number, depth: number] — ⚠ undocumented
2646
2747
  - `TerrainHeightSampler` (type): type TerrainHeightSampler = (x: number, z: number) => number — ⚠ undocumented
2647
2748
  - `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
2749
  - `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
2750
+ - `TerrainVertexColorOptions` (interface): interface TerrainVertexColorOptions { low?: THREE.ColorRepresentation; high?: THREE.ColorRepresentation; waterline?: THREE.ColorRepresentation; waterlineHeight?: number } — ⚠ undocumented
2650
2751
 
2651
2752
  ## @jgengine/shell/touch/OrientationHint
2652
2753
 
@@ -2654,7 +2755,7 @@
2654
2755
 
2655
2756
  ## @jgengine/shell/touch/TouchControlsOverlay
2656
2757
 
2657
- - `TouchCodeSink` (interface): interface TouchCodeSink — ⚠ undocumented
2758
+ - `TouchCodeSink` (interface): interface TouchCodeSink { onCodeDown(code: string): void; onCodeUp(code: string): void; onAnalog?(values: Readonly<Record<string, number>> | null): void } — ⚠ undocumented
2658
2759
  - `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
2760
  - `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
2761
  - `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.
@@ -2682,7 +2783,7 @@
2682
2783
 
2683
2784
  ## @jgengine/shell/vision/FrustumSensorHud
2684
2785
 
2685
- - `FrustumSensorProbeOptions` (interface): interface FrustumSensorProbeOptions extends FramingConfig — ⚠ undocumented
2786
+ - `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
2787
  - `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
2788
  - `frustumSampleDisplayEqual` (function): function frustumSampleDisplayEqual(a: FrustumSample | null, b: FrustumSample | null): boolean — ⚠ undocumented
2688
2789
  - `useFrustumSensor` (function): function useFrustumSensor(options: FrustumSensorProbeOptions): FrustumSample | null — ⚠ undocumented
@@ -2690,16 +2791,16 @@
2690
2791
  ## @jgengine/shell/vision/HiddenStateProbeHud
2691
2792
 
2692
2793
  - `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
2794
+ - `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
2795
  - `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
2796
 
2696
2797
  ## @jgengine/shell/vision/RevealVision
2697
2798
 
2698
2799
  - `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
2800
+ - `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
2801
  - `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
2802
+ - `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).
2803
+ - `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
2804
  - `useRevealHits` (function): function useRevealHits(options: RevealVisionOptions): readonly RevealHit[] — Occlusion-ignoring tagged-entity radius query (#115), bound to the live scene.
2704
2805
 
2705
2806
  ## @jgengine/shell/vision/frustumSampleEqual
@@ -2713,20 +2814,20 @@
2713
2814
  - `MAX_OCEAN_WAVES` (const): const MAX_OCEAN_WAVES: 6 — ⚠ undocumented
2714
2815
  - `OCEAN_QUALITY_PRESETS` (const): const OCEAN_QUALITY_PRESETS: Record<OceanQualityPreset, { size: number; resolution: number }> — ⚠ undocumented
2715
2816
  - `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
2817
+ - `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
2818
+ - `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
2819
+ - `OceanDirectionVector` (interface): interface OceanDirectionVector { x: number; z: number } — ⚠ undocumented
2820
+ - `OceanFoamConfig` (interface): interface OceanFoamConfig { crestThreshold?: number; softness?: number; intensity?: number; coverage?: number; shoreWidth?: number } — ⚠ undocumented
2821
+ - `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
2822
+ - `OceanProps` (interface): interface OceanProps extends Omit<ThreeElements["mesh"], "args" | "children" | "geometry" | "material"> { config?: OceanConfig; depthAt?: (x: number, z: number) => number } — ⚠ undocumented
2722
2823
  - `OceanQualityPreset` (type): type OceanQualityPreset = "low" | "medium" | "high" | "ultra" — ⚠ undocumented
2723
2824
  - `OceanShaderMaterial` (type): type OceanShaderMaterial = THREE.ShaderMaterial & { uniforms: OceanMaterialUniforms } — ⚠ undocumented
2724
- - `OceanWaveConfig` (interface): interface OceanWaveConfig — ⚠ undocumented
2825
+ - `OceanWaveConfig` (interface): interface OceanWaveConfig { amplitude?: number; wavelength?: number; speed?: number; direction?: OceanWaveDirection; steepness?: number } — ⚠ undocumented
2725
2826
  - `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
2827
+ - `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
2828
+ - `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
2829
+ - `ResolvedOceanFoamConfig` (interface): interface ResolvedOceanFoamConfig { crestThreshold: number; softness: number; intensity: number; coverage: number; shoreWidth: number } — ⚠ undocumented
2830
+ - `ResolvedOceanWaveConfig` (interface): interface ResolvedOceanWaveConfig { amplitude: number; wavelength: number; speed: number; direction: OceanDirectionVector; steepness: number } — ⚠ undocumented
2730
2831
  - `createOceanMaterial` (function): function createOceanMaterial(config: ResolvedOceanConfig): OceanShaderMaterial — ⚠ undocumented
2731
2832
  - `syncOceanMaterial` (function): function syncOceanMaterial(material: OceanShaderMaterial, config: ResolvedOceanConfig, elapsedSeconds: number): void — ⚠ undocumented
2732
2833
 
@@ -2737,27 +2838,27 @@
2737
2838
  - `MAX_OCEAN_WAVES` (const): const MAX_OCEAN_WAVES: 6 — ⚠ undocumented
2738
2839
  - `OCEAN_QUALITY_PRESETS` (const): const OCEAN_QUALITY_PRESETS: Record<OceanQualityPreset, { size: number; resolution: number }> — ⚠ undocumented
2739
2840
  - `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
2841
+ - `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
2842
+ - `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
2843
+ - `OceanDirectionVector` (interface): interface OceanDirectionVector { x: number; z: number } — ⚠ undocumented
2844
+ - `OceanFoamConfig` (interface): interface OceanFoamConfig { crestThreshold?: number; softness?: number; intensity?: number; coverage?: number; shoreWidth?: number } — ⚠ undocumented
2845
+ - `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
2846
+ - `OceanProps` (interface): interface OceanProps extends Omit<ThreeElements["mesh"], "args" | "children" | "geometry" | "material"> { config?: OceanConfig; depthAt?: (x: number, z: number) => number } — ⚠ undocumented
2746
2847
  - `OceanQualityPreset` (type): type OceanQualityPreset = "low" | "medium" | "high" | "ultra" — ⚠ undocumented
2747
2848
  - `OceanShaderMaterial` (type): type OceanShaderMaterial = THREE.ShaderMaterial & { uniforms: OceanMaterialUniforms } — ⚠ undocumented
2748
- - `OceanWaveConfig` (interface): interface OceanWaveConfig — ⚠ undocumented
2849
+ - `OceanWaveConfig` (interface): interface OceanWaveConfig { amplitude?: number; wavelength?: number; speed?: number; direction?: OceanWaveDirection; steepness?: number } — ⚠ undocumented
2749
2850
  - `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
2851
+ - `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
2852
+ - `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
2853
+ - `ResolvedOceanFoamConfig` (interface): interface ResolvedOceanFoamConfig { crestThreshold: number; softness: number; intensity: number; coverage: number; shoreWidth: number } — ⚠ undocumented
2854
+ - `ResolvedOceanWaveConfig` (interface): interface ResolvedOceanWaveConfig { amplitude: number; wavelength: number; speed: number; direction: OceanDirectionVector; steepness: number } — ⚠ undocumented
2754
2855
  - `createOceanMaterial` (function): function createOceanMaterial(config: ResolvedOceanConfig): OceanShaderMaterial — ⚠ undocumented
2755
2856
  - `syncOceanMaterial` (function): function syncOceanMaterial(material: OceanShaderMaterial, config: ResolvedOceanConfig, elapsedSeconds: number): void — ⚠ undocumented
2756
2857
 
2757
2858
  ## @jgengine/shell/water/Ocean
2758
2859
 
2759
2860
  - `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
2861
+ - `OceanProps` (interface): interface OceanProps extends Omit<ThreeElements["mesh"], "args" | "children" | "geometry" | "material"> { config?: OceanConfig; depthAt?: (x: number, z: number) => number } — ⚠ undocumented
2761
2862
 
2762
2863
  ## @jgengine/shell/water/OceanConfig
2763
2864
 
@@ -2765,21 +2866,21 @@
2765
2866
  - `DEFAULT_OCEAN_WAVE_SCALE` (const): const DEFAULT_OCEAN_WAVE_SCALE: 18 — Shared with `@jgengine/core/world/water` — primary wavelength in world units.
2766
2867
  - `MAX_OCEAN_WAVES` (const): const MAX_OCEAN_WAVES: 6 — ⚠ undocumented
2767
2868
  - `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
2869
+ - `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
2870
+ - `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
2871
+ - `OceanDirectionVector` (interface): interface OceanDirectionVector { x: number; z: number } — ⚠ undocumented
2872
+ - `OceanFoamConfig` (interface): interface OceanFoamConfig { crestThreshold?: number; softness?: number; intensity?: number; coverage?: number; shoreWidth?: number } — ⚠ undocumented
2772
2873
  - `OceanQualityPreset` (type): type OceanQualityPreset = "low" | "medium" | "high" | "ultra" — ⚠ undocumented
2773
- - `OceanWaveConfig` (interface): interface OceanWaveConfig — ⚠ undocumented
2874
+ - `OceanWaveConfig` (interface): interface OceanWaveConfig { amplitude?: number; wavelength?: number; speed?: number; direction?: OceanWaveDirection; steepness?: number } — ⚠ undocumented
2774
2875
  - `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
2876
+ - `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
2877
+ - `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
2878
+ - `ResolvedOceanFoamConfig` (interface): interface ResolvedOceanFoamConfig { crestThreshold: number; softness: number; intensity: number; coverage: number; shoreWidth: number } — ⚠ undocumented
2879
+ - `ResolvedOceanWaveConfig` (interface): interface ResolvedOceanWaveConfig { amplitude: number; wavelength: number; speed: number; direction: OceanDirectionVector; steepness: number } — ⚠ undocumented
2779
2880
 
2780
2881
  ## @jgengine/shell/water/OceanMaterial
2781
2882
 
2782
- - `OceanMaterialUniforms` (interface): interface OceanMaterialUniforms — ⚠ undocumented
2883
+ - `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
2884
  - `OceanShaderMaterial` (type): type OceanShaderMaterial = THREE.ShaderMaterial & { uniforms: OceanMaterialUniforms } — ⚠ undocumented
2784
2885
  - `createOceanMaterial` (function): function createOceanMaterial(config: ResolvedOceanConfig): OceanShaderMaterial — ⚠ undocumented
2785
2886
  - `syncOceanMaterial` (function): function syncOceanMaterial(material: OceanShaderMaterial, config: ResolvedOceanConfig, elapsedSeconds: number): void — ⚠ undocumented
@@ -2794,18 +2895,18 @@
2794
2895
  - `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.
2795
2896
  - `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
2897
  - `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
2898
+ - `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 } — ⚠ undocumented
2798
2899
  - `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
2900
+ - `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
2800
2901
  - `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
2902
+ - `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
2802
2903
  - `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
2904
+ - `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
2804
2905
  - `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
2805
2906
  - `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
2907
+ - `WeatherLayerProps` (interface): interface WeatherLayerProps { mode?: WeatherLayerMode; intensity?: number; wind?: WeatherVector; lightning?: number; timeScale?: number; rain?: Omit<RainFieldProps, "wind" | "lightning" | "timeScale"> | false; snow?: Omit<SnowFieldProps, "wind" | "timeScale"> | false;… — ⚠ undocumented
2908
+ - `WeatherUniformOptions` (interface): interface WeatherUniformOptions { wind?: WeatherVector; lightning?: number; timeScale?: number } — ⚠ undocumented
2909
+ - `WeatherUniformSet` (interface): interface WeatherUniformSet { time: THREE.IUniform<number>; wind: THREE.IUniform<THREE.Vector3>; lightning: THREE.IUniform<number> } — ⚠ undocumented
2809
2910
  - `WeatherVector` (type): type WeatherVector = readonly [number, number, number] — ⚠ undocumented
2810
2911
 
2811
2912
  ## @jgengine/shell/weather/DustField
@@ -2816,28 +2917,28 @@
2816
2917
  ## @jgengine/shell/weather/FireSpreadLayer
2817
2918
 
2818
2919
  - `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
2920
+ - `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 } — ⚠ undocumented
2820
2921
 
2821
2922
  ## @jgengine/shell/weather/LightningStrike
2822
2923
 
2823
2924
  - `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
2925
+ - `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
2926
 
2826
2927
  ## @jgengine/shell/weather/RainField
2827
2928
 
2828
2929
  - `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
2930
+ - `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
2830
2931
 
2831
2932
  ## @jgengine/shell/weather/SnowField
2832
2933
 
2833
2934
  - `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
2935
+ - `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
2936
 
2836
2937
  ## @jgengine/shell/weather/WeatherLayer
2837
2938
 
2838
2939
  - `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
2839
2940
  - `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
2941
+ - `WeatherLayerProps` (interface): interface WeatherLayerProps { mode?: WeatherLayerMode; intensity?: number; wind?: WeatherVector; lightning?: number; timeScale?: number; rain?: Omit<RainFieldProps, "wind" | "lightning" | "timeScale"> | false; snow?: Omit<SnowFieldProps, "wind" | "timeScale"> | false;… — ⚠ undocumented
2841
2942
 
2842
2943
  ## @jgengine/shell/weather/weatherMath
2843
2944
 
@@ -2847,12 +2948,12 @@
2847
2948
  - `DEFAULT_RAIN_DENSITY` (const): const DEFAULT_RAIN_DENSITY: 0.45 — ⚠ undocumented
2848
2949
  - `DEFAULT_SNOW_COUNT` (const): const DEFAULT_SNOW_COUNT: 1500 — ⚠ undocumented
2849
2950
  - `DEFAULT_SNOW_DENSITY` (const): const DEFAULT_SNOW_DENSITY: 0.5 — ⚠ undocumented
2850
- - `WeatherSeedAttributes` (interface): interface WeatherSeedAttributes — ⚠ undocumented
2951
+ - `WeatherSeedAttributes` (interface): interface WeatherSeedAttributes { spawn: Float32Array; drift: Float32Array } — ⚠ undocumented
2851
2952
 
2852
2953
  ## @jgengine/shell/weather/weatherUniforms
2853
2954
 
2854
- - `WeatherUniformOptions` (interface): interface WeatherUniformOptions — ⚠ undocumented
2855
- - `WeatherUniformSet` (interface): interface WeatherUniformSet — ⚠ undocumented
2955
+ - `WeatherUniformOptions` (interface): interface WeatherUniformOptions { wind?: WeatherVector; lightning?: number; timeScale?: number } — ⚠ undocumented
2956
+ - `WeatherUniformSet` (interface): interface WeatherUniformSet { time: THREE.IUniform<number>; wind: THREE.IUniform<THREE.Vector3>; lightning: THREE.IUniform<number> } — ⚠ undocumented
2856
2957
  - `WeatherVector` (type): type WeatherVector = readonly [number, number, number] — ⚠ undocumented
2857
2958
 
2858
2959
  ## @jgengine/shell/world/DataObjects
@@ -2863,23 +2964,23 @@
2863
2964
  ## @jgengine/shell/world/GridWorldScene
2864
2965
 
2865
2966
  - `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
2967
+ - `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
2968
 
2868
2969
  ## @jgengine/shell/world/InstancedBodies
2869
2970
 
2870
2971
  - `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
2972
+ - `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
2973
 
2873
2974
  ## @jgengine/shell/world/InstancedJoints
2874
2975
 
2875
2976
  - `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
2977
+ - `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
2978
 
2878
2979
  ## @jgengine/shell/world/SpriteBatch
2879
2980
 
2880
2981
  - `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
2982
+ - `SpriteBatchInstance` (interface): interface SpriteBatchInstance { x: number; y: number; z?: number; frame?: number; scale?: number; rotation?: number } — ⚠ undocumented
2983
+ - `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
2984
 
2884
2985
  ## @jgengine/shell/world/TileLayerRenderer
2885
2986
 
@@ -2899,7 +3000,7 @@
2899
3000
  - `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.
2900
3001
  - `ProjectileTracers` (function): function ProjectileTracers({ lifeMs = 130 }: { lifeMs?: number }): React.JSX.Element — ⚠ undocumented
2901
3002
  - `Reticle` (function): function Reticle({ className }: { className?: string }): React.JSX.Element — ⚠ undocumented
2902
- - `WorldBarSample` (interface): interface WorldBarSample — ⚠ undocumented
3003
+ - `WorldBarSample` (interface): interface WorldBarSample { x: number; y: number; percent: number } — ⚠ undocumented
2903
3004
  - `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
2904
3005
  - `WorldFloatText` (function): function WorldFloatText({ height = 1.9, lifeMs = 950 }: { height?: number; lifeMs?: number }): React.JSX.Element — ⚠ undocumented
2905
3006
  - `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.
@@ -2928,13 +3029,13 @@
2928
3029
 
2929
3030
  ## @jgengine/shell/world/entityPose
2930
3031
 
2931
- - `PoseSource` (interface): interface PoseSource — ⚠ undocumented
2932
- - `PoseWritable` (interface): interface PoseWritable — ⚠ undocumented
3032
+ - `PoseSource` (interface): interface PoseSource { position: readonly [number, number, number]; rotationY: number } — ⚠ undocumented
3033
+ - `PoseWritable` (interface): interface PoseWritable { position: {set(x: number, y: number, z: number): void}; rotation: {y: number} } — ⚠ undocumented
2933
3034
 
2934
3035
  ## @jgengine/shell/world/floatTextStyle
2935
3036
 
2936
- - `FloatTextInfo` (interface): interface FloatTextInfo — ⚠ undocumented
2937
- - `FloatTextStyle` (interface): interface FloatTextStyle — ⚠ undocumented
3037
+ - `FloatTextInfo` (interface): interface FloatTextInfo { kind: string; hitType?: string; element?: string; crit?: boolean; scale?: number } — ⚠ undocumented
3038
+ - `FloatTextStyle` (interface): interface FloatTextStyle { color: string; fontSizePx: number; fontWeight: number; glow: string } — ⚠ undocumented
2938
3039
 
2939
3040
  ## @jgengine/shell/world/pingPulse
2940
3041
 
@@ -2950,8 +3051,8 @@
2950
3051
  ## @jgengine/shell/world/worldBarSamples
2951
3052
 
2952
3053
  - `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
3054
+ - `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
3055
+ - `WorldBarSample` (interface): interface WorldBarSample { x: number; y: number; percent: number } — ⚠ undocumented
2955
3056
 
2956
3057
  ## @jgengine/shell/worldSync
2957
3058