@gjsify/devtools 0.49.0 → 0.51.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/README.md CHANGED
@@ -130,7 +130,7 @@ A peer call carries no destination bus name at all, so `nameHasOwner` / `listIns
130
130
  | `DumpTree` | `-> s` | the widget tree as JSON, with stable `toplevel:N/child:M` paths. |
131
131
  | `GetProperty` | `(path, prop) -> s` | read one property off the widget at `path`. |
132
132
  | `GetFocused` / `ListToplevels` | `-> s` | the focused widget's path; the toplevel list. |
133
- | `FindWidget` | `(selector) -> s` | first VISIBLE+mapped match for `Type`, `:css-class` or `Type:css-class`, depth-first from the active window; `''` when none. |
133
+ | `FindWidget` | `(selector) -> s` | first VISIBLE+mapped match for `Type`, `:css-class` or `Type:css-class`, depth-first from the active window; `''` when none. `Type` matches IS-A. |
134
134
  | `SendKey` | `(accel, path) -> b` | deliver a key to a widget's key controllers; an empty path means the FOCUSED widget. |
135
135
  | `ActivateWidget` | `(path) -> b` | click-drive: `gtk_widget_activate` for Button/Entry/Toggle, with a `GtkListBox` select-row / `row-activated` fallback for nav and preference rows. |
136
136
  | `GetStatus` | `-> s` | liveness + whatever extensions contribute. |
@@ -143,7 +143,7 @@ GActions are auto-bridged into the command registry (handling the `Adw.Applicati
143
143
 
144
144
  Three of those need their reason stated, because the obvious use is the wrong one:
145
145
 
146
- - **`FindWidget` exists because widget paths are POSITIONAL.** A `toplevel:0/child:3` written into a script is wrong the moment a widget is inserted above it, and every click-driving caller was re-walking `DumpTree` JSON in its own language to avoid that.
146
+ - **`FindWidget` exists because widget paths are POSITIONAL.** A `toplevel:0/child:3` written into a script is wrong the moment a widget is inserted above it, and every click-driving caller was re-walking `DumpTree` JSON in its own language to avoid that. **Its `Type` half matches IS-A**, because an application subclassing `Adw.Dialog` or `Gtk.Box` is the ordinary way to write a GTK app, and the exact-GType comparison excluded exactly that: a rig waiting for `AdwDialog` reported "opened no AdwDialog" for a dialog the same run's `DumpTree` showed mapped and visible (#1582). An EXACT type still wins over a subclass **within one search**, so every selector that resolved before the widening resolves to the same path after it. `g_type_is_a` also answers for an INTERFACE, so `GtkOrientable` matches every box and `GtkAccessible` matches the first visible widget in any tree — name a capability on purpose, not by accident. The same is-a reading holds `ActivateWidget`'s `GtkListBox` parent check and `SendKey`'s `GtkEventControllerKey` check — `widgetIsA` is the one helper all three call.
147
147
  - **`SendKey` is the half of headless driving `ActivateWidget` does not cover.** Nothing could be TYPED, so every `Gtk.EventControllerKey` handler was unverifiable. It emits `key-pressed` on the widget's own controllers rather than fabricating a `Gdk.Event` — GTK4 made events opaque with no public constructor — so it proves the HANDLER, not GDK's routing. Pair it with `GetProperty(path, "focusable")`: an unfocusable widget swallows every real key silently.
148
148
  - **`ActivateWidget` is the mutating counterpart of `GetProperty`.**
149
149
 
package/lib/esm/index.js CHANGED
@@ -1 +1 @@
1
- import{buildVariant as e,variantKindFor as t}from"./gvariant.js";import{activateAction as n,changeActionState as r,describeActions as i}from"./actions.js";import{dumpCss as a,removeCss as o,swapCss as s}from"./css.js";import{buildDevtoolsIfaceXml as c}from"./devtools-iface.js";import{DevtoolsPeerServerError as l,removeDevtoolsAddressFile as u,startDevtoolsPeerServer as d,writeDevtoolsAddressFile as f}from"./peer-transport.js";import{captureWidget as p,captureWidgetPng as m}from"./screenshot.js";import{dumpGSettings as h}from"./gsettings.js";import{buildWidgetPath as g,dumpTree as _,getWidgetProperty as v,listToplevels as y,parseWidgetPath as b,pathOfWidget as x,resolveWidgetPath as S,widgetType as C}from"./widget-tree.js";import{DevtoolsService as w}from"./devtools-service.js";import{chooseDevtoolsTransport as T,installDevtools as E,uninstallDevtools as D}from"./install.js";export*from"@gjsify/devtools-protocol";export{l as DevtoolsPeerServerError,w as DevtoolsService,n as activateAction,c as buildDevtoolsIfaceXml,e as buildVariant,g as buildWidgetPath,p as captureWidget,m as captureWidgetPng,r as changeActionState,T as chooseDevtoolsTransport,i as describeActions,a as dumpCss,h as dumpGSettings,_ as dumpTree,v as getWidgetProperty,E as installDevtools,y as listToplevels,b as parseWidgetPath,x as pathOfWidget,o as removeCss,u as removeDevtoolsAddressFile,S as resolveWidgetPath,d as startDevtoolsPeerServer,s as swapCss,D as uninstallDevtools,t as variantKindFor,C as widgetType,f as writeDevtoolsAddressFile};
1
+ import{buildVariant as e,variantKindFor as t}from"./gvariant.js";import{activateAction as n,changeActionState as r,describeActions as i}from"./actions.js";import{dumpCss as a,removeCss as o,swapCss as s}from"./css.js";import{buildDevtoolsIfaceXml as c}from"./devtools-iface.js";import{DevtoolsPeerServerError as l,removeDevtoolsAddressFile as u,startDevtoolsPeerServer as d,writeDevtoolsAddressFile as f}from"./peer-transport.js";import{captureWidget as p,captureWidgetPng as m}from"./screenshot.js";import{dumpGSettings as h}from"./gsettings.js";import{buildWidgetPath as g,dumpTree as _,getWidgetProperty as v,listToplevels as y,parseWidgetPath as b,pathOfWidget as x,resolveWidgetPath as S,widgetIsA as C,widgetType as w}from"./widget-tree.js";import{DevtoolsService as T}from"./devtools-service.js";import{chooseDevtoolsTransport as E,installDevtools as D,uninstallDevtools as O}from"./install.js";export*from"@gjsify/devtools-protocol";export{l as DevtoolsPeerServerError,T as DevtoolsService,n as activateAction,c as buildDevtoolsIfaceXml,e as buildVariant,g as buildWidgetPath,p as captureWidget,m as captureWidgetPng,r as changeActionState,E as chooseDevtoolsTransport,i as describeActions,a as dumpCss,h as dumpGSettings,_ as dumpTree,v as getWidgetProperty,D as installDevtools,y as listToplevels,b as parseWidgetPath,x as pathOfWidget,o as removeCss,u as removeDevtoolsAddressFile,S as resolveWidgetPath,d as startDevtoolsPeerServer,s as swapCss,O as uninstallDevtools,t as variantKindFor,C as widgetIsA,w as widgetType,f as writeDevtoolsAddressFile};
@@ -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}const t=40;function geometryOf(t){if(!t.get_mapped())return;let n=t.get_allocation(),r=n.width+t.get_margin_start()+t.get_margin_end(),i=n.height+t.get_margin_top()+t.get_margin_bottom(),[a,o]=t.measure(e.Orientation.HORIZONTAL,-1),s=r>0?Math.max(r,a):-1,[c,l]=t.measure(e.Orientation.VERTICAL,s),u={width:r,height:i,widthRequest:[a,o],heightRequest:[c,l]};return(r<a||i<c)&&(u.short=!0),u}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:[]},i=geometryOf(e);if(i!==void 0&&(r.geometry=i),t<=0)return e.get_first_child()&&(r.truncated=!0),r;let a=e.get_first_child(),o=0;for(;a;)r.children.push(dumpTree(a,t-1,`${n}/child:${o}`)),a=a.get_next_sibling(),o++;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};
1
+ import"./_virtual/_rolldown/runtime.js";import e from"gi://Gtk?version=4.0";import t from"gi://GObject?version=2.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 widgetIsA(e,n){let r=gtypeName(e);if(r===null||!n)return!1;if(r===n)return!0;let i=t.type_from_name(r),a=t.type_from_name(n);return!i||!a?!1:t.type_is_a(i,a)}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&&widgetIsA(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);widgetIsA(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){let r={path:null};return walkForSelector(e,t,n,r)??r.path}function walkForSelector(e,t,n,r){if(!e.get_visible()||!e.get_mapped())return null;if(!t.cssClass||e.get_css_classes().includes(t.cssClass)){if(!t.type||widgetType(e)===t.type)return n;r.path===null&&widgetIsA(e,t.type)&&(r.path=n)}let i=e.get_first_child(),a=0;for(;i;){let e=walkForSelector(i,t,`${n}/child:${a}`,r);if(e)return e;i=i.get_next_sibling(),a++}return null}const n=40;function geometryOf(t){if(!t.get_mapped())return;let n=t.get_allocation(),r=n.width+t.get_margin_start()+t.get_margin_end(),i=n.height+t.get_margin_top()+t.get_margin_bottom(),[a,o]=t.measure(e.Orientation.HORIZONTAL,-1),s=r>0?Math.max(r,a):-1,[c,l]=t.measure(e.Orientation.VERTICAL,s),u={width:r,height:i,widthRequest:[a,o],heightRequest:[c,l]};return(r<a||i<c)&&(u.short=!0),u}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:[]},i=geometryOf(e);if(i!==void 0&&(r.geometry=i),t<=0)return e.get_first_child()&&(r.truncated=!0),r;let a=e.get_first_child(),o=0;for(;a;)r.children.push(dumpTree(a,t-1,`${n}/child:${o}`)),a=a.get_next_sibling(),o++;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{n as DEFAULT_DUMP_DEPTH,activateWidget,buildWidgetPath,dumpTree,findWidgetPath,getWidgetProperty,listToplevels,parseWidgetPath,parseWidgetSelector,pathOfWidget,resolveWidgetPath,sendKeyToWidget,widgetIsA,widgetType};
@@ -135,6 +135,9 @@ export declare class DevtoolsService {
135
135
  * rather than an error for "no match": not finding a widget is an ordinary
136
136
  * answer a caller acts on (wait and retry, or report), not a fault.
137
137
  *
138
+ * The type half matches IS-A, so `AdwDialog` reaches an application's own
139
+ * `Adw.Dialog` subclass, and an INTERFACE name (`GtkOrientable`, `GtkEditable`) matches
140
+ * every implementor; an exact-type match is still preferred over any of them.
138
141
  * Invisible and unmapped subtrees are skipped — see {@link findWidgetPath}. */
139
142
  FindWidget(selector: string): string;
140
143
  /** `SendKey(accelerator, path) -> b` — deliver a key to the key controllers of the
@@ -9,7 +9,7 @@ export { buildVariant, variantKindFor } from './gvariant.js';
9
9
  export type { VariantKind } from './gvariant.js';
10
10
  export { activateAction, changeActionState, describeActions } from './actions.js';
11
11
  export { buildDevtoolsIfaceXml } from './devtools-iface.js';
12
- export { buildWidgetPath, dumpTree, getWidgetProperty, listToplevels, parseWidgetPath, pathOfWidget, resolveWidgetPath, widgetType, } from './widget-tree.js';
12
+ export { buildWidgetPath, dumpTree, getWidgetProperty, listToplevels, parseWidgetPath, pathOfWidget, resolveWidgetPath, widgetIsA, widgetType, } from './widget-tree.js';
13
13
  export { dumpCss, removeCss, swapCss } from './css.js';
14
14
  export { dumpGSettings } from './gsettings.js';
15
15
  export * from '@gjsify/devtools-protocol';
@@ -25,6 +25,32 @@ export declare function buildWidgetPath(toplevel: number, children: readonly num
25
25
  * `$typeName` — prefer it when present. (GJS has no `$typeName` on instances.)
26
26
  */
27
27
  export declare function widgetType(widget: Gtk.Widget): string;
28
+ /**
29
+ * Does `object`'s runtime GType derive from `typeName` — the type itself, an ancestor, or an
30
+ * INTERFACE it implements?
31
+ *
32
+ * The interface arm is `g_type_is_a`'s and is kept rather than filtered out: `GtkOrientable`,
33
+ * `GtkEditable` and `GtkAccessible` describe what a widget can DO, which is the same question
34
+ * a selector asks, and a rig author who tries one is not making a mistake. It does mean
35
+ * `GtkAccessible` and `GtkBuildable` match the first visible widget in any GTK tree — every
36
+ * widget implements both — so a selector that broad answers a widget rather than nothing.
37
+ *
38
+ * **The name comparison comes first and is the whole answer for a type GObject does not
39
+ * know.** That covers a unit-test shape and a runtime that never registered the type, and
40
+ * it is what makes every exact-name spec in this module still mean what it says.
41
+ *
42
+ * Where GObject does know both names, `g_type_is_a` answers. Registration is a RUNTIME
43
+ * fact and not a static one — measured under GJS 1.88.1, `GObject.type_from_name('AdwDialog')`
44
+ * answers null until something in JS touches `Adw.Dialog`, and registering a SUBCLASS
45
+ * registers its whole ancestor chain. So the lookup happens per call and is never cached:
46
+ * the base type of a live instance is always registered (the instance's class registered
47
+ * it), and a name that resolves to nothing today may resolve a moment later.
48
+ *
49
+ * Written against the NAME rather than `instanceof` for the same reason as
50
+ * {@link widgetType}: under node-gi a returned handle is a GENERIC wrapper, so
51
+ * `instanceof Gtk.ListBox` answers false for an object that is one.
52
+ */
53
+ export declare function widgetIsA(object: unknown, typeName: string): boolean;
28
54
  /**
29
55
  * Activate a widget — how external tooling click-drives a running GUI: resolve a
30
56
  * widget by path, then activate it. Two paths, tried in order:
@@ -107,6 +133,26 @@ export declare function parseWidgetSelector(selector: string): WidgetSelector |
107
133
  * split view — and activating one of those proves nothing about what a user can reach. Skipping the
108
134
  * subtree as well as the node matters: the children of an unmapped stack page are unmapped too, but
109
135
  * a caller checking only the leaf would happily match one.
136
+ *
137
+ * ## The type half matches IS-A, with the exact type preferred
138
+ *
139
+ * `Type` used to mean the exact GType name, which excludes the ordinary way GTK applications
140
+ * are written: three of bauplaner's six dialogs are `Adw.Dialog` SUBCLASSES, so a rig waiting
141
+ * for `AdwDialog` reported "opened no AdwDialog" for a dialog the tree dump showed mapped and
142
+ * visible (#1582). The failure reads like a defect in the consumer, and the workaround every
143
+ * consumer invents — matching a STYLE CLASS instead — addresses the widget by how it looks
144
+ * rather than by what it is.
145
+ *
146
+ * An EXACT match anywhere in THIS CALL's subtree still wins over a subclass match, even one
147
+ * earlier in reading order, and that ordering is the compatibility guarantee: every selector
148
+ * that resolved before this widening resolves to the SAME path after it, and the subclass leg
149
+ * is consulted only where the answer used to be none. It costs one full walk in the case that
150
+ * used to walk the tree anyway — the miss.
151
+ *
152
+ * PER CALL is the whole scope of that guarantee, and a caller sweeping several roots has to
153
+ * say so itself: `FindWidget` passes ONE root (the active window), but a consumer looping over
154
+ * `ListToplevels` gets a subclass hit in an early toplevel ahead of an exact hit in a later
155
+ * one. Collect the per-root answers and prefer the exact one, or pass the exact type.
110
156
  */
111
157
  export declare function findWidgetPath(root: Gtk.Widget, selector: WidgetSelector, basePath: string): string | null;
112
158
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gjsify/devtools",
3
- "version": "0.49.0",
3
+ "version": "0.51.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",
@@ -37,20 +37,20 @@
37
37
  "mcp"
38
38
  ],
39
39
  "dependencies": {
40
- "@girs/adw-1": "4.6.0",
41
- "@girs/gdk-4.0": "4.6.0",
42
- "@girs/gio-2.0": "4.6.0",
43
- "@girs/gjs": "4.6.0",
44
- "@girs/glib-2.0": "4.6.0",
45
- "@girs/gobject-2.0": "4.6.0",
46
- "@girs/graphene-1.0": "4.6.0",
47
- "@girs/gtk-4.0": "4.6.0",
48
- "@gjsify/devtools-protocol": "^0.49.0"
40
+ "@girs/adw-1": "5.0.0",
41
+ "@girs/gdk-4.0": "5.0.0",
42
+ "@girs/gio-2.0": "5.0.0",
43
+ "@girs/gjs": "5.0.0",
44
+ "@girs/glib-2.0": "5.0.0",
45
+ "@girs/gobject-2.0": "5.0.0",
46
+ "@girs/graphene-1.0": "5.0.0",
47
+ "@girs/gtk-4.0": "5.0.0",
48
+ "@gjsify/devtools-protocol": "^0.51.0"
49
49
  },
50
50
  "devDependencies": {
51
- "@gjsify/cli": "^0.49.0",
51
+ "@gjsify/cli": "^0.51.0",
52
52
  "@gjsify/node-gi": "file:../../node-gi/node-gi",
53
- "@gjsify/unit": "^0.49.0",
53
+ "@gjsify/unit": "^0.51.0",
54
54
  "@types/node": "^25.9.2",
55
55
  "typescript": "^6.0.3"
56
56
  },