cubeforge 0.5.2 → 0.6.1

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 (3) hide show
  1. package/dist/index.d.ts +372 -2
  2. package/dist/index.js +1109 -475
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -7829,6 +7829,7 @@ import { useEffect as useEffect28, useContext as useContext19 } from "react";
7829
7829
 
7830
7830
  // src/components/particlePresets.ts
7831
7831
  var PARTICLE_PRESETS = {
7832
+ // ── Action / combat ──
7832
7833
  explosion: {
7833
7834
  rate: 60,
7834
7835
  speed: 200,
@@ -7837,8 +7838,12 @@ var PARTICLE_PRESETS = {
7837
7838
  particleLife: 0.5,
7838
7839
  particleSize: 6,
7839
7840
  color: "#ff6b35",
7841
+ colorOverLife: ["#fff3b0", "#ff6b35", "#6d4c41"],
7842
+ sizeOverLife: { start: 1.4, end: 0 },
7840
7843
  gravity: 300,
7841
- maxParticles: 80
7844
+ maxParticles: 80,
7845
+ blendMode: "additive",
7846
+ particleShape: "soft"
7842
7847
  },
7843
7848
  spark: {
7844
7849
  rate: 40,
@@ -7849,7 +7854,70 @@ var PARTICLE_PRESETS = {
7849
7854
  particleSize: 3,
7850
7855
  color: "#ffd54f",
7851
7856
  gravity: 400,
7852
- maxParticles: 50
7857
+ maxParticles: 50,
7858
+ blendMode: "additive",
7859
+ particleShape: "soft"
7860
+ },
7861
+ damage: {
7862
+ rate: 45,
7863
+ speed: 120,
7864
+ spread: Math.PI * 2,
7865
+ angle: 0,
7866
+ particleLife: 0.35,
7867
+ particleSize: 4,
7868
+ color: "#ef5350",
7869
+ colorOverLife: ["#ffab91", "#ef5350", "#4a148c"],
7870
+ sizeOverLife: { start: 1, end: 0.2 },
7871
+ gravity: 150,
7872
+ maxParticles: 40,
7873
+ blendMode: "normal",
7874
+ particleShape: "circle"
7875
+ },
7876
+ heal: {
7877
+ rate: 22,
7878
+ speed: 50,
7879
+ spread: Math.PI / 2,
7880
+ angle: -Math.PI / 2,
7881
+ particleLife: 0.9,
7882
+ particleSize: 5,
7883
+ color: "#8bc34a",
7884
+ colorOverLife: ["#e8f5e9", "#8bc34a"],
7885
+ sizeOverLife: { start: 0.5, end: 1.2 },
7886
+ gravity: -60,
7887
+ maxParticles: 30,
7888
+ blendMode: "additive",
7889
+ particleShape: "soft"
7890
+ },
7891
+ magic: {
7892
+ rate: 32,
7893
+ speed: 80,
7894
+ spread: Math.PI * 2,
7895
+ angle: 0,
7896
+ particleLife: 1.1,
7897
+ particleSize: 5,
7898
+ color: "#ba68c8",
7899
+ colorOverLife: ["#e1bee7", "#ba68c8", "#4a148c"],
7900
+ sizeOverLife: { start: 1, end: 0 },
7901
+ gravity: -20,
7902
+ maxParticles: 60,
7903
+ blendMode: "additive",
7904
+ particleShape: "soft"
7905
+ },
7906
+ // ── Environment ──
7907
+ fire: {
7908
+ rate: 40,
7909
+ speed: 60,
7910
+ spread: 0.6,
7911
+ angle: -Math.PI / 2,
7912
+ particleLife: 0.7,
7913
+ particleSize: 7,
7914
+ color: "#ff9800",
7915
+ colorOverLife: ["#ffe082", "#ff9800", "#6d4c41", "#37474f"],
7916
+ sizeOverLife: { start: 1.2, end: 0.3 },
7917
+ gravity: -120,
7918
+ maxParticles: 70,
7919
+ blendMode: "additive",
7920
+ particleShape: "soft"
7853
7921
  },
7854
7922
  smoke: {
7855
7923
  rate: 15,
@@ -7859,9 +7927,54 @@ var PARTICLE_PRESETS = {
7859
7927
  particleLife: 1.2,
7860
7928
  particleSize: 10,
7861
7929
  color: "#90a4ae",
7930
+ colorOverLife: ["#90a4ae", "rgba(55,71,79,0)"],
7931
+ sizeOverLife: { start: 0.5, end: 1.8 },
7862
7932
  gravity: -20,
7863
- maxParticles: 40
7933
+ maxParticles: 40,
7934
+ blendMode: "normal",
7935
+ particleShape: "soft"
7936
+ },
7937
+ rain: {
7938
+ rate: 80,
7939
+ speed: 350,
7940
+ spread: 0.05,
7941
+ angle: Math.PI / 2,
7942
+ particleLife: 0.8,
7943
+ particleSize: 2,
7944
+ color: "#81d4fa",
7945
+ gravity: 900,
7946
+ maxParticles: 300,
7947
+ blendMode: "normal",
7948
+ particleShape: "square"
7949
+ },
7950
+ snow: {
7951
+ rate: 20,
7952
+ speed: 40,
7953
+ spread: 0.4,
7954
+ angle: Math.PI / 2,
7955
+ particleLife: 4,
7956
+ particleSize: 3,
7957
+ color: "#ffffff",
7958
+ gravity: 20,
7959
+ maxParticles: 150,
7960
+ blendMode: "normal",
7961
+ particleShape: "circle"
7962
+ },
7963
+ fountain: {
7964
+ rate: 50,
7965
+ speed: 300,
7966
+ spread: 0.3,
7967
+ angle: -Math.PI / 2,
7968
+ particleLife: 1.5,
7969
+ particleSize: 4,
7970
+ color: "#4fc3f7",
7971
+ colorOverLife: ["#b3e5fc", "#4fc3f7", "#0277bd"],
7972
+ gravity: 500,
7973
+ maxParticles: 200,
7974
+ blendMode: "additive",
7975
+ particleShape: "soft"
7864
7976
  },
7977
+ // ── UI / reward ──
7865
7978
  coinPickup: {
7866
7979
  rate: 30,
7867
7980
  speed: 80,
@@ -7871,8 +7984,54 @@ var PARTICLE_PRESETS = {
7871
7984
  particleSize: 4,
7872
7985
  color: "#ffd700",
7873
7986
  gravity: 200,
7874
- maxParticles: 20
7987
+ maxParticles: 20,
7988
+ blendMode: "additive",
7989
+ particleShape: "soft"
7875
7990
  },
7991
+ pickup: {
7992
+ rate: 35,
7993
+ speed: 70,
7994
+ spread: Math.PI * 2,
7995
+ angle: 0,
7996
+ particleLife: 0.5,
7997
+ particleSize: 5,
7998
+ color: "#4fc3f7",
7999
+ colorOverLife: ["#ffffff", "#4fc3f7"],
8000
+ sizeOverLife: { start: 1, end: 0 },
8001
+ gravity: -80,
8002
+ maxParticles: 24,
8003
+ blendMode: "additive",
8004
+ particleShape: "soft"
8005
+ },
8006
+ sparkle: {
8007
+ rate: 14,
8008
+ speed: 40,
8009
+ spread: Math.PI * 2,
8010
+ angle: 0,
8011
+ particleLife: 0.8,
8012
+ particleSize: 3,
8013
+ color: "#fffde7",
8014
+ sizeOverLife: { start: 0, end: 1 },
8015
+ gravity: 0,
8016
+ maxParticles: 30,
8017
+ blendMode: "additive",
8018
+ particleShape: "soft"
8019
+ },
8020
+ confetti: {
8021
+ rate: 70,
8022
+ speed: 280,
8023
+ spread: Math.PI / 3,
8024
+ angle: -Math.PI / 2,
8025
+ particleLife: 2.2,
8026
+ particleSize: 5,
8027
+ color: "#ffc107",
8028
+ colorOverLife: ["#f44336", "#ffc107", "#4caf50", "#2196f3", "#9c27b0"],
8029
+ gravity: 400,
8030
+ maxParticles: 150,
8031
+ blendMode: "normal",
8032
+ particleShape: "square"
8033
+ },
8034
+ // ── Character ──
7876
8035
  jumpDust: {
7877
8036
  rate: 25,
7878
8037
  speed: 60,
@@ -7882,7 +8041,23 @@ var PARTICLE_PRESETS = {
7882
8041
  particleSize: 5,
7883
8042
  color: "#b0bec5",
7884
8043
  gravity: 80,
7885
- maxParticles: 20
8044
+ maxParticles: 20,
8045
+ blendMode: "normal",
8046
+ particleShape: "soft"
8047
+ },
8048
+ trail: {
8049
+ rate: 30,
8050
+ speed: 8,
8051
+ spread: 0.2,
8052
+ angle: Math.PI / 2,
8053
+ particleLife: 0.5,
8054
+ particleSize: 4,
8055
+ color: "#4fc3f7",
8056
+ sizeOverLife: { start: 1, end: 0 },
8057
+ gravity: 0,
8058
+ maxParticles: 40,
8059
+ blendMode: "additive",
8060
+ particleShape: "soft"
7886
8061
  }
7887
8062
  };
7888
8063
 
@@ -7928,6 +8103,10 @@ function ParticleEmitter({
7928
8103
  const resolvedColor = color ?? presetConfig.color ?? "#ffffff";
7929
8104
  const resolvedGravity = gravity ?? presetConfig.gravity ?? 200;
7930
8105
  const resolvedMaxParticles = maxParticles ?? presetConfig.maxParticles ?? 100;
8106
+ const resolvedBlendMode = blendMode ?? presetConfig.blendMode;
8107
+ const resolvedParticleShape = particleShape ?? presetConfig.particleShape;
8108
+ const resolvedColorOverLife = colorOverLife ?? presetConfig.colorOverLife;
8109
+ const resolvedSizeOverLife = sizeOverLife ?? presetConfig.sizeOverLife;
7931
8110
  const engine = useContext19(EngineContext);
7932
8111
  const entityId = useContext19(EntityContext);
7933
8112
  useEffect28(() => {
@@ -7953,15 +8132,15 @@ function ParticleEmitter({
7953
8132
  textureSrc,
7954
8133
  enableRotation,
7955
8134
  rotationSpeedRange,
7956
- sizeOverLife,
8135
+ sizeOverLife: resolvedSizeOverLife,
7957
8136
  attractors,
7958
- colorOverLife,
7959
- blendMode,
8137
+ colorOverLife: resolvedColorOverLife,
8138
+ blendMode: resolvedBlendMode,
7960
8139
  mode,
7961
8140
  formationPoints,
7962
8141
  seekStrength,
7963
8142
  colorTransitionDuration,
7964
- particleShape
8143
+ particleShape: resolvedParticleShape
7965
8144
  });
7966
8145
  return () => engine.ecs.removeComponent(entityId, "ParticlePool");
7967
8146
  }, []);
@@ -7973,9 +8152,9 @@ function ParticleEmitter({
7973
8152
  useEffect28(() => {
7974
8153
  const pool = engine.ecs.getComponent(entityId, "ParticlePool");
7975
8154
  if (!pool) return;
7976
- pool.blendMode = blendMode;
7977
- pool.particleShape = particleShape;
7978
- }, [blendMode, particleShape, engine, entityId]);
8155
+ pool.blendMode = resolvedBlendMode;
8156
+ pool.particleShape = resolvedParticleShape;
8157
+ }, [resolvedBlendMode, resolvedParticleShape, engine, entityId]);
7979
8158
  useEffect28(() => {
7980
8159
  const pool = engine.ecs.getComponent(entityId, "ParticlePool");
7981
8160
  if (!pool) return;
@@ -8684,12 +8863,151 @@ function CameraZone({ x, y, width, height, watchTag = "player", targetX, targetY
8684
8863
  return /* @__PURE__ */ jsx13(Fragment6, { children: children ?? null });
8685
8864
  }
8686
8865
 
8687
- // src/components/Trail.tsx
8866
+ // src/components/VirtualCamera.tsx
8688
8867
  import { useEffect as useEffect32, useContext as useContext23 } from "react";
8689
- function Trail({ length = 20, color = "#ffffff", width = 3 }) {
8868
+ var registries = /* @__PURE__ */ new WeakMap();
8869
+ var driverEids = /* @__PURE__ */ new WeakMap();
8870
+ var driverRefs = /* @__PURE__ */ new WeakMap();
8871
+ var driverStates = /* @__PURE__ */ new WeakMap();
8872
+ function getRegistry(engine) {
8873
+ if (!registries.has(engine)) registries.set(engine, /* @__PURE__ */ new Map());
8874
+ return registries.get(engine);
8875
+ }
8876
+ function easeInOutQuad(t) {
8877
+ return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
8878
+ }
8879
+ function acquireDriver(engine) {
8880
+ const count = driverRefs.get(engine) ?? 0;
8881
+ driverRefs.set(engine, count + 1);
8882
+ if (count > 0) return;
8883
+ const state = { activeId: null, blend: null };
8884
+ driverStates.set(engine, state);
8885
+ const eid = engine.ecs.createEntity();
8886
+ driverEids.set(engine, eid);
8887
+ engine.ecs.addComponent(
8888
+ eid,
8889
+ createScript((_id, _world, _input, dt) => {
8890
+ const registry2 = getRegistry(engine);
8891
+ const camId = engine.ecs.queryOne("Camera2D");
8892
+ if (camId === void 0) return;
8893
+ const cam = engine.ecs.getComponent(camId, "Camera2D");
8894
+ if (!cam) return;
8895
+ let best = null;
8896
+ for (const vc of registry2.values()) {
8897
+ if (!vc.active) continue;
8898
+ if (!best || vc.priority > best.priority) best = vc;
8899
+ }
8900
+ if (!best) return;
8901
+ if (best.id !== state.activeId) {
8902
+ state.blend = {
8903
+ fromX: cam.x,
8904
+ fromY: cam.y,
8905
+ fromZoom: cam.zoom,
8906
+ elapsed: 0,
8907
+ duration: best.blendDuration
8908
+ };
8909
+ state.activeId = best.id;
8910
+ cam.followEntityId = void 0;
8911
+ }
8912
+ if (state.blend) {
8913
+ if (state.blend.duration <= 0) {
8914
+ state.blend = null;
8915
+ } else {
8916
+ state.blend.elapsed += dt;
8917
+ const t = Math.min(1, state.blend.elapsed / state.blend.duration);
8918
+ const eased = easeInOutQuad(t);
8919
+ const targetX = best.x ?? state.blend.fromX;
8920
+ const targetY = best.y ?? state.blend.fromY;
8921
+ const targetZoom = best.zoom ?? 1;
8922
+ cam.x = state.blend.fromX + (targetX - state.blend.fromX) * eased;
8923
+ cam.y = state.blend.fromY + (targetY - state.blend.fromY) * eased;
8924
+ cam.zoom = state.blend.fromZoom + (targetZoom - state.blend.fromZoom) * eased;
8925
+ if (t >= 1) {
8926
+ state.blend = null;
8927
+ cam.followEntityId = best.followEntityId;
8928
+ if (best.smoothing !== void 0) cam.smoothing = best.smoothing;
8929
+ if (best.bounds !== void 0) cam.bounds = best.bounds;
8930
+ }
8931
+ return;
8932
+ }
8933
+ }
8934
+ cam.followEntityId = best.followEntityId;
8935
+ if (best.followEntityId === void 0) {
8936
+ if (best.x !== void 0) cam.x = best.x;
8937
+ if (best.y !== void 0) cam.y = best.y;
8938
+ }
8939
+ if (best.zoom !== void 0) cam.zoom = best.zoom;
8940
+ if (best.smoothing !== void 0) cam.smoothing = best.smoothing;
8941
+ if (best.bounds !== void 0) cam.bounds = best.bounds;
8942
+ })
8943
+ );
8944
+ }
8945
+ function releaseDriver(engine) {
8946
+ const count = (driverRefs.get(engine) ?? 1) - 1;
8947
+ driverRefs.set(engine, Math.max(0, count));
8948
+ if (count > 0) return;
8949
+ const eid = driverEids.get(engine);
8950
+ if (eid !== void 0 && engine.ecs.hasEntity(eid)) engine.ecs.destroyEntity(eid);
8951
+ driverEids.delete(engine);
8952
+ driverStates.delete(engine);
8953
+ driverRefs.delete(engine);
8954
+ getRegistry(engine).clear();
8955
+ }
8956
+ function VirtualCamera({
8957
+ id,
8958
+ priority = 0,
8959
+ active = true,
8960
+ followEntity,
8961
+ x,
8962
+ y,
8963
+ zoom = 1,
8964
+ smoothing,
8965
+ bounds,
8966
+ blendDuration = 0.4
8967
+ }) {
8690
8968
  const engine = useContext23(EngineContext);
8691
- const entityId = useContext23(EntityContext);
8692
8969
  useEffect32(() => {
8970
+ acquireDriver(engine);
8971
+ const registry2 = getRegistry(engine);
8972
+ registry2.set(id, {
8973
+ id,
8974
+ priority,
8975
+ active,
8976
+ followEntityId: followEntity,
8977
+ x,
8978
+ y,
8979
+ zoom,
8980
+ smoothing,
8981
+ bounds,
8982
+ blendDuration
8983
+ });
8984
+ return () => {
8985
+ getRegistry(engine).delete(id);
8986
+ releaseDriver(engine);
8987
+ };
8988
+ }, []);
8989
+ useEffect32(() => {
8990
+ const entry = getRegistry(engine).get(id);
8991
+ if (!entry) return;
8992
+ entry.priority = priority;
8993
+ entry.active = active;
8994
+ entry.followEntityId = followEntity;
8995
+ entry.x = x;
8996
+ entry.y = y;
8997
+ entry.zoom = zoom;
8998
+ entry.smoothing = smoothing;
8999
+ entry.bounds = bounds;
9000
+ entry.blendDuration = blendDuration;
9001
+ }, [id, priority, active, followEntity, x, y, zoom, smoothing, bounds, blendDuration, engine]);
9002
+ return null;
9003
+ }
9004
+
9005
+ // src/components/Trail.tsx
9006
+ import { useEffect as useEffect33, useContext as useContext24 } from "react";
9007
+ function Trail({ length = 20, color = "#ffffff", width = 3 }) {
9008
+ const engine = useContext24(EngineContext);
9009
+ const entityId = useContext24(EntityContext);
9010
+ useEffect33(() => {
8693
9011
  engine.ecs.addComponent(entityId, createTrail({ length, color, width }));
8694
9012
  return () => engine.ecs.removeComponent(entityId, "Trail");
8695
9013
  }, []);
@@ -8697,7 +9015,7 @@ function Trail({ length = 20, color = "#ffffff", width = 3 }) {
8697
9015
  }
8698
9016
 
8699
9017
  // src/components/NineSlice.tsx
8700
- import { useEffect as useEffect33, useContext as useContext24 } from "react";
9018
+ import { useEffect as useEffect34, useContext as useContext25 } from "react";
8701
9019
  function NineSlice({
8702
9020
  src,
8703
9021
  width,
@@ -8708,9 +9026,9 @@ function NineSlice({
8708
9026
  borderLeft = 8,
8709
9027
  zIndex = 0
8710
9028
  }) {
8711
- const engine = useContext24(EngineContext);
8712
- const entityId = useContext24(EntityContext);
8713
- useEffect33(() => {
9029
+ const engine = useContext25(EngineContext);
9030
+ const entityId = useContext25(EntityContext);
9031
+ useEffect34(() => {
8714
9032
  engine.ecs.addComponent(
8715
9033
  entityId,
8716
9034
  createNineSlice(src, width, height, {
@@ -8727,18 +9045,18 @@ function NineSlice({
8727
9045
  }
8728
9046
 
8729
9047
  // src/components/AssetLoader.tsx
8730
- import { useEffect as useEffect35 } from "react";
9048
+ import { useEffect as useEffect36 } from "react";
8731
9049
 
8732
9050
  // src/hooks/usePreload.ts
8733
- import { useState as useState7, useEffect as useEffect34, useContext as useContext25 } from "react";
9051
+ import { useState as useState7, useEffect as useEffect35, useContext as useContext26 } from "react";
8734
9052
  function usePreload(assets) {
8735
- const engine = useContext25(EngineContext);
9053
+ const engine = useContext26(EngineContext);
8736
9054
  const [state, setState] = useState7({
8737
9055
  progress: assets.length === 0 ? 1 : 0,
8738
9056
  loaded: assets.length === 0,
8739
9057
  error: null
8740
9058
  });
8741
- useEffect34(() => {
9059
+ useEffect35(() => {
8742
9060
  if (assets.length === 0) {
8743
9061
  setState({ progress: 1, loaded: true, error: null });
8744
9062
  return;
@@ -8775,7 +9093,7 @@ function usePreload(assets) {
8775
9093
  import { Fragment as Fragment7, jsx as jsx14 } from "react/jsx-runtime";
8776
9094
  function AssetLoader({ assets, fallback = null, onError, children }) {
8777
9095
  const { loaded, error } = usePreload(assets);
8778
- useEffect35(() => {
9096
+ useEffect36(() => {
8779
9097
  if (error && onError) onError(error);
8780
9098
  }, [error, onError]);
8781
9099
  if (!loaded) {
@@ -8785,7 +9103,7 @@ function AssetLoader({ assets, fallback = null, onError, children }) {
8785
9103
  }
8786
9104
 
8787
9105
  // src/components/Circle.tsx
8788
- import { useEffect as useEffect36, useContext as useContext26 } from "react";
9106
+ import { useEffect as useEffect37, useContext as useContext27 } from "react";
8789
9107
  function Circle({
8790
9108
  radius = 16,
8791
9109
  color = "#ffffff",
@@ -8794,9 +9112,9 @@ function Circle({
8794
9112
  zIndex = 0,
8795
9113
  opacity = 1
8796
9114
  }) {
8797
- const engine = useContext26(EngineContext);
8798
- const entityId = useContext26(EntityContext);
8799
- useEffect36(() => {
9115
+ const engine = useContext27(EngineContext);
9116
+ const entityId = useContext27(EntityContext);
9117
+ useEffect37(() => {
8800
9118
  const comp = createCircleShape({
8801
9119
  radius,
8802
9120
  color,
@@ -8808,7 +9126,7 @@ function Circle({
8808
9126
  engine.ecs.addComponent(entityId, comp);
8809
9127
  return () => engine.ecs.removeComponent(entityId, "CircleShape");
8810
9128
  }, []);
8811
- useEffect36(() => {
9129
+ useEffect37(() => {
8812
9130
  const comp = engine.ecs.getComponent(entityId, "CircleShape");
8813
9131
  if (!comp) return;
8814
9132
  comp.radius = radius;
@@ -8822,7 +9140,7 @@ function Circle({
8822
9140
  }
8823
9141
 
8824
9142
  // src/components/Line.tsx
8825
- import { useEffect as useEffect37, useContext as useContext27 } from "react";
9143
+ import { useEffect as useEffect38, useContext as useContext28 } from "react";
8826
9144
  function Line({
8827
9145
  endX,
8828
9146
  endY,
@@ -8832,9 +9150,9 @@ function Line({
8832
9150
  opacity = 1,
8833
9151
  lineCap = "round"
8834
9152
  }) {
8835
- const engine = useContext27(EngineContext);
8836
- const entityId = useContext27(EntityContext);
8837
- useEffect37(() => {
9153
+ const engine = useContext28(EngineContext);
9154
+ const entityId = useContext28(EntityContext);
9155
+ useEffect38(() => {
8838
9156
  const comp = createLineShape({
8839
9157
  endX,
8840
9158
  endY,
@@ -8847,7 +9165,7 @@ function Line({
8847
9165
  engine.ecs.addComponent(entityId, comp);
8848
9166
  return () => engine.ecs.removeComponent(entityId, "LineShape");
8849
9167
  }, []);
8850
- useEffect37(() => {
9168
+ useEffect38(() => {
8851
9169
  const comp = engine.ecs.getComponent(entityId, "LineShape");
8852
9170
  if (!comp) return;
8853
9171
  comp.endX = endX;
@@ -8862,7 +9180,7 @@ function Line({
8862
9180
  }
8863
9181
 
8864
9182
  // src/components/Polygon.tsx
8865
- import { useEffect as useEffect38, useContext as useContext28 } from "react";
9183
+ import { useEffect as useEffect39, useContext as useContext29 } from "react";
8866
9184
  function Polygon({
8867
9185
  points,
8868
9186
  color = "#ffffff",
@@ -8872,9 +9190,9 @@ function Polygon({
8872
9190
  opacity = 1,
8873
9191
  closed = true
8874
9192
  }) {
8875
- const engine = useContext28(EngineContext);
8876
- const entityId = useContext28(EntityContext);
8877
- useEffect38(() => {
9193
+ const engine = useContext29(EngineContext);
9194
+ const entityId = useContext29(EntityContext);
9195
+ useEffect39(() => {
8878
9196
  const comp = createPolygonShape({
8879
9197
  points,
8880
9198
  color,
@@ -8887,7 +9205,7 @@ function Polygon({
8887
9205
  engine.ecs.addComponent(entityId, comp);
8888
9206
  return () => engine.ecs.removeComponent(entityId, "PolygonShape");
8889
9207
  }, []);
8890
- useEffect38(() => {
9208
+ useEffect39(() => {
8891
9209
  const comp = engine.ecs.getComponent(entityId, "PolygonShape");
8892
9210
  if (!comp) return;
8893
9211
  comp.points = points;
@@ -8902,7 +9220,7 @@ function Polygon({
8902
9220
  }
8903
9221
 
8904
9222
  // src/components/Gradient.tsx
8905
- import { useEffect as useEffect39, useContext as useContext29 } from "react";
9223
+ import { useEffect as useEffect40, useContext as useContext30 } from "react";
8906
9224
  function Gradient({
8907
9225
  gradientType = "linear",
8908
9226
  stops,
@@ -8915,9 +9233,9 @@ function Gradient({
8915
9233
  anchorX = 0.5,
8916
9234
  anchorY = 0.5
8917
9235
  }) {
8918
- const engine = useContext29(EngineContext);
8919
- const entityId = useContext29(EntityContext);
8920
- useEffect39(() => {
9236
+ const engine = useContext30(EngineContext);
9237
+ const entityId = useContext30(EntityContext);
9238
+ useEffect40(() => {
8921
9239
  const comp = createGradient({
8922
9240
  gradientType,
8923
9241
  stops,
@@ -8933,7 +9251,7 @@ function Gradient({
8933
9251
  engine.ecs.addComponent(entityId, comp);
8934
9252
  return () => engine.ecs.removeComponent(entityId, "Gradient");
8935
9253
  }, []);
8936
- useEffect39(() => {
9254
+ useEffect40(() => {
8937
9255
  const comp = engine.ecs.getComponent(entityId, "Gradient");
8938
9256
  if (!comp) return;
8939
9257
  comp.gradientType = gradientType;
@@ -8947,16 +9265,16 @@ function Gradient({
8947
9265
  }
8948
9266
 
8949
9267
  // src/components/Mask.tsx
8950
- import { useEffect as useEffect40, useContext as useContext30 } from "react";
9268
+ import { useEffect as useEffect41, useContext as useContext31 } from "react";
8951
9269
  function Mask({ shape = "rect", width = 64, height = 64, radius = 32, inverted = false }) {
8952
- const engine = useContext30(EngineContext);
8953
- const entityId = useContext30(EntityContext);
8954
- useEffect40(() => {
9270
+ const engine = useContext31(EngineContext);
9271
+ const entityId = useContext31(EntityContext);
9272
+ useEffect41(() => {
8955
9273
  const comp = createMask({ shape, width, height, radius, inverted });
8956
9274
  engine.ecs.addComponent(entityId, comp);
8957
9275
  return () => engine.ecs.removeComponent(entityId, "Mask");
8958
9276
  }, []);
8959
- useEffect40(() => {
9277
+ useEffect41(() => {
8960
9278
  const comp = engine.ecs.getComponent(entityId, "Mask");
8961
9279
  if (!comp) return;
8962
9280
  comp.shape = shape;
@@ -8969,11 +9287,11 @@ function Mask({ shape = "rect", width = 64, height = 64, radius = 32, inverted =
8969
9287
  }
8970
9288
 
8971
9289
  // src/components/Joint.tsx
8972
- import { useEffect as useEffect41, useContext as useContext31 } from "react";
9290
+ import { useEffect as useEffect42, useContext as useContext32 } from "react";
8973
9291
  function Joint({ type, target, anchorA, anchorB, length, stiffness, damping, maxLength }) {
8974
- const engine = useContext31(EngineContext);
8975
- const entityId = useContext31(EntityContext);
8976
- useEffect41(() => {
9292
+ const engine = useContext32(EngineContext);
9293
+ const entityId = useContext32(EntityContext);
9294
+ useEffect42(() => {
8977
9295
  const checkId = setTimeout(() => {
8978
9296
  const targetEntityId = engine.entityIds.get(target);
8979
9297
  if (!targetEntityId) {
@@ -9008,7 +9326,7 @@ function Joint({ type, target, anchorA, anchorB, length, stiffness, damping, max
9008
9326
  }
9009
9327
 
9010
9328
  // src/components/ConvexCollider.tsx
9011
- import { useEffect as useEffect42, useContext as useContext32 } from "react";
9329
+ import { useEffect as useEffect43, useContext as useContext33 } from "react";
9012
9330
  function ConvexCollider({
9013
9331
  vertices,
9014
9332
  offsetX = 0,
@@ -9022,9 +9340,9 @@ function ConvexCollider({
9022
9340
  restitutionCombineRule = "average",
9023
9341
  enabled = true
9024
9342
  }) {
9025
- const engine = useContext32(EngineContext);
9026
- const entityId = useContext32(EntityContext);
9027
- useEffect42(() => {
9343
+ const engine = useContext33(EngineContext);
9344
+ const entityId = useContext33(EntityContext);
9345
+ useEffect43(() => {
9028
9346
  engine.ecs.addComponent(
9029
9347
  entityId,
9030
9348
  createConvexPolygonCollider(vertices, {
@@ -9046,7 +9364,7 @@ function ConvexCollider({
9046
9364
  }
9047
9365
 
9048
9366
  // src/components/TriangleCollider.tsx
9049
- import { useEffect as useEffect43, useContext as useContext33 } from "react";
9367
+ import { useEffect as useEffect44, useContext as useContext34 } from "react";
9050
9368
  function TriangleCollider({
9051
9369
  a,
9052
9370
  b,
@@ -9062,9 +9380,9 @@ function TriangleCollider({
9062
9380
  restitutionCombineRule = "average",
9063
9381
  enabled = true
9064
9382
  }) {
9065
- const engine = useContext33(EngineContext);
9066
- const entityId = useContext33(EntityContext);
9067
- useEffect43(() => {
9383
+ const engine = useContext34(EngineContext);
9384
+ const entityId = useContext34(EntityContext);
9385
+ useEffect44(() => {
9068
9386
  engine.ecs.addComponent(
9069
9387
  entityId,
9070
9388
  createTriangleCollider(a, b, c, {
@@ -9086,7 +9404,7 @@ function TriangleCollider({
9086
9404
  }
9087
9405
 
9088
9406
  // src/components/SegmentCollider.tsx
9089
- import { useEffect as useEffect44, useContext as useContext34 } from "react";
9407
+ import { useEffect as useEffect45, useContext as useContext35 } from "react";
9090
9408
  function SegmentCollider({
9091
9409
  start: start2,
9092
9410
  end,
@@ -9100,9 +9418,9 @@ function SegmentCollider({
9100
9418
  restitutionCombineRule = "average",
9101
9419
  enabled = true
9102
9420
  }) {
9103
- const engine = useContext34(EngineContext);
9104
- const entityId = useContext34(EntityContext);
9105
- useEffect44(() => {
9421
+ const engine = useContext35(EngineContext);
9422
+ const entityId = useContext35(EntityContext);
9423
+ useEffect45(() => {
9106
9424
  engine.ecs.addComponent(
9107
9425
  entityId,
9108
9426
  createSegmentCollider(start2, end, {
@@ -9123,7 +9441,7 @@ function SegmentCollider({
9123
9441
  }
9124
9442
 
9125
9443
  // src/components/HeightFieldCollider.tsx
9126
- import { useEffect as useEffect45, useContext as useContext35 } from "react";
9444
+ import { useEffect as useEffect46, useContext as useContext36 } from "react";
9127
9445
  function HeightFieldCollider({
9128
9446
  heights,
9129
9447
  scaleX = 1,
@@ -9136,9 +9454,9 @@ function HeightFieldCollider({
9136
9454
  restitutionCombineRule = "average",
9137
9455
  enabled = true
9138
9456
  }) {
9139
- const engine = useContext35(EngineContext);
9140
- const entityId = useContext35(EntityContext);
9141
- useEffect45(() => {
9457
+ const engine = useContext36(EngineContext);
9458
+ const entityId = useContext36(EntityContext);
9459
+ useEffect46(() => {
9142
9460
  engine.ecs.addComponent(
9143
9461
  entityId,
9144
9462
  createHeightFieldCollider(heights, {
@@ -9159,7 +9477,7 @@ function HeightFieldCollider({
9159
9477
  }
9160
9478
 
9161
9479
  // src/components/HalfSpaceCollider.tsx
9162
- import { useEffect as useEffect46, useContext as useContext36 } from "react";
9480
+ import { useEffect as useEffect47, useContext as useContext37 } from "react";
9163
9481
  function HalfSpaceCollider({
9164
9482
  normalX = 0,
9165
9483
  normalY = -1,
@@ -9171,9 +9489,9 @@ function HalfSpaceCollider({
9171
9489
  restitutionCombineRule = "average",
9172
9490
  enabled = true
9173
9491
  }) {
9174
- const engine = useContext36(EngineContext);
9175
- const entityId = useContext36(EntityContext);
9176
- useEffect46(() => {
9492
+ const engine = useContext37(EngineContext);
9493
+ const entityId = useContext37(EntityContext);
9494
+ useEffect47(() => {
9177
9495
  engine.ecs.addComponent(
9178
9496
  entityId,
9179
9497
  createHalfSpaceCollider({
@@ -9194,7 +9512,7 @@ function HalfSpaceCollider({
9194
9512
  }
9195
9513
 
9196
9514
  // src/components/TriMeshCollider.tsx
9197
- import { useEffect as useEffect47, useContext as useContext37 } from "react";
9515
+ import { useEffect as useEffect48, useContext as useContext38 } from "react";
9198
9516
  function TriMeshCollider({
9199
9517
  vertices,
9200
9518
  indices,
@@ -9206,9 +9524,9 @@ function TriMeshCollider({
9206
9524
  restitutionCombineRule = "average",
9207
9525
  enabled = true
9208
9526
  }) {
9209
- const engine = useContext37(EngineContext);
9210
- const entityId = useContext37(EntityContext);
9211
- useEffect47(() => {
9527
+ const engine = useContext38(EngineContext);
9528
+ const entityId = useContext38(EntityContext);
9529
+ useEffect48(() => {
9212
9530
  engine.ecs.addComponent(
9213
9531
  entityId,
9214
9532
  createTriMeshCollider(vertices, indices, {
@@ -9227,9 +9545,9 @@ function TriMeshCollider({
9227
9545
  }
9228
9546
 
9229
9547
  // src/hooks/useGame.ts
9230
- import { useContext as useContext38 } from "react";
9548
+ import { useContext as useContext39 } from "react";
9231
9549
  function useGame() {
9232
- const engine = useContext38(EngineContext);
9550
+ const engine = useContext39(EngineContext);
9233
9551
  if (!engine) throw new Error("useGame must be used inside <Game>");
9234
9552
  return engine;
9235
9553
  }
@@ -9291,11 +9609,11 @@ function useCamera() {
9291
9609
  }
9292
9610
 
9293
9611
  // src/hooks/useCameraLookahead.ts
9294
- import { useContext as useContext39, useEffect as useEffect48 } from "react";
9612
+ import { useContext as useContext40, useEffect as useEffect49 } from "react";
9295
9613
  function useCameraLookahead(entityId, opts = {}) {
9296
- const engine = useContext39(EngineContext);
9614
+ const engine = useContext40(EngineContext);
9297
9615
  const { distance = 100, smoothing = 3, vertical = false } = opts;
9298
- useEffect48(() => {
9616
+ useEffect49(() => {
9299
9617
  let offsetX = 0;
9300
9618
  let offsetY = 0;
9301
9619
  const script = createScript((id, world, _input, dt) => {
@@ -9329,11 +9647,161 @@ function useCameraLookahead(entityId, opts = {}) {
9329
9647
  }, [distance, smoothing, vertical]);
9330
9648
  }
9331
9649
 
9332
- // src/hooks/useSnapshot.ts
9650
+ // src/hooks/useCameraBlend.ts
9333
9651
  import { useMemo as useMemo2 } from "react";
9652
+ var OVERRIDE_PRIORITY = 999999;
9653
+ var originalPriorities = /* @__PURE__ */ new WeakMap();
9654
+ function useCameraBlend() {
9655
+ const engine = useGame();
9656
+ return useMemo2(() => {
9657
+ function getOverrides() {
9658
+ if (!originalPriorities.has(engine)) originalPriorities.set(engine, /* @__PURE__ */ new Map());
9659
+ return originalPriorities.get(engine);
9660
+ }
9661
+ return {
9662
+ activate(id) {
9663
+ const entry = getRegistry(engine).get(id);
9664
+ if (entry) entry.active = true;
9665
+ },
9666
+ deactivate(id) {
9667
+ const entry = getRegistry(engine).get(id);
9668
+ if (entry) entry.active = false;
9669
+ },
9670
+ override(id) {
9671
+ const entry = getRegistry(engine).get(id);
9672
+ if (!entry) return;
9673
+ const overrides = getOverrides();
9674
+ if (!overrides.has(id)) overrides.set(id, entry.priority);
9675
+ entry.priority = OVERRIDE_PRIORITY;
9676
+ entry.active = true;
9677
+ },
9678
+ restore(id) {
9679
+ const entry = getRegistry(engine).get(id);
9680
+ if (!entry) return;
9681
+ const overrides = getOverrides();
9682
+ const original = overrides.get(id);
9683
+ if (original !== void 0) {
9684
+ entry.priority = original;
9685
+ overrides.delete(id);
9686
+ }
9687
+ },
9688
+ getActiveId() {
9689
+ const registry2 = getRegistry(engine);
9690
+ let best = null;
9691
+ for (const vc of registry2.values()) {
9692
+ if (!vc.active) continue;
9693
+ if (!best || vc.priority > best.priority) best = vc;
9694
+ }
9695
+ return best?.id ?? null;
9696
+ }
9697
+ };
9698
+ }, [engine]);
9699
+ }
9700
+
9701
+ // src/hooks/useCinematicSequence.ts
9702
+ import { useEffect as useEffect50, useRef as useRef17, useCallback as useCallback2, useState as useState8, useContext as useContext41 } from "react";
9703
+ function useCinematicSequence(steps, opts) {
9704
+ const engine = useContext41(EngineContext);
9705
+ const stepsRef = useRef17(steps);
9706
+ stepsRef.current = steps;
9707
+ const optsRef = useRef17(opts);
9708
+ optsRef.current = opts;
9709
+ const [isPlaying, setIsPlaying] = useState8(false);
9710
+ const [currentStep, setCurrentStep] = useState8(-1);
9711
+ const stateRef = useRef17({ playing: false, step: -1, elapsed: 0, scriptEid: null });
9712
+ const activateStep = useCallback2(
9713
+ (idx) => {
9714
+ const steps2 = stepsRef.current;
9715
+ if (idx < 0 || idx >= steps2.length) return;
9716
+ const step = steps2[idx];
9717
+ const registry2 = getRegistry(engine);
9718
+ for (const s2 of steps2) {
9719
+ if (s2.cameraId !== step.cameraId) {
9720
+ const entry2 = registry2.get(s2.cameraId);
9721
+ if (entry2) entry2.active = false;
9722
+ }
9723
+ }
9724
+ const entry = registry2.get(step.cameraId);
9725
+ if (entry) {
9726
+ if (step.blendDuration !== void 0) entry.blendDuration = step.blendDuration;
9727
+ entry.active = true;
9728
+ }
9729
+ },
9730
+ [engine]
9731
+ );
9732
+ const play = useCallback2(() => {
9733
+ const state = stateRef.current;
9734
+ state.playing = true;
9735
+ state.step = 0;
9736
+ state.elapsed = 0;
9737
+ setIsPlaying(true);
9738
+ setCurrentStep(0);
9739
+ activateStep(0);
9740
+ }, [activateStep]);
9741
+ const stop = useCallback2(() => {
9742
+ const state = stateRef.current;
9743
+ if (!state.playing) return;
9744
+ state.playing = false;
9745
+ setIsPlaying(false);
9746
+ setCurrentStep(-1);
9747
+ const registry2 = getRegistry(engine);
9748
+ for (const s2 of stepsRef.current) {
9749
+ const entry = registry2.get(s2.cameraId);
9750
+ if (entry) entry.active = false;
9751
+ }
9752
+ }, [engine]);
9753
+ useEffect50(() => {
9754
+ const state = stateRef.current;
9755
+ const eid = engine.ecs.createEntity();
9756
+ state.scriptEid = eid;
9757
+ engine.ecs.addComponent(
9758
+ eid,
9759
+ createScript((_id, _world, _input, dt) => {
9760
+ if (!state.playing) return;
9761
+ const steps2 = stepsRef.current;
9762
+ if (steps2.length === 0) return;
9763
+ const step = steps2[state.step];
9764
+ if (!step) return;
9765
+ if (step.holdFor === Infinity) return;
9766
+ state.elapsed += dt;
9767
+ if (state.elapsed >= step.holdFor) {
9768
+ state.elapsed = 0;
9769
+ const next = state.step + 1;
9770
+ if (next >= steps2.length) {
9771
+ state.playing = false;
9772
+ setIsPlaying(false);
9773
+ setCurrentStep(-1);
9774
+ optsRef.current?.onComplete?.();
9775
+ } else {
9776
+ state.step = next;
9777
+ setCurrentStep(next);
9778
+ activateStep(next);
9779
+ }
9780
+ }
9781
+ })
9782
+ );
9783
+ return () => {
9784
+ state.scriptEid = null;
9785
+ if (engine.ecs.hasEntity(eid)) engine.ecs.destroyEntity(eid);
9786
+ };
9787
+ }, [engine]);
9788
+ return {
9789
+ play,
9790
+ stop,
9791
+ get isPlaying() {
9792
+ return isPlaying;
9793
+ },
9794
+ get currentStep() {
9795
+ return currentStep;
9796
+ }
9797
+ };
9798
+ }
9799
+
9800
+ // src/hooks/useSnapshot.ts
9801
+ import { useMemo as useMemo3 } from "react";
9334
9802
  function useSnapshot() {
9335
9803
  const engine = useGame();
9336
- return useMemo2(
9804
+ return useMemo3(
9337
9805
  () => ({
9338
9806
  save: () => engine.ecs.getSnapshot(),
9339
9807
  restore: (snapshot) => engine.ecs.restoreSnapshot(snapshot)
@@ -9343,21 +9811,21 @@ function useSnapshot() {
9343
9811
  }
9344
9812
 
9345
9813
  // src/hooks/useEntity.ts
9346
- import { useContext as useContext40 } from "react";
9814
+ import { useContext as useContext42 } from "react";
9347
9815
  function useEntity() {
9348
- const id = useContext40(EntityContext);
9816
+ const id = useContext42(EntityContext);
9349
9817
  if (id === null) throw new Error("useEntity must be used inside <Entity>");
9350
9818
  return id;
9351
9819
  }
9352
9820
 
9353
9821
  // src/hooks/useDestroyEntity.ts
9354
- import { useCallback as useCallback2, useContext as useContext41 } from "react";
9822
+ import { useCallback as useCallback3, useContext as useContext43 } from "react";
9355
9823
  function useDestroyEntity() {
9356
- const engine = useContext41(EngineContext);
9357
- const entityId = useContext41(EntityContext);
9824
+ const engine = useContext43(EngineContext);
9825
+ const entityId = useContext43(EntityContext);
9358
9826
  if (!engine) throw new Error("useDestroyEntity must be used inside <Game>");
9359
9827
  if (entityId === null) throw new Error("useDestroyEntity must be used inside <Entity>");
9360
- return useCallback2(() => {
9828
+ return useCallback3(() => {
9361
9829
  if (engine.ecs.hasEntity(entityId)) {
9362
9830
  engine.ecs.destroyEntity(entityId);
9363
9831
  }
@@ -9365,19 +9833,19 @@ function useDestroyEntity() {
9365
9833
  }
9366
9834
 
9367
9835
  // src/hooks/useInput.ts
9368
- import { useContext as useContext42 } from "react";
9836
+ import { useContext as useContext44 } from "react";
9369
9837
  function useInput() {
9370
- const engine = useContext42(EngineContext);
9838
+ const engine = useContext44(EngineContext);
9371
9839
  if (!engine) throw new Error("useInput must be used inside <Game>");
9372
9840
  return engine.input;
9373
9841
  }
9374
9842
 
9375
9843
  // src/hooks/useInputMap.ts
9376
- import { useMemo as useMemo3 } from "react";
9844
+ import { useMemo as useMemo4 } from "react";
9377
9845
  function useInputMap(bindings) {
9378
9846
  const input = useInput();
9379
- const map = useMemo3(() => createInputMap(bindings), [JSON.stringify(bindings)]);
9380
- return useMemo3(
9847
+ const map = useMemo4(() => createInputMap(bindings), [JSON.stringify(bindings)]);
9848
+ return useMemo4(
9381
9849
  () => ({
9382
9850
  isActionDown: (action) => map.isActionDown(input, action),
9383
9851
  isActionPressed: (action) => map.isActionPressed(input, action),
@@ -9390,26 +9858,26 @@ function useInputMap(bindings) {
9390
9858
  }
9391
9859
 
9392
9860
  // src/hooks/useEvents.ts
9393
- import { useContext as useContext43, useEffect as useEffect49, useRef as useRef17 } from "react";
9861
+ import { useContext as useContext45, useEffect as useEffect51, useRef as useRef18 } from "react";
9394
9862
  function useEvents() {
9395
- const engine = useContext43(EngineContext);
9863
+ const engine = useContext45(EngineContext);
9396
9864
  if (!engine) throw new Error("useEvents must be used inside <Game>");
9397
9865
  return engine.events;
9398
9866
  }
9399
9867
  function useEvent(event, handler) {
9400
9868
  const events = useEvents();
9401
- const handlerRef = useRef17(handler);
9869
+ const handlerRef = useRef18(handler);
9402
9870
  handlerRef.current = handler;
9403
- useEffect49(() => {
9871
+ useEffect51(() => {
9404
9872
  return events.on(event, (data) => handlerRef.current(data));
9405
9873
  }, [events, event]);
9406
9874
  }
9407
9875
 
9408
9876
  // src/hooks/useCoordinates.ts
9409
- import { useCallback as useCallback3, useContext as useContext44 } from "react";
9877
+ import { useCallback as useCallback4, useContext as useContext46 } from "react";
9410
9878
  function useCoordinates() {
9411
- const engine = useContext44(EngineContext);
9412
- const worldToScreen = useCallback3(
9879
+ const engine = useContext46(EngineContext);
9880
+ const worldToScreen = useCallback4(
9413
9881
  (wx, wy) => {
9414
9882
  const canvas = engine.canvas;
9415
9883
  const camId = engine.ecs.queryOne("Camera2D");
@@ -9423,7 +9891,7 @@ function useCoordinates() {
9423
9891
  },
9424
9892
  [engine.ecs, engine.canvas]
9425
9893
  );
9426
- const screenToWorld3 = useCallback3(
9894
+ const screenToWorld3 = useCallback4(
9427
9895
  (sx, sy) => {
9428
9896
  const canvas = engine.canvas;
9429
9897
  const camId = engine.ecs.queryOne("Camera2D");
@@ -9441,14 +9909,14 @@ function useCoordinates() {
9441
9909
  }
9442
9910
 
9443
9911
  // src/hooks/useWorldQuery.ts
9444
- import { useEffect as useEffect50, useRef as useRef18, useState as useState8 } from "react";
9912
+ import { useEffect as useEffect52, useRef as useRef19, useState as useState9 } from "react";
9445
9913
  function useWorldQuery(...components) {
9446
9914
  const engine = useGame();
9447
- const [result, setResult] = useState8(() => engine.ecs.query(...components));
9448
- const prevRef = useRef18([]);
9449
- const rafRef = useRef18(0);
9450
- const mountedRef = useRef18(true);
9451
- useEffect50(() => {
9915
+ const [result, setResult] = useState9(() => engine.ecs.query(...components));
9916
+ const prevRef = useRef19([]);
9917
+ const rafRef = useRef19(0);
9918
+ const mountedRef = useRef19(true);
9919
+ useEffect52(() => {
9452
9920
  mountedRef.current = true;
9453
9921
  const tick2 = () => {
9454
9922
  if (!mountedRef.current) return;
@@ -9470,14 +9938,14 @@ function useWorldQuery(...components) {
9470
9938
  }
9471
9939
 
9472
9940
  // src/hooks/useInputContext.ts
9473
- import { useEffect as useEffect51, useMemo as useMemo4 } from "react";
9941
+ import { useEffect as useEffect53, useMemo as useMemo5 } from "react";
9474
9942
  function useInputContext(ctx) {
9475
- useEffect51(() => {
9943
+ useEffect53(() => {
9476
9944
  if (!ctx) return;
9477
9945
  globalInputContext.push(ctx);
9478
9946
  return () => globalInputContext.pop(ctx);
9479
9947
  }, [ctx]);
9480
- return useMemo4(
9948
+ return useMemo5(
9481
9949
  () => ({
9482
9950
  push: (c) => globalInputContext.push(c),
9483
9951
  pop: (c) => globalInputContext.pop(c),
@@ -9490,17 +9958,17 @@ function useInputContext(ctx) {
9490
9958
  }
9491
9959
 
9492
9960
  // src/hooks/usePlayerInput.ts
9493
- import { useMemo as useMemo5 } from "react";
9961
+ import { useMemo as useMemo6 } from "react";
9494
9962
  function usePlayerInput(playerId, bindings) {
9495
9963
  const input = useInput();
9496
- return useMemo5(() => createPlayerInput(playerId, bindings, input), [playerId, input, JSON.stringify(bindings)]);
9964
+ return useMemo6(() => createPlayerInput(playerId, bindings, input), [playerId, input, JSON.stringify(bindings)]);
9497
9965
  }
9498
9966
 
9499
9967
  // src/hooks/useLocalMultiplayer.ts
9500
- import { useMemo as useMemo6 } from "react";
9968
+ import { useMemo as useMemo7 } from "react";
9501
9969
  function useLocalMultiplayer(bindingsPerPlayer) {
9502
9970
  const input = useInput();
9503
- return useMemo6(
9971
+ return useMemo7(
9504
9972
  () => bindingsPerPlayer.map((bindings, i) => createPlayerInput(i + 1, bindings, input)),
9505
9973
  // eslint-disable-next-line react-hooks/exhaustive-deps
9506
9974
  [input, JSON.stringify(bindingsPerPlayer)]
@@ -9508,18 +9976,18 @@ function useLocalMultiplayer(bindingsPerPlayer) {
9508
9976
  }
9509
9977
 
9510
9978
  // src/hooks/useInputRecorder.ts
9511
- import { useMemo as useMemo7 } from "react";
9979
+ import { useMemo as useMemo8 } from "react";
9512
9980
  function useInputRecorder() {
9513
- return useMemo7(() => createInputRecorder(), []);
9981
+ return useMemo8(() => createInputRecorder(), []);
9514
9982
  }
9515
9983
 
9516
9984
  // src/hooks/useGamepad.ts
9517
- import { useEffect as useEffect52, useRef as useRef19, useState as useState9 } from "react";
9985
+ import { useEffect as useEffect54, useRef as useRef20, useState as useState10 } from "react";
9518
9986
  var EMPTY_STATE = { connected: false, axes: [], buttons: [] };
9519
9987
  function useGamepad(playerIndex = 0) {
9520
- const [state, setState] = useState9(EMPTY_STATE);
9521
- const rafRef = useRef19(0);
9522
- useEffect52(() => {
9988
+ const [state, setState] = useState10(EMPTY_STATE);
9989
+ const rafRef = useRef20(0);
9990
+ useEffect54(() => {
9523
9991
  const poll = () => {
9524
9992
  const gp = navigator.getGamepads()[playerIndex];
9525
9993
  if (gp) {
@@ -9540,19 +10008,19 @@ function useGamepad(playerIndex = 0) {
9540
10008
  }
9541
10009
 
9542
10010
  // src/hooks/usePause.ts
9543
- import { useContext as useContext45, useState as useState10, useCallback as useCallback4 } from "react";
10011
+ import { useContext as useContext47, useState as useState11, useCallback as useCallback5 } from "react";
9544
10012
  function usePause() {
9545
- const engine = useContext45(EngineContext);
9546
- const [paused, setPaused] = useState10(false);
9547
- const pause = useCallback4(() => {
10013
+ const engine = useContext47(EngineContext);
10014
+ const [paused, setPaused] = useState11(false);
10015
+ const pause = useCallback5(() => {
9548
10016
  engine.loop.pause();
9549
10017
  setPaused(true);
9550
10018
  }, [engine]);
9551
- const resume = useCallback4(() => {
10019
+ const resume = useCallback5(() => {
9552
10020
  engine.loop.resume();
9553
10021
  setPaused(false);
9554
10022
  }, [engine]);
9555
- const toggle = useCallback4(() => {
10023
+ const toggle = useCallback5(() => {
9556
10024
  if (engine.loop.isPaused) resume();
9557
10025
  else pause();
9558
10026
  }, [engine, pause, resume]);
@@ -9560,7 +10028,7 @@ function usePause() {
9560
10028
  }
9561
10029
 
9562
10030
  // src/hooks/useProfiler.ts
9563
- import { useContext as useContext46, useEffect as useEffect53, useRef as useRef20, useState as useState11 } from "react";
10031
+ import { useContext as useContext48, useEffect as useEffect55, useRef as useRef21, useState as useState12 } from "react";
9564
10032
  var EMPTY = {
9565
10033
  fps: 0,
9566
10034
  frameTime: 0,
@@ -9568,12 +10036,12 @@ var EMPTY = {
9568
10036
  systemTimings: /* @__PURE__ */ new Map()
9569
10037
  };
9570
10038
  function useProfiler() {
9571
- const engine = useContext46(EngineContext);
9572
- const [data, setData] = useState11(EMPTY);
9573
- const frameTimesRef = useRef20([]);
9574
- const lastUpdateRef = useRef20(0);
9575
- const prevTimeRef = useRef20(0);
9576
- useEffect53(() => {
10039
+ const engine = useContext48(EngineContext);
10040
+ const [data, setData] = useState12(EMPTY);
10041
+ const frameTimesRef = useRef21([]);
10042
+ const lastUpdateRef = useRef21(0);
10043
+ const prevTimeRef = useRef21(0);
10044
+ useEffect55(() => {
9577
10045
  if (!engine) return;
9578
10046
  let rafId2;
9579
10047
  const frameTimes = frameTimesRef.current;
@@ -9613,10 +10081,10 @@ function useProfiler() {
9613
10081
  }
9614
10082
 
9615
10083
  // src/hooks/usePostProcess.ts
9616
- import { useEffect as useEffect54 } from "react";
10084
+ import { useEffect as useEffect56 } from "react";
9617
10085
  function usePostProcess(effect) {
9618
10086
  const engine = useGame();
9619
- useEffect54(() => {
10087
+ useEffect56(() => {
9620
10088
  engine.postProcessStack.add(effect);
9621
10089
  return () => {
9622
10090
  engine.postProcessStack.remove(effect);
@@ -9625,11 +10093,11 @@ function usePostProcess(effect) {
9625
10093
  }
9626
10094
 
9627
10095
  // src/hooks/useWebGLPostProcess.ts
9628
- import { useEffect as useEffect55, useMemo as useMemo8 } from "react";
10096
+ import { useEffect as useEffect57, useMemo as useMemo9 } from "react";
9629
10097
  function useWebGLPostProcess(opts) {
9630
10098
  const engine = useGame();
9631
- const optsKey = useMemo8(() => JSON.stringify(opts), [JSON.stringify(opts)]);
9632
- useEffect55(() => {
10099
+ const optsKey = useMemo9(() => JSON.stringify(opts), [JSON.stringify(opts)]);
10100
+ useEffect57(() => {
9633
10101
  const rs = engine.activeRenderSystem;
9634
10102
  rs.setPostProcessOptions?.(opts);
9635
10103
  return () => {
@@ -9639,10 +10107,10 @@ function useWebGLPostProcess(opts) {
9639
10107
  }
9640
10108
 
9641
10109
  // src/hooks/useAudioListener.ts
9642
- import { useEffect as useEffect56, useContext as useContext47 } from "react";
10110
+ import { useEffect as useEffect58, useContext as useContext49 } from "react";
9643
10111
  function useAudioListener() {
9644
- const engine = useContext47(EngineContext);
9645
- useEffect56(() => {
10112
+ const engine = useContext49(EngineContext);
10113
+ useEffect58(() => {
9646
10114
  const eid = engine.ecs.createEntity();
9647
10115
  engine.ecs.addComponent(
9648
10116
  eid,
@@ -9661,9 +10129,9 @@ function useAudioListener() {
9661
10129
  }
9662
10130
 
9663
10131
  // src/hooks/useTouch.ts
9664
- import { useContext as useContext48 } from "react";
10132
+ import { useContext as useContext50 } from "react";
9665
10133
  function useTouch() {
9666
- const engine = useContext48(EngineContext);
10134
+ const engine = useContext50(EngineContext);
9667
10135
  if (!engine) throw new Error("useTouch must be used inside <Game>");
9668
10136
  const t = engine.input.touch;
9669
10137
  return {
@@ -9686,13 +10154,13 @@ function useTouch() {
9686
10154
  }
9687
10155
 
9688
10156
  // src/hooks/useGestures.ts
9689
- import { useEffect as useEffect57, useRef as useRef21 } from "react";
10157
+ import { useEffect as useEffect59, useRef as useRef22 } from "react";
9690
10158
  function useGestures(handlers, opts = {}) {
9691
- const handlersRef = useRef21(handlers);
10159
+ const handlersRef = useRef22(handlers);
9692
10160
  handlersRef.current = handlers;
9693
- const optsRef = useRef21(opts);
10161
+ const optsRef = useRef22(opts);
9694
10162
  optsRef.current = opts;
9695
- useEffect57(() => {
10163
+ useEffect59(() => {
9696
10164
  const target = optsRef.current.target ?? window;
9697
10165
  let starts = [];
9698
10166
  let longPressTimer = null;
@@ -9827,14 +10295,14 @@ function useGamepadHaptics(playerIndex = 0) {
9827
10295
  }
9828
10296
 
9829
10297
  // src/hooks/useTimer.ts
9830
- import { useRef as useRef22, useCallback as useCallback5, useEffect as useEffect58, useContext as useContext49 } from "react";
10298
+ import { useRef as useRef23, useCallback as useCallback6, useEffect as useEffect60, useContext as useContext51 } from "react";
9831
10299
  function useTimer(duration, onComplete, opts) {
9832
- const engine = useContext49(EngineContext);
9833
- const onCompleteRef = useRef22(onComplete);
10300
+ const engine = useContext51(EngineContext);
10301
+ const onCompleteRef = useRef23(onComplete);
9834
10302
  onCompleteRef.current = onComplete;
9835
- const loopRef = useRef22(opts?.loop ?? false);
10303
+ const loopRef = useRef23(opts?.loop ?? false);
9836
10304
  loopRef.current = opts?.loop ?? false;
9837
- const timerRef = useRef22(null);
10305
+ const timerRef = useRef23(null);
9838
10306
  if (!timerRef.current) {
9839
10307
  timerRef.current = createTimer(
9840
10308
  duration,
@@ -9847,7 +10315,7 @@ function useTimer(duration, onComplete, opts) {
9847
10315
  opts?.autoStart ?? false
9848
10316
  );
9849
10317
  }
9850
- useEffect58(() => {
10318
+ useEffect60(() => {
9851
10319
  const eid = engine.ecs.createEntity();
9852
10320
  engine.ecs.addComponent(
9853
10321
  eid,
@@ -9859,13 +10327,13 @@ function useTimer(duration, onComplete, opts) {
9859
10327
  if (engine.ecs.hasEntity(eid)) engine.ecs.destroyEntity(eid);
9860
10328
  };
9861
10329
  }, [engine.ecs]);
9862
- const start2 = useCallback5(() => {
10330
+ const start2 = useCallback6(() => {
9863
10331
  timerRef.current.start();
9864
10332
  }, []);
9865
- const stop = useCallback5(() => {
10333
+ const stop = useCallback6(() => {
9866
10334
  timerRef.current.stop();
9867
10335
  }, []);
9868
- const reset = useCallback5(() => {
10336
+ const reset = useCallback6(() => {
9869
10337
  timerRef.current.reset();
9870
10338
  }, []);
9871
10339
  return {
@@ -9888,15 +10356,15 @@ function useTimer(duration, onComplete, opts) {
9888
10356
  }
9889
10357
 
9890
10358
  // src/hooks/useCoroutine.ts
9891
- import { useRef as useRef23, useEffect as useEffect59, useContext as useContext50 } from "react";
10359
+ import { useRef as useRef24, useEffect as useEffect61, useContext as useContext52 } from "react";
9892
10360
  var wait = (seconds) => ({ type: "wait", seconds });
9893
10361
  var waitFrames = (frames) => ({ type: "waitFrames", frames });
9894
10362
  var waitUntil = (condition) => ({ type: "waitUntil", condition });
9895
10363
  var nextCoroutineId = 1;
9896
10364
  function useCoroutine() {
9897
- const engine = useContext50(EngineContext);
9898
- const coroutinesRef = useRef23(/* @__PURE__ */ new Map());
9899
- useEffect59(() => {
10365
+ const engine = useContext52(EngineContext);
10366
+ const coroutinesRef = useRef24(/* @__PURE__ */ new Map());
10367
+ useEffect61(() => {
9900
10368
  const eid = engine.ecs.createEntity();
9901
10369
  engine.ecs.addComponent(
9902
10370
  eid,
@@ -9961,33 +10429,33 @@ function useCoroutine() {
9961
10429
  return coroutinesRef.current.size;
9962
10430
  }
9963
10431
  };
9964
- const controlsRef = useRef23(controls);
10432
+ const controlsRef = useRef24(controls);
9965
10433
  return controlsRef.current;
9966
10434
  }
9967
10435
 
9968
10436
  // src/hooks/useSceneManager.ts
9969
- import { useState as useState12, useCallback as useCallback6, useRef as useRef24 } from "react";
10437
+ import { useState as useState13, useCallback as useCallback7, useRef as useRef25 } from "react";
9970
10438
  function useSceneManager(initialScene) {
9971
- const [stack, setStack] = useState12([initialScene]);
9972
- const stackRef = useRef24(stack);
10439
+ const [stack, setStack] = useState13([initialScene]);
10440
+ const stackRef = useRef25(stack);
9973
10441
  stackRef.current = stack;
9974
- const push = useCallback6((scene) => {
10442
+ const push = useCallback7((scene) => {
9975
10443
  setStack((prev) => [...prev, scene]);
9976
10444
  }, []);
9977
- const pop = useCallback6(() => {
10445
+ const pop = useCallback7(() => {
9978
10446
  const prev = stackRef.current;
9979
10447
  if (prev.length <= 1) return void 0;
9980
10448
  const popped = prev[prev.length - 1];
9981
10449
  setStack(prev.slice(0, -1));
9982
10450
  return popped;
9983
10451
  }, []);
9984
- const replace = useCallback6((scene) => {
10452
+ const replace = useCallback7((scene) => {
9985
10453
  setStack((prev) => [...prev.slice(0, -1), scene]);
9986
10454
  }, []);
9987
- const reset = useCallback6((scene) => {
10455
+ const reset = useCallback7((scene) => {
9988
10456
  setStack([scene]);
9989
10457
  }, []);
9990
- const has = useCallback6(
10458
+ const has = useCallback7(
9991
10459
  (scene) => {
9992
10460
  return stack.includes(scene);
9993
10461
  },
@@ -10005,20 +10473,20 @@ function useSceneManager(initialScene) {
10005
10473
  }
10006
10474
 
10007
10475
  // src/hooks/useSceneTransition.ts
10008
- import { useState as useState13, useCallback as useCallback7, useRef as useRef25, useEffect as useEffect60 } from "react";
10476
+ import { useState as useState14, useCallback as useCallback8, useRef as useRef26, useEffect as useEffect62 } from "react";
10009
10477
  function useSceneTransition(initialScene, defaultTransition) {
10010
- const [stack, setStack] = useState13([initialScene]);
10011
- const [transState, setTransState] = useState13({
10478
+ const [stack, setStack] = useState14([initialScene]);
10479
+ const [transState, setTransState] = useState14({
10012
10480
  phase: "idle",
10013
10481
  progress: 0,
10014
10482
  effect: null,
10015
10483
  pendingAction: null
10016
10484
  });
10017
- const stackRef = useRef25(stack);
10485
+ const stackRef = useRef26(stack);
10018
10486
  stackRef.current = stack;
10019
- const transRef = useRef25(transState);
10487
+ const transRef = useRef26(transState);
10020
10488
  transRef.current = transState;
10021
- useEffect60(() => {
10489
+ useEffect62(() => {
10022
10490
  if (transState.phase === "idle") return;
10023
10491
  const effect = transState.effect;
10024
10492
  if (!effect || effect.type === "instant") return;
@@ -10047,7 +10515,7 @@ function useSceneTransition(initialScene, defaultTransition) {
10047
10515
  rafId2 = requestAnimationFrame(animate);
10048
10516
  return () => cancelAnimationFrame(rafId2);
10049
10517
  }, [transState.phase, transState.effect]);
10050
- const startTransition = useCallback7(
10518
+ const startTransition = useCallback8(
10051
10519
  (effect, action) => {
10052
10520
  const eff = effect ?? defaultTransition ?? { type: "instant" };
10053
10521
  if (eff.type === "instant" || isReducedMotionPreferred()) {
@@ -10063,13 +10531,13 @@ function useSceneTransition(initialScene, defaultTransition) {
10063
10531
  },
10064
10532
  [defaultTransition]
10065
10533
  );
10066
- const push = useCallback7(
10534
+ const push = useCallback8(
10067
10535
  (scene, transition) => {
10068
10536
  startTransition(transition, () => setStack((prev) => [...prev, scene]));
10069
10537
  },
10070
10538
  [startTransition]
10071
10539
  );
10072
- const pop = useCallback7(
10540
+ const pop = useCallback8(
10073
10541
  (transition) => {
10074
10542
  const prev = stackRef.current;
10075
10543
  if (prev.length <= 1) return void 0;
@@ -10079,13 +10547,13 @@ function useSceneTransition(initialScene, defaultTransition) {
10079
10547
  },
10080
10548
  [startTransition]
10081
10549
  );
10082
- const replace = useCallback7(
10550
+ const replace = useCallback8(
10083
10551
  (scene, transition) => {
10084
10552
  startTransition(transition, () => setStack((prev) => [...prev.slice(0, -1), scene]));
10085
10553
  },
10086
10554
  [startTransition]
10087
10555
  );
10088
- const reset = useCallback7(
10556
+ const reset = useCallback8(
10089
10557
  (scene, transition) => {
10090
10558
  startTransition(transition, () => setStack([scene]));
10091
10559
  },
@@ -10186,10 +10654,10 @@ function SceneTransitionOverlay({ controls }) {
10186
10654
  }
10187
10655
 
10188
10656
  // src/hooks/useHitstop.ts
10189
- import { useContext as useContext51, useCallback as useCallback8 } from "react";
10657
+ import { useContext as useContext53, useCallback as useCallback9 } from "react";
10190
10658
  function useHitstop() {
10191
- const engine = useContext51(EngineContext);
10192
- const freeze = useCallback8(
10659
+ const engine = useContext53(EngineContext);
10660
+ const freeze = useCallback9(
10193
10661
  (seconds) => {
10194
10662
  engine.loop.hitPause(seconds);
10195
10663
  },
@@ -10199,16 +10667,16 @@ function useHitstop() {
10199
10667
  }
10200
10668
 
10201
10669
  // src/hooks/useInputBuffer.ts
10202
- import { useMemo as useMemo9 } from "react";
10670
+ import { useMemo as useMemo10 } from "react";
10203
10671
  function useInputBuffer(opts) {
10204
- return useMemo9(() => new InputBuffer(opts), []);
10672
+ return useMemo10(() => new InputBuffer(opts), []);
10205
10673
  }
10206
10674
 
10207
10675
  // src/hooks/useComboDetector.ts
10208
- import { useMemo as useMemo10, useRef as useRef26 } from "react";
10676
+ import { useMemo as useMemo11, useRef as useRef27 } from "react";
10209
10677
  function useComboDetector(combos) {
10210
- const lastComboRef = useRef26(null);
10211
- const result = useMemo10(() => {
10678
+ const lastComboRef = useRef27(null);
10679
+ const result = useMemo11(() => {
10212
10680
  const detector = new ComboDetector({ combos });
10213
10681
  const api = {
10214
10682
  feed(action) {
@@ -10230,11 +10698,11 @@ function useComboDetector(combos) {
10230
10698
  }
10231
10699
 
10232
10700
  // src/hooks/useParent.ts
10233
- import { useEffect as useEffect61, useContext as useContext52 } from "react";
10701
+ import { useEffect as useEffect63, useContext as useContext54 } from "react";
10234
10702
  function useParent(childEntityId, parentEntityId) {
10235
- const engine = useContext52(EngineContext);
10703
+ const engine = useContext54(EngineContext);
10236
10704
  if (!engine) throw new Error("useParent must be used inside <Game>");
10237
- useEffect61(() => {
10705
+ useEffect63(() => {
10238
10706
  setParent(engine.ecs, childEntityId, parentEntityId);
10239
10707
  return () => {
10240
10708
  removeParent(engine.ecs, childEntityId);
@@ -10243,7 +10711,7 @@ function useParent(childEntityId, parentEntityId) {
10243
10711
  }
10244
10712
 
10245
10713
  // src/hooks/useAccessibility.ts
10246
- import { useCallback as useCallback9, useSyncExternalStore } from "react";
10714
+ import { useCallback as useCallback10, useSyncExternalStore } from "react";
10247
10715
  var _version = 0;
10248
10716
  var _listeners = /* @__PURE__ */ new Set();
10249
10717
  function subscribe(cb) {
@@ -10255,12 +10723,12 @@ function getSnapshot() {
10255
10723
  }
10256
10724
  function useAccessibility() {
10257
10725
  useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
10258
- const setOptions = useCallback9((opts) => {
10726
+ const setOptions = useCallback10((opts) => {
10259
10727
  setAccessibilityOptions(opts);
10260
10728
  _version++;
10261
10729
  for (const cb of _listeners) cb();
10262
10730
  }, []);
10263
- const announce = useCallback9((text, priority) => {
10731
+ const announce = useCallback10((text, priority) => {
10264
10732
  announceToScreenReader(text, priority);
10265
10733
  }, []);
10266
10734
  return {
@@ -10271,18 +10739,18 @@ function useAccessibility() {
10271
10739
  }
10272
10740
 
10273
10741
  // src/hooks/useHMR.ts
10274
- import { useEffect as useEffect62, useRef as useRef27, useMemo as useMemo11 } from "react";
10742
+ import { useEffect as useEffect64, useRef as useRef28, useMemo as useMemo12 } from "react";
10275
10743
  var idCounter = 0;
10276
10744
  function useHMR(hmrKey) {
10277
- const idRef = useRef27(null);
10745
+ const idRef = useRef28(null);
10278
10746
  if (idRef.current === null) {
10279
10747
  idRef.current = hmrKey ?? `__hmr_${idCounter++}`;
10280
10748
  }
10281
10749
  const key = idRef.current;
10282
10750
  const isHotReload = hmrLoadState(key) !== void 0;
10283
- const disposeRef = useRef27(null);
10284
- const acceptRef = useRef27(null);
10285
- useEffect62(() => {
10751
+ const disposeRef = useRef28(null);
10752
+ const acceptRef = useRef28(null);
10753
+ useEffect64(() => {
10286
10754
  const hot = import.meta.hot;
10287
10755
  if (!hot) return;
10288
10756
  hot.dispose(() => {
@@ -10296,7 +10764,7 @@ function useHMR(hmrKey) {
10296
10764
  acceptRef.current(prev);
10297
10765
  }
10298
10766
  }, [key]);
10299
- return useMemo11(
10767
+ return useMemo12(
10300
10768
  () => ({
10301
10769
  onDispose(handler) {
10302
10770
  disposeRef.current = handler;
@@ -10315,11 +10783,11 @@ function useHMR(hmrKey) {
10315
10783
  }
10316
10784
 
10317
10785
  // src/hooks/useSquashStretch.ts
10318
- import { useCallback as useCallback10, useContext as useContext53 } from "react";
10786
+ import { useCallback as useCallback11, useContext as useContext55 } from "react";
10319
10787
  function useSquashStretch() {
10320
- const engine = useContext53(EngineContext);
10321
- const entityId = useContext53(EntityContext);
10322
- const trigger = useCallback10(
10788
+ const engine = useContext55(EngineContext);
10789
+ const entityId = useContext55(EntityContext);
10790
+ const trigger = useCallback11(
10323
10791
  (scaleX, scaleY) => {
10324
10792
  const ss = engine.ecs.getComponent(entityId, "SquashStretch");
10325
10793
  if (!ss) return;
@@ -10332,16 +10800,16 @@ function useSquashStretch() {
10332
10800
  }
10333
10801
 
10334
10802
  // src/hooks/useHistory.ts
10335
- import { useCallback as useCallback11, useContext as useContext54, useEffect as useEffect63, useRef as useRef28, useState as useState14 } from "react";
10803
+ import { useCallback as useCallback12, useContext as useContext56, useEffect as useEffect65, useRef as useRef29, useState as useState15 } from "react";
10336
10804
  function useHistory(options) {
10337
- const engine = useContext54(EngineContext);
10805
+ const engine = useContext56(EngineContext);
10338
10806
  const capacity = options?.capacity ?? 50;
10339
10807
  const bindKeys = options?.bindKeyboardShortcuts ?? false;
10340
- const stackRef = useRef28([]);
10341
- const indexRef = useRef28(-1);
10342
- const [version, setVersion] = useState14(0);
10343
- const bump = useCallback11(() => setVersion((v) => v + 1), []);
10344
- const push = useCallback11(() => {
10808
+ const stackRef = useRef29([]);
10809
+ const indexRef = useRef29(-1);
10810
+ const [version, setVersion] = useState15(0);
10811
+ const bump = useCallback12(() => setVersion((v) => v + 1), []);
10812
+ const push = useCallback12(() => {
10345
10813
  const snap = engine.ecs.getSnapshot();
10346
10814
  const stack = stackRef.current;
10347
10815
  if (indexRef.current < stack.length - 1) {
@@ -10352,14 +10820,14 @@ function useHistory(options) {
10352
10820
  indexRef.current = stack.length - 1;
10353
10821
  bump();
10354
10822
  }, [engine, capacity, bump]);
10355
- const undo = useCallback11(() => {
10823
+ const undo = useCallback12(() => {
10356
10824
  if (indexRef.current <= 0) return;
10357
10825
  indexRef.current -= 1;
10358
10826
  engine.ecs.restoreSnapshot(stackRef.current[indexRef.current]);
10359
10827
  engine.loop.markDirty();
10360
10828
  bump();
10361
10829
  }, [engine, bump]);
10362
- const redo = useCallback11(() => {
10830
+ const redo = useCallback12(() => {
10363
10831
  const stack = stackRef.current;
10364
10832
  if (indexRef.current >= stack.length - 1) return;
10365
10833
  indexRef.current += 1;
@@ -10367,12 +10835,12 @@ function useHistory(options) {
10367
10835
  engine.loop.markDirty();
10368
10836
  bump();
10369
10837
  }, [engine, bump]);
10370
- const clear = useCallback11(() => {
10838
+ const clear = useCallback12(() => {
10371
10839
  stackRef.current = [];
10372
10840
  indexRef.current = -1;
10373
10841
  bump();
10374
10842
  }, [bump]);
10375
- useEffect63(() => {
10843
+ useEffect65(() => {
10376
10844
  if (!bindKeys) return;
10377
10845
  const onKey = (e) => {
10378
10846
  const mod = e.ctrlKey || e.metaKey;
@@ -10397,13 +10865,13 @@ function useHistory(options) {
10397
10865
  }
10398
10866
 
10399
10867
  // src/hooks/useSelection.tsx
10400
- import { createContext as createContext2, useCallback as useCallback12, useContext as useContext55, useEffect as useEffect64, useMemo as useMemo12, useState as useState15 } from "react";
10868
+ import { createContext as createContext2, useCallback as useCallback13, useContext as useContext57, useEffect as useEffect66, useMemo as useMemo13, useState as useState16 } from "react";
10401
10869
  import { jsx as jsx15 } from "react/jsx-runtime";
10402
10870
  var SelectionContext = createContext2(null);
10403
10871
  function Selection({ initial, onChange, children }) {
10404
- const engine = useContext55(EngineContext);
10405
- const [selected, setSelected] = useState15(initial ?? []);
10406
- useEffect64(() => {
10872
+ const engine = useContext57(EngineContext);
10873
+ const [selected, setSelected] = useState16(initial ?? []);
10874
+ useEffect66(() => {
10407
10875
  if (!engine) return;
10408
10876
  return engine.ecs.onDestroyEntity((destroyedId) => {
10409
10877
  setSelected((prev) => {
@@ -10414,14 +10882,14 @@ function Selection({ initial, onChange, children }) {
10414
10882
  });
10415
10883
  });
10416
10884
  }, [engine, onChange]);
10417
- const commit = useCallback12(
10885
+ const commit = useCallback13(
10418
10886
  (next) => {
10419
10887
  setSelected(next);
10420
10888
  onChange?.(next);
10421
10889
  },
10422
10890
  [onChange]
10423
10891
  );
10424
- const select = useCallback12(
10892
+ const select = useCallback13(
10425
10893
  (id, opts) => {
10426
10894
  if (opts?.additive) {
10427
10895
  setSelected((prev) => {
@@ -10436,7 +10904,7 @@ function Selection({ initial, onChange, children }) {
10436
10904
  },
10437
10905
  [commit, onChange]
10438
10906
  );
10439
- const deselect = useCallback12(
10907
+ const deselect = useCallback13(
10440
10908
  (id) => {
10441
10909
  setSelected((prev) => {
10442
10910
  if (!prev.includes(id)) return prev;
@@ -10447,7 +10915,7 @@ function Selection({ initial, onChange, children }) {
10447
10915
  },
10448
10916
  [onChange]
10449
10917
  );
10450
- const toggle = useCallback12(
10918
+ const toggle = useCallback13(
10451
10919
  (id) => {
10452
10920
  setSelected((prev) => {
10453
10921
  const next = prev.includes(id) ? prev.filter((x) => x !== id) : [...prev, id];
@@ -10457,25 +10925,25 @@ function Selection({ initial, onChange, children }) {
10457
10925
  },
10458
10926
  [onChange]
10459
10927
  );
10460
- const clear = useCallback12(() => commit([]), [commit]);
10461
- const isSelected = useCallback12((id) => selected.includes(id), [selected]);
10462
- const value = useMemo12(
10928
+ const clear = useCallback13(() => commit([]), [commit]);
10929
+ const isSelected = useCallback13((id) => selected.includes(id), [selected]);
10930
+ const value = useMemo13(
10463
10931
  () => ({ selected, select, deselect, toggle, clear, isSelected }),
10464
10932
  [selected, select, deselect, toggle, clear, isSelected]
10465
10933
  );
10466
10934
  return /* @__PURE__ */ jsx15(SelectionContext.Provider, { value, children });
10467
10935
  }
10468
10936
  function useSelection() {
10469
- const ctx = useContext55(SelectionContext);
10937
+ const ctx = useContext57(SelectionContext);
10470
10938
  if (!ctx) throw new Error("useSelection must be used inside a <Selection> provider");
10471
10939
  return ctx;
10472
10940
  }
10473
10941
 
10474
10942
  // src/components/TransformHandles.tsx
10475
- import { useContext as useContext56, useEffect as useEffect66, useRef as useRef29 } from "react";
10943
+ import { useContext as useContext58, useEffect as useEffect68, useRef as useRef30 } from "react";
10476
10944
 
10477
10945
  // src/hooks/useOverlayTick.ts
10478
- import { useEffect as useEffect65 } from "react";
10946
+ import { useEffect as useEffect67 } from "react";
10479
10947
  var callbacks = /* @__PURE__ */ new Set();
10480
10948
  var rafId = 0;
10481
10949
  function tick() {
@@ -10498,7 +10966,7 @@ function register(cb) {
10498
10966
  };
10499
10967
  }
10500
10968
  function useOverlayTick(callback, deps = []) {
10501
- useEffect65(() => {
10969
+ useEffect67(() => {
10502
10970
  return register(callback);
10503
10971
  }, deps);
10504
10972
  }
@@ -10512,10 +10980,10 @@ function TransformHandles({
10512
10980
  color = "#4fc3f7",
10513
10981
  handleSize = 10
10514
10982
  }) {
10515
- const engine = useContext56(EngineContext);
10983
+ const engine = useContext58(EngineContext);
10516
10984
  const selection = useSelection();
10517
- const overlayRef = useRef29(null);
10518
- const dragRef = useRef29(null);
10985
+ const overlayRef = useRef30(null);
10986
+ const dragRef = useRef30(null);
10519
10987
  useOverlayTick(() => {
10520
10988
  if (!engine) return;
10521
10989
  const overlay = overlayRef.current;
@@ -10550,7 +11018,7 @@ function TransformHandles({
10550
11018
  node.style.transform = `translate(-50%, -50%) rotate(${t.rotation}rad)`;
10551
11019
  }
10552
11020
  }, [engine, selection.selected]);
10553
- useEffect66(() => {
11021
+ useEffect68(() => {
10554
11022
  if (!engine) return;
10555
11023
  const onMove = (e) => {
10556
11024
  const drag = dragRef.current;
@@ -10774,15 +11242,15 @@ function worldToScreenCss(engine, wx, wy) {
10774
11242
  }
10775
11243
 
10776
11244
  // src/hooks/useSnap.ts
10777
- import { useCallback as useCallback13, useContext as useContext57 } from "react";
11245
+ import { useCallback as useCallback14, useContext as useContext59 } from "react";
10778
11246
  function useSnap(options) {
10779
- const engine = useContext57(EngineContext);
11247
+ const engine = useContext59(EngineContext);
10780
11248
  const gridX = typeof options?.grid === "number" ? options.grid : options?.grid?.x ?? 0;
10781
11249
  const gridY = typeof options?.grid === "number" ? options.grid : options?.grid?.y ?? 0;
10782
11250
  const snapToEntities = options?.snapToEntities ?? false;
10783
11251
  const threshold = options?.threshold ?? 8;
10784
11252
  const exclude = options?.exclude;
10785
- const snapToGrid = useCallback13(
11253
+ const snapToGrid = useCallback14(
10786
11254
  (x, y) => {
10787
11255
  const sx = gridX > 0 ? Math.round(x / gridX) * gridX : x;
10788
11256
  const sy = gridY > 0 ? Math.round(y / gridY) * gridY : y;
@@ -10790,7 +11258,7 @@ function useSnap(options) {
10790
11258
  },
10791
11259
  [gridX, gridY]
10792
11260
  );
10793
- const snap = useCallback13(
11261
+ const snap = useCallback14(
10794
11262
  (x, y) => {
10795
11263
  let outX = x;
10796
11264
  let outY = y;
@@ -10865,7 +11333,7 @@ function getBounds(ecs, id) {
10865
11333
  }
10866
11334
 
10867
11335
  // src/components/EditableText.tsx
10868
- import { useContext as useContext58, useEffect as useEffect67, useRef as useRef30 } from "react";
11336
+ import { useContext as useContext60, useEffect as useEffect69, useRef as useRef31 } from "react";
10869
11337
  import { jsx as jsx17 } from "react/jsx-runtime";
10870
11338
  function EditableText({
10871
11339
  value,
@@ -10887,10 +11355,10 @@ function EditableText({
10887
11355
  autoFocus = false,
10888
11356
  disabled = false
10889
11357
  }) {
10890
- const engine = useContext58(EngineContext);
10891
- const entityId = useContext58(EntityContext);
10892
- const containerRef = useRef30(null);
10893
- const inputRef = useRef30(null);
11358
+ const engine = useContext60(EngineContext);
11359
+ const entityId = useContext60(EntityContext);
11360
+ const containerRef = useRef31(null);
11361
+ const inputRef = useRef31(null);
10894
11362
  useOverlayTick(() => {
10895
11363
  if (!engine || entityId === null || entityId === void 0) return;
10896
11364
  const container = containerRef.current;
@@ -10917,7 +11385,7 @@ function EditableText({
10917
11385
  container.style.setProperty("--cubeforge-canvas-left", `${rect.left + window.scrollX}px`);
10918
11386
  container.style.setProperty("--cubeforge-canvas-top", `${rect.top + window.scrollY}px`);
10919
11387
  }, [engine, entityId, width, height, fontSize]);
10920
- useEffect67(() => {
11388
+ useEffect69(() => {
10921
11389
  if (autoFocus) inputRef.current?.focus();
10922
11390
  }, [autoFocus]);
10923
11391
  if (!engine) return null;
@@ -11048,7 +11516,7 @@ function copyRegion(source, region, scale) {
11048
11516
  }
11049
11517
 
11050
11518
  // src/components/A11yNode.tsx
11051
- import { useContext as useContext59, useRef as useRef31 } from "react";
11519
+ import { useContext as useContext61, useRef as useRef32 } from "react";
11052
11520
  import { jsx as jsx18 } from "react/jsx-runtime";
11053
11521
  function A11yNode({
11054
11522
  label,
@@ -11062,9 +11530,9 @@ function A11yNode({
11062
11530
  selected,
11063
11531
  bounds
11064
11532
  }) {
11065
- const engine = useContext59(EngineContext);
11066
- const entityId = useContext59(EntityContext);
11067
- const nodeRef = useRef31(null);
11533
+ const engine = useContext61(EngineContext);
11534
+ const entityId = useContext61(EntityContext);
11535
+ const nodeRef = useRef32(null);
11068
11536
  useOverlayTick(() => {
11069
11537
  if (!engine || entityId === null || entityId === void 0) return;
11070
11538
  const node = nodeRef.current;
@@ -11153,7 +11621,7 @@ function worldToScreenCss3(engine, wx, wy) {
11153
11621
  }
11154
11622
 
11155
11623
  // src/components/VectorPath.tsx
11156
- import { useContext as useContext60, useRef as useRef32 } from "react";
11624
+ import { useContext as useContext62, useRef as useRef33 } from "react";
11157
11625
  import { jsx as jsx19 } from "react/jsx-runtime";
11158
11626
  function VectorPath({
11159
11627
  d,
@@ -11167,9 +11635,9 @@ function VectorPath({
11167
11635
  opacity = 1,
11168
11636
  visible = true
11169
11637
  }) {
11170
- const engine = useContext60(EngineContext);
11171
- const entityId = useContext60(EntityContext);
11172
- const svgRef = useRef32(null);
11638
+ const engine = useContext62(EngineContext);
11639
+ const entityId = useContext62(EntityContext);
11640
+ const svgRef = useRef33(null);
11173
11641
  useOverlayTick(() => {
11174
11642
  if (!engine || entityId === null || entityId === void 0) return;
11175
11643
  const svg = svgRef.current;
@@ -11230,12 +11698,12 @@ function worldToScreenCss4(engine, wx, wy) {
11230
11698
  }
11231
11699
 
11232
11700
  // src/hooks/useGrid.ts
11233
- import { useCallback as useCallback14, useContext as useContext61, useMemo as useMemo13, useRef as useRef33, useState as useState16 } from "react";
11701
+ import { useCallback as useCallback15, useContext as useContext63, useMemo as useMemo14, useRef as useRef34, useState as useState17 } from "react";
11234
11702
  function useGrid({ width, height, fill }) {
11235
- const engine = useContext61(EngineContext);
11236
- const [version, setVersion] = useState16(0);
11237
- const bump = useCallback14(() => setVersion((v) => v + 1), []);
11238
- const dataRef = useRef33(null);
11703
+ const engine = useContext63(EngineContext);
11704
+ const [version, setVersion] = useState17(0);
11705
+ const bump = useCallback15(() => setVersion((v) => v + 1), []);
11706
+ const dataRef = useRef34(null);
11239
11707
  if (dataRef.current === null) {
11240
11708
  const arr = new Array(width * height);
11241
11709
  if (typeof fill === "function") {
@@ -11250,19 +11718,19 @@ function useGrid({ width, height, fill }) {
11250
11718
  }
11251
11719
  dataRef.current = arr;
11252
11720
  }
11253
- const markChanged = useCallback14(() => {
11721
+ const markChanged = useCallback15(() => {
11254
11722
  engine?.loop.markDirty();
11255
11723
  bump();
11256
11724
  }, [engine, bump]);
11257
- const inBounds = useCallback14((x, y) => x >= 0 && y >= 0 && x < width && y < height, [width, height]);
11258
- const get = useCallback14(
11725
+ const inBounds = useCallback15((x, y) => x >= 0 && y >= 0 && x < width && y < height, [width, height]);
11726
+ const get = useCallback15(
11259
11727
  (x, y) => {
11260
11728
  if (!inBounds(x, y)) return void 0;
11261
11729
  return dataRef.current[y * width + x];
11262
11730
  },
11263
11731
  [width, inBounds]
11264
11732
  );
11265
- const set = useCallback14(
11733
+ const set = useCallback15(
11266
11734
  (x, y, value) => {
11267
11735
  if (!inBounds(x, y)) return;
11268
11736
  const data = dataRef.current;
@@ -11273,7 +11741,7 @@ function useGrid({ width, height, fill }) {
11273
11741
  },
11274
11742
  [width, inBounds, markChanged]
11275
11743
  );
11276
- const swap = useCallback14(
11744
+ const swap = useCallback15(
11277
11745
  (ax, ay, bx, by) => {
11278
11746
  if (!inBounds(ax, ay) || !inBounds(bx, by)) return;
11279
11747
  const data = dataRef.current;
@@ -11287,7 +11755,7 @@ function useGrid({ width, height, fill }) {
11287
11755
  },
11288
11756
  [width, inBounds, markChanged]
11289
11757
  );
11290
- const forEach = useCallback14(
11758
+ const forEach = useCallback15(
11291
11759
  (cb) => {
11292
11760
  const data = dataRef.current;
11293
11761
  for (let y = 0; y < height; y++) {
@@ -11298,7 +11766,7 @@ function useGrid({ width, height, fill }) {
11298
11766
  },
11299
11767
  [width, height]
11300
11768
  );
11301
- const find = useCallback14(
11769
+ const find = useCallback15(
11302
11770
  (pred) => {
11303
11771
  const data = dataRef.current;
11304
11772
  for (let y = 0; y < height; y++) {
@@ -11311,7 +11779,7 @@ function useGrid({ width, height, fill }) {
11311
11779
  },
11312
11780
  [width, height]
11313
11781
  );
11314
- const count = useCallback14(
11782
+ const count = useCallback15(
11315
11783
  (pred) => {
11316
11784
  const data = dataRef.current;
11317
11785
  let n = 0;
@@ -11324,7 +11792,7 @@ function useGrid({ width, height, fill }) {
11324
11792
  },
11325
11793
  [width, height]
11326
11794
  );
11327
- const neighbors = useCallback14(
11795
+ const neighbors = useCallback15(
11328
11796
  (x, y, diagonal = false) => {
11329
11797
  const data = dataRef.current;
11330
11798
  const result = [];
@@ -11352,7 +11820,7 @@ function useGrid({ width, height, fill }) {
11352
11820
  },
11353
11821
  [width, inBounds]
11354
11822
  );
11355
- const fillAll = useCallback14(
11823
+ const fillAll = useCallback15(
11356
11824
  (value) => {
11357
11825
  const data = dataRef.current;
11358
11826
  if (typeof value === "function") {
@@ -11369,7 +11837,7 @@ function useGrid({ width, height, fill }) {
11369
11837
  },
11370
11838
  [width, height, markChanged]
11371
11839
  );
11372
- const toArray = useCallback14(() => {
11840
+ const toArray = useCallback15(() => {
11373
11841
  const data = dataRef.current;
11374
11842
  const out = new Array(height);
11375
11843
  for (let y = 0; y < height; y++) {
@@ -11379,7 +11847,7 @@ function useGrid({ width, height, fill }) {
11379
11847
  }
11380
11848
  return out;
11381
11849
  }, [width, height]);
11382
- const fromArray = useCallback14(
11850
+ const fromArray = useCallback15(
11383
11851
  (arr) => {
11384
11852
  const data = dataRef.current;
11385
11853
  for (let y = 0; y < height; y++) {
@@ -11394,7 +11862,7 @@ function useGrid({ width, height, fill }) {
11394
11862
  [width, height, markChanged]
11395
11863
  );
11396
11864
  void version;
11397
- return useMemo13(
11865
+ return useMemo14(
11398
11866
  () => ({
11399
11867
  width,
11400
11868
  height,
@@ -11415,7 +11883,7 @@ function useGrid({ width, height, fill }) {
11415
11883
  }
11416
11884
 
11417
11885
  // src/hooks/useTurnSystem.ts
11418
- import { useCallback as useCallback15, useContext as useContext62, useEffect as useEffect68, useMemo as useMemo14, useRef as useRef34, useState as useState17 } from "react";
11886
+ import { useCallback as useCallback16, useContext as useContext64, useEffect as useEffect70, useMemo as useMemo15, useRef as useRef35, useState as useState18 } from "react";
11419
11887
  function useTurnSystem({
11420
11888
  players,
11421
11889
  initialIndex = 0,
@@ -11424,15 +11892,15 @@ function useTurnSystem({
11424
11892
  aiDelay = 0
11425
11893
  }) {
11426
11894
  if (players.length === 0) throw new Error("useTurnSystem requires at least one player");
11427
- const engine = useContext62(EngineContext);
11428
- const [activeIndex, setActiveIndex] = useState17(initialIndex % players.length);
11429
- const [turn, setTurn] = useState17(0);
11430
- const [isPending, setIsPending] = useState17(false);
11431
- const pendingRafId = useRef34(null);
11432
- const pendingRemaining = useRef34(0);
11433
- const pendingTarget = useRef34(null);
11434
- const pendingLastTime = useRef34(0);
11435
- const clearPending = useCallback15(() => {
11895
+ const engine = useContext64(EngineContext);
11896
+ const [activeIndex, setActiveIndex] = useState18(initialIndex % players.length);
11897
+ const [turn, setTurn] = useState18(0);
11898
+ const [isPending, setIsPending] = useState18(false);
11899
+ const pendingRafId = useRef35(null);
11900
+ const pendingRemaining = useRef35(0);
11901
+ const pendingTarget = useRef35(null);
11902
+ const pendingLastTime = useRef35(0);
11903
+ const clearPending = useCallback16(() => {
11436
11904
  if (pendingRafId.current !== null) {
11437
11905
  cancelAnimationFrame(pendingRafId.current);
11438
11906
  pendingRafId.current = null;
@@ -11440,13 +11908,13 @@ function useTurnSystem({
11440
11908
  pendingTarget.current = null;
11441
11909
  setIsPending(false);
11442
11910
  }, []);
11443
- const startedOnce = useRef34(false);
11444
- useEffect68(() => {
11911
+ const startedOnce = useRef35(false);
11912
+ useEffect70(() => {
11445
11913
  if (startedOnce.current) return;
11446
11914
  startedOnce.current = true;
11447
11915
  onTurnStart?.({ player: players[activeIndex], index: activeIndex, turn: 0 });
11448
11916
  }, []);
11449
- const applyChange = useCallback15(
11917
+ const applyChange = useCallback16(
11450
11918
  (nextIndex) => {
11451
11919
  const curr = players[activeIndex];
11452
11920
  onTurnEnd?.({ player: curr, index: activeIndex, turn });
@@ -11459,7 +11927,7 @@ function useTurnSystem({
11459
11927
  },
11460
11928
  [players, activeIndex, turn, onTurnStart, onTurnEnd, engine]
11461
11929
  );
11462
- const scheduleChange = useCallback15(
11930
+ const scheduleChange = useCallback16(
11463
11931
  (nextIndex) => {
11464
11932
  clearPending();
11465
11933
  if (aiDelay <= 0) {
@@ -11491,9 +11959,9 @@ function useTurnSystem({
11491
11959
  },
11492
11960
  [aiDelay, applyChange, clearPending, engine]
11493
11961
  );
11494
- const nextTurn = useCallback15(() => scheduleChange(activeIndex + 1), [scheduleChange, activeIndex]);
11495
- const prevTurn = useCallback15(() => scheduleChange(activeIndex - 1), [scheduleChange, activeIndex]);
11496
- const skipTo = useCallback15(
11962
+ const nextTurn = useCallback16(() => scheduleChange(activeIndex + 1), [scheduleChange, activeIndex]);
11963
+ const prevTurn = useCallback16(() => scheduleChange(activeIndex - 1), [scheduleChange, activeIndex]);
11964
+ const skipTo = useCallback16(
11497
11965
  (target) => {
11498
11966
  let idx;
11499
11967
  if (typeof target === "number") {
@@ -11506,19 +11974,19 @@ function useTurnSystem({
11506
11974
  },
11507
11975
  [players, scheduleChange]
11508
11976
  );
11509
- const reset = useCallback15(() => {
11977
+ const reset = useCallback16(() => {
11510
11978
  clearPending();
11511
11979
  setActiveIndex(initialIndex % players.length);
11512
11980
  setTurn(0);
11513
11981
  engine?.loop.markDirty();
11514
11982
  }, [players.length, initialIndex, clearPending, engine]);
11515
- useEffect68(
11983
+ useEffect70(
11516
11984
  () => () => {
11517
11985
  if (pendingRafId.current !== null) cancelAnimationFrame(pendingRafId.current);
11518
11986
  },
11519
11987
  []
11520
11988
  );
11521
- return useMemo14(
11989
+ return useMemo15(
11522
11990
  () => ({
11523
11991
  activePlayer: players[activeIndex],
11524
11992
  activeIndex,
@@ -11534,13 +12002,13 @@ function useTurnSystem({
11534
12002
  }
11535
12003
 
11536
12004
  // src/hooks/useHoverable.ts
11537
- import { useContext as useContext63, useEffect as useEffect69, useState as useState18 } from "react";
12005
+ import { useContext as useContext65, useEffect as useEffect71, useState as useState19 } from "react";
11538
12006
  function useHoverable(options) {
11539
- const engine = useContext63(EngineContext);
11540
- const ctxEntityId = useContext63(EntityContext);
12007
+ const engine = useContext65(EngineContext);
12008
+ const ctxEntityId = useContext65(EntityContext);
11541
12009
  const entityId = options?.entityId ?? ctxEntityId;
11542
- const [isHovered, setIsHovered] = useState18(false);
11543
- useEffect69(() => {
12010
+ const [isHovered, setIsHovered] = useState19(false);
12011
+ useEffect71(() => {
11544
12012
  if (!engine || entityId === null || entityId === void 0 || options?.disabled) return;
11545
12013
  const canvas = engine.canvas;
11546
12014
  let hovered = false;
@@ -11617,7 +12085,7 @@ function screenToWorld(engine, cssX, cssY) {
11617
12085
  }
11618
12086
 
11619
12087
  // src/hooks/useDragDrop.ts
11620
- import { useContext as useContext64, useEffect as useEffect70, useRef as useRef35, useState as useState19 } from "react";
12088
+ import { useContext as useContext66, useEffect as useEffect72, useRef as useRef36, useState as useState20 } from "react";
11621
12089
  var dragStateByEngine = /* @__PURE__ */ new WeakMap();
11622
12090
  function getActiveDrag(engine) {
11623
12091
  return dragStateByEngine.get(engine) ?? null;
@@ -11637,14 +12105,14 @@ function subscribeToActiveDrag(engine, cb) {
11637
12105
  return () => dragStateByEngine.get(engine)?.subscribers.delete(cb);
11638
12106
  }
11639
12107
  function useDraggable(options) {
11640
- const engine = useContext64(EngineContext);
11641
- const ctxEntityId = useContext64(EntityContext);
12108
+ const engine = useContext66(EngineContext);
12109
+ const ctxEntityId = useContext66(EntityContext);
11642
12110
  const entityId = options?.entityId ?? ctxEntityId;
11643
- const [isDragging, setIsDragging] = useState19(false);
11644
- const [position, setPosition] = useState19({ x: 0, y: 0 });
11645
- const optsRef = useRef35(options);
12111
+ const [isDragging, setIsDragging] = useState20(false);
12112
+ const [position, setPosition] = useState20({ x: 0, y: 0 });
12113
+ const optsRef = useRef36(options);
11646
12114
  optsRef.current = options;
11647
- useEffect70(() => {
12115
+ useEffect72(() => {
11648
12116
  if (!engine || entityId === null || entityId === void 0 || options?.disabled) return;
11649
12117
  const canvas = engine.canvas;
11650
12118
  let dragging = false;
@@ -11749,14 +12217,14 @@ function useDraggable(options) {
11749
12217
  return { isDragging, position };
11750
12218
  }
11751
12219
  function useDroppable(options) {
11752
- const engine = useContext64(EngineContext);
11753
- const ctxEntityId = useContext64(EntityContext);
12220
+ const engine = useContext66(EngineContext);
12221
+ const ctxEntityId = useContext66(EntityContext);
11754
12222
  const entityId = options?.entityId ?? ctxEntityId;
11755
- const [isOver, setIsOver] = useState19(false);
11756
- const [hoveredBy, setHoveredBy] = useState19(null);
11757
- const optsRef = useRef35(options);
12223
+ const [isOver, setIsOver] = useState20(false);
12224
+ const [hoveredBy, setHoveredBy] = useState20(null);
12225
+ const optsRef = useRef36(options);
11758
12226
  optsRef.current = options;
11759
- useEffect70(() => {
12227
+ useEffect72(() => {
11760
12228
  if (!engine || entityId === null || entityId === void 0 || options?.disabled) return;
11761
12229
  let currentlyOver = false;
11762
12230
  const check = () => {
@@ -11858,7 +12326,7 @@ function findDroppableUnder(engine, drag) {
11858
12326
  }
11859
12327
 
11860
12328
  // src/hooks/useKeyboardFocus.ts
11861
- import { useContext as useContext65, useEffect as useEffect71, useRef as useRef36, useState as useState20 } from "react";
12329
+ import { useContext as useContext67, useEffect as useEffect73, useRef as useRef37, useState as useState21 } from "react";
11862
12330
  var registry = /* @__PURE__ */ new Map();
11863
12331
  var focusedId = null;
11864
12332
  var subscribers = /* @__PURE__ */ new Set();
@@ -11874,10 +12342,10 @@ function setFocused(id) {
11874
12342
  notifyFocus();
11875
12343
  }
11876
12344
  function useFocusable(options) {
11877
- const entityIdCtx = useContext65(EntityContext);
11878
- const optsRef = useRef36(options);
12345
+ const entityIdCtx = useContext67(EntityContext);
12346
+ const optsRef = useRef37(options);
11879
12347
  optsRef.current = options;
11880
- useEffect71(() => {
12348
+ useEffect73(() => {
11881
12349
  if (entityIdCtx === null || entityIdCtx === void 0) return;
11882
12350
  const id = entityIdCtx;
11883
12351
  const entry = {
@@ -11895,16 +12363,16 @@ function useFocusable(options) {
11895
12363
  }, [entityIdCtx]);
11896
12364
  }
11897
12365
  function useKeyboardFocus() {
11898
- const engine = useContext65(EngineContext);
11899
- const [focused, setFocusedState] = useState20(focusedId);
11900
- useEffect71(() => {
12366
+ const engine = useContext67(EngineContext);
12367
+ const [focused, setFocusedState] = useState21(focusedId);
12368
+ useEffect73(() => {
11901
12369
  const cb = () => setFocusedState(focusedId);
11902
12370
  subscribers.add(cb);
11903
12371
  return () => {
11904
12372
  subscribers.delete(cb);
11905
12373
  };
11906
12374
  }, []);
11907
- useEffect71(() => {
12375
+ useEffect73(() => {
11908
12376
  if (!engine) return;
11909
12377
  const onKey = (e) => {
11910
12378
  const target = e.target;
@@ -12030,7 +12498,7 @@ function subscribeFocus(cb) {
12030
12498
  }
12031
12499
 
12032
12500
  // src/components/FocusRing.tsx
12033
- import { useContext as useContext66, useEffect as useEffect72, useRef as useRef37, useState as useState21 } from "react";
12501
+ import { useContext as useContext68, useEffect as useEffect74, useRef as useRef38, useState as useState22 } from "react";
12034
12502
  import { Fragment as Fragment9, jsx as jsx20, jsxs as jsxs9 } from "react/jsx-runtime";
12035
12503
  function FocusRing({
12036
12504
  color = "#4fc3f7",
@@ -12039,15 +12507,15 @@ function FocusRing({
12039
12507
  borderRadius = 4,
12040
12508
  pulse = true
12041
12509
  }) {
12042
- const engine = useContext66(EngineContext);
12043
- const ringRef = useRef37(null);
12044
- const [focused, setFocused2] = useState21(getFocusedEntityId());
12045
- const [reducedMotion, setReducedMotion] = useState21(false);
12046
- useEffect72(() => {
12510
+ const engine = useContext68(EngineContext);
12511
+ const ringRef = useRef38(null);
12512
+ const [focused, setFocused2] = useState22(getFocusedEntityId());
12513
+ const [reducedMotion, setReducedMotion] = useState22(false);
12514
+ useEffect74(() => {
12047
12515
  const unsub = subscribeFocus(() => setFocused2(getFocusedEntityId()));
12048
12516
  return unsub;
12049
12517
  }, []);
12050
- useEffect72(() => {
12518
+ useEffect74(() => {
12051
12519
  if (typeof window === "undefined" || !window.matchMedia) return;
12052
12520
  const mq = window.matchMedia("(prefers-reduced-motion: reduce)");
12053
12521
  setReducedMotion(mq.matches);
@@ -12184,10 +12652,10 @@ function listSavedScenes(prefix = "") {
12184
12652
  }
12185
12653
 
12186
12654
  // ../gameplay/src/hooks/useAnimationController.ts
12187
- import { useState as useState22, useCallback as useCallback16 } from "react";
12655
+ import { useState as useState23, useCallback as useCallback17 } from "react";
12188
12656
  function useAnimationController(states, initial) {
12189
- const [stateName, setStateName] = useState22(initial);
12190
- const setState = useCallback16((next) => {
12657
+ const [stateName, setStateName] = useState23(initial);
12658
+ const setState = useCallback17((next) => {
12191
12659
  setStateName((prev) => prev === next ? prev : next);
12192
12660
  }, []);
12193
12661
  const clip = states[stateName] ?? states[initial];
@@ -12209,39 +12677,39 @@ function useAnimationController(states, initial) {
12209
12677
  }
12210
12678
 
12211
12679
  // ../gameplay/src/hooks/useAISteering.ts
12212
- import { useCallback as useCallback17 } from "react";
12680
+ import { useCallback as useCallback18 } from "react";
12213
12681
  function useAISteering() {
12214
- const seek$ = useCallback17((pos, target, speed) => seek(pos, target, speed), []);
12215
- const flee$ = useCallback17((pos, threat, speed) => flee(pos, threat, speed), []);
12216
- const arrive$ = useCallback17(
12682
+ const seek$ = useCallback18((pos, target, speed) => seek(pos, target, speed), []);
12683
+ const flee$ = useCallback18((pos, threat, speed) => flee(pos, threat, speed), []);
12684
+ const arrive$ = useCallback18(
12217
12685
  (pos, target, speed, slowRadius) => arrive(pos, target, speed, slowRadius),
12218
12686
  []
12219
12687
  );
12220
- const patrol$ = useCallback17(
12688
+ const patrol$ = useCallback18(
12221
12689
  (pos, waypoints, speed, currentIdx, arriveThreshold) => patrol(pos, waypoints, speed, currentIdx, arriveThreshold),
12222
12690
  []
12223
12691
  );
12224
- const wander$ = useCallback17(
12692
+ const wander$ = useCallback18(
12225
12693
  (pos, angle, speed, jitter) => wander(pos, angle, speed, jitter),
12226
12694
  []
12227
12695
  );
12228
- const pursuit$ = useCallback17(
12696
+ const pursuit$ = useCallback18(
12229
12697
  (pos, targetPos, targetVel, speed, lookAhead) => pursuit(pos, targetPos, targetVel, speed, lookAhead),
12230
12698
  []
12231
12699
  );
12232
- const evade$ = useCallback17(
12700
+ const evade$ = useCallback18(
12233
12701
  (pos, threatPos, threatVel, speed, lookAhead) => evade(pos, threatPos, threatVel, speed, lookAhead),
12234
12702
  []
12235
12703
  );
12236
- const separation$ = useCallback17(
12704
+ const separation$ = useCallback18(
12237
12705
  (pos, neighbors, speed, radius) => separation(pos, neighbors, speed, radius),
12238
12706
  []
12239
12707
  );
12240
- const cohesion$ = useCallback17(
12708
+ const cohesion$ = useCallback18(
12241
12709
  (pos, neighbors, speed) => cohesion(pos, neighbors, speed),
12242
12710
  []
12243
12711
  );
12244
- const alignment$ = useCallback17(
12712
+ const alignment$ = useCallback18(
12245
12713
  (neighborVelocities, speed) => alignment(neighborVelocities, speed),
12246
12714
  []
12247
12715
  );
@@ -12260,9 +12728,9 @@ function useAISteering() {
12260
12728
  }
12261
12729
 
12262
12730
  // ../gameplay/src/hooks/useDamageZone.ts
12263
- import { useContext as useContext67 } from "react";
12731
+ import { useContext as useContext69 } from "react";
12264
12732
  function useDamageZone(damage, opts = {}) {
12265
- const engine = useContext67(EngineContext);
12733
+ const engine = useContext69(EngineContext);
12266
12734
  useTriggerEnter(
12267
12735
  (other) => {
12268
12736
  engine.events.emit(`damage:${other}`, { amount: damage });
@@ -12272,11 +12740,11 @@ function useDamageZone(damage, opts = {}) {
12272
12740
  }
12273
12741
 
12274
12742
  // ../gameplay/src/hooks/useDropThrough.ts
12275
- import { useContext as useContext68, useCallback as useCallback18 } from "react";
12743
+ import { useContext as useContext70, useCallback as useCallback19 } from "react";
12276
12744
  function useDropThrough(frames = 8) {
12277
- const engine = useContext68(EngineContext);
12278
- const entityId = useContext68(EntityContext);
12279
- const dropThrough = useCallback18(() => {
12745
+ const engine = useContext70(EngineContext);
12746
+ const entityId = useContext70(EntityContext);
12747
+ const dropThrough = useCallback19(() => {
12280
12748
  const rb = engine.ecs.getComponent(entityId, "RigidBody");
12281
12749
  if (rb) rb.dropThrough = frames;
12282
12750
  }, [engine.ecs, entityId, frames]);
@@ -12284,17 +12752,17 @@ function useDropThrough(frames = 8) {
12284
12752
  }
12285
12753
 
12286
12754
  // ../gameplay/src/hooks/useGameStateMachine.ts
12287
- import { useState as useState23, useRef as useRef38, useCallback as useCallback19, useEffect as useEffect73, useContext as useContext69 } from "react";
12755
+ import { useState as useState24, useRef as useRef39, useCallback as useCallback20, useEffect as useEffect75, useContext as useContext71 } from "react";
12288
12756
  function useGameStateMachine(states, initial) {
12289
- const engine = useContext69(EngineContext);
12290
- const [state, setState] = useState23(initial);
12291
- const stateRef = useRef38(initial);
12292
- const statesRef = useRef38(states);
12757
+ const engine = useContext71(EngineContext);
12758
+ const [state, setState] = useState24(initial);
12759
+ const stateRef = useRef39(initial);
12760
+ const statesRef = useRef39(states);
12293
12761
  statesRef.current = states;
12294
- useEffect73(() => {
12762
+ useEffect75(() => {
12295
12763
  statesRef.current[initial]?.onEnter?.();
12296
12764
  }, []);
12297
- const transition = useCallback19((to) => {
12765
+ const transition = useCallback20((to) => {
12298
12766
  const current = stateRef.current;
12299
12767
  if (current === to) return;
12300
12768
  statesRef.current[current]?.onExit?.();
@@ -12302,7 +12770,7 @@ function useGameStateMachine(states, initial) {
12302
12770
  setState(to);
12303
12771
  statesRef.current[to]?.onEnter?.();
12304
12772
  }, []);
12305
- useEffect73(() => {
12773
+ useEffect75(() => {
12306
12774
  const eid = engine.ecs.createEntity();
12307
12775
  engine.ecs.addComponent(
12308
12776
  eid,
@@ -12318,27 +12786,27 @@ function useGameStateMachine(states, initial) {
12318
12786
  }
12319
12787
 
12320
12788
  // ../gameplay/src/hooks/useHealth.ts
12321
- import { useRef as useRef39, useEffect as useEffect74, useContext as useContext70, useCallback as useCallback20 } from "react";
12789
+ import { useRef as useRef40, useEffect as useEffect76, useContext as useContext72, useCallback as useCallback21 } from "react";
12322
12790
  function useHealth(maxHp, opts = {}) {
12323
- const engine = useContext70(EngineContext);
12324
- const entityId = useContext70(EntityContext);
12325
- const hpRef = useRef39(maxHp);
12326
- const invincibleRef = useRef39(false);
12791
+ const engine = useContext72(EngineContext);
12792
+ const entityId = useContext72(EntityContext);
12793
+ const hpRef = useRef40(maxHp);
12794
+ const invincibleRef = useRef40(false);
12327
12795
  const iFrameDuration = opts.iFrames ?? 1;
12328
- const onDeathRef = useRef39(opts.onDeath);
12329
- const onDamageRef = useRef39(opts.onDamage);
12330
- useEffect74(() => {
12796
+ const onDeathRef = useRef40(opts.onDeath);
12797
+ const onDamageRef = useRef40(opts.onDamage);
12798
+ useEffect76(() => {
12331
12799
  onDeathRef.current = opts.onDeath;
12332
12800
  });
12333
- useEffect74(() => {
12801
+ useEffect76(() => {
12334
12802
  onDamageRef.current = opts.onDamage;
12335
12803
  });
12336
- const timerRef = useRef39(
12804
+ const timerRef = useRef40(
12337
12805
  createTimer(iFrameDuration, () => {
12338
12806
  invincibleRef.current = false;
12339
12807
  })
12340
12808
  );
12341
- const takeDamage = useCallback20(
12809
+ const takeDamage = useCallback21(
12342
12810
  (amount = 1) => {
12343
12811
  if (invincibleRef.current || hpRef.current <= 0) return;
12344
12812
  hpRef.current = Math.max(0, hpRef.current - amount);
@@ -12351,28 +12819,28 @@ function useHealth(maxHp, opts = {}) {
12351
12819
  },
12352
12820
  [iFrameDuration]
12353
12821
  );
12354
- const takeDamageRef = useRef39(takeDamage);
12355
- useEffect74(() => {
12822
+ const takeDamageRef = useRef40(takeDamage);
12823
+ useEffect76(() => {
12356
12824
  takeDamageRef.current = takeDamage;
12357
12825
  }, [takeDamage]);
12358
- useEffect74(() => {
12826
+ useEffect76(() => {
12359
12827
  return engine.events.on(`damage:${entityId}`, ({ amount }) => {
12360
12828
  takeDamageRef.current(amount);
12361
12829
  });
12362
12830
  }, [engine.events, entityId]);
12363
- const heal = useCallback20(
12831
+ const heal = useCallback21(
12364
12832
  (amount) => {
12365
12833
  hpRef.current = Math.min(maxHp, hpRef.current + amount);
12366
12834
  },
12367
12835
  [maxHp]
12368
12836
  );
12369
- const setHp = useCallback20(
12837
+ const setHp = useCallback21(
12370
12838
  (hp) => {
12371
12839
  hpRef.current = Math.min(maxHp, Math.max(0, hp));
12372
12840
  },
12373
12841
  [maxHp]
12374
12842
  );
12375
- const update = useCallback20((dt) => {
12843
+ const update = useCallback21((dt) => {
12376
12844
  timerRef.current.update(dt);
12377
12845
  }, []);
12378
12846
  return {
@@ -12396,11 +12864,11 @@ function useHealth(maxHp, opts = {}) {
12396
12864
  }
12397
12865
 
12398
12866
  // ../gameplay/src/hooks/useKinematicBody.ts
12399
- import { useContext as useContext71, useCallback as useCallback21 } from "react";
12867
+ import { useContext as useContext73, useCallback as useCallback22 } from "react";
12400
12868
  function useKinematicBody() {
12401
- const engine = useContext71(EngineContext);
12402
- const entityId = useContext71(EntityContext);
12403
- const moveAndCollide = useCallback21(
12869
+ const engine = useContext73(EngineContext);
12870
+ const entityId = useContext73(EntityContext);
12871
+ const moveAndCollide = useCallback22(
12404
12872
  (dx, dy) => {
12405
12873
  const transform = engine.ecs.getComponent(entityId, "Transform");
12406
12874
  if (!transform) return { dx: 0, dy: 0 };
@@ -12436,7 +12904,7 @@ function useKinematicBody() {
12436
12904
  },
12437
12905
  [engine.ecs, entityId]
12438
12906
  );
12439
- const setVelocity = useCallback21(
12907
+ const setVelocity = useCallback22(
12440
12908
  (vx, vy) => {
12441
12909
  const rb = engine.ecs.getComponent(entityId, "RigidBody");
12442
12910
  if (rb) {
@@ -12446,7 +12914,7 @@ function useKinematicBody() {
12446
12914
  },
12447
12915
  [engine.ecs, entityId]
12448
12916
  );
12449
- const setAngularVelocity = useCallback21(
12917
+ const setAngularVelocity = useCallback22(
12450
12918
  (w) => {
12451
12919
  const rb = engine.ecs.getComponent(entityId, "RigidBody");
12452
12920
  if (rb) rb.angularVelocity = w;
@@ -12457,12 +12925,12 @@ function useKinematicBody() {
12457
12925
  }
12458
12926
 
12459
12927
  // ../gameplay/src/hooks/useLevelTransition.ts
12460
- import { useState as useState24, useRef as useRef40, useCallback as useCallback22 } from "react";
12928
+ import { useState as useState25, useRef as useRef41, useCallback as useCallback23 } from "react";
12461
12929
  function useLevelTransition(initial) {
12462
- const [currentLevel, setCurrentLevel] = useState24(initial);
12463
- const [isTransitioning, setIsTransitioning] = useState24(false);
12464
- const overlayRef = useRef40(null);
12465
- const transitionTo = useCallback22((level, opts = {}) => {
12930
+ const [currentLevel, setCurrentLevel] = useState25(initial);
12931
+ const [isTransitioning, setIsTransitioning] = useState25(false);
12932
+ const overlayRef = useRef41(null);
12933
+ const transitionTo = useCallback23((level, opts = {}) => {
12466
12934
  const { duration = 0.4, type = "fade" } = opts;
12467
12935
  if (type === "instant") {
12468
12936
  setCurrentLevel(level);
@@ -12499,13 +12967,13 @@ function useLevelTransition(initial) {
12499
12967
  }
12500
12968
 
12501
12969
  // ../gameplay/src/hooks/usePlatformerController.ts
12502
- import { useContext as useContext72, useEffect as useEffect75 } from "react";
12970
+ import { useContext as useContext74, useEffect as useEffect77 } from "react";
12503
12971
  function normalizeKeys(val, defaults) {
12504
12972
  if (!val) return defaults;
12505
12973
  return Array.isArray(val) ? val : [val];
12506
12974
  }
12507
12975
  function usePlatformerController(entityId, opts = {}) {
12508
- const engine = useContext72(EngineContext);
12976
+ const engine = useContext74(EngineContext);
12509
12977
  const {
12510
12978
  speed = 200,
12511
12979
  jumpForce = -500,
@@ -12523,7 +12991,7 @@ function usePlatformerController(entityId, opts = {}) {
12523
12991
  const leftKeys = normalizeKeys(bindings?.left, ["ArrowLeft", "KeyA", "a"]);
12524
12992
  const rightKeys = normalizeKeys(bindings?.right, ["ArrowRight", "KeyD", "d"]);
12525
12993
  const jumpKeys = normalizeKeys(bindings?.jump, ["Space", "ArrowUp", "KeyW", "w"]);
12526
- useEffect75(() => {
12994
+ useEffect77(() => {
12527
12995
  const state = {
12528
12996
  coyoteTimer: 0,
12529
12997
  jumpBuffer: 0,
@@ -12587,26 +13055,26 @@ function usePlatformerController(entityId, opts = {}) {
12587
13055
  }
12588
13056
 
12589
13057
  // ../gameplay/src/hooks/usePathfinding.ts
12590
- import { useCallback as useCallback23 } from "react";
13058
+ import { useCallback as useCallback24 } from "react";
12591
13059
  function usePathfinding() {
12592
- const createGrid$ = useCallback23(
13060
+ const createGrid$ = useCallback24(
12593
13061
  (cols, rows, cellSize) => createNavGrid(cols, rows, cellSize),
12594
13062
  []
12595
13063
  );
12596
- const setWalkable$ = useCallback23(
13064
+ const setWalkable$ = useCallback24(
12597
13065
  (grid, col, row, walkable) => setWalkable(grid, col, row, walkable),
12598
13066
  []
12599
13067
  );
12600
- const findPath$ = useCallback23((grid, start2, goal) => findPath(grid, start2, goal), []);
13068
+ const findPath$ = useCallback24((grid, start2, goal) => findPath(grid, start2, goal), []);
12601
13069
  return { createGrid: createGrid$, setWalkable: setWalkable$, findPath: findPath$ };
12602
13070
  }
12603
13071
 
12604
13072
  // ../gameplay/src/hooks/usePersistedBindings.ts
12605
- import { useState as useState25, useCallback as useCallback24, useMemo as useMemo15, useContext as useContext73 } from "react";
13073
+ import { useState as useState26, useCallback as useCallback25, useMemo as useMemo16, useContext as useContext75 } from "react";
12606
13074
  function usePersistedBindings(storageKey, defaults) {
12607
- const engine = useContext73(EngineContext);
13075
+ const engine = useContext75(EngineContext);
12608
13076
  const input = engine.input;
12609
- const [bindings, setBindings] = useState25(() => {
13077
+ const [bindings, setBindings] = useState26(() => {
12610
13078
  try {
12611
13079
  const stored = localStorage.getItem(storageKey);
12612
13080
  if (stored) return { ...defaults, ...JSON.parse(stored) };
@@ -12614,7 +13082,7 @@ function usePersistedBindings(storageKey, defaults) {
12614
13082
  }
12615
13083
  return defaults;
12616
13084
  });
12617
- const normalized = useMemo15(() => {
13085
+ const normalized = useMemo16(() => {
12618
13086
  const out = {};
12619
13087
  for (const [action, keys] of Object.entries(bindings)) {
12620
13088
  if (typeof keys === "string") out[action] = [keys];
@@ -12622,7 +13090,7 @@ function usePersistedBindings(storageKey, defaults) {
12622
13090
  }
12623
13091
  return out;
12624
13092
  }, [bindings]);
12625
- const rebind = useCallback24(
13093
+ const rebind = useCallback25(
12626
13094
  (action, keys) => {
12627
13095
  setBindings((prev) => {
12628
13096
  const next = { ...prev, [action]: Array.isArray(keys) ? keys : [keys] };
@@ -12635,14 +13103,14 @@ function usePersistedBindings(storageKey, defaults) {
12635
13103
  },
12636
13104
  [storageKey]
12637
13105
  );
12638
- const reset = useCallback24(() => {
13106
+ const reset = useCallback25(() => {
12639
13107
  try {
12640
13108
  localStorage.removeItem(storageKey);
12641
13109
  } catch {
12642
13110
  }
12643
13111
  setBindings(defaults);
12644
13112
  }, [storageKey]);
12645
- return useMemo15(
13113
+ return useMemo16(
12646
13114
  () => ({
12647
13115
  bindings,
12648
13116
  rebind,
@@ -12656,21 +13124,21 @@ function usePersistedBindings(storageKey, defaults) {
12656
13124
  }
12657
13125
 
12658
13126
  // ../gameplay/src/hooks/useRestart.ts
12659
- import { useState as useState26, useCallback as useCallback25 } from "react";
13127
+ import { useState as useState27, useCallback as useCallback26 } from "react";
12660
13128
  function useRestart() {
12661
- const [restartKey, setRestartKey] = useState26(0);
12662
- const restart = useCallback25(() => {
13129
+ const [restartKey, setRestartKey] = useState27(0);
13130
+ const restart = useCallback26(() => {
12663
13131
  setRestartKey((k) => k + 1);
12664
13132
  }, []);
12665
13133
  return { restartKey, restart };
12666
13134
  }
12667
13135
 
12668
13136
  // ../gameplay/src/hooks/useSave.ts
12669
- import { useCallback as useCallback26, useRef as useRef41 } from "react";
13137
+ import { useCallback as useCallback27, useRef as useRef42 } from "react";
12670
13138
  function useSave(key, defaultValue, opts = {}) {
12671
13139
  const version = opts.version ?? 1;
12672
- const dataRef = useRef41(defaultValue);
12673
- const save = useCallback26(
13140
+ const dataRef = useRef42(defaultValue);
13141
+ const save = useCallback27(
12674
13142
  (value) => {
12675
13143
  dataRef.current = value;
12676
13144
  const slot = { version, data: value };
@@ -12682,7 +13150,7 @@ function useSave(key, defaultValue, opts = {}) {
12682
13150
  },
12683
13151
  [key, version]
12684
13152
  );
12685
- const load = useCallback26(() => {
13153
+ const load = useCallback27(() => {
12686
13154
  try {
12687
13155
  const raw = localStorage.getItem(key);
12688
13156
  if (!raw) return defaultValue;
@@ -12702,11 +13170,11 @@ function useSave(key, defaultValue, opts = {}) {
12702
13170
  return defaultValue;
12703
13171
  }
12704
13172
  }, [key, version]);
12705
- const clear = useCallback26(() => {
13173
+ const clear = useCallback27(() => {
12706
13174
  localStorage.removeItem(key);
12707
13175
  dataRef.current = defaultValue;
12708
13176
  }, [key]);
12709
- const exists = useCallback26(() => {
13177
+ const exists = useCallback27(() => {
12710
13178
  return localStorage.getItem(key) !== null;
12711
13179
  }, [key]);
12712
13180
  return {
@@ -12721,7 +13189,7 @@ function useSave(key, defaultValue, opts = {}) {
12721
13189
  }
12722
13190
 
12723
13191
  // ../gameplay/src/hooks/useIDBSave.ts
12724
- import { useCallback as useCallback27, useRef as useRef42 } from "react";
13192
+ import { useCallback as useCallback28, useRef as useRef43 } from "react";
12725
13193
  var DB_NAME = "cubeforge-saves";
12726
13194
  var STORE_NAME = "saves";
12727
13195
  function openDB() {
@@ -12739,12 +13207,12 @@ function openDB() {
12739
13207
  }
12740
13208
  function useIDBSave(key, defaultValue, opts = {}) {
12741
13209
  const version = opts.version ?? 1;
12742
- const dbRef = useRef42(null);
12743
- const getDB = useCallback27(() => {
13210
+ const dbRef = useRef43(null);
13211
+ const getDB = useCallback28(() => {
12744
13212
  if (!dbRef.current) dbRef.current = openDB();
12745
13213
  return dbRef.current;
12746
13214
  }, []);
12747
- const save = useCallback27(
13215
+ const save = useCallback28(
12748
13216
  async (value) => {
12749
13217
  const db = await getDB();
12750
13218
  const slot = { version, data: value };
@@ -12757,7 +13225,7 @@ function useIDBSave(key, defaultValue, opts = {}) {
12757
13225
  },
12758
13226
  [getDB, key, version]
12759
13227
  );
12760
- const load = useCallback27(async () => {
13228
+ const load = useCallback28(async () => {
12761
13229
  const db = await getDB();
12762
13230
  return new Promise((resolve, reject) => {
12763
13231
  const tx = db.transaction(STORE_NAME, "readonly");
@@ -12783,7 +13251,7 @@ function useIDBSave(key, defaultValue, opts = {}) {
12783
13251
  req.onerror = () => reject(req.error);
12784
13252
  });
12785
13253
  }, [getDB, key, version]);
12786
- const clear = useCallback27(async () => {
13254
+ const clear = useCallback28(async () => {
12787
13255
  const db = await getDB();
12788
13256
  return new Promise((resolve, reject) => {
12789
13257
  const tx = db.transaction(STORE_NAME, "readwrite");
@@ -12792,7 +13260,7 @@ function useIDBSave(key, defaultValue, opts = {}) {
12792
13260
  req.onerror = () => reject(req.error);
12793
13261
  });
12794
13262
  }, [getDB, key]);
12795
- const exists = useCallback27(async () => {
13263
+ const exists = useCallback28(async () => {
12796
13264
  const db = await getDB();
12797
13265
  return new Promise((resolve, reject) => {
12798
13266
  const tx = db.transaction(STORE_NAME, "readonly");
@@ -12805,16 +13273,16 @@ function useIDBSave(key, defaultValue, opts = {}) {
12805
13273
  }
12806
13274
 
12807
13275
  // ../gameplay/src/hooks/useTopDownMovement.ts
12808
- import { useContext as useContext74, useEffect as useEffect76 } from "react";
13276
+ import { useContext as useContext76, useEffect as useEffect78 } from "react";
12809
13277
  function moveToward(current, target, maxDelta) {
12810
13278
  const diff = target - current;
12811
13279
  if (Math.abs(diff) <= maxDelta) return target;
12812
13280
  return current + Math.sign(diff) * maxDelta;
12813
13281
  }
12814
13282
  function useTopDownMovement(entityId, opts = {}) {
12815
- const engine = useContext74(EngineContext);
13283
+ const engine = useContext76(EngineContext);
12816
13284
  const { speed = 200, normalizeDiagonal = true, acceleration = Infinity, deceleration = acceleration } = opts;
12817
- useEffect76(() => {
13285
+ useEffect78(() => {
12818
13286
  const updateFn = (id, world, input, dt) => {
12819
13287
  if (!world.hasEntity(id)) return;
12820
13288
  const rb = world.getComponent(id, "RigidBody");
@@ -12854,18 +13322,18 @@ function useTopDownMovement(entityId, opts = {}) {
12854
13322
  }
12855
13323
 
12856
13324
  // ../gameplay/src/hooks/useDialogue.ts
12857
- import { useState as useState27, useCallback as useCallback28, useRef as useRef43 } from "react";
13325
+ import { useState as useState28, useCallback as useCallback29, useRef as useRef44 } from "react";
12858
13326
  function useDialogue() {
12859
- const [active, setActive] = useState27(false);
12860
- const [currentId, setCurrentId] = useState27(null);
12861
- const scriptRef = useRef43(null);
12862
- const start2 = useCallback28((script, startId) => {
13327
+ const [active, setActive] = useState28(false);
13328
+ const [currentId, setCurrentId] = useState28(null);
13329
+ const scriptRef = useRef44(null);
13330
+ const start2 = useCallback29((script, startId) => {
12863
13331
  scriptRef.current = script;
12864
13332
  const id = startId ?? Object.keys(script)[0];
12865
13333
  setCurrentId(id);
12866
13334
  setActive(true);
12867
13335
  }, []);
12868
- const advance = useCallback28(
13336
+ const advance = useCallback29(
12869
13337
  (choiceIndex) => {
12870
13338
  if (!scriptRef.current || !currentId) return;
12871
13339
  const line = scriptRef.current[currentId];
@@ -12895,7 +13363,7 @@ function useDialogue() {
12895
13363
  },
12896
13364
  [currentId]
12897
13365
  );
12898
- const close = useCallback28(() => {
13366
+ const close = useCallback29(() => {
12899
13367
  setActive(false);
12900
13368
  setCurrentId(null);
12901
13369
  scriptRef.current = null;
@@ -13013,17 +13481,17 @@ function DialogueBox({ dialogue, onAdvance, onClose, style = {} }) {
13013
13481
  }
13014
13482
 
13015
13483
  // ../gameplay/src/hooks/useCutscene.ts
13016
- import { useState as useState28, useCallback as useCallback29, useRef as useRef44, useEffect as useEffect77, useContext as useContext75 } from "react";
13484
+ import { useState as useState29, useCallback as useCallback30, useRef as useRef45, useEffect as useEffect79, useContext as useContext77 } from "react";
13017
13485
  function useCutscene() {
13018
- const engine = useContext75(EngineContext);
13019
- const [playing, setPlaying] = useState28(false);
13020
- const [stepIndex, setStepIndex] = useState28(0);
13021
- const stepsRef = useRef44([]);
13022
- const timerRef = useRef44(0);
13023
- const idxRef = useRef44(0);
13024
- const playingRef = useRef44(false);
13025
- const entityRef = useRef44(null);
13026
- const finish = useCallback29(() => {
13486
+ const engine = useContext77(EngineContext);
13487
+ const [playing, setPlaying] = useState29(false);
13488
+ const [stepIndex, setStepIndex] = useState29(0);
13489
+ const stepsRef = useRef45([]);
13490
+ const timerRef = useRef45(0);
13491
+ const idxRef = useRef45(0);
13492
+ const playingRef = useRef45(false);
13493
+ const entityRef = useRef45(null);
13494
+ const finish = useCallback30(() => {
13027
13495
  playingRef.current = false;
13028
13496
  setPlaying(false);
13029
13497
  setStepIndex(0);
@@ -13033,14 +13501,14 @@ function useCutscene() {
13033
13501
  entityRef.current = null;
13034
13502
  }
13035
13503
  }, [engine.ecs]);
13036
- const fireStep = useCallback29((step) => {
13504
+ const fireStep = useCallback30((step) => {
13037
13505
  if (step.type === "call") step.fn();
13038
13506
  if (step.type === "parallel")
13039
13507
  step.steps.forEach((s2) => {
13040
13508
  if (s2.type === "call") s2.fn();
13041
13509
  });
13042
13510
  }, []);
13043
- const play = useCallback29(
13511
+ const play = useCallback30(
13044
13512
  (steps) => {
13045
13513
  stepsRef.current = steps;
13046
13514
  idxRef.current = 0;
@@ -13097,7 +13565,7 @@ function useCutscene() {
13097
13565
  },
13098
13566
  [engine.ecs, finish, fireStep]
13099
13567
  );
13100
- const skip = useCallback29(() => {
13568
+ const skip = useCallback30(() => {
13101
13569
  for (let i = idxRef.current; i < stepsRef.current.length; i++) {
13102
13570
  const step = stepsRef.current[i];
13103
13571
  if (step.type === "call") step.fn();
@@ -13108,7 +13576,7 @@ function useCutscene() {
13108
13576
  }
13109
13577
  finish();
13110
13578
  }, [finish]);
13111
- useEffect77(() => {
13579
+ useEffect79(() => {
13112
13580
  return () => {
13113
13581
  if (entityRef.current !== null && engine.ecs.hasEntity(entityRef.current)) {
13114
13582
  engine.ecs.destroyEntity(entityRef.current);
@@ -13119,7 +13587,7 @@ function useCutscene() {
13119
13587
  }
13120
13588
 
13121
13589
  // ../gameplay/src/hooks/useGameStore.ts
13122
- import { useSyncExternalStore as useSyncExternalStore2, useCallback as useCallback30 } from "react";
13590
+ import { useSyncExternalStore as useSyncExternalStore2, useCallback as useCallback31 } from "react";
13123
13591
  function createStore(initialState) {
13124
13592
  let state = { ...initialState };
13125
13593
  const listeners = /* @__PURE__ */ new Set();
@@ -13145,7 +13613,7 @@ function useGameStore(key, initialState) {
13145
13613
  }
13146
13614
  const store = stores.get(key);
13147
13615
  const state = useSyncExternalStore2(store.subscribe, store.getState);
13148
- const setState = useCallback30(
13616
+ const setState = useCallback31(
13149
13617
  (partial) => {
13150
13618
  store.setState(partial);
13151
13619
  },
@@ -13155,21 +13623,21 @@ function useGameStore(key, initialState) {
13155
13623
  }
13156
13624
 
13157
13625
  // ../gameplay/src/hooks/useTween.ts
13158
- import { useRef as useRef45, useCallback as useCallback31, useEffect as useEffect78 } from "react";
13626
+ import { useRef as useRef46, useCallback as useCallback32, useEffect as useEffect80 } from "react";
13159
13627
  function useTween(opts) {
13160
- const rafRef = useRef45(null);
13161
- const startTimeRef = useRef45(0);
13162
- const runningRef = useRef45(false);
13163
- const optsRef = useRef45(opts);
13628
+ const rafRef = useRef46(null);
13629
+ const startTimeRef = useRef46(0);
13630
+ const runningRef = useRef46(false);
13631
+ const optsRef = useRef46(opts);
13164
13632
  optsRef.current = opts;
13165
- const stop = useCallback31(() => {
13633
+ const stop = useCallback32(() => {
13166
13634
  if (rafRef.current !== null) {
13167
13635
  cancelAnimationFrame(rafRef.current);
13168
13636
  rafRef.current = null;
13169
13637
  }
13170
13638
  runningRef.current = false;
13171
13639
  }, []);
13172
- const start2 = useCallback31(() => {
13640
+ const start2 = useCallback32(() => {
13173
13641
  stop();
13174
13642
  runningRef.current = true;
13175
13643
  startTimeRef.current = performance.now();
@@ -13191,12 +13659,12 @@ function useTween(opts) {
13191
13659
  };
13192
13660
  rafRef.current = requestAnimationFrame(tick2);
13193
13661
  }, [stop]);
13194
- useEffect78(() => {
13662
+ useEffect80(() => {
13195
13663
  if (opts.autoStart) {
13196
13664
  start2();
13197
13665
  }
13198
13666
  }, []);
13199
- useEffect78(() => {
13667
+ useEffect80(() => {
13200
13668
  return () => {
13201
13669
  if (rafRef.current !== null) {
13202
13670
  cancelAnimationFrame(rafRef.current);
@@ -13215,15 +13683,15 @@ function useTween(opts) {
13215
13683
  }
13216
13684
 
13217
13685
  // ../gameplay/src/hooks/useObjectPool.ts
13218
- import { useRef as useRef46, useMemo as useMemo16, useEffect as useEffect79 } from "react";
13686
+ import { useRef as useRef47, useMemo as useMemo17, useEffect as useEffect81 } from "react";
13219
13687
  function useObjectPool(factory, reset, initialSize) {
13220
- const poolRef = useRef46([]);
13221
- const activeRef = useRef46(0);
13222
- const factoryRef = useRef46(factory);
13688
+ const poolRef = useRef47([]);
13689
+ const activeRef = useRef47(0);
13690
+ const factoryRef = useRef47(factory);
13223
13691
  factoryRef.current = factory;
13224
- const resetRef = useRef46(reset);
13692
+ const resetRef = useRef47(reset);
13225
13693
  resetRef.current = reset;
13226
- useEffect79(() => {
13694
+ useEffect81(() => {
13227
13695
  if (initialSize != null && initialSize > 0) {
13228
13696
  const pool = poolRef.current;
13229
13697
  for (let i = 0; i < initialSize; i++) {
@@ -13231,7 +13699,7 @@ function useObjectPool(factory, reset, initialSize) {
13231
13699
  }
13232
13700
  }
13233
13701
  }, []);
13234
- return useMemo16(
13702
+ return useMemo17(
13235
13703
  () => ({
13236
13704
  acquire() {
13237
13705
  activeRef.current++;
@@ -13262,11 +13730,11 @@ function useObjectPool(factory, reset, initialSize) {
13262
13730
  }
13263
13731
 
13264
13732
  // ../gameplay/src/hooks/useForces.ts
13265
- import { useContext as useContext76, useMemo as useMemo17 } from "react";
13733
+ import { useContext as useContext78, useMemo as useMemo18 } from "react";
13266
13734
  function useForces() {
13267
- const engine = useContext76(EngineContext);
13268
- const entityId = useContext76(EntityContext);
13269
- return useMemo17(() => {
13735
+ const engine = useContext78(EngineContext);
13736
+ const entityId = useContext78(EntityContext);
13737
+ return useMemo18(() => {
13270
13738
  const getRb = () => engine.ecs.getComponent(entityId, "RigidBody");
13271
13739
  const getTransform = () => engine.ecs.getComponent(entityId, "Transform");
13272
13740
  const getCenter = () => {
@@ -13334,11 +13802,11 @@ function useForces() {
13334
13802
  }
13335
13803
 
13336
13804
  // ../gameplay/src/hooks/useCharacterController.ts
13337
- import { useContext as useContext77, useMemo as useMemo18 } from "react";
13805
+ import { useContext as useContext79, useMemo as useMemo19 } from "react";
13338
13806
  function useCharacterController(config = {}) {
13339
- const engine = useContext77(EngineContext);
13340
- const entityId = useContext77(EntityContext);
13341
- return useMemo18(() => {
13807
+ const engine = useContext79(EngineContext);
13808
+ const entityId = useContext79(EntityContext);
13809
+ return useMemo19(() => {
13342
13810
  const controller = new CharacterController(config);
13343
13811
  return {
13344
13812
  move(dx, dy) {
@@ -13358,6 +13826,164 @@ function createAtlas(names, _columns) {
13358
13826
  return atlas;
13359
13827
  }
13360
13828
 
13829
+ // src/components/HUD.tsx
13830
+ import { createContext as createContext3, useContext as useContext80, useMemo as useMemo20 } from "react";
13831
+ import { jsx as jsx22, jsxs as jsxs11 } from "react/jsx-runtime";
13832
+ var HUDContextRef = createContext3(null);
13833
+ function HUD({
13834
+ dimDuringTransitions = true,
13835
+ dimmedOpacity = 0.25,
13836
+ visible = true,
13837
+ safeArea = true,
13838
+ padding = 12,
13839
+ style,
13840
+ className,
13841
+ children
13842
+ }) {
13843
+ const ctx = useMemo20(() => ({ padding, safeArea }), [padding, safeArea]);
13844
+ const rootStyle = {
13845
+ position: "absolute",
13846
+ inset: 0,
13847
+ pointerEvents: "none",
13848
+ zIndex: 100,
13849
+ opacity: visible ? 1 : 0,
13850
+ transition: "opacity 180ms ease-out",
13851
+ userSelect: "none",
13852
+ ...style
13853
+ };
13854
+ void dimDuringTransitions;
13855
+ void dimmedOpacity;
13856
+ return /* @__PURE__ */ jsx22(HUDContextRef.Provider, { value: ctx, children: /* @__PURE__ */ jsx22("div", { className, style: rootStyle, "data-cubeforge-hud": "true", children }) });
13857
+ }
13858
+ function useHUD() {
13859
+ return useContext80(HUDContextRef) ?? { padding: 12, safeArea: false };
13860
+ }
13861
+ function HUDZone({
13862
+ position = "topLeft",
13863
+ direction,
13864
+ gap = 8,
13865
+ interactive = true,
13866
+ style,
13867
+ className,
13868
+ children
13869
+ }) {
13870
+ const { padding, safeArea } = useHUD();
13871
+ const isTop = position.startsWith("top");
13872
+ const isBottom = position.startsWith("bottom");
13873
+ const isCenter = position.startsWith("center");
13874
+ const isLeft = position.endsWith("Left") || position === "centerLeft";
13875
+ const isRight = position.endsWith("Right") || position === "centerRight";
13876
+ const isHCenter = position.endsWith("Center") || position === "center";
13877
+ const flexDirection = direction ?? (isTop || isBottom ? "row" : "column");
13878
+ const sa = (side) => safeArea ? `max(${padding}px, env(safe-area-inset-${side}))` : `${padding}px`;
13879
+ const zoneStyle = {
13880
+ position: "absolute",
13881
+ display: "flex",
13882
+ flexDirection,
13883
+ gap,
13884
+ pointerEvents: interactive ? "auto" : "none",
13885
+ ...isTop && { top: sa("top") },
13886
+ ...isBottom && { bottom: sa("bottom") },
13887
+ ...isLeft && { left: sa("left") },
13888
+ ...isRight && { right: sa("right") },
13889
+ ...isCenter && !isHCenter && { top: "50%", transform: "translateY(-50%)" },
13890
+ ...isHCenter && !isCenter && { left: "50%", transform: "translateX(-50%)" },
13891
+ ...position === "center" && { top: "50%", left: "50%", transform: "translate(-50%, -50%)" },
13892
+ ...isHCenter && !isCenter && isTop && { top: sa("top") },
13893
+ ...isHCenter && !isCenter && isBottom && { bottom: sa("bottom") },
13894
+ alignItems: isHCenter ? "center" : isRight ? "flex-end" : "flex-start",
13895
+ ...style
13896
+ };
13897
+ return /* @__PURE__ */ jsx22("div", { className, style: zoneStyle, children });
13898
+ }
13899
+ function HUDBar({
13900
+ value,
13901
+ max,
13902
+ label,
13903
+ color = "#4fc3f7",
13904
+ trackColor = "rgba(255,255,255,0.08)",
13905
+ width = 180,
13906
+ height = 14,
13907
+ showValue = true,
13908
+ rtl = false,
13909
+ rounded = true,
13910
+ animated = true,
13911
+ style
13912
+ }) {
13913
+ const pct = max > 0 ? Math.max(0, Math.min(1, value / max)) : 0;
13914
+ const fillStyle = {
13915
+ width: `${pct * 100}%`,
13916
+ height: "100%",
13917
+ background: color,
13918
+ borderRadius: rounded ? height / 2 : 0,
13919
+ transition: animated ? "width 160ms ease-out" : void 0,
13920
+ ...rtl && { marginLeft: "auto" }
13921
+ };
13922
+ const trackStyle = {
13923
+ width,
13924
+ height,
13925
+ background: trackColor,
13926
+ borderRadius: rounded ? height / 2 : 0,
13927
+ overflow: "hidden",
13928
+ position: "relative",
13929
+ display: "flex",
13930
+ flexDirection: rtl ? "row-reverse" : "row"
13931
+ };
13932
+ const labelStyle = {
13933
+ fontFamily: "system-ui, sans-serif",
13934
+ fontSize: 11,
13935
+ letterSpacing: 0.5,
13936
+ textTransform: "uppercase",
13937
+ color: "#e0e7f1",
13938
+ opacity: 0.85,
13939
+ display: "flex",
13940
+ justifyContent: "space-between",
13941
+ marginBottom: 4
13942
+ };
13943
+ return /* @__PURE__ */ jsxs11("div", { style: { display: "flex", flexDirection: "column", ...style }, "aria-label": label, children: [
13944
+ (label || showValue) && /* @__PURE__ */ jsxs11("div", { style: labelStyle, children: [
13945
+ label && /* @__PURE__ */ jsx22("span", { children: label }),
13946
+ showValue && /* @__PURE__ */ jsxs11("span", { style: { fontVariantNumeric: "tabular-nums" }, children: [
13947
+ Math.round(value),
13948
+ " / ",
13949
+ max
13950
+ ] })
13951
+ ] }),
13952
+ /* @__PURE__ */ jsx22("div", { style: trackStyle, role: "progressbar", "aria-valuenow": value, "aria-valuemin": 0, "aria-valuemax": max, children: /* @__PURE__ */ jsx22("div", { style: fillStyle }) })
13953
+ ] });
13954
+ }
13955
+ function HUDCounter({
13956
+ value,
13957
+ icon,
13958
+ label,
13959
+ pulse = true,
13960
+ color = "#fff",
13961
+ fontSize = 18,
13962
+ style
13963
+ }) {
13964
+ void pulse;
13965
+ return /* @__PURE__ */ jsxs11(
13966
+ "div",
13967
+ {
13968
+ style: {
13969
+ display: "flex",
13970
+ alignItems: "center",
13971
+ gap: 6,
13972
+ fontFamily: "system-ui, sans-serif",
13973
+ fontSize,
13974
+ color,
13975
+ fontVariantNumeric: "tabular-nums",
13976
+ ...style
13977
+ },
13978
+ children: [
13979
+ icon && /* @__PURE__ */ jsx22("span", { style: { fontSize: fontSize * 1.1 }, children: icon }),
13980
+ /* @__PURE__ */ jsx22("span", { children: value }),
13981
+ label && /* @__PURE__ */ jsx22("span", { style: { opacity: 0.7, fontSize: fontSize * 0.75 }, children: label })
13982
+ ]
13983
+ }
13984
+ );
13985
+ }
13986
+
13361
13987
  // src/utils/animationHelpers.ts
13362
13988
  function playClip(world, entityId, clipName) {
13363
13989
  const anim = world.getComponent(entityId, "AnimationState");
@@ -13387,7 +14013,7 @@ function definePrefab(name, defaults, render) {
13387
14013
  }
13388
14014
 
13389
14015
  // src/hooks/useNetworkSync.ts
13390
- import { useEffect as useEffect81, useRef as useRef48 } from "react";
14016
+ import { useEffect as useEffect83, useRef as useRef49 } from "react";
13391
14017
 
13392
14018
  // ../../packages/net/src/transport.ts
13393
14019
  function isBinaryTransport(t) {
@@ -13732,17 +14358,17 @@ function syncEntity(config) {
13732
14358
  }
13733
14359
 
13734
14360
  // ../../packages/net/src/useNetworkInput.ts
13735
- import { useState as useState29, useEffect as useEffect80, useRef as useRef47 } from "react";
14361
+ import { useState as useState30, useEffect as useEffect82, useRef as useRef48 } from "react";
13736
14362
  var INPUT_MSG_TYPE = "input:state";
13737
14363
  function useNetworkInput(config) {
13738
14364
  const { room, keys, input, tickRate = 20 } = config;
13739
14365
  const intervalMs = 1e3 / tickRate;
13740
- const [localInput, setLocalInput] = useState29(
14366
+ const [localInput, setLocalInput] = useState30(
13741
14367
  () => Object.fromEntries(keys.map((k) => [k, false]))
13742
14368
  );
13743
- const [remoteInputs] = useState29(() => /* @__PURE__ */ new Map());
13744
- const localInputRef = useRef47(localInput);
13745
- useEffect80(() => {
14369
+ const [remoteInputs] = useState30(() => /* @__PURE__ */ new Map());
14370
+ const localInputRef = useRef48(localInput);
14371
+ useEffect82(() => {
13746
14372
  let cleanupDom = null;
13747
14373
  if (!input) {
13748
14374
  let handleKeyDown2 = function(e) {
@@ -13956,9 +14582,9 @@ function lerpState(a, b, t) {
13956
14582
 
13957
14583
  // src/hooks/useNetworkSync.ts
13958
14584
  function useNetworkSync(entityId, components, room, world, owner, opts = {}) {
13959
- const optsRef = useRef48(opts);
14585
+ const optsRef = useRef49(opts);
13960
14586
  optsRef.current = opts;
13961
- useEffect81(() => {
14587
+ useEffect83(() => {
13962
14588
  const sync = syncEntity({
13963
14589
  entityId,
13964
14590
  components,
@@ -13973,18 +14599,18 @@ function useNetworkSync(entityId, components, room, world, owner, opts = {}) {
13973
14599
  }
13974
14600
 
13975
14601
  // src/hooks/useRemotePlayer.ts
13976
- import { useEffect as useEffect82, useRef as useRef49, useState as useState30 } from "react";
14602
+ import { useEffect as useEffect84, useRef as useRef50, useState as useState31 } from "react";
13977
14603
  var PEER_JOIN_MSG = "peer:join";
13978
14604
  var PEER_LEAVE_MSG = "peer:leave";
13979
14605
  function useRemotePlayer(config) {
13980
14606
  const { room, world, createEntity, destroyEntity } = config;
13981
- const [players, setPlayers] = useState30(() => /* @__PURE__ */ new Map());
13982
- const playersRef = useRef49(players);
13983
- const createRef = useRef49(createEntity);
14607
+ const [players, setPlayers] = useState31(() => /* @__PURE__ */ new Map());
14608
+ const playersRef = useRef50(players);
14609
+ const createRef = useRef50(createEntity);
13984
14610
  createRef.current = createEntity;
13985
- const destroyRef = useRef49(destroyEntity);
14611
+ const destroyRef = useRef50(destroyEntity);
13986
14612
  destroyRef.current = destroyEntity;
13987
- useEffect82(() => {
14613
+ useEffect84(() => {
13988
14614
  function spawnPeer(peerId) {
13989
14615
  if (playersRef.current.has(peerId)) return;
13990
14616
  const entityId = createRef.current(peerId);
@@ -14056,6 +14682,10 @@ export {
14056
14682
  FocusRing,
14057
14683
  Game,
14058
14684
  Gradient,
14685
+ HUD,
14686
+ HUDBar,
14687
+ HUDCounter,
14688
+ HUDZone,
14059
14689
  HalfSpaceCollider,
14060
14690
  HeightFieldCollider,
14061
14691
  HierarchySystem,
@@ -14066,6 +14696,7 @@ export {
14066
14696
  Mask,
14067
14697
  MovingPlatform,
14068
14698
  NineSlice,
14699
+ PARTICLE_PRESETS,
14069
14700
  ParallaxLayer,
14070
14701
  ParticleEmitter,
14071
14702
  Polygon,
@@ -14090,6 +14721,7 @@ export {
14090
14721
  TriMeshCollider,
14091
14722
  TriangleCollider,
14092
14723
  VectorPath,
14724
+ VirtualCamera,
14093
14725
  VirtualJoystick,
14094
14726
  World,
14095
14727
  addForce,
@@ -14230,8 +14862,10 @@ export {
14230
14862
  useAudioListener,
14231
14863
  useAudioScheduler,
14232
14864
  useCamera,
14865
+ useCameraBlend,
14233
14866
  useCameraLookahead,
14234
14867
  useCharacterController,
14868
+ useCinematicSequence,
14235
14869
  useCircleEnter,
14236
14870
  useCircleExit,
14237
14871
  useCircleStay,