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.

278 lines (277 loc) • 10.3 kB
import { getParam } from "../engine_utils.js"; import { getErrorCount } from "./debug_overlay.js"; let consoleInstance = undefined; let consoleHtmlElement = null; let consoleSwitchButton = null; let isLoading = false; let isVisible = false; let watchInterval = null; const defaultButtonIcon = "terminal"; const showConsole = getParam("console"); if (showConsole) { showDebugConsole(); } const $defaultConsoleParent = Symbol("consoleParent"); export function showDebugConsole() { if (consoleInstance) { isVisible = true; consoleInstance.showSwitch(); return; } createConsole(); } export function hideDebugConsole() { if (!consoleInstance) return; isVisible = false; consoleInstance.hide(); consoleInstance.hideSwitch(); } function beginWatchingLogs() { if (watchInterval) return; watchInterval = setInterval(consoleElementUpdateInterval, 500); } let lastErrorCount = 0; function consoleElementUpdateInterval() { const currentCount = getErrorCount(); const receivedNewErrors = currentCount !== lastErrorCount; lastErrorCount = currentCount; if (receivedNewErrors) { onNewConsoleErrors(); } } function onNewConsoleErrors() { showDebugConsole(); if (consoleSwitchButton) { consoleSwitchButton.setAttribute("error", "true"); consoleSwitchButton.innerText = "🤬"; } } function onConsoleSwitchButtonClicked() { if (consoleSwitchButton) { consoleSwitchButton.removeAttribute("error"); consoleSwitchButton.innerText = defaultButtonIcon; } } function onResetConsoleElementToDefaultParent() { if (consoleHtmlElement && consoleHtmlElement[$defaultConsoleParent]) { consoleHtmlElement[$defaultConsoleParent].appendChild(consoleHtmlElement); } } function createConsole(startHidden = false) { if (consoleInstance !== undefined) return; if (isLoading) return; isLoading = true; const script = document.createElement("script"); script.onload = () => { // check if VConsole is now defined on globalThis if (!globalThis.VConsole) { console.warn("🌵 Debug console failed to load."); isLoading = false; consoleInstance = null; return; } isLoading = false; isVisible = true; beginWatchingLogs(); consoleInstance = new VConsole({ // defaultPlugins: ['system', 'network'], pluginOrder: ['default', 'needle-console'], }); const files = globalThis["needle:codegen_files"]; if (files && files.length > 0) { consoleInstance.addPlugin(createInspectPlugin()); } consoleHtmlElement = getConsoleElement(); if (consoleHtmlElement) { consoleHtmlElement[$defaultConsoleParent] = consoleHtmlElement.parentElement; consoleHtmlElement.style.position = "absolute"; consoleHtmlElement.style.zIndex = Number.MAX_SAFE_INTEGER.toString(); // const styleSheetList = document.styleSheets; // for (let i = 0; i < styleSheetList.length; i++) { // const styleSheet = styleSheetList[i]; // const firstRule = styleSheet.cssRules[0] as CSSStyleRule; // if(firstRule && firstRule.selectorText === "#__vconsole") { // console.log("found vconsole style sheet"); // const styleTag = document.createElement("style"); // styleTag.innerHTML = "#__needleconsole {}"; // for (let j = 0; j < styleSheet.cssRules.length; j++) { // const rule = styleSheet.cssRules[j] as CSSStyleRule; // styleTag.innerHTML += rule.cssText; // } // consoleHtmlElement.appendChild(styleTag); // } // } } consoleInstance.setSwitchPosition(20, 30); consoleSwitchButton = getConsoleSwitchButton(); if (consoleSwitchButton) { consoleSwitchButton.innerText = defaultButtonIcon; consoleSwitchButton.addEventListener("click", onConsoleSwitchButtonClicked); const styles = document.createElement("style"); const size = 40; styles.innerHTML = ` #__vconsole .vc-switch { border: 1px solid rgba(255, 255, 255, .1); border-radius: 50%; width: ${size}px; height: ${size}px; padding: 0; line-height: ${size}px; font-size: ${size * .4}px; text-align: center; background: #ffffff5c; backdrop-filter: blur(16px); -webkit-backdrop-filter: blur(16px); user-select: none; pointer-events: auto; transition: transform .2s ease-in-out; box-shadow: 0px 7px 0.5rem 0px rgb(0 0 0 / 6%), inset 0px 0px 1.3rem rgba(0,0,0,.05); font-family: 'Material Symbols Outlined'; color: black; font-size: 2.3em; font-weight: 100; } #__vconsole .vc-switch:hover { cursor: pointer; transform: scale(1.1); transition: transform .1s ease-in-out, background .1s linear; background: rgba(245, 245, 245, .8); outline: rgba(0, 0, 0, .05) 1px solid; } #__vconsole .vc-switch[error] { background: rgba(255,0,0,.2); animation: vconsole-notify 1s ease-in-out; line-height: 35px; } @keyframes vconsole-notify { from { transform: scale(1, 1); } 10% { transform: scale(1.3, 1.3); } 70% { transform: scale(1.4, 1.4); } to { transform: scale(1, 1); } } #__vconsole .vc-panel { font-family: monospace; font-size: 11px; } #__vconsole .vc-plugin-box.vc-actived { height: 100%; } #__vconsole .vc-mask { overflow: hidden; } `; consoleHtmlElement?.prepend(styles); if (startHidden === true && getErrorCount() <= 0) hideDebugConsole(); console.log("🌵 Debug console has loaded"); } }; script.onerror = () => { console.warn("🌵 Debug console failed to load." + (window.crossOriginIsolated ? "This page is using cross-origin isolation, so external scripts can't be loaded." : "")); isLoading = false; consoleInstance = null; }; script.src = "https://cdn.jsdelivr.net/npm/vconsole@3.15.1/dist/vconsole.min.js"; document.body.appendChild(script); } function createInspectPlugin() { if (!globalThis.VConsole) return; const plugin = new VConsole.VConsolePlugin("needle-console", "🌵 Inspect glTF"); const getIframe = () => { return document.querySelector("#__vc_plug_" + plugin._id + " iframe"); }; plugin.on('renderTab', function (callback) { const files = globalThis["needle:codegen_files"]; if (!files || files.length === 0) return; let query = globalThis["needle:codegen_files"][0]; const index = query.indexOf("?"); if (index > -1) query = query.substring(0, index); const currentAbsolutePath = location.protocol + '//' + location.host + location.pathname; const currentPath = currentAbsolutePath + "/" + query; const urlEncoded = encodeURIComponent(currentPath); plugin.fullUrl = "https://viewer.needle.tools?inspect&file=" + urlEncoded; var html = `<iframe src="" style="width: 100%; height: 99%; border: none;"></iframe>`; callback(html); }); plugin.on('show', function () { const elem = getIframe(); if (elem && elem.src !== plugin.fullUrl) elem.src = plugin.fullUrl; }); plugin.on('hide', function () { const elem = getIframe(); if (elem) elem.src = ""; }); /* bottom tool bar plugin.on('addTool', function(callback) { var button = { name: 'Reload', onClick: function(event) { location.reload(); } }; callback([button]); }); */ plugin.on('addTopBar', function (callback) { var btnList = new Array(); btnList.push({ name: 'Open in new window ↗', onClick: function (_event) { window.open(plugin.fullUrl, '_blank'); consoleInstance?.hide(); } }); btnList.push({ name: 'Reload', onClick: function (_event) { const iframe = getIframe(); if (iframe) iframe.src = plugin.fullUrl; } }); btnList.push({ name: 'Fullscreen', onClick: function (_event) { const iframe = getIframe(); if (iframe.requestFullscreen) { iframe.requestFullscreen(); } else if (iframe["webkitRequestFullscreen"] instanceof Function) { iframe["webkitRequestFullscreen"](); } } }); callback(btnList); }); return plugin; } function getConsoleSwitchButton() { const el = document.querySelector("#__vconsole .vc-switch"); if (el) return el; return null; } function getConsoleElement() { const el = document.querySelector("#__vconsole"); if (el) return el; return null; } //# sourceMappingURL=debug_console.js.map