@needle-tools/engine 4.12.0-next.ca2cebd → 4.12.0-next.dcaf2b0
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 +1 -1
- package/components.needle.json +1 -1
- package/dist/generateMeshBVH.worker-iyfPIK6R.js +21 -0
- package/dist/{gltf-progressive-DWcmTMCh.umd.cjs → gltf-progressive-Bfpfaz84.umd.cjs} +1 -1
- package/dist/{gltf-progressive-DgYz5BYa.js → gltf-progressive-DPunMlEM.js} +24 -24
- package/dist/{gltf-progressive-DZrY8VT6.min.js → gltf-progressive-hFPACYio.min.js} +2 -2
- package/dist/{loader.worker-Dip-PthR.js → loader.worker-DWzfDpAl.js} +4 -4
- package/dist/{needle-engine.bundle-CFkbGdL5.js → needle-engine.bundle-DTXsNmA-.js} +9789 -9421
- package/dist/needle-engine.bundle-HRGeyfKM.umd.cjs +1647 -0
- package/dist/needle-engine.bundle-OAD5_P8d.min.js +1647 -0
- package/dist/needle-engine.d.ts +105 -33
- package/dist/needle-engine.js +48 -48
- package/dist/needle-engine.min.js +1 -1
- package/dist/needle-engine.umd.cjs +1 -1
- package/dist/{postprocessing-CMgoN5t5.umd.cjs → postprocessing-BHQvwehB.umd.cjs} +81 -81
- package/dist/{postprocessing-DYDtB188.min.js → postprocessing-ClLv0reO.min.js} +54 -54
- package/dist/{postprocessing-BTW9pD_s.js → postprocessing-DLI2N3LL.js} +450 -441
- package/dist/{three-DfMvBzXi.js → three-BCCkyCA5.js} +1 -7
- package/dist/{three-qj71I7J3.umd.cjs → three-Bf2NBxAw.umd.cjs} +2 -2
- package/dist/{three-B7CT31Bt.min.js → three-W7zWTcfP.min.js} +1 -1
- package/dist/{three-examples-D1SK93ek.js → three-examples-D4rE49Ui.js} +12 -22
- package/dist/{three-examples-D1P7eEhn.min.js → three-examples-DB5Uoja4.min.js} +12 -12
- package/dist/{three-examples-CsW4_6LI.umd.cjs → three-examples-Djbk6WA4.umd.cjs} +6 -6
- package/dist/{three-mesh-ui-C_uSB5dD.js → three-mesh-ui-3nSSizT4.js} +1 -1
- package/dist/{three-mesh-ui-LQ44s0AL.min.js → three-mesh-ui-CIez6qJQ.min.js} +1 -1
- package/dist/{three-mesh-ui-DpATDXwU.umd.cjs → three-mesh-ui-zsOOA5Pq.umd.cjs} +1 -1
- package/dist/{vendor-D0zoswDa.js → vendor-DMZcbVO1.js} +3707 -3527
- package/dist/vendor-sURMCFSI.min.js +1116 -0
- package/dist/{vendor-UCpFAwt1.umd.cjs → vendor-tyBvnMF-.umd.cjs} +39 -39
- package/lib/engine/codegen/register_types.js +0 -2
- package/lib/engine/codegen/register_types.js.map +1 -1
- package/lib/engine/debug/debug_console.js +403 -1
- package/lib/engine/debug/debug_console.js.map +1 -1
- package/lib/engine/engine_components.js +3 -3
- package/lib/engine/engine_components.js.map +1 -1
- package/lib/engine/engine_context.js +2 -0
- package/lib/engine/engine_context.js.map +1 -1
- package/lib/engine/engine_input.d.ts +5 -0
- package/lib/engine/engine_input.js +6 -0
- package/lib/engine/engine_input.js.map +1 -1
- package/lib/engine/engine_license.d.ts +18 -0
- package/lib/engine/engine_license.js +163 -18
- package/lib/engine/engine_license.js.map +1 -1
- package/lib/engine/engine_networking.js +20 -5
- package/lib/engine/engine_networking.js.map +1 -1
- package/lib/engine/engine_physics.js.map +1 -1
- package/lib/engine/engine_physics_rapier.js +1 -1
- package/lib/engine/engine_physics_rapier.js.map +1 -1
- package/lib/engine/engine_serialization_builtin_serializer.js +1 -1
- package/lib/engine/engine_serialization_builtin_serializer.js.map +1 -1
- package/lib/engine/engine_three_utils.js +2 -2
- package/lib/engine/engine_three_utils.js.map +1 -1
- package/lib/engine/engine_utils.d.ts +4 -1
- package/lib/engine/engine_utils.js +28 -4
- package/lib/engine/engine_utils.js.map +1 -1
- package/lib/engine/extensions/extensions.d.ts +29 -7
- package/lib/engine/extensions/extensions.js.map +1 -1
- package/lib/engine/webcomponents/WebXRButtons.js +13 -5
- package/lib/engine/webcomponents/WebXRButtons.js.map +1 -1
- package/lib/engine/webcomponents/needle menu/needle-menu-spatial.js +2 -1
- package/lib/engine/webcomponents/needle menu/needle-menu-spatial.js.map +1 -1
- package/lib/engine/webcomponents/needle menu/needle-menu.d.ts +2 -0
- package/lib/engine/webcomponents/needle menu/needle-menu.js +37 -6
- package/lib/engine/webcomponents/needle menu/needle-menu.js.map +1 -1
- package/lib/engine/webcomponents/needle-engine.ar-overlay.js +4 -0
- package/lib/engine/webcomponents/needle-engine.ar-overlay.js.map +1 -1
- package/lib/engine/webcomponents/needle-engine.js +1 -1
- package/lib/engine/webcomponents/needle-engine.js.map +1 -1
- package/lib/engine/xr/NeedleXRSession.d.ts +1 -1
- package/lib/engine/xr/NeedleXRSession.js +106 -22
- package/lib/engine/xr/NeedleXRSession.js.map +1 -1
- package/lib/engine/xr/TempXRContext.js +12 -2
- package/lib/engine/xr/TempXRContext.js.map +1 -1
- package/lib/engine/xr/usdz.js +6 -2
- package/lib/engine/xr/usdz.js.map +1 -1
- package/lib/engine-components/AlignmentConstraint.d.ts +1 -1
- package/lib/engine-components/AlignmentConstraint.js +1 -1
- package/lib/engine-components/Animation.d.ts +1 -1
- package/lib/engine-components/Animation.js +1 -1
- package/lib/engine-components/Animator.d.ts +1 -1
- package/lib/engine-components/Animator.js +1 -1
- package/lib/engine-components/AudioListener.d.ts +1 -1
- package/lib/engine-components/AudioListener.js +1 -1
- package/lib/engine-components/AudioSource.d.ts +1 -1
- package/lib/engine-components/AudioSource.js +1 -1
- package/lib/engine-components/Camera.d.ts +1 -1
- package/lib/engine-components/Camera.js +5 -2
- package/lib/engine-components/Camera.js.map +1 -1
- package/lib/engine-components/CharacterController.d.ts +6 -2
- package/lib/engine-components/CharacterController.js +6 -2
- package/lib/engine-components/CharacterController.js.map +1 -1
- package/lib/engine-components/Collider.d.ts +1 -1
- package/lib/engine-components/Collider.js.map +1 -1
- package/lib/engine-components/Component.d.ts +2 -1
- package/lib/engine-components/Component.js +3 -2
- package/lib/engine-components/Component.js.map +1 -1
- package/lib/engine-components/DragControls.js +4 -1
- package/lib/engine-components/DragControls.js.map +1 -1
- package/lib/engine-components/DropListener.d.ts +1 -0
- package/lib/engine-components/DropListener.js +26 -8
- package/lib/engine-components/DropListener.js.map +1 -1
- package/lib/engine-components/EventList.js +4 -1
- package/lib/engine-components/EventList.js.map +1 -1
- package/lib/engine-components/Joints.d.ts +14 -0
- package/lib/engine-components/Joints.js +14 -0
- package/lib/engine-components/Joints.js.map +1 -1
- package/lib/engine-components/LookAtConstraint.d.ts +1 -1
- package/lib/engine-components/LookAtConstraint.js +1 -1
- package/lib/engine-components/OrbitControls.d.ts +1 -1
- package/lib/engine-components/OrbitControls.js +1 -1
- package/lib/engine-components/Renderer.d.ts +6 -0
- package/lib/engine-components/Renderer.js +6 -0
- package/lib/engine-components/Renderer.js.map +1 -1
- package/lib/engine-components/RendererInstancing.js +5 -3
- package/lib/engine-components/RendererInstancing.js.map +1 -1
- package/lib/engine-components/SceneSwitcher.d.ts +3 -2
- package/lib/engine-components/SceneSwitcher.js +38 -25
- package/lib/engine-components/SceneSwitcher.js.map +1 -1
- package/lib/engine-components/SpectatorCamera.js +15 -7
- package/lib/engine-components/SpectatorCamera.js.map +1 -1
- package/lib/engine-components/SpriteRenderer.d.ts +2 -1
- package/lib/engine-components/SpriteRenderer.js +2 -1
- package/lib/engine-components/SpriteRenderer.js.map +1 -1
- package/lib/engine-components/api.d.ts +1 -0
- package/lib/engine-components/api.js +1 -0
- package/lib/engine-components/api.js.map +1 -1
- package/lib/engine-components/codegen/components.d.ts +0 -1
- package/lib/engine-components/codegen/components.js +0 -1
- package/lib/engine-components/codegen/components.js.map +1 -1
- package/lib/engine-components/export/usdz/extensions/behavior/BehaviourComponents.js +8 -0
- package/lib/engine-components/export/usdz/extensions/behavior/BehaviourComponents.js.map +1 -1
- package/lib/engine-components/timeline/SignalAsset.d.ts +1 -1
- package/lib/engine-components/timeline/SignalAsset.js +1 -1
- package/lib/engine-components/ui/Raycaster.d.ts +3 -2
- package/lib/engine-components/ui/Raycaster.js +3 -2
- package/lib/engine-components/ui/Raycaster.js.map +1 -1
- package/lib/engine-components/ui/RectTransform.d.ts +6 -0
- package/lib/engine-components/ui/RectTransform.js +6 -0
- package/lib/engine-components/ui/RectTransform.js.map +1 -1
- package/lib/engine-components/utils/LookAt.d.ts +2 -1
- package/lib/engine-components/utils/LookAt.js +2 -1
- package/lib/engine-components/utils/LookAt.js.map +1 -1
- package/lib/engine-components/web/CursorFollow.d.ts +1 -1
- package/lib/engine-components/web/CursorFollow.js +1 -1
- package/lib/engine-components/web/HoverAnimation.d.ts +1 -1
- package/lib/engine-components/web/HoverAnimation.js +1 -1
- package/lib/engine-components/web/ViewBox.d.ts +1 -1
- package/lib/engine-components/web/ViewBox.js +1 -1
- package/lib/engine-components/webxr/Avatar.js +2 -0
- package/lib/engine-components/webxr/Avatar.js.map +1 -1
- package/lib/engine-components/webxr/WebARSessionRoot.d.ts +5 -2
- package/lib/engine-components/webxr/WebARSessionRoot.js +5 -2
- package/lib/engine-components/webxr/WebARSessionRoot.js.map +1 -1
- package/lib/engine-components/webxr/WebXR.d.ts +3 -1
- package/lib/engine-components/webxr/WebXR.js +21 -13
- package/lib/engine-components/webxr/WebXR.js.map +1 -1
- package/package.json +6 -6
- package/plugins/vite/poster-client.js +8 -1
- package/src/engine/codegen/register_types.ts +0 -2
- package/src/engine/debug/debug_console.ts +449 -1
- package/src/engine/engine_components.ts +4 -4
- package/src/engine/engine_context.ts +2 -0
- package/src/engine/engine_input.ts +7 -0
- package/src/engine/engine_license.ts +180 -17
- package/src/engine/engine_networking.ts +20 -5
- package/src/engine/engine_physics.ts +3 -3
- package/src/engine/engine_physics_rapier.ts +1 -1
- package/src/engine/engine_serialization_builtin_serializer.ts +1 -1
- package/src/engine/engine_three_utils.ts +4 -2
- package/src/engine/engine_utils.ts +23 -4
- package/src/engine/extensions/extensions.ts +30 -6
- package/src/engine/webcomponents/WebXRButtons.ts +15 -5
- package/src/engine/webcomponents/needle menu/needle-menu-spatial.ts +2 -1
- package/src/engine/webcomponents/needle menu/needle-menu.ts +39 -7
- package/src/engine/webcomponents/needle-engine.ar-overlay.ts +6 -0
- package/src/engine/webcomponents/needle-engine.ts +2 -2
- package/src/engine/xr/NeedleXRSession.ts +120 -24
- package/src/engine/xr/TempXRContext.ts +12 -2
- package/src/engine/xr/usdz.ts +6 -1
- package/src/engine-components/AlignmentConstraint.ts +1 -1
- package/src/engine-components/Animation.ts +1 -1
- package/src/engine-components/Animator.ts +1 -1
- package/src/engine-components/AudioListener.ts +1 -1
- package/src/engine-components/AudioSource.ts +1 -1
- package/src/engine-components/Camera.ts +5 -2
- package/src/engine-components/CharacterController.ts +6 -2
- package/src/engine-components/Collider.ts +1 -1
- package/src/engine-components/Component.ts +5 -4
- package/src/engine-components/DragControls.ts +5 -1
- package/src/engine-components/DropListener.ts +29 -8
- package/src/engine-components/EventList.ts +5 -1
- package/src/engine-components/Joints.ts +14 -0
- package/src/engine-components/LookAtConstraint.ts +1 -1
- package/src/engine-components/OrbitControls.ts +1 -1
- package/src/engine-components/Renderer.ts +6 -0
- package/src/engine-components/RendererInstancing.ts +6 -3
- package/src/engine-components/SceneSwitcher.ts +40 -30
- package/src/engine-components/SpectatorCamera.ts +21 -10
- package/src/engine-components/SpriteRenderer.ts +2 -1
- package/src/engine-components/api.ts +2 -1
- package/src/engine-components/codegen/components.ts +0 -1
- package/src/engine-components/export/usdz/extensions/behavior/BehaviourComponents.ts +11 -0
- package/src/engine-components/timeline/SignalAsset.ts +1 -1
- package/src/engine-components/ui/Raycaster.ts +3 -2
- package/src/engine-components/ui/RectTransform.ts +6 -0
- package/src/engine-components/utils/LookAt.ts +2 -1
- package/src/engine-components/web/CursorFollow.ts +1 -1
- package/src/engine-components/web/HoverAnimation.ts +1 -1
- package/src/engine-components/web/ViewBox.ts +1 -1
- package/src/engine-components/webxr/Avatar.ts +4 -0
- package/src/engine-components/webxr/WebARSessionRoot.ts +7 -3
- package/src/engine-components/webxr/WebXR.ts +23 -13
- package/dist/generateMeshBVH.worker-mO20N_b8.js +0 -21
- package/dist/needle-engine.bundle-4A5NjznD.min.js +0 -1647
- package/dist/needle-engine.bundle-BJg4_HhU.umd.cjs +0 -1647
- package/dist/vendor-BKGa4GE0.min.js +0 -1116
|
@@ -1,9 +1,14 @@
|
|
|
1
|
+
import { dof } from "three/src/nodes/TSL.js";
|
|
2
|
+
|
|
1
3
|
import { isDevEnvironment } from "./debug/index.js";
|
|
2
4
|
import { BUILD_TIME, GENERATOR, PUBLIC_KEY, VERSION } from "./engine_constants.js";
|
|
3
5
|
import { ContextEvent, ContextRegistry } from "./engine_context_registry.js";
|
|
6
|
+
import { onInitialized } from "./engine_lifecycle_api.js";
|
|
7
|
+
import { isLocalNetwork } from "./engine_networking_utils.js";
|
|
4
8
|
import { Context } from "./engine_setup.js";
|
|
5
9
|
import type { IContext } from "./engine_types.js";
|
|
6
10
|
import { getParam } from "./engine_utils.js";
|
|
11
|
+
import { InternalAttributeUtils } from "./engine_utils_attributes.js";
|
|
7
12
|
|
|
8
13
|
const debug = getParam("debuglicense");
|
|
9
14
|
|
|
@@ -67,6 +72,174 @@ function invokeLicenseCheckResultChanged(result: boolean) {
|
|
|
67
72
|
}
|
|
68
73
|
}
|
|
69
74
|
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
// #region Telemetry
|
|
78
|
+
export namespace Telemetry {
|
|
79
|
+
|
|
80
|
+
window.addEventListener("error", (event: ErrorEvent) => {
|
|
81
|
+
sendError(Context.Current, "unhandled_error", event);
|
|
82
|
+
});
|
|
83
|
+
window.addEventListener("unhandledrejection", (event: PromiseRejectionEvent) => {
|
|
84
|
+
sendError(Context.Current, "unhandled_promise_rejection", {
|
|
85
|
+
message: event.reason?.message,
|
|
86
|
+
stack: event.reason?.stack,
|
|
87
|
+
timestamp: Date.now(),
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
onInitialized((ctx => sendPageViewEvent(ctx)), { once: true });
|
|
92
|
+
|
|
93
|
+
function sendPageViewEvent(ctx: IContext) {
|
|
94
|
+
if (!isAllowed(ctx)) {
|
|
95
|
+
if (debug) console.debug("Telemetry is disabled via no-telemetry attribute");
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
return doFetch({
|
|
99
|
+
site_id: "dabb8317376f",
|
|
100
|
+
type: "pageview",
|
|
101
|
+
pathname: window.location.pathname,
|
|
102
|
+
hostname: window.location.hostname,
|
|
103
|
+
page_title: document.title,
|
|
104
|
+
referrer: document.referrer,
|
|
105
|
+
user_agent: navigator.userAgent,
|
|
106
|
+
querystring: window.location.search,
|
|
107
|
+
language: navigator.language,
|
|
108
|
+
screenWidth: window.screen.width,
|
|
109
|
+
screenHeight: window.screen.height,
|
|
110
|
+
event_name: "page_view"
|
|
111
|
+
}).then(res => {
|
|
112
|
+
if (res instanceof Response && res.ok && isLocalNetwork()) {
|
|
113
|
+
const src = ctx.domElement?.getAttribute("src") || "";
|
|
114
|
+
const sessionKey = src + VERSION + GENERATOR + BUILD_TIME + PUBLIC_KEY;
|
|
115
|
+
if (window.sessionStorage.getItem("session_key") !== sessionKey) {
|
|
116
|
+
window.sessionStorage.setItem("session_key", sessionKey);
|
|
117
|
+
sendEvent(ctx, "info", {
|
|
118
|
+
src: ctx.domElement?.getAttribute("src") || "",
|
|
119
|
+
version: VERSION,
|
|
120
|
+
generator: GENERATOR,
|
|
121
|
+
build_time: BUILD_TIME,
|
|
122
|
+
public_key: PUBLIC_KEY,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
})
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function isAllowed(context: IContext | null | undefined): boolean {
|
|
130
|
+
let domElement = context?.domElement as HTMLElement | null;
|
|
131
|
+
if (!domElement) domElement = document.querySelector<HTMLElement>("needle-engine");
|
|
132
|
+
if (!domElement && !context) return false;
|
|
133
|
+
|
|
134
|
+
const attribute = domElement?.getAttribute("no-telemetry");
|
|
135
|
+
if (attribute === "" || attribute === "true" || attribute === "1") {
|
|
136
|
+
if (NEEDLE_ENGINE_LICENSE_TYPE === "pro" || NEEDLE_ENGINE_LICENSE_TYPE === "enterprise") {
|
|
137
|
+
if (debug) console.debug("Telemetry is disabled via no-telemetry attribute");
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const id = "dabb8317376f";
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Sends a telemetry event
|
|
148
|
+
*/
|
|
149
|
+
export async function sendEvent(context: IContext | null | undefined, eventName: string, properties?: Record<string, any>) {
|
|
150
|
+
if (!isAllowed(context)) {
|
|
151
|
+
if (debug) console.debug("Telemetry is disabled");
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
const body = {
|
|
155
|
+
site_id: id,
|
|
156
|
+
type: "custom_event",
|
|
157
|
+
pathname: window.location.pathname,
|
|
158
|
+
event_name: eventName,
|
|
159
|
+
properties: properties ? JSON.stringify(properties) : undefined,
|
|
160
|
+
}
|
|
161
|
+
return doFetch(body);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
type ErrorData = {
|
|
165
|
+
message?: string;
|
|
166
|
+
stack?: string;
|
|
167
|
+
filename?: string;
|
|
168
|
+
lineno?: number;
|
|
169
|
+
colno?: number;
|
|
170
|
+
timestamp?: number;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export async function sendError(context: IContext, errorName: string, error: ErrorData | ErrorEvent | Error) {
|
|
174
|
+
|
|
175
|
+
if (!isAllowed(context)) {
|
|
176
|
+
if (debug) console.debug("Telemetry is disabled");
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (error instanceof ErrorEvent) {
|
|
181
|
+
error = {
|
|
182
|
+
message: error.message,
|
|
183
|
+
stack: error.error?.stack,
|
|
184
|
+
filename: error.filename,
|
|
185
|
+
lineno: error.lineno,
|
|
186
|
+
colno: error.colno,
|
|
187
|
+
timestamp: error.timeStamp || Date.now(),
|
|
188
|
+
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
else if (error instanceof Error) {
|
|
192
|
+
error = {
|
|
193
|
+
message: error.message,
|
|
194
|
+
stack: error.stack,
|
|
195
|
+
timestamp: Date.now(),
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
const body = {
|
|
199
|
+
site_id: id,
|
|
200
|
+
type: "error",
|
|
201
|
+
event_name: errorName || "error",
|
|
202
|
+
properties: JSON.stringify({
|
|
203
|
+
error_name: errorName,
|
|
204
|
+
message: error.message,
|
|
205
|
+
stack: error.stack,
|
|
206
|
+
filename: error.filename,
|
|
207
|
+
lineno: error.lineno,
|
|
208
|
+
colno: error.colno,
|
|
209
|
+
timestamp: error.timestamp,
|
|
210
|
+
})
|
|
211
|
+
}
|
|
212
|
+
return doFetch(body);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function doFetch(body: Record<string, any>) {
|
|
216
|
+
try {
|
|
217
|
+
const url = "https://needle.tools/api/v1/rum/t";
|
|
218
|
+
return fetch(url, {
|
|
219
|
+
method: "POST",
|
|
220
|
+
body: JSON.stringify(body),
|
|
221
|
+
headers: {
|
|
222
|
+
'Content-Type': 'application/json'
|
|
223
|
+
},
|
|
224
|
+
// Ensures request completes even if page unloads
|
|
225
|
+
keepalive: true,
|
|
226
|
+
// Allow CORS requests
|
|
227
|
+
mode: 'cors',
|
|
228
|
+
// Low priority to avoid blocking other requests
|
|
229
|
+
// @ts-ignore
|
|
230
|
+
priority: 'low',
|
|
231
|
+
}).catch(e => {
|
|
232
|
+
if (debug) console.error("Failed to send telemetry", e);
|
|
233
|
+
})
|
|
234
|
+
}
|
|
235
|
+
catch (err) {
|
|
236
|
+
if (debug) console.error(err);
|
|
237
|
+
}
|
|
238
|
+
return Promise.resolve();
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
|
|
70
243
|
ContextRegistry.registerCallback(ContextEvent.ContextRegistered, evt => {
|
|
71
244
|
showLicenseInfo(evt.context);
|
|
72
245
|
handleForbidden(evt.context);
|
|
@@ -81,7 +254,7 @@ async function checkLicense() {
|
|
|
81
254
|
if (runtimeLicenseCheckPromise) return runtimeLicenseCheckPromise;
|
|
82
255
|
if (NEEDLE_ENGINE_LICENSE_TYPE === "basic") {
|
|
83
256
|
try {
|
|
84
|
-
const licenseUrl = "https://
|
|
257
|
+
const licenseUrl = "https://needle.tools/api/v1/needle-engine/check?location=" + encodeURIComponent(window.location.href) + "&version=" + VERSION + "&generator=" + encodeURIComponent(GENERATOR);
|
|
85
258
|
const res = await fetch(licenseUrl, {
|
|
86
259
|
method: "GET",
|
|
87
260
|
}).catch(_err => {
|
|
@@ -341,28 +514,18 @@ async function sendUsageMessageToAnalyticsBackend(context: IContext) {
|
|
|
341
514
|
// We can't send beacons from cross-origin isolated pages
|
|
342
515
|
if (window.crossOriginIsolated) return;
|
|
343
516
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
if (attribute === "" || attribute === "true" || attribute === "1") {
|
|
348
|
-
if (debug) console.debug("Telemetry is disabled");
|
|
349
|
-
return;
|
|
350
|
-
}
|
|
351
|
-
if (debug) console.debug("Telemetry attribute: " + attribute);
|
|
517
|
+
if (!Telemetry.isAllowed(context)) {
|
|
518
|
+
if (debug) console.debug("Telemetry is disabled via no-telemetry attribute");
|
|
519
|
+
return;
|
|
352
520
|
}
|
|
353
521
|
|
|
354
522
|
try {
|
|
355
|
-
const analyticsUrl = "htt" + "ps://
|
|
523
|
+
const analyticsUrl = "htt" + "ps://" + "needle" + ".tools/" + "api/v1/needle-engine/ping";
|
|
356
524
|
if (analyticsUrl) {
|
|
357
525
|
|
|
358
526
|
// current url without query parameters
|
|
359
527
|
const currentUrl = window.location.href.split("?")[0];
|
|
360
|
-
|
|
361
|
-
let endpoint = "api/v2/new/request";
|
|
362
|
-
if (!analyticsUrl.endsWith("/")) endpoint = "/" + endpoint;
|
|
363
528
|
const license = NEEDLE_ENGINE_LICENSE_TYPE;
|
|
364
|
-
const finalUrl = `${analyticsUrl}${endpoint}`;
|
|
365
|
-
if (debug) console.debug("Sending beacon");
|
|
366
529
|
|
|
367
530
|
const beaconData = {
|
|
368
531
|
license,
|
|
@@ -376,7 +539,7 @@ async function sendUsageMessageToAnalyticsBackend(context: IContext) {
|
|
|
376
539
|
build_time: BUILD_TIME,
|
|
377
540
|
public_key: PUBLIC_KEY,
|
|
378
541
|
};
|
|
379
|
-
const res = navigator.sendBeacon?.(
|
|
542
|
+
const res = navigator.sendBeacon?.(analyticsUrl, JSON.stringify(beaconData));
|
|
380
543
|
if (debug) console.debug("Sent beacon: " + res);
|
|
381
544
|
}
|
|
382
545
|
}
|
|
@@ -384,4 +547,4 @@ async function sendUsageMessageToAnalyticsBackend(context: IContext) {
|
|
|
384
547
|
if (debug)
|
|
385
548
|
console.log("Failed to send non-commercial usage message to analytics backend", err);
|
|
386
549
|
}
|
|
387
|
-
}
|
|
550
|
+
}
|
|
@@ -6,6 +6,7 @@ import { type Websocket } from 'websocket-ts';
|
|
|
6
6
|
|
|
7
7
|
import * as schemes from "../engine-schemes/schemes.js";
|
|
8
8
|
import { isDevEnvironment } from './debug/index.js';
|
|
9
|
+
import { Telemetry } from './engine_license.js';
|
|
9
10
|
import { PeerNetworking } from './engine_networking_peer.js';
|
|
10
11
|
import { type IModel, type INetworkConnection, SendQueue } from './engine_networking_types.js';
|
|
11
12
|
import { isHostedOnGlitch } from './engine_networking_utils.js';
|
|
@@ -629,7 +630,7 @@ export class NetworkConnection implements INetworkConnection {
|
|
|
629
630
|
return;
|
|
630
631
|
}
|
|
631
632
|
|
|
632
|
-
console.debug("
|
|
633
|
+
console.debug("Connecting to networking backend on\n" + networkingServerUrl)
|
|
633
634
|
const pkg = await import('websocket-ts');
|
|
634
635
|
// @ts-ignore
|
|
635
636
|
const WebsocketBuilder = pkg.default?.WebsocketBuilder ?? pkg.WebsocketBuilder;
|
|
@@ -641,8 +642,8 @@ export class NetworkConnection implements INetworkConnection {
|
|
|
641
642
|
this._connectingToWebsocketPromise = null;
|
|
642
643
|
this._ws = ws;
|
|
643
644
|
this.connected = true;
|
|
644
|
-
if (isDevEnvironment() || debugNet) console.log("
|
|
645
|
-
else console.debug("
|
|
645
|
+
if (isDevEnvironment() || debugNet) console.log("Connected to networking backend\n" + networkingServerUrl);
|
|
646
|
+
else console.debug("Connected to networking backend", networkingServerUrl);
|
|
646
647
|
resolve(true);
|
|
647
648
|
this.onSendQueued(SendQueue.OnConnection);
|
|
648
649
|
})
|
|
@@ -656,10 +657,13 @@ export class NetworkConnection implements INetworkConnection {
|
|
|
656
657
|
console.error(msg);
|
|
657
658
|
})
|
|
658
659
|
.onError((_e) => {
|
|
659
|
-
console.error("
|
|
660
|
+
console.error("Websocket connection failed...");
|
|
660
661
|
resolve(false);
|
|
662
|
+
Telemetry.sendEvent(this.context, "networking", {
|
|
663
|
+
event: "connection_error",
|
|
664
|
+
});
|
|
661
665
|
})
|
|
662
|
-
.onRetry(() => { console.log("
|
|
666
|
+
.onRetry(() => { console.log("Retry connecting to networking websocket") })
|
|
663
667
|
.build();
|
|
664
668
|
ws.addEventListener(pkg.WebsocketEvent.message, (socket, msg) => {
|
|
665
669
|
this.onMessage(socket, msg);
|
|
@@ -727,6 +731,9 @@ export class NetworkConnection implements INetworkConnection {
|
|
|
727
731
|
"server did not send connection id", connection.id);
|
|
728
732
|
console.debug("Your id is: " + connection.id, this.context.alias ?? "");
|
|
729
733
|
this._connectionId = connection.id;
|
|
734
|
+
Telemetry.sendEvent(this.context, "networking", {
|
|
735
|
+
event: "connected",
|
|
736
|
+
});
|
|
730
737
|
}
|
|
731
738
|
}
|
|
732
739
|
else console.warn("Expected connection id in " + message.key);
|
|
@@ -754,6 +761,10 @@ export class NetworkConnection implements INetworkConnection {
|
|
|
754
761
|
}
|
|
755
762
|
|
|
756
763
|
this.onSendQueued(SendQueue.OnRoomJoin);
|
|
764
|
+
Telemetry.sendEvent(this.context, "networking", {
|
|
765
|
+
event: "joined_room",
|
|
766
|
+
room: this._currentRoomName,
|
|
767
|
+
});
|
|
757
768
|
break;
|
|
758
769
|
|
|
759
770
|
case RoomEvents.LeftRoom:
|
|
@@ -765,6 +776,10 @@ export class NetworkConnection implements INetworkConnection {
|
|
|
765
776
|
this._currentInRoom.length = 0;
|
|
766
777
|
if (debugnetBin || isDevEnvironment()) console.debug("Left Needle Engine Room: " + model.room);
|
|
767
778
|
}
|
|
779
|
+
Telemetry.sendEvent(this.context, "networking", {
|
|
780
|
+
event: "left_room",
|
|
781
|
+
room: model.room,
|
|
782
|
+
});
|
|
768
783
|
break;
|
|
769
784
|
case RoomEvents.UserJoinedRoom:
|
|
770
785
|
if (message.data) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { getRaycastMesh } from '@needle-tools/gltf-progressive';
|
|
2
2
|
import { ArrayCamera, Box3, BufferGeometry, Camera, type Intersection, Layers, Line, Matrix3, Matrix4, Mesh, Object3D, PerspectiveCamera, Plane, Ray, Raycaster, SkinnedMesh, Sphere, SphereGeometry, Vector2, Vector3 } from 'three'
|
|
3
3
|
import { GroundedSkybox } from 'three/examples/jsm/objects/GroundedSkybox.js';
|
|
4
|
-
import type {
|
|
4
|
+
import type { ComputeBVHOptions, GeometryBVH, MeshBVH, StaticGeometryGenerator } from 'three-mesh-bvh';
|
|
5
5
|
import type { GenerateMeshBVHWorker } from 'three-mesh-bvh/src/workers/GenerateMeshBVHWorker.js';
|
|
6
6
|
|
|
7
7
|
import { isDevEnvironment } from './debug/index.js';
|
|
@@ -762,7 +762,7 @@ namespace NEMeshBVH {
|
|
|
762
762
|
return true;
|
|
763
763
|
}
|
|
764
764
|
else if (method instanceof Sphere) {
|
|
765
|
-
const bvh = geom.boundsTree;
|
|
765
|
+
const bvh = geom.boundsTree as MeshBVH | undefined;
|
|
766
766
|
if (bvh) {
|
|
767
767
|
const sphere = method;
|
|
768
768
|
// Gizmos.DrawWireSphere(sphere.center, sphere.radius, 0xdddd00, 1, false);
|
|
@@ -789,7 +789,7 @@ namespace NEMeshBVH {
|
|
|
789
789
|
let _acceleratedRaycast: Function | null = null;
|
|
790
790
|
let _MeshBVH: ConstructorConcrete<MeshBVH> | null = null;
|
|
791
791
|
let _StaticGeometryGenerator: ConstructorConcrete<StaticGeometryGenerator> | null = null;
|
|
792
|
-
let _computeBoundsTree: ((
|
|
792
|
+
let _computeBoundsTree: (( options?: ComputeBVHOptions ) => GeometryBVH) | null = null;
|
|
793
793
|
|
|
794
794
|
function loadMeshBVHLibrary() {
|
|
795
795
|
if (didLoadMeshBVHLibrary) return;
|
|
@@ -273,7 +273,7 @@ export class RapierPhysics implements IPhysicsEngine {
|
|
|
273
273
|
if (MODULES.RAPIER_PHYSICS.MAYBEMODULE == undefined) {
|
|
274
274
|
if (debugPhysics) console.trace("Loading rapier physics engine");
|
|
275
275
|
const module = await MODULES.RAPIER_PHYSICS.load();
|
|
276
|
-
// https://github.com/dimforge/rapier/issues/811
|
|
276
|
+
// https://github.com/dimforge/rapier/issues/811 < but single object parameter still warns
|
|
277
277
|
await module.init();
|
|
278
278
|
}
|
|
279
279
|
if (debugPhysics) console.log("Physics engine initialized, creating world...");
|
|
@@ -337,7 +337,7 @@ class EventListSerializer extends TypeSerializer {
|
|
|
337
337
|
args = call.arguments.map(deserializeArgument);
|
|
338
338
|
}
|
|
339
339
|
const method = target[call.method];
|
|
340
|
-
if (
|
|
340
|
+
if (method === undefined) {
|
|
341
341
|
console.warn(`EventList method not found: \"${call.method}\" on ${target?.name}`);
|
|
342
342
|
}
|
|
343
343
|
else {
|
|
@@ -242,8 +242,10 @@ export function setWorldPosition(obj: Object3D, val: Vector3): Object3D {
|
|
|
242
242
|
const wp = _worldPositions.get();
|
|
243
243
|
if (val !== wp)
|
|
244
244
|
wp.copy(val);
|
|
245
|
-
|
|
246
|
-
|
|
245
|
+
|
|
246
|
+
if (obj.parent !== null)
|
|
247
|
+
obj.parent.worldToLocal(wp);
|
|
248
|
+
|
|
247
249
|
obj.position.set(wp.x, wp.y, wp.z);
|
|
248
250
|
return obj;
|
|
249
251
|
}
|
|
@@ -606,7 +606,8 @@ export namespace DeviceUtilities {
|
|
|
606
606
|
/** @returns `true` if we're currently on an iPad */
|
|
607
607
|
export function isiPad() {
|
|
608
608
|
if (__isiPad !== undefined) return __isiPad;
|
|
609
|
-
|
|
609
|
+
const userAgent = navigator.userAgent.toLowerCase();
|
|
610
|
+
return __isiPad = /iPad/.test(navigator.userAgent) || userAgent.includes("macintosh") && "ontouchend" in document;
|
|
610
611
|
}
|
|
611
612
|
|
|
612
613
|
let __isAndroidDevice: boolean | undefined;
|
|
@@ -623,17 +624,25 @@ export namespace DeviceUtilities {
|
|
|
623
624
|
return __isMozillaXR = /WebXRViewer\//i.test(navigator.userAgent);
|
|
624
625
|
}
|
|
625
626
|
|
|
627
|
+
let __isNeedleAppClip: boolean | undefined;
|
|
628
|
+
/** @returns `true` if we're currently in the Needle App Clip */
|
|
629
|
+
export function isNeedleAppClip() {
|
|
630
|
+
if (__isNeedleAppClip !== undefined) return __isNeedleAppClip;
|
|
631
|
+
return __isNeedleAppClip = /NeedleAppClip\//i.test(navigator.userAgent);
|
|
632
|
+
}
|
|
633
|
+
|
|
626
634
|
let __isMacOS: boolean | undefined;
|
|
627
635
|
// https://developer.mozilla.org/en-US/docs/Web/API/Navigator/userAgentData
|
|
628
636
|
/** @returns `true` for MacOS devices */
|
|
629
637
|
export function isMacOS() {
|
|
630
638
|
if (__isMacOS !== undefined) return __isMacOS;
|
|
639
|
+
if (isiOS() || isiPad()) return __isMacOS = false;
|
|
640
|
+
const userAgent = navigator.userAgent.toLowerCase();
|
|
631
641
|
if (navigator.userAgentData) {
|
|
632
642
|
// Use modern UA Client Hints API if available
|
|
633
643
|
return __isMacOS = navigator.userAgentData.platform === 'macOS';
|
|
634
644
|
} else {
|
|
635
645
|
// Fallback to user agent string parsing
|
|
636
|
-
const userAgent = navigator.userAgent.toLowerCase();
|
|
637
646
|
return __isMacOS = userAgent.includes('mac os x') || userAgent.includes('macintosh');
|
|
638
647
|
}
|
|
639
648
|
}
|
|
@@ -642,13 +651,13 @@ export namespace DeviceUtilities {
|
|
|
642
651
|
/** @returns `true` for VisionOS devices */
|
|
643
652
|
export function isVisionOS() {
|
|
644
653
|
if (__isVisionOS !== undefined) return __isVisionOS;
|
|
645
|
-
return __isVisionOS = (
|
|
654
|
+
return __isVisionOS = (isiPad() && "xr" in navigator && supportsQuickLookAR());
|
|
646
655
|
}
|
|
647
656
|
|
|
648
657
|
let __isiOS: boolean | undefined;
|
|
649
658
|
const iosDevices = ['iPad Simulator', 'iPhone Simulator', 'iPod Simulator', 'iPad', 'iPhone', 'iPod'];
|
|
650
659
|
|
|
651
|
-
/** @returns `true` for
|
|
660
|
+
/** @returns `true` for mobile Apple devices like iPad, iPhone, iPod, Vision Pro, ... */
|
|
652
661
|
export function isiOS() {
|
|
653
662
|
if (__isiOS !== undefined) return __isiOS;
|
|
654
663
|
// eslint-disable-next-line deprecation/deprecation
|
|
@@ -725,6 +734,16 @@ export namespace DeviceUtilities {
|
|
|
725
734
|
else __chromeVersion = null;
|
|
726
735
|
return __chromeVersion;
|
|
727
736
|
}
|
|
737
|
+
|
|
738
|
+
let __safariVersion: string | null | undefined;
|
|
739
|
+
export function getSafariVersion() {
|
|
740
|
+
if (__safariVersion !== undefined) return __safariVersion;
|
|
741
|
+
const match = navigator.userAgent.match(/Version\/(\d+\.\d+)/);
|
|
742
|
+
if (match && isSafari()) {
|
|
743
|
+
__safariVersion = match[1];
|
|
744
|
+
} else __safariVersion = null;
|
|
745
|
+
return __safariVersion;
|
|
746
|
+
}
|
|
728
747
|
}
|
|
729
748
|
|
|
730
749
|
/**
|
|
@@ -30,20 +30,44 @@ const KHR_ANIMATIONPOINTER_IMPORT = import("@needle-tools/three-animation-pointe
|
|
|
30
30
|
});
|
|
31
31
|
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
/**
|
|
34
|
+
* Callback type for glTF import plugins. See {@link INeedleGLTFExtensionPlugin.onImport}
|
|
35
|
+
*/
|
|
36
|
+
export type OnImportCallback = (loader: GLTFLoader, url: string, context: Context) => void;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Callback type for glTF export plugins. See {@link INeedleGLTFExtensionPlugin.onExport}
|
|
40
|
+
*/
|
|
41
|
+
export type OnExportCallback = (exporter: GLTFExporter, context: Context) => void;
|
|
35
42
|
|
|
36
43
|
/**
|
|
37
|
-
* Interface for registering custom glTF extensions to the Needle Engine GLTFLoaders.
|
|
44
|
+
* Interface for registering custom glTF extensions to the Needle Engine GLTFLoaders.
|
|
45
|
+
* Register your plugin using the {@link addCustomExtensionPlugin} method
|
|
38
46
|
*/
|
|
39
47
|
export interface INeedleGLTFExtensionPlugin {
|
|
40
48
|
/** The Name of your plugin */
|
|
41
49
|
name: string;
|
|
42
|
-
/** Called before starting to load a glTF file. This callback can be used to add custom extensions to the GLTFLoader
|
|
50
|
+
/** Called before starting to load a glTF file. This callback can be used to add custom extensions to the [GLTFLoader](https://threejs.org/docs/#GLTFLoader.register)
|
|
51
|
+
*
|
|
52
|
+
* @example Add a custom extension to the GLTFloader
|
|
53
|
+
* ```ts
|
|
54
|
+
* onImport: (loader, url, context) => {
|
|
55
|
+
* loader.register((parser) => new MyCustomExtension(parser));
|
|
56
|
+
* }
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
43
59
|
onImport?: OnImportCallback;
|
|
44
|
-
/** Called after
|
|
60
|
+
/** Called after a glTF file has been loaded */
|
|
45
61
|
onLoaded?: (url: string, gltf: GLTF, context: Context) => void;
|
|
46
|
-
/** Called before starting to export a glTF file. This callback can be used to add custom extensions to the GLTFExporter
|
|
62
|
+
/** Called before starting to export a glTF file. This callback can be used to add custom extensions to the [GLTFExporter](https://threejs.org/docs/#examples/en/exporters/GLTFExporter.register)
|
|
63
|
+
*
|
|
64
|
+
* @example Add a custom extension to the GLTFExporter
|
|
65
|
+
* ```ts
|
|
66
|
+
* onExport: (exporter, context) => {
|
|
67
|
+
* exporter.register((writer) => new MyCustomExportExtension(writer));
|
|
68
|
+
* }
|
|
69
|
+
*
|
|
70
|
+
*/
|
|
47
71
|
onExport?: OnExportCallback;
|
|
48
72
|
}
|
|
49
73
|
|
|
@@ -57,13 +57,16 @@ export class WebXRButtonFactory {
|
|
|
57
57
|
this._quicklookButton = button;
|
|
58
58
|
button.dataset["needle"] = "quicklook-button";
|
|
59
59
|
const supportsQuickLook = DeviceUtilities.supportsQuickLookAR();
|
|
60
|
+
let buttonText = "View in AR";
|
|
60
61
|
// we can immediately enter this scene, because the platform supports rel="ar" links
|
|
61
|
-
if (
|
|
62
|
-
|
|
62
|
+
if (DeviceUtilities.isVisionOS()) {
|
|
63
|
+
buttonText = "View in AR";
|
|
63
64
|
}
|
|
64
|
-
else {
|
|
65
|
-
|
|
65
|
+
else if (supportsQuickLook || DeviceUtilities.isiOS()) {
|
|
66
|
+
buttonText = "Open in Quicklook";
|
|
66
67
|
}
|
|
68
|
+
|
|
69
|
+
button.innerText = buttonText;
|
|
67
70
|
button.prepend(getIconElement("view_in_ar"));
|
|
68
71
|
|
|
69
72
|
let createdExporter = false;
|
|
@@ -209,13 +212,20 @@ export class WebXRButtonFactory {
|
|
|
209
212
|
}
|
|
210
213
|
|
|
211
214
|
private updateSessionSupported(button: HTMLButtonElement, mode: XRSessionMode) {
|
|
215
|
+
if (mode === "immersive-ar" && DeviceUtilities.isiOS() && !DeviceUtilities.isVisionOS()) {
|
|
216
|
+
// on iOS, we can forward to the AppClip experience from the same button,
|
|
217
|
+
// so we always show the button. No AppClip support on VisionOS for now, so
|
|
218
|
+
// button state depends on WebXR support there.
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
|
|
212
222
|
if (!("xr" in navigator)) {
|
|
213
223
|
button.style.display = "none";
|
|
214
224
|
return;
|
|
215
225
|
}
|
|
216
226
|
NeedleXRSession.isSessionSupported(mode).then(supported => {
|
|
217
227
|
button.style.display = !supported ? "none" : "";
|
|
218
|
-
if (isDevEnvironment() && !supported) console.log("[WebXR] \"" + mode + "\" is not supported on this device
|
|
228
|
+
if (isDevEnvironment() && !supported) console.log("[WebXR] \"" + mode + "\" is not supported on this device. Make sure your server runs using HTTPS and you have a device connected that supports " + mode);
|
|
219
229
|
});
|
|
220
230
|
}
|
|
221
231
|
|
|
@@ -104,7 +104,8 @@ export class NeedleSpatialMenu {
|
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
const xr = this._context.xr;
|
|
107
|
-
|
|
107
|
+
const isImmersiveXR = xr?.running && (xr?.isPassThrough || xr?.isVR)
|
|
108
|
+
if (!isImmersiveXR) {
|
|
108
109
|
if (this._wasInXR) {
|
|
109
110
|
this._wasInXR = false;
|
|
110
111
|
this.onExitXR();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Context } from "../../engine_context.js";
|
|
2
|
-
import { hasCommercialLicense, onLicenseCheckResultChanged } from "../../engine_license.js";
|
|
2
|
+
import { hasCommercialLicense, onLicenseCheckResultChanged, Telemetry } from "../../engine_license.js";
|
|
3
3
|
import { isLocalNetwork } from "../../engine_networking_utils.js";
|
|
4
4
|
import { DeviceUtilities, getParam } from "../../engine_utils.js";
|
|
5
5
|
import { onXRSessionStart, XRSessionEventArgs } from "../../xr/events.js";
|
|
@@ -149,6 +149,9 @@ export class NeedleMenu {
|
|
|
149
149
|
else console.error("NeedleMenu: onclick is not a valid link", buttoninfo.onclick);
|
|
150
150
|
}
|
|
151
151
|
}
|
|
152
|
+
Telemetry.sendEvent(this._context, "needle-menu", {
|
|
153
|
+
action: "button_added_via_postmessage",
|
|
154
|
+
});
|
|
152
155
|
this._menu.appendChild(button);
|
|
153
156
|
}
|
|
154
157
|
else if (debug) console.error("NeedleMenu: unknown postMessage event", data);
|
|
@@ -601,16 +604,15 @@ export class NeedleMenuElement extends HTMLElement {
|
|
|
601
604
|
bottom: calc(100% + 5px);
|
|
602
605
|
z-index: 100;
|
|
603
606
|
width: auto;
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
+
max-width: 90vw;
|
|
608
|
+
left: 50%;
|
|
609
|
+
transform: translateX(-50%);
|
|
610
|
+
padding: .2rem 1em;
|
|
607
611
|
|
|
608
612
|
}
|
|
609
613
|
.compact.logo-hidden .foldout {
|
|
610
614
|
/** for when there's no logo we want to center the foldout **/
|
|
611
615
|
min-width: 24ch;
|
|
612
|
-
margin-left: 50%;
|
|
613
|
-
transform: translateX(calc(-50% - .2rem));
|
|
614
616
|
}
|
|
615
617
|
|
|
616
618
|
.compact.top .foldout {
|
|
@@ -936,12 +938,39 @@ export class NeedleMenuElement extends HTMLElement {
|
|
|
936
938
|
/** @private foldout container used in compact mode */
|
|
937
939
|
private readonly foldout: HTMLDivElement;
|
|
938
940
|
|
|
941
|
+
|
|
942
|
+
private readonly trackedElements: WeakSet<Node> = new WeakSet();
|
|
943
|
+
private trackElement(el: Node) {
|
|
944
|
+
if (this.trackedElements.has(el)) return;
|
|
945
|
+
this.trackedElements.add(el);
|
|
946
|
+
el.addEventListener("click", (evt) => {
|
|
947
|
+
Telemetry.sendEvent(this._context, "needle-menu", {
|
|
948
|
+
action: "button_clicked",
|
|
949
|
+
element: evt.target instanceof Node ? evt.target.nodeName : el.nodeName,
|
|
950
|
+
label: el.textContent,
|
|
951
|
+
title: (el instanceof HTMLElement) ? el.title : undefined,
|
|
952
|
+
pointerid: (evt instanceof PointerEvent) ? evt.pointerId : undefined,
|
|
953
|
+
});
|
|
954
|
+
});
|
|
955
|
+
// el.addEventListener("pointerenter", (evt) => {
|
|
956
|
+
// Telemetry.sendEvent(this._context, "needle-menu", {
|
|
957
|
+
// action: "button_hovered",
|
|
958
|
+
// element: evt.target instanceof Node ? evt.target.nodeName : el.nodeName,
|
|
959
|
+
// label: el.textContent,
|
|
960
|
+
// title: (el instanceof HTMLElement) ? el.title : undefined,
|
|
961
|
+
// pointerid: (evt instanceof PointerEvent) ? evt.pointerId : undefined,
|
|
962
|
+
// });
|
|
963
|
+
// });
|
|
964
|
+
}
|
|
965
|
+
|
|
939
966
|
append(...nodes: (string | Node)[]): void {
|
|
940
967
|
for (const node of nodes) {
|
|
941
968
|
if (typeof node === "string") {
|
|
942
969
|
const element = document.createTextNode(node);
|
|
970
|
+
this.trackElement(element);
|
|
943
971
|
this.options.appendChild(element);
|
|
944
972
|
} else {
|
|
973
|
+
this.trackElement(node);
|
|
945
974
|
this.options.appendChild(node);
|
|
946
975
|
}
|
|
947
976
|
}
|
|
@@ -988,9 +1017,10 @@ export class NeedleMenuElement extends HTMLElement {
|
|
|
988
1017
|
if (node.class) {
|
|
989
1018
|
button.classList.add(node.class);
|
|
990
1019
|
}
|
|
1020
|
+
|
|
991
1021
|
node = button as unknown as T;
|
|
992
1022
|
}
|
|
993
|
-
|
|
1023
|
+
this.trackElement(node);
|
|
994
1024
|
const res = this.options.appendChild(node);
|
|
995
1025
|
return res;
|
|
996
1026
|
}
|
|
@@ -998,8 +1028,10 @@ export class NeedleMenuElement extends HTMLElement {
|
|
|
998
1028
|
for (const node of nodes) {
|
|
999
1029
|
if (typeof node === "string") {
|
|
1000
1030
|
const element = document.createTextNode(node);
|
|
1031
|
+
this.trackElement(element);
|
|
1001
1032
|
this.options.prepend(element);
|
|
1002
1033
|
} else {
|
|
1034
|
+
this.trackElement(node);
|
|
1003
1035
|
this.options.prepend(node);
|
|
1004
1036
|
}
|
|
1005
1037
|
}
|