@jgengine/shell 0.32.0 → 0.33.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 (41) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/dist/GamePlayerShell.js +7 -0
  3. package/dist/Shell3dPresentation.js +5 -3
  4. package/dist/audio/AudioComponents.js +61 -51
  5. package/dist/audio/FeedbackHost.js +8 -4
  6. package/dist/audio/audioEngine.js +101 -24
  7. package/dist/audio/emitterTracking.d.ts +40 -0
  8. package/dist/audio/emitterTracking.js +153 -0
  9. package/dist/audio/eventFeedback.d.ts +15 -5
  10. package/dist/audio/eventFeedback.js +12 -7
  11. package/dist/audio/hapticsRoute.d.ts +28 -0
  12. package/dist/audio/hapticsRoute.js +59 -0
  13. package/dist/audio/loopParams.js +20 -10
  14. package/dist/devtools/FrameTelemetry.d.ts +1 -1
  15. package/dist/devtools/FrameTelemetry.js +4 -2
  16. package/dist/devtools/agentBridge.js +13 -0
  17. package/dist/devtools/frameSampler.js +4 -0
  18. package/dist/drivers/CanvasCoverFrameloop.d.ts +13 -0
  19. package/dist/drivers/CanvasCoverFrameloop.js +22 -0
  20. package/dist/drivers/FrameRateLimiter.d.ts +2 -1
  21. package/dist/drivers/FrameRateLimiter.js +6 -3
  22. package/dist/postfx/PostProcessing.d.ts +78 -2
  23. package/dist/postfx/PostProcessing.js +149 -76
  24. package/dist/postfx/postfxOverlay.d.ts +14 -4
  25. package/dist/postfx/postfxOverlay.js +90 -10
  26. package/dist/postfx/shadowPrepass.d.ts +6 -2
  27. package/dist/postfx/shadowPrepass.js +7 -3
  28. package/dist/render/characterLod.js +6 -0
  29. package/dist/render/useFootIk.js +54 -15
  30. package/dist/render/useModelAnimation.js +22 -5
  31. package/dist/settings/settingsController.d.ts +10 -1
  32. package/dist/settings/settingsController.js +81 -69
  33. package/dist/vision/RevealVision.d.ts +3 -1
  34. package/dist/vision/RevealVision.js +34 -15
  35. package/dist/world/WorldItems.js +11 -3
  36. package/package.json +4 -4
  37. package/skills/jgengine/api.md +2 -2
  38. package/skills/jgengine-ui/api.md +45 -24
  39. package/skills/jgengine-ui/capabilities.md +8 -0
  40. package/skills/jgengine-ui/reference-primitives.md +2 -0
  41. package/skills/jgengine-world/api.md +4 -4
@@ -1,8 +1,9 @@
1
1
  import { bindingLabel, type ActionCodesMap } from "@jgengine/core/input/actionBindings";
2
2
  import type { BindingOverrides } from "@jgengine/core/input/bindingOverrides";
3
3
  import type { AudioBusDef } from "@jgengine/core/audio/audioFalloff";
4
- import { type GameSettingDef, type SettingCategory, type SettingCategoryDef } from "@jgengine/core/settings/settingsModel";
4
+ import { type GameSettingDef, type SettingCategory, type SettingCategoryDef, type SettingsStore } from "@jgengine/core/settings/settingsModel";
5
5
  import { type GraphicsProfileOverrides } from "@jgengine/core/settings/graphicsSettings";
6
+ import type { PostProcessingConfig } from "@jgengine/core/render/postProcessing";
6
7
  import { type SettingsActionView, type SettingsKeybindRow, type SettingsCategoryView, type SettingsController, type SettingsRow } from "@jgengine/react/settings";
7
8
  export type { SettingsActionView, SettingsKeybindRow, SettingsCategoryView, SettingsController, SettingsRow };
8
9
  export interface SettingsControllerInput {
@@ -14,6 +15,8 @@ export interface SettingsControllerInput {
14
15
  fovEnabled: boolean;
15
16
  /** The game's per-tier profile overrides (`defineGame({ graphics })`), so tier defaults match what renders. */
16
17
  graphics?: GraphicsProfileOverrides;
18
+ /** The post chain this game actually runs, so a stage row never outlives its pass. */
19
+ postProcessing?: PostProcessingConfig;
17
20
  hideBindings: readonly string[];
18
21
  /** Show the touch-controls skin selector in Controls (touch device with a dock). */
19
22
  touchStyle: boolean;
@@ -22,4 +25,10 @@ export interface SettingsControllerInput {
22
25
  resetBinding: (action: string) => void;
23
26
  }
24
27
  export declare function useSettingsCategories(config: SettingsControllerInput): SettingsCategoryView[];
28
+ /**
29
+ * The built-in Graphics rows: tier, scale, filtering, shadows, one toggle per postfx stage this
30
+ * game's chain can build, then UI scale and FPS.
31
+ * @internal
32
+ */
33
+ export declare function graphicsSettingRows(store: SettingsStore, overrides: GraphicsProfileOverrides | undefined, postProcessing: PostProcessingConfig | undefined): SettingsRow[];
25
34
  export { bindingLabel };
@@ -1,9 +1,10 @@
1
1
  import { actionLabel, bindingLabel } from "@jgengine/core/input/actionBindings";
2
2
  import { BUILT_IN_SETTING_CATEGORIES, busVolumeSettingId, DEFAULT_GRAPHICS_SHADOWS, DEFAULT_MASTER_VOLUME, DEFAULT_UI_SCALE, GRAPHICS_QUALITY_OPTIONS, SETTING_IDS, UI_SCALE_MAX, UI_SCALE_MIN, } from "@jgengine/core/settings/settingsModel";
3
- import { ANISOTROPY_OPTIONS, applyGraphicsQuality, GRAPHICS_POST_STAGE_LABELS, GRAPHICS_POST_STAGES, graphicsPostStageSettingId, readGraphicsSettings, RENDER_SCALE_MAX, RENDER_SCALE_MIN, RENDER_SCALE_STEP, } from "@jgengine/core/settings/graphicsSettings";
3
+ import { ANISOTROPY_OPTIONS, applyGraphicsQuality, GRAPHICS_POST_STAGE_LABELS, graphicsPostStageSettingId, readGraphicsSettings, RENDER_SCALE_MAX, RENDER_SCALE_MIN, RENDER_SCALE_STEP, } from "@jgengine/core/settings/graphicsSettings";
4
4
  import { FRAME_RATE_LIMIT_DISPLAY, FRAME_RATE_LIMIT_OPTIONS } from "@jgengine/core/settings/frameRateLimit";
5
5
  import { accessibilitySettingRows } from "@jgengine/core/settings/accessibilitySettings";
6
6
  import { busMuteSettingId, MASTER_MUTE_SETTING_ID } from "../audio/busMix.js";
7
+ import { availablePostfxStages } from "../postfx/PostProcessing.js";
7
8
  import { DEFAULT_TOUCH_JOYSTICK_VARIANT, TOUCH_JOYSTICK_VARIANT_OPTIONS, TOUCH_STYLE_OPTIONS, } from "@jgengine/core/input/touchScheme";
8
9
  import { TOUCH_STYLE_AUTO, useSettingsRevision } from "./appliedSettings.js";
9
10
  import { useSettingsStore, } from "@jgengine/react/settings";
@@ -80,75 +81,8 @@ export function useSettingsCategories(config) {
80
81
  },
81
82
  ...extrasFor("sound"),
82
83
  ];
83
- const graphics = readGraphicsSettings(store, config.graphics);
84
84
  const graphicsRows = [
85
- {
86
- id: SETTING_IDS.graphicsQuality,
87
- label: "Quality",
88
- kind: "select",
89
- value: graphics.quality,
90
- options: GRAPHICS_QUALITY_OPTIONS,
91
- set: (value) => applyGraphicsQuality(store, String(value), config.graphics),
92
- },
93
- {
94
- id: SETTING_IDS.graphicsRenderScale,
95
- label: "Render scale",
96
- kind: "slider",
97
- value: graphics.profile.renderScale,
98
- min: RENDER_SCALE_MIN,
99
- max: RENDER_SCALE_MAX,
100
- step: RENDER_SCALE_STEP,
101
- format: percent,
102
- set: (value) => store.set(SETTING_IDS.graphicsRenderScale, value),
103
- },
104
- {
105
- id: SETTING_IDS.graphicsFrameRateLimit,
106
- label: "Frame rate limit",
107
- kind: "select",
108
- value: store.get(SETTING_IDS.graphicsFrameRateLimit, FRAME_RATE_LIMIT_DISPLAY),
109
- options: FRAME_RATE_LIMIT_OPTIONS,
110
- set: (value) => store.set(SETTING_IDS.graphicsFrameRateLimit, String(value)),
111
- },
112
- {
113
- id: SETTING_IDS.graphicsAnisotropy,
114
- label: "Texture filtering",
115
- kind: "select",
116
- value: String(graphics.profile.anisotropy),
117
- options: ANISOTROPY_OPTIONS,
118
- set: (value) => store.set(SETTING_IDS.graphicsAnisotropy, String(value)),
119
- },
120
- {
121
- id: SETTING_IDS.graphicsShadows,
122
- label: "Shadows",
123
- kind: "toggle",
124
- value: store.get(SETTING_IDS.graphicsShadows, DEFAULT_GRAPHICS_SHADOWS),
125
- set: (value) => store.set(SETTING_IDS.graphicsShadows, value),
126
- },
127
- ...GRAPHICS_POST_STAGES.map((stage) => ({
128
- id: graphicsPostStageSettingId(stage),
129
- label: GRAPHICS_POST_STAGE_LABELS[stage],
130
- kind: "toggle",
131
- value: graphics.profile.postStages[stage],
132
- set: (value) => store.set(graphicsPostStageSettingId(stage), value),
133
- })),
134
- {
135
- id: SETTING_IDS.graphicsUiScale,
136
- label: "UI scale",
137
- kind: "slider",
138
- value: store.get(SETTING_IDS.graphicsUiScale, DEFAULT_UI_SCALE),
139
- min: UI_SCALE_MIN,
140
- max: UI_SCALE_MAX,
141
- step: 0.05,
142
- format: percent,
143
- set: (value) => store.set(SETTING_IDS.graphicsUiScale, value),
144
- },
145
- {
146
- id: SETTING_IDS.graphicsShowFps,
147
- label: "Show FPS",
148
- kind: "toggle",
149
- value: store.get(SETTING_IDS.graphicsShowFps, false),
150
- set: (value) => store.set(SETTING_IDS.graphicsShowFps, value),
151
- },
85
+ ...graphicsSettingRows(store, config.graphics, config.postProcessing),
152
86
  ...extrasFor("graphics"),
153
87
  ];
154
88
  const gameplayRows = [
@@ -240,6 +174,84 @@ export function useSettingsCategories(config) {
240
174
  views.sort((a, b) => (declaredOrder.get(a.id) ?? indexOrder(order, a.id)) - (declaredOrder.get(b.id) ?? indexOrder(order, b.id)));
241
175
  return views;
242
176
  }
177
+ /**
178
+ * The built-in Graphics rows: tier, scale, filtering, shadows, one toggle per postfx stage this
179
+ * game's chain can build, then UI scale and FPS.
180
+ * @internal
181
+ */
182
+ export function graphicsSettingRows(store, overrides, postProcessing) {
183
+ const graphics = readGraphicsSettings(store, overrides);
184
+ const stages = postProcessing === undefined || postProcessing.enabled === false ? [] : availablePostfxStages(postProcessing);
185
+ return [
186
+ {
187
+ id: SETTING_IDS.graphicsQuality,
188
+ label: "Quality",
189
+ kind: "select",
190
+ value: graphics.quality,
191
+ options: GRAPHICS_QUALITY_OPTIONS,
192
+ set: (value) => applyGraphicsQuality(store, String(value), overrides),
193
+ },
194
+ {
195
+ id: SETTING_IDS.graphicsRenderScale,
196
+ label: "Render scale",
197
+ kind: "slider",
198
+ value: graphics.profile.renderScale,
199
+ min: RENDER_SCALE_MIN,
200
+ max: RENDER_SCALE_MAX,
201
+ step: RENDER_SCALE_STEP,
202
+ format: percent,
203
+ set: (value) => store.set(SETTING_IDS.graphicsRenderScale, value),
204
+ },
205
+ {
206
+ id: SETTING_IDS.graphicsFrameRateLimit,
207
+ label: "Frame rate limit",
208
+ kind: "select",
209
+ value: store.get(SETTING_IDS.graphicsFrameRateLimit, FRAME_RATE_LIMIT_DISPLAY),
210
+ options: FRAME_RATE_LIMIT_OPTIONS,
211
+ set: (value) => store.set(SETTING_IDS.graphicsFrameRateLimit, String(value)),
212
+ },
213
+ {
214
+ id: SETTING_IDS.graphicsAnisotropy,
215
+ label: "Texture filtering",
216
+ kind: "select",
217
+ value: String(graphics.profile.anisotropy),
218
+ options: ANISOTROPY_OPTIONS,
219
+ set: (value) => store.set(SETTING_IDS.graphicsAnisotropy, String(value)),
220
+ },
221
+ {
222
+ id: SETTING_IDS.graphicsShadows,
223
+ label: "Shadows",
224
+ kind: "toggle",
225
+ value: store.get(SETTING_IDS.graphicsShadows, DEFAULT_GRAPHICS_SHADOWS),
226
+ set: (value) => store.set(SETTING_IDS.graphicsShadows, value),
227
+ },
228
+ ...stages.map((stage) => ({
229
+ id: graphicsPostStageSettingId(stage),
230
+ label: GRAPHICS_POST_STAGE_LABELS[stage],
231
+ kind: "toggle",
232
+ value: graphics.profile.postStages[stage],
233
+ set: (value) => store.set(graphicsPostStageSettingId(stage), value),
234
+ })),
235
+ {
236
+ id: SETTING_IDS.graphicsUiScale,
237
+ label: "UI scale",
238
+ kind: "slider",
239
+ value: store.get(SETTING_IDS.graphicsUiScale, DEFAULT_UI_SCALE),
240
+ min: UI_SCALE_MIN,
241
+ max: UI_SCALE_MAX,
242
+ step: 0.05,
243
+ format: percent,
244
+ set: (value) => store.set(SETTING_IDS.graphicsUiScale, value),
245
+ },
246
+ {
247
+ id: SETTING_IDS.graphicsShowFps,
248
+ label: "Show FPS",
249
+ kind: "toggle",
250
+ value: store.get(SETTING_IDS.graphicsShowFps, false),
251
+ set: (value) => store.set(SETTING_IDS.graphicsShowFps, value),
252
+ },
253
+ ];
254
+ }
243
255
  function indexOrder(order, id) {
244
256
  const index = order.indexOf(id);
245
257
  return index === -1 ? 999 : index;
@@ -6,7 +6,7 @@ export interface RevealVisionOptions {
6
6
  tags: readonly string[];
7
7
  resolveTags: (entity: SceneEntity) => readonly string[];
8
8
  }
9
- /** Occlusion-ignoring tagged-entity radius query (#115), bound to the live scene. */
9
+ /** Occlusion-ignoring tagged-entity radius query (#115), bound to the live scene and refreshed on render. */
10
10
  export declare function useRevealHits(options: RevealVisionOptions): readonly RevealHit[];
11
11
  export interface RevealHighlightsProps extends RevealVisionOptions {
12
12
  enabled: boolean;
@@ -17,6 +17,8 @@ export interface RevealHighlightsProps extends RevealVisionOptions {
17
17
  * occluders (Dark Sight / detective-vision / wallhack style). Renders with
18
18
  * `depthTest: false` so the highlight draws over any wall standing between
19
19
  * the origin and the revealed entity, rather than the usual depth-sorted scene.
20
+ * Highlights track the origin and their targets every frame while enabled, so
21
+ * a disabled mode costs no renders.
20
22
  */
21
23
  export declare function RevealHighlights(props: RevealHighlightsProps): import("react").JSX.Element | null;
22
24
  export interface RevealScreenTintProps {
@@ -1,37 +1,56 @@
1
1
  import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useMemo } from "react";
2
+ import { useMemo, useState } from "react";
3
+ import { useFrame } from "@react-three/fiber";
3
4
  import { DoubleSide } from "three";
4
5
  import { createRevealQuery } from "@jgengine/core/sensor/revealQuery";
5
6
  import { usePlayer, useSceneEntities } from "@jgengine/react/hooks";
6
- /** Occlusion-ignoring tagged-entity radius query (#115), bound to the live scene. */
7
+ function revealHits(entities, originId, options) {
8
+ const byId = new Map(entities.map((entity) => [entity.id, entity]));
9
+ const query = createRevealQuery({
10
+ resolvePosition: (id) => byId.get(id)?.position,
11
+ resolveTags: (id) => {
12
+ const entity = byId.get(id);
13
+ return entity === undefined ? [] : options.resolveTags(entity);
14
+ },
15
+ candidates: () => entities.map((entity) => entity.id),
16
+ });
17
+ return query.inRadius(originId, options.radius, options.tags);
18
+ }
19
+ /** Occlusion-ignoring tagged-entity radius query (#115), bound to the live scene and refreshed on render. */
7
20
  export function useRevealHits(options) {
8
21
  const entities = useSceneEntities();
9
22
  const player = usePlayer();
10
23
  const originId = options.originEntityId ?? player.userId;
11
24
  const tagsKey = options.tags.join("|");
12
- return useMemo(() => {
13
- const byId = new Map(entities.map((entity) => [entity.id, entity]));
14
- const query = createRevealQuery({
15
- resolvePosition: (id) => byId.get(id)?.position,
16
- resolveTags: (id) => {
17
- const entity = byId.get(id);
18
- return entity === undefined ? [] : options.resolveTags(entity);
19
- },
20
- candidates: () => entities.map((entity) => entity.id),
21
- });
22
- return query.inRadius(originId, options.radius, options.tags);
23
- }, [entities, originId, options.radius, tagsKey, options.tags, options.resolveTags]);
25
+ return useMemo(() => revealHits(entities, originId, options), [entities, originId, options.radius, tagsKey, options.tags, options.resolveTags]);
26
+ }
27
+ /** Frames on a counter while `active`: the hook lists only change on membership, so a live overlay drives its own re-render. */
28
+ function useFrameRefresh(active) {
29
+ const [frame, setFrame] = useState(0);
30
+ useFrame(() => {
31
+ if (active)
32
+ setFrame((value) => (value + 1) % 1_000_000);
33
+ });
34
+ return frame;
24
35
  }
25
36
  /**
26
37
  * Screen-space reveal effect (#115) — highlights tagged entities through
27
38
  * occluders (Dark Sight / detective-vision / wallhack style). Renders with
28
39
  * `depthTest: false` so the highlight draws over any wall standing between
29
40
  * the origin and the revealed entity, rather than the usual depth-sorted scene.
41
+ * Highlights track the origin and their targets every frame while enabled, so
42
+ * a disabled mode costs no renders.
30
43
  */
31
44
  export function RevealHighlights(props) {
32
45
  const { enabled, color = "#7dd3fc" } = props;
33
- const hits = useRevealHits(props);
46
+ const frame = useFrameRefresh(enabled);
34
47
  const entities = useSceneEntities();
48
+ const player = usePlayer();
49
+ const originId = props.originEntityId ?? player.userId;
50
+ const tagsKey = props.tags.join("|");
51
+ const hits = useMemo(() => (enabled ? revealHits(entities, originId, props) : []),
52
+ // `frame` is the refresh: the list identity says nothing about where the origin or the targets moved.
53
+ [enabled, frame, entities, originId, props.radius, tagsKey, props.tags, props.resolveTags]);
35
54
  if (!enabled)
36
55
  return null;
37
56
  return (_jsx(_Fragment, { children: hits.map((hit) => {
@@ -5,12 +5,16 @@ import { useFrame } from "@react-three/fiber";
5
5
  import { resolveWorldItemPresentation } from "@jgengine/core/game/worldItem";
6
6
  import { useGameContext } from "@jgengine/react/provider";
7
7
  import { useWorldItems } from "@jgengine/react/hooks";
8
+ import { emptyPoseMemo, writePoseIfChanged } from "./entityPose.js";
8
9
  import { POINTER_ENTITY_KEY } from "../pointer/pointerService.js";
9
10
  const DEFAULT_BEAM_HEIGHT = 2.5;
10
11
  const FALLBACK_COLOR = "#e5e7eb";
11
12
  const REST_HEIGHT = 0.32;
12
13
  const SPAWN_DURATION = 0.4;
13
- function WorldItemMarker({ instanceId, position, color, beam, label, beamHeight, }) {
14
+ function WorldItemMarker({ instanceId, color, beam, label, beamHeight, }) {
15
+ const ctx = useGameContext();
16
+ const groupRef = useRef(null);
17
+ const poseRef = useRef(emptyPoseMemo());
14
18
  const beamPoints = useMemo(() => [
15
19
  [0, 0, 0],
16
20
  [0, beamHeight, 0],
@@ -21,6 +25,10 @@ function WorldItemMarker({ instanceId, position, color, beam, label, beamHeight,
21
25
  const gemRef = useRef(null);
22
26
  const ageRef = useRef(0);
23
27
  useFrame((_state, delta) => {
28
+ const group = groupRef.current;
29
+ const live = ctx.scene.entity.get(instanceId);
30
+ if (group !== null && live !== null)
31
+ writePoseIfChanged(group, live, poseRef.current);
24
32
  const gem = gemRef.current;
25
33
  if (gem === null)
26
34
  return;
@@ -31,7 +39,7 @@ function WorldItemMarker({ instanceId, position, color, beam, label, beamHeight,
31
39
  gem.position.y = REST_HEIGHT + bob + spawn;
32
40
  gem.rotation.y = age * 1.6;
33
41
  });
34
- return (_jsxs("group", { position: [position[0], position[1], position[2]], userData: { [POINTER_ENTITY_KEY]: instanceId }, children: [_jsxs("mesh", { "rotation-x": -Math.PI / 2, "position-y": 0.03, children: [_jsx("ringGeometry", { args: [0.26, 0.4, 24] }), _jsx("meshBasicMaterial", { color: color, transparent: true, opacity: 0.85 })] }), _jsx("group", { ref: gemRef, "position-y": REST_HEIGHT, children: _jsxs("mesh", { children: [_jsx("octahedronGeometry", { args: [0.22, 0] }), _jsx("meshStandardMaterial", { color: color, emissive: color, emissiveIntensity: 0.6 })] }) }), beam ? _jsx(Line, { points: beamPoints, color: color, lineWidth: 2, transparent: true, opacity: 0.55 }) : null, label !== undefined && label.length > 0 ? (_jsx(Html, { position: [0, beamHeight + 0.2, 0], center: true, distanceFactor: 12, zIndexRange: [15, 0], children: _jsx("div", { className: "whitespace-nowrap rounded-sm border px-1.5 py-0.5 text-[11px] font-bold shadow", style: { borderColor: color, color, backgroundColor: "rgba(10,10,14,0.75)" }, children: label }) })) : null] }));
42
+ return (_jsxs("group", { ref: groupRef, matrixAutoUpdate: false, userData: { [POINTER_ENTITY_KEY]: instanceId }, children: [_jsxs("mesh", { "rotation-x": -Math.PI / 2, "position-y": 0.03, children: [_jsx("ringGeometry", { args: [0.26, 0.4, 24] }), _jsx("meshBasicMaterial", { color: color, transparent: true, opacity: 0.85 })] }), _jsx("group", { ref: gemRef, "position-y": REST_HEIGHT, children: _jsxs("mesh", { children: [_jsx("octahedronGeometry", { args: [0.22, 0] }), _jsx("meshStandardMaterial", { color: color, emissive: color, emissiveIntensity: 0.6 })] }) }), beam ? _jsx(Line, { points: beamPoints, color: color, lineWidth: 2, transparent: true, opacity: 0.55 }) : null, label !== undefined && label.length > 0 ? (_jsx(Html, { position: [0, beamHeight + 0.2, 0], center: true, distanceFactor: 12, zIndexRange: [15, 0], children: _jsx("div", { className: "whitespace-nowrap rounded-sm border px-1.5 py-0.5 text-[11px] font-bold shadow", style: { borderColor: color, color, backgroundColor: "rgba(10,10,14,0.75)" }, children: label }) })) : null] }));
35
43
  }
36
44
  /** Rarity→beam/color/label render binding + loot-filter overlay (#32/#33) for every dropped `worldItem`. */
37
45
  export function WorldItems({ config }) {
@@ -45,6 +53,6 @@ export function WorldItems({ config }) {
45
53
  const presentation = resolveWorldItemPresentation(item, config?.rarityStyle, config?.filter);
46
54
  if (presentation.hidden)
47
55
  return null;
48
- return (_jsx(WorldItemMarker, { instanceId: item.instanceId, position: entity.position, color: presentation.color ?? FALLBACK_COLOR, beam: presentation.beam, label: presentation.label, beamHeight: beamHeight }, item.instanceId));
56
+ return (_jsx(WorldItemMarker, { instanceId: item.instanceId, color: presentation.color ?? FALLBACK_COLOR, beam: presentation.beam, label: presentation.label, beamHeight: beamHeight }, item.instanceId));
49
57
  }) }));
50
58
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jgengine/shell",
3
- "version": "0.32.0",
3
+ "version": "0.33.0",
4
4
  "description": "Game player shell for JGengine: React Three Fiber canvas, orbit camera, input tracking, HUD mounting, GameUiPreview, and a demo game. Consumers supply a GameRegistry.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -35,9 +35,9 @@
35
35
  "test": "bun test src"
36
36
  },
37
37
  "dependencies": {
38
- "@jgengine/core": "^0.32.0",
39
- "@jgengine/react": "^0.32.0",
40
- "@jgengine/ws": "^0.32.0"
38
+ "@jgengine/core": "^0.33.0",
39
+ "@jgengine/react": "^0.33.0",
40
+ "@jgengine/ws": "^0.33.0"
41
41
  },
42
42
  "peerDependencies": {
43
43
  "@react-three/drei": "^10.0.0",
@@ -6,7 +6,7 @@
6
6
 
7
7
  - `CHANGELOG` (const): const CHANGELOG: Record<string, ChangelogEntry> — Per-version engine changelog keyed by semver string (e.g. `"0.10.0"`).
8
8
  - `ChangelogEntry` (interface): interface ChangelogEntry — One release's migrate steps plus added/changed/removed notes (typed mirror of CHANGELOG.md).
9
- - `VERSION` (const): const VERSION: "0.32.0" — Installed `@jgengine/core` semver — compare against {@link CHANGELOG} keys when migrating.
9
+ - `VERSION` (const): const VERSION: "0.33.0" — Installed `@jgengine/core` semver — compare against {@link CHANGELOG} keys when migrating.
10
10
 
11
11
  ## @jgengine/core/authoring
12
12
 
@@ -285,7 +285,7 @@
285
285
 
286
286
  - `CHANGELOG` (const): const CHANGELOG: Record<string, ChangelogEntry> — Per-version engine changelog keyed by semver string (e.g. `"0.10.0"`).
287
287
  - `ChangelogEntry` (interface): interface ChangelogEntry — One release's migrate steps plus added/changed/removed notes (typed mirror of CHANGELOG.md).
288
- - `VERSION` (const): const VERSION: "0.32.0" — Installed `@jgengine/core` semver — compare against {@link CHANGELOG} keys when migrating.
288
+ - `VERSION` (const): const VERSION: "0.33.0" — Installed `@jgengine/core` semver — compare against {@link CHANGELOG} keys when migrating.
289
289
 
290
290
  ## @jgengine/core/runtime/adapter
291
291