UNPKG

@needle-tools/engine

Version:

Needle Engine is a web-based runtime for 3D apps. It runs on your machine for development with great integrations into editors like Unity or Blender - and can be deployed onto any device! It is flexible, extensible and networking and XR are built-in.

317 lines (309 loc) • 10.8 kB
import { ContextRegistry } from "../engine_context_registry.js"; import { isLocalNetwork } from "../engine_networking_utils.js"; import { getParam } from "../engine_utils.js"; const debug = getParam("debugdebug"); let hide = false; if (getParam("noerrors") || getParam("nooverlaymessages")) hide = true; const globalErrorContainerKey = "needle_engine_global_error_container"; export var LogType; (function (LogType) { LogType[LogType["Log"] = 0] = "Log"; LogType[LogType["Warn"] = 1] = "Warn"; LogType[LogType["Error"] = 2] = "Error"; })(LogType || (LogType = {})); export function getErrorCount() { return _errorCount; } const _errorListeners = new Array(); /** Register callback when a new error happens */ export function onError(cb) { _errorListeners.push(cb); } /** Unregister error callback */ export function offError(cb) { _errorListeners.splice(_errorListeners.indexOf(cb), 1); } let isInvokingErrorListeners = false; function invokeErrorListeners(...args) { if (isInvokingErrorListeners) return; // prevent infinite loop isInvokingErrorListeners = true; try { for (let i = 0; i < _errorListeners.length; i++) { _errorListeners[i](...args); } } catch (e) { console.error(e); } isInvokingErrorListeners = false; } const originalConsoleError = console.error; const patchedConsoleError = function (...args) { originalConsoleError.apply(console, args); onParseError(args); addLog(LogType.Error, args, null, null); onReceivedError(...args); }; /** Set false to prevent overlay messages from being shown */ export function setAllowBalloonMessages(allow) { hide = !allow; if (allow) console.error = patchedConsoleError; else console.error = originalConsoleError; } /** * @deprecated Use {@link setAllowBalloonMessages} instead */ export function setAllowOverlayMessages(allow) { return setAllowBalloonMessages(allow); } export function makeErrorsVisibleForDevelopment() { if (hide) return; if (debug) console.warn("Patch console", window.location.hostname); console.error = patchedConsoleError; window.addEventListener("error", (event) => { if (!event) return; const message = event.error; if (message === undefined) { if (isLocalNetwork()) console.warn("Received unknown error", event, event.target); return; } addLog(LogType.Error, message, event.filename, event.lineno); onReceivedError(event); }, true); window.addEventListener("unhandledrejection", (event) => { if (hide) return; if (!event) return; if (event.reason) addLog(LogType.Error, event.reason.message, event.reason.stack); else addLog(LogType.Error, "unhandled rejection"); onReceivedError(event); }); } let _errorCount = 0; function onReceivedError(...args) { _errorCount += 1; invokeErrorListeners(...args); } function onParseError(args) { if (Array.isArray(args)) { for (let i = 0; i < args.length; i++) { const arg = args[i]; if (typeof arg === "string" && arg.startsWith("THREE.PropertyBinding: Trying to update node for track:")) { args[i] = "Some animated objects couldn't be found: see console for details"; } } } } export function addLog(type, message, _file, _line) { if (hide) return; const context = ContextRegistry.Current; const domElement = context?.domElement ?? document.querySelector("needle-engine"); if (!domElement) return; if (Array.isArray(message)) { let newMessage = ""; for (let i = 0; i < message.length; i++) { let msg = message[i]; if (msg instanceof Error) { msg = msg.message; } if (typeof msg === "object") continue; if (i > 0) newMessage += " "; newMessage += msg; } message = newMessage; } if (!message || message.length <= 0) return; showMessage(type, domElement, message); } // function getLocation(err: Error): string { // const location = err.stack; // console.log(location); // if (location) { // locationRegex.exec(location); // const match = locationRegex.exec(location); // return match ? match[1] : ""; // } // return ""; // } /** * */ const currentMessages = new Map(); function showMessage(type, element, msg) { if (msg === null || msg === undefined) return; const container = getLogsContainer(element); if (container.childElementCount >= 20) { const last = container.lastElementChild; returnMessageContainer(last); } // truncate long messages before they go into the cache/set if (msg.length > 400) msg = msg.substring(0, 400) + "..."; const key = msg; if (currentMessages.has(key)) { return; } const msgcontainer = getMessageContainer(type, msg); container.prepend(msgcontainer); const removeFunction = () => { currentMessages.delete(key); returnMessageContainer(msgcontainer); }; currentMessages.set(key, removeFunction); setTimeout(removeFunction, 10_000); } /** * Clear all overlay messages from the screen */ export function clearMessages() { if (debug) console.log("Clearing messages"); for (const cur of currentMessages.values()) { cur?.call(cur); } currentMessages.clear(); } const logsContainerStyles = ` @import url('https://fonts.googleapis.com/css2?family=Roboto+Flex:opsz,wght@8..144,100..1000&display=swap'); div[data-needle_engine_debug_overlay] { font-family: 'Roboto Flex', sans-serif; font-weight: 400; font-size: 16px; } div[data-needle_engine_debug_overlay] strong { font-weight: 700; } div[data-needle_engine_debug_overlay] a { color: white; text-decoration: none; border-bottom: 1px solid rgba(255, 255, 255, 0.3); } div[data-needle_engine_debug_overlay] a:hover { text-decoration: none; border: none; } div[data-needle_engine_debug_overlay] .log strong { color: rgba(200,200,200,.9); } div[data-needle_engine_debug_overlay] .warn strong { color: rgba(255,255,230, 1); } div[data-needle_engine_debug_overlay] .error strong { color: rgba(255,100,120, 1); } `; function getLogsContainer(domElement) { if (!globalThis[globalErrorContainerKey]) { globalThis[globalErrorContainerKey] = new Map(); } const errorsMap = globalThis[globalErrorContainerKey]; if (errorsMap.has(domElement)) { return errorsMap.get(domElement); } else { const container = document.createElement("div"); errorsMap.set(domElement, container); container.setAttribute("data-needle_engine_debug_overlay", ""); container.classList.add("debug-container"); container.style.cssText = ` position: absolute; top: 0; right: 5px; padding-top: 0px; max-width: 70%; max-height: calc(100% - 5px); z-index: 9999999999; pointer-events: scroll; display: flex; align-items: end; flex-direction: column; color: white; overflow: auto; word-break: break-word; `; if (domElement.shadowRoot) domElement.shadowRoot.appendChild(container); else domElement.appendChild(container); const style = document.createElement("style"); style.innerHTML = logsContainerStyles; container.appendChild(style); return container; } } const typeSymbol = Symbol("logtype"); const containerCache = new Map(); function returnMessageContainer(container) { container.remove(); const type = container[typeSymbol]; const containers = containerCache.get(type) ?? []; containers.push(container); containerCache.set(type, containers); } function getMessageContainer(type, msg) { if (containerCache.has(type)) { const containers = containerCache.get(type); if (containers.length > 0) { const container = containers.pop(); container.innerHTML = msg; return container; } } const element = document.createElement("div"); element.setAttribute("data-id", "__needle_engine_debug_overlay"); element.style.marginRight = "5px"; element.style.padding = ".5em"; element.style.backgroundColor = "rgba(0,0,0,.9)"; element.style.marginTop = "5px"; element.style.marginBottom = "3px"; element.style.borderRadius = "8px"; element.style.pointerEvents = "all"; element.style.userSelect = "text"; element.style.maxWidth = "250px"; element.style.whiteSpace = "pre-wrap"; element.style["backdrop-filter"] = "blur(10px)"; element.style["-webkit-backdrop-filter"] = "blur(10px)"; element.style.backgroundColor = "rgba(20,20,20,.8)"; element.style.boxShadow = "inset 0 0 80px rgba(0,0,0,.2), 0 0 5px rgba(0,0,0,.2)"; element.style.border = "1px solid rgba(160,160,160,.2)"; element[typeSymbol] = type; switch (type) { case LogType.Log: element.classList.add("log"); element.style.color = "rgba(200,200,200,.7)"; element.style.backgroundColor = "rgba(40,40,40,.7)"; // element.style.backgroundColor = "rgba(200,200,200,.5)"; break; case LogType.Warn: element.classList.add("warn"); element.style.color = "rgb(255, 255, 150)"; element.style.backgroundColor = "rgba(50,50,20,.8)"; // element.style.backgroundColor = "rgba(245,245,50,.5)"; break; case LogType.Error: element.classList.add("error"); element.style.color = "rgb(255, 50, 50"; element.style.backgroundColor = "rgba(50,20,20,.8)"; // element.style.backgroundColor = "rgba(255,50,50,.5)"; break; } element.title = "Open the browser console (F12) for more information"; // msg = msg.replace(/[\n\r]/g, "<br/>"); // console.log(msg); element.innerHTML = msg; return element; } //# sourceMappingURL=debug_overlay.js.map