@almadar/ui 5.110.0 → 5.111.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var React6 = require('react');
3
+ var React3 = require('react');
4
4
  var providers = require('@almadar/ui/providers');
5
5
  var logger = require('@almadar/logger');
6
6
  var fiber = require('@react-three/fiber');
@@ -37,7 +37,7 @@ function _interopNamespace(e) {
37
37
  return Object.freeze(n);
38
38
  }
39
39
 
40
- var React6__default = /*#__PURE__*/_interopDefault(React6);
40
+ var React3__default = /*#__PURE__*/_interopDefault(React3);
41
41
  var THREE9__namespace = /*#__PURE__*/_interopNamespace(THREE9);
42
42
 
43
43
  var __defProp = Object.defineProperty;
@@ -117,10 +117,10 @@ var fallbackEventBus = {
117
117
  }
118
118
  };
119
119
  function useEventBus() {
120
- const context = React6.useContext(providers.EventBusContext);
120
+ const context = React3.useContext(providers.EventBusContext);
121
121
  const baseBus = context ?? getGlobalEventBus() ?? fallbackEventBus;
122
122
  const scope = providers.useTraitScope();
123
- return React6.useMemo(() => {
123
+ return React3.useMemo(() => {
124
124
  if (!scope) {
125
125
  return {
126
126
  ...baseBus,
@@ -156,7 +156,7 @@ function useEventBus() {
156
156
  }
157
157
  function useEmitEvent() {
158
158
  const eventBus = useEventBus();
159
- return React6.useCallback(
159
+ return React3.useCallback(
160
160
  (type, payload, source) => {
161
161
  eventBus.emit(type, payload, source);
162
162
  },
@@ -208,9 +208,9 @@ function useGameCanvas3DEvents(options) {
208
208
  onUnitAnimation
209
209
  } = options;
210
210
  const emit = useEmitEvent();
211
- const optionsRef = React6.useRef(options);
211
+ const optionsRef = React3.useRef(options);
212
212
  optionsRef.current = options;
213
- const handleTileClick = React6.useCallback(
213
+ const handleTileClick = React3.useCallback(
214
214
  (tile, event) => {
215
215
  if (tileClickEvent) {
216
216
  emit(tileClickEvent, {
@@ -226,7 +226,7 @@ function useGameCanvas3DEvents(options) {
226
226
  },
227
227
  [tileClickEvent, emit]
228
228
  );
229
- const handleUnitClick = React6.useCallback(
229
+ const handleUnitClick = React3.useCallback(
230
230
  (unit, event) => {
231
231
  if (unitClickEvent) {
232
232
  emit(unitClickEvent, {
@@ -245,7 +245,7 @@ function useGameCanvas3DEvents(options) {
245
245
  },
246
246
  [unitClickEvent, emit]
247
247
  );
248
- const handleFeatureClick = React6.useCallback(
248
+ const handleFeatureClick = React3.useCallback(
249
249
  (feature, event) => {
250
250
  if (featureClickEvent) {
251
251
  emit(featureClickEvent, {
@@ -260,7 +260,7 @@ function useGameCanvas3DEvents(options) {
260
260
  },
261
261
  [featureClickEvent, emit]
262
262
  );
263
- const handleCanvasClick = React6.useCallback(
263
+ const handleCanvasClick = React3.useCallback(
264
264
  (event) => {
265
265
  if (canvasClickEvent) {
266
266
  emit(canvasClickEvent, {
@@ -273,7 +273,7 @@ function useGameCanvas3DEvents(options) {
273
273
  },
274
274
  [canvasClickEvent, emit]
275
275
  );
276
- const handleTileHover = React6.useCallback(
276
+ const handleTileHover = React3.useCallback(
277
277
  (tile, event) => {
278
278
  if (tile) {
279
279
  if (tileHoverEvent) {
@@ -293,7 +293,7 @@ function useGameCanvas3DEvents(options) {
293
293
  },
294
294
  [tileHoverEvent, tileLeaveEvent, emit]
295
295
  );
296
- const handleUnitAnimation = React6.useCallback(
296
+ const handleUnitAnimation = React3.useCallback(
297
297
  (unitId, state) => {
298
298
  if (unitAnimationEvent) {
299
299
  emit(unitAnimationEvent, {
@@ -306,7 +306,7 @@ function useGameCanvas3DEvents(options) {
306
306
  },
307
307
  [unitAnimationEvent, emit]
308
308
  );
309
- const handleCameraChange = React6.useCallback(
309
+ const handleCameraChange = React3.useCallback(
310
310
  (position) => {
311
311
  if (cameraChangeEvent) {
312
312
  emit(cameraChangeEvent, {
@@ -373,7 +373,7 @@ function Canvas3DLoadingState({
373
373
  ] });
374
374
  }
375
375
  var log2 = logger.createLogger("almadar:ui:game:canvas3d:error-boundary");
376
- var Canvas3DErrorBoundary = class extends React6.Component {
376
+ var Canvas3DErrorBoundary = class extends React3.Component {
377
377
  constructor(props) {
378
378
  super(props);
379
379
  __publicField(this, "handleReset", () => {
@@ -502,7 +502,7 @@ function CameraController3D({
502
502
  onCameraChange
503
503
  }) {
504
504
  const { camera } = fiber.useThree();
505
- React6.useEffect(() => {
505
+ React3.useEffect(() => {
506
506
  if (onCameraChange) {
507
507
  onCameraChange({
508
508
  x: camera.position.x,
@@ -518,8 +518,8 @@ function FollowCamera3D({
518
518
  offset
519
519
  }) {
520
520
  const { camera } = fiber.useThree();
521
- const look = React6.useRef(new THREE9__namespace.Vector3(target[0], target[1], target[2]));
522
- const goal = React6.useRef(new THREE9__namespace.Vector3());
521
+ const look = React3.useRef(new THREE9__namespace.Vector3(target[0], target[1], target[2]));
522
+ const goal = React3.useRef(new THREE9__namespace.Vector3());
523
523
  fiber.useFrame((_, delta) => {
524
524
  const t = Math.min(1, delta * 5);
525
525
  goal.current.set(target[0] + offset[0], target[1] + offset[1], target[2] + offset[2]);
@@ -529,6 +529,54 @@ function FollowCamera3D({
529
529
  });
530
530
  return null;
531
531
  }
532
+
533
+ // lib/atlasSlice.ts
534
+ var atlasCache = /* @__PURE__ */ new Map();
535
+ function isTilesheet(a) {
536
+ return typeof a.tileWidth === "number";
537
+ }
538
+ function getAtlas(url, onReady) {
539
+ if (atlasCache.has(url)) return atlasCache.get(url) ?? void 0;
540
+ atlasCache.set(url, void 0);
541
+ fetch(url).then((r) => r.json()).then((json) => {
542
+ atlasCache.set(url, json);
543
+ onReady();
544
+ }).catch(() => {
545
+ atlasCache.set(url, null);
546
+ });
547
+ return void 0;
548
+ }
549
+ function subRectFor(atlas, sprite) {
550
+ if (isTilesheet(atlas)) {
551
+ let col;
552
+ let row;
553
+ if (sprite.includes(",")) {
554
+ const [c, r] = sprite.split(",").map((n) => Number(n.trim()));
555
+ col = c;
556
+ row = r;
557
+ } else {
558
+ const i = Number(sprite);
559
+ if (!Number.isFinite(i)) return null;
560
+ col = i % atlas.columns;
561
+ row = Math.floor(i / atlas.columns);
562
+ }
563
+ if (!Number.isFinite(col) || !Number.isFinite(row) || col < 0 || row < 0 || col >= atlas.columns || row >= atlas.rows) return null;
564
+ const margin = atlas.margin ?? 0;
565
+ const spacing = atlas.spacing ?? 0;
566
+ return {
567
+ sx: margin + col * (atlas.tileWidth + spacing),
568
+ sy: margin + row * (atlas.tileHeight + spacing),
569
+ sw: atlas.tileWidth,
570
+ sh: atlas.tileHeight
571
+ };
572
+ }
573
+ const st = atlas.subTextures[sprite];
574
+ if (!st) return null;
575
+ return { sx: st.x, sy: st.y, sw: st.width, sh: st.height };
576
+ }
577
+ function isAtlasAsset(asset) {
578
+ return !!asset && typeof asset.atlas === "string" && typeof asset.sprite === "string";
579
+ }
532
580
  var log3 = logger.createLogger("almadar:ui:game:model-loader");
533
581
  var gltfCache = /* @__PURE__ */ new Map();
534
582
  function loadGltfCached(url, assetRoot) {
@@ -573,10 +621,10 @@ function stateFromEntry(entry) {
573
621
  return { model: null, clips: [], isLoading: !entry.error, error: entry.error ?? null };
574
622
  }
575
623
  function useGLTFModel(url, resourceBasePath) {
576
- const [state, setState] = React6.useState(
624
+ const [state, setState] = React3.useState(
577
625
  () => stateFromEntry(url ? loadGltfCached(url, resourceBasePath || detectAssetRoot(url)) : null)
578
626
  );
579
- React6.useEffect(() => {
627
+ React3.useEffect(() => {
580
628
  if (!url) {
581
629
  setState({ model: null, clips: [], isLoading: false, error: null });
582
630
  return;
@@ -611,7 +659,7 @@ function ModelLoader({
611
659
  tint
612
660
  }) {
613
661
  const { model: loadedModel, clips, isLoading, error } = useGLTFModel(url, resourceBasePath);
614
- const model = React6.useMemo(() => {
662
+ const model = React3.useMemo(() => {
615
663
  if (!loadedModel) return null;
616
664
  const cloned = SkeletonUtils.clone(loadedModel);
617
665
  cloned.updateMatrixWorld(true);
@@ -629,8 +677,8 @@ function ModelLoader({
629
677
  });
630
678
  return cloned;
631
679
  }, [loadedModel, castShadow, receiveShadow, tint]);
632
- const mixer = React6.useMemo(() => model ? new THREE9__namespace.AnimationMixer(model) : null, [model]);
633
- React6.useEffect(() => {
680
+ const mixer = React3.useMemo(() => model ? new THREE9__namespace.AnimationMixer(model) : null, [model]);
681
+ React3.useEffect(() => {
634
682
  if (!mixer || !animation || clips.length === 0) return;
635
683
  const wanted = animation.toLowerCase();
636
684
  const clip = clips.find((c) => c.name === animation) ?? clips.find((c) => c.name.toLowerCase() === wanted);
@@ -644,7 +692,7 @@ function ModelLoader({
644
692
  fiber.useFrame((_, delta) => {
645
693
  mixer?.update(delta);
646
694
  });
647
- const normFactor = React6.useMemo(() => {
695
+ const normFactor = React3.useMemo(() => {
648
696
  if (!model) return 1;
649
697
  const box = new THREE9__namespace.Box3().setFromObject(model);
650
698
  const size = new THREE9__namespace.Vector3();
@@ -653,11 +701,11 @@ function ModelLoader({
653
701
  if (!Number.isFinite(maxDim) || maxDim < 0.05) return 1;
654
702
  return 1 / maxDim;
655
703
  }, [model]);
656
- const scaleArray = React6.useMemo(() => {
704
+ const scaleArray = React3.useMemo(() => {
657
705
  const base = typeof scale === "number" ? [scale, scale, scale] : scale;
658
706
  return [base[0] * normFactor, base[1] * normFactor, base[2] * normFactor];
659
707
  }, [scale, normFactor]);
660
- const rotationRad = React6.useMemo(() => {
708
+ const rotationRad = React3.useMemo(() => {
661
709
  return [
662
710
  rotation[0] * Math.PI / 180,
663
711
  rotation[1] * Math.PI / 180,
@@ -730,22 +778,126 @@ function ModelLoader({
730
778
  }
731
779
  );
732
780
  }
781
+ var CrossOriginTextureLoader = class extends THREE9__namespace.TextureLoader {
782
+ constructor() {
783
+ super();
784
+ this.crossOrigin = "anonymous";
785
+ }
786
+ };
787
+ function useBillboardTexture(url) {
788
+ const [state, setState] = React3__default.default.useState({
789
+ texture: null,
790
+ error: false
791
+ });
792
+ React3__default.default.useEffect(() => {
793
+ let active = true;
794
+ const loader = new CrossOriginTextureLoader();
795
+ loader.load(
796
+ url,
797
+ (texture) => {
798
+ if (!active) return;
799
+ console.log("[SpriteBillboard] texture loaded", url, texture.image?.width, texture.image?.height);
800
+ texture.colorSpace = THREE9__namespace.SRGBColorSpace;
801
+ setState({ texture, error: false });
802
+ },
803
+ void 0,
804
+ (err) => {
805
+ if (!active) return;
806
+ console.error("[SpriteBillboard] texture load error", url, err);
807
+ setState({ texture: null, error: true });
808
+ }
809
+ );
810
+ return () => {
811
+ active = false;
812
+ };
813
+ }, [url]);
814
+ return state;
815
+ }
816
+ function useAtlasFrame(asset) {
817
+ const [tick, setTick] = React3__default.default.useState(0);
818
+ React3__default.default.useEffect(() => {
819
+ if (!isAtlasAsset(asset)) return;
820
+ getAtlas(asset.atlas, () => setTick((t) => t + 1));
821
+ }, [asset, tick]);
822
+ return React3__default.default.useMemo(() => {
823
+ if (!isAtlasAsset(asset)) return { frame: null, ready: true };
824
+ const atlas = getAtlas(asset.atlas, () => setTick((t) => t + 1));
825
+ if (!atlas) {
826
+ console.log("[SpriteBillboard] atlas not ready", asset.atlas, asset.sprite);
827
+ return { frame: null, ready: false };
828
+ }
829
+ const r = subRectFor(atlas, asset.sprite);
830
+ console.log("[SpriteBillboard] atlas frame", asset.atlas, asset.sprite, r);
831
+ if (!r) return { frame: null, ready: true };
832
+ return { frame: { x: r.sx, y: r.sy, w: r.sw, h: r.sh }, ready: true };
833
+ }, [asset, tick]);
834
+ }
733
835
  function SpriteBillboard({ node, world }) {
734
- const texture = fiber.useLoader(THREE9__namespace.TextureLoader, node.asset.url);
735
- const img = texture.image;
736
- const imgW = img?.width || 1;
737
- const imgH = img?.height || 1;
738
- if (node.frame) {
739
- texture.repeat.set(node.frame.w / imgW, node.frame.h / imgH);
740
- texture.offset.set(node.frame.x / imgW, 1 - (node.frame.y + node.frame.h) / imgH);
741
- texture.magFilter = texture.minFilter = THREE9__namespace.NearestFilter;
742
- texture.needsUpdate = true;
836
+ const { texture, error: textureError } = useBillboardTexture(node.asset.url);
837
+ const { frame: atlasFrame, ready: atlasReady } = useAtlasFrame(node.asset);
838
+ const frame = node.frame ?? atlasFrame;
839
+ const anchor = node.anchor ?? "top-left";
840
+ console.log("[SpriteBillboard] render", node.asset.url, anchor, frame, "texture?", !!texture, "atlasReady?", atlasReady);
841
+ const size = React3__default.default.useMemo(() => {
842
+ const img = texture?.image;
843
+ const imgW = img?.width || 1;
844
+ const imgH = img?.height || 1;
845
+ const aspect = frame ? frame.w / frame.h : imgW / imgH;
846
+ if (anchor === "top-left") {
847
+ const width2 = node.width ?? 1;
848
+ const height2 = node.height ?? 1;
849
+ return { width: width2, height: height2, imgW, imgH };
850
+ }
851
+ const height = node.height ?? 1;
852
+ const width = node.width ?? height * aspect;
853
+ return { width, height, imgW, imgH };
854
+ }, [texture, frame, node.height, node.width, anchor]);
855
+ if (!texture || !atlasReady) {
856
+ if (anchor === "top-left") {
857
+ return /* @__PURE__ */ jsxRuntime.jsx("group", { position: [world[0] + size.width / 2, 0.02, world[2] + size.height / 2], children: /* @__PURE__ */ jsxRuntime.jsxs("mesh", { rotation: [-Math.PI / 2, 0, 0], children: [
858
+ /* @__PURE__ */ jsxRuntime.jsx("planeGeometry", { args: [size.width, size.height] }),
859
+ /* @__PURE__ */ jsxRuntime.jsx("meshBasicMaterial", { color: textureError ? 16729156 : 8947848, transparent: true, opacity: 0.6, side: THREE9__namespace.DoubleSide })
860
+ ] }) });
861
+ }
862
+ return /* @__PURE__ */ jsxRuntime.jsx("group", { position: [world[0], world[1] + size.height / 2, world[2]], children: /* @__PURE__ */ jsxRuntime.jsxs("mesh", { children: [
863
+ /* @__PURE__ */ jsxRuntime.jsx("planeGeometry", { args: [size.width, size.height] }),
864
+ /* @__PURE__ */ jsxRuntime.jsx("meshBasicMaterial", { color: textureError ? 16729156 : 8947848, transparent: true, opacity: 0.6, side: THREE9__namespace.DoubleSide })
865
+ ] }) });
866
+ }
867
+ const groundGeometry = React3__default.default.useMemo(() => {
868
+ if (anchor !== "top-left" || !texture || !atlasReady) return null;
869
+ const g = new THREE9__namespace.PlaneGeometry(size.width, size.height);
870
+ g.rotateX(-Math.PI / 2);
871
+ if (frame) {
872
+ const u0 = frame.x / size.imgW;
873
+ const v0 = 1 - (frame.y + frame.h) / size.imgH;
874
+ const u1 = u0 + frame.w / size.imgW;
875
+ const v1 = v0 + frame.h / size.imgH;
876
+ const uvs = new Float32Array([u0, v1, u1, v1, u0, v0, u1, v0]);
877
+ g.setAttribute("uv", new THREE9__namespace.BufferAttribute(uvs, 2));
878
+ }
879
+ return g;
880
+ }, [anchor, texture, atlasReady, size.width, size.height, size.imgW, size.imgH, frame]);
881
+ texture.magFilter = texture.minFilter = THREE9__namespace.NearestFilter;
882
+ texture.needsUpdate = true;
883
+ if (anchor === "top-left") {
884
+ return /* @__PURE__ */ jsxRuntime.jsx("group", { position: [world[0] + size.width / 2, 0.01, world[2] + size.height / 2], children: /* @__PURE__ */ jsxRuntime.jsx("mesh", { geometry: groundGeometry ?? void 0, children: /* @__PURE__ */ jsxRuntime.jsx(
885
+ "meshBasicMaterial",
886
+ {
887
+ map: texture,
888
+ transparent: true,
889
+ alphaTest: 0.1,
890
+ side: THREE9__namespace.DoubleSide,
891
+ opacity: node.opacity ?? 1
892
+ }
893
+ ) }) });
894
+ }
895
+ if (frame) {
896
+ texture.repeat.set(frame.w / size.imgW, frame.h / size.imgH);
897
+ texture.offset.set(frame.x / size.imgW, 1 - (frame.y + frame.h) / size.imgH);
743
898
  }
744
- const aspect = node.frame ? node.frame.w / node.frame.h : imgW / imgH;
745
- const height = node.height ?? 1;
746
- const width = node.width ?? height * aspect;
747
- return /* @__PURE__ */ jsxRuntime.jsx(drei.Billboard, { position: [world[0], world[1] + height / 2, world[2]], children: /* @__PURE__ */ jsxRuntime.jsxs("mesh", { children: [
748
- /* @__PURE__ */ jsxRuntime.jsx("planeGeometry", { args: [width, height] }),
899
+ return /* @__PURE__ */ jsxRuntime.jsx(drei.Billboard, { position: [world[0], world[1] + size.height / 2, world[2]], children: /* @__PURE__ */ jsxRuntime.jsxs("mesh", { children: [
900
+ /* @__PURE__ */ jsxRuntime.jsx("planeGeometry", { args: [size.width, size.height] }),
749
901
  /* @__PURE__ */ jsxRuntime.jsx(
750
902
  "meshBasicMaterial",
751
903
  {
@@ -868,7 +1020,7 @@ var DEFAULT_GRID_CONFIG = {
868
1020
  offsetX: 0,
869
1021
  offsetZ: 0
870
1022
  };
871
- var Canvas3DHost = React6.forwardRef(
1023
+ var Canvas3DHost = React3.forwardRef(
872
1024
  ({
873
1025
  cameraMode = "isometric",
874
1026
  followTarget,
@@ -894,12 +1046,12 @@ var Canvas3DHost = React6.forwardRef(
894
1046
  children,
895
1047
  drawables
896
1048
  }, ref) => {
897
- const containerRef = React6.useRef(null);
898
- const controlsRef = React6.useRef(null);
899
- const [internalError, setInternalError] = React6.useState(null);
1049
+ const containerRef = React3.useRef(null);
1050
+ const controlsRef = React3.useRef(null);
1051
+ const [internalError, setInternalError] = React3.useState(null);
900
1052
  const eventBus = useEventBus();
901
- const keysRef = React6.useRef(/* @__PURE__ */ new Set());
902
- React6.useEffect(() => {
1053
+ const keysRef = React3.useRef(/* @__PURE__ */ new Set());
1054
+ React3.useEffect(() => {
903
1055
  if (!keyMap && !keyUpMap) return;
904
1056
  const down = (e) => {
905
1057
  if (keysRef.current.has(e.code)) return;
@@ -932,10 +1084,10 @@ var Canvas3DHost = React6.forwardRef(
932
1084
  unitAnimationEvent,
933
1085
  cameraChangeEvent
934
1086
  });
935
- const drawnItems = React6.useMemo(() => collectDrawnItems(drawables ?? []), [drawables]);
936
- const scenePositions = React6.useMemo(() => drawnItems.map((i) => i.pos), [drawnItems]);
937
- const hitIndex = React6.useMemo(() => buildHitIndex(drawnItems), [drawnItems]);
938
- const gridBounds = React6.useMemo(() => {
1087
+ const drawnItems = React3.useMemo(() => collectDrawnItems(drawables ?? []), [drawables]);
1088
+ const scenePositions = React3.useMemo(() => drawnItems.map((i) => i.pos), [drawnItems]);
1089
+ const hitIndex = React3.useMemo(() => buildHitIndex(drawnItems), [drawnItems]);
1090
+ const gridBounds = React3.useMemo(() => {
939
1091
  if (scenePositions.length === 0) {
940
1092
  return { minX: 0, maxX: 10, minZ: 0, maxZ: 10 };
941
1093
  }
@@ -951,7 +1103,7 @@ var Canvas3DHost = React6.forwardRef(
951
1103
  }
952
1104
  return { minX, maxX, minZ, maxZ };
953
1105
  }, [scenePositions]);
954
- const cameraTarget = React6.useMemo(
1106
+ const cameraTarget = React3.useMemo(
955
1107
  () => [
956
1108
  (gridBounds.minX + gridBounds.maxX) / 2,
957
1109
  0,
@@ -959,7 +1111,7 @@ var Canvas3DHost = React6.forwardRef(
959
1111
  ],
960
1112
  [gridBounds]
961
1113
  );
962
- const gridConfig = React6.useMemo(
1114
+ const gridConfig = React3.useMemo(
963
1115
  () => ({
964
1116
  ...DEFAULT_GRID_CONFIG,
965
1117
  offsetX: -(gridBounds.maxX - gridBounds.minX) / 2,
@@ -967,7 +1119,7 @@ var Canvas3DHost = React6.forwardRef(
967
1119
  }),
968
1120
  [gridBounds]
969
1121
  );
970
- const drawableProjector = React6.useMemo(
1122
+ const drawableProjector = React3.useMemo(
971
1123
  () => create3DProjector({
972
1124
  cellSize: gridConfig.cellSize,
973
1125
  offsetX: -gridBounds.minX * gridConfig.cellSize,
@@ -975,7 +1127,7 @@ var Canvas3DHost = React6.forwardRef(
975
1127
  }),
976
1128
  [gridBounds, gridConfig]
977
1129
  );
978
- React6.useImperativeHandle(ref, () => ({
1130
+ React3.useImperativeHandle(ref, () => ({
979
1131
  getCameraPosition: () => {
980
1132
  if (controlsRef.current) {
981
1133
  const pos = controlsRef.current.object.position;
@@ -1008,7 +1160,7 @@ var Canvas3DHost = React6.forwardRef(
1008
1160
  return null;
1009
1161
  }
1010
1162
  }));
1011
- const cameraConfig = React6.useMemo(() => {
1163
+ const cameraConfig = React3.useMemo(() => {
1012
1164
  const size = Math.max(
1013
1165
  gridBounds.maxX - gridBounds.minX,
1014
1166
  gridBounds.maxZ - gridBounds.minZ,
@@ -1030,15 +1182,15 @@ var Canvas3DHost = React6.forwardRef(
1030
1182
  return { position: [cx + d, d, cz + d], fov: 45 };
1031
1183
  }
1032
1184
  }, [cameraMode, gridBounds]);
1033
- const followWorld = React6.useMemo(() => {
1185
+ const followWorld = React3.useMemo(() => {
1034
1186
  if (followTarget) return drawableProjector.toWorld(followTarget);
1035
1187
  return cameraTarget;
1036
1188
  }, [followTarget, drawableProjector, cameraTarget]);
1037
- const followOffset = React6.useMemo(
1189
+ const followOffset = React3.useMemo(
1038
1190
  () => cameraMode === "chase" ? [0, 4, -7] : [5, 7, 5],
1039
1191
  [cameraMode]
1040
1192
  );
1041
- const handleGroundClick = React6.useCallback((e) => {
1193
+ const handleGroundClick = React3.useCallback((e) => {
1042
1194
  if (!tileClickEvent && !unitClickEvent) return;
1043
1195
  e.stopPropagation();
1044
1196
  const cell = {
@@ -1191,8 +1343,8 @@ var Canvas3DHost = React6.forwardRef(
1191
1343
  Canvas3DHost.displayName = "Canvas3DHost";
1192
1344
  function Scene3D({ background = "#1a1a2e", fog, children }) {
1193
1345
  const { scene } = fiber.useThree();
1194
- const initializedRef = React6.useRef(false);
1195
- React6.useEffect(() => {
1346
+ const initializedRef = React3.useRef(false);
1347
+ React3.useEffect(() => {
1196
1348
  if (initializedRef.current) return;
1197
1349
  initializedRef.current = true;
1198
1350
  if (background.startsWith("#") || background.startsWith("rgb")) {
@@ -1213,7 +1365,7 @@ function Scene3D({ background = "#1a1a2e", fog, children }) {
1213
1365
  }, [scene, background, fog]);
1214
1366
  return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
1215
1367
  }
1216
- var Camera3D = React6.forwardRef(
1368
+ var Camera3D = React3.forwardRef(
1217
1369
  ({
1218
1370
  mode = "isometric",
1219
1371
  position = [10, 10, 10],
@@ -1226,10 +1378,10 @@ var Camera3D = React6.forwardRef(
1226
1378
  onChange
1227
1379
  }, ref) => {
1228
1380
  const { camera, set, viewport } = fiber.useThree();
1229
- const controlsRef = React6.useRef(null);
1230
- const initialPosition = React6.useRef(new THREE9__namespace.Vector3(...position));
1231
- const initialTarget = React6.useRef(new THREE9__namespace.Vector3(...target));
1232
- React6.useEffect(() => {
1381
+ const controlsRef = React3.useRef(null);
1382
+ const initialPosition = React3.useRef(new THREE9__namespace.Vector3(...position));
1383
+ const initialTarget = React3.useRef(new THREE9__namespace.Vector3(...target));
1384
+ React3.useEffect(() => {
1233
1385
  let newCamera;
1234
1386
  if (mode === "isometric") {
1235
1387
  const aspect = viewport.aspect;
@@ -1260,7 +1412,7 @@ var Camera3D = React6.forwardRef(
1260
1412
  onChange(camera);
1261
1413
  }
1262
1414
  });
1263
- React6.useImperativeHandle(ref, () => ({
1415
+ React3.useImperativeHandle(ref, () => ({
1264
1416
  getCamera: () => camera,
1265
1417
  setPosition: (x, y, z) => {
1266
1418
  camera.position.set(x, y, z);
@@ -1514,21 +1666,21 @@ var DEFAULT_OPTIONS = {
1514
1666
  };
1515
1667
  function useThree4(options = {}) {
1516
1668
  const opts = { ...DEFAULT_OPTIONS, ...options };
1517
- const containerRef = React6.useRef(null);
1518
- const canvasRef = React6.useRef(null);
1519
- const rendererRef = React6.useRef(null);
1520
- const sceneRef = React6.useRef(null);
1521
- const cameraRef = React6.useRef(null);
1522
- const controlsRef = React6.useRef(null);
1523
- const gridHelperRef = React6.useRef(null);
1524
- const rafRef = React6.useRef(0);
1525
- const [isReady, setIsReady] = React6.useState(false);
1526
- const [dimensions, setDimensions] = React6.useState({ width: 0, height: 0 });
1527
- const initialCameraPosition = React6.useMemo(
1669
+ const containerRef = React3.useRef(null);
1670
+ const canvasRef = React3.useRef(null);
1671
+ const rendererRef = React3.useRef(null);
1672
+ const sceneRef = React3.useRef(null);
1673
+ const cameraRef = React3.useRef(null);
1674
+ const controlsRef = React3.useRef(null);
1675
+ const gridHelperRef = React3.useRef(null);
1676
+ const rafRef = React3.useRef(0);
1677
+ const [isReady, setIsReady] = React3.useState(false);
1678
+ const [dimensions, setDimensions] = React3.useState({ width: 0, height: 0 });
1679
+ const initialCameraPosition = React3.useMemo(
1528
1680
  () => new THREE9__namespace.Vector3(...opts.cameraPosition),
1529
1681
  []
1530
1682
  );
1531
- React6.useEffect(() => {
1683
+ React3.useEffect(() => {
1532
1684
  if (!containerRef.current) return;
1533
1685
  const container = containerRef.current;
1534
1686
  const { clientWidth, clientHeight } = container;
@@ -1621,7 +1773,7 @@ function useThree4(options = {}) {
1621
1773
  scene.clear();
1622
1774
  };
1623
1775
  }, []);
1624
- React6.useEffect(() => {
1776
+ React3.useEffect(() => {
1625
1777
  if (!cameraRef.current || !containerRef.current) return;
1626
1778
  const container = containerRef.current;
1627
1779
  const { clientWidth, clientHeight } = container;
@@ -1656,20 +1808,20 @@ function useThree4(options = {}) {
1656
1808
  animate();
1657
1809
  }
1658
1810
  }, [opts.cameraMode]);
1659
- const setCameraPosition = React6.useCallback((x, y, z) => {
1811
+ const setCameraPosition = React3.useCallback((x, y, z) => {
1660
1812
  if (cameraRef.current) {
1661
1813
  cameraRef.current.position.set(x, y, z);
1662
1814
  controlsRef.current?.update();
1663
1815
  }
1664
1816
  }, []);
1665
- const lookAt = React6.useCallback((x, y, z) => {
1817
+ const lookAt = React3.useCallback((x, y, z) => {
1666
1818
  if (cameraRef.current) {
1667
1819
  cameraRef.current.lookAt(x, y, z);
1668
1820
  controlsRef.current?.target.set(x, y, z);
1669
1821
  controlsRef.current?.update();
1670
1822
  }
1671
1823
  }, []);
1672
- const resetCamera = React6.useCallback(() => {
1824
+ const resetCamera = React3.useCallback(() => {
1673
1825
  if (cameraRef.current) {
1674
1826
  cameraRef.current.position.copy(initialCameraPosition);
1675
1827
  cameraRef.current.lookAt(0, 0, 0);
@@ -1679,7 +1831,7 @@ function useThree4(options = {}) {
1679
1831
  }
1680
1832
  }
1681
1833
  }, [initialCameraPosition]);
1682
- const fitView = React6.useCallback(
1834
+ const fitView = React3.useCallback(
1683
1835
  (bounds) => {
1684
1836
  if (!cameraRef.current) return;
1685
1837
  const centerX = (bounds.minX + bounds.maxX) / 2;
@@ -1710,20 +1862,20 @@ function useThree4(options = {}) {
1710
1862
  }
1711
1863
  function useAssetLoader(options = {}) {
1712
1864
  const { preloadUrls = [], loader: customLoader } = options;
1713
- const loaderRef = React6.useRef(customLoader || new AssetLoader());
1714
- const [state, setState] = React6.useState({
1865
+ const loaderRef = React3.useRef(customLoader || new AssetLoader());
1866
+ const [state, setState] = React3.useState({
1715
1867
  isLoading: false,
1716
1868
  progress: 0,
1717
1869
  loaded: 0,
1718
1870
  total: 0,
1719
1871
  errors: []
1720
1872
  });
1721
- React6.useEffect(() => {
1873
+ React3.useEffect(() => {
1722
1874
  if (preloadUrls.length > 0) {
1723
1875
  preload(preloadUrls);
1724
1876
  }
1725
1877
  }, []);
1726
- const updateProgress = React6.useCallback((loaded, total) => {
1878
+ const updateProgress = React3.useCallback((loaded, total) => {
1727
1879
  setState((prev) => ({
1728
1880
  ...prev,
1729
1881
  loaded,
@@ -1731,7 +1883,7 @@ function useAssetLoader(options = {}) {
1731
1883
  progress: total > 0 ? Math.round(loaded / total * 100) : 0
1732
1884
  }));
1733
1885
  }, []);
1734
- const loadModel = React6.useCallback(
1886
+ const loadModel = React3.useCallback(
1735
1887
  async (url) => {
1736
1888
  setState((prev) => ({ ...prev, isLoading: true }));
1737
1889
  try {
@@ -1754,7 +1906,7 @@ function useAssetLoader(options = {}) {
1754
1906
  },
1755
1907
  []
1756
1908
  );
1757
- const loadOBJ = React6.useCallback(
1909
+ const loadOBJ = React3.useCallback(
1758
1910
  async (url) => {
1759
1911
  setState((prev) => ({ ...prev, isLoading: true }));
1760
1912
  try {
@@ -1777,7 +1929,7 @@ function useAssetLoader(options = {}) {
1777
1929
  },
1778
1930
  []
1779
1931
  );
1780
- const loadTexture = React6.useCallback(
1932
+ const loadTexture = React3.useCallback(
1781
1933
  async (url) => {
1782
1934
  setState((prev) => ({ ...prev, isLoading: true }));
1783
1935
  try {
@@ -1800,7 +1952,7 @@ function useAssetLoader(options = {}) {
1800
1952
  },
1801
1953
  []
1802
1954
  );
1803
- const preload = React6.useCallback(
1955
+ const preload = React3.useCallback(
1804
1956
  async (urls) => {
1805
1957
  setState((prev) => ({
1806
1958
  ...prev,
@@ -1839,27 +1991,27 @@ function useAssetLoader(options = {}) {
1839
1991
  },
1840
1992
  [updateProgress]
1841
1993
  );
1842
- const hasModel = React6.useCallback((url) => {
1994
+ const hasModel = React3.useCallback((url) => {
1843
1995
  return loaderRef.current.hasModel(url);
1844
1996
  }, []);
1845
- const hasTexture = React6.useCallback((url) => {
1997
+ const hasTexture = React3.useCallback((url) => {
1846
1998
  return loaderRef.current.hasTexture(url);
1847
1999
  }, []);
1848
- const getModel = React6.useCallback((url) => {
2000
+ const getModel = React3.useCallback((url) => {
1849
2001
  try {
1850
2002
  return loaderRef.current.getModel(url);
1851
2003
  } catch {
1852
2004
  return void 0;
1853
2005
  }
1854
2006
  }, []);
1855
- const getTexture = React6.useCallback((url) => {
2007
+ const getTexture = React3.useCallback((url) => {
1856
2008
  try {
1857
2009
  return loaderRef.current.getTexture(url);
1858
2010
  } catch {
1859
2011
  return void 0;
1860
2012
  }
1861
2013
  }, []);
1862
- const clearCache = React6.useCallback(() => {
2014
+ const clearCache = React3.useCallback(() => {
1863
2015
  loaderRef.current.clearCache();
1864
2016
  setState({
1865
2017
  isLoading: false,
@@ -1883,25 +2035,25 @@ function useAssetLoader(options = {}) {
1883
2035
  };
1884
2036
  }
1885
2037
  function useSceneGraph() {
1886
- const nodesRef = React6.useRef(/* @__PURE__ */ new Map());
1887
- const addNode = React6.useCallback((node) => {
2038
+ const nodesRef = React3.useRef(/* @__PURE__ */ new Map());
2039
+ const addNode = React3.useCallback((node) => {
1888
2040
  const existing = nodesRef.current.get(node.id);
1889
2041
  if (existing) {
1890
2042
  existing.mesh.removeFromParent();
1891
2043
  }
1892
2044
  nodesRef.current.set(node.id, node);
1893
2045
  }, []);
1894
- const removeNode = React6.useCallback((id) => {
2046
+ const removeNode = React3.useCallback((id) => {
1895
2047
  const node = nodesRef.current.get(id);
1896
2048
  if (node) {
1897
2049
  node.mesh.removeFromParent();
1898
2050
  nodesRef.current.delete(id);
1899
2051
  }
1900
2052
  }, []);
1901
- const getNode = React6.useCallback((id) => {
2053
+ const getNode = React3.useCallback((id) => {
1902
2054
  return nodesRef.current.get(id);
1903
2055
  }, []);
1904
- const updateNodePosition = React6.useCallback(
2056
+ const updateNodePosition = React3.useCallback(
1905
2057
  (id, x, y, z) => {
1906
2058
  const node = nodesRef.current.get(id);
1907
2059
  if (node) {
@@ -1911,7 +2063,7 @@ function useSceneGraph() {
1911
2063
  },
1912
2064
  []
1913
2065
  );
1914
- const updateNodeGridPosition = React6.useCallback(
2066
+ const updateNodeGridPosition = React3.useCallback(
1915
2067
  (id, gridX, gridZ) => {
1916
2068
  const node = nodesRef.current.get(id);
1917
2069
  if (node) {
@@ -1920,7 +2072,7 @@ function useSceneGraph() {
1920
2072
  },
1921
2073
  []
1922
2074
  );
1923
- const getNodeAtGrid = React6.useCallback(
2075
+ const getNodeAtGrid = React3.useCallback(
1924
2076
  (x, z, type) => {
1925
2077
  return Array.from(nodesRef.current.values()).find((node) => {
1926
2078
  const matchesGrid = node.gridPosition.x === x && node.gridPosition.z === z;
@@ -1929,10 +2081,10 @@ function useSceneGraph() {
1929
2081
  },
1930
2082
  []
1931
2083
  );
1932
- const getNodesByType = React6.useCallback((type) => {
2084
+ const getNodesByType = React3.useCallback((type) => {
1933
2085
  return Array.from(nodesRef.current.values()).filter((node) => node.type === type);
1934
2086
  }, []);
1935
- const getNodesInBounds = React6.useCallback(
2087
+ const getNodesInBounds = React3.useCallback(
1936
2088
  (minX, maxX, minZ, maxZ) => {
1937
2089
  return Array.from(nodesRef.current.values()).filter((node) => {
1938
2090
  const { x, z } = node.gridPosition;
@@ -1941,13 +2093,13 @@ function useSceneGraph() {
1941
2093
  },
1942
2094
  []
1943
2095
  );
1944
- const clearNodes = React6.useCallback(() => {
2096
+ const clearNodes = React3.useCallback(() => {
1945
2097
  nodesRef.current.forEach((node) => {
1946
2098
  node.mesh.removeFromParent();
1947
2099
  });
1948
2100
  nodesRef.current.clear();
1949
2101
  }, []);
1950
- const countNodes = React6.useCallback((type) => {
2102
+ const countNodes = React3.useCallback((type) => {
1951
2103
  if (!type) {
1952
2104
  return nodesRef.current.size;
1953
2105
  }
@@ -1969,9 +2121,9 @@ function useSceneGraph() {
1969
2121
  }
1970
2122
  function useRaycaster(options) {
1971
2123
  const { camera, canvas, cellSize = 1, offsetX = 0, offsetZ = 0 } = options;
1972
- const raycaster = React6.useRef(new THREE9__namespace.Raycaster());
1973
- const mouse = React6.useRef(new THREE9__namespace.Vector2());
1974
- const clientToNDC = React6.useCallback(
2124
+ const raycaster = React3.useRef(new THREE9__namespace.Raycaster());
2125
+ const mouse = React3.useRef(new THREE9__namespace.Vector2());
2126
+ const clientToNDC = React3.useCallback(
1975
2127
  (clientX, clientY) => {
1976
2128
  if (!canvas) {
1977
2129
  return { x: 0, y: 0 };
@@ -1984,7 +2136,7 @@ function useRaycaster(options) {
1984
2136
  },
1985
2137
  [canvas]
1986
2138
  );
1987
- const isWithinCanvas = React6.useCallback(
2139
+ const isWithinCanvas = React3.useCallback(
1988
2140
  (clientX, clientY) => {
1989
2141
  if (!canvas) return false;
1990
2142
  const rect = canvas.getBoundingClientRect();
@@ -1992,7 +2144,7 @@ function useRaycaster(options) {
1992
2144
  },
1993
2145
  [canvas]
1994
2146
  );
1995
- const getIntersection = React6.useCallback(
2147
+ const getIntersection = React3.useCallback(
1996
2148
  (clientX, clientY, objects) => {
1997
2149
  if (!camera || !canvas) return null;
1998
2150
  const ndc = clientToNDC(clientX, clientY);
@@ -2015,7 +2167,7 @@ function useRaycaster(options) {
2015
2167
  },
2016
2168
  [camera, canvas, clientToNDC]
2017
2169
  );
2018
- const getAllIntersections = React6.useCallback(
2170
+ const getAllIntersections = React3.useCallback(
2019
2171
  (clientX, clientY, objects) => {
2020
2172
  if (!camera || !canvas) return [];
2021
2173
  const ndc = clientToNDC(clientX, clientY);
@@ -2034,7 +2186,7 @@ function useRaycaster(options) {
2034
2186
  },
2035
2187
  [camera, canvas, clientToNDC]
2036
2188
  );
2037
- const getGridCoordinates = React6.useCallback(
2189
+ const getGridCoordinates = React3.useCallback(
2038
2190
  (clientX, clientY) => {
2039
2191
  if (!camera || !canvas) return null;
2040
2192
  const ndc = clientToNDC(clientX, clientY);
@@ -2052,7 +2204,7 @@ function useRaycaster(options) {
2052
2204
  },
2053
2205
  [camera, canvas, cellSize, offsetX, offsetZ, clientToNDC]
2054
2206
  );
2055
- const getTileAtPosition = React6.useCallback(
2207
+ const getTileAtPosition = React3.useCallback(
2056
2208
  (clientX, clientY, scene) => {
2057
2209
  if (!camera || !canvas) return null;
2058
2210
  const tileMeshes = [];
@@ -2387,24 +2539,24 @@ var SpatialHashGrid = class {
2387
2539
  };
2388
2540
  function useTapReveal(options = {}) {
2389
2541
  const { onReveal, onDismiss, refs, enabled = true } = options;
2390
- const [revealed, setRevealed] = React6.useState(false);
2391
- const onRevealRef = React6.useRef(onReveal);
2392
- const onDismissRef = React6.useRef(onDismiss);
2542
+ const [revealed, setRevealed] = React3.useState(false);
2543
+ const onRevealRef = React3.useRef(onReveal);
2544
+ const onDismissRef = React3.useRef(onDismiss);
2393
2545
  onRevealRef.current = onReveal;
2394
2546
  onDismissRef.current = onDismiss;
2395
- const reveal = React6.useCallback(() => {
2547
+ const reveal = React3.useCallback(() => {
2396
2548
  setRevealed((wasRevealed) => {
2397
2549
  if (!wasRevealed) onRevealRef.current?.();
2398
2550
  return true;
2399
2551
  });
2400
2552
  }, []);
2401
- const dismiss = React6.useCallback(() => {
2553
+ const dismiss = React3.useCallback(() => {
2402
2554
  setRevealed((wasRevealed) => {
2403
2555
  if (wasRevealed) onDismissRef.current?.();
2404
2556
  return false;
2405
2557
  });
2406
2558
  }, []);
2407
- const onPointerDown = React6.useCallback(
2559
+ const onPointerDown = React3.useCallback(
2408
2560
  (e) => {
2409
2561
  if (!enabled || e.pointerType === "mouse") return;
2410
2562
  if (revealed) dismiss();
@@ -2412,7 +2564,7 @@ function useTapReveal(options = {}) {
2412
2564
  },
2413
2565
  [enabled, revealed, reveal, dismiss]
2414
2566
  );
2415
- React6.useEffect(() => {
2567
+ React3.useEffect(() => {
2416
2568
  if (!revealed || typeof document === "undefined") return;
2417
2569
  const onDocDown = (ev) => {
2418
2570
  const target = ev.target;
@@ -2532,7 +2684,7 @@ var positionStyles = {
2532
2684
  fixed: "fixed",
2533
2685
  sticky: "sticky"
2534
2686
  };
2535
- var Box = React6__default.default.forwardRef(
2687
+ var Box = React3__default.default.forwardRef(
2536
2688
  ({
2537
2689
  padding,
2538
2690
  paddingX,
@@ -2564,20 +2716,20 @@ var Box = React6__default.default.forwardRef(
2564
2716
  ...rest
2565
2717
  }, ref) => {
2566
2718
  const eventBus = useEventBus();
2567
- const handleClick = React6.useCallback((e) => {
2719
+ const handleClick = React3.useCallback((e) => {
2568
2720
  if (action) {
2569
2721
  e.stopPropagation();
2570
2722
  eventBus.emit(`UI:${action}`, actionPayload ?? {});
2571
2723
  }
2572
2724
  onClick?.(e);
2573
2725
  }, [action, actionPayload, eventBus, onClick]);
2574
- const handleMouseEnter = React6.useCallback((e) => {
2726
+ const handleMouseEnter = React3.useCallback((e) => {
2575
2727
  if (hoverEvent) {
2576
2728
  eventBus.emit(`UI:${hoverEvent}`, { hovered: true });
2577
2729
  }
2578
2730
  onMouseEnter?.(e);
2579
2731
  }, [hoverEvent, eventBus, onMouseEnter]);
2580
- const handleMouseLeave = React6.useCallback((e) => {
2732
+ const handleMouseLeave = React3.useCallback((e) => {
2581
2733
  if (hoverEvent) {
2582
2734
  eventBus.emit(`UI:${hoverEvent}`, { hovered: false });
2583
2735
  }
@@ -2585,19 +2737,19 @@ var Box = React6__default.default.forwardRef(
2585
2737
  }, [hoverEvent, eventBus, onMouseLeave]);
2586
2738
  const { triggerProps } = useTapReveal({
2587
2739
  enabled: tapReveal && !!hoverEvent,
2588
- onReveal: React6.useCallback(() => {
2740
+ onReveal: React3.useCallback(() => {
2589
2741
  if (hoverEvent) eventBus.emit(`UI:${hoverEvent}`, { hovered: true });
2590
2742
  }, [hoverEvent, eventBus]),
2591
- onDismiss: React6.useCallback(() => {
2743
+ onDismiss: React3.useCallback(() => {
2592
2744
  if (hoverEvent) eventBus.emit(`UI:${hoverEvent}`, { hovered: false });
2593
2745
  }, [hoverEvent, eventBus])
2594
2746
  });
2595
- const handlePointerDown = React6.useCallback((e) => {
2747
+ const handlePointerDown = React3.useCallback((e) => {
2596
2748
  if (hoverEvent && tapReveal) triggerProps.onPointerDown(e);
2597
2749
  onPointerDown?.(e);
2598
2750
  }, [hoverEvent, tapReveal, triggerProps, onPointerDown]);
2599
2751
  const isClickable = action || onClick;
2600
- return React6__default.default.createElement(
2752
+ return React3__default.default.createElement(
2601
2753
  Component2,
2602
2754
  {
2603
2755
  ref,
@@ -2718,7 +2870,7 @@ var Typography = ({
2718
2870
  }) => {
2719
2871
  const variant = variantProp ?? (level ? `h${level}` : "body1");
2720
2872
  const Component2 = as || defaultElements[variant];
2721
- return React6__default.default.createElement(
2873
+ return React3__default.default.createElement(
2722
2874
  Component2,
2723
2875
  {
2724
2876
  id,
@@ -2791,7 +2943,7 @@ var Stack = ({
2791
2943
  };
2792
2944
  const isHorizontal = direction === "horizontal";
2793
2945
  const directionClass = responsive && isHorizontal ? reverse ? "flex-col-reverse md:flex-row-reverse" : "flex-col md:flex-row" : isHorizontal ? reverse ? "flex-row-reverse" : "flex-row" : reverse ? "flex-col-reverse" : "flex-col";
2794
- return React6__default.default.createElement(
2946
+ return React3__default.default.createElement(
2795
2947
  Component2,
2796
2948
  {
2797
2949
  className: cn(
@@ -3538,10 +3690,10 @@ var Avl3DOrbitalNode = ({
3538
3690
  position,
3539
3691
  onClick
3540
3692
  }) => {
3541
- const groupRef = React6.useRef(null);
3542
- const [hovered, setHovered] = React6.useState(false);
3543
- const scale = React6.useMemo(() => 0.6 + Math.min(traitCount, 8) * 0.3, [traitCount]);
3544
- const currentScale = React6.useRef(scale);
3693
+ const groupRef = React3.useRef(null);
3694
+ const [hovered, setHovered] = React3.useState(false);
3695
+ const scale = React3.useMemo(() => 0.6 + Math.min(traitCount, 8) * 0.3, [traitCount]);
3696
+ const currentScale = React3.useRef(scale);
3545
3697
  const targetScale = hovered ? scale * 1.06 : scale;
3546
3698
  fiber.useFrame((_, delta) => {
3547
3699
  if (!groupRef.current) return;
@@ -3683,7 +3835,7 @@ var Avl3DCrossWire = ({
3683
3835
  eventName,
3684
3836
  color = AVL_3D_COLORS.crossWire
3685
3837
  }) => {
3686
- const { tubeArgs, midpoint } = React6.useMemo(() => {
3838
+ const { tubeArgs, midpoint } = React3.useMemo(() => {
3687
3839
  const curve = arcCurve3D(from, to, 2);
3688
3840
  const mid = curve.getPoint(0.5);
3689
3841
  return {
@@ -3722,9 +3874,9 @@ var Avl3DApplicationScene = ({
3722
3874
  data,
3723
3875
  onOrbitalClick
3724
3876
  }) => {
3725
- const groupRef = React6.useRef(null);
3726
- const [, setHoverTrigger] = React6.useState(0);
3727
- const orbitalPositions = React6.useMemo(() => {
3877
+ const groupRef = React3.useRef(null);
3878
+ const [, setHoverTrigger] = React3.useState(0);
3879
+ const orbitalPositions = React3.useMemo(() => {
3728
3880
  const baseRadius = Math.max(4, data.orbitals.length * 1.5);
3729
3881
  if (data.crossLinks.length === 0) {
3730
3882
  return goldenSpiralPositions(data.orbitals.length, baseRadius);
@@ -3737,7 +3889,7 @@ var Avl3DApplicationScene = ({
3737
3889
  })).filter((e) => e.from >= 0 && e.to >= 0);
3738
3890
  return forceDirectedPositions(data.orbitals.length, edges, baseRadius);
3739
3891
  }, [data.orbitals, data.crossLinks]);
3740
- const positionByName = React6.useMemo(() => {
3892
+ const positionByName = React3.useMemo(() => {
3741
3893
  const map = /* @__PURE__ */ new Map();
3742
3894
  data.orbitals.forEach((orb, i) => {
3743
3895
  const pos = orbitalPositions[i];
@@ -3820,7 +3972,7 @@ var Avl3DEntityCore = ({
3820
3972
  position,
3821
3973
  fields
3822
3974
  }) => {
3823
- const coreRef = React6.useRef(null);
3975
+ const coreRef = React3.useRef(null);
3824
3976
  fiber.useFrame((_, delta) => {
3825
3977
  if (!coreRef.current) return;
3826
3978
  coreRef.current.rotation.y += delta * 0.3;
@@ -3973,8 +4125,8 @@ function TraitOrbit({
3973
4125
  onClick,
3974
4126
  onHover
3975
4127
  }) {
3976
- const groupRef = React6.useRef(null);
3977
- const angleRef = React6.useRef(Math.random() * Math.PI * 2);
4128
+ const groupRef = React3.useRef(null);
4129
+ const angleRef = React3.useRef(Math.random() * Math.PI * 2);
3978
4130
  fiber.useFrame((_, delta) => {
3979
4131
  angleRef.current += delta * speed;
3980
4132
  if (!groupRef.current) return;
@@ -4105,9 +4257,9 @@ var Avl3DOrbitalScene = ({
4105
4257
  highlightedTrait,
4106
4258
  onTraitHighlight
4107
4259
  }) => {
4108
- const [localHighlight, setLocalHighlight] = React6.useState(null);
4260
+ const [localHighlight, setLocalHighlight] = React3.useState(null);
4109
4261
  const activeHighlight = highlightedTrait ?? localHighlight;
4110
- const traitOrbits = React6.useMemo(() => {
4262
+ const traitOrbits = React3.useMemo(() => {
4111
4263
  return data.traits.map((trait, i) => ({
4112
4264
  name: trait.name,
4113
4265
  radius: 3 + i * 1.2,
@@ -4118,7 +4270,7 @@ var Avl3DOrbitalScene = ({
4118
4270
  transitionCount: trait.transitionCount
4119
4271
  }));
4120
4272
  }, [data.traits]);
4121
- const pagePositions = React6.useMemo(() => {
4273
+ const pagePositions = React3.useMemo(() => {
4122
4274
  const outerRadius = 3 + data.traits.length * 1.2 + 1.5;
4123
4275
  return data.pages.map((_, i) => {
4124
4276
  const angle = 2 * Math.PI * i / data.pages.length - Math.PI / 2;
@@ -4182,12 +4334,12 @@ var Avl3DStateNode = ({
4182
4334
  outgoingCount = 0,
4183
4335
  onClick
4184
4336
  }) => {
4185
- const [hovered, setHovered] = React6.useState(false);
4337
+ const [hovered, setHovered] = React3.useState(false);
4186
4338
  const baseColor = active ? AVL_3D_COLORS.stateActive : AVL_3D_COLORS.stateIdle;
4187
4339
  const emissiveColor = active ? AVL_3D_COLORS.stateActive : AVL_3D_COLORS.stateEdge;
4188
4340
  const emissiveIntensity = active ? 2 : hovered ? 0.8 : 0.3;
4189
4341
  const targetScale = hovered ? 1.08 : 1;
4190
- const currentScale = React6.useRef(1);
4342
+ const currentScale = React3.useRef(1);
4191
4343
  fiber.useFrame((_, delta) => {
4192
4344
  currentScale.current = THREE9.MathUtils.damp(currentScale.current, targetScale, 6, delta);
4193
4345
  });
@@ -4361,8 +4513,8 @@ var Avl3DTransitionArc = ({
4361
4513
  onClick,
4362
4514
  color = AVL_3D_COLORS.transitionArc
4363
4515
  }) => {
4364
- const [hovered, setHovered] = React6.useState(false);
4365
- const { tubeArgs, labelPos, guardPos, arrowPos, arrowQuat, effectPositions } = React6.useMemo(() => {
4516
+ const [hovered, setHovered] = React3.useState(false);
4517
+ const { tubeArgs, labelPos, guardPos, arrowPos, arrowQuat, effectPositions } = React3.useMemo(() => {
4366
4518
  const offset = 1.5 + index * 0.8;
4367
4519
  const curve = isSelf ? selfLoopCurve3D(from, 1.2) : arcCurve3D(from, to, offset);
4368
4520
  const mid = curve.getPoint(0.5);
@@ -4495,7 +4647,7 @@ var Avl3DTraitScene = ({
4495
4647
  data,
4496
4648
  onTransitionClick
4497
4649
  }) => {
4498
- const statePositions = React6.useMemo(() => {
4650
+ const statePositions = React3.useMemo(() => {
4499
4651
  const radius = Math.max(3, data.states.length * 0.8);
4500
4652
  const positions = fibonacciSpherePositions(data.states.length, radius);
4501
4653
  const posMap = /* @__PURE__ */ new Map();
@@ -4505,7 +4657,7 @@ var Avl3DTraitScene = ({
4505
4657
  });
4506
4658
  return posMap;
4507
4659
  }, [data.states]);
4508
- const transitionIndices = React6.useMemo(() => {
4660
+ const transitionIndices = React3.useMemo(() => {
4509
4661
  const pairCount = /* @__PURE__ */ new Map();
4510
4662
  return data.transitions.map((t) => {
4511
4663
  const key = `${t.from}->${t.to}`;
@@ -4514,7 +4666,7 @@ var Avl3DTraitScene = ({
4514
4666
  return idx;
4515
4667
  });
4516
4668
  }, [data.transitions]);
4517
- const transitionCounts = React6.useMemo(() => {
4669
+ const transitionCounts = React3.useMemo(() => {
4518
4670
  const incoming = /* @__PURE__ */ new Map();
4519
4671
  const outgoing = /* @__PURE__ */ new Map();
4520
4672
  for (const t of data.transitions) {
@@ -4624,11 +4776,11 @@ var Avl3DExprTree = ({
4624
4776
  expression,
4625
4777
  position
4626
4778
  }) => {
4627
- const layoutResults = React6.useMemo(() => {
4779
+ const layoutResults = React3.useMemo(() => {
4628
4780
  const origin = { x: position[0], y: position[1], z: position[2] };
4629
4781
  return treeLayout3D(expression, origin, 2.5, 1.5);
4630
4782
  }, [expression, position]);
4631
- const edges = React6.useMemo(() => {
4783
+ const edges = React3.useMemo(() => {
4632
4784
  const result = [];
4633
4785
  function collectEdges(node, parentPos) {
4634
4786
  const entry = layoutResults.find((r) => r.node === node);
@@ -4908,7 +5060,7 @@ var Avl3DEffects = ({
4908
5060
  const dof = DOF_CONFIG[level];
4909
5061
  const sparkles = SPARKLE_CONFIGS[level];
4910
5062
  const stars = STARS_CONFIG[level];
4911
- const starsKey = React6.useMemo(() => `stars-${level}`, [level]);
5063
+ const starsKey = React3.useMemo(() => `stars-${level}`, [level]);
4912
5064
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
4913
5065
  /* @__PURE__ */ jsxRuntime.jsx(
4914
5066
  drei.Stars,
@@ -4972,16 +5124,16 @@ var DEFAULT_CONFIG2 = {
4972
5124
  modelOverrides: {},
4973
5125
  effectsEnabled: true
4974
5126
  };
4975
- var Avl3DContext = React6.createContext(DEFAULT_CONFIG2);
5127
+ var Avl3DContext = React3.createContext(DEFAULT_CONFIG2);
4976
5128
  function useAvl3DConfig() {
4977
- return React6.useContext(Avl3DContext);
5129
+ return React3.useContext(Avl3DContext);
4978
5130
  }
4979
5131
  function CameraController({ targetPosition, targetLookAt, animated }) {
4980
5132
  const { camera } = fiber.useThree();
4981
- const targetPosVec = React6.useRef(new THREE9__namespace.Vector3(...targetPosition));
4982
- const targetLookVec = React6.useRef(new THREE9__namespace.Vector3(...targetLookAt));
4983
- const isAnimating = React6.useRef(false);
4984
- React6.useEffect(() => {
5133
+ const targetPosVec = React3.useRef(new THREE9__namespace.Vector3(...targetPosition));
5134
+ const targetLookVec = React3.useRef(new THREE9__namespace.Vector3(...targetLookAt));
5135
+ const isAnimating = React3.useRef(false);
5136
+ React3.useEffect(() => {
4985
5137
  const newTarget = new THREE9__namespace.Vector3(...targetPosition);
4986
5138
  const newLookAt = new THREE9__namespace.Vector3(...targetLookAt);
4987
5139
  if (!newTarget.equals(targetPosVec.current) || !newLookAt.equals(targetLookVec.current)) {
@@ -5010,8 +5162,8 @@ function CameraController({ targetPosition, targetLookAt, animated }) {
5010
5162
  return null;
5011
5163
  }
5012
5164
  function SceneFade({ animating, children }) {
5013
- const groupRef = React6.useRef(null);
5014
- const opacityRef = React6.useRef(1);
5165
+ const groupRef = React3.useRef(null);
5166
+ const opacityRef = React3.useRef(1);
5015
5167
  fiber.useFrame((_, delta) => {
5016
5168
  if (!groupRef.current) return;
5017
5169
  const target = animating ? 0 : 1;
@@ -5035,7 +5187,7 @@ var Avl3DViewer = ({
5035
5187
  modelOverrides = {}
5036
5188
  }) => {
5037
5189
  const { t } = hooks.useTranslate();
5038
- const schema = React6.useMemo(() => {
5190
+ const schema = React3.useMemo(() => {
5039
5191
  if (typeof schemaProp === "string") {
5040
5192
  try {
5041
5193
  return JSON.parse(schemaProp);
@@ -5045,12 +5197,12 @@ var Avl3DViewer = ({
5045
5197
  }
5046
5198
  return schemaProp;
5047
5199
  }, [schemaProp]);
5048
- const configValue = React6.useMemo(() => ({
5200
+ const configValue = React3.useMemo(() => ({
5049
5201
  modelOverrides,
5050
5202
  effectsEnabled: effects
5051
5203
  }), [modelOverrides, effects]);
5052
- const [state, dispatch] = React6.useReducer(zoomReducer, initialZoomState);
5053
- React6.useEffect(() => {
5204
+ const [state, dispatch] = React3.useReducer(zoomReducer, initialZoomState);
5205
+ React3.useEffect(() => {
5054
5206
  if (initialOrbital) {
5055
5207
  dispatch({ type: "ZOOM_INTO_ORBITAL", orbital: initialOrbital, targetPosition: { x: 0, y: 0 } });
5056
5208
  setTimeout(() => dispatch({ type: "ANIMATION_COMPLETE" }), 0);
@@ -5062,35 +5214,35 @@ var Avl3DViewer = ({
5062
5214
  }
5063
5215
  }
5064
5216
  }, [initialOrbital, initialTrait]);
5065
- React6.useEffect(() => {
5217
+ React3.useEffect(() => {
5066
5218
  onZoomChange?.(state.level, {
5067
5219
  orbital: state.selectedOrbital ?? void 0,
5068
5220
  trait: state.selectedTrait ?? void 0
5069
5221
  });
5070
5222
  }, [state.level, state.selectedOrbital, state.selectedTrait, onZoomChange]);
5071
- React6.useEffect(() => {
5223
+ React3.useEffect(() => {
5072
5224
  if (!state.animating) return;
5073
5225
  const timer = setTimeout(() => {
5074
5226
  dispatch({ type: "ANIMATION_COMPLETE" });
5075
5227
  }, 800);
5076
5228
  return () => clearTimeout(timer);
5077
5229
  }, [state.animating]);
5078
- const handleKeyDown = React6.useCallback((e) => {
5230
+ const handleKeyDown = React3.useCallback((e) => {
5079
5231
  if (e.key === "Escape") {
5080
5232
  dispatch({ type: "ZOOM_OUT" });
5081
5233
  }
5082
5234
  }, []);
5083
- const handleOrbitalClick = React6.useCallback((name) => {
5235
+ const handleOrbitalClick = React3.useCallback((name) => {
5084
5236
  dispatch({ type: "ZOOM_INTO_ORBITAL", orbital: name, targetPosition: { x: 0, y: 0 } });
5085
5237
  }, []);
5086
- const handleTraitClick = React6.useCallback((name) => {
5238
+ const handleTraitClick = React3.useCallback((name) => {
5087
5239
  dispatch({ type: "ZOOM_INTO_TRAIT", trait: name, targetPosition: { x: 0, y: 0 } });
5088
5240
  }, []);
5089
- const [highlightedTrait, setHighlightedTrait] = React6__default.default.useState(null);
5090
- const handleTransitionClick = React6.useCallback((index) => {
5241
+ const [highlightedTrait, setHighlightedTrait] = React3__default.default.useState(null);
5242
+ const handleTransitionClick = React3.useCallback((index) => {
5091
5243
  dispatch({ type: "ZOOM_INTO_TRANSITION", transitionIndex: index, targetPosition: { x: 0, y: 0 } });
5092
5244
  }, []);
5093
- const handleBreadcrumbClick = React6.useCallback((targetLevel) => {
5245
+ const handleBreadcrumbClick = React3.useCallback((targetLevel) => {
5094
5246
  const levelOrder = ["application", "orbital", "trait", "transition"];
5095
5247
  const currentIdx = levelOrder.indexOf(state.level);
5096
5248
  const targetIdx = levelOrder.indexOf(targetLevel);
@@ -5099,7 +5251,7 @@ var Avl3DViewer = ({
5099
5251
  }
5100
5252
  }, [state.level]);
5101
5253
  const cameraConfig = CAMERA_POSITIONS[state.level];
5102
- const sceneContent = React6.useMemo(() => {
5254
+ const sceneContent = React3.useMemo(() => {
5103
5255
  switch (state.level) {
5104
5256
  case "application": {
5105
5257
  const data = parseApplicationLevel(schema);
@@ -5173,7 +5325,7 @@ var Avl3DViewer = ({
5173
5325
  gap: "xs",
5174
5326
  align: "center",
5175
5327
  className: "absolute top-2 left-2 z-10 bg-surface/80 backdrop-blur rounded-md px-3 py-1.5",
5176
- children: breadcrumbs.map((crumb, i) => /* @__PURE__ */ jsxRuntime.jsxs(React6__default.default.Fragment, { children: [
5328
+ children: breadcrumbs.map((crumb, i) => /* @__PURE__ */ jsxRuntime.jsxs(React3__default.default.Fragment, { children: [
5177
5329
  i > 0 && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", color: "muted", className: "mx-1", children: "/" }),
5178
5330
  i < breadcrumbs.length - 1 ? /* @__PURE__ */ jsxRuntime.jsx(
5179
5331
  Box,