@jgengine/react 0.26.0 → 0.28.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.
@@ -8,6 +8,14 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
8
8
 
9
9
  - `AccessibilityProvider` (function) · `import { AccessibilityProvider } from "@jgengine/react"`
10
10
 
11
+ ## accessibility-settings — read accessibility preferences and the hold-vs-toggle choice from the shared settings store
12
+
13
+ - `readAccessibilitySettings` (function) · `import { readAccessibilitySettings } from "@jgengine/core/settings/accessibilitySettings"`
14
+
15
+ ## accessibility-settings-rows — accessibility setting rows (text scale, colorblind, reduced motion, high contrast, captions, hold-vs-toggle) backed by the shared settings store
16
+
17
+ - `accessibilitySettingRows` (function) · `import { accessibilitySettingRows } from "@jgengine/core/settings/accessibilitySettings"`
18
+
11
19
  ## accessibility-store — serializable, observable accessibility preferences (reduced motion, high contrast, text scale, colorblind mode, captions) a game binds to its settings and presentation
12
20
 
13
21
  - `createAccessibilityStore` (function) · `import { createAccessibilityStore } from "@jgengine/core/ui"`
@@ -44,6 +52,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
44
52
 
45
53
  - `ActionTooltip` (function) · `import { ActionTooltip } from "@jgengine/react"`
46
54
 
55
+ ## animation — turn a killed rigged character into a physics ragdoll on death
56
+
57
+ - `useRagdoll` (function) · `import { useRagdoll } from "@jgengine/shell/render/useRagdoll"`
58
+
47
59
  ## animation-motion-params — read resolved player movement into reusable animation graph parameters
48
60
 
49
61
  - `readModelAnimationParams` (function) · `import { readModelAnimationParams } from "@jgengine/shell/render/useModelAnimation"`
@@ -85,6 +97,11 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
85
97
 
86
98
  - `observeBrowserSuspension` (function) · `import { observeBrowserSuspension } from "@jgengine/react"`
87
99
 
100
+ ## camera-clearance — keep any camera out of world collision with a spring-arm pull-in
101
+
102
+ - `clearCameraBoom` (function) · `import { clearCameraBoom } from "@jgengine/shell/camera"`
103
+ - `sceneCameraQuery` (function) · `import { sceneCameraQuery } from "@jgengine/shell/camera"`
104
+
88
105
  ## camera-shake-meter — HUD readout for a core camera-shake controller — a trauma meter plus current impact-kind label, reskinnable and kind-labelled
89
106
 
90
107
  - `CameraShakeMeter` (function) · `import { CameraShakeMeter } from "@jgengine/react"`
@@ -97,6 +114,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
97
114
 
98
115
  - `captureCanvas` (function) · `import { captureCanvas } from "@jgengine/shell/render/sceneCapture"`
99
116
 
117
+ ## character-crowd — draw distant animated characters on the GPU instead of posing their skeletons
118
+
119
+ - `CharacterCrowd` (function) · `import { CharacterCrowd } from "@jgengine/shell/render/CharacterCrowd"`
120
+
100
121
  ## character-paperdoll — equip-slot paperdoll — flanking columns around a portrait, activatable slots
101
122
 
102
123
  - `Paperdoll` (function) · `import { Paperdoll } from "@jgengine/react"`
@@ -154,6 +175,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
154
175
 
155
176
  - `ControlsList` (function) · `import { ControlsList } from "@jgengine/react"`
156
177
 
178
+ ## credits-panel — scrollable, navigable credits screen with a back affordance
179
+
180
+ - `CreditsPanel` (function) · `import { CreditsPanel } from "@jgengine/react"`
181
+
157
182
  ## credits-screen — drop-in credits view over a CreditsDocument — headed sections, optional links, HudTheme-skinned
158
183
 
159
184
  - `CreditsScreen` (function) · `import { CreditsScreen } from "@jgengine/react/creditsScreen"`
@@ -282,6 +307,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
282
307
 
283
308
  - `displaceHeightfieldGeometry` (function) · `import { displaceHeightfieldGeometry } from "@jgengine/shell/terrain"`
284
309
 
310
+ ## hold-vs-toggle — resolve one action frame under a hold-or-toggle preference
311
+
312
+ - `holdToggleActive` (function) · `import { holdToggleActive } from "@jgengine/core/settings/accessibilitySettings"`
313
+
285
314
  ## hud-button — themed button with hover lift, press scale, focus ring, disabled state, and an interaction cue seam
286
315
 
287
316
  - `HudButton` (const) · `import { HudButton } from "@jgengine/react/hudButton"`
@@ -354,7 +383,7 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
354
383
 
355
384
  - `deriveHudTheme` (function) · `import { deriveHudTheme } from "@jgengine/react"`
356
385
 
357
- ## hud-theme-root — apply the default (or a game) HudTheme and the engine stylesheet at the HUD root
386
+ ## hud-theme-root — apply the default (or a game) HudTheme, the player's accessibility preferences and the engine stylesheet at the HUD root
358
387
 
359
388
  - `HudThemeRoot` (function) · `import { HudThemeRoot } from "@jgengine/react/hudThemeRoot"`
360
389
 
@@ -411,6 +440,14 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
411
440
 
412
441
  - `useLiveMarkers` (function) · `import { useLiveMarkers } from "@jgengine/react"`
413
442
 
443
+ ## loading-screen — loading block with progress, rotating tips, a ready gate, and a scene backdrop
444
+
445
+ - `LoadingScreen` (function) · `import { LoadingScreen } from "@jgengine/react"`
446
+
447
+ ## loading-tips — deterministic loading-screen tip rotation with a seeded shuffle and snapshot/restore
448
+
449
+ - `createTipRotation` (function) · `import { createTipRotation } from "@jgengine/core/ui/loadingTips"`
450
+
414
451
  ## map-legend — marker-kind key (glyph + color + label per kind) sharing the map's kindStyles
415
452
 
416
453
  - `MapLegend` (function) · `import { MapLegend } from "@jgengine/react"`
@@ -450,14 +487,38 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
450
487
 
451
488
  - `useTriplanarAssetMaterial` (function) · `import { useTriplanarAssetMaterial } from "@jgengine/shell/render/triplanarMaterial"`
452
489
 
490
+ ## menu-key-intent — resolve a keyboard code to a menu intent (move / confirm / back)
491
+
492
+ - `menuIntentForKey` (function) · `import { menuIntentForKey } from "@jgengine/core/ui"`
493
+
494
+ ## menu-nav-move — move menu focus by direction over a list or grid, skipping disabled items
495
+
496
+ - `menuNavMove` (function) · `import { menuNavMove } from "@jgengine/core/ui"`
497
+
498
+ ## menu-nav-repeat — gate held menu navigation into an initial press plus repeats
499
+
500
+ - `shouldRepeatMenuNav` (function) · `import { shouldRepeatMenuNav } from "@jgengine/core/ui"`
501
+
502
+ ## menu-pad-intent — resolve a standard gamepad button to a menu intent
503
+
504
+ - `menuIntentForPadButton` (function) · `import { menuIntentForPadButton } from "@jgengine/core/ui"`
505
+
453
506
  ## menu-router — screen stack for a game front-end — push/back/replace/reset with optional Escape-to-back
454
507
 
455
508
  - `useMenuRouter` (function) · `import { useMenuRouter } from "@jgengine/react/menuRouter"`
456
509
 
510
+ ## menu-screen — animated enter/exit menu screen over a live scene, with translucent scrim, backdrop slot, reduced-motion support and exit unmount
511
+
512
+ - `MenuScreen` (function) · `import { MenuScreen } from "@jgengine/react"`
513
+
457
514
  ## menu-stack-reducer — pure push/back/replace/reset transitions for a menu screen stack
458
515
 
459
516
  - `menuStackReducer` (function) · `import { menuStackReducer } from "@jgengine/react/menuRouter"`
460
517
 
518
+ ## menu-transition — enter/exit phase timing for an animated menu screen, with reduced-motion collapse and snapshot/restore
519
+
520
+ - `createMenuTransition` (function) · `import { createMenuTransition } from "@jgengine/core/ui"`
521
+
461
522
  ## merge-post-processing — layer a partial post chain over a look preset without losing its stages
462
523
 
463
524
  - `mergePostProcessing` (function) · `import { mergePostProcessing } from "@jgengine/core/render/lookPreset"`
@@ -508,6 +569,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
508
569
 
509
570
  - `modelLoadStarts` (function) · `import { modelLoadStarts } from "@jgengine/shell/render/modelLoad"`
510
571
 
572
+ ## model-wind — sway a foliage model in the shared gust field
573
+
574
+ - `applyModelWind` (function) · `import { applyModelWind } from "@jgengine/shell/render/modelWind"`
575
+
511
576
  ## multiplayer-p2p — bootstrap a playable authoritative world for browser peer co-op
512
577
 
513
578
  - `resolvePeerShellMultiplayer` (function) · `import { resolvePeerShellMultiplayer } from "@jgengine/shell/multiplayer"`
@@ -689,6 +754,22 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
689
754
 
690
755
  - `useServerSession` (function) · `import { useServerSession } from "@jgengine/react/useServerSession"`
691
756
 
757
+ ## settings-keybind-row — one rebindable controls row — key glyph, armed state and reset
758
+
759
+ - `SettingsKeybindControl` (function) · `import { SettingsKeybindControl } from "@jgengine/react/settingsBlocks"`
760
+
761
+ ## settings-section — settings category block — rows plus click-to-capture keybind rows, HudTheme-styled, store-backed
762
+
763
+ - `SettingsSection` (function) · `import { SettingsSection } from "@jgengine/react/settingsBlocks"`
764
+
765
+ ## settings-slider — a settings slider row that mirrors locally and persists on release
766
+
767
+ - `SettingsSlider` (function) · `import { SettingsSlider } from "@jgengine/react/settingsBlocks"`
768
+
769
+ ## settings-tabs — scrollable settings category tabs with pressed state
770
+
771
+ - `SettingsTabs` (function) · `import { SettingsTabs } from "@jgengine/react/settingsBlocks"`
772
+
692
773
  ## shop-grid-host — drop-in vendor/shop grid over a caller-owned wallet — item cards with icon/price/stock, afford-aware Buy, optional Sell, and a balance readout, token-themed
693
774
 
694
775
  - `ShopGrid` (function) · `import { ShopGrid } from "@jgengine/react"`
@@ -718,6 +799,11 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
718
799
 
719
800
  - `StaticShapeInstances` (function) · `import { StaticShapeInstances } from "@jgengine/shell/render/staticShapeInstances"`
720
801
 
802
+ ## static-transforms — static-transforms
803
+
804
+ - `freezeStaticTransforms` (function) · `import { freezeStaticTransforms } from "@jgengine/shell/render/staticTransforms"`
805
+ - `thawStaticTransforms` (function) · `import { thawStaticTransforms } from "@jgengine/shell/render/staticTransforms"`
806
+
721
807
  ## status-effect-bar — timeline HUD row of active statuses — icon + radial countdown ring + stack badge, driven off the status model, HudTheme-skinned
722
808
 
723
809
  - `StatusEffectBar` (function) · `import { StatusEffectBar } from "@jgengine/react"`
@@ -750,6 +836,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
750
836
 
751
837
  - `TimerRing` (function) · `import { TimerRing } from "@jgengine/react"`
752
838
 
839
+ ## title-menu — title-screen block — wordmark, tagline, navigable verb list, footer, backdrop slot, animated over a live scene
840
+
841
+ - `TitleMenu` (function) · `import { TitleMenu } from "@jgengine/react"`
842
+
753
843
  ## trans — inline translated-message component bound to the active locale
754
844
 
755
845
  - `Trans` (function) · `import { Trans } from "@jgengine/react"`
@@ -770,6 +860,14 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
770
860
 
771
861
  - `useUiCue` (function) · `import { useUiCue } from "@jgengine/react/uiCues"`
772
862
 
863
+ ## use-accessibility-rows — accessibility setting rows for a game-owned settings layout
864
+
865
+ - `useAccessibilityRows` (function) · `import { useAccessibilityRows } from "@jgengine/react/accessibilitySettings"`
866
+
867
+ ## use-accessibility-settings — live accessibility preferences from the shared settings store
868
+
869
+ - `useAccessibilitySettings` (function) · `import { useAccessibilitySettings } from "@jgengine/react/accessibilitySettings"`
870
+
773
871
  ## use-camera-shake — React hook binding a camera-shake controller — subscribes for live trauma/kind and can self-drive its decay on a frame loop
774
872
 
775
873
  - `useCameraShake` (function) · `import { useCameraShake } from "@jgengine/react"`
@@ -798,10 +896,18 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
798
896
 
799
897
  - `useFastTravel` (function) · `import { useFastTravel } from "@jgengine/react"`
800
898
 
899
+ ## use-hold-vs-toggle — live hold-or-toggle preference from the shared settings store
900
+
901
+ - `useHoldVsToggle` (function) · `import { useHoldVsToggle } from "@jgengine/react/accessibilitySettings"`
902
+
801
903
  ## use-interaction-prompt — React hook binding a prompt registry to a moving player — resolves the nearest interactable prompt and returns the active one
802
904
 
803
905
  - `useInteractionPrompt` (function) · `import { useInteractionPrompt } from "@jgengine/react"`
804
906
 
907
+ ## use-menu-nav — keyboard and gamepad focus navigation for a menu — roving focus order, confirm/back, disabled-item skipping, no focus trap
908
+
909
+ - `useMenuNav` (function) · `import { useMenuNav } from "@jgengine/react"`
910
+
805
911
  ## use-modal-stack — React hook binding a modal stack to a component — re-renders on push/pop/resolve
806
912
 
807
913
  - `useModalStack` (function) · `import { useModalStack } from "@jgengine/react"`
@@ -96,6 +96,8 @@ AO and depth-of-field prepasses reuse the beauty pass's shadow maps; they do not
96
96
 
97
97
  **Orbit camera occlusion:** the third-person orbit rig takes `camera: { collision: { enabled, padding?, minTargetDistance? } }` — a spring-arm that raycasts target→camera each frame and pulls the boom in past walls/terrain so the camera never clips inside geometry. Off by default (unchanged chase feel); enable it for any world with interiors or dense structures.
98
98
 
99
+ **Camera clearance:** `camera: { firstPerson: { collision } }` keeps the `first` rig's eye out of geometry the body can reach. It is on by default: the boom anchors `armBack` (1.5) behind the eye along the look ray, `clearance` defaults to the near plane plus 0.15, and a blocking surface pulls the camera back toward that anchor so the near plane stays clear. `false` restores the unchecked camera. A game that drives its own camera calls the same seam: `clearCameraBoom({ pivot, desired, query: sceneCameraQuery(ctx.scene.raycast), previousArm, deltaSeconds })` returns the clear `position` and the `arm` to pass back next frame. The query reads `ctx.scene.raycast`, so world solids, scene-object colliders and walls all block it without a new collision layer; entities and terrain stay out so the followed body and the ground cannot pull the boom.
100
+
99
101
  Lighting is `defineGame({ lighting })` (`LightingConfig`): ambient / hemisphere / directional, replacing the shell's default lights when set. A `DirectionalLightingConfig` with `castShadow` takes `shadowMapSize`, `shadowCameraSize`, `shadowBias`, `shadowNormalBias` and `shadowRadius` (PCF penumbra in texels, default 1.25; the shell uses `PCFShadowMap` — in three r182 `PCFSoftShadowMap` silently compiles as unfiltered basic shadows). Every look but `"flat"` adds image-based light from a procedural sky probe (sky/horizon/ground from `hemisphere`, an HDR sun lobe from the first directional light, horizon leaning toward the ambient colour, intensity 0.45); `environmentSource` swaps in an HDRI or cube map. Under that IBL, `ambient` intensity is scaled by `iblAmbientScale` (default 0.5; set 1 to keep it), and lighting with no `directional` entry gets a soft shadowless key tinted by the ambient colour unless `fallbackKey: false` (`balanceLightingForIbl` in `@jgengine/core/render/lightingBalance`). Sky-lit worlds (a `sky()` world feature) get a high-res sun whose shadow camera follows the view each frame, so grounded shadows stay sharp under the player anywhere in a large world.
100
102
 
101
103
  ## First-person viewmodel, WorldOverlay props, render cues (#542)
@@ -243,10 +243,14 @@ Focus must look authored while remaining accessible. Menus should support keyboa
243
243
 
244
244
  Compose it from the game's own markup over the headless `StartScreen` (`@jgengine/react`, `start-screen` capability) — a full-bleed `data-jg-menu` overlay that centers your content and offers an opt-in settings corner; it imposes no look. Menu rows are the game's own buttons and focus handling restyled into its chrome; the shipped pieces to compose in are `ControlsList` (`@jgengine/react`) for the control legend and `KeyHint`/`Keycap` for key badges (`KeyHint` already renders nothing on a coarse pointer) — never shipped as raw scaffold.
245
245
 
246
+ **The engine ships the front-end machinery; the game owns its look.** `MenuScreen` (`@jgengine/react/menuScreen`) is one animated screen over the live scene: transform/opacity enter and exit that unmount when the exit finishes, a translucent scrim (`"none"` keeps the 3D scene fully visible), and a `backdrop` slot for a poster or preview. `TitleMenu` (`@jgengine/react/titleMenu`) is the title shape — wordmark, eyebrow, tagline, verb list, footer, control hint — where the game supplies the wording, verbs and art and skins it with `--jg-*` tokens. Both read `useMenuNav` (`@jgengine/react/menuNav`): arrow keys/WASD/Home/End, a standard gamepad's d-pad and left stick (with hold-to-repeat), Enter/Space/A confirm, Escape/Backspace/B/Start back. Tab is never intercepted and typing in a field is never swallowed, so no menu traps focus; keys still work if the canvas kept focus. A game that composes its own markup gets the same behavior from `useMenuNav` alone.
247
+
246
248
  **The front-end entries, when the game has them.** A menu is more than one Start button. Offer the real front-end verbs a player expects, gated to what the game actually supports:
247
249
 
248
250
  - **New Game** — begin a fresh run. When a save exists, keep it distinct from Continue rather than silently overwriting.
249
251
  - **Continue / Load** — resume the most recent save, and (when the game keeps more than one) a **Load** entry that lists save slots. Save slots are `createSaveStore` instances keyed by slot id; read them reactively with `useSaveStore` (`@jgengine/react`, offline `localSaveBackend` or cloud `remoteSaveBackend` — same hook, different backend). Disable Continue/Load with a real disabled state when there is nothing to resume; do not hide the verb entirely.
252
+
253
+ **Wire the list to the save document, not hand-typed metadata.** `SaveSlotMenu` renders a `createSaveSlots` index; `refreshSaveSlots(gameSaveSlotSource(save), index, { nameOf, metaOf })` (`@jgengine/core/game/saveSlotIndex`) fills it from the slots `createGameSave` actually wrote — name, saved time and a playtime chip from the document — and clears slots whose document was deleted. `save.peek(slot)` reads a document without applying it, so opening the menu never disturbs the live run. A game name can come from its own profile section via `nameOf`; extra chips (`chapter`, `location`) come from `metaOf`, which the index passes through untouched.
250
254
  - **Create Character / Choose Class** — when the game has characters, classes, loadouts, or profiles, the create/select flow lives here as an authored screen, not a stray dropdown. Keep the character/roster model serializable and game-owned (persist it through the game's save store); the menu is its presentation. Do not reach for a genre "class picker" preset — compose the choices this game actually offers.
251
255
  - **Load Character / Roster** — choose an existing profile or party when the game persists more than one.
252
256
  - **Settings** — reachable from the main menu, always (see Settings menu below). This is one of the two canonical places settings live; pause is the other.
@@ -275,6 +279,10 @@ HUD-only games (`presentation: "hud"` or a camera with rig `"none"`) use the sam
275
279
 
276
280
  **Present it any way you want.** `useSettings()` (`@jgengine/react`) returns the live controller — `{ categories, actions, variant, surface, isOpen, open, close, setOpen }` — so a game can drive its own pause-menu button, or render `categories`/`actions` (rows carry `value`/`set`/bounds, keybinds carry `rebind`/`reset`) entirely inside its own HUD. `useHasSettings()` gates a custom entry; `useSetting(id, fallback)` reads/writes one value. Set a slider's `min`/`max` explicitly — an omitted range collapses the thumb to 0/1.
277
281
 
282
+ **Composing your own settings layout.** `SettingsSection` (`@jgengine/react/settingsBlocks`) renders one controller category — its rows, then its click-to-capture keybind rows — and `SettingsTabs` renders the category rail (pass `tabProps` from `useMenuNav` so a gamepad can switch categories). `SettingsRowControl`, `SettingsSlider`, `SettingsToggle`, `SettingsSelect` and `SettingsKeybindControl` are the individual rows if the layout is not a list. All of them read `--jg-*` tokens and write through the controller, so the game's rebinds land in the same keybinding store as the stock menu. The stock menu uses these same blocks, so a game-composed screen and the engine dialog cannot drift.
283
+
284
+ **Accessibility is a shipped category.** The settings menu includes `accessibility` — text size, colourblind palette, reduce motion, high contrast, captions and hold-or-toggle — unless the game hides it. Text size multiplies every `--jg-type-*` token through `--jg-text-scale`, reduce motion zeroes `--jg-motion-scale`, and high contrast/colourblind/captions set `data-*` hooks on the HUD root; read them with `useAccessibilitySettings()` and `useHoldVsToggle()` for a custom layout. Apply the colourblind matrix (`ColorblindFilters`, `@jgengine/react/accessibility`) around the surface that should be daltonized — the shell sets the hook but does not wrap the 3D canvas.
285
+
278
286
  ## 8. Motion and game feel
279
287
 
280
288
  Add purposeful motion for: