@relu-interactives/spatial-ecs 0.8.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/dist/{System-8D-XBDKZ.d.cts → System-BldSHxZV.d.cts} +214 -81
  2. package/dist/{System-DPMAW30g.d.ts → System-CADhPyQs.d.ts} +214 -81
  3. package/dist/{XRHandPose-G0Dl-F0w.d.ts → XRHandPose-BNSwd1y9.d.ts} +145 -2
  4. package/dist/{XRHandPose-VQMT1WXj.d.cts → XRHandPose-D3Ab1Dfs.d.cts} +145 -2
  5. package/dist/{chunk-G4XJ53LS.js → chunk-JEPUCLZX.js} +3 -3
  6. package/dist/{chunk-G4XJ53LS.js.map → chunk-JEPUCLZX.js.map} +1 -1
  7. package/dist/{chunk-NNDUOQ7O.js → chunk-JNYPJB55.js} +250 -3
  8. package/dist/chunk-JNYPJB55.js.map +1 -0
  9. package/dist/{chunk-SGF73YTF.js → chunk-NXASMBDS.js} +4 -4
  10. package/dist/{chunk-SGF73YTF.js.map → chunk-NXASMBDS.js.map} +1 -1
  11. package/dist/{chunk-JIJQFW6P.js → chunk-TY3XCHOR.js} +1390 -485
  12. package/dist/chunk-TY3XCHOR.js.map +1 -0
  13. package/dist/{chunk-U727NTWQ.js → chunk-WWP46AEF.js} +26 -4
  14. package/dist/chunk-WWP46AEF.js.map +1 -0
  15. package/dist/components/index.cjs +151 -0
  16. package/dist/components/index.cjs.map +1 -1
  17. package/dist/components/index.d.cts +3 -3
  18. package/dist/components/index.d.ts +3 -3
  19. package/dist/components/index.js +1 -1
  20. package/dist/docs/guide/bone-attachments-and-ik.md +1 -1
  21. package/dist/docs/guide/multiplayer.md +65 -0
  22. package/dist/docs/guide/spatial-ui.md +83 -7
  23. package/dist/factories/index.cjs +151 -0
  24. package/dist/factories/index.cjs.map +1 -1
  25. package/dist/factories/index.d.cts +2 -2
  26. package/dist/factories/index.d.ts +2 -2
  27. package/dist/factories/index.js +3 -3
  28. package/dist/{index-C61n65PE.d.ts → index-6pZO_gV7.d.ts} +27 -2
  29. package/dist/{index-D9DjrUn3.d.cts → index-BXjwEdNq.d.cts} +6 -3
  30. package/dist/{index-C70N4BYG.d.cts → index-BdghvXKE.d.cts} +27 -2
  31. package/dist/{index-DppafVu4.d.ts → index-CVG-hREM.d.ts} +6 -3
  32. package/dist/index.cjs +1800 -626
  33. package/dist/index.cjs.map +1 -1
  34. package/dist/index.d.cts +8 -30
  35. package/dist/index.d.ts +8 -30
  36. package/dist/index.js +10 -10
  37. package/dist/network/index.cjs +858 -8
  38. package/dist/network/index.cjs.map +1 -1
  39. package/dist/network/index.d.cts +126 -5
  40. package/dist/network/index.d.ts +126 -5
  41. package/dist/network/index.js +683 -8
  42. package/dist/network/index.js.map +1 -1
  43. package/dist/systems/index.cjs +1702 -528
  44. package/dist/systems/index.cjs.map +1 -1
  45. package/dist/systems/index.d.cts +2 -2
  46. package/dist/systems/index.d.ts +2 -2
  47. package/dist/systems/index.js +4 -4
  48. package/dist/types.d.cts +1 -1
  49. package/dist/types.d.ts +1 -1
  50. package/dist/webgpu/index.d.ts +2 -2
  51. package/dist/worldSnapshotUtils.d.cts +1 -1
  52. package/dist/worldSnapshotUtils.d.ts +1 -1
  53. package/package.json +1 -1
  54. package/dist/chunk-JIJQFW6P.js.map +0 -1
  55. package/dist/chunk-NNDUOQ7O.js.map +0 -1
  56. package/dist/chunk-U727NTWQ.js.map +0 -1
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var THREE30 = require('three');
3
+ var THREE37 = require('three');
4
4
  var DRACOLoader_js = require('three/examples/jsm/loaders/DRACOLoader.js');
5
5
  var GLTFLoader_js = require('three/examples/jsm/loaders/GLTFLoader.js');
6
6
  var meshopt_decoder_module_js = require('three/examples/jsm/libs/meshopt_decoder.module.js');
@@ -43,7 +43,7 @@ function _interopNamespace(e) {
43
43
  return Object.freeze(n);
44
44
  }
45
45
 
46
- var THREE30__namespace = /*#__PURE__*/_interopNamespace(THREE30);
46
+ var THREE37__namespace = /*#__PURE__*/_interopNamespace(THREE37);
47
47
 
48
48
  var __defProp = Object.defineProperty;
49
49
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
@@ -320,7 +320,7 @@ function findModelBone(root, ref) {
320
320
  for (const b of bones) if (!byName.has(b.name)) byName.set(b.name, b);
321
321
  const name = ref.bone ?? "";
322
322
  if (name) {
323
- const hit = byName.get(name) ?? byName.get(THREE30__namespace.PropertyBinding.sanitizeNodeName(name));
323
+ const hit = byName.get(name) ?? byName.get(THREE37__namespace.PropertyBinding.sanitizeNodeName(name));
324
324
  if (hit) return hit;
325
325
  }
326
326
  if (ref.humanoid) {
@@ -373,7 +373,7 @@ var SHARED_MODEL_RESOURCE_FLAG = "spatialEcsSharedModelResource";
373
373
  var modelCache = /* @__PURE__ */ new Map();
374
374
  function markSharedResources(root) {
375
375
  root.traverse((child) => {
376
- if (!(child instanceof THREE30__namespace.Mesh)) {
376
+ if (!(child instanceof THREE37__namespace.Mesh)) {
377
377
  return;
378
378
  }
379
379
  if (child.geometry) {
@@ -676,14 +676,14 @@ var _AnimationComponent = class _AnimationComponent extends Component {
676
676
  return;
677
677
  }
678
678
  this.importedClips = clips.map((entry) => {
679
- if (entry instanceof THREE30__namespace.AnimationClip) {
679
+ if (entry instanceof THREE37__namespace.AnimationClip) {
680
680
  return { clip: entry, sourceUrl: null };
681
681
  }
682
682
  if (!entry || typeof entry !== "object") {
683
683
  return null;
684
684
  }
685
685
  const candidate = entry;
686
- if (!(candidate.clip instanceof THREE30__namespace.AnimationClip)) {
686
+ if (!(candidate.clip instanceof THREE37__namespace.AnimationClip)) {
687
687
  return null;
688
688
  }
689
689
  return {
@@ -890,7 +890,7 @@ var DEFAULT_ORTHO_TOP = 5;
890
890
  var DEFAULT_ORTHO_BOTTOM = -5;
891
891
  var DEFAULT_ZOOM = 1;
892
892
  function inferProjection(camera) {
893
- if (camera instanceof THREE30__namespace.OrthographicCamera) {
893
+ if (camera instanceof THREE37__namespace.OrthographicCamera) {
894
894
  return "orthographic";
895
895
  }
896
896
  return "perspective";
@@ -911,7 +911,7 @@ function readCameraFar(camera) {
911
911
  }
912
912
  __name(readCameraFar, "readCameraFar");
913
913
  function inferFov(camera) {
914
- if (camera instanceof THREE30__namespace.PerspectiveCamera && Number.isFinite(camera.fov)) {
914
+ if (camera instanceof THREE37__namespace.PerspectiveCamera && Number.isFinite(camera.fov)) {
915
915
  return camera.fov;
916
916
  }
917
917
  return DEFAULT_FOV;
@@ -934,28 +934,28 @@ function inferFar(camera) {
934
934
  }
935
935
  __name(inferFar, "inferFar");
936
936
  function inferOrthoLeft(camera) {
937
- if (camera instanceof THREE30__namespace.OrthographicCamera && Number.isFinite(camera.left)) {
937
+ if (camera instanceof THREE37__namespace.OrthographicCamera && Number.isFinite(camera.left)) {
938
938
  return camera.left;
939
939
  }
940
940
  return DEFAULT_ORTHO_LEFT;
941
941
  }
942
942
  __name(inferOrthoLeft, "inferOrthoLeft");
943
943
  function inferOrthoRight(camera) {
944
- if (camera instanceof THREE30__namespace.OrthographicCamera && Number.isFinite(camera.right)) {
944
+ if (camera instanceof THREE37__namespace.OrthographicCamera && Number.isFinite(camera.right)) {
945
945
  return camera.right;
946
946
  }
947
947
  return DEFAULT_ORTHO_RIGHT;
948
948
  }
949
949
  __name(inferOrthoRight, "inferOrthoRight");
950
950
  function inferOrthoTop(camera) {
951
- if (camera instanceof THREE30__namespace.OrthographicCamera && Number.isFinite(camera.top)) {
951
+ if (camera instanceof THREE37__namespace.OrthographicCamera && Number.isFinite(camera.top)) {
952
952
  return camera.top;
953
953
  }
954
954
  return DEFAULT_ORTHO_TOP;
955
955
  }
956
956
  __name(inferOrthoTop, "inferOrthoTop");
957
957
  function inferOrthoBottom(camera) {
958
- if (camera instanceof THREE30__namespace.OrthographicCamera && Number.isFinite(camera.bottom)) {
958
+ if (camera instanceof THREE37__namespace.OrthographicCamera && Number.isFinite(camera.bottom)) {
959
959
  return camera.bottom;
960
960
  }
961
961
  return DEFAULT_ORTHO_BOTTOM;
@@ -971,7 +971,7 @@ __name(inferZoom, "inferZoom");
971
971
  var _CameraComponent = class _CameraComponent extends Component {
972
972
  constructor(payload = {}) {
973
973
  super();
974
- this.camera = payload.camera ?? new THREE30__namespace.PerspectiveCamera();
974
+ this.camera = payload.camera ?? new THREE37__namespace.PerspectiveCamera();
975
975
  this.type = payload.type ?? "3d";
976
976
  this.isDefault = typeof payload.isDefault === "boolean" ? payload.isDefault : false;
977
977
  this.projection = payload.projection ?? inferProjection(payload.camera);
@@ -1475,15 +1475,15 @@ function createValueState(light) {
1475
1475
  castShadow: light.castShadow ?? false,
1476
1476
  frustumCulling: light.frustumCulled
1477
1477
  };
1478
- if (light instanceof THREE30__namespace.PointLight) {
1478
+ if (light instanceof THREE37__namespace.PointLight) {
1479
1479
  base.distance = light.distance;
1480
1480
  base.decay = light.decay;
1481
- } else if (light instanceof THREE30__namespace.SpotLight) {
1481
+ } else if (light instanceof THREE37__namespace.SpotLight) {
1482
1482
  base.distance = light.distance;
1483
1483
  base.decay = light.decay;
1484
1484
  base.angle = light.angle;
1485
1485
  base.penumbra = light.penumbra;
1486
- } else if (light instanceof THREE30__namespace.RectAreaLight) {
1486
+ } else if (light instanceof THREE37__namespace.RectAreaLight) {
1487
1487
  base.width = light.width;
1488
1488
  base.height = light.height;
1489
1489
  }
@@ -1518,13 +1518,13 @@ function toHexColor2(value, fallback) {
1518
1518
  }
1519
1519
  __name(toHexColor2, "toHexColor");
1520
1520
  function getMaterialType(material) {
1521
- if (material instanceof THREE30__namespace.MeshPhysicalMaterial) {
1521
+ if (material instanceof THREE37__namespace.MeshPhysicalMaterial) {
1522
1522
  return "physical";
1523
1523
  }
1524
- if (material instanceof THREE30__namespace.MeshPhongMaterial) {
1524
+ if (material instanceof THREE37__namespace.MeshPhongMaterial) {
1525
1525
  return "phong";
1526
1526
  }
1527
- if (material instanceof THREE30__namespace.MeshBasicMaterial) {
1527
+ if (material instanceof THREE37__namespace.MeshBasicMaterial) {
1528
1528
  return "basic";
1529
1529
  }
1530
1530
  return "standard";
@@ -1559,8 +1559,8 @@ function createMaterialValue(material) {
1559
1559
  opacity: typeof materialRecord.opacity === "number" ? materialRecord.opacity : 1,
1560
1560
  emissive: toHexColor2(materialRecord.emissive, "#000000"),
1561
1561
  emissiveIntensity: typeof materialRecord.emissiveIntensity === "number" ? materialRecord.emissiveIntensity : 0,
1562
- side: typeof materialRecord.side === "number" ? materialRecord.side : THREE30__namespace.FrontSide,
1563
- blending: typeof materialRecord.blending === "number" ? materialRecord.blending : THREE30__namespace.NormalBlending,
1562
+ side: typeof materialRecord.side === "number" ? materialRecord.side : THREE37__namespace.FrontSide,
1563
+ blending: typeof materialRecord.blending === "number" ? materialRecord.blending : THREE37__namespace.NormalBlending,
1564
1564
  depthTest: materialRecord.depthTest !== false,
1565
1565
  depthWrite: materialRecord.depthWrite !== false,
1566
1566
  wireframe: Boolean(materialRecord.wireframe),
@@ -1592,7 +1592,7 @@ function createMaterialValue(material) {
1592
1592
  materialRecord.lightMap
1593
1593
  ),
1594
1594
  envMap: textureImageSrc(materialRecord.envMap),
1595
- textureFiltering: map?.magFilter === THREE30__namespace.NearestFilter ? "sharp" : "smooth",
1595
+ textureFiltering: map?.magFilter === THREE37__namespace.NearestFilter ? "sharp" : "smooth",
1596
1596
  useMipmaps: map?.generateMipmaps !== false
1597
1597
  };
1598
1598
  }
@@ -1607,8 +1607,8 @@ function defaultMaterialValue() {
1607
1607
  opacity: 1,
1608
1608
  emissive: "#000000",
1609
1609
  emissiveIntensity: 0,
1610
- side: THREE30__namespace.FrontSide,
1611
- blending: THREE30__namespace.NormalBlending,
1610
+ side: THREE37__namespace.FrontSide,
1611
+ blending: THREE37__namespace.NormalBlending,
1612
1612
  depthTest: true,
1613
1613
  depthWrite: true,
1614
1614
  wireframe: false,
@@ -2065,14 +2065,14 @@ var _TransformComponent = class _TransformComponent extends Component {
2065
2065
  z: scale.z ?? 1
2066
2066
  };
2067
2067
  }
2068
- toPositionVector3(target = new THREE30.Vector3()) {
2068
+ toPositionVector3(target = new THREE37.Vector3()) {
2069
2069
  return target.set(this.position.x, this.position.y, this.position.z);
2070
2070
  }
2071
- toRotationEuler(target = new THREE30.Euler(), order = "XYZ") {
2071
+ toRotationEuler(target = new THREE37.Euler(), order = "XYZ") {
2072
2072
  return target.set(this.rotation.x, this.rotation.y, this.rotation.z, order);
2073
2073
  }
2074
- toRotationQuaternion(target = new THREE30.Quaternion(), order = "XYZ") {
2075
- const euler = new THREE30.Euler(
2074
+ toRotationQuaternion(target = new THREE37.Quaternion(), order = "XYZ") {
2075
+ const euler = new THREE37.Euler(
2076
2076
  this.rotation.x,
2077
2077
  this.rotation.y,
2078
2078
  this.rotation.z,
@@ -2080,7 +2080,7 @@ var _TransformComponent = class _TransformComponent extends Component {
2080
2080
  );
2081
2081
  return target.setFromEuler(euler);
2082
2082
  }
2083
- toScaleVector3(target = new THREE30.Vector3()) {
2083
+ toScaleVector3(target = new THREE37.Vector3()) {
2084
2084
  return target.set(this.scale.x, this.scale.y, this.scale.z);
2085
2085
  }
2086
2086
  setPosition(input) {
@@ -2098,8 +2098,8 @@ var _TransformComponent = class _TransformComponent extends Component {
2098
2098
  return this;
2099
2099
  }
2100
2100
  setRotation(input, order = "XYZ") {
2101
- if (input instanceof THREE30.Quaternion) {
2102
- const euler = new THREE30.Euler().setFromQuaternion(input, order);
2101
+ if (input instanceof THREE37.Quaternion) {
2102
+ const euler = new THREE37.Euler().setFromQuaternion(input, order);
2103
2103
  this.rotation.x = euler.x;
2104
2104
  this.rotation.y = euler.y;
2105
2105
  this.rotation.z = euler.z;
@@ -2701,6 +2701,223 @@ var _XRInteractorSideComponent = class _XRInteractorSideComponent extends Compon
2701
2701
  __name(_XRInteractorSideComponent, "XRInteractorSideComponent");
2702
2702
  var XRInteractorSideComponent = _XRInteractorSideComponent;
2703
2703
 
2704
+ // core/ui/uiStyle.ts
2705
+ var isRecord = /* @__PURE__ */ __name((value) => typeof value === "object" && value !== null && !Array.isArray(value), "isRecord");
2706
+ var finite = /* @__PURE__ */ __name((value) => typeof value === "number" && Number.isFinite(value), "finite");
2707
+ var nonEmpty = /* @__PURE__ */ __name((value) => typeof value === "string" && value.length > 0, "nonEmpty");
2708
+ function normalizeStops(value) {
2709
+ if (!Array.isArray(value)) return null;
2710
+ const stops = [];
2711
+ for (const stop of value) {
2712
+ if (isRecord(stop) && finite(stop.at) && nonEmpty(stop.color)) {
2713
+ stops.push({ at: Math.min(1, Math.max(0, stop.at)), color: stop.color });
2714
+ }
2715
+ }
2716
+ return stops.length >= 2 ? stops : null;
2717
+ }
2718
+ __name(normalizeStops, "normalizeStops");
2719
+ function normalizePaint(value) {
2720
+ if (!isRecord(value)) return void 0;
2721
+ if (value.kind === "solid" && nonEmpty(value.color)) {
2722
+ return { kind: "solid", color: value.color };
2723
+ }
2724
+ if (value.kind === "linear") {
2725
+ const stops = normalizeStops(value.stops);
2726
+ if (stops) {
2727
+ return {
2728
+ kind: "linear",
2729
+ angle: finite(value.angle) ? value.angle : 180,
2730
+ stops
2731
+ };
2732
+ }
2733
+ }
2734
+ if (value.kind === "radial") {
2735
+ const stops = normalizeStops(value.stops);
2736
+ if (stops) return { kind: "radial", stops };
2737
+ }
2738
+ if (value.kind === "image" && nonEmpty(value.src)) {
2739
+ const fit = value.fit === "contain" || value.fit === "fill" ? value.fit : "cover";
2740
+ return {
2741
+ kind: "image",
2742
+ src: value.src,
2743
+ fit,
2744
+ ...nonEmpty(value.tint) ? { tint: value.tint } : {}
2745
+ };
2746
+ }
2747
+ return void 0;
2748
+ }
2749
+ __name(normalizePaint, "normalizePaint");
2750
+ function normalizeShadow(value) {
2751
+ if (!isRecord(value) || !nonEmpty(value.color)) return void 0;
2752
+ return {
2753
+ x: finite(value.x) ? value.x : 0,
2754
+ y: finite(value.y) ? value.y : 0,
2755
+ blur: finite(value.blur) ? Math.max(0, value.blur) : 0,
2756
+ color: value.color
2757
+ };
2758
+ }
2759
+ __name(normalizeShadow, "normalizeShadow");
2760
+ function normalizeText(value) {
2761
+ if (!isRecord(value)) return void 0;
2762
+ const text = {};
2763
+ if (nonEmpty(value.font)) text.font = value.font;
2764
+ if (finite(value.weight)) text.weight = value.weight;
2765
+ if (finite(value.tracking)) text.tracking = value.tracking;
2766
+ if (value.transform === "upper" || value.transform === "lower") {
2767
+ text.transform = value.transform;
2768
+ }
2769
+ const paint = normalizePaint(value.paint);
2770
+ if (paint) text.paint = paint;
2771
+ if (finite(value.maxLines) && value.maxLines >= 1) {
2772
+ text.maxLines = Math.floor(value.maxLines);
2773
+ }
2774
+ const shadow = normalizeShadow(value.shadow);
2775
+ if (shadow) text.shadow = shadow;
2776
+ return Object.keys(text).length > 0 ? text : void 0;
2777
+ }
2778
+ __name(normalizeText, "normalizeText");
2779
+ function normalizeUiStyle(value) {
2780
+ if (!isRecord(value)) return void 0;
2781
+ const style = {};
2782
+ const fill = normalizePaint(value.fill);
2783
+ if (fill) style.fill = fill;
2784
+ if (isRecord(value.border) && finite(value.border.width)) {
2785
+ const paint = normalizePaint(value.border.paint);
2786
+ if (paint) {
2787
+ style.border = { width: Math.max(0, value.border.width), paint };
2788
+ }
2789
+ }
2790
+ if (finite(value.radius)) {
2791
+ style.radius = Math.max(0, value.radius);
2792
+ } else if (Array.isArray(value.radius) && value.radius.length === 4 && value.radius.every(finite)) {
2793
+ style.radius = value.radius.map((r) => Math.max(0, r));
2794
+ }
2795
+ if (Array.isArray(value.shadows)) {
2796
+ const shadows = value.shadows.map(normalizeShadow).filter((s) => !!s);
2797
+ if (shadows.length > 0) style.shadows = shadows;
2798
+ }
2799
+ if (isRecord(value.edge) && finite(value.edge.width) && nonEmpty(value.edge.color)) {
2800
+ style.edge = {
2801
+ width: Math.max(0, value.edge.width),
2802
+ color: value.edge.color
2803
+ };
2804
+ }
2805
+ if (finite(value.opacity)) {
2806
+ style.opacity = Math.min(1, Math.max(0, value.opacity));
2807
+ }
2808
+ const text = normalizeText(value.text);
2809
+ if (text) style.text = text;
2810
+ return Object.keys(style).length > 0 ? style : void 0;
2811
+ }
2812
+ __name(normalizeUiStyle, "normalizeUiStyle");
2813
+ function normalizeUiTokens(value) {
2814
+ if (!isRecord(value)) return void 0;
2815
+ const tokens = {};
2816
+ for (const [key, token] of Object.entries(value)) {
2817
+ if (nonEmpty(token) && key.length > 0 && !key.includes("/")) {
2818
+ tokens[key] = token;
2819
+ }
2820
+ }
2821
+ return Object.keys(tokens).length > 0 ? tokens : void 0;
2822
+ }
2823
+ __name(normalizeUiTokens, "normalizeUiTokens");
2824
+ function resolveToken(value, tokens, fallback = value) {
2825
+ if (!value.startsWith("$")) return value;
2826
+ return tokens?.[value.slice(1)] ?? fallback;
2827
+ }
2828
+ __name(resolveToken, "resolveToken");
2829
+ function resolvePaint(paint, tokens) {
2830
+ switch (paint.kind) {
2831
+ case "solid":
2832
+ return { kind: "solid", color: resolveToken(paint.color, tokens) };
2833
+ case "linear":
2834
+ case "radial":
2835
+ return {
2836
+ ...paint,
2837
+ stops: paint.stops.map((stop) => ({
2838
+ at: stop.at,
2839
+ color: resolveToken(stop.color, tokens)
2840
+ }))
2841
+ };
2842
+ case "image":
2843
+ return {
2844
+ ...paint,
2845
+ src: resolveToken(paint.src, tokens),
2846
+ ...paint.tint ? { tint: resolveToken(paint.tint, tokens) } : {}
2847
+ };
2848
+ }
2849
+ }
2850
+ __name(resolvePaint, "resolvePaint");
2851
+ function solidOf(paint) {
2852
+ if (paint.kind === "solid") return paint.color;
2853
+ if (paint.kind === "image") return paint.tint ?? "#ffffff";
2854
+ return paint.stops[0].color;
2855
+ }
2856
+ __name(solidOf, "solidOf");
2857
+ function radiusOf(radius, fallback) {
2858
+ if (Array.isArray(radius)) return radius;
2859
+ const r = radius ?? fallback;
2860
+ return [r, r, r, r];
2861
+ }
2862
+ __name(radiusOf, "radiusOf");
2863
+ function resolveChrome(flat, style, tokens) {
2864
+ const borderWidth = style?.border?.width ?? flat.borderWidth;
2865
+ const border = borderWidth > 0 ? {
2866
+ width: borderWidth,
2867
+ color: style?.border ? solidOf(resolvePaint(style.border.paint, tokens)) : resolveToken(flat.borderColor, tokens)
2868
+ } : null;
2869
+ return {
2870
+ fill: style?.fill ? resolvePaint(style.fill, tokens) : { kind: "solid", color: resolveToken(flat.backgroundColor, tokens) },
2871
+ fillOpacity: style?.fill ? 1 : flat.backgroundOpacity,
2872
+ radius: radiusOf(style?.radius, flat.cornerRadius),
2873
+ border,
2874
+ shadows: (style?.shadows ?? []).map((shadow) => ({
2875
+ ...shadow,
2876
+ color: resolveToken(shadow.color, tokens)
2877
+ })),
2878
+ edge: style?.edge ? { ...style.edge, color: resolveToken(style.edge.color, tokens) } : null
2879
+ };
2880
+ }
2881
+ __name(resolveChrome, "resolveChrome");
2882
+ function resolveText(flat, style, tokens) {
2883
+ const text = style?.text;
2884
+ return {
2885
+ ...flat,
2886
+ color: resolveToken(flat.color, tokens),
2887
+ fontWeight: text?.weight ?? flat.fontWeight,
2888
+ paint: text?.paint ? resolvePaint(text.paint, tokens) : { kind: "solid", color: resolveToken(flat.color, tokens) },
2889
+ font: text?.font ? resolveToken(text.font, tokens) : null,
2890
+ tracking: text?.tracking ?? 0,
2891
+ transform: text?.transform ?? null,
2892
+ maxLines: text?.maxLines ?? null,
2893
+ shadow: text?.shadow ? {
2894
+ ...text.shadow,
2895
+ color: resolveToken(text.shadow.color, tokens)
2896
+ } : null
2897
+ };
2898
+ }
2899
+ __name(resolveText, "resolveText");
2900
+ function chromeBleed(chrome) {
2901
+ let bleed = 0;
2902
+ for (const shadow of chrome.shadows) {
2903
+ bleed = Math.max(
2904
+ bleed,
2905
+ shadow.blur + Math.max(Math.abs(shadow.x), Math.abs(shadow.y))
2906
+ );
2907
+ }
2908
+ return bleed;
2909
+ }
2910
+ __name(chromeBleed, "chromeBleed");
2911
+ function styleAssets(chrome, text) {
2912
+ const images2 = [];
2913
+ const fonts2 = [];
2914
+ if (chrome?.fill?.kind === "image") images2.push(chrome.fill.src);
2915
+ if (text?.paint.kind === "image") images2.push(text.paint.src);
2916
+ if (text?.font) fonts2.push(text.font);
2917
+ return { images: images2, fonts: fonts2 };
2918
+ }
2919
+ __name(styleAssets, "styleAssets");
2920
+
2704
2921
  // core/components/UIButton.ts
2705
2922
  var _UIButtonComponent = class _UIButtonComponent extends Component {
2706
2923
  constructor(options) {
@@ -2717,6 +2934,13 @@ var _UIButtonComponent = class _UIButtonComponent extends Component {
2717
2934
  this.cornerRadius = options?.cornerRadius ?? 0.02;
2718
2935
  this.borderWidth = options?.borderWidth ?? 2e-3;
2719
2936
  this.borderColor = options?.borderColor ?? "#3a3a3a";
2937
+ if (options?.pixelDensity !== void 0) {
2938
+ this.pixelDensity = options.pixelDensity;
2939
+ }
2940
+ const style = normalizeUiStyle(options?.style);
2941
+ if (style) this.style = style;
2942
+ if (options?.icon) this.icon = options.icon;
2943
+ if (options?.iconPosition) this.iconPosition = options.iconPosition;
2720
2944
  }
2721
2945
  };
2722
2946
  __name(_UIButtonComponent, "UIButtonComponent");
@@ -2735,6 +2959,10 @@ var _UIPanelComponent = class _UIPanelComponent extends Component {
2735
2959
  this.borderColor = options?.borderColor ?? "#333333";
2736
2960
  this.pixelDensity = options?.pixelDensity ?? 1024;
2737
2961
  this.follow = options?.follow ?? "world";
2962
+ const style = normalizeUiStyle(options?.style);
2963
+ if (style) this.style = style;
2964
+ const tokens = normalizeUiTokens(options?.tokens);
2965
+ if (tokens) this.tokens = tokens;
2738
2966
  }
2739
2967
  };
2740
2968
  __name(_UIPanelComponent, "UIPanelComponent");
@@ -2755,6 +2983,14 @@ var _UISliderComponent = class _UISliderComponent extends Component {
2755
2983
  this.trackColor = options?.trackColor ?? "#2a2a2a";
2756
2984
  this.accentColor = options?.accentColor ?? "#ff6b35";
2757
2985
  this.knobColor = options?.knobColor ?? "#ffffff";
2986
+ if (options?.pixelDensity !== void 0) {
2987
+ this.pixelDensity = options.pixelDensity;
2988
+ }
2989
+ const style = normalizeUiStyle(options?.style);
2990
+ if (style) this.style = style;
2991
+ if (options?.variant) this.variant = options.variant;
2992
+ if (options?.step !== void 0) this.step = options.step;
2993
+ if (options?.readOnly !== void 0) this.readOnly = options.readOnly;
2758
2994
  }
2759
2995
  };
2760
2996
  __name(_UISliderComponent, "UISliderComponent");
@@ -2774,6 +3010,11 @@ var _UITextComponent = class _UITextComponent extends Component {
2774
3010
  this.valign = options?.valign ?? "middle";
2775
3011
  this.lineHeight = options?.lineHeight ?? 1.25;
2776
3012
  this.padding = options?.padding ?? 0.01;
3013
+ if (options?.pixelDensity !== void 0) {
3014
+ this.pixelDensity = options.pixelDensity;
3015
+ }
3016
+ const style = normalizeUiStyle(options?.style);
3017
+ if (style) this.style = style;
2777
3018
  }
2778
3019
  };
2779
3020
  __name(_UITextComponent, "UITextComponent");
@@ -2790,6 +3031,12 @@ var _UIToggleComponent = class _UIToggleComponent extends Component {
2790
3031
  this.trackColor = options?.trackColor ?? "#2a2a2a";
2791
3032
  this.accentColor = options?.accentColor ?? "#ff6b35";
2792
3033
  this.knobColor = options?.knobColor ?? "#ffffff";
3034
+ if (options?.pixelDensity !== void 0) {
3035
+ this.pixelDensity = options.pixelDensity;
3036
+ }
3037
+ const style = normalizeUiStyle(options?.style);
3038
+ if (style) this.style = style;
3039
+ if (options?.variant) this.variant = options.variant;
2793
3040
  }
2794
3041
  };
2795
3042
  __name(_UIToggleComponent, "UIToggleComponent");
@@ -2923,8 +3170,8 @@ __name(reloadModelEntity, "reloadModelEntity");
2923
3170
 
2924
3171
  // core/systems/ObjectManagementSystem.ts
2925
3172
  var BONE_SOCKET_FLAG = "__reluBoneSocket";
2926
- var _boneScale = new THREE30__namespace.Vector3();
2927
- var _anchorScale = new THREE30__namespace.Vector3();
3173
+ var _boneScale = new THREE37__namespace.Vector3();
3174
+ var _anchorScale = new THREE37__namespace.Vector3();
2928
3175
  function applyTransform(object, transform) {
2929
3176
  object.position.set(
2930
3177
  transform.position.x,
@@ -2959,10 +3206,10 @@ var _ObjectManagementSystem = class _ObjectManagementSystem {
2959
3206
  }
2960
3207
  getOrCreateAudioListener() {
2961
3208
  const existing = this.camera.userData.ecsAudioListener;
2962
- if (existing instanceof THREE30__namespace.AudioListener) {
3209
+ if (existing instanceof THREE37__namespace.AudioListener) {
2963
3210
  return existing;
2964
3211
  }
2965
- const listener = new THREE30__namespace.AudioListener();
3212
+ const listener = new THREE37__namespace.AudioListener();
2966
3213
  this.camera.add(listener);
2967
3214
  this.camera.userData.ecsAudioListener = listener;
2968
3215
  return listener;
@@ -3036,7 +3283,7 @@ var _ObjectManagementSystem = class _ObjectManagementSystem {
3036
3283
  }
3037
3284
  if (dispose && object3d) {
3038
3285
  object3d.traverse((child) => {
3039
- if (child instanceof THREE30__namespace.Mesh) {
3286
+ if (child instanceof THREE37__namespace.Mesh) {
3040
3287
  if (!child.geometry?.userData?.[SHARED_MODEL_RESOURCE_FLAG]) {
3041
3288
  child.geometry?.dispose();
3042
3289
  }
@@ -3188,7 +3435,7 @@ var _ObjectManagementSystem = class _ObjectManagementSystem {
3188
3435
  return null;
3189
3436
  }
3190
3437
  this.boneMisses.delete(entityId);
3191
- const socket = new THREE30__namespace.Object3D();
3438
+ const socket = new THREE37__namespace.Object3D();
3192
3439
  socket.name = `socket:${bone.name}`;
3193
3440
  socket.userData[BONE_SOCKET_FLAG] = entityId;
3194
3441
  bone.add(socket);
@@ -3473,7 +3720,7 @@ function transitionToClip(runtime, nextClipId, crossFadeDuration) {
3473
3720
  }
3474
3721
  __name(transitionToClip, "transitionToClip");
3475
3722
  function applyActionState(action, component, paused, shouldLoop) {
3476
- const loopMode = shouldLoop ? THREE30__namespace.LoopRepeat : THREE30__namespace.LoopOnce;
3723
+ const loopMode = shouldLoop ? THREE37__namespace.LoopRepeat : THREE37__namespace.LoopOnce;
3477
3724
  const repetitions = shouldLoop ? Infinity : 1;
3478
3725
  action.setLoop(loopMode, repetitions);
3479
3726
  action.clampWhenFinished = !shouldLoop;
@@ -3646,7 +3893,7 @@ var _AnimationSystem = class _AnimationSystem {
3646
3893
  if (runtime) {
3647
3894
  stopAllActions(runtime);
3648
3895
  }
3649
- const mixer = new THREE30__namespace.AnimationMixer(root);
3896
+ const mixer = new THREE37__namespace.AnimationMixer(root);
3650
3897
  const entries2 = readRuntimeClipEntries(animation);
3651
3898
  const indexedActions = createActions(mixer, entries2);
3652
3899
  runtime = {
@@ -3772,11 +4019,11 @@ function normalizeOrthoBounds(component) {
3772
4019
  }
3773
4020
  __name(normalizeOrthoBounds, "normalizeOrthoBounds");
3774
4021
  function isPerspectiveCamera(camera) {
3775
- return camera instanceof THREE30__namespace.PerspectiveCamera;
4022
+ return camera instanceof THREE37__namespace.PerspectiveCamera;
3776
4023
  }
3777
4024
  __name(isPerspectiveCamera, "isPerspectiveCamera");
3778
4025
  function isOrthographicCamera(camera) {
3779
- return camera instanceof THREE30__namespace.OrthographicCamera;
4026
+ return camera instanceof THREE37__namespace.OrthographicCamera;
3780
4027
  }
3781
4028
  __name(isOrthographicCamera, "isOrthographicCamera");
3782
4029
  function readWindowAspect() {
@@ -3830,7 +4077,7 @@ var _CameraSyncSystem = class _CameraSyncSystem {
3830
4077
  continue;
3831
4078
  }
3832
4079
  const camera = objectRef.object;
3833
- if (!(camera instanceof THREE30__namespace.Camera)) {
4080
+ if (!(camera instanceof THREE37__namespace.Camera)) {
3834
4081
  continue;
3835
4082
  }
3836
4083
  if (xrCamera !== null && camera === xrCamera) {
@@ -3895,8 +4142,8 @@ var ObjectVisibilitySystem = _ObjectVisibilitySystem;
3895
4142
  function getUrlExtension(url) {
3896
4143
  const path = url.split(/[?#]/)[0] ?? "";
3897
4144
  const lastSegment = path.split("/").pop() ?? "";
3898
- const dot = lastSegment.lastIndexOf(".");
3899
- return dot >= 0 ? lastSegment.slice(dot + 1).toLowerCase() : "";
4145
+ const dot2 = lastSegment.lastIndexOf(".");
4146
+ return dot2 >= 0 ? lastSegment.slice(dot2 + 1).toLowerCase() : "";
3900
4147
  }
3901
4148
  __name(getUrlExtension, "getUrlExtension");
3902
4149
  async function loadEquirectTexture(url) {
@@ -3909,10 +4156,10 @@ async function loadEquirectTexture(url) {
3909
4156
  const { EXRLoader } = await import('three/examples/jsm/loaders/EXRLoader.js');
3910
4157
  texture = await new EXRLoader().loadAsync(url);
3911
4158
  } else {
3912
- texture = await new THREE30__namespace.TextureLoader().loadAsync(url);
3913
- texture.colorSpace = THREE30__namespace.SRGBColorSpace;
4159
+ texture = await new THREE37__namespace.TextureLoader().loadAsync(url);
4160
+ texture.colorSpace = THREE37__namespace.SRGBColorSpace;
3914
4161
  }
3915
- texture.mapping = THREE30__namespace.EquirectangularReflectionMapping;
4162
+ texture.mapping = THREE37__namespace.EquirectangularReflectionMapping;
3916
4163
  texture.needsUpdate = true;
3917
4164
  return texture;
3918
4165
  }
@@ -3921,13 +4168,13 @@ var rendererStates = /* @__PURE__ */ new WeakMap();
3921
4168
  function toThreeToneMapping(mode) {
3922
4169
  switch (mode) {
3923
4170
  case "aces":
3924
- return THREE30__namespace.ACESFilmicToneMapping;
4171
+ return THREE37__namespace.ACESFilmicToneMapping;
3925
4172
  case "agx":
3926
- return THREE30__namespace.AgXToneMapping;
4173
+ return THREE37__namespace.AgXToneMapping;
3927
4174
  case "neutral":
3928
- return THREE30__namespace.NeutralToneMapping;
4175
+ return THREE37__namespace.NeutralToneMapping;
3929
4176
  default:
3930
- return THREE30__namespace.NoToneMapping;
4177
+ return THREE37__namespace.NoToneMapping;
3931
4178
  }
3932
4179
  }
3933
4180
  __name(toThreeToneMapping, "toThreeToneMapping");
@@ -3948,7 +4195,7 @@ function setRendererToneMappingSource(renderer, source, request) {
3948
4195
  if (request === null) return;
3949
4196
  state = {
3950
4197
  baseline: {
3951
- toneMapping: target.toneMapping ?? THREE30__namespace.NoToneMapping,
4198
+ toneMapping: target.toneMapping ?? THREE37__namespace.NoToneMapping,
3952
4199
  exposure: typeof target.toneMappingExposure === "number" ? target.toneMappingExposure : 1
3953
4200
  },
3954
4201
  environment: null,
@@ -4000,7 +4247,7 @@ var MIN_DENSITY = 0;
4000
4247
  function createGradientTexture(effect, colors) {
4001
4248
  if (typeof document === "undefined") {
4002
4249
  const pixel = new Uint8Array([30, 30, 30, 255]);
4003
- const fallback = new THREE30__namespace.DataTexture(pixel, 1, 1, THREE30__namespace.RGBAFormat);
4250
+ const fallback = new THREE37__namespace.DataTexture(pixel, 1, 1, THREE37__namespace.RGBAFormat);
4004
4251
  fallback.needsUpdate = true;
4005
4252
  return fallback;
4006
4253
  }
@@ -4009,7 +4256,7 @@ function createGradientTexture(effect, colors) {
4009
4256
  canvas.height = 512;
4010
4257
  const context = canvas.getContext("2d");
4011
4258
  if (!context) {
4012
- const fallback = new THREE30__namespace.CanvasTexture(canvas);
4259
+ const fallback = new THREE37__namespace.CanvasTexture(canvas);
4013
4260
  fallback.needsUpdate = true;
4014
4261
  return fallback;
4015
4262
  }
@@ -4037,8 +4284,8 @@ function createGradientTexture(effect, colors) {
4037
4284
  context.fillRect(0, 0, 512, 512);
4038
4285
  context.globalCompositeOperation = "source-over";
4039
4286
  }
4040
- const texture = new THREE30__namespace.CanvasTexture(canvas);
4041
- texture.colorSpace = THREE30__namespace.SRGBColorSpace;
4287
+ const texture = new THREE37__namespace.CanvasTexture(canvas);
4288
+ texture.colorSpace = THREE37__namespace.SRGBColorSpace;
4042
4289
  texture.needsUpdate = true;
4043
4290
  return texture;
4044
4291
  }
@@ -4055,7 +4302,7 @@ function readRenderer(world) {
4055
4302
  __name(readRenderer, "readRenderer");
4056
4303
  var _EnvironmentSyncSystem = class _EnvironmentSyncSystem {
4057
4304
  constructor(scene) {
4058
- this.textureLoader = new THREE30__namespace.TextureLoader();
4305
+ this.textureLoader = new THREE37__namespace.TextureLoader();
4059
4306
  /** `true` once an Environment entity has been seen (drives the reset path). */
4060
4307
  this.hasEnvironment = false;
4061
4308
  this.skyboxSignature = null;
@@ -4139,7 +4386,7 @@ var _EnvironmentSyncSystem = class _EnvironmentSyncSystem {
4139
4386
  if (skybox.type === "color") {
4140
4387
  this.disposeActiveTexture();
4141
4388
  this.pendingImageUrl = null;
4142
- this.setSkyboxBackground(new THREE30__namespace.Color(skybox.color || "#1e1e1e"));
4389
+ this.setSkyboxBackground(new THREE37__namespace.Color(skybox.color || "#1e1e1e"));
4143
4390
  return;
4144
4391
  }
4145
4392
  if (skybox.type === "gradient") {
@@ -4174,7 +4421,7 @@ var _EnvironmentSyncSystem = class _EnvironmentSyncSystem {
4174
4421
  return;
4175
4422
  }
4176
4423
  if (!equirect) {
4177
- texture.colorSpace = THREE30__namespace.SRGBColorSpace;
4424
+ texture.colorSpace = THREE37__namespace.SRGBColorSpace;
4178
4425
  }
4179
4426
  this.activeTexture = texture;
4180
4427
  this.setSkyboxBackground(texture);
@@ -4202,16 +4449,16 @@ var _EnvironmentSyncSystem = class _EnvironmentSyncSystem {
4202
4449
  this.scene.fog = null;
4203
4450
  return;
4204
4451
  }
4205
- const fogColor = new THREE30__namespace.Color(fog.color || "#1e1e1e");
4452
+ const fogColor = new THREE37__namespace.Color(fog.color || "#1e1e1e");
4206
4453
  if (fog.type === "linear") {
4207
4454
  const near = Number.isFinite(fog.linear.near) ? fog.linear.near : 1;
4208
4455
  const farCandidate = Number.isFinite(fog.linear.far) ? fog.linear.far : near + 10;
4209
4456
  const far = Math.max(near + MIN_FOG_DELTA, farCandidate);
4210
- this.scene.fog = new THREE30__namespace.Fog(fogColor, near, far);
4457
+ this.scene.fog = new THREE37__namespace.Fog(fogColor, near, far);
4211
4458
  return;
4212
4459
  }
4213
4460
  const density = Number.isFinite(fog.exponential.density) ? Math.max(MIN_DENSITY, fog.exponential.density) : 2e-3;
4214
- this.scene.fog = new THREE30__namespace.FogExp2(fogColor, density);
4461
+ this.scene.fog = new THREE37__namespace.FogExp2(fogColor, density);
4215
4462
  }
4216
4463
  // ── Image-based lighting ────────────────────────────────────────────────
4217
4464
  disposeEnvResources() {
@@ -4273,7 +4520,7 @@ var _EnvironmentSyncSystem = class _EnvironmentSyncSystem {
4273
4520
  if (!renderer || getRendererKindOf(renderer) === "webgpu") return null;
4274
4521
  let generator = null;
4275
4522
  try {
4276
- generator = new THREE30__namespace.PMREMGenerator(renderer);
4523
+ generator = new THREE37__namespace.PMREMGenerator(renderer);
4277
4524
  return generator.fromEquirectangular(texture);
4278
4525
  } catch (error) {
4279
4526
  console.warn(
@@ -4413,7 +4660,7 @@ var PixelationShader = {
4413
4660
  name: "PixelationShader",
4414
4661
  uniforms: {
4415
4662
  tDiffuse: { value: null },
4416
- resolution: { value: new THREE30__namespace.Vector2(1, 1) },
4663
+ resolution: { value: new THREE37__namespace.Vector2(1, 1) },
4417
4664
  pixelSize: { value: 6 }
4418
4665
  },
4419
4666
  vertexShader: (
@@ -4483,10 +4730,10 @@ function createWebglPostprocessing({
4483
4730
  );
4484
4731
  }
4485
4732
  if (values.bloom.enabled) {
4486
- const size = renderer.getSize(new THREE30__namespace.Vector2());
4733
+ const size = renderer.getSize(new THREE37__namespace.Vector2());
4487
4734
  composer.addPass(
4488
4735
  new UnrealBloomPass_js.UnrealBloomPass(
4489
- new THREE30__namespace.Vector2(size.width, size.height),
4736
+ new THREE37__namespace.Vector2(size.width, size.height),
4490
4737
  values.bloom.intensity,
4491
4738
  values.bloom.radius,
4492
4739
  values.bloom.threshold
@@ -4531,7 +4778,7 @@ function createWebglPostprocessing({
4531
4778
  if (values.dotScreen.enabled) {
4532
4779
  composer.addPass(
4533
4780
  new DotScreenPass_js.DotScreenPass(
4534
- new THREE30__namespace.Vector2(0, 0),
4781
+ new THREE37__namespace.Vector2(0, 0),
4535
4782
  values.dotScreen.angle ?? values.dotScreen.dotScreenAngle,
4536
4783
  values.dotScreen.scale ?? values.dotScreen.dotScreenScale
4537
4784
  )
@@ -4580,10 +4827,10 @@ function createWebglPostprocessing({
4580
4827
  __name(createWebglPostprocessing, "createWebglPostprocessing");
4581
4828
  var CHROMA_KEY_FLAG = "ecsChromaKey";
4582
4829
  function createWebglChromaKeyMaterial(texture, options) {
4583
- const material = new THREE30__namespace.ShaderMaterial({
4830
+ const material = new THREE37__namespace.ShaderMaterial({
4584
4831
  uniforms: {
4585
4832
  tex: { value: texture },
4586
- keyColor: { value: new THREE30__namespace.Color(options.keyColor) },
4833
+ keyColor: { value: new THREE37__namespace.Color(options.keyColor) },
4587
4834
  similarity: { value: options.similarity },
4588
4835
  smoothness: { value: options.smoothness },
4589
4836
  spill: { value: options.spill }
@@ -4615,14 +4862,14 @@ function createWebglChromaKeyMaterial(texture, options) {
4615
4862
  }
4616
4863
  `,
4617
4864
  transparent: true,
4618
- side: THREE30__namespace.DoubleSide
4865
+ side: THREE37__namespace.DoubleSide
4619
4866
  });
4620
4867
  material.userData[CHROMA_KEY_FLAG] = true;
4621
4868
  return material;
4622
4869
  }
4623
4870
  __name(createWebglChromaKeyMaterial, "createWebglChromaKeyMaterial");
4624
4871
  function updateWebglChromaKeyMaterial(material, options) {
4625
- if (!(material instanceof THREE30__namespace.ShaderMaterial)) return false;
4872
+ if (!(material instanceof THREE37__namespace.ShaderMaterial)) return false;
4626
4873
  const { keyColor, similarity, smoothness, spill } = material.uniforms;
4627
4874
  if (!keyColor || !similarity || !smoothness || !spill) return false;
4628
4875
  keyColor.value.set(options.keyColor);
@@ -4824,7 +5071,7 @@ function computeColliderState(colliderValues, object) {
4824
5071
  if (!object) {
4825
5072
  return colliderValues;
4826
5073
  }
4827
- const scale = new THREE30__namespace.Vector3();
5074
+ const scale = new THREE37__namespace.Vector3();
4828
5075
  object.getWorldScale(scale);
4829
5076
  return {
4830
5077
  ...colliderValues,
@@ -4875,8 +5122,8 @@ function createColliderDesc(RAPIER, values, triMeshData) {
4875
5122
  values.offsetPosition.y,
4876
5123
  values.offsetPosition.z
4877
5124
  );
4878
- const offsetQuaternion = new THREE30__namespace.Quaternion().setFromEuler(
4879
- new THREE30__namespace.Euler(
5125
+ const offsetQuaternion = new THREE37__namespace.Quaternion().setFromEuler(
5126
+ new THREE37__namespace.Euler(
4880
5127
  values.offsetRotation.x,
4881
5128
  values.offsetRotation.y,
4882
5129
  values.offsetRotation.z
@@ -4891,17 +5138,17 @@ function collectTriMeshParts(object) {
4891
5138
  return null;
4892
5139
  }
4893
5140
  object.updateWorldMatrix(true, true);
4894
- const worldScale = new THREE30__namespace.Vector3();
5141
+ const worldScale = new THREE37__namespace.Vector3();
4895
5142
  object.getWorldScale(worldScale);
4896
5143
  const scale = {
4897
5144
  x: Math.abs(worldScale.x) || 1,
4898
5145
  y: Math.abs(worldScale.y) || 1,
4899
5146
  z: Math.abs(worldScale.z) || 1
4900
5147
  };
4901
- const worldToObject = new THREE30__namespace.Matrix4().copy(object.matrixWorld).invert();
5148
+ const worldToObject = new THREE37__namespace.Matrix4().copy(object.matrixWorld).invert();
4902
5149
  const parts = [];
4903
5150
  object.traverse((child) => {
4904
- if (!(child instanceof THREE30__namespace.Mesh) || !child.geometry) {
5151
+ if (!(child instanceof THREE37__namespace.Mesh) || !child.geometry) {
4905
5152
  return;
4906
5153
  }
4907
5154
  const geometry = child.geometry;
@@ -4918,7 +5165,7 @@ function collectTriMeshParts(object) {
4918
5165
  if (indexCount < 3) {
4919
5166
  return;
4920
5167
  }
4921
- const relativeMatrix = new THREE30__namespace.Matrix4().multiplyMatrices(
5168
+ const relativeMatrix = new THREE37__namespace.Matrix4().multiplyMatrices(
4922
5169
  worldToObject,
4923
5170
  child.matrixWorld
4924
5171
  );
@@ -4959,7 +5206,7 @@ function createTriMeshData(collected) {
4959
5206
  }
4960
5207
  const vertices = new Float32Array(vertexTotal * 3);
4961
5208
  const indices = new Uint32Array(indexTotal);
4962
- const point = new THREE30__namespace.Vector3();
5209
+ const point = new THREE37__namespace.Vector3();
4963
5210
  let vertexOffset = 0;
4964
5211
  let indexOffset = 0;
4965
5212
  for (const part of parts) {
@@ -4991,8 +5238,8 @@ function createTriMeshData(collected) {
4991
5238
  }
4992
5239
  __name(createTriMeshData, "createTriMeshData");
4993
5240
  function syncBodyFromTransform(body, transform) {
4994
- const quaternion = new THREE30__namespace.Quaternion().setFromEuler(
4995
- new THREE30__namespace.Euler(transform.rotation.x, transform.rotation.y, transform.rotation.z)
5241
+ const quaternion = new THREE37__namespace.Quaternion().setFromEuler(
5242
+ new THREE37__namespace.Euler(transform.rotation.x, transform.rotation.y, transform.rotation.z)
4996
5243
  );
4997
5244
  body.setTranslation(
4998
5245
  {
@@ -5019,8 +5266,8 @@ function syncTransformFromBody(transform, body) {
5019
5266
  transform.position.x = position.x;
5020
5267
  transform.position.y = position.y;
5021
5268
  transform.position.z = position.z;
5022
- const euler = new THREE30__namespace.Euler().setFromQuaternion(
5023
- new THREE30__namespace.Quaternion(rotation.x, rotation.y, rotation.z, rotation.w)
5269
+ const euler = new THREE37__namespace.Euler().setFromQuaternion(
5270
+ new THREE37__namespace.Quaternion(rotation.x, rotation.y, rotation.z, rotation.w)
5024
5271
  );
5025
5272
  transform.rotation.x = euler.x;
5026
5273
  transform.rotation.y = euler.y;
@@ -5133,8 +5380,8 @@ var _PhysicsSyncSystem = class _PhysicsSyncSystem {
5133
5380
  transform.position.y,
5134
5381
  transform.position.z
5135
5382
  );
5136
- const quaternion = new THREE30__namespace.Quaternion().setFromEuler(
5137
- new THREE30__namespace.Euler(
5383
+ const quaternion = new THREE37__namespace.Quaternion().setFromEuler(
5384
+ new THREE37__namespace.Euler(
5138
5385
  transform.rotation.x,
5139
5386
  transform.rotation.y,
5140
5387
  transform.rotation.z
@@ -5865,7 +6112,7 @@ function applyLightValues(lightComponent, rendererKind) {
5865
6112
  values.castShadow
5866
6113
  );
5867
6114
  }
5868
- if (light instanceof THREE30__namespace.PointLight) {
6115
+ if (light instanceof THREE37__namespace.PointLight) {
5869
6116
  if (typeof values.distance === "number" && Number.isFinite(values.distance)) {
5870
6117
  light.distance = Math.max(MIN_LIGHT_DISTANCE, values.distance);
5871
6118
  }
@@ -5873,7 +6120,7 @@ function applyLightValues(lightComponent, rendererKind) {
5873
6120
  light.decay = Math.max(0, values.decay);
5874
6121
  }
5875
6122
  }
5876
- if (light instanceof THREE30__namespace.SpotLight) {
6123
+ if (light instanceof THREE37__namespace.SpotLight) {
5877
6124
  if (typeof values.distance === "number" && Number.isFinite(values.distance)) {
5878
6125
  light.distance = Math.max(MIN_LIGHT_DISTANCE, values.distance);
5879
6126
  }
@@ -5887,7 +6134,7 @@ function applyLightValues(lightComponent, rendererKind) {
5887
6134
  light.penumbra = Math.max(0, Math.min(1, values.penumbra));
5888
6135
  }
5889
6136
  }
5890
- if (light instanceof THREE30__namespace.RectAreaLight) {
6137
+ if (light instanceof THREE37__namespace.RectAreaLight) {
5891
6138
  if (typeof values.width === "number" && Number.isFinite(values.width)) {
5892
6139
  light.width = Math.max(MIN_AREA_SIZE, values.width);
5893
6140
  }
@@ -5951,15 +6198,15 @@ function resolveMaterialPropertyKey(key) {
5951
6198
  __name(resolveMaterialPropertyKey, "resolveMaterialPropertyKey");
5952
6199
  function createMaterialByType(type) {
5953
6200
  if (type === "basic") {
5954
- return new THREE30__namespace.MeshBasicMaterial();
6201
+ return new THREE37__namespace.MeshBasicMaterial();
5955
6202
  }
5956
6203
  if (type === "phong") {
5957
- return new THREE30__namespace.MeshPhongMaterial();
6204
+ return new THREE37__namespace.MeshPhongMaterial();
5958
6205
  }
5959
6206
  if (type === "physical") {
5960
- return new THREE30__namespace.MeshPhysicalMaterial();
6207
+ return new THREE37__namespace.MeshPhysicalMaterial();
5961
6208
  }
5962
- return new THREE30__namespace.MeshStandardMaterial();
6209
+ return new THREE37__namespace.MeshStandardMaterial();
5963
6210
  }
5964
6211
  __name(createMaterialByType, "createMaterialByType");
5965
6212
  function replaceMaterialAcrossObject(object, previous, next) {
@@ -6018,12 +6265,12 @@ function applyTextureFiltering(material, filtering) {
6018
6265
  const target = material;
6019
6266
  for (const slot of materialTextureSlots) {
6020
6267
  const texture = target[slot];
6021
- if (!texture || !(texture instanceof THREE30__namespace.Texture)) {
6268
+ if (!texture || !(texture instanceof THREE37__namespace.Texture)) {
6022
6269
  continue;
6023
6270
  }
6024
6271
  const useMipmaps = texture.generateMipmaps !== false;
6025
- texture.magFilter = sharp ? THREE30__namespace.NearestFilter : THREE30__namespace.LinearFilter;
6026
- texture.minFilter = useMipmaps ? sharp ? THREE30__namespace.NearestMipmapNearestFilter : THREE30__namespace.LinearMipmapLinearFilter : sharp ? THREE30__namespace.NearestFilter : THREE30__namespace.LinearFilter;
6272
+ texture.magFilter = sharp ? THREE37__namespace.NearestFilter : THREE37__namespace.LinearFilter;
6273
+ texture.minFilter = useMipmaps ? sharp ? THREE37__namespace.NearestMipmapNearestFilter : THREE37__namespace.LinearMipmapLinearFilter : sharp ? THREE37__namespace.NearestFilter : THREE37__namespace.LinearFilter;
6027
6274
  texture.needsUpdate = true;
6028
6275
  }
6029
6276
  material.needsUpdate = true;
@@ -6033,12 +6280,12 @@ function setTextureMipmaps(material, enabled) {
6033
6280
  const target = material;
6034
6281
  for (const slot of materialTextureSlots) {
6035
6282
  const texture = target[slot];
6036
- if (!texture || !(texture instanceof THREE30__namespace.Texture)) {
6283
+ if (!texture || !(texture instanceof THREE37__namespace.Texture)) {
6037
6284
  continue;
6038
6285
  }
6039
6286
  texture.generateMipmaps = enabled;
6040
- const sharp = texture.magFilter === THREE30__namespace.NearestFilter;
6041
- texture.minFilter = enabled ? sharp ? THREE30__namespace.NearestMipmapNearestFilter : THREE30__namespace.LinearMipmapLinearFilter : sharp ? THREE30__namespace.NearestFilter : THREE30__namespace.LinearFilter;
6287
+ const sharp = texture.magFilter === THREE37__namespace.NearestFilter;
6288
+ texture.minFilter = enabled ? sharp ? THREE37__namespace.NearestMipmapNearestFilter : THREE37__namespace.LinearMipmapLinearFilter : sharp ? THREE37__namespace.NearestFilter : THREE37__namespace.LinearFilter;
6042
6289
  texture.needsUpdate = true;
6043
6290
  }
6044
6291
  material.needsUpdate = true;
@@ -6049,7 +6296,7 @@ function assignMaterialTexture(material, rawKey, url, textureCache) {
6049
6296
  const key = resolveMaterialPropertyKey(rawKey);
6050
6297
  let texture = textureCache.get(url);
6051
6298
  if (!texture) {
6052
- texture = new THREE30__namespace.TextureLoader().load(url);
6299
+ texture = new THREE37__namespace.TextureLoader().load(url);
6053
6300
  textureCache.set(url, texture);
6054
6301
  }
6055
6302
  target[key] = texture;
@@ -6087,7 +6334,7 @@ function applyMaterialState(material, value, textureCache) {
6087
6334
  }
6088
6335
  target.side = value.side;
6089
6336
  target.blending = value.blending;
6090
- target.premultipliedAlpha = value.blending === THREE30__namespace.MultiplyBlending || value.blending === THREE30__namespace.SubtractiveBlending;
6337
+ target.premultipliedAlpha = value.blending === THREE37__namespace.MultiplyBlending || value.blending === THREE37__namespace.SubtractiveBlending;
6091
6338
  target.depthTest = value.depthTest;
6092
6339
  target.depthWrite = value.depthWrite;
6093
6340
  target.wireframe = value.wireframe;
@@ -6248,12 +6495,12 @@ function resolvePrimaryMesh(object) {
6248
6495
  if (!object) {
6249
6496
  return null;
6250
6497
  }
6251
- if (object instanceof THREE30__namespace.Mesh) {
6498
+ if (object instanceof THREE37__namespace.Mesh) {
6252
6499
  return object;
6253
6500
  }
6254
6501
  let found = null;
6255
6502
  object.traverse((child) => {
6256
- if (!found && child instanceof THREE30__namespace.Mesh) {
6503
+ if (!found && child instanceof THREE37__namespace.Mesh) {
6257
6504
  found = child;
6258
6505
  }
6259
6506
  });
@@ -6274,22 +6521,22 @@ function applyGeometry(component) {
6274
6521
  switch (component.kind) {
6275
6522
  case "cube": {
6276
6523
  const params = sanitized;
6277
- nextGeometry = new THREE30__namespace.BoxGeometry(params.width, params.height, params.depth);
6524
+ nextGeometry = new THREE37__namespace.BoxGeometry(params.width, params.height, params.depth);
6278
6525
  break;
6279
6526
  }
6280
6527
  case "plane": {
6281
6528
  const params = sanitized;
6282
- nextGeometry = new THREE30__namespace.PlaneGeometry(params.width, params.height);
6529
+ nextGeometry = new THREE37__namespace.PlaneGeometry(params.width, params.height);
6283
6530
  break;
6284
6531
  }
6285
6532
  case "sphere": {
6286
6533
  const params = sanitized;
6287
- nextGeometry = new THREE30__namespace.SphereGeometry(params.radius, 24, 24);
6534
+ nextGeometry = new THREE37__namespace.SphereGeometry(params.radius, 24, 24);
6288
6535
  break;
6289
6536
  }
6290
6537
  case "cylinder": {
6291
6538
  const params = sanitized;
6292
- nextGeometry = new THREE30__namespace.CylinderGeometry(
6539
+ nextGeometry = new THREE37__namespace.CylinderGeometry(
6293
6540
  params.radius,
6294
6541
  params.radius,
6295
6542
  params.height,
@@ -6299,7 +6546,7 @@ function applyGeometry(component) {
6299
6546
  }
6300
6547
  case "capsule": {
6301
6548
  const params = sanitized;
6302
- nextGeometry = new THREE30__namespace.CapsuleGeometry(params.radius, params.height, 4, 8);
6549
+ nextGeometry = new THREE37__namespace.CapsuleGeometry(params.radius, params.height, 4, 8);
6303
6550
  break;
6304
6551
  }
6305
6552
  default:
@@ -6315,7 +6562,7 @@ function applyShadowFlags(component) {
6315
6562
  return;
6316
6563
  }
6317
6564
  component.object.traverse((child) => {
6318
- if (child instanceof THREE30__namespace.Mesh) {
6565
+ if (child instanceof THREE37__namespace.Mesh) {
6319
6566
  child.castShadow = component.castShadow;
6320
6567
  child.receiveShadow = component.receiveShadow;
6321
6568
  }
@@ -6391,35 +6638,35 @@ function buildGeometry(component) {
6391
6638
  switch (component.meshKind) {
6392
6639
  case "cube": {
6393
6640
  const params = p;
6394
- return new THREE30__namespace.BoxGeometry(params.width, params.height, params.depth);
6641
+ return new THREE37__namespace.BoxGeometry(params.width, params.height, params.depth);
6395
6642
  }
6396
6643
  case "plane": {
6397
6644
  const params = p;
6398
- return new THREE30__namespace.PlaneGeometry(params.width, params.height);
6645
+ return new THREE37__namespace.PlaneGeometry(params.width, params.height);
6399
6646
  }
6400
6647
  case "sphere": {
6401
6648
  const params = p;
6402
- return new THREE30__namespace.SphereGeometry(params.radius, 24, 24);
6649
+ return new THREE37__namespace.SphereGeometry(params.radius, 24, 24);
6403
6650
  }
6404
6651
  case "cylinder": {
6405
6652
  const params = p;
6406
- return new THREE30__namespace.CylinderGeometry(params.radius, params.radius, params.height, 20);
6653
+ return new THREE37__namespace.CylinderGeometry(params.radius, params.radius, params.height, 20);
6407
6654
  }
6408
6655
  case "capsule": {
6409
6656
  const params = p;
6410
- return new THREE30__namespace.CapsuleGeometry(params.radius, params.height, 4, 8);
6657
+ return new THREE37__namespace.CapsuleGeometry(params.radius, params.height, 4, 8);
6411
6658
  }
6412
6659
  default:
6413
- return new THREE30__namespace.BoxGeometry(1, 1, 1);
6660
+ return new THREE37__namespace.BoxGeometry(1, 1, 1);
6414
6661
  }
6415
6662
  }
6416
6663
  __name(buildGeometry, "buildGeometry");
6417
- var _position = new THREE30__namespace.Vector3();
6418
- var _quaternion = new THREE30__namespace.Quaternion();
6419
- var _euler = new THREE30__namespace.Euler();
6420
- var _scale = new THREE30__namespace.Vector3();
6421
- var _matrix = new THREE30__namespace.Matrix4();
6422
- var _modelMatrix = new THREE30__namespace.Matrix4();
6664
+ var _position = new THREE37__namespace.Vector3();
6665
+ var _quaternion = new THREE37__namespace.Quaternion();
6666
+ var _euler = new THREE37__namespace.Euler();
6667
+ var _scale = new THREE37__namespace.Vector3();
6668
+ var _matrix = new THREE37__namespace.Matrix4();
6669
+ var _modelMatrix = new THREE37__namespace.Matrix4();
6423
6670
  function instanceTransformFromComponent(transform, out) {
6424
6671
  _position.set(transform.position.x, transform.position.y, transform.position.z);
6425
6672
  _euler.set(transform.rotation.x, transform.rotation.y, transform.rotation.z, "XYZ");
@@ -6501,9 +6748,9 @@ var _InstancedMeshSystem = class _InstancedMeshSystem {
6501
6748
  const instanceCount = comp.instanceEntityIds.length;
6502
6749
  const geometrySignature = comp.meshKind === "model" ? `model:${comp.modelPath}:${instanceCount}` : `${comp.meshKind}:${JSON.stringify(comp.meshParameters)}:${instanceCount}`;
6503
6750
  if (comp.instancedMesh === null) {
6504
- const tracked = this.trackedMeshes.get(entityId);
6505
- if (tracked) {
6506
- comp.instancedMesh = tracked;
6751
+ const tracked2 = this.trackedMeshes.get(entityId);
6752
+ if (tracked2) {
6753
+ comp.instancedMesh = tracked2;
6507
6754
  }
6508
6755
  }
6509
6756
  const pendingLoadSignature = this.loadingSignatures.get(entityId);
@@ -6572,7 +6819,7 @@ var _InstancedMeshSystem = class _InstancedMeshSystem {
6572
6819
  const sourceMaterial = Array.isArray(meshChild.material) ? meshChild.material.map((m) => m.clone()) : meshChild.material.clone();
6573
6820
  meshChild.updateWorldMatrix(true, false);
6574
6821
  const localMatrix = meshChild.matrixWorld.clone();
6575
- const instancedMesh2 = new THREE30__namespace.InstancedMesh(
6822
+ const instancedMesh2 = new THREE37__namespace.InstancedMesh(
6576
6823
  geometry2,
6577
6824
  Array.isArray(sourceMaterial) ? sourceMaterial : sourceMaterial,
6578
6825
  count2
@@ -6597,7 +6844,7 @@ var _InstancedMeshSystem = class _InstancedMeshSystem {
6597
6844
  this.loadingSignatures.delete(targetEntityId);
6598
6845
  return;
6599
6846
  }
6600
- const group = new THREE30__namespace.Group();
6847
+ const group = new THREE37__namespace.Group();
6601
6848
  for (const entry of entries) {
6602
6849
  group.add(entry.instancedMesh);
6603
6850
  }
@@ -6615,8 +6862,8 @@ var _InstancedMeshSystem = class _InstancedMeshSystem {
6615
6862
  }
6616
6863
  const count = instanceCount;
6617
6864
  const geometry = buildGeometry(comp);
6618
- const material = new THREE30__namespace.MeshStandardMaterial({ color: "#ffffff" });
6619
- const instancedMesh = new THREE30__namespace.InstancedMesh(geometry, material, count);
6865
+ const material = new THREE37__namespace.MeshStandardMaterial({ color: "#ffffff" });
6866
+ const instancedMesh = new THREE37__namespace.InstancedMesh(geometry, material, count);
6620
6867
  instancedMesh.castShadow = comp.castShadow;
6621
6868
  instancedMesh.receiveShadow = comp.receiveShadow;
6622
6869
  for (let i = 0; i < count; i++) {
@@ -6952,6 +7199,14 @@ function applyParentLinks(snapshot, idMap, objectManagementSystem, world) {
6952
7199
  __name(applyParentLinks, "applyParentLinks");
6953
7200
 
6954
7201
  // core/hydration/postCreateComponents.ts
7202
+ var OPTIONAL_UI_FIELDS = {
7203
+ pixelDensity: "number",
7204
+ icon: "string",
7205
+ iconPosition: "string",
7206
+ variant: "string",
7207
+ step: "number",
7208
+ readOnly: "boolean"
7209
+ };
6955
7210
  function remapEntityRefsInScriptData(data, idMap) {
6956
7211
  const result = {};
6957
7212
  for (const [key, value] of Object.entries(data)) {
@@ -7335,6 +7590,20 @@ function restorePostCreateComponents(world, objectManagementSystem, targetEntity
7335
7590
  const target = component;
7336
7591
  for (const [key, value] of Object.entries(source)) {
7337
7592
  const current = target[key];
7593
+ if (key === "style") {
7594
+ const style = normalizeUiStyle(value);
7595
+ if (style) target.style = style;
7596
+ continue;
7597
+ }
7598
+ if (key === "tokens") {
7599
+ const tokens = normalizeUiTokens(value);
7600
+ if (tokens) target.tokens = tokens;
7601
+ continue;
7602
+ }
7603
+ if (OPTIONAL_UI_FIELDS[key] === typeof value) {
7604
+ target[key] = value;
7605
+ continue;
7606
+ }
7338
7607
  if ((typeof value === "string" || typeof value === "number" || typeof value === "boolean") && typeof current === typeof value) {
7339
7608
  target[key] = value;
7340
7609
  }
@@ -8297,16 +8566,16 @@ function instantiateSubtree(world, objectManagementSystem, snapshot) {
8297
8566
  __name(instantiateSubtree, "instantiateSubtree");
8298
8567
 
8299
8568
  // core/prefab/types.ts
8300
- var isRecord = /* @__PURE__ */ __name((value) => value !== null && typeof value === "object" && !Array.isArray(value), "isRecord");
8569
+ var isRecord2 = /* @__PURE__ */ __name((value) => value !== null && typeof value === "object" && !Array.isArray(value), "isRecord");
8301
8570
  function parsePrefabAsset(json) {
8302
- if (!isRecord(json)) {
8571
+ if (!isRecord2(json)) {
8303
8572
  return null;
8304
8573
  }
8305
8574
  if (typeof json.version !== "number" || json.version < 1) {
8306
8575
  return null;
8307
8576
  }
8308
8577
  const snapshot = json.snapshot;
8309
- if (!isRecord(snapshot)) {
8578
+ if (!isRecord2(snapshot)) {
8310
8579
  return null;
8311
8580
  }
8312
8581
  const entities = snapshot.entities;
@@ -8318,7 +8587,7 @@ function parsePrefabAsset(json) {
8318
8587
  return null;
8319
8588
  }
8320
8589
  if (!components.every(
8321
- (block) => isRecord(block) && typeof block.component === "string" && Array.isArray(block.entries)
8590
+ (block) => isRecord2(block) && typeof block.component === "string" && Array.isArray(block.entries)
8322
8591
  )) {
8323
8592
  return null;
8324
8593
  }
@@ -8576,7 +8845,7 @@ var _Behaviour = class _Behaviour {
8576
8845
  if (!this.threeRef) {
8577
8846
  return null;
8578
8847
  }
8579
- const position = new THREE30__namespace.Vector3();
8848
+ const position = new THREE37__namespace.Vector3();
8580
8849
  this.threeRef.object.getWorldPosition(position);
8581
8850
  return position;
8582
8851
  }
@@ -8588,7 +8857,7 @@ var _Behaviour = class _Behaviour {
8588
8857
  if (!this.threeRef) {
8589
8858
  return null;
8590
8859
  }
8591
- const target = new THREE30__namespace.Vector3();
8860
+ const target = new THREE37__namespace.Vector3();
8592
8861
  this.threeRef.object.getWorldDirection(target);
8593
8862
  return target;
8594
8863
  }
@@ -8650,7 +8919,7 @@ var _Behaviour = class _Behaviour {
8650
8919
  if (!this.threeRef) {
8651
8920
  return;
8652
8921
  }
8653
- const rotation = new THREE30__namespace.Quaternion();
8922
+ const rotation = new THREE37__namespace.Quaternion();
8654
8923
  this.threeRef.object.getWorldQuaternion(rotation);
8655
8924
  rotation.slerp(targetRotation, alpha);
8656
8925
  this.transform.setRotation(rotation);
@@ -8667,7 +8936,7 @@ var _Behaviour = class _Behaviour {
8667
8936
  return;
8668
8937
  }
8669
8938
  const clone = this.threeRef.object.clone();
8670
- const rotation = new THREE30__namespace.Quaternion();
8939
+ const rotation = new THREE37__namespace.Quaternion();
8671
8940
  clone.lookAt(targetPosition);
8672
8941
  clone.getWorldQuaternion(rotation);
8673
8942
  this.transform.setRotation(rotation);
@@ -9113,8 +9382,8 @@ var _ScriptBehaviourSystem = class _ScriptBehaviourSystem {
9113
9382
  const retryDelay = options?.errorRetryDelay;
9114
9383
  this.errorRetryDelay = typeof retryDelay === "number" && Number.isFinite(retryDelay) ? Math.max(0, retryDelay) : DEFAULT_SCRIPT_ERROR_RETRY_DELAY;
9115
9384
  this.selectionCtx = selectionContext ?? null;
9116
- this.raycaster = new THREE30__namespace.Raycaster();
9117
- this.ndcPointer = new THREE30__namespace.Vector2();
9385
+ this.raycaster = new THREE37__namespace.Raycaster();
9386
+ this.ndcPointer = new THREE37__namespace.Vector2();
9118
9387
  if (this.selectionCtx) {
9119
9388
  this.selectionCtx.domElement.addEventListener("pointerdown", this.onPointerDown);
9120
9389
  this.selectionCtx.domElement.addEventListener("pointerup", this.onPointerUp);
@@ -9558,7 +9827,7 @@ function disposeBuiltObject(root) {
9558
9827
  for (const mat of materials) {
9559
9828
  if (mat.userData?.[SHARED_MODEL_RESOURCE_FLAG]) continue;
9560
9829
  for (const value of Object.values(mat)) {
9561
- if (value instanceof THREE30__namespace.Texture) value.dispose();
9830
+ if (value instanceof THREE37__namespace.Texture) value.dispose();
9562
9831
  }
9563
9832
  mat.dispose?.();
9564
9833
  }
@@ -10159,7 +10428,7 @@ var _AudioSystem = class _AudioSystem {
10159
10428
  if (!prev || prev.volume !== opts.volume) {
10160
10429
  sound.setVolume(opts.volume);
10161
10430
  }
10162
- if (opts.positional && sound instanceof THREE30__namespace.PositionalAudio) {
10431
+ if (opts.positional && sound instanceof THREE37__namespace.PositionalAudio) {
10163
10432
  if (!prev || prev.refDistance !== opts.refDistance) {
10164
10433
  sound.setRefDistance(opts.refDistance);
10165
10434
  }
@@ -10299,7 +10568,7 @@ var _VideoSystem = class _VideoSystem {
10299
10568
  spill: opts.spill
10300
10569
  });
10301
10570
  }
10302
- if (opts.positional && component.audioNode instanceof THREE30__namespace.PositionalAudio) {
10571
+ if (opts.positional && component.audioNode instanceof THREE37__namespace.PositionalAudio) {
10303
10572
  const posAudio = component.audioNode;
10304
10573
  if (!prev || prev.refDistance !== opts.refDistance) {
10305
10574
  posAudio.setRefDistance(opts.refDistance);
@@ -10339,8 +10608,8 @@ function blankState(source) {
10339
10608
  handedness: handednessOf(source),
10340
10609
  kind: kindOf(source),
10341
10610
  connected: true,
10342
- rayOrigin: new THREE30__namespace.Vector3(),
10343
- rayDirection: new THREE30__namespace.Vector3(0, 0, -1),
10611
+ rayOrigin: new THREE37__namespace.Vector3(),
10612
+ rayDirection: new THREE37__namespace.Vector3(0, 0, -1),
10344
10613
  gripPosition: null,
10345
10614
  gripQuaternion: null,
10346
10615
  select: false,
@@ -10366,7 +10635,7 @@ function readHandJoints(frame, hand, referenceSpace, rigMatrix, existing) {
10366
10635
  }
10367
10636
  if (spaces.length === 0) return null;
10368
10637
  const target = existing ?? {};
10369
- const matrix = new THREE30__namespace.Matrix4();
10638
+ const matrix = new THREE37__namespace.Matrix4();
10370
10639
  const buffer = spaces.length * 16 <= JOINT_MATRIX_BUFFER.length ? JOINT_MATRIX_BUFFER : new Float32Array(spaces.length * 16);
10371
10640
  const fillPoses = frame.fillPoses;
10372
10641
  if (typeof fillPoses === "function") {
@@ -10379,7 +10648,7 @@ function readHandJoints(frame, hand, referenceSpace, rigMatrix, existing) {
10379
10648
  if (filled) {
10380
10649
  for (let i = 0; i < names.length; i += 1) {
10381
10650
  matrix.fromArray(buffer, i * 16).premultiply(rigMatrix);
10382
- const vector = target[names[i]] ?? new THREE30__namespace.Vector3();
10651
+ const vector = target[names[i]] ?? new THREE37__namespace.Vector3();
10383
10652
  vector.setFromMatrixPosition(matrix);
10384
10653
  target[names[i]] = vector;
10385
10654
  }
@@ -10397,7 +10666,7 @@ function readHandJoints(frame, hand, referenceSpace, rigMatrix, existing) {
10397
10666
  continue;
10398
10667
  }
10399
10668
  matrix.fromArray(pose.transform.matrix).premultiply(rigMatrix);
10400
- const vector = target[names[i]] ?? new THREE30__namespace.Vector3();
10669
+ const vector = target[names[i]] ?? new THREE37__namespace.Vector3();
10401
10670
  vector.setFromMatrixPosition(matrix);
10402
10671
  target[names[i]] = vector;
10403
10672
  anyTracked = true;
@@ -10409,7 +10678,7 @@ var _XRInputSystem = class _XRInputSystem {
10409
10678
  constructor() {
10410
10679
  this.records = /* @__PURE__ */ new Map();
10411
10680
  /** Reference-space → world matrix, rebuilt each frame from the XR rig. */
10412
- this.referenceToWorld = new THREE30__namespace.Matrix4();
10681
+ this.referenceToWorld = new THREE37__namespace.Matrix4();
10413
10682
  this.session = null;
10414
10683
  this.onSelectStart = /* @__PURE__ */ __name((event) => this.mark(event, "pendingSelectStart"), "onSelectStart");
10415
10684
  this.onSelectEnd = /* @__PURE__ */ __name((event) => this.mark(event, "pendingSelectEnd"), "onSelectEnd");
@@ -10507,19 +10776,19 @@ var _XRInputSystem = class _XRInputSystem {
10507
10776
  if (frame && referenceSpace && source.targetRaySpace) {
10508
10777
  const pose = frame.getPose(source.targetRaySpace, referenceSpace);
10509
10778
  if (pose) {
10510
- const m = new THREE30__namespace.Matrix4().fromArray(pose.transform.matrix).premultiply(rigMatrix);
10779
+ const m = new THREE37__namespace.Matrix4().fromArray(pose.transform.matrix).premultiply(rigMatrix);
10511
10780
  state.rayOrigin.setFromMatrixPosition(m);
10512
10781
  state.rayDirection.set(0, 0, -1).applyQuaternion(
10513
- new THREE30__namespace.Quaternion().setFromRotationMatrix(m)
10782
+ new THREE37__namespace.Quaternion().setFromRotationMatrix(m)
10514
10783
  ).normalize();
10515
10784
  }
10516
10785
  }
10517
10786
  if (frame && referenceSpace && source.gripSpace) {
10518
10787
  const gripPose = frame.getPose(source.gripSpace, referenceSpace);
10519
10788
  if (gripPose) {
10520
- const m = new THREE30__namespace.Matrix4().fromArray(gripPose.transform.matrix).premultiply(rigMatrix);
10521
- state.gripPosition ?? (state.gripPosition = new THREE30__namespace.Vector3());
10522
- state.gripQuaternion ?? (state.gripQuaternion = new THREE30__namespace.Quaternion());
10789
+ const m = new THREE37__namespace.Matrix4().fromArray(gripPose.transform.matrix).premultiply(rigMatrix);
10790
+ state.gripPosition ?? (state.gripPosition = new THREE37__namespace.Vector3());
10791
+ state.gripQuaternion ?? (state.gripQuaternion = new THREE37__namespace.Quaternion());
10523
10792
  state.gripPosition.setFromMatrixPosition(m);
10524
10793
  state.gripQuaternion.setFromRotationMatrix(m);
10525
10794
  }
@@ -10621,6 +10890,51 @@ var _XRInputSystem = class _XRInputSystem {
10621
10890
  };
10622
10891
  __name(_XRInputSystem, "XRInputSystem");
10623
10892
  var XRInputSystem = _XRInputSystem;
10893
+
10894
+ // core/ui/uiWidgetTree.ts
10895
+ var UI_PART_KEY = "__uiPart";
10896
+ function applyPartColor(mesh, tag) {
10897
+ const material = mesh.material;
10898
+ material.color.copy(tag.baseColor).multiplyScalar(tag.premultiplied ? tag.tint * tag.opacity : tag.tint);
10899
+ material.opacity = tag.opacity;
10900
+ }
10901
+ __name(applyPartColor, "applyPartColor");
10902
+ function isUiWidget(world, entityId) {
10903
+ return !!world.getComponent(entityId, UIPanelComponent) || !!world.getComponent(entityId, UITextComponent) || !!world.getComponent(entityId, UIButtonComponent) || !!world.getComponent(entityId, UIToggleComponent) || !!world.getComponent(entityId, UISliderComponent);
10904
+ }
10905
+ __name(isUiWidget, "isUiWidget");
10906
+ function isShownInScene(world, entityId) {
10907
+ let object = world.getComponent(entityId, Object3DRef)?.object ?? null;
10908
+ while (object) {
10909
+ if (!object.visible) return false;
10910
+ object = object.parent;
10911
+ }
10912
+ return true;
10913
+ }
10914
+ __name(isShownInScene, "isShownInScene");
10915
+ function nearestWidgetAncestor(world, entityId) {
10916
+ const visited = /* @__PURE__ */ new Set([entityId]);
10917
+ let current = entityId;
10918
+ for (; ; ) {
10919
+ const parentId = world.getComponent(current, ParentId)?.value;
10920
+ if (typeof parentId !== "number" || visited.has(parentId)) return null;
10921
+ if (isUiWidget(world, parentId)) return parentId;
10922
+ visited.add(parentId);
10923
+ current = parentId;
10924
+ }
10925
+ }
10926
+ __name(nearestWidgetAncestor, "nearestWidgetAncestor");
10927
+ function widgetTreeRoot(world, entityId) {
10928
+ const visited = /* @__PURE__ */ new Set([entityId]);
10929
+ let root = entityId;
10930
+ for (; ; ) {
10931
+ const parent = nearestWidgetAncestor(world, root);
10932
+ if (parent === null || visited.has(parent)) return root;
10933
+ visited.add(parent);
10934
+ root = parent;
10935
+ }
10936
+ }
10937
+ __name(widgetTreeRoot, "widgetTreeRoot");
10624
10938
  var VELOCITY_SAMPLES = 5;
10625
10939
  var MAX_SAMPLE_AGE_SECONDS = 0.2;
10626
10940
  var MAX_PLAUSIBLE_SPEED = 20;
@@ -10647,15 +10961,15 @@ var _ThrowVelocityTracker = class _ThrowVelocityTracker {
10647
10961
  const fresh = this.samples.filter(
10648
10962
  (sample) => now - sample.time <= MAX_SAMPLE_AGE_SECONDS
10649
10963
  );
10650
- if (fresh.length < 2) return new THREE30__namespace.Vector3();
10651
- let best = new THREE30__namespace.Vector3();
10964
+ if (fresh.length < 2) return new THREE37__namespace.Vector3();
10965
+ let best = new THREE37__namespace.Vector3();
10652
10966
  let bestSpeed = 0;
10653
10967
  for (let i = 1; i < fresh.length; i += 1) {
10654
10968
  const previous = fresh[i - 1];
10655
10969
  const current = fresh[i];
10656
10970
  const dt = current.time - previous.time;
10657
10971
  if (dt <= 1e-5) continue;
10658
- const velocity = new THREE30__namespace.Vector3().subVectors(current.position, previous.position).divideScalar(dt);
10972
+ const velocity = new THREE37__namespace.Vector3().subVectors(current.position, previous.position).divideScalar(dt);
10659
10973
  const speed = velocity.length();
10660
10974
  if (speed > MAX_PLAUSIBLE_SPEED) continue;
10661
10975
  if (speed > bestSpeed) {
@@ -10672,15 +10986,15 @@ var _ThrowVelocityTracker = class _ThrowVelocityTracker {
10672
10986
  __name(_ThrowVelocityTracker, "ThrowVelocityTracker");
10673
10987
  var ThrowVelocityTracker = _ThrowVelocityTracker;
10674
10988
  function angularVelocityFrom(previous, current, deltaTime) {
10675
- if (deltaTime <= 1e-5) return new THREE30__namespace.Vector3();
10989
+ if (deltaTime <= 1e-5) return new THREE37__namespace.Vector3();
10676
10990
  const delta = current.clone().multiply(previous.clone().invert());
10677
10991
  if (delta.w < 0) {
10678
10992
  delta.set(-delta.x, -delta.y, -delta.z, -delta.w);
10679
10993
  }
10680
- const angle = 2 * Math.acos(THREE30__namespace.MathUtils.clamp(delta.w, -1, 1));
10994
+ const angle = 2 * Math.acos(THREE37__namespace.MathUtils.clamp(delta.w, -1, 1));
10681
10995
  const sinHalf = Math.sqrt(Math.max(0, 1 - delta.w * delta.w));
10682
- if (sinHalf < 1e-6 || angle < 1e-6) return new THREE30__namespace.Vector3();
10683
- return new THREE30__namespace.Vector3(delta.x, delta.y, delta.z).divideScalar(sinHalf).multiplyScalar(angle / deltaTime);
10996
+ if (sinHalf < 1e-6 || angle < 1e-6) return new THREE37__namespace.Vector3();
10997
+ return new THREE37__namespace.Vector3(delta.x, delta.y, delta.z).divideScalar(sinHalf).multiplyScalar(angle / deltaTime);
10684
10998
  }
10685
10999
  __name(angularVelocityFrom, "angularVelocityFrom");
10686
11000
  var MIN_HAND_SEPARATION = 0.06;
@@ -10688,7 +11002,7 @@ var MIN_ANCHOR_SEPARATION = 1e-3;
10688
11002
  var SCALE_DEADZONE = 0.05;
10689
11003
  function solveTwoHandTransform(primary, secondary, basis, options) {
10690
11004
  const separation = primary.position.distanceTo(secondary.position);
10691
- const anchorDelta = new THREE30__namespace.Vector3().subVectors(
11005
+ const anchorDelta = new THREE37__namespace.Vector3().subVectors(
10692
11006
  basis.secondaryLocal,
10693
11007
  basis.primaryLocal
10694
11008
  );
@@ -10712,8 +11026,8 @@ function solveTwoHandTransform(primary, secondary, basis, options) {
10712
11026
  } else {
10713
11027
  const axisLocal = anchorDelta.clone().normalize();
10714
11028
  const currentAxis = axisLocal.clone().applyQuaternion(base).normalize();
10715
- const desiredAxis = new THREE30__namespace.Vector3().subVectors(secondary.position, primary.position).normalize();
10716
- const correction = new THREE30__namespace.Quaternion().setFromUnitVectors(
11029
+ const desiredAxis = new THREE37__namespace.Vector3().subVectors(secondary.position, primary.position).normalize();
11030
+ const correction = new THREE37__namespace.Quaternion().setFromUnitVectors(
10717
11031
  currentAxis,
10718
11032
  desiredAxis
10719
11033
  );
@@ -10728,11 +11042,11 @@ __name(solveTwoHandTransform, "solveTwoHandTransform");
10728
11042
  // core/systems/XRInteractionSystem.ts
10729
11043
  var RAYLESS_HOLD_DISTANCE = 0.5;
10730
11044
  function quatFromEulerVec(r) {
10731
- return new THREE30__namespace.Quaternion().setFromEuler(new THREE30__namespace.Euler(r.x, r.y, r.z));
11045
+ return new THREE37__namespace.Quaternion().setFromEuler(new THREE37__namespace.Euler(r.x, r.y, r.z));
10732
11046
  }
10733
11047
  __name(quatFromEulerVec, "quatFromEulerVec");
10734
11048
  function writeEuler(target, q) {
10735
- const euler = new THREE30__namespace.Euler().setFromQuaternion(q);
11049
+ const euler = new THREE37__namespace.Euler().setFromQuaternion(q);
10736
11050
  target.x = euler.x;
10737
11051
  target.y = euler.y;
10738
11052
  target.z = euler.z;
@@ -10740,7 +11054,7 @@ function writeEuler(target, q) {
10740
11054
  __name(writeEuler, "writeEuler");
10741
11055
  var _XRInteractionSystem = class _XRInteractionSystem {
10742
11056
  constructor(inputSystem) {
10743
- this.raycaster = new THREE30__namespace.Raycaster();
11057
+ this.raycaster = new THREE37__namespace.Raycaster();
10744
11058
  this.grabs = /* @__PURE__ */ new Map();
10745
11059
  this.hoveredEntityId = null;
10746
11060
  /** Per-hand hover, with the hit distance so the ray can stop at the surface. */
@@ -10851,7 +11165,7 @@ var _XRInteractionSystem = class _XRInteractionSystem {
10851
11165
  const hand = this.handPosition(input);
10852
11166
  const handQuaternion = this.handQuaternion(input);
10853
11167
  const objectQuaternion = quatFromEulerVec(transform.rotation);
10854
- record.offset = new THREE30__namespace.Vector3(
11168
+ record.offset = new THREE37__namespace.Vector3(
10855
11169
  transform.position.x - hand.x,
10856
11170
  transform.position.y - hand.y,
10857
11171
  transform.position.z - hand.z
@@ -10883,7 +11197,7 @@ var _XRInteractionSystem = class _XRInteractionSystem {
10883
11197
  primary.record.twoHand = {
10884
11198
  primaryLocal,
10885
11199
  secondaryLocal,
10886
- initialScale: new THREE30__namespace.Vector3(
11200
+ initialScale: new THREE37__namespace.Vector3(
10887
11201
  transform.scale.x,
10888
11202
  transform.scale.y,
10889
11203
  transform.scale.z
@@ -10904,7 +11218,7 @@ var _XRInteractionSystem = class _XRInteractionSystem {
10904
11218
  /** World point → the object's UNSCALED local space. */
10905
11219
  objectLocalPoint(transform, worldPoint) {
10906
11220
  const local = worldPoint.clone().sub(
10907
- new THREE30__namespace.Vector3(
11221
+ new THREE37__namespace.Vector3(
10908
11222
  transform.position.x,
10909
11223
  transform.position.y,
10910
11224
  transform.position.z
@@ -10962,7 +11276,7 @@ var _XRInteractionSystem = class _XRInteractionSystem {
10962
11276
  );
10963
11277
  if (!interactable2) continue;
10964
11278
  if (interactable2.grabReach === "distance") continue;
10965
- const position = new THREE30__namespace.Vector3();
11279
+ const position = new THREE37__namespace.Vector3();
10966
11280
  entry.object.getWorldPosition(position);
10967
11281
  const distance = position.distanceTo(hand);
10968
11282
  if (distance <= interactable2.grabRadius && distance < nearestDistance) {
@@ -10994,6 +11308,7 @@ var _XRInteractionSystem = class _XRInteractionSystem {
10994
11308
  const interactable = world.getComponent(entityId, XRInteractableComponent);
10995
11309
  if (!interactable?.enabled) continue;
10996
11310
  if (!world.isEntityVisible(entityId)) continue;
11311
+ if (!isShownInScene(world, entityId)) continue;
10997
11312
  const object = world.getComponent(entityId, Object3DRef)?.object;
10998
11313
  if (object) {
10999
11314
  out.push({ entityId, object, pointable: interactable.pointable });
@@ -11034,13 +11349,13 @@ var _XRInteractionSystem = class _XRInteractionSystem {
11034
11349
  this.holdersOf(entityId).map((holder) => holder.record.attachPointId).filter((id) => id !== null)
11035
11350
  );
11036
11351
  const hand = this.handPosition(input);
11037
- const objectPosition = new THREE30__namespace.Vector3(
11352
+ const objectPosition = new THREE37__namespace.Vector3(
11038
11353
  transform.position.x,
11039
11354
  transform.position.y,
11040
11355
  transform.position.z
11041
11356
  );
11042
11357
  const objectQuaternion = quatFromEulerVec(transform.rotation);
11043
- const scale = new THREE30__namespace.Vector3(
11358
+ const scale = new THREE37__namespace.Vector3(
11044
11359
  transform.scale.x,
11045
11360
  transform.scale.y,
11046
11361
  transform.scale.z
@@ -11051,7 +11366,7 @@ var _XRInteractionSystem = class _XRInteractionSystem {
11051
11366
  if (taken.has(point.id)) continue;
11052
11367
  if (point.hand !== "any" && point.hand !== input.handedness) continue;
11053
11368
  if (point.role !== "any" && point.role !== role) continue;
11054
- const world3 = new THREE30__namespace.Vector3(
11369
+ const world3 = new THREE37__namespace.Vector3(
11055
11370
  point.position.x,
11056
11371
  point.position.y,
11057
11372
  point.position.z
@@ -11074,21 +11389,21 @@ var _XRInteractionSystem = class _XRInteractionSystem {
11074
11389
  let offset;
11075
11390
  let remoteDistance = 0;
11076
11391
  if (remote) {
11077
- const position = new THREE30__namespace.Vector3(
11392
+ const position = new THREE37__namespace.Vector3(
11078
11393
  transform.position.x,
11079
11394
  transform.position.y,
11080
11395
  transform.position.z
11081
11396
  );
11082
11397
  remoteDistance = position.distanceTo(input.rayOrigin);
11083
- offset = new THREE30__namespace.Vector3();
11398
+ offset = new THREE37__namespace.Vector3();
11084
11399
  } else if (viaDistance) {
11085
- offset = new THREE30__namespace.Vector3(
11400
+ offset = new THREE37__namespace.Vector3(
11086
11401
  interactable.grabOffsetPosition.x,
11087
11402
  interactable.grabOffsetPosition.y,
11088
11403
  interactable.grabOffsetPosition.z
11089
11404
  );
11090
11405
  } else {
11091
- offset = new THREE30__namespace.Vector3(
11406
+ offset = new THREE37__namespace.Vector3(
11092
11407
  transform.position.x - hand.x,
11093
11408
  transform.position.y - hand.y,
11094
11409
  transform.position.z - hand.z
@@ -11115,7 +11430,7 @@ var _XRInteractionSystem = class _XRInteractionSystem {
11115
11430
  entityId,
11116
11431
  role,
11117
11432
  attachPointId: attachPoint?.id ?? null,
11118
- attachLocal: attachPoint ? new THREE30__namespace.Vector3(
11433
+ attachLocal: attachPoint ? new THREE37__namespace.Vector3(
11119
11434
  attachPoint.position.x,
11120
11435
  attachPoint.position.y,
11121
11436
  attachPoint.position.z
@@ -11124,8 +11439,8 @@ var _XRInteractionSystem = class _XRInteractionSystem {
11124
11439
  handRotationOffset,
11125
11440
  twoHand: null,
11126
11441
  offset,
11127
- rotationOffset: new THREE30__namespace.Quaternion().setFromEuler(
11128
- new THREE30__namespace.Euler(
11442
+ rotationOffset: new THREE37__namespace.Quaternion().setFromEuler(
11443
+ new THREE37__namespace.Euler(
11129
11444
  interactable.grabOffsetRotation.x,
11130
11445
  interactable.grabOffsetRotation.y,
11131
11446
  interactable.grabOffsetRotation.z
@@ -11136,7 +11451,7 @@ var _XRInteractionSystem = class _XRInteractionSystem {
11136
11451
  restoreBodyType,
11137
11452
  tracker,
11138
11453
  lastHandQuaternion: handQuaternion.clone(),
11139
- angularVelocity: new THREE30__namespace.Vector3()
11454
+ angularVelocity: new THREE37__namespace.Vector3()
11140
11455
  });
11141
11456
  if (role === "primary") {
11142
11457
  this.dispatch(world, entityId, "onGrab", (b) => b.onGrab(input));
@@ -11209,14 +11524,14 @@ var _XRInteractionSystem = class _XRInteractionSystem {
11209
11524
  const handQuaternion = this.handQuaternion(input);
11210
11525
  const objectQuaternion = held.attachQuaternion ? handQuaternion.clone().multiply(held.rotationOffset).multiply(held.attachQuaternion.clone().invert()) : handQuaternion.clone().multiply(held.handRotationOffset);
11211
11526
  if (held.remote) {
11212
- const target = new THREE30__namespace.Vector3().copy(input.rayDirection).multiplyScalar(held.remoteDistance).add(input.rayOrigin);
11527
+ const target = new THREE37__namespace.Vector3().copy(input.rayDirection).multiplyScalar(held.remoteDistance).add(input.rayOrigin);
11213
11528
  transform.position.x = target.x;
11214
11529
  transform.position.y = target.y;
11215
11530
  transform.position.z = target.z;
11216
11531
  writeEuler(transform.rotation, objectQuaternion);
11217
11532
  } else if (held.attachLocal) {
11218
11533
  const anchor = held.attachLocal.clone().multiply(
11219
- new THREE30__namespace.Vector3(
11534
+ new THREE37__namespace.Vector3(
11220
11535
  transform.scale.x,
11221
11536
  transform.scale.y,
11222
11537
  transform.scale.z
@@ -11234,7 +11549,7 @@ var _XRInteractionSystem = class _XRInteractionSystem {
11234
11549
  writeEuler(transform.rotation, objectQuaternion);
11235
11550
  }
11236
11551
  held.tracker.record(
11237
- held.remote ? new THREE30__namespace.Vector3(
11552
+ held.remote ? new THREE37__namespace.Vector3(
11238
11553
  transform.position.x,
11239
11554
  transform.position.y,
11240
11555
  transform.position.z
@@ -11309,7 +11624,7 @@ var _XRInteractionSystem = class _XRInteractionSystem {
11309
11624
  */
11310
11625
  handPosition(input) {
11311
11626
  if (input.gripPosition) return input.gripPosition;
11312
- return new THREE30__namespace.Vector3().copy(input.rayDirection).multiplyScalar(RAYLESS_HOLD_DISTANCE).add(input.rayOrigin);
11627
+ return new THREE37__namespace.Vector3().copy(input.rayDirection).multiplyScalar(RAYLESS_HOLD_DISTANCE).add(input.rayOrigin);
11313
11628
  }
11314
11629
  /**
11315
11630
  * Hand orientation. Grip-less sources (gaze / transient-pointer) have none,
@@ -11317,8 +11632,8 @@ var _XRInteractionSystem = class _XRInteractionSystem {
11317
11632
  */
11318
11633
  handQuaternion(input) {
11319
11634
  if (input.gripQuaternion) return input.gripQuaternion.clone();
11320
- return new THREE30__namespace.Quaternion().setFromUnitVectors(
11321
- new THREE30__namespace.Vector3(0, 0, -1),
11635
+ return new THREE37__namespace.Quaternion().setFromUnitVectors(
11636
+ new THREE37__namespace.Vector3(0, 0, -1),
11322
11637
  input.rayDirection.clone().normalize()
11323
11638
  );
11324
11639
  }
@@ -11455,7 +11770,7 @@ function sampleTeleportArc(origin, direction, out = [], options) {
11455
11770
  for (let i = 0; i < steps; i += 1) {
11456
11771
  const t = i / (steps - 1) * maxTime;
11457
11772
  out.push(
11458
- new THREE30__namespace.Vector3(
11773
+ new THREE37__namespace.Vector3(
11459
11774
  origin.x + velocity.x * t,
11460
11775
  origin.y + velocity.y * t - 0.5 * gravity * t * t,
11461
11776
  origin.z + velocity.z * t
@@ -11503,11 +11818,11 @@ __name(_TeleportAimGate, "TeleportAimGate");
11503
11818
  var TeleportAimGate = _TeleportAimGate;
11504
11819
 
11505
11820
  // core/systems/XRLocomotionSystem.ts
11506
- var UP = new THREE30__namespace.Vector3(0, 1, 0);
11821
+ var UP = new THREE37__namespace.Vector3(0, 1, 0);
11507
11822
  var AXIS_DEADZONE = 0.5;
11508
11823
  var _XRLocomotionSystem = class _XRLocomotionSystem {
11509
11824
  constructor(inputSystem) {
11510
- this.raycaster = new THREE30__namespace.Raycaster();
11825
+ this.raycaster = new THREE37__namespace.Raycaster();
11511
11826
  /** Per-source latch so one stick push yields exactly one snap turn. */
11512
11827
  this.turnLatched = /* @__PURE__ */ new WeakSet();
11513
11828
  /** Reused arc buffer — aiming runs every frame. */
@@ -11633,7 +11948,7 @@ var _XRLocomotionSystem = class _XRLocomotionSystem {
11633
11948
  }
11634
11949
  if (this.turnLatched.has(source)) continue;
11635
11950
  this.turnLatched.add(source);
11636
- transform.rotation.y -= Math.sign(x) * THREE30__namespace.MathUtils.degToRad(rig.snapTurnDegrees);
11951
+ transform.rotation.y -= Math.sign(x) * THREE37__namespace.MathUtils.degToRad(rig.snapTurnDegrees);
11637
11952
  }
11638
11953
  }
11639
11954
  applySmoothMove(world, rig, transform, inputs, deltaTime) {
@@ -11643,12 +11958,12 @@ var _XRLocomotionSystem = class _XRLocomotionSystem {
11643
11958
  const y = input.axes[1] ?? 0;
11644
11959
  if (Math.abs(x) < 0.15 && Math.abs(y) < 0.15) continue;
11645
11960
  const camera = world.getCamera();
11646
- const forward = new THREE30__namespace.Vector3();
11961
+ const forward = new THREE37__namespace.Vector3();
11647
11962
  camera.getWorldDirection(forward);
11648
11963
  forward.y = 0;
11649
11964
  if (forward.lengthSq() < 1e-6) continue;
11650
11965
  forward.normalize();
11651
- const right = new THREE30__namespace.Vector3().crossVectors(forward, UP).normalize();
11966
+ const right = new THREE37__namespace.Vector3().crossVectors(forward, UP).normalize();
11652
11967
  const step = rig.moveSpeed * Math.max(0, deltaTime);
11653
11968
  transform.position.x += (forward.x * -y + right.x * x) * step;
11654
11969
  transform.position.z += (forward.z * -y + right.z * x) * step;
@@ -11729,7 +12044,7 @@ var _XRLocomotionSystem = class _XRLocomotionSystem {
11729
12044
  for (let i = 1; i < points.length; i += 1) {
11730
12045
  const from = points[i - 1];
11731
12046
  const to = points[i];
11732
- const delta = new THREE30__namespace.Vector3().subVectors(to, from);
12047
+ const delta = new THREE37__namespace.Vector3().subVectors(to, from);
11733
12048
  const distance = delta.length();
11734
12049
  if (distance < 1e-6) continue;
11735
12050
  this.raycaster.set(from, delta.divideScalar(distance));
@@ -12002,12 +12317,12 @@ function planeColliderFromPolygon(polygon) {
12002
12317
  }
12003
12318
  __name(planeColliderFromPolygon, "planeColliderFromPolygon");
12004
12319
  function writeXrMeshGeometry(vertices, indices, existing) {
12005
- const geometry = existing ?? new THREE30__namespace.BufferGeometry();
12320
+ const geometry = existing ?? new THREE37__namespace.BufferGeometry();
12006
12321
  geometry.setAttribute(
12007
12322
  "position",
12008
- new THREE30__namespace.BufferAttribute(vertices.slice(), 3)
12323
+ new THREE37__namespace.BufferAttribute(vertices.slice(), 3)
12009
12324
  );
12010
- geometry.setIndex(new THREE30__namespace.BufferAttribute(indices.slice(), 1));
12325
+ geometry.setIndex(new THREE37__namespace.BufferAttribute(indices.slice(), 1));
12011
12326
  geometry.deleteAttribute("normal");
12012
12327
  geometry.computeBoundingSphere();
12013
12328
  geometry.computeVertexNormals();
@@ -12015,14 +12330,14 @@ function writeXrMeshGeometry(vertices, indices, existing) {
12015
12330
  }
12016
12331
  __name(writeXrMeshGeometry, "writeXrMeshGeometry");
12017
12332
  function decomposeXrPose(matrixElements, target) {
12018
- const matrix = new THREE30__namespace.Matrix4().fromArray(
12333
+ const matrix = new THREE37__namespace.Matrix4().fromArray(
12019
12334
  matrixElements
12020
12335
  );
12021
- const position = new THREE30__namespace.Vector3();
12022
- const quaternion = new THREE30__namespace.Quaternion();
12023
- const scale = new THREE30__namespace.Vector3();
12336
+ const position = new THREE37__namespace.Vector3();
12337
+ const quaternion = new THREE37__namespace.Quaternion();
12338
+ const scale = new THREE37__namespace.Vector3();
12024
12339
  matrix.decompose(position, quaternion, scale);
12025
- const euler = new THREE30__namespace.Euler().setFromQuaternion(quaternion, "XYZ");
12340
+ const euler = new THREE37__namespace.Euler().setFromQuaternion(quaternion, "XYZ");
12026
12341
  target.position.x = position.x;
12027
12342
  target.position.y = position.y;
12028
12343
  target.position.z = position.z;
@@ -12184,8 +12499,8 @@ var _XREnvironmentSystem = class _XREnvironmentSystem {
12184
12499
  this.viewerPos = null;
12185
12500
  /** The ONE global room mesh currently allowed to spawn. */
12186
12501
  this.electedGlobalMesh = null;
12187
- this.tmpViewerMatrix = new THREE30__namespace.Matrix4();
12188
- this.tmpViewPos = new THREE30__namespace.Vector3();
12502
+ this.tmpViewerMatrix = new THREE37__namespace.Matrix4();
12503
+ this.tmpViewPos = new THREE37__namespace.Vector3();
12189
12504
  }
12190
12505
  update(world, deltaTime = 0) {
12191
12506
  const presenting = world.isXrPresenting();
@@ -12544,7 +12859,7 @@ var _XREnvironmentSystem = class _XREnvironmentSystem {
12544
12859
  const geometry = writeXrMeshGeometry(mesh.vertices, mesh.indices);
12545
12860
  const label = mesh.semanticLabel ?? null;
12546
12861
  const isGlobalMesh = label === "global mesh";
12547
- const material = new THREE30__namespace.MeshBasicMaterial({
12862
+ const material = new THREE37__namespace.MeshBasicMaterial({
12548
12863
  color: debugColorFor("mesh", label),
12549
12864
  wireframe: true,
12550
12865
  // The global scan is a coarse triangle soup of the whole room; at full
@@ -12565,8 +12880,8 @@ var _XREnvironmentSystem = class _XREnvironmentSystem {
12565
12880
  depthTest: true,
12566
12881
  depthWrite: false
12567
12882
  });
12568
- const root = new THREE30__namespace.Group();
12569
- const child = new THREE30__namespace.Mesh(geometry, material);
12883
+ const root = new THREE37__namespace.Group();
12884
+ const child = new THREE37__namespace.Mesh(geometry, material);
12570
12885
  child.visible = this.debugVisible;
12571
12886
  child.renderOrder = DEBUG_RENDER_ORDER;
12572
12887
  root.add(child);
@@ -12592,21 +12907,21 @@ var _XREnvironmentSystem = class _XREnvironmentSystem {
12592
12907
  }
12593
12908
  /** A thin box so a detected plane can be SEEN, not just collided with. */
12594
12909
  attachPlaneDebug(world, entry, collider, semanticLabel) {
12595
- const box = new THREE30__namespace.BoxGeometry(
12910
+ const box = new THREE37__namespace.BoxGeometry(
12596
12911
  collider.size.x,
12597
12912
  collider.size.y,
12598
12913
  collider.size.z
12599
12914
  );
12600
- const geometry = new THREE30__namespace.EdgesGeometry(box);
12915
+ const geometry = new THREE37__namespace.EdgesGeometry(box);
12601
12916
  box.dispose();
12602
- const material = new THREE30__namespace.LineBasicMaterial({
12917
+ const material = new THREE37__namespace.LineBasicMaterial({
12603
12918
  color: debugColorFor("plane", semanticLabel),
12604
12919
  // Same depth policy as the mesh overlay — see createMeshEntity.
12605
12920
  depthTest: true,
12606
12921
  depthWrite: false
12607
12922
  });
12608
- const root = new THREE30__namespace.Group();
12609
- const child = new THREE30__namespace.LineSegments(geometry, material);
12923
+ const root = new THREE37__namespace.Group();
12924
+ const child = new THREE37__namespace.LineSegments(geometry, material);
12610
12925
  child.renderOrder = DEBUG_RENDER_ORDER;
12611
12926
  child.position.set(collider.offset.x, collider.offset.y, collider.offset.z);
12612
12927
  child.visible = this.debugVisible;
@@ -12682,6 +12997,14 @@ function normalizeOrientation(value) {
12682
12997
  return value === "horizontal" || value === "vertical" ? value : null;
12683
12998
  }
12684
12999
  __name(normalizeOrientation, "normalizeOrientation");
13000
+ var isDisplayOnlySlider = /* @__PURE__ */ __name((slider) => slider.readOnly === true || slider.variant !== void 0 && slider.variant !== "slider", "isDisplayOnlySlider");
13001
+ function snapToStep(value, slider) {
13002
+ const { step, min, max } = slider;
13003
+ if (!step || step <= 0) return value;
13004
+ const snapped = min + Math.round((value - min) / step) * step;
13005
+ return Math.min(max, Math.max(min, snapped));
13006
+ }
13007
+ __name(snapToStep, "snapToStep");
12685
13008
  var MOUSE_CLICK_TRAVEL_PX = 6;
12686
13009
  var PRESS_TINT_MIN_SECONDS = 0.1;
12687
13010
  var PRESS_TINT = 0.72;
@@ -12695,11 +13018,12 @@ var _UIInteractionSystem = class _UIInteractionSystem {
12695
13018
  constructor(interaction, options) {
12696
13019
  this.xrPresses = /* @__PURE__ */ new Map();
12697
13020
  this.mousePress = null;
12698
- this.mouseHover = null;
13021
+ /** What each pointer is on. Two hands can rest on one widget. */
13022
+ this.hovers = /* @__PURE__ */ new Map();
12699
13023
  /** entityId → seconds of pressed-tint left to display. */
12700
13024
  this.tintFloors = /* @__PURE__ */ new Map();
12701
13025
  this.tinted = /* @__PURE__ */ new Set();
12702
- this.raycaster = new THREE30__namespace.Raycaster();
13026
+ this.raycaster = new THREE37__namespace.Raycaster();
12703
13027
  this.interaction = interaction;
12704
13028
  this.inputSystem = options?.inputSystem ?? null;
12705
13029
  interaction?.registerBusyContributor(
@@ -12709,8 +13033,10 @@ var _UIInteractionSystem = class _UIInteractionSystem {
12709
13033
  update(world, deltaTime = 0) {
12710
13034
  if (world.isEditorMode()) return;
12711
13035
  if (world.isXrPresenting()) {
13036
+ this.setHover(world, "mouse", null, null);
12712
13037
  this.updateXr(world);
12713
13038
  } else {
13039
+ this.dropGoneXrHovers(world, []);
12714
13040
  this.updateMouse(world);
12715
13041
  }
12716
13042
  this.updateTints(world, deltaTime);
@@ -12724,6 +13050,7 @@ var _UIInteractionSystem = class _UIInteractionSystem {
12724
13050
  for (const input of inputs) {
12725
13051
  const hover = interaction.getHoverForInput(input);
12726
13052
  const hovered = hover ? this.widgetKind(world, hover.entityId) : null;
13053
+ this.setHover(world, input, hover && hovered ? hover.entityId : null, input);
12727
13054
  if (input.selectStart && hover && hovered) {
12728
13055
  this.xrPresses.set(input, {
12729
13056
  entityId: hover.entityId,
@@ -12752,6 +13079,15 @@ var _UIInteractionSystem = class _UIInteractionSystem {
12752
13079
  for (const [input] of this.xrPresses) {
12753
13080
  if (!inputs.includes(input)) this.xrPresses.delete(input);
12754
13081
  }
13082
+ this.dropGoneXrHovers(world, inputs);
13083
+ }
13084
+ /** A transient source leaves with no select edge to clear its hover. */
13085
+ dropGoneXrHovers(world, inputs) {
13086
+ for (const source of [...this.hovers.keys()]) {
13087
+ if (source !== "mouse" && !inputs.includes(source)) {
13088
+ this.setHover(world, source, null, source);
13089
+ }
13090
+ }
12755
13091
  }
12756
13092
  // ── Mouse path (flat preview) ──────────────────────────────────────────────
12757
13093
  updateMouse(world) {
@@ -12759,22 +13095,7 @@ var _UIInteractionSystem = class _UIInteractionSystem {
12759
13095
  if (!input) return;
12760
13096
  const hit = this.raycastMouse(world);
12761
13097
  const hovered = hit && this.widgetKind(world, hit) ? hit : null;
12762
- if (hovered !== this.mouseHover) {
12763
- if (this.mouseHover !== null) {
12764
- this.setTint(world, this.mouseHover, 1);
12765
- this.dispatchBubbling(
12766
- world,
12767
- this.mouseHover,
12768
- "onUiHoverEnd",
12769
- (b, e) => b.onUiHoverEnd(e)
12770
- );
12771
- }
12772
- if (hovered !== null) {
12773
- this.setTint(world, hovered, HOVER_TINT);
12774
- this.dispatchBubbling(world, hovered, "onUiHoverStart", (b, e) => b.onUiHoverStart(e));
12775
- }
12776
- this.mouseHover = hovered;
12777
- }
13098
+ this.setHover(world, "mouse", hovered, null);
12778
13099
  if (input.getMouseDown("left") && hovered) {
12779
13100
  const kind = this.widgetKind(world, hovered);
12780
13101
  if (kind) {
@@ -12782,12 +13103,12 @@ var _UIInteractionSystem = class _UIInteractionSystem {
12782
13103
  this.beginPressTint(world, hovered);
12783
13104
  }
12784
13105
  }
12785
- if (this.mousePress) {
13106
+ if (this.mousePress && !input.getMouseDown("left")) {
12786
13107
  const delta = input.getMouseDelta();
12787
13108
  this.mousePress.travelPx += Math.hypot(delta.x, delta.y);
12788
- if (this.mousePress.kind === "slider") {
12789
- this.dragSliderMouse(world, this.mousePress.entityId);
12790
- }
13109
+ }
13110
+ if (this.mousePress?.kind === "slider") {
13111
+ this.dragSliderMouse(world, this.mousePress.entityId);
12791
13112
  }
12792
13113
  if (input.getMouseUp("left") && this.mousePress) {
12793
13114
  const press = this.mousePress;
@@ -12816,15 +13137,24 @@ var _UIInteractionSystem = class _UIInteractionSystem {
12816
13137
  ndcY = -((pos.y - rect.top) / Math.max(1, rect.height) * 2 - 1);
12817
13138
  if (ndcX < -1 || ndcX > 1 || ndcY < -1 || ndcY > 1) return null;
12818
13139
  }
12819
- this.raycaster.setFromCamera(new THREE30__namespace.Vector2(ndcX, ndcY), world.getCamera());
13140
+ this.raycaster.setFromCamera(new THREE37__namespace.Vector2(ndcX, ndcY), world.getCamera());
12820
13141
  const hits = world.raycast(
12821
13142
  this.raycaster.ray.origin,
12822
13143
  this.raycaster.ray.direction
12823
13144
  );
13145
+ let surfaceTree = null;
12824
13146
  for (const hit of hits) {
12825
- if (typeof hit.entityId !== "number") continue;
12826
- if (this.widgetKind(world, hit.entityId)) return hit.entityId;
12827
- return null;
13147
+ const entityId = hit.entityId;
13148
+ if (typeof entityId !== "number") continue;
13149
+ if (!isShownInScene(world, entityId)) continue;
13150
+ if (this.widgetKind(world, entityId)) {
13151
+ const sameSurface = surfaceTree === null || widgetTreeRoot(world, entityId) === surfaceTree;
13152
+ return sameSurface ? entityId : null;
13153
+ }
13154
+ if (!isUiWidget(world, entityId)) return null;
13155
+ const tree = widgetTreeRoot(world, entityId);
13156
+ if (surfaceTree !== null && tree !== surfaceTree) return null;
13157
+ surfaceTree = tree;
12828
13158
  }
12829
13159
  return null;
12830
13160
  }
@@ -12832,7 +13162,8 @@ var _UIInteractionSystem = class _UIInteractionSystem {
12832
13162
  widgetKind(world, entityId) {
12833
13163
  if (world.getComponent(entityId, UIButtonComponent)) return "button";
12834
13164
  if (world.getComponent(entityId, UIToggleComponent)) return "toggle";
12835
- if (world.getComponent(entityId, UISliderComponent)) return "slider";
13165
+ const slider = world.getComponent(entityId, UISliderComponent);
13166
+ if (slider && !isDisplayOnlySlider(slider)) return "slider";
12836
13167
  return null;
12837
13168
  }
12838
13169
  fire(world, press, input) {
@@ -12883,7 +13214,10 @@ var _UIInteractionSystem = class _UIInteractionSystem {
12883
13214
  const inset = slider.height / 2;
12884
13215
  const usable = Math.max(1e-3, slider.width - inset * 2);
12885
13216
  const t = Math.min(1, Math.max(0, local.x / usable + 0.5));
12886
- const next = slider.min + t * (slider.max - slider.min);
13217
+ const next = snapToStep(
13218
+ slider.min + t * (slider.max - slider.min),
13219
+ slider
13220
+ );
12887
13221
  if (Math.abs(next - slider.value) < 1e-4) return;
12888
13222
  slider.value = next;
12889
13223
  this.dispatchBubbling(
@@ -12961,6 +13295,43 @@ var _UIInteractionSystem = class _UIInteractionSystem {
12961
13295
  const name = world.getComponent(entityId, Name)?.value;
12962
13296
  return typeof name === "string" ? name : String(entityId);
12963
13297
  }
13298
+ // ── Hover ──────────────────────────────────────────────────────────────────
13299
+ setHover(world, source, next, input) {
13300
+ const previous = this.hovers.get(source) ?? null;
13301
+ if (previous === next) return;
13302
+ if (next === null) this.hovers.delete(source);
13303
+ else this.hovers.set(source, next);
13304
+ if (previous !== null && !this.isHovered(previous)) {
13305
+ if (!this.tintFloors.has(previous)) this.setTint(world, previous, 1);
13306
+ this.dispatchBubbling(
13307
+ world,
13308
+ previous,
13309
+ "onUiHoverEnd",
13310
+ (b, e) => b.onUiHoverEnd(e),
13311
+ input
13312
+ );
13313
+ }
13314
+ if (next !== null && this.hoverCount(next) === 1) {
13315
+ if (!this.tintFloors.has(next)) this.setTint(world, next, HOVER_TINT);
13316
+ this.dispatchBubbling(
13317
+ world,
13318
+ next,
13319
+ "onUiHoverStart",
13320
+ (b, e) => b.onUiHoverStart(e),
13321
+ input
13322
+ );
13323
+ }
13324
+ }
13325
+ hoverCount(entityId) {
13326
+ let count = 0;
13327
+ for (const hovered of this.hovers.values()) {
13328
+ if (hovered === entityId) count += 1;
13329
+ }
13330
+ return count;
13331
+ }
13332
+ isHovered(entityId) {
13333
+ return this.hoverCount(entityId) > 0;
13334
+ }
12964
13335
  // ── Pressed/hover tint ─────────────────────────────────────────────────────
12965
13336
  beginPressTint(world, entityId) {
12966
13337
  this.setTint(world, entityId, PRESS_TINT);
@@ -12978,11 +13349,7 @@ var _UIInteractionSystem = class _UIInteractionSystem {
12978
13349
  if (stillHeld) continue;
12979
13350
  if (next <= 0) {
12980
13351
  this.tintFloors.delete(entityId);
12981
- this.setTint(
12982
- world,
12983
- entityId,
12984
- this.mouseHover === entityId ? HOVER_TINT : 1
12985
- );
13352
+ this.setTint(world, entityId, this.isHovered(entityId) ? HOVER_TINT : 1);
12986
13353
  } else {
12987
13354
  this.tintFloors.set(entityId, next);
12988
13355
  }
@@ -12993,14 +13360,101 @@ var _UIInteractionSystem = class _UIInteractionSystem {
12993
13360
  if (!object) return;
12994
13361
  if (scalar === 1) this.tinted.delete(entityId);
12995
13362
  else this.tinted.add(entityId);
12996
- object.traverse((child) => {
12997
- const material = child.material;
12998
- if (material?.color) material.color.setScalar(scalar);
12999
- });
13363
+ for (const child of object.children) {
13364
+ const tag = child.userData[UI_PART_KEY];
13365
+ if (!tag) continue;
13366
+ tag.tint = scalar;
13367
+ applyPartColor(child, tag);
13368
+ }
13000
13369
  }
13001
13370
  };
13002
13371
  __name(_UIInteractionSystem, "UIInteractionSystem");
13003
13372
  var UIInteractionSystem = _UIInteractionSystem;
13373
+ var fonts = /* @__PURE__ */ new Map();
13374
+ var images = /* @__PURE__ */ new Map();
13375
+ function hash(text) {
13376
+ let value = 5381;
13377
+ for (let i = 0; i < text.length; i += 1) {
13378
+ value = (value * 33 ^ text.charCodeAt(i)) >>> 0;
13379
+ }
13380
+ return value.toString(36);
13381
+ }
13382
+ __name(hash, "hash");
13383
+ function tracked(url, work) {
13384
+ const manager = THREE37__namespace.DefaultLoadingManager;
13385
+ manager.itemStart(url);
13386
+ return work.catch((error) => {
13387
+ manager.itemError(url);
13388
+ throw error;
13389
+ }).finally(() => manager.itemEnd(url));
13390
+ }
13391
+ __name(tracked, "tracked");
13392
+ function uiFontFamily(src) {
13393
+ if (typeof FontFace === "undefined" || typeof document === "undefined") {
13394
+ return null;
13395
+ }
13396
+ const known = fonts.get(src);
13397
+ if (known) return known.value;
13398
+ const entry = { value: null, settled: false };
13399
+ fonts.set(src, entry);
13400
+ const url = resolvePrefabAssetUrl(src);
13401
+ const family = `relu-ui-${hash(url)}`;
13402
+ tracked(
13403
+ url,
13404
+ (async () => {
13405
+ const response = await fetch(url);
13406
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
13407
+ const buffer = await response.arrayBuffer();
13408
+ const face = new FontFace(family, buffer, { weight: "100 900" });
13409
+ await face.load();
13410
+ document.fonts.add(face);
13411
+ })()
13412
+ ).then(() => {
13413
+ entry.value = family;
13414
+ }).catch((error) => {
13415
+ console.warn(`[spatial-ecs] Couldn't load UI font ${src}.`, error);
13416
+ }).finally(() => {
13417
+ entry.settled = true;
13418
+ });
13419
+ return null;
13420
+ }
13421
+ __name(uiFontFamily, "uiFontFamily");
13422
+ function uiImage(src) {
13423
+ if (typeof Image === "undefined" || typeof fetch === "undefined") {
13424
+ return null;
13425
+ }
13426
+ const known = images.get(src);
13427
+ if (known) return known.value;
13428
+ const entry = { value: null, settled: false };
13429
+ images.set(src, entry);
13430
+ const url = resolvePrefabAssetUrl(src);
13431
+ tracked(
13432
+ url,
13433
+ (async () => {
13434
+ const response = await fetch(url);
13435
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
13436
+ const blob = await response.blob();
13437
+ const typed = /\.svg(\?|#|$)/i.test(url) ? new Blob([blob], { type: "image/svg+xml" }) : blob;
13438
+ const image = new Image();
13439
+ const objectUrl = URL.createObjectURL(typed);
13440
+ try {
13441
+ image.src = objectUrl;
13442
+ await image.decode();
13443
+ return image;
13444
+ } finally {
13445
+ URL.revokeObjectURL(objectUrl);
13446
+ }
13447
+ })()
13448
+ ).then((image) => {
13449
+ entry.value = image;
13450
+ }).catch((error) => {
13451
+ console.warn(`[spatial-ecs] Couldn't load UI image ${src}.`, error);
13452
+ }).finally(() => {
13453
+ entry.settled = true;
13454
+ });
13455
+ return null;
13456
+ }
13457
+ __name(uiImage, "uiImage");
13004
13458
 
13005
13459
  // core/ui/uiCanvas.ts
13006
13460
  var DEFAULT_PIXELS_PER_METER = 1024;
@@ -13073,18 +13527,21 @@ function clampLines(measure, lines, maxLines, maxWidthPx) {
13073
13527
  }
13074
13528
  __name(clampLines, "clampLines");
13075
13529
  var UI_FONT_STACK = '-apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif';
13076
- function tracePath(ctx, x, y, w, h, r) {
13077
- const radius = Math.max(0, Math.min(r, w / 2, h / 2));
13530
+ var NO_ASSETS = { image: /* @__PURE__ */ __name(() => null, "image") };
13531
+ function tracePath(ctx, x, y, w, h, radii) {
13532
+ const limit = Math.min(w / 2, h / 2);
13533
+ const fit = /* @__PURE__ */ __name((r) => Math.max(0, Math.min(r, limit)), "fit");
13534
+ const [tl, tr, br, bl] = (Array.isArray(radii) ? radii : [radii, radii, radii, radii]).map(fit);
13078
13535
  ctx.beginPath();
13079
13536
  if (typeof ctx.roundRect === "function") {
13080
- ctx.roundRect(x, y, w, h, radius);
13537
+ ctx.roundRect(x, y, w, h, [tl, tr, br, bl]);
13081
13538
  return;
13082
13539
  }
13083
- ctx.moveTo(x + radius, y);
13084
- ctx.arcTo(x + w, y, x + w, y + h, radius);
13085
- ctx.arcTo(x + w, y + h, x, y + h, radius);
13086
- ctx.arcTo(x, y + h, x, y, radius);
13087
- ctx.arcTo(x, y, x + w, y, radius);
13540
+ ctx.moveTo(x + tl, y);
13541
+ ctx.arcTo(x + w, y, x + w, y + h, tr);
13542
+ ctx.arcTo(x + w, y + h, x, y + h, br);
13543
+ ctx.arcTo(x, y + h, x, y, bl);
13544
+ ctx.arcTo(x, y, x + w, y, tl);
13088
13545
  ctx.closePath();
13089
13546
  }
13090
13547
  __name(tracePath, "tracePath");
@@ -13104,108 +13561,244 @@ function withOpacity(ctx, color, opacity) {
13104
13561
  return parsed;
13105
13562
  }
13106
13563
  __name(withOpacity, "withOpacity");
13107
- function drawChrome(ctx, pxWidth, pxHeight, density, style) {
13108
- ctx.clearRect(0, 0, pxWidth, pxHeight);
13109
- const border = Math.max(0, style.borderWidthMeters * density);
13110
- const radius = style.cornerRadiusMeters * density;
13111
- tracePath(ctx, 0, 0, pxWidth, pxHeight, radius);
13112
- ctx.fillStyle = withOpacity(
13113
- ctx,
13114
- style.backgroundColor,
13115
- Math.max(0, Math.min(1, style.backgroundOpacity))
13116
- );
13564
+ function fillStyleFor(ctx, paint, box, opacity) {
13565
+ if (paint.kind === "solid") return withOpacity(ctx, paint.color, opacity);
13566
+ const cx = box.x + box.w / 2;
13567
+ const cy = box.y + box.h / 2;
13568
+ let gradient;
13569
+ if (paint.kind === "linear") {
13570
+ const radians = paint.angle * Math.PI / 180;
13571
+ const dx = Math.sin(radians);
13572
+ const dy = -Math.cos(radians);
13573
+ const half = (Math.abs(box.w * dx) + Math.abs(box.h * dy)) / 2;
13574
+ gradient = ctx.createLinearGradient(
13575
+ cx - dx * half,
13576
+ cy - dy * half,
13577
+ cx + dx * half,
13578
+ cy + dy * half
13579
+ );
13580
+ } else {
13581
+ gradient = ctx.createRadialGradient(
13582
+ cx,
13583
+ cy,
13584
+ 0,
13585
+ cx,
13586
+ cy,
13587
+ Math.max(box.w, box.h) / 2
13588
+ );
13589
+ }
13590
+ for (const stop of paint.stops) {
13591
+ gradient.addColorStop(stop.at, withOpacity(ctx, stop.color, opacity));
13592
+ }
13593
+ return gradient;
13594
+ }
13595
+ __name(fillStyleFor, "fillStyleFor");
13596
+ function drawImagePaint(ctx, paint, box, assets) {
13597
+ const image = assets.image(paint.src);
13598
+ if (!image) return;
13599
+ const sourceW = image.naturalWidth || box.w;
13600
+ const sourceH = image.naturalHeight || box.h;
13601
+ const scale = paint.fit === "fill" ? 1 : paint.fit === "cover" ? Math.max(box.w / sourceW, box.h / sourceH) : Math.min(box.w / sourceW, box.h / sourceH);
13602
+ const w = paint.fit === "fill" ? box.w : sourceW * scale;
13603
+ const h = paint.fit === "fill" ? box.h : sourceH * scale;
13604
+ const x = box.x + (box.w - w) / 2;
13605
+ const y = box.y + (box.h - h) / 2;
13606
+ if (!paint.tint) {
13607
+ ctx.drawImage(image, x, y, w, h);
13608
+ return;
13609
+ }
13610
+ const scratch = assets.scratch?.(Math.ceil(w), Math.ceil(h));
13611
+ if (!scratch) {
13612
+ ctx.drawImage(image, x, y, w, h);
13613
+ return;
13614
+ }
13615
+ scratch.drawImage(image, 0, 0, w, h);
13616
+ scratch.globalCompositeOperation = "source-in";
13617
+ scratch.fillStyle = paint.tint;
13618
+ scratch.fillRect(0, 0, w, h);
13619
+ ctx.drawImage(scratch.canvas, x, y, w, h);
13620
+ }
13621
+ __name(drawImagePaint, "drawImagePaint");
13622
+ function pxRadii(radius, density) {
13623
+ return radius.map((r) => r * density);
13624
+ }
13625
+ __name(pxRadii, "pxRadii");
13626
+ function drawBoxShadow(ctx, box, radii, shadow, density) {
13627
+ ctx.save();
13628
+ ctx.shadowColor = shadow.color;
13629
+ ctx.shadowBlur = shadow.blur * density;
13630
+ ctx.shadowOffsetX = shadow.x * density;
13631
+ ctx.shadowOffsetY = -shadow.y * density;
13632
+ ctx.fillStyle = "#000000";
13633
+ tracePath(ctx, box.x, box.y, box.w, box.h, radii);
13117
13634
  ctx.fill();
13118
- if (border > 0) {
13635
+ ctx.restore();
13636
+ ctx.save();
13637
+ ctx.globalCompositeOperation = "destination-out";
13638
+ ctx.fillStyle = "#000000";
13639
+ tracePath(ctx, box.x, box.y, box.w, box.h, radii);
13640
+ ctx.fill();
13641
+ ctx.restore();
13642
+ }
13643
+ __name(drawBoxShadow, "drawBoxShadow");
13644
+ function drawChrome(ctx, box, density, chrome, assets = NO_ASSETS) {
13645
+ const radii = pxRadii(chrome.radius, density);
13646
+ for (const shadow of chrome.shadows) {
13647
+ drawBoxShadow(ctx, box, radii, shadow, density);
13648
+ }
13649
+ const opacity = Math.max(0, Math.min(1, chrome.fillOpacity));
13650
+ if (chrome.fill?.kind === "image") {
13651
+ ctx.save();
13652
+ tracePath(ctx, box.x, box.y, box.w, box.h, radii);
13653
+ ctx.clip();
13654
+ ctx.globalAlpha = opacity;
13655
+ drawImagePaint(ctx, chrome.fill, box, assets);
13656
+ ctx.restore();
13657
+ } else if (chrome.fill) {
13658
+ tracePath(ctx, box.x, box.y, box.w, box.h, radii);
13659
+ ctx.fillStyle = fillStyleFor(ctx, chrome.fill, box, opacity);
13660
+ ctx.fill();
13661
+ }
13662
+ if (chrome.edge) {
13663
+ const width = chrome.edge.width * density;
13664
+ ctx.save();
13665
+ tracePath(ctx, box.x, box.y, box.w, box.h, radii);
13666
+ ctx.clip();
13667
+ const fade = ctx.createLinearGradient(0, box.y, 0, box.y + box.h / 2);
13668
+ fade.addColorStop(0, chrome.edge.color);
13669
+ fade.addColorStop(1, withOpacity(ctx, chrome.edge.color, 0));
13670
+ ctx.strokeStyle = fade;
13671
+ ctx.lineWidth = width * 2;
13672
+ tracePath(ctx, box.x, box.y, box.w, box.h, radii);
13673
+ ctx.stroke();
13674
+ ctx.restore();
13675
+ }
13676
+ if (chrome.border) {
13677
+ const border = chrome.border.width * density;
13119
13678
  tracePath(
13120
13679
  ctx,
13121
- border / 2,
13122
- border / 2,
13123
- pxWidth - border,
13124
- pxHeight - border,
13125
- Math.max(0, radius - border / 2)
13680
+ box.x + border / 2,
13681
+ box.y + border / 2,
13682
+ box.w - border,
13683
+ box.h - border,
13684
+ radii.map((r) => Math.max(0, r - border / 2))
13126
13685
  );
13127
- ctx.strokeStyle = style.borderColor;
13686
+ ctx.strokeStyle = chrome.border.color;
13128
13687
  ctx.lineWidth = border;
13129
13688
  ctx.stroke();
13130
13689
  }
13131
13690
  }
13132
13691
  __name(drawChrome, "drawChrome");
13133
- function drawTextBlock(ctx, pxWidth, pxHeight, density, text, style) {
13134
- const fontPx = Math.max(4, style.fontSizeMeters * density);
13135
- const paddingPx = style.paddingMeters * density;
13136
- const contentW = Math.max(1, pxWidth - paddingPx * 2);
13137
- const contentH = Math.max(1, pxHeight - paddingPx * 2);
13692
+ var applyTransform2 = /* @__PURE__ */ __name((text, transform) => transform === "upper" ? text.toUpperCase() : transform === "lower" ? text.toLowerCase() : text, "applyTransform");
13693
+ function fillTracked(ctx, line, x, y, trackingPx, width) {
13694
+ let cursor = ctx.textAlign === "center" ? x - width / 2 : ctx.textAlign === "right" ? x - width : x;
13695
+ const align = ctx.textAlign;
13696
+ ctx.textAlign = "left";
13697
+ for (const glyph of line) {
13698
+ ctx.fillText(glyph, cursor, y);
13699
+ cursor += ctx.measureText(glyph).width + trackingPx;
13700
+ }
13701
+ ctx.textAlign = align;
13702
+ }
13703
+ __name(fillTracked, "fillTracked");
13704
+ function drawTextBlock(ctx, box, density, text, style, family = null, assets = NO_ASSETS) {
13705
+ const fontPx = Math.max(4, style.fontSize * density);
13706
+ const paddingPx = style.padding * density;
13707
+ const contentW = Math.max(1, box.w - paddingPx * 2);
13708
+ const contentH = Math.max(1, box.h - paddingPx * 2);
13138
13709
  const lineHeightPx = fontPx * style.lineHeight;
13139
- ctx.font = `${style.fontWeight} ${fontPx}px ${UI_FONT_STACK}`;
13140
- const measure = /* @__PURE__ */ __name((s) => ctx.measureText(s).width, "measure");
13141
- const maxLines = Math.max(1, Math.floor(contentH / lineHeightPx));
13710
+ const trackingPx = style.tracking * density;
13711
+ const stack = family ? `"${family}", ${UI_FONT_STACK}` : UI_FONT_STACK;
13712
+ ctx.font = `${style.fontWeight} ${fontPx}px ${stack}`;
13713
+ const nativeTracking = trackingPx !== 0 && "letterSpacing" in ctx;
13714
+ if (nativeTracking) {
13715
+ ctx.letterSpacing = `${trackingPx}px`;
13716
+ }
13717
+ const manualTracking = trackingPx !== 0 && !nativeTracking;
13718
+ const measure = /* @__PURE__ */ __name((s) => ctx.measureText(s).width + (manualTracking ? trackingPx * s.length : 0), "measure");
13719
+ const fits = Math.max(1, Math.floor(contentH / lineHeightPx));
13720
+ const maxLines = style.maxLines ? Math.min(fits, style.maxLines) : fits;
13142
13721
  const lines = clampLines(
13143
13722
  measure,
13144
- wrapText(measure, text, contentW),
13723
+ wrapText(measure, applyTransform2(text, style.transform), contentW),
13145
13724
  maxLines,
13146
13725
  contentW
13147
13726
  );
13148
13727
  const blockH = lines.length * lineHeightPx;
13149
- const top = style.valign === "top" ? paddingPx : style.valign === "bottom" ? pxHeight - paddingPx - blockH : (pxHeight - blockH) / 2;
13150
- const anchorX = style.align === "left" ? paddingPx : style.align === "right" ? pxWidth - paddingPx : pxWidth / 2;
13728
+ const top = style.valign === "top" ? box.y + paddingPx : style.valign === "bottom" ? box.y + box.h - paddingPx - blockH : box.y + (box.h - blockH) / 2;
13729
+ const anchorX = style.align === "left" ? box.x + paddingPx : style.align === "right" ? box.x + box.w - paddingPx : box.x + box.w / 2;
13151
13730
  ctx.save();
13152
- tracePath(ctx, 0, 0, pxWidth, pxHeight, 0);
13153
- ctx.clip();
13154
- ctx.fillStyle = style.color;
13731
+ if (style.paint.kind === "image") {
13732
+ ctx.fillStyle = "#ffffff";
13733
+ } else {
13734
+ ctx.fillStyle = fillStyleFor(ctx, style.paint, box, 1);
13735
+ }
13736
+ if (style.shadow) {
13737
+ ctx.shadowColor = style.shadow.color;
13738
+ ctx.shadowBlur = style.shadow.blur * density;
13739
+ ctx.shadowOffsetX = style.shadow.x * density;
13740
+ ctx.shadowOffsetY = -style.shadow.y * density;
13741
+ }
13155
13742
  ctx.textAlign = style.align;
13156
13743
  ctx.textBaseline = "middle";
13157
13744
  for (let i = 0; i < lines.length; i += 1) {
13158
- ctx.fillText(lines[i], anchorX, top + (i + 0.5) * lineHeightPx);
13745
+ const y = top + (i + 0.5) * lineHeightPx;
13746
+ if (manualTracking) {
13747
+ fillTracked(ctx, lines[i], anchorX, y, trackingPx, measure(lines[i]));
13748
+ } else {
13749
+ ctx.fillText(lines[i], anchorX, y);
13750
+ }
13159
13751
  }
13160
13752
  ctx.restore();
13753
+ if (style.paint.kind === "image") {
13754
+ ctx.save();
13755
+ ctx.globalCompositeOperation = "source-atop";
13756
+ drawImagePaint(ctx, style.paint, box, assets);
13757
+ ctx.restore();
13758
+ }
13759
+ if (nativeTracking) {
13760
+ ctx.letterSpacing = "0px";
13761
+ }
13161
13762
  }
13162
13763
  __name(drawTextBlock, "drawTextBlock");
13163
- function panelSignature(widthM, heightM, size, style) {
13764
+ function panelSignature(widthM, heightM, size, chrome, loaded = null) {
13164
13765
  return JSON.stringify([
13165
13766
  widthM,
13166
13767
  heightM,
13167
13768
  size.pxWidth,
13168
13769
  size.pxHeight,
13169
- style.backgroundColor,
13170
- style.backgroundOpacity,
13171
- style.cornerRadiusMeters,
13172
- style.borderWidthMeters,
13173
- style.borderColor
13770
+ chrome,
13771
+ loaded
13174
13772
  ]);
13175
13773
  }
13176
13774
  __name(panelSignature, "panelSignature");
13177
- function textSignature(widthM, heightM, size, text, style) {
13775
+ function textSignature(widthM, heightM, size, text, style, family = null) {
13178
13776
  return JSON.stringify([
13179
13777
  widthM,
13180
13778
  heightM,
13181
13779
  size.pxWidth,
13182
13780
  size.pxHeight,
13183
13781
  text,
13184
- style.fontSizeMeters,
13185
- style.fontWeight,
13186
- style.color,
13187
- style.align,
13188
- style.valign,
13189
- style.lineHeight,
13190
- style.paddingMeters
13782
+ style,
13783
+ family
13191
13784
  ]);
13192
13785
  }
13193
13786
  __name(textSignature, "textSignature");
13194
- function buttonSignature(widthM, heightM, size, panel, label, text) {
13787
+ function buttonSignature(widthM, heightM, size, chrome, label, text, family = null, loaded = null) {
13195
13788
  return JSON.stringify([
13196
- panelSignature(widthM, heightM, size, panel),
13789
+ panelSignature(widthM, heightM, size, chrome, loaded),
13197
13790
  label,
13198
- text.fontSizeMeters,
13199
- text.fontWeight,
13200
- text.color
13791
+ text,
13792
+ family
13201
13793
  ]);
13202
13794
  }
13203
13795
  __name(buttonSignature, "buttonSignature");
13204
- function trackSignature(widthM, heightM, size, panel, accentColor, knobColor) {
13796
+ function trackSignature(widthM, heightM, size, chrome, accentColor, knobColor, variant = null) {
13205
13797
  return JSON.stringify([
13206
- panelSignature(widthM, heightM, size, panel),
13798
+ panelSignature(widthM, heightM, size, chrome),
13207
13799
  accentColor,
13208
- knobColor
13800
+ knobColor,
13801
+ variant
13209
13802
  ]);
13210
13803
  }
13211
13804
  __name(trackSignature, "trackSignature");
@@ -13215,17 +13808,226 @@ function drawRoundedBar(ctx, pxX, pxY, pxW, pxH, color) {
13215
13808
  ctx.fill();
13216
13809
  }
13217
13810
  __name(drawRoundedBar, "drawRoundedBar");
13811
+ function drawCheckMark(ctx, box, color) {
13812
+ ctx.beginPath();
13813
+ ctx.moveTo(box.x + box.w * 0.24, box.y + box.h * 0.54);
13814
+ ctx.lineTo(box.x + box.w * 0.43, box.y + box.h * 0.72);
13815
+ ctx.lineTo(box.x + box.w * 0.77, box.y + box.h * 0.3);
13816
+ ctx.strokeStyle = color;
13817
+ ctx.lineWidth = box.h * 0.13;
13818
+ ctx.lineCap = "round";
13819
+ ctx.lineJoin = "round";
13820
+ ctx.stroke();
13821
+ }
13822
+ __name(drawCheckMark, "drawCheckMark");
13823
+ function drawDot(ctx, cx, cy, radius, color) {
13824
+ ctx.beginPath();
13825
+ ctx.arc(cx, cy, radius, 0, Math.PI * 2);
13826
+ ctx.fillStyle = color;
13827
+ ctx.fill();
13828
+ }
13829
+ __name(drawDot, "drawDot");
13830
+ function drawRing(ctx, box, t, trackColor, accentColor) {
13831
+ const thickness = Math.min(box.w, box.h) * 0.14;
13832
+ const radius = Math.min(box.w, box.h) / 2 - thickness / 2;
13833
+ const cx = box.x + box.w / 2;
13834
+ const cy = box.y + box.h / 2;
13835
+ ctx.lineWidth = thickness;
13836
+ ctx.lineCap = "round";
13837
+ ctx.beginPath();
13838
+ ctx.arc(cx, cy, radius, 0, Math.PI * 2);
13839
+ ctx.strokeStyle = trackColor;
13840
+ ctx.stroke();
13841
+ if (t <= 0) return;
13842
+ const start = -Math.PI / 2;
13843
+ ctx.beginPath();
13844
+ ctx.arc(cx, cy, radius, start, start + t * Math.PI * 2);
13845
+ ctx.strokeStyle = accentColor;
13846
+ ctx.stroke();
13847
+ }
13848
+ __name(drawRing, "drawRing");
13849
+ function drawIconLabel(ctx, box, density, label, style, family, icon, position, assets) {
13850
+ const fontPx = style.fontSize * density;
13851
+ const iconPx = fontPx * 1.3;
13852
+ const gapPx = label ? fontPx * 0.5 : 0;
13853
+ const stack = family ? `"${family}", ${UI_FONT_STACK}` : UI_FONT_STACK;
13854
+ ctx.font = `${style.fontWeight} ${fontPx}px ${stack}`;
13855
+ const labelPx = label ? ctx.measureText(label).width : 0;
13856
+ const groupPx = iconPx + gapPx + labelPx;
13857
+ const startX = box.x + (box.w - groupPx) / 2;
13858
+ const iconX = position === "left" ? startX : startX + labelPx + gapPx;
13859
+ const labelX = position === "left" ? startX + iconPx + gapPx : startX;
13860
+ drawImagePaint(
13861
+ ctx,
13862
+ { src: icon, fit: "contain", tint: style.color },
13863
+ { x: iconX, y: box.y + (box.h - iconPx) / 2, w: iconPx, h: iconPx },
13864
+ assets
13865
+ );
13866
+ if (label) {
13867
+ drawTextBlock(
13868
+ ctx,
13869
+ { x: labelX, y: box.y, w: labelPx + 2, h: box.h },
13870
+ density,
13871
+ label,
13872
+ { ...style, align: "left", padding: 0 },
13873
+ family,
13874
+ assets
13875
+ );
13876
+ }
13877
+ }
13878
+ __name(drawIconLabel, "drawIconLabel");
13879
+
13880
+ // core/ui/uiPaintOrder.ts
13881
+ var BASE_RENDER_ORDER = 10;
13882
+ var MAX_RENDER_ORDER = 890;
13883
+ var MAX_TREE_RANK = MAX_RENDER_ORDER - BASE_RENDER_ORDER - 1;
13884
+ var SLOT_STEP = 1e-4;
13885
+ var SLOTS_PER_WIDGET = 3;
13886
+ var MAX_WIDGET_INDEX = Math.floor(1 / SLOT_STEP / SLOTS_PER_WIDGET) - 1;
13887
+ var PLANE_EPSILON_METERS = 1e-3;
13888
+ var PARALLEL_DOT = 1 - 1e-4;
13889
+ var dot = /* @__PURE__ */ __name((a, b) => a.x * b.x + a.y * b.y + a.z * b.z, "dot");
13890
+ var length = /* @__PURE__ */ __name((a) => Math.sqrt(dot(a, a)), "length");
13891
+ var subtract = /* @__PURE__ */ __name((a, b) => ({
13892
+ x: a.x - b.x,
13893
+ y: a.y - b.y,
13894
+ z: a.z - b.z
13895
+ }), "subtract");
13896
+ function unit(a) {
13897
+ const len = length(a);
13898
+ if (len < 1e-9) return { x: 0, y: 0, z: 0 };
13899
+ return { x: a.x / len, y: a.y / len, z: a.z / len };
13900
+ }
13901
+ __name(unit, "unit");
13902
+ function normalOf(rect) {
13903
+ const { halfX: a, halfY: b } = rect;
13904
+ return unit({
13905
+ x: a.y * b.z - a.z * b.y,
13906
+ y: a.z * b.x - a.x * b.z,
13907
+ z: a.x * b.y - a.y * b.x
13908
+ });
13909
+ }
13910
+ __name(normalOf, "normalOf");
13911
+ var planeDistance = /* @__PURE__ */ __name((rect, normal, eye) => Math.abs(dot(subtract(eye, rect.center), normal)), "planeDistance");
13912
+ function closestPointDistance(rect, eye) {
13913
+ const toEye = subtract(eye, rect.center);
13914
+ const lenX = length(rect.halfX);
13915
+ const lenY = length(rect.halfY);
13916
+ const axisX = unit(rect.halfX);
13917
+ const axisY = unit(rect.halfY);
13918
+ const s = Math.max(-lenX, Math.min(lenX, dot(toEye, axisX)));
13919
+ const t = Math.max(-lenY, Math.min(lenY, dot(toEye, axisY)));
13920
+ return length({
13921
+ x: toEye.x - axisX.x * s - axisY.x * t,
13922
+ y: toEye.y - axisX.y * s - axisY.y * t,
13923
+ z: toEye.z - axisX.z * s - axisY.z * t
13924
+ });
13925
+ }
13926
+ __name(closestPointDistance, "closestPointDistance");
13927
+ var areaOf = /* @__PURE__ */ __name((rect) => length(rect.halfX) * length(rect.halfY), "areaOf");
13928
+ function comparePaintRects(a, b, eye) {
13929
+ const normalA = normalOf(a);
13930
+ const normalB = normalOf(b);
13931
+ const parallel = Math.abs(dot(normalA, normalB)) >= PARALLEL_DOT;
13932
+ const distanceA = parallel ? planeDistance(a, normalA, eye) : closestPointDistance(a, eye);
13933
+ const distanceB = parallel ? planeDistance(b, normalB, eye) : closestPointDistance(b, eye);
13934
+ if (Math.abs(distanceA - distanceB) > PLANE_EPSILON_METERS) {
13935
+ return distanceB - distanceA;
13936
+ }
13937
+ const areaA = areaOf(a);
13938
+ const areaB = areaOf(b);
13939
+ if (areaA !== areaB) return areaB - areaA;
13940
+ return a.id - b.id;
13941
+ }
13942
+ __name(comparePaintRects, "comparePaintRects");
13943
+ function buildPaintTrees(nodes) {
13944
+ const known = new Set(nodes.map((node) => node.id));
13945
+ const children = /* @__PURE__ */ new Map();
13946
+ const roots = [];
13947
+ for (const node of nodes) {
13948
+ if (node.parent !== null && known.has(node.parent)) {
13949
+ const siblings = children.get(node.parent);
13950
+ if (siblings) siblings.push(node.id);
13951
+ else children.set(node.parent, [node.id]);
13952
+ } else {
13953
+ roots.push(node.id);
13954
+ }
13955
+ }
13956
+ const trees = [];
13957
+ const visited = /* @__PURE__ */ new Set();
13958
+ const walk = /* @__PURE__ */ __name((id, order) => {
13959
+ if (visited.has(id)) return;
13960
+ visited.add(id);
13961
+ order.push(id);
13962
+ const kids = children.get(id);
13963
+ if (!kids) return;
13964
+ for (const kid of [...kids].sort((a, b) => a - b)) walk(kid, order);
13965
+ }, "walk");
13966
+ for (const rootId of [...roots].sort((a, b) => a - b)) {
13967
+ const order = [];
13968
+ walk(rootId, order);
13969
+ trees.push({ rootId, order });
13970
+ }
13971
+ return trees;
13972
+ }
13973
+ __name(buildPaintTrees, "buildPaintTrees");
13974
+ function paintSlots(treeRank, widgetIndex) {
13975
+ const base = BASE_RENDER_ORDER + Math.min(treeRank, MAX_TREE_RANK);
13976
+ const index = Math.min(widgetIndex, MAX_WIDGET_INDEX);
13977
+ const slot = /* @__PURE__ */ __name((offset) => base + (index * SLOTS_PER_WIDGET + offset) * SLOT_STEP, "slot");
13978
+ return { face: slot(0), parts: slot(1), descendants: slot(2) };
13979
+ }
13980
+ __name(paintSlots, "paintSlots");
13981
+ function assignPaintOrder(trees, rects, eye) {
13982
+ const sorted = trees.filter((tree) => rects.has(tree.rootId)).sort(
13983
+ (a, b) => comparePaintRects(
13984
+ rects.get(a.rootId),
13985
+ rects.get(b.rootId),
13986
+ eye
13987
+ )
13988
+ );
13989
+ const slots = /* @__PURE__ */ new Map();
13990
+ sorted.forEach((tree, rank) => {
13991
+ tree.order.forEach((id, index) => {
13992
+ slots.set(id, paintSlots(rank, index));
13993
+ });
13994
+ });
13995
+ return slots;
13996
+ }
13997
+ __name(assignPaintOrder, "assignPaintOrder");
13218
13998
 
13219
13999
  // core/systems/UIPanelSystem.ts
13220
- var FACE_RENDER_ORDER = 10;
13221
14000
  var CHILD_Z = 2e-3;
14001
+ var DESCENDANT_WALK_INTERVAL = 30;
14002
+ var METER_STEPS = 200;
14003
+ var CLEAR_CHROME = {
14004
+ backgroundColor: "#000000",
14005
+ backgroundOpacity: 0,
14006
+ cornerRadius: 0,
14007
+ borderWidth: 0,
14008
+ borderColor: "#000000"
14009
+ };
14010
+ var PAINT_STAMP_KEY = "__uiPaintStamp";
13222
14011
  var _UIPanelSystem = class _UIPanelSystem {
13223
14012
  constructor(options) {
13224
14013
  this.entries = /* @__PURE__ */ new Map();
14014
+ this.framesUntilDescendantWalk = 0;
14015
+ /** Whether any panel defines tokens, so plain scenes skip the parent walk. */
14016
+ this.anyTokens = false;
14017
+ this.drawAssets = {
14018
+ image: /* @__PURE__ */ __name((src) => uiImage(src), "image"),
14019
+ scratch: /* @__PURE__ */ __name((width, height) => {
14020
+ const canvas = this.createCanvas();
14021
+ canvas.width = width;
14022
+ canvas.height = height;
14023
+ return canvas.getContext("2d");
14024
+ }, "scratch")
14025
+ };
13225
14026
  this.createCanvas = options?.createCanvas ?? (() => document.createElement("canvas"));
13226
14027
  }
13227
14028
  update(world, deltaTime = 0) {
13228
14029
  const live = /* @__PURE__ */ new Set();
14030
+ this.anyTokens = world.query(UIPanelComponent).some((id) => world.getComponent(id, UIPanelComponent)?.tokens);
13229
14031
  for (const entityId of world.query(UIPanelComponent, Object3DRef)) {
13230
14032
  const panel = world.getComponent(entityId, UIPanelComponent);
13231
14033
  if (!panel) continue;
@@ -13263,6 +14065,7 @@ var _UIPanelSystem = class _UIPanelSystem {
13263
14065
  this.entries.delete(entityId);
13264
14066
  }
13265
14067
  }
14068
+ this.applyPaintOrder(world);
13266
14069
  }
13267
14070
  /** All GPU/canvas resources; call on world teardown. */
13268
14071
  dispose() {
@@ -13271,84 +14074,188 @@ var _UIPanelSystem = class _UIPanelSystem {
13271
14074
  }
13272
14075
  // ── Widgets ────────────────────────────────────────────────────────────────
13273
14076
  syncPanel(world, entityId, panel) {
13274
- const size = resolveCanvasSize(panel.width, panel.height, panel.pixelDensity);
13275
- const style = {
13276
- backgroundColor: panel.backgroundColor,
13277
- backgroundOpacity: panel.backgroundOpacity,
13278
- cornerRadiusMeters: panel.cornerRadius,
13279
- borderWidthMeters: panel.borderWidth,
13280
- borderColor: panel.borderColor
13281
- };
13282
- const signature = panelSignature(panel.width, panel.height, size, style);
13283
- this.rebuildIfChanged(world, entityId, signature, panel.width, panel.height, size, (ctx) => {
13284
- drawChrome(ctx, size.pxWidth, size.pxHeight, size.density, style);
13285
- });
14077
+ const chrome = resolveChrome(
14078
+ panel,
14079
+ panel.style,
14080
+ this.tokensFor(world, entityId)
14081
+ );
14082
+ const bleed = chromeBleed(chrome);
14083
+ const size = resolveCanvasSize(
14084
+ panel.width + bleed * 2,
14085
+ panel.height + bleed * 2,
14086
+ panel.pixelDensity
14087
+ );
14088
+ const signature = panelSignature(
14089
+ panel.width,
14090
+ panel.height,
14091
+ size,
14092
+ chrome,
14093
+ imagesLoaded(chrome)
14094
+ );
14095
+ this.rebuildIfChanged(
14096
+ world,
14097
+ entityId,
14098
+ signature,
14099
+ panel,
14100
+ bleed,
14101
+ size,
14102
+ (ctx) => {
14103
+ drawChrome(
14104
+ ctx,
14105
+ layoutBox(panel, bleed, size.density),
14106
+ size.density,
14107
+ chrome,
14108
+ this.drawAssets
14109
+ );
14110
+ },
14111
+ THREE37__namespace.DoubleSide
14112
+ );
13286
14113
  }
13287
14114
  syncText(world, entityId, text) {
13288
- const size = resolveCanvasSize(text.width, text.height);
13289
- const style = {
13290
- fontSizeMeters: text.fontSize,
13291
- fontWeight: text.fontWeight,
13292
- color: text.color,
13293
- align: text.align,
13294
- valign: text.valign,
13295
- lineHeight: text.lineHeight,
13296
- paddingMeters: text.padding
13297
- };
13298
- const signature = textSignature(text.width, text.height, size, text.text, style);
13299
- this.rebuildIfChanged(world, entityId, signature, text.width, text.height, size, (ctx) => {
13300
- ctx.clearRect(0, 0, size.pxWidth, size.pxHeight);
13301
- drawTextBlock(ctx, size.pxWidth, size.pxHeight, size.density, text.text, style);
13302
- });
14115
+ const tokens = this.tokensFor(world, entityId);
14116
+ const chrome = text.style ? resolveChrome(CLEAR_CHROME, text.style, tokens) : null;
14117
+ const bleed = chrome ? chromeBleed(chrome) : 0;
14118
+ const size = resolveCanvasSize(
14119
+ text.width + bleed * 2,
14120
+ text.height + bleed * 2,
14121
+ text.pixelDensity
14122
+ );
14123
+ const style = resolveText(text, text.style, tokens);
14124
+ const family = familyOf(style.font);
14125
+ const signature = JSON.stringify([
14126
+ textSignature(text.width, text.height, size, text.text, style, family),
14127
+ chrome,
14128
+ chrome ? imagesLoaded(chrome) : null
14129
+ ]);
14130
+ this.rebuildIfChanged(
14131
+ world,
14132
+ entityId,
14133
+ signature,
14134
+ text,
14135
+ bleed,
14136
+ size,
14137
+ (ctx) => {
14138
+ const box = layoutBox(text, bleed, size.density);
14139
+ if (chrome) {
14140
+ drawChrome(ctx, box, size.density, chrome, this.drawAssets);
14141
+ }
14142
+ drawTextBlock(
14143
+ ctx,
14144
+ box,
14145
+ size.density,
14146
+ text.text,
14147
+ style,
14148
+ family,
14149
+ this.drawAssets
14150
+ );
14151
+ }
14152
+ );
13303
14153
  }
13304
14154
  syncButton(world, entityId, button) {
13305
- const size = resolveCanvasSize(button.width, button.height);
13306
- const chrome = {
13307
- backgroundColor: button.backgroundColor,
13308
- backgroundOpacity: button.backgroundOpacity,
13309
- cornerRadiusMeters: button.cornerRadius,
13310
- borderWidthMeters: button.borderWidth,
13311
- borderColor: button.borderColor
13312
- };
13313
- const label = {
13314
- fontSizeMeters: button.fontSize,
13315
- fontWeight: button.fontWeight,
13316
- color: button.textColor,
13317
- align: "center",
13318
- valign: "middle",
13319
- lineHeight: 1.2,
13320
- paddingMeters: 0.01
13321
- };
14155
+ const tokens = this.tokensFor(world, entityId);
14156
+ const chrome = resolveChrome(button, button.style, tokens);
14157
+ const bleed = chromeBleed(chrome);
14158
+ const size = resolveCanvasSize(
14159
+ button.width + bleed * 2,
14160
+ button.height + bleed * 2,
14161
+ button.pixelDensity
14162
+ );
14163
+ const label = resolveText(
14164
+ {
14165
+ fontSize: button.fontSize,
14166
+ fontWeight: button.fontWeight,
14167
+ color: button.textColor,
14168
+ align: "center",
14169
+ valign: "middle",
14170
+ lineHeight: 1.2,
14171
+ padding: 0.01
14172
+ },
14173
+ button.style,
14174
+ tokens
14175
+ );
14176
+ const family = familyOf(label.font);
14177
+ const icon = button.icon ? resolveToken(button.icon, tokens) : null;
13322
14178
  const signature = buttonSignature(
13323
14179
  button.width,
13324
14180
  button.height,
13325
14181
  size,
13326
14182
  chrome,
13327
14183
  button.label,
13328
- label
14184
+ label,
14185
+ family,
14186
+ [imagesLoaded(chrome), icon, icon ? !!uiImage(icon) : false]
14187
+ );
14188
+ this.rebuildIfChanged(
14189
+ world,
14190
+ entityId,
14191
+ signature,
14192
+ button,
14193
+ bleed,
14194
+ size,
14195
+ (ctx) => {
14196
+ const box = layoutBox(button, bleed, size.density);
14197
+ drawChrome(ctx, box, size.density, chrome, this.drawAssets);
14198
+ if (icon) {
14199
+ drawIconLabel(
14200
+ ctx,
14201
+ box,
14202
+ size.density,
14203
+ button.label,
14204
+ label,
14205
+ family,
14206
+ icon,
14207
+ button.iconPosition ?? "left",
14208
+ this.drawAssets
14209
+ );
14210
+ } else {
14211
+ drawTextBlock(
14212
+ ctx,
14213
+ box,
14214
+ size.density,
14215
+ button.label,
14216
+ label,
14217
+ family,
14218
+ this.drawAssets
14219
+ );
14220
+ }
14221
+ }
13329
14222
  );
13330
- this.rebuildIfChanged(world, entityId, signature, button.width, button.height, size, (ctx) => {
13331
- drawChrome(ctx, size.pxWidth, size.pxHeight, size.density, chrome);
13332
- drawTextBlock(ctx, size.pxWidth, size.pxHeight, size.density, button.label, label);
13333
- });
13334
14223
  }
13335
14224
  syncToggle(world, entityId, toggle, deltaTime) {
13336
- const size = resolveCanvasSize(toggle.width, toggle.height);
13337
- const chrome = {
13338
- backgroundColor: toggle.value ? toggle.accentColor : toggle.trackColor,
13339
- backgroundOpacity: 1,
13340
- cornerRadiusMeters: toggle.height / 2,
13341
- borderWidthMeters: 0,
13342
- borderColor: "#000000"
13343
- };
14225
+ const tokens = this.tokensFor(world, entityId);
14226
+ const accent = resolveToken(toggle.accentColor, tokens);
14227
+ const track = resolveToken(toggle.trackColor, tokens);
14228
+ const knobColor = resolveToken(toggle.knobColor, tokens);
14229
+ const size = resolveCanvasSize(
14230
+ toggle.width,
14231
+ toggle.height,
14232
+ toggle.pixelDensity
14233
+ );
14234
+ const variant = toggle.variant ?? "switch";
14235
+ if (variant !== "switch") {
14236
+ this.syncMarkToggle(world, entityId, toggle, accent, track, knobColor);
14237
+ return;
14238
+ }
14239
+ const chrome = resolveChrome(
14240
+ {
14241
+ backgroundColor: toggle.value ? accent : track,
14242
+ backgroundOpacity: 1,
14243
+ cornerRadius: toggle.height / 2,
14244
+ borderWidth: 0,
14245
+ borderColor: "#000000"
14246
+ },
14247
+ toggle.style,
14248
+ tokens
14249
+ );
13344
14250
  const signature = JSON.stringify([
13345
14251
  trackSignature(
13346
14252
  toggle.width,
13347
14253
  toggle.height,
13348
14254
  size,
13349
14255
  chrome,
13350
- toggle.accentColor,
13351
- toggle.knobColor
14256
+ accent,
14257
+ knobColor,
14258
+ variant
13352
14259
  ),
13353
14260
  toggle.value
13354
14261
  ]);
@@ -13356,18 +14263,24 @@ var _UIPanelSystem = class _UIPanelSystem {
13356
14263
  world,
13357
14264
  entityId,
13358
14265
  signature,
13359
- toggle.width,
13360
- toggle.height,
14266
+ toggle,
14267
+ 0,
13361
14268
  size,
13362
14269
  (ctx) => {
13363
- drawChrome(ctx, size.pxWidth, size.pxHeight, size.density, chrome);
14270
+ drawChrome(
14271
+ ctx,
14272
+ layoutBox(toggle, 0, size.density),
14273
+ size.density,
14274
+ chrome,
14275
+ this.drawAssets
14276
+ );
13364
14277
  }
13365
14278
  );
13366
14279
  if (!entry) return;
13367
14280
  if (!entry.knob) {
13368
14281
  entry.knob = this.makeDisc(
13369
14282
  toggle.height * 0.4,
13370
- toggle.knobColor,
14283
+ knobColor,
13371
14284
  entry.face.parent,
13372
14285
  CHILD_Z
13373
14286
  );
@@ -13379,35 +14292,95 @@ var _UIPanelSystem = class _UIPanelSystem {
13379
14292
  knob.position.x += (target - knob.position.x) * (deltaTime > 0 ? ease : 1);
13380
14293
  knob.position.z = CHILD_Z;
13381
14294
  }
14295
+ /** Checkbox and radio: a box or ring at the left of the widget's area. */
14296
+ syncMarkToggle(world, entityId, toggle, accent, track, markColor) {
14297
+ const tokens = this.tokensFor(world, entityId);
14298
+ const radio = toggle.variant === "radio";
14299
+ const size = resolveCanvasSize(
14300
+ toggle.width,
14301
+ toggle.height,
14302
+ toggle.pixelDensity
14303
+ );
14304
+ const side = toggle.height;
14305
+ const chrome = resolveChrome(
14306
+ {
14307
+ backgroundColor: toggle.value ? accent : track,
14308
+ backgroundOpacity: 1,
14309
+ cornerRadius: radio ? side / 2 : side * 0.2,
14310
+ borderWidth: 0,
14311
+ borderColor: "#000000"
14312
+ },
14313
+ toggle.style,
14314
+ tokens
14315
+ );
14316
+ const signature = JSON.stringify([
14317
+ trackSignature(
14318
+ toggle.width,
14319
+ toggle.height,
14320
+ size,
14321
+ chrome,
14322
+ accent,
14323
+ markColor,
14324
+ toggle.variant ?? null
14325
+ ),
14326
+ toggle.value
14327
+ ]);
14328
+ this.rebuildIfChanged(world, entityId, signature, toggle, 0, size, (ctx) => {
14329
+ const px = side * size.density;
14330
+ const box = { x: 0, y: 0, w: px, h: px };
14331
+ drawChrome(ctx, box, size.density, chrome, this.drawAssets);
14332
+ if (!toggle.value) return;
14333
+ if (radio) drawDot(ctx, px / 2, px / 2, px * 0.24, markColor);
14334
+ else drawCheckMark(ctx, box, markColor);
14335
+ });
14336
+ }
13382
14337
  syncSlider(world, entityId, slider) {
13383
- const size = resolveCanvasSize(slider.width, slider.height);
14338
+ const tokens = this.tokensFor(world, entityId);
14339
+ const accent = resolveToken(slider.accentColor, tokens);
14340
+ const track = resolveToken(slider.trackColor, tokens);
14341
+ const knobColor = resolveToken(slider.knobColor, tokens);
14342
+ const size = resolveCanvasSize(
14343
+ slider.width,
14344
+ slider.height,
14345
+ slider.pixelDensity
14346
+ );
14347
+ const range = Math.max(1e-6, slider.max - slider.min);
14348
+ const t = Math.min(1, Math.max(0, (slider.value - slider.min) / range));
14349
+ if (slider.variant === "progress" || slider.variant === "ring") {
14350
+ this.syncMeter(world, entityId, slider, t, accent, track, knobColor);
14351
+ return;
14352
+ }
13384
14353
  const trackThickness = slider.height * 0.32;
13385
14354
  const knobRadius = slider.height * 0.45;
13386
14355
  const usable = Math.max(1e-3, slider.width - slider.height);
13387
- const chrome = {
13388
- backgroundColor: slider.trackColor,
13389
- backgroundOpacity: 1,
13390
- cornerRadiusMeters: trackThickness / 2,
13391
- borderWidthMeters: 0,
13392
- borderColor: "#000000"
13393
- };
14356
+ const chrome = resolveChrome(
14357
+ {
14358
+ backgroundColor: track,
14359
+ backgroundOpacity: 1,
14360
+ cornerRadius: trackThickness / 2,
14361
+ borderWidth: 0,
14362
+ borderColor: "#000000"
14363
+ },
14364
+ void 0,
14365
+ null
14366
+ );
13394
14367
  const signature = trackSignature(
13395
14368
  slider.width,
13396
14369
  slider.height,
13397
14370
  size,
13398
14371
  chrome,
13399
- slider.accentColor,
13400
- slider.knobColor
14372
+ accent,
14373
+ knobColor,
14374
+ slider.variant ?? null
13401
14375
  );
13402
14376
  const entry = this.rebuildIfChanged(
13403
14377
  world,
13404
14378
  entityId,
13405
14379
  signature,
13406
- slider.width,
13407
- slider.height,
14380
+ slider,
14381
+ 0,
13408
14382
  size,
13409
14383
  (ctx) => {
13410
- ctx.clearRect(0, 0, size.pxWidth, size.pxHeight);
13411
14384
  const trackWidthM = usable + trackThickness;
13412
14385
  drawRoundedBar(
13413
14386
  ctx,
@@ -13415,7 +14388,7 @@ var _UIPanelSystem = class _UIPanelSystem {
13415
14388
  (slider.height - trackThickness) / 2 * size.density,
13416
14389
  trackWidthM * size.density,
13417
14390
  trackThickness * size.density,
13418
- slider.trackColor
14391
+ track
13419
14392
  );
13420
14393
  }
13421
14394
  );
@@ -13424,13 +14397,13 @@ var _UIPanelSystem = class _UIPanelSystem {
13424
14397
  entry.fill = this.makeQuad(
13425
14398
  1,
13426
14399
  trackThickness,
13427
- slider.accentColor,
14400
+ accent,
13428
14401
  entry.face.parent,
13429
14402
  CHILD_Z / 2
13430
14403
  );
13431
14404
  entry.cap = this.makeDisc(
13432
14405
  trackThickness / 2,
13433
- slider.accentColor,
14406
+ accent,
13434
14407
  entry.face.parent,
13435
14408
  CHILD_Z / 2,
13436
14409
  Math.PI / 2,
@@ -13441,51 +14414,124 @@ var _UIPanelSystem = class _UIPanelSystem {
13441
14414
  if (!entry.knob) {
13442
14415
  entry.knob = this.makeDisc(
13443
14416
  knobRadius,
13444
- slider.knobColor,
14417
+ knobColor,
13445
14418
  entry.face.parent,
13446
14419
  CHILD_Z
13447
14420
  );
13448
14421
  }
13449
- const range = Math.max(1e-6, slider.max - slider.min);
13450
- const t = Math.min(1, Math.max(0, (slider.value - slider.min) / range));
13451
14422
  entry.knob.position.x = -usable / 2 + t * usable;
13452
14423
  entry.knob.position.z = CHILD_Z;
13453
14424
  entry.fill.scale.x = Math.max(1e-4, t * usable);
13454
14425
  entry.fill.position.x = -usable / 2 + t * usable / 2;
13455
14426
  entry.fill.position.z = CHILD_Z / 2;
13456
14427
  }
14428
+ /**
14429
+ * Progress bars and rings. Unlike a slider these change rarely and have no
14430
+ * drag to keep smooth, so the value is drawn, in steps of half a percent.
14431
+ */
14432
+ syncMeter(world, entityId, slider, t, accent, track, knobColor) {
14433
+ const size = resolveCanvasSize(
14434
+ slider.width,
14435
+ slider.height,
14436
+ slider.pixelDensity
14437
+ );
14438
+ const shown = Math.round(t * METER_STEPS) / METER_STEPS;
14439
+ const signature = JSON.stringify([
14440
+ slider.width,
14441
+ slider.height,
14442
+ size,
14443
+ slider.variant,
14444
+ accent,
14445
+ track,
14446
+ knobColor,
14447
+ shown
14448
+ ]);
14449
+ this.rebuildIfChanged(world, entityId, signature, slider, 0, size, (ctx) => {
14450
+ const box = {
14451
+ x: 0,
14452
+ y: 0,
14453
+ w: size.pxWidth,
14454
+ h: size.pxHeight
14455
+ };
14456
+ if (slider.variant === "ring") {
14457
+ drawRing(ctx, box, shown, track, accent);
14458
+ return;
14459
+ }
14460
+ drawRoundedBar(ctx, 0, 0, box.w, box.h, track);
14461
+ if (shown <= 0) return;
14462
+ const fill = Math.max(box.h, box.w * shown);
14463
+ drawRoundedBar(ctx, 0, 0, fill, box.h, accent);
14464
+ });
14465
+ }
14466
+ /** The tokens in scope for a widget, nearest panel first. */
14467
+ tokensFor(world, entityId) {
14468
+ if (!this.anyTokens) return null;
14469
+ let merged = null;
14470
+ const visited = /* @__PURE__ */ new Set();
14471
+ let current = entityId;
14472
+ while (current !== null && !visited.has(current)) {
14473
+ visited.add(current);
14474
+ const tokens = world.getComponent(current, UIPanelComponent)?.tokens;
14475
+ if (tokens) merged = { ...tokens, ...merged ?? {} };
14476
+ const parent = world.getComponent(current, ParentId)?.value;
14477
+ current = typeof parent === "number" ? parent : null;
14478
+ }
14479
+ return merged;
14480
+ }
13457
14481
  // ── Machinery ──────────────────────────────────────────────────────────────
13458
- rebuildIfChanged(world, entityId, signature, widthM, heightM, size, draw) {
14482
+ rebuildIfChanged(world, entityId, signature, widget, bleed, size, draw, side = THREE37__namespace.FrontSide) {
13459
14483
  const root = world.getComponent(entityId, Object3DRef)?.object;
13460
14484
  if (!root) return null;
14485
+ const widthM = widget.width;
14486
+ const heightM = widget.height;
14487
+ const faceWidth = widthM + bleed * 2;
14488
+ const faceHeight = heightM + bleed * 2;
13461
14489
  let entry = this.entries.get(entityId);
14490
+ if (entry) entry.opacity = widget.style?.opacity ?? 1;
13462
14491
  if (entry && entry.signature === signature) return entry;
13463
14492
  if (entry) this.disposeMovingParts(entry);
13464
14493
  if (!entry) {
13465
14494
  const canvas2 = this.createCanvas();
13466
- const texture = new THREE30__namespace.CanvasTexture(
14495
+ const texture = new THREE37__namespace.CanvasTexture(
13467
14496
  canvas2
13468
14497
  );
13469
14498
  this.configureTexture(world, texture);
13470
- const material = new THREE30__namespace.MeshBasicMaterial({
14499
+ const material = new THREE37__namespace.MeshBasicMaterial({
13471
14500
  map: texture,
13472
14501
  transparent: true,
13473
14502
  depthWrite: false,
13474
14503
  // UI colours are authored absolutes; scene tone mapping would shift
13475
14504
  // them (the classic "why is my white UI beige" bug).
13476
14505
  toneMapped: false,
13477
- side: THREE30__namespace.DoubleSide,
13478
- premultipliedAlpha: true
14506
+ side,
14507
+ premultipliedAlpha: false,
14508
+ blending: THREE37__namespace.CustomBlending,
14509
+ blendSrc: THREE37__namespace.OneFactor,
14510
+ blendDst: THREE37__namespace.OneMinusSrcAlphaFactor,
14511
+ blendSrcAlpha: THREE37__namespace.OneFactor,
14512
+ blendDstAlpha: THREE37__namespace.OneMinusSrcAlphaFactor
13479
14513
  });
13480
- const face = new THREE30__namespace.Mesh(
13481
- new THREE30__namespace.PlaneGeometry(widthM, heightM),
14514
+ const face = new THREE37__namespace.Mesh(
14515
+ new THREE37__namespace.PlaneGeometry(faceWidth, faceHeight),
13482
14516
  material
13483
14517
  );
13484
- face.renderOrder = FACE_RENDER_ORDER;
14518
+ tagPart(face, 16777215, true);
14519
+ face.raycast = () => {
14520
+ };
13485
14521
  root.add(face);
14522
+ const hit = new THREE37__namespace.Mesh(
14523
+ new THREE37__namespace.PlaneGeometry(widthM, heightM),
14524
+ new THREE37__namespace.MeshBasicMaterial({ visible: false, side })
14525
+ );
14526
+ root.add(hit);
13486
14527
  entry = {
13487
14528
  signature: "",
14529
+ root,
14530
+ width: widthM,
14531
+ height: heightM,
14532
+ opacity: widget.style?.opacity ?? 1,
13488
14533
  face,
14534
+ hit,
13489
14535
  texture,
13490
14536
  material,
13491
14537
  canvas: canvas2,
@@ -13499,23 +14545,26 @@ var _UIPanelSystem = class _UIPanelSystem {
13499
14545
  if (canvas.width !== size.pxWidth || canvas.height !== size.pxHeight) {
13500
14546
  canvas.width = size.pxWidth;
13501
14547
  canvas.height = size.pxHeight;
14548
+ if (entry.signature) entry.texture.dispose();
13502
14549
  }
13503
- const params = entry.face.geometry.parameters;
13504
- if (params.width !== widthM || params.height !== heightM) {
13505
- entry.face.geometry.dispose();
13506
- entry.face.geometry = new THREE30__namespace.PlaneGeometry(widthM, heightM);
13507
- }
14550
+ entry.width = widthM;
14551
+ entry.height = heightM;
14552
+ resizePlane(entry.face, faceWidth, faceHeight);
14553
+ resizePlane(entry.hit, widthM, heightM);
13508
14554
  const ctx = canvas.getContext("2d");
13509
- if (ctx) draw(ctx);
14555
+ if (ctx) {
14556
+ ctx.clearRect(0, 0, size.pxWidth, size.pxHeight);
14557
+ draw(ctx);
14558
+ }
13510
14559
  entry.texture.needsUpdate = true;
13511
14560
  entry.signature = signature;
13512
14561
  return entry;
13513
14562
  }
13514
14563
  configureTexture(world, texture) {
13515
- texture.colorSpace = THREE30__namespace.SRGBColorSpace;
14564
+ texture.colorSpace = THREE37__namespace.SRGBColorSpace;
13516
14565
  texture.generateMipmaps = true;
13517
- texture.minFilter = THREE30__namespace.LinearMipmapLinearFilter;
13518
- texture.magFilter = THREE30__namespace.LinearFilter;
14566
+ texture.minFilter = THREE37__namespace.LinearMipmapLinearFilter;
14567
+ texture.magFilter = THREE37__namespace.LinearFilter;
13519
14568
  texture.premultiplyAlpha = true;
13520
14569
  try {
13521
14570
  const renderer = world.getRenderer();
@@ -13526,7 +14575,7 @@ var _UIPanelSystem = class _UIPanelSystem {
13526
14575
  }
13527
14576
  makeQuad(widthM, heightM, color, parent, z) {
13528
14577
  return this.addPart(
13529
- new THREE30__namespace.PlaneGeometry(widthM, heightM),
14578
+ new THREE37__namespace.PlaneGeometry(widthM, heightM),
13530
14579
  color,
13531
14580
  parent,
13532
14581
  z
@@ -13535,24 +14584,23 @@ var _UIPanelSystem = class _UIPanelSystem {
13535
14584
  /** A filled disc (or arc segment, for the fill's rounded cap). */
13536
14585
  makeDisc(radiusM, color, parent, z, thetaStart = 0, thetaLength = Math.PI * 2) {
13537
14586
  return this.addPart(
13538
- new THREE30__namespace.CircleGeometry(radiusM, 32, thetaStart, thetaLength),
14587
+ new THREE37__namespace.CircleGeometry(radiusM, 32, thetaStart, thetaLength),
13539
14588
  color,
13540
14589
  parent,
13541
14590
  z
13542
14591
  );
13543
14592
  }
13544
14593
  addPart(geometry, color, parent, z) {
13545
- const mesh = new THREE30__namespace.Mesh(
14594
+ const mesh = new THREE37__namespace.Mesh(
13546
14595
  geometry,
13547
- new THREE30__namespace.MeshBasicMaterial({
14596
+ new THREE37__namespace.MeshBasicMaterial({
13548
14597
  color,
13549
14598
  transparent: true,
13550
14599
  depthWrite: false,
13551
- toneMapped: false,
13552
- side: THREE30__namespace.DoubleSide
14600
+ toneMapped: false
13553
14601
  })
13554
14602
  );
13555
- mesh.renderOrder = FACE_RENDER_ORDER + 1;
14603
+ tagPart(mesh, color);
13556
14604
  mesh.position.z = z;
13557
14605
  mesh.raycast = () => {
13558
14606
  };
@@ -13583,22 +14631,144 @@ var _UIPanelSystem = class _UIPanelSystem {
13583
14631
  const object = world.getComponent(entityId, Object3DRef)?.object;
13584
14632
  if (!transform || !object) return;
13585
14633
  const camera = world.getCamera();
13586
- const cam = new THREE30__namespace.Vector3();
13587
- const self = new THREE30__namespace.Vector3();
14634
+ const cam = new THREE37__namespace.Vector3();
14635
+ const self = new THREE37__namespace.Vector3();
13588
14636
  camera.getWorldPosition(cam);
13589
14637
  object.getWorldPosition(self);
13590
14638
  transform.rotation.y = Math.atan2(cam.x - self.x, cam.z - self.z);
13591
14639
  }
14640
+ /**
14641
+ * Stamp every widget's meshes with this frame's paint order. Written each
14642
+ * frame because the eye moves: the order between trees can change at any
14643
+ * time, and a plain property write costs less than checking it.
14644
+ */
14645
+ applyPaintOrder(world) {
14646
+ if (this.entries.size === 0) return;
14647
+ const camera = world.getCamera();
14648
+ camera.getWorldPosition(eyeScratch);
14649
+ const nodes = [...this.entries.keys()].map((id) => ({
14650
+ id,
14651
+ parent: nearestWidgetAncestor(world, id)
14652
+ }));
14653
+ const trees = buildPaintTrees(nodes);
14654
+ const rects = /* @__PURE__ */ new Map();
14655
+ for (const tree of trees) {
14656
+ const entry = this.entries.get(tree.rootId);
14657
+ if (entry) rects.set(tree.rootId, rootRect(tree.rootId, entry));
14658
+ }
14659
+ const slots = assignPaintOrder(trees, rects, eyeScratch);
14660
+ this.framesUntilDescendantWalk -= 1;
14661
+ const walkDescendants = this.framesUntilDescendantWalk <= 0;
14662
+ if (walkDescendants) {
14663
+ this.framesUntilDescendantWalk = DESCENDANT_WALK_INTERVAL;
14664
+ }
14665
+ const widgetRoots = /* @__PURE__ */ new Set();
14666
+ if (walkDescendants) {
14667
+ for (const entry of this.entries.values()) widgetRoots.add(entry.root);
14668
+ }
14669
+ for (const [entityId, entry] of this.entries) {
14670
+ const slot = slots.get(entityId);
14671
+ if (!slot) continue;
14672
+ entry.face.renderOrder = slot.face;
14673
+ for (const part of [entry.knob, entry.fill, entry.cap]) {
14674
+ if (part) part.renderOrder = slot.parts;
14675
+ }
14676
+ if (walkDescendants) stampDescendants(entry.root, slot, widgetRoots);
14677
+ }
14678
+ const effective = /* @__PURE__ */ new Map();
14679
+ const parentOf = new Map(nodes.map((node) => [node.id, node.parent]));
14680
+ for (const tree of trees) {
14681
+ for (const id of tree.order) {
14682
+ const entry = this.entries.get(id);
14683
+ if (!entry) continue;
14684
+ const parent = parentOf.get(id);
14685
+ const inherited = parent != null ? effective.get(parent) ?? 1 : 1;
14686
+ const opacity = entry.opacity * inherited;
14687
+ effective.set(id, opacity);
14688
+ for (const mesh of [entry.face, entry.knob, entry.fill, entry.cap]) {
14689
+ if (!mesh) continue;
14690
+ const tag = mesh.userData[UI_PART_KEY];
14691
+ if (tag.opacity === opacity) continue;
14692
+ tag.opacity = opacity;
14693
+ applyPartColor(mesh, tag);
14694
+ }
14695
+ }
14696
+ }
14697
+ }
13592
14698
  destroyEntry(entry) {
13593
14699
  this.disposeMovingParts(entry);
13594
14700
  entry.face.removeFromParent();
13595
14701
  entry.face.geometry.dispose();
13596
14702
  entry.face.material.dispose();
14703
+ entry.hit.removeFromParent();
14704
+ entry.hit.geometry.dispose();
14705
+ entry.hit.material.dispose();
13597
14706
  entry.texture.dispose();
13598
14707
  }
13599
14708
  };
13600
14709
  __name(_UIPanelSystem, "UIPanelSystem");
13601
14710
  var UIPanelSystem = _UIPanelSystem;
14711
+ var eyeScratch = new THREE37__namespace.Vector3();
14712
+ function tagPart(mesh, color, premultiplied = false) {
14713
+ const tag = {
14714
+ baseColor: new THREE37__namespace.Color(color),
14715
+ tint: 1,
14716
+ opacity: 1,
14717
+ premultiplied
14718
+ };
14719
+ mesh.userData[UI_PART_KEY] = tag;
14720
+ }
14721
+ __name(tagPart, "tagPart");
14722
+ function resizePlane(mesh, width, height) {
14723
+ const params = mesh.geometry.parameters;
14724
+ if (params.width === width && params.height === height) return;
14725
+ mesh.geometry.dispose();
14726
+ mesh.geometry = new THREE37__namespace.PlaneGeometry(width, height);
14727
+ }
14728
+ __name(resizePlane, "resizePlane");
14729
+ function layoutBox(widget, bleed, density) {
14730
+ return {
14731
+ x: bleed * density,
14732
+ y: bleed * density,
14733
+ w: widget.width * density,
14734
+ h: widget.height * density
14735
+ };
14736
+ }
14737
+ __name(layoutBox, "layoutBox");
14738
+ function familyOf(font) {
14739
+ return font ? uiFontFamily(font) : null;
14740
+ }
14741
+ __name(familyOf, "familyOf");
14742
+ function imagesLoaded(chrome) {
14743
+ return styleAssets(chrome, null).images.map((src) => !!uiImage(src));
14744
+ }
14745
+ __name(imagesLoaded, "imagesLoaded");
14746
+ function rootRect(id, entry) {
14747
+ const e = entry.root.matrixWorld.elements;
14748
+ const halfW = entry.width / 2;
14749
+ const halfH = entry.height / 2;
14750
+ return {
14751
+ id,
14752
+ center: { x: e[12], y: e[13], z: e[14] },
14753
+ halfX: { x: e[0] * halfW, y: e[1] * halfW, z: e[2] * halfW },
14754
+ halfY: { x: e[4] * halfH, y: e[5] * halfH, z: e[6] * halfH }
14755
+ };
14756
+ }
14757
+ __name(rootRect, "rootRect");
14758
+ function stampDescendants(root, slot, widgetRoots) {
14759
+ for (const child of root.children) {
14760
+ if (widgetRoots.has(child) || child.userData[UI_PART_KEY]) continue;
14761
+ const material = child.material;
14762
+ const transparent = Array.isArray(material) ? material.some((m) => m.transparent) : material?.transparent === true;
14763
+ const untouched = child.renderOrder === 0 || child.userData[PAINT_STAMP_KEY] === child.renderOrder;
14764
+ if (transparent && untouched) {
14765
+ child.renderOrder = slot.descendants;
14766
+ child.userData[PAINT_STAMP_KEY] = slot.descendants;
14767
+ }
14768
+ stampDescendants(child, slot, widgetRoots);
14769
+ }
14770
+ }
14771
+ __name(stampDescendants, "stampDescendants");
13602
14772
  var _XRHitTestSystem = class _XRHitTestSystem {
13603
14773
  constructor() {
13604
14774
  this.sources = /* @__PURE__ */ new Map();
@@ -13635,12 +14805,12 @@ var _XRHitTestSystem = class _XRHitTestSystem {
13635
14805
  if (!hits || hits.length === 0) continue;
13636
14806
  const pose = hits[0].getPose(referenceSpace);
13637
14807
  if (!pose) continue;
13638
- const matrix = new THREE30__namespace.Matrix4().fromArray(
14808
+ const matrix = new THREE37__namespace.Matrix4().fromArray(
13639
14809
  pose.transform.matrix
13640
14810
  );
13641
- const position = new THREE30__namespace.Vector3();
13642
- const quaternion = new THREE30__namespace.Quaternion();
13643
- matrix.decompose(position, quaternion, new THREE30__namespace.Vector3());
14811
+ const position = new THREE37__namespace.Vector3();
14812
+ const quaternion = new THREE37__namespace.Quaternion();
14813
+ matrix.decompose(position, quaternion, new THREE37__namespace.Vector3());
13644
14814
  this.results.set(key, { position, quaternion });
13645
14815
  }
13646
14816
  }
@@ -13714,8 +14884,8 @@ var _XRAnchorSystem = class _XRAnchorSystem {
13714
14884
  /** Per-anchor head-glue gates — see XREnvironmentSystem's TrackedEntry. */
13715
14885
  this.gates = /* @__PURE__ */ new Map();
13716
14886
  this.presenting = false;
13717
- this.tmpViewerMatrix = new THREE30__namespace.Matrix4();
13718
- this.tmpViewPos = new THREE30__namespace.Vector3();
14887
+ this.tmpViewerMatrix = new THREE37__namespace.Matrix4();
14888
+ this.tmpViewPos = new THREE37__namespace.Vector3();
13719
14889
  }
13720
14890
  update(world, deltaTime = 0) {
13721
14891
  const presenting = world.isXrPresenting();
@@ -13789,8 +14959,8 @@ var _XRAnchorSystem = class _XRAnchorSystem {
13789
14959
  if (typeof frame.createAnchor !== "function") return;
13790
14960
  const RigidTransform = globalThis.XRRigidTransform;
13791
14961
  if (!RigidTransform) return;
13792
- const quaternion = new THREE30__namespace.Quaternion().setFromEuler(
13793
- new THREE30__namespace.Euler(
14962
+ const quaternion = new THREE37__namespace.Quaternion().setFromEuler(
14963
+ new THREE37__namespace.Euler(
13794
14964
  transform.rotation.x,
13795
14965
  transform.rotation.y,
13796
14966
  transform.rotation.z,
@@ -13833,13 +15003,13 @@ var _XRAnchorSystem = class _XRAnchorSystem {
13833
15003
  };
13834
15004
  __name(_XRAnchorSystem, "XRAnchorSystem");
13835
15005
  var XRAnchorSystem = _XRAnchorSystem;
13836
- var _q = new THREE30__namespace.Quaternion();
13837
- var _q2 = new THREE30__namespace.Quaternion();
13838
- var _s = new THREE30__namespace.Vector3();
13839
- var _e = new THREE30__namespace.Vector3();
13840
- var _w = new THREE30__namespace.Vector3();
13841
- var _v = new THREE30__namespace.Vector3();
13842
- var _p = new THREE30__namespace.Vector3();
15006
+ var _q = new THREE37__namespace.Quaternion();
15007
+ var _q2 = new THREE37__namespace.Quaternion();
15008
+ var _s = new THREE37__namespace.Vector3();
15009
+ var _e = new THREE37__namespace.Vector3();
15010
+ var _w = new THREE37__namespace.Vector3();
15011
+ var _v = new THREE37__namespace.Vector3();
15012
+ var _p = new THREE37__namespace.Vector3();
13843
15013
  function setWorldQuaternion(o, q) {
13844
15014
  if (o.parent) {
13845
15015
  o.parent.getWorldQuaternion(_q2).invert();
@@ -13864,19 +15034,19 @@ function twoBoneIK(upper, lower, end, target, pole) {
13864
15034
  if (a < 1e-6 || b < 1e-6) return target.distanceTo(_w);
13865
15035
  const toT = _v.subVectors(target, _s);
13866
15036
  const want = toT.length();
13867
- const c = THREE30__namespace.MathUtils.clamp(want, Math.abs(a - b) + 1e-4, a + b - 1e-4);
15037
+ const c = THREE37__namespace.MathUtils.clamp(want, Math.abs(a - b) + 1e-4, a + b - 1e-4);
13868
15038
  const dir = toT.divideScalar(want || 1).clone();
13869
15039
  const pv = _p.subVectors(pole, _s);
13870
15040
  pv.addScaledVector(dir, -pv.dot(dir));
13871
15041
  if (pv.lengthSq() < 1e-8) pv.set(0, -1, 0).addScaledVector(dir, dir.y);
13872
15042
  pv.normalize();
13873
- const cosA = THREE30__namespace.MathUtils.clamp((a * a + c * c - b * b) / (2 * a * c), -1, 1);
15043
+ const cosA = THREE37__namespace.MathUtils.clamp((a * a + c * c - b * b) / (2 * a * c), -1, 1);
13874
15044
  const joint = _s.clone().addScaledVector(dir, a * cosA).addScaledVector(pv, a * Math.sqrt(1 - cosA * cosA));
13875
- turn(upper, new THREE30__namespace.Quaternion().setFromUnitVectors(_e.clone().sub(_s).normalize(), joint.clone().sub(_s).normalize()));
15045
+ turn(upper, new THREE37__namespace.Quaternion().setFromUnitVectors(_e.clone().sub(_s).normalize(), joint.clone().sub(_s).normalize()));
13876
15046
  lower.getWorldPosition(_e);
13877
15047
  end.getWorldPosition(_w);
13878
15048
  const reach = _s.clone().addScaledVector(dir, c);
13879
- turn(lower, new THREE30__namespace.Quaternion().setFromUnitVectors(_w.clone().sub(_e).normalize(), reach.clone().sub(_e).normalize()));
15049
+ turn(lower, new THREE37__namespace.Quaternion().setFromUnitVectors(_w.clone().sub(_e).normalize(), reach.clone().sub(_e).normalize()));
13880
15050
  return want - c;
13881
15051
  }
13882
15052
  __name(twoBoneIK, "twoBoneIK");
@@ -13888,18 +15058,20 @@ var LIMB_BONES = {
13888
15058
  leftFoot: ["leftUpperLeg", "leftLowerLeg", "leftFoot"],
13889
15059
  rightFoot: ["rightUpperLeg", "rightLowerLeg", "rightFoot"]
13890
15060
  };
13891
- var _t = new THREE30__namespace.Vector3();
13892
- var _p2 = new THREE30__namespace.Vector3();
13893
- var _q3 = new THREE30__namespace.Quaternion();
13894
- var _pq = new THREE30__namespace.Quaternion();
15061
+ var _t = new THREE37__namespace.Vector3();
15062
+ var _p2 = new THREE37__namespace.Vector3();
15063
+ var _q3 = new THREE37__namespace.Quaternion();
15064
+ var _pq = new THREE37__namespace.Quaternion();
13895
15065
  var _IKSystem = class _IKSystem {
13896
15066
  constructor() {
13897
15067
  this.runtimes = /* @__PURE__ */ new Map();
13898
- this.reach = /* @__PURE__ */ new Map();
13899
15068
  }
13900
- /** How far a limb's end stayed from its target last frame, in metres (0 = reached). */
15069
+ /**
15070
+ * How far a limb's end stayed from its target last frame, in metres
15071
+ * (0 = reached), or null if the limb's chain didn't run.
15072
+ */
13901
15073
  getReachError(entityId, limb) {
13902
- return this.reach.get(`${entityId}:${limb}`) ?? null;
15074
+ return this.runtimes.get(entityId)?.reach.get(limb) ?? null;
13903
15075
  }
13904
15076
  restore(runtime) {
13905
15077
  for (const [bone, w] of runtime.written) {
@@ -13925,13 +15097,14 @@ var _IKSystem = class _IKSystem {
13925
15097
  let runtime = this.runtimes.get(id);
13926
15098
  if (!runtime || runtime.rootUuid !== root.uuid) {
13927
15099
  if (runtime) this.restore(runtime);
13928
- runtime = { rootUuid: root.uuid, limbs: /* @__PURE__ */ new Map(), written: /* @__PURE__ */ new Map() };
15100
+ runtime = { rootUuid: root.uuid, limbs: /* @__PURE__ */ new Map(), written: /* @__PURE__ */ new Map(), reach: /* @__PURE__ */ new Map() };
13929
15101
  this.runtimes.set(id, runtime);
13930
15102
  }
13931
15103
  for (const [bone, w] of runtime.written) {
13932
15104
  if (bone.quaternion.equals(w.written)) bone.quaternion.copy(w.before);
13933
15105
  }
13934
15106
  runtime.written.clear();
15107
+ runtime.reach.clear();
13935
15108
  if (!ik.enabled) continue;
13936
15109
  const active = ik.chains.filter((c) => c.enabled && c.weight > 0 && typeof c.target === "number");
13937
15110
  if (active.length === 0) continue;
@@ -13952,7 +15125,7 @@ var _IKSystem = class _IKSystem {
13952
15125
  const [upper, lower, end] = limb.bones;
13953
15126
  for (const bone of [upper, lower, end]) {
13954
15127
  if (!runtime.written.has(bone)) {
13955
- runtime.written.set(bone, { before: bone.quaternion.clone(), written: new THREE30__namespace.Quaternion() });
15128
+ runtime.written.set(bone, { before: bone.quaternion.clone(), written: new THREE37__namespace.Quaternion() });
13956
15129
  }
13957
15130
  }
13958
15131
  const animated = [upper.quaternion.clone(), lower.quaternion.clone(), end.quaternion.clone()];
@@ -13961,7 +15134,7 @@ var _IKSystem = class _IKSystem {
13961
15134
  if (poleObject) poleObject.getWorldPosition(_p2);
13962
15135
  else lower.getWorldPosition(_p2);
13963
15136
  const miss = twoBoneIK(upper, lower, end, _t, _p2);
13964
- this.reach.set(`${id}:${chain.limb}`, Math.max(0, miss));
15137
+ runtime.reach.set(chain.limb, Math.max(0, miss));
13965
15138
  if (chain.matchRotation) {
13966
15139
  target.getWorldQuaternion(_q3);
13967
15140
  end.parent?.getWorldQuaternion(_pq);
@@ -13990,9 +15163,9 @@ var IKSystem = _IKSystem;
13990
15163
  function buildGeometry2(area) {
13991
15164
  if (area.shape === "circle") {
13992
15165
  const radius = Math.max(5e-3, area.width / 2);
13993
- return new THREE30__namespace.CircleGeometry(radius, 48);
15166
+ return new THREE37__namespace.CircleGeometry(radius, 48);
13994
15167
  }
13995
- return new THREE30__namespace.PlaneGeometry(
15168
+ return new THREE37__namespace.PlaneGeometry(
13996
15169
  Math.max(0.01, area.width),
13997
15170
  Math.max(0.01, area.depth)
13998
15171
  );
@@ -14009,13 +15182,13 @@ var _VRTeleportAreaSystem = class _VRTeleportAreaSystem {
14009
15182
  update(world) {
14010
15183
  const entityIds = world.query(VRTeleportAreaComponent, Object3DRef);
14011
15184
  const alive = new Set(entityIds);
14012
- for (const tracked of this.signatures.keys()) {
14013
- if (!alive.has(tracked)) this.signatures.delete(tracked);
15185
+ for (const tracked2 of this.signatures.keys()) {
15186
+ if (!alive.has(tracked2)) this.signatures.delete(tracked2);
14014
15187
  }
14015
15188
  for (const entityId of entityIds) {
14016
15189
  const area = world.getComponent(entityId, VRTeleportAreaComponent);
14017
15190
  const object = world.getComponent(entityId, Object3DRef)?.object;
14018
- if (!area || !(object instanceof THREE30__namespace.Mesh)) continue;
15191
+ if (!area || !(object instanceof THREE37__namespace.Mesh)) continue;
14019
15192
  const signature = signatureOf(area);
14020
15193
  if (this.signatures.get(entityId) === signature) continue;
14021
15194
  this.signatures.set(entityId, signature);
@@ -14027,18 +15200,18 @@ var _VRTeleportAreaSystem = class _VRTeleportAreaSystem {
14027
15200
  __name(_VRTeleportAreaSystem, "VRTeleportAreaSystem");
14028
15201
  var VRTeleportAreaSystem = _VRTeleportAreaSystem;
14029
15202
  function createHandMaterial(preset, options) {
14030
- const color = new THREE30__namespace.Color(options.color);
15203
+ const color = new THREE37__namespace.Color(options.color);
14031
15204
  switch (preset) {
14032
15205
  case "ghost":
14033
- return new THREE30__namespace.MeshBasicMaterial({
15206
+ return new THREE37__namespace.MeshBasicMaterial({
14034
15207
  color,
14035
15208
  transparent: true,
14036
15209
  opacity: Math.min(1, Math.max(0.05, options.opacity * 0.45)),
14037
15210
  depthWrite: false,
14038
- side: THREE30__namespace.DoubleSide
15211
+ side: THREE37__namespace.DoubleSide
14039
15212
  });
14040
15213
  case "neon":
14041
- return new THREE30__namespace.MeshStandardMaterial({
15214
+ return new THREE37__namespace.MeshStandardMaterial({
14042
15215
  color,
14043
15216
  emissive: color,
14044
15217
  emissiveIntensity: Math.max(0.5, options.emissiveIntensity || 2),
@@ -14048,7 +15221,7 @@ function createHandMaterial(preset, options) {
14048
15221
  opacity: options.opacity
14049
15222
  });
14050
15223
  case "skeleton":
14051
- return new THREE30__namespace.MeshBasicMaterial({
15224
+ return new THREE37__namespace.MeshBasicMaterial({
14052
15225
  color,
14053
15226
  wireframe: true,
14054
15227
  transparent: true,
@@ -14056,7 +15229,7 @@ function createHandMaterial(preset, options) {
14056
15229
  });
14057
15230
  case "custom":
14058
15231
  case "default":
14059
- return new THREE30__namespace.MeshStandardMaterial({
15232
+ return new THREE37__namespace.MeshStandardMaterial({
14060
15233
  color,
14061
15234
  roughness: 0.85,
14062
15235
  metalness: 0,
@@ -14064,10 +15237,10 @@ function createHandMaterial(preset, options) {
14064
15237
  emissiveIntensity: Math.max(0, options.emissiveIntensity),
14065
15238
  transparent: options.opacity < 1,
14066
15239
  opacity: options.opacity,
14067
- side: THREE30__namespace.DoubleSide
15240
+ side: THREE37__namespace.DoubleSide
14068
15241
  });
14069
15242
  default: {
14070
- return new THREE30__namespace.MeshStandardMaterial({ color });
15243
+ return new THREE37__namespace.MeshStandardMaterial({ color });
14071
15244
  }
14072
15245
  }
14073
15246
  }
@@ -14092,9 +15265,9 @@ var _XRControllerVisualSystem = class _XRControllerVisualSystem {
14092
15265
  constructor(options) {
14093
15266
  this.controllerModelFactory = new XRControllerModelFactory_js.XRControllerModelFactory();
14094
15267
  this.handModelFactory = new XRHandModelFactory_js.XRHandModelFactory();
14095
- this.group = new THREE30__namespace.Group();
15268
+ this.group = new THREE37__namespace.Group();
14096
15269
  /** Reused so a per-frame colour compare allocates nothing. */
14097
- this.rayColorScratch = new THREE30__namespace.Color();
15270
+ this.rayColorScratch = new THREE37__namespace.Color();
14098
15271
  this.slots = /* @__PURE__ */ new Map();
14099
15272
  this.attachedTo = null;
14100
15273
  /** Last applied preset signature, so materials rebuild only on real change. */
@@ -14231,8 +15404,8 @@ var _XRControllerVisualSystem = class _XRControllerVisualSystem {
14231
15404
  */
14232
15405
  fitRayToHover(ray, side) {
14233
15406
  const hover = this.interactionSystem?.getHoverFor(side) ?? null;
14234
- const length = hover && Number.isFinite(hover.distance) && hover.distance > 0 ? Math.min(hover.distance, RAY_LENGTH) : RAY_LENGTH;
14235
- ray.scale.z = length;
15407
+ const length2 = hover && Number.isFinite(hover.distance) && hover.distance > 0 ? Math.min(hover.distance, RAY_LENGTH) : RAY_LENGTH;
15408
+ ray.scale.z = length2;
14236
15409
  }
14237
15410
  /** Lazily build per-slot visuals; the device reports profiles late. */
14238
15411
  ensureSlots(world) {
@@ -14281,17 +15454,17 @@ var _XRControllerVisualSystem = class _XRControllerVisualSystem {
14281
15454
  * without pulling in a line-rendering extension.
14282
15455
  */
14283
15456
  createRay() {
14284
- const geometry = new THREE30__namespace.CylinderGeometry(RAY_RADIUS, RAY_RADIUS, 1, 8, 1);
15457
+ const geometry = new THREE37__namespace.CylinderGeometry(RAY_RADIUS, RAY_RADIUS, 1, 8, 1);
14285
15458
  geometry.rotateX(-Math.PI / 2);
14286
15459
  geometry.translate(0, 0, -0.5);
14287
- const material = new THREE30__namespace.MeshBasicMaterial({
15460
+ const material = new THREE37__namespace.MeshBasicMaterial({
14288
15461
  color: "#4da3ff",
14289
15462
  transparent: true,
14290
15463
  opacity: 0.75,
14291
15464
  depthTest: false,
14292
15465
  depthWrite: false
14293
15466
  });
14294
- const mesh = new THREE30__namespace.Mesh(geometry, material);
15467
+ const mesh = new THREE37__namespace.Mesh(geometry, material);
14295
15468
  mesh.name = "XRPointerRay";
14296
15469
  mesh.renderOrder = 999;
14297
15470
  mesh.visible = false;
@@ -14322,14 +15495,14 @@ var _XRControllerVisualSystem = class _XRControllerVisualSystem {
14322
15495
  __name(_XRControllerVisualSystem, "XRControllerVisualSystem");
14323
15496
  var XRControllerVisualSystem = _XRControllerVisualSystem;
14324
15497
  var MARKER_RADIUS = 0.28;
14325
- var ACCENT = new THREE30__namespace.Color("#4da3ff");
14326
- var SEGMENT_AXIS = new THREE30__namespace.Vector3(0, 1, 0);
15498
+ var ACCENT = new THREE37__namespace.Color("#4da3ff");
15499
+ var SEGMENT_AXIS = new THREE37__namespace.Vector3(0, 1, 0);
14327
15500
  var HEAD_CLEARANCE = 0.25;
14328
15501
  var _XRTeleportVisualSystem = class _XRTeleportVisualSystem {
14329
15502
  constructor(locomotionSystem) {
14330
- this.group = new THREE30__namespace.Group();
15503
+ this.group = new THREE37__namespace.Group();
14331
15504
  /** Holds the segment chain that draws the arc. */
14332
- this.arc = new THREE30__namespace.Group();
15505
+ this.arc = new THREE37__namespace.Group();
14333
15506
  /**
14334
15507
  * Unit cylinder along +Y, reused by every segment.
14335
15508
  *
@@ -14338,14 +15511,14 @@ var _XRTeleportVisualSystem = class _XRTeleportVisualSystem {
14338
15511
  * scaled cylinders gives real thickness while rebuilding no geometry: each
14339
15512
  * frame only moves, rotates and scales meshes that already exist.
14340
15513
  */
14341
- this.segmentGeometry = new THREE30__namespace.CylinderGeometry(1, 1, 1, 6, 1);
15514
+ this.segmentGeometry = new THREE37__namespace.CylinderGeometry(1, 1, 1, 6, 1);
14342
15515
  this.segments = [];
14343
- this.marker = new THREE30__namespace.Group();
15516
+ this.marker = new THREE37__namespace.Group();
14344
15517
  this.attached = false;
14345
- this.scratchDirection = new THREE30__namespace.Vector3();
14346
- this.scratchHead = new THREE30__namespace.Vector3();
14347
- this.validColor = new THREE30__namespace.Color();
14348
- this.invalidColor = new THREE30__namespace.Color();
15518
+ this.scratchDirection = new THREE37__namespace.Vector3();
15519
+ this.scratchHead = new THREE37__namespace.Vector3();
15520
+ this.validColor = new THREE37__namespace.Color();
15521
+ this.invalidColor = new THREE37__namespace.Color();
14349
15522
  this.locomotionSystem = locomotionSystem;
14350
15523
  this.group.name = "XRTeleportVisuals";
14351
15524
  this.group.userData.ecsPreviewHidden = true;
@@ -14403,8 +15576,8 @@ var _XRTeleportVisualSystem = class _XRTeleportVisualSystem {
14403
15576
  const from = curve[i];
14404
15577
  const to = curve[i + 1];
14405
15578
  this.scratchDirection.subVectors(to, from);
14406
- const length = this.scratchDirection.length();
14407
- if (length < 1e-6) {
15579
+ const length2 = this.scratchDirection.length();
15580
+ if (length2 < 1e-6) {
14408
15581
  segment.visible = false;
14409
15582
  continue;
14410
15583
  }
@@ -14412,9 +15585,9 @@ var _XRTeleportVisualSystem = class _XRTeleportVisualSystem {
14412
15585
  segment.position.copy(from).addScaledVector(this.scratchDirection, 0.5);
14413
15586
  segment.quaternion.setFromUnitVectors(
14414
15587
  SEGMENT_AXIS,
14415
- this.scratchDirection.divideScalar(length)
15588
+ this.scratchDirection.divideScalar(length2)
14416
15589
  );
14417
- segment.scale.set(radius, length, radius);
15590
+ segment.scale.set(radius, length2, radius);
14418
15591
  const t = needed > 1 ? i / (needed - 1) : 0;
14419
15592
  const fade = 1 - t * 0.75;
14420
15593
  segment.material.color.copy(tint).multiplyScalar(fade);
@@ -14423,12 +15596,12 @@ var _XRTeleportVisualSystem = class _XRTeleportVisualSystem {
14423
15596
  /** Grow the segment pool to cover the current arc. */
14424
15597
  ensureSegments(count) {
14425
15598
  while (this.segments.length < count) {
14426
- const material = new THREE30__namespace.MeshBasicMaterial({
15599
+ const material = new THREE37__namespace.MeshBasicMaterial({
14427
15600
  transparent: true,
14428
15601
  depthTest: false,
14429
15602
  depthWrite: false
14430
15603
  });
14431
- const mesh = new THREE30__namespace.Mesh(this.segmentGeometry, material);
15604
+ const mesh = new THREE37__namespace.Mesh(this.segmentGeometry, material);
14432
15605
  mesh.frustumCulled = false;
14433
15606
  mesh.renderOrder = 998;
14434
15607
  mesh.raycast = () => {
@@ -14452,33 +15625,33 @@ var _XRTeleportVisualSystem = class _XRTeleportVisualSystem {
14452
15625
  return world.getComponent(entityId, XRRigComponent) ?? null;
14453
15626
  }
14454
15627
  createRing() {
14455
- const geometry = new THREE30__namespace.RingGeometry(
15628
+ const geometry = new THREE37__namespace.RingGeometry(
14456
15629
  MARKER_RADIUS * 0.72,
14457
15630
  MARKER_RADIUS,
14458
15631
  48
14459
15632
  );
14460
15633
  geometry.rotateX(-Math.PI / 2);
14461
- return new THREE30__namespace.Mesh(
15634
+ return new THREE37__namespace.Mesh(
14462
15635
  geometry,
14463
- new THREE30__namespace.MeshBasicMaterial({
15636
+ new THREE37__namespace.MeshBasicMaterial({
14464
15637
  color: ACCENT,
14465
15638
  transparent: true,
14466
15639
  opacity: 0.9,
14467
- side: THREE30__namespace.DoubleSide,
15640
+ side: THREE37__namespace.DoubleSide,
14468
15641
  depthTest: false
14469
15642
  })
14470
15643
  );
14471
15644
  }
14472
15645
  createDot() {
14473
- const geometry = new THREE30__namespace.CircleGeometry(MARKER_RADIUS * 0.16, 24);
15646
+ const geometry = new THREE37__namespace.CircleGeometry(MARKER_RADIUS * 0.16, 24);
14474
15647
  geometry.rotateX(-Math.PI / 2);
14475
- return new THREE30__namespace.Mesh(
15648
+ return new THREE37__namespace.Mesh(
14476
15649
  geometry,
14477
- new THREE30__namespace.MeshBasicMaterial({
15650
+ new THREE37__namespace.MeshBasicMaterial({
14478
15651
  color: ACCENT,
14479
15652
  transparent: true,
14480
15653
  opacity: 0.55,
14481
- side: THREE30__namespace.DoubleSide,
15654
+ side: THREE37__namespace.DoubleSide,
14482
15655
  depthTest: false
14483
15656
  })
14484
15657
  );
@@ -14589,7 +15762,7 @@ function boneDirections(joints, finger) {
14589
15762
  const from = joints[chain[i - 1]];
14590
15763
  const to = joints[chain[i]];
14591
15764
  if (!from || !to) return null;
14592
- const direction = new THREE30__namespace.Vector3().subVectors(to, from);
15765
+ const direction = new THREE37__namespace.Vector3().subVectors(to, from);
14593
15766
  if (direction.lengthSq() < 1e-12) return null;
14594
15767
  directions.push(direction.normalize());
14595
15768
  }
@@ -14662,8 +15835,8 @@ function computeSpread(joints, a, b) {
14662
15835
  const bBase = joints[METACARPAL_JOINTS[b]];
14663
15836
  const bTip = joints[TIP_JOINTS[b]];
14664
15837
  if (!aBase || !aTip || !bBase || !bTip) return null;
14665
- const dirA = new THREE30__namespace.Vector3().subVectors(aTip, aBase);
14666
- const dirB = new THREE30__namespace.Vector3().subVectors(bTip, bBase);
15838
+ const dirA = new THREE37__namespace.Vector3().subVectors(aTip, aBase);
15839
+ const dirB = new THREE37__namespace.Vector3().subVectors(bTip, bBase);
14667
15840
  if (dirA.lengthSq() < 1e-12 || dirB.lengthSq() < 1e-12) return null;
14668
15841
  return clamp01((1 - dirA.normalize().dot(dirB.normalize())) / 0.45);
14669
15842
  }
@@ -14673,18 +15846,18 @@ function computePalmPose(joints, handedness) {
14673
15846
  const indexBase = joints["index-finger-metacarpal"];
14674
15847
  const pinkyBase = joints["pinky-finger-metacarpal"];
14675
15848
  if (!wrist || !indexBase || !pinkyBase) return null;
14676
- const u = new THREE30__namespace.Vector3().subVectors(indexBase, wrist);
14677
- const v = new THREE30__namespace.Vector3().subVectors(pinkyBase, wrist);
14678
- const normal = new THREE30__namespace.Vector3().crossVectors(u, v);
15849
+ const u = new THREE37__namespace.Vector3().subVectors(indexBase, wrist);
15850
+ const v = new THREE37__namespace.Vector3().subVectors(pinkyBase, wrist);
15851
+ const normal = new THREE37__namespace.Vector3().crossVectors(u, v);
14679
15852
  if (normal.lengthSq() < 1e-12) return null;
14680
15853
  normal.normalize();
14681
15854
  if (handedness === "left") normal.multiplyScalar(-1);
14682
- const right = new THREE30__namespace.Vector3().subVectors(indexBase, pinkyBase);
15855
+ const right = new THREE37__namespace.Vector3().subVectors(indexBase, pinkyBase);
14683
15856
  if (right.lengthSq() < 1e-12) return null;
14684
15857
  right.normalize();
14685
15858
  if (handedness === "left") right.multiplyScalar(-1);
14686
- const up = new THREE30__namespace.Vector3().crossVectors(normal, right).normalize();
14687
- const center = new THREE30__namespace.Vector3().add(wrist).add(indexBase).add(pinkyBase).divideScalar(3);
15859
+ const up = new THREE37__namespace.Vector3().crossVectors(normal, right).normalize();
15860
+ const center = new THREE37__namespace.Vector3().add(wrist).add(indexBase).add(pinkyBase).divideScalar(3);
14688
15861
  return {
14689
15862
  center,
14690
15863
  normal,
@@ -14696,17 +15869,17 @@ function computePalmPose(joints, handedness) {
14696
15869
  __name(computePalmPose, "computePalmPose");
14697
15870
  function computeHandShape(joints, handedness) {
14698
15871
  const fingers2 = {};
14699
- let tracked = 0;
15872
+ let tracked2 = 0;
14700
15873
  for (const finger of XR_FINGERS) {
14701
15874
  const curl = computeFingerCurl(joints, finger);
14702
15875
  if (curl) {
14703
15876
  fingers2[finger] = curl;
14704
- tracked += 1;
15877
+ tracked2 += 1;
14705
15878
  } else {
14706
15879
  fingers2[finger] = { baseCurl: 0, tipCurl: 0, curl: 0 };
14707
15880
  }
14708
15881
  }
14709
- if (tracked === 0) return null;
15882
+ if (tracked2 === 0) return null;
14710
15883
  const pinch = {};
14711
15884
  for (const finger of PINCH_FINGERS) {
14712
15885
  pinch[finger] = computePinch(joints, finger) ?? 0;
@@ -14715,7 +15888,7 @@ function computeHandShape(joints, handedness) {
14715
15888
  const thumbTip = joints["thumb-tip"];
14716
15889
  let thumbDirection = null;
14717
15890
  if (thumbBase && thumbTip) {
14718
- const direction = new THREE30__namespace.Vector3().subVectors(thumbTip, thumbBase);
15891
+ const direction = new THREE37__namespace.Vector3().subVectors(thumbTip, thumbBase);
14719
15892
  if (direction.lengthSq() > 1e-12) thumbDirection = direction.normalize();
14720
15893
  }
14721
15894
  const fingerDirections = {};
@@ -14726,7 +15899,7 @@ function computeHandShape(joints, handedness) {
14726
15899
  fingerDirections[finger] = null;
14727
15900
  continue;
14728
15901
  }
14729
- const direction = new THREE30__namespace.Vector3().subVectors(tip, base);
15902
+ const direction = new THREE37__namespace.Vector3().subVectors(tip, base);
14730
15903
  fingerDirections[finger] = direction.lengthSq() > 1e-12 ? direction.normalize() : null;
14731
15904
  }
14732
15905
  return {
@@ -14760,12 +15933,12 @@ function scoreAgainst(actual, target, tolerance) {
14760
15933
  __name(scoreAgainst, "scoreAgainst");
14761
15934
  var DEFAULT_DIRECTION_TOLERANCE = 45;
14762
15935
  var WORLD_DIRECTIONS = {
14763
- up: new THREE30__namespace.Vector3(0, 1, 0),
14764
- down: new THREE30__namespace.Vector3(0, -1, 0),
14765
- forward: new THREE30__namespace.Vector3(0, 0, -1),
14766
- back: new THREE30__namespace.Vector3(0, 0, 1),
14767
- left: new THREE30__namespace.Vector3(-1, 0, 0),
14768
- right: new THREE30__namespace.Vector3(1, 0, 0)
15936
+ up: new THREE37__namespace.Vector3(0, 1, 0),
15937
+ down: new THREE37__namespace.Vector3(0, -1, 0),
15938
+ forward: new THREE37__namespace.Vector3(0, 0, -1),
15939
+ back: new THREE37__namespace.Vector3(0, 0, 1),
15940
+ left: new THREE37__namespace.Vector3(-1, 0, 0),
15941
+ right: new THREE37__namespace.Vector3(1, 0, 0)
14769
15942
  };
14770
15943
  function axisVector(shape, axis) {
14771
15944
  if (axis === "thumb") return shape.thumbDirection;
@@ -14780,7 +15953,7 @@ function resolveDirection(direction, shape, context) {
14780
15953
  const head = context?.headPosition;
14781
15954
  const origin = shape.palm?.center;
14782
15955
  if (!head || !origin) return null;
14783
- const toHead = new THREE30__namespace.Vector3().subVectors(head, origin);
15956
+ const toHead = new THREE37__namespace.Vector3().subVectors(head, origin);
14784
15957
  if (toHead.lengthSq() < 1e-12) return null;
14785
15958
  toHead.normalize();
14786
15959
  return direction === "toward-head" ? toHead : toHead.multiplyScalar(-1);
@@ -14792,8 +15965,8 @@ function scoreDirections(actual, accepted, shape, toleranceDegrees, context) {
14792
15965
  for (const direction of accepted) {
14793
15966
  const wanted = resolveDirection(direction, shape, context);
14794
15967
  if (!wanted) continue;
14795
- const angle = THREE30__namespace.MathUtils.radToDeg(
14796
- Math.acos(THREE30__namespace.MathUtils.clamp(actual.dot(wanted), -1, 1))
15968
+ const angle = THREE37__namespace.MathUtils.radToDeg(
15969
+ Math.acos(THREE37__namespace.MathUtils.clamp(actual.dot(wanted), -1, 1))
14797
15970
  );
14798
15971
  const normalizedError = Math.max(0, angle - toleranceDegrees) / 90;
14799
15972
  const score = clamp01(1 - normalizedError / MATCH_FALLOFF);
@@ -14858,8 +16031,8 @@ function matchHandPose(shape, definition, context) {
14858
16031
  if (!handAxis || !wanted) {
14859
16032
  consider(0, "orientation");
14860
16033
  } else {
14861
- const angle = THREE30__namespace.MathUtils.radToDeg(
14862
- Math.acos(THREE30__namespace.MathUtils.clamp(handAxis.dot(wanted), -1, 1))
16034
+ const angle = THREE37__namespace.MathUtils.radToDeg(
16035
+ Math.acos(THREE37__namespace.MathUtils.clamp(handAxis.dot(wanted), -1, 1))
14863
16036
  );
14864
16037
  const normalizedError = Math.max(0, angle - toleranceDegrees) / 90;
14865
16038
  consider(clamp01(1 - normalizedError / MATCH_FALLOFF), "orientation");
@@ -14937,7 +16110,7 @@ var _XRHandPoseSystem = class _XRHandPoseSystem {
14937
16110
  this.trackers = /* @__PURE__ */ new Map();
14938
16111
  this.shapes = /* @__PURE__ */ new Map();
14939
16112
  /** Reused so head-relative orientation checks allocate nothing per frame. */
14940
- this.headPosition = new THREE30__namespace.Vector3();
16113
+ this.headPosition = new THREE37__namespace.Vector3();
14941
16114
  this.elapsed = 0;
14942
16115
  this.warnedNoHands = false;
14943
16116
  this.noHandSeconds = 0;
@@ -14953,8 +16126,8 @@ var _XRHandPoseSystem = class _XRHandPoseSystem {
14953
16126
  const entries = this.collectEntries(world);
14954
16127
  if (entries.length === 0) return;
14955
16128
  if (!this.warnedNoHands) {
14956
- const tracked = this.shapes.get("left") !== null || this.shapes.get("right") !== null;
14957
- if (tracked) {
16129
+ const tracked2 = this.shapes.get("left") !== null || this.shapes.get("right") !== null;
16130
+ if (tracked2) {
14958
16131
  this.warnedNoHands = true;
14959
16132
  } else {
14960
16133
  this.noHandSeconds += deltaTime;
@@ -15100,6 +16273,7 @@ var _XRHoverVisualSystem = class _XRHoverVisualSystem {
15100
16273
  const interactable = world.getComponent(entityId, XRInteractableComponent);
15101
16274
  if (!interactable?.enabled) continue;
15102
16275
  if (interactable.hoverBehavior === "none") continue;
16276
+ if (isUiWidget(world, entityId)) continue;
15103
16277
  this.begin(world, entityId, interactable);
15104
16278
  }
15105
16279
  for (const [entityId, record] of this.records) {
@@ -15120,7 +16294,7 @@ var _XRHoverVisualSystem = class _XRHoverVisualSystem {
15120
16294
  const wantsColor = interactable.hoverBehavior === "color" || interactable.hoverBehavior === "both";
15121
16295
  this.records.set(entityId, {
15122
16296
  shell: wantsColor ? this.addShell(world, entityId, interactable) : null,
15123
- baseScale: new THREE30__namespace.Vector3(
16297
+ baseScale: new THREE37__namespace.Vector3(
15124
16298
  transform.scale.x,
15125
16299
  transform.scale.y,
15126
16300
  transform.scale.z
@@ -15133,27 +16307,27 @@ var _XRHoverVisualSystem = class _XRHoverVisualSystem {
15133
16307
  addShell(world, entityId, interactable) {
15134
16308
  const object = world.getComponent(entityId, Object3DRef)?.object;
15135
16309
  if (!object) return null;
15136
- const material = new THREE30__namespace.MeshBasicMaterial({
15137
- color: new THREE30__namespace.Color(interactable.hoverColor),
16310
+ const material = new THREE37__namespace.MeshBasicMaterial({
16311
+ color: new THREE37__namespace.Color(interactable.hoverColor),
15138
16312
  // Back faces only, so the shell shows just outside the silhouette instead
15139
16313
  // of covering the object.
15140
- side: THREE30__namespace.BackSide,
16314
+ side: THREE37__namespace.BackSide,
15141
16315
  transparent: true,
15142
16316
  opacity: 0.85,
15143
16317
  depthWrite: false
15144
16318
  });
15145
- const group = new THREE30__namespace.Group();
16319
+ const group = new THREE37__namespace.Group();
15146
16320
  group.name = "XRHoverShell";
15147
16321
  group.userData[XR_HOVER_SHELL_FLAG] = true;
15148
16322
  group.renderOrder = 998;
15149
16323
  object.traverse((child) => {
15150
- if (!(child instanceof THREE30__namespace.Mesh) || !child.geometry) return;
16324
+ if (!(child instanceof THREE37__namespace.Mesh) || !child.geometry) return;
15151
16325
  if (child.userData[XR_HOVER_SHELL_FLAG]) return;
15152
- const clone = new THREE30__namespace.Mesh(child.geometry, material);
16326
+ const clone = new THREE37__namespace.Mesh(child.geometry, material);
15153
16327
  child.updateWorldMatrix(true, false);
15154
16328
  object.updateWorldMatrix(true, false);
15155
16329
  clone.matrixAutoUpdate = false;
15156
- clone.matrix.copy(object.matrixWorld).invert().multiply(child.matrixWorld).scale(new THREE30__namespace.Vector3(SHELL_INFLATE, SHELL_INFLATE, SHELL_INFLATE));
16330
+ clone.matrix.copy(object.matrixWorld).invert().multiply(child.matrixWorld).scale(new THREE37__namespace.Vector3(SHELL_INFLATE, SHELL_INFLATE, SHELL_INFLATE));
15157
16331
  clone.userData[XR_HOVER_SHELL_FLAG] = true;
15158
16332
  clone.raycast = () => {
15159
16333
  };
@@ -15202,7 +16376,7 @@ var _XRHoverVisualSystem = class _XRHoverVisualSystem {
15202
16376
  if (!shell) return;
15203
16377
  shell.parent?.remove(shell);
15204
16378
  shell.traverse((child) => {
15205
- if (child instanceof THREE30__namespace.Mesh) {
16379
+ if (child instanceof THREE37__namespace.Mesh) {
15206
16380
  const material = child.material;
15207
16381
  if (Array.isArray(material)) {
15208
16382
  for (const entry of material) entry.dispose();