@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.
- package/CHANGELOG.md +174 -0
- package/dist/accessibilitySettings.d.ts +25 -0
- package/dist/accessibilitySettings.js +44 -0
- package/dist/creditsPanel.d.ts +29 -0
- package/dist/creditsPanel.js +35 -0
- package/dist/hudTheme.js +2 -1
- package/dist/hudThemeRoot.d.ts +9 -2
- package/dist/hudThemeRoot.js +11 -4
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/loadingScreen.d.ts +42 -0
- package/dist/loadingScreen.js +108 -0
- package/dist/menuNav.d.ts +63 -0
- package/dist/menuNav.js +257 -0
- package/dist/menuScreen.d.ts +43 -0
- package/dist/menuScreen.js +98 -0
- package/dist/modals.js +13 -1
- package/dist/settingsBlocks.d.ts +85 -0
- package/dist/settingsBlocks.js +204 -0
- package/dist/titleMenu.d.ts +60 -0
- package/dist/titleMenu.js +66 -0
- package/package.json +2 -2
- package/skills/jgengine/api.md +2 -2
- package/skills/jgengine-ui/SKILL.md +1 -0
- package/skills/jgengine-ui/api.md +231 -9
- package/skills/jgengine-ui/capabilities.md +107 -1
- package/skills/jgengine-ui/reference-primitives.md +2 -0
- package/skills/jgengine-ui/reference.md +8 -0
|
@@ -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:
|