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
46 changes: 41 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,17 +158,53 @@ import '@santoshyadavdev/ng-devtools/overlay';
It looks for the devframe connection next to the page and then at
`/__ng-devtools/`.

`initOverlay` is exported for a devtools mounted somewhere else. Importing the
module has already started an overlay on the default URLs by then, so dispose of
that one before starting another, or the page ends up with two connections and
two polling intervals:
For a devtools mounted somewhere else, import `initOverlay` from
`/overlay/manual`, which starts nothing on import and adds no popup:

```ts
import { initOverlay } from '@santoshyadavdev/ng-devtools/overlay';
import { initOverlay } from '@santoshyadavdev/ng-devtools/overlay/manual';

const dispose = await initOverlay({ baseURL: '/__my-devtools/' });
```

### Capacitor and Ionic

An Ionic or Capacitor app runs in a WebView, so the overlay works there as it
does in a browser, forms included. The devtools server runs on your machine and
the app connects to it:

```ts
// main.ts, after bootstrapApplication(...)
if (isDevMode()) {
import('@santoshyadavdev/ng-devtools/overlay/manual').then(({ initOverlay }) =>
initOverlay({
baseURL: 'http://localhost:4000/__ng-devtools/',
connectionMeta: { backend: 'sse', sse: { path: '__sse' } },
}),
);
}
```

`connectionMeta` describes the server, so the app does not have to fetch
`__connection.json`, which the WebView blocks as a cross-origin request. Use
`{ backend: 'sse', sse: { path: '__sse' } }` for the Express middleware with
`ws: false`, and open `__connection.json` in a browser to see what another
server uses.

| Where the app runs | How it reaches the server |
| -------------------------- | ------------------------------------------------------------- |
| Android emulator or device | `adb reverse tcp:4000 tcp:4000` (USB), then `localhost` works |
| iOS simulator | `localhost` works as is |

Android blocks plain `http` unless you allow it for development in
`capacitor.config.ts`:

```ts
server: { cleartext: true },
```

Open the devtools UI in your desktop browser at the server's `/__ng-devtools/`.

### In-Page Popup

The devtools can appear as a floating popup directly on your page — no browser extension needed:
Expand Down
6 changes: 4 additions & 2 deletions packages/ng-devtools/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,17 @@
"exports": {
".": "./src/devframe.ts",
"./devframe": "./src/devframe.ts",
"./overlay": "./src/overlay.ts",
"./overlay": "./src/overlay-auto.ts",
"./overlay/manual": "./src/overlay.ts",
"./popup": "./src/popup.ts",
"./package.json": "./package.json"
},
"publishConfig": {
"exports": {
".": "./dist/devframe.mjs",
"./devframe": "./dist/devframe.mjs",
"./overlay": "./dist/overlay.mjs",
"./overlay": "./dist/overlay-auto.mjs",
"./overlay/manual": "./dist/overlay.mjs",
"./popup": "./dist/popup.mjs",
"./package.json": "./package.json"
}
Expand Down
12 changes: 12 additions & 0 deletions packages/ng-devtools/src/overlay-auto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { initOverlay } from './overlay.ts';

export * from './overlay.ts';

// Auto-init when loaded as a script (skip during test environment)
if (
typeof document !== 'undefined' &&
!(typeof process !== 'undefined' && process.env?.['VITEST'])
) {
initOverlay().catch(console.error);
import('./popup.ts').then((m) => m.createDevtoolsPopup()).catch(console.error);
}
23 changes: 11 additions & 12 deletions packages/ng-devtools/src/overlay.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { connectDevframe } from 'devframe/client';
import { connectDevframe, type SetupDevframeConnectionOptions } from 'devframe/client';
import {
collectForms,
diffForms,
Expand Down Expand Up @@ -83,10 +83,18 @@ function isFieldTarget(target: unknown): target is { formId: string; path: strin
);
}

export async function initOverlay(options: { baseURL?: string | string[] } = {}) {
export interface OverlayOptions {
baseURL?: string | string[];
connectionMeta?: SetupDevframeConnectionOptions['connectionMeta'];
}

export async function initOverlay(options: OverlayOptions = {}) {
// `connectDevframe()` alone looks for the connection next to the page, which
// misses the documented `/__ng-devtools/` mount in a host app.
const rpc = await connectDevframe({ baseURL: options.baseURL ?? ['./', '/__ng-devtools/'] });
const rpc = await connectDevframe({
baseURL: options.baseURL ?? ['./', '/__ng-devtools/'],
connectionMeta: options.connectionMeta,
});
const my = rpc.scope('ng-devtools');

async function pushTree() {
Expand Down Expand Up @@ -806,12 +814,3 @@ function safeSerialize(val: unknown): unknown {
return String(val);
}
}

// Auto-init when loaded as a script (skip during test environment)
if (
typeof document !== 'undefined' &&
!(typeof process !== 'undefined' && process.env?.['VITEST'])
) {
initOverlay().catch(console.error);
import('./popup.ts').then((m) => m.createDevtoolsPopup()).catch(console.error);
}
2 changes: 1 addition & 1 deletion packages/ng-devtools/tsdown.config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { defineConfig } from 'tsdown';

export default defineConfig({
entry: ['src/devframe.ts', 'src/popup.ts', 'src/overlay.ts'],
entry: ['src/devframe.ts', 'src/popup.ts', 'src/overlay.ts', 'src/overlay-auto.ts'],
format: 'esm',
platform: 'node',
dts: true,
Expand Down
Loading