@needle-tools/engine 5.1.4 → 5.1.5
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.
- package/CHANGELOG.md +6 -0
- package/dist/{needle-engine.bundle-CEba8eTv.js → needle-engine.bundle-CxsZfV6C.js} +5245 -5166
- package/dist/{needle-engine.bundle-BvxvjlSN.min.js → needle-engine.bundle-DUb8msPR.min.js} +140 -140
- package/dist/{needle-engine.bundle-GUaSFX8u.umd.cjs → needle-engine.bundle-D_h1sDs-.umd.cjs} +137 -137
- package/dist/needle-engine.d.ts +39 -30
- package/dist/needle-engine.js +242 -242
- package/dist/needle-engine.min.js +1 -1
- package/dist/needle-engine.umd.cjs +1 -1
- package/lib/engine/api.d.ts +1 -1
- package/lib/engine/api.js +1 -1
- package/lib/engine/api.js.map +1 -1
- package/lib/engine/debug/debug_console.js +39 -17
- package/lib/engine/debug/debug_console.js.map +1 -1
- package/lib/engine/debug/debug_spatial_console.js +14 -0
- package/lib/engine/debug/debug_spatial_console.js.map +1 -1
- package/lib/engine/engine_init.js +2 -2
- package/lib/engine/engine_init.js.map +1 -1
- package/lib/engine/engine_license.d.ts +7 -7
- package/lib/engine/engine_license.js +71 -71
- package/lib/engine/engine_license.js.map +1 -1
- package/lib/engine/engine_networking_blob.js +3 -3
- package/lib/engine/engine_utils_qrcode.js +2 -2
- package/lib/engine/engine_utils_screenshot.js +1 -0
- package/lib/engine/engine_utils_screenshot.js.map +1 -1
- package/lib/engine/engine_utils_screenshot.xr.js +1 -1
- package/lib/engine/engine_utils_screenshot.xr.js.map +1 -1
- package/lib/engine/webcomponents/needle menu/needle-menu-spatial.js +2 -2
- package/lib/engine/webcomponents/needle menu/needle-menu.js +18 -5
- package/lib/engine/webcomponents/needle menu/needle-menu.js.map +1 -1
- package/lib/engine/webcomponents/needle-engine.ar-overlay.js +33 -7
- package/lib/engine/webcomponents/needle-engine.ar-overlay.js.map +1 -1
- package/lib/engine/webcomponents/needle-engine.js +2 -2
- package/lib/engine/webcomponents/needle-engine.loading.js +2 -2
- package/lib/engine/webcomponents/needle-engine.loading.js.map +1 -1
- package/lib/engine/xr/NeedleXRSession.js +10 -4
- package/lib/engine/xr/NeedleXRSession.js.map +1 -1
- package/lib/engine/xr/NeedleXRSync.js +47 -47
- package/lib/engine/xr/NeedleXRSync.js.map +1 -1
- package/lib/engine/xr/TempXRContext.js +2 -2
- package/lib/engine-components/DragControls.d.ts +9 -0
- package/lib/engine-components/DragControls.js +174 -73
- package/lib/engine-components/DragControls.js.map +1 -1
- package/lib/engine-components/DragControlsConstraints.d.ts +11 -2
- package/lib/engine-components/DragControlsConstraints.js +18 -11
- package/lib/engine-components/DragControlsConstraints.js.map +1 -1
- package/lib/engine-components/OrbitControls.js +32 -6
- package/lib/engine-components/OrbitControls.js.map +1 -1
- package/lib/engine-components/export/usdz/USDZExporter.js +4 -4
- package/package.json +1 -1
- package/plugins/common/license.js +4 -4
- package/plugins/vite/license.js +4 -4
- package/src/engine/api.ts +1 -1
- package/src/engine/debug/debug_console.ts +33 -17
- package/src/engine/debug/debug_spatial_console.ts +15 -0
- package/src/engine/engine_init.ts +2 -2
- package/src/engine/engine_license.ts +68 -68
- package/src/engine/engine_networking_blob.ts +3 -3
- package/src/engine/engine_utils_qrcode.ts +2 -2
- package/src/engine/engine_utils_screenshot.ts +1 -0
- package/src/engine/engine_utils_screenshot.xr.ts +1 -1
- package/src/engine/webcomponents/needle menu/needle-menu-spatial.ts +2 -2
- package/src/engine/webcomponents/needle menu/needle-menu.ts +19 -5
- package/src/engine/webcomponents/needle-engine.ar-overlay.ts +33 -5
- package/src/engine/webcomponents/needle-engine.loading.ts +6 -6
- package/src/engine/webcomponents/needle-engine.ts +2 -2
- package/src/engine/xr/NeedleXRSession.ts +10 -3
- package/src/engine/xr/NeedleXRSync.ts +47 -50
- package/src/engine/xr/TempXRContext.ts +2 -2
- package/src/engine-components/DragControls.ts +189 -80
- package/src/engine-components/DragControlsConstraints.ts +20 -12
- package/src/engine-components/OrbitControls.ts +31 -4
- package/src/engine-components/export/usdz/USDZExporter.ts +4 -4
|
@@ -9,19 +9,19 @@ import { DeviceUtilities, getParam } from "./engine_utils.js";
|
|
|
9
9
|
|
|
10
10
|
const debug = getParam("__debuglic__");
|
|
11
11
|
|
|
12
|
-
const
|
|
12
|
+
const $qnPTnU: ((result: boolean) => void)[] = [];
|
|
13
13
|
|
|
14
14
|
// DO NOT EDIT MANUALLY
|
|
15
|
-
let
|
|
15
|
+
let __IsXDNQb: string = "";
|
|
16
16
|
// eslint-disable-next-line prefer-const
|
|
17
|
-
let
|
|
17
|
+
let $ZDwS: string = "";
|
|
18
18
|
|
|
19
19
|
if (debug) {
|
|
20
|
-
console.log("License Type: " +
|
|
21
|
-
if (
|
|
22
|
-
console.log("License JWT: " +
|
|
20
|
+
console.log("License Type: " + __IsXDNQb);
|
|
21
|
+
if ($ZDwS) {
|
|
22
|
+
console.log("License JWT: " + $ZDwS);
|
|
23
23
|
try {
|
|
24
|
-
const payload = JSON.parse(atob(
|
|
24
|
+
const payload = JSON.parse(atob($ZDwS.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
|
|
36
|
-
switch (
|
|
35
|
+
export function otIKt() {
|
|
36
|
+
switch (__IsXDNQb) {
|
|
37
37
|
case "pro":
|
|
38
38
|
case "enterprise":
|
|
39
39
|
return true;
|
|
@@ -42,8 +42,8 @@ export function KLaEb() {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
/** @internal */
|
|
45
|
-
export function
|
|
46
|
-
switch (
|
|
45
|
+
export function _gNHPUn() {
|
|
46
|
+
switch (__IsXDNQb) {
|
|
47
47
|
case "indie":
|
|
48
48
|
return true;
|
|
49
49
|
}
|
|
@@ -51,8 +51,8 @@ export function wPA() {
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
/** @internal */
|
|
54
|
-
export function
|
|
55
|
-
switch (
|
|
54
|
+
export function $aiMWPAef() {
|
|
55
|
+
switch (__IsXDNQb) {
|
|
56
56
|
case "edu":
|
|
57
57
|
return true;
|
|
58
58
|
}
|
|
@@ -60,20 +60,20 @@ export function _bEUa() {
|
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
/** @internal */
|
|
63
|
-
export function
|
|
64
|
-
return
|
|
63
|
+
export function $PLKRCQIf() {
|
|
64
|
+
return otIKt() || _gNHPUn() || $aiMWPAef();
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
|
|
68
68
|
/** @internal */
|
|
69
|
-
export function
|
|
70
|
-
if (
|
|
69
|
+
export function _$sDvKPqp(cb: (result: boolean) => void) {
|
|
70
|
+
if (otIKt() || _gNHPUn() || $aiMWPAef())
|
|
71
71
|
return cb(true);
|
|
72
|
-
|
|
72
|
+
$qnPTnU.push(cb);
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
function $
|
|
76
|
-
for (const cb of
|
|
75
|
+
function _$wgFzq(result: boolean) {
|
|
76
|
+
for (const cb of $qnPTnU) {
|
|
77
77
|
try {
|
|
78
78
|
cb(result);
|
|
79
79
|
}
|
|
@@ -88,7 +88,7 @@ function $VtYVpv(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
|
|
91
|
+
const __lgCVJdwb = {
|
|
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
|
|
115
|
+
async function __ZcVBPZ(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 _crQsWky(jwt: string): Promise<string | null> {
|
|
|
122
122
|
|
|
123
123
|
const key = await crypto.subtle.importKey(
|
|
124
124
|
"jwk",
|
|
125
|
-
|
|
125
|
+
__lgCVJdwb,
|
|
126
126
|
{ name: "ECDSA", namedCurve: "P-256" },
|
|
127
127
|
false,
|
|
128
128
|
["verify"]
|
|
@@ -182,24 +182,24 @@ async function _crQsWky(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
|
|
185
|
+
* The engine ONLY trusts the JWT — the plain __IsXDNQb string is ignored. */
|
|
186
186
|
let _jwtVerificationPromise: Promise<void> | undefined = undefined;
|
|
187
|
-
async function $
|
|
188
|
-
// NOTE: do NOT add an `if (
|
|
187
|
+
async function _$BXZmXmG(): Promise<void> {
|
|
188
|
+
// NOTE: do NOT add an `if (!$ZDwS) 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 =
|
|
191
|
+
const jwt = $ZDwS;
|
|
192
192
|
// Clear after reading — this reassignment also prevents esbuild from
|
|
193
193
|
// constant-folding the variable (it now has multiple assignment sites).
|
|
194
|
-
|
|
195
|
-
const verifiedType = await
|
|
194
|
+
$ZDwS = "";
|
|
195
|
+
const verifiedType = await __ZcVBPZ(jwt);
|
|
196
196
|
if (verifiedType) {
|
|
197
|
-
|
|
197
|
+
__IsXDNQb = verifiedType;
|
|
198
198
|
if (debug) console.log("License type set from verified JWT: " + verifiedType);
|
|
199
|
-
$
|
|
199
|
+
_$wgFzq($PLKRCQIf());
|
|
200
200
|
}
|
|
201
201
|
else {
|
|
202
|
-
|
|
202
|
+
__IsXDNQb = "basic";
|
|
203
203
|
if (debug && jwt) console.warn("JWT verification failed — license reset to basic");
|
|
204
204
|
}
|
|
205
205
|
}
|
|
@@ -650,18 +650,18 @@ export namespace Telemetry {
|
|
|
650
650
|
}
|
|
651
651
|
|
|
652
652
|
|
|
653
|
-
export function _$
|
|
653
|
+
export function _$zEuYwGe() {
|
|
654
654
|
|
|
655
|
-
if(
|
|
655
|
+
if(__IsXDNQb === "") __IsXDNQb = "basic";
|
|
656
656
|
|
|
657
657
|
// Start JWT verification — must be here (not top-level) to avoid tree-shaking
|
|
658
|
-
_jwtVerificationPromise = $
|
|
658
|
+
_jwtVerificationPromise = _$BXZmXmG();
|
|
659
659
|
|
|
660
660
|
Telemetry.init();
|
|
661
661
|
ContextRegistry.registerCallback(ContextEvent.ContextRegistered, evt => {
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
setTimeout(() =>
|
|
662
|
+
__mJiL(evt.context);
|
|
663
|
+
$NDvkjBNe(evt.context);
|
|
664
|
+
setTimeout(() => $vIjopOW(evt.context), 2000);
|
|
665
665
|
});
|
|
666
666
|
// ContextCreated fires after the renderer exists, so the canvas is available for context-loss monitoring
|
|
667
667
|
ContextRegistry.registerCallback(ContextEvent.ContextCreated, evt => {
|
|
@@ -670,17 +670,17 @@ export function _$auU() {
|
|
|
670
670
|
});
|
|
671
671
|
}
|
|
672
672
|
|
|
673
|
-
export let
|
|
673
|
+
export let $oUFilKP: Promise<void> | undefined = undefined;
|
|
674
674
|
let applicationIsForbidden = false;
|
|
675
675
|
let applicationForbiddenText = "";
|
|
676
|
-
async function
|
|
676
|
+
async function $IWNB() {
|
|
677
677
|
// Only perform the runtime license check once
|
|
678
|
-
if (
|
|
678
|
+
if ($oUFilKP) return $oUFilKP;
|
|
679
679
|
// Wait for JWT verification to complete first (if running)
|
|
680
680
|
if (_jwtVerificationPromise) {
|
|
681
681
|
await _jwtVerificationPromise;
|
|
682
682
|
}
|
|
683
|
-
if (
|
|
683
|
+
if (__IsXDNQb === "basic") {
|
|
684
684
|
try {
|
|
685
685
|
const licenseUrl = "https://needle.tools/api/v1/needle-engine/check?location=" + encodeURIComponent(window.location.href) + "&version=" + VERSION + "&generator=" + encodeURIComponent(GENERATOR);
|
|
686
686
|
const res = await fetch(licenseUrl, {
|
|
@@ -692,29 +692,29 @@ async function _iRYfLx() {
|
|
|
692
692
|
if (res?.status === 200) {
|
|
693
693
|
applicationIsForbidden = false;
|
|
694
694
|
if (debug) console.log("License check succeeded");
|
|
695
|
-
|
|
696
|
-
$
|
|
695
|
+
__IsXDNQb = "pro";
|
|
696
|
+
_$wgFzq(true);
|
|
697
697
|
}
|
|
698
698
|
else if (res?.status === 403) {
|
|
699
|
-
$
|
|
699
|
+
_$wgFzq(false);
|
|
700
700
|
applicationIsForbidden = true;
|
|
701
701
|
applicationForbiddenText = await res.text();
|
|
702
702
|
}
|
|
703
703
|
else {
|
|
704
|
-
$
|
|
704
|
+
_$wgFzq(false);
|
|
705
705
|
if (debug) console.log("License check failed with status " + res?.status);
|
|
706
706
|
}
|
|
707
707
|
}
|
|
708
708
|
catch (err) {
|
|
709
|
-
$
|
|
709
|
+
_$wgFzq(false);
|
|
710
710
|
if (debug) console.error("License check failed", err);
|
|
711
711
|
}
|
|
712
712
|
}
|
|
713
|
-
else if (debug) console.log("Runtime license check is skipped because license is already applied as \"" +
|
|
713
|
+
else if (debug) console.log("Runtime license check is skipped because license is already applied as \"" + __IsXDNQb + "\"");
|
|
714
714
|
}
|
|
715
|
-
|
|
715
|
+
$oUFilKP = $IWNB();
|
|
716
716
|
|
|
717
|
-
async function
|
|
717
|
+
async function $NDvkjBNe(ctx: IContext) {
|
|
718
718
|
function createForbiddenElement() {
|
|
719
719
|
const div = document.createElement("div");
|
|
720
720
|
div.className = "needle-forbidden";
|
|
@@ -779,40 +779,40 @@ async function __DWQeDNnb(ctx: IContext) {
|
|
|
779
779
|
}, 500)
|
|
780
780
|
}
|
|
781
781
|
|
|
782
|
-
async function
|
|
782
|
+
async function __mJiL(ctx: IContext) {
|
|
783
783
|
try {
|
|
784
|
-
if (!
|
|
785
|
-
return
|
|
784
|
+
if (!otIKt() && !_gNHPUn()) {
|
|
785
|
+
return $pTRKOi(ctx);
|
|
786
786
|
}
|
|
787
787
|
}
|
|
788
788
|
catch (err) {
|
|
789
789
|
if (debug) console.log("License check failed", err)
|
|
790
|
-
return
|
|
790
|
+
return $pTRKOi(ctx)
|
|
791
791
|
}
|
|
792
|
-
if (debug)
|
|
792
|
+
if (debug) $pTRKOi(ctx)
|
|
793
793
|
}
|
|
794
794
|
|
|
795
795
|
|
|
796
796
|
|
|
797
|
-
async function
|
|
797
|
+
async function $pTRKOi(ctx: IContext) {
|
|
798
798
|
|
|
799
799
|
// if the engine loads faster than the license check, we need to capture the ready event here
|
|
800
800
|
let isReady = false;
|
|
801
801
|
ctx.domElement.addEventListener("ready", () => isReady = true);
|
|
802
802
|
|
|
803
|
-
await
|
|
803
|
+
await $oUFilKP?.catch(() => { });
|
|
804
804
|
|
|
805
805
|
|
|
806
|
-
if (
|
|
807
|
-
if (
|
|
806
|
+
if (otIKt() || _gNHPUn()) return;
|
|
807
|
+
if ($PLKRCQIf() === false) __GWmHmYep();
|
|
808
808
|
|
|
809
809
|
// check if the engine is already ready (meaning has finished loading)
|
|
810
810
|
if (isReady) {
|
|
811
|
-
|
|
811
|
+
xbmulpf(ctx);
|
|
812
812
|
}
|
|
813
813
|
else {
|
|
814
814
|
ctx.domElement.addEventListener("ready", () => {
|
|
815
|
-
|
|
815
|
+
xbmulpf(ctx);
|
|
816
816
|
});
|
|
817
817
|
}
|
|
818
818
|
}
|
|
@@ -820,7 +820,7 @@ async function __POTiHWtw(ctx: IContext) {
|
|
|
820
820
|
// const licenseElementIdentifier = "needle-license-element";
|
|
821
821
|
// const licenseDuration = 10000;
|
|
822
822
|
// const licenseDelay = 1200;
|
|
823
|
-
function
|
|
823
|
+
function xbmulpf(ctx: IContext) {
|
|
824
824
|
|
|
825
825
|
const style = `
|
|
826
826
|
position: relative;
|
|
@@ -833,7 +833,7 @@ function _fZxtASpV(ctx: IContext) {
|
|
|
833
833
|
padding: 10px;
|
|
834
834
|
padding-left: 30px;
|
|
835
835
|
`;
|
|
836
|
-
if (
|
|
836
|
+
if (__IsXDNQb === "edu") {
|
|
837
837
|
if (navigator.webdriver) {
|
|
838
838
|
console.log("This project is supported by Needle for Education – https://needle.tools");
|
|
839
839
|
}
|
|
@@ -890,7 +890,7 @@ function _fZxtASpV(ctx: IContext) {
|
|
|
890
890
|
}
|
|
891
891
|
}, 1000);
|
|
892
892
|
|
|
893
|
-
if (
|
|
893
|
+
if ($aiMWPAef()) {
|
|
894
894
|
const removeDelay = 20_000;
|
|
895
895
|
setTimeout(() => {
|
|
896
896
|
clearInterval(interval);
|
|
@@ -899,7 +899,7 @@ function _fZxtASpV(ctx: IContext) {
|
|
|
899
899
|
const intervalInMinutes = 5;
|
|
900
900
|
setTimeout(() => {
|
|
901
901
|
if (ctx.domElement.parentNode)
|
|
902
|
-
|
|
902
|
+
xbmulpf(ctx);
|
|
903
903
|
}, 1000 * 60 * intervalInMinutes)
|
|
904
904
|
}, removeDelay);
|
|
905
905
|
}
|
|
@@ -910,7 +910,7 @@ function _fZxtASpV(ctx: IContext) {
|
|
|
910
910
|
const base64Logo = "data:image/webp;base64,UklGRrABAABXRUJQVlA4WAoAAAAQAAAAHwAAHwAAQUxQSKEAAAARN6CmbSM4WR7vdARON11EBDq3fLiNbVtVzpMCPlKAEzsx0Y/x+Ovuv4dn0EFE/ydAvz6YggXzgh5sVgXM/zOC/4sii7qgGvB5N7hmuQYwkvazWAu1JPW41FXSHq6pnaQWvqYH18Fc0j1hO/BFTtIeSBlJi5w6qIIO7IOrwhFsB2Yxukif0FTRLpXswHR8MxbslKe9VZsn/Ub5C7YFOpqSTABWUDgg6AAAAFAGAJ0BKiAAIAA+7VyoTqmkpCI3+qgBMB2JbACdMt69DwMIQBLhkTO6XwY00UEDK6cNIDnuNibPf0EgAP7Y1myuiQHLDsF/0h5unrGh6WAbv7aegg2ZMd3uRKfT/3SJztcaujYfTvMXspfCTmYcoO6a+vhC3ss4M8uM58t4siiu59I4aOl59e9Sr6xoxYlHf2v+NnBNpJYeJf8jABQAId/PXuBkLEFkiCucgSGEcfhvajql/j3reCGl0M5/9gQWy7ayNPs+wlvIxFnNfSlfuND4CZOCyxOHhRqOmHN4ULHo3tCSrUNvgAA=";
|
|
911
911
|
|
|
912
912
|
let lastLogTime = 0;
|
|
913
|
-
async function
|
|
913
|
+
async function __GWmHmYep(_logo?: string) {
|
|
914
914
|
const now = Date.now();
|
|
915
915
|
if (now - lastLogTime < 2000) return;
|
|
916
916
|
lastLogTime = now;
|
|
@@ -943,7 +943,7 @@ async function __Pexfzvu(_logo?: string) {
|
|
|
943
943
|
}
|
|
944
944
|
|
|
945
945
|
|
|
946
|
-
async function
|
|
946
|
+
async function $vIjopOW(context: IContext) {
|
|
947
947
|
// isAllowed() now also covers cross-origin-isolated pages (where beacons can't be sent)
|
|
948
948
|
// and Do Not Track / GPC.
|
|
949
949
|
if (!Telemetry.isAllowed(context)) {
|
|
@@ -957,7 +957,7 @@ async function Zanq(context: IContext) {
|
|
|
957
957
|
|
|
958
958
|
// current url without query parameters
|
|
959
959
|
const currentUrl = window.location.href.split("?")[0];
|
|
960
|
-
const license =
|
|
960
|
+
const license = __IsXDNQb;
|
|
961
961
|
|
|
962
962
|
const beaconData = {
|
|
963
963
|
license,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { FileLoader } from "three";
|
|
2
2
|
|
|
3
3
|
import { showBalloonWarning } from "./debug/index.js";
|
|
4
|
-
import {
|
|
4
|
+
import { $PLKRCQIf } 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 (
|
|
48
|
+
if ($PLKRCQIf()) {
|
|
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 (
|
|
103
|
+
else if (!$PLKRCQIf() && 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 {
|
|
7
|
+
import { $PLKRCQIf } 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 =
|
|
138
|
+
const canUseCustomLogo = $PLKRCQIf();
|
|
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)
|
|
@@ -380,6 +380,7 @@ export function screenshot2(opts: ScreenshotOptionsShare): Promise<ScreenshotOpt
|
|
|
380
380
|
* @see {@link saveImage} for downloading data URLs
|
|
381
381
|
* @see {@link ScreenshotOptions} for all available options
|
|
382
382
|
*/
|
|
383
|
+
// #region screenshot 2
|
|
383
384
|
export function screenshot2(opts: ScreenshotOptionsDataUrl | ScreenshotOptionsTexture | ScreenshotOptionsBlob | ScreenshotOptionsShare)
|
|
384
385
|
: Texture | string | null | Promise<Blob | null> | Promise<ScreenshotOptionsShareReturnType> {
|
|
385
386
|
|
|
@@ -29,7 +29,7 @@ NeedleXRSession.onSessionRequestStart(evt => {
|
|
|
29
29
|
});
|
|
30
30
|
`);
|
|
31
31
|
if (isDevEnvironment()) {
|
|
32
|
-
showBalloonError("No camera feed available - please request the 'camera-access' feature before starting WebXR or add the ARCameraBackground component to your scene");
|
|
32
|
+
showBalloonError("No camera feed available - please request the 'camera-access' feature before starting WebXR or add the ARCameraBackground component to your scene", { duration: 300_000 });
|
|
33
33
|
}
|
|
34
34
|
return false;
|
|
35
35
|
}
|
|
@@ -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 {
|
|
5
|
+
import { $PLKRCQIf } 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 &&
|
|
324
|
+
if (!this._showNeedleLogo && $PLKRCQIf()) {
|
|
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 {
|
|
3
|
+
import { $PLKRCQIf, _$sDvKPqp, 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";
|
|
@@ -186,6 +186,11 @@ export class NeedleMenu {
|
|
|
186
186
|
if (args.session.isScreenBasedAR) {
|
|
187
187
|
this._menu["previousParent"] = this._menu.parentNode;
|
|
188
188
|
this._context.arOverlayElement.appendChild(this._menu);
|
|
189
|
+
// The host defaults to z-index 0 (to not overlay elements outside <needle-engine> in 2D).
|
|
190
|
+
// In screen-based AR the overlay may be reparented to document.body (e.g. Needle Go App Clip /
|
|
191
|
+
// Mozilla XR) where the WebXR polyfill's fullscreen canvas wrapper sits at z-index 1 and would
|
|
192
|
+
// render on top of the menu — visible through the transparent canvas but swallowing all taps.
|
|
193
|
+
this._menu.style.zIndex = "9999";
|
|
189
194
|
args.session.session.addEventListener("end", this.onExitXR);
|
|
190
195
|
|
|
191
196
|
// Close the foldout if it's open on entering AR
|
|
@@ -194,6 +199,7 @@ export class NeedleMenu {
|
|
|
194
199
|
}
|
|
195
200
|
|
|
196
201
|
private onExitXR = () => {
|
|
202
|
+
this._menu.style.zIndex = "";
|
|
197
203
|
if (this._menu["previousParent"]) {
|
|
198
204
|
this._menu["previousParent"].appendChild(this._menu);
|
|
199
205
|
delete this._menu["previousParent"];
|
|
@@ -778,8 +784,8 @@ export class NeedleMenuElement extends HTMLElementBase {
|
|
|
778
784
|
try {
|
|
779
785
|
// if the user has a license then we CAN hide the needle logo
|
|
780
786
|
// calling this method immediately will cause an issue with vite bundling tho
|
|
781
|
-
window.requestAnimationFrame(() =>
|
|
782
|
-
if (res == true &&
|
|
787
|
+
window.requestAnimationFrame(() => _$sDvKPqp(res => {
|
|
788
|
+
if (res == true && $PLKRCQIf() && !debugNonCommercial) {
|
|
783
789
|
let visible = this._userRequestedLogoVisible;
|
|
784
790
|
if (visible === undefined) visible = false;
|
|
785
791
|
this.___onSetLogoVisible(visible);
|
|
@@ -830,7 +836,7 @@ export class NeedleMenuElement extends HTMLElementBase {
|
|
|
830
836
|
// ensure the menu is not hidden or removed
|
|
831
837
|
const requiredParent = this?.parentNode;
|
|
832
838
|
if (this.style.display != "flex" || this.style.visibility != "visible" || this.style.opacity != "1" || requiredParent != this._domElement?.shadowRoot) {
|
|
833
|
-
if (
|
|
839
|
+
if (!$PLKRCQIf()) {
|
|
834
840
|
const change = changeEventCounter++;
|
|
835
841
|
// if a user doesn't have a local pro license *but* for development the menu is hidden then we show a warning
|
|
836
842
|
if (isLocalNetwork() && this._userRequestedMenuVisible === false) {
|
|
@@ -880,6 +886,7 @@ export class NeedleMenuElement extends HTMLElementBase {
|
|
|
880
886
|
connectedCallback() {
|
|
881
887
|
this.ensureInitialized();
|
|
882
888
|
window.addEventListener("resize", this.handleSizeChange);
|
|
889
|
+
this.addEventListener("beforexrselect", this.#onBeforeXRSelect);
|
|
883
890
|
this.handleMenuVisible();
|
|
884
891
|
this._sizeChangeInterval = setInterval(() => this.handleSizeChange(undefined, false), 5000);
|
|
885
892
|
// the dom element is set after initialization runs
|
|
@@ -890,11 +897,18 @@ export class NeedleMenuElement extends HTMLElementBase {
|
|
|
890
897
|
}
|
|
891
898
|
disconnectedCallback() {
|
|
892
899
|
window.removeEventListener("resize", this.handleSizeChange);
|
|
900
|
+
this.removeEventListener("beforexrselect", this.#onBeforeXRSelect);
|
|
893
901
|
clearInterval(this._sizeChangeInterval);
|
|
894
902
|
this._domElement?.removeEventListener("resize", this.handleSizeChange);
|
|
895
903
|
this._context?.domElement.removeEventListener("click", this.#onClick);
|
|
896
904
|
}
|
|
897
905
|
|
|
906
|
+
/** Prevent XR select from firing into the scene (e.g. tap-to-place) when the user
|
|
907
|
+
* interacts with the menu during screen-based AR (dom-overlay / Needle Go App Clip) */
|
|
908
|
+
#onBeforeXRSelect = (e: Event) => {
|
|
909
|
+
e.preventDefault();
|
|
910
|
+
}
|
|
911
|
+
|
|
898
912
|
#onClick = (e: Event) => {
|
|
899
913
|
// detect a click outside the opened foldout to automatically close it
|
|
900
914
|
if (!e.defaultPrevented
|
|
@@ -918,7 +932,7 @@ export class NeedleMenuElement extends HTMLElementBase {
|
|
|
918
932
|
showNeedleLogo(visible: boolean) {
|
|
919
933
|
this._userRequestedLogoVisible = visible;
|
|
920
934
|
if (!visible) {
|
|
921
|
-
if (
|
|
935
|
+
if (!$PLKRCQIf() || debugNonCommercial) {
|
|
922
936
|
console.warn("[Needle Engine] You need a commercial license to hide the Needle Engine logo in production.");
|
|
923
937
|
const localNetwork = isLocalNetwork()
|
|
924
938
|
if (!localNetwork) return;
|
|
@@ -29,11 +29,11 @@ export class AROverlayHandler {
|
|
|
29
29
|
this.arContainer = overlayContainer;
|
|
30
30
|
|
|
31
31
|
if (DeviceUtilities.isMozillaXR() || DeviceUtilities.isNeedleAppClip()) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
if (el === this.arContainer)
|
|
32
|
+
// Copy the live HTMLCollection: appendChild removes elements from it while
|
|
33
|
+
// iterating, which previously skipped every other element
|
|
34
|
+
const arElements = Array.from(context.domElement!.children);
|
|
35
|
+
for (const el of arElements) {
|
|
36
|
+
if (!el || el === this.arContainer) continue;
|
|
37
37
|
this._reparentedObjects.push({ el: el, previousParent: el.parentElement });
|
|
38
38
|
this.arContainer?.appendChild(el);
|
|
39
39
|
}
|
|
@@ -47,6 +47,17 @@ export class AROverlayHandler {
|
|
|
47
47
|
overlayContainer.style.display = "";
|
|
48
48
|
overlayContainer.style.visibility = "";
|
|
49
49
|
document.body.appendChild(overlayContainer);
|
|
50
|
+
// Outside the shadow root the container loses its shadow CSS (fullscreen, z-index).
|
|
51
|
+
// Without an explicit position it stays static, its z-index is ignored and it doesn't
|
|
52
|
+
// create a stacking context — overlay children then compete individually with the
|
|
53
|
+
// WebXR polyfill's fullscreen canvas wrapper (z-index 1) and can end up behind the
|
|
54
|
+
// transparent canvas: visible, but unable to receive any taps.
|
|
55
|
+
overlayContainer.style.position = "fixed";
|
|
56
|
+
overlayContainer.style.top = "0";
|
|
57
|
+
overlayContainer.style.left = "0";
|
|
58
|
+
overlayContainer.style.width = "100%";
|
|
59
|
+
overlayContainer.style.height = "100%";
|
|
60
|
+
overlayContainer.style.zIndex = "10000";
|
|
50
61
|
}
|
|
51
62
|
}
|
|
52
63
|
else {
|
|
@@ -71,6 +82,23 @@ export class AROverlayHandler {
|
|
|
71
82
|
}
|
|
72
83
|
this._reparentedObjects.length = 0;
|
|
73
84
|
|
|
85
|
+
// Restore the overlay container that was reparented to document.body in onBegin
|
|
86
|
+
// (the polyfill only restores it when it received the domOverlay root) and remove
|
|
87
|
+
// the inline styles that replaced its shadow CSS while it was outside the shadow root
|
|
88
|
+
if (this.arContainer && this.originalDomOverlayParent) {
|
|
89
|
+
const style = this.arContainer.style;
|
|
90
|
+
style.position = "";
|
|
91
|
+
style.top = "";
|
|
92
|
+
style.left = "";
|
|
93
|
+
style.width = "";
|
|
94
|
+
style.height = "";
|
|
95
|
+
style.zIndex = "";
|
|
96
|
+
if (this.arContainer.parentNode !== this.originalDomOverlayParent) {
|
|
97
|
+
this.originalDomOverlayParent.appendChild(this.arContainer);
|
|
98
|
+
}
|
|
99
|
+
this.originalDomOverlayParent = null;
|
|
100
|
+
}
|
|
101
|
+
|
|
74
102
|
// mozilla XR exit AR fixes
|
|
75
103
|
if (DeviceUtilities.isMozillaXR()) {
|
|
76
104
|
// without the timeout we get errors in mozillas code and can not enter XR again
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { needleLogoOnlySVG } from "../assets/index.js"
|
|
2
2
|
import { isDevEnvironment, showBalloonWarning } from "../debug/index.js";
|
|
3
|
-
import {
|
|
3
|
+
import { $PLKRCQIf, otIKt, $oUFilKP } 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 =
|
|
208
|
+
const hasLicense = otIKt();
|
|
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 =
|
|
376
|
+
// let commercialLicense = $PLKRCQIf();
|
|
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() &&
|
|
396
|
+
// if (!isDevEnvironment() && $oUFilKP) {
|
|
397
397
|
// if (debugLicense) console.log("Waiting for runtime license check");
|
|
398
|
-
// await
|
|
399
|
-
// commercialLicense =
|
|
398
|
+
// await $oUFilKP;
|
|
399
|
+
// commercialLicense = $PLKRCQIf();
|
|
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 {
|
|
6
|
+
import { $PLKRCQIf, Telemetry } 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 =
|
|
582
|
+
const allowOverridingDefaultLoading = $PLKRCQIf();
|
|
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", {
|
|
@@ -772,11 +772,18 @@ export class NeedleXRSession implements INeedleXRSession {
|
|
|
772
772
|
this._currentSessionRequestMode = mode;
|
|
773
773
|
/**@type {XRSystem} */
|
|
774
774
|
const newSession = await (this._currentSessionRequest)?.catch(e => {
|
|
775
|
-
console.error(e, "Code: " + e?.code);
|
|
776
|
-
if (e?.code === 9)
|
|
775
|
+
console.error(e, "Code: " + e?.code, "requiredFeatures:", init.requiredFeatures, "optionalFeatures:", init.optionalFeatures);
|
|
776
|
+
if (e?.code === 9) {
|
|
777
|
+
// NotSupportedError: surface the platform's reason if it provides one
|
|
778
|
+
// (e.g. which required feature is unsupported) instead of only a generic hint
|
|
779
|
+
const reason = typeof e?.message === "string" && e.message.length > 0
|
|
780
|
+
? e.message
|
|
781
|
+
: "Make sure you allow the required permissions.";
|
|
782
|
+
showBalloonWarning(`Couldn't start XR session: ${reason}`, { duration: 30_000 });
|
|
783
|
+
}
|
|
777
784
|
console.log("If the specified XR configuration is not supported (e.g. entering AR doesnt work) - make sure you access the website on a secure connection (HTTPS) and your device has the required permissions (e.g. camera access)");
|
|
778
785
|
const notSecure = location.protocol === 'http:';
|
|
779
|
-
if (notSecure) showBalloonWarning("XR requires a secure connection (HTTPS)");
|
|
786
|
+
if (notSecure) showBalloonWarning("XR requires a secure connection (HTTPS)", { duration: 30_000 });
|
|
780
787
|
});
|
|
781
788
|
this._currentSessionRequest = undefined;
|
|
782
789
|
this._currentSessionRequestMode = null;
|