@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
package/dist/index.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var THREE60 = require('three');
3
+ var THREE67 = 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');
@@ -44,7 +44,7 @@ function _interopNamespace(e) {
44
44
  return Object.freeze(n);
45
45
  }
46
46
 
47
- var THREE60__namespace = /*#__PURE__*/_interopNamespace(THREE60);
47
+ var THREE67__namespace = /*#__PURE__*/_interopNamespace(THREE67);
48
48
 
49
49
  var __defProp = Object.defineProperty;
50
50
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
@@ -826,14 +826,14 @@ var _AnimationComponent = class _AnimationComponent extends Component {
826
826
  return;
827
827
  }
828
828
  this.importedClips = clips.map((entry) => {
829
- if (entry instanceof THREE60__namespace.AnimationClip) {
829
+ if (entry instanceof THREE67__namespace.AnimationClip) {
830
830
  return { clip: entry, sourceUrl: null };
831
831
  }
832
832
  if (!entry || typeof entry !== "object") {
833
833
  return null;
834
834
  }
835
835
  const candidate = entry;
836
- if (!(candidate.clip instanceof THREE60__namespace.AnimationClip)) {
836
+ if (!(candidate.clip instanceof THREE67__namespace.AnimationClip)) {
837
837
  return null;
838
838
  }
839
839
  return {
@@ -1042,11 +1042,11 @@ function normalizeDistanceModel(distanceModel) {
1042
1042
  }
1043
1043
  __name(normalizeDistanceModel, "normalizeDistanceModel");
1044
1044
  function createConfiguredAudio(listener, options) {
1045
- const sound = options.positional ? new THREE60__namespace.PositionalAudio(listener) : new THREE60__namespace.Audio(listener);
1045
+ const sound = options.positional ? new THREE67__namespace.PositionalAudio(listener) : new THREE67__namespace.Audio(listener);
1046
1046
  sound.setLoop(options.loop);
1047
1047
  sound.setVolume(options.volume);
1048
1048
  sound.setPlaybackRate(options.pitch);
1049
- if (sound instanceof THREE60__namespace.PositionalAudio) {
1049
+ if (sound instanceof THREE67__namespace.PositionalAudio) {
1050
1050
  sound.setRefDistance(options.refDistance);
1051
1051
  sound.setDistanceModel(normalizeDistanceModel(options.distanceModel));
1052
1052
  sound.setRolloffFactor(options.rolloffFactor);
@@ -1056,16 +1056,16 @@ function createConfiguredAudio(listener, options) {
1056
1056
  __name(createConfiguredAudio, "createConfiguredAudio");
1057
1057
  function createAudioEditorHelper() {
1058
1058
  if (typeof document === "undefined") {
1059
- const geometry = new THREE60__namespace.EdgesGeometry(
1060
- new THREE60__namespace.OctahedronGeometry(0.22, 0)
1059
+ const geometry = new THREE67__namespace.EdgesGeometry(
1060
+ new THREE67__namespace.OctahedronGeometry(0.22, 0)
1061
1061
  );
1062
- const material2 = new THREE60__namespace.LineBasicMaterial({
1062
+ const material2 = new THREE67__namespace.LineBasicMaterial({
1063
1063
  color: "#8bc8ff",
1064
1064
  transparent: true,
1065
1065
  opacity: 0.95,
1066
1066
  depthTest: false
1067
1067
  });
1068
- const fallback = new THREE60__namespace.LineSegments(geometry, material2);
1068
+ const fallback = new THREE67__namespace.LineSegments(geometry, material2);
1069
1069
  fallback.renderOrder = 10;
1070
1070
  fallback.userData.ecsHelper = true;
1071
1071
  fallback.visible = false;
@@ -1077,7 +1077,7 @@ function createAudioEditorHelper() {
1077
1077
  canvas.height = size;
1078
1078
  const ctx = canvas.getContext("2d");
1079
1079
  if (!ctx) {
1080
- return new THREE60__namespace.Object3D();
1080
+ return new THREE67__namespace.Object3D();
1081
1081
  }
1082
1082
  ctx.clearRect(0, 0, size, size);
1083
1083
  ctx.fillStyle = "rgba(27, 35, 52, 0.9)";
@@ -1108,14 +1108,14 @@ function createAudioEditorHelper() {
1108
1108
  ctx.beginPath();
1109
1109
  ctx.arc(56, 48, 16, -0.8, 0.8);
1110
1110
  ctx.stroke();
1111
- const texture = new THREE60__namespace.CanvasTexture(canvas);
1111
+ const texture = new THREE67__namespace.CanvasTexture(canvas);
1112
1112
  texture.needsUpdate = true;
1113
- const material = new THREE60__namespace.SpriteMaterial({
1113
+ const material = new THREE67__namespace.SpriteMaterial({
1114
1114
  map: texture,
1115
1115
  transparent: true,
1116
1116
  depthTest: false
1117
1117
  });
1118
- const helper = new THREE60__namespace.Sprite(material);
1118
+ const helper = new THREE67__namespace.Sprite(material);
1119
1119
  helper.scale.set(0.68, 0.68, 0.68);
1120
1120
  helper.renderOrder = 10;
1121
1121
  helper.userData.ecsHelper = true;
@@ -1149,7 +1149,7 @@ var DEFAULT_ORTHO_TOP = 5;
1149
1149
  var DEFAULT_ORTHO_BOTTOM = -5;
1150
1150
  var DEFAULT_ZOOM = 1;
1151
1151
  function inferProjection(camera) {
1152
- if (camera instanceof THREE60__namespace.OrthographicCamera) {
1152
+ if (camera instanceof THREE67__namespace.OrthographicCamera) {
1153
1153
  return "orthographic";
1154
1154
  }
1155
1155
  return "perspective";
@@ -1170,7 +1170,7 @@ function readCameraFar(camera) {
1170
1170
  }
1171
1171
  __name(readCameraFar, "readCameraFar");
1172
1172
  function inferFov(camera) {
1173
- if (camera instanceof THREE60__namespace.PerspectiveCamera && Number.isFinite(camera.fov)) {
1173
+ if (camera instanceof THREE67__namespace.PerspectiveCamera && Number.isFinite(camera.fov)) {
1174
1174
  return camera.fov;
1175
1175
  }
1176
1176
  return DEFAULT_FOV;
@@ -1193,28 +1193,28 @@ function inferFar(camera) {
1193
1193
  }
1194
1194
  __name(inferFar, "inferFar");
1195
1195
  function inferOrthoLeft(camera) {
1196
- if (camera instanceof THREE60__namespace.OrthographicCamera && Number.isFinite(camera.left)) {
1196
+ if (camera instanceof THREE67__namespace.OrthographicCamera && Number.isFinite(camera.left)) {
1197
1197
  return camera.left;
1198
1198
  }
1199
1199
  return DEFAULT_ORTHO_LEFT;
1200
1200
  }
1201
1201
  __name(inferOrthoLeft, "inferOrthoLeft");
1202
1202
  function inferOrthoRight(camera) {
1203
- if (camera instanceof THREE60__namespace.OrthographicCamera && Number.isFinite(camera.right)) {
1203
+ if (camera instanceof THREE67__namespace.OrthographicCamera && Number.isFinite(camera.right)) {
1204
1204
  return camera.right;
1205
1205
  }
1206
1206
  return DEFAULT_ORTHO_RIGHT;
1207
1207
  }
1208
1208
  __name(inferOrthoRight, "inferOrthoRight");
1209
1209
  function inferOrthoTop(camera) {
1210
- if (camera instanceof THREE60__namespace.OrthographicCamera && Number.isFinite(camera.top)) {
1210
+ if (camera instanceof THREE67__namespace.OrthographicCamera && Number.isFinite(camera.top)) {
1211
1211
  return camera.top;
1212
1212
  }
1213
1213
  return DEFAULT_ORTHO_TOP;
1214
1214
  }
1215
1215
  __name(inferOrthoTop, "inferOrthoTop");
1216
1216
  function inferOrthoBottom(camera) {
1217
- if (camera instanceof THREE60__namespace.OrthographicCamera && Number.isFinite(camera.bottom)) {
1217
+ if (camera instanceof THREE67__namespace.OrthographicCamera && Number.isFinite(camera.bottom)) {
1218
1218
  return camera.bottom;
1219
1219
  }
1220
1220
  return DEFAULT_ORTHO_BOTTOM;
@@ -1230,7 +1230,7 @@ __name(inferZoom, "inferZoom");
1230
1230
  var _CameraComponent = class _CameraComponent extends Component {
1231
1231
  constructor(payload = {}) {
1232
1232
  super();
1233
- this.camera = payload.camera ?? new THREE60__namespace.PerspectiveCamera();
1233
+ this.camera = payload.camera ?? new THREE67__namespace.PerspectiveCamera();
1234
1234
  this.type = payload.type ?? "3d";
1235
1235
  this.isDefault = typeof payload.isDefault === "boolean" ? payload.isDefault : false;
1236
1236
  this.projection = payload.projection ?? inferProjection(payload.camera);
@@ -1261,7 +1261,7 @@ var Object3DRef = _Object3DRef;
1261
1261
  // core/factories/components/CameraFactory.ts
1262
1262
  function createCameraComponent(world, entityId, payload = {}) {
1263
1263
  const object = world.getComponent(entityId, Object3DRef)?.object;
1264
- const objectCamera = object instanceof THREE60__namespace.Camera ? object : void 0;
1264
+ const objectCamera = object instanceof THREE67__namespace.Camera ? object : void 0;
1265
1265
  const component = new CameraComponent({
1266
1266
  ...payload,
1267
1267
  camera: payload.camera ?? objectCamera
@@ -1885,15 +1885,15 @@ function createValueState(light) {
1885
1885
  castShadow: light.castShadow ?? false,
1886
1886
  frustumCulling: light.frustumCulled
1887
1887
  };
1888
- if (light instanceof THREE60__namespace.PointLight) {
1888
+ if (light instanceof THREE67__namespace.PointLight) {
1889
1889
  base.distance = light.distance;
1890
1890
  base.decay = light.decay;
1891
- } else if (light instanceof THREE60__namespace.SpotLight) {
1891
+ } else if (light instanceof THREE67__namespace.SpotLight) {
1892
1892
  base.distance = light.distance;
1893
1893
  base.decay = light.decay;
1894
1894
  base.angle = light.angle;
1895
1895
  base.penumbra = light.penumbra;
1896
- } else if (light instanceof THREE60__namespace.RectAreaLight) {
1896
+ } else if (light instanceof THREE67__namespace.RectAreaLight) {
1897
1897
  base.width = light.width;
1898
1898
  base.height = light.height;
1899
1899
  }
@@ -1936,13 +1936,13 @@ function toHexColor2(value, fallback) {
1936
1936
  }
1937
1937
  __name(toHexColor2, "toHexColor");
1938
1938
  function getMaterialType(material) {
1939
- if (material instanceof THREE60__namespace.MeshPhysicalMaterial) {
1939
+ if (material instanceof THREE67__namespace.MeshPhysicalMaterial) {
1940
1940
  return "physical";
1941
1941
  }
1942
- if (material instanceof THREE60__namespace.MeshPhongMaterial) {
1942
+ if (material instanceof THREE67__namespace.MeshPhongMaterial) {
1943
1943
  return "phong";
1944
1944
  }
1945
- if (material instanceof THREE60__namespace.MeshBasicMaterial) {
1945
+ if (material instanceof THREE67__namespace.MeshBasicMaterial) {
1946
1946
  return "basic";
1947
1947
  }
1948
1948
  return "standard";
@@ -1977,8 +1977,8 @@ function createMaterialValue(material) {
1977
1977
  opacity: typeof materialRecord.opacity === "number" ? materialRecord.opacity : 1,
1978
1978
  emissive: toHexColor2(materialRecord.emissive, "#000000"),
1979
1979
  emissiveIntensity: typeof materialRecord.emissiveIntensity === "number" ? materialRecord.emissiveIntensity : 0,
1980
- side: typeof materialRecord.side === "number" ? materialRecord.side : THREE60__namespace.FrontSide,
1981
- blending: typeof materialRecord.blending === "number" ? materialRecord.blending : THREE60__namespace.NormalBlending,
1980
+ side: typeof materialRecord.side === "number" ? materialRecord.side : THREE67__namespace.FrontSide,
1981
+ blending: typeof materialRecord.blending === "number" ? materialRecord.blending : THREE67__namespace.NormalBlending,
1982
1982
  depthTest: materialRecord.depthTest !== false,
1983
1983
  depthWrite: materialRecord.depthWrite !== false,
1984
1984
  wireframe: Boolean(materialRecord.wireframe),
@@ -2010,7 +2010,7 @@ function createMaterialValue(material) {
2010
2010
  materialRecord.lightMap
2011
2011
  ),
2012
2012
  envMap: textureImageSrc(materialRecord.envMap),
2013
- textureFiltering: map?.magFilter === THREE60__namespace.NearestFilter ? "sharp" : "smooth",
2013
+ textureFiltering: map?.magFilter === THREE67__namespace.NearestFilter ? "sharp" : "smooth",
2014
2014
  useMipmaps: map?.generateMipmaps !== false
2015
2015
  };
2016
2016
  }
@@ -2025,8 +2025,8 @@ function defaultMaterialValue() {
2025
2025
  opacity: 1,
2026
2026
  emissive: "#000000",
2027
2027
  emissiveIntensity: 0,
2028
- side: THREE60__namespace.FrontSide,
2029
- blending: THREE60__namespace.NormalBlending,
2028
+ side: THREE67__namespace.FrontSide,
2029
+ blending: THREE67__namespace.NormalBlending,
2030
2030
  depthTest: true,
2031
2031
  depthWrite: true,
2032
2032
  wireframe: false,
@@ -2594,14 +2594,14 @@ var _TransformComponent = class _TransformComponent extends Component {
2594
2594
  z: scale.z ?? 1
2595
2595
  };
2596
2596
  }
2597
- toPositionVector3(target = new THREE60.Vector3()) {
2597
+ toPositionVector3(target = new THREE67.Vector3()) {
2598
2598
  return target.set(this.position.x, this.position.y, this.position.z);
2599
2599
  }
2600
- toRotationEuler(target = new THREE60.Euler(), order = "XYZ") {
2600
+ toRotationEuler(target = new THREE67.Euler(), order = "XYZ") {
2601
2601
  return target.set(this.rotation.x, this.rotation.y, this.rotation.z, order);
2602
2602
  }
2603
- toRotationQuaternion(target = new THREE60.Quaternion(), order = "XYZ") {
2604
- const euler = new THREE60.Euler(
2603
+ toRotationQuaternion(target = new THREE67.Quaternion(), order = "XYZ") {
2604
+ const euler = new THREE67.Euler(
2605
2605
  this.rotation.x,
2606
2606
  this.rotation.y,
2607
2607
  this.rotation.z,
@@ -2609,7 +2609,7 @@ var _TransformComponent = class _TransformComponent extends Component {
2609
2609
  );
2610
2610
  return target.setFromEuler(euler);
2611
2611
  }
2612
- toScaleVector3(target = new THREE60.Vector3()) {
2612
+ toScaleVector3(target = new THREE67.Vector3()) {
2613
2613
  return target.set(this.scale.x, this.scale.y, this.scale.z);
2614
2614
  }
2615
2615
  setPosition(input) {
@@ -2627,8 +2627,8 @@ var _TransformComponent = class _TransformComponent extends Component {
2627
2627
  return this;
2628
2628
  }
2629
2629
  setRotation(input, order = "XYZ") {
2630
- if (input instanceof THREE60.Quaternion) {
2631
- const euler = new THREE60.Euler().setFromQuaternion(input, order);
2630
+ if (input instanceof THREE67.Quaternion) {
2631
+ const euler = new THREE67.Euler().setFromQuaternion(input, order);
2632
2632
  this.rotation.x = euler.x;
2633
2633
  this.rotation.y = euler.y;
2634
2634
  this.rotation.z = euler.z;
@@ -3373,6 +3373,223 @@ function createXRInteractorSideComponent(world, entityId, ...args) {
3373
3373
  }
3374
3374
  __name(createXRInteractorSideComponent, "createXRInteractorSideComponent");
3375
3375
 
3376
+ // core/ui/uiStyle.ts
3377
+ var isRecord = /* @__PURE__ */ __name((value) => typeof value === "object" && value !== null && !Array.isArray(value), "isRecord");
3378
+ var finite = /* @__PURE__ */ __name((value) => typeof value === "number" && Number.isFinite(value), "finite");
3379
+ var nonEmpty = /* @__PURE__ */ __name((value) => typeof value === "string" && value.length > 0, "nonEmpty");
3380
+ function normalizeStops(value) {
3381
+ if (!Array.isArray(value)) return null;
3382
+ const stops = [];
3383
+ for (const stop of value) {
3384
+ if (isRecord(stop) && finite(stop.at) && nonEmpty(stop.color)) {
3385
+ stops.push({ at: Math.min(1, Math.max(0, stop.at)), color: stop.color });
3386
+ }
3387
+ }
3388
+ return stops.length >= 2 ? stops : null;
3389
+ }
3390
+ __name(normalizeStops, "normalizeStops");
3391
+ function normalizePaint(value) {
3392
+ if (!isRecord(value)) return void 0;
3393
+ if (value.kind === "solid" && nonEmpty(value.color)) {
3394
+ return { kind: "solid", color: value.color };
3395
+ }
3396
+ if (value.kind === "linear") {
3397
+ const stops = normalizeStops(value.stops);
3398
+ if (stops) {
3399
+ return {
3400
+ kind: "linear",
3401
+ angle: finite(value.angle) ? value.angle : 180,
3402
+ stops
3403
+ };
3404
+ }
3405
+ }
3406
+ if (value.kind === "radial") {
3407
+ const stops = normalizeStops(value.stops);
3408
+ if (stops) return { kind: "radial", stops };
3409
+ }
3410
+ if (value.kind === "image" && nonEmpty(value.src)) {
3411
+ const fit = value.fit === "contain" || value.fit === "fill" ? value.fit : "cover";
3412
+ return {
3413
+ kind: "image",
3414
+ src: value.src,
3415
+ fit,
3416
+ ...nonEmpty(value.tint) ? { tint: value.tint } : {}
3417
+ };
3418
+ }
3419
+ return void 0;
3420
+ }
3421
+ __name(normalizePaint, "normalizePaint");
3422
+ function normalizeShadow(value) {
3423
+ if (!isRecord(value) || !nonEmpty(value.color)) return void 0;
3424
+ return {
3425
+ x: finite(value.x) ? value.x : 0,
3426
+ y: finite(value.y) ? value.y : 0,
3427
+ blur: finite(value.blur) ? Math.max(0, value.blur) : 0,
3428
+ color: value.color
3429
+ };
3430
+ }
3431
+ __name(normalizeShadow, "normalizeShadow");
3432
+ function normalizeText(value) {
3433
+ if (!isRecord(value)) return void 0;
3434
+ const text = {};
3435
+ if (nonEmpty(value.font)) text.font = value.font;
3436
+ if (finite(value.weight)) text.weight = value.weight;
3437
+ if (finite(value.tracking)) text.tracking = value.tracking;
3438
+ if (value.transform === "upper" || value.transform === "lower") {
3439
+ text.transform = value.transform;
3440
+ }
3441
+ const paint = normalizePaint(value.paint);
3442
+ if (paint) text.paint = paint;
3443
+ if (finite(value.maxLines) && value.maxLines >= 1) {
3444
+ text.maxLines = Math.floor(value.maxLines);
3445
+ }
3446
+ const shadow = normalizeShadow(value.shadow);
3447
+ if (shadow) text.shadow = shadow;
3448
+ return Object.keys(text).length > 0 ? text : void 0;
3449
+ }
3450
+ __name(normalizeText, "normalizeText");
3451
+ function normalizeUiStyle(value) {
3452
+ if (!isRecord(value)) return void 0;
3453
+ const style = {};
3454
+ const fill = normalizePaint(value.fill);
3455
+ if (fill) style.fill = fill;
3456
+ if (isRecord(value.border) && finite(value.border.width)) {
3457
+ const paint = normalizePaint(value.border.paint);
3458
+ if (paint) {
3459
+ style.border = { width: Math.max(0, value.border.width), paint };
3460
+ }
3461
+ }
3462
+ if (finite(value.radius)) {
3463
+ style.radius = Math.max(0, value.radius);
3464
+ } else if (Array.isArray(value.radius) && value.radius.length === 4 && value.radius.every(finite)) {
3465
+ style.radius = value.radius.map((r) => Math.max(0, r));
3466
+ }
3467
+ if (Array.isArray(value.shadows)) {
3468
+ const shadows = value.shadows.map(normalizeShadow).filter((s) => !!s);
3469
+ if (shadows.length > 0) style.shadows = shadows;
3470
+ }
3471
+ if (isRecord(value.edge) && finite(value.edge.width) && nonEmpty(value.edge.color)) {
3472
+ style.edge = {
3473
+ width: Math.max(0, value.edge.width),
3474
+ color: value.edge.color
3475
+ };
3476
+ }
3477
+ if (finite(value.opacity)) {
3478
+ style.opacity = Math.min(1, Math.max(0, value.opacity));
3479
+ }
3480
+ const text = normalizeText(value.text);
3481
+ if (text) style.text = text;
3482
+ return Object.keys(style).length > 0 ? style : void 0;
3483
+ }
3484
+ __name(normalizeUiStyle, "normalizeUiStyle");
3485
+ function normalizeUiTokens(value) {
3486
+ if (!isRecord(value)) return void 0;
3487
+ const tokens = {};
3488
+ for (const [key, token] of Object.entries(value)) {
3489
+ if (nonEmpty(token) && key.length > 0 && !key.includes("/")) {
3490
+ tokens[key] = token;
3491
+ }
3492
+ }
3493
+ return Object.keys(tokens).length > 0 ? tokens : void 0;
3494
+ }
3495
+ __name(normalizeUiTokens, "normalizeUiTokens");
3496
+ function resolveToken(value, tokens, fallback = value) {
3497
+ if (!value.startsWith("$")) return value;
3498
+ return tokens?.[value.slice(1)] ?? fallback;
3499
+ }
3500
+ __name(resolveToken, "resolveToken");
3501
+ function resolvePaint(paint, tokens) {
3502
+ switch (paint.kind) {
3503
+ case "solid":
3504
+ return { kind: "solid", color: resolveToken(paint.color, tokens) };
3505
+ case "linear":
3506
+ case "radial":
3507
+ return {
3508
+ ...paint,
3509
+ stops: paint.stops.map((stop) => ({
3510
+ at: stop.at,
3511
+ color: resolveToken(stop.color, tokens)
3512
+ }))
3513
+ };
3514
+ case "image":
3515
+ return {
3516
+ ...paint,
3517
+ src: resolveToken(paint.src, tokens),
3518
+ ...paint.tint ? { tint: resolveToken(paint.tint, tokens) } : {}
3519
+ };
3520
+ }
3521
+ }
3522
+ __name(resolvePaint, "resolvePaint");
3523
+ function solidOf(paint) {
3524
+ if (paint.kind === "solid") return paint.color;
3525
+ if (paint.kind === "image") return paint.tint ?? "#ffffff";
3526
+ return paint.stops[0].color;
3527
+ }
3528
+ __name(solidOf, "solidOf");
3529
+ function radiusOf(radius, fallback) {
3530
+ if (Array.isArray(radius)) return radius;
3531
+ const r = radius ?? fallback;
3532
+ return [r, r, r, r];
3533
+ }
3534
+ __name(radiusOf, "radiusOf");
3535
+ function resolveChrome(flat, style, tokens) {
3536
+ const borderWidth = style?.border?.width ?? flat.borderWidth;
3537
+ const border = borderWidth > 0 ? {
3538
+ width: borderWidth,
3539
+ color: style?.border ? solidOf(resolvePaint(style.border.paint, tokens)) : resolveToken(flat.borderColor, tokens)
3540
+ } : null;
3541
+ return {
3542
+ fill: style?.fill ? resolvePaint(style.fill, tokens) : { kind: "solid", color: resolveToken(flat.backgroundColor, tokens) },
3543
+ fillOpacity: style?.fill ? 1 : flat.backgroundOpacity,
3544
+ radius: radiusOf(style?.radius, flat.cornerRadius),
3545
+ border,
3546
+ shadows: (style?.shadows ?? []).map((shadow) => ({
3547
+ ...shadow,
3548
+ color: resolveToken(shadow.color, tokens)
3549
+ })),
3550
+ edge: style?.edge ? { ...style.edge, color: resolveToken(style.edge.color, tokens) } : null
3551
+ };
3552
+ }
3553
+ __name(resolveChrome, "resolveChrome");
3554
+ function resolveText(flat, style, tokens) {
3555
+ const text = style?.text;
3556
+ return {
3557
+ ...flat,
3558
+ color: resolveToken(flat.color, tokens),
3559
+ fontWeight: text?.weight ?? flat.fontWeight,
3560
+ paint: text?.paint ? resolvePaint(text.paint, tokens) : { kind: "solid", color: resolveToken(flat.color, tokens) },
3561
+ font: text?.font ? resolveToken(text.font, tokens) : null,
3562
+ tracking: text?.tracking ?? 0,
3563
+ transform: text?.transform ?? null,
3564
+ maxLines: text?.maxLines ?? null,
3565
+ shadow: text?.shadow ? {
3566
+ ...text.shadow,
3567
+ color: resolveToken(text.shadow.color, tokens)
3568
+ } : null
3569
+ };
3570
+ }
3571
+ __name(resolveText, "resolveText");
3572
+ function chromeBleed(chrome) {
3573
+ let bleed = 0;
3574
+ for (const shadow of chrome.shadows) {
3575
+ bleed = Math.max(
3576
+ bleed,
3577
+ shadow.blur + Math.max(Math.abs(shadow.x), Math.abs(shadow.y))
3578
+ );
3579
+ }
3580
+ return bleed;
3581
+ }
3582
+ __name(chromeBleed, "chromeBleed");
3583
+ function styleAssets(chrome, text) {
3584
+ const images2 = [];
3585
+ const fonts2 = [];
3586
+ if (chrome?.fill?.kind === "image") images2.push(chrome.fill.src);
3587
+ if (text?.paint.kind === "image") images2.push(text.paint.src);
3588
+ if (text?.font) fonts2.push(text.font);
3589
+ return { images: images2, fonts: fonts2 };
3590
+ }
3591
+ __name(styleAssets, "styleAssets");
3592
+
3376
3593
  // core/components/UIButton.ts
3377
3594
  var _UIButtonComponent = class _UIButtonComponent extends Component {
3378
3595
  constructor(options) {
@@ -3389,6 +3606,13 @@ var _UIButtonComponent = class _UIButtonComponent extends Component {
3389
3606
  this.cornerRadius = options?.cornerRadius ?? 0.02;
3390
3607
  this.borderWidth = options?.borderWidth ?? 2e-3;
3391
3608
  this.borderColor = options?.borderColor ?? "#3a3a3a";
3609
+ if (options?.pixelDensity !== void 0) {
3610
+ this.pixelDensity = options.pixelDensity;
3611
+ }
3612
+ const style = normalizeUiStyle(options?.style);
3613
+ if (style) this.style = style;
3614
+ if (options?.icon) this.icon = options.icon;
3615
+ if (options?.iconPosition) this.iconPosition = options.iconPosition;
3392
3616
  }
3393
3617
  };
3394
3618
  __name(_UIButtonComponent, "UIButtonComponent");
@@ -3415,6 +3639,10 @@ var _UIPanelComponent = class _UIPanelComponent extends Component {
3415
3639
  this.borderColor = options?.borderColor ?? "#333333";
3416
3640
  this.pixelDensity = options?.pixelDensity ?? 1024;
3417
3641
  this.follow = options?.follow ?? "world";
3642
+ const style = normalizeUiStyle(options?.style);
3643
+ if (style) this.style = style;
3644
+ const tokens = normalizeUiTokens(options?.tokens);
3645
+ if (tokens) this.tokens = tokens;
3418
3646
  }
3419
3647
  };
3420
3648
  __name(_UIPanelComponent, "UIPanelComponent");
@@ -3443,6 +3671,14 @@ var _UISliderComponent = class _UISliderComponent extends Component {
3443
3671
  this.trackColor = options?.trackColor ?? "#2a2a2a";
3444
3672
  this.accentColor = options?.accentColor ?? "#ff6b35";
3445
3673
  this.knobColor = options?.knobColor ?? "#ffffff";
3674
+ if (options?.pixelDensity !== void 0) {
3675
+ this.pixelDensity = options.pixelDensity;
3676
+ }
3677
+ const style = normalizeUiStyle(options?.style);
3678
+ if (style) this.style = style;
3679
+ if (options?.variant) this.variant = options.variant;
3680
+ if (options?.step !== void 0) this.step = options.step;
3681
+ if (options?.readOnly !== void 0) this.readOnly = options.readOnly;
3446
3682
  }
3447
3683
  };
3448
3684
  __name(_UISliderComponent, "UISliderComponent");
@@ -3470,6 +3706,11 @@ var _UITextComponent = class _UITextComponent extends Component {
3470
3706
  this.valign = options?.valign ?? "middle";
3471
3707
  this.lineHeight = options?.lineHeight ?? 1.25;
3472
3708
  this.padding = options?.padding ?? 0.01;
3709
+ if (options?.pixelDensity !== void 0) {
3710
+ this.pixelDensity = options.pixelDensity;
3711
+ }
3712
+ const style = normalizeUiStyle(options?.style);
3713
+ if (style) this.style = style;
3473
3714
  }
3474
3715
  };
3475
3716
  __name(_UITextComponent, "UITextComponent");
@@ -3494,6 +3735,12 @@ var _UIToggleComponent = class _UIToggleComponent extends Component {
3494
3735
  this.trackColor = options?.trackColor ?? "#2a2a2a";
3495
3736
  this.accentColor = options?.accentColor ?? "#ff6b35";
3496
3737
  this.knobColor = options?.knobColor ?? "#ffffff";
3738
+ if (options?.pixelDensity !== void 0) {
3739
+ this.pixelDensity = options.pixelDensity;
3740
+ }
3741
+ const style = normalizeUiStyle(options?.style);
3742
+ if (style) this.style = style;
3743
+ if (options?.variant) this.variant = options.variant;
3497
3744
  }
3498
3745
  };
3499
3746
  __name(_UIToggleComponent, "UIToggleComponent");
@@ -3846,7 +4093,7 @@ function findModelBone(root, ref) {
3846
4093
  for (const b of bones) if (!byName.has(b.name)) byName.set(b.name, b);
3847
4094
  const name = ref.bone ?? "";
3848
4095
  if (name) {
3849
- const hit = byName.get(name) ?? byName.get(THREE60__namespace.PropertyBinding.sanitizeNodeName(name));
4096
+ const hit = byName.get(name) ?? byName.get(THREE67__namespace.PropertyBinding.sanitizeNodeName(name));
3850
4097
  if (hit) return hit;
3851
4098
  }
3852
4099
  if (ref.humanoid) {
@@ -3907,7 +4154,7 @@ var SHARED_MODEL_RESOURCE_FLAG = "spatialEcsSharedModelResource";
3907
4154
  var modelCache = /* @__PURE__ */ new Map();
3908
4155
  function markSharedResources(root) {
3909
4156
  root.traverse((child) => {
3910
- if (!(child instanceof THREE60__namespace.Mesh)) {
4157
+ if (!(child instanceof THREE67__namespace.Mesh)) {
3911
4158
  return;
3912
4159
  }
3913
4160
  if (child.geometry) {
@@ -4081,7 +4328,7 @@ function createModel({
4081
4328
  payload,
4082
4329
  isEditor
4083
4330
  }) {
4084
- const anchor = new THREE60__namespace.Object3D();
4331
+ const anchor = new THREE67__namespace.Object3D();
4085
4332
  const modelPath = payload.path ?? "";
4086
4333
  const entityId = createBaseEntity(world, anchor, {
4087
4334
  label: payload.name ?? "Model",
@@ -4186,8 +4433,8 @@ __name(reloadModelEntity, "reloadModelEntity");
4186
4433
 
4187
4434
  // core/systems/ObjectManagementSystem.ts
4188
4435
  var BONE_SOCKET_FLAG = "__reluBoneSocket";
4189
- var _boneScale = new THREE60__namespace.Vector3();
4190
- var _anchorScale = new THREE60__namespace.Vector3();
4436
+ var _boneScale = new THREE67__namespace.Vector3();
4437
+ var _anchorScale = new THREE67__namespace.Vector3();
4191
4438
  function createTransform(input) {
4192
4439
  return new TransformComponent(input);
4193
4440
  }
@@ -4226,10 +4473,10 @@ var _ObjectManagementSystem = class _ObjectManagementSystem {
4226
4473
  }
4227
4474
  getOrCreateAudioListener() {
4228
4475
  const existing = this.camera.userData.ecsAudioListener;
4229
- if (existing instanceof THREE60__namespace.AudioListener) {
4476
+ if (existing instanceof THREE67__namespace.AudioListener) {
4230
4477
  return existing;
4231
4478
  }
4232
- const listener = new THREE60__namespace.AudioListener();
4479
+ const listener = new THREE67__namespace.AudioListener();
4233
4480
  this.camera.add(listener);
4234
4481
  this.camera.userData.ecsAudioListener = listener;
4235
4482
  return listener;
@@ -4303,7 +4550,7 @@ var _ObjectManagementSystem = class _ObjectManagementSystem {
4303
4550
  }
4304
4551
  if (dispose && object3d) {
4305
4552
  object3d.traverse((child) => {
4306
- if (child instanceof THREE60__namespace.Mesh) {
4553
+ if (child instanceof THREE67__namespace.Mesh) {
4307
4554
  if (!child.geometry?.userData?.[SHARED_MODEL_RESOURCE_FLAG]) {
4308
4555
  child.geometry?.dispose();
4309
4556
  }
@@ -4455,7 +4702,7 @@ var _ObjectManagementSystem = class _ObjectManagementSystem {
4455
4702
  return null;
4456
4703
  }
4457
4704
  this.boneMisses.delete(entityId);
4458
- const socket = new THREE60__namespace.Object3D();
4705
+ const socket = new THREE67__namespace.Object3D();
4459
4706
  socket.name = `socket:${bone.name}`;
4460
4707
  socket.userData[BONE_SOCKET_FLAG] = entityId;
4461
4708
  bone.add(socket);
@@ -4629,10 +4876,10 @@ __name(createBaseEntity, "createBaseEntity");
4629
4876
  // core/factories/objects/AudioFactory.ts
4630
4877
  function getOrCreateListener(camera) {
4631
4878
  const existing = camera.userData.ecsAudioListener;
4632
- if (existing instanceof THREE60__namespace.AudioListener) {
4879
+ if (existing instanceof THREE67__namespace.AudioListener) {
4633
4880
  return existing;
4634
4881
  }
4635
- const listener = new THREE60__namespace.AudioListener();
4882
+ const listener = new THREE67__namespace.AudioListener();
4636
4883
  camera.add(listener);
4637
4884
  camera.userData.ecsAudioListener = listener;
4638
4885
  return listener;
@@ -4645,7 +4892,7 @@ function createAudio({
4645
4892
  payload,
4646
4893
  isEditor
4647
4894
  }) {
4648
- const anchor = new THREE60__namespace.Object3D();
4895
+ const anchor = new THREE67__namespace.Object3D();
4649
4896
  const entityId = createBaseEntity(world, anchor, {
4650
4897
  label: payload.name ?? "Audio",
4651
4898
  entityType: "audio",
@@ -4667,7 +4914,7 @@ function createAudio({
4667
4914
  return entityId;
4668
4915
  }
4669
4916
  if (!isEditor) {
4670
- const loader = new THREE60__namespace.AudioLoader();
4917
+ const loader = new THREE67__namespace.AudioLoader();
4671
4918
  loader.load(
4672
4919
  audio.path,
4673
4920
  (buffer) => {
@@ -4742,7 +4989,7 @@ function applyCameraSettings(camera, options) {
4742
4989
  const near = typeof options.near === "number" ? options.near : DEFAULT_NEAR2;
4743
4990
  const far = typeof options.far === "number" ? options.far : DEFAULT_FAR2;
4744
4991
  const projection = options.projection;
4745
- if (camera instanceof THREE60__namespace.PerspectiveCamera) {
4992
+ if (camera instanceof THREE67__namespace.PerspectiveCamera) {
4746
4993
  if (!projection || projection === "perspective") {
4747
4994
  camera.fov = typeof options.fov === "number" ? options.fov : DEFAULT_FOV2;
4748
4995
  }
@@ -4752,7 +4999,7 @@ function applyCameraSettings(camera, options) {
4752
4999
  camera.updateProjectionMatrix();
4753
5000
  return;
4754
5001
  }
4755
- if (camera instanceof THREE60__namespace.OrthographicCamera) {
5002
+ if (camera instanceof THREE67__namespace.OrthographicCamera) {
4756
5003
  camera.left = typeof options.left === "number" ? options.left : -DEFAULT_ORTHOGRAPHIC_HALF_HEIGHT;
4757
5004
  camera.right = typeof options.right === "number" ? options.right : DEFAULT_ORTHOGRAPHIC_HALF_HEIGHT;
4758
5005
  camera.top = typeof options.top === "number" ? options.top : DEFAULT_ORTHOGRAPHIC_HALF_HEIGHT;
@@ -4773,7 +5020,7 @@ function createCameraObject(options) {
4773
5020
  const bottom = options.bottom ?? -DEFAULT_ORTHOGRAPHIC_HALF_HEIGHT;
4774
5021
  const left = options.left ?? -DEFAULT_ORTHOGRAPHIC_HALF_HEIGHT;
4775
5022
  const right = options.right ?? DEFAULT_ORTHOGRAPHIC_HALF_HEIGHT;
4776
- const camera = new THREE60__namespace.OrthographicCamera(
5023
+ const camera = new THREE67__namespace.OrthographicCamera(
4777
5024
  left,
4778
5025
  right,
4779
5026
  top,
@@ -4785,7 +5032,7 @@ function createCameraObject(options) {
4785
5032
  camera.updateProjectionMatrix();
4786
5033
  return camera;
4787
5034
  }
4788
- return new THREE60__namespace.PerspectiveCamera(
5035
+ return new THREE67__namespace.PerspectiveCamera(
4789
5036
  options.fov ?? DEFAULT_FOV2,
4790
5037
  readViewportAspect(),
4791
5038
  near,
@@ -4803,7 +5050,7 @@ function createCamera({ world, scene, camera, payload, isEditor }) {
4803
5050
  transformInput: payload.transform
4804
5051
  });
4805
5052
  const objectRef = world.getComponent(entityId, Object3DRef);
4806
- if (objectRef?.object instanceof THREE60__namespace.Camera) {
5053
+ if (objectRef?.object instanceof THREE67__namespace.Camera) {
4807
5054
  objectRef.object.userData.entityId = entityId;
4808
5055
  }
4809
5056
  const componentOptions = {
@@ -4835,9 +5082,9 @@ function createCapsule({
4835
5082
  scene,
4836
5083
  payload
4837
5084
  }) {
4838
- const geometry = new THREE60__namespace.CapsuleGeometry(0.5, 1, 4, 8);
4839
- const material = new THREE60__namespace.MeshStandardMaterial({ color: "#ffffff" });
4840
- const mesh = new THREE60__namespace.Mesh(geometry, material);
5085
+ const geometry = new THREE67__namespace.CapsuleGeometry(0.5, 1, 4, 8);
5086
+ const material = new THREE67__namespace.MeshStandardMaterial({ color: "#ffffff" });
5087
+ const mesh = new THREE67__namespace.Mesh(geometry, material);
4841
5088
  const entityId = createBaseEntity(world, mesh, {
4842
5089
  label: payload.name ?? "Capsule",
4843
5090
  entityType: "capsule",
@@ -4857,9 +5104,9 @@ function createCapsule({
4857
5104
  }
4858
5105
  __name(createCapsule, "createCapsule");
4859
5106
  function createCube({ world, scene, payload }) {
4860
- const geometry = new THREE60__namespace.BoxGeometry(1, 1, 1);
4861
- const material = new THREE60__namespace.MeshStandardMaterial({ color: "#ffffff" });
4862
- const mesh = new THREE60__namespace.Mesh(geometry, material);
5107
+ const geometry = new THREE67__namespace.BoxGeometry(1, 1, 1);
5108
+ const material = new THREE67__namespace.MeshStandardMaterial({ color: "#ffffff" });
5109
+ const mesh = new THREE67__namespace.Mesh(geometry, material);
4863
5110
  const entityId = createBaseEntity(world, mesh, {
4864
5111
  label: payload.name ?? "Cube",
4865
5112
  entityType: "cube",
@@ -4884,9 +5131,9 @@ function createCylinder({
4884
5131
  scene,
4885
5132
  payload
4886
5133
  }) {
4887
- const geometry = new THREE60__namespace.CylinderGeometry(0.5, 0.5, 1, 20);
4888
- const material = new THREE60__namespace.MeshStandardMaterial({ color: "#ffffff" });
4889
- const mesh = new THREE60__namespace.Mesh(geometry, material);
5134
+ const geometry = new THREE67__namespace.CylinderGeometry(0.5, 0.5, 1, 20);
5135
+ const material = new THREE67__namespace.MeshStandardMaterial({ color: "#ffffff" });
5136
+ const mesh = new THREE67__namespace.Mesh(geometry, material);
4890
5137
  const entityId = createBaseEntity(world, mesh, {
4891
5138
  label: payload.name ?? "Cylinder",
4892
5139
  entityType: "cylinder",
@@ -4906,7 +5153,7 @@ function createCylinder({
4906
5153
  }
4907
5154
  __name(createCylinder, "createCylinder");
4908
5155
  function createEmpty({ world, scene, payload, isEditor }) {
4909
- const object = new THREE60__namespace.Group();
5156
+ const object = new THREE67__namespace.Group();
4910
5157
  const entityId = createBaseEntity(world, object, {
4911
5158
  label: payload.name ?? "Empty",
4912
5159
  entityType: "empty",
@@ -4917,7 +5164,7 @@ function createEmpty({ world, scene, payload, isEditor }) {
4917
5164
  }
4918
5165
  __name(createEmpty, "createEmpty");
4919
5166
  function createImage({ world, scene, payload }) {
4920
- const anchor = new THREE60__namespace.Object3D();
5167
+ const anchor = new THREE67__namespace.Object3D();
4921
5168
  const imagePath = payload.path ?? "";
4922
5169
  const entityId = createBaseEntity(world, anchor, {
4923
5170
  label: payload.name ?? "Image",
@@ -4930,7 +5177,7 @@ function createImage({ world, scene, payload }) {
4930
5177
  if (!imagePath) {
4931
5178
  return entityId;
4932
5179
  }
4933
- const loader = new THREE60__namespace.TextureLoader();
5180
+ const loader = new THREE67__namespace.TextureLoader();
4934
5181
  loader.load(
4935
5182
  imagePath,
4936
5183
  (texture) => {
@@ -4938,13 +5185,13 @@ function createImage({ world, scene, payload }) {
4938
5185
  texture.dispose();
4939
5186
  return;
4940
5187
  }
4941
- const material = new THREE60__namespace.MeshBasicMaterial({
5188
+ const material = new THREE67__namespace.MeshBasicMaterial({
4942
5189
  map: texture,
4943
5190
  transparent: true,
4944
- side: THREE60__namespace.DoubleSide
5191
+ side: THREE67__namespace.DoubleSide
4945
5192
  });
4946
- const geometry = new THREE60__namespace.PlaneGeometry(1, 1);
4947
- const plane = new THREE60__namespace.Mesh(geometry, material);
5193
+ const geometry = new THREE67__namespace.PlaneGeometry(1, 1);
5194
+ const plane = new THREE67__namespace.Mesh(geometry, material);
4948
5195
  plane.userData.entityId = entityId;
4949
5196
  const textureImage = texture.image;
4950
5197
  const width = textureImage?.width ?? 1;
@@ -4990,7 +5237,7 @@ function createImageTarget({
4990
5237
  payload,
4991
5238
  isEditor
4992
5239
  }) {
4993
- const anchor = new THREE60__namespace.Object3D();
5240
+ const anchor = new THREE67__namespace.Object3D();
4994
5241
  const imagePath = payload.path ?? "";
4995
5242
  const entityId = createBaseEntity(world, anchor, {
4996
5243
  label: payload.name ?? "Image Target",
@@ -5007,7 +5254,7 @@ function createImageTarget({
5007
5254
  if (!isEditor || !imagePath) {
5008
5255
  return entityId;
5009
5256
  }
5010
- const loader = new THREE60__namespace.TextureLoader();
5257
+ const loader = new THREE67__namespace.TextureLoader();
5011
5258
  loader.load(
5012
5259
  imagePath,
5013
5260
  (texture) => {
@@ -5015,13 +5262,13 @@ function createImageTarget({
5015
5262
  texture.dispose();
5016
5263
  return;
5017
5264
  }
5018
- const material = new THREE60__namespace.MeshBasicMaterial({
5265
+ const material = new THREE67__namespace.MeshBasicMaterial({
5019
5266
  map: texture,
5020
5267
  transparent: true,
5021
- side: THREE60__namespace.DoubleSide
5268
+ side: THREE67__namespace.DoubleSide
5022
5269
  });
5023
- const geometry = new THREE60__namespace.PlaneGeometry(1, 1);
5024
- const plane = new THREE60__namespace.Mesh(geometry, material);
5270
+ const geometry = new THREE67__namespace.PlaneGeometry(1, 1);
5271
+ const plane = new THREE67__namespace.Mesh(geometry, material);
5025
5272
  plane.userData.entityId = entityId;
5026
5273
  const textureImage = texture.image;
5027
5274
  const width = textureImage?.width ?? 1;
@@ -5072,7 +5319,7 @@ function createInstancedMesh({
5072
5319
  scene,
5073
5320
  payload
5074
5321
  }) {
5075
- const anchor = new THREE60__namespace.Object3D();
5322
+ const anchor = new THREE67__namespace.Object3D();
5076
5323
  const opts = payload.options ?? {};
5077
5324
  const meshKind = isInstancedMeshKind(opts.meshKind) ? opts.meshKind : "cube";
5078
5325
  const meshParameters = opts.meshParameters && typeof opts.meshParameters === "object" ? opts.meshParameters : void 0;
@@ -5143,7 +5390,7 @@ function createAmbientLight({
5143
5390
  scene,
5144
5391
  payload
5145
5392
  }) {
5146
- const light = new THREE60__namespace.AmbientLight("#ffffff", 0.6);
5393
+ const light = new THREE67__namespace.AmbientLight("#ffffff", 0.6);
5147
5394
  const entityId = createBaseEntity(world, light, {
5148
5395
  label: payload.name ?? "Ambient Light",
5149
5396
  entityType: "ambientLight",
@@ -5157,10 +5404,10 @@ __name(createAmbientLight, "createAmbientLight");
5157
5404
  var unsupportedWebGpuFallbackBackend = {
5158
5405
  kind: "webgpu",
5159
5406
  createPostprocessing: /* @__PURE__ */ __name(() => null, "createPostprocessing"),
5160
- createChromaKeyMaterial: /* @__PURE__ */ __name((texture) => new THREE60__namespace.MeshBasicMaterial({
5407
+ createChromaKeyMaterial: /* @__PURE__ */ __name((texture) => new THREE67__namespace.MeshBasicMaterial({
5161
5408
  map: texture,
5162
5409
  transparent: true,
5163
- side: THREE60__namespace.DoubleSide
5410
+ side: THREE67__namespace.DoubleSide
5164
5411
  }), "createChromaKeyMaterial"),
5165
5412
  updateChromaKeyMaterial: /* @__PURE__ */ __name(() => false, "updateChromaKeyMaterial")
5166
5413
  };
@@ -5189,7 +5436,7 @@ var PixelationShader = {
5189
5436
  name: "PixelationShader",
5190
5437
  uniforms: {
5191
5438
  tDiffuse: { value: null },
5192
- resolution: { value: new THREE60__namespace.Vector2(1, 1) },
5439
+ resolution: { value: new THREE67__namespace.Vector2(1, 1) },
5193
5440
  pixelSize: { value: 6 }
5194
5441
  },
5195
5442
  vertexShader: (
@@ -5259,10 +5506,10 @@ function createWebglPostprocessing({
5259
5506
  );
5260
5507
  }
5261
5508
  if (values.bloom.enabled) {
5262
- const size = renderer.getSize(new THREE60__namespace.Vector2());
5509
+ const size = renderer.getSize(new THREE67__namespace.Vector2());
5263
5510
  composer.addPass(
5264
5511
  new UnrealBloomPass_js.UnrealBloomPass(
5265
- new THREE60__namespace.Vector2(size.width, size.height),
5512
+ new THREE67__namespace.Vector2(size.width, size.height),
5266
5513
  values.bloom.intensity,
5267
5514
  values.bloom.radius,
5268
5515
  values.bloom.threshold
@@ -5307,7 +5554,7 @@ function createWebglPostprocessing({
5307
5554
  if (values.dotScreen.enabled) {
5308
5555
  composer.addPass(
5309
5556
  new DotScreenPass_js.DotScreenPass(
5310
- new THREE60__namespace.Vector2(0, 0),
5557
+ new THREE67__namespace.Vector2(0, 0),
5311
5558
  values.dotScreen.angle ?? values.dotScreen.dotScreenAngle,
5312
5559
  values.dotScreen.scale ?? values.dotScreen.dotScreenScale
5313
5560
  )
@@ -5356,10 +5603,10 @@ function createWebglPostprocessing({
5356
5603
  __name(createWebglPostprocessing, "createWebglPostprocessing");
5357
5604
  var CHROMA_KEY_FLAG = "ecsChromaKey";
5358
5605
  function createWebglChromaKeyMaterial(texture, options) {
5359
- const material = new THREE60__namespace.ShaderMaterial({
5606
+ const material = new THREE67__namespace.ShaderMaterial({
5360
5607
  uniforms: {
5361
5608
  tex: { value: texture },
5362
- keyColor: { value: new THREE60__namespace.Color(options.keyColor) },
5609
+ keyColor: { value: new THREE67__namespace.Color(options.keyColor) },
5363
5610
  similarity: { value: options.similarity },
5364
5611
  smoothness: { value: options.smoothness },
5365
5612
  spill: { value: options.spill }
@@ -5391,14 +5638,14 @@ function createWebglChromaKeyMaterial(texture, options) {
5391
5638
  }
5392
5639
  `,
5393
5640
  transparent: true,
5394
- side: THREE60__namespace.DoubleSide
5641
+ side: THREE67__namespace.DoubleSide
5395
5642
  });
5396
5643
  material.userData[CHROMA_KEY_FLAG] = true;
5397
5644
  return material;
5398
5645
  }
5399
5646
  __name(createWebglChromaKeyMaterial, "createWebglChromaKeyMaterial");
5400
5647
  function updateWebglChromaKeyMaterial(material, options) {
5401
- if (!(material instanceof THREE60__namespace.ShaderMaterial)) return false;
5648
+ if (!(material instanceof THREE67__namespace.ShaderMaterial)) return false;
5402
5649
  const { keyColor, similarity, smoothness, spill } = material.uniforms;
5403
5650
  if (!keyColor || !similarity || !smoothness || !spill) return false;
5404
5651
  keyColor.value.set(options.keyColor);
@@ -5475,7 +5722,7 @@ function createAreaLight({
5475
5722
  isEditor
5476
5723
  }) {
5477
5724
  readWorldRenderBackend(world).prepareAreaLights?.(world);
5478
- const light = new THREE60__namespace.RectAreaLight("#ffffff", 5, 4, 4);
5725
+ const light = new THREE67__namespace.RectAreaLight("#ffffff", 5, 4, 4);
5479
5726
  light.lookAt(0, 0, 0);
5480
5727
  const entityId = createBaseEntity(world, light, {
5481
5728
  label: payload.name ?? "Area Light",
@@ -5504,7 +5751,7 @@ function createDirectionalLight({
5504
5751
  payload,
5505
5752
  isEditor
5506
5753
  }) {
5507
- const light = new THREE60__namespace.DirectionalLight("#ffffff", 1.5);
5754
+ const light = new THREE67__namespace.DirectionalLight("#ffffff", 1.5);
5508
5755
  light.castShadow = true;
5509
5756
  const entityId = createBaseEntity(world, light, {
5510
5757
  label: payload.name ?? "Directional Light",
@@ -5515,7 +5762,7 @@ function createDirectionalLight({
5515
5762
  });
5516
5763
  createLightComponent(world, entityId, light);
5517
5764
  if (isEditor) {
5518
- const helper = new THREE60__namespace.DirectionalLightHelper(light, 1.8, "#38bdf8");
5765
+ const helper = new THREE67__namespace.DirectionalLightHelper(light, 1.8, "#38bdf8");
5519
5766
  helper.onBeforeRender = () => {
5520
5767
  helper.update();
5521
5768
  };
@@ -5533,7 +5780,7 @@ function createPointLight({
5533
5780
  payload,
5534
5781
  isEditor
5535
5782
  }) {
5536
- const light = new THREE60__namespace.PointLight("#ffffff", 1, 10);
5783
+ const light = new THREE67__namespace.PointLight("#ffffff", 1, 10);
5537
5784
  const entityId = createBaseEntity(world, light, {
5538
5785
  label: payload.name ?? "Point Light",
5539
5786
  entityType: "pointLight",
@@ -5543,7 +5790,7 @@ function createPointLight({
5543
5790
  });
5544
5791
  createLightComponent(world, entityId, light);
5545
5792
  if (isEditor) {
5546
- const helper = new THREE60__namespace.PointLightHelper(light, 0.35, "#fbbf24");
5793
+ const helper = new THREE67__namespace.PointLightHelper(light, 0.35, "#fbbf24");
5547
5794
  helper.onBeforeRender = () => {
5548
5795
  helper.update();
5549
5796
  };
@@ -5561,7 +5808,7 @@ function createSpotLight({
5561
5808
  payload,
5562
5809
  isEditor
5563
5810
  }) {
5564
- const light = new THREE60__namespace.SpotLight("#ffffff", 1, 20, Math.PI / 6, 0.1, 1);
5811
+ const light = new THREE67__namespace.SpotLight("#ffffff", 1, 20, Math.PI / 6, 0.1, 1);
5565
5812
  const entityId = createBaseEntity(world, light, {
5566
5813
  label: payload.name ?? "Spot Light",
5567
5814
  entityType: "spotLight",
@@ -5571,7 +5818,7 @@ function createSpotLight({
5571
5818
  });
5572
5819
  createLightComponent(world, entityId, light);
5573
5820
  if (isEditor) {
5574
- const helper = new THREE60__namespace.SpotLightHelper(light, "#f59e0b");
5821
+ const helper = new THREE67__namespace.SpotLightHelper(light, "#f59e0b");
5575
5822
  helper.onBeforeRender = () => {
5576
5823
  helper.update();
5577
5824
  };
@@ -5584,12 +5831,12 @@ function createSpotLight({
5584
5831
  }
5585
5832
  __name(createSpotLight, "createSpotLight");
5586
5833
  function createPlane({ world, scene, payload }) {
5587
- const geometry = new THREE60__namespace.PlaneGeometry(100, 100);
5588
- const material = new THREE60__namespace.MeshStandardMaterial({
5834
+ const geometry = new THREE67__namespace.PlaneGeometry(100, 100);
5835
+ const material = new THREE67__namespace.MeshStandardMaterial({
5589
5836
  color: "#ffffff",
5590
- side: THREE60__namespace.DoubleSide
5837
+ side: THREE67__namespace.DoubleSide
5591
5838
  });
5592
- const mesh = new THREE60__namespace.Mesh(geometry, material);
5839
+ const mesh = new THREE67__namespace.Mesh(geometry, material);
5593
5840
  const entityId = createBaseEntity(
5594
5841
  world,
5595
5842
  mesh,
@@ -5625,7 +5872,7 @@ function createScriptObject({
5625
5872
  scene,
5626
5873
  payload
5627
5874
  }) {
5628
- const object = new THREE60__namespace.Group();
5875
+ const object = new THREE67__namespace.Group();
5629
5876
  const entityId = createBaseEntity(world, object, {
5630
5877
  label: payload.name ?? "Script Object",
5631
5878
  entityType: "scriptObject",
@@ -5650,15 +5897,15 @@ function createTeleportArea({
5650
5897
  const options = payload.options ?? {};
5651
5898
  const width = typeof options.width === "number" ? options.width : 10;
5652
5899
  const depth = typeof options.depth === "number" ? options.depth : 10;
5653
- const geometry = new THREE60__namespace.PlaneGeometry(width, depth);
5654
- const material = new THREE60__namespace.MeshBasicMaterial({
5900
+ const geometry = new THREE67__namespace.PlaneGeometry(width, depth);
5901
+ const material = new THREE67__namespace.MeshBasicMaterial({
5655
5902
  color: "#4da3ff",
5656
5903
  transparent: true,
5657
5904
  opacity: 0.18,
5658
- side: THREE60__namespace.DoubleSide,
5905
+ side: THREE67__namespace.DoubleSide,
5659
5906
  depthWrite: false
5660
5907
  });
5661
- const object = new THREE60__namespace.Mesh(geometry, material);
5908
+ const object = new THREE67__namespace.Mesh(geometry, material);
5662
5909
  const entityId = createBaseEntity(world, object, {
5663
5910
  label: payload.name ?? "Teleport Area",
5664
5911
  entityType: "teleportArea",
@@ -5686,7 +5933,7 @@ function createXRInteractor({
5686
5933
  scene,
5687
5934
  payload
5688
5935
  }) {
5689
- const root = new THREE60__namespace.Group();
5936
+ const root = new THREE67__namespace.Group();
5690
5937
  const options = payload.options ?? {};
5691
5938
  const entityId = createBaseEntity(world, root, {
5692
5939
  label: payload.name ?? "XR Interactor",
@@ -5699,7 +5946,7 @@ function createXRInteractor({
5699
5946
  return entityId;
5700
5947
  }
5701
5948
  for (const side of ["left", "right"]) {
5702
- const slot = new THREE60__namespace.Group();
5949
+ const slot = new THREE67__namespace.Group();
5703
5950
  const label = side === "left" ? "Left Interactor" : "Right Interactor";
5704
5951
  const slotEntityId = createBaseEntity(world, slot, {
5705
5952
  label,
@@ -5717,7 +5964,7 @@ function createUIButton({
5717
5964
  scene,
5718
5965
  payload
5719
5966
  }) {
5720
- const object = new THREE60__namespace.Group();
5967
+ const object = new THREE67__namespace.Group();
5721
5968
  const entityId = createBaseEntity(world, object, {
5722
5969
  label: payload.name ?? "UI Button",
5723
5970
  entityType: "uiButton",
@@ -5742,7 +5989,7 @@ function createUIPanel({
5742
5989
  scene,
5743
5990
  payload
5744
5991
  }) {
5745
- const object = new THREE60__namespace.Group();
5992
+ const object = new THREE67__namespace.Group();
5746
5993
  const entityId = createBaseEntity(world, object, {
5747
5994
  label: payload.name ?? "UI Panel",
5748
5995
  entityType: "uiPanel",
@@ -5759,7 +6006,7 @@ function createUISlider({
5759
6006
  scene,
5760
6007
  payload
5761
6008
  }) {
5762
- const object = new THREE60__namespace.Group();
6009
+ const object = new THREE67__namespace.Group();
5763
6010
  const entityId = createBaseEntity(world, object, {
5764
6011
  label: payload.name ?? "UI Slider",
5765
6012
  entityType: "uiSlider",
@@ -5784,7 +6031,7 @@ function createUIText({
5784
6031
  scene,
5785
6032
  payload
5786
6033
  }) {
5787
- const object = new THREE60__namespace.Group();
6034
+ const object = new THREE67__namespace.Group();
5788
6035
  const entityId = createBaseEntity(world, object, {
5789
6036
  label: payload.name ?? "UI Text",
5790
6037
  entityType: "uiText",
@@ -5801,7 +6048,7 @@ function createUIToggle({
5801
6048
  scene,
5802
6049
  payload
5803
6050
  }) {
5804
- const object = new THREE60__namespace.Group();
6051
+ const object = new THREE67__namespace.Group();
5805
6052
  const entityId = createBaseEntity(world, object, {
5806
6053
  label: payload.name ?? "UI Toggle",
5807
6054
  entityType: "uiToggle",
@@ -5826,7 +6073,7 @@ function createXRRig({
5826
6073
  scene,
5827
6074
  payload
5828
6075
  }) {
5829
- const object = new THREE60__namespace.Group();
6076
+ const object = new THREE67__namespace.Group();
5830
6077
  const entityId = createBaseEntity(world, object, {
5831
6078
  label: payload.name ?? "XR Rig",
5832
6079
  entityType: "xrRig",
@@ -5839,7 +6086,7 @@ function createXRRig({
5839
6086
  }
5840
6087
  __name(createXRRig, "createXRRig");
5841
6088
  function createSprite({ world, scene, payload }) {
5842
- const anchor = new THREE60__namespace.Object3D();
6089
+ const anchor = new THREE67__namespace.Object3D();
5843
6090
  const imagePath = payload.path ?? "";
5844
6091
  const entityId = createBaseEntity(world, anchor, {
5845
6092
  label: payload.name ?? "Sprite",
@@ -5851,7 +6098,7 @@ function createSprite({ world, scene, payload }) {
5851
6098
  if (!imagePath) {
5852
6099
  return entityId;
5853
6100
  }
5854
- const loader = new THREE60__namespace.TextureLoader();
6101
+ const loader = new THREE67__namespace.TextureLoader();
5855
6102
  loader.load(
5856
6103
  imagePath,
5857
6104
  (texture) => {
@@ -5859,11 +6106,11 @@ function createSprite({ world, scene, payload }) {
5859
6106
  texture.dispose();
5860
6107
  return;
5861
6108
  }
5862
- const material = new THREE60__namespace.SpriteMaterial({
6109
+ const material = new THREE67__namespace.SpriteMaterial({
5863
6110
  map: texture,
5864
6111
  transparent: true
5865
6112
  });
5866
- const sprite = new THREE60__namespace.Sprite(material);
6113
+ const sprite = new THREE67__namespace.Sprite(material);
5867
6114
  sprite.userData.entityId = entityId;
5868
6115
  const textureImage = texture.image;
5869
6116
  spriteComponent.width = textureImage?.width ?? 1;
@@ -5881,9 +6128,9 @@ function createSprite({ world, scene, payload }) {
5881
6128
  }
5882
6129
  __name(createSprite, "createSprite");
5883
6130
  function createSphere({ world, scene, payload }) {
5884
- const geometry = new THREE60__namespace.SphereGeometry(0.5, 24, 24);
5885
- const material = new THREE60__namespace.MeshStandardMaterial({ color: "#ffffff" });
5886
- const mesh = new THREE60__namespace.Mesh(geometry, material);
6131
+ const geometry = new THREE67__namespace.SphereGeometry(0.5, 24, 24);
6132
+ const material = new THREE67__namespace.MeshStandardMaterial({ color: "#ffffff" });
6133
+ const mesh = new THREE67__namespace.Mesh(geometry, material);
5887
6134
  const entityId = createBaseEntity(world, mesh, {
5888
6135
  label: payload.name ?? "Sphere",
5889
6136
  entityType: "sphere",
@@ -5903,10 +6150,10 @@ function createSphere({ world, scene, payload }) {
5903
6150
  __name(createSphere, "createSphere");
5904
6151
  function getOrCreateListener2(camera) {
5905
6152
  const existing = camera.userData.ecsAudioListener;
5906
- if (existing instanceof THREE60__namespace.AudioListener) {
6153
+ if (existing instanceof THREE67__namespace.AudioListener) {
5907
6154
  return existing;
5908
6155
  }
5909
- const listener = new THREE60__namespace.AudioListener();
6156
+ const listener = new THREE67__namespace.AudioListener();
5910
6157
  camera.add(listener);
5911
6158
  camera.userData.ecsAudioListener = listener;
5912
6159
  return listener;
@@ -5956,22 +6203,22 @@ function normalizeOptions(input) {
5956
6203
  }
5957
6204
  __name(normalizeOptions, "normalizeOptions");
5958
6205
  function createStandardMaterial(texture) {
5959
- return new THREE60__namespace.MeshBasicMaterial({
6206
+ return new THREE67__namespace.MeshBasicMaterial({
5960
6207
  map: texture,
5961
6208
  transparent: true,
5962
- side: THREE60__namespace.DoubleSide
6209
+ side: THREE67__namespace.DoubleSide
5963
6210
  });
5964
6211
  }
5965
6212
  __name(createStandardMaterial, "createStandardMaterial");
5966
6213
  function createVideoMesh(entityId, texture, options, backend) {
5967
- const geometry = new THREE60__namespace.PlaneGeometry(1, 1);
6214
+ const geometry = new THREE67__namespace.PlaneGeometry(1, 1);
5968
6215
  const material = options.isGreenScreen ? backend.createChromaKeyMaterial(texture, {
5969
6216
  keyColor: options.backgroundColor,
5970
6217
  similarity: options.similarity,
5971
6218
  smoothness: options.smoothness,
5972
6219
  spill: options.spill
5973
6220
  }) : createStandardMaterial(texture);
5974
- const mesh = new THREE60__namespace.Mesh(geometry, material);
6221
+ const mesh = new THREE67__namespace.Mesh(geometry, material);
5975
6222
  mesh.userData.entityId = entityId;
5976
6223
  mesh.scale.set(options.width, options.height, 1);
5977
6224
  return { mesh, material };
@@ -6006,7 +6253,7 @@ function createVideo({
6006
6253
  payload,
6007
6254
  isEditor
6008
6255
  }) {
6009
- const anchor = new THREE60__namespace.Object3D();
6256
+ const anchor = new THREE67__namespace.Object3D();
6010
6257
  const path = payload.path ?? "";
6011
6258
  const options = normalizeOptions(payload.options);
6012
6259
  const entityId = createBaseEntity(world, anchor, {
@@ -6021,8 +6268,8 @@ function createVideo({
6021
6268
  return entityId;
6022
6269
  }
6023
6270
  const video = createVideoElement(path, options);
6024
- const texture = new THREE60__namespace.VideoTexture(video);
6025
- texture.colorSpace = THREE60__namespace.SRGBColorSpace;
6271
+ const texture = new THREE67__namespace.VideoTexture(video);
6272
+ texture.colorSpace = THREE67__namespace.SRGBColorSpace;
6026
6273
  texture.needsUpdate = true;
6027
6274
  const { mesh, material } = createVideoMesh(
6028
6275
  entityId,
@@ -6043,7 +6290,7 @@ function createVideo({
6043
6290
  });
6044
6291
  if (!isEditor && options.positional) {
6045
6292
  const listener = getOrCreateListener2(camera);
6046
- const audioNode = new THREE60__namespace.PositionalAudio(listener);
6293
+ const audioNode = new THREE67__namespace.PositionalAudio(listener);
6047
6294
  audioNode.setVolume(options.volume);
6048
6295
  audioNode.setRefDistance(options.refDistance);
6049
6296
  audioNode.setDistanceModel(normalizeDistanceModel2(options.distanceModel));
@@ -6289,6 +6536,14 @@ function applyParentLinks(snapshot, idMap, objectManagementSystem, world) {
6289
6536
  __name(applyParentLinks, "applyParentLinks");
6290
6537
 
6291
6538
  // core/hydration/postCreateComponents.ts
6539
+ var OPTIONAL_UI_FIELDS = {
6540
+ pixelDensity: "number",
6541
+ icon: "string",
6542
+ iconPosition: "string",
6543
+ variant: "string",
6544
+ step: "number",
6545
+ readOnly: "boolean"
6546
+ };
6292
6547
  function remapEntityRefsInScriptData(data, idMap) {
6293
6548
  const result = {};
6294
6549
  for (const [key, value] of Object.entries(data)) {
@@ -6672,6 +6927,20 @@ function restorePostCreateComponents(world, objectManagementSystem, targetEntity
6672
6927
  const target = component;
6673
6928
  for (const [key, value] of Object.entries(source)) {
6674
6929
  const current = target[key];
6930
+ if (key === "style") {
6931
+ const style = normalizeUiStyle(value);
6932
+ if (style) target.style = style;
6933
+ continue;
6934
+ }
6935
+ if (key === "tokens") {
6936
+ const tokens = normalizeUiTokens(value);
6937
+ if (tokens) target.tokens = tokens;
6938
+ continue;
6939
+ }
6940
+ if (OPTIONAL_UI_FIELDS[key] === typeof value) {
6941
+ target[key] = value;
6942
+ continue;
6943
+ }
6675
6944
  if ((typeof value === "string" || typeof value === "number" || typeof value === "boolean") && typeof current === typeof value) {
6676
6945
  target[key] = value;
6677
6946
  }
@@ -8175,16 +8444,16 @@ function prefabFileVersionFor(snapshot) {
8175
8444
  return findTopLevelNestedRegions(snapshot).length > 0 ? PREFAB_FILE_VERSION_NESTED : PREFAB_FILE_VERSION;
8176
8445
  }
8177
8446
  __name(prefabFileVersionFor, "prefabFileVersionFor");
8178
- var isRecord = /* @__PURE__ */ __name((value) => value !== null && typeof value === "object" && !Array.isArray(value), "isRecord");
8447
+ var isRecord2 = /* @__PURE__ */ __name((value) => value !== null && typeof value === "object" && !Array.isArray(value), "isRecord");
8179
8448
  function parsePrefabAsset(json) {
8180
- if (!isRecord(json)) {
8449
+ if (!isRecord2(json)) {
8181
8450
  return null;
8182
8451
  }
8183
8452
  if (typeof json.version !== "number" || json.version < 1) {
8184
8453
  return null;
8185
8454
  }
8186
8455
  const snapshot = json.snapshot;
8187
- if (!isRecord(snapshot)) {
8456
+ if (!isRecord2(snapshot)) {
8188
8457
  return null;
8189
8458
  }
8190
8459
  const entities = snapshot.entities;
@@ -8196,7 +8465,7 @@ function parsePrefabAsset(json) {
8196
8465
  return null;
8197
8466
  }
8198
8467
  if (!components.every(
8199
- (block) => isRecord(block) && typeof block.component === "string" && Array.isArray(block.entries)
8468
+ (block) => isRecord2(block) && typeof block.component === "string" && Array.isArray(block.entries)
8200
8469
  )) {
8201
8470
  return null;
8202
8471
  }
@@ -8471,7 +8740,7 @@ function transitionToClip(runtime, nextClipId, crossFadeDuration) {
8471
8740
  }
8472
8741
  __name(transitionToClip, "transitionToClip");
8473
8742
  function applyActionState(action, component, paused, shouldLoop) {
8474
- const loopMode = shouldLoop ? THREE60__namespace.LoopRepeat : THREE60__namespace.LoopOnce;
8743
+ const loopMode = shouldLoop ? THREE67__namespace.LoopRepeat : THREE67__namespace.LoopOnce;
8475
8744
  const repetitions = shouldLoop ? Infinity : 1;
8476
8745
  action.setLoop(loopMode, repetitions);
8477
8746
  action.clampWhenFinished = !shouldLoop;
@@ -8644,7 +8913,7 @@ var _AnimationSystem = class _AnimationSystem {
8644
8913
  if (runtime) {
8645
8914
  stopAllActions(runtime);
8646
8915
  }
8647
- const mixer = new THREE60__namespace.AnimationMixer(root);
8916
+ const mixer = new THREE67__namespace.AnimationMixer(root);
8648
8917
  const entries2 = readRuntimeClipEntries(animation);
8649
8918
  const indexedActions = createActions(mixer, entries2);
8650
8919
  runtime = {
@@ -8770,11 +9039,11 @@ function normalizeOrthoBounds(component) {
8770
9039
  }
8771
9040
  __name(normalizeOrthoBounds, "normalizeOrthoBounds");
8772
9041
  function isPerspectiveCamera(camera) {
8773
- return camera instanceof THREE60__namespace.PerspectiveCamera;
9042
+ return camera instanceof THREE67__namespace.PerspectiveCamera;
8774
9043
  }
8775
9044
  __name(isPerspectiveCamera, "isPerspectiveCamera");
8776
9045
  function isOrthographicCamera(camera) {
8777
- return camera instanceof THREE60__namespace.OrthographicCamera;
9046
+ return camera instanceof THREE67__namespace.OrthographicCamera;
8778
9047
  }
8779
9048
  __name(isOrthographicCamera, "isOrthographicCamera");
8780
9049
  function readWindowAspect() {
@@ -8828,7 +9097,7 @@ var _CameraSyncSystem = class _CameraSyncSystem {
8828
9097
  continue;
8829
9098
  }
8830
9099
  const camera = objectRef.object;
8831
- if (!(camera instanceof THREE60__namespace.Camera)) {
9100
+ if (!(camera instanceof THREE67__namespace.Camera)) {
8832
9101
  continue;
8833
9102
  }
8834
9103
  if (xrCamera !== null && camera === xrCamera) {
@@ -8893,8 +9162,8 @@ var ObjectVisibilitySystem = _ObjectVisibilitySystem;
8893
9162
  function getUrlExtension(url) {
8894
9163
  const path = url.split(/[?#]/)[0] ?? "";
8895
9164
  const lastSegment = path.split("/").pop() ?? "";
8896
- const dot = lastSegment.lastIndexOf(".");
8897
- return dot >= 0 ? lastSegment.slice(dot + 1).toLowerCase() : "";
9165
+ const dot2 = lastSegment.lastIndexOf(".");
9166
+ return dot2 >= 0 ? lastSegment.slice(dot2 + 1).toLowerCase() : "";
8898
9167
  }
8899
9168
  __name(getUrlExtension, "getUrlExtension");
8900
9169
  async function loadEquirectTexture(url) {
@@ -8907,10 +9176,10 @@ async function loadEquirectTexture(url) {
8907
9176
  const { EXRLoader } = await import('three/examples/jsm/loaders/EXRLoader.js');
8908
9177
  texture = await new EXRLoader().loadAsync(url);
8909
9178
  } else {
8910
- texture = await new THREE60__namespace.TextureLoader().loadAsync(url);
8911
- texture.colorSpace = THREE60__namespace.SRGBColorSpace;
9179
+ texture = await new THREE67__namespace.TextureLoader().loadAsync(url);
9180
+ texture.colorSpace = THREE67__namespace.SRGBColorSpace;
8912
9181
  }
8913
- texture.mapping = THREE60__namespace.EquirectangularReflectionMapping;
9182
+ texture.mapping = THREE67__namespace.EquirectangularReflectionMapping;
8914
9183
  texture.needsUpdate = true;
8915
9184
  return texture;
8916
9185
  }
@@ -8919,13 +9188,13 @@ var rendererStates = /* @__PURE__ */ new WeakMap();
8919
9188
  function toThreeToneMapping(mode) {
8920
9189
  switch (mode) {
8921
9190
  case "aces":
8922
- return THREE60__namespace.ACESFilmicToneMapping;
9191
+ return THREE67__namespace.ACESFilmicToneMapping;
8923
9192
  case "agx":
8924
- return THREE60__namespace.AgXToneMapping;
9193
+ return THREE67__namespace.AgXToneMapping;
8925
9194
  case "neutral":
8926
- return THREE60__namespace.NeutralToneMapping;
9195
+ return THREE67__namespace.NeutralToneMapping;
8927
9196
  default:
8928
- return THREE60__namespace.NoToneMapping;
9197
+ return THREE67__namespace.NoToneMapping;
8929
9198
  }
8930
9199
  }
8931
9200
  __name(toThreeToneMapping, "toThreeToneMapping");
@@ -8946,7 +9215,7 @@ function setRendererToneMappingSource(renderer, source, request) {
8946
9215
  if (request === null) return;
8947
9216
  state = {
8948
9217
  baseline: {
8949
- toneMapping: target.toneMapping ?? THREE60__namespace.NoToneMapping,
9218
+ toneMapping: target.toneMapping ?? THREE67__namespace.NoToneMapping,
8950
9219
  exposure: typeof target.toneMappingExposure === "number" ? target.toneMappingExposure : 1
8951
9220
  },
8952
9221
  environment: null,
@@ -8977,7 +9246,7 @@ var MIN_DENSITY = 0;
8977
9246
  function createGradientTexture(effect, colors) {
8978
9247
  if (typeof document === "undefined") {
8979
9248
  const pixel = new Uint8Array([30, 30, 30, 255]);
8980
- const fallback = new THREE60__namespace.DataTexture(pixel, 1, 1, THREE60__namespace.RGBAFormat);
9249
+ const fallback = new THREE67__namespace.DataTexture(pixel, 1, 1, THREE67__namespace.RGBAFormat);
8981
9250
  fallback.needsUpdate = true;
8982
9251
  return fallback;
8983
9252
  }
@@ -8986,7 +9255,7 @@ function createGradientTexture(effect, colors) {
8986
9255
  canvas.height = 512;
8987
9256
  const context = canvas.getContext("2d");
8988
9257
  if (!context) {
8989
- const fallback = new THREE60__namespace.CanvasTexture(canvas);
9258
+ const fallback = new THREE67__namespace.CanvasTexture(canvas);
8990
9259
  fallback.needsUpdate = true;
8991
9260
  return fallback;
8992
9261
  }
@@ -9014,8 +9283,8 @@ function createGradientTexture(effect, colors) {
9014
9283
  context.fillRect(0, 0, 512, 512);
9015
9284
  context.globalCompositeOperation = "source-over";
9016
9285
  }
9017
- const texture = new THREE60__namespace.CanvasTexture(canvas);
9018
- texture.colorSpace = THREE60__namespace.SRGBColorSpace;
9286
+ const texture = new THREE67__namespace.CanvasTexture(canvas);
9287
+ texture.colorSpace = THREE67__namespace.SRGBColorSpace;
9019
9288
  texture.needsUpdate = true;
9020
9289
  return texture;
9021
9290
  }
@@ -9032,7 +9301,7 @@ function readRenderer(world) {
9032
9301
  __name(readRenderer, "readRenderer");
9033
9302
  var _EnvironmentSyncSystem = class _EnvironmentSyncSystem {
9034
9303
  constructor(scene) {
9035
- this.textureLoader = new THREE60__namespace.TextureLoader();
9304
+ this.textureLoader = new THREE67__namespace.TextureLoader();
9036
9305
  /** `true` once an Environment entity has been seen (drives the reset path). */
9037
9306
  this.hasEnvironment = false;
9038
9307
  this.skyboxSignature = null;
@@ -9116,7 +9385,7 @@ var _EnvironmentSyncSystem = class _EnvironmentSyncSystem {
9116
9385
  if (skybox.type === "color") {
9117
9386
  this.disposeActiveTexture();
9118
9387
  this.pendingImageUrl = null;
9119
- this.setSkyboxBackground(new THREE60__namespace.Color(skybox.color || "#1e1e1e"));
9388
+ this.setSkyboxBackground(new THREE67__namespace.Color(skybox.color || "#1e1e1e"));
9120
9389
  return;
9121
9390
  }
9122
9391
  if (skybox.type === "gradient") {
@@ -9151,7 +9420,7 @@ var _EnvironmentSyncSystem = class _EnvironmentSyncSystem {
9151
9420
  return;
9152
9421
  }
9153
9422
  if (!equirect) {
9154
- texture.colorSpace = THREE60__namespace.SRGBColorSpace;
9423
+ texture.colorSpace = THREE67__namespace.SRGBColorSpace;
9155
9424
  }
9156
9425
  this.activeTexture = texture;
9157
9426
  this.setSkyboxBackground(texture);
@@ -9179,16 +9448,16 @@ var _EnvironmentSyncSystem = class _EnvironmentSyncSystem {
9179
9448
  this.scene.fog = null;
9180
9449
  return;
9181
9450
  }
9182
- const fogColor = new THREE60__namespace.Color(fog.color || "#1e1e1e");
9451
+ const fogColor = new THREE67__namespace.Color(fog.color || "#1e1e1e");
9183
9452
  if (fog.type === "linear") {
9184
9453
  const near = Number.isFinite(fog.linear.near) ? fog.linear.near : 1;
9185
9454
  const farCandidate = Number.isFinite(fog.linear.far) ? fog.linear.far : near + 10;
9186
9455
  const far = Math.max(near + MIN_FOG_DELTA, farCandidate);
9187
- this.scene.fog = new THREE60__namespace.Fog(fogColor, near, far);
9456
+ this.scene.fog = new THREE67__namespace.Fog(fogColor, near, far);
9188
9457
  return;
9189
9458
  }
9190
9459
  const density = Number.isFinite(fog.exponential.density) ? Math.max(MIN_DENSITY, fog.exponential.density) : 2e-3;
9191
- this.scene.fog = new THREE60__namespace.FogExp2(fogColor, density);
9460
+ this.scene.fog = new THREE67__namespace.FogExp2(fogColor, density);
9192
9461
  }
9193
9462
  // ── Image-based lighting ────────────────────────────────────────────────
9194
9463
  disposeEnvResources() {
@@ -9250,7 +9519,7 @@ var _EnvironmentSyncSystem = class _EnvironmentSyncSystem {
9250
9519
  if (!renderer || getRendererKindOf(renderer) === "webgpu") return null;
9251
9520
  let generator = null;
9252
9521
  try {
9253
- generator = new THREE60__namespace.PMREMGenerator(renderer);
9522
+ generator = new THREE67__namespace.PMREMGenerator(renderer);
9254
9523
  return generator.fromEquirectangular(texture);
9255
9524
  } catch (error) {
9256
9525
  console.warn(
@@ -9545,7 +9814,7 @@ function computeColliderState(colliderValues, object) {
9545
9814
  if (!object) {
9546
9815
  return colliderValues;
9547
9816
  }
9548
- const scale = new THREE60__namespace.Vector3();
9817
+ const scale = new THREE67__namespace.Vector3();
9549
9818
  object.getWorldScale(scale);
9550
9819
  return {
9551
9820
  ...colliderValues,
@@ -9596,8 +9865,8 @@ function createColliderDesc(RAPIER, values, triMeshData) {
9596
9865
  values.offsetPosition.y,
9597
9866
  values.offsetPosition.z
9598
9867
  );
9599
- const offsetQuaternion = new THREE60__namespace.Quaternion().setFromEuler(
9600
- new THREE60__namespace.Euler(
9868
+ const offsetQuaternion = new THREE67__namespace.Quaternion().setFromEuler(
9869
+ new THREE67__namespace.Euler(
9601
9870
  values.offsetRotation.x,
9602
9871
  values.offsetRotation.y,
9603
9872
  values.offsetRotation.z
@@ -9612,17 +9881,17 @@ function collectTriMeshParts(object) {
9612
9881
  return null;
9613
9882
  }
9614
9883
  object.updateWorldMatrix(true, true);
9615
- const worldScale = new THREE60__namespace.Vector3();
9884
+ const worldScale = new THREE67__namespace.Vector3();
9616
9885
  object.getWorldScale(worldScale);
9617
9886
  const scale = {
9618
9887
  x: Math.abs(worldScale.x) || 1,
9619
9888
  y: Math.abs(worldScale.y) || 1,
9620
9889
  z: Math.abs(worldScale.z) || 1
9621
9890
  };
9622
- const worldToObject = new THREE60__namespace.Matrix4().copy(object.matrixWorld).invert();
9891
+ const worldToObject = new THREE67__namespace.Matrix4().copy(object.matrixWorld).invert();
9623
9892
  const parts = [];
9624
9893
  object.traverse((child) => {
9625
- if (!(child instanceof THREE60__namespace.Mesh) || !child.geometry) {
9894
+ if (!(child instanceof THREE67__namespace.Mesh) || !child.geometry) {
9626
9895
  return;
9627
9896
  }
9628
9897
  const geometry = child.geometry;
@@ -9639,7 +9908,7 @@ function collectTriMeshParts(object) {
9639
9908
  if (indexCount < 3) {
9640
9909
  return;
9641
9910
  }
9642
- const relativeMatrix = new THREE60__namespace.Matrix4().multiplyMatrices(
9911
+ const relativeMatrix = new THREE67__namespace.Matrix4().multiplyMatrices(
9643
9912
  worldToObject,
9644
9913
  child.matrixWorld
9645
9914
  );
@@ -9680,7 +9949,7 @@ function createTriMeshData(collected) {
9680
9949
  }
9681
9950
  const vertices = new Float32Array(vertexTotal * 3);
9682
9951
  const indices = new Uint32Array(indexTotal);
9683
- const point = new THREE60__namespace.Vector3();
9952
+ const point = new THREE67__namespace.Vector3();
9684
9953
  let vertexOffset = 0;
9685
9954
  let indexOffset = 0;
9686
9955
  for (const part of parts) {
@@ -9712,8 +9981,8 @@ function createTriMeshData(collected) {
9712
9981
  }
9713
9982
  __name(createTriMeshData, "createTriMeshData");
9714
9983
  function syncBodyFromTransform(body, transform) {
9715
- const quaternion = new THREE60__namespace.Quaternion().setFromEuler(
9716
- new THREE60__namespace.Euler(transform.rotation.x, transform.rotation.y, transform.rotation.z)
9984
+ const quaternion = new THREE67__namespace.Quaternion().setFromEuler(
9985
+ new THREE67__namespace.Euler(transform.rotation.x, transform.rotation.y, transform.rotation.z)
9717
9986
  );
9718
9987
  body.setTranslation(
9719
9988
  {
@@ -9740,8 +10009,8 @@ function syncTransformFromBody(transform, body) {
9740
10009
  transform.position.x = position.x;
9741
10010
  transform.position.y = position.y;
9742
10011
  transform.position.z = position.z;
9743
- const euler = new THREE60__namespace.Euler().setFromQuaternion(
9744
- new THREE60__namespace.Quaternion(rotation.x, rotation.y, rotation.z, rotation.w)
10012
+ const euler = new THREE67__namespace.Euler().setFromQuaternion(
10013
+ new THREE67__namespace.Quaternion(rotation.x, rotation.y, rotation.z, rotation.w)
9745
10014
  );
9746
10015
  transform.rotation.x = euler.x;
9747
10016
  transform.rotation.y = euler.y;
@@ -9854,8 +10123,8 @@ var _PhysicsSyncSystem = class _PhysicsSyncSystem {
9854
10123
  transform.position.y,
9855
10124
  transform.position.z
9856
10125
  );
9857
- const quaternion = new THREE60__namespace.Quaternion().setFromEuler(
9858
- new THREE60__namespace.Euler(
10126
+ const quaternion = new THREE67__namespace.Quaternion().setFromEuler(
10127
+ new THREE67__namespace.Euler(
9859
10128
  transform.rotation.x,
9860
10129
  transform.rotation.y,
9861
10130
  transform.rotation.z
@@ -10586,7 +10855,7 @@ function applyLightValues(lightComponent, rendererKind) {
10586
10855
  values.castShadow
10587
10856
  );
10588
10857
  }
10589
- if (light instanceof THREE60__namespace.PointLight) {
10858
+ if (light instanceof THREE67__namespace.PointLight) {
10590
10859
  if (typeof values.distance === "number" && Number.isFinite(values.distance)) {
10591
10860
  light.distance = Math.max(MIN_LIGHT_DISTANCE, values.distance);
10592
10861
  }
@@ -10594,7 +10863,7 @@ function applyLightValues(lightComponent, rendererKind) {
10594
10863
  light.decay = Math.max(0, values.decay);
10595
10864
  }
10596
10865
  }
10597
- if (light instanceof THREE60__namespace.SpotLight) {
10866
+ if (light instanceof THREE67__namespace.SpotLight) {
10598
10867
  if (typeof values.distance === "number" && Number.isFinite(values.distance)) {
10599
10868
  light.distance = Math.max(MIN_LIGHT_DISTANCE, values.distance);
10600
10869
  }
@@ -10608,7 +10877,7 @@ function applyLightValues(lightComponent, rendererKind) {
10608
10877
  light.penumbra = Math.max(0, Math.min(1, values.penumbra));
10609
10878
  }
10610
10879
  }
10611
- if (light instanceof THREE60__namespace.RectAreaLight) {
10880
+ if (light instanceof THREE67__namespace.RectAreaLight) {
10612
10881
  if (typeof values.width === "number" && Number.isFinite(values.width)) {
10613
10882
  light.width = Math.max(MIN_AREA_SIZE, values.width);
10614
10883
  }
@@ -10672,15 +10941,15 @@ function resolveMaterialPropertyKey(key) {
10672
10941
  __name(resolveMaterialPropertyKey, "resolveMaterialPropertyKey");
10673
10942
  function createMaterialByType(type) {
10674
10943
  if (type === "basic") {
10675
- return new THREE60__namespace.MeshBasicMaterial();
10944
+ return new THREE67__namespace.MeshBasicMaterial();
10676
10945
  }
10677
10946
  if (type === "phong") {
10678
- return new THREE60__namespace.MeshPhongMaterial();
10947
+ return new THREE67__namespace.MeshPhongMaterial();
10679
10948
  }
10680
10949
  if (type === "physical") {
10681
- return new THREE60__namespace.MeshPhysicalMaterial();
10950
+ return new THREE67__namespace.MeshPhysicalMaterial();
10682
10951
  }
10683
- return new THREE60__namespace.MeshStandardMaterial();
10952
+ return new THREE67__namespace.MeshStandardMaterial();
10684
10953
  }
10685
10954
  __name(createMaterialByType, "createMaterialByType");
10686
10955
  function replaceMaterialAcrossObject(object, previous, next) {
@@ -10739,12 +11008,12 @@ function applyTextureFiltering(material, filtering) {
10739
11008
  const target = material;
10740
11009
  for (const slot of materialTextureSlots) {
10741
11010
  const texture = target[slot];
10742
- if (!texture || !(texture instanceof THREE60__namespace.Texture)) {
11011
+ if (!texture || !(texture instanceof THREE67__namespace.Texture)) {
10743
11012
  continue;
10744
11013
  }
10745
11014
  const useMipmaps = texture.generateMipmaps !== false;
10746
- texture.magFilter = sharp ? THREE60__namespace.NearestFilter : THREE60__namespace.LinearFilter;
10747
- texture.minFilter = useMipmaps ? sharp ? THREE60__namespace.NearestMipmapNearestFilter : THREE60__namespace.LinearMipmapLinearFilter : sharp ? THREE60__namespace.NearestFilter : THREE60__namespace.LinearFilter;
11015
+ texture.magFilter = sharp ? THREE67__namespace.NearestFilter : THREE67__namespace.LinearFilter;
11016
+ texture.minFilter = useMipmaps ? sharp ? THREE67__namespace.NearestMipmapNearestFilter : THREE67__namespace.LinearMipmapLinearFilter : sharp ? THREE67__namespace.NearestFilter : THREE67__namespace.LinearFilter;
10748
11017
  texture.needsUpdate = true;
10749
11018
  }
10750
11019
  material.needsUpdate = true;
@@ -10754,12 +11023,12 @@ function setTextureMipmaps(material, enabled) {
10754
11023
  const target = material;
10755
11024
  for (const slot of materialTextureSlots) {
10756
11025
  const texture = target[slot];
10757
- if (!texture || !(texture instanceof THREE60__namespace.Texture)) {
11026
+ if (!texture || !(texture instanceof THREE67__namespace.Texture)) {
10758
11027
  continue;
10759
11028
  }
10760
11029
  texture.generateMipmaps = enabled;
10761
- const sharp = texture.magFilter === THREE60__namespace.NearestFilter;
10762
- texture.minFilter = enabled ? sharp ? THREE60__namespace.NearestMipmapNearestFilter : THREE60__namespace.LinearMipmapLinearFilter : sharp ? THREE60__namespace.NearestFilter : THREE60__namespace.LinearFilter;
11030
+ const sharp = texture.magFilter === THREE67__namespace.NearestFilter;
11031
+ texture.minFilter = enabled ? sharp ? THREE67__namespace.NearestMipmapNearestFilter : THREE67__namespace.LinearMipmapLinearFilter : sharp ? THREE67__namespace.NearestFilter : THREE67__namespace.LinearFilter;
10763
11032
  texture.needsUpdate = true;
10764
11033
  }
10765
11034
  material.needsUpdate = true;
@@ -10770,7 +11039,7 @@ function assignMaterialTexture(material, rawKey, url, textureCache) {
10770
11039
  const key = resolveMaterialPropertyKey(rawKey);
10771
11040
  let texture = textureCache.get(url);
10772
11041
  if (!texture) {
10773
- texture = new THREE60__namespace.TextureLoader().load(url);
11042
+ texture = new THREE67__namespace.TextureLoader().load(url);
10774
11043
  textureCache.set(url, texture);
10775
11044
  }
10776
11045
  target[key] = texture;
@@ -10808,7 +11077,7 @@ function applyMaterialState(material, value, textureCache) {
10808
11077
  }
10809
11078
  target.side = value.side;
10810
11079
  target.blending = value.blending;
10811
- target.premultipliedAlpha = value.blending === THREE60__namespace.MultiplyBlending || value.blending === THREE60__namespace.SubtractiveBlending;
11080
+ target.premultipliedAlpha = value.blending === THREE67__namespace.MultiplyBlending || value.blending === THREE67__namespace.SubtractiveBlending;
10812
11081
  target.depthTest = value.depthTest;
10813
11082
  target.depthWrite = value.depthWrite;
10814
11083
  target.wireframe = value.wireframe;
@@ -10969,12 +11238,12 @@ function resolvePrimaryMesh(object) {
10969
11238
  if (!object) {
10970
11239
  return null;
10971
11240
  }
10972
- if (object instanceof THREE60__namespace.Mesh) {
11241
+ if (object instanceof THREE67__namespace.Mesh) {
10973
11242
  return object;
10974
11243
  }
10975
11244
  let found = null;
10976
11245
  object.traverse((child) => {
10977
- if (!found && child instanceof THREE60__namespace.Mesh) {
11246
+ if (!found && child instanceof THREE67__namespace.Mesh) {
10978
11247
  found = child;
10979
11248
  }
10980
11249
  });
@@ -10995,22 +11264,22 @@ function applyGeometry(component) {
10995
11264
  switch (component.kind) {
10996
11265
  case "cube": {
10997
11266
  const params = sanitized;
10998
- nextGeometry = new THREE60__namespace.BoxGeometry(params.width, params.height, params.depth);
11267
+ nextGeometry = new THREE67__namespace.BoxGeometry(params.width, params.height, params.depth);
10999
11268
  break;
11000
11269
  }
11001
11270
  case "plane": {
11002
11271
  const params = sanitized;
11003
- nextGeometry = new THREE60__namespace.PlaneGeometry(params.width, params.height);
11272
+ nextGeometry = new THREE67__namespace.PlaneGeometry(params.width, params.height);
11004
11273
  break;
11005
11274
  }
11006
11275
  case "sphere": {
11007
11276
  const params = sanitized;
11008
- nextGeometry = new THREE60__namespace.SphereGeometry(params.radius, 24, 24);
11277
+ nextGeometry = new THREE67__namespace.SphereGeometry(params.radius, 24, 24);
11009
11278
  break;
11010
11279
  }
11011
11280
  case "cylinder": {
11012
11281
  const params = sanitized;
11013
- nextGeometry = new THREE60__namespace.CylinderGeometry(
11282
+ nextGeometry = new THREE67__namespace.CylinderGeometry(
11014
11283
  params.radius,
11015
11284
  params.radius,
11016
11285
  params.height,
@@ -11020,7 +11289,7 @@ function applyGeometry(component) {
11020
11289
  }
11021
11290
  case "capsule": {
11022
11291
  const params = sanitized;
11023
- nextGeometry = new THREE60__namespace.CapsuleGeometry(params.radius, params.height, 4, 8);
11292
+ nextGeometry = new THREE67__namespace.CapsuleGeometry(params.radius, params.height, 4, 8);
11024
11293
  break;
11025
11294
  }
11026
11295
  default:
@@ -11036,7 +11305,7 @@ function applyShadowFlags(component) {
11036
11305
  return;
11037
11306
  }
11038
11307
  component.object.traverse((child) => {
11039
- if (child instanceof THREE60__namespace.Mesh) {
11308
+ if (child instanceof THREE67__namespace.Mesh) {
11040
11309
  child.castShadow = component.castShadow;
11041
11310
  child.receiveShadow = component.receiveShadow;
11042
11311
  }
@@ -11112,35 +11381,35 @@ function buildGeometry(component) {
11112
11381
  switch (component.meshKind) {
11113
11382
  case "cube": {
11114
11383
  const params = p;
11115
- return new THREE60__namespace.BoxGeometry(params.width, params.height, params.depth);
11384
+ return new THREE67__namespace.BoxGeometry(params.width, params.height, params.depth);
11116
11385
  }
11117
11386
  case "plane": {
11118
11387
  const params = p;
11119
- return new THREE60__namespace.PlaneGeometry(params.width, params.height);
11388
+ return new THREE67__namespace.PlaneGeometry(params.width, params.height);
11120
11389
  }
11121
11390
  case "sphere": {
11122
11391
  const params = p;
11123
- return new THREE60__namespace.SphereGeometry(params.radius, 24, 24);
11392
+ return new THREE67__namespace.SphereGeometry(params.radius, 24, 24);
11124
11393
  }
11125
11394
  case "cylinder": {
11126
11395
  const params = p;
11127
- return new THREE60__namespace.CylinderGeometry(params.radius, params.radius, params.height, 20);
11396
+ return new THREE67__namespace.CylinderGeometry(params.radius, params.radius, params.height, 20);
11128
11397
  }
11129
11398
  case "capsule": {
11130
11399
  const params = p;
11131
- return new THREE60__namespace.CapsuleGeometry(params.radius, params.height, 4, 8);
11400
+ return new THREE67__namespace.CapsuleGeometry(params.radius, params.height, 4, 8);
11132
11401
  }
11133
11402
  default:
11134
- return new THREE60__namespace.BoxGeometry(1, 1, 1);
11403
+ return new THREE67__namespace.BoxGeometry(1, 1, 1);
11135
11404
  }
11136
11405
  }
11137
11406
  __name(buildGeometry, "buildGeometry");
11138
- var _position = new THREE60__namespace.Vector3();
11139
- var _quaternion = new THREE60__namespace.Quaternion();
11140
- var _euler = new THREE60__namespace.Euler();
11141
- var _scale = new THREE60__namespace.Vector3();
11142
- var _matrix = new THREE60__namespace.Matrix4();
11143
- var _modelMatrix = new THREE60__namespace.Matrix4();
11407
+ var _position = new THREE67__namespace.Vector3();
11408
+ var _quaternion = new THREE67__namespace.Quaternion();
11409
+ var _euler = new THREE67__namespace.Euler();
11410
+ var _scale = new THREE67__namespace.Vector3();
11411
+ var _matrix = new THREE67__namespace.Matrix4();
11412
+ var _modelMatrix = new THREE67__namespace.Matrix4();
11144
11413
  function instanceTransformFromComponent(transform, out) {
11145
11414
  _position.set(transform.position.x, transform.position.y, transform.position.z);
11146
11415
  _euler.set(transform.rotation.x, transform.rotation.y, transform.rotation.z, "XYZ");
@@ -11222,9 +11491,9 @@ var _InstancedMeshSystem = class _InstancedMeshSystem {
11222
11491
  const instanceCount = comp.instanceEntityIds.length;
11223
11492
  const geometrySignature = comp.meshKind === "model" ? `model:${comp.modelPath}:${instanceCount}` : `${comp.meshKind}:${JSON.stringify(comp.meshParameters)}:${instanceCount}`;
11224
11493
  if (comp.instancedMesh === null) {
11225
- const tracked = this.trackedMeshes.get(entityId);
11226
- if (tracked) {
11227
- comp.instancedMesh = tracked;
11494
+ const tracked2 = this.trackedMeshes.get(entityId);
11495
+ if (tracked2) {
11496
+ comp.instancedMesh = tracked2;
11228
11497
  }
11229
11498
  }
11230
11499
  const pendingLoadSignature = this.loadingSignatures.get(entityId);
@@ -11293,7 +11562,7 @@ var _InstancedMeshSystem = class _InstancedMeshSystem {
11293
11562
  const sourceMaterial = Array.isArray(meshChild.material) ? meshChild.material.map((m) => m.clone()) : meshChild.material.clone();
11294
11563
  meshChild.updateWorldMatrix(true, false);
11295
11564
  const localMatrix = meshChild.matrixWorld.clone();
11296
- const instancedMesh2 = new THREE60__namespace.InstancedMesh(
11565
+ const instancedMesh2 = new THREE67__namespace.InstancedMesh(
11297
11566
  geometry2,
11298
11567
  Array.isArray(sourceMaterial) ? sourceMaterial : sourceMaterial,
11299
11568
  count2
@@ -11318,7 +11587,7 @@ var _InstancedMeshSystem = class _InstancedMeshSystem {
11318
11587
  this.loadingSignatures.delete(targetEntityId);
11319
11588
  return;
11320
11589
  }
11321
- const group = new THREE60__namespace.Group();
11590
+ const group = new THREE67__namespace.Group();
11322
11591
  for (const entry of entries) {
11323
11592
  group.add(entry.instancedMesh);
11324
11593
  }
@@ -11336,8 +11605,8 @@ var _InstancedMeshSystem = class _InstancedMeshSystem {
11336
11605
  }
11337
11606
  const count = instanceCount;
11338
11607
  const geometry = buildGeometry(comp);
11339
- const material = new THREE60__namespace.MeshStandardMaterial({ color: "#ffffff" });
11340
- const instancedMesh = new THREE60__namespace.InstancedMesh(geometry, material, count);
11608
+ const material = new THREE67__namespace.MeshStandardMaterial({ color: "#ffffff" });
11609
+ const instancedMesh = new THREE67__namespace.InstancedMesh(geometry, material, count);
11341
11610
  instancedMesh.castShadow = comp.castShadow;
11342
11611
  instancedMesh.receiveShadow = comp.receiveShadow;
11343
11612
  for (let i = 0; i < count; i++) {
@@ -11607,7 +11876,7 @@ var _Behaviour = class _Behaviour {
11607
11876
  if (!this.threeRef) {
11608
11877
  return null;
11609
11878
  }
11610
- const position = new THREE60__namespace.Vector3();
11879
+ const position = new THREE67__namespace.Vector3();
11611
11880
  this.threeRef.object.getWorldPosition(position);
11612
11881
  return position;
11613
11882
  }
@@ -11619,7 +11888,7 @@ var _Behaviour = class _Behaviour {
11619
11888
  if (!this.threeRef) {
11620
11889
  return null;
11621
11890
  }
11622
- const target = new THREE60__namespace.Vector3();
11891
+ const target = new THREE67__namespace.Vector3();
11623
11892
  this.threeRef.object.getWorldDirection(target);
11624
11893
  return target;
11625
11894
  }
@@ -11681,7 +11950,7 @@ var _Behaviour = class _Behaviour {
11681
11950
  if (!this.threeRef) {
11682
11951
  return;
11683
11952
  }
11684
- const rotation = new THREE60__namespace.Quaternion();
11953
+ const rotation = new THREE67__namespace.Quaternion();
11685
11954
  this.threeRef.object.getWorldQuaternion(rotation);
11686
11955
  rotation.slerp(targetRotation, alpha);
11687
11956
  this.transform.setRotation(rotation);
@@ -11698,7 +11967,7 @@ var _Behaviour = class _Behaviour {
11698
11967
  return;
11699
11968
  }
11700
11969
  const clone3 = this.threeRef.object.clone();
11701
- const rotation = new THREE60__namespace.Quaternion();
11970
+ const rotation = new THREE67__namespace.Quaternion();
11702
11971
  clone3.lookAt(targetPosition);
11703
11972
  clone3.getWorldQuaternion(rotation);
11704
11973
  this.transform.setRotation(rotation);
@@ -11961,8 +12230,8 @@ var _ScriptBehaviourSystem = class _ScriptBehaviourSystem {
11961
12230
  const retryDelay = options?.errorRetryDelay;
11962
12231
  this.errorRetryDelay = typeof retryDelay === "number" && Number.isFinite(retryDelay) ? Math.max(0, retryDelay) : DEFAULT_SCRIPT_ERROR_RETRY_DELAY;
11963
12232
  this.selectionCtx = selectionContext ?? null;
11964
- this.raycaster = new THREE60__namespace.Raycaster();
11965
- this.ndcPointer = new THREE60__namespace.Vector2();
12233
+ this.raycaster = new THREE67__namespace.Raycaster();
12234
+ this.ndcPointer = new THREE67__namespace.Vector2();
11966
12235
  if (this.selectionCtx) {
11967
12236
  this.selectionCtx.domElement.addEventListener("pointerdown", this.onPointerDown);
11968
12237
  this.selectionCtx.domElement.addEventListener("pointerup", this.onPointerUp);
@@ -12406,7 +12675,7 @@ function disposeBuiltObject(root) {
12406
12675
  for (const mat of materials) {
12407
12676
  if (mat.userData?.[SHARED_MODEL_RESOURCE_FLAG]) continue;
12408
12677
  for (const value of Object.values(mat)) {
12409
- if (value instanceof THREE60__namespace.Texture) value.dispose();
12678
+ if (value instanceof THREE67__namespace.Texture) value.dispose();
12410
12679
  }
12411
12680
  mat.dispose?.();
12412
12681
  }
@@ -13007,7 +13276,7 @@ var _AudioSystem = class _AudioSystem {
13007
13276
  if (!prev || prev.volume !== opts.volume) {
13008
13277
  sound.setVolume(opts.volume);
13009
13278
  }
13010
- if (opts.positional && sound instanceof THREE60__namespace.PositionalAudio) {
13279
+ if (opts.positional && sound instanceof THREE67__namespace.PositionalAudio) {
13011
13280
  if (!prev || prev.refDistance !== opts.refDistance) {
13012
13281
  sound.setRefDistance(opts.refDistance);
13013
13282
  }
@@ -13147,7 +13416,7 @@ var _VideoSystem = class _VideoSystem {
13147
13416
  spill: opts.spill
13148
13417
  });
13149
13418
  }
13150
- if (opts.positional && component.audioNode instanceof THREE60__namespace.PositionalAudio) {
13419
+ if (opts.positional && component.audioNode instanceof THREE67__namespace.PositionalAudio) {
13151
13420
  const posAudio = component.audioNode;
13152
13421
  if (!prev || prev.refDistance !== opts.refDistance) {
13153
13422
  posAudio.setRefDistance(opts.refDistance);
@@ -13187,8 +13456,8 @@ function blankState(source) {
13187
13456
  handedness: handednessOf(source),
13188
13457
  kind: kindOf(source),
13189
13458
  connected: true,
13190
- rayOrigin: new THREE60__namespace.Vector3(),
13191
- rayDirection: new THREE60__namespace.Vector3(0, 0, -1),
13459
+ rayOrigin: new THREE67__namespace.Vector3(),
13460
+ rayDirection: new THREE67__namespace.Vector3(0, 0, -1),
13192
13461
  gripPosition: null,
13193
13462
  gripQuaternion: null,
13194
13463
  select: false,
@@ -13214,7 +13483,7 @@ function readHandJoints(frame, hand, referenceSpace, rigMatrix, existing) {
13214
13483
  }
13215
13484
  if (spaces.length === 0) return null;
13216
13485
  const target = existing ?? {};
13217
- const matrix = new THREE60__namespace.Matrix4();
13486
+ const matrix = new THREE67__namespace.Matrix4();
13218
13487
  const buffer = spaces.length * 16 <= JOINT_MATRIX_BUFFER.length ? JOINT_MATRIX_BUFFER : new Float32Array(spaces.length * 16);
13219
13488
  const fillPoses = frame.fillPoses;
13220
13489
  if (typeof fillPoses === "function") {
@@ -13227,7 +13496,7 @@ function readHandJoints(frame, hand, referenceSpace, rigMatrix, existing) {
13227
13496
  if (filled) {
13228
13497
  for (let i = 0; i < names.length; i += 1) {
13229
13498
  matrix.fromArray(buffer, i * 16).premultiply(rigMatrix);
13230
- const vector = target[names[i]] ?? new THREE60__namespace.Vector3();
13499
+ const vector = target[names[i]] ?? new THREE67__namespace.Vector3();
13231
13500
  vector.setFromMatrixPosition(matrix);
13232
13501
  target[names[i]] = vector;
13233
13502
  }
@@ -13245,7 +13514,7 @@ function readHandJoints(frame, hand, referenceSpace, rigMatrix, existing) {
13245
13514
  continue;
13246
13515
  }
13247
13516
  matrix.fromArray(pose.transform.matrix).premultiply(rigMatrix);
13248
- const vector = target[names[i]] ?? new THREE60__namespace.Vector3();
13517
+ const vector = target[names[i]] ?? new THREE67__namespace.Vector3();
13249
13518
  vector.setFromMatrixPosition(matrix);
13250
13519
  target[names[i]] = vector;
13251
13520
  anyTracked = true;
@@ -13257,7 +13526,7 @@ var _XRInputSystem = class _XRInputSystem {
13257
13526
  constructor() {
13258
13527
  this.records = /* @__PURE__ */ new Map();
13259
13528
  /** Reference-space → world matrix, rebuilt each frame from the XR rig. */
13260
- this.referenceToWorld = new THREE60__namespace.Matrix4();
13529
+ this.referenceToWorld = new THREE67__namespace.Matrix4();
13261
13530
  this.session = null;
13262
13531
  this.onSelectStart = /* @__PURE__ */ __name((event) => this.mark(event, "pendingSelectStart"), "onSelectStart");
13263
13532
  this.onSelectEnd = /* @__PURE__ */ __name((event) => this.mark(event, "pendingSelectEnd"), "onSelectEnd");
@@ -13355,19 +13624,19 @@ var _XRInputSystem = class _XRInputSystem {
13355
13624
  if (frame && referenceSpace && source.targetRaySpace) {
13356
13625
  const pose = frame.getPose(source.targetRaySpace, referenceSpace);
13357
13626
  if (pose) {
13358
- const m = new THREE60__namespace.Matrix4().fromArray(pose.transform.matrix).premultiply(rigMatrix);
13627
+ const m = new THREE67__namespace.Matrix4().fromArray(pose.transform.matrix).premultiply(rigMatrix);
13359
13628
  state.rayOrigin.setFromMatrixPosition(m);
13360
13629
  state.rayDirection.set(0, 0, -1).applyQuaternion(
13361
- new THREE60__namespace.Quaternion().setFromRotationMatrix(m)
13630
+ new THREE67__namespace.Quaternion().setFromRotationMatrix(m)
13362
13631
  ).normalize();
13363
13632
  }
13364
13633
  }
13365
13634
  if (frame && referenceSpace && source.gripSpace) {
13366
13635
  const gripPose = frame.getPose(source.gripSpace, referenceSpace);
13367
13636
  if (gripPose) {
13368
- const m = new THREE60__namespace.Matrix4().fromArray(gripPose.transform.matrix).premultiply(rigMatrix);
13369
- state.gripPosition ?? (state.gripPosition = new THREE60__namespace.Vector3());
13370
- state.gripQuaternion ?? (state.gripQuaternion = new THREE60__namespace.Quaternion());
13637
+ const m = new THREE67__namespace.Matrix4().fromArray(gripPose.transform.matrix).premultiply(rigMatrix);
13638
+ state.gripPosition ?? (state.gripPosition = new THREE67__namespace.Vector3());
13639
+ state.gripQuaternion ?? (state.gripQuaternion = new THREE67__namespace.Quaternion());
13371
13640
  state.gripPosition.setFromMatrixPosition(m);
13372
13641
  state.gripQuaternion.setFromRotationMatrix(m);
13373
13642
  }
@@ -13469,6 +13738,51 @@ var _XRInputSystem = class _XRInputSystem {
13469
13738
  };
13470
13739
  __name(_XRInputSystem, "XRInputSystem");
13471
13740
  var XRInputSystem = _XRInputSystem;
13741
+
13742
+ // core/ui/uiWidgetTree.ts
13743
+ var UI_PART_KEY = "__uiPart";
13744
+ function applyPartColor(mesh, tag) {
13745
+ const material = mesh.material;
13746
+ material.color.copy(tag.baseColor).multiplyScalar(tag.premultiplied ? tag.tint * tag.opacity : tag.tint);
13747
+ material.opacity = tag.opacity;
13748
+ }
13749
+ __name(applyPartColor, "applyPartColor");
13750
+ function isUiWidget(world, entityId) {
13751
+ return !!world.getComponent(entityId, UIPanelComponent) || !!world.getComponent(entityId, UITextComponent) || !!world.getComponent(entityId, UIButtonComponent) || !!world.getComponent(entityId, UIToggleComponent) || !!world.getComponent(entityId, UISliderComponent);
13752
+ }
13753
+ __name(isUiWidget, "isUiWidget");
13754
+ function isShownInScene(world, entityId) {
13755
+ let object = world.getComponent(entityId, Object3DRef)?.object ?? null;
13756
+ while (object) {
13757
+ if (!object.visible) return false;
13758
+ object = object.parent;
13759
+ }
13760
+ return true;
13761
+ }
13762
+ __name(isShownInScene, "isShownInScene");
13763
+ function nearestWidgetAncestor(world, entityId) {
13764
+ const visited = /* @__PURE__ */ new Set([entityId]);
13765
+ let current = entityId;
13766
+ for (; ; ) {
13767
+ const parentId = world.getComponent(current, ParentId)?.value;
13768
+ if (typeof parentId !== "number" || visited.has(parentId)) return null;
13769
+ if (isUiWidget(world, parentId)) return parentId;
13770
+ visited.add(parentId);
13771
+ current = parentId;
13772
+ }
13773
+ }
13774
+ __name(nearestWidgetAncestor, "nearestWidgetAncestor");
13775
+ function widgetTreeRoot(world, entityId) {
13776
+ const visited = /* @__PURE__ */ new Set([entityId]);
13777
+ let root = entityId;
13778
+ for (; ; ) {
13779
+ const parent = nearestWidgetAncestor(world, root);
13780
+ if (parent === null || visited.has(parent)) return root;
13781
+ visited.add(parent);
13782
+ root = parent;
13783
+ }
13784
+ }
13785
+ __name(widgetTreeRoot, "widgetTreeRoot");
13472
13786
  var VELOCITY_SAMPLES = 5;
13473
13787
  var MAX_SAMPLE_AGE_SECONDS = 0.2;
13474
13788
  var MAX_PLAUSIBLE_SPEED = 20;
@@ -13495,15 +13809,15 @@ var _ThrowVelocityTracker = class _ThrowVelocityTracker {
13495
13809
  const fresh = this.samples.filter(
13496
13810
  (sample) => now - sample.time <= MAX_SAMPLE_AGE_SECONDS
13497
13811
  );
13498
- if (fresh.length < 2) return new THREE60__namespace.Vector3();
13499
- let best = new THREE60__namespace.Vector3();
13812
+ if (fresh.length < 2) return new THREE67__namespace.Vector3();
13813
+ let best = new THREE67__namespace.Vector3();
13500
13814
  let bestSpeed = 0;
13501
13815
  for (let i = 1; i < fresh.length; i += 1) {
13502
13816
  const previous = fresh[i - 1];
13503
13817
  const current = fresh[i];
13504
13818
  const dt = current.time - previous.time;
13505
13819
  if (dt <= 1e-5) continue;
13506
- const velocity = new THREE60__namespace.Vector3().subVectors(current.position, previous.position).divideScalar(dt);
13820
+ const velocity = new THREE67__namespace.Vector3().subVectors(current.position, previous.position).divideScalar(dt);
13507
13821
  const speed = velocity.length();
13508
13822
  if (speed > MAX_PLAUSIBLE_SPEED) continue;
13509
13823
  if (speed > bestSpeed) {
@@ -13520,15 +13834,15 @@ var _ThrowVelocityTracker = class _ThrowVelocityTracker {
13520
13834
  __name(_ThrowVelocityTracker, "ThrowVelocityTracker");
13521
13835
  var ThrowVelocityTracker = _ThrowVelocityTracker;
13522
13836
  function angularVelocityFrom(previous, current, deltaTime) {
13523
- if (deltaTime <= 1e-5) return new THREE60__namespace.Vector3();
13837
+ if (deltaTime <= 1e-5) return new THREE67__namespace.Vector3();
13524
13838
  const delta = current.clone().multiply(previous.clone().invert());
13525
13839
  if (delta.w < 0) {
13526
13840
  delta.set(-delta.x, -delta.y, -delta.z, -delta.w);
13527
13841
  }
13528
- const angle = 2 * Math.acos(THREE60__namespace.MathUtils.clamp(delta.w, -1, 1));
13842
+ const angle = 2 * Math.acos(THREE67__namespace.MathUtils.clamp(delta.w, -1, 1));
13529
13843
  const sinHalf = Math.sqrt(Math.max(0, 1 - delta.w * delta.w));
13530
- if (sinHalf < 1e-6 || angle < 1e-6) return new THREE60__namespace.Vector3();
13531
- return new THREE60__namespace.Vector3(delta.x, delta.y, delta.z).divideScalar(sinHalf).multiplyScalar(angle / deltaTime);
13844
+ if (sinHalf < 1e-6 || angle < 1e-6) return new THREE67__namespace.Vector3();
13845
+ return new THREE67__namespace.Vector3(delta.x, delta.y, delta.z).divideScalar(sinHalf).multiplyScalar(angle / deltaTime);
13532
13846
  }
13533
13847
  __name(angularVelocityFrom, "angularVelocityFrom");
13534
13848
  var MIN_HAND_SEPARATION = 0.06;
@@ -13536,7 +13850,7 @@ var MIN_ANCHOR_SEPARATION = 1e-3;
13536
13850
  var SCALE_DEADZONE = 0.05;
13537
13851
  function solveTwoHandTransform(primary, secondary, basis, options) {
13538
13852
  const separation = primary.position.distanceTo(secondary.position);
13539
- const anchorDelta = new THREE60__namespace.Vector3().subVectors(
13853
+ const anchorDelta = new THREE67__namespace.Vector3().subVectors(
13540
13854
  basis.secondaryLocal,
13541
13855
  basis.primaryLocal
13542
13856
  );
@@ -13560,8 +13874,8 @@ function solveTwoHandTransform(primary, secondary, basis, options) {
13560
13874
  } else {
13561
13875
  const axisLocal = anchorDelta.clone().normalize();
13562
13876
  const currentAxis = axisLocal.clone().applyQuaternion(base).normalize();
13563
- const desiredAxis = new THREE60__namespace.Vector3().subVectors(secondary.position, primary.position).normalize();
13564
- const correction = new THREE60__namespace.Quaternion().setFromUnitVectors(
13877
+ const desiredAxis = new THREE67__namespace.Vector3().subVectors(secondary.position, primary.position).normalize();
13878
+ const correction = new THREE67__namespace.Quaternion().setFromUnitVectors(
13565
13879
  currentAxis,
13566
13880
  desiredAxis
13567
13881
  );
@@ -13576,11 +13890,11 @@ __name(solveTwoHandTransform, "solveTwoHandTransform");
13576
13890
  // core/systems/XRInteractionSystem.ts
13577
13891
  var RAYLESS_HOLD_DISTANCE = 0.5;
13578
13892
  function quatFromEulerVec(r) {
13579
- return new THREE60__namespace.Quaternion().setFromEuler(new THREE60__namespace.Euler(r.x, r.y, r.z));
13893
+ return new THREE67__namespace.Quaternion().setFromEuler(new THREE67__namespace.Euler(r.x, r.y, r.z));
13580
13894
  }
13581
13895
  __name(quatFromEulerVec, "quatFromEulerVec");
13582
13896
  function writeEuler(target, q) {
13583
- const euler = new THREE60__namespace.Euler().setFromQuaternion(q);
13897
+ const euler = new THREE67__namespace.Euler().setFromQuaternion(q);
13584
13898
  target.x = euler.x;
13585
13899
  target.y = euler.y;
13586
13900
  target.z = euler.z;
@@ -13588,7 +13902,7 @@ function writeEuler(target, q) {
13588
13902
  __name(writeEuler, "writeEuler");
13589
13903
  var _XRInteractionSystem = class _XRInteractionSystem {
13590
13904
  constructor(inputSystem) {
13591
- this.raycaster = new THREE60__namespace.Raycaster();
13905
+ this.raycaster = new THREE67__namespace.Raycaster();
13592
13906
  this.grabs = /* @__PURE__ */ new Map();
13593
13907
  this.hoveredEntityId = null;
13594
13908
  /** Per-hand hover, with the hit distance so the ray can stop at the surface. */
@@ -13699,7 +14013,7 @@ var _XRInteractionSystem = class _XRInteractionSystem {
13699
14013
  const hand = this.handPosition(input);
13700
14014
  const handQuaternion = this.handQuaternion(input);
13701
14015
  const objectQuaternion = quatFromEulerVec(transform.rotation);
13702
- record.offset = new THREE60__namespace.Vector3(
14016
+ record.offset = new THREE67__namespace.Vector3(
13703
14017
  transform.position.x - hand.x,
13704
14018
  transform.position.y - hand.y,
13705
14019
  transform.position.z - hand.z
@@ -13731,7 +14045,7 @@ var _XRInteractionSystem = class _XRInteractionSystem {
13731
14045
  primary.record.twoHand = {
13732
14046
  primaryLocal,
13733
14047
  secondaryLocal,
13734
- initialScale: new THREE60__namespace.Vector3(
14048
+ initialScale: new THREE67__namespace.Vector3(
13735
14049
  transform.scale.x,
13736
14050
  transform.scale.y,
13737
14051
  transform.scale.z
@@ -13752,7 +14066,7 @@ var _XRInteractionSystem = class _XRInteractionSystem {
13752
14066
  /** World point → the object's UNSCALED local space. */
13753
14067
  objectLocalPoint(transform, worldPoint) {
13754
14068
  const local = worldPoint.clone().sub(
13755
- new THREE60__namespace.Vector3(
14069
+ new THREE67__namespace.Vector3(
13756
14070
  transform.position.x,
13757
14071
  transform.position.y,
13758
14072
  transform.position.z
@@ -13810,7 +14124,7 @@ var _XRInteractionSystem = class _XRInteractionSystem {
13810
14124
  );
13811
14125
  if (!interactable2) continue;
13812
14126
  if (interactable2.grabReach === "distance") continue;
13813
- const position = new THREE60__namespace.Vector3();
14127
+ const position = new THREE67__namespace.Vector3();
13814
14128
  entry.object.getWorldPosition(position);
13815
14129
  const distance = position.distanceTo(hand);
13816
14130
  if (distance <= interactable2.grabRadius && distance < nearestDistance) {
@@ -13842,6 +14156,7 @@ var _XRInteractionSystem = class _XRInteractionSystem {
13842
14156
  const interactable = world.getComponent(entityId, XRInteractableComponent);
13843
14157
  if (!interactable?.enabled) continue;
13844
14158
  if (!world.isEntityVisible(entityId)) continue;
14159
+ if (!isShownInScene(world, entityId)) continue;
13845
14160
  const object = world.getComponent(entityId, Object3DRef)?.object;
13846
14161
  if (object) {
13847
14162
  out.push({ entityId, object, pointable: interactable.pointable });
@@ -13882,13 +14197,13 @@ var _XRInteractionSystem = class _XRInteractionSystem {
13882
14197
  this.holdersOf(entityId).map((holder) => holder.record.attachPointId).filter((id) => id !== null)
13883
14198
  );
13884
14199
  const hand = this.handPosition(input);
13885
- const objectPosition = new THREE60__namespace.Vector3(
14200
+ const objectPosition = new THREE67__namespace.Vector3(
13886
14201
  transform.position.x,
13887
14202
  transform.position.y,
13888
14203
  transform.position.z
13889
14204
  );
13890
14205
  const objectQuaternion = quatFromEulerVec(transform.rotation);
13891
- const scale = new THREE60__namespace.Vector3(
14206
+ const scale = new THREE67__namespace.Vector3(
13892
14207
  transform.scale.x,
13893
14208
  transform.scale.y,
13894
14209
  transform.scale.z
@@ -13899,7 +14214,7 @@ var _XRInteractionSystem = class _XRInteractionSystem {
13899
14214
  if (taken.has(point.id)) continue;
13900
14215
  if (point.hand !== "any" && point.hand !== input.handedness) continue;
13901
14216
  if (point.role !== "any" && point.role !== role) continue;
13902
- const world3 = new THREE60__namespace.Vector3(
14217
+ const world3 = new THREE67__namespace.Vector3(
13903
14218
  point.position.x,
13904
14219
  point.position.y,
13905
14220
  point.position.z
@@ -13922,21 +14237,21 @@ var _XRInteractionSystem = class _XRInteractionSystem {
13922
14237
  let offset;
13923
14238
  let remoteDistance = 0;
13924
14239
  if (remote) {
13925
- const position = new THREE60__namespace.Vector3(
14240
+ const position = new THREE67__namespace.Vector3(
13926
14241
  transform.position.x,
13927
14242
  transform.position.y,
13928
14243
  transform.position.z
13929
14244
  );
13930
14245
  remoteDistance = position.distanceTo(input.rayOrigin);
13931
- offset = new THREE60__namespace.Vector3();
14246
+ offset = new THREE67__namespace.Vector3();
13932
14247
  } else if (viaDistance) {
13933
- offset = new THREE60__namespace.Vector3(
14248
+ offset = new THREE67__namespace.Vector3(
13934
14249
  interactable.grabOffsetPosition.x,
13935
14250
  interactable.grabOffsetPosition.y,
13936
14251
  interactable.grabOffsetPosition.z
13937
14252
  );
13938
14253
  } else {
13939
- offset = new THREE60__namespace.Vector3(
14254
+ offset = new THREE67__namespace.Vector3(
13940
14255
  transform.position.x - hand.x,
13941
14256
  transform.position.y - hand.y,
13942
14257
  transform.position.z - hand.z
@@ -13963,7 +14278,7 @@ var _XRInteractionSystem = class _XRInteractionSystem {
13963
14278
  entityId,
13964
14279
  role,
13965
14280
  attachPointId: attachPoint?.id ?? null,
13966
- attachLocal: attachPoint ? new THREE60__namespace.Vector3(
14281
+ attachLocal: attachPoint ? new THREE67__namespace.Vector3(
13967
14282
  attachPoint.position.x,
13968
14283
  attachPoint.position.y,
13969
14284
  attachPoint.position.z
@@ -13972,8 +14287,8 @@ var _XRInteractionSystem = class _XRInteractionSystem {
13972
14287
  handRotationOffset,
13973
14288
  twoHand: null,
13974
14289
  offset,
13975
- rotationOffset: new THREE60__namespace.Quaternion().setFromEuler(
13976
- new THREE60__namespace.Euler(
14290
+ rotationOffset: new THREE67__namespace.Quaternion().setFromEuler(
14291
+ new THREE67__namespace.Euler(
13977
14292
  interactable.grabOffsetRotation.x,
13978
14293
  interactable.grabOffsetRotation.y,
13979
14294
  interactable.grabOffsetRotation.z
@@ -13984,7 +14299,7 @@ var _XRInteractionSystem = class _XRInteractionSystem {
13984
14299
  restoreBodyType,
13985
14300
  tracker,
13986
14301
  lastHandQuaternion: handQuaternion.clone(),
13987
- angularVelocity: new THREE60__namespace.Vector3()
14302
+ angularVelocity: new THREE67__namespace.Vector3()
13988
14303
  });
13989
14304
  if (role === "primary") {
13990
14305
  this.dispatch(world, entityId, "onGrab", (b) => b.onGrab(input));
@@ -14057,14 +14372,14 @@ var _XRInteractionSystem = class _XRInteractionSystem {
14057
14372
  const handQuaternion = this.handQuaternion(input);
14058
14373
  const objectQuaternion = held.attachQuaternion ? handQuaternion.clone().multiply(held.rotationOffset).multiply(held.attachQuaternion.clone().invert()) : handQuaternion.clone().multiply(held.handRotationOffset);
14059
14374
  if (held.remote) {
14060
- const target = new THREE60__namespace.Vector3().copy(input.rayDirection).multiplyScalar(held.remoteDistance).add(input.rayOrigin);
14375
+ const target = new THREE67__namespace.Vector3().copy(input.rayDirection).multiplyScalar(held.remoteDistance).add(input.rayOrigin);
14061
14376
  transform.position.x = target.x;
14062
14377
  transform.position.y = target.y;
14063
14378
  transform.position.z = target.z;
14064
14379
  writeEuler(transform.rotation, objectQuaternion);
14065
14380
  } else if (held.attachLocal) {
14066
14381
  const anchor = held.attachLocal.clone().multiply(
14067
- new THREE60__namespace.Vector3(
14382
+ new THREE67__namespace.Vector3(
14068
14383
  transform.scale.x,
14069
14384
  transform.scale.y,
14070
14385
  transform.scale.z
@@ -14082,7 +14397,7 @@ var _XRInteractionSystem = class _XRInteractionSystem {
14082
14397
  writeEuler(transform.rotation, objectQuaternion);
14083
14398
  }
14084
14399
  held.tracker.record(
14085
- held.remote ? new THREE60__namespace.Vector3(
14400
+ held.remote ? new THREE67__namespace.Vector3(
14086
14401
  transform.position.x,
14087
14402
  transform.position.y,
14088
14403
  transform.position.z
@@ -14157,7 +14472,7 @@ var _XRInteractionSystem = class _XRInteractionSystem {
14157
14472
  */
14158
14473
  handPosition(input) {
14159
14474
  if (input.gripPosition) return input.gripPosition;
14160
- return new THREE60__namespace.Vector3().copy(input.rayDirection).multiplyScalar(RAYLESS_HOLD_DISTANCE).add(input.rayOrigin);
14475
+ return new THREE67__namespace.Vector3().copy(input.rayDirection).multiplyScalar(RAYLESS_HOLD_DISTANCE).add(input.rayOrigin);
14161
14476
  }
14162
14477
  /**
14163
14478
  * Hand orientation. Grip-less sources (gaze / transient-pointer) have none,
@@ -14165,8 +14480,8 @@ var _XRInteractionSystem = class _XRInteractionSystem {
14165
14480
  */
14166
14481
  handQuaternion(input) {
14167
14482
  if (input.gripQuaternion) return input.gripQuaternion.clone();
14168
- return new THREE60__namespace.Quaternion().setFromUnitVectors(
14169
- new THREE60__namespace.Vector3(0, 0, -1),
14483
+ return new THREE67__namespace.Quaternion().setFromUnitVectors(
14484
+ new THREE67__namespace.Vector3(0, 0, -1),
14170
14485
  input.rayDirection.clone().normalize()
14171
14486
  );
14172
14487
  }
@@ -14303,7 +14618,7 @@ function sampleTeleportArc(origin, direction, out = [], options) {
14303
14618
  for (let i = 0; i < steps; i += 1) {
14304
14619
  const t = i / (steps - 1) * maxTime;
14305
14620
  out.push(
14306
- new THREE60__namespace.Vector3(
14621
+ new THREE67__namespace.Vector3(
14307
14622
  origin.x + velocity.x * t,
14308
14623
  origin.y + velocity.y * t - 0.5 * gravity * t * t,
14309
14624
  origin.z + velocity.z * t
@@ -14320,7 +14635,7 @@ function arcGroundCrossing(points, groundY) {
14320
14635
  if (previous.y >= groundY && current.y <= groundY) {
14321
14636
  const span = previous.y - current.y;
14322
14637
  const alpha = span > 1e-6 ? (previous.y - groundY) / span : 0;
14323
- return new THREE60__namespace.Vector3().lerpVectors(previous, current, alpha);
14638
+ return new THREE67__namespace.Vector3().lerpVectors(previous, current, alpha);
14324
14639
  }
14325
14640
  }
14326
14641
  return null;
@@ -14364,11 +14679,11 @@ __name(_TeleportAimGate, "TeleportAimGate");
14364
14679
  var TeleportAimGate = _TeleportAimGate;
14365
14680
 
14366
14681
  // core/systems/XRLocomotionSystem.ts
14367
- var UP = new THREE60__namespace.Vector3(0, 1, 0);
14682
+ var UP = new THREE67__namespace.Vector3(0, 1, 0);
14368
14683
  var AXIS_DEADZONE = 0.5;
14369
14684
  var _XRLocomotionSystem = class _XRLocomotionSystem {
14370
14685
  constructor(inputSystem) {
14371
- this.raycaster = new THREE60__namespace.Raycaster();
14686
+ this.raycaster = new THREE67__namespace.Raycaster();
14372
14687
  /** Per-source latch so one stick push yields exactly one snap turn. */
14373
14688
  this.turnLatched = /* @__PURE__ */ new WeakSet();
14374
14689
  /** Reused arc buffer — aiming runs every frame. */
@@ -14494,7 +14809,7 @@ var _XRLocomotionSystem = class _XRLocomotionSystem {
14494
14809
  }
14495
14810
  if (this.turnLatched.has(source)) continue;
14496
14811
  this.turnLatched.add(source);
14497
- transform.rotation.y -= Math.sign(x) * THREE60__namespace.MathUtils.degToRad(rig.snapTurnDegrees);
14812
+ transform.rotation.y -= Math.sign(x) * THREE67__namespace.MathUtils.degToRad(rig.snapTurnDegrees);
14498
14813
  }
14499
14814
  }
14500
14815
  applySmoothMove(world, rig, transform, inputs, deltaTime) {
@@ -14504,12 +14819,12 @@ var _XRLocomotionSystem = class _XRLocomotionSystem {
14504
14819
  const y = input.axes[1] ?? 0;
14505
14820
  if (Math.abs(x) < 0.15 && Math.abs(y) < 0.15) continue;
14506
14821
  const camera = world.getCamera();
14507
- const forward = new THREE60__namespace.Vector3();
14822
+ const forward = new THREE67__namespace.Vector3();
14508
14823
  camera.getWorldDirection(forward);
14509
14824
  forward.y = 0;
14510
14825
  if (forward.lengthSq() < 1e-6) continue;
14511
14826
  forward.normalize();
14512
- const right = new THREE60__namespace.Vector3().crossVectors(forward, UP).normalize();
14827
+ const right = new THREE67__namespace.Vector3().crossVectors(forward, UP).normalize();
14513
14828
  const step = rig.moveSpeed * Math.max(0, deltaTime);
14514
14829
  transform.position.x += (forward.x * -y + right.x * x) * step;
14515
14830
  transform.position.z += (forward.z * -y + right.z * x) * step;
@@ -14590,7 +14905,7 @@ var _XRLocomotionSystem = class _XRLocomotionSystem {
14590
14905
  for (let i = 1; i < points.length; i += 1) {
14591
14906
  const from = points[i - 1];
14592
14907
  const to = points[i];
14593
- const delta = new THREE60__namespace.Vector3().subVectors(to, from);
14908
+ const delta = new THREE67__namespace.Vector3().subVectors(to, from);
14594
14909
  const distance = delta.length();
14595
14910
  if (distance < 1e-6) continue;
14596
14911
  this.raycaster.set(from, delta.divideScalar(distance));
@@ -14863,12 +15178,12 @@ function planeColliderFromPolygon(polygon) {
14863
15178
  }
14864
15179
  __name(planeColliderFromPolygon, "planeColliderFromPolygon");
14865
15180
  function writeXrMeshGeometry(vertices, indices, existing) {
14866
- const geometry = existing ?? new THREE60__namespace.BufferGeometry();
15181
+ const geometry = existing ?? new THREE67__namespace.BufferGeometry();
14867
15182
  geometry.setAttribute(
14868
15183
  "position",
14869
- new THREE60__namespace.BufferAttribute(vertices.slice(), 3)
15184
+ new THREE67__namespace.BufferAttribute(vertices.slice(), 3)
14870
15185
  );
14871
- geometry.setIndex(new THREE60__namespace.BufferAttribute(indices.slice(), 1));
15186
+ geometry.setIndex(new THREE67__namespace.BufferAttribute(indices.slice(), 1));
14872
15187
  geometry.deleteAttribute("normal");
14873
15188
  geometry.computeBoundingSphere();
14874
15189
  geometry.computeVertexNormals();
@@ -14876,14 +15191,14 @@ function writeXrMeshGeometry(vertices, indices, existing) {
14876
15191
  }
14877
15192
  __name(writeXrMeshGeometry, "writeXrMeshGeometry");
14878
15193
  function decomposeXrPose(matrixElements, target) {
14879
- const matrix = new THREE60__namespace.Matrix4().fromArray(
15194
+ const matrix = new THREE67__namespace.Matrix4().fromArray(
14880
15195
  matrixElements
14881
15196
  );
14882
- const position = new THREE60__namespace.Vector3();
14883
- const quaternion = new THREE60__namespace.Quaternion();
14884
- const scale = new THREE60__namespace.Vector3();
15197
+ const position = new THREE67__namespace.Vector3();
15198
+ const quaternion = new THREE67__namespace.Quaternion();
15199
+ const scale = new THREE67__namespace.Vector3();
14885
15200
  matrix.decompose(position, quaternion, scale);
14886
- const euler = new THREE60__namespace.Euler().setFromQuaternion(quaternion, "XYZ");
15201
+ const euler = new THREE67__namespace.Euler().setFromQuaternion(quaternion, "XYZ");
14887
15202
  target.position.x = position.x;
14888
15203
  target.position.y = position.y;
14889
15204
  target.position.z = position.z;
@@ -15045,8 +15360,8 @@ var _XREnvironmentSystem = class _XREnvironmentSystem {
15045
15360
  this.viewerPos = null;
15046
15361
  /** The ONE global room mesh currently allowed to spawn. */
15047
15362
  this.electedGlobalMesh = null;
15048
- this.tmpViewerMatrix = new THREE60__namespace.Matrix4();
15049
- this.tmpViewPos = new THREE60__namespace.Vector3();
15363
+ this.tmpViewerMatrix = new THREE67__namespace.Matrix4();
15364
+ this.tmpViewPos = new THREE67__namespace.Vector3();
15050
15365
  }
15051
15366
  update(world, deltaTime = 0) {
15052
15367
  const presenting = world.isXrPresenting();
@@ -15405,7 +15720,7 @@ var _XREnvironmentSystem = class _XREnvironmentSystem {
15405
15720
  const geometry = writeXrMeshGeometry(mesh.vertices, mesh.indices);
15406
15721
  const label = mesh.semanticLabel ?? null;
15407
15722
  const isGlobalMesh = label === "global mesh";
15408
- const material = new THREE60__namespace.MeshBasicMaterial({
15723
+ const material = new THREE67__namespace.MeshBasicMaterial({
15409
15724
  color: debugColorFor("mesh", label),
15410
15725
  wireframe: true,
15411
15726
  // The global scan is a coarse triangle soup of the whole room; at full
@@ -15426,8 +15741,8 @@ var _XREnvironmentSystem = class _XREnvironmentSystem {
15426
15741
  depthTest: true,
15427
15742
  depthWrite: false
15428
15743
  });
15429
- const root = new THREE60__namespace.Group();
15430
- const child = new THREE60__namespace.Mesh(geometry, material);
15744
+ const root = new THREE67__namespace.Group();
15745
+ const child = new THREE67__namespace.Mesh(geometry, material);
15431
15746
  child.visible = this.debugVisible;
15432
15747
  child.renderOrder = DEBUG_RENDER_ORDER;
15433
15748
  root.add(child);
@@ -15453,21 +15768,21 @@ var _XREnvironmentSystem = class _XREnvironmentSystem {
15453
15768
  }
15454
15769
  /** A thin box so a detected plane can be SEEN, not just collided with. */
15455
15770
  attachPlaneDebug(world, entry, collider, semanticLabel) {
15456
- const box = new THREE60__namespace.BoxGeometry(
15771
+ const box = new THREE67__namespace.BoxGeometry(
15457
15772
  collider.size.x,
15458
15773
  collider.size.y,
15459
15774
  collider.size.z
15460
15775
  );
15461
- const geometry = new THREE60__namespace.EdgesGeometry(box);
15776
+ const geometry = new THREE67__namespace.EdgesGeometry(box);
15462
15777
  box.dispose();
15463
- const material = new THREE60__namespace.LineBasicMaterial({
15778
+ const material = new THREE67__namespace.LineBasicMaterial({
15464
15779
  color: debugColorFor("plane", semanticLabel),
15465
15780
  // Same depth policy as the mesh overlay — see createMeshEntity.
15466
15781
  depthTest: true,
15467
15782
  depthWrite: false
15468
15783
  });
15469
- const root = new THREE60__namespace.Group();
15470
- const child = new THREE60__namespace.LineSegments(geometry, material);
15784
+ const root = new THREE67__namespace.Group();
15785
+ const child = new THREE67__namespace.LineSegments(geometry, material);
15471
15786
  child.renderOrder = DEBUG_RENDER_ORDER;
15472
15787
  child.position.set(collider.offset.x, collider.offset.y, collider.offset.z);
15473
15788
  child.visible = this.debugVisible;
@@ -15543,6 +15858,14 @@ function normalizeOrientation(value) {
15543
15858
  return value === "horizontal" || value === "vertical" ? value : null;
15544
15859
  }
15545
15860
  __name(normalizeOrientation, "normalizeOrientation");
15861
+ var isDisplayOnlySlider = /* @__PURE__ */ __name((slider) => slider.readOnly === true || slider.variant !== void 0 && slider.variant !== "slider", "isDisplayOnlySlider");
15862
+ function snapToStep(value, slider) {
15863
+ const { step, min, max } = slider;
15864
+ if (!step || step <= 0) return value;
15865
+ const snapped = min + Math.round((value - min) / step) * step;
15866
+ return Math.min(max, Math.max(min, snapped));
15867
+ }
15868
+ __name(snapToStep, "snapToStep");
15546
15869
  var MOUSE_CLICK_TRAVEL_PX = 6;
15547
15870
  var PRESS_TINT_MIN_SECONDS = 0.1;
15548
15871
  var PRESS_TINT = 0.72;
@@ -15556,11 +15879,12 @@ var _UIInteractionSystem = class _UIInteractionSystem {
15556
15879
  constructor(interaction, options) {
15557
15880
  this.xrPresses = /* @__PURE__ */ new Map();
15558
15881
  this.mousePress = null;
15559
- this.mouseHover = null;
15882
+ /** What each pointer is on. Two hands can rest on one widget. */
15883
+ this.hovers = /* @__PURE__ */ new Map();
15560
15884
  /** entityId → seconds of pressed-tint left to display. */
15561
15885
  this.tintFloors = /* @__PURE__ */ new Map();
15562
15886
  this.tinted = /* @__PURE__ */ new Set();
15563
- this.raycaster = new THREE60__namespace.Raycaster();
15887
+ this.raycaster = new THREE67__namespace.Raycaster();
15564
15888
  this.interaction = interaction;
15565
15889
  this.inputSystem = options?.inputSystem ?? null;
15566
15890
  interaction?.registerBusyContributor(
@@ -15570,8 +15894,10 @@ var _UIInteractionSystem = class _UIInteractionSystem {
15570
15894
  update(world, deltaTime = 0) {
15571
15895
  if (world.isEditorMode()) return;
15572
15896
  if (world.isXrPresenting()) {
15897
+ this.setHover(world, "mouse", null, null);
15573
15898
  this.updateXr(world);
15574
15899
  } else {
15900
+ this.dropGoneXrHovers(world, []);
15575
15901
  this.updateMouse(world);
15576
15902
  }
15577
15903
  this.updateTints(world, deltaTime);
@@ -15585,6 +15911,7 @@ var _UIInteractionSystem = class _UIInteractionSystem {
15585
15911
  for (const input of inputs) {
15586
15912
  const hover = interaction.getHoverForInput(input);
15587
15913
  const hovered = hover ? this.widgetKind(world, hover.entityId) : null;
15914
+ this.setHover(world, input, hover && hovered ? hover.entityId : null, input);
15588
15915
  if (input.selectStart && hover && hovered) {
15589
15916
  this.xrPresses.set(input, {
15590
15917
  entityId: hover.entityId,
@@ -15613,6 +15940,15 @@ var _UIInteractionSystem = class _UIInteractionSystem {
15613
15940
  for (const [input] of this.xrPresses) {
15614
15941
  if (!inputs.includes(input)) this.xrPresses.delete(input);
15615
15942
  }
15943
+ this.dropGoneXrHovers(world, inputs);
15944
+ }
15945
+ /** A transient source leaves with no select edge to clear its hover. */
15946
+ dropGoneXrHovers(world, inputs) {
15947
+ for (const source of [...this.hovers.keys()]) {
15948
+ if (source !== "mouse" && !inputs.includes(source)) {
15949
+ this.setHover(world, source, null, source);
15950
+ }
15951
+ }
15616
15952
  }
15617
15953
  // ── Mouse path (flat preview) ──────────────────────────────────────────────
15618
15954
  updateMouse(world) {
@@ -15620,22 +15956,7 @@ var _UIInteractionSystem = class _UIInteractionSystem {
15620
15956
  if (!input) return;
15621
15957
  const hit = this.raycastMouse(world);
15622
15958
  const hovered = hit && this.widgetKind(world, hit) ? hit : null;
15623
- if (hovered !== this.mouseHover) {
15624
- if (this.mouseHover !== null) {
15625
- this.setTint(world, this.mouseHover, 1);
15626
- this.dispatchBubbling(
15627
- world,
15628
- this.mouseHover,
15629
- "onUiHoverEnd",
15630
- (b, e) => b.onUiHoverEnd(e)
15631
- );
15632
- }
15633
- if (hovered !== null) {
15634
- this.setTint(world, hovered, HOVER_TINT);
15635
- this.dispatchBubbling(world, hovered, "onUiHoverStart", (b, e) => b.onUiHoverStart(e));
15636
- }
15637
- this.mouseHover = hovered;
15638
- }
15959
+ this.setHover(world, "mouse", hovered, null);
15639
15960
  if (input.getMouseDown("left") && hovered) {
15640
15961
  const kind = this.widgetKind(world, hovered);
15641
15962
  if (kind) {
@@ -15643,12 +15964,12 @@ var _UIInteractionSystem = class _UIInteractionSystem {
15643
15964
  this.beginPressTint(world, hovered);
15644
15965
  }
15645
15966
  }
15646
- if (this.mousePress) {
15967
+ if (this.mousePress && !input.getMouseDown("left")) {
15647
15968
  const delta = input.getMouseDelta();
15648
15969
  this.mousePress.travelPx += Math.hypot(delta.x, delta.y);
15649
- if (this.mousePress.kind === "slider") {
15650
- this.dragSliderMouse(world, this.mousePress.entityId);
15651
- }
15970
+ }
15971
+ if (this.mousePress?.kind === "slider") {
15972
+ this.dragSliderMouse(world, this.mousePress.entityId);
15652
15973
  }
15653
15974
  if (input.getMouseUp("left") && this.mousePress) {
15654
15975
  const press = this.mousePress;
@@ -15677,15 +15998,24 @@ var _UIInteractionSystem = class _UIInteractionSystem {
15677
15998
  ndcY = -((pos.y - rect.top) / Math.max(1, rect.height) * 2 - 1);
15678
15999
  if (ndcX < -1 || ndcX > 1 || ndcY < -1 || ndcY > 1) return null;
15679
16000
  }
15680
- this.raycaster.setFromCamera(new THREE60__namespace.Vector2(ndcX, ndcY), world.getCamera());
16001
+ this.raycaster.setFromCamera(new THREE67__namespace.Vector2(ndcX, ndcY), world.getCamera());
15681
16002
  const hits = world.raycast(
15682
16003
  this.raycaster.ray.origin,
15683
16004
  this.raycaster.ray.direction
15684
16005
  );
16006
+ let surfaceTree = null;
15685
16007
  for (const hit of hits) {
15686
- if (typeof hit.entityId !== "number") continue;
15687
- if (this.widgetKind(world, hit.entityId)) return hit.entityId;
15688
- return null;
16008
+ const entityId = hit.entityId;
16009
+ if (typeof entityId !== "number") continue;
16010
+ if (!isShownInScene(world, entityId)) continue;
16011
+ if (this.widgetKind(world, entityId)) {
16012
+ const sameSurface = surfaceTree === null || widgetTreeRoot(world, entityId) === surfaceTree;
16013
+ return sameSurface ? entityId : null;
16014
+ }
16015
+ if (!isUiWidget(world, entityId)) return null;
16016
+ const tree = widgetTreeRoot(world, entityId);
16017
+ if (surfaceTree !== null && tree !== surfaceTree) return null;
16018
+ surfaceTree = tree;
15689
16019
  }
15690
16020
  return null;
15691
16021
  }
@@ -15693,7 +16023,8 @@ var _UIInteractionSystem = class _UIInteractionSystem {
15693
16023
  widgetKind(world, entityId) {
15694
16024
  if (world.getComponent(entityId, UIButtonComponent)) return "button";
15695
16025
  if (world.getComponent(entityId, UIToggleComponent)) return "toggle";
15696
- if (world.getComponent(entityId, UISliderComponent)) return "slider";
16026
+ const slider = world.getComponent(entityId, UISliderComponent);
16027
+ if (slider && !isDisplayOnlySlider(slider)) return "slider";
15697
16028
  return null;
15698
16029
  }
15699
16030
  fire(world, press, input) {
@@ -15744,7 +16075,10 @@ var _UIInteractionSystem = class _UIInteractionSystem {
15744
16075
  const inset = slider.height / 2;
15745
16076
  const usable = Math.max(1e-3, slider.width - inset * 2);
15746
16077
  const t = Math.min(1, Math.max(0, local.x / usable + 0.5));
15747
- const next = slider.min + t * (slider.max - slider.min);
16078
+ const next = snapToStep(
16079
+ slider.min + t * (slider.max - slider.min),
16080
+ slider
16081
+ );
15748
16082
  if (Math.abs(next - slider.value) < 1e-4) return;
15749
16083
  slider.value = next;
15750
16084
  this.dispatchBubbling(
@@ -15822,6 +16156,43 @@ var _UIInteractionSystem = class _UIInteractionSystem {
15822
16156
  const name = world.getComponent(entityId, Name)?.value;
15823
16157
  return typeof name === "string" ? name : String(entityId);
15824
16158
  }
16159
+ // ── Hover ──────────────────────────────────────────────────────────────────
16160
+ setHover(world, source, next, input) {
16161
+ const previous = this.hovers.get(source) ?? null;
16162
+ if (previous === next) return;
16163
+ if (next === null) this.hovers.delete(source);
16164
+ else this.hovers.set(source, next);
16165
+ if (previous !== null && !this.isHovered(previous)) {
16166
+ if (!this.tintFloors.has(previous)) this.setTint(world, previous, 1);
16167
+ this.dispatchBubbling(
16168
+ world,
16169
+ previous,
16170
+ "onUiHoverEnd",
16171
+ (b, e) => b.onUiHoverEnd(e),
16172
+ input
16173
+ );
16174
+ }
16175
+ if (next !== null && this.hoverCount(next) === 1) {
16176
+ if (!this.tintFloors.has(next)) this.setTint(world, next, HOVER_TINT);
16177
+ this.dispatchBubbling(
16178
+ world,
16179
+ next,
16180
+ "onUiHoverStart",
16181
+ (b, e) => b.onUiHoverStart(e),
16182
+ input
16183
+ );
16184
+ }
16185
+ }
16186
+ hoverCount(entityId) {
16187
+ let count = 0;
16188
+ for (const hovered of this.hovers.values()) {
16189
+ if (hovered === entityId) count += 1;
16190
+ }
16191
+ return count;
16192
+ }
16193
+ isHovered(entityId) {
16194
+ return this.hoverCount(entityId) > 0;
16195
+ }
15825
16196
  // ── Pressed/hover tint ─────────────────────────────────────────────────────
15826
16197
  beginPressTint(world, entityId) {
15827
16198
  this.setTint(world, entityId, PRESS_TINT);
@@ -15839,11 +16210,7 @@ var _UIInteractionSystem = class _UIInteractionSystem {
15839
16210
  if (stillHeld) continue;
15840
16211
  if (next <= 0) {
15841
16212
  this.tintFloors.delete(entityId);
15842
- this.setTint(
15843
- world,
15844
- entityId,
15845
- this.mouseHover === entityId ? HOVER_TINT : 1
15846
- );
16213
+ this.setTint(world, entityId, this.isHovered(entityId) ? HOVER_TINT : 1);
15847
16214
  } else {
15848
16215
  this.tintFloors.set(entityId, next);
15849
16216
  }
@@ -15854,14 +16221,101 @@ var _UIInteractionSystem = class _UIInteractionSystem {
15854
16221
  if (!object) return;
15855
16222
  if (scalar === 1) this.tinted.delete(entityId);
15856
16223
  else this.tinted.add(entityId);
15857
- object.traverse((child) => {
15858
- const material = child.material;
15859
- if (material?.color) material.color.setScalar(scalar);
15860
- });
16224
+ for (const child of object.children) {
16225
+ const tag = child.userData[UI_PART_KEY];
16226
+ if (!tag) continue;
16227
+ tag.tint = scalar;
16228
+ applyPartColor(child, tag);
16229
+ }
15861
16230
  }
15862
16231
  };
15863
16232
  __name(_UIInteractionSystem, "UIInteractionSystem");
15864
16233
  var UIInteractionSystem = _UIInteractionSystem;
16234
+ var fonts = /* @__PURE__ */ new Map();
16235
+ var images = /* @__PURE__ */ new Map();
16236
+ function hash(text) {
16237
+ let value = 5381;
16238
+ for (let i = 0; i < text.length; i += 1) {
16239
+ value = (value * 33 ^ text.charCodeAt(i)) >>> 0;
16240
+ }
16241
+ return value.toString(36);
16242
+ }
16243
+ __name(hash, "hash");
16244
+ function tracked(url, work) {
16245
+ const manager = THREE67__namespace.DefaultLoadingManager;
16246
+ manager.itemStart(url);
16247
+ return work.catch((error) => {
16248
+ manager.itemError(url);
16249
+ throw error;
16250
+ }).finally(() => manager.itemEnd(url));
16251
+ }
16252
+ __name(tracked, "tracked");
16253
+ function uiFontFamily(src) {
16254
+ if (typeof FontFace === "undefined" || typeof document === "undefined") {
16255
+ return null;
16256
+ }
16257
+ const known = fonts.get(src);
16258
+ if (known) return known.value;
16259
+ const entry = { value: null, settled: false };
16260
+ fonts.set(src, entry);
16261
+ const url = resolvePrefabAssetUrl(src);
16262
+ const family = `relu-ui-${hash(url)}`;
16263
+ tracked(
16264
+ url,
16265
+ (async () => {
16266
+ const response = await fetch(url);
16267
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
16268
+ const buffer = await response.arrayBuffer();
16269
+ const face = new FontFace(family, buffer, { weight: "100 900" });
16270
+ await face.load();
16271
+ document.fonts.add(face);
16272
+ })()
16273
+ ).then(() => {
16274
+ entry.value = family;
16275
+ }).catch((error) => {
16276
+ console.warn(`[spatial-ecs] Couldn't load UI font ${src}.`, error);
16277
+ }).finally(() => {
16278
+ entry.settled = true;
16279
+ });
16280
+ return null;
16281
+ }
16282
+ __name(uiFontFamily, "uiFontFamily");
16283
+ function uiImage(src) {
16284
+ if (typeof Image === "undefined" || typeof fetch === "undefined") {
16285
+ return null;
16286
+ }
16287
+ const known = images.get(src);
16288
+ if (known) return known.value;
16289
+ const entry = { value: null, settled: false };
16290
+ images.set(src, entry);
16291
+ const url = resolvePrefabAssetUrl(src);
16292
+ tracked(
16293
+ url,
16294
+ (async () => {
16295
+ const response = await fetch(url);
16296
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
16297
+ const blob = await response.blob();
16298
+ const typed = /\.svg(\?|#|$)/i.test(url) ? new Blob([blob], { type: "image/svg+xml" }) : blob;
16299
+ const image = new Image();
16300
+ const objectUrl = URL.createObjectURL(typed);
16301
+ try {
16302
+ image.src = objectUrl;
16303
+ await image.decode();
16304
+ return image;
16305
+ } finally {
16306
+ URL.revokeObjectURL(objectUrl);
16307
+ }
16308
+ })()
16309
+ ).then((image) => {
16310
+ entry.value = image;
16311
+ }).catch((error) => {
16312
+ console.warn(`[spatial-ecs] Couldn't load UI image ${src}.`, error);
16313
+ }).finally(() => {
16314
+ entry.settled = true;
16315
+ });
16316
+ return null;
16317
+ }
16318
+ __name(uiImage, "uiImage");
15865
16319
 
15866
16320
  // core/ui/uiCanvas.ts
15867
16321
  var DEFAULT_PIXELS_PER_METER = 1024;
@@ -15934,18 +16388,21 @@ function clampLines(measure, lines, maxLines, maxWidthPx) {
15934
16388
  }
15935
16389
  __name(clampLines, "clampLines");
15936
16390
  var UI_FONT_STACK = '-apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif';
15937
- function tracePath(ctx, x, y, w, h, r) {
15938
- const radius = Math.max(0, Math.min(r, w / 2, h / 2));
16391
+ var NO_ASSETS = { image: /* @__PURE__ */ __name(() => null, "image") };
16392
+ function tracePath(ctx, x, y, w, h, radii) {
16393
+ const limit = Math.min(w / 2, h / 2);
16394
+ const fit = /* @__PURE__ */ __name((r) => Math.max(0, Math.min(r, limit)), "fit");
16395
+ const [tl, tr, br, bl] = (Array.isArray(radii) ? radii : [radii, radii, radii, radii]).map(fit);
15939
16396
  ctx.beginPath();
15940
16397
  if (typeof ctx.roundRect === "function") {
15941
- ctx.roundRect(x, y, w, h, radius);
16398
+ ctx.roundRect(x, y, w, h, [tl, tr, br, bl]);
15942
16399
  return;
15943
16400
  }
15944
- ctx.moveTo(x + radius, y);
15945
- ctx.arcTo(x + w, y, x + w, y + h, radius);
15946
- ctx.arcTo(x + w, y + h, x, y + h, radius);
15947
- ctx.arcTo(x, y + h, x, y, radius);
15948
- ctx.arcTo(x, y, x + w, y, radius);
16401
+ ctx.moveTo(x + tl, y);
16402
+ ctx.arcTo(x + w, y, x + w, y + h, tr);
16403
+ ctx.arcTo(x + w, y + h, x, y + h, br);
16404
+ ctx.arcTo(x, y + h, x, y, bl);
16405
+ ctx.arcTo(x, y, x + w, y, tl);
15949
16406
  ctx.closePath();
15950
16407
  }
15951
16408
  __name(tracePath, "tracePath");
@@ -15965,108 +16422,244 @@ function withOpacity(ctx, color, opacity) {
15965
16422
  return parsed;
15966
16423
  }
15967
16424
  __name(withOpacity, "withOpacity");
15968
- function drawChrome(ctx, pxWidth, pxHeight, density, style) {
15969
- ctx.clearRect(0, 0, pxWidth, pxHeight);
15970
- const border = Math.max(0, style.borderWidthMeters * density);
15971
- const radius = style.cornerRadiusMeters * density;
15972
- tracePath(ctx, 0, 0, pxWidth, pxHeight, radius);
15973
- ctx.fillStyle = withOpacity(
15974
- ctx,
15975
- style.backgroundColor,
15976
- Math.max(0, Math.min(1, style.backgroundOpacity))
15977
- );
16425
+ function fillStyleFor(ctx, paint, box, opacity) {
16426
+ if (paint.kind === "solid") return withOpacity(ctx, paint.color, opacity);
16427
+ const cx = box.x + box.w / 2;
16428
+ const cy = box.y + box.h / 2;
16429
+ let gradient;
16430
+ if (paint.kind === "linear") {
16431
+ const radians = paint.angle * Math.PI / 180;
16432
+ const dx = Math.sin(radians);
16433
+ const dy = -Math.cos(radians);
16434
+ const half = (Math.abs(box.w * dx) + Math.abs(box.h * dy)) / 2;
16435
+ gradient = ctx.createLinearGradient(
16436
+ cx - dx * half,
16437
+ cy - dy * half,
16438
+ cx + dx * half,
16439
+ cy + dy * half
16440
+ );
16441
+ } else {
16442
+ gradient = ctx.createRadialGradient(
16443
+ cx,
16444
+ cy,
16445
+ 0,
16446
+ cx,
16447
+ cy,
16448
+ Math.max(box.w, box.h) / 2
16449
+ );
16450
+ }
16451
+ for (const stop of paint.stops) {
16452
+ gradient.addColorStop(stop.at, withOpacity(ctx, stop.color, opacity));
16453
+ }
16454
+ return gradient;
16455
+ }
16456
+ __name(fillStyleFor, "fillStyleFor");
16457
+ function drawImagePaint(ctx, paint, box, assets) {
16458
+ const image = assets.image(paint.src);
16459
+ if (!image) return;
16460
+ const sourceW = image.naturalWidth || box.w;
16461
+ const sourceH = image.naturalHeight || box.h;
16462
+ 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);
16463
+ const w = paint.fit === "fill" ? box.w : sourceW * scale;
16464
+ const h = paint.fit === "fill" ? box.h : sourceH * scale;
16465
+ const x = box.x + (box.w - w) / 2;
16466
+ const y = box.y + (box.h - h) / 2;
16467
+ if (!paint.tint) {
16468
+ ctx.drawImage(image, x, y, w, h);
16469
+ return;
16470
+ }
16471
+ const scratch = assets.scratch?.(Math.ceil(w), Math.ceil(h));
16472
+ if (!scratch) {
16473
+ ctx.drawImage(image, x, y, w, h);
16474
+ return;
16475
+ }
16476
+ scratch.drawImage(image, 0, 0, w, h);
16477
+ scratch.globalCompositeOperation = "source-in";
16478
+ scratch.fillStyle = paint.tint;
16479
+ scratch.fillRect(0, 0, w, h);
16480
+ ctx.drawImage(scratch.canvas, x, y, w, h);
16481
+ }
16482
+ __name(drawImagePaint, "drawImagePaint");
16483
+ function pxRadii(radius, density) {
16484
+ return radius.map((r) => r * density);
16485
+ }
16486
+ __name(pxRadii, "pxRadii");
16487
+ function drawBoxShadow(ctx, box, radii, shadow, density) {
16488
+ ctx.save();
16489
+ ctx.shadowColor = shadow.color;
16490
+ ctx.shadowBlur = shadow.blur * density;
16491
+ ctx.shadowOffsetX = shadow.x * density;
16492
+ ctx.shadowOffsetY = -shadow.y * density;
16493
+ ctx.fillStyle = "#000000";
16494
+ tracePath(ctx, box.x, box.y, box.w, box.h, radii);
16495
+ ctx.fill();
16496
+ ctx.restore();
16497
+ ctx.save();
16498
+ ctx.globalCompositeOperation = "destination-out";
16499
+ ctx.fillStyle = "#000000";
16500
+ tracePath(ctx, box.x, box.y, box.w, box.h, radii);
15978
16501
  ctx.fill();
15979
- if (border > 0) {
16502
+ ctx.restore();
16503
+ }
16504
+ __name(drawBoxShadow, "drawBoxShadow");
16505
+ function drawChrome(ctx, box, density, chrome, assets = NO_ASSETS) {
16506
+ const radii = pxRadii(chrome.radius, density);
16507
+ for (const shadow of chrome.shadows) {
16508
+ drawBoxShadow(ctx, box, radii, shadow, density);
16509
+ }
16510
+ const opacity = Math.max(0, Math.min(1, chrome.fillOpacity));
16511
+ if (chrome.fill?.kind === "image") {
16512
+ ctx.save();
16513
+ tracePath(ctx, box.x, box.y, box.w, box.h, radii);
16514
+ ctx.clip();
16515
+ ctx.globalAlpha = opacity;
16516
+ drawImagePaint(ctx, chrome.fill, box, assets);
16517
+ ctx.restore();
16518
+ } else if (chrome.fill) {
16519
+ tracePath(ctx, box.x, box.y, box.w, box.h, radii);
16520
+ ctx.fillStyle = fillStyleFor(ctx, chrome.fill, box, opacity);
16521
+ ctx.fill();
16522
+ }
16523
+ if (chrome.edge) {
16524
+ const width = chrome.edge.width * density;
16525
+ ctx.save();
16526
+ tracePath(ctx, box.x, box.y, box.w, box.h, radii);
16527
+ ctx.clip();
16528
+ const fade = ctx.createLinearGradient(0, box.y, 0, box.y + box.h / 2);
16529
+ fade.addColorStop(0, chrome.edge.color);
16530
+ fade.addColorStop(1, withOpacity(ctx, chrome.edge.color, 0));
16531
+ ctx.strokeStyle = fade;
16532
+ ctx.lineWidth = width * 2;
16533
+ tracePath(ctx, box.x, box.y, box.w, box.h, radii);
16534
+ ctx.stroke();
16535
+ ctx.restore();
16536
+ }
16537
+ if (chrome.border) {
16538
+ const border = chrome.border.width * density;
15980
16539
  tracePath(
15981
16540
  ctx,
15982
- border / 2,
15983
- border / 2,
15984
- pxWidth - border,
15985
- pxHeight - border,
15986
- Math.max(0, radius - border / 2)
16541
+ box.x + border / 2,
16542
+ box.y + border / 2,
16543
+ box.w - border,
16544
+ box.h - border,
16545
+ radii.map((r) => Math.max(0, r - border / 2))
15987
16546
  );
15988
- ctx.strokeStyle = style.borderColor;
16547
+ ctx.strokeStyle = chrome.border.color;
15989
16548
  ctx.lineWidth = border;
15990
16549
  ctx.stroke();
15991
16550
  }
15992
16551
  }
15993
16552
  __name(drawChrome, "drawChrome");
15994
- function drawTextBlock(ctx, pxWidth, pxHeight, density, text, style) {
15995
- const fontPx = Math.max(4, style.fontSizeMeters * density);
15996
- const paddingPx = style.paddingMeters * density;
15997
- const contentW = Math.max(1, pxWidth - paddingPx * 2);
15998
- const contentH = Math.max(1, pxHeight - paddingPx * 2);
16553
+ var applyTransform2 = /* @__PURE__ */ __name((text, transform) => transform === "upper" ? text.toUpperCase() : transform === "lower" ? text.toLowerCase() : text, "applyTransform");
16554
+ function fillTracked(ctx, line, x, y, trackingPx, width) {
16555
+ let cursor = ctx.textAlign === "center" ? x - width / 2 : ctx.textAlign === "right" ? x - width : x;
16556
+ const align = ctx.textAlign;
16557
+ ctx.textAlign = "left";
16558
+ for (const glyph of line) {
16559
+ ctx.fillText(glyph, cursor, y);
16560
+ cursor += ctx.measureText(glyph).width + trackingPx;
16561
+ }
16562
+ ctx.textAlign = align;
16563
+ }
16564
+ __name(fillTracked, "fillTracked");
16565
+ function drawTextBlock(ctx, box, density, text, style, family = null, assets = NO_ASSETS) {
16566
+ const fontPx = Math.max(4, style.fontSize * density);
16567
+ const paddingPx = style.padding * density;
16568
+ const contentW = Math.max(1, box.w - paddingPx * 2);
16569
+ const contentH = Math.max(1, box.h - paddingPx * 2);
15999
16570
  const lineHeightPx = fontPx * style.lineHeight;
16000
- ctx.font = `${style.fontWeight} ${fontPx}px ${UI_FONT_STACK}`;
16001
- const measure = /* @__PURE__ */ __name((s) => ctx.measureText(s).width, "measure");
16002
- const maxLines = Math.max(1, Math.floor(contentH / lineHeightPx));
16571
+ const trackingPx = style.tracking * density;
16572
+ const stack = family ? `"${family}", ${UI_FONT_STACK}` : UI_FONT_STACK;
16573
+ ctx.font = `${style.fontWeight} ${fontPx}px ${stack}`;
16574
+ const nativeTracking = trackingPx !== 0 && "letterSpacing" in ctx;
16575
+ if (nativeTracking) {
16576
+ ctx.letterSpacing = `${trackingPx}px`;
16577
+ }
16578
+ const manualTracking = trackingPx !== 0 && !nativeTracking;
16579
+ const measure = /* @__PURE__ */ __name((s) => ctx.measureText(s).width + (manualTracking ? trackingPx * s.length : 0), "measure");
16580
+ const fits = Math.max(1, Math.floor(contentH / lineHeightPx));
16581
+ const maxLines = style.maxLines ? Math.min(fits, style.maxLines) : fits;
16003
16582
  const lines = clampLines(
16004
16583
  measure,
16005
- wrapText(measure, text, contentW),
16584
+ wrapText(measure, applyTransform2(text, style.transform), contentW),
16006
16585
  maxLines,
16007
16586
  contentW
16008
16587
  );
16009
16588
  const blockH = lines.length * lineHeightPx;
16010
- const top = style.valign === "top" ? paddingPx : style.valign === "bottom" ? pxHeight - paddingPx - blockH : (pxHeight - blockH) / 2;
16011
- const anchorX = style.align === "left" ? paddingPx : style.align === "right" ? pxWidth - paddingPx : pxWidth / 2;
16589
+ const top = style.valign === "top" ? box.y + paddingPx : style.valign === "bottom" ? box.y + box.h - paddingPx - blockH : box.y + (box.h - blockH) / 2;
16590
+ const anchorX = style.align === "left" ? box.x + paddingPx : style.align === "right" ? box.x + box.w - paddingPx : box.x + box.w / 2;
16012
16591
  ctx.save();
16013
- tracePath(ctx, 0, 0, pxWidth, pxHeight, 0);
16014
- ctx.clip();
16015
- ctx.fillStyle = style.color;
16592
+ if (style.paint.kind === "image") {
16593
+ ctx.fillStyle = "#ffffff";
16594
+ } else {
16595
+ ctx.fillStyle = fillStyleFor(ctx, style.paint, box, 1);
16596
+ }
16597
+ if (style.shadow) {
16598
+ ctx.shadowColor = style.shadow.color;
16599
+ ctx.shadowBlur = style.shadow.blur * density;
16600
+ ctx.shadowOffsetX = style.shadow.x * density;
16601
+ ctx.shadowOffsetY = -style.shadow.y * density;
16602
+ }
16016
16603
  ctx.textAlign = style.align;
16017
16604
  ctx.textBaseline = "middle";
16018
16605
  for (let i = 0; i < lines.length; i += 1) {
16019
- ctx.fillText(lines[i], anchorX, top + (i + 0.5) * lineHeightPx);
16606
+ const y = top + (i + 0.5) * lineHeightPx;
16607
+ if (manualTracking) {
16608
+ fillTracked(ctx, lines[i], anchorX, y, trackingPx, measure(lines[i]));
16609
+ } else {
16610
+ ctx.fillText(lines[i], anchorX, y);
16611
+ }
16020
16612
  }
16021
16613
  ctx.restore();
16614
+ if (style.paint.kind === "image") {
16615
+ ctx.save();
16616
+ ctx.globalCompositeOperation = "source-atop";
16617
+ drawImagePaint(ctx, style.paint, box, assets);
16618
+ ctx.restore();
16619
+ }
16620
+ if (nativeTracking) {
16621
+ ctx.letterSpacing = "0px";
16622
+ }
16022
16623
  }
16023
16624
  __name(drawTextBlock, "drawTextBlock");
16024
- function panelSignature(widthM, heightM, size, style) {
16625
+ function panelSignature(widthM, heightM, size, chrome, loaded = null) {
16025
16626
  return JSON.stringify([
16026
16627
  widthM,
16027
16628
  heightM,
16028
16629
  size.pxWidth,
16029
16630
  size.pxHeight,
16030
- style.backgroundColor,
16031
- style.backgroundOpacity,
16032
- style.cornerRadiusMeters,
16033
- style.borderWidthMeters,
16034
- style.borderColor
16631
+ chrome,
16632
+ loaded
16035
16633
  ]);
16036
16634
  }
16037
16635
  __name(panelSignature, "panelSignature");
16038
- function textSignature(widthM, heightM, size, text, style) {
16636
+ function textSignature(widthM, heightM, size, text, style, family = null) {
16039
16637
  return JSON.stringify([
16040
16638
  widthM,
16041
16639
  heightM,
16042
16640
  size.pxWidth,
16043
16641
  size.pxHeight,
16044
16642
  text,
16045
- style.fontSizeMeters,
16046
- style.fontWeight,
16047
- style.color,
16048
- style.align,
16049
- style.valign,
16050
- style.lineHeight,
16051
- style.paddingMeters
16643
+ style,
16644
+ family
16052
16645
  ]);
16053
16646
  }
16054
16647
  __name(textSignature, "textSignature");
16055
- function buttonSignature(widthM, heightM, size, panel, label, text) {
16648
+ function buttonSignature(widthM, heightM, size, chrome, label, text, family = null, loaded = null) {
16056
16649
  return JSON.stringify([
16057
- panelSignature(widthM, heightM, size, panel),
16650
+ panelSignature(widthM, heightM, size, chrome, loaded),
16058
16651
  label,
16059
- text.fontSizeMeters,
16060
- text.fontWeight,
16061
- text.color
16652
+ text,
16653
+ family
16062
16654
  ]);
16063
16655
  }
16064
16656
  __name(buttonSignature, "buttonSignature");
16065
- function trackSignature(widthM, heightM, size, panel, accentColor, knobColor) {
16657
+ function trackSignature(widthM, heightM, size, chrome, accentColor, knobColor, variant = null) {
16066
16658
  return JSON.stringify([
16067
- panelSignature(widthM, heightM, size, panel),
16659
+ panelSignature(widthM, heightM, size, chrome),
16068
16660
  accentColor,
16069
- knobColor
16661
+ knobColor,
16662
+ variant
16070
16663
  ]);
16071
16664
  }
16072
16665
  __name(trackSignature, "trackSignature");
@@ -16076,17 +16669,226 @@ function drawRoundedBar(ctx, pxX, pxY, pxW, pxH, color) {
16076
16669
  ctx.fill();
16077
16670
  }
16078
16671
  __name(drawRoundedBar, "drawRoundedBar");
16672
+ function drawCheckMark(ctx, box, color) {
16673
+ ctx.beginPath();
16674
+ ctx.moveTo(box.x + box.w * 0.24, box.y + box.h * 0.54);
16675
+ ctx.lineTo(box.x + box.w * 0.43, box.y + box.h * 0.72);
16676
+ ctx.lineTo(box.x + box.w * 0.77, box.y + box.h * 0.3);
16677
+ ctx.strokeStyle = color;
16678
+ ctx.lineWidth = box.h * 0.13;
16679
+ ctx.lineCap = "round";
16680
+ ctx.lineJoin = "round";
16681
+ ctx.stroke();
16682
+ }
16683
+ __name(drawCheckMark, "drawCheckMark");
16684
+ function drawDot(ctx, cx, cy, radius, color) {
16685
+ ctx.beginPath();
16686
+ ctx.arc(cx, cy, radius, 0, Math.PI * 2);
16687
+ ctx.fillStyle = color;
16688
+ ctx.fill();
16689
+ }
16690
+ __name(drawDot, "drawDot");
16691
+ function drawRing(ctx, box, t, trackColor, accentColor) {
16692
+ const thickness = Math.min(box.w, box.h) * 0.14;
16693
+ const radius = Math.min(box.w, box.h) / 2 - thickness / 2;
16694
+ const cx = box.x + box.w / 2;
16695
+ const cy = box.y + box.h / 2;
16696
+ ctx.lineWidth = thickness;
16697
+ ctx.lineCap = "round";
16698
+ ctx.beginPath();
16699
+ ctx.arc(cx, cy, radius, 0, Math.PI * 2);
16700
+ ctx.strokeStyle = trackColor;
16701
+ ctx.stroke();
16702
+ if (t <= 0) return;
16703
+ const start = -Math.PI / 2;
16704
+ ctx.beginPath();
16705
+ ctx.arc(cx, cy, radius, start, start + t * Math.PI * 2);
16706
+ ctx.strokeStyle = accentColor;
16707
+ ctx.stroke();
16708
+ }
16709
+ __name(drawRing, "drawRing");
16710
+ function drawIconLabel(ctx, box, density, label, style, family, icon, position, assets) {
16711
+ const fontPx = style.fontSize * density;
16712
+ const iconPx = fontPx * 1.3;
16713
+ const gapPx = label ? fontPx * 0.5 : 0;
16714
+ const stack = family ? `"${family}", ${UI_FONT_STACK}` : UI_FONT_STACK;
16715
+ ctx.font = `${style.fontWeight} ${fontPx}px ${stack}`;
16716
+ const labelPx = label ? ctx.measureText(label).width : 0;
16717
+ const groupPx = iconPx + gapPx + labelPx;
16718
+ const startX = box.x + (box.w - groupPx) / 2;
16719
+ const iconX = position === "left" ? startX : startX + labelPx + gapPx;
16720
+ const labelX = position === "left" ? startX + iconPx + gapPx : startX;
16721
+ drawImagePaint(
16722
+ ctx,
16723
+ { src: icon, fit: "contain", tint: style.color },
16724
+ { x: iconX, y: box.y + (box.h - iconPx) / 2, w: iconPx, h: iconPx },
16725
+ assets
16726
+ );
16727
+ if (label) {
16728
+ drawTextBlock(
16729
+ ctx,
16730
+ { x: labelX, y: box.y, w: labelPx + 2, h: box.h },
16731
+ density,
16732
+ label,
16733
+ { ...style, align: "left", padding: 0 },
16734
+ family,
16735
+ assets
16736
+ );
16737
+ }
16738
+ }
16739
+ __name(drawIconLabel, "drawIconLabel");
16740
+
16741
+ // core/ui/uiPaintOrder.ts
16742
+ var BASE_RENDER_ORDER = 10;
16743
+ var MAX_RENDER_ORDER = 890;
16744
+ var MAX_TREE_RANK = MAX_RENDER_ORDER - BASE_RENDER_ORDER - 1;
16745
+ var SLOT_STEP = 1e-4;
16746
+ var SLOTS_PER_WIDGET = 3;
16747
+ var MAX_WIDGET_INDEX = Math.floor(1 / SLOT_STEP / SLOTS_PER_WIDGET) - 1;
16748
+ var PLANE_EPSILON_METERS = 1e-3;
16749
+ var PARALLEL_DOT = 1 - 1e-4;
16750
+ var dot = /* @__PURE__ */ __name((a, b) => a.x * b.x + a.y * b.y + a.z * b.z, "dot");
16751
+ var length = /* @__PURE__ */ __name((a) => Math.sqrt(dot(a, a)), "length");
16752
+ var subtract = /* @__PURE__ */ __name((a, b) => ({
16753
+ x: a.x - b.x,
16754
+ y: a.y - b.y,
16755
+ z: a.z - b.z
16756
+ }), "subtract");
16757
+ function unit(a) {
16758
+ const len = length(a);
16759
+ if (len < 1e-9) return { x: 0, y: 0, z: 0 };
16760
+ return { x: a.x / len, y: a.y / len, z: a.z / len };
16761
+ }
16762
+ __name(unit, "unit");
16763
+ function normalOf(rect) {
16764
+ const { halfX: a, halfY: b } = rect;
16765
+ return unit({
16766
+ x: a.y * b.z - a.z * b.y,
16767
+ y: a.z * b.x - a.x * b.z,
16768
+ z: a.x * b.y - a.y * b.x
16769
+ });
16770
+ }
16771
+ __name(normalOf, "normalOf");
16772
+ var planeDistance = /* @__PURE__ */ __name((rect, normal, eye) => Math.abs(dot(subtract(eye, rect.center), normal)), "planeDistance");
16773
+ function closestPointDistance(rect, eye) {
16774
+ const toEye = subtract(eye, rect.center);
16775
+ const lenX = length(rect.halfX);
16776
+ const lenY = length(rect.halfY);
16777
+ const axisX = unit(rect.halfX);
16778
+ const axisY = unit(rect.halfY);
16779
+ const s = Math.max(-lenX, Math.min(lenX, dot(toEye, axisX)));
16780
+ const t = Math.max(-lenY, Math.min(lenY, dot(toEye, axisY)));
16781
+ return length({
16782
+ x: toEye.x - axisX.x * s - axisY.x * t,
16783
+ y: toEye.y - axisX.y * s - axisY.y * t,
16784
+ z: toEye.z - axisX.z * s - axisY.z * t
16785
+ });
16786
+ }
16787
+ __name(closestPointDistance, "closestPointDistance");
16788
+ var areaOf = /* @__PURE__ */ __name((rect) => length(rect.halfX) * length(rect.halfY), "areaOf");
16789
+ function comparePaintRects(a, b, eye) {
16790
+ const normalA = normalOf(a);
16791
+ const normalB = normalOf(b);
16792
+ const parallel = Math.abs(dot(normalA, normalB)) >= PARALLEL_DOT;
16793
+ const distanceA = parallel ? planeDistance(a, normalA, eye) : closestPointDistance(a, eye);
16794
+ const distanceB = parallel ? planeDistance(b, normalB, eye) : closestPointDistance(b, eye);
16795
+ if (Math.abs(distanceA - distanceB) > PLANE_EPSILON_METERS) {
16796
+ return distanceB - distanceA;
16797
+ }
16798
+ const areaA = areaOf(a);
16799
+ const areaB = areaOf(b);
16800
+ if (areaA !== areaB) return areaB - areaA;
16801
+ return a.id - b.id;
16802
+ }
16803
+ __name(comparePaintRects, "comparePaintRects");
16804
+ function buildPaintTrees(nodes) {
16805
+ const known = new Set(nodes.map((node) => node.id));
16806
+ const children = /* @__PURE__ */ new Map();
16807
+ const roots = [];
16808
+ for (const node of nodes) {
16809
+ if (node.parent !== null && known.has(node.parent)) {
16810
+ const siblings = children.get(node.parent);
16811
+ if (siblings) siblings.push(node.id);
16812
+ else children.set(node.parent, [node.id]);
16813
+ } else {
16814
+ roots.push(node.id);
16815
+ }
16816
+ }
16817
+ const trees = [];
16818
+ const visited = /* @__PURE__ */ new Set();
16819
+ const walk = /* @__PURE__ */ __name((id, order) => {
16820
+ if (visited.has(id)) return;
16821
+ visited.add(id);
16822
+ order.push(id);
16823
+ const kids = children.get(id);
16824
+ if (!kids) return;
16825
+ for (const kid of [...kids].sort((a, b) => a - b)) walk(kid, order);
16826
+ }, "walk");
16827
+ for (const rootId of [...roots].sort((a, b) => a - b)) {
16828
+ const order = [];
16829
+ walk(rootId, order);
16830
+ trees.push({ rootId, order });
16831
+ }
16832
+ return trees;
16833
+ }
16834
+ __name(buildPaintTrees, "buildPaintTrees");
16835
+ function paintSlots(treeRank, widgetIndex) {
16836
+ const base = BASE_RENDER_ORDER + Math.min(treeRank, MAX_TREE_RANK);
16837
+ const index = Math.min(widgetIndex, MAX_WIDGET_INDEX);
16838
+ const slot = /* @__PURE__ */ __name((offset) => base + (index * SLOTS_PER_WIDGET + offset) * SLOT_STEP, "slot");
16839
+ return { face: slot(0), parts: slot(1), descendants: slot(2) };
16840
+ }
16841
+ __name(paintSlots, "paintSlots");
16842
+ function assignPaintOrder(trees, rects, eye) {
16843
+ const sorted = trees.filter((tree) => rects.has(tree.rootId)).sort(
16844
+ (a, b) => comparePaintRects(
16845
+ rects.get(a.rootId),
16846
+ rects.get(b.rootId),
16847
+ eye
16848
+ )
16849
+ );
16850
+ const slots = /* @__PURE__ */ new Map();
16851
+ sorted.forEach((tree, rank) => {
16852
+ tree.order.forEach((id, index) => {
16853
+ slots.set(id, paintSlots(rank, index));
16854
+ });
16855
+ });
16856
+ return slots;
16857
+ }
16858
+ __name(assignPaintOrder, "assignPaintOrder");
16079
16859
 
16080
16860
  // core/systems/UIPanelSystem.ts
16081
- var FACE_RENDER_ORDER = 10;
16082
16861
  var CHILD_Z = 2e-3;
16862
+ var DESCENDANT_WALK_INTERVAL = 30;
16863
+ var METER_STEPS = 200;
16864
+ var CLEAR_CHROME = {
16865
+ backgroundColor: "#000000",
16866
+ backgroundOpacity: 0,
16867
+ cornerRadius: 0,
16868
+ borderWidth: 0,
16869
+ borderColor: "#000000"
16870
+ };
16871
+ var PAINT_STAMP_KEY = "__uiPaintStamp";
16083
16872
  var _UIPanelSystem = class _UIPanelSystem {
16084
16873
  constructor(options) {
16085
16874
  this.entries = /* @__PURE__ */ new Map();
16875
+ this.framesUntilDescendantWalk = 0;
16876
+ /** Whether any panel defines tokens, so plain scenes skip the parent walk. */
16877
+ this.anyTokens = false;
16878
+ this.drawAssets = {
16879
+ image: /* @__PURE__ */ __name((src) => uiImage(src), "image"),
16880
+ scratch: /* @__PURE__ */ __name((width, height) => {
16881
+ const canvas = this.createCanvas();
16882
+ canvas.width = width;
16883
+ canvas.height = height;
16884
+ return canvas.getContext("2d");
16885
+ }, "scratch")
16886
+ };
16086
16887
  this.createCanvas = options?.createCanvas ?? (() => document.createElement("canvas"));
16087
16888
  }
16088
16889
  update(world, deltaTime = 0) {
16089
16890
  const live = /* @__PURE__ */ new Set();
16891
+ this.anyTokens = world.query(UIPanelComponent).some((id) => world.getComponent(id, UIPanelComponent)?.tokens);
16090
16892
  for (const entityId of world.query(UIPanelComponent, Object3DRef)) {
16091
16893
  const panel = world.getComponent(entityId, UIPanelComponent);
16092
16894
  if (!panel) continue;
@@ -16124,6 +16926,7 @@ var _UIPanelSystem = class _UIPanelSystem {
16124
16926
  this.entries.delete(entityId);
16125
16927
  }
16126
16928
  }
16929
+ this.applyPaintOrder(world);
16127
16930
  }
16128
16931
  /** All GPU/canvas resources; call on world teardown. */
16129
16932
  dispose() {
@@ -16132,84 +16935,188 @@ var _UIPanelSystem = class _UIPanelSystem {
16132
16935
  }
16133
16936
  // ── Widgets ────────────────────────────────────────────────────────────────
16134
16937
  syncPanel(world, entityId, panel) {
16135
- const size = resolveCanvasSize(panel.width, panel.height, panel.pixelDensity);
16136
- const style = {
16137
- backgroundColor: panel.backgroundColor,
16138
- backgroundOpacity: panel.backgroundOpacity,
16139
- cornerRadiusMeters: panel.cornerRadius,
16140
- borderWidthMeters: panel.borderWidth,
16141
- borderColor: panel.borderColor
16142
- };
16143
- const signature = panelSignature(panel.width, panel.height, size, style);
16144
- this.rebuildIfChanged(world, entityId, signature, panel.width, panel.height, size, (ctx) => {
16145
- drawChrome(ctx, size.pxWidth, size.pxHeight, size.density, style);
16146
- });
16938
+ const chrome = resolveChrome(
16939
+ panel,
16940
+ panel.style,
16941
+ this.tokensFor(world, entityId)
16942
+ );
16943
+ const bleed = chromeBleed(chrome);
16944
+ const size = resolveCanvasSize(
16945
+ panel.width + bleed * 2,
16946
+ panel.height + bleed * 2,
16947
+ panel.pixelDensity
16948
+ );
16949
+ const signature = panelSignature(
16950
+ panel.width,
16951
+ panel.height,
16952
+ size,
16953
+ chrome,
16954
+ imagesLoaded(chrome)
16955
+ );
16956
+ this.rebuildIfChanged(
16957
+ world,
16958
+ entityId,
16959
+ signature,
16960
+ panel,
16961
+ bleed,
16962
+ size,
16963
+ (ctx) => {
16964
+ drawChrome(
16965
+ ctx,
16966
+ layoutBox(panel, bleed, size.density),
16967
+ size.density,
16968
+ chrome,
16969
+ this.drawAssets
16970
+ );
16971
+ },
16972
+ THREE67__namespace.DoubleSide
16973
+ );
16147
16974
  }
16148
16975
  syncText(world, entityId, text) {
16149
- const size = resolveCanvasSize(text.width, text.height);
16150
- const style = {
16151
- fontSizeMeters: text.fontSize,
16152
- fontWeight: text.fontWeight,
16153
- color: text.color,
16154
- align: text.align,
16155
- valign: text.valign,
16156
- lineHeight: text.lineHeight,
16157
- paddingMeters: text.padding
16158
- };
16159
- const signature = textSignature(text.width, text.height, size, text.text, style);
16160
- this.rebuildIfChanged(world, entityId, signature, text.width, text.height, size, (ctx) => {
16161
- ctx.clearRect(0, 0, size.pxWidth, size.pxHeight);
16162
- drawTextBlock(ctx, size.pxWidth, size.pxHeight, size.density, text.text, style);
16163
- });
16976
+ const tokens = this.tokensFor(world, entityId);
16977
+ const chrome = text.style ? resolveChrome(CLEAR_CHROME, text.style, tokens) : null;
16978
+ const bleed = chrome ? chromeBleed(chrome) : 0;
16979
+ const size = resolveCanvasSize(
16980
+ text.width + bleed * 2,
16981
+ text.height + bleed * 2,
16982
+ text.pixelDensity
16983
+ );
16984
+ const style = resolveText(text, text.style, tokens);
16985
+ const family = familyOf(style.font);
16986
+ const signature = JSON.stringify([
16987
+ textSignature(text.width, text.height, size, text.text, style, family),
16988
+ chrome,
16989
+ chrome ? imagesLoaded(chrome) : null
16990
+ ]);
16991
+ this.rebuildIfChanged(
16992
+ world,
16993
+ entityId,
16994
+ signature,
16995
+ text,
16996
+ bleed,
16997
+ size,
16998
+ (ctx) => {
16999
+ const box = layoutBox(text, bleed, size.density);
17000
+ if (chrome) {
17001
+ drawChrome(ctx, box, size.density, chrome, this.drawAssets);
17002
+ }
17003
+ drawTextBlock(
17004
+ ctx,
17005
+ box,
17006
+ size.density,
17007
+ text.text,
17008
+ style,
17009
+ family,
17010
+ this.drawAssets
17011
+ );
17012
+ }
17013
+ );
16164
17014
  }
16165
17015
  syncButton(world, entityId, button) {
16166
- const size = resolveCanvasSize(button.width, button.height);
16167
- const chrome = {
16168
- backgroundColor: button.backgroundColor,
16169
- backgroundOpacity: button.backgroundOpacity,
16170
- cornerRadiusMeters: button.cornerRadius,
16171
- borderWidthMeters: button.borderWidth,
16172
- borderColor: button.borderColor
16173
- };
16174
- const label = {
16175
- fontSizeMeters: button.fontSize,
16176
- fontWeight: button.fontWeight,
16177
- color: button.textColor,
16178
- align: "center",
16179
- valign: "middle",
16180
- lineHeight: 1.2,
16181
- paddingMeters: 0.01
16182
- };
17016
+ const tokens = this.tokensFor(world, entityId);
17017
+ const chrome = resolveChrome(button, button.style, tokens);
17018
+ const bleed = chromeBleed(chrome);
17019
+ const size = resolveCanvasSize(
17020
+ button.width + bleed * 2,
17021
+ button.height + bleed * 2,
17022
+ button.pixelDensity
17023
+ );
17024
+ const label = resolveText(
17025
+ {
17026
+ fontSize: button.fontSize,
17027
+ fontWeight: button.fontWeight,
17028
+ color: button.textColor,
17029
+ align: "center",
17030
+ valign: "middle",
17031
+ lineHeight: 1.2,
17032
+ padding: 0.01
17033
+ },
17034
+ button.style,
17035
+ tokens
17036
+ );
17037
+ const family = familyOf(label.font);
17038
+ const icon = button.icon ? resolveToken(button.icon, tokens) : null;
16183
17039
  const signature = buttonSignature(
16184
17040
  button.width,
16185
17041
  button.height,
16186
17042
  size,
16187
17043
  chrome,
16188
17044
  button.label,
16189
- label
17045
+ label,
17046
+ family,
17047
+ [imagesLoaded(chrome), icon, icon ? !!uiImage(icon) : false]
17048
+ );
17049
+ this.rebuildIfChanged(
17050
+ world,
17051
+ entityId,
17052
+ signature,
17053
+ button,
17054
+ bleed,
17055
+ size,
17056
+ (ctx) => {
17057
+ const box = layoutBox(button, bleed, size.density);
17058
+ drawChrome(ctx, box, size.density, chrome, this.drawAssets);
17059
+ if (icon) {
17060
+ drawIconLabel(
17061
+ ctx,
17062
+ box,
17063
+ size.density,
17064
+ button.label,
17065
+ label,
17066
+ family,
17067
+ icon,
17068
+ button.iconPosition ?? "left",
17069
+ this.drawAssets
17070
+ );
17071
+ } else {
17072
+ drawTextBlock(
17073
+ ctx,
17074
+ box,
17075
+ size.density,
17076
+ button.label,
17077
+ label,
17078
+ family,
17079
+ this.drawAssets
17080
+ );
17081
+ }
17082
+ }
16190
17083
  );
16191
- this.rebuildIfChanged(world, entityId, signature, button.width, button.height, size, (ctx) => {
16192
- drawChrome(ctx, size.pxWidth, size.pxHeight, size.density, chrome);
16193
- drawTextBlock(ctx, size.pxWidth, size.pxHeight, size.density, button.label, label);
16194
- });
16195
17084
  }
16196
17085
  syncToggle(world, entityId, toggle, deltaTime) {
16197
- const size = resolveCanvasSize(toggle.width, toggle.height);
16198
- const chrome = {
16199
- backgroundColor: toggle.value ? toggle.accentColor : toggle.trackColor,
16200
- backgroundOpacity: 1,
16201
- cornerRadiusMeters: toggle.height / 2,
16202
- borderWidthMeters: 0,
16203
- borderColor: "#000000"
16204
- };
17086
+ const tokens = this.tokensFor(world, entityId);
17087
+ const accent = resolveToken(toggle.accentColor, tokens);
17088
+ const track = resolveToken(toggle.trackColor, tokens);
17089
+ const knobColor = resolveToken(toggle.knobColor, tokens);
17090
+ const size = resolveCanvasSize(
17091
+ toggle.width,
17092
+ toggle.height,
17093
+ toggle.pixelDensity
17094
+ );
17095
+ const variant = toggle.variant ?? "switch";
17096
+ if (variant !== "switch") {
17097
+ this.syncMarkToggle(world, entityId, toggle, accent, track, knobColor);
17098
+ return;
17099
+ }
17100
+ const chrome = resolveChrome(
17101
+ {
17102
+ backgroundColor: toggle.value ? accent : track,
17103
+ backgroundOpacity: 1,
17104
+ cornerRadius: toggle.height / 2,
17105
+ borderWidth: 0,
17106
+ borderColor: "#000000"
17107
+ },
17108
+ toggle.style,
17109
+ tokens
17110
+ );
16205
17111
  const signature = JSON.stringify([
16206
17112
  trackSignature(
16207
17113
  toggle.width,
16208
17114
  toggle.height,
16209
17115
  size,
16210
17116
  chrome,
16211
- toggle.accentColor,
16212
- toggle.knobColor
17117
+ accent,
17118
+ knobColor,
17119
+ variant
16213
17120
  ),
16214
17121
  toggle.value
16215
17122
  ]);
@@ -16217,18 +17124,24 @@ var _UIPanelSystem = class _UIPanelSystem {
16217
17124
  world,
16218
17125
  entityId,
16219
17126
  signature,
16220
- toggle.width,
16221
- toggle.height,
17127
+ toggle,
17128
+ 0,
16222
17129
  size,
16223
17130
  (ctx) => {
16224
- drawChrome(ctx, size.pxWidth, size.pxHeight, size.density, chrome);
17131
+ drawChrome(
17132
+ ctx,
17133
+ layoutBox(toggle, 0, size.density),
17134
+ size.density,
17135
+ chrome,
17136
+ this.drawAssets
17137
+ );
16225
17138
  }
16226
17139
  );
16227
17140
  if (!entry) return;
16228
17141
  if (!entry.knob) {
16229
17142
  entry.knob = this.makeDisc(
16230
17143
  toggle.height * 0.4,
16231
- toggle.knobColor,
17144
+ knobColor,
16232
17145
  entry.face.parent,
16233
17146
  CHILD_Z
16234
17147
  );
@@ -16240,35 +17153,95 @@ var _UIPanelSystem = class _UIPanelSystem {
16240
17153
  knob.position.x += (target - knob.position.x) * (deltaTime > 0 ? ease : 1);
16241
17154
  knob.position.z = CHILD_Z;
16242
17155
  }
17156
+ /** Checkbox and radio: a box or ring at the left of the widget's area. */
17157
+ syncMarkToggle(world, entityId, toggle, accent, track, markColor) {
17158
+ const tokens = this.tokensFor(world, entityId);
17159
+ const radio = toggle.variant === "radio";
17160
+ const size = resolveCanvasSize(
17161
+ toggle.width,
17162
+ toggle.height,
17163
+ toggle.pixelDensity
17164
+ );
17165
+ const side = toggle.height;
17166
+ const chrome = resolveChrome(
17167
+ {
17168
+ backgroundColor: toggle.value ? accent : track,
17169
+ backgroundOpacity: 1,
17170
+ cornerRadius: radio ? side / 2 : side * 0.2,
17171
+ borderWidth: 0,
17172
+ borderColor: "#000000"
17173
+ },
17174
+ toggle.style,
17175
+ tokens
17176
+ );
17177
+ const signature = JSON.stringify([
17178
+ trackSignature(
17179
+ toggle.width,
17180
+ toggle.height,
17181
+ size,
17182
+ chrome,
17183
+ accent,
17184
+ markColor,
17185
+ toggle.variant ?? null
17186
+ ),
17187
+ toggle.value
17188
+ ]);
17189
+ this.rebuildIfChanged(world, entityId, signature, toggle, 0, size, (ctx) => {
17190
+ const px = side * size.density;
17191
+ const box = { x: 0, y: 0, w: px, h: px };
17192
+ drawChrome(ctx, box, size.density, chrome, this.drawAssets);
17193
+ if (!toggle.value) return;
17194
+ if (radio) drawDot(ctx, px / 2, px / 2, px * 0.24, markColor);
17195
+ else drawCheckMark(ctx, box, markColor);
17196
+ });
17197
+ }
16243
17198
  syncSlider(world, entityId, slider) {
16244
- const size = resolveCanvasSize(slider.width, slider.height);
17199
+ const tokens = this.tokensFor(world, entityId);
17200
+ const accent = resolveToken(slider.accentColor, tokens);
17201
+ const track = resolveToken(slider.trackColor, tokens);
17202
+ const knobColor = resolveToken(slider.knobColor, tokens);
17203
+ const size = resolveCanvasSize(
17204
+ slider.width,
17205
+ slider.height,
17206
+ slider.pixelDensity
17207
+ );
17208
+ const range = Math.max(1e-6, slider.max - slider.min);
17209
+ const t = Math.min(1, Math.max(0, (slider.value - slider.min) / range));
17210
+ if (slider.variant === "progress" || slider.variant === "ring") {
17211
+ this.syncMeter(world, entityId, slider, t, accent, track, knobColor);
17212
+ return;
17213
+ }
16245
17214
  const trackThickness = slider.height * 0.32;
16246
17215
  const knobRadius = slider.height * 0.45;
16247
17216
  const usable = Math.max(1e-3, slider.width - slider.height);
16248
- const chrome = {
16249
- backgroundColor: slider.trackColor,
16250
- backgroundOpacity: 1,
16251
- cornerRadiusMeters: trackThickness / 2,
16252
- borderWidthMeters: 0,
16253
- borderColor: "#000000"
16254
- };
17217
+ const chrome = resolveChrome(
17218
+ {
17219
+ backgroundColor: track,
17220
+ backgroundOpacity: 1,
17221
+ cornerRadius: trackThickness / 2,
17222
+ borderWidth: 0,
17223
+ borderColor: "#000000"
17224
+ },
17225
+ void 0,
17226
+ null
17227
+ );
16255
17228
  const signature = trackSignature(
16256
17229
  slider.width,
16257
17230
  slider.height,
16258
17231
  size,
16259
17232
  chrome,
16260
- slider.accentColor,
16261
- slider.knobColor
17233
+ accent,
17234
+ knobColor,
17235
+ slider.variant ?? null
16262
17236
  );
16263
17237
  const entry = this.rebuildIfChanged(
16264
17238
  world,
16265
17239
  entityId,
16266
17240
  signature,
16267
- slider.width,
16268
- slider.height,
17241
+ slider,
17242
+ 0,
16269
17243
  size,
16270
17244
  (ctx) => {
16271
- ctx.clearRect(0, 0, size.pxWidth, size.pxHeight);
16272
17245
  const trackWidthM = usable + trackThickness;
16273
17246
  drawRoundedBar(
16274
17247
  ctx,
@@ -16276,7 +17249,7 @@ var _UIPanelSystem = class _UIPanelSystem {
16276
17249
  (slider.height - trackThickness) / 2 * size.density,
16277
17250
  trackWidthM * size.density,
16278
17251
  trackThickness * size.density,
16279
- slider.trackColor
17252
+ track
16280
17253
  );
16281
17254
  }
16282
17255
  );
@@ -16285,13 +17258,13 @@ var _UIPanelSystem = class _UIPanelSystem {
16285
17258
  entry.fill = this.makeQuad(
16286
17259
  1,
16287
17260
  trackThickness,
16288
- slider.accentColor,
17261
+ accent,
16289
17262
  entry.face.parent,
16290
17263
  CHILD_Z / 2
16291
17264
  );
16292
17265
  entry.cap = this.makeDisc(
16293
17266
  trackThickness / 2,
16294
- slider.accentColor,
17267
+ accent,
16295
17268
  entry.face.parent,
16296
17269
  CHILD_Z / 2,
16297
17270
  Math.PI / 2,
@@ -16302,51 +17275,124 @@ var _UIPanelSystem = class _UIPanelSystem {
16302
17275
  if (!entry.knob) {
16303
17276
  entry.knob = this.makeDisc(
16304
17277
  knobRadius,
16305
- slider.knobColor,
17278
+ knobColor,
16306
17279
  entry.face.parent,
16307
17280
  CHILD_Z
16308
17281
  );
16309
17282
  }
16310
- const range = Math.max(1e-6, slider.max - slider.min);
16311
- const t = Math.min(1, Math.max(0, (slider.value - slider.min) / range));
16312
17283
  entry.knob.position.x = -usable / 2 + t * usable;
16313
17284
  entry.knob.position.z = CHILD_Z;
16314
17285
  entry.fill.scale.x = Math.max(1e-4, t * usable);
16315
17286
  entry.fill.position.x = -usable / 2 + t * usable / 2;
16316
17287
  entry.fill.position.z = CHILD_Z / 2;
16317
17288
  }
17289
+ /**
17290
+ * Progress bars and rings. Unlike a slider these change rarely and have no
17291
+ * drag to keep smooth, so the value is drawn, in steps of half a percent.
17292
+ */
17293
+ syncMeter(world, entityId, slider, t, accent, track, knobColor) {
17294
+ const size = resolveCanvasSize(
17295
+ slider.width,
17296
+ slider.height,
17297
+ slider.pixelDensity
17298
+ );
17299
+ const shown = Math.round(t * METER_STEPS) / METER_STEPS;
17300
+ const signature = JSON.stringify([
17301
+ slider.width,
17302
+ slider.height,
17303
+ size,
17304
+ slider.variant,
17305
+ accent,
17306
+ track,
17307
+ knobColor,
17308
+ shown
17309
+ ]);
17310
+ this.rebuildIfChanged(world, entityId, signature, slider, 0, size, (ctx) => {
17311
+ const box = {
17312
+ x: 0,
17313
+ y: 0,
17314
+ w: size.pxWidth,
17315
+ h: size.pxHeight
17316
+ };
17317
+ if (slider.variant === "ring") {
17318
+ drawRing(ctx, box, shown, track, accent);
17319
+ return;
17320
+ }
17321
+ drawRoundedBar(ctx, 0, 0, box.w, box.h, track);
17322
+ if (shown <= 0) return;
17323
+ const fill = Math.max(box.h, box.w * shown);
17324
+ drawRoundedBar(ctx, 0, 0, fill, box.h, accent);
17325
+ });
17326
+ }
17327
+ /** The tokens in scope for a widget, nearest panel first. */
17328
+ tokensFor(world, entityId) {
17329
+ if (!this.anyTokens) return null;
17330
+ let merged = null;
17331
+ const visited = /* @__PURE__ */ new Set();
17332
+ let current = entityId;
17333
+ while (current !== null && !visited.has(current)) {
17334
+ visited.add(current);
17335
+ const tokens = world.getComponent(current, UIPanelComponent)?.tokens;
17336
+ if (tokens) merged = { ...tokens, ...merged ?? {} };
17337
+ const parent = world.getComponent(current, ParentId)?.value;
17338
+ current = typeof parent === "number" ? parent : null;
17339
+ }
17340
+ return merged;
17341
+ }
16318
17342
  // ── Machinery ──────────────────────────────────────────────────────────────
16319
- rebuildIfChanged(world, entityId, signature, widthM, heightM, size, draw) {
17343
+ rebuildIfChanged(world, entityId, signature, widget, bleed, size, draw, side = THREE67__namespace.FrontSide) {
16320
17344
  const root = world.getComponent(entityId, Object3DRef)?.object;
16321
17345
  if (!root) return null;
17346
+ const widthM = widget.width;
17347
+ const heightM = widget.height;
17348
+ const faceWidth = widthM + bleed * 2;
17349
+ const faceHeight = heightM + bleed * 2;
16322
17350
  let entry = this.entries.get(entityId);
17351
+ if (entry) entry.opacity = widget.style?.opacity ?? 1;
16323
17352
  if (entry && entry.signature === signature) return entry;
16324
17353
  if (entry) this.disposeMovingParts(entry);
16325
17354
  if (!entry) {
16326
17355
  const canvas2 = this.createCanvas();
16327
- const texture = new THREE60__namespace.CanvasTexture(
17356
+ const texture = new THREE67__namespace.CanvasTexture(
16328
17357
  canvas2
16329
17358
  );
16330
17359
  this.configureTexture(world, texture);
16331
- const material = new THREE60__namespace.MeshBasicMaterial({
17360
+ const material = new THREE67__namespace.MeshBasicMaterial({
16332
17361
  map: texture,
16333
17362
  transparent: true,
16334
17363
  depthWrite: false,
16335
17364
  // UI colours are authored absolutes; scene tone mapping would shift
16336
17365
  // them (the classic "why is my white UI beige" bug).
16337
17366
  toneMapped: false,
16338
- side: THREE60__namespace.DoubleSide,
16339
- premultipliedAlpha: true
17367
+ side,
17368
+ premultipliedAlpha: false,
17369
+ blending: THREE67__namespace.CustomBlending,
17370
+ blendSrc: THREE67__namespace.OneFactor,
17371
+ blendDst: THREE67__namespace.OneMinusSrcAlphaFactor,
17372
+ blendSrcAlpha: THREE67__namespace.OneFactor,
17373
+ blendDstAlpha: THREE67__namespace.OneMinusSrcAlphaFactor
16340
17374
  });
16341
- const face = new THREE60__namespace.Mesh(
16342
- new THREE60__namespace.PlaneGeometry(widthM, heightM),
17375
+ const face = new THREE67__namespace.Mesh(
17376
+ new THREE67__namespace.PlaneGeometry(faceWidth, faceHeight),
16343
17377
  material
16344
17378
  );
16345
- face.renderOrder = FACE_RENDER_ORDER;
17379
+ tagPart(face, 16777215, true);
17380
+ face.raycast = () => {
17381
+ };
16346
17382
  root.add(face);
17383
+ const hit = new THREE67__namespace.Mesh(
17384
+ new THREE67__namespace.PlaneGeometry(widthM, heightM),
17385
+ new THREE67__namespace.MeshBasicMaterial({ visible: false, side })
17386
+ );
17387
+ root.add(hit);
16347
17388
  entry = {
16348
17389
  signature: "",
17390
+ root,
17391
+ width: widthM,
17392
+ height: heightM,
17393
+ opacity: widget.style?.opacity ?? 1,
16349
17394
  face,
17395
+ hit,
16350
17396
  texture,
16351
17397
  material,
16352
17398
  canvas: canvas2,
@@ -16360,23 +17406,26 @@ var _UIPanelSystem = class _UIPanelSystem {
16360
17406
  if (canvas.width !== size.pxWidth || canvas.height !== size.pxHeight) {
16361
17407
  canvas.width = size.pxWidth;
16362
17408
  canvas.height = size.pxHeight;
17409
+ if (entry.signature) entry.texture.dispose();
16363
17410
  }
16364
- const params = entry.face.geometry.parameters;
16365
- if (params.width !== widthM || params.height !== heightM) {
16366
- entry.face.geometry.dispose();
16367
- entry.face.geometry = new THREE60__namespace.PlaneGeometry(widthM, heightM);
16368
- }
17411
+ entry.width = widthM;
17412
+ entry.height = heightM;
17413
+ resizePlane(entry.face, faceWidth, faceHeight);
17414
+ resizePlane(entry.hit, widthM, heightM);
16369
17415
  const ctx = canvas.getContext("2d");
16370
- if (ctx) draw(ctx);
17416
+ if (ctx) {
17417
+ ctx.clearRect(0, 0, size.pxWidth, size.pxHeight);
17418
+ draw(ctx);
17419
+ }
16371
17420
  entry.texture.needsUpdate = true;
16372
17421
  entry.signature = signature;
16373
17422
  return entry;
16374
17423
  }
16375
17424
  configureTexture(world, texture) {
16376
- texture.colorSpace = THREE60__namespace.SRGBColorSpace;
17425
+ texture.colorSpace = THREE67__namespace.SRGBColorSpace;
16377
17426
  texture.generateMipmaps = true;
16378
- texture.minFilter = THREE60__namespace.LinearMipmapLinearFilter;
16379
- texture.magFilter = THREE60__namespace.LinearFilter;
17427
+ texture.minFilter = THREE67__namespace.LinearMipmapLinearFilter;
17428
+ texture.magFilter = THREE67__namespace.LinearFilter;
16380
17429
  texture.premultiplyAlpha = true;
16381
17430
  try {
16382
17431
  const renderer = world.getRenderer();
@@ -16387,7 +17436,7 @@ var _UIPanelSystem = class _UIPanelSystem {
16387
17436
  }
16388
17437
  makeQuad(widthM, heightM, color, parent, z) {
16389
17438
  return this.addPart(
16390
- new THREE60__namespace.PlaneGeometry(widthM, heightM),
17439
+ new THREE67__namespace.PlaneGeometry(widthM, heightM),
16391
17440
  color,
16392
17441
  parent,
16393
17442
  z
@@ -16396,24 +17445,23 @@ var _UIPanelSystem = class _UIPanelSystem {
16396
17445
  /** A filled disc (or arc segment, for the fill's rounded cap). */
16397
17446
  makeDisc(radiusM, color, parent, z, thetaStart = 0, thetaLength = Math.PI * 2) {
16398
17447
  return this.addPart(
16399
- new THREE60__namespace.CircleGeometry(radiusM, 32, thetaStart, thetaLength),
17448
+ new THREE67__namespace.CircleGeometry(radiusM, 32, thetaStart, thetaLength),
16400
17449
  color,
16401
17450
  parent,
16402
17451
  z
16403
17452
  );
16404
17453
  }
16405
17454
  addPart(geometry, color, parent, z) {
16406
- const mesh = new THREE60__namespace.Mesh(
17455
+ const mesh = new THREE67__namespace.Mesh(
16407
17456
  geometry,
16408
- new THREE60__namespace.MeshBasicMaterial({
17457
+ new THREE67__namespace.MeshBasicMaterial({
16409
17458
  color,
16410
17459
  transparent: true,
16411
17460
  depthWrite: false,
16412
- toneMapped: false,
16413
- side: THREE60__namespace.DoubleSide
17461
+ toneMapped: false
16414
17462
  })
16415
17463
  );
16416
- mesh.renderOrder = FACE_RENDER_ORDER + 1;
17464
+ tagPart(mesh, color);
16417
17465
  mesh.position.z = z;
16418
17466
  mesh.raycast = () => {
16419
17467
  };
@@ -16444,22 +17492,144 @@ var _UIPanelSystem = class _UIPanelSystem {
16444
17492
  const object = world.getComponent(entityId, Object3DRef)?.object;
16445
17493
  if (!transform || !object) return;
16446
17494
  const camera = world.getCamera();
16447
- const cam = new THREE60__namespace.Vector3();
16448
- const self = new THREE60__namespace.Vector3();
17495
+ const cam = new THREE67__namespace.Vector3();
17496
+ const self = new THREE67__namespace.Vector3();
16449
17497
  camera.getWorldPosition(cam);
16450
17498
  object.getWorldPosition(self);
16451
17499
  transform.rotation.y = Math.atan2(cam.x - self.x, cam.z - self.z);
16452
17500
  }
17501
+ /**
17502
+ * Stamp every widget's meshes with this frame's paint order. Written each
17503
+ * frame because the eye moves: the order between trees can change at any
17504
+ * time, and a plain property write costs less than checking it.
17505
+ */
17506
+ applyPaintOrder(world) {
17507
+ if (this.entries.size === 0) return;
17508
+ const camera = world.getCamera();
17509
+ camera.getWorldPosition(eyeScratch);
17510
+ const nodes = [...this.entries.keys()].map((id) => ({
17511
+ id,
17512
+ parent: nearestWidgetAncestor(world, id)
17513
+ }));
17514
+ const trees = buildPaintTrees(nodes);
17515
+ const rects = /* @__PURE__ */ new Map();
17516
+ for (const tree of trees) {
17517
+ const entry = this.entries.get(tree.rootId);
17518
+ if (entry) rects.set(tree.rootId, rootRect(tree.rootId, entry));
17519
+ }
17520
+ const slots = assignPaintOrder(trees, rects, eyeScratch);
17521
+ this.framesUntilDescendantWalk -= 1;
17522
+ const walkDescendants = this.framesUntilDescendantWalk <= 0;
17523
+ if (walkDescendants) {
17524
+ this.framesUntilDescendantWalk = DESCENDANT_WALK_INTERVAL;
17525
+ }
17526
+ const widgetRoots = /* @__PURE__ */ new Set();
17527
+ if (walkDescendants) {
17528
+ for (const entry of this.entries.values()) widgetRoots.add(entry.root);
17529
+ }
17530
+ for (const [entityId, entry] of this.entries) {
17531
+ const slot = slots.get(entityId);
17532
+ if (!slot) continue;
17533
+ entry.face.renderOrder = slot.face;
17534
+ for (const part of [entry.knob, entry.fill, entry.cap]) {
17535
+ if (part) part.renderOrder = slot.parts;
17536
+ }
17537
+ if (walkDescendants) stampDescendants(entry.root, slot, widgetRoots);
17538
+ }
17539
+ const effective = /* @__PURE__ */ new Map();
17540
+ const parentOf = new Map(nodes.map((node) => [node.id, node.parent]));
17541
+ for (const tree of trees) {
17542
+ for (const id of tree.order) {
17543
+ const entry = this.entries.get(id);
17544
+ if (!entry) continue;
17545
+ const parent = parentOf.get(id);
17546
+ const inherited = parent != null ? effective.get(parent) ?? 1 : 1;
17547
+ const opacity = entry.opacity * inherited;
17548
+ effective.set(id, opacity);
17549
+ for (const mesh of [entry.face, entry.knob, entry.fill, entry.cap]) {
17550
+ if (!mesh) continue;
17551
+ const tag = mesh.userData[UI_PART_KEY];
17552
+ if (tag.opacity === opacity) continue;
17553
+ tag.opacity = opacity;
17554
+ applyPartColor(mesh, tag);
17555
+ }
17556
+ }
17557
+ }
17558
+ }
16453
17559
  destroyEntry(entry) {
16454
17560
  this.disposeMovingParts(entry);
16455
17561
  entry.face.removeFromParent();
16456
17562
  entry.face.geometry.dispose();
16457
17563
  entry.face.material.dispose();
17564
+ entry.hit.removeFromParent();
17565
+ entry.hit.geometry.dispose();
17566
+ entry.hit.material.dispose();
16458
17567
  entry.texture.dispose();
16459
17568
  }
16460
17569
  };
16461
17570
  __name(_UIPanelSystem, "UIPanelSystem");
16462
17571
  var UIPanelSystem = _UIPanelSystem;
17572
+ var eyeScratch = new THREE67__namespace.Vector3();
17573
+ function tagPart(mesh, color, premultiplied = false) {
17574
+ const tag = {
17575
+ baseColor: new THREE67__namespace.Color(color),
17576
+ tint: 1,
17577
+ opacity: 1,
17578
+ premultiplied
17579
+ };
17580
+ mesh.userData[UI_PART_KEY] = tag;
17581
+ }
17582
+ __name(tagPart, "tagPart");
17583
+ function resizePlane(mesh, width, height) {
17584
+ const params = mesh.geometry.parameters;
17585
+ if (params.width === width && params.height === height) return;
17586
+ mesh.geometry.dispose();
17587
+ mesh.geometry = new THREE67__namespace.PlaneGeometry(width, height);
17588
+ }
17589
+ __name(resizePlane, "resizePlane");
17590
+ function layoutBox(widget, bleed, density) {
17591
+ return {
17592
+ x: bleed * density,
17593
+ y: bleed * density,
17594
+ w: widget.width * density,
17595
+ h: widget.height * density
17596
+ };
17597
+ }
17598
+ __name(layoutBox, "layoutBox");
17599
+ function familyOf(font) {
17600
+ return font ? uiFontFamily(font) : null;
17601
+ }
17602
+ __name(familyOf, "familyOf");
17603
+ function imagesLoaded(chrome) {
17604
+ return styleAssets(chrome, null).images.map((src) => !!uiImage(src));
17605
+ }
17606
+ __name(imagesLoaded, "imagesLoaded");
17607
+ function rootRect(id, entry) {
17608
+ const e = entry.root.matrixWorld.elements;
17609
+ const halfW = entry.width / 2;
17610
+ const halfH = entry.height / 2;
17611
+ return {
17612
+ id,
17613
+ center: { x: e[12], y: e[13], z: e[14] },
17614
+ halfX: { x: e[0] * halfW, y: e[1] * halfW, z: e[2] * halfW },
17615
+ halfY: { x: e[4] * halfH, y: e[5] * halfH, z: e[6] * halfH }
17616
+ };
17617
+ }
17618
+ __name(rootRect, "rootRect");
17619
+ function stampDescendants(root, slot, widgetRoots) {
17620
+ for (const child of root.children) {
17621
+ if (widgetRoots.has(child) || child.userData[UI_PART_KEY]) continue;
17622
+ const material = child.material;
17623
+ const transparent = Array.isArray(material) ? material.some((m) => m.transparent) : material?.transparent === true;
17624
+ const untouched = child.renderOrder === 0 || child.userData[PAINT_STAMP_KEY] === child.renderOrder;
17625
+ if (transparent && untouched) {
17626
+ child.renderOrder = slot.descendants;
17627
+ child.userData[PAINT_STAMP_KEY] = slot.descendants;
17628
+ }
17629
+ stampDescendants(child, slot, widgetRoots);
17630
+ }
17631
+ }
17632
+ __name(stampDescendants, "stampDescendants");
16463
17633
  var _XRHitTestSystem = class _XRHitTestSystem {
16464
17634
  constructor() {
16465
17635
  this.sources = /* @__PURE__ */ new Map();
@@ -16496,12 +17666,12 @@ var _XRHitTestSystem = class _XRHitTestSystem {
16496
17666
  if (!hits || hits.length === 0) continue;
16497
17667
  const pose = hits[0].getPose(referenceSpace);
16498
17668
  if (!pose) continue;
16499
- const matrix = new THREE60__namespace.Matrix4().fromArray(
17669
+ const matrix = new THREE67__namespace.Matrix4().fromArray(
16500
17670
  pose.transform.matrix
16501
17671
  );
16502
- const position = new THREE60__namespace.Vector3();
16503
- const quaternion = new THREE60__namespace.Quaternion();
16504
- matrix.decompose(position, quaternion, new THREE60__namespace.Vector3());
17672
+ const position = new THREE67__namespace.Vector3();
17673
+ const quaternion = new THREE67__namespace.Quaternion();
17674
+ matrix.decompose(position, quaternion, new THREE67__namespace.Vector3());
16505
17675
  this.results.set(key, { position, quaternion });
16506
17676
  }
16507
17677
  }
@@ -16575,8 +17745,8 @@ var _XRAnchorSystem = class _XRAnchorSystem {
16575
17745
  /** Per-anchor head-glue gates — see XREnvironmentSystem's TrackedEntry. */
16576
17746
  this.gates = /* @__PURE__ */ new Map();
16577
17747
  this.presenting = false;
16578
- this.tmpViewerMatrix = new THREE60__namespace.Matrix4();
16579
- this.tmpViewPos = new THREE60__namespace.Vector3();
17748
+ this.tmpViewerMatrix = new THREE67__namespace.Matrix4();
17749
+ this.tmpViewPos = new THREE67__namespace.Vector3();
16580
17750
  }
16581
17751
  update(world, deltaTime = 0) {
16582
17752
  const presenting = world.isXrPresenting();
@@ -16650,8 +17820,8 @@ var _XRAnchorSystem = class _XRAnchorSystem {
16650
17820
  if (typeof frame.createAnchor !== "function") return;
16651
17821
  const RigidTransform = globalThis.XRRigidTransform;
16652
17822
  if (!RigidTransform) return;
16653
- const quaternion = new THREE60__namespace.Quaternion().setFromEuler(
16654
- new THREE60__namespace.Euler(
17823
+ const quaternion = new THREE67__namespace.Quaternion().setFromEuler(
17824
+ new THREE67__namespace.Euler(
16655
17825
  transform.rotation.x,
16656
17826
  transform.rotation.y,
16657
17827
  transform.rotation.z,
@@ -16694,13 +17864,13 @@ var _XRAnchorSystem = class _XRAnchorSystem {
16694
17864
  };
16695
17865
  __name(_XRAnchorSystem, "XRAnchorSystem");
16696
17866
  var XRAnchorSystem = _XRAnchorSystem;
16697
- var _q = new THREE60__namespace.Quaternion();
16698
- var _q2 = new THREE60__namespace.Quaternion();
16699
- var _s = new THREE60__namespace.Vector3();
16700
- var _e = new THREE60__namespace.Vector3();
16701
- var _w = new THREE60__namespace.Vector3();
16702
- var _v = new THREE60__namespace.Vector3();
16703
- var _p = new THREE60__namespace.Vector3();
17867
+ var _q = new THREE67__namespace.Quaternion();
17868
+ var _q2 = new THREE67__namespace.Quaternion();
17869
+ var _s = new THREE67__namespace.Vector3();
17870
+ var _e = new THREE67__namespace.Vector3();
17871
+ var _w = new THREE67__namespace.Vector3();
17872
+ var _v = new THREE67__namespace.Vector3();
17873
+ var _p = new THREE67__namespace.Vector3();
16704
17874
  function setWorldQuaternion(o, q) {
16705
17875
  if (o.parent) {
16706
17876
  o.parent.getWorldQuaternion(_q2).invert();
@@ -16725,31 +17895,31 @@ function twoBoneIK(upper, lower, end, target, pole) {
16725
17895
  if (a < 1e-6 || b < 1e-6) return target.distanceTo(_w);
16726
17896
  const toT = _v.subVectors(target, _s);
16727
17897
  const want = toT.length();
16728
- const c = THREE60__namespace.MathUtils.clamp(want, Math.abs(a - b) + 1e-4, a + b - 1e-4);
17898
+ const c = THREE67__namespace.MathUtils.clamp(want, Math.abs(a - b) + 1e-4, a + b - 1e-4);
16729
17899
  const dir = toT.divideScalar(want || 1).clone();
16730
17900
  const pv = _p.subVectors(pole, _s);
16731
17901
  pv.addScaledVector(dir, -pv.dot(dir));
16732
17902
  if (pv.lengthSq() < 1e-8) pv.set(0, -1, 0).addScaledVector(dir, dir.y);
16733
17903
  pv.normalize();
16734
- const cosA = THREE60__namespace.MathUtils.clamp((a * a + c * c - b * b) / (2 * a * c), -1, 1);
17904
+ const cosA = THREE67__namespace.MathUtils.clamp((a * a + c * c - b * b) / (2 * a * c), -1, 1);
16735
17905
  const joint = _s.clone().addScaledVector(dir, a * cosA).addScaledVector(pv, a * Math.sqrt(1 - cosA * cosA));
16736
- turn(upper, new THREE60__namespace.Quaternion().setFromUnitVectors(_e.clone().sub(_s).normalize(), joint.clone().sub(_s).normalize()));
17906
+ turn(upper, new THREE67__namespace.Quaternion().setFromUnitVectors(_e.clone().sub(_s).normalize(), joint.clone().sub(_s).normalize()));
16737
17907
  lower.getWorldPosition(_e);
16738
17908
  end.getWorldPosition(_w);
16739
17909
  const reach = _s.clone().addScaledVector(dir, c);
16740
- turn(lower, new THREE60__namespace.Quaternion().setFromUnitVectors(_w.clone().sub(_e).normalize(), reach.clone().sub(_e).normalize()));
17910
+ turn(lower, new THREE67__namespace.Quaternion().setFromUnitVectors(_w.clone().sub(_e).normalize(), reach.clone().sub(_e).normalize()));
16741
17911
  return want - c;
16742
17912
  }
16743
17913
  __name(twoBoneIK, "twoBoneIK");
16744
- function gripQuaternion(axis, back, forearm, out = new THREE60__namespace.Quaternion()) {
17914
+ function gripQuaternion(axis, back, forearm, out = new THREE67__namespace.Quaternion()) {
16745
17915
  let X = axis.clone();
16746
17916
  const Z = back.clone().addScaledVector(X, -back.dot(X)).normalize().negate();
16747
- let Y = new THREE60__namespace.Vector3().crossVectors(Z, X);
17917
+ let Y = new THREE67__namespace.Vector3().crossVectors(Z, X);
16748
17918
  if (Y.dot(forearm) < 0) {
16749
17919
  X = X.negate();
16750
17920
  Y = Y.negate();
16751
17921
  }
16752
- return out.setFromRotationMatrix(new THREE60__namespace.Matrix4().makeBasis(X, Y, Z));
17922
+ return out.setFromRotationMatrix(new THREE67__namespace.Matrix4().makeBasis(X, Y, Z));
16753
17923
  }
16754
17924
  __name(gripQuaternion, "gripQuaternion");
16755
17925
 
@@ -16760,18 +17930,20 @@ var LIMB_BONES = {
16760
17930
  leftFoot: ["leftUpperLeg", "leftLowerLeg", "leftFoot"],
16761
17931
  rightFoot: ["rightUpperLeg", "rightLowerLeg", "rightFoot"]
16762
17932
  };
16763
- var _t = new THREE60__namespace.Vector3();
16764
- var _p2 = new THREE60__namespace.Vector3();
16765
- var _q3 = new THREE60__namespace.Quaternion();
16766
- var _pq = new THREE60__namespace.Quaternion();
17933
+ var _t = new THREE67__namespace.Vector3();
17934
+ var _p2 = new THREE67__namespace.Vector3();
17935
+ var _q3 = new THREE67__namespace.Quaternion();
17936
+ var _pq = new THREE67__namespace.Quaternion();
16767
17937
  var _IKSystem = class _IKSystem {
16768
17938
  constructor() {
16769
17939
  this.runtimes = /* @__PURE__ */ new Map();
16770
- this.reach = /* @__PURE__ */ new Map();
16771
17940
  }
16772
- /** How far a limb's end stayed from its target last frame, in metres (0 = reached). */
17941
+ /**
17942
+ * How far a limb's end stayed from its target last frame, in metres
17943
+ * (0 = reached), or null if the limb's chain didn't run.
17944
+ */
16773
17945
  getReachError(entityId, limb) {
16774
- return this.reach.get(`${entityId}:${limb}`) ?? null;
17946
+ return this.runtimes.get(entityId)?.reach.get(limb) ?? null;
16775
17947
  }
16776
17948
  restore(runtime) {
16777
17949
  for (const [bone, w] of runtime.written) {
@@ -16797,13 +17969,14 @@ var _IKSystem = class _IKSystem {
16797
17969
  let runtime = this.runtimes.get(id);
16798
17970
  if (!runtime || runtime.rootUuid !== root.uuid) {
16799
17971
  if (runtime) this.restore(runtime);
16800
- runtime = { rootUuid: root.uuid, limbs: /* @__PURE__ */ new Map(), written: /* @__PURE__ */ new Map() };
17972
+ runtime = { rootUuid: root.uuid, limbs: /* @__PURE__ */ new Map(), written: /* @__PURE__ */ new Map(), reach: /* @__PURE__ */ new Map() };
16801
17973
  this.runtimes.set(id, runtime);
16802
17974
  }
16803
17975
  for (const [bone, w] of runtime.written) {
16804
17976
  if (bone.quaternion.equals(w.written)) bone.quaternion.copy(w.before);
16805
17977
  }
16806
17978
  runtime.written.clear();
17979
+ runtime.reach.clear();
16807
17980
  if (!ik.enabled) continue;
16808
17981
  const active = ik.chains.filter((c) => c.enabled && c.weight > 0 && typeof c.target === "number");
16809
17982
  if (active.length === 0) continue;
@@ -16824,7 +17997,7 @@ var _IKSystem = class _IKSystem {
16824
17997
  const [upper, lower, end] = limb.bones;
16825
17998
  for (const bone of [upper, lower, end]) {
16826
17999
  if (!runtime.written.has(bone)) {
16827
- runtime.written.set(bone, { before: bone.quaternion.clone(), written: new THREE60__namespace.Quaternion() });
18000
+ runtime.written.set(bone, { before: bone.quaternion.clone(), written: new THREE67__namespace.Quaternion() });
16828
18001
  }
16829
18002
  }
16830
18003
  const animated = [upper.quaternion.clone(), lower.quaternion.clone(), end.quaternion.clone()];
@@ -16833,7 +18006,7 @@ var _IKSystem = class _IKSystem {
16833
18006
  if (poleObject) poleObject.getWorldPosition(_p2);
16834
18007
  else lower.getWorldPosition(_p2);
16835
18008
  const miss = twoBoneIK(upper, lower, end, _t, _p2);
16836
- this.reach.set(`${id}:${chain.limb}`, Math.max(0, miss));
18009
+ runtime.reach.set(chain.limb, Math.max(0, miss));
16837
18010
  if (chain.matchRotation) {
16838
18011
  target.getWorldQuaternion(_q3);
16839
18012
  end.parent?.getWorldQuaternion(_pq);
@@ -16862,9 +18035,9 @@ var IKSystem = _IKSystem;
16862
18035
  function buildGeometry2(area) {
16863
18036
  if (area.shape === "circle") {
16864
18037
  const radius = Math.max(5e-3, area.width / 2);
16865
- return new THREE60__namespace.CircleGeometry(radius, 48);
18038
+ return new THREE67__namespace.CircleGeometry(radius, 48);
16866
18039
  }
16867
- return new THREE60__namespace.PlaneGeometry(
18040
+ return new THREE67__namespace.PlaneGeometry(
16868
18041
  Math.max(0.01, area.width),
16869
18042
  Math.max(0.01, area.depth)
16870
18043
  );
@@ -16881,13 +18054,13 @@ var _VRTeleportAreaSystem = class _VRTeleportAreaSystem {
16881
18054
  update(world) {
16882
18055
  const entityIds = world.query(VRTeleportAreaComponent, Object3DRef);
16883
18056
  const alive = new Set(entityIds);
16884
- for (const tracked of this.signatures.keys()) {
16885
- if (!alive.has(tracked)) this.signatures.delete(tracked);
18057
+ for (const tracked2 of this.signatures.keys()) {
18058
+ if (!alive.has(tracked2)) this.signatures.delete(tracked2);
16886
18059
  }
16887
18060
  for (const entityId of entityIds) {
16888
18061
  const area = world.getComponent(entityId, VRTeleportAreaComponent);
16889
18062
  const object = world.getComponent(entityId, Object3DRef)?.object;
16890
- if (!area || !(object instanceof THREE60__namespace.Mesh)) continue;
18063
+ if (!area || !(object instanceof THREE67__namespace.Mesh)) continue;
16891
18064
  const signature = signatureOf(area);
16892
18065
  if (this.signatures.get(entityId) === signature) continue;
16893
18066
  this.signatures.set(entityId, signature);
@@ -16899,18 +18072,18 @@ var _VRTeleportAreaSystem = class _VRTeleportAreaSystem {
16899
18072
  __name(_VRTeleportAreaSystem, "VRTeleportAreaSystem");
16900
18073
  var VRTeleportAreaSystem = _VRTeleportAreaSystem;
16901
18074
  function createHandMaterial(preset, options) {
16902
- const color = new THREE60__namespace.Color(options.color);
18075
+ const color = new THREE67__namespace.Color(options.color);
16903
18076
  switch (preset) {
16904
18077
  case "ghost":
16905
- return new THREE60__namespace.MeshBasicMaterial({
18078
+ return new THREE67__namespace.MeshBasicMaterial({
16906
18079
  color,
16907
18080
  transparent: true,
16908
18081
  opacity: Math.min(1, Math.max(0.05, options.opacity * 0.45)),
16909
18082
  depthWrite: false,
16910
- side: THREE60__namespace.DoubleSide
18083
+ side: THREE67__namespace.DoubleSide
16911
18084
  });
16912
18085
  case "neon":
16913
- return new THREE60__namespace.MeshStandardMaterial({
18086
+ return new THREE67__namespace.MeshStandardMaterial({
16914
18087
  color,
16915
18088
  emissive: color,
16916
18089
  emissiveIntensity: Math.max(0.5, options.emissiveIntensity || 2),
@@ -16920,7 +18093,7 @@ function createHandMaterial(preset, options) {
16920
18093
  opacity: options.opacity
16921
18094
  });
16922
18095
  case "skeleton":
16923
- return new THREE60__namespace.MeshBasicMaterial({
18096
+ return new THREE67__namespace.MeshBasicMaterial({
16924
18097
  color,
16925
18098
  wireframe: true,
16926
18099
  transparent: true,
@@ -16928,7 +18101,7 @@ function createHandMaterial(preset, options) {
16928
18101
  });
16929
18102
  case "custom":
16930
18103
  case "default":
16931
- return new THREE60__namespace.MeshStandardMaterial({
18104
+ return new THREE67__namespace.MeshStandardMaterial({
16932
18105
  color,
16933
18106
  roughness: 0.85,
16934
18107
  metalness: 0,
@@ -16936,10 +18109,10 @@ function createHandMaterial(preset, options) {
16936
18109
  emissiveIntensity: Math.max(0, options.emissiveIntensity),
16937
18110
  transparent: options.opacity < 1,
16938
18111
  opacity: options.opacity,
16939
- side: THREE60__namespace.DoubleSide
18112
+ side: THREE67__namespace.DoubleSide
16940
18113
  });
16941
18114
  default: {
16942
- return new THREE60__namespace.MeshStandardMaterial({ color });
18115
+ return new THREE67__namespace.MeshStandardMaterial({ color });
16943
18116
  }
16944
18117
  }
16945
18118
  }
@@ -16964,9 +18137,9 @@ var _XRControllerVisualSystem = class _XRControllerVisualSystem {
16964
18137
  constructor(options) {
16965
18138
  this.controllerModelFactory = new XRControllerModelFactory_js.XRControllerModelFactory();
16966
18139
  this.handModelFactory = new XRHandModelFactory_js.XRHandModelFactory();
16967
- this.group = new THREE60__namespace.Group();
18140
+ this.group = new THREE67__namespace.Group();
16968
18141
  /** Reused so a per-frame colour compare allocates nothing. */
16969
- this.rayColorScratch = new THREE60__namespace.Color();
18142
+ this.rayColorScratch = new THREE67__namespace.Color();
16970
18143
  this.slots = /* @__PURE__ */ new Map();
16971
18144
  this.attachedTo = null;
16972
18145
  /** Last applied preset signature, so materials rebuild only on real change. */
@@ -17103,8 +18276,8 @@ var _XRControllerVisualSystem = class _XRControllerVisualSystem {
17103
18276
  */
17104
18277
  fitRayToHover(ray, side) {
17105
18278
  const hover = this.interactionSystem?.getHoverFor(side) ?? null;
17106
- const length = hover && Number.isFinite(hover.distance) && hover.distance > 0 ? Math.min(hover.distance, RAY_LENGTH) : RAY_LENGTH;
17107
- ray.scale.z = length;
18279
+ const length2 = hover && Number.isFinite(hover.distance) && hover.distance > 0 ? Math.min(hover.distance, RAY_LENGTH) : RAY_LENGTH;
18280
+ ray.scale.z = length2;
17108
18281
  }
17109
18282
  /** Lazily build per-slot visuals; the device reports profiles late. */
17110
18283
  ensureSlots(world) {
@@ -17153,17 +18326,17 @@ var _XRControllerVisualSystem = class _XRControllerVisualSystem {
17153
18326
  * without pulling in a line-rendering extension.
17154
18327
  */
17155
18328
  createRay() {
17156
- const geometry = new THREE60__namespace.CylinderGeometry(RAY_RADIUS, RAY_RADIUS, 1, 8, 1);
18329
+ const geometry = new THREE67__namespace.CylinderGeometry(RAY_RADIUS, RAY_RADIUS, 1, 8, 1);
17157
18330
  geometry.rotateX(-Math.PI / 2);
17158
18331
  geometry.translate(0, 0, -0.5);
17159
- const material = new THREE60__namespace.MeshBasicMaterial({
18332
+ const material = new THREE67__namespace.MeshBasicMaterial({
17160
18333
  color: "#4da3ff",
17161
18334
  transparent: true,
17162
18335
  opacity: 0.75,
17163
18336
  depthTest: false,
17164
18337
  depthWrite: false
17165
18338
  });
17166
- const mesh = new THREE60__namespace.Mesh(geometry, material);
18339
+ const mesh = new THREE67__namespace.Mesh(geometry, material);
17167
18340
  mesh.name = "XRPointerRay";
17168
18341
  mesh.renderOrder = 999;
17169
18342
  mesh.visible = false;
@@ -17194,14 +18367,14 @@ var _XRControllerVisualSystem = class _XRControllerVisualSystem {
17194
18367
  __name(_XRControllerVisualSystem, "XRControllerVisualSystem");
17195
18368
  var XRControllerVisualSystem = _XRControllerVisualSystem;
17196
18369
  var MARKER_RADIUS = 0.28;
17197
- var ACCENT = new THREE60__namespace.Color("#4da3ff");
17198
- var SEGMENT_AXIS = new THREE60__namespace.Vector3(0, 1, 0);
18370
+ var ACCENT = new THREE67__namespace.Color("#4da3ff");
18371
+ var SEGMENT_AXIS = new THREE67__namespace.Vector3(0, 1, 0);
17199
18372
  var HEAD_CLEARANCE = 0.25;
17200
18373
  var _XRTeleportVisualSystem = class _XRTeleportVisualSystem {
17201
18374
  constructor(locomotionSystem) {
17202
- this.group = new THREE60__namespace.Group();
18375
+ this.group = new THREE67__namespace.Group();
17203
18376
  /** Holds the segment chain that draws the arc. */
17204
- this.arc = new THREE60__namespace.Group();
18377
+ this.arc = new THREE67__namespace.Group();
17205
18378
  /**
17206
18379
  * Unit cylinder along +Y, reused by every segment.
17207
18380
  *
@@ -17210,14 +18383,14 @@ var _XRTeleportVisualSystem = class _XRTeleportVisualSystem {
17210
18383
  * scaled cylinders gives real thickness while rebuilding no geometry: each
17211
18384
  * frame only moves, rotates and scales meshes that already exist.
17212
18385
  */
17213
- this.segmentGeometry = new THREE60__namespace.CylinderGeometry(1, 1, 1, 6, 1);
18386
+ this.segmentGeometry = new THREE67__namespace.CylinderGeometry(1, 1, 1, 6, 1);
17214
18387
  this.segments = [];
17215
- this.marker = new THREE60__namespace.Group();
18388
+ this.marker = new THREE67__namespace.Group();
17216
18389
  this.attached = false;
17217
- this.scratchDirection = new THREE60__namespace.Vector3();
17218
- this.scratchHead = new THREE60__namespace.Vector3();
17219
- this.validColor = new THREE60__namespace.Color();
17220
- this.invalidColor = new THREE60__namespace.Color();
18390
+ this.scratchDirection = new THREE67__namespace.Vector3();
18391
+ this.scratchHead = new THREE67__namespace.Vector3();
18392
+ this.validColor = new THREE67__namespace.Color();
18393
+ this.invalidColor = new THREE67__namespace.Color();
17221
18394
  this.locomotionSystem = locomotionSystem;
17222
18395
  this.group.name = "XRTeleportVisuals";
17223
18396
  this.group.userData.ecsPreviewHidden = true;
@@ -17275,8 +18448,8 @@ var _XRTeleportVisualSystem = class _XRTeleportVisualSystem {
17275
18448
  const from = curve[i];
17276
18449
  const to = curve[i + 1];
17277
18450
  this.scratchDirection.subVectors(to, from);
17278
- const length = this.scratchDirection.length();
17279
- if (length < 1e-6) {
18451
+ const length2 = this.scratchDirection.length();
18452
+ if (length2 < 1e-6) {
17280
18453
  segment.visible = false;
17281
18454
  continue;
17282
18455
  }
@@ -17284,9 +18457,9 @@ var _XRTeleportVisualSystem = class _XRTeleportVisualSystem {
17284
18457
  segment.position.copy(from).addScaledVector(this.scratchDirection, 0.5);
17285
18458
  segment.quaternion.setFromUnitVectors(
17286
18459
  SEGMENT_AXIS,
17287
- this.scratchDirection.divideScalar(length)
18460
+ this.scratchDirection.divideScalar(length2)
17288
18461
  );
17289
- segment.scale.set(radius, length, radius);
18462
+ segment.scale.set(radius, length2, radius);
17290
18463
  const t = needed > 1 ? i / (needed - 1) : 0;
17291
18464
  const fade = 1 - t * 0.75;
17292
18465
  segment.material.color.copy(tint).multiplyScalar(fade);
@@ -17295,12 +18468,12 @@ var _XRTeleportVisualSystem = class _XRTeleportVisualSystem {
17295
18468
  /** Grow the segment pool to cover the current arc. */
17296
18469
  ensureSegments(count) {
17297
18470
  while (this.segments.length < count) {
17298
- const material = new THREE60__namespace.MeshBasicMaterial({
18471
+ const material = new THREE67__namespace.MeshBasicMaterial({
17299
18472
  transparent: true,
17300
18473
  depthTest: false,
17301
18474
  depthWrite: false
17302
18475
  });
17303
- const mesh = new THREE60__namespace.Mesh(this.segmentGeometry, material);
18476
+ const mesh = new THREE67__namespace.Mesh(this.segmentGeometry, material);
17304
18477
  mesh.frustumCulled = false;
17305
18478
  mesh.renderOrder = 998;
17306
18479
  mesh.raycast = () => {
@@ -17324,33 +18497,33 @@ var _XRTeleportVisualSystem = class _XRTeleportVisualSystem {
17324
18497
  return world.getComponent(entityId, XRRigComponent) ?? null;
17325
18498
  }
17326
18499
  createRing() {
17327
- const geometry = new THREE60__namespace.RingGeometry(
18500
+ const geometry = new THREE67__namespace.RingGeometry(
17328
18501
  MARKER_RADIUS * 0.72,
17329
18502
  MARKER_RADIUS,
17330
18503
  48
17331
18504
  );
17332
18505
  geometry.rotateX(-Math.PI / 2);
17333
- return new THREE60__namespace.Mesh(
18506
+ return new THREE67__namespace.Mesh(
17334
18507
  geometry,
17335
- new THREE60__namespace.MeshBasicMaterial({
18508
+ new THREE67__namespace.MeshBasicMaterial({
17336
18509
  color: ACCENT,
17337
18510
  transparent: true,
17338
18511
  opacity: 0.9,
17339
- side: THREE60__namespace.DoubleSide,
18512
+ side: THREE67__namespace.DoubleSide,
17340
18513
  depthTest: false
17341
18514
  })
17342
18515
  );
17343
18516
  }
17344
18517
  createDot() {
17345
- const geometry = new THREE60__namespace.CircleGeometry(MARKER_RADIUS * 0.16, 24);
18518
+ const geometry = new THREE67__namespace.CircleGeometry(MARKER_RADIUS * 0.16, 24);
17346
18519
  geometry.rotateX(-Math.PI / 2);
17347
- return new THREE60__namespace.Mesh(
18520
+ return new THREE67__namespace.Mesh(
17348
18521
  geometry,
17349
- new THREE60__namespace.MeshBasicMaterial({
18522
+ new THREE67__namespace.MeshBasicMaterial({
17350
18523
  color: ACCENT,
17351
18524
  transparent: true,
17352
18525
  opacity: 0.55,
17353
- side: THREE60__namespace.DoubleSide,
18526
+ side: THREE67__namespace.DoubleSide,
17354
18527
  depthTest: false
17355
18528
  })
17356
18529
  );
@@ -17461,7 +18634,7 @@ function boneDirections(joints, finger) {
17461
18634
  const from = joints[chain[i - 1]];
17462
18635
  const to = joints[chain[i]];
17463
18636
  if (!from || !to) return null;
17464
- const direction = new THREE60__namespace.Vector3().subVectors(to, from);
18637
+ const direction = new THREE67__namespace.Vector3().subVectors(to, from);
17465
18638
  if (direction.lengthSq() < 1e-12) return null;
17466
18639
  directions.push(direction.normalize());
17467
18640
  }
@@ -17534,8 +18707,8 @@ function computeSpread(joints, a, b) {
17534
18707
  const bBase = joints[METACARPAL_JOINTS[b]];
17535
18708
  const bTip = joints[TIP_JOINTS[b]];
17536
18709
  if (!aBase || !aTip || !bBase || !bTip) return null;
17537
- const dirA = new THREE60__namespace.Vector3().subVectors(aTip, aBase);
17538
- const dirB = new THREE60__namespace.Vector3().subVectors(bTip, bBase);
18710
+ const dirA = new THREE67__namespace.Vector3().subVectors(aTip, aBase);
18711
+ const dirB = new THREE67__namespace.Vector3().subVectors(bTip, bBase);
17539
18712
  if (dirA.lengthSq() < 1e-12 || dirB.lengthSq() < 1e-12) return null;
17540
18713
  return clamp01((1 - dirA.normalize().dot(dirB.normalize())) / 0.45);
17541
18714
  }
@@ -17545,18 +18718,18 @@ function computePalmPose(joints, handedness) {
17545
18718
  const indexBase = joints["index-finger-metacarpal"];
17546
18719
  const pinkyBase = joints["pinky-finger-metacarpal"];
17547
18720
  if (!wrist || !indexBase || !pinkyBase) return null;
17548
- const u = new THREE60__namespace.Vector3().subVectors(indexBase, wrist);
17549
- const v = new THREE60__namespace.Vector3().subVectors(pinkyBase, wrist);
17550
- const normal = new THREE60__namespace.Vector3().crossVectors(u, v);
18721
+ const u = new THREE67__namespace.Vector3().subVectors(indexBase, wrist);
18722
+ const v = new THREE67__namespace.Vector3().subVectors(pinkyBase, wrist);
18723
+ const normal = new THREE67__namespace.Vector3().crossVectors(u, v);
17551
18724
  if (normal.lengthSq() < 1e-12) return null;
17552
18725
  normal.normalize();
17553
18726
  if (handedness === "left") normal.multiplyScalar(-1);
17554
- const right = new THREE60__namespace.Vector3().subVectors(indexBase, pinkyBase);
18727
+ const right = new THREE67__namespace.Vector3().subVectors(indexBase, pinkyBase);
17555
18728
  if (right.lengthSq() < 1e-12) return null;
17556
18729
  right.normalize();
17557
18730
  if (handedness === "left") right.multiplyScalar(-1);
17558
- const up = new THREE60__namespace.Vector3().crossVectors(normal, right).normalize();
17559
- const center = new THREE60__namespace.Vector3().add(wrist).add(indexBase).add(pinkyBase).divideScalar(3);
18731
+ const up = new THREE67__namespace.Vector3().crossVectors(normal, right).normalize();
18732
+ const center = new THREE67__namespace.Vector3().add(wrist).add(indexBase).add(pinkyBase).divideScalar(3);
17560
18733
  return {
17561
18734
  center,
17562
18735
  normal,
@@ -17568,17 +18741,17 @@ function computePalmPose(joints, handedness) {
17568
18741
  __name(computePalmPose, "computePalmPose");
17569
18742
  function computeHandShape(joints, handedness) {
17570
18743
  const fingers2 = {};
17571
- let tracked = 0;
18744
+ let tracked2 = 0;
17572
18745
  for (const finger of XR_FINGERS) {
17573
18746
  const curl = computeFingerCurl(joints, finger);
17574
18747
  if (curl) {
17575
18748
  fingers2[finger] = curl;
17576
- tracked += 1;
18749
+ tracked2 += 1;
17577
18750
  } else {
17578
18751
  fingers2[finger] = { baseCurl: 0, tipCurl: 0, curl: 0 };
17579
18752
  }
17580
18753
  }
17581
- if (tracked === 0) return null;
18754
+ if (tracked2 === 0) return null;
17582
18755
  const pinch = {};
17583
18756
  for (const finger of PINCH_FINGERS) {
17584
18757
  pinch[finger] = computePinch(joints, finger) ?? 0;
@@ -17587,7 +18760,7 @@ function computeHandShape(joints, handedness) {
17587
18760
  const thumbTip = joints["thumb-tip"];
17588
18761
  let thumbDirection = null;
17589
18762
  if (thumbBase && thumbTip) {
17590
- const direction = new THREE60__namespace.Vector3().subVectors(thumbTip, thumbBase);
18763
+ const direction = new THREE67__namespace.Vector3().subVectors(thumbTip, thumbBase);
17591
18764
  if (direction.lengthSq() > 1e-12) thumbDirection = direction.normalize();
17592
18765
  }
17593
18766
  const fingerDirections = {};
@@ -17598,7 +18771,7 @@ function computeHandShape(joints, handedness) {
17598
18771
  fingerDirections[finger] = null;
17599
18772
  continue;
17600
18773
  }
17601
- const direction = new THREE60__namespace.Vector3().subVectors(tip, base);
18774
+ const direction = new THREE67__namespace.Vector3().subVectors(tip, base);
17602
18775
  fingerDirections[finger] = direction.lengthSq() > 1e-12 ? direction.normalize() : null;
17603
18776
  }
17604
18777
  return {
@@ -17632,12 +18805,12 @@ function scoreAgainst(actual, target, tolerance) {
17632
18805
  __name(scoreAgainst, "scoreAgainst");
17633
18806
  var DEFAULT_DIRECTION_TOLERANCE = 45;
17634
18807
  var WORLD_DIRECTIONS = {
17635
- up: new THREE60__namespace.Vector3(0, 1, 0),
17636
- down: new THREE60__namespace.Vector3(0, -1, 0),
17637
- forward: new THREE60__namespace.Vector3(0, 0, -1),
17638
- back: new THREE60__namespace.Vector3(0, 0, 1),
17639
- left: new THREE60__namespace.Vector3(-1, 0, 0),
17640
- right: new THREE60__namespace.Vector3(1, 0, 0)
18808
+ up: new THREE67__namespace.Vector3(0, 1, 0),
18809
+ down: new THREE67__namespace.Vector3(0, -1, 0),
18810
+ forward: new THREE67__namespace.Vector3(0, 0, -1),
18811
+ back: new THREE67__namespace.Vector3(0, 0, 1),
18812
+ left: new THREE67__namespace.Vector3(-1, 0, 0),
18813
+ right: new THREE67__namespace.Vector3(1, 0, 0)
17641
18814
  };
17642
18815
  function axisVector(shape, axis) {
17643
18816
  if (axis === "thumb") return shape.thumbDirection;
@@ -17652,7 +18825,7 @@ function resolveDirection(direction, shape, context) {
17652
18825
  const head = context?.headPosition;
17653
18826
  const origin = shape.palm?.center;
17654
18827
  if (!head || !origin) return null;
17655
- const toHead = new THREE60__namespace.Vector3().subVectors(head, origin);
18828
+ const toHead = new THREE67__namespace.Vector3().subVectors(head, origin);
17656
18829
  if (toHead.lengthSq() < 1e-12) return null;
17657
18830
  toHead.normalize();
17658
18831
  return direction === "toward-head" ? toHead : toHead.multiplyScalar(-1);
@@ -17664,8 +18837,8 @@ function scoreDirections(actual, accepted, shape, toleranceDegrees, context) {
17664
18837
  for (const direction of accepted) {
17665
18838
  const wanted = resolveDirection(direction, shape, context);
17666
18839
  if (!wanted) continue;
17667
- const angle = THREE60__namespace.MathUtils.radToDeg(
17668
- Math.acos(THREE60__namespace.MathUtils.clamp(actual.dot(wanted), -1, 1))
18840
+ const angle = THREE67__namespace.MathUtils.radToDeg(
18841
+ Math.acos(THREE67__namespace.MathUtils.clamp(actual.dot(wanted), -1, 1))
17669
18842
  );
17670
18843
  const normalizedError = Math.max(0, angle - toleranceDegrees) / 90;
17671
18844
  const score = clamp01(1 - normalizedError / MATCH_FALLOFF);
@@ -17730,8 +18903,8 @@ function matchHandPose(shape, definition, context) {
17730
18903
  if (!handAxis || !wanted) {
17731
18904
  consider(0, "orientation");
17732
18905
  } else {
17733
- const angle = THREE60__namespace.MathUtils.radToDeg(
17734
- Math.acos(THREE60__namespace.MathUtils.clamp(handAxis.dot(wanted), -1, 1))
18906
+ const angle = THREE67__namespace.MathUtils.radToDeg(
18907
+ Math.acos(THREE67__namespace.MathUtils.clamp(handAxis.dot(wanted), -1, 1))
17735
18908
  );
17736
18909
  const normalizedError = Math.max(0, angle - toleranceDegrees) / 90;
17737
18910
  consider(clamp01(1 - normalizedError / MATCH_FALLOFF), "orientation");
@@ -17809,7 +18982,7 @@ var _XRHandPoseSystem = class _XRHandPoseSystem {
17809
18982
  this.trackers = /* @__PURE__ */ new Map();
17810
18983
  this.shapes = /* @__PURE__ */ new Map();
17811
18984
  /** Reused so head-relative orientation checks allocate nothing per frame. */
17812
- this.headPosition = new THREE60__namespace.Vector3();
18985
+ this.headPosition = new THREE67__namespace.Vector3();
17813
18986
  this.elapsed = 0;
17814
18987
  this.warnedNoHands = false;
17815
18988
  this.noHandSeconds = 0;
@@ -17825,8 +18998,8 @@ var _XRHandPoseSystem = class _XRHandPoseSystem {
17825
18998
  const entries = this.collectEntries(world);
17826
18999
  if (entries.length === 0) return;
17827
19000
  if (!this.warnedNoHands) {
17828
- const tracked = this.shapes.get("left") !== null || this.shapes.get("right") !== null;
17829
- if (tracked) {
19001
+ const tracked2 = this.shapes.get("left") !== null || this.shapes.get("right") !== null;
19002
+ if (tracked2) {
17830
19003
  this.warnedNoHands = true;
17831
19004
  } else {
17832
19005
  this.noHandSeconds += deltaTime;
@@ -17972,6 +19145,7 @@ var _XRHoverVisualSystem = class _XRHoverVisualSystem {
17972
19145
  const interactable = world.getComponent(entityId, XRInteractableComponent);
17973
19146
  if (!interactable?.enabled) continue;
17974
19147
  if (interactable.hoverBehavior === "none") continue;
19148
+ if (isUiWidget(world, entityId)) continue;
17975
19149
  this.begin(world, entityId, interactable);
17976
19150
  }
17977
19151
  for (const [entityId, record] of this.records) {
@@ -17992,7 +19166,7 @@ var _XRHoverVisualSystem = class _XRHoverVisualSystem {
17992
19166
  const wantsColor = interactable.hoverBehavior === "color" || interactable.hoverBehavior === "both";
17993
19167
  this.records.set(entityId, {
17994
19168
  shell: wantsColor ? this.addShell(world, entityId, interactable) : null,
17995
- baseScale: new THREE60__namespace.Vector3(
19169
+ baseScale: new THREE67__namespace.Vector3(
17996
19170
  transform.scale.x,
17997
19171
  transform.scale.y,
17998
19172
  transform.scale.z
@@ -18005,27 +19179,27 @@ var _XRHoverVisualSystem = class _XRHoverVisualSystem {
18005
19179
  addShell(world, entityId, interactable) {
18006
19180
  const object = world.getComponent(entityId, Object3DRef)?.object;
18007
19181
  if (!object) return null;
18008
- const material = new THREE60__namespace.MeshBasicMaterial({
18009
- color: new THREE60__namespace.Color(interactable.hoverColor),
19182
+ const material = new THREE67__namespace.MeshBasicMaterial({
19183
+ color: new THREE67__namespace.Color(interactable.hoverColor),
18010
19184
  // Back faces only, so the shell shows just outside the silhouette instead
18011
19185
  // of covering the object.
18012
- side: THREE60__namespace.BackSide,
19186
+ side: THREE67__namespace.BackSide,
18013
19187
  transparent: true,
18014
19188
  opacity: 0.85,
18015
19189
  depthWrite: false
18016
19190
  });
18017
- const group = new THREE60__namespace.Group();
19191
+ const group = new THREE67__namespace.Group();
18018
19192
  group.name = "XRHoverShell";
18019
19193
  group.userData[XR_HOVER_SHELL_FLAG] = true;
18020
19194
  group.renderOrder = 998;
18021
19195
  object.traverse((child) => {
18022
- if (!(child instanceof THREE60__namespace.Mesh) || !child.geometry) return;
19196
+ if (!(child instanceof THREE67__namespace.Mesh) || !child.geometry) return;
18023
19197
  if (child.userData[XR_HOVER_SHELL_FLAG]) return;
18024
- const clone3 = new THREE60__namespace.Mesh(child.geometry, material);
19198
+ const clone3 = new THREE67__namespace.Mesh(child.geometry, material);
18025
19199
  child.updateWorldMatrix(true, false);
18026
19200
  object.updateWorldMatrix(true, false);
18027
19201
  clone3.matrixAutoUpdate = false;
18028
- clone3.matrix.copy(object.matrixWorld).invert().multiply(child.matrixWorld).scale(new THREE60__namespace.Vector3(SHELL_INFLATE, SHELL_INFLATE, SHELL_INFLATE));
19202
+ clone3.matrix.copy(object.matrixWorld).invert().multiply(child.matrixWorld).scale(new THREE67__namespace.Vector3(SHELL_INFLATE, SHELL_INFLATE, SHELL_INFLATE));
18029
19203
  clone3.userData[XR_HOVER_SHELL_FLAG] = true;
18030
19204
  clone3.raycast = () => {
18031
19205
  };
@@ -18074,7 +19248,7 @@ var _XRHoverVisualSystem = class _XRHoverVisualSystem {
18074
19248
  if (!shell) return;
18075
19249
  shell.parent?.remove(shell);
18076
19250
  shell.traverse((child) => {
18077
- if (child instanceof THREE60__namespace.Mesh) {
19251
+ if (child instanceof THREE67__namespace.Mesh) {
18078
19252
  const material = child.material;
18079
19253
  if (Array.isArray(material)) {
18080
19254
  for (const entry of material) entry.dispose();
@@ -18852,8 +20026,8 @@ var _World = class _World {
18852
20026
  * ```
18853
20027
  */
18854
20028
  raycast(origin, direction, near = 0, far = Infinity) {
18855
- const rayOrigin = new THREE60__namespace.Vector3(origin.x, origin.y, origin.z);
18856
- const rayDirection = new THREE60__namespace.Vector3(
20029
+ const rayOrigin = new THREE67__namespace.Vector3(origin.x, origin.y, origin.z);
20030
+ const rayDirection = new THREE67__namespace.Vector3(
18857
20031
  direction.x,
18858
20032
  direction.y,
18859
20033
  direction.z
@@ -18862,7 +20036,7 @@ var _World = class _World {
18862
20036
  return [];
18863
20037
  }
18864
20038
  rayDirection.normalize();
18865
- const raycaster = new THREE60__namespace.Raycaster(rayOrigin, rayDirection, near, far);
20039
+ const raycaster = new THREE67__namespace.Raycaster(rayOrigin, rayDirection, near, far);
18866
20040
  const intersections = raycaster.intersectObjects(
18867
20041
  this.scene.children,
18868
20042
  true
@@ -18896,14 +20070,14 @@ var _World = class _World {
18896
20070
  if (!sourceObject) {
18897
20071
  return [];
18898
20072
  }
18899
- const rayOrigin = new THREE60__namespace.Vector3();
18900
- const rayDirection = new THREE60__namespace.Vector3();
20073
+ const rayOrigin = new THREE67__namespace.Vector3();
20074
+ const rayDirection = new THREE67__namespace.Vector3();
18901
20075
  sourceObject.getWorldPosition(rayOrigin);
18902
20076
  sourceObject.getWorldDirection(rayDirection);
18903
20077
  if (rayDirection.lengthSq() === 0) {
18904
20078
  return [];
18905
20079
  }
18906
- const raycaster = new THREE60__namespace.Raycaster(rayOrigin, rayDirection, near, far);
20080
+ const raycaster = new THREE67__namespace.Raycaster(rayOrigin, rayDirection, near, far);
18907
20081
  const intersections = raycaster.intersectObjects(
18908
20082
  this.scene.children,
18909
20083
  true