From 06e8dd8a964bfa3aa9ce75c71f418f5f0a6db479 Mon Sep 17 00:00:00 2001 From: Jake Yallop <30874283+JakeYallop@users.noreply.github.com> Date: Thu, 5 Mar 2026 22:58:20 +0000 Subject: [PATCH 1/8] Build extension for Firefox --- package-lock.json | 53 ++++++------- package.json | 2 +- packages/devtool-chrome/manifest.firefox.json | 42 +++++++++++ packages/devtool-chrome/package.json | 7 +- .../devtool-chrome/src/background/index.ts | 1 - .../devtool-chrome/src/devtools/devtools.ts | 37 +++++---- .../src/devtools/panel/panel.tsx | 23 +++++- .../vite.firefox-scripts.config.ts | 66 ++++++++++++++++ .../devtool-chrome/vite.firefox.config.ts | 75 +++++++++++++++++++ 9 files changed, 252 insertions(+), 54 deletions(-) create mode 100644 packages/devtool-chrome/manifest.firefox.json create mode 100644 packages/devtool-chrome/vite.firefox-scripts.config.ts create mode 100644 packages/devtool-chrome/vite.firefox.config.ts diff --git a/package-lock.json b/package-lock.json index c80900e..57842a9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -164,7 +164,6 @@ "version": "4.23.3", "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.23.3.tgz", "integrity": "sha512-P4VAKFHqU0wx9O+q29Q8YVuaowaZ5EM77rxfmGnkHUJggh28useXQdopokgwMeYw2XUht49WX5RcTQ40rZIabw==", - "peer": true, "dependencies": { "@algolia/client-common": "4.23.3", "@algolia/requester-common": "4.23.3", @@ -285,7 +284,6 @@ "version": "7.24.6", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.6.tgz", "integrity": "sha512-qAHSfAdVyFmIvl0VHELib8xar7ONuSHrE2hLnsaWkYNTI68dmi1x8GYDhJjMI/e7XWal9QBlZkwbOnkcw7Z8gQ==", - "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.24.6", @@ -2154,6 +2152,7 @@ "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", "optional": true, + "peer": true, "dependencies": { "@jridgewell/trace-mapping": "0.3.9" }, @@ -2166,6 +2165,7 @@ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", "optional": true, + "peer": true, "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -7584,7 +7584,6 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz", "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", - "peer": true, "dependencies": { "@babel/core": "^7.21.3", "@svgr/babel-preset": "8.1.0", @@ -7905,25 +7904,29 @@ "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", - "optional": true + "optional": true, + "peer": true }, "node_modules/@tsconfig/node12": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "optional": true + "optional": true, + "peer": true }, "node_modules/@tsconfig/node14": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "optional": true + "optional": true, + "peer": true }, "node_modules/@tsconfig/node16": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "optional": true + "optional": true, + "peer": true }, "node_modules/@types/acorn": { "version": "4.0.6", @@ -8221,7 +8224,6 @@ "version": "20.14.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.1.tgz", "integrity": "sha512-T2MzSGEu+ysB/FkWfqmhV3PLyQlowdptmmgD20C6QxsS8Fmv5SjpZ1ayXaEC0S21/h5UJ9iA6W/5vSNU5l00OA==", - "peer": true, "dependencies": { "undici-types": "~5.26.4" } @@ -8263,7 +8265,6 @@ "version": "18.3.3", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.3.tgz", "integrity": "sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==", - "peer": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" @@ -8284,7 +8285,6 @@ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.0.tgz", "integrity": "sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==", "devOptional": true, - "peer": true, "dependencies": { "@types/react": "*" } @@ -8463,7 +8463,6 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.12.0.tgz", "integrity": "sha512-dm/J2UDY3oV3TKius2OUZIFHsomQmpHtsV0FTh1WO8EKgHLQ1QCADUqscPgTpU+ih1e21FQSRjXckHn3txn6kQ==", "dev": true, - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "7.12.0", "@typescript-eslint/types": "7.12.0", @@ -8851,7 +8850,6 @@ "version": "8.11.3", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -8907,7 +8905,6 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -8967,7 +8964,6 @@ "version": "4.23.3", "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-4.23.3.tgz", "integrity": "sha512-Le/3YgNvjW9zxIQMRhUHuhiUjAlKY/zsdZpfq4dlLqg6mEm0nL6yk+7f2hDOtLpxsgE4jSzDmvHL7nXdBp5feg==", - "peer": true, "dependencies": { "@algolia/cache-browser-local-storage": "4.23.3", "@algolia/cache-common": "4.23.3", @@ -9747,7 +9743,6 @@ "url": "https://github.com/sponsors/ai" } ], - "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001587", "electron-to-chromium": "^1.4.668", @@ -10605,7 +10600,8 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "optional": true + "optional": true, + "peer": true }, "node_modules/cross-spawn": { "version": "7.0.3", @@ -11307,6 +11303,7 @@ "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", "optional": true, + "peer": true, "engines": { "node": ">=0.3.1" } @@ -11793,7 +11790,6 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", "devOptional": true, - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -11849,7 +11845,6 @@ "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", "dev": true, - "peer": true, "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -15887,7 +15882,8 @@ "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "optional": true + "optional": true, + "peer": true }, "node_modules/markdown-extensions": { "version": "2.0.0", @@ -19385,7 +19381,6 @@ "url": "https://github.com/sponsors/ai" } ], - "peer": true, "dependencies": { "nanoid": "^3.3.7", "picocolors": "^1.0.0", @@ -20119,7 +20114,6 @@ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.0.tgz", "integrity": "sha512-J9odKxERhCQ10OC2yb93583f6UnYutOeiV5i0zEDS7UGTdUt0u+y8erxl3lBKvwo/JHyyoEdXjwp4dke9oyZ/g==", "dev": true, - "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -20468,7 +20462,6 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", - "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -20667,7 +20660,6 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", - "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -20731,7 +20723,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-6.0.0.tgz", "integrity": "sha512-YMMxTUQV/QFSnbgrP3tjDzLHRg7vsbMn8e9HAa8o/1iXoiomo48b7sk/kkmWEuWNDPJVlKSJRB6Y2fHqdJk+SQ==", - "peer": true, "dependencies": { "@types/react": "*" }, @@ -20821,7 +20812,6 @@ "version": "5.3.4", "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.3.4.tgz", "integrity": "sha512-Ys9K+ppnJah3QuaRiLxk+jDWOR1MekYQrlytiXxC1RyfbdsZkS5pvKAzCCr031xHixZwpnsYNT5xysdFHQaYsA==", - "peer": true, "dependencies": { "@babel/runtime": "^7.12.13", "history": "^4.9.0", @@ -21744,7 +21734,6 @@ "version": "8.16.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.16.0.tgz", "integrity": "sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw==", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "json-schema-traverse": "^1.0.0", @@ -22816,7 +22805,6 @@ "version": "3.4.3", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.3.tgz", "integrity": "sha512-U7sxQk/n397Bmx4JHbJx/iSOOv5G+II3f1kpLpY2QeUv5DcPdcTsYLlusZfq1NthHS1c1cZoyFmmkex1rzke0A==", - "peer": true, "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", @@ -23112,6 +23100,7 @@ "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "optional": true, + "peer": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -23154,7 +23143,8 @@ "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "optional": true + "optional": true, + "peer": true }, "node_modules/tsconfig-paths": { "version": "3.15.0", @@ -23350,7 +23340,6 @@ "version": "5.4.5", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -23875,7 +23864,8 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "optional": true + "optional": true, + "peer": true }, "node_modules/validate-npm-package-license": { "version": "3.0.4", @@ -23960,7 +23950,6 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.12.tgz", "integrity": "sha512-/gC8GxzxMK5ntBwb48pR32GGhENnjtY30G4A0jemunsBkiEZFw60s8InGpN8gkhHEkjnRK1aSAxeQgwvFhUHAA==", "dev": true, - "peer": true, "dependencies": { "esbuild": "^0.20.1", "postcss": "^8.4.38", @@ -24087,7 +24076,6 @@ "version": "5.91.0", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.91.0.tgz", "integrity": "sha512-rzVwlLeBWHJbmgTC/8TvAcu5vpJNII+MelQpylD4jNERPwpBJOE2lEcko1zJX3QJeLjTTAnQxn/OJ8bjDzVQaw==", - "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.3", "@types/estree": "^1.0.5", @@ -24850,6 +24838,7 @@ "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", "optional": true, + "peer": true, "engines": { "node": ">=6" } diff --git a/package.json b/package.json index a28455f..1901cd9 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "start:docs": "npm run start --workspace @devtool/docs", "prebuild": "npm run clean", "build": "npm run --ws build --if-present", - "postbuild": "mkdir -p .upload && cd ./packages/devtool-chrome && zip -r chrome.zip ./dist/chrome/* && mv chrome.zip ../../.upload/", + "postbuild": "mkdir -p .upload && cd ./packages/devtool-chrome && zip -r chrome.zip ./dist/chrome/* && mv chrome.zip ../../.upload/ && zip -r firefox.zip ./dist/firefox/* && mv firefox.zip ../../.upload/", "watch": "npm run --ws watch --if-present", "types": "tsc --noEmit", "release": "tsx ./scripts/release.mts", diff --git a/packages/devtool-chrome/manifest.firefox.json b/packages/devtool-chrome/manifest.firefox.json new file mode 100644 index 0000000..2a17e1a --- /dev/null +++ b/packages/devtool-chrome/manifest.firefox.json @@ -0,0 +1,42 @@ +{ + "manifest_version": 3, + "name": "PixiJS DevTools", + "description": "DevTools for PixiJS", + "action": { + "default_icon": { + "16": "pixi-icon-inactive-16.png", + "48": "pixi-icon-inactive-48.png", + "128": "pixi-icon-inactive-128.png" + } + }, + "content_scripts": [ + { + "matches": ["http://*/*", "https://*/*", ""], + "js": ["content/index.js"], + "css": ["contentStyle.css"], + "run_at": "document_start" + } + ], + "background": { + "scripts": ["background/index.js"] + }, + "icons": { + "16": "pixi-icon-active-16.png", + "48": "pixi-icon-active-48.png", + "128": "pixi-icon-active-128.png" + }, + "permissions": ["activeTab"], + "devtools_page": "devtools.html", + "web_accessible_resources": [ + { + "resources": ["contentStyle.css", "inject/index.js", "inject/index2.js"], + "matches": [""] + } + ], + "browser_specific_settings": { + "gecko": { + "id": "pixijs-devtools@pixijs.io", + "strict_min_version": "109.0" + } + } +} diff --git a/packages/devtool-chrome/package.json b/packages/devtool-chrome/package.json index ea6a7b3..d1ce8ed 100644 --- a/packages/devtool-chrome/package.json +++ b/packages/devtool-chrome/package.json @@ -4,9 +4,14 @@ "version": "2.3.1", "type": "module", "scripts": { - "build": "run-s build:*", + "build": "run-s build:chrome build:inject build:firefox build:firefox-devtools build:firefox-bg build:firefox-content build:firefox-inject", "build:chrome": "vite build --config vite.chrome.config.ts", "build:inject": "vite build --config vite.inject.config.ts", + "build:firefox": "vite build --config vite.firefox.config.ts", + "build:firefox-devtools": "ENTRY=devtools vite build --config vite.firefox-scripts.config.ts", + "build:firefox-bg": "ENTRY=background vite build --config vite.firefox-scripts.config.ts", + "build:firefox-content": "ENTRY=content vite build --config vite.firefox-scripts.config.ts", + "build:firefox-inject": "ENTRY=inject vite build --config vite.firefox-scripts.config.ts", "watch": "run-p watch:*", "watch:chrome": "vite dev --config vite.chrome.config.ts", "watch:inject": "node -e \"setTimeout(() => process.exit(0), 5000)\" && vite build --config vite.inject.config.ts --watch --mode development", diff --git a/packages/devtool-chrome/src/background/index.ts b/packages/devtool-chrome/src/background/index.ts index 7a4c72b..df6a234 100644 --- a/packages/devtool-chrome/src/background/index.ts +++ b/packages/devtool-chrome/src/background/index.ts @@ -84,7 +84,6 @@ chrome.runtime.onMessage.addListener((request: Message, sender: chrome.runtime.M } else { sendResponse({ status: 'error', message: 'sender.tab not defined.' }); } - return true; }); // update icon and popup when the active tab changes diff --git a/packages/devtool-chrome/src/devtools/devtools.ts b/packages/devtool-chrome/src/devtools/devtools.ts index dc90623..95be213 100644 --- a/packages/devtool-chrome/src/devtools/devtools.ts +++ b/packages/devtool-chrome/src/devtools/devtools.ts @@ -1,21 +1,26 @@ import { DevtoolMessage } from '@devtool/frontend/types'; import { convertPostMessage } from '../messageUtils'; -chrome.devtools.panels.create( - import.meta.env.DEV ? 'Dev: PixiJS DevTools' : 'PixiJS DevTools', - 'pixi-icon-active-128.png', - 'devtools/panel/panel.html', - (panel) => { - const tabId = chrome.devtools.inspectedWindow.tabId; +function onPanelCreated(panel: chrome.devtools.panels.ExtensionPanel) { + const tabId = chrome.devtools.inspectedWindow.tabId; - panel.onShown.addListener(() => { - const message = convertPostMessage(DevtoolMessage.panelShown, {}); - chrome.runtime.sendMessage({ ...message, tabId }); - }); + panel.onShown.addListener(() => { + const message = convertPostMessage(DevtoolMessage.panelShown, {}); + chrome.runtime.sendMessage({ ...message, tabId }); + }); - panel.onHidden.addListener(() => { - const message = convertPostMessage(DevtoolMessage.panelHidden, {}); - chrome.runtime.sendMessage({ ...message, tabId }); - }); - }, -); + panel.onHidden.addListener(() => { + const message = convertPostMessage(DevtoolMessage.panelHidden, {}); + chrome.runtime.sendMessage({ ...message, tabId }); + }); +} + +const title = import.meta.env.DEV ? 'Dev: PixiJS DevTools' : 'PixiJS DevTools'; + +// Firefox: browser.devtools.panels.create() returns a Promise +// Chrome: chrome.devtools.panels.create() uses a callback +if (typeof browser !== 'undefined' && browser.devtools) { + browser.devtools.panels.create(title, 'pixi-icon-active-128.png', 'devtools/panel/panel.html').then(onPanelCreated); +} else { + chrome.devtools.panels.create(title, 'pixi-icon-active-128.png', 'devtools/panel/panel.html', onPanelCreated); +} diff --git a/packages/devtool-chrome/src/devtools/panel/panel.tsx b/packages/devtool-chrome/src/devtools/panel/panel.tsx index b4b5305..e9a18e6 100644 --- a/packages/devtool-chrome/src/devtools/panel/panel.tsx +++ b/packages/devtool-chrome/src/devtools/panel/panel.tsx @@ -3,12 +3,25 @@ import type { BridgeFn } from '@devtool/frontend/lib/utils'; import { createRoot } from 'react-dom/client'; +// Firefox exposes devtools APIs on `browser`, not `chrome` +const devtools = (typeof browser !== 'undefined' && browser.devtools) ? browser.devtools : chrome.devtools; + /** * Thanks pixi-inspector for this snippet * https://github.com/bfanger/pixi-inspector */ -const bridge: BridgeFn = (code: string) => - new Promise((resolve, reject) => { +const bridge: BridgeFn = (code: string) => { + // Firefox: browser.devtools.inspectedWindow.eval() returns a Promise + if (typeof browser !== 'undefined' && browser.devtools) { + return browser.devtools.inspectedWindow.eval(code).then(([result, err]: [any, any]) => { + if (err) { + throw new Error((err.value || err.description || err.code) + `\n${code}`); + } + return result; + }); + } + // Chrome: callback-based + return new Promise((resolve, reject) => { chrome.devtools.inspectedWindow.eval(code, (result, err) => { if (err) { if (err instanceof Error) { @@ -19,10 +32,14 @@ const bridge: BridgeFn = (code: string) => resolve(result as any); }); }); +}; + +// Combine chrome.runtime with browser.devtools for cross-browser compat +const chromeProxy = { runtime: chrome.runtime, devtools } as typeof chrome; const container = document.getElementById('app-container'); const root = createRoot(container!); -root.render(); +root.render(); if (import.meta.env.DEV) { new EventSource('http://localhost:10808').addEventListener('change', () => { diff --git a/packages/devtool-chrome/vite.firefox-scripts.config.ts b/packages/devtool-chrome/vite.firefox-scripts.config.ts new file mode 100644 index 0000000..6ad9ef9 --- /dev/null +++ b/packages/devtool-chrome/vite.firefox-scripts.config.ts @@ -0,0 +1,66 @@ +import path, { resolve } from 'node:path'; +import { defineConfig } from 'vite'; + +// Build script groups individually to prevent code splitting. +// Set ENTRY env var to: 'background', 'content', 'inject', or 'devtools' +const entryGroup = process.env.ENTRY || 'background'; + +const entryMap: Record> = { + background: { + 'background/index': resolve(__dirname, 'src/background/index.ts'), + }, + content: { + 'content/index': resolve(__dirname, 'src/content/index.ts'), + }, + inject: { + 'inject/index': resolve(__dirname, 'src/inject/index.ts'), + 'inject/index2': resolve(__dirname, 'src/inject/close.ts'), + }, + devtools: { + devtools: resolve(__dirname, 'src/devtools/devtools.ts'), + }, +}; + +export default defineConfig((config) => { + const isDev = config.mode === 'development'; + const outDir = isDev ? 'firefox-dev' : 'firefox'; + const input = entryMap[entryGroup]; + + if (!input) { + throw new Error(`Unknown ENTRY group: ${entryGroup}. Use 'background', 'content', or 'inject'.`); + } + + return { + resolve: { + alias: { + '@devtool/frontend': path.resolve(process.cwd(), '../../packages/frontend/src/'), + '@devtool/backend': path.resolve(process.cwd(), '../../packages/backend/src/'), + }, + }, + root: resolve(__dirname, 'src/'), + plugins: [ + { + name: 'wrap-in-iife', + generateBundle(_outputOptions, bundle) { + Object.keys(bundle).forEach((fileName) => { + const file = bundle[fileName]; + if (fileName.slice(-3) === '.js' && 'code' in file) { + file.code = `(() => {\n${file.code}})()`; + } + }); + }, + }, + ], + build: { + rollupOptions: { + input, + output: { + format: 'es', + entryFileNames: '[name].js', + }, + }, + target: 'es2020', + outDir: resolve(__dirname, `dist/${outDir}`), + }, + }; +}); diff --git a/packages/devtool-chrome/vite.firefox.config.ts b/packages/devtool-chrome/vite.firefox.config.ts new file mode 100644 index 0000000..1dd417b --- /dev/null +++ b/packages/devtool-chrome/vite.firefox.config.ts @@ -0,0 +1,75 @@ +import react from '@vitejs/plugin-react-swc'; +import fs from 'fs'; +import path, { resolve } from 'node:path'; +import { defineConfig } from 'vite'; + +import manifest from './manifest.firefox.json'; +import pkg from './package.json'; + +export default defineConfig((config) => { + const isDev = config.mode === 'development'; + const publicDir = resolve(__dirname, './assets'); + const outDir = resolve(__dirname, isDev ? 'dist/firefox-dev' : 'dist/firefox'); + + return { + root: resolve(__dirname, 'src/'), + resolve: { + alias: { + '@devtool/frontend': path.resolve(process.cwd(), '../../packages/frontend/src/'), + '@devtool/backend': path.resolve(process.cwd(), '../../packages/backend/src/'), + }, + }, + plugins: [ + react(), + { + // Strip crossorigin attributes — unnecessary in extension contexts + // and can cause silent loading failures + name: 'strip-module-attrs', + transformIndexHtml: { + order: 'post', + handler(html: string) { + return html.replace(/ crossorigin/g, ''); + }, + }, + }, + { + name: 'firefox-manifest-and-devtools', + writeBundle() { + // Write Firefox manifest + const firefoxManifest = { + ...manifest, + version: pkg.version, + }; + fs.writeFileSync(path.resolve(outDir, 'manifest.json'), JSON.stringify(firefoxManifest, null, 2)); + + // Write static devtools page at root (Firefox requires root-level devtools_page) + // Must use classic script, not module + fs.writeFileSync( + path.resolve(outDir, 'devtools.html'), + '\n\n\n\n', + ); + }, + }, + ], + publicDir, + // Extension pages don't need crossorigin or modulepreload + base: './', + build: { + outDir, + sourcemap: isDev, + modulePreload: false, + crossOriginLoading: false, + rollupOptions: { + input: { + panel: resolve(__dirname, 'src/devtools/panel/panel.html'), + }, + output: { + // Keep assets alongside HTML for simpler relative paths + assetFileNames: 'assets/[name]-[hash].[ext]', + chunkFileNames: 'assets/[name]-[hash].js', + entryFileNames: 'assets/[name]-[hash].js', + }, + }, + }, + }; +}); From 15cbecb4d475c6f5c51120e99f99b9a47e80ab3f Mon Sep 17 00:00:00 2001 From: Jake Yallop <30874283+JakeYallop@users.noreply.github.com> Date: Thu, 5 Mar 2026 23:11:24 +0000 Subject: [PATCH 2/8] Share config --- packages/devtool-chrome/manifest.firefox.json | 2 +- .../src/devtools/panel/panel.tsx | 2 +- packages/devtool-chrome/vite.chrome.config.ts | 6 ++--- .../vite.firefox-scripts.config.ts | 23 ++++--------------- .../devtool-chrome/vite.firefox.config.ts | 6 ++--- packages/devtool-chrome/vite.inject.config.ts | 23 ++++--------------- packages/devtool-chrome/vite.shared.ts | 21 +++++++++++++++++ 7 files changed, 37 insertions(+), 46 deletions(-) create mode 100644 packages/devtool-chrome/vite.shared.ts diff --git a/packages/devtool-chrome/manifest.firefox.json b/packages/devtool-chrome/manifest.firefox.json index 2a17e1a..cc1ffa6 100644 --- a/packages/devtool-chrome/manifest.firefox.json +++ b/packages/devtool-chrome/manifest.firefox.json @@ -11,7 +11,7 @@ }, "content_scripts": [ { - "matches": ["http://*/*", "https://*/*", ""], + "matches": [""], "js": ["content/index.js"], "css": ["contentStyle.css"], "run_at": "document_start" diff --git a/packages/devtool-chrome/src/devtools/panel/panel.tsx b/packages/devtool-chrome/src/devtools/panel/panel.tsx index e9a18e6..dd23321 100644 --- a/packages/devtool-chrome/src/devtools/panel/panel.tsx +++ b/packages/devtool-chrome/src/devtools/panel/panel.tsx @@ -4,7 +4,7 @@ import type { BridgeFn } from '@devtool/frontend/lib/utils'; import { createRoot } from 'react-dom/client'; // Firefox exposes devtools APIs on `browser`, not `chrome` -const devtools = (typeof browser !== 'undefined' && browser.devtools) ? browser.devtools : chrome.devtools; +const devtools = typeof browser !== 'undefined' && browser.devtools ? browser.devtools : chrome.devtools; /** * Thanks pixi-inspector for this snippet diff --git a/packages/devtool-chrome/vite.chrome.config.ts b/packages/devtool-chrome/vite.chrome.config.ts index 173834d..69d7752 100644 --- a/packages/devtool-chrome/vite.chrome.config.ts +++ b/packages/devtool-chrome/vite.chrome.config.ts @@ -8,6 +8,7 @@ import path, { resolve } from 'node:path'; import pkg from './package.json'; import devManifest from './manifest.dev.json'; import manifest from './manifest.json'; +import { resolveAliases } from './vite.shared'; export default defineConfig((config) => { const isDev = config.mode === 'development'; @@ -23,10 +24,7 @@ export default defineConfig((config) => { return { root: resolve(__dirname, 'src/'), resolve: { - alias: { - '@devtool/frontend': path.resolve(process.cwd(), '../../packages/frontend/src/'), - '@devtool/backend': path.resolve(process.cwd(), '../../packages/backend/src/'), - }, + alias: resolveAliases, }, plugins: [ react(), diff --git a/packages/devtool-chrome/vite.firefox-scripts.config.ts b/packages/devtool-chrome/vite.firefox-scripts.config.ts index 6ad9ef9..d7a7797 100644 --- a/packages/devtool-chrome/vite.firefox-scripts.config.ts +++ b/packages/devtool-chrome/vite.firefox-scripts.config.ts @@ -1,6 +1,8 @@ -import path, { resolve } from 'node:path'; +import { resolve } from 'node:path'; import { defineConfig } from 'vite'; +import { resolveAliases, wrapInIife } from './vite.shared'; + // Build script groups individually to prevent code splitting. // Set ENTRY env var to: 'background', 'content', 'inject', or 'devtools' const entryGroup = process.env.ENTRY || 'background'; @@ -32,25 +34,10 @@ export default defineConfig((config) => { return { resolve: { - alias: { - '@devtool/frontend': path.resolve(process.cwd(), '../../packages/frontend/src/'), - '@devtool/backend': path.resolve(process.cwd(), '../../packages/backend/src/'), - }, + alias: resolveAliases, }, root: resolve(__dirname, 'src/'), - plugins: [ - { - name: 'wrap-in-iife', - generateBundle(_outputOptions, bundle) { - Object.keys(bundle).forEach((fileName) => { - const file = bundle[fileName]; - if (fileName.slice(-3) === '.js' && 'code' in file) { - file.code = `(() => {\n${file.code}})()`; - } - }); - }, - }, - ], + plugins: [wrapInIife()], build: { rollupOptions: { input, diff --git a/packages/devtool-chrome/vite.firefox.config.ts b/packages/devtool-chrome/vite.firefox.config.ts index 1dd417b..b2e8217 100644 --- a/packages/devtool-chrome/vite.firefox.config.ts +++ b/packages/devtool-chrome/vite.firefox.config.ts @@ -5,6 +5,7 @@ import { defineConfig } from 'vite'; import manifest from './manifest.firefox.json'; import pkg from './package.json'; +import { resolveAliases } from './vite.shared'; export default defineConfig((config) => { const isDev = config.mode === 'development'; @@ -14,10 +15,7 @@ export default defineConfig((config) => { return { root: resolve(__dirname, 'src/'), resolve: { - alias: { - '@devtool/frontend': path.resolve(process.cwd(), '../../packages/frontend/src/'), - '@devtool/backend': path.resolve(process.cwd(), '../../packages/backend/src/'), - }, + alias: resolveAliases, }, plugins: [ react(), diff --git a/packages/devtool-chrome/vite.inject.config.ts b/packages/devtool-chrome/vite.inject.config.ts index af5c996..535aafc 100644 --- a/packages/devtool-chrome/vite.inject.config.ts +++ b/packages/devtool-chrome/vite.inject.config.ts @@ -1,31 +1,18 @@ -import path, { resolve } from 'node:path'; +import { resolve } from 'node:path'; import { defineConfig } from 'vite'; +import { resolveAliases, wrapInIife } from './vite.shared'; + // the crx plugin doesn't seem to work with additional files, so we'll just build the injection library here export default defineConfig((config) => { const isDev = config.mode === 'development'; const outDir = isDev ? 'chrome-dev' : 'chrome'; return { resolve: { - alias: { - '@devtool/frontend': path.resolve(process.cwd(), '../../packages/frontend/src/'), - '@devtool/backend': path.resolve(process.cwd(), '../../packages/backend/src/'), - }, + alias: resolveAliases, }, root: resolve(__dirname, 'src/'), - plugins: [ - { - name: 'wrap-in-iife', - generateBundle(_outputOptions, bundle) { - Object.keys(bundle).forEach((fileName) => { - const file = bundle[fileName]; - if (fileName.slice(-3) === '.js' && 'code' in file) { - file.code = `(() => {\n${file.code}})()`; - } - }); - }, - }, - ], + plugins: [wrapInIife()], build: { lib: { entry: ['inject/index.ts', 'inject/close.ts'], diff --git a/packages/devtool-chrome/vite.shared.ts b/packages/devtool-chrome/vite.shared.ts new file mode 100644 index 0000000..df0d776 --- /dev/null +++ b/packages/devtool-chrome/vite.shared.ts @@ -0,0 +1,21 @@ +import path from 'node:path'; +import type { Plugin } from 'vite'; + +export const resolveAliases = { + '@devtool/frontend': path.resolve(process.cwd(), '../../packages/frontend/src/'), + '@devtool/backend': path.resolve(process.cwd(), '../../packages/backend/src/'), +}; + +export function wrapInIife(): Plugin { + return { + name: 'wrap-in-iife', + generateBundle(_outputOptions, bundle) { + Object.keys(bundle).forEach((fileName) => { + const file = bundle[fileName]; + if (fileName.slice(-3) === '.js' && 'code' in file) { + file.code = `(() => {\n${file.code}})()`; + } + }); + }, + }; +} From cb4a3b60abe832d9f8cc1a16c2777c5ef203183d Mon Sep 17 00:00:00 2001 From: Jake Yallop <30874283+JakeYallop@users.noreply.github.com> Date: Thu, 5 Mar 2026 23:12:36 +0000 Subject: [PATCH 3/8] Guard performance.memory --- packages/backend/src/rendering/stats.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/backend/src/rendering/stats.ts b/packages/backend/src/rendering/stats.ts index 4ed4612..f0e7140 100644 --- a/packages/backend/src/rendering/stats.ts +++ b/packages/backend/src/rendering/stats.ts @@ -18,8 +18,10 @@ export class Stats { // @ts-expect-error it does exist in chrome const memory = performance.memory; - this.memory = memory.usedJSHeapSize / 1048576; - this.maxMemory = memory.jsHeapSizeLimit / 1048576; + if (memory) { + this.memory = memory.usedJSHeapSize / 1048576; + this.maxMemory = memory.jsHeapSizeLimit / 1048576; + } } } From 506d96db04d5e1b0943fe609d9b425d3e28af962 Mon Sep 17 00:00:00 2001 From: Jake Yallop <30874283+JakeYallop@users.noreply.github.com> Date: Fri, 6 Mar 2026 18:04:06 +0000 Subject: [PATCH 4/8] Simplify config --- packages/devtool-chrome/vite.firefox.config.ts | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/packages/devtool-chrome/vite.firefox.config.ts b/packages/devtool-chrome/vite.firefox.config.ts index b2e8217..17c21ea 100644 --- a/packages/devtool-chrome/vite.firefox.config.ts +++ b/packages/devtool-chrome/vite.firefox.config.ts @@ -19,17 +19,6 @@ export default defineConfig((config) => { }, plugins: [ react(), - { - // Strip crossorigin attributes — unnecessary in extension contexts - // and can cause silent loading failures - name: 'strip-module-attrs', - transformIndexHtml: { - order: 'post', - handler(html: string) { - return html.replace(/ crossorigin/g, ''); - }, - }, - }, { name: 'firefox-manifest-and-devtools', writeBundle() { @@ -50,13 +39,10 @@ export default defineConfig((config) => { }, ], publicDir, - // Extension pages don't need crossorigin or modulepreload base: './', build: { outDir, sourcemap: isDev, - modulePreload: false, - crossOriginLoading: false, rollupOptions: { input: { panel: resolve(__dirname, 'src/devtools/panel/panel.html'), From 3b9d2e1421f99354a4e41eaf300ff5fb7f247041 Mon Sep 17 00:00:00 2001 From: Jake Yallop <30874283+JakeYallop@users.noreply.github.com> Date: Fri, 6 Mar 2026 18:11:55 +0000 Subject: [PATCH 5/8] Remove unneeded output options --- packages/devtool-chrome/vite.firefox.config.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/packages/devtool-chrome/vite.firefox.config.ts b/packages/devtool-chrome/vite.firefox.config.ts index 17c21ea..fccde28 100644 --- a/packages/devtool-chrome/vite.firefox.config.ts +++ b/packages/devtool-chrome/vite.firefox.config.ts @@ -47,12 +47,6 @@ export default defineConfig((config) => { input: { panel: resolve(__dirname, 'src/devtools/panel/panel.html'), }, - output: { - // Keep assets alongside HTML for simpler relative paths - assetFileNames: 'assets/[name]-[hash].[ext]', - chunkFileNames: 'assets/[name]-[hash].js', - entryFileNames: 'assets/[name]-[hash].js', - }, }, }, }; From 8d857e25a60bbfdab3894bc7a154509988b5eb44 Mon Sep 17 00:00:00 2001 From: Jake Yallop <30874283+JakeYallop@users.noreply.github.com> Date: Fri, 6 Mar 2026 19:20:33 +0000 Subject: [PATCH 6/8] More simplification --- .../devtool-chrome/src/background/index.ts | 1 + .../devtool-chrome/src/devtools/devtools.ts | 16 ++++--------- .../src/devtools/panel/panel.tsx | 23 +++---------------- 3 files changed, 8 insertions(+), 32 deletions(-) diff --git a/packages/devtool-chrome/src/background/index.ts b/packages/devtool-chrome/src/background/index.ts index df6a234..7a4c72b 100644 --- a/packages/devtool-chrome/src/background/index.ts +++ b/packages/devtool-chrome/src/background/index.ts @@ -84,6 +84,7 @@ chrome.runtime.onMessage.addListener((request: Message, sender: chrome.runtime.M } else { sendResponse({ status: 'error', message: 'sender.tab not defined.' }); } + return true; }); // update icon and popup when the active tab changes diff --git a/packages/devtool-chrome/src/devtools/devtools.ts b/packages/devtool-chrome/src/devtools/devtools.ts index 95be213..6b5cabe 100644 --- a/packages/devtool-chrome/src/devtools/devtools.ts +++ b/packages/devtool-chrome/src/devtools/devtools.ts @@ -1,7 +1,9 @@ import { DevtoolMessage } from '@devtool/frontend/types'; import { convertPostMessage } from '../messageUtils'; -function onPanelCreated(panel: chrome.devtools.panels.ExtensionPanel) { +const title = import.meta.env.DEV ? 'Dev: PixiJS DevTools' : 'PixiJS DevTools'; + +chrome.devtools.panels.create(title, 'pixi-icon-active-128.png', 'devtools/panel/panel.html', (panel) => { const tabId = chrome.devtools.inspectedWindow.tabId; panel.onShown.addListener(() => { @@ -13,14 +15,4 @@ function onPanelCreated(panel: chrome.devtools.panels.ExtensionPanel) { const message = convertPostMessage(DevtoolMessage.panelHidden, {}); chrome.runtime.sendMessage({ ...message, tabId }); }); -} - -const title = import.meta.env.DEV ? 'Dev: PixiJS DevTools' : 'PixiJS DevTools'; - -// Firefox: browser.devtools.panels.create() returns a Promise -// Chrome: chrome.devtools.panels.create() uses a callback -if (typeof browser !== 'undefined' && browser.devtools) { - browser.devtools.panels.create(title, 'pixi-icon-active-128.png', 'devtools/panel/panel.html').then(onPanelCreated); -} else { - chrome.devtools.panels.create(title, 'pixi-icon-active-128.png', 'devtools/panel/panel.html', onPanelCreated); -} +}); diff --git a/packages/devtool-chrome/src/devtools/panel/panel.tsx b/packages/devtool-chrome/src/devtools/panel/panel.tsx index dd23321..b4b5305 100644 --- a/packages/devtool-chrome/src/devtools/panel/panel.tsx +++ b/packages/devtool-chrome/src/devtools/panel/panel.tsx @@ -3,25 +3,12 @@ import type { BridgeFn } from '@devtool/frontend/lib/utils'; import { createRoot } from 'react-dom/client'; -// Firefox exposes devtools APIs on `browser`, not `chrome` -const devtools = typeof browser !== 'undefined' && browser.devtools ? browser.devtools : chrome.devtools; - /** * Thanks pixi-inspector for this snippet * https://github.com/bfanger/pixi-inspector */ -const bridge: BridgeFn = (code: string) => { - // Firefox: browser.devtools.inspectedWindow.eval() returns a Promise - if (typeof browser !== 'undefined' && browser.devtools) { - return browser.devtools.inspectedWindow.eval(code).then(([result, err]: [any, any]) => { - if (err) { - throw new Error((err.value || err.description || err.code) + `\n${code}`); - } - return result; - }); - } - // Chrome: callback-based - return new Promise((resolve, reject) => { +const bridge: BridgeFn = (code: string) => + new Promise((resolve, reject) => { chrome.devtools.inspectedWindow.eval(code, (result, err) => { if (err) { if (err instanceof Error) { @@ -32,14 +19,10 @@ const bridge: BridgeFn = (code: string) => { resolve(result as any); }); }); -}; - -// Combine chrome.runtime with browser.devtools for cross-browser compat -const chromeProxy = { runtime: chrome.runtime, devtools } as typeof chrome; const container = document.getElementById('app-container'); const root = createRoot(container!); -root.render(); +root.render(); if (import.meta.env.DEV) { new EventSource('http://localhost:10808').addEventListener('change', () => { From a19e6e15599f1eb8835dceb7d6415d0c4e1577ed Mon Sep 17 00:00:00 2001 From: Jake Yallop <30874283+JakeYallop@users.noreply.github.com> Date: Fri, 6 Mar 2026 20:16:43 +0000 Subject: [PATCH 7/8] Revert unnecessary changes --- .../devtool-chrome/src/devtools/devtools.ts | 29 ++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/packages/devtool-chrome/src/devtools/devtools.ts b/packages/devtool-chrome/src/devtools/devtools.ts index 6b5cabe..dc90623 100644 --- a/packages/devtool-chrome/src/devtools/devtools.ts +++ b/packages/devtool-chrome/src/devtools/devtools.ts @@ -1,18 +1,21 @@ import { DevtoolMessage } from '@devtool/frontend/types'; import { convertPostMessage } from '../messageUtils'; -const title = import.meta.env.DEV ? 'Dev: PixiJS DevTools' : 'PixiJS DevTools'; +chrome.devtools.panels.create( + import.meta.env.DEV ? 'Dev: PixiJS DevTools' : 'PixiJS DevTools', + 'pixi-icon-active-128.png', + 'devtools/panel/panel.html', + (panel) => { + const tabId = chrome.devtools.inspectedWindow.tabId; -chrome.devtools.panels.create(title, 'pixi-icon-active-128.png', 'devtools/panel/panel.html', (panel) => { - const tabId = chrome.devtools.inspectedWindow.tabId; + panel.onShown.addListener(() => { + const message = convertPostMessage(DevtoolMessage.panelShown, {}); + chrome.runtime.sendMessage({ ...message, tabId }); + }); - panel.onShown.addListener(() => { - const message = convertPostMessage(DevtoolMessage.panelShown, {}); - chrome.runtime.sendMessage({ ...message, tabId }); - }); - - panel.onHidden.addListener(() => { - const message = convertPostMessage(DevtoolMessage.panelHidden, {}); - chrome.runtime.sendMessage({ ...message, tabId }); - }); -}); + panel.onHidden.addListener(() => { + const message = convertPostMessage(DevtoolMessage.panelHidden, {}); + chrome.runtime.sendMessage({ ...message, tabId }); + }); + }, +); From 2b9967282fa082dfd846c04a07c0141b8ea04ea7 Mon Sep 17 00:00:00 2001 From: Jake Yallop <30874283+JakeYallop@users.noreply.github.com> Date: Fri, 6 Mar 2026 20:19:53 +0000 Subject: [PATCH 8/8] Remove incorrect comment --- packages/devtool-chrome/vite.firefox.config.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/devtool-chrome/vite.firefox.config.ts b/packages/devtool-chrome/vite.firefox.config.ts index fccde28..f5bb5bc 100644 --- a/packages/devtool-chrome/vite.firefox.config.ts +++ b/packages/devtool-chrome/vite.firefox.config.ts @@ -29,8 +29,7 @@ export default defineConfig((config) => { }; fs.writeFileSync(path.resolve(outDir, 'manifest.json'), JSON.stringify(firefoxManifest, null, 2)); - // Write static devtools page at root (Firefox requires root-level devtools_page) - // Must use classic script, not module + // Write devtools page with a classic script tag (not type="module") fs.writeFileSync( path.resolve(outDir, 'devtools.html'), '\n\n\n\n',