@gjsify/devtools 0.8.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/actions.js +1 -0
- package/lib/esm/css.js +1 -0
- package/lib/esm/devtools-iface.js +60 -0
- package/lib/esm/devtools-service.js +1 -0
- package/lib/esm/extension.js +0 -0
- package/lib/esm/gsettings.js +1 -0
- package/lib/esm/gvariant.js +1 -0
- package/lib/esm/index.js +1 -0
- package/lib/esm/install.js +1 -0
- package/lib/esm/screenshot.js +1 -0
- package/lib/esm/widget-tree.js +1 -0
- package/lib/types/actions.d.ts +14 -0
- package/lib/types/devtools-iface.d.ts +9 -0
- package/lib/types/devtools-service.d.ts +43 -0
- package/lib/types/extension.d.ts +36 -0
- package/lib/types/gvariant.d.ts +18 -0
- package/lib/types/gvariant.spec.d.ts +2 -0
- package/lib/types/index.d.ts +9 -0
- package/lib/types/install.d.ts +20 -0
- package/lib/types/screenshot.d.ts +12 -0
- package/package.json +72 -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{buildVariant as e}from"./gvariant.js";function describeActions(e){return e?e.list_actions().sort((e,t)=>e.localeCompare(t)).map(t=>({name:t,enabled:e.get_action_enabled(t),parameterType:e.get_action_parameter_type(t)?.dup_string()??null,stateType:e.get_action_state_type(t)?.dup_string()??null})):[]}function activateAction(t,n,r){let i=t.get_action_parameter_type(n),a=r==null?null:e(i,r);t.activate_action(n,a)}function changeActionState(t,n,r){t.change_action_state(n,e(t.get_action_state_type(n),r))}export{activateAction,changeActionState,describeActions};
|
package/lib/esm/css.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./_virtual/_rolldown/runtime.js";import e from"@girs/gtk-4.0";import t from"@girs/gdk-4.0";const n=new Map;function swapCss(r,i){let a=t.Display.get_default();if(!a)return!1;let o=n.get(r);return o||(o=new e.CssProvider,e.StyleContext.add_provider_for_display(a,o,e.STYLE_PROVIDER_PRIORITY_USER+1),n.set(r,o)),o.load_from_string(i),!0}function removeCss(r){let i=n.get(r);if(!i)return!1;let a=t.Display.get_default();return a&&e.StyleContext.remove_provider_for_display(a,i),n.delete(r),!0}function dumpCss(){return{providers:[...n.keys()]}}export{dumpCss,removeCss,swapCss};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import"./_virtual/_rolldown/runtime.js";import{DEVTOOLS_INTERFACE as e}from"@gjsify/devtools-protocol";function buildDevtoolsIfaceXml(t=[]){return`<node>
|
|
2
|
+
<interface name="${e}">
|
|
3
|
+
<method name="GetStatus">
|
|
4
|
+
<arg type="s" direction="out" name="status_json"/>
|
|
5
|
+
</method>
|
|
6
|
+
<method name="Screenshot">
|
|
7
|
+
<arg type="s" direction="in" name="scope"/>
|
|
8
|
+
<arg type="ay" direction="out" name="png_bytes"/>
|
|
9
|
+
</method>
|
|
10
|
+
<method name="ListActions">
|
|
11
|
+
<arg type="s" direction="out" name="actions_json"/>
|
|
12
|
+
</method>
|
|
13
|
+
<method name="ActivateAction">
|
|
14
|
+
<arg type="s" direction="in" name="scope"/>
|
|
15
|
+
<arg type="s" direction="in" name="name"/>
|
|
16
|
+
<arg type="s" direction="in" name="value_json"/>
|
|
17
|
+
</method>
|
|
18
|
+
<method name="ChangeActionState">
|
|
19
|
+
<arg type="s" direction="in" name="scope"/>
|
|
20
|
+
<arg type="s" direction="in" name="name"/>
|
|
21
|
+
<arg type="s" direction="in" name="value_json"/>
|
|
22
|
+
</method>
|
|
23
|
+
<method name="PresentWindow"/>
|
|
24
|
+
<method name="ResizeWindow">
|
|
25
|
+
<arg type="i" direction="in" name="width"/>
|
|
26
|
+
<arg type="i" direction="in" name="height"/>
|
|
27
|
+
<arg type="i" direction="out" name="result_width"/>
|
|
28
|
+
<arg type="i" direction="out" name="result_height"/>
|
|
29
|
+
</method>
|
|
30
|
+
<method name="ListToplevels">
|
|
31
|
+
<arg type="s" direction="out" name="toplevels_json"/>
|
|
32
|
+
</method>
|
|
33
|
+
<method name="DumpTree">
|
|
34
|
+
<arg type="s" direction="in" name="root"/>
|
|
35
|
+
<arg type="i" direction="in" name="depth"/>
|
|
36
|
+
<arg type="s" direction="out" name="tree_json"/>
|
|
37
|
+
</method>
|
|
38
|
+
<method name="GetProperty">
|
|
39
|
+
<arg type="s" direction="in" name="path"/>
|
|
40
|
+
<arg type="s" direction="in" name="prop"/>
|
|
41
|
+
<arg type="s" direction="out" name="value_json"/>
|
|
42
|
+
</method>
|
|
43
|
+
<method name="GetFocused">
|
|
44
|
+
<arg type="s" direction="out" name="focused_json"/>
|
|
45
|
+
</method>
|
|
46
|
+
<method name="DumpGSettings">
|
|
47
|
+
<arg type="s" direction="in" name="schema_id"/>
|
|
48
|
+
<arg type="s" direction="out" name="settings_json"/>
|
|
49
|
+
</method>
|
|
50
|
+
<method name="DumpCss">
|
|
51
|
+
<arg type="s" direction="out" name="css_json"/>
|
|
52
|
+
</method>
|
|
53
|
+
<method name="SwapCss">
|
|
54
|
+
<arg type="s" direction="in" name="name"/>
|
|
55
|
+
<arg type="s" direction="in" name="css"/>
|
|
56
|
+
<arg type="b" direction="out" name="applied"/>
|
|
57
|
+
</method>${t.length>0?`\n${t.join(`
|
|
58
|
+
`)}`:``}
|
|
59
|
+
</interface>
|
|
60
|
+
</node>`}export{buildDevtoolsIfaceXml};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./_virtual/_rolldown/runtime.js";import{captureWidgetPng as e}from"./screenshot.js";import{buildDevtoolsIfaceXml as t}from"./devtools-iface.js";import{dumpCss as n,swapCss as r}from"./css.js";import{activateAction as i,changeActionState as a,describeActions as o}from"./actions.js";import{dumpGSettings as s}from"./gsettings.js";import{dumpTree as c,getWidgetProperty as l,listToplevels as u,pathOfWidget as d,resolveWidgetPath as f,widgetType as p}from"./widget-tree.js";import m from"@girs/gtk-4.0";import{GENERIC_METHODS as h,formatDbusErrorMessage as g}from"@gjsify/devtools-protocol";import _ from"@girs/gio-2.0";var DevtoolsService=class{constructor(e,t){this._app=e,this._options=t,this._exported=null,this._kinds=new Map,this._extensions=t.extend??[];for(let[e,t]of Object.entries(h))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,n){if(this._exported)return;let r=t(this._extensions.flatMap(e=>e.methodsXml??[])),i=_.DBusExportedObject.wrapJSObject(r,this);i.export(e,n),this._exported=i}unexport(){this._exported?.unexport(),this._exported=null}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:m.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 Screenshot(t){let n=this._app.get_active_window();return n?e(n)??new Uint8Array:new Uint8Array}ListActions(){let e={app:o(this._actionGroup(`app`)),win:o(this._actionGroup(`win`))};return JSON.stringify(e)}ActivateAction(e,t,n){this._guard(`ActivateAction`);let r=this._scope(e);i(this._requireGroup(r,t),t,this._parseValue(n))}ChangeActionState(e,t,n){this._guard(`ChangeActionState`);let r=this._scope(e);a(this._requireGroup(r,t),t,this._parseValue(n))}PresentWindow(){this._app.get_active_window()?.present()}ResizeWindow(e,t){this._guard(`ResizeWindow`);let n=this._app.get_active_window();if(!n)throw Error(g(`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(u())}DumpTree(e,t){let n=this._resolveRootWidget(e);if(!n)throw Error(g(`not-found`,`no widget at '${e||`active window`}'`));return JSON.stringify(c(n.widget,t>0?t:8,n.path))}GetProperty(e,t){let n=this._resolveRootWidget(e);if(!n)throw Error(g(`not-found`,`no widget at '${e}'`));let r=l(n.widget,t);if(r===void 0)throw Error(g(`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:d(t),name:t.get_name()||null,type:p(t)}:null)}DumpGSettings(e){return JSON.stringify(s(e))}DumpCss(){return JSON.stringify(n())}SwapCss(e,t){return this._guard(`SwapCss`),r(e,t)}_resolveRootWidget(e){if(!e||e===`window`||e===`active`){let e=this._app.get_active_window();return e?{widget:e,path:d(e)??`toplevel:0`}:null}let t=f(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(g(`internal`,`unclassified devtools method '${e}'`));if(t===`mutating`&&this._isPaused())throw Error(g(`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 m.ApplicationWindow?t:null}_requireGroup(e,t){let n=this._actionGroup(e);if(!n)throw Error(g(`unavailable`,`no '${e}' action group`));if(!n.has_action(t))throw Error(g(`not-found`,`unknown action ${e}.${t}`));return n}_parseValue(e){if(!e||e===`null`)return null;try{return JSON.parse(e)}catch{throw Error(g(`invalid-params`,`value_json is not valid JSON: ${e}`))}}};export{DevtoolsService};
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./_virtual/_rolldown/runtime.js";import{formatDbusErrorMessage as e}from"@gjsify/devtools-protocol";import t from"@girs/gio-2.0";function dumpGSettings(n){let r=t.SettingsSchemaSource.get_default()?.lookup(n,!0);if(!r)throw Error(e(`not-found`,`GSettings schema '${n}' is not installed`));let i=new t.Settings({settings_schema:r}),a={};for(let e of r.list_keys())a[e]=i.get_value(e).recursiveUnpack();return a}export{dumpGSettings};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./_virtual/_rolldown/runtime.js";import e from"@girs/glib-2.0";function variantKindFor(e,t){switch(e){case`s`:case`b`:case`i`:case`u`:case`d`:return e}if(typeof t==`string`)return`s`;if(typeof t==`boolean`)return`b`;if(typeof t==`number`)return Number.isInteger(t)?`i`:`d`;throw Error(`Cannot build a GLib.Variant from ${typeof t}`)}function buildVariant(t,n){switch(variantKindFor(t?.dup_string()??null,n)){case`s`:return e.Variant.new_string(String(n));case`b`:return e.Variant.new_boolean(!!n);case`i`:return e.Variant.new_int32(Number(n));case`u`:return e.Variant.new_uint32(Number(n));case`d`:return e.Variant.new_double(Number(n))}}export{buildVariant,variantKindFor};
|
package/lib/esm/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{captureWidgetPng as e}from"./screenshot.js";import{buildDevtoolsIfaceXml as t}from"./devtools-iface.js";import{buildVariant as n,variantKindFor as r}from"./gvariant.js";import{dumpCss as i,removeCss as a,swapCss as o}from"./css.js";import{activateAction as s,changeActionState as c,describeActions as l}from"./actions.js";import{dumpGSettings as u}from"./gsettings.js";import{buildWidgetPath as d,dumpTree as f,getWidgetProperty as p,listToplevels as m,parseWidgetPath as h,pathOfWidget as g,resolveWidgetPath as _,widgetType as v}from"./widget-tree.js";import{DevtoolsService as y}from"./devtools-service.js";import{installDevtools as b,uninstallDevtools as x}from"./install.js";export*from"@gjsify/devtools-protocol";export{y as DevtoolsService,s as activateAction,t as buildDevtoolsIfaceXml,n as buildVariant,d as buildWidgetPath,e as captureWidgetPng,c as changeActionState,l as describeActions,i as dumpCss,u as dumpGSettings,f as dumpTree,p as getWidgetProperty,b as installDevtools,m as listToplevels,h as parseWidgetPath,g as pathOfWidget,a as removeCss,_ as resolveWidgetPath,o as swapCss,x as uninstallDevtools,r as variantKindFor,v as widgetType};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./_virtual/_rolldown/runtime.js";import{DevtoolsService as e}from"./devtools-service.js";import t from"@girs/glib-2.0";const n=new Set;function envEnabled(){let e=t.getenv(`GJSIFY_DEVTOOLS`);if(e==null)return!1;let n=e.toLowerCase();return n!==``&&n!==`0`&&n!==`false`}function envInstance(){let e=t.getenv(`GJSIFY_DEVTOOLS_INSTANCE`);return e&&e!==``?e:void 0}function installDevtools(t,r={}){if(!(r.enabled??envEnabled()))return null;let i=new e(t,{...r,instance:r.instance??envInstance()}),a=t.get_dbus_connection(),o=t.get_dbus_object_path();return a&&o?i.export(a,`${o}/devtools`):console.error(`[gjsify-devtools] no session-bus connection yet — call installDevtools() from the GtkApplication "startup" handler`),n.add(i),i}function uninstallDevtools(e){e.unexport(),n.delete(e)}export{installDevtools,uninstallDevtools};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./_virtual/_rolldown/runtime.js";import e from"@girs/graphene-1.0";import t from"@girs/gtk-4.0";function captureWidgetPng(n){let r=n.get_native()?.get_renderer();if(!r)return null;let i=n.get_width(),a=n.get_height();if(i<=0||a<=0)return null;let o=t.WidgetPaintable.new(n),s=t.Snapshot.new();o.snapshot(s,i,a);let c=s.to_node();if(!c)return null;let l=new e.Rect;l.init(0,0,i,a);let u=r.render_texture(c,l).save_to_png_bytes().get_data();return u?new Uint8Array(u):null}export{captureWidgetPng};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./_virtual/_rolldown/runtime.js";import e from"@girs/gtk-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 e.constructor?.$gtype?.name??`GtkWidget`}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 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{buildWidgetPath,dumpTree,getWidgetProperty,listToplevels,parseWidgetPath,pathOfWidget,resolveWidgetPath,widgetType};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type Gio from '@girs/gio-2.0';
|
|
2
|
+
import type { ActionDescriptor } from '@gjsify/devtools-protocol';
|
|
3
|
+
/** Describe every action in a group (name, enabled, parameter/state types), sorted by name. */
|
|
4
|
+
export declare function describeActions(group: Gio.ActionGroup | null): ActionDescriptor[];
|
|
5
|
+
/**
|
|
6
|
+
* Activate an action with an optional parameter built from the action's
|
|
7
|
+
* declared parameter type (so a plain JS value is enough).
|
|
8
|
+
*/
|
|
9
|
+
export declare function activateAction(group: Gio.ActionGroup, name: string, value: unknown): void;
|
|
10
|
+
/**
|
|
11
|
+
* Set a stateful action's state — used for idempotent toggles where plain
|
|
12
|
+
* activation would only flip the current value.
|
|
13
|
+
*/
|
|
14
|
+
export declare function changeActionState(group: Gio.ActionGroup, name: string, value: unknown): void;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Well-known interface name implemented on each app's own bus name + object path. */
|
|
2
|
+
export declare const DEVTOOLS_INTERFACE = "org.gjsify.Devtools";
|
|
3
|
+
/**
|
|
4
|
+
* Assemble the full `org.gjsify.Devtools` introspection node XML, merging any
|
|
5
|
+
* app-specific `<method>…</method>` fragments contributed by extensions into
|
|
6
|
+
* the single interface — so app-specific methods are driveable over the same
|
|
7
|
+
* bus object as the generic ones.
|
|
8
|
+
*/
|
|
9
|
+
export declare function buildDevtoolsIfaceXml(extraMethodsXml?: readonly string[]): string;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import Gio from '@girs/gio-2.0';
|
|
2
|
+
import Gtk from '@girs/gtk-4.0';
|
|
3
|
+
import type { InstallDevtoolsOptions } from './extension.js';
|
|
4
|
+
/**
|
|
5
|
+
* The permanent `org.gjsify.Devtools` DBus interface for a GTK app — lets
|
|
6
|
+
* external tooling (the MCP bridge, `gdbus`, scripts) inspect, screenshot,
|
|
7
|
+
* and drive the running app. Generic methods are implemented here; app
|
|
8
|
+
* extensions contribute extra methods that are attached (pause-guarded) and
|
|
9
|
+
* merged into the same interface. Resolves the live window lazily per call so
|
|
10
|
+
* it survives window recreation.
|
|
11
|
+
*/
|
|
12
|
+
export declare class DevtoolsService {
|
|
13
|
+
private readonly _app;
|
|
14
|
+
private readonly _options;
|
|
15
|
+
private _exported;
|
|
16
|
+
private readonly _extensions;
|
|
17
|
+
private readonly _kinds;
|
|
18
|
+
constructor(_app: Gtk.Application, _options: InstallDevtoolsOptions);
|
|
19
|
+
/** Export the interface at `objectPath` on `connection`. Idempotent. */
|
|
20
|
+
export(connection: Gio.DBusConnection, objectPath: string): void;
|
|
21
|
+
/** Tear the interface down. Idempotent. */
|
|
22
|
+
unexport(): void;
|
|
23
|
+
/** `GetStatus() -> s` — JSON snapshot of the app's live state. */
|
|
24
|
+
GetStatus(): string;
|
|
25
|
+
/** `Screenshot(scope) -> ay` — PNG bytes of the active window via the GSK renderer. */
|
|
26
|
+
Screenshot(_scope: string): Promise<Uint8Array>;
|
|
27
|
+
/** `ListActions() -> s` — JSON of the `app.*` + `win.*` actions. */
|
|
28
|
+
ListActions(): string;
|
|
29
|
+
/** `ActivateAction(scope, name, value_json)` — activate a GAction with an optional parameter. */
|
|
30
|
+
ActivateAction(scope: string, name: string, valueJson: string): void;
|
|
31
|
+
/** `ChangeActionState(scope, name, value_json)` — set a stateful action's state. */
|
|
32
|
+
ChangeActionState(scope: string, name: string, valueJson: string): void;
|
|
33
|
+
/** `PresentWindow()` — raise the active window (needed by the screenshot retry path). */
|
|
34
|
+
PresentWindow(): void;
|
|
35
|
+
/** `ResizeWindow(w, h) -> (w, h)` — resize the active window; returns the requested size. */
|
|
36
|
+
ResizeWindow(width: number, height: number): [number, number];
|
|
37
|
+
private _isPaused;
|
|
38
|
+
private _guard;
|
|
39
|
+
private _scope;
|
|
40
|
+
private _actionGroup;
|
|
41
|
+
private _requireGroup;
|
|
42
|
+
private _parseValue;
|
|
43
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type Gio from '@girs/gio-2.0';
|
|
2
|
+
import type { MethodKind } from '@gjsify/devtools-protocol';
|
|
3
|
+
/**
|
|
4
|
+
* App-specific extension of the devtools control plane. The host contributes
|
|
5
|
+
* extra DBus methods (e.g. the map-editor's PaintTile) without forking the
|
|
6
|
+
* generic core: the `<method>` XML is merged into `org.gjsify.Devtools`, the
|
|
7
|
+
* handlers are attached as concrete methods (positional DBus args), and each
|
|
8
|
+
* carries a pause classification the guard enforces.
|
|
9
|
+
*/
|
|
10
|
+
export interface DevtoolsExtension {
|
|
11
|
+
/** `<method>…</method>` XML fragments merged into the interface. */
|
|
12
|
+
readonly methodsXml?: readonly string[];
|
|
13
|
+
/** Concrete DBus method implementations, keyed by the XML method name. Receive positional args. */
|
|
14
|
+
readonly handlers: Record<string, (...args: never[]) => unknown>;
|
|
15
|
+
/** Pause classification for each handler (the guard rejects an unclassified name). */
|
|
16
|
+
readonly methodKinds: Record<string, MethodKind>;
|
|
17
|
+
/** Extra keys merged into `GetStatus()`. */
|
|
18
|
+
readonly contributeStatus?: () => Record<string, unknown>;
|
|
19
|
+
}
|
|
20
|
+
/** Options for {@link installDevtools}. */
|
|
21
|
+
export interface InstallDevtoolsOptions {
|
|
22
|
+
/** Force-enable regardless of `GJSIFY_DEVTOOLS` (e.g. a dev-only build). Default: env-gated. */
|
|
23
|
+
enabled?: boolean;
|
|
24
|
+
/** Per-instance label (multi-instance side-by-side). Default: the `GJSIFY_DEVTOOLS_INSTANCE` env var. */
|
|
25
|
+
instance?: string;
|
|
26
|
+
/** App-specific extensions. */
|
|
27
|
+
extend?: readonly DevtoolsExtension[];
|
|
28
|
+
/**
|
|
29
|
+
* Action group for `win.*` when the active window does not implement
|
|
30
|
+
* `Gio.ActionGroup` itself — i.e. `Adw.ApplicationWindow`, which (unlike
|
|
31
|
+
* `Gtk.ApplicationWindow`) does not expose its `win.*` actions as a group.
|
|
32
|
+
*/
|
|
33
|
+
winActionGroup?: Gio.ActionGroup;
|
|
34
|
+
/** Predicate the pause guard consults; mutating methods are rejected when it returns true. */
|
|
35
|
+
paused?: () => boolean;
|
|
36
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import GLib from '@girs/glib-2.0';
|
|
2
|
+
/** The `GLib.Variant` scalar kinds the action plane marshals to. */
|
|
3
|
+
export type VariantKind = 's' | 'b' | 'i' | 'u' | 'd';
|
|
4
|
+
/**
|
|
5
|
+
* Decide which `GLib.Variant` scalar kind to build for an action
|
|
6
|
+
* parameter/state: the action's declared variant type string when it's a
|
|
7
|
+
* known scalar, otherwise inferred from the JS runtime type (string → `s`,
|
|
8
|
+
* boolean → `b`, integer → `i`, non-integer number → `d`). Throws when
|
|
9
|
+
* neither a known declared type nor an inferable JS type applies. Pure (no
|
|
10
|
+
* GLib) so the marshalling decision is unit-testable on node + gjs.
|
|
11
|
+
*/
|
|
12
|
+
export declare function variantKindFor(declaredType: string | null, value: unknown): VariantKind;
|
|
13
|
+
/**
|
|
14
|
+
* Build a `GLib.Variant` for an action parameter/state from a plain JS value,
|
|
15
|
+
* using the action's declared type when known and otherwise inferring from
|
|
16
|
+
* the JS runtime type (see {@link variantKindFor}).
|
|
17
|
+
*/
|
|
18
|
+
export declare function buildVariant(type: GLib.VariantType | null, value: unknown): GLib.Variant;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { installDevtools, uninstallDevtools } from './install.js';
|
|
2
|
+
export { DevtoolsService } from './devtools-service.js';
|
|
3
|
+
export type { DevtoolsExtension, InstallDevtoolsOptions } from './extension.js';
|
|
4
|
+
export { captureWidgetPng } from './screenshot.js';
|
|
5
|
+
export { buildVariant, variantKindFor } from './gvariant.js';
|
|
6
|
+
export type { VariantKind } from './gvariant.js';
|
|
7
|
+
export { activateAction, changeActionState, describeActions } from './actions.js';
|
|
8
|
+
export { buildDevtoolsIfaceXml, DEVTOOLS_INTERFACE } from './devtools-iface.js';
|
|
9
|
+
export * from '@gjsify/devtools-protocol';
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type Gtk from '@girs/gtk-4.0';
|
|
2
|
+
import { DevtoolsService } from './devtools-service.js';
|
|
3
|
+
import type { InstallDevtoolsOptions } from './extension.js';
|
|
4
|
+
/**
|
|
5
|
+
* Install the devtools control plane on a GTK application. A no-op returning
|
|
6
|
+
* `null` unless `GJSIFY_DEVTOOLS` is set (or `options.enabled` is true), so it
|
|
7
|
+
* is safe to leave in production code. Call from the application's `startup`
|
|
8
|
+
* handler — the session-bus connection + object path are only available after
|
|
9
|
+
* the application has registered.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* this.connect('startup', () => {
|
|
14
|
+
* installDevtools(this, { extend: [myAppDevtoolsExtension()] });
|
|
15
|
+
* });
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export declare function installDevtools(app: Gtk.Application, options?: InstallDevtoolsOptions): DevtoolsService | null;
|
|
19
|
+
/** Stop exposing a previously-installed service and release its GC root. */
|
|
20
|
+
export declare function uninstallDevtools(service: DevtoolsService): void;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import Gtk from '@girs/gtk-4.0';
|
|
2
|
+
/**
|
|
3
|
+
* Render a GTK widget — typically the top-level window — to PNG bytes, fully
|
|
4
|
+
* in-process via the GSK renderer. No external screenshot tools, no
|
|
5
|
+
* compositor portal: the widget's own `Gsk.Renderer` rasterises a
|
|
6
|
+
* `Gtk.WidgetPaintable` snapshot to a `Gdk.Texture`, serialised to PNG.
|
|
7
|
+
*
|
|
8
|
+
* Returns `null` when the widget isn't realised yet (no renderer or zero
|
|
9
|
+
* size) so callers can surface a clear "not ready" error instead of an empty
|
|
10
|
+
* image.
|
|
11
|
+
*/
|
|
12
|
+
export declare function captureWidgetPng(widget: Gtk.Widget): Uint8Array | null;
|
package/package.json
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@gjsify/devtools",
|
|
3
|
+
"version": "0.8.0",
|
|
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
|
+
"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 tmp tsconfig.tsbuildinfo test.gjs.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",
|
|
24
|
+
"build:test:gjs": "gjsify build src/test.mts --app gjs --outfile test.gjs.mjs",
|
|
25
|
+
"test": "gjsify run build:gjsify && gjsify run build:test && gjsify run test:gjs",
|
|
26
|
+
"test:gjs": "gjsify run test.gjs.mjs"
|
|
27
|
+
},
|
|
28
|
+
"keywords": [
|
|
29
|
+
"gjs",
|
|
30
|
+
"gtk",
|
|
31
|
+
"adwaita",
|
|
32
|
+
"devtools",
|
|
33
|
+
"debug",
|
|
34
|
+
"dbus",
|
|
35
|
+
"mcp"
|
|
36
|
+
],
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"@girs/adw-1": "1.10.0-4.0.4",
|
|
39
|
+
"@girs/gdk-4.0": "4.0.0-4.0.4",
|
|
40
|
+
"@girs/gio-2.0": "2.88.0-4.0.4",
|
|
41
|
+
"@girs/gjs": "4.0.4",
|
|
42
|
+
"@girs/glib-2.0": "2.88.0-4.0.4",
|
|
43
|
+
"@girs/gobject-2.0": "2.88.0-4.0.4",
|
|
44
|
+
"@girs/graphene-1.0": "1.0.0-4.0.4",
|
|
45
|
+
"@girs/gtk-4.0": "4.23.0-4.0.4",
|
|
46
|
+
"@gjsify/devtools-protocol": "^0.8.0"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@gjsify/cli": "^0.8.0",
|
|
50
|
+
"@gjsify/unit": "^0.8.0",
|
|
51
|
+
"@types/node": "^25.9.2",
|
|
52
|
+
"typescript": "^6.0.3"
|
|
53
|
+
},
|
|
54
|
+
"gjsify": {
|
|
55
|
+
"runtimes": {
|
|
56
|
+
"gjs": "polyfill",
|
|
57
|
+
"node": "none",
|
|
58
|
+
"browser": "none",
|
|
59
|
+
"nativescript": "none"
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
"license": "MIT",
|
|
63
|
+
"repository": {
|
|
64
|
+
"type": "git",
|
|
65
|
+
"url": "git+https://github.com/gjsify/gjsify.git",
|
|
66
|
+
"directory": "packages/framework/devtools"
|
|
67
|
+
},
|
|
68
|
+
"bugs": {
|
|
69
|
+
"url": "https://github.com/gjsify/gjsify/issues"
|
|
70
|
+
},
|
|
71
|
+
"homepage": "https://github.com/gjsify/gjsify/tree/main/packages/framework/devtools#readme"
|
|
72
|
+
}
|