@needle-tools/engine 5.1.0-alpha.8 → 5.1.0-beta

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 (122) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/components.needle.json +1 -1
  3. package/dist/{materialx-CE2sUv2B.umd.cjs → materialx-B47Bz-xs.umd.cjs} +1 -1
  4. package/dist/{materialx-u1EqYrhu.min.js → materialx-B85WjP7E.min.js} +1 -1
  5. package/dist/{materialx-vyB2Zbt4.js → materialx-jkHmVPez.js} +1 -1
  6. package/dist/{needle-engine.bundle-3LOy9tj8.js → needle-engine.bundle-BWkDSRyF.js} +6248 -6001
  7. package/dist/{needle-engine.bundle-BuI2pjv1.min.js → needle-engine.bundle-BvgS8xMP.min.js} +171 -171
  8. package/dist/{needle-engine.bundle-DQy9vQdJ.umd.cjs → needle-engine.bundle-CdVr_iIe.umd.cjs} +142 -142
  9. package/dist/needle-engine.d.ts +359 -46
  10. package/dist/needle-engine.js +618 -606
  11. package/dist/needle-engine.min.js +1 -1
  12. package/dist/needle-engine.umd.cjs +1 -1
  13. package/dist/{vendor-ButPLzor.umd.cjs → vendor-1UvpPPSB.umd.cjs} +2 -2
  14. package/dist/{vendor-DqZC4Is7.min.js → vendor-BQ2Vuntm.min.js} +4 -4
  15. package/dist/{vendor-DkWSNjMV.js → vendor-BslSKZPo.js} +113 -106
  16. package/lib/engine/api.d.ts +1 -1
  17. package/lib/engine/api.js +1 -1
  18. package/lib/engine/api.js.map +1 -1
  19. package/lib/engine/engine_audio.d.ts +30 -1
  20. package/lib/engine/engine_audio.js +57 -13
  21. package/lib/engine/engine_audio.js.map +1 -1
  22. package/lib/engine/engine_init.js +2 -2
  23. package/lib/engine/engine_init.js.map +1 -1
  24. package/lib/engine/engine_license.d.ts +7 -7
  25. package/lib/engine/engine_license.js +72 -72
  26. package/lib/engine/engine_license.js.map +1 -1
  27. package/lib/engine/engine_math.d.ts +1 -1
  28. package/lib/engine/engine_math.js +10 -4
  29. package/lib/engine/engine_math.js.map +1 -1
  30. package/lib/engine/engine_networking_blob.js +3 -3
  31. package/lib/engine/engine_networking_blob.js.map +1 -1
  32. package/lib/engine/engine_scenedata.js +7 -0
  33. package/lib/engine/engine_scenedata.js.map +1 -1
  34. package/lib/engine/engine_serialization_core.js +7 -2
  35. package/lib/engine/engine_serialization_core.js.map +1 -1
  36. package/lib/engine/engine_time_utils.d.ts +10 -6
  37. package/lib/engine/engine_time_utils.js +10 -6
  38. package/lib/engine/engine_time_utils.js.map +1 -1
  39. package/lib/engine/engine_utils_qrcode.js +2 -2
  40. package/lib/engine/engine_utils_qrcode.js.map +1 -1
  41. package/lib/engine/webcomponents/needle menu/needle-menu-spatial.js +2 -2
  42. package/lib/engine/webcomponents/needle menu/needle-menu.js +5 -5
  43. package/lib/engine/webcomponents/needle menu/needle-menu.js.map +1 -1
  44. package/lib/engine/webcomponents/needle-engine.js +2 -2
  45. package/lib/engine/webcomponents/needle-engine.js.map +1 -1
  46. package/lib/engine/webcomponents/needle-engine.loading.js +2 -2
  47. package/lib/engine/webcomponents/needle-engine.loading.js.map +1 -1
  48. package/lib/engine/xr/TempXRContext.js +2 -2
  49. package/lib/engine/xr/TempXRContext.js.map +1 -1
  50. package/lib/engine-components/AudioSource.d.ts +46 -2
  51. package/lib/engine-components/AudioSource.js +268 -60
  52. package/lib/engine-components/AudioSource.js.map +1 -1
  53. package/lib/engine-components/Component.js +2 -1
  54. package/lib/engine-components/Component.js.map +1 -1
  55. package/lib/engine-components/DragControls.js +2 -2
  56. package/lib/engine-components/DragControls.js.map +1 -1
  57. package/lib/engine-components/DragControlsConstraints.d.ts +0 -15
  58. package/lib/engine-components/DragControlsConstraints.js +14 -8
  59. package/lib/engine-components/DragControlsConstraints.js.map +1 -1
  60. package/lib/engine-components/EventList.js +1 -1
  61. package/lib/engine-components/EventList.js.map +1 -1
  62. package/lib/engine-components/SceneSwitcher.d.ts +2 -0
  63. package/lib/engine-components/SceneSwitcher.js +43 -16
  64. package/lib/engine-components/SceneSwitcher.js.map +1 -1
  65. package/lib/engine-components/SeeThrough.d.ts +39 -0
  66. package/lib/engine-components/SeeThrough.js +69 -1
  67. package/lib/engine-components/SeeThrough.js.map +1 -1
  68. package/lib/engine-components/api.d.ts +1 -0
  69. package/lib/engine-components/api.js +1 -0
  70. package/lib/engine-components/api.js.map +1 -1
  71. package/lib/engine-components/codegen/components.d.ts +10 -0
  72. package/lib/engine-components/codegen/components.js +10 -0
  73. package/lib/engine-components/codegen/components.js.map +1 -1
  74. package/lib/engine-components/export/usdz/USDZExporter.js +4 -4
  75. package/lib/engine-components/timeline/PlayableDirector.js +1 -1
  76. package/lib/engine-components/timeline/PlayableDirector.js.map +1 -1
  77. package/lib/engine-components/web/ScrollFollow.d.ts +33 -0
  78. package/lib/engine-components/web/ScrollFollow.js +164 -22
  79. package/lib/engine-components/web/ScrollFollow.js.map +1 -1
  80. package/lib/needle-app.d.ts +18 -0
  81. package/lib/needle-app.js +19 -0
  82. package/lib/needle-app.js.map +1 -0
  83. package/package.json +3 -2
  84. package/plugins/common/license.js +25 -4
  85. package/plugins/common/needle-engine.js +5 -2
  86. package/plugins/types/userconfig.d.ts +22 -0
  87. package/plugins/vite/dts-generator.d.ts +24 -0
  88. package/plugins/vite/dts-generator.js +40 -7
  89. package/plugins/vite/index.d.ts +2 -0
  90. package/plugins/vite/index.js +17 -0
  91. package/plugins/vite/license.js +4 -4
  92. package/plugins/vite/needle-app.js +78 -5
  93. package/plugins/vite/remote.d.ts +28 -0
  94. package/plugins/vite/remote.js +124 -0
  95. package/src/engine/api.ts +1 -1
  96. package/src/engine/engine_audio.ts +68 -15
  97. package/src/engine/engine_gameobject.ts +1 -1
  98. package/src/engine/engine_init.ts +2 -2
  99. package/src/engine/engine_license.ts +69 -69
  100. package/src/engine/engine_math.ts +11 -4
  101. package/src/engine/engine_networking_blob.ts +3 -3
  102. package/src/engine/engine_scenedata.ts +7 -2
  103. package/src/engine/engine_serialization_core.ts +7 -2
  104. package/src/engine/engine_time_utils.ts +10 -6
  105. package/src/engine/engine_utils_qrcode.ts +2 -2
  106. package/src/engine/webcomponents/needle menu/needle-menu-spatial.ts +2 -2
  107. package/src/engine/webcomponents/needle menu/needle-menu.ts +5 -5
  108. package/src/engine/webcomponents/needle-engine.loading.ts +6 -6
  109. package/src/engine/webcomponents/needle-engine.ts +2 -2
  110. package/src/engine/xr/TempXRContext.ts +2 -2
  111. package/src/engine-components/AudioSource.ts +256 -55
  112. package/src/engine-components/Component.ts +2 -1
  113. package/src/engine-components/DragControls.ts +3 -2
  114. package/src/engine-components/DragControlsConstraints.ts +16 -8
  115. package/src/engine-components/EventList.ts +1 -1
  116. package/src/engine-components/SceneSwitcher.ts +37 -15
  117. package/src/engine-components/SeeThrough.ts +75 -1
  118. package/src/engine-components/api.ts +1 -0
  119. package/src/engine-components/codegen/components.ts +10 -0
  120. package/src/engine-components/export/usdz/USDZExporter.ts +4 -4
  121. package/src/engine-components/timeline/PlayableDirector.ts +1 -1
  122. package/src/engine-components/web/ScrollFollow.ts +168 -22
@@ -1,25 +1,78 @@
1
1
  import { AudioContext } from "three";
2
2
 
3
3
  import { Application } from "./engine_application.js";
4
+ import { getParam } from "./engine_utils.js";
4
5
 
5
- /**
6
- * @internal
7
- * Ensure the audio context is resumed if it gets suspended or interrupted */
6
+ /** Verbose audio logging, opt-in via `?debugaudio` (same flag AudioSource uses). */
7
+ const debug = getParam("debugaudio");
8
+
9
+ /**
10
+ * iOS Safari exposes a non-standard `"interrupted"` state in addition to the
11
+ * spec'd `AudioContextState`s when the audio session is taken away (e.g. the
12
+ * device is locked or another app starts playing audio).
13
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/BaseAudioContext/state#resuming_interrupted_play_states_in_ios_safari
14
+ */
15
+ export type InterruptibleAudioContextState = AudioContextState | "interrupted";
16
+
17
+ /**
18
+ * @internal
19
+ * Pure policy deciding whether `AudioContext.resume()` should be attempted right now.
20
+ *
21
+ * The context needs resuming when it is `"suspended"` or (on iOS) `"interrupted"`.
22
+ * But resuming is only allowed while the document is **visible**: on iOS Safari,
23
+ * calling `resume()` while the page is backgrounded or the device is locked fails
24
+ * with `InvalidStateError: Failed to start the audio device`, because the audio
25
+ * hardware cannot be started in the background. In that case the resume must be
26
+ * deferred until the page becomes visible again.
27
+ *
28
+ * @param state The current (possibly iOS-interrupted) audio context state.
29
+ * @param visibility The current `document.visibilityState`.
30
+ * @returns `true` if `resume()` should be called now, `false` if it should be deferred.
31
+ */
32
+ export function shouldResumeAudioContext(
33
+ state: InterruptibleAudioContextState,
34
+ visibility: DocumentVisibilityState,
35
+ ): boolean {
36
+ // Resuming while hidden throws InvalidStateError on iOS — defer until visible.
37
+ if (visibility !== "visible") return false;
38
+ return state === "suspended" || state === "interrupted";
39
+ }
40
+
41
+ /** Resume the shared AudioContext if it is suspended/interrupted and the page is visible. */
42
+ function recoverAudioContext() {
43
+ const ctx = AudioContext.getContext();
44
+ const state = ctx.state as InterruptibleAudioContextState;
45
+ const visibility = typeof document !== "undefined" ? document.visibilityState : "visible";
46
+ if (!shouldResumeAudioContext(state, visibility)) return;
47
+ ctx.resume()
48
+ .then(() => { if (debug) console.log("AudioContext resumed successfully"); })
49
+ .catch(e => { if (debug) console.warn("Failed to resume AudioContext: " + e); });
50
+ }
51
+
52
+ /**
53
+ * @internal
54
+ * Keep the shared AudioContext alive across suspends/interruptions (tab switch, device lock, audio
55
+ * session interruption). On returning to the foreground we simply resume() the EXISTING context —
56
+ * the same approach three.js uses, which recovers cleanly because AudioSource plays clips through a
57
+ * MediaElementAudioSourceNode (an <audio> element), whose media session iOS keeps alive across a
58
+ * lock. We deliberately do NOT close/recreate the context: on-device testing showed that makes iOS
59
+ * worse (a recreated context's output stays dead) and it is unnecessary.
60
+ */
8
61
  export function ensureAudioContextIsResumed() {
9
62
  Application.registerWaitForInteraction(() => {
10
- // this is a fix for https://github.com/mrdoob/three.js/issues/27779 & https://linear.app/needle/issue/NE-4257
63
+ // https://github.com/mrdoob/three.js/issues/27779 & https://linear.app/needle/issue/NE-4257
11
64
  const ctx = AudioContext.getContext();
12
- ctx.addEventListener("statechange", () => {
13
- setTimeout(() => {
14
- // on iOS the audiocontext can be interrupted: https://developer.mozilla.org/en-US/docs/Web/API/BaseAudioContext/state#resuming_interrupted_play_states_in_ios_safari
15
- const state = ctx.state as AudioContextState | "interrupted";
16
- if (state === "suspended" || state === "interrupted") {
17
- ctx.resume()
18
- .then(() => { console.log("AudioContext resumed successfully"); })
19
- .catch((e) => { console.log("Failed to resume AudioContext: " + e); });
20
- }
21
- }, 500);
22
- });
65
+ ctx.addEventListener("statechange", () => setTimeout(recoverAudioContext, 300));
66
+
67
+ // iOS frequently rejects the first resume() right after foregrounding with InvalidStateError,
68
+ // then accepts one a few hundred ms later with no follow-up statechange — so retry with a
69
+ // short backoff. Each attempt is a no-op once running.
70
+ if (typeof document !== "undefined") {
71
+ document.addEventListener("visibilitychange", () => {
72
+ if (document.visibilityState !== "visible") return;
73
+ for (const delay of [0, 250, 600, 1200, 2000]) setTimeout(recoverAudioContext, delay);
74
+ });
75
+ }
23
76
  });
24
77
  }
25
78
 
@@ -746,4 +746,4 @@ function resolveAndBindSkinnedMeshBones(
746
746
 
747
747
  // }
748
748
 
749
- // Reference resolution is now handled by the unified module: engine_instantiate_resolve.ts
749
+ // Reference resolution is now handled by the unified module: engine_instantiate_resolve.ts
@@ -8,7 +8,7 @@ import { initBuiltinTypes } from "./codegen/register_types.js";
8
8
  import { initSpatialConsole } from "./debug/debug_spatial_console.js";
9
9
  import { initAddressableSerializers } from "./engine_addressables.js";
10
10
  import { ensureAudioContextIsResumed } from "./engine_audio.js";
11
- import { $cLf } from "./engine_license.js";
11
+ import { $lySygz } from "./engine_license.js";
12
12
  import { initNeedleLoader } from "./engine_loaders.js";
13
13
  import { initPhysics } from "./engine_physics_rapier.js";
14
14
  import { initBuiltinSerializers } from "./engine_serialization_builtin_serializer.js";
@@ -59,7 +59,7 @@ export function initEngine() {
59
59
  initPhysics();
60
60
  initXR();
61
61
  initSpatialConsole();
62
- $cLf();
62
+ $lySygz();
63
63
 
64
64
  // Must be last. customElements.define() upgrades existing <needle-engine>
65
65
  // elements synchronously, which can trigger context.create() and dispatch
@@ -9,19 +9,19 @@ import { getParam } from "./engine_utils.js";
9
9
 
10
10
  const debug = getParam("__debuglic__");
11
11
 
12
- const _$zbtJJVy: ((result: boolean) => void)[] = [];
12
+ const _jmbVzxw: ((result: boolean) => void)[] = [];
13
13
 
14
14
  // DO NOT EDIT MANUALLY
15
- let miOmwPp: string = "";
15
+ let $rJboqN: string = "";
16
16
  // eslint-disable-next-line prefer-const
17
- let _$ALiqbpo: string = "";
17
+ let _$RRhFut: string = "";
18
18
 
19
19
  if (debug) {
20
- console.log("License Type: " + miOmwPp);
21
- if (_$ALiqbpo) {
22
- console.log("License JWT: " + _$ALiqbpo);
20
+ console.log("License Type: " + $rJboqN);
21
+ if (_$RRhFut) {
22
+ console.log("License JWT: " + _$RRhFut);
23
23
  try {
24
- const payload = JSON.parse(atob(_$ALiqbpo.split(".")[1].replace(/-/g, '+').replace(/_/g, '/')));
24
+ const payload = JSON.parse(atob(_$RRhFut.split(".")[1].replace(/-/g, '+').replace(/_/g, '/')));
25
25
  console.log("License JWT payload:", payload);
26
26
  }
27
27
  catch { console.log("License JWT payload: (failed to decode)"); }
@@ -32,8 +32,8 @@ if (debug) {
32
32
  }
33
33
 
34
34
  /** @internal */
35
- export function _$QyV() {
36
- switch (miOmwPp) {
35
+ export function _XtEQ() {
36
+ switch ($rJboqN) {
37
37
  case "pro":
38
38
  case "enterprise":
39
39
  return true;
@@ -42,8 +42,8 @@ export function _$QyV() {
42
42
  }
43
43
 
44
44
  /** @internal */
45
- export function __Carg() {
46
- switch (miOmwPp) {
45
+ export function _$qWUhYZU() {
46
+ switch ($rJboqN) {
47
47
  case "indie":
48
48
  return true;
49
49
  }
@@ -51,8 +51,8 @@ export function __Carg() {
51
51
  }
52
52
 
53
53
  /** @internal */
54
- export function NVzibd() {
55
- switch (miOmwPp) {
54
+ export function _BxFwJ() {
55
+ switch ($rJboqN) {
56
56
  case "edu":
57
57
  return true;
58
58
  }
@@ -60,20 +60,20 @@ export function NVzibd() {
60
60
  }
61
61
 
62
62
  /** @internal */
63
- export function bLW() {
64
- return _$QyV() || __Carg() || NVzibd();
63
+ export function $fwGZrGe() {
64
+ return _XtEQ() || _$qWUhYZU() || _BxFwJ();
65
65
  }
66
66
 
67
67
 
68
68
  /** @internal */
69
- export function $bxGE(cb: (result: boolean) => void) {
70
- if (_$QyV() || __Carg() || NVzibd())
69
+ export function $BWnhWzrW(cb: (result: boolean) => void) {
70
+ if (_XtEQ() || _$qWUhYZU() || _BxFwJ())
71
71
  return cb(true);
72
- _$zbtJJVy.push(cb);
72
+ _jmbVzxw.push(cb);
73
73
  }
74
74
 
75
- function __JogRhlxZ(result: boolean) {
76
- for (const cb of _$zbtJJVy) {
75
+ function $bjsviJ(result: boolean) {
76
+ for (const cb of _jmbVzxw) {
77
77
  try {
78
78
  cb(result);
79
79
  }
@@ -88,7 +88,7 @@ function __JogRhlxZ(result: boolean) {
88
88
 
89
89
  // ECDSA P-256 public key for verifying license JWTs (verification-only, safe to ship)
90
90
  /* eslint-disable no-secrets/no-secrets -- public key, not a secret */
91
- const CcFRcciC = {
91
+ const _$cibPFcX = {
92
92
  kty: "EC",
93
93
  crv: "P-256",
94
94
  x: "A34nyKMjhQYVgzeE4tyLUYdx34TAKogDa7v7PRaO9Lg",
@@ -112,7 +112,7 @@ function base64urlDecode(str: string): Uint8Array {
112
112
  * Verify a JWT license token and return the `type` claim if valid.
113
113
  * Returns null if the JWT is missing, malformed, or has an invalid signature.
114
114
  */
115
- async function ZhJ(jwt: string): Promise<string | null> {
115
+ async function $JcYsyq(jwt: string): Promise<string | null> {
116
116
  if (!jwt) return null;
117
117
  try {
118
118
  const parts = jwt.split(".");
@@ -122,7 +122,7 @@ async function ZhJ(jwt: string): Promise<string | null> {
122
122
 
123
123
  const key = await crypto.subtle.importKey(
124
124
  "jwk",
125
- CcFRcciC,
125
+ _$cibPFcX,
126
126
  { name: "ECDSA", namedCurve: "P-256" },
127
127
  false,
128
128
  ["verify"]
@@ -182,24 +182,24 @@ async function ZhJ(jwt: string): Promise<string | null> {
182
182
  }
183
183
 
184
184
  /** Verify the injected JWT and update the license type if valid.
185
- * The engine ONLY trusts the JWT — the plain miOmwPp string is ignored. */
185
+ * The engine ONLY trusts the JWT — the plain $rJboqN string is ignored. */
186
186
  let _jwtVerificationPromise: Promise<void> | undefined = undefined;
187
- async function $ESIFccD(): Promise<void> {
188
- // NOTE: do NOT add an `if (!_$ALiqbpo) return` guard here.
187
+ async function _$qeTBLq(): Promise<void> {
188
+ // NOTE: do NOT add an `if (!_$RRhFut) return` guard here.
189
189
  // esbuild (Vite dependency pre-bundling) would see the variable as constant ""
190
190
  // and tree-shake the entire JWT verification code path.
191
- const jwt = _$ALiqbpo;
191
+ const jwt = _$RRhFut;
192
192
  // Clear after reading — this reassignment also prevents esbuild from
193
193
  // constant-folding the variable (it now has multiple assignment sites).
194
- _$ALiqbpo = "";
195
- const verifiedType = await ZhJ(jwt);
194
+ _$RRhFut = "";
195
+ const verifiedType = await $JcYsyq(jwt);
196
196
  if (verifiedType) {
197
- miOmwPp = verifiedType;
197
+ $rJboqN = verifiedType;
198
198
  if (debug) console.log("License type set from verified JWT: " + verifiedType);
199
- __JogRhlxZ(bLW());
199
+ $bjsviJ($fwGZrGe());
200
200
  }
201
201
  else {
202
- miOmwPp = "basic";
202
+ $rJboqN = "basic";
203
203
  if (debug && jwt) console.warn("JWT verification failed — license reset to basic");
204
204
  }
205
205
  }
@@ -271,7 +271,7 @@ export namespace Telemetry {
271
271
 
272
272
  const attribute = domElement?.getAttribute("no-telemetry");
273
273
  if (attribute === "" || attribute === "true" || attribute === "1") {
274
- if (miOmwPp === "pro" || miOmwPp === "enterprise") {
274
+ if ($rJboqN === "pro" || $rJboqN === "enterprise") {
275
275
  if (debug) console.debug("Telemetry is disabled via no-telemetry attribute");
276
276
  return false;
277
277
  }
@@ -378,32 +378,32 @@ export namespace Telemetry {
378
378
  }
379
379
 
380
380
 
381
- export function $cLf() {
381
+ export function $lySygz() {
382
382
 
383
- if(miOmwPp === "") miOmwPp = "basic";
383
+ if($rJboqN === "") $rJboqN = "basic";
384
384
 
385
385
  // Start JWT verification — must be here (not top-level) to avoid tree-shaking
386
- _jwtVerificationPromise = $ESIFccD();
386
+ _jwtVerificationPromise = _$qeTBLq();
387
387
 
388
388
  Telemetry.init();
389
389
  ContextRegistry.registerCallback(ContextEvent.ContextRegistered, evt => {
390
- _JTSOMz(evt.context);
391
- __rlGEVK(evt.context);
392
- setTimeout(() => __OmIq(evt.context), 2000);
390
+ __HewJRdQ(evt.context);
391
+ __jSWGURd(evt.context);
392
+ setTimeout(() => _$wmvwSlSB(evt.context), 2000);
393
393
  });
394
394
  }
395
395
 
396
- export let _MHgSj: Promise<void> | undefined = undefined;
396
+ export let ICD: Promise<void> | undefined = undefined;
397
397
  let applicationIsForbidden = false;
398
398
  let applicationForbiddenText = "";
399
- async function $pek() {
399
+ async function _EiMi() {
400
400
  // Only perform the runtime license check once
401
- if (_MHgSj) return _MHgSj;
401
+ if (ICD) return ICD;
402
402
  // Wait for JWT verification to complete first (if running)
403
403
  if (_jwtVerificationPromise) {
404
404
  await _jwtVerificationPromise;
405
405
  }
406
- if (miOmwPp === "basic") {
406
+ if ($rJboqN === "basic") {
407
407
  try {
408
408
  const licenseUrl = "https://needle.tools/api/v1/needle-engine/check?location=" + encodeURIComponent(window.location.href) + "&version=" + VERSION + "&generator=" + encodeURIComponent(GENERATOR);
409
409
  const res = await fetch(licenseUrl, {
@@ -415,29 +415,29 @@ async function $pek() {
415
415
  if (res?.status === 200) {
416
416
  applicationIsForbidden = false;
417
417
  if (debug) console.log("License check succeeded");
418
- miOmwPp = "pro";
419
- __JogRhlxZ(true);
418
+ $rJboqN = "pro";
419
+ $bjsviJ(true);
420
420
  }
421
421
  else if (res?.status === 403) {
422
- __JogRhlxZ(false);
422
+ $bjsviJ(false);
423
423
  applicationIsForbidden = true;
424
424
  applicationForbiddenText = await res.text();
425
425
  }
426
426
  else {
427
- __JogRhlxZ(false);
427
+ $bjsviJ(false);
428
428
  if (debug) console.log("License check failed with status " + res?.status);
429
429
  }
430
430
  }
431
431
  catch (err) {
432
- __JogRhlxZ(false);
432
+ $bjsviJ(false);
433
433
  if (debug) console.error("License check failed", err);
434
434
  }
435
435
  }
436
- else if (debug) console.log("Runtime license check is skipped because license is already applied as \"" + miOmwPp + "\"");
436
+ else if (debug) console.log("Runtime license check is skipped because license is already applied as \"" + $rJboqN + "\"");
437
437
  }
438
- _MHgSj = $pek();
438
+ ICD = _EiMi();
439
439
 
440
- async function __rlGEVK(ctx: IContext) {
440
+ async function __jSWGURd(ctx: IContext) {
441
441
  function createForbiddenElement() {
442
442
  const div = document.createElement("div");
443
443
  div.className = "needle-forbidden";
@@ -502,40 +502,40 @@ async function __rlGEVK(ctx: IContext) {
502
502
  }, 500)
503
503
  }
504
504
 
505
- async function _JTSOMz(ctx: IContext) {
505
+ async function __HewJRdQ(ctx: IContext) {
506
506
  try {
507
- if (!_$QyV() && !__Carg()) {
508
- return dtvHGhBg(ctx);
507
+ if (!_XtEQ() && !_$qWUhYZU()) {
508
+ return __ujmxoKP(ctx);
509
509
  }
510
510
  }
511
511
  catch (err) {
512
512
  if (debug) console.log("License check failed", err)
513
- return dtvHGhBg(ctx)
513
+ return __ujmxoKP(ctx)
514
514
  }
515
- if (debug) dtvHGhBg(ctx)
515
+ if (debug) __ujmxoKP(ctx)
516
516
  }
517
517
 
518
518
 
519
519
 
520
- async function dtvHGhBg(ctx: IContext) {
520
+ async function __ujmxoKP(ctx: IContext) {
521
521
 
522
522
  // if the engine loads faster than the license check, we need to capture the ready event here
523
523
  let isReady = false;
524
524
  ctx.domElement.addEventListener("ready", () => isReady = true);
525
525
 
526
- await _MHgSj?.catch(() => { });
526
+ await ICD?.catch(() => { });
527
527
 
528
528
 
529
- if (_$QyV() || __Carg()) return;
530
- if (bLW() === false) __PJeisZrd();
529
+ if (_XtEQ() || _$qWUhYZU()) return;
530
+ if ($fwGZrGe() === false) _rIQtT();
531
531
 
532
532
  // check if the engine is already ready (meaning has finished loading)
533
533
  if (isReady) {
534
- __CRBs(ctx);
534
+ ZzOMzy(ctx);
535
535
  }
536
536
  else {
537
537
  ctx.domElement.addEventListener("ready", () => {
538
- __CRBs(ctx);
538
+ ZzOMzy(ctx);
539
539
  });
540
540
  }
541
541
  }
@@ -543,7 +543,7 @@ async function dtvHGhBg(ctx: IContext) {
543
543
  // const licenseElementIdentifier = "needle-license-element";
544
544
  // const licenseDuration = 10000;
545
545
  // const licenseDelay = 1200;
546
- function __CRBs(ctx: IContext) {
546
+ function ZzOMzy(ctx: IContext) {
547
547
 
548
548
  const style = `
549
549
  position: relative;
@@ -556,7 +556,7 @@ function __CRBs(ctx: IContext) {
556
556
  padding: 10px;
557
557
  padding-left: 30px;
558
558
  `;
559
- if (miOmwPp === "edu") {
559
+ if ($rJboqN === "edu") {
560
560
  if (navigator.webdriver) {
561
561
  console.log("This project is supported by Needle for Education – https://needle.tools");
562
562
  }
@@ -613,7 +613,7 @@ function __CRBs(ctx: IContext) {
613
613
  }
614
614
  }, 1000);
615
615
 
616
- if (NVzibd()) {
616
+ if (_BxFwJ()) {
617
617
  const removeDelay = 20_000;
618
618
  setTimeout(() => {
619
619
  clearInterval(interval);
@@ -622,7 +622,7 @@ function __CRBs(ctx: IContext) {
622
622
  const intervalInMinutes = 5;
623
623
  setTimeout(() => {
624
624
  if (ctx.domElement.parentNode)
625
- __CRBs(ctx);
625
+ ZzOMzy(ctx);
626
626
  }, 1000 * 60 * intervalInMinutes)
627
627
  }, removeDelay);
628
628
  }
@@ -633,7 +633,7 @@ function __CRBs(ctx: IContext) {
633
633
  const base64Logo = "data:image/webp;base64,UklGRrABAABXRUJQVlA4WAoAAAAQAAAAHwAAHwAAQUxQSKEAAAARN6CmbSM4WR7vdARON11EBDq3fLiNbVtVzpMCPlKAEzsx0Y/x+Ovuv4dn0EFE/ydAvz6YggXzgh5sVgXM/zOC/4sii7qgGvB5N7hmuQYwkvazWAu1JPW41FXSHq6pnaQWvqYH18Fc0j1hO/BFTtIeSBlJi5w6qIIO7IOrwhFsB2Yxukif0FTRLpXswHR8MxbslKe9VZsn/Ub5C7YFOpqSTABWUDgg6AAAAFAGAJ0BKiAAIAA+7VyoTqmkpCI3+qgBMB2JbACdMt69DwMIQBLhkTO6XwY00UEDK6cNIDnuNibPf0EgAP7Y1myuiQHLDsF/0h5unrGh6WAbv7aegg2ZMd3uRKfT/3SJztcaujYfTvMXspfCTmYcoO6a+vhC3ss4M8uM58t4siiu59I4aOl59e9Sr6xoxYlHf2v+NnBNpJYeJf8jABQAId/PXuBkLEFkiCucgSGEcfhvajql/j3reCGl0M5/9gQWy7ayNPs+wlvIxFnNfSlfuND4CZOCyxOHhRqOmHN4ULHo3tCSrUNvgAA=";
634
634
 
635
635
  let lastLogTime = 0;
636
- async function __PJeisZrd(_logo?: string) {
636
+ async function _rIQtT(_logo?: string) {
637
637
  const now = Date.now();
638
638
  if (now - lastLogTime < 2000) return;
639
639
  lastLogTime = now;
@@ -666,7 +666,7 @@ async function __PJeisZrd(_logo?: string) {
666
666
  }
667
667
 
668
668
 
669
- async function __OmIq(context: IContext) {
669
+ async function _$wmvwSlSB(context: IContext) {
670
670
  // We can't send beacons from cross-origin isolated pages
671
671
  if (window.crossOriginIsolated) return;
672
672
 
@@ -681,7 +681,7 @@ async function __OmIq(context: IContext) {
681
681
 
682
682
  // current url without query parameters
683
683
  const currentUrl = window.location.href.split("?")[0];
684
- const license = miOmwPp;
684
+ const license = $rJboqN;
685
685
 
686
686
  const beaconData = {
687
687
  license,
@@ -247,9 +247,11 @@ class LowPassFilter {
247
247
  return this.y;
248
248
  }
249
249
 
250
- reset(value: number) {
251
- this.y = value;
252
- this.s = value;
250
+ reset(value?: number) {
251
+ // undefined clears the sample (y/s become null) so the next filter() call
252
+ // is treated as the first sample again, i.e. a passthrough.
253
+ this.y = value ?? null;
254
+ this.s = value ?? null;
253
255
  }
254
256
  }
255
257
 
@@ -358,7 +360,12 @@ export class OneEuroFilter {
358
360
  }
359
361
 
360
362
  reset(x?: number) {
361
- if (x != undefined) this.x.reset(x);
363
+ // Always clear the filtered sample (seeded with x when provided) AND the
364
+ // derivative filter, so the next filter() call is treated as the first
365
+ // sample again. Previously a bare reset() (no argument) left the old sample
366
+ // in place, so the next filter() kept smoothing against stale state.
367
+ this.x.reset(x);
368
+ this.dx.reset();
362
369
  this.x.alpha = this.alpha(this.minCutOff);
363
370
  this.dx.alpha = this.alpha(this.dCutOff);
364
371
  this.lasttime = null;
@@ -1,7 +1,7 @@
1
1
  import { FileLoader } from "three";
2
2
 
3
3
  import { showBalloonWarning } from "./debug/index.js";
4
- import { bLW } from "./engine_license.js";
4
+ import { $fwGZrGe } from "./engine_license.js";
5
5
  import { delay } from "./engine_utils.js";
6
6
  import { md5AsBytes, md5Hex, sha256Base64 } from "./engine_utils_hash.js";
7
7
 
@@ -45,7 +45,7 @@ export namespace BlobStorage {
45
45
  */
46
46
  export function canUpload(info: { filesize: number }) {
47
47
  const sizeInMB = info.filesize / 1024 / 1024;
48
- if (bLW()) {
48
+ if ($fwGZrGe()) {
49
49
  return sizeInMB < maxSizeInMB;
50
50
  }
51
51
  return sizeInMB < maxFreeSizeInMB;
@@ -100,7 +100,7 @@ export namespace BlobStorage {
100
100
  console.warn(`Your file is too large for uploading (${filesizeInMB.toFixed(1)}MB). Max allowed size is ${maxSizeInMB}MB`);
101
101
  return null;
102
102
  }
103
- else if (!bLW() && filesizeInMB > maxFreeSizeInMB) {
103
+ else if (!$fwGZrGe() && filesizeInMB > maxFreeSizeInMB) {
104
104
  if (opts?.silent !== true) showBalloonWarning(`File is too large for uploading. Please get a <a href=\"https://needle.tools/pricing\" target=\"_blank\">commercial license</a> to upload files larger than 5MB`);
105
105
  console.warn(`Your file is too large for uploading (${filesizeInMB.toFixed(1)}MB). Max size is 5MB for non-commercial users. Please get a commercial license at https://needle.tools/pricing for larger files (up to 50MB)`);
106
106
  return null;
@@ -54,13 +54,18 @@ const cache = new WeakMap<IContext, SceneData>();
54
54
  */
55
55
  function makeErrorProxy(message: string): object {
56
56
  const handler: ProxyHandler<object> = {
57
- get(_t, prop: string) {
57
+ get(_t, prop: string | symbol) {
58
58
  if (prop === "then") return undefined; // not a Promise
59
+ // Symbol-keyed access (Symbol.toPrimitive, Symbol.toStringTag, Symbol.iterator, …)
60
+ // must return undefined — returning another proxy causes a TypeError when JS tries
61
+ // to coerce the value (e.g. in template literals calling .toString()).
62
+ if (typeof prop === "symbol") return undefined;
59
63
  const fn = isDevEnvironment() ? console.error : console.warn;
60
64
  fn(`[SceneData] ${message}`);
61
65
  return makeErrorProxy(message);
62
66
  },
63
- set(_t, prop: string) {
67
+ set(_t, prop: string | symbol) {
68
+ if (typeof prop === "symbol") return true;
64
69
  const fn = isDevEnvironment() ? console.error : console.warn;
65
70
  fn(`[SceneData] ${message} (tried to set "${prop}")`);
66
71
  return true; // suppress TypeError
@@ -227,8 +227,13 @@ export function serializeObject(obj: ISerializable, context: SerializationContex
227
227
  // if the object bein serialized is some type of object check if we have special handling registered for it
228
228
  if (val !== undefined && val !== null && typeof val === "object") {
229
229
  // get type name
230
- // get registered serialization handler
231
- const ser = helper.getSerializerForConstructor(val);
230
+ // get registered serialization handler.
231
+ // Pass the constructor (not the instance): getSerializerForConstructor
232
+ // looks types up in a constructor-keyed map and walks the constructor's
233
+ // prototype chain — mirroring the deserialize path. Passing the instance
234
+ // here previously missed every lookup, so e.g. a Color was never converted
235
+ // to { r, g, b } and an Euler / Object3D reference was emitted raw.
236
+ const ser = helper.getSerializerForConstructor(val.constructor);
232
237
  if (ser) {
233
238
  // serialize data using that handler
234
239
  res[key] = ser.onSerialize(val, context);
@@ -62,11 +62,14 @@ export class Progress {
62
62
  * Progress.end("export-usdz");
63
63
  *
64
64
  * // Auto step with weights:
65
+ * // A numeric weight applies to the NEXT step — it sets how many steps the
66
+ * // following auto-step advances. The call it is passed on advances by the
67
+ * // previously-set weight (1 by default).
65
68
  * Progress.start("export-usdz", undefined, (progress) => console.log("Progress: " + progress));
66
- * Progress.report("export-usdz", { message: "Exporting objects", autoStep: true, totalSteps: 10 });
67
- * Progress.report("export-usdz", { message: "Exporting object 1", autoStep: 8 }); // will advance to 80% progress
68
- * Progress.report("export-usdz", "Exporting object 2"); // 90%
69
- * Progress.report("export-usdz", "Exporting object 3"); // 100%
69
+ * Progress.report("export-usdz", { message: "Exporting objects", autoStep: true, totalSteps: 10 }); // step 1/10 (10%)
70
+ * Progress.report("export-usdz", { message: "Heavy step", autoStep: 8 }); // step 2/10 (20%); weight 8 applies to the next step
71
+ * Progress.report("export-usdz", "Exporting object 2"); // advances by 8 -> step 10/10 (100%)
72
+ * Progress.report("export-usdz", "Exporting object 3"); // stays at 100%
70
73
  *
71
74
  * // Child scopes:
72
75
  * Progress.start("export-usdz", undefined, (progress) => console.log("Progress: " + progress));
@@ -103,8 +106,9 @@ export class Progress {
103
106
  * Progress.report("export-usdz", { totalSteps: 20, autoStep: true });
104
107
  * // show the progress as 50%
105
108
  * Progress.report("export-usdz", { progress: 0.5 });
106
- * // give this step a weight of 20, which changes how progress is calculated. Useful for steps that take longer and/or have child scopes.
107
- * Progress.report("export-usdz", { message. "Long process", autoStep: 20 });
109
+ * // give a weight of 20 to the NEXT step (useful for steps that take longer
110
+ * // and/or have child scopes); the following auto-step will advance by 20.
111
+ * Progress.report("export-usdz", { message: "Long process", autoStep: 20 });
108
112
  * // show the current step and total steps and implicitly calculate progress as 10%
109
113
  * Progress.report("export-usdz", { currentStep: 1, totalSteps: 10 });
110
114
  */
@@ -4,7 +4,7 @@ import { Quaternion, Vector2, Vector3, Vector4 } from "three";
4
4
 
5
5
  import { needleLogoOnlySVG } from "./assets/index.js";
6
6
  import { isDevEnvironment } from "./debug/debug.js";
7
- import { bLW } from "./engine_license.js";
7
+ import { $fwGZrGe } from "./engine_license.js";
8
8
  import { InternalAttributeUtils } from "./engine_utils_attributes.js";
9
9
  import type { NeedleEngineWebComponent } from "./webcomponents/needle-engine.js";
10
10
 
@@ -135,7 +135,7 @@ async function internalRenderQRCodeOverlays(canvas: HTMLCanvasElement, args: { s
135
135
  console.debug("[QR Code] No web component found")
136
136
  }
137
137
 
138
- const canUseCustomLogo = bLW();
138
+ const canUseCustomLogo = $fwGZrGe();
139
139
 
140
140
  // Query logo src from needle-engine attribute.
141
141
  // For any supported attribute it's possible to use "falsey" values (e.g. "0" or "false" to disable the logo in the QR code)
@@ -2,7 +2,7 @@ import { Mesh, Object3D, TextureLoader, Vector4 } from "three";
2
2
  import ThreeMeshUI from "three-mesh-ui";
3
3
 
4
4
  import { addNewComponent } from "../../engine_components.js";
5
- import { _$QyV } from "../../engine_license.js";
5
+ import { _XtEQ } from "../../engine_license.js";
6
6
  import { OneEuroFilterXYZ } from "../../engine_math.js";
7
7
  import type { Context } from "../../engine_setup.js";
8
8
  import { lookAtObject } from "../../engine_three_utils.js";
@@ -321,7 +321,7 @@ export class NeedleSpatialMenu {
321
321
  }
322
322
  if (this.menu) {
323
323
  const index = this.menu.children.indexOf(this._poweredByNeedleElement as any);
324
- if (!this._showNeedleLogo && _$QyV()) {
324
+ if (!this._showNeedleLogo && _XtEQ()) {
325
325
  if (index >= 0) {
326
326
  this._poweredByNeedleElement.removeFromParent();
327
327
  this.markDirty();