Skip to content
Draft
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 @@ -5,3 +5,7 @@ pnpm-lock.yaml
extension/ui
packages/ng-devtools-assets/dist
docs/privacy-policy.html
app-nativescript/platforms
app-nativescript/hooks
app-nativescript/App_Resources
app-nativescript/package-lock.json
57 changes: 57 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,63 @@ createDevtoolsPopup();

This adds a purple FAB 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
```

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

### Example app

`app-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.

```sh
pnpm devtools:nativescript # devtools server scanning app-nativescript/src
cd app-nativescript && ns debug ios --no-hmr
```

## Demo App

The repository includes a demo Angular app (`src/`) that showcases the devtools with a product catalog built using `@ngrx/signals`:
Expand Down
19 changes: 19 additions & 0 deletions app-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 app-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 app-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 app-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 app-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>
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8" ?>
<resources xmlns:android="http://schemas.android.com/apk/res/android">

<!-- Application theme -->
<style name="AppThemeBase21" parent="AppThemeBase">
<!-- Uncomment this to make the app show underneat the status bar -->
<!-- <item name="android:windowTranslucentStatus">true</item> -->

<item name="android:datePickerStyle">@style/SpinnerDatePicker</item>
<item name="android:timePickerStyle">@style/SpinnerTimePicker</item>
</style>

<style name="AppTheme" parent="AppThemeBase21">
</style>

<!-- Default style for DatePicker - in spinner mode -->
<style name="SpinnerDatePicker" parent="android:Widget.Material.Light.DatePicker">
<!-- set the default mode for the date picker (supported values: spinner, calendar) -->
<item name="android:datePickerMode">spinner</item>
</style>

<!-- Default style for TimePicker - in spinner mode -->
<style name="SpinnerTimePicker" parent="android:Widget.Material.Light.TimePicker">
<!-- set the default mode for the time picker (supported values: spinner, clock) -->
<item name="android:timePickerMode">spinner</item>
</style>

<style name="NativeScriptToolbarStyle" parent="NativeScriptToolbarStyleBase">
<item name="android:elevation">4dp</item>

<!-- Add padding to the ActionBar - useful when android:windowTranslucentStatus is set to true -->
<!-- <item name="android:paddingTop">24dp</item> -->
</style>
</resources>
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:android="http://schemas.android.com/apk/res/android">

<!-- Application theme -->
<style name="AppThemeBase29" parent="AppThemeBase21">

<!--
Disable forced dark mode on newer devices.
Enabling this will make your app appear in dark-mode,
but the way the app is converted may lead to visual issues
-->
<item name="android:forceDarkAllowed">false</item>
</style>

<style name="AppTheme" parent="AppThemeBase29">
</style>

</resources>
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8" ?>
<resources>
<!-- The default color of the ActionBar -->
<color name="ns_primary">#F5F5F5</color>

<!-- (API21+) The color of the status bar and contextual app bars; this is normally a dark version of colorPrimary. -->
<color name="ns_primaryDark">#757575</color>

<!-- The color of UI controls such as check boxes, radio buttons, and edit text boxes. -->
<color name="ns_accent">#65ADF1</color>

<!-- this is unused in the default themes, so we might want to remove it? -->
<!-- <color name="ns_blue">#272734</color> -->
</resources>
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#FFFFFF</color>
</resources>
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:android="http://schemas.android.com/apk/res/android">

<!-- theme to use FOR launch screen-->
<style name="LaunchScreenThemeBase" parent="Theme.AppCompat.Light.NoActionBar">
<item name="toolbarStyle">@style/NativeScriptToolbarStyle</item>

<item name="colorPrimary">@color/ns_primary</item>
<item name="colorPrimaryDark">@color/ns_primaryDark</item>
<item name="colorAccent">@color/ns_accent</item>

<item name="android:windowBackground">@drawable/splash_screen</item>

<item name="android:windowActionBarOverlay">true</item>
<item name="android:windowTranslucentStatus">true</item>
</style>

<style name="LaunchScreenTheme" parent="LaunchScreenThemeBase">
</style>

<!-- theme to use AFTER launch screen is loaded-->
<style name="AppThemeBase" parent="Theme.AppCompat.Light.NoActionBar">
<item name="toolbarStyle">@style/NativeScriptToolbarStyle</item>

<item name="colorPrimary">@color/ns_primary</item>
<item name="colorPrimaryDark">@color/ns_primaryDark</item>
<item name="colorAccent">@color/ns_accent</item>
</style>

<style name="AppTheme" parent="AppThemeBase">
</style>

<!-- theme for action-bar -->
<style name="NativeScriptToolbarStyleBase" parent="Widget.AppCompat.Toolbar">
<item name="android:background">@color/ns_primary</item>
<item name="theme">@style/ThemeOverlay.AppCompat.ActionBar</item>
<item name="popupTheme">@style/ThemeOverlay.AppCompat</item>
</style>

<style name="NativeScriptToolbarStyle" parent="NativeScriptToolbarStyleBase">
</style>
</resources>
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true">
<trust-anchors>
<certificates src="system" />
<certificates src="user" />
</trust-anchors>
</base-config>
<domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="true">localhost</domain>
<domain includeSubdomains="true">127.0.0.1</domain>
<domain includeSubdomains="true">10.0.2.2</domain>
</domain-config>
</network-security-config>


Loading