| title | Chrome extension |
|---|---|
| description | Open the devtools as a panel inside Chrome DevTools, see the selected component in the Elements panel, and check a tab from the toolbar. |
The Chrome extension adds a panel named Pangular Inspector to Chrome DevTools. The panel loads the devtools UI and connects it to the dev server of the page you are inspecting. It also adds an Angular component pane to the Elements panel, and a toolbar button that tells you what it sees on the current tab.
The page still needs the devtools mounted on its server and the overlay loaded. The extension is one more way to open the devtools. It does not replace the setup. Start with Angular CLI and Express or Vite and Analog. The manifest setsminimum_chrome_version to 111.
The extension lives in the extension/ folder. You build it from source.
The repository itself needs Node.js 24 or later and pnpm 10 or later.
Run pnpm install in the root of the repository.
Run pnpm extension:build. It builds the devtools UI and copies it into extension/ui.
Go to chrome://extensions and turn on Developer mode.
Click Load unpacked and select the extension/ directory.
The Pangular Inspector panel appears next to the built-in panels.
git clone https://github.com/pangular-inspector/devtools.git
cd devtools
pnpm install
pnpm extension:buildBuild the extension covers the build and the store package in detail.
When DevTools opens, the extension checks the inspected page for Angular: an ng-version attribute or a window.ng global. It retries for about five seconds for apps that bootstrap late, and checks again after each navigation. The extension creates the panel only on Angular pages. It runs nothing in pages while DevTools is closed.
When the top page has no Angular, the extension looks in its frames, such as a Storybook preview, a micro-frontend shell or an embedded preview. It checks up to 20 http and https frames in the order DevTools lists them, and uses the first one that runs Angular. The panel then reads that frame for everything: the page id, the Elements selection, Reveal in Elements and Open source. It looks for the devtools server on the frame's origin, not the top page's.
The top page always wins when it runs Angular itself. The search gives up after 3 seconds and uses the top page. Chrome picks a frame by its URL, so frames that share a URL count as one: the extension checks and reads the first of them only. If your app runs in one of several frames with the same URL, give that frame a URL of its own, for example with a query parameter. The Angular component pane in the Elements panel reads the top page only, since Chrome evaluates its expression there.
The panel looks for the devtools server on the origin of the inspected page. It tries these paths in order:
| Path | Mounted by |
|---|---|
/__pangular/ |
A panel mounted with initDevframe() |
/__devframes/pangular/ |
The Express hub or the Vite plugin |
/__devframe/ |
A bare devframe mount |
/ |
A devframe served at the root |
Under each path it asks for __devframe/__connection.json and __connection.json. It asks the first URL, /__pangular/__devframe/__connection.json, alone for up to 150 milliseconds. If that answers, it sends nothing else. Otherwise it sends the other requests at once, connects the UI to the first path, in the order above, that answers with a connection file, and cancels the requests still running. Each request times out after 1.5 seconds.
If no path answers, the panel says "No devtools server answered" and lists every URL it tried, each with the HTTP status it got or "no answer". It links to the setup instructions.
If any URL got 401 or 403, the panel says the server refused the request instead, and shows the start of the response text. The Vite plugin answers 403 to requests that do not come from your machine, for example when you open the app by its LAN IP. The panel then links to Answers only your machine.
Both messages have a Try again button. Click it after you start or fix the server, and the panel looks for the server again without a page reload. The panel also looks again by itself every 5 seconds, so it connects once the server starts.
The panel only connects to pages served over http or https. On other pages it says so, with a Try again button.
The panel reads the origin of the inspected page through DevTools. Right after a navigation the page can have no execution context yet, so the panel reads it again every 300 milliseconds, up to 10 times. If every read fails, it says it couldn't read the page and offers Try again.
A page that waits on a dialog, such as the overlay's prompt for the one-time code, doesn't answer at all. After 2 seconds the panel says the page is not answering and points at that prompt. It keeps checking every 2 seconds and connects once the prompt is closed.
The extension can reach loopback hosts from the start. For any other host, such as a LAN IP or a tunnel, the panel shows an Allow access button instead of looking for the server. Click it and confirm the Chrome prompt. The panel then looks for the server again. See Host access for what the button grants.
The overlay gives each page an id and exposes it on the page as window.__pangularPageId. The panel passes the id of the page it inspects to the UI. If several tabs run the same app, the panel shows the tab you inspect, not the one that reported last.
The overlay claims the id after it connects to the server, so it can come later than the server answers. The panel waits up to five seconds for the id. If no id appears in that time, it uses the id the tab kept from an earlier load, if there is one. Without any id, it loads the UI and shows the page that reported last.
When the inspected page navigates, the panel shows "Detecting Angular app…", looks for the server again and reconnects. It reloads the devtools UI, so the tab, filter and selection start fresh. Keeping the UI across navigations is not planned: the page id changes on every load, and a kept UI would show the page before the reload.
The panel always looks for the server at the paths above. The hub's path is fixed, so there is no setting for a custom base path and no list of paths to remember per origin.
The Vite plugin and the Express hub ask for a one-time code by default. Inside the extension, the panel asks for the code itself, with the same form the panel shows when you open it at /__devframes/pangular/:
The server trades the code for a token. The extension keeps one token per server origin, in its own storage, so the panel connects without the code next time, and a token is never sent to a server that did not issue it. The code that the app page asks for in its own prompt doesn't count for the panel: the page and the panel are separate clients, and each gets its own token.
The panel follows the DevTools theme. If you switch DevTools between light and dark (Settings > Preferences > Theme), the panel switches with it.
While the Components tab is open, select an element in the Chrome Elements panel. The Components tab selects the component that hosts that element (the element itself, or the nearest ancestor that is a component host). It expands the parent rows, clears the filter if it hides the row, and scrolls the row into view. On other tabs, the Elements selection does nothing. It also does nothing when inspectors.components is false in the configuration.
This needs the overlay on the page, since the overlay answers which component hosts the element.
The extension adds an Angular component pane to the Elements panel, next to Styles. Select an element and the pane shows the component that hosts it, whatever tab the Pangular Inspector panel has open:
| Field | Shows |
|---|---|
component |
The class name. |
host |
The host element's tag. |
selected |
The selected element's tag, when you selected an element inside the host. |
file |
The component file and the line of its class, from Angular's development debug info. |
inputs |
Each input and its value, input signals included. |
signals |
The other signals and resources that are fields, with their current value. |
properties |
The other fields, except injected services and methods. |
outputs |
The output names. |
directives |
The other directives on the host, when there are any. |
The values are the ones the panel gets, with secrets masked by the same redaction rules. The pane needs the overlay on the page and inspectors.components on. Without the overlay it says so. An element outside any component shows "No Angular component owns the selected element."
Click the Pangular Inspector button in the Chrome toolbar (pin it from the puzzle piece menu first) to see what the extension finds on the current tab:
| Row | Shows |
|---|---|
| Angular | The version from the ng-version attribute, or Not found. |
| Build | Development when Angular's debug API is there, Production when it isn't. The devtools need development. |
| Devtools server | The URL where a server answered, the status it refused with, or No answer. |
| Overlay | Connected once the overlay on the page claimed its page id, or Not loaded. |
Under the rows it says what to do next, such as running a development build or mounting the devtools in your dev server. It only looks at the top page; for an app inside a frame, open DevTools.
Site access opens the site access page. The button reads the tab only when you click it, through the activeTab permission, and runs nothing in pages you don't click it on.
When you select a component in the Components tab, its header has two buttons:
| Button | What it does |
|---|---|
| Reveal in Elements | Selects the host element in the Elements panel. |
| Open source | Opens the component file in the Sources panel at the class line. If the source maps don't list the file, it opens the compiled class instead. |
The file and line come from the debug info Angular adds in development builds. If Open source finds neither the file nor the class, the panel shows the file and line so you can open it in your editor.
Both buttons work only for components on the tab DevTools inspects. If the Components tab shows another page, they say so.
The manifest asks for two permissions, neither of which shows a warning when you install the extension:
| Permission | Why |
|---|---|
activeTab |
Lets the toolbar button read the tab you clicked it on, once. |
scripting |
Runs that one read in the tab: the Angular version, the build and whether the overlay runs. |
Its host permissions cover loopback hosts only, over HTTP and HTTPS:
| Host | Covers |
|---|---|
*.localhost |
localhost and every subdomain, such as app.localhost |
127.0.0.1 |
The IPv4 loopback address |
[::1] |
The IPv6 loopback address |
Other hosts are optional host permissions. Allow access asks Chrome for the host of the inspected page only, on the scheme of that page (http or https) and on any port. The extension never asks for all hosts at once.
The site access page lists the hosts you allowed with Allow access, each with a Remove button. Open it from Site access in the toolbar button, or from Details > Extension options in chrome://extensions. The loopback hosts are not listed: the manifest grants them on install and they can't be removed. After you remove a host, the panel asks again the next time you inspect a page on it.
Granting the extension a host doesn't change what the devtools server accepts. The server still applies its own checks. The Vite plugin, for example, only answers requests from a loopback address. See Access and redaction.
The panel sends requests from its own origin, chrome-extension://<id>. Any installed extension can send requests to a loopback host, so the Vite plugin and the Express hub trust only the extension IDs they know.
The key in extension/manifest.json fixes the ID of this extension to dcogniffeelebaolkkfbopmjcblhblfk, wherever you load it from. The Vite plugin and the Express hub trust chrome-extension://dcogniffeelebaolkkfbopmjcblhblfk by default, so the panel works with no allowedOrigins setting. They refuse every other extension origin.
If you build the extension with another key, or without one, Chrome gives it another ID. Copy that ID from the extension card in chrome://extensions and add its origin to allowedOrigins:
// vite.config.ts
pangular({allowedOrigins: ['chrome-extension://<id>']});// src/server.ts
const devtools = initPangularHub({
allowedOrigins: ['chrome-extension://<id>'],
});In the Vite plugin, an extension entry does not turn the one-time code on. If the server refuses a build with another ID, the panel names its own origin in the message. See Access and redaction.
A trusted extension origin passes the origin check only. With the one-time code on, the panel still enters the code once per server. See One-time code.
The extension has no content scripts and no background worker, so it runs nothing in the pages you browse. It reads a page only through DevTools, in the tab DevTools inspects and only while DevTools is open, or once in the tab you click the toolbar button on. Chrome gives DevTools extensions that access without a host permission.
The panel needs a devtools server: it reads the page through the overlay and the server, not on its own. A mode that reads the page without a server is not planned for 1.x.
The troubleshooting page lists the problems every setup shares, by symptom.
The page did not look like an Angular app within about five seconds of opening DevTools. Check that it renders anng-version attribute or exposes window.ng, which development builds do. If the page shows a prompt for the one-time code, the extension cannot check the page until you answer or close the prompt. Then close and reopen DevTools, or reload the page.
The page is not on a loopback host. Click Allow access to let the extension reach that host. Chrome asks you to confirm.
The server has the one-time code on, which is the default for the Vite plugin and the Express hub. Enter the code from the terminal of the dev server. See One-time code.
The page waits on a dialog, usually the overlay's prompt for the one-time code. Enter the code in the page or close the prompt. The panel goes on by itself.
None of them served a connection file. The status next to each URL shows what the server answered. Check that the server of the page mounts the devtools and that the server accepts the request, then click Try again or wait a few seconds. See Access and redaction.
The server answered 401 or 403. If you built the extension with another ID, the server refuses its origin until you add it to allowedOrigins. On 403, the message names the origin to add. See Server origin. The Vite plugin also refuses requests that do not come from your machine. Open the app on localhost, or see Answers only your machine.
The overlay on the inspected page did not report its page id within five seconds, so the panel loaded without it. Check that the overlay starts on that page, then close and reopen DevTools.
Open the Components tab first, and check that the overlay is loaded. Elements outside any component select nothing.
The source maps of the page don't list the component file, so the panel opened the class from the bundle. Turn on source maps for your development build.
The extension looks in the frames when the top page has no Angular, and the panel then reads that frame and looks for its server on the frame's origin. See Apps inside a frame. The Angular component pane reads the top page only.
Open the site access page and click Remove next to it.
No. The overlay still adds the button to the page. Use the button or the panel, whichever you prefer.
The build, the zip and the store package.
Which origins the devtools trust.