@needle-tools/engine 5.1.0-next.1780579670.dramatic-pierogi.b8fa941 → 5.1.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 (122) hide show
  1. package/CHANGELOG.md +119 -0
  2. package/components.needle.json +1 -1
  3. package/dist/gltf-progressive-BGQ-cjly.umd.cjs +10 -0
  4. package/dist/gltf-progressive-BvQHrERj.min.js +10 -0
  5. package/dist/{gltf-progressive-CH7aqHHr.js → gltf-progressive-CtYnVfLc.js} +420 -351
  6. package/dist/{materialx-B85WjP7E.min.js → materialx-B5gKeM65.min.js} +1 -1
  7. package/dist/{materialx-B47Bz-xs.umd.cjs → materialx-CFRcCYWy.umd.cjs} +1 -1
  8. package/dist/{materialx-jkHmVPez.js → materialx-DqGmteNO.js} +1 -1
  9. package/dist/{needle-engine.bundle-WQeWxmZD.js → needle-engine.bundle-5oIL49YP.js} +6826 -6685
  10. package/dist/{needle-engine.bundle-BSPk6RkD.min.js → needle-engine.bundle-DAAfj8Cs.min.js} +161 -149
  11. package/dist/{needle-engine.bundle-DCPHDPAd.umd.cjs → needle-engine.bundle-DvOrfXws.umd.cjs} +159 -147
  12. package/dist/needle-engine.d.ts +106 -44
  13. package/dist/needle-engine.js +610 -608
  14. package/dist/needle-engine.min.js +1 -1
  15. package/dist/needle-engine.umd.cjs +1 -1
  16. package/dist/three.js +3 -0
  17. package/dist/three.min.js +22 -22
  18. package/dist/three.umd.cjs +24 -24
  19. package/dist/{vendor-BQ2Vuntm.min.js → vendor-2--bD1c9.min.js} +19 -19
  20. package/dist/{vendor-BslSKZPo.js → vendor-BNtcIZkv.js} +1141 -1138
  21. package/dist/{vendor-1UvpPPSB.umd.cjs → vendor-vbe9sv1t.umd.cjs} +20 -20
  22. package/lib/engine/api.d.ts +1 -1
  23. package/lib/engine/api.js +1 -1
  24. package/lib/engine/api.js.map +1 -1
  25. package/lib/engine/engine_context.js +16 -3
  26. package/lib/engine/engine_context.js.map +1 -1
  27. package/lib/engine/engine_init.js +2 -2
  28. package/lib/engine/engine_init.js.map +1 -1
  29. package/lib/engine/engine_license.d.ts +7 -7
  30. package/lib/engine/engine_license.js +72 -72
  31. package/lib/engine/engine_license.js.map +1 -1
  32. package/lib/engine/engine_networking_blob.js +3 -3
  33. package/lib/engine/engine_networking_blob.js.map +1 -1
  34. package/lib/engine/engine_utils_qrcode.js +2 -2
  35. package/lib/engine/engine_utils_qrcode.js.map +1 -1
  36. package/lib/engine/postprocessing/postprocessing.d.ts +2 -1
  37. package/lib/engine/postprocessing/postprocessing.js +3 -2
  38. package/lib/engine/postprocessing/postprocessing.js.map +1 -1
  39. package/lib/engine/webcomponents/needle menu/needle-menu-spatial.js +2 -2
  40. package/lib/engine/webcomponents/needle menu/needle-menu.d.ts +2 -2
  41. package/lib/engine/webcomponents/needle menu/needle-menu.js +9 -9
  42. package/lib/engine/webcomponents/needle menu/needle-menu.js.map +1 -1
  43. package/lib/engine/webcomponents/needle-engine.js +2 -2
  44. package/lib/engine/webcomponents/needle-engine.js.map +1 -1
  45. package/lib/engine/webcomponents/needle-engine.loading.js +2 -2
  46. package/lib/engine/webcomponents/needle-engine.loading.js.map +1 -1
  47. package/lib/engine/xr/NeedleXRSession.d.ts +24 -0
  48. package/lib/engine/xr/NeedleXRSession.js +52 -7
  49. package/lib/engine/xr/NeedleXRSession.js.map +1 -1
  50. package/lib/engine/xr/TempXRContext.js +2 -2
  51. package/lib/engine/xr/TempXRContext.js.map +1 -1
  52. package/lib/engine/xr/init.js +2 -3
  53. package/lib/engine/xr/init.js.map +1 -1
  54. package/lib/engine-components/Component.js +14 -1
  55. package/lib/engine-components/Component.js.map +1 -1
  56. package/lib/engine-components/ContactShadows.d.ts +7 -2
  57. package/lib/engine-components/ContactShadows.js +65 -45
  58. package/lib/engine-components/ContactShadows.js.map +1 -1
  59. package/lib/engine-components/GroundProjection.d.ts +6 -0
  60. package/lib/engine-components/GroundProjection.js +69 -7
  61. package/lib/engine-components/GroundProjection.js.map +1 -1
  62. package/lib/engine-components/NeedleMenu.d.ts +1 -1
  63. package/lib/engine-components/NeedleMenu.js +5 -2
  64. package/lib/engine-components/NeedleMenu.js.map +1 -1
  65. package/lib/engine-components/SceneSwitcher.d.ts +2 -0
  66. package/lib/engine-components/SceneSwitcher.js +26 -14
  67. package/lib/engine-components/SceneSwitcher.js.map +1 -1
  68. package/lib/engine-components/SeeThrough.d.ts +39 -0
  69. package/lib/engine-components/SeeThrough.js +69 -1
  70. package/lib/engine-components/SeeThrough.js.map +1 -1
  71. package/lib/engine-components/Skybox.js +9 -0
  72. package/lib/engine-components/Skybox.js.map +1 -1
  73. package/lib/engine-components/api.d.ts +1 -0
  74. package/lib/engine-components/api.js +1 -0
  75. package/lib/engine-components/api.js.map +1 -1
  76. package/lib/engine-components/export/usdz/USDZExporter.js +4 -4
  77. package/lib/engine-components/export/usdz/USDZExporter.js.map +1 -1
  78. package/lib/engine-components/export/usdz/extensions/behavior/BehaviourComponents.js +9 -5
  79. package/lib/engine-components/export/usdz/extensions/behavior/BehaviourComponents.js.map +1 -1
  80. package/lib/engine-components/particlesystem/ParticleSystem.js +4 -0
  81. package/lib/engine-components/particlesystem/ParticleSystem.js.map +1 -1
  82. package/lib/engine-components/postprocessing/Volume.d.ts +1 -9
  83. package/lib/engine-components/postprocessing/Volume.js +2 -14
  84. package/lib/engine-components/postprocessing/Volume.js.map +1 -1
  85. package/lib/engine-components/webxr/WebXRImageTracking.js +3 -0
  86. package/lib/engine-components/webxr/WebXRImageTracking.js.map +1 -1
  87. package/package.json +4 -4
  88. package/plugins/common/license.js +4 -4
  89. package/plugins/types/userconfig.d.ts +10 -0
  90. package/plugins/vite/build-pipeline.d.ts +19 -0
  91. package/plugins/vite/build-pipeline.js +67 -5
  92. package/plugins/vite/license.js +4 -4
  93. package/src/engine/api.ts +1 -1
  94. package/src/engine/engine_context.ts +15 -2
  95. package/src/engine/engine_gameobject.ts +1 -1
  96. package/src/engine/engine_init.ts +2 -2
  97. package/src/engine/engine_license.ts +69 -69
  98. package/src/engine/engine_networking_blob.ts +3 -3
  99. package/src/engine/engine_utils_qrcode.ts +2 -2
  100. package/src/engine/postprocessing/postprocessing.ts +3 -2
  101. package/src/engine/webcomponents/needle menu/needle-menu-spatial.ts +2 -2
  102. package/src/engine/webcomponents/needle menu/needle-menu.ts +10 -10
  103. package/src/engine/webcomponents/needle-engine.loading.ts +6 -6
  104. package/src/engine/webcomponents/needle-engine.ts +2 -2
  105. package/src/engine/xr/NeedleXRSession.ts +53 -7
  106. package/src/engine/xr/TempXRContext.ts +2 -2
  107. package/src/engine/xr/init.ts +2 -3
  108. package/src/engine-components/Component.ts +13 -1
  109. package/src/engine-components/ContactShadows.ts +75 -49
  110. package/src/engine-components/GroundProjection.ts +71 -7
  111. package/src/engine-components/NeedleMenu.ts +6 -3
  112. package/src/engine-components/SceneSwitcher.ts +23 -13
  113. package/src/engine-components/SeeThrough.ts +75 -1
  114. package/src/engine-components/Skybox.ts +8 -0
  115. package/src/engine-components/api.ts +1 -0
  116. package/src/engine-components/export/usdz/USDZExporter.ts +4 -4
  117. package/src/engine-components/export/usdz/extensions/behavior/BehaviourComponents.ts +12 -5
  118. package/src/engine-components/particlesystem/ParticleSystem.ts +4 -0
  119. package/src/engine-components/postprocessing/Volume.ts +2 -13
  120. package/src/engine-components/webxr/WebXRImageTracking.ts +1 -0
  121. package/dist/gltf-progressive-9tlS5iCx.umd.cjs +0 -10
  122. package/dist/gltf-progressive-CVg80COO.min.js +0 -10
@@ -9,19 +9,19 @@ import { getParam } from "./engine_utils.js";
9
9
 
10
10
  const debug = getParam("__debuglic__");
11
11
 
12
- const __rcJRW: ((result: boolean) => void)[] = [];
12
+ const __HMYWc: ((result: boolean) => void)[] = [];
13
13
 
14
14
  // DO NOT EDIT MANUALLY
15
- let sFwQAmo: string = "";
15
+ let _LquBhwVh: string = "";
16
16
  // eslint-disable-next-line prefer-const
17
- let jEi: string = "";
17
+ let BEvs: string = "";
18
18
 
19
19
  if (debug) {
20
- console.log("License Type: " + sFwQAmo);
21
- if (jEi) {
22
- console.log("License JWT: " + jEi);
20
+ console.log("License Type: " + _LquBhwVh);
21
+ if (BEvs) {
22
+ console.log("License JWT: " + BEvs);
23
23
  try {
24
- const payload = JSON.parse(atob(jEi.split(".")[1].replace(/-/g, '+').replace(/_/g, '/')));
24
+ const payload = JSON.parse(atob(BEvs.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 _$zFmYj() {
36
- switch (sFwQAmo) {
35
+ export function rXELCf() {
36
+ switch (_LquBhwVh) {
37
37
  case "pro":
38
38
  case "enterprise":
39
39
  return true;
@@ -42,8 +42,8 @@ export function _$zFmYj() {
42
42
  }
43
43
 
44
44
  /** @internal */
45
- export function _$DeHzisx() {
46
- switch (sFwQAmo) {
45
+ export function $RHhhju() {
46
+ switch (_LquBhwVh) {
47
47
  case "indie":
48
48
  return true;
49
49
  }
@@ -51,8 +51,8 @@ export function _$DeHzisx() {
51
51
  }
52
52
 
53
53
  /** @internal */
54
- export function $nFnGL() {
55
- switch (sFwQAmo) {
54
+ export function FeDU() {
55
+ switch (_LquBhwVh) {
56
56
  case "edu":
57
57
  return true;
58
58
  }
@@ -60,20 +60,20 @@ export function $nFnGL() {
60
60
  }
61
61
 
62
62
  /** @internal */
63
- export function __EOmPHdLs() {
64
- return _$zFmYj() || _$DeHzisx() || $nFnGL();
63
+ export function $iujBhf() {
64
+ return rXELCf() || $RHhhju() || FeDU();
65
65
  }
66
66
 
67
67
 
68
68
  /** @internal */
69
- export function __scPCAmj(cb: (result: boolean) => void) {
70
- if (_$zFmYj() || _$DeHzisx() || $nFnGL())
69
+ export function _$GTZ(cb: (result: boolean) => void) {
70
+ if (rXELCf() || $RHhhju() || FeDU())
71
71
  return cb(true);
72
- __rcJRW.push(cb);
72
+ __HMYWc.push(cb);
73
73
  }
74
74
 
75
- function _hpBlmJnZ(result: boolean) {
76
- for (const cb of __rcJRW) {
75
+ function _zbDz(result: boolean) {
76
+ for (const cb of __HMYWc) {
77
77
  try {
78
78
  cb(result);
79
79
  }
@@ -88,7 +88,7 @@ function _hpBlmJnZ(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 __FmduHvp = {
91
+ const _FzlE = {
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 __ChbkwvSj(jwt: string): Promise<string | null> {
115
+ async function _$jmD(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 __ChbkwvSj(jwt: string): Promise<string | null> {
122
122
 
123
123
  const key = await crypto.subtle.importKey(
124
124
  "jwk",
125
- __FmduHvp,
125
+ _FzlE,
126
126
  { name: "ECDSA", namedCurve: "P-256" },
127
127
  false,
128
128
  ["verify"]
@@ -182,24 +182,24 @@ async function __ChbkwvSj(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 sFwQAmo string is ignored. */
185
+ * The engine ONLY trusts the JWT — the plain _LquBhwVh string is ignored. */
186
186
  let _jwtVerificationPromise: Promise<void> | undefined = undefined;
187
- async function _$mNAO(): Promise<void> {
188
- // NOTE: do NOT add an `if (!jEi) return` guard here.
187
+ async function _gclCQI(): Promise<void> {
188
+ // NOTE: do NOT add an `if (!BEvs) 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 = jEi;
191
+ const jwt = BEvs;
192
192
  // Clear after reading — this reassignment also prevents esbuild from
193
193
  // constant-folding the variable (it now has multiple assignment sites).
194
- jEi = "";
195
- const verifiedType = await __ChbkwvSj(jwt);
194
+ BEvs = "";
195
+ const verifiedType = await _$jmD(jwt);
196
196
  if (verifiedType) {
197
- sFwQAmo = verifiedType;
197
+ _LquBhwVh = verifiedType;
198
198
  if (debug) console.log("License type set from verified JWT: " + verifiedType);
199
- _hpBlmJnZ(__EOmPHdLs());
199
+ _zbDz($iujBhf());
200
200
  }
201
201
  else {
202
- sFwQAmo = "basic";
202
+ _LquBhwVh = "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 (sFwQAmo === "pro" || sFwQAmo === "enterprise") {
274
+ if (_LquBhwVh === "pro" || _LquBhwVh === "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 _$sRDk() {
381
+ export function __tBFssqnm() {
382
382
 
383
- if(sFwQAmo === "") sFwQAmo = "basic";
383
+ if(_LquBhwVh === "") _LquBhwVh = "basic";
384
384
 
385
385
  // Start JWT verification — must be here (not top-level) to avoid tree-shaking
386
- _jwtVerificationPromise = _$mNAO();
386
+ _jwtVerificationPromise = _gclCQI();
387
387
 
388
388
  Telemetry.init();
389
389
  ContextRegistry.registerCallback(ContextEvent.ContextRegistered, evt => {
390
- $jdMSrl(evt.context);
391
- nJF(evt.context);
392
- setTimeout(() => _fqbFnUXZ(evt.context), 2000);
390
+ IiDral(evt.context);
391
+ _ZrecT(evt.context);
392
+ setTimeout(() => bdYvL(evt.context), 2000);
393
393
  });
394
394
  }
395
395
 
396
- export let MYnb: Promise<void> | undefined = undefined;
396
+ export let $rKLNDMS: Promise<void> | undefined = undefined;
397
397
  let applicationIsForbidden = false;
398
398
  let applicationForbiddenText = "";
399
- async function GNE() {
399
+ async function _$aKpvmr() {
400
400
  // Only perform the runtime license check once
401
- if (MYnb) return MYnb;
401
+ if ($rKLNDMS) return $rKLNDMS;
402
402
  // Wait for JWT verification to complete first (if running)
403
403
  if (_jwtVerificationPromise) {
404
404
  await _jwtVerificationPromise;
405
405
  }
406
- if (sFwQAmo === "basic") {
406
+ if (_LquBhwVh === "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 GNE() {
415
415
  if (res?.status === 200) {
416
416
  applicationIsForbidden = false;
417
417
  if (debug) console.log("License check succeeded");
418
- sFwQAmo = "pro";
419
- _hpBlmJnZ(true);
418
+ _LquBhwVh = "pro";
419
+ _zbDz(true);
420
420
  }
421
421
  else if (res?.status === 403) {
422
- _hpBlmJnZ(false);
422
+ _zbDz(false);
423
423
  applicationIsForbidden = true;
424
424
  applicationForbiddenText = await res.text();
425
425
  }
426
426
  else {
427
- _hpBlmJnZ(false);
427
+ _zbDz(false);
428
428
  if (debug) console.log("License check failed with status " + res?.status);
429
429
  }
430
430
  }
431
431
  catch (err) {
432
- _hpBlmJnZ(false);
432
+ _zbDz(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 \"" + sFwQAmo + "\"");
436
+ else if (debug) console.log("Runtime license check is skipped because license is already applied as \"" + _LquBhwVh + "\"");
437
437
  }
438
- MYnb = GNE();
438
+ $rKLNDMS = _$aKpvmr();
439
439
 
440
- async function nJF(ctx: IContext) {
440
+ async function _ZrecT(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 nJF(ctx: IContext) {
502
502
  }, 500)
503
503
  }
504
504
 
505
- async function $jdMSrl(ctx: IContext) {
505
+ async function IiDral(ctx: IContext) {
506
506
  try {
507
- if (!_$zFmYj() && !_$DeHzisx()) {
508
- return __hjIiBVlU(ctx);
507
+ if (!rXELCf() && !$RHhhju()) {
508
+ return __CidXaFe(ctx);
509
509
  }
510
510
  }
511
511
  catch (err) {
512
512
  if (debug) console.log("License check failed", err)
513
- return __hjIiBVlU(ctx)
513
+ return __CidXaFe(ctx)
514
514
  }
515
- if (debug) __hjIiBVlU(ctx)
515
+ if (debug) __CidXaFe(ctx)
516
516
  }
517
517
 
518
518
 
519
519
 
520
- async function __hjIiBVlU(ctx: IContext) {
520
+ async function __CidXaFe(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 MYnb?.catch(() => { });
526
+ await $rKLNDMS?.catch(() => { });
527
527
 
528
528
 
529
- if (_$zFmYj() || _$DeHzisx()) return;
530
- if (__EOmPHdLs() === false) __IemeBi();
529
+ if (rXELCf() || $RHhhju()) return;
530
+ if ($iujBhf() === false) _$vPpCic();
531
531
 
532
532
  // check if the engine is already ready (meaning has finished loading)
533
533
  if (isReady) {
534
- wQoTi(ctx);
534
+ _UnZAOAPG(ctx);
535
535
  }
536
536
  else {
537
537
  ctx.domElement.addEventListener("ready", () => {
538
- wQoTi(ctx);
538
+ _UnZAOAPG(ctx);
539
539
  });
540
540
  }
541
541
  }
@@ -543,7 +543,7 @@ async function __hjIiBVlU(ctx: IContext) {
543
543
  // const licenseElementIdentifier = "needle-license-element";
544
544
  // const licenseDuration = 10000;
545
545
  // const licenseDelay = 1200;
546
- function wQoTi(ctx: IContext) {
546
+ function _UnZAOAPG(ctx: IContext) {
547
547
 
548
548
  const style = `
549
549
  position: relative;
@@ -556,7 +556,7 @@ function wQoTi(ctx: IContext) {
556
556
  padding: 10px;
557
557
  padding-left: 30px;
558
558
  `;
559
- if (sFwQAmo === "edu") {
559
+ if (_LquBhwVh === "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 wQoTi(ctx: IContext) {
613
613
  }
614
614
  }, 1000);
615
615
 
616
- if ($nFnGL()) {
616
+ if (FeDU()) {
617
617
  const removeDelay = 20_000;
618
618
  setTimeout(() => {
619
619
  clearInterval(interval);
@@ -622,7 +622,7 @@ function wQoTi(ctx: IContext) {
622
622
  const intervalInMinutes = 5;
623
623
  setTimeout(() => {
624
624
  if (ctx.domElement.parentNode)
625
- wQoTi(ctx);
625
+ _UnZAOAPG(ctx);
626
626
  }, 1000 * 60 * intervalInMinutes)
627
627
  }, removeDelay);
628
628
  }
@@ -633,7 +633,7 @@ function wQoTi(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 __IemeBi(_logo?: string) {
636
+ async function _$vPpCic(_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 __IemeBi(_logo?: string) {
666
666
  }
667
667
 
668
668
 
669
- async function _fqbFnUXZ(context: IContext) {
669
+ async function bdYvL(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 _fqbFnUXZ(context: IContext) {
681
681
 
682
682
  // current url without query parameters
683
683
  const currentUrl = window.location.href.split("?")[0];
684
- const license = sFwQAmo;
684
+ const license = _LquBhwVh;
685
685
 
686
686
  const beaconData = {
687
687
  license,
@@ -1,7 +1,7 @@
1
1
  import { FileLoader } from "three";
2
2
 
3
3
  import { showBalloonWarning } from "./debug/index.js";
4
- import { __EOmPHdLs } from "./engine_license.js";
4
+ import { $iujBhf } 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 (__EOmPHdLs()) {
48
+ if ($iujBhf()) {
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 (!__EOmPHdLs() && filesizeInMB > maxFreeSizeInMB) {
103
+ else if (!$iujBhf() && 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;
@@ -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 { __EOmPHdLs } from "./engine_license.js";
7
+ import { $iujBhf } 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 = __EOmPHdLs();
138
+ const canUseCustomLogo = $iujBhf();
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)
@@ -62,9 +62,10 @@ export class PostProcessing {
62
62
 
63
63
  /** When enabled, the device pixel ratio will be gradually reduced when FPS is low
64
64
  * and restored when performance recovers.
65
- * @default true
65
+ * @experimental
66
+ * @default false
66
67
  */
67
- adaptiveResolution: boolean = true;
68
+ adaptiveResolution: boolean = false;
68
69
 
69
70
  constructor(context: Context) {
70
71
  this._context = context;
@@ -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 { _$zFmYj } from "../../engine_license.js";
5
+ import { $iujBhf } 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 && _$zFmYj()) {
324
+ if (!this._showNeedleLogo && $iujBhf()) {
325
325
  if (index >= 0) {
326
326
  this._poweredByNeedleElement.removeFromParent();
327
327
  this.markDirty();
@@ -1,6 +1,6 @@
1
1
  import { showBalloonMessage } from "../../debug/debug.js";
2
2
  import type { Context } from "../../engine_context.js";
3
- import { __EOmPHdLs, __scPCAmj, Telemetry } from "../../engine_license.js";
3
+ import { $iujBhf, _$GTZ, Telemetry } from "../../engine_license.js";
4
4
  import { isLocalNetwork } from "../../engine_networking_utils.js";
5
5
  import { HTMLElementBase } from "../../engine_ssr.js";
6
6
  import { DeviceUtilities, getParam } from "../../engine_utils.js";
@@ -208,14 +208,14 @@ export class NeedleMenu {
208
208
  }
209
209
 
210
210
  /**
211
- * Call to show or hide the menu.
212
- * NOTE: Hiding the menu is a PRO feature and requires a needle engine license. Hiding the menu will not work in production without a license.
211
+ * Call to show or hide the menu.
212
+ * NOTE: Hiding the menu requires a commercial license. Hiding the menu will not work in production without a license.
213
213
  */
214
214
  setVisible(visible: boolean) {
215
215
  this._menu.setVisible(visible);
216
216
  }
217
217
 
218
- /** When set to false, the Needle Engine logo will be hidden. Hiding the logo requires a needle engine license */
218
+ /** When set to false, the Needle Engine logo will be hidden. Hiding the logo requires a commercial license */
219
219
  showNeedleLogo(visible: boolean) {
220
220
  this._menu.showNeedleLogo(visible);
221
221
  this._spatialMenu?.showNeedleLogo(visible);
@@ -778,8 +778,8 @@ export class NeedleMenuElement extends HTMLElementBase {
778
778
  try {
779
779
  // if the user has a license then we CAN hide the needle logo
780
780
  // calling this method immediately will cause an issue with vite bundling tho
781
- window.requestAnimationFrame(() => __scPCAmj(res => {
782
- if (res == true && __EOmPHdLs() && !debugNonCommercial) {
781
+ window.requestAnimationFrame(() => _$GTZ(res => {
782
+ if (res == true && $iujBhf() && !debugNonCommercial) {
783
783
  let visible = this._userRequestedLogoVisible;
784
784
  if (visible === undefined) visible = false;
785
785
  this.___onSetLogoVisible(visible);
@@ -830,7 +830,7 @@ export class NeedleMenuElement extends HTMLElementBase {
830
830
  // ensure the menu is not hidden or removed
831
831
  const requiredParent = this?.parentNode;
832
832
  if (this.style.display != "flex" || this.style.visibility != "visible" || this.style.opacity != "1" || requiredParent != this._domElement?.shadowRoot) {
833
- if (!__EOmPHdLs()) {
833
+ if (!$iujBhf()) {
834
834
  const change = changeEventCounter++;
835
835
  // if a user doesn't have a local pro license *but* for development the menu is hidden then we show a warning
836
836
  if (isLocalNetwork() && this._userRequestedMenuVisible === false) {
@@ -841,7 +841,7 @@ export class NeedleMenuElement extends HTMLElementBase {
841
841
  }
842
842
  // warn only once
843
843
  if (change === 1) {
844
- console.warn(`Needle Menu Warning: You need a PRO license to hide the Needle Engine menu → The menu will be visible in your deployed website if you don't have a PRO license. See https://needle.tools/pricing for details.`);
844
+ console.warn(`Needle Menu Warning: You need a commercial license to hide the Needle Engine menu → The menu will be visible in your deployed website without one. See https://needle.tools/pricing for details.`);
845
845
  }
846
846
  }
847
847
  else {
@@ -918,8 +918,8 @@ export class NeedleMenuElement extends HTMLElementBase {
918
918
  showNeedleLogo(visible: boolean) {
919
919
  this._userRequestedLogoVisible = visible;
920
920
  if (!visible) {
921
- if (!__EOmPHdLs() || debugNonCommercial) {
922
- console.warn("[Needle Engine] You need a PRO license to hide the Needle Engine logo in production.");
921
+ if (!$iujBhf() || debugNonCommercial) {
922
+ console.warn("[Needle Engine] You need a commercial license to hide the Needle Engine logo in production.");
923
923
  const localNetwork = isLocalNetwork()
924
924
  if (!localNetwork) return;
925
925
  }
@@ -1,6 +1,6 @@
1
1
  import { needleLogoOnlySVG } from "../assets/index.js"
2
2
  import { isDevEnvironment, showBalloonWarning } from "../debug/index.js";
3
- import { __EOmPHdLs, _$zFmYj, MYnb } from "../engine_license.js";
3
+ import { $iujBhf, rXELCf, $rKLNDMS } from "../engine_license.js";
4
4
  import { Mathf } from "../engine_math.js";
5
5
  import { LoadingProgressArgs } from "../engine_setup.js";
6
6
  import { getParam } from "../engine_utils.js";
@@ -205,7 +205,7 @@ export class EngineLoadingView implements ILoadingViewHandler {
205
205
  }
206
206
 
207
207
 
208
- const hasLicense = _$zFmYj();
208
+ const hasLicense = rXELCf();
209
209
  if (!existing) {
210
210
  this._loadingElement.style.position = "absolute";
211
211
  this._loadingElement.style.width = "100%";
@@ -373,7 +373,7 @@ export class EngineLoadingView implements ILoadingViewHandler {
373
373
 
374
374
  // private async handleRuntimeLicense(loadingElement: HTMLElement) {
375
375
  // // First check if we have a commercial license
376
- // let commercialLicense = __EOmPHdLs();
376
+ // let commercialLicense = $iujBhf();
377
377
  // // if it's the case then we don't need to perform a runtime check
378
378
  // if (commercialLicense) return;
379
379
 
@@ -393,10 +393,10 @@ export class EngineLoadingView implements ILoadingViewHandler {
393
393
  // loadingElement.appendChild(nonCommercialContainer);
394
394
 
395
395
  // // Use the runtime license check
396
- // if (!isDevEnvironment() && MYnb) {
396
+ // if (!isDevEnvironment() && $rKLNDMS) {
397
397
  // if (debugLicense) console.log("Waiting for runtime license check");
398
- // await MYnb;
399
- // commercialLicense = __EOmPHdLs();
398
+ // await $rKLNDMS;
399
+ // commercialLicense = $iujBhf();
400
400
  // }
401
401
  // if (commercialLicense) return;
402
402
  // nonCommercialContainer.style.transition = "opacity .5s ease-in-out";
@@ -3,7 +3,7 @@ import type { Texture } from "three";
3
3
  import { isDevEnvironment, showBalloonWarning } from "../debug/index.js";
4
4
  import { PUBLIC_KEY, VERSION } from "../engine_constants.js";
5
5
  import { ContextEvent, ContextRegistry } from "../engine_context_registry.js";
6
- import { __EOmPHdLs } from "../engine_license.js";
6
+ import { $iujBhf } from "../engine_license.js";
7
7
  import { onStart } from "../engine_lifecycle_api.js";
8
8
  import { setDracoDecoderPath, setDracoDecoderType, setKtx2TranscoderPath } from "../engine_loaders.gltf.js";
9
9
  import { Context, ContextCreateArgs } from "../engine_setup.js";
@@ -579,7 +579,7 @@ export class NeedleEngineWebComponent extends HTMLElementBase implements INeedle
579
579
 
580
580
 
581
581
  // Loading start events
582
- const allowOverridingDefaultLoading = __EOmPHdLs();
582
+ const allowOverridingDefaultLoading = $iujBhf();
583
583
  // default loading can be overriden by calling preventDefault in the onload start event
584
584
  this.ensureLoadStartIsRegistered();
585
585
  let useDefaultLoading = this.dispatchEvent(new CustomEvent("loadstart", {
@@ -291,6 +291,49 @@ export class NeedleXRSession implements INeedleXRSession {
291
291
  static get currentSessionRequest(): XRSessionMode | null { return this._currentSessionRequestMode; }
292
292
  private static _currentSessionRequestMode: XRSessionMode | null = null;
293
293
 
294
+ /** Base URL for branded Needle AppClip experiences */
295
+ private static readonly appClipBaseUrl = "https://appclip.needle.tools/x/";
296
+ private static _appClipUrl: string | null = null;
297
+ /**
298
+ * Optionally provide a custom Needle AppClip experience to open on iOS instead of the default Needle AppClip.
299
+ * This enables custom App Clip card branding (your own title and image) when launching AR on iPhone and iPad.
300
+ *
301
+ * You can provide either:
302
+ * - the full URL, e.g. `https://appclip.needle.tools/x/your-experience`
303
+ * - or just the experience id, e.g. `your-experience` (expanded to `https://appclip.needle.tools/x/your-experience`)
304
+ *
305
+ * **IMPORTANT**: the resulting URL must exactly match an App Clip experience URL you have registered (with custom card metadata).
306
+ * For this reason the URL is opened as-is, without appending any query parameters.
307
+ * @link https://appclip.needle.tools
308
+ */
309
+ static get appClipUrl(): string | null { return this._appClipUrl; }
310
+ static set appClipUrl(value: string | null) {
311
+ if (!value) {
312
+ this._appClipUrl = null;
313
+ return;
314
+ }
315
+ const trimmed = value.trim();
316
+ // accept a full URL as-is, otherwise treat the value as an experience id and expand it using the base url
317
+ this._appClipUrl = /^https?:\/\//i.test(trimmed)
318
+ ? trimmed
319
+ : this.appClipBaseUrl + trimmed.replace(/^\/+/, "");
320
+ }
321
+
322
+ /**
323
+ * @internal
324
+ * Returns the AppClip launch URL to open (or prefetch) on iOS for the current page.
325
+ * When a custom {@link appClipUrl} is set this is the branded experience URL (opened as-is so it matches the registered URL);
326
+ * otherwise it is the default Needle AppClip launch URL with the current page forwarded via the `url` parameter.
327
+ */
328
+ static getAppClipLaunchUrl(): URL {
329
+ if (this._appClipUrl) return new URL(this._appClipUrl);
330
+ // Forward to the default Needle AppClip experience (using the apple.com url the appclip overlay shows immediately)
331
+ // alternative: `https://appclip.needle.tools/ar?url=${location.href}`
332
+ const url = new URL("https://appclip.apple.com/id?p=tools.needle.launch-app.Clip");
333
+ url.searchParams.set("url", location.href);
334
+ return url;
335
+ }
336
+
294
337
  /**
295
338
  * @returns the active @type {NeedleXRSession} (if any active) or null
296
339
  */
@@ -498,10 +541,10 @@ export class NeedleXRSession implements INeedleXRSession {
498
541
  const topWindow = window.top || window;
499
542
  const originalUrl = topWindow.location.href;
500
543
 
501
- // Forward to the AppClip experience (Using the apple.com url the appclip overlay shows immediately)
502
- // const url =`https://appclip.needle.tools/ar?url=${(location.href)}`;
503
- const url = new URL("https://appclip.apple.com/id?p=tools.needle.launch-app.Clip");
504
- url.searchParams.set("url", location.href);
544
+ // Forward to the AppClip experience. By default this is the Needle AppClip; a custom branded
545
+ // experience can be set via NeedleXRSession.appClipUrl (which is then opened as-is so it matches the registered URL).
546
+ const customAppClip = NeedleXRSession.appClipUrl !== null;
547
+ const url = NeedleXRSession.getAppClipLaunchUrl();
505
548
  const urlStr = url.toString();
506
549
 
507
550
  Telemetry.sendEvent(Context.Current, "xr", {
@@ -513,12 +556,15 @@ export class NeedleXRSession implements INeedleXRSession {
513
556
  try {
514
557
  console.debug("iOS device detected - opening Needle App Clip for AR experience", { mode, init, url });
515
558
 
516
- // Prewarm the appclip by opening the URL
559
+ // Prewarm the appclip by opening the URL (with an extra ?prewarm=1 param) to fetch the app clip
560
+ // metadata faster - this fixes a double-tap issue on iOS.
517
561
  // BUT on safari triggering this with multiple URLs causes the
518
562
  // Browser to show the dialogue AGAIN in the background
519
563
  // SO: we don't do it here...
520
- const key = "appclip-prewarm:" + new URL(url).origin;
521
- const allowMultipleNavigations = sessionStorage.getItem(key) === null;
564
+ // We also skip prewarming for a custom branded experience: that URL must match the registered
565
+ // App Clip experience URL exactly, so we must not append any query parameters to it.
566
+ const key = "appclip-prewarm:" + url.origin;
567
+ const allowMultipleNavigations = !customAppClip && sessionStorage.getItem(key) === null;
522
568
  if (allowMultipleNavigations) {
523
569
  const secondUrl = new URL(url);
524
570
  secondUrl.searchParams.set("prewarm", "1");
@@ -3,7 +3,7 @@ import { ArrayCamera, AxesHelper, Camera, Color, DirectionalLight, Fog, GridHelp
3
3
  import { needleLogoOnlySVG } from "../assets/index.js";
4
4
  import { isDevEnvironment } from "../debug/index.js";
5
5
  import { ObjectUtils, PrimitiveType } from "../engine_create_objects.js";
6
- import { __EOmPHdLs } from "../engine_license.js";
6
+ import { $iujBhf } from "../engine_license.js";
7
7
  import { Mathf } from "../engine_math.js";
8
8
  import { delay, DeviceUtilities } from "../engine_utils.js";
9
9
 
@@ -215,7 +215,7 @@ export class TemporaryXRContext {
215
215
  this._scene.background = new Color(0x000000);
216
216
 
217
217
  let logoSrc = needleLogoOnlySVG;
218
- if (__EOmPHdLs()) {
218
+ if ($iujBhf()) {
219
219
  const htmlComponent = document.querySelector("needle-engine");
220
220
  if (htmlComponent) {
221
221
  const licenseLogo = htmlComponent.getAttribute("logo-src");