Skip to content
Open
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
4 changes: 4 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,7 @@ dist
pnpm-lock.yaml
extension/ui
docs/privacy-policy.html
examples/nativescript/platforms
examples/nativescript/hooks
examples/nativescript/App_Resources
examples/nativescript/package-lock.json
68 changes: 68 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,74 @@ createDevtoolsPopup();

This adds a floating button (bottom-right) that opens the full devtools UI in an iframe. Supports three dock modes (float, bottom, right), dragging, resizing, and persists position via localStorage. The popup is automatically loaded in development when using the demo app.

## NativeScript

The same devtools work inside a NativeScript Angular app. The app has no DOM, so a
separate overlay walks the native view tree through Angular's debug API and reports
over a WebSocket to a devtools server running on your machine. The component tree,
signal graph, injector tree (including environment injectors and their providers),
the highlight tool and the MCP surface all work; the source scanners run against
the app's `src/`.

### Set up an app

```sh
npm install @santoshyadavdev/ng-devtools @valor/nativescript-websockets
```

```ts
// src/polyfills.ts — first import, so the runtime has a WebSocket global
import '@valor/nativescript-websockets';
```

```ts
// src/main.ts — before runNativeScriptAngularApp()
import { initNativeScriptOverlay } from '@santoshyadavdev/ng-devtools/overlay-nativescript';

if (__DEV__) {
initNativeScriptOverlay();
}
```

`initNativeScriptOverlay()` must run before Angular bootstraps: the DI inspector
relies on Angular's injector profiler, which Angular only wires while it creates
the platform. The overlay connects to `http://localhost:9999/` on the iOS
simulator and `http://10.0.2.2:9999/` on the Android emulator; pass
`{ baseURL }` for a physical device, and allow plain HTTP to that address in
`Info.plist` / `AndroidManifest.xml`.

### Run the devtools server

```sh
cd my-nativescript-app
npx @santoshyadavdev/ng-devtools dev --host 0.0.0.0 --no-auth
```
Comment thread
coderabbitai[bot] marked this conversation as resolved.

Then open `http://localhost:9999/` for the UI, or point an MCP client at
`http://localhost:9999/__mcp`.

> [!WARNING]
> `--host 0.0.0.0 --no-auth` exposes the devtools RPC and MCP endpoints,
> unauthenticated, to every host that can reach your machine. Use it only on a
> trusted network. The simulators reach the server over loopback, so drop
> `--host 0.0.0.0` unless a physical device needs it, and prefer binding to the
> one interface that device uses (e.g. `--host 192.168.1.20`).

### Example app

`examples/nativescript/` is a `ns create --ng` project wired up this way, with a small
showcase component (signals, a computed, an effect and a component-level
provider). Tested on the iOS simulator; Android has not been verified yet.

The app maps `@santoshyadavdev/ng-devtools/*` to the package's build output in
`packages/ng-devtools/dist`, so build the package first.

```sh
pnpm devtools:build-pkg # builds packages/ng-devtools/dist, UI included
pnpm devtools:nativescript # devtools server scanning examples/nativescript/src
cd examples/nativescript && npm install && ns debug ios --no-hmr
```

## Demo App

The repository includes a demo app, **Angular Travel** (`src/`), that looks and behaves like a real booking site so every inspector has something to show:
Expand Down
45 changes: 26 additions & 19 deletions app/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,22 +53,6 @@ const VIEW_TAB: Partial<Record<View, Tab>> = { ngrx: 'store', analog: 'analog' }
const TAB_VIEW: Partial<Record<Tab, View>> = { store: 'ngrx', analog: 'analog' };

const COMING_SOON: Partial<Record<View, ComingSoonInfo>> = {
nativescript: {
id: 'nativescript',
name: 'NativeScript',
badge: 'Coming Soon',
heading: 'NativeScript Support',
summary:
'Inspect NativeScript Angular apps running natively on iOS and Android, with the same tools.',
color: '#3c5afd',
plans: [
'Native overlay that reports the component tree from the device',
'Standalone devtools server your phone or simulator connects to',
'Example NativeScript app to try it end to end',
],
pr: 16,
author: { name: 'Nathan Walker', login: 'NathanWalker' },
},
capacitor: {
id: 'capacitor',
name: 'Capacitor',
Expand All @@ -86,6 +70,26 @@ const COMING_SOON: Partial<Record<View, ComingSoonInfo>> = {
},
};

const NATIVESCRIPT_SETUP: ComingSoonInfo = {
id: 'nativescript',
name: 'NativeScript',
badge: 'Available',
heading: 'Inspect NativeScript apps',
summary:
'A NativeScript Angular app reports from the simulator or device to this server, and its components, signals, injectors and NgRx stores show up in the Angular dock.',
color: '#3c5afd',
plansTitle: 'Set up an app',
plans: [
'Install @santoshyadavdev/ng-devtools and @valor/nativescript-websockets',
'Call initNativeScriptOverlay() in main.ts, before the app bootstraps',
'Run ng-devtools dev --host 0.0.0.0 in the app, then open the Angular dock',
],
link: {
label: 'NativeScript setup guide',
href: 'https://github.com/santoshyadavdev/angular-devtools#nativescript',
},
};

const NOT_ANALOG: ComingSoonInfo = {
id: 'analog',
name: 'Analog',
Expand Down Expand Up @@ -614,6 +618,7 @@ export class App implements OnInit, OnDestroy {
readonly comingSoon = computed(() => {
const view = this.view();
if (view === 'analog') return this.analogKnown() && !this.analog() ? NOT_ANALOG : undefined;
if (view === 'nativescript') return NATIVESCRIPT_SETUP;
return view ? COMING_SOON[view] : undefined;
});
readonly tabs = computed(() => {
Expand Down Expand Up @@ -803,7 +808,9 @@ export class App implements OnInit, OnDestroy {
}
}

// Chrome extension passes ?baseURL=...; embedded uses /__ng-devtools/; standalone uses default
// Chrome extension passes ?baseURL=...; the Vite bridge mounts the connection
// at /__ng-devtools/ beside a page served from /; the standalone server and the
// Express mount serve it next to the page.
function sameOrigin(value: string): boolean {
try {
return new URL(value, location.href).origin === location.origin;
Expand All @@ -827,7 +834,7 @@ function loopbackFromExtension(value: string): boolean {
}
}

function detectBaseURL(): string | undefined {
function detectBaseURL(): string | string[] | undefined {
const params = new URLSearchParams(location.search);
const fromQuery = params.get('baseURL');
// Same origin only: any page can open this URL, and this value decides where
Expand All @@ -841,5 +848,5 @@ function detectBaseURL(): string | undefined {
if (location.pathname.includes('__ng-devtools') || location.pathname.includes('__devframes/')) {
return undefined;
}
return '/__ng-devtools/';
return ['./', '/__ng-devtools/'];
}
9 changes: 7 additions & 2 deletions app/src/pages/coming-soon.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Component, input } from '@angular/core';
import { Component, computed, input } from '@angular/core';

export interface ComingSoonInfo {
id: 'nativescript' | 'capacitor' | 'analog';
Expand All @@ -8,6 +8,7 @@ export interface ComingSoonInfo {
summary: string;
color: string;
plans: string[];
plansTitle?: string;
pr?: number;
author?: { name: string; login: string };
link?: { label: string; href: string };
Expand Down Expand Up @@ -105,7 +106,7 @@ export interface ComingSoonInfo {
}

<h3 class="plans-title">
{{ info().link ? 'In an ' + info().name + ' app' : 'On the roadmap' }}
{{ plansTitle() }}
</h3>
<ul class="plans">
@for (plan of info().plans; track plan; let i = $index) {
Expand Down Expand Up @@ -464,4 +465,8 @@ export interface ComingSoonInfo {
})
export class ComingSoon {
readonly info = input.required<ComingSoonInfo>();
readonly plansTitle = computed(() => {
const info = this.info();
return info.plansTitle ?? (info.link ? `In an ${info.name} app` : 'On the roadmap');
});
}
19 changes: 19 additions & 0 deletions examples/nativescript/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
root = true

[*]
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
charset = utf-8

[*.json]
indent_style = space
indent_size = 2

[*.js]
indent_style = space
indent_size = 2

[*.ts]
indent_style = space
indent_size = 2
28 changes: 28 additions & 0 deletions examples/nativescript/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# NativeScript
hooks/
node_modules/
platforms/

# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# General
.DS_Store
.AppleDouble
.LSOverride
.idea
.cloud
.project
tmp/
typings/

# Visual Studio Code
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
3 changes: 3 additions & 0 deletions examples/nativescript/.vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"recommendations": ["nativescript.nativescript"]
}
25 changes: 25 additions & 0 deletions examples/nativescript/App_Resources/Android/app.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// You can add your native dependencies here
dependencies {
// implementation 'androidx.multidex:multidex:2.0.1'
}

android {
compileSdkVersion 36
buildToolsVersion "36"
// ndkVersion ""

defaultConfig {
minSdkVersion 24
targetSdkVersion 36

// Version Information
versionCode 1
versionName "1.0.0"

generatedDensities = []
}

aaptOptions {
additionalParameters "--no-version-vectors"
}
}
15 changes: 15 additions & 0 deletions examples/nativescript/App_Resources/Android/before-plugins.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// this configurations is loaded before building plugins, as well as before building
// the app - this is where you can apply global settings and overrides

project.ext {
// androidXAppCompat = "1.4.1"
// androidXExifInterface = "1.3.3"
// androidXFragment = "1.4.1"
// androidXMaterial = "1.5.0"
// androidXMultidex = "2.0.1"
// androidXTransition = "1.4.1"
// androidXViewPager = "1.0.0"

// useKotlin = true
// kotlinVersion = "1.6.0"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="__PACKAGE__">

<supports-screens
android:smallScreens="true"
android:normalScreens="true"
android:largeScreens="true"
android:xlargeScreens="true"/>

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

<application
android:usesCleartextTraffic="true"
android:name="com.tns.NativeScriptApplication"
android:networkSecurityConfig="@xml/network_security"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme"
android:hardwareAccelerated="true">

<activity
android:name="com.tns.NativeScriptActivity"
android:label="@string/title_activity_kimera"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|smallestScreenSize|screenLayout|locale|uiMode"
android:theme="@style/LaunchScreenTheme"
android:hardwareAccelerated="true"
android:launchMode="singleTask"
android:exported="true">

<meta-data android:name="SET_THEME_ON_LAUNCH" android:resource="@style/AppTheme" />

<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="com.tns.ErrorReportActivity"/>
</application>
</manifest>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" android:gravity="fill">
<item>
<bitmap android:gravity="fill" android:src="@drawable/background" />
</item>
<item>
<bitmap android:gravity="center" android:src="@drawable/logo" />
</item>
</layer-list>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<group android:scaleX="0.07382812"
android:scaleY="0.07382812"
android:translateX="16.2"
android:translateY="16.2">
<path
android:pathData="M843,511.86L843,512.96C843,513.22 842.8,513.43 842.55,513.46C809.18,516.58 783.07,543.69 783.07,577.86L783.07,714.8C783.07,724.37 781.06,733.6 777.43,742.03C767.08,766.1 743.59,783.6 716.27,783.6L647.07,783.6L646.59,783L377.41,445.68L377.41,783.6L307.73,783.6C270.84,783.6 240.93,753.7 240.93,716.8L240.93,577.86C240.93,543.69 214.82,516.58 181.45,513.46C181.2,513.43 181,513.22 181,512.96L181,511.86C181,511.61 181.19,511.4 181.45,511.37C214.69,508.14 240.67,480.35 240.67,446.26L240.67,307.2C240.67,297.91 242.57,289.05 246,281.01C256.18,257.14 279.87,240.39 307.47,240.39L377.13,240.39L377.41,240.74L646.59,578.07L646.59,240.39L716.53,240.39C753.42,240.39 783.33,270.3 783.33,307.2L783.33,446.26C783.33,480.35 809.31,508.14 842.55,511.37C842.81,511.4 843,511.61 843,511.86"
android:fillColor="#65ADF1"
android:fillType="nonZero"/>
</group>
</vector>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8" ?>
<resources>
<!-- You can override base colors from values/colors.xml here, for example: -->
<!-- <color name="ns_accent">#65ADF1</color> -->
</resources>
Loading