feat(extension): add browser extension package with Chrome MV3 and Firefox MV2#97
Open
Aejkatappaja wants to merge 11 commits intoQwikDev:mainfrom
Open
feat(extension): add browser extension package with Chrome MV3 and Firefox MV2#97Aejkatappaja wants to merge 11 commits intoQwikDev:mainfrom
Aejkatappaja wants to merge 11 commits intoQwikDev:mainfrom
Conversation
…refox MV2 New packages/browser-extension package using WXT framework. Includes content script with Qwik detection, SPA navigation tracking, element picker with main-world click interception, background relay, and DevTools panel entry points. Supports Chrome MV3 and Firefox MV2.
Add hookRuntime.ts that installs window.__QWIK_DEVTOOLS_HOOK__ with signal inspection, component snapshots, state editing, and render event tracking. Add vnodeBridge.ts that traverses the VNode tree using Qwik internal APIs and pushes real-time updates via MutationObserver. Bridge render events from perfRuntime via postMessage for extension consumption.
Add PageDataSource interface abstracting page data access for both overlay (direct window) and extension (inspectedWindow.eval) modes. Add RemotePageDataSource with VNode bridge injection via dynamic import, package detection from container attributes, Vite plugin detection, and render event subscription. Add QwikDevtoolsExtension component and CSR entry point for the extension panel.
Full component tree panel reusing Tree, RenderTreeTabs, and HookFiltersCard from the overlay. Features: real-time updates via subscribeTreeUpdates, SPA navigation handling with treeVersion key, name-based fingerprint to skip redundant re-renders, element picker integration, hover highlight via hook.highlightNode, expand/collapse toggle, inline signal editing, and data-node-id attributes on tree rows for reliable hover targeting.
…ents Adapt Overview for extension mode: hide pages/assets cards, show packages from container attributes, Vite plugin detection banner. Add live render events to Performance tab with SSR+CSR seed data. Gray out Vite-only tabs with placeholder messages. Add shared IconButton, InfoBanner components and IconTarget, IconExpandShrink, IconMonitor, IconInfoCircle icons. Fix HookFiltersCard grid overflow.
Add devtools-hook.js injected by content script for hook installation without the Vite plugin. Add vnode-bridge.js as ES module fallback. VNode bridge is also installed via inspectedWindow.eval with dynamic import that reuses the page's cached Qwik core module URL from performance entries. Extension now works on any Qwik dev site.
31 tests for hookTreeHelpers (toTreeNodes, treeIdFingerprint, findNodeById, findNodeByDomAttr, getElementType, valueToTree, buildDetailTree) and 8 tests for isExtensionMessage type guard.
🦋 Changeset detectedLatest commit: 3ce0da9 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
There was a problem hiding this comment.
Pull request overview
Adds a new browser extension package (Chrome MV3 / Firefox MV2) that embeds the Qwik DevTools UI inside the browser’s DevTools panel, while extending the existing plugin + UI to support extension/remote data access and real-time component/render instrumentation.
Changes:
- Introduces
@devtools/browser-extension(WXT-based) with DevTools panel, background/content scripts, and page eval/data plumbing. - Refactors UI data loading to use injectable
DataProvider+PageDataSource, and adds an extension-only UI entry/layout. - Adds a runtime devtools hook + VNode bridge (virtual module + SSR injection) and streams live render events via
postMessage.
Reviewed changes
Copilot reviewed 60 out of 70 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/ui/vite.config.mts | Builds an additional extension entry bundle. |
| packages/ui/src/features/RenderTree/components/HookFiltersCard.tsx | Layout tweaks for hook filter grid/labels. |
| packages/ui/src/features/Preloads/Preloads.tsx | Reads/clears/subscribes via PageDataSource abstraction. |
| packages/ui/src/features/Performance/Performance.tsx | Adds live render events + remote perf reading support. |
| packages/ui/src/features/Overview/Overview.tsx | Adds extension-mode UI adjustments + plugin-detected banner. |
| packages/ui/src/features/HookTree/hookTreeHelpers.ts | Shared helpers for VNode/state tree manipulation. |
| packages/ui/src/features/HookTree/hookTreeHelpers.test.ts | Tests for HookTree helper utilities. |
| packages/ui/src/features/HookTree/HookTree.tsx | New extension-mode component tree + state inspector UI. |
| packages/ui/src/entry.extension.tsx | New CSR-only extension entry point mounting QwikDevtoolsExtension. |
| packages/ui/src/devtools/tabs.tsx | Marks tabs as viteOnly for extension disabling/placeholder. |
| packages/ui/src/devtools/state.ts | Adds vitePluginDetected flag to state. |
| packages/ui/src/devtools/rpc.ts | Introduces DataProvider indirection (Vite RPC vs extension). |
| packages/ui/src/devtools/QwikDevtoolsExtension.tsx | Extension-only devtools layout (sidebar + content, no overlay chrome). |
| packages/ui/src/devtools/QwikDevtools.tsx | Skips preload runtime setup when running under extension provider. |
| packages/ui/src/devtools/page-data-source.ts | Adds PageDataSource abstraction + default in-page implementation. |
| packages/ui/src/devtools/DevtoolsSidebar.tsx | Disables viteOnly tabs in extension mode. |
| packages/ui/src/devtools/DevtoolsContent.tsx | Adds extension-mode placeholders + HookTree swap-in. |
| packages/ui/src/devtools/data-provider.ts | Defines DataProvider interface + window injection point. |
| packages/ui/src/components/Tree/Tree.tsx | Adds data-node-* attributes for hover/picker targeting. |
| packages/ui/src/components/Tree/filterVnode.ts | Lazily caches container lookup for VNode traversal. |
| packages/ui/src/components/Tab/Tab.tsx | Adds disabled state + title handling for overlay-only tabs. |
| packages/ui/src/components/InfoBanner/InfoBanner.tsx | New reusable informational banner component. |
| packages/ui/src/components/Icons/Icons.tsx | Adds icons used by extension UI/placeholder/toolbars. |
| packages/ui/src/components/IconButton/IconButton.tsx | New small toolbar button component. |
| packages/ui/package.json | Exports ./entry.extension + bumps Qwik peer/dev deps. |
| packages/plugin/src/virtualmodules/vnodeBridge.ts | New VNode bridge virtual module for serializable tree/DOM helpers. |
| packages/plugin/src/virtualmodules/virtualModules.ts | Registers VNode bridge virtual module. |
| packages/plugin/src/virtualmodules/useCollectHooks.ts | Formatting/termination fixes. |
| packages/plugin/src/virtualmodules/perfRuntime.ts | Posts RENDER_EVENT messages + notifies devtools hook. |
| packages/plugin/src/virtualmodules/hookRuntime.ts | New runtime hook string installed via SSR injection / perf runtime. |
| packages/plugin/src/plugin/statistics/ssrPerfMiddleware.ts | Injects hook runtime + bridge script tags into HTML. |
| packages/plugin/package.json | Bumps Qwik dev dependency. |
| packages/playgrounds/package.json | Bumps Qwik core/router + eslint-plugin-qwik versions. |
| packages/kit/src/index.ts | Re-exports new hook types module. |
| packages/kit/src/hook-types.ts | Adds typed definition of __QWIK_DEVTOOLS_HOOK__ API. |
| packages/kit/src/globals.ts | Adds __QWIK_DEVTOOLS_HOOK__ global typing. |
| packages/kit/package.json | Bumps eslint-plugin-qwik version. |
| packages/devtools/package.json | Bumps peer ranges for Qwik core/router. |
| packages/browser-extension/wxt.config.ts | New WXT config + manifest for DevTools extension. |
| packages/browser-extension/tsconfig.json | New TS config for extension package. |
| packages/browser-extension/src/styles/panel.css | Imports UI styles + adapts “glass” styling for panel context. |
| packages/browser-extension/src/lib/types.ts | Defines extension messaging types + type guard. |
| packages/browser-extension/src/lib/types.test.ts | Tests for isExtensionMessage type guard. |
| packages/browser-extension/src/lib/extension-data-provider.ts | Implements extension DataProvider + remote PageDataSource via eval. |
| packages/browser-extension/src/lib/constants.ts | Shared constants + utilities for extension DOM parsing/detection. |
| packages/browser-extension/src/entrypoints/panel.html | DevTools panel bootstrap: connects port + mounts UI extension entry. |
| packages/browser-extension/src/entrypoints/devtools.html | Creates the “Qwik” DevTools panel when Qwik is detected. |
| packages/browser-extension/src/entrypoints/content.ts | Content script: detection, SPA nav, picker overlay, message forwarding. |
| packages/browser-extension/src/entrypoints/background.ts | Background relay between DevTools panel and content scripts. |
| packages/browser-extension/public/vnode-bridge.js | Public ES module VNode bridge for injection scenarios. |
| packages/browser-extension/public/theme-init.js | Initializes theme attribute from stored preference. |
| packages/browser-extension/public/qwikloader.js | Bundled Qwik loader script for panel runtime. |
| packages/browser-extension/public/nav-hook.js | Main-world SPA navigation hook (pushState/replaceState). |
| packages/browser-extension/public/inspect-hook.js | Main-world element picker (pre-qwikloader click interception). |
| packages/browser-extension/public/icon-48.png | Extension icon asset. |
| packages/browser-extension/public/icon-32.png | Extension icon asset. |
| packages/browser-extension/public/icon-16.png | Extension icon asset. |
| packages/browser-extension/public/icon-128.png | Extension icon asset. |
| packages/browser-extension/package.json | New extension package definition + scripts/deps. |
| packages/browser-extension/.wxt/wxt.d.ts | WXT generated types. |
| packages/browser-extension/.wxt/types/paths.d.ts | WXT generated public-path typings. |
| packages/browser-extension/.wxt/types/imports-module.d.ts | WXT generated #imports typings. |
| packages/browser-extension/.wxt/types/i18n.d.ts | WXT generated i18n typings. |
| packages/browser-extension/.wxt/types/globals.d.ts | WXT generated env typings. |
| packages/browser-extension/.wxt/tsconfig.json | WXT generated TS config. |
| packages/browser-extension/.gitignore | Ignores extension build output and node_modules. |
| package.json | Adds root scripts for extension dev/build workflows. |
| .changeset/browser-extension.md | Announces new browser extension feature release. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
JerryWu1234
reviewed
Apr 13, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Browser extension (Chrome/Firefox) that brings Qwik DevTools to the browser's DevTools panel, following the same architecture as Vue DevTools: extension for runtime features, Vite overlay for server features.
The extension shares the UI components with the existing in-app overlay and works both with and without the
qwikDevtools()Vite plugin.Quick Demo
CleanShot.2026-04-10.at.3.28.18.mp4
What's implemented
Component Tree (real-time)
@qwik.dev/core/internalAPIsq:idinstead of sequential countersPAGE_CHANGEDTree,RenderTreeTabs,HookFiltersCardcomponentsState inspection
Element picker
inspect-hook.jsintercepts clicks before qwikloaderdata-qwik-inspector+ VNode mapHover highlight
hook.highlightNode(nodeId)which walks VNode to first DOM elementdata-node-idattributes on tree rows for reliable targetingLive render events
perfRuntimevia postMessageRENDER_EVENTto panel__QWIK_PERF__snapshot (SSR + CSR)Performance & Preloads
__QWIK_PERF__and__QWIK_PRELOADS__viainspectedWindow.eval()Overview
q:containerHTML attributes (q:version)Standalone mode (without Vite plugin)
devtools-hook.js(plain script, no imports)inspectedWindow.eval()with dynamicimport()performance.getEntriesByType('resource')to reuse cached module (no duplicate Qwik instance)qwikDevtools()in vite.configVite-only tabs
Shared components
IconButton- toolbar button with icon slot and active stateInfoBanner- contextual info message with iconIconTarget,IconExpandShrink,IconMonitor,IconInfoCircleadded to IconsTests
Architecture
What works per mode
What's NOT in this PR (follow-up work)
Production support
The VNode bridge uses
import('@qwik.dev/core/internal')which only resolves in dev mode (Vite serves bare imports). In production, the Qwik core is bundled and minified, internal APIs aren't accessible. This requires either:<script type="qwik/vnode">directlySignals without Vite plugin
QWIK_DEVTOOLS_GLOBAL_STATEis populated by the plugin's component instrumentation. Without it, signals live in closures inside components and can't be accessed externally. Requires Qwik core to register signals on the hook whenuseSignal()is called (same as Vue's approach).Both items above are best addressed after the devtools repo moves into the Qwik monorepo, where internal APIs can be tightly coupled (as discussed in the RFC thread).
Testing
Load the Chrome extension from
packages/browser-extension/.output/chrome-mv3/viachrome://extensions(developer mode).Load the Firefox extension from
packages/browser-extension/.output/firefox-mv2/viaabout:debugging.