@gjsify/devtools 0.46.0 → 0.48.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.
@@ -1 +1 @@
1
- import"./_virtual/_rolldown/runtime.js";import{activateAction as e,changeActionState as t,describeActions as n}from"./actions.js";import{dumpCss as r,swapCss as i}from"./css.js";import{buildDevtoolsIfaceXml as a}from"./devtools-iface.js";import{removeDevtoolsAddressFile as o}from"./peer-transport.js";import{captureWidgetPng as s}from"./screenshot.js";import{dumpGSettings as c}from"./gsettings.js";import{activateWidget as l,dumpTree as u,findWidgetPath as d,getWidgetProperty as f,listToplevels as p,parseWidgetSelector as m,pathOfWidget as h,resolveWidgetPath as g,sendKeyToWidget as _,widgetType as v}from"./widget-tree.js";import y from"gi://GLib?version=2.0";import b from"gi://Gtk?version=4.0";import{GENERIC_METHODS as x,formatDbusErrorMessage as S}from"@gjsify/devtools-protocol";import C from"gi://Gio?version=2.0";function isActiveWindowScope(e){return!e||e===`window`||e===`active`}function frameDelay(e){return new Promise(t=>{y.timeout_add(y.PRIORITY_DEFAULT,e,()=>(t(),y.SOURCE_REMOVE))})}async function captureWidgetWhenRenderable(e,t=12,n=50){for(let r=0;r<t;r++){let t=s(e);if(t)return t;await frameDelay(n)}return s(e)}var DevtoolsService=class{constructor(e,t){this._app=e,this._options=t,this._exported=new Map,this._peer=null,this._addressFile=null,this._kinds=new Map,this._extensions=t.extend??[];for(let[e,t]of Object.entries(x))this._kinds.set(e,t);for(let e of this._extensions){for(let[t,n]of Object.entries(e.methodKinds))this._kinds.set(t,n);for(let[t,n]of Object.entries(e.handlers)){let e=n;this[t]=(...n)=>(this._guard(t),e(...n))}}}export(e,t){if(this._exported.has(e))return;let n=a(this._extensions.flatMap(e=>e.methodsXml??[])),r=C.DBusExportedObject.wrapJSObject(n,this);r.export(e,t),this._exported.set(e,r)}unexport(e){if(e){this._exported.get(e)?.unexport(),this._exported.delete(e);return}for(let e of this._exported.values())e.unexport();this._exported.clear(),this._peer?.stop(),this._peer=null,this._addressFile&&=(o(this._addressFile),null)}attachPeerServer(e,t){this._peer=e,this._addressFile=t??null}get peerAddress(){return this._peer?.address??null}get addressFile(){return this._addressFile}GetStatus(){let e=this._app.get_active_window(),t={appId:this._app.get_application_id()??`unknown`,instance:this._options.instance??`default`,activeWindow:e?{id:e.get_name()||`window`,title:e.get_title()??``,mapped:e.get_mapped()}:null,toplevelCount:b.Window.get_toplevels().get_n_items(),focusedWidget:null,paused:this._isPaused()};for(let e of this._extensions){let n=e.contributeStatus?.();n&&Object.assign(t,n)}return JSON.stringify(t)}async ScreenshotAsync(e,t){try{let n=typeof e?.[0]==`string`?e[0]:``,r=await this._captureScopePng(n);t.return_value(new y.Variant(`(ay)`,[r]))}catch(e){t.return_dbus_error(`org.gjsify.Devtools.Error.Screenshot`,e instanceof Error?e.message:String(e))}}async _captureScopePng(e){let t=this._resolveRootWidget(e);if(!t){if(isActiveWindowScope(e))return new Uint8Array;throw Error(S(`not-found`,`no widget at '${e}'`))}let n=s(t.widget);if(!n){let e=t.widget.get_root();e instanceof b.Window&&e.present(),n=await captureWidgetWhenRenderable(t.widget)}return n??new Uint8Array}ListActions(){let e={app:n(this._actionGroup(`app`)),win:n(this._actionGroup(`win`))};return JSON.stringify(e)}ActivateAction(t,n,r){this._guard(`ActivateAction`);let i=this._scope(t);e(this._requireGroup(i,n),n,this._parseValue(r))}ChangeActionState(e,n,r){this._guard(`ChangeActionState`);let i=this._scope(e);t(this._requireGroup(i,n),n,this._parseValue(r))}PresentWindow(){this._app.get_active_window()?.present()}ResizeWindow(e,t){this._guard(`ResizeWindow`);let n=this._app.get_active_window();if(!n)throw Error(S(`unavailable`,`no active window to resize`));let r=Math.max(1,Math.round(e)),i=Math.max(1,Math.round(t));return n.is_maximized()&&n.unmaximize(),n.is_fullscreen()&&n.unfullscreen(),n.set_default_size(r,i),[r,i]}ListToplevels(){return JSON.stringify(p())}DumpTree(e,t){let n=this._resolveRootWidget(e);if(!n)throw Error(S(`not-found`,`no widget at '${e||`active window`}'`));return JSON.stringify(u(n.widget,t>0?t:8,n.path))}GetProperty(e,t){let n=this._resolveRootWidget(e);if(!n)throw Error(S(`not-found`,`no widget at '${e}'`));let r=f(n.widget,t);if(r===void 0)throw Error(S(`not-found`,`widget has no property '${t}'`));return JSON.stringify(r)}GetFocused(){let e=this._app.get_active_window(),t=e?e.get_focus():null;return JSON.stringify(t?{path:h(t),name:t.get_name()||null,type:v(t)}:null)}ActivateWidget(e){this._guard(`ActivateWidget`);let t=this._resolveRootWidget(e);if(!t)throw Error(S(`not-found`,`no widget at '${e}'`));return l(t.widget)}FindWidget(e){let t=m(e);if(!t)throw Error(S(`invalid-params`,`empty selector '${e}'`));let n=this._resolveRootWidget(``);return n?d(n.widget,t,n.path)??``:``}SendKey(e,t){this._guard(`SendKey`);let[n,r,i]=b.accelerator_parse(e);if(!n)throw Error(S(`invalid-params`,`cannot parse accelerator '${e}'`));let a;if(a=t?this._resolveRootWidget(t)?.widget??null:this._app.get_active_window()?.get_focus()??null,!a)throw Error(S(`not-found`,`no widget at '${t||`focus`}'`));return _(a,r,i??0)}DumpGSettings(e){return JSON.stringify(c(e))}DumpCss(){return JSON.stringify(r())}SwapCss(e,t){return this._guard(`SwapCss`),i(e,t)}_resolveRootWidget(e){if(isActiveWindowScope(e)){let e=this._app.get_active_window();return e?{widget:e,path:h(e)??`toplevel:0`}:null}let t=g(e);return t?{widget:t,path:e}:null}_isPaused(){return this._options.paused?.()??!1}_guard(e){let t=this._kinds.get(e);if(!t)throw Error(S(`internal`,`unclassified devtools method '${e}'`));if(t===`mutating`&&this._isPaused())throw Error(S(`paused`,`${e} rejected — external control is paused. Read-only methods keep working.`))}_scope(e){return e===`app`?`app`:`win`}_actionGroup(e){if(e===`app`)return this._app;if(this._options.winActionGroup)return this._options.winActionGroup;let t=this._app.get_active_window();return t instanceof b.ApplicationWindow?t:null}_requireGroup(e,t){let n=this._actionGroup(e);if(!n)throw Error(S(`unavailable`,`no '${e}' action group`));if(!n.has_action(t))throw Error(S(`not-found`,`unknown action ${e}.${t}`));return n}_parseValue(e){if(!e||e===`null`)return null;try{return JSON.parse(e)}catch{throw Error(S(`invalid-params`,`value_json is not valid JSON: ${e}`))}}};export{DevtoolsService};
1
+ import"./_virtual/_rolldown/runtime.js";import{activateAction as e,changeActionState as t,describeActions as n}from"./actions.js";import{dumpCss as r,swapCss as i}from"./css.js";import{buildDevtoolsIfaceXml as a}from"./devtools-iface.js";import{removeDevtoolsAddressFile as o}from"./peer-transport.js";import{captureWidgetPng as s}from"./screenshot.js";import{dumpGSettings as c}from"./gsettings.js";import{activateWidget as l,dumpTree as u,findWidgetPath as d,getWidgetProperty as f,listToplevels as p,parseWidgetSelector as m,pathOfWidget as h,resolveWidgetPath as g,sendKeyToWidget as _,widgetType as v}from"./widget-tree.js";import y from"gi://GLib?version=2.0";import b from"gi://Gtk?version=4.0";import{GENERIC_METHODS as x,formatDbusErrorMessage as S}from"@gjsify/devtools-protocol";import C from"gi://Gio?version=2.0";function isActiveWindowScope(e){return!e||e===`window`||e===`active`}function frameDelay(e){return new Promise(t=>{y.timeout_add(y.PRIORITY_DEFAULT,e,()=>(t(),y.SOURCE_REMOVE))})}async function captureWidgetWhenRenderable(e,t=12,n=50){for(let r=0;r<t;r++){let t=s(e);if(t)return t;await frameDelay(n)}return s(e)}var DevtoolsService=class{constructor(e,t){this._app=e,this._options=t,this._exported=new Map,this._peer=null,this._addressFile=null,this._kinds=new Map,this._extensions=t.extend??[];for(let[e,t]of Object.entries(x))this._kinds.set(e,t);for(let e of this._extensions){for(let[t,n]of Object.entries(e.methodKinds))this._kinds.set(t,n);for(let[t,n]of Object.entries(e.handlers)){let e=n;this[t]=(...n)=>(this._guard(t),e(...n))}}}export(e,t){if(this._exported.has(e))return;let n=a(this._extensions.flatMap(e=>e.methodsXml??[])),r=C.DBusExportedObject.wrapJSObject(n,this);r.export(e,t),this._exported.set(e,r)}unexport(e){if(e){this._exported.get(e)?.unexport(),this._exported.delete(e);return}for(let e of this._exported.values())e.unexport();this._exported.clear(),this._peer?.stop(),this._peer=null,this._addressFile&&=(o(this._addressFile),null)}attachPeerServer(e,t){this._peer=e,this._addressFile=t??null}get peerAddress(){return this._peer?.address??null}get addressFile(){return this._addressFile}GetStatus(){let e=this._app.get_active_window(),t={appId:this._app.get_application_id()??`unknown`,instance:this._options.instance??`default`,activeWindow:e?{id:e.get_name()||`window`,title:e.get_title()??``,mapped:e.get_mapped()}:null,toplevelCount:b.Window.get_toplevels().get_n_items(),focusedWidget:null,paused:this._isPaused()};for(let e of this._extensions){let n=e.contributeStatus?.();n&&Object.assign(t,n)}return JSON.stringify(t)}async ScreenshotAsync(e,t){try{let n=typeof e?.[0]==`string`?e[0]:``,r=await this._captureScopePng(n);t.return_value(new y.Variant(`(ay)`,[r]))}catch(e){t.return_dbus_error(`org.gjsify.Devtools.Error.Screenshot`,e instanceof Error?e.message:String(e))}}async _captureScopePng(e){let t=this._resolveRootWidget(e);if(!t){if(isActiveWindowScope(e))return new Uint8Array;throw Error(S(`not-found`,`no widget at '${e}'`))}let n=s(t.widget);if(!n){let e=t.widget.get_root();e instanceof b.Window&&e.present(),n=await captureWidgetWhenRenderable(t.widget)}return n??new Uint8Array}ListActions(){let e={app:n(this._actionGroup(`app`)),win:n(this._actionGroup(`win`))};return JSON.stringify(e)}ActivateAction(t,n,r){this._guard(`ActivateAction`);let i=this._scope(t);e(this._requireGroup(i,n),n,this._parseValue(r))}ChangeActionState(e,n,r){this._guard(`ChangeActionState`);let i=this._scope(e);t(this._requireGroup(i,n),n,this._parseValue(r))}PresentWindow(){this._app.get_active_window()?.present()}ResizeWindow(e,t){this._guard(`ResizeWindow`);let n=this._app.get_active_window();if(!n)throw Error(S(`unavailable`,`no active window to resize`));let r=Math.max(1,Math.round(e)),i=Math.max(1,Math.round(t));return n.is_maximized()&&n.unmaximize(),n.is_fullscreen()&&n.unfullscreen(),n.set_default_size(r,i),[r,i]}ListToplevels(){return JSON.stringify(p())}DumpTree(e,t){let n=this._resolveRootWidget(e);if(!n)throw Error(S(`not-found`,`no widget at '${e||`active window`}'`));return JSON.stringify(u(n.widget,t>0?t:40,n.path))}GetProperty(e,t){let n=this._resolveRootWidget(e);if(!n)throw Error(S(`not-found`,`no widget at '${e}'`));let r=f(n.widget,t);if(r===void 0)throw Error(S(`not-found`,`widget has no property '${t}'`));return JSON.stringify(r)}GetFocused(){let e=this._app.get_active_window(),t=e?e.get_focus():null;return JSON.stringify(t?{path:h(t),name:t.get_name()||null,type:v(t)}:null)}ActivateWidget(e){this._guard(`ActivateWidget`);let t=this._resolveRootWidget(e);if(!t)throw Error(S(`not-found`,`no widget at '${e}'`));return l(t.widget)}FindWidget(e){let t=m(e);if(!t)throw Error(S(`invalid-params`,`empty selector '${e}'`));let n=this._resolveRootWidget(``);return n?d(n.widget,t,n.path)??``:``}SendKey(e,t){this._guard(`SendKey`);let[n,r,i]=b.accelerator_parse(e);if(!n)throw Error(S(`invalid-params`,`cannot parse accelerator '${e}'`));let a;if(a=t?this._resolveRootWidget(t)?.widget??null:this._app.get_active_window()?.get_focus()??null,!a)throw Error(S(`not-found`,`no widget at '${t||`focus`}'`));return _(a,r,i??0)}DumpGSettings(e){return JSON.stringify(c(e))}DumpCss(){return JSON.stringify(r())}SwapCss(e,t){return this._guard(`SwapCss`),i(e,t)}_resolveRootWidget(e){if(isActiveWindowScope(e)){let e=this._app.get_active_window();return e?{widget:e,path:h(e)??`toplevel:0`}:null}let t=g(e);return t?{widget:t,path:e}:null}_isPaused(){return this._options.paused?.()??!1}_guard(e){let t=this._kinds.get(e);if(!t)throw Error(S(`internal`,`unclassified devtools method '${e}'`));if(t===`mutating`&&this._isPaused())throw Error(S(`paused`,`${e} rejected — external control is paused. Read-only methods keep working.`))}_scope(e){return e===`app`?`app`:`win`}_actionGroup(e){if(e===`app`)return this._app;if(this._options.winActionGroup)return this._options.winActionGroup;let t=this._app.get_active_window();return t instanceof b.ApplicationWindow?t:null}_requireGroup(e,t){let n=this._actionGroup(e);if(!n)throw Error(S(`unavailable`,`no '${e}' action group`));if(!n.has_action(t))throw Error(S(`not-found`,`unknown action ${e}.${t}`));return n}_parseValue(e){if(!e||e===`null`)return null;try{return JSON.parse(e)}catch{throw Error(S(`invalid-params`,`value_json is not valid JSON: ${e}`))}}};export{DevtoolsService};
@@ -1 +1 @@
1
- import"./_virtual/_rolldown/runtime.js";import e from"gi://Gtk?version=4.0";function parseWidgetPath(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 buildWidgetPath(e,t){return[`toplevel:${e}`,...t.map(e=>`child:${e}`)].join(`/`)}function widgetType(e){return gtypeName(e)??`GtkWidget`}function gtypeName(e){let t=e;return typeof t?.$typeName==`string`&&t.$typeName.length>0?t.$typeName:t?.constructor?.$gtype?.name??null}function activateWidget(e){let t=e;if(typeof t.activate==`function`&&t.activate()===!0)return!0;let n=typeof t.get_parent==`function`?t.get_parent():null;if(n&&widgetType(n)===`GtkListBox`){let t=n,r=!1;return typeof t.select_row==`function`&&(t.select_row(e),r=!0),typeof t.emit==`function`&&(t.emit(`row-activated`,e),r=!0),r}return!1}function sendKeyToWidget(e,t,n){let r=e.observe_controllers(),i=!1;for(let e=0;e<r.get_n_items();e++){let a=r.get_item(e);gtypeName(a)===`GtkEventControllerKey`&&(a.emit(`key-pressed`,t,0,n),i=!0)}return i}function nthChild(e,t){let n=e.get_first_child(),r=0;for(;n&&r<t;)n=n.get_next_sibling(),r++;return r===t?n:null}function listToplevels(){let t=e.Window.get_toplevels(),n=t.get_n_items(),r=[];for(let e=0;e<n;e++){let n=t.get_item(e);n&&r.push({path:`toplevel:${e}`,type:widgetType(n),title:n.get_title?.()??null,mapped:n.get_mapped(),focused:n.is_active??!1})}return r}function resolveWidgetPath(t){let n=parseWidgetPath(t);if(!n)return null;let r=e.Window.get_toplevels().get_item(n.toplevel);if(!r)return null;for(let e of n.children)if(r=nthChild(r,e),!r)return null;return r}function parseWidgetSelector(e){let t=e.indexOf(`:`),n=(t<0?e:e.slice(0,t)).trim(),r=(t<0?``:e.slice(t+1)).trim();return!n&&!r?null:{type:n,cssClass:r}}function findWidgetPath(e,t,n){if(!e.get_visible()||!e.get_mapped())return null;let r=!t.type||widgetType(e)===t.type,i=!t.cssClass||e.get_css_classes().includes(t.cssClass);if(r&&i)return n;let a=e.get_first_child(),o=0;for(;a;){let e=findWidgetPath(a,t,`${n}/child:${o}`);if(e)return e;a=a.get_next_sibling(),o++}return null}function dumpTree(e,t,n){let r={path:n,type:widgetType(e),name:e.get_name()||null,cssClasses:e.get_css_classes(),mapped:e.get_mapped(),visible:e.get_visible(),children:[]};if(t<=0)return r;let i=e.get_first_child(),a=0;for(;i;)r.children.push(dumpTree(i,t-1,`${n}/child:${a}`)),i=i.get_next_sibling(),a++;return r}function jsonSafe(e){let t=typeof e;return e==null||t===`string`||t===`number`||t===`boolean`?e??null:`[${t}]`}function getWidgetProperty(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])}function childIndex(e,t){let n=e.get_first_child(),r=0;for(;n;){if(n===t)return r;n=n.get_next_sibling(),r++}return-1}function pathOfWidget(t){let n=[],r=t,i=r.get_parent();for(;i;){let e=childIndex(i,r);if(e<0)return null;n.unshift(e),r=i,i=r.get_parent()}let a=e.Window.get_toplevels(),o=a.get_n_items();for(let e=0;e<o;e++)if(a.get_item(e)===r)return buildWidgetPath(e,n);return null}export{activateWidget,buildWidgetPath,dumpTree,findWidgetPath,getWidgetProperty,listToplevels,parseWidgetPath,parseWidgetSelector,pathOfWidget,resolveWidgetPath,sendKeyToWidget,widgetType};
1
+ import"./_virtual/_rolldown/runtime.js";import e from"gi://Gtk?version=4.0";function parseWidgetPath(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 buildWidgetPath(e,t){return[`toplevel:${e}`,...t.map(e=>`child:${e}`)].join(`/`)}function widgetType(e){return gtypeName(e)??`GtkWidget`}function gtypeName(e){let t=e;return typeof t?.$typeName==`string`&&t.$typeName.length>0?t.$typeName:t?.constructor?.$gtype?.name??null}function activateWidget(e){let t=e;if(typeof t.activate==`function`&&t.activate()===!0)return!0;let n=typeof t.get_parent==`function`?t.get_parent():null;if(n&&widgetType(n)===`GtkListBox`){let t=n,r=!1;return typeof t.select_row==`function`&&(t.select_row(e),r=!0),typeof t.emit==`function`&&(t.emit(`row-activated`,e),r=!0),r}return!1}function sendKeyToWidget(e,t,n){let r=e.observe_controllers(),i=!1;for(let e=0;e<r.get_n_items();e++){let a=r.get_item(e);gtypeName(a)===`GtkEventControllerKey`&&(a.emit(`key-pressed`,t,0,n),i=!0)}return i}function nthChild(e,t){let n=e.get_first_child(),r=0;for(;n&&r<t;)n=n.get_next_sibling(),r++;return r===t?n:null}function listToplevels(){let t=e.Window.get_toplevels(),n=t.get_n_items(),r=[];for(let e=0;e<n;e++){let n=t.get_item(e);n&&r.push({path:`toplevel:${e}`,type:widgetType(n),title:n.get_title?.()??null,mapped:n.get_mapped(),focused:n.is_active??!1})}return r}function resolveWidgetPath(t){let n=parseWidgetPath(t);if(!n)return null;let r=e.Window.get_toplevels().get_item(n.toplevel);if(!r)return null;for(let e of n.children)if(r=nthChild(r,e),!r)return null;return r}function parseWidgetSelector(e){let t=e.indexOf(`:`),n=(t<0?e:e.slice(0,t)).trim(),r=(t<0?``:e.slice(t+1)).trim();return!n&&!r?null:{type:n,cssClass:r}}function findWidgetPath(e,t,n){if(!e.get_visible()||!e.get_mapped())return null;let r=!t.type||widgetType(e)===t.type,i=!t.cssClass||e.get_css_classes().includes(t.cssClass);if(r&&i)return n;let a=e.get_first_child(),o=0;for(;a;){let e=findWidgetPath(a,t,`${n}/child:${o}`);if(e)return e;a=a.get_next_sibling(),o++}return null}const t=40;function dumpTree(e,t,n){let r={path:n,type:widgetType(e),name:e.get_name()||null,cssClasses:e.get_css_classes(),mapped:e.get_mapped(),visible:e.get_visible(),children:[]};if(t<=0)return e.get_first_child()&&(r.truncated=!0),r;let i=e.get_first_child(),a=0;for(;i;)r.children.push(dumpTree(i,t-1,`${n}/child:${a}`)),i=i.get_next_sibling(),a++;return r}function jsonSafe(e){let t=typeof e;return e==null||t===`string`||t===`number`||t===`boolean`?e??null:`[${t}]`}function getWidgetProperty(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])}function childIndex(e,t){let n=e.get_first_child(),r=0;for(;n;){if(n===t)return r;n=n.get_next_sibling(),r++}return-1}function pathOfWidget(t){let n=[],r=t,i=r.get_parent();for(;i;){let e=childIndex(i,r);if(e<0)return null;n.unshift(e),r=i,i=r.get_parent()}let a=e.Window.get_toplevels(),o=a.get_n_items();for(let e=0;e<o;e++)if(a.get_item(e)===r)return buildWidgetPath(e,n);return null}export{t as DEFAULT_DUMP_DEPTH,activateWidget,buildWidgetPath,dumpTree,findWidgetPath,getWidgetProperty,listToplevels,parseWidgetPath,parseWidgetSelector,pathOfWidget,resolveWidgetPath,sendKeyToWidget,widgetType};
@@ -94,7 +94,23 @@ export declare class DevtoolsService {
94
94
  ResizeWindow(width: number, height: number): [number, number];
95
95
  /** `ListToplevels() -> s` — JSON of live toplevel windows. */
96
96
  ListToplevels(): string;
97
- /** `DumpTree(root, depth) -> s` — JSON widget tree from `root` ('' = active window). */
97
+ /**
98
+ * `DumpTree(root, depth) -> s` — JSON widget tree from `root` ('' = active window).
99
+ *
100
+ * THE DEFAULT WAS 8 AND DID NOT REACH THE BOTTOM OF AN ORDINARY WINDOW.
101
+ * Measured on a routed application: the navigation page's own
102
+ * `AdwToolbarView` sits at level 7 and its `AdwHeaderBar` below that, so the
103
+ * default answered ZERO header bars for a window that drew one — and the
104
+ * existing devtools vectors' widgets sit at exactly 8, one nesting away from
105
+ * vanishing the same way (#1553).
106
+ *
107
+ * {@link DEFAULT_DUMP_DEPTH} is the new floor and the bound stays: this is the
108
+ * answer to "what did the application draw", read over D-Bus, and an unbounded
109
+ * walk of a deep tree is not free. What changed with it is that the bound now
110
+ * SAYS so — `truncated: true` on any node whose children were not walked — so
111
+ * a caller reading zero children can tell "nothing there" from "I stopped
112
+ * looking", which is the distinction a bigger number alone would not buy.
113
+ */
98
114
  DumpTree(root: string, depth: number): string;
99
115
  /** `GetProperty(path, prop) -> s` — JSON value of a widget's GObject property. */
100
116
  GetProperty(path: string, prop: string): string;
@@ -109,7 +109,26 @@ export declare function parseWidgetSelector(selector: string): WidgetSelector |
109
109
  * a caller checking only the leaf would happily match one.
110
110
  */
111
111
  export declare function findWidgetPath(root: Gtk.Widget, selector: WidgetSelector, basePath: string): string | null;
112
- /** Dump a widget subtree to {@link NodeInfo}, bounded by `maxDepth`. */
112
+ /**
113
+ * How deep `DumpTree` walks when the caller names no depth.
114
+ *
115
+ * 8 was the first guess and it was measured short: an ordinary Adwaita window
116
+ * puts an `AdwToolbarView` at level 7 and its `AdwHeaderBar` below that, so the
117
+ * default answered zero header bars for a window that drew one (#1553). 40 is
118
+ * chosen to clear a real application tree with room, not to be unbounded — the
119
+ * dump crosses D-Bus, and `truncated` is what makes the remaining bound honest
120
+ * rather than invisible.
121
+ */
122
+ export declare const DEFAULT_DUMP_DEPTH = 40;
123
+ /**
124
+ * Dump a widget subtree to {@link NodeInfo}, bounded by `maxDepth`.
125
+ *
126
+ * The bound LEAVES A TRACE, and that is the half worth stating: a node cut off
127
+ * here carries `truncated: true`, so a caller reading zero children knows which
128
+ * of the two zeros it got (#1553). A bound itself is right — an unbounded dump of
129
+ * a deep tree over D-Bus is not free — and it is only expensive when it is
130
+ * invisible in the answer.
131
+ */
113
132
  export declare function dumpTree(root: Gtk.Widget, maxDepth: number, basePath: string): NodeInfo;
114
133
  /**
115
134
  * Read a GObject property by name, trying the original, snake_case and
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gjsify/devtools",
3
- "version": "0.46.0",
3
+ "version": "0.48.0",
4
4
  "description": "In-app DBus devtools control plane for GJS/GTK apps — inspect, screenshot, and drive a running app over org.gjsify.Devtools (gdbus or the MCP bridge)",
5
5
  "type": "module",
6
6
  "module": "lib/esm/index.js",
@@ -45,12 +45,12 @@
45
45
  "@girs/gobject-2.0": "4.6.0",
46
46
  "@girs/graphene-1.0": "4.6.0",
47
47
  "@girs/gtk-4.0": "4.6.0",
48
- "@gjsify/devtools-protocol": "^0.46.0"
48
+ "@gjsify/devtools-protocol": "^0.48.0"
49
49
  },
50
50
  "devDependencies": {
51
- "@gjsify/cli": "^0.46.0",
51
+ "@gjsify/cli": "^0.48.0",
52
52
  "@gjsify/node-gi": "file:../../node-gi/node-gi",
53
- "@gjsify/unit": "^0.46.0",
53
+ "@gjsify/unit": "^0.48.0",
54
54
  "@types/node": "^25.9.2",
55
55
  "typescript": "^6.0.3"
56
56
  },