@gjsify/devtools-nativescript 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/esm/_virtual/_rolldown/runtime.js +1 -0
- package/lib/esm/handlers.js +1 -0
- package/lib/esm/index.js +1 -0
- package/lib/esm/install.js +1 -0
- package/lib/esm/screenshot.android.js +1 -0
- package/lib/esm/screenshot.ios.js +1 -0
- package/lib/esm/screenshot.js +1 -0
- package/lib/esm/view-tree.js +1 -0
- package/lib/types/handlers.d.ts +68 -0
- package/lib/types/index.d.ts +4 -0
- package/lib/types/index.spec.d.ts +2 -0
- package/lib/types/install.d.ts +67 -0
- package/lib/types/screenshot.android.d.ts +8 -0
- package/lib/types/screenshot.d.ts +7 -0
- package/lib/types/screenshot.ios.d.ts +7 -0
- package/lib/types/view-tree.d.ts +95 -0
- package/package.json +77 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e=Object.defineProperty,__name=(t,n)=>e(t,`name`,{value:n,configurable:!0});export{__name};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./_virtual/_rolldown/runtime.js";import{captureViewPng as e}from"./screenshot.js";import{buildViewPath as t,dumpTree as n,listToplevels as r,readViewProperty as i,resolveViewPath as a,rootView as o,viewType as s}from"./view-tree.js";import{DevtoolsError as c,GENERIC_METHODS as l,MethodRegistry as u}from"@gjsify/devtools-protocol";function rootOf(e){return o(e.application,e.frame)}function requireRoot(e){let t=rootOf(e);if(!t)throw new c(`unavailable`,`no root view — the NativeScript app is not bootstrapped yet`);return t}function resolveRoot(e,t){let n=requireRoot(e);if(!t||t===`window`||t===`active`||t===`toplevel:0`)return{view:n,path:`toplevel:0`};let r=a(n,t);if(!r)throw new c(`not-found`,`no view at '${t}'`);return{view:r,path:t}}function createNativescriptRegistry(t){let a=new u,kind=e=>l[e];a.register({name:`GetStatus`,kind:kind(`GetStatus`),handler:()=>{let e=rootOf(t),n={appId:t.appId??`nativescript`,instance:t.instance??`default`,platform:`nativescript`,devtools:!0,activeWindow:e?{id:`root`,title:s(e),mapped:e.isLoaded??!0}:null,toplevelCount:+!!e,focusedWidget:null,paused:t.paused?.()??!1};for(let e of t.extensions??[]){let t=e.contributeStatus?.();t&&Object.assign(n,t)}return n}}),a.register({name:`Screenshot`,kind:kind(`Screenshot`),handler:()=>{let n=rootOf(t);return{format:`png-base64`,data:n?e(n):null}}}),a.register({name:`ListToplevels`,kind:kind(`ListToplevels`),handler:()=>r(rootOf(t))}),a.register({name:`DumpTree`,kind:kind(`DumpTree`),handler:e=>{let r=typeof e.root==`string`?e.root:``,i=typeof e.depth==`number`&&e.depth>0?e.depth:8,a=resolveRoot(t,r);return n(a.view,i,a.path)}}),a.register({name:`GetProperty`,kind:kind(`GetProperty`),handler:e=>{let n=typeof e.path==`string`?e.path:``,r=typeof e.prop==`string`?e.prop:``;if(!r)throw new c(`invalid-params`,"GetProperty requires a `prop` name");let a=i(resolveRoot(t,n).view,r);if(a===void 0)throw new c(`not-found`,`view has no property '${r}'`);return a}}),a.register({name:`ActivateAction`,kind:kind(`ActivateAction`),handler:e=>{let n=resolveRoot(t,typeof e.path==`string`?e.path:typeof e.name==`string`?e.name:``),r=n.view;if(typeof r.notify!=`function`)throw new c(`unsupported`,`view at '${n.path}' cannot receive a tap`);return r.notify({eventName:`tap`,object:r}),{tapped:n.path}}});for(let e of t.extensions??[])for(let t of e.methods)a.register({name:t.name,kind:t.kind,handler:e=>t.handler(e)});return a}export{t as buildViewPath,createNativescriptRegistry};
|
package/lib/esm/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{buildViewPath as e,dumpTree as t,listToplevels as n,parseViewPath as r,readViewProperty as i,resolveViewPath as a,rootView as o,viewType as s}from"./view-tree.js";import{createNativescriptRegistry as c}from"./handlers.js";import{createAgent as l,installDevtools as u,isNativeScript as d,uninstallDevtools as f}from"./install.js";export*from"@gjsify/devtools-protocol";export{e as buildViewPath,l as createAgent,c as createNativescriptRegistry,t as dumpTree,u as installDevtools,d as isNativeScript,n as listToplevels,r as parseViewPath,i as readViewProperty,a as resolveViewPath,o as rootView,f as uninstallDevtools,s as viewType};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./_virtual/_rolldown/runtime.js";import{createNativescriptRegistry as e}from"./handlers.js";import{errResponse as t}from"@gjsify/devtools-protocol";import{assertNativeScript as n,isNativeScript as r}from"@gjsify/native-platform";const i=new Set;function envEnabled(){let e=globalThis.process?.env?.GJSIFY_DEVTOOLS;if(e==null)return!1;let t=String(e).toLowerCase();return t!==``&&t!==`0`&&t!==`false`}function envInstance(){let e=globalThis.process?.env?.GJSIFY_DEVTOOLS_INSTANCE;return e&&e!==``?e:void 0}function createAgent(e,n){return{instance:n.instance??`default`,async dispatch(r){let i;try{i=JSON.parse(r)}catch{return JSON.stringify(t({code:`invalid-params`,message:`request is not valid JSON`}))}let a=await e.dispatch(i,{paused:n.paused?.()??!1});return JSON.stringify(a)}}}function installDevtools(t={}){if(n(),!(t.enabled??envEnabled()))return null;let r={instance:t.instance??envInstance()??`default`,appId:t.appId,paused:t.paused,application:t.application??null,frame:t.frame??null,extensions:t.extensions},a=createAgent(e(r),r);return globalThis.__adwDevtools=a,i.add(a),a}function uninstallDevtools(e){let t=globalThis;t.__adwDevtools===e&&delete t.__adwDevtools,i.delete(e)}export{createAgent,installDevtools,r as isNativeScript,uninstallDevtools};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./_virtual/_rolldown/runtime.js";function isDrawable(e){return!!e&&typeof e.getMeasuredWidth==`function`&&typeof e.draw==`function`}function resolveNativeView(e){let t=e,n=t.android,r=n?.activity?.getWindow?.()?.getDecorView?.()??n?.currentActivity?.getWindow?.()?.getDecorView?.(),i=[t.nativeViewProtected,t.android,r,n?.rootViewGroup];for(let e of i)if(isDrawable(e))return e;return null}function captureViewPng(e){let t=resolveNativeView(e);if(!t)return null;let n=t.getMeasuredWidth()||t.getWidth()||e.getMeasuredWidth?.()||0,r=t.getMeasuredHeight()||t.getHeight()||e.getMeasuredHeight?.()||0;if(n<=0||r<=0)return null;let i=android.graphics,a=i.Bitmap.createBitmap(n,r,i.Bitmap.Config.ARGB_8888),o=new i.Canvas(a);o.drawColor(i.Color.TRANSPARENT),t.draw(o);let s=new java.io.ByteArrayOutputStream;try{a.compress(i.Bitmap.CompressFormat.PNG,100,s);let e=s.toByteArray(),t=android.util;return t.Base64.encodeToString(e,t.Base64.NO_WRAP)}finally{s.close(),a.recycle()}}export{captureViewPng};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./_virtual/_rolldown/runtime.js";function captureViewPng(e){let t=e.ios;if(!t||typeof UIGraphicsImageRenderer>`u`||typeof UIImagePNGRepresentation>`u`)return null;let n=t.bounds;if(n.size.width<=0||n.size.height<=0)return null;let r=UIGraphicsImageRenderer.alloc().initWithBounds(n).imageWithActions(()=>{t.drawViewHierarchyInRectAfterScreenUpdates(n,!0)}),i=UIImagePNGRepresentation(r);return i?i.base64EncodedStringWithOptions(0):null}export{captureViewPng};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./_virtual/_rolldown/runtime.js";function captureViewPng(e){throw Error(`Platform not supported`)}export{captureViewPng};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./_virtual/_rolldown/runtime.js";function parseViewPath(e){let t=e.split(`/`).filter(e=>e.length>0);if(t.length===0)return null;let n=t[0].match(/^toplevel:(\d+)$/);if(!n)return null;let r=[];for(let e of t.slice(1)){let t=e.match(/^child:(\d+)$/);if(!t)return null;r.push(Number(t[1]))}return{toplevel:Number(n[1]),children:r}}function buildViewPath(e,t){return[`toplevel:${e}`,...t.map(e=>`child:${e}`)].join(`/`)}function viewType(e){return typeof e.typeName==`string`&&e.typeName?e.typeName:e.constructor?.name??`View`}function viewName(e){return e.automationText||e.id||null}function viewCssClasses(e){let t=e.cssClasses;return t instanceof Set||Array.isArray(t)?[...t]:typeof e.className==`string`&&e.className?e.className.split(/\s+/).filter(Boolean):[]}function viewVisible(e){return typeof e.visibility==`string`?e.visibility===`visible`:!0}function childViews(e){let t=[];return typeof e.eachChildView==`function`&&e.eachChildView(e=>(t.push(e),!0)),t}function viewText(e){return typeof e.text==`string`&&e.text!==``?e.text:null}function rootView(e,t){return e?.getRootView?.()||(t?.topmost?.()?.currentPage??null)}function listToplevels(e){return e?[{path:`toplevel:0`,type:viewType(e),title:viewText(e),mapped:e.isLoaded??!0,focused:!0}]:[]}function resolveViewPath(e,t){let n=parseViewPath(t);if(!n||n.toplevel!==0||!e)return null;let r=e;for(let e of n.children)if(r=childViews(r)[e]??null,!r)return null;return r}function dumpTree(e,t,n){let r=viewText(e),i={path:n,type:viewType(e),name:viewName(e),cssClasses:viewCssClasses(e),mapped:e.isLoaded??!0,visible:viewVisible(e),children:[]};if(r!==null&&(i.props={text:r}),t<=0)return i;let a=childViews(e);for(let e=0;e<a.length;e++)i.children.push(dumpTree(a[e],t-1,`${n}/child:${e}`));return i}function jsonSafe(e){let t=typeof e;return e==null||t===`string`||t===`number`||t===`boolean`?e??null:`[${t}]`}function readViewProperty(e,t){let n=e,r=t.replace(/-/g,`_`),i=t.replace(/[-_]([a-z])/g,(e,t)=>t.toUpperCase());for(let e of[t,r,i])if(e in n)return jsonSafe(n[e])}export{buildViewPath,dumpTree,listToplevels,parseViewPath,readViewProperty,resolveViewPath,rootView,viewType};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { type MethodKind, MethodRegistry } from '@gjsify/devtools-protocol';
|
|
2
|
+
import { buildViewPath, type NsApplicationLike, type NsFrameLike } from './view-tree.js';
|
|
3
|
+
/**
|
|
4
|
+
* The `Screenshot` result shape for the NativeScript transport. The GTK adapter
|
|
5
|
+
* returns raw PNG `ay` bytes over DBus; the NS agent travels over the V8 CDP
|
|
6
|
+
* `Runtime.evaluate` JSON channel where a `Uint8Array` cannot survive
|
|
7
|
+
* `returnByValue`, so the PNG is base64-encoded here and the host profile
|
|
8
|
+
* decodes it. `format` documents the encoding so the host need not guess.
|
|
9
|
+
*/
|
|
10
|
+
export interface ScreenshotResult {
|
|
11
|
+
format: 'png-base64';
|
|
12
|
+
/** base64-encoded PNG, or `null` when the root view is not laid out yet. */
|
|
13
|
+
data: string | null;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* One app-specific devtools method an extension contributes — a name, a pause
|
|
17
|
+
* classification, and a handler over the registry's named-`params` record (the
|
|
18
|
+
* NS transport sends `{ method, params }`, so the extension maps the params it
|
|
19
|
+
* needs; e.g. the storybook adapter reads `params.title` / `params.name`).
|
|
20
|
+
*/
|
|
21
|
+
export interface NsDevtoolsMethod {
|
|
22
|
+
/** Method name (unique across the registry). */
|
|
23
|
+
name: string;
|
|
24
|
+
/** Pause classification (`'mutating'` methods reject while paused). */
|
|
25
|
+
kind: MethodKind;
|
|
26
|
+
/** Implementation — receives the request's params, returns the result value. */
|
|
27
|
+
handler: (params: Record<string, unknown>) => unknown | Promise<unknown>;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* An app-specific devtools surface merged into the registry (e.g. the storybook
|
|
31
|
+
* `ListStories`/`OpenStory`/… methods from
|
|
32
|
+
* `buildStorybookDevtoolsExtension(controller)`). Structurally compatible with
|
|
33
|
+
* `@gjsify/storybook-core`'s `StorybookDevtoolsExtension`.
|
|
34
|
+
*/
|
|
35
|
+
export interface NsDevtoolsExtension {
|
|
36
|
+
/** Extra methods registered alongside the generic ones. */
|
|
37
|
+
methods: NsDevtoolsMethod[];
|
|
38
|
+
/** Extra keys merged into `GetStatus()`. */
|
|
39
|
+
contributeStatus?: () => Record<string, unknown>;
|
|
40
|
+
}
|
|
41
|
+
/** Wiring inputs — the live NS application + frame surfaces (duck-typed). */
|
|
42
|
+
export interface NsDevtoolsContext {
|
|
43
|
+
/** A label for this devtools instance (multi-instance side-by-side). */
|
|
44
|
+
readonly instance?: string;
|
|
45
|
+
/** The NS `Application` module (for `getRootView()`). */
|
|
46
|
+
readonly application: NsApplicationLike | null;
|
|
47
|
+
/** The NS `Frame` module (for `topmost()?.currentPage` fallback). */
|
|
48
|
+
readonly frame: NsFrameLike | null;
|
|
49
|
+
/** Predicate the pause guard consults; mutating methods reject when true. */
|
|
50
|
+
readonly paused?: () => boolean;
|
|
51
|
+
/** App id reported by `GetStatus` (defaults to the NS bundle id when known). */
|
|
52
|
+
readonly appId?: string;
|
|
53
|
+
/**
|
|
54
|
+
* App-specific devtools surfaces merged into the registry — extra methods +
|
|
55
|
+
* `GetStatus` keys (e.g. the storybook control surface). Backward-compatible:
|
|
56
|
+
* omitted by the spike showcase install.
|
|
57
|
+
*/
|
|
58
|
+
readonly extensions?: readonly NsDevtoolsExtension[];
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Build a {@link MethodRegistry} populated with the generic devtools handlers
|
|
62
|
+
* bound to the NativeScript runtime. The mutating-method pause policy is
|
|
63
|
+
* enforced by the registry's dispatcher (`paused` option) — the same policy the
|
|
64
|
+
* GTK adapter applies — so no per-handler guard is needed here.
|
|
65
|
+
*/
|
|
66
|
+
export declare function createNativescriptRegistry(ctx: NsDevtoolsContext): MethodRegistry;
|
|
67
|
+
/** Build the stable path of a child index list (re-exported for callers/tests). */
|
|
68
|
+
export { buildViewPath };
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { createAgent, installDevtools, uninstallDevtools, isNativeScript, type InstallNsDevtoolsOptions, type NsDevtoolsAgent, } from './install.js';
|
|
2
|
+
export { createNativescriptRegistry, type NsDevtoolsContext, type NsDevtoolsExtension, type NsDevtoolsMethod, type ScreenshotResult, } from './handlers.js';
|
|
3
|
+
export { buildViewPath, dumpTree, listToplevels, parseViewPath, readViewProperty, resolveViewPath, rootView, viewType, type NsApplicationLike, type NsFrameLike, type NsView, type ParsedViewPath, } from './view-tree.js';
|
|
4
|
+
export * from '@gjsify/devtools-protocol';
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { type MethodRegistry } from '@gjsify/devtools-protocol';
|
|
2
|
+
import { isNativeScript } from '@gjsify/native-platform';
|
|
3
|
+
import { type NsDevtoolsContext, type NsDevtoolsExtension } from './handlers.js';
|
|
4
|
+
import type { NsApplicationLike, NsFrameLike } from './view-tree.js';
|
|
5
|
+
/** The agent object attached to `globalThis.__adwDevtools`. */
|
|
6
|
+
export interface NsDevtoolsAgent {
|
|
7
|
+
/**
|
|
8
|
+
* Dispatch a JSON-encoded {@link DevtoolsRequest} through the method
|
|
9
|
+
* registry, returning a JSON-encoded `DevtoolsResponse`. Always resolves —
|
|
10
|
+
* a malformed request or a handler failure becomes an error response. This
|
|
11
|
+
* is the single entry point the host bridge evaluates over CDP.
|
|
12
|
+
*/
|
|
13
|
+
dispatch(reqJson: string): Promise<string>;
|
|
14
|
+
/** The instance label this agent reports. */
|
|
15
|
+
readonly instance: string;
|
|
16
|
+
}
|
|
17
|
+
/** Options for {@link installDevtools}. */
|
|
18
|
+
export interface InstallNsDevtoolsOptions {
|
|
19
|
+
/** Force-enable regardless of `GJSIFY_DEVTOOLS` (e.g. a dev-only build). */
|
|
20
|
+
enabled?: boolean;
|
|
21
|
+
/** Per-instance label (multi-instance side-by-side). Default: `'default'`. */
|
|
22
|
+
instance?: string;
|
|
23
|
+
/** App id reported by `GetStatus`. Default: `'nativescript'`. */
|
|
24
|
+
appId?: string;
|
|
25
|
+
/** Predicate the pause guard consults; mutating methods reject when true. */
|
|
26
|
+
paused?: () => boolean;
|
|
27
|
+
/**
|
|
28
|
+
* The NS `Application` module (`getRootView()`). Pass it from the app
|
|
29
|
+
* (`import { Application } from '@nativescript/core'`) so this package needs
|
|
30
|
+
* no hard `@nativescript/core` dependency. Falls back to the global
|
|
31
|
+
* `NativeScriptGlobals`/`require` lookup when omitted.
|
|
32
|
+
*/
|
|
33
|
+
application?: NsApplicationLike;
|
|
34
|
+
/** The NS `Frame` module (`topmost()?.currentPage`). Same rationale as `application`. */
|
|
35
|
+
frame?: NsFrameLike;
|
|
36
|
+
/**
|
|
37
|
+
* App-specific devtools surfaces merged into the registry — extra methods +
|
|
38
|
+
* `GetStatus` keys (e.g. `buildStorybookDevtoolsExtension(controller)` so an
|
|
39
|
+
* MCP/CDP agent can ListStories/OpenStory/SetStoryArg against a running NS
|
|
40
|
+
* storybook). Backward-compatible — the spike showcase omits it.
|
|
41
|
+
*/
|
|
42
|
+
extensions?: readonly NsDevtoolsExtension[];
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Build the agent object around a method registry. Exposed for the unit tests
|
|
46
|
+
* (which drive `dispatch` directly against a mock view tree without touching
|
|
47
|
+
* `globalThis`).
|
|
48
|
+
*/
|
|
49
|
+
export declare function createAgent(registry: MethodRegistry, ctx: NsDevtoolsContext): NsDevtoolsAgent;
|
|
50
|
+
/**
|
|
51
|
+
* Install the in-app NativeScript devtools agent. A no-op returning `null`
|
|
52
|
+
* unless `GJSIFY_DEVTOOLS` is set (or `options.enabled` is true), so it is safe
|
|
53
|
+
* to leave in production code. Call once from the app's bootstrap (e.g. in
|
|
54
|
+
* `app.ts` after `Application.run({...})` is wired).
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```ts
|
|
58
|
+
* import { Application, Frame } from '@nativescript/core';
|
|
59
|
+
* import { installDevtools } from '@gjsify/devtools-nativescript';
|
|
60
|
+
* installDevtools({ application: Application, frame: Frame });
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
export declare function installDevtools(options?: InstallNsDevtoolsOptions): NsDevtoolsAgent | null;
|
|
64
|
+
/** Remove a previously-installed agent and release its scope root. */
|
|
65
|
+
export declare function uninstallDevtools(agent: NsDevtoolsAgent): void;
|
|
66
|
+
/** Whether the current runtime is NativeScript (re-exported convenience). */
|
|
67
|
+
export { isNativeScript };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { NsView } from './view-tree.js';
|
|
2
|
+
/**
|
|
3
|
+
* Capture a NativeScript view to a base64-encoded PNG string (no data-uri
|
|
4
|
+
* prefix). Returns `null` when the view has no backing native view yet or
|
|
5
|
+
* measures zero (not laid out), so the caller can surface a clear "not ready"
|
|
6
|
+
* error rather than a blank image.
|
|
7
|
+
*/
|
|
8
|
+
export declare function captureViewPng(view: NsView): string | null;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { NsView } from './view-tree.js';
|
|
2
|
+
/**
|
|
3
|
+
* Capture a NativeScript view to a base64 PNG. Platform-specific
|
|
4
|
+
* implementations live in `screenshot.android.ts` / `screenshot.ios.ts`; this
|
|
5
|
+
* base throws because no capture backend exists off-platform.
|
|
6
|
+
*/
|
|
7
|
+
export declare function captureViewPng(_view: NsView): string | null;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { NsView } from './view-tree.js';
|
|
2
|
+
/**
|
|
3
|
+
* Capture a NativeScript view to a base64-encoded PNG string (no data-uri
|
|
4
|
+
* prefix). Returns `null` when the view has no backing `UIView` or measures
|
|
5
|
+
* zero. UNTESTED — verify on a real iOS target before relying on it.
|
|
6
|
+
*/
|
|
7
|
+
export declare function captureViewPng(view: NsView): string | null;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import type { NodeInfo } from '@gjsify/devtools-protocol';
|
|
2
|
+
/**
|
|
3
|
+
* The structural subset of a NativeScript `View` the introspection needs. Kept
|
|
4
|
+
* minimal + duck-typed so the walker works against a real `@nativescript/core`
|
|
5
|
+
* View on-device AND a plain mock object in the unit tests. Every member is
|
|
6
|
+
* optional because custom views and layout containers expose different subsets.
|
|
7
|
+
*/
|
|
8
|
+
export interface NsView {
|
|
9
|
+
/** Registered component type name, e.g. `"StackLayout"`, `"Button"`. */
|
|
10
|
+
readonly typeName?: string;
|
|
11
|
+
/** NS automation id (the closest analogue to a DOM id / gtk widget name). */
|
|
12
|
+
automationText?: string;
|
|
13
|
+
id?: string;
|
|
14
|
+
/** Text-bearing views (Label / Button / TextField) expose `text`. */
|
|
15
|
+
text?: string;
|
|
16
|
+
/** CSS classes applied to the view. */
|
|
17
|
+
cssClasses?: Set<string> | string[];
|
|
18
|
+
className?: string;
|
|
19
|
+
/** Visibility — NS uses the `'visible' | 'collapse' | 'hidden'` enum. */
|
|
20
|
+
visibility?: string;
|
|
21
|
+
/** Whether the view is currently attached + laid out. */
|
|
22
|
+
isLoaded?: boolean;
|
|
23
|
+
getMeasuredWidth?(): number;
|
|
24
|
+
getMeasuredHeight?(): number;
|
|
25
|
+
/** Iterate the direct child views; the callback returns false to stop. */
|
|
26
|
+
eachChildView?(callback: (child: NsView) => boolean): void;
|
|
27
|
+
/** Index signature so {@link readViewProperty} can read arbitrary props. */
|
|
28
|
+
[key: string]: unknown;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* The NS application surface used to find the root view (duck-typed).
|
|
32
|
+
* `getRootView()` is typed as returning `unknown` (not `NsView`) so the real
|
|
33
|
+
* `@nativescript/core` `Application` — whose `getRootView(): View` lacks the
|
|
34
|
+
* internal `NsView` index signature — is assignable without a cast at the call
|
|
35
|
+
* site. {@link rootView} narrows the result to `NsView` for the walk.
|
|
36
|
+
*/
|
|
37
|
+
export interface NsApplicationLike {
|
|
38
|
+
getRootView?(): unknown;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The NS `Frame.topmost()` surface used to find the current page (duck-typed).
|
|
42
|
+
* `currentPage` is `unknown` for the same reason as {@link NsApplicationLike} —
|
|
43
|
+
* so the real `@nativescript/core` `Frame` module is assignable without a cast.
|
|
44
|
+
*/
|
|
45
|
+
export interface NsFrameLike {
|
|
46
|
+
topmost?(): {
|
|
47
|
+
currentPage?: unknown;
|
|
48
|
+
} | null | undefined;
|
|
49
|
+
}
|
|
50
|
+
/** A parsed view path: a toplevel index + a chain of child indices. */
|
|
51
|
+
export interface ParsedViewPath {
|
|
52
|
+
toplevel: number;
|
|
53
|
+
children: number[];
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Parse a stable view path like `toplevel:0/child:2/child:0` into its indices.
|
|
57
|
+
* Pure (no NS) so it is unit-testable on Node + GJS. Returns null for a
|
|
58
|
+
* malformed path. Byte-identical grammar to the GTK widget-tree paths so the
|
|
59
|
+
* same MCP `dump_tree` / `get_property` tooling addresses both runtimes.
|
|
60
|
+
*/
|
|
61
|
+
export declare function parseViewPath(path: string): ParsedViewPath | null;
|
|
62
|
+
/** Build a view path from a toplevel index + child-index chain. */
|
|
63
|
+
export declare function buildViewPath(toplevel: number, children: readonly number[]): string;
|
|
64
|
+
/** Best-effort registered type name of a NativeScript view. */
|
|
65
|
+
export declare function viewType(view: NsView): string;
|
|
66
|
+
/**
|
|
67
|
+
* Resolve the topmost root view of the running NS app. Prefers
|
|
68
|
+
* `Application.getRootView()`; falls back to `Frame.topmost()?.currentPage`.
|
|
69
|
+
* Returns null when neither is available (app not yet bootstrapped).
|
|
70
|
+
*/
|
|
71
|
+
export declare function rootView(app: NsApplicationLike | null, frame: NsFrameLike | null): NsView | null;
|
|
72
|
+
/**
|
|
73
|
+
* Enumerate live "toplevels". NativeScript has a single window with one root
|
|
74
|
+
* view, so this returns a one-element list (`toplevel:0`) describing the root —
|
|
75
|
+
* matching the GTK adapter's `ListToplevels` shape so the same MCP tool renders
|
|
76
|
+
* both runtimes.
|
|
77
|
+
*/
|
|
78
|
+
export declare function listToplevels(root: NsView | null): Array<{
|
|
79
|
+
path: string;
|
|
80
|
+
type: string;
|
|
81
|
+
title: string | null;
|
|
82
|
+
mapped: boolean;
|
|
83
|
+
focused: boolean;
|
|
84
|
+
}>;
|
|
85
|
+
/** Resolve a view path to its live view, or null if it no longer exists. */
|
|
86
|
+
export declare function resolveViewPath(root: NsView | null, path: string): NsView | null;
|
|
87
|
+
/** Dump a view subtree to {@link NodeInfo}, bounded by `maxDepth`. */
|
|
88
|
+
export declare function dumpTree(root: NsView, maxDepth: number, basePath: string): NodeInfo;
|
|
89
|
+
/**
|
|
90
|
+
* Read a view property by name, trying the original, snake_case and camelCase
|
|
91
|
+
* forms NativeScript exposes (NS uses camelCase JS properties). Returns a
|
|
92
|
+
* JSON-safe value (non-scalars become a `[type]` marker), or `undefined` when
|
|
93
|
+
* the property is absent.
|
|
94
|
+
*/
|
|
95
|
+
export declare function readViewProperty(view: NsView, prop: string): unknown;
|
package/package.json
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@gjsify/devtools-nativescript",
|
|
3
|
+
"version": "0.11.0",
|
|
4
|
+
"description": "In-app NativeScript devtools agent — exposes a running NS app's view tree + screenshots to the @gjsify/devtools-mcp bridge over the V8 CDP inspector (org.gjsify.Devtools method registry)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"module": "lib/esm/index.js",
|
|
7
|
+
"types": "lib/types/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./lib/types/index.d.ts",
|
|
11
|
+
"default": "./lib/esm/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"lib"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"clear": "rm -rf lib tsconfig.tsbuildinfo tsconfig.types.tsbuildinfo test.gjs.mjs test.node.mjs || exit 0",
|
|
19
|
+
"check": "gjsify tsc --noEmit",
|
|
20
|
+
"build": "gjsify run build:gjsify && gjsify run build:types",
|
|
21
|
+
"build:gjsify": "gjsify build --library 'src/**/*.{ts,js}' --exclude 'src/**/*.spec.{mts,ts}' 'src/test.{mts,ts}'",
|
|
22
|
+
"build:types": "gjsify tsc",
|
|
23
|
+
"build:test": "gjsify run build:test:gjs && gjsify run build:test:node",
|
|
24
|
+
"build:test:gjs": "gjsify build src/test.mts --app gjs --outfile test.gjs.mjs",
|
|
25
|
+
"build:test:node": "gjsify build src/test.mts --app node --outfile test.node.mjs",
|
|
26
|
+
"test": "gjsify run build:gjsify && gjsify run build:test && gjsify run test:node && gjsify run test:gjs",
|
|
27
|
+
"test:gjs": "gjsify run test.gjs.mjs",
|
|
28
|
+
"test:node": "node test.node.mjs"
|
|
29
|
+
},
|
|
30
|
+
"keywords": [
|
|
31
|
+
"gjs",
|
|
32
|
+
"nativescript",
|
|
33
|
+
"devtools",
|
|
34
|
+
"debug",
|
|
35
|
+
"mcp",
|
|
36
|
+
"cdp",
|
|
37
|
+
"android",
|
|
38
|
+
"ios"
|
|
39
|
+
],
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"@gjsify/devtools-protocol": "^0.11.0",
|
|
42
|
+
"@gjsify/native-platform": "^0.11.0"
|
|
43
|
+
},
|
|
44
|
+
"peerDependencies": {
|
|
45
|
+
"@nativescript/core": "*"
|
|
46
|
+
},
|
|
47
|
+
"peerDependenciesMeta": {
|
|
48
|
+
"@nativescript/core": {
|
|
49
|
+
"optional": true
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@gjsify/cli": "^0.11.0",
|
|
54
|
+
"@gjsify/unit": "^0.11.0",
|
|
55
|
+
"@nativescript/types": "~9.0.0",
|
|
56
|
+
"@types/node": "^25.9.2",
|
|
57
|
+
"typescript": "^6.0.3"
|
|
58
|
+
},
|
|
59
|
+
"gjsify": {
|
|
60
|
+
"runtimes": {
|
|
61
|
+
"gjs": "none",
|
|
62
|
+
"node": "none",
|
|
63
|
+
"browser": "none",
|
|
64
|
+
"nativescript": "native"
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
"license": "MIT",
|
|
68
|
+
"repository": {
|
|
69
|
+
"type": "git",
|
|
70
|
+
"url": "git+https://github.com/gjsify/gjsify.git",
|
|
71
|
+
"directory": "packages/nativescript-bridge/devtools"
|
|
72
|
+
},
|
|
73
|
+
"bugs": {
|
|
74
|
+
"url": "https://github.com/gjsify/gjsify/issues"
|
|
75
|
+
},
|
|
76
|
+
"homepage": "https://github.com/gjsify/gjsify/tree/main/packages/nativescript-bridge/devtools#readme"
|
|
77
|
+
}
|