Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ The package is published on npm as [`@pangular-inspector/devtools`](https://www.
npm install @pangular-inspector/devtools devframe
```

Then follow the [installation guide](./apps/docs/src/content/getting-started/installation.md) for your setup: Angular CLI with Express, Vite and Analog, the standalone CLI, or [Angular Native](./apps/docs/src/content/getting-started/angular-native.md). For a coding agent, run `npx @pangular-inspector/devtools mcp`.
Then follow the [installation guide](./apps/docs/src/content/getting-started/installation.md) for your setup: Angular CLI with Express, Vite and Analog, the standalone CLI, or [Angular Native](./apps/docs/src/content/guides/angular-native.md). For a coding agent, run `npx @pangular-inspector/devtools mcp`.

## Documentation

Expand Down
2 changes: 1 addition & 1 deletion app/src/__tests__/angular-native-view.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ describe('Angular Native view', () => {
expect(text).toContain('No Angular Native app is connected');
const link = host(fixture).querySelector<HTMLAnchorElement>('a.cta');
expect(link?.textContent).toContain('Set up Angular Native');
expect(link?.href).toContain('getting-started/angular-native/');
expect(link?.href).toContain('guides/angular-native/');
expect(tabNames(fixture)).toEqual([]);
});

Expand Down
2 changes: 1 addition & 1 deletion app/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ const NO_ANGULAR_NATIVE: ComingSoonInfo = {
],
link: {
label: 'Set up Angular Native',
href: 'https://pangular-inspector.dev/getting-started/angular-native/',
href: 'https://pangular-inspector.dev/guides/angular-native/',
},
};

Expand Down
80 changes: 6 additions & 74 deletions apps/docs/src/content/getting-started/angular-native.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ description: Send live components, signals, injectors and NgRx stores from an An

[Angular Native](https://ng-native.com) renders *Angular onto React Native's Fabric renderer, so the app has no DOM for the [browser overlay](./overlay.md) to walk. The `@pangular-inspector/devtools/overlay-angular-native` entry point walks Angular Native's own node tree instead and sends the same live data to a devtools server that runs on your machine.

This page describes what the overlay shows and how it works. To add it to an app, follow [Set up Angular Native](../guides/angular-native.md).

## What it shows

| Tab | On Angular Native |
Expand Down Expand Up @@ -53,80 +55,9 @@ The overlay marks its component reports with `platform: 'angular-native'`, and t
- An app started with `mount()` from `@ng-native/platform`. The overlay needs the root node it returns.
- A `WebSocket` global and a standards-compliant `URL`. React Native provides `WebSocket`. Expo provides `URL`. React Native's own `URL` is read-only, and the client sets the `protocol` of the socket URL, so a bare React Native app needs a polyfill such as `react-native-url-polyfill`.

## Set up the app

The [Angular Native demo](../contributing/demo-apps.md#angular-native-demo) (`examples/angular-native`) is this setup, with a store, a service and signals to inspect. Its README runs it on iOS and Android.

<ngmd-workflow>
<ngmd-step title="Install the package">
Add <code>&#64;pangular-inspector/devtools</code> and <code>devframe</code> to the app, as on the <a href="./installation.md">installation</a> page.
</ngmd-step>
<ngmd-step title="Start the overlay after mount()">
Pass the root node of the mounted app. The check keeps the overlay out of release builds.
</ngmd-step>
<ngmd-step title="Start the devtools server">
Run the CLI from the root of the app, so the source scans read its <code>src</code> folder.
</ngmd-step>
<ngmd-step title="Open the panel">
Open the <strong>Angular Native apps</strong> URL the server prints, <code>http://localhost:9999/?view=angular-native</code>, on your machine. The live tabs fill once the app connects.
</ngmd-step>
</ngmd-workflow>

### Start the overlay

```ts
// src/main.ts
import {AppRegistry, Image, Platform, processColor} from 'react-native';
import {mount} from '@ng-native/platform';
import {getFabricUIManager, registerPlatformComponents} from '@ng-native/fabric';
import {initAngularNativeOverlay} from '@pangular-inspector/devtools/overlay-angular-native';
import {App} from './app/app.ts';

registerPlatformComponents(Platform.OS);

AppRegistry.registerRunnable('main', ({rootTag}) => {
const app = mount(Number(rootTag), App, getFabricUIManager(), {
processColor,
resolveAssetSource: (value) => Image.resolveAssetSource(value as never),
});
if (__DEV__) initAngularNativeOverlay({root: app.engine.root});
});
```

`initAngularNativeOverlay()` returns a function that stops the overlay and tells the server to forget the app.

### Start the server

```bash
npx @pangular-inspector/devtools dev --no-auth
```

When it is ready, the server prints the Angular Native view on its own line:

```text
pangular v0.0.7
Panel: http://localhost:9999/
Angular Native apps: http://localhost:9999/?view=angular-native
MCP: http://localhost:9999/__mcp
```

The app can't answer the one-time code the server asks for, so the server needs `--no-auth`. Without it, the overlay logs a warning that names the flag and keeps retrying.

<ngmd-callout type="warning" title="Keep the server on a trusted network">
<code>--no-auth</code> lets anything that reaches the server use its RPC and MCP endpoints. Keep the default <code>localhost</code> bind when you can, and see <a href="../security.md">Access and redaction</a>.
</ngmd-callout>

## Reach the server from a device

The overlay connects to `http://localhost:9999/` unless you pass `baseURL`.

| Where the app runs | What to do |
| -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| iOS simulator | Nothing. The simulator shares your machine's `localhost`. |
| Android emulator or USB Android device | Run `adb reverse tcp:9999 tcp:9999`, the same forwarding Metro uses for its own port. |
| Physical device over Wi-Fi | Start the server with `--host` set to your machine's LAN address, and pass that address as `baseURL`, such as `http://192.168.1.20:9999/`. |
## Set up

A device that reaches the server over plain HTTP may need a cleartext exception for that address in `Info.plist` or the Android network security config.
[Set up Angular Native](../guides/angular-native.md) walks through it: install the package, start the overlay after `mount()`, run the server with `--no-auth` and open the panel. It also covers how the app reaches the server from a simulator, an emulator or a device, and how to run the [Angular Native demo](../contributing/demo-apps.md#angular-native-demo).

## Options

Expand All @@ -149,7 +80,7 @@ The server's [configuration](./configuration.md) applies to the app: inspectors

<ngmd-accordion>
<ngmd-accordion-item title="The live tabs stay empty" open>
Check the Metro log for a <code>[pangular]</code> line. A warning about <code>--no-auth</code> means the server asked for a code. A warning about reaching the server means the address is wrong for where the app runs. A release build has no <code>ng</code> global, so nothing is collected there.
Check the Metro log for a <code>[pangular]</code> line. A warning about <code>--no-auth</code> means the server asked for a code. A warning about reaching the server means the address is wrong for where the app runs. See <a href="../guides/angular-native.md#where-the-overlay-connects">where the overlay connects</a>. A release build has no <code>ng</code> global, so nothing is collected there.
</ngmd-accordion-item>
<ngmd-accordion-item title="The Providers list of an injector is empty">
Angular records providers only when a <code>window</code> global exists as <code>mount()</code> creates the platform. If the list stays empty, check that <code>window</code> is defined before <code>mount()</code> runs.
Expand All @@ -159,6 +90,7 @@ The server's [configuration](./configuration.md) applies to the app: inspectors
## Where to next

<ngmd-pill-row>
<ngmd-pill href="/guides/angular-native" title="Set up Angular Native"></ngmd-pill>
<ngmd-pill href="/getting-started/cli" title="Standalone CLI"></ngmd-pill>
<ngmd-pill href="/getting-started/configuration" title="Configuration"></ngmd-pill>
<ngmd-pill href="/inspectors/components" title="Components"></ngmd-pill>
Expand Down
1 change: 1 addition & 0 deletions apps/docs/src/content/getting-started/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ MCP agent support (`@devframes/agentic`) is included. You don't install it separ
| `@pangular-inspector/devtools/vite` | The Vite plugin for Analog apps. |
| `@pangular-inspector/devtools/overlay` | The browser script that collects live data from your page. |
| `@pangular-inspector/devtools/overlay-angular-native` | The overlay for an Angular Native app. See [Angular Native](./angular-native.md). |
| `@pangular-inspector/devtools/overlay-nativescript` | The overlay for a NativeScript Angular app. See [NativeScript](./nativescript.md). |
| `@pangular-inspector/devtools/popup` | The floating button and panel on your page. |
| `@pangular-inspector/devtools/http` | The HTTP interceptor and hydration hooks for the SSR & HTTP tab. |
| `@pangular-inspector/devtools/config` | The `PangularConfig` type and its defaults. See [Configuration](./configuration.md). |
Expand Down
114 changes: 114 additions & 0 deletions apps/docs/src/content/getting-started/nativescript.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
---
title: NativeScript
description: Send live components, signals, injectors and NgRx stores from a NativeScript Angular app on a simulator or device to the devtools on your machine.
---

<ngmd-hero title="NativeScript" logo="https://cdn.simpleicons.org/nativescript/3C5AFD" gradient>
Inspect a NativeScript Angular app running on a simulator, an emulator or a phone. The app reports to a devtools server on your machine over a WebSocket.
</ngmd-hero>

# NativeScript

A NativeScript Angular app renders into `@nativescript/core` views, so it has no DOM for the [browser overlay](./overlay.md) to walk. The `@pangular-inspector/devtools/overlay-nativescript` entry point walks the NativeScript view tree instead and sends live data to a devtools server that runs on your machine.

This page describes what the overlay shows and how it works. To add it to an app, follow [Set up NativeScript](../guides/nativescript.md).

## What it shows

| Tab | On NativeScript |
| ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| Components | The component tree, paths and the detail panel. Pointing at a component in the panel outlines it on the device. |
| Signals | The signal graph of the selected component, with its write history. |
| Injectors | Element injectors and environment injectors with their providers. |
| NgRx | Live `@ngrx/signals` and `@ngrx/store` state and the change log. The store list names the app `NativeScript (iOS)` or `NativeScript (Android)`. |

The source scans (routes, pipes, NgRx declarations) come from the server, so they work as with the [Standalone CLI](./cli.md). The Pipes, Router, Forms, SSR & HTTP and change detection tabs show no live data for a NativeScript app, and there is no in-app popup.

### Controls without an effect

The panel treats a NativeScript app like a browser page and hides none of its controls. Two of them have nothing to work with:

| Control | Why it does nothing |
| -------------------------- | ----------------------------------------------------------------- |
| **Pick component on page** | The NativeScript overlay does not listen for a pick. |
| **Change detection** block | The NativeScript overlay does not record change detection cycles. |

Hovering a row still outlines the component on the device.

## Where it shows in the panel

The live data shows in the same tabs as a browser page.

| Server | Where the app shows |
| ---------------------------------- | ------------------------------------------------------- |
| [Standalone CLI](./cli.md) | The panel at `http://localhost:9999/`, without `?view`. |
| A hub (Express or the Vite plugin) | The **Angular** dock in the side rail. |

The **NativeScript** dock (`?view=nativescript`) shows no live data. It is a setup card, **Inspect NativeScript apps**, with the setup steps and a link to the [setup guide](../guides/nativescript.md).

The overlay sends no platform marker, so `list-pages` lists a NativeScript app with the `browser` platform. See [Agent tools](../agents/tools.md#list-pages).

## Requirements

- A development build. Angular publishes its debug API on `globalThis.ng` only when `ngDevMode` is on. Start the overlay inside an `if (__DEV__)` check.
- `@nativescript/core`, an optional peer dependency of the package. The overlay reads the root view and the platform from it.
- A `WebSocket` global. The NativeScript runtime has none, so import `@valor/nativescript-websockets` first in `src/polyfills.ts`. Without it, the overlay logs a warning that names the package and does not start.
- `initNativeScriptOverlay()` called before `runNativeScriptAngularApp()`, so the injector inspector gets its provider lists. See [How it works](#how-it-works).
- A server started with `--no-auth`, since the app cannot enter the one-time code the panel asks for.

## Set up

[Set up NativeScript](../guides/nativescript.md) walks through it: install the packages, add the WebSocket global, start the overlay and run the server. It also covers where the overlay connects from a simulator, an emulator or a device, and how to run the demo in `examples/nativescript`.

## Options

`initNativeScriptOverlay()` takes an optional object:

| Option | Default | What it does |
| ------------ | -------------------------- | ----------------------------------------------------------------------------- |
| `baseURL` | `defaultDevtoolsBaseURL()` | Absolute URL of the devtools server. |
| `intervalMs` | `3000` | How often the app reports, in milliseconds. |
| `retryMs` | `5000` | How long to wait before connecting again after a failure or a dropped socket. |

`defaultDevtoolsBaseURL(port = 9999)` is exported from the same entry point and picks the address by platform:

| Platform | Default `baseURL` |
| -------- | ------------------------ |
| iOS | `http://localhost:9999/` |
| Android | `http://10.0.2.2:9999/` |

`10.0.2.2` is how the Android emulator reaches its host. A physical device needs your machine's LAN address as `baseURL`.

`initNativeScriptOverlay()` returns a function that stops the overlay and tells the server to forget the app's component, injector and NgRx reports.

## How it works

- **Host tree**: the overlay walks the `@nativescript/core` views under the app's root view with the same collectors as the browser overlay. It starts at the host of the root component, found through *Angular's debug API, and looks the root up again on every walk, since NativeScript replaces the root view on some navigations. A host is named by its component's selector.
- **Injector profiler**: *Angular wires the profiler that backs the provider lists only when a `window` global exists as the platform is created. The overlay defines `window` until *Angular publishes `ng.getComponent`, then removes it. If `window` or `ng` already exist, it leaves them alone.
- **Web shims**: the devframe client reads `location` and `navigator`. The overlay defines them from `baseURL` when the runtime has none, and leaves them in place.
- **Transport**: the overlay connects over a WebSocket only, since the NativeScript `fetch` has no streaming body.
- **Reporting**: the app collects every `intervalMs` and sends a report when it changed, or on every fourth tick as a keepalive. When the connection fails or drops, it logs one warning and tries again every `retryMs`. Each new connection reports under a new page id.
- **Highlight**: when the panel or the `highlight` agent tool points at a component, the overlay sets a 2 px `#68b6ff` border on the first view under the host that draws something, and restores the previous border after two seconds.

## FAQ

<ngmd-accordion>
<ngmd-accordion-item title="The live tabs stay empty" open>
Check the app log for a <code>[pangular]</code> line. A warning about a missing <code>WebSocket</code> global means <code>&#64;valor/nativescript-websockets</code> is not imported first in <code>src/polyfills.ts</code>. A warning about reaching the server means the address is wrong for where the app runs; see <a href="../guides/nativescript.md#where-the-overlay-connects">where the overlay connects</a>. A release build has no <code>ng</code> global, so nothing is collected there.
</ngmd-accordion-item>
<ngmd-accordion-item title="The Providers list of an injector is empty">
The overlay has to start before <code>runNativeScriptAngularApp()</code> creates the platform. Call <code>initNativeScriptOverlay()</code> above it in <code>src/main.ts</code>.
</ngmd-accordion-item>
<ngmd-accordion-item title="The NativeScript dock shows no data">
The dock is a setup card. The app's data shows in the <strong>Angular</strong> dock, or in the panel without <code>?view</code> on the standalone CLI.
</ngmd-accordion-item>
</ngmd-accordion>

## Where to next

<ngmd-pill-row>
<ngmd-pill href="/guides/nativescript" title="Set up NativeScript"></ngmd-pill>
<ngmd-pill href="/getting-started/cli" title="Standalone CLI"></ngmd-pill>
<ngmd-pill href="/inspectors/components" title="Components"></ngmd-pill>
<ngmd-pill href="/security" title="Access and redaction"></ngmd-pill>
</ngmd-pill-row>
Loading
Loading