@melonjs/debug-plugin
Advanced tools
| { | ||
| "version": 3, | ||
| "sources": ["../src/index.js", "../package.json", "../src/debugPanel.js", "../src/counters.js", "../src/graphs.js", "../src/styles.js", "../src/patches.js"], | ||
| "sourcesContent": ["import { event, input, plugin, utils } from \"melonjs\";\nimport { homepage, name, version } from \"../package.json\";\nimport { DebugPanel } from \"./debugPanel.js\";\n\n/**\n * @classdesc\n * a simple debug panel plugin <br>\n * <img src=\"images/debugPanel.png\"/> <br>\n * <b>usage : </b><br>\n * • upon loading the debug panel, it will be automatically registered under me.plugins.debugPanel <br>\n * • you can then press the default \"s\" key to show or hide the panel, or use me.plugins.debugPanel.show() and me.plugins.debugPanel.hide(), or add #debug as a parameter to your URL e.g. http://myURL/index.html#debug <br>\n * • default key can be configured using the following parameters in the url : e.g. http://myURL/index.html#debugToggleKey=d <br>\n * <b>the debug panel provides the following information : </b><br>\n * • amount of total objects currently active in the current stage <br>\n * • amount of draws operation <br>\n * • amount of body shape (for collision) <br>\n * • amount of bounding box <br>\n * • amount of sprites objects <br>\n * • amount of objects currently inactive in the the object pool <br>\n * • memory usage (Heap Memory information is only available under Chrome) <br>\n * • frame update time (in ms) <br>\n * • frame draw time (in ms) <br>\n * • current fps rate vs target fps <br>\n * additionally, using the checkbox in the panel it is also possible to display : <br>\n * • the hitbox or bounding box for all objects <br>\n * • current velocity vector <br>\n * • quadtree spatial visualization <br>\n * @augments plugin.BasePlugin\n */\nexport class DebugPanelPlugin extends plugin.BasePlugin {\n\t/**\n\t * @param {number} [debugToggle=input.KEY.S] - a default key to toggle the debug panel visibility state\n\t * @see input.KEY for default key options\n\t */\n\tconstructor(debugToggle = input.KEY.S) {\n\t\tsuper();\n\n\t\tthis.version = \"15.12.0\";\n\n\t\tconsole.log(`${name} ${version} | ${homepage}`);\n\n\t\tthis.debugToggle = debugToggle;\n\t\tthis.panel = new DebugPanel(debugToggle);\n\n\t\tthis._onKeyDown = (_action, keyCode) => {\n\t\t\tif (keyCode === this.debugToggle) {\n\t\t\t\tthis.toggle();\n\t\t\t}\n\t\t};\n\t\tevent.on(event.KEYDOWN, this._onKeyDown);\n\n\t\t// if \"#debug\" is present in the URL\n\t\tif (utils.getUriFragment().debug === true) {\n\t\t\tthis.show();\n\t\t}\n\t}\n\n\t/**\n\t * destroy the debug plugin\n\t */\n\tdestroy() {\n\t\tthis.hide();\n\t\tevent.off(event.KEYDOWN, this._onKeyDown);\n\t}\n\n\t/**\n\t * show the debug panel\n\t */\n\tshow() {\n\t\tthis.panel.show();\n\t}\n\n\t/**\n\t * hide the debug panel\n\t */\n\thide() {\n\t\tthis.panel.hide();\n\t}\n\n\t/**\n\t * toggle the debug panel visibility state\n\t */\n\ttoggle() {\n\t\tif (this.panel.visible) {\n\t\t\tthis.panel.hide();\n\t\t} else {\n\t\t\tthis.panel.show();\n\t\t}\n\t}\n}\n", "{\n\t\"name\": \"@melonjs/debug-plugin\",\n\t\"version\": \"15.0.2\",\n\t\"description\": \"melonJS debug plugin\",\n\t\"homepage\": \"https://github.com/melonjs/melonJS/tree/master/packages/debug-plugin#readme\",\n\t\"type\": \"module\",\n\t\"keywords\": [\n\t\t\"2D\",\n\t\t\"HTML5\",\n\t\t\"javascript\",\n\t\t\"TypeScript\",\n\t\t\"es6\",\n\t\t\"Canvas\",\n\t\t\"WebGL\",\n\t\t\"WebGL2\",\n\t\t\"game\",\n\t\t\"engine\",\n\t\t\"debug\",\n\t\t\"browser\"\n\t],\n\t\"repository\": {\n\t\t\"type\": \"git\",\n\t\t\"url\": \"git+https://github.com/melonjs/melonJS.git\",\n\t\t\"directory\": \"packages/debug-plugin\"\n\t},\n\t\"bugs\": {\n\t\t\"url\": \"https://github.com/melonjs/melonJS/issues\"\n\t},\n\t\"license\": \"MIT\",\n\t\"author\": \"Olivier Biot (AltByte Pte Ltd)\",\n\t\"funding\": \"https://github.com/sponsors/melonjs\",\n\t\"engines\": {\n\t\t\"node\": \">= 19\"\n\t},\n\t\"types\": \"./build/index.d.ts\",\n\t\"exports\": {\n\t\t\".\": \"./build/index.js\"\n\t},\n\t\"files\": [\n\t\t\"build/\",\n\t\t\"debug-panel.png\",\n\t\t\"package.json\",\n\t\t\"README.md\"\n\t],\n\t\"peerDependencies\": {\n\t\t\"melonjs\": \">=15.12.0\"\n\t},\n\t\"devDependencies\": {\n\t\t\"concurrently\": \"^9.2.1\",\n\t\t\"esbuild\": \"^0.27.3\",\n\t\t\"melonjs\": \"workspace:*\",\n\t\t\"tsconfig\": \"workspace:*\",\n\t\t\"tsx\": \"^4.21.0\",\n\t\t\"typescript\": \"^5.9.3\"\n\t},\n\t\"scripts\": {\n\t\t\"dev\": \"concurrently --raw \\\"pnpm build:watch\\\" \\\"pnpm tsc:watch\\\"\",\n\t\t\"build\": \"tsx scripts/build.ts && pnpm types\",\n\t\t\"build:watch\": \"tsx scripts/build.ts watch\",\n\t\t\"lint\": \"eslint src/**.js\",\n\t\t\"prepublishOnly\": \"pnpm clean && pnpm build\",\n\t\t\"clean\": \"tsx scripts/clean.ts\",\n\t\t\"types\": \"tsc --project tsconfig.build.json\",\n\t\t\"tsc:watch\": \"tsc --project tsconfig.build.json --watch --noUnusedParameters false --noUnusedLocals false --preserveWatchOutput\"\n\t}\n}\n", "import { event, game, pool, timer, utils, video } from \"melonjs\";\n\nimport Counters from \"./counters\";\nimport { drawFrameGraph, drawMemGraph } from \"./graphs\";\nimport { applyPatches } from \"./patches\";\nimport { GRAPH_HEIGHT, GRAPH_SAMPLES, registerStyles } from \"./styles\";\n\nexport class DebugPanel {\n\tconstructor(debugToggle) {\n\t\tthis.counters = new Counters([\n\t\t\t\"shapes\",\n\t\t\t\"sprites\",\n\t\t\t\"velocity\",\n\t\t\t\"bounds\",\n\t\t\t\"children\",\n\t\t]);\n\n\t\tthis.visible = false;\n\t\tthis.version = \"__VERSION__\";\n\t\tthis.debugToggle = debugToggle;\n\n\t\t// checkbox state\n\t\tconst hash = utils.getUriFragment();\n\t\tthis.options = {\n\t\t\thitbox: hash.hitbox || false,\n\t\t\tvelocity: hash.velocity || false,\n\t\t\tquadtree: hash.quadtree || false,\n\t\t};\n\n\t\t// frame time history for the sparkline graphs\n\t\tthis.updateHistory = new Float32Array(GRAPH_SAMPLES);\n\t\tthis.drawHistory = new Float32Array(GRAPH_SAMPLES);\n\t\tthis.memHistory = new Float32Array(GRAPH_SAMPLES);\n\t\tthis.historyIndex = 0;\n\n\t\t// register the web font / CSS and build the HTML overlay\n\t\tregisterStyles();\n\t\tthis.panel = null;\n\t\tthis.panelWrap = null;\n\t\tthis.stats = {};\n\t\tthis._buildPanel();\n\n\t\t// frame timing\n\t\tthis.frameUpdateStartTime = 0;\n\t\tthis.frameDrawStartTime = 0;\n\t\tthis.frameUpdateTime = 0;\n\t\tthis.frameDrawTime = 0;\n\n\t\t// event listener references for cleanup\n\t\tthis._onResize = () => {\n\t\t\tthis._syncPosition();\n\t\t};\n\t\tthis._onBeforeUpdate = (time) => {\n\t\t\tthis.frameUpdateStartTime = time;\n\t\t};\n\t\tthis._onAfterUpdate = (time) => {\n\t\t\tthis.frameUpdateTime = time - this.frameUpdateStartTime;\n\t\t\tif (this.visible) {\n\t\t\t\ttimer.countFPS();\n\t\t\t}\n\t\t};\n\t\tthis._onBeforeDraw = (time) => {\n\t\t\tthis.frameDrawStartTime = time;\n\t\t\tthis.counters.reset();\n\t\t};\n\t\tthis._onAfterDraw = (time) => {\n\t\t\tthis.frameDrawTime = time - this.frameDrawStartTime;\n\t\t\tif (this.visible) {\n\t\t\t\tthis._updatePanel();\n\t\t\t\tthis._drawQuadTree();\n\t\t\t}\n\t\t};\n\n\t\tevent.on(event.CANVAS_ONRESIZE, this._onResize);\n\t\tevent.on(event.GAME_BEFORE_UPDATE, this._onBeforeUpdate);\n\t\tevent.on(event.GAME_AFTER_UPDATE, this._onAfterUpdate);\n\t\tevent.on(event.GAME_BEFORE_DRAW, this._onBeforeDraw);\n\t\tevent.on(event.GAME_AFTER_DRAW, this._onAfterDraw);\n\n\t\tapplyPatches(this);\n\t}\n\n\t/** @private */\n\t_buildPanel() {\n\t\tthis.panelWrap = document.createElement(\"div\");\n\t\tthis.panelWrap.className = \"dbg-wrap\";\n\n\t\tthis.panel = document.createElement(\"div\");\n\t\tthis.panel.className = \"dbg\";\n\t\tthis.panelWrap.appendChild(this.panel);\n\t\tthis.panelWrap.style.display = \"none\";\n\n\t\t// helper to create a stat as two grid cells: label | value\n\t\tconst stat = (id, label) => {\n\t\t\tconst dimEl = document.createElement(\"span\");\n\t\t\tdimEl.className = \"dim\";\n\t\t\tdimEl.textContent = label;\n\t\t\tconst valEl = document.createElement(\"span\");\n\t\t\tvalEl.className = \"val\";\n\t\t\tvalEl.textContent = \"0\";\n\t\t\tthis.stats[id] = valEl;\n\t\t\treturn [dimEl, valEl];\n\t\t};\n\n\t\t// helper to create a checkbox label\n\t\tconst checkbox = (key, label) => {\n\t\t\tconst el = document.createElement(\"label\");\n\t\t\tconst cb = document.createElement(\"input\");\n\t\t\tcb.type = \"checkbox\";\n\t\t\tcb.checked = this.options[key];\n\t\t\tcb.addEventListener(\"change\", () => {\n\t\t\t\tthis.options[key] = cb.checked;\n\t\t\t\tgame.repaint();\n\t\t\t});\n\t\t\tel.appendChild(cb);\n\t\t\tel.appendChild(document.createTextNode(label));\n\t\t\treturn el;\n\t\t};\n\n\t\tconst toggleKey = String.fromCharCode(32 + this.debugToggle);\n\n\t\t// helper to append a stat (two grid cells) or a single element spanning 2 columns\n\t\tconst addStat = (id, label) => {\n\t\t\tconst [dimEl, valEl] = stat(id, label);\n\t\t\tthis.panel.appendChild(dimEl);\n\t\t\tthis.panel.appendChild(valEl);\n\t\t};\n\t\tconst addSpan2 = (el) => {\n\t\t\tel.style.gridColumn = \"span 2\";\n\t\t\tthis.panel.appendChild(el);\n\t\t};\n\n\t\t// row 1\n\t\taddStat(\"objects\", \"#objects\");\n\t\taddSpan2(checkbox(\"hitbox\", \"hitbox\"));\n\t\taddSpan2(checkbox(\"quadtree\", \"quadtree\"));\n\t\taddStat(\"update\", \"Update\");\n\t\taddStat(\"heap\", \"Heap\");\n\t\tconst fpsEl = document.createElement(\"span\");\n\t\tfpsEl.className = \"fps-val\";\n\t\tthis.stats.fps = fpsEl;\n\t\taddSpan2(fpsEl);\n\n\t\t// row 2\n\t\taddStat(\"draws\", \"#draws\");\n\t\taddSpan2(checkbox(\"velocity\", \"velocity\"));\n\t\taddSpan2(document.createElement(\"span\"));\n\t\taddStat(\"draw\", \"Draw\");\n\t\taddStat(\"pool\", \"Pool\");\n\t\taddSpan2(document.createElement(\"span\"));\n\n\t\t// row 3\n\t\taddStat(\"shapes\", \"Shapes\");\n\t\taddStat(\"sprites\", \"Sprites\");\n\t\taddStat(\"velocity_count\", \"Velocity\");\n\t\taddStat(\"bounds\", \"Bounds\");\n\t\taddStat(\"children\", \"Children\");\n\t\tconst helpEl = document.createElement(\"span\");\n\t\thelpEl.className = \"help\";\n\t\thelpEl.textContent = `[${toggleKey}] show/hide`;\n\t\taddSpan2(helpEl);\n\n\t\t// row 4: frame time graph (full width)\n\t\tconst frameRow = document.createElement(\"div\");\n\t\tframeRow.className = \"graph-row\";\n\n\t\tthis.graphCanvas = document.createElement(\"canvas\");\n\t\tthis.graphCanvas.height = GRAPH_HEIGHT;\n\t\tframeRow.appendChild(this.graphCanvas);\n\n\t\tconst frameLegend = document.createElement(\"div\");\n\t\tframeLegend.className = \"graph-label\";\n\t\tconst peakEl = document.createElement(\"span\");\n\t\tthis.stats.peak = peakEl;\n\t\tframeLegend.innerHTML = `<span class=\"update-color\">▬ update</span><span class=\"draw-color\">▬ draw</span>`;\n\t\tframeLegend.appendChild(peakEl);\n\t\tframeRow.appendChild(frameLegend);\n\t\tthis.panel.appendChild(frameRow);\n\n\t\t// row 5: memory graph (full width, Chrome only)\n\t\tif (window.performance?.memory) {\n\t\t\tconst memRow = document.createElement(\"div\");\n\t\t\tmemRow.className = \"graph-row\";\n\n\t\t\tthis.memCanvas = document.createElement(\"canvas\");\n\t\t\tthis.memCanvas.height = GRAPH_HEIGHT;\n\t\t\tmemRow.appendChild(this.memCanvas);\n\n\t\t\tconst memLegend = document.createElement(\"div\");\n\t\t\tmemLegend.className = \"graph-label\";\n\t\t\tconst memPeakEl = document.createElement(\"span\");\n\t\t\tthis.stats.memPeak = memPeakEl;\n\t\t\tmemLegend.innerHTML = `<span class=\"mem-color\">▬ heap</span>`;\n\t\t\tmemLegend.appendChild(memPeakEl);\n\t\t\tmemRow.appendChild(memLegend);\n\t\t\tthis.panel.appendChild(memRow);\n\t\t}\n\n\t\t// append to <html> element to avoid triggering melonJS's MutationObserver\n\t\t// (which watches document.body / canvas parent for DOM changes and triggers resize)\n\t\tdocument.documentElement.appendChild(this.panelWrap);\n\t\tthis._syncPosition();\n\t}\n\n\t/** @private */\n\t_syncPosition() {\n\t\tconst rect = video.renderer.getCanvas().getBoundingClientRect();\n\t\tconst s = this.panelWrap.style;\n\t\ts.top = `${rect.top}px`;\n\t\ts.left = `${rect.left}px`;\n\t\ts.width = `${rect.width}px`;\n\t}\n\n\t/** @private */\n\t_updatePanel() {\n\t\tthis.stats.objects.textContent = game.world.children.length;\n\t\tthis.stats.draws.textContent = game.world.drawCount;\n\t\tthis.stats.update.textContent = `${this.frameUpdateTime.toFixed(2)}ms`;\n\t\tthis.stats.draw.textContent = `${this.frameDrawTime.toFixed(2)}ms`;\n\t\tthis.stats.fps.textContent = `${timer.fps}/${timer.maxfps} fps`;\n\t\tthis.stats.shapes.textContent = this.counters.get(\"shapes\");\n\t\tthis.stats.sprites.textContent = this.counters.get(\"sprites\");\n\t\tthis.stats.velocity_count.textContent = this.counters.get(\"velocity\");\n\t\tthis.stats.bounds.textContent = this.counters.get(\"bounds\");\n\t\tthis.stats.children.textContent = this.counters.get(\"children\");\n\t\tthis.stats.pool.textContent = pool.getInstanceCount();\n\n\t\tif (window.performance?.memory) {\n\t\t\tconst used = Number(\n\t\t\t\t(window.performance.memory.usedJSHeapSize / 1048576).toFixed(1),\n\t\t\t);\n\t\t\tconst total = Number(\n\t\t\t\t(window.performance.memory.totalJSHeapSize / 1048576).toFixed(1),\n\t\t\t);\n\t\t\tthis.stats.heap.textContent = `${used}/${total}MB`;\n\t\t} else {\n\t\t\tthis.stats.heap.textContent = \"n/a\";\n\t\t}\n\n\t\t// record frame times and memory\n\t\tthis.updateHistory[this.historyIndex] = this.frameUpdateTime;\n\t\tthis.drawHistory[this.historyIndex] = this.frameDrawTime;\n\t\tif (window.performance?.memory) {\n\t\t\tthis.memHistory[this.historyIndex] =\n\t\t\t\twindow.performance.memory.usedJSHeapSize / 1048576;\n\t\t}\n\t\tthis.historyIndex = (this.historyIndex + 1) % GRAPH_SAMPLES;\n\n\t\tdrawFrameGraph(\n\t\t\tthis.graphCanvas,\n\t\t\tthis.updateHistory,\n\t\t\tthis.drawHistory,\n\t\t\tthis.historyIndex,\n\t\t\tthis.stats.peak,\n\t\t);\n\t\tif (this.memCanvas) {\n\t\t\tdrawMemGraph(\n\t\t\t\tthis.memCanvas,\n\t\t\t\tthis.memHistory,\n\t\t\t\tthis.historyIndex,\n\t\t\t\tthis.stats.memPeak,\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * show the debug panel\n\t */\n\tshow() {\n\t\tif (!this.visible) {\n\t\t\tthis.panelWrap.style.display = \"\";\n\t\t\tthis.visible = true;\n\t\t\tthis._syncPosition();\n\t\t\tgame.repaint();\n\t\t}\n\t}\n\n\t/**\n\t * hide the debug panel\n\t */\n\thide() {\n\t\tif (this.visible) {\n\t\t\tthis.panelWrap.style.display = \"none\";\n\t\t\tthis.visible = false;\n\t\t\tgame.repaint();\n\t\t}\n\t}\n\n\t/** @private */\n\t_drawQuadTree() {\n\t\tif (!this.options.quadtree) {\n\t\t\treturn;\n\t\t}\n\t\tconst renderer = video.renderer;\n\t\trenderer.save();\n\t\tconst { x, y } = game.viewport.pos;\n\t\trenderer.translate(-x, -y);\n\t\tthis._drawQuadTreeNode(renderer, game.world.broadphase);\n\t\trenderer.translate(x, y);\n\t\trenderer.restore();\n\t\t// flush is needed because this runs after GAME_AFTER_DRAW,\n\t\t// which is emitted after the main renderer.flush()\n\t\trenderer.flush();\n\t}\n\n\t/** @private */\n\t_drawQuadTreeNode(renderer, node) {\n\t\tconst bounds = node.bounds;\n\n\t\t// wireframe outline \u2014 color based on density\n\t\tif (node.objects.length > 0) {\n\t\t\tconst ratio = Math.min(node.objects.length / node.max_objects, 1);\n\t\t\t// green (few) \u2192 yellow \u2192 red (full)\n\t\t\tconst r = Math.round(ratio < 0.5 ? ratio * 2 * 255 : 255);\n\t\t\tconst g = Math.round(ratio < 0.5 ? 255 : 255 * (1 - (ratio - 0.5) * 2));\n\t\t\trenderer.setColor(`rgb(${r},${g},0)`);\n\t\t} else {\n\t\t\trenderer.setColor(\"green\");\n\t\t}\n\t\trenderer.strokeRect(bounds.left, bounds.top, bounds.width, bounds.height);\n\n\t\t// recurse into subnodes\n\t\tfor (let i = 0; i < node.nodes.length; i++) {\n\t\t\tthis._drawQuadTreeNode(renderer, node.nodes[i]);\n\t\t}\n\t}\n\n\tdestroy() {\n\t\tthis.hide();\n\t\t// remove the HTML overlay\n\t\tif (this.panelWrap?.parentElement) {\n\t\t\tthis.panelWrap.parentElement.removeChild(this.panelWrap);\n\t\t}\n\t\tevent.off(event.CANVAS_ONRESIZE, this._onResize);\n\t\tevent.off(event.GAME_BEFORE_UPDATE, this._onBeforeUpdate);\n\t\tevent.off(event.GAME_AFTER_UPDATE, this._onAfterUpdate);\n\t\tevent.off(event.GAME_BEFORE_DRAW, this._onBeforeDraw);\n\t\tevent.off(event.GAME_AFTER_DRAW, this._onAfterDraw);\n\t}\n}\n", "export default class Counters extends Map {\n\tconstructor(keys) {\n\t\tsuper(\n\t\t\tkeys?.map((key) => {\n\t\t\t\treturn [key, 0];\n\t\t\t}),\n\t\t);\n\t}\n\n\treset() {\n\t\tfor (const key of this.keys()) {\n\t\t\tthis.set(key, 0);\n\t\t}\n\t}\n\n\tinc(stat, value = 1) {\n\t\tthis.set(stat, (this.get(stat) ?? 0) + value);\n\t}\n}\n", "import { timer } from \"melonjs\";\nimport { GRAPH_HEIGHT, GRAPH_SAMPLES } from \"./styles\";\n\n/**\n * Resize a canvas backing store to match its CSS layout width.\n * @param {HTMLCanvasElement} canvas\n */\nfunction fitCanvas(canvas) {\n\tconst w = canvas.clientWidth;\n\tif (w > 0 && canvas.width !== w) {\n\t\tcanvas.width = w;\n\t}\n}\n\n/**\n * Draw the stacked frame-time sparkline (update + draw).\n * @param {HTMLCanvasElement} canvas\n * @param {Float32Array} updateHistory\n * @param {Float32Array} drawHistory\n * @param {number} historyIndex - current write position in the ring buffer\n * @param {HTMLElement} peakEl - element to display peak value\n */\nexport function drawFrameGraph(\n\tcanvas,\n\tupdateHistory,\n\tdrawHistory,\n\thistoryIndex,\n\tpeakEl,\n) {\n\tfitCanvas(canvas);\n\tconst ctx = canvas.getContext(\"2d\");\n\tconst w = canvas.width;\n\tconst h = GRAPH_HEIGHT;\n\tif (w === 0) {\n\t\treturn;\n\t}\n\n\t// find peak for scaling\n\tlet peak = 1;\n\tfor (let i = 0; i < GRAPH_SAMPLES; i++) {\n\t\tconst total = updateHistory[i] + drawHistory[i];\n\t\tif (total > peak) {\n\t\t\tpeak = total;\n\t\t}\n\t}\n\tpeak = Math.ceil(peak / 4) * 4;\n\tif (peak < 4) {\n\t\tpeak = 4;\n\t}\n\n\tpeakEl.textContent = `peak ${peak}ms`;\n\n\tctx.clearRect(0, 0, w, h);\n\n\t// target frame time line (e.g. 16.67ms for 60fps)\n\tconst targetMs = 1000 / timer.maxfps;\n\tconst targetY = h - (targetMs / peak) * h;\n\tif (targetY > 0 && targetY < h) {\n\t\tctx.strokeStyle = \"rgba(40,80,140,0.5)\";\n\t\tctx.setLineDash([2, 2]);\n\t\tctx.beginPath();\n\t\tctx.moveTo(0, targetY);\n\t\tctx.lineTo(w, targetY);\n\t\tctx.stroke();\n\t\tctx.setLineDash([]);\n\t}\n\n\t// stacked bars: draw (amber) on bottom, update (cyan) on top\n\tconst barW = w / GRAPH_SAMPLES;\n\tfor (let i = 0; i < GRAPH_SAMPLES; i++) {\n\t\tconst idx = (historyIndex + i) % GRAPH_SAMPLES;\n\t\tconst updateVal = updateHistory[idx];\n\t\tconst drawVal = drawHistory[idx];\n\t\tconst updateH = (updateVal / peak) * h;\n\t\tconst drawH = (drawVal / peak) * h;\n\t\tconst x = Math.round(i * barW);\n\t\tconst bw = Math.max(1, Math.round((i + 1) * barW) - x);\n\n\t\t// draw time on bottom (amber)\n\t\tif (drawH > 0) {\n\t\t\tctx.fillStyle = \"#ffb800\";\n\t\t\tctx.fillRect(x, h - drawH - updateH, bw, drawH);\n\t\t}\n\t\t// update time on top (cyan)\n\t\tif (updateH > 0) {\n\t\t\tctx.fillStyle = \"#00e0ff\";\n\t\t\tctx.fillRect(x, h - updateH, bw, updateH);\n\t\t}\n\t}\n}\n\n/**\n * Draw the memory heap area graph.\n * @param {HTMLCanvasElement} canvas\n * @param {Float32Array} memHistory - heap usage in MB\n * @param {number} historyIndex - current write position in the ring buffer\n * @param {HTMLElement} peakEl - element to display peak value\n */\nexport function drawMemGraph(canvas, memHistory, historyIndex, peakEl) {\n\tfitCanvas(canvas);\n\tconst ctx = canvas.getContext(\"2d\");\n\tconst w = canvas.width;\n\tconst h = GRAPH_HEIGHT;\n\tif (w === 0) {\n\t\treturn;\n\t}\n\n\t// find peak memory for scaling\n\tlet peak = 1;\n\tfor (let i = 0; i < GRAPH_SAMPLES; i++) {\n\t\tif (memHistory[i] > peak) {\n\t\t\tpeak = memHistory[i];\n\t\t}\n\t}\n\tpeak = Math.ceil(peak / 4) * 4;\n\tif (peak < 4) {\n\t\tpeak = 4;\n\t}\n\n\tpeakEl.textContent = `peak ${peak}MB`;\n\n\tctx.clearRect(0, 0, w, h);\n\n\t// filled area graph\n\tconst barW = w / GRAPH_SAMPLES;\n\tctx.fillStyle = \"rgba(198,120,221,0.3)\";\n\tctx.strokeStyle = \"#c678dd\";\n\tctx.lineWidth = 1;\n\tctx.beginPath();\n\tctx.moveTo(0, h);\n\tfor (let i = 0; i < GRAPH_SAMPLES; i++) {\n\t\tconst idx = (historyIndex + i) % GRAPH_SAMPLES;\n\t\tconst val = memHistory[idx];\n\t\tconst y = h - (val / peak) * h;\n\t\tconst x = Math.round(i * barW);\n\t\tctx.lineTo(x, y);\n\t}\n\tctx.lineTo(w, h);\n\tctx.closePath();\n\tctx.fill();\n\n\t// stroke the top edge\n\tctx.beginPath();\n\tfor (let i = 0; i < GRAPH_SAMPLES; i++) {\n\t\tconst idx = (historyIndex + i) % GRAPH_SAMPLES;\n\t\tconst val = memHistory[idx];\n\t\tconst y = h - (val / peak) * h;\n\t\tconst x = Math.round(i * barW);\n\t\tif (i === 0) {\n\t\t\tctx.moveTo(x, y);\n\t\t} else {\n\t\t\tctx.lineTo(x, y);\n\t\t}\n\t}\n\tctx.stroke();\n}\n", "import fontSource from \"./font/PressStart2P.ttf\";\n\nconst FONT_NAME = \"PressStart2P\";\n\nexport const GRAPH_HEIGHT = 30;\nexport const GRAPH_SAMPLES = 200;\n\nlet registered = false;\n\n/**\n * Inject the @font-face rule and all debug panel CSS into the document.\n * Safe to call multiple times \u2014 only injects once.\n */\nexport function registerStyles() {\n\tif (registered) {\n\t\treturn;\n\t}\n\tconst style = document.createElement(\"style\");\n\tstyle.textContent = `\n@font-face { font-family: \"${FONT_NAME}\"; src: url(\"${fontSource}\") format(\"truetype\"); }\n\n.dbg-wrap {\n position: fixed; pointer-events: none; z-index: 9999;\n}\n\n.dbg {\n font-family: \"${FONT_NAME}\", monospace; font-size: 8px; color: #b0c4d8;\n background:\n repeating-linear-gradient(0deg, transparent 0px, transparent 2px, rgba(0,0,0,0.06) 2px, rgba(0,0,0,0.06) 4px),\n linear-gradient(180deg, rgba(6,8,18,0.94) 0%, rgba(10,14,28,0.92) 100%);\n border-bottom: 2px solid #1a3050;\n box-shadow: inset 0 0 0 1px rgba(40,80,140,0.3), 0 2px 8px rgba(0,0,0,0.5);\n padding: 6px 10px 5px; pointer-events: none;\n display: grid; grid-template-columns: repeat(6, auto 1fr); gap: 2px 0;\n align-items: center; line-height: 1.7;\n image-rendering: pixelated;\n}\n\n/* labels: muted steel blue, right-aligned */\n.dbg .dim {\n color: #4a6a88; text-align: right; padding-right: 6px;\n white-space: nowrap; text-transform: lowercase;\n}\n\n/* values: bright cyan */\n.dbg .val {\n color: #00e0ff; white-space: nowrap; padding-right: 14px;\n text-shadow: 0 0 6px rgba(0,224,255,0.2);\n}\n\n/* fps: amber accent, bold presence */\n.dbg .fps-val {\n color: #ffb800; text-shadow: 0 0 6px rgba(255,184,0,0.25);\n justify-content: flex-end; white-space: nowrap;\n}\n\n/* help text */\n.dbg .help {\n color: #384858; justify-content: flex-end;\n font-size: 7px; white-space: nowrap;\n}\n\n/* checkbox labels */\n.dbg label {\n cursor: pointer; white-space: nowrap; pointer-events: auto;\n display: flex; align-items: center; gap: 5px;\n color: #6888a8; transition: color 0.15s;\n}\n.dbg label:hover { color: #00e0ff; }\n\n/* pixel-art checkbox: NES-style toggle */\n.dbg input[type=checkbox] {\n appearance: none; -webkit-appearance: none;\n width: 10px; height: 10px; margin: 0; cursor: pointer;\n border: 2px solid #2a4a6a; background: rgba(0,20,40,0.5);\n image-rendering: pixelated; flex-shrink: 0;\n transition: border-color 0.15s, background 0.15s, box-shadow 0.15s;\n}\n.dbg input[type=checkbox]:hover {\n border-color: #00b8d4;\n}\n.dbg input[type=checkbox]:checked {\n border-color: #00e0ff; background: #00e0ff;\n box-shadow: 0 0 4px rgba(0,224,255,0.4), inset 0 0 0 1px rgba(6,8,18,0.9);\n}\n\n/* graph rows */\n.dbg .graph-row {\n grid-column: 1 / span 12;\n display: flex; align-items: center; gap: 8px;\n border-top: 1px solid rgba(40,80,140,0.2);\n padding-top: 4px; margin-top: 2px;\n}\n.dbg .graph-row canvas {\n flex: 1; min-width: 0; height: ${GRAPH_HEIGHT}px;\n image-rendering: pixelated;\n border: 1px solid #1a3050;\n background: rgba(0,0,0,0.35);\n box-shadow: 0 0 4px rgba(0,0,0,0.3);\n}\n.dbg .graph-label {\n font-size: 7px; color: #384858; white-space: nowrap;\n display: flex; flex-direction: column; gap: 2px; min-width: 70px;\n}\n.dbg .graph-label .update-color { color: #00e0ff; }\n.dbg .graph-label .draw-color { color: #ffb800; }\n.dbg .graph-label .mem-color { color: #c678dd; }\n`;\n\tdocument.head.appendChild(style);\n\tregistered = true;\n}\n", "import {\n\tBitmapText,\n\tCamera2d,\n\tContainer,\n\tEntity,\n\tImageLayer,\n\tplugin,\n\tRenderable,\n\tText,\n} from \"melonjs\";\n\n/**\n * Monkey-patch melonJS rendering classes to draw debug overlays\n * (hitboxes, bounding boxes, velocity vectors) and collect stats.\n *\n * @param {import(\"./debugPanel\").DebugPanel} panel - the debug panel instance\n */\nexport function applyPatches(panel) {\n\t// patch Renderable\n\tplugin.patch(Renderable, \"postDraw\", function (renderer) {\n\t\t// biome-ignore lint/complexity/noArguments: needed to forward all arguments to patched method\n\t\tthis._patched.apply(this, arguments);\n\n\t\tif (this.image !== undefined) {\n\t\t\tpanel.counters.inc(\"sprites\");\n\t\t}\n\t\tpanel.counters.inc(\"bounds\");\n\t\tif (this instanceof Container) {\n\t\t\tpanel.counters.inc(\"children\");\n\t\t}\n\n\t\t// skip types that have their own dedicated patches\n\t\tif (\n\t\t\t!panel.visible ||\n\t\t\t!panel.options.hitbox ||\n\t\t\tthis instanceof Entity ||\n\t\t\tthis.ancestor instanceof Entity ||\n\t\t\tthis instanceof Text ||\n\t\t\tthis instanceof BitmapText ||\n\t\t\tthis instanceof Camera2d ||\n\t\t\tthis instanceof ImageLayer\n\t\t) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst bounds = this.getBounds();\n\t\tif (!bounds.isFinite()) {\n\t\t\treturn;\n\t\t}\n\n\t\trenderer.save();\n\n\t\t// undo ancestor world transform for non-floating renderables\n\t\tif (this.ancestor !== undefined && !this.floating) {\n\t\t\tconst absPos = this.ancestor.getAbsolutePosition();\n\t\t\trenderer.translate(-absPos.x, -absPos.y);\n\t\t}\n\n\t\t// renderable bounding box (green)\n\t\trenderer.setColor(\"green\");\n\t\trenderer.stroke(bounds);\n\n\t\t// sprite mask (orange)\n\t\tif (this.mask !== undefined) {\n\t\t\trenderer.setColor(\"orange\");\n\t\t\trenderer.stroke(this.mask);\n\t\t}\n\n\t\t// body bounds and collision shapes\n\t\tif (this.body !== undefined) {\n\t\t\trenderer.translate(bounds.x, bounds.y);\n\n\t\t\trenderer.setColor(\"orange\");\n\t\t\trenderer.stroke(this.body.getBounds());\n\n\t\t\trenderer.setColor(\"red\");\n\t\t\tfor (const shape of this.body.shapes) {\n\t\t\t\trenderer.stroke(shape);\n\t\t\t\tpanel.counters.inc(\"shapes\");\n\t\t\t}\n\t\t}\n\n\t\trenderer.restore();\n\t});\n\n\t// patch BitmapText\n\tplugin.patch(BitmapText, \"draw\", function (renderer) {\n\t\t// biome-ignore lint/complexity/noArguments: needed to forward all arguments to patched method\n\t\tthis._patched.apply(this, arguments);\n\n\t\tif (!panel.visible || !panel.options.hitbox) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst bounds = this.getBounds();\n\n\t\trenderer.save();\n\n\t\t// adjust for anchor point offset since bounds position is already anchored\n\t\tif (this.ancestor !== undefined) {\n\t\t\trenderer.translate(\n\t\t\t\tthis.anchorPoint.x * bounds.width,\n\t\t\t\tthis.anchorPoint.y * bounds.height,\n\t\t\t);\n\t\t}\n\n\t\trenderer.setColor(\"green\");\n\t\trenderer.stroke(bounds);\n\n\t\trenderer.restore();\n\t});\n\n\t// patch Text\n\tplugin.patch(Text, \"draw\", function (renderer) {\n\t\t// biome-ignore lint/complexity/noArguments: needed to forward all arguments to patched method\n\t\tthis._patched.apply(this, arguments);\n\n\t\tif (!panel.visible || !panel.options.hitbox) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst bounds = this.getBounds();\n\n\t\trenderer.save();\n\n\t\t// undo ancestor world transform for floating text\n\t\tif (\n\t\t\tthis.ancestor !== undefined &&\n\t\t\t!this.root &&\n\t\t\t!this.ancestor.root &&\n\t\t\tthis.ancestor.isFloating\n\t\t) {\n\t\t\tconst absPos = this.ancestor.getAbsolutePosition();\n\t\t\trenderer.translate(-absPos.x, -absPos.y);\n\t\t}\n\n\t\trenderer.setColor(\"green\");\n\t\trenderer.stroke(bounds);\n\n\t\trenderer.restore();\n\t});\n\n\t// patch Entity\n\tplugin.patch(Entity, \"postDraw\", function (renderer) {\n\t\tif (panel.visible && panel.options.hitbox) {\n\t\t\trenderer.save();\n\n\t\t\tconst bodyBounds = this.body.getBounds();\n\n\t\t\t// entity renderable bounding box (green) \u2014 drawn in entity's\n\t\t\t// preDraw local space where origin = entity anchor point\n\t\t\tif (this.renderable instanceof Renderable) {\n\t\t\t\tconst r = this.renderable;\n\t\t\t\trenderer.setColor(\"green\");\n\t\t\t\trenderer.strokeRect(\n\t\t\t\t\t-r.anchorPoint.x * r.width,\n\t\t\t\t\t-r.anchorPoint.y * r.height,\n\t\t\t\t\tr.width,\n\t\t\t\t\tr.height,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// move from anchor point to body origin for body/collision overlays\n\t\t\trenderer.translate(\n\t\t\t\t-this.anchorPoint.x * bodyBounds.width,\n\t\t\t\t-this.anchorPoint.y * bodyBounds.height,\n\t\t\t);\n\n\t\t\trenderer.setColor(\"orange\");\n\t\t\trenderer.stroke(bodyBounds);\n\n\t\t\trenderer.setColor(\"red\");\n\t\t\tfor (const shape of this.body.shapes) {\n\t\t\t\trenderer.stroke(shape);\n\t\t\t\tpanel.counters.inc(\"shapes\");\n\t\t\t}\n\n\t\t\trenderer.restore();\n\t\t}\n\n\t\tif (\n\t\t\tpanel.visible &&\n\t\t\tpanel.options.velocity &&\n\t\t\t(this.body.vel.x || this.body.vel.y)\n\t\t) {\n\t\t\tconst bodyBounds = this.body.getBounds();\n\t\t\tconst hWidth = bodyBounds.width / 2;\n\t\t\tconst hHeight = bodyBounds.height / 2;\n\n\t\t\trenderer.save();\n\t\t\trenderer.lineWidth = 1;\n\t\t\trenderer.setColor(\"blue\");\n\t\t\trenderer.translate(0, -hHeight);\n\t\t\trenderer.strokeLine(\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t\tMath.trunc(this.body.vel.x * hWidth),\n\t\t\t\tMath.trunc(this.body.vel.y * hHeight),\n\t\t\t);\n\t\t\tpanel.counters.inc(\"velocity\");\n\t\t\trenderer.restore();\n\t\t}\n\n\t\t// biome-ignore lint/complexity/noArguments: needed to forward all arguments to patched method\n\t\tthis._patched.apply(this, arguments);\n\t});\n}\n"], | ||
| "sourcesContent": ["import { event, input, plugin, utils } from \"melonjs\";\nimport { homepage, name, version } from \"../package.json\";\nimport { DebugPanel } from \"./debugPanel.js\";\n\n/**\n * @classdesc\n * a simple debug panel plugin <br>\n * <img src=\"images/debugPanel.png\"/> <br>\n * <b>usage : </b><br>\n * • upon loading the debug panel, it will be automatically registered under me.plugins.debugPanel <br>\n * • you can then press the default \"s\" key to show or hide the panel, or use me.plugins.debugPanel.show() and me.plugins.debugPanel.hide(), or add #debug as a parameter to your URL e.g. http://myURL/index.html#debug <br>\n * • default key can be configured using the following parameters in the url : e.g. http://myURL/index.html#debugToggleKey=d <br>\n * <b>the debug panel provides the following information : </b><br>\n * • amount of total objects currently active in the current stage <br>\n * • amount of draws operation <br>\n * • amount of body shape (for collision) <br>\n * • amount of bounding box <br>\n * • amount of sprites objects <br>\n * • amount of objects currently inactive in the the object pool <br>\n * • memory usage (Heap Memory information is only available under Chrome) <br>\n * • frame update time (in ms) <br>\n * • frame draw time (in ms) <br>\n * • current fps rate vs target fps <br>\n * additionally, using the checkbox in the panel it is also possible to display : <br>\n * • the hitbox or bounding box for all objects <br>\n * • current velocity vector <br>\n * • quadtree spatial visualization <br>\n * @augments plugin.BasePlugin\n */\nexport class DebugPanelPlugin extends plugin.BasePlugin {\n\t/**\n\t * @param {number} [debugToggle=input.KEY.S] - a default key to toggle the debug panel visibility state\n\t * @see input.KEY for default key options\n\t */\n\tconstructor(debugToggle = input.KEY.S) {\n\t\tsuper();\n\n\t\tthis.version = \"15.12.0\";\n\n\t\tconsole.log(`${name} ${version} | ${homepage}`);\n\n\t\tthis.debugToggle = debugToggle;\n\t\tthis.panel = new DebugPanel(debugToggle);\n\n\t\tthis._onKeyDown = (_action, keyCode) => {\n\t\t\tif (keyCode === this.debugToggle) {\n\t\t\t\tthis.toggle();\n\t\t\t}\n\t\t};\n\t\tevent.on(event.KEYDOWN, this._onKeyDown);\n\n\t\t// if \"#debug\" is present in the URL\n\t\tif (utils.getUriFragment().debug === true) {\n\t\t\tthis.show();\n\t\t}\n\t}\n\n\t/**\n\t * destroy the debug plugin\n\t */\n\tdestroy() {\n\t\tthis.hide();\n\t\tevent.off(event.KEYDOWN, this._onKeyDown);\n\t}\n\n\t/**\n\t * show the debug panel\n\t */\n\tshow() {\n\t\tthis.panel.show();\n\t}\n\n\t/**\n\t * hide the debug panel\n\t */\n\thide() {\n\t\tthis.panel.hide();\n\t}\n\n\t/**\n\t * toggle the debug panel visibility state\n\t */\n\ttoggle() {\n\t\tif (this.panel.visible) {\n\t\t\tthis.panel.hide();\n\t\t} else {\n\t\t\tthis.panel.show();\n\t\t}\n\t}\n}\n", "{\n\t\"name\": \"@melonjs/debug-plugin\",\n\t\"version\": \"15.0.3\",\n\t\"description\": \"melonJS debug plugin\",\n\t\"homepage\": \"https://www.npmjs.com/package/@melonjs/debug-plugin\",\n\t\"type\": \"module\",\n\t\"keywords\": [\n\t\t\"2D\",\n\t\t\"HTML5\",\n\t\t\"javascript\",\n\t\t\"TypeScript\",\n\t\t\"es6\",\n\t\t\"Canvas\",\n\t\t\"WebGL\",\n\t\t\"WebGL2\",\n\t\t\"game\",\n\t\t\"engine\",\n\t\t\"debug\",\n\t\t\"browser\"\n\t],\n\t\"repository\": {\n\t\t\"type\": \"git\",\n\t\t\"url\": \"git+https://github.com/melonjs/melonJS.git\",\n\t\t\"directory\": \"packages/debug-plugin\"\n\t},\n\t\"bugs\": {\n\t\t\"url\": \"https://github.com/melonjs/melonJS/issues\"\n\t},\n\t\"license\": \"MIT\",\n\t\"author\": \"Olivier Biot (AltByte Pte Ltd)\",\n\t\"funding\": \"https://github.com/sponsors/melonjs\",\n\t\"engines\": {\n\t\t\"node\": \">= 19\"\n\t},\n\t\"types\": \"./build/index.d.ts\",\n\t\"exports\": {\n\t\t\".\": \"./build/index.js\"\n\t},\n\t\"files\": [\n\t\t\"build/\",\n\t\t\"debug-panel.png\",\n\t\t\"package.json\",\n\t\t\"README.md\"\n\t],\n\t\"peerDependencies\": {\n\t\t\"melonjs\": \">=15.12.0\"\n\t},\n\t\"devDependencies\": {\n\t\t\"concurrently\": \"^9.2.1\",\n\t\t\"esbuild\": \"^0.27.3\",\n\t\t\"melonjs\": \"workspace:*\",\n\t\t\"tsconfig\": \"workspace:*\",\n\t\t\"tsx\": \"^4.21.0\",\n\t\t\"typescript\": \"^5.9.3\"\n\t},\n\t\"scripts\": {\n\t\t\"dev\": \"concurrently --raw \\\"pnpm build:watch\\\" \\\"pnpm tsc:watch\\\"\",\n\t\t\"build\": \"tsx scripts/build.ts && pnpm types\",\n\t\t\"build:watch\": \"tsx scripts/build.ts watch\",\n\t\t\"lint\": \"eslint src/**.js\",\n\t\t\"prepublishOnly\": \"pnpm clean && pnpm build\",\n\t\t\"clean\": \"tsx scripts/clean.ts\",\n\t\t\"types\": \"tsc --project tsconfig.build.json\",\n\t\t\"tsc:watch\": \"tsc --project tsconfig.build.json --watch --noUnusedParameters false --noUnusedLocals false --preserveWatchOutput\"\n\t}\n}\n", "import { event, game, pool, timer, utils, video } from \"melonjs\";\n\nimport Counters from \"./counters\";\nimport { drawFrameGraph, drawMemGraph } from \"./graphs\";\nimport { applyPatches } from \"./patches\";\nimport { GRAPH_HEIGHT, GRAPH_SAMPLES, registerStyles } from \"./styles\";\n\nexport class DebugPanel {\n\tconstructor(debugToggle) {\n\t\tthis.counters = new Counters([\n\t\t\t\"shapes\",\n\t\t\t\"sprites\",\n\t\t\t\"velocity\",\n\t\t\t\"bounds\",\n\t\t\t\"children\",\n\t\t]);\n\n\t\tthis.visible = false;\n\t\tthis.version = \"__VERSION__\";\n\t\tthis.debugToggle = debugToggle;\n\n\t\t// checkbox state\n\t\tconst hash = utils.getUriFragment();\n\t\tthis.options = {\n\t\t\thitbox: hash.hitbox || false,\n\t\t\tvelocity: hash.velocity || false,\n\t\t\tquadtree: hash.quadtree || false,\n\t\t};\n\n\t\t// frame time history for the sparkline graphs\n\t\tthis.updateHistory = new Float32Array(GRAPH_SAMPLES);\n\t\tthis.drawHistory = new Float32Array(GRAPH_SAMPLES);\n\t\tthis.memHistory = new Float32Array(GRAPH_SAMPLES);\n\t\tthis.historyIndex = 0;\n\n\t\t// register the web font / CSS and build the HTML overlay\n\t\tregisterStyles();\n\t\tthis.panel = null;\n\t\tthis.panelWrap = null;\n\t\tthis.stats = {};\n\t\tthis._buildPanel();\n\n\t\t// frame timing\n\t\tthis.frameUpdateStartTime = 0;\n\t\tthis.frameDrawStartTime = 0;\n\t\tthis.frameUpdateTime = 0;\n\t\tthis.frameDrawTime = 0;\n\n\t\t// event listener references for cleanup\n\t\tthis._onResize = () => {\n\t\t\tthis._syncPosition();\n\t\t};\n\t\tthis._onBeforeUpdate = (time) => {\n\t\t\tthis.frameUpdateStartTime = time;\n\t\t};\n\t\tthis._onAfterUpdate = (time) => {\n\t\t\tthis.frameUpdateTime = time - this.frameUpdateStartTime;\n\t\t\tif (this.visible) {\n\t\t\t\ttimer.countFPS();\n\t\t\t}\n\t\t};\n\t\tthis._onBeforeDraw = (time) => {\n\t\t\tthis.frameDrawStartTime = time;\n\t\t\tthis.counters.reset();\n\t\t};\n\t\tthis._onAfterDraw = (time) => {\n\t\t\tthis.frameDrawTime = time - this.frameDrawStartTime;\n\t\t\tif (this.visible) {\n\t\t\t\tthis._updatePanel();\n\t\t\t\tthis._drawQuadTree();\n\t\t\t}\n\t\t};\n\n\t\tevent.on(event.CANVAS_ONRESIZE, this._onResize);\n\t\tevent.on(event.GAME_BEFORE_UPDATE, this._onBeforeUpdate);\n\t\tevent.on(event.GAME_AFTER_UPDATE, this._onAfterUpdate);\n\t\tevent.on(event.GAME_BEFORE_DRAW, this._onBeforeDraw);\n\t\tevent.on(event.GAME_AFTER_DRAW, this._onAfterDraw);\n\n\t\tapplyPatches(this);\n\t}\n\n\t/** @private */\n\t_buildPanel() {\n\t\tthis.panelWrap = document.createElement(\"div\");\n\t\tthis.panelWrap.className = \"dbg-wrap\";\n\n\t\tthis.panel = document.createElement(\"div\");\n\t\tthis.panel.className = \"dbg\";\n\t\tthis.panelWrap.appendChild(this.panel);\n\t\tthis.panelWrap.style.display = \"none\";\n\n\t\t// helper to create a stat as two grid cells: label | value\n\t\tconst stat = (id, label) => {\n\t\t\tconst dimEl = document.createElement(\"span\");\n\t\t\tdimEl.className = \"dim\";\n\t\t\tdimEl.textContent = label;\n\t\t\tconst valEl = document.createElement(\"span\");\n\t\t\tvalEl.className = \"val\";\n\t\t\tvalEl.textContent = \"0\";\n\t\t\tthis.stats[id] = valEl;\n\t\t\treturn [dimEl, valEl];\n\t\t};\n\n\t\t// helper to create a checkbox label\n\t\tconst checkbox = (key, label) => {\n\t\t\tconst el = document.createElement(\"label\");\n\t\t\tconst cb = document.createElement(\"input\");\n\t\t\tcb.type = \"checkbox\";\n\t\t\tcb.checked = this.options[key];\n\t\t\tcb.addEventListener(\"change\", () => {\n\t\t\t\tthis.options[key] = cb.checked;\n\t\t\t\tgame.repaint();\n\t\t\t});\n\t\t\tel.appendChild(cb);\n\t\t\tel.appendChild(document.createTextNode(label));\n\t\t\treturn el;\n\t\t};\n\n\t\tconst toggleKey = String.fromCharCode(32 + this.debugToggle);\n\n\t\t// helper to append a stat (two grid cells) or a single element spanning 2 columns\n\t\tconst addStat = (id, label) => {\n\t\t\tconst [dimEl, valEl] = stat(id, label);\n\t\t\tthis.panel.appendChild(dimEl);\n\t\t\tthis.panel.appendChild(valEl);\n\t\t};\n\t\tconst addSpan2 = (el) => {\n\t\t\tel.style.gridColumn = \"span 2\";\n\t\t\tthis.panel.appendChild(el);\n\t\t};\n\n\t\t// row 1\n\t\taddStat(\"objects\", \"#objects\");\n\t\taddSpan2(checkbox(\"hitbox\", \"hitbox\"));\n\t\taddSpan2(checkbox(\"quadtree\", \"quadtree\"));\n\t\taddStat(\"update\", \"Update\");\n\t\taddStat(\"heap\", \"Heap\");\n\t\tconst fpsEl = document.createElement(\"span\");\n\t\tfpsEl.className = \"fps-val\";\n\t\tthis.stats.fps = fpsEl;\n\t\taddSpan2(fpsEl);\n\n\t\t// row 2\n\t\taddStat(\"draws\", \"#draws\");\n\t\taddSpan2(checkbox(\"velocity\", \"velocity\"));\n\t\taddSpan2(document.createElement(\"span\"));\n\t\taddStat(\"draw\", \"Draw\");\n\t\taddStat(\"pool\", \"Pool\");\n\t\taddSpan2(document.createElement(\"span\"));\n\n\t\t// row 3\n\t\taddStat(\"shapes\", \"Shapes\");\n\t\taddStat(\"sprites\", \"Sprites\");\n\t\taddStat(\"velocity_count\", \"Velocity\");\n\t\taddStat(\"bounds\", \"Bounds\");\n\t\taddStat(\"children\", \"Children\");\n\t\tconst helpEl = document.createElement(\"span\");\n\t\thelpEl.className = \"help\";\n\t\thelpEl.textContent = `[${toggleKey}] show/hide`;\n\t\taddSpan2(helpEl);\n\n\t\t// row 4: frame time graph (full width)\n\t\tconst frameRow = document.createElement(\"div\");\n\t\tframeRow.className = \"graph-row\";\n\n\t\tthis.graphCanvas = document.createElement(\"canvas\");\n\t\tthis.graphCanvas.height = GRAPH_HEIGHT;\n\t\tframeRow.appendChild(this.graphCanvas);\n\n\t\tconst frameLegend = document.createElement(\"div\");\n\t\tframeLegend.className = \"graph-label\";\n\t\tconst peakEl = document.createElement(\"span\");\n\t\tthis.stats.peak = peakEl;\n\t\tframeLegend.innerHTML = `<span class=\"update-color\">▬ update</span><span class=\"draw-color\">▬ draw</span>`;\n\t\tframeLegend.appendChild(peakEl);\n\t\tframeRow.appendChild(frameLegend);\n\t\tthis.panel.appendChild(frameRow);\n\n\t\t// row 5: memory graph (full width, Chrome only)\n\t\tif (window.performance?.memory) {\n\t\t\tconst memRow = document.createElement(\"div\");\n\t\t\tmemRow.className = \"graph-row\";\n\n\t\t\tthis.memCanvas = document.createElement(\"canvas\");\n\t\t\tthis.memCanvas.height = GRAPH_HEIGHT;\n\t\t\tmemRow.appendChild(this.memCanvas);\n\n\t\t\tconst memLegend = document.createElement(\"div\");\n\t\t\tmemLegend.className = \"graph-label\";\n\t\t\tconst memPeakEl = document.createElement(\"span\");\n\t\t\tthis.stats.memPeak = memPeakEl;\n\t\t\tmemLegend.innerHTML = `<span class=\"mem-color\">▬ heap</span>`;\n\t\t\tmemLegend.appendChild(memPeakEl);\n\t\t\tmemRow.appendChild(memLegend);\n\t\t\tthis.panel.appendChild(memRow);\n\t\t}\n\n\t\t// append to <html> element to avoid triggering melonJS's MutationObserver\n\t\t// (which watches document.body / canvas parent for DOM changes and triggers resize)\n\t\tdocument.documentElement.appendChild(this.panelWrap);\n\t\tthis._syncPosition();\n\t}\n\n\t/** @private */\n\t_syncPosition() {\n\t\tconst rect = video.renderer.getCanvas().getBoundingClientRect();\n\t\tconst s = this.panelWrap.style;\n\t\ts.top = `${rect.top}px`;\n\t\ts.left = `${rect.left}px`;\n\t\ts.width = `${rect.width}px`;\n\t}\n\n\t/** @private */\n\t_updatePanel() {\n\t\tthis.stats.objects.textContent = game.world.children.length;\n\t\tthis.stats.draws.textContent = game.world.drawCount;\n\t\tthis.stats.update.textContent = `${this.frameUpdateTime.toFixed(2)}ms`;\n\t\tthis.stats.draw.textContent = `${this.frameDrawTime.toFixed(2)}ms`;\n\t\tthis.stats.fps.textContent = `${timer.fps}/${timer.maxfps} fps`;\n\t\tthis.stats.shapes.textContent = this.counters.get(\"shapes\");\n\t\tthis.stats.sprites.textContent = this.counters.get(\"sprites\");\n\t\tthis.stats.velocity_count.textContent = this.counters.get(\"velocity\");\n\t\tthis.stats.bounds.textContent = this.counters.get(\"bounds\");\n\t\tthis.stats.children.textContent = this.counters.get(\"children\");\n\t\tthis.stats.pool.textContent = pool.getInstanceCount();\n\n\t\tif (window.performance?.memory) {\n\t\t\tconst used = Number(\n\t\t\t\t(window.performance.memory.usedJSHeapSize / 1048576).toFixed(1),\n\t\t\t);\n\t\t\tconst total = Number(\n\t\t\t\t(window.performance.memory.totalJSHeapSize / 1048576).toFixed(1),\n\t\t\t);\n\t\t\tthis.stats.heap.textContent = `${used}/${total}MB`;\n\t\t} else {\n\t\t\tthis.stats.heap.textContent = \"n/a\";\n\t\t}\n\n\t\t// record frame times and memory\n\t\tthis.updateHistory[this.historyIndex] = this.frameUpdateTime;\n\t\tthis.drawHistory[this.historyIndex] = this.frameDrawTime;\n\t\tif (window.performance?.memory) {\n\t\t\tthis.memHistory[this.historyIndex] =\n\t\t\t\twindow.performance.memory.usedJSHeapSize / 1048576;\n\t\t}\n\t\tthis.historyIndex = (this.historyIndex + 1) % GRAPH_SAMPLES;\n\n\t\tdrawFrameGraph(\n\t\t\tthis.graphCanvas,\n\t\t\tthis.updateHistory,\n\t\t\tthis.drawHistory,\n\t\t\tthis.historyIndex,\n\t\t\tthis.stats.peak,\n\t\t);\n\t\tif (this.memCanvas) {\n\t\t\tdrawMemGraph(\n\t\t\t\tthis.memCanvas,\n\t\t\t\tthis.memHistory,\n\t\t\t\tthis.historyIndex,\n\t\t\t\tthis.stats.memPeak,\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * show the debug panel\n\t */\n\tshow() {\n\t\tif (!this.visible) {\n\t\t\tthis.panelWrap.style.display = \"\";\n\t\t\tthis.visible = true;\n\t\t\tthis._syncPosition();\n\t\t\tgame.repaint();\n\t\t}\n\t}\n\n\t/**\n\t * hide the debug panel\n\t */\n\thide() {\n\t\tif (this.visible) {\n\t\t\tthis.panelWrap.style.display = \"none\";\n\t\t\tthis.visible = false;\n\t\t\tgame.repaint();\n\t\t}\n\t}\n\n\t/** @private */\n\t_drawQuadTree() {\n\t\tif (!this.options.quadtree) {\n\t\t\treturn;\n\t\t}\n\t\tconst renderer = video.renderer;\n\t\trenderer.save();\n\t\tconst { x, y } = game.viewport.pos;\n\t\trenderer.translate(-x, -y);\n\t\tthis._drawQuadTreeNode(renderer, game.world.broadphase);\n\t\trenderer.translate(x, y);\n\t\trenderer.restore();\n\t\t// flush is needed because this runs after GAME_AFTER_DRAW,\n\t\t// which is emitted after the main renderer.flush()\n\t\trenderer.flush();\n\t}\n\n\t/** @private */\n\t_drawQuadTreeNode(renderer, node) {\n\t\tconst bounds = node.bounds;\n\n\t\t// wireframe outline \u2014 color based on density\n\t\tif (node.objects.length > 0) {\n\t\t\tconst ratio = Math.min(node.objects.length / node.max_objects, 1);\n\t\t\t// green (few) \u2192 yellow \u2192 red (full)\n\t\t\tconst r = Math.round(ratio < 0.5 ? ratio * 2 * 255 : 255);\n\t\t\tconst g = Math.round(ratio < 0.5 ? 255 : 255 * (1 - (ratio - 0.5) * 2));\n\t\t\trenderer.setColor(`rgb(${r},${g},0)`);\n\t\t} else {\n\t\t\trenderer.setColor(\"green\");\n\t\t}\n\t\trenderer.strokeRect(bounds.left, bounds.top, bounds.width, bounds.height);\n\n\t\t// recurse into subnodes\n\t\tfor (let i = 0; i < node.nodes.length; i++) {\n\t\t\tthis._drawQuadTreeNode(renderer, node.nodes[i]);\n\t\t}\n\t}\n\n\tdestroy() {\n\t\tthis.hide();\n\t\t// remove the HTML overlay\n\t\tif (this.panelWrap?.parentElement) {\n\t\t\tthis.panelWrap.parentElement.removeChild(this.panelWrap);\n\t\t}\n\t\tevent.off(event.CANVAS_ONRESIZE, this._onResize);\n\t\tevent.off(event.GAME_BEFORE_UPDATE, this._onBeforeUpdate);\n\t\tevent.off(event.GAME_AFTER_UPDATE, this._onAfterUpdate);\n\t\tevent.off(event.GAME_BEFORE_DRAW, this._onBeforeDraw);\n\t\tevent.off(event.GAME_AFTER_DRAW, this._onAfterDraw);\n\t}\n}\n", "export default class Counters extends Map {\n\tconstructor(keys) {\n\t\tsuper(\n\t\t\tkeys?.map((key) => {\n\t\t\t\treturn [key, 0];\n\t\t\t}),\n\t\t);\n\t}\n\n\treset() {\n\t\tfor (const key of this.keys()) {\n\t\t\tthis.set(key, 0);\n\t\t}\n\t}\n\n\tinc(stat, value = 1) {\n\t\tthis.set(stat, (this.get(stat) ?? 0) + value);\n\t}\n}\n", "import { timer } from \"melonjs\";\nimport { GRAPH_HEIGHT, GRAPH_SAMPLES } from \"./styles\";\n\n/**\n * Resize a canvas backing store to match its CSS layout width.\n * @param {HTMLCanvasElement} canvas\n */\nfunction fitCanvas(canvas) {\n\tconst w = canvas.clientWidth;\n\tif (w > 0 && canvas.width !== w) {\n\t\tcanvas.width = w;\n\t}\n}\n\n/**\n * Draw the stacked frame-time sparkline (update + draw).\n * @param {HTMLCanvasElement} canvas\n * @param {Float32Array} updateHistory\n * @param {Float32Array} drawHistory\n * @param {number} historyIndex - current write position in the ring buffer\n * @param {HTMLElement} peakEl - element to display peak value\n */\nexport function drawFrameGraph(\n\tcanvas,\n\tupdateHistory,\n\tdrawHistory,\n\thistoryIndex,\n\tpeakEl,\n) {\n\tfitCanvas(canvas);\n\tconst ctx = canvas.getContext(\"2d\");\n\tconst w = canvas.width;\n\tconst h = GRAPH_HEIGHT;\n\tif (w === 0) {\n\t\treturn;\n\t}\n\n\t// find peak for scaling\n\tlet peak = 1;\n\tfor (let i = 0; i < GRAPH_SAMPLES; i++) {\n\t\tconst total = updateHistory[i] + drawHistory[i];\n\t\tif (total > peak) {\n\t\t\tpeak = total;\n\t\t}\n\t}\n\tpeak = Math.ceil(peak / 4) * 4;\n\tif (peak < 4) {\n\t\tpeak = 4;\n\t}\n\n\tpeakEl.textContent = `peak ${peak}ms`;\n\n\tctx.clearRect(0, 0, w, h);\n\n\t// target frame time line (e.g. 16.67ms for 60fps)\n\tconst targetMs = 1000 / timer.maxfps;\n\tconst targetY = h - (targetMs / peak) * h;\n\tif (targetY > 0 && targetY < h) {\n\t\tctx.strokeStyle = \"rgba(40,80,140,0.5)\";\n\t\tctx.setLineDash([2, 2]);\n\t\tctx.beginPath();\n\t\tctx.moveTo(0, targetY);\n\t\tctx.lineTo(w, targetY);\n\t\tctx.stroke();\n\t\tctx.setLineDash([]);\n\t}\n\n\t// stacked bars: draw (amber) on bottom, update (cyan) on top\n\tconst barW = w / GRAPH_SAMPLES;\n\tfor (let i = 0; i < GRAPH_SAMPLES; i++) {\n\t\tconst idx = (historyIndex + i) % GRAPH_SAMPLES;\n\t\tconst updateVal = updateHistory[idx];\n\t\tconst drawVal = drawHistory[idx];\n\t\tconst updateH = (updateVal / peak) * h;\n\t\tconst drawH = (drawVal / peak) * h;\n\t\tconst x = Math.round(i * barW);\n\t\tconst bw = Math.max(1, Math.round((i + 1) * barW) - x);\n\n\t\t// draw time on bottom (amber)\n\t\tif (drawH > 0) {\n\t\t\tctx.fillStyle = \"#ffb800\";\n\t\t\tctx.fillRect(x, h - drawH - updateH, bw, drawH);\n\t\t}\n\t\t// update time on top (cyan)\n\t\tif (updateH > 0) {\n\t\t\tctx.fillStyle = \"#00e0ff\";\n\t\t\tctx.fillRect(x, h - updateH, bw, updateH);\n\t\t}\n\t}\n}\n\n/**\n * Draw the memory heap area graph.\n * @param {HTMLCanvasElement} canvas\n * @param {Float32Array} memHistory - heap usage in MB\n * @param {number} historyIndex - current write position in the ring buffer\n * @param {HTMLElement} peakEl - element to display peak value\n */\nexport function drawMemGraph(canvas, memHistory, historyIndex, peakEl) {\n\tfitCanvas(canvas);\n\tconst ctx = canvas.getContext(\"2d\");\n\tconst w = canvas.width;\n\tconst h = GRAPH_HEIGHT;\n\tif (w === 0) {\n\t\treturn;\n\t}\n\n\t// find peak memory for scaling\n\tlet peak = 1;\n\tfor (let i = 0; i < GRAPH_SAMPLES; i++) {\n\t\tif (memHistory[i] > peak) {\n\t\t\tpeak = memHistory[i];\n\t\t}\n\t}\n\tpeak = Math.ceil(peak / 4) * 4;\n\tif (peak < 4) {\n\t\tpeak = 4;\n\t}\n\n\tpeakEl.textContent = `peak ${peak}MB`;\n\n\tctx.clearRect(0, 0, w, h);\n\n\t// filled area graph\n\tconst barW = w / GRAPH_SAMPLES;\n\tctx.fillStyle = \"rgba(198,120,221,0.3)\";\n\tctx.strokeStyle = \"#c678dd\";\n\tctx.lineWidth = 1;\n\tctx.beginPath();\n\tctx.moveTo(0, h);\n\tfor (let i = 0; i < GRAPH_SAMPLES; i++) {\n\t\tconst idx = (historyIndex + i) % GRAPH_SAMPLES;\n\t\tconst val = memHistory[idx];\n\t\tconst y = h - (val / peak) * h;\n\t\tconst x = Math.round(i * barW);\n\t\tctx.lineTo(x, y);\n\t}\n\tctx.lineTo(w, h);\n\tctx.closePath();\n\tctx.fill();\n\n\t// stroke the top edge\n\tctx.beginPath();\n\tfor (let i = 0; i < GRAPH_SAMPLES; i++) {\n\t\tconst idx = (historyIndex + i) % GRAPH_SAMPLES;\n\t\tconst val = memHistory[idx];\n\t\tconst y = h - (val / peak) * h;\n\t\tconst x = Math.round(i * barW);\n\t\tif (i === 0) {\n\t\t\tctx.moveTo(x, y);\n\t\t} else {\n\t\t\tctx.lineTo(x, y);\n\t\t}\n\t}\n\tctx.stroke();\n}\n", "import fontSource from \"./font/PressStart2P.ttf\";\n\nconst FONT_NAME = \"PressStart2P\";\n\nexport const GRAPH_HEIGHT = 30;\nexport const GRAPH_SAMPLES = 200;\n\nlet registered = false;\n\n/**\n * Inject the @font-face rule and all debug panel CSS into the document.\n * Safe to call multiple times \u2014 only injects once.\n */\nexport function registerStyles() {\n\tif (registered) {\n\t\treturn;\n\t}\n\tconst style = document.createElement(\"style\");\n\tstyle.textContent = `\n@font-face { font-family: \"${FONT_NAME}\"; src: url(\"${fontSource}\") format(\"truetype\"); }\n\n.dbg-wrap {\n position: fixed; pointer-events: none; z-index: 9999;\n}\n\n.dbg {\n font-family: \"${FONT_NAME}\", monospace; font-size: 8px; color: #b0c4d8;\n background:\n repeating-linear-gradient(0deg, transparent 0px, transparent 2px, rgba(0,0,0,0.06) 2px, rgba(0,0,0,0.06) 4px),\n linear-gradient(180deg, rgba(6,8,18,0.94) 0%, rgba(10,14,28,0.92) 100%);\n border-bottom: 2px solid #1a3050;\n box-shadow: inset 0 0 0 1px rgba(40,80,140,0.3), 0 2px 8px rgba(0,0,0,0.5);\n padding: 6px 10px 5px; pointer-events: none;\n display: grid; grid-template-columns: repeat(6, auto 1fr); gap: 2px 0;\n align-items: center; line-height: 1.7;\n image-rendering: pixelated;\n}\n\n/* labels: muted steel blue, right-aligned */\n.dbg .dim {\n color: #4a6a88; text-align: right; padding-right: 6px;\n white-space: nowrap; text-transform: lowercase;\n}\n\n/* values: bright cyan */\n.dbg .val {\n color: #00e0ff; white-space: nowrap; padding-right: 14px;\n text-shadow: 0 0 6px rgba(0,224,255,0.2);\n}\n\n/* fps: amber accent, bold presence */\n.dbg .fps-val {\n color: #ffb800; text-shadow: 0 0 6px rgba(255,184,0,0.25);\n justify-content: flex-end; white-space: nowrap;\n}\n\n/* help text */\n.dbg .help {\n color: #384858; justify-content: flex-end;\n font-size: 7px; white-space: nowrap;\n}\n\n/* checkbox labels */\n.dbg label {\n cursor: pointer; white-space: nowrap; pointer-events: auto;\n display: flex; align-items: center; gap: 5px;\n color: #6888a8; transition: color 0.15s;\n}\n.dbg label:hover { color: #00e0ff; }\n\n/* pixel-art checkbox: NES-style toggle */\n.dbg input[type=checkbox] {\n appearance: none; -webkit-appearance: none;\n width: 10px; height: 10px; margin: 0; cursor: pointer;\n border: 2px solid #2a4a6a; background: rgba(0,20,40,0.5);\n image-rendering: pixelated; flex-shrink: 0;\n transition: border-color 0.15s, background 0.15s, box-shadow 0.15s;\n}\n.dbg input[type=checkbox]:hover {\n border-color: #00b8d4;\n}\n.dbg input[type=checkbox]:checked {\n border-color: #00e0ff; background: #00e0ff;\n box-shadow: 0 0 4px rgba(0,224,255,0.4), inset 0 0 0 1px rgba(6,8,18,0.9);\n}\n\n/* graph rows */\n.dbg .graph-row {\n grid-column: 1 / span 12;\n display: flex; align-items: center; gap: 8px;\n border-top: 1px solid rgba(40,80,140,0.2);\n padding-top: 4px; margin-top: 2px;\n}\n.dbg .graph-row canvas {\n flex: 1; min-width: 0; height: ${GRAPH_HEIGHT}px;\n image-rendering: pixelated;\n border: 1px solid #1a3050;\n background: rgba(0,0,0,0.35);\n box-shadow: 0 0 4px rgba(0,0,0,0.3);\n}\n.dbg .graph-label {\n font-size: 7px; color: #384858; white-space: nowrap;\n display: flex; flex-direction: column; gap: 2px; min-width: 70px;\n}\n.dbg .graph-label .update-color { color: #00e0ff; }\n.dbg .graph-label .draw-color { color: #ffb800; }\n.dbg .graph-label .mem-color { color: #c678dd; }\n`;\n\tdocument.head.appendChild(style);\n\tregistered = true;\n}\n", "import {\n\tBitmapText,\n\tCamera2d,\n\tContainer,\n\tEntity,\n\tImageLayer,\n\tplugin,\n\tRenderable,\n\tText,\n} from \"melonjs\";\n\n/**\n * Monkey-patch melonJS rendering classes to draw debug overlays\n * (hitboxes, bounding boxes, velocity vectors) and collect stats.\n *\n * @param {import(\"./debugPanel\").DebugPanel} panel - the debug panel instance\n */\nexport function applyPatches(panel) {\n\t// patch Renderable\n\tplugin.patch(Renderable, \"postDraw\", function (renderer) {\n\t\t// biome-ignore lint/complexity/noArguments: needed to forward all arguments to patched method\n\t\tthis._patched.apply(this, arguments);\n\n\t\tif (this.image !== undefined) {\n\t\t\tpanel.counters.inc(\"sprites\");\n\t\t}\n\t\tpanel.counters.inc(\"bounds\");\n\t\tif (this instanceof Container) {\n\t\t\tpanel.counters.inc(\"children\");\n\t\t}\n\n\t\t// skip types that have their own dedicated patches\n\t\tif (\n\t\t\t!panel.visible ||\n\t\t\t!panel.options.hitbox ||\n\t\t\tthis instanceof Entity ||\n\t\t\tthis.ancestor instanceof Entity ||\n\t\t\tthis instanceof Text ||\n\t\t\tthis instanceof BitmapText ||\n\t\t\tthis instanceof Camera2d ||\n\t\t\tthis instanceof ImageLayer\n\t\t) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst bounds = this.getBounds();\n\t\tif (!bounds.isFinite()) {\n\t\t\treturn;\n\t\t}\n\n\t\trenderer.save();\n\n\t\t// undo ancestor world transform for non-floating renderables\n\t\tif (this.ancestor !== undefined && !this.floating) {\n\t\t\tconst absPos = this.ancestor.getAbsolutePosition();\n\t\t\trenderer.translate(-absPos.x, -absPos.y);\n\t\t}\n\n\t\t// renderable bounding box (green)\n\t\trenderer.setColor(\"green\");\n\t\trenderer.stroke(bounds);\n\n\t\t// sprite mask (orange)\n\t\tif (this.mask !== undefined) {\n\t\t\trenderer.setColor(\"orange\");\n\t\t\trenderer.stroke(this.mask);\n\t\t}\n\n\t\t// body bounds and collision shapes\n\t\tif (this.body !== undefined) {\n\t\t\trenderer.translate(bounds.x, bounds.y);\n\n\t\t\trenderer.setColor(\"orange\");\n\t\t\trenderer.stroke(this.body.getBounds());\n\n\t\t\trenderer.setColor(\"red\");\n\t\t\tfor (const shape of this.body.shapes) {\n\t\t\t\trenderer.stroke(shape);\n\t\t\t\tpanel.counters.inc(\"shapes\");\n\t\t\t}\n\t\t}\n\n\t\trenderer.restore();\n\t});\n\n\t// patch BitmapText\n\tplugin.patch(BitmapText, \"draw\", function (renderer) {\n\t\t// biome-ignore lint/complexity/noArguments: needed to forward all arguments to patched method\n\t\tthis._patched.apply(this, arguments);\n\n\t\tif (!panel.visible || !panel.options.hitbox) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst bounds = this.getBounds();\n\n\t\trenderer.save();\n\n\t\t// adjust for anchor point offset since bounds position is already anchored\n\t\tif (this.ancestor !== undefined) {\n\t\t\trenderer.translate(\n\t\t\t\tthis.anchorPoint.x * bounds.width,\n\t\t\t\tthis.anchorPoint.y * bounds.height,\n\t\t\t);\n\t\t}\n\n\t\trenderer.setColor(\"green\");\n\t\trenderer.stroke(bounds);\n\n\t\trenderer.restore();\n\t});\n\n\t// patch Text\n\tplugin.patch(Text, \"draw\", function (renderer) {\n\t\t// biome-ignore lint/complexity/noArguments: needed to forward all arguments to patched method\n\t\tthis._patched.apply(this, arguments);\n\n\t\tif (!panel.visible || !panel.options.hitbox) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst bounds = this.getBounds();\n\n\t\trenderer.save();\n\n\t\t// undo ancestor world transform for floating text\n\t\tif (\n\t\t\tthis.ancestor !== undefined &&\n\t\t\t!this.root &&\n\t\t\t!this.ancestor.root &&\n\t\t\tthis.ancestor.isFloating\n\t\t) {\n\t\t\tconst absPos = this.ancestor.getAbsolutePosition();\n\t\t\trenderer.translate(-absPos.x, -absPos.y);\n\t\t}\n\n\t\trenderer.setColor(\"green\");\n\t\trenderer.stroke(bounds);\n\n\t\trenderer.restore();\n\t});\n\n\t// patch Entity\n\tplugin.patch(Entity, \"postDraw\", function (renderer) {\n\t\tif (panel.visible && panel.options.hitbox) {\n\t\t\trenderer.save();\n\n\t\t\tconst bodyBounds = this.body.getBounds();\n\n\t\t\t// entity renderable bounding box (green) \u2014 drawn in entity's\n\t\t\t// preDraw local space where origin = entity anchor point\n\t\t\tif (this.renderable instanceof Renderable) {\n\t\t\t\tconst r = this.renderable;\n\t\t\t\trenderer.setColor(\"green\");\n\t\t\t\trenderer.strokeRect(\n\t\t\t\t\t-r.anchorPoint.x * r.width,\n\t\t\t\t\t-r.anchorPoint.y * r.height,\n\t\t\t\t\tr.width,\n\t\t\t\t\tr.height,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// move from anchor point to body origin for body/collision overlays\n\t\t\trenderer.translate(\n\t\t\t\t-this.anchorPoint.x * bodyBounds.width,\n\t\t\t\t-this.anchorPoint.y * bodyBounds.height,\n\t\t\t);\n\n\t\t\trenderer.setColor(\"orange\");\n\t\t\trenderer.stroke(bodyBounds);\n\n\t\t\trenderer.setColor(\"red\");\n\t\t\tfor (const shape of this.body.shapes) {\n\t\t\t\trenderer.stroke(shape);\n\t\t\t\tpanel.counters.inc(\"shapes\");\n\t\t\t}\n\n\t\t\trenderer.restore();\n\t\t}\n\n\t\tif (\n\t\t\tpanel.visible &&\n\t\t\tpanel.options.velocity &&\n\t\t\t(this.body.vel.x || this.body.vel.y)\n\t\t) {\n\t\t\tconst bodyBounds = this.body.getBounds();\n\t\t\tconst hWidth = bodyBounds.width / 2;\n\t\t\tconst hHeight = bodyBounds.height / 2;\n\n\t\t\trenderer.save();\n\t\t\trenderer.lineWidth = 1;\n\t\t\trenderer.setColor(\"blue\");\n\t\t\trenderer.translate(0, -hHeight);\n\t\t\trenderer.strokeLine(\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t\tMath.trunc(this.body.vel.x * hWidth),\n\t\t\t\tMath.trunc(this.body.vel.y * hHeight),\n\t\t\t);\n\t\t\tpanel.counters.inc(\"velocity\");\n\t\t\trenderer.restore();\n\t\t}\n\n\t\t// biome-ignore lint/complexity/noArguments: needed to forward all arguments to patched method\n\t\tthis._patched.apply(this, arguments);\n\t});\n}\n"], | ||
| "mappings": "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", | ||
| "names": ["event", "plugin", "utils", "timer", "timer", "plugin", "event", "utils"] | ||
| } |
+2
-2
| { | ||
| "name": "@melonjs/debug-plugin", | ||
| "version": "15.0.2", | ||
| "version": "15.0.3", | ||
| "description": "melonJS debug plugin", | ||
| "homepage": "https://github.com/melonjs/melonJS/tree/master/packages/debug-plugin#readme", | ||
| "homepage": "https://www.npmjs.com/package/@melonjs/debug-plugin", | ||
| "type": "module", | ||
@@ -7,0 +7,0 @@ "keywords": [ |
Sorry, the diff of this file is too big to display
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
300178
-0.02%