@gtkx/testing 1.0.0-rc.1 → 1.0.0-rc.2

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.
Files changed (182) hide show
  1. package/dist/act.d.ts +6 -6
  2. package/dist/act.d.ts.map +1 -1
  3. package/dist/act.js +34 -26
  4. package/dist/act.js.map +1 -1
  5. package/dist/bound-queries.d.ts +4 -3
  6. package/dist/bound-queries.d.ts.map +1 -1
  7. package/dist/bound-queries.js.map +1 -1
  8. package/dist/build-queries.d.ts +5 -5
  9. package/dist/build-queries.d.ts.map +1 -1
  10. package/dist/build-queries.js +21 -9
  11. package/dist/build-queries.js.map +1 -1
  12. package/dist/cleanup-registry.d.ts +4 -3
  13. package/dist/cleanup-registry.d.ts.map +1 -1
  14. package/dist/cleanup-registry.js +13 -3
  15. package/dist/cleanup-registry.js.map +1 -1
  16. package/dist/config.d.ts +5 -4
  17. package/dist/config.d.ts.map +1 -1
  18. package/dist/config.js +11 -9
  19. package/dist/config.js.map +1 -1
  20. package/dist/editable.d.ts +6 -5
  21. package/dist/editable.d.ts.map +1 -1
  22. package/dist/editable.js +7 -5
  23. package/dist/editable.js.map +1 -1
  24. package/dist/errors.d.ts +7 -6
  25. package/dist/errors.d.ts.map +1 -1
  26. package/dist/errors.js +64 -48
  27. package/dist/errors.js.map +1 -1
  28. package/dist/fire-event.d.ts +2 -1
  29. package/dist/fire-event.d.ts.map +1 -1
  30. package/dist/fire-event.js +2 -1
  31. package/dist/fire-event.js.map +1 -1
  32. package/dist/frame-sync.d.ts +2 -1
  33. package/dist/frame-sync.d.ts.map +1 -1
  34. package/dist/frame-sync.js +28 -21
  35. package/dist/frame-sync.js.map +1 -1
  36. package/dist/index.d.ts +2 -2
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +1 -1
  39. package/dist/index.js.map +1 -1
  40. package/dist/matchers.d.ts +38 -34
  41. package/dist/matchers.d.ts.map +1 -1
  42. package/dist/matchers.js +72 -58
  43. package/dist/matchers.js.map +1 -1
  44. package/dist/pretty-widget.d.ts +6 -4
  45. package/dist/pretty-widget.d.ts.map +1 -1
  46. package/dist/pretty-widget.js +64 -24
  47. package/dist/pretty-widget.js.map +1 -1
  48. package/dist/queries.d.ts +143 -86
  49. package/dist/queries.d.ts.map +1 -1
  50. package/dist/queries.js +231 -147
  51. package/dist/queries.js.map +1 -1
  52. package/dist/register-test-hooks.js +2 -1
  53. package/dist/register-test-hooks.js.map +1 -1
  54. package/dist/render-hook.d.ts +3 -2
  55. package/dist/render-hook.d.ts.map +1 -1
  56. package/dist/render-hook.js +2 -1
  57. package/dist/render-hook.js.map +1 -1
  58. package/dist/render.d.ts +3 -2
  59. package/dist/render.d.ts.map +1 -1
  60. package/dist/render.js +24 -18
  61. package/dist/render.js.map +1 -1
  62. package/dist/role-helpers.d.ts +6 -5
  63. package/dist/role-helpers.d.ts.map +1 -1
  64. package/dist/role-helpers.js +16 -12
  65. package/dist/role-helpers.js.map +1 -1
  66. package/dist/screen.d.ts +4 -3
  67. package/dist/screen.d.ts.map +1 -1
  68. package/dist/screen.js +13 -11
  69. package/dist/screen.js.map +1 -1
  70. package/dist/screenshot.d.ts +4 -3
  71. package/dist/screenshot.d.ts.map +1 -1
  72. package/dist/screenshot.js +43 -36
  73. package/dist/screenshot.js.map +1 -1
  74. package/dist/suggestions.d.ts +11 -6
  75. package/dist/suggestions.d.ts.map +1 -1
  76. package/dist/suggestions.js +10 -5
  77. package/dist/suggestions.js.map +1 -1
  78. package/dist/traversal.d.ts +7 -7
  79. package/dist/traversal.d.ts.map +1 -1
  80. package/dist/traversal.js +17 -11
  81. package/dist/traversal.js.map +1 -1
  82. package/dist/types.d.ts +39 -24
  83. package/dist/types.d.ts.map +1 -1
  84. package/dist/types.js.map +1 -1
  85. package/dist/user-event/adjustment.d.ts +5 -3
  86. package/dist/user-event/adjustment.d.ts.map +1 -1
  87. package/dist/user-event/adjustment.js +6 -4
  88. package/dist/user-event/adjustment.js.map +1 -1
  89. package/dist/user-event/click.d.ts +6 -5
  90. package/dist/user-event/click.d.ts.map +1 -1
  91. package/dist/user-event/click.js +29 -21
  92. package/dist/user-event/click.js.map +1 -1
  93. package/dist/user-event/controller.d.ts +6 -5
  94. package/dist/user-event/controller.d.ts.map +1 -1
  95. package/dist/user-event/controller.js +9 -6
  96. package/dist/user-event/controller.js.map +1 -1
  97. package/dist/user-event/dispatch.d.ts +3 -3
  98. package/dist/user-event/dispatch.d.ts.map +1 -1
  99. package/dist/user-event/dispatch.js +3 -2
  100. package/dist/user-event/dispatch.js.map +1 -1
  101. package/dist/user-event/event-wrapper.d.ts +2 -1
  102. package/dist/user-event/event-wrapper.d.ts.map +1 -1
  103. package/dist/user-event/event-wrapper.js +27 -16
  104. package/dist/user-event/event-wrapper.js.map +1 -1
  105. package/dist/user-event/gesture.d.ts +18 -14
  106. package/dist/user-event/gesture.d.ts.map +1 -1
  107. package/dist/user-event/gesture.js +70 -37
  108. package/dist/user-event/gesture.js.map +1 -1
  109. package/dist/user-event/index.d.ts +17 -10
  110. package/dist/user-event/index.d.ts.map +1 -1
  111. package/dist/user-event/index.js +7 -3
  112. package/dist/user-event/index.js.map +1 -1
  113. package/dist/user-event/keyboard.d.ts +4 -3
  114. package/dist/user-event/keyboard.d.ts.map +1 -1
  115. package/dist/user-event/keyboard.js +89 -58
  116. package/dist/user-event/keyboard.js.map +1 -1
  117. package/dist/user-event/pointer.d.ts +7 -3
  118. package/dist/user-event/pointer.d.ts.map +1 -1
  119. package/dist/user-event/pointer.js +4 -1
  120. package/dist/user-event/pointer.js.map +1 -1
  121. package/dist/user-event/selection.d.ts +3 -2
  122. package/dist/user-event/selection.d.ts.map +1 -1
  123. package/dist/user-event/selection.js +26 -27
  124. package/dist/user-event/selection.js.map +1 -1
  125. package/dist/user-event/state.d.ts +3 -2
  126. package/dist/user-event/state.d.ts.map +1 -1
  127. package/dist/user-event/state.js +2 -1
  128. package/dist/user-event/state.js.map +1 -1
  129. package/dist/user-event/text.d.ts +8 -7
  130. package/dist/user-event/text.d.ts.map +1 -1
  131. package/dist/user-event/text.js +39 -33
  132. package/dist/user-event/text.js.map +1 -1
  133. package/dist/wait-for.d.ts +5 -5
  134. package/dist/wait-for.d.ts.map +1 -1
  135. package/dist/wait-for.js +45 -30
  136. package/dist/wait-for.js.map +1 -1
  137. package/dist/widget-accessible-properties.d.ts +26 -25
  138. package/dist/widget-accessible-properties.d.ts.map +1 -1
  139. package/dist/widget-accessible-properties.js +106 -61
  140. package/dist/widget-accessible-properties.js.map +1 -1
  141. package/dist/within.d.ts +2 -1
  142. package/dist/within.d.ts.map +1 -1
  143. package/dist/within.js +2 -1
  144. package/dist/within.js.map +1 -1
  145. package/package.json +11 -9
  146. package/src/act.ts +44 -35
  147. package/src/bound-queries.ts +5 -4
  148. package/src/build-queries.ts +84 -43
  149. package/src/cleanup-registry.ts +18 -5
  150. package/src/config.ts +16 -12
  151. package/src/editable.ts +13 -6
  152. package/src/errors.ts +106 -45
  153. package/src/fire-event.ts +3 -1
  154. package/src/frame-sync.ts +38 -21
  155. package/src/index.ts +2 -1
  156. package/src/matchers.ts +158 -107
  157. package/src/pretty-widget.ts +114 -42
  158. package/src/queries.ts +422 -233
  159. package/src/register-test-hooks.ts +4 -1
  160. package/src/render-hook.tsx +10 -4
  161. package/src/render.tsx +58 -33
  162. package/src/role-helpers.ts +29 -16
  163. package/src/screen.ts +17 -12
  164. package/src/screenshot.ts +61 -38
  165. package/src/suggestions.ts +29 -10
  166. package/src/traversal.ts +35 -15
  167. package/src/types.ts +65 -32
  168. package/src/user-event/adjustment.ts +23 -10
  169. package/src/user-event/click.ts +49 -20
  170. package/src/user-event/controller.ts +21 -7
  171. package/src/user-event/dispatch.ts +4 -2
  172. package/src/user-event/event-wrapper.ts +42 -16
  173. package/src/user-event/gesture.ts +135 -50
  174. package/src/user-event/index.ts +19 -12
  175. package/src/user-event/keyboard.ts +147 -61
  176. package/src/user-event/pointer.ts +20 -8
  177. package/src/user-event/selection.ts +69 -32
  178. package/src/user-event/state.ts +4 -2
  179. package/src/user-event/text.ts +73 -38
  180. package/src/wait-for.ts +77 -36
  181. package/src/widget-accessible-properties.ts +218 -74
  182. package/src/within.ts +7 -4
package/dist/screen.d.ts CHANGED
@@ -3,7 +3,8 @@ import type { RenderResult, Screen } from "./bound-queries.js";
3
3
  * Queries and debug utilities bound to the most recent render, scoped to the
4
4
  * current toplevel windows. Accessing it before any render throws.
5
5
  */
6
- export declare const screen: Screen;
7
- export declare const setScreen: (result: RenderResult) => void;
8
- export declare const clearScreen: () => void;
6
+ declare const screen: Screen;
7
+ declare const setScreen: (result: RenderResult) => void;
8
+ declare const clearScreen: () => void;
9
+ export { screen, setScreen, clearScreen };
9
10
  //# sourceMappingURL=screen.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"screen.d.ts","sourceRoot":"","sources":["../src/screen.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAiB/D;;;GAGG;AACH,eAAO,MAAM,MAAM,EAAE,MAEnB,CAAC;AAEH,eAAO,MAAM,SAAS,WAAY,YAAY,KAAG,IAEhD,CAAC;AAEF,eAAO,MAAM,WAAW,QAAO,IAE9B,CAAC"}
1
+ {"version":3,"file":"screen.d.ts","sourceRoot":"","sources":["../src/screen.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAgB/D;;;GAGG;AACH,QAAA,MAAM,MAAM,EAAE,MAEZ,CAAC;AAMH,QAAA,MAAM,SAAS,GAAI,QAAQ,YAAY,KAAG,IAEzC,CAAC;AAEF,QAAA,MAAM,WAAW,QAAO,IAEvB,CAAC;AAEF,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,CAAC"}
package/dist/screen.js CHANGED
@@ -1,26 +1,28 @@
1
1
  const NO_RENDER_MESSAGE = "No render has been performed: call render() before using screen queries";
2
- const throwNoRender = () => {
3
- throw new Error(NO_RENDER_MESSAGE);
4
- };
5
2
  const defaultScreen = new Proxy({}, {
6
3
  get: (_target, property) => {
7
- if (property === "baseElement" || property === "container")
4
+ if (property === "baseElement" || property === "container") {
8
5
  return throwNoRender();
6
+ }
9
7
  return throwNoRender;
10
8
  },
11
9
  });
12
- let activeScreen = defaultScreen;
10
+ const activeScreen = { current: defaultScreen };
13
11
  /**
14
12
  * Queries and debug utilities bound to the most recent render, scoped to the
15
13
  * current toplevel windows. Accessing it before any render throws.
16
14
  */
17
- export const screen = new Proxy({}, {
18
- get: (_target, property) => Reflect.get(activeScreen, property),
15
+ const screen = new Proxy({}, {
16
+ get: (_target, property) => Reflect.get(activeScreen.current, property),
19
17
  });
20
- export const setScreen = (result) => {
21
- activeScreen = result;
18
+ const throwNoRender = () => {
19
+ throw new Error(NO_RENDER_MESSAGE);
20
+ };
21
+ const setScreen = (result) => {
22
+ activeScreen.current = result;
22
23
  };
23
- export const clearScreen = () => {
24
- activeScreen = defaultScreen;
24
+ const clearScreen = () => {
25
+ activeScreen.current = defaultScreen;
25
26
  };
27
+ export { screen, setScreen, clearScreen };
26
28
  //# sourceMappingURL=screen.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"screen.js","sourceRoot":"","sources":["../src/screen.ts"],"names":[],"mappings":"AAEA,MAAM,iBAAiB,GAAG,yEAAyE,CAAC;AAEpG,MAAM,aAAa,GAAG,GAAU,EAAE;IAC9B,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;AACvC,CAAC,CAAC;AAEF,MAAM,aAAa,GAAiB,IAAI,KAAK,CAAC,EAAkB,EAAE;IAC9D,GAAG,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,EAAE;QACvB,IAAI,QAAQ,KAAK,aAAa,IAAI,QAAQ,KAAK,WAAW;YAAE,OAAO,aAAa,EAAE,CAAC;QACnF,OAAO,aAAa,CAAC;IACzB,CAAC;CACJ,CAAC,CAAC;AAEH,IAAI,YAAY,GAAiB,aAAa,CAAC;AAE/C;;;GAGG;AACH,MAAM,CAAC,MAAM,MAAM,GAAW,IAAI,KAAK,CAAC,EAAY,EAAE;IAClD,GAAG,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,QAAQ,CAAC;CAClE,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,MAAoB,EAAQ,EAAE;IACpD,YAAY,GAAG,MAAM,CAAC;AAC1B,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,WAAW,GAAG,GAAS,EAAE;IAClC,YAAY,GAAG,aAAa,CAAC;AACjC,CAAC,CAAC","sourcesContent":["import type { RenderResult, Screen } from \"./bound-queries.js\";\n\nconst NO_RENDER_MESSAGE = \"No render has been performed: call render() before using screen queries\";\n\nconst throwNoRender = (): never => {\n throw new Error(NO_RENDER_MESSAGE);\n};\n\nconst defaultScreen: RenderResult = new Proxy({} as RenderResult, {\n get: (_target, property) => {\n if (property === \"baseElement\" || property === \"container\") return throwNoRender();\n return throwNoRender;\n },\n});\n\nlet activeScreen: RenderResult = defaultScreen;\n\n/**\n * Queries and debug utilities bound to the most recent render, scoped to the\n * current toplevel windows. Accessing it before any render throws.\n */\nexport const screen: Screen = new Proxy({} as Screen, {\n get: (_target, property) => Reflect.get(activeScreen, property),\n});\n\nexport const setScreen = (result: RenderResult): void => {\n activeScreen = result;\n};\n\nexport const clearScreen = (): void => {\n activeScreen = defaultScreen;\n};\n"]}
1
+ {"version":3,"file":"screen.js","sourceRoot":"","sources":["../src/screen.ts"],"names":[],"mappings":"AAEA,MAAM,iBAAiB,GAAG,yEAAyE,CAAC;AAEpG,MAAM,aAAa,GAAiB,IAAI,KAAK,CAAC,EAAkB,EAAE;IAC9D,GAAG,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,EAAE;QACvB,IAAI,QAAQ,KAAK,aAAa,IAAI,QAAQ,KAAK,WAAW,EAAE,CAAC;YACzD,OAAO,aAAa,EAAE,CAAC;QAC3B,CAAC;QAED,OAAO,aAAa,CAAC;IACzB,CAAC;CACJ,CAAC,CAAC;AAEH,MAAM,YAAY,GAA8B,EAAE,OAAO,EAAE,aAAa,EAAE,CAAC;AAE3E;;;GAGG;AACH,MAAM,MAAM,GAAW,IAAI,KAAK,CAAC,EAAY,EAAE;IAC3C,GAAG,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAW,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,OAAO,EAAE,QAAQ,CAAC;CACnF,CAAC,CAAC;AAEH,MAAM,aAAa,GAAG,GAAU,EAAE;IAC9B,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;AACvC,CAAC,CAAC;AAEF,MAAM,SAAS,GAAG,CAAC,MAAoB,EAAQ,EAAE;IAC7C,YAAY,CAAC,OAAO,GAAG,MAAM,CAAC;AAClC,CAAC,CAAC;AAEF,MAAM,WAAW,GAAG,GAAS,EAAE;IAC3B,YAAY,CAAC,OAAO,GAAG,aAAa,CAAC;AACzC,CAAC,CAAC;AAEF,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,CAAC","sourcesContent":["import type { RenderResult, Screen } from \"./bound-queries.js\";\n\nconst NO_RENDER_MESSAGE = \"No render has been performed: call render() before using screen queries\";\n\nconst defaultScreen: RenderResult = new Proxy({} as RenderResult, {\n get: (_target, property) => {\n if (property === \"baseElement\" || property === \"container\") {\n return throwNoRender();\n }\n\n return throwNoRender;\n },\n});\n\nconst activeScreen: { current: RenderResult } = { current: defaultScreen };\n\n/**\n * Queries and debug utilities bound to the most recent render, scoped to the\n * current toplevel windows. Accessing it before any render throws.\n */\nconst screen: Screen = new Proxy({} as Screen, {\n get: (_target, property): unknown => Reflect.get(activeScreen.current, property),\n});\n\nconst throwNoRender = (): never => {\n throw new Error(NO_RENDER_MESSAGE);\n};\n\nconst setScreen = (result: RenderResult): void => {\n activeScreen.current = result;\n};\n\nconst clearScreen = (): void => {\n activeScreen.current = defaultScreen;\n};\n\nexport { screen, setScreen, clearScreen };\n"]}
@@ -7,13 +7,13 @@ import type { ScreenshotOptions, ScreenshotResult, WindowSelector } from "./type
7
7
  * @param options Optional scale, timeout, and retry interval.
8
8
  * @returns The base64-encoded PNG data along with its mime type and dimensions.
9
9
  */
10
- export declare const screenshot: (widget: Gtk.Widget, options?: ScreenshotOptions) => Promise<ScreenshotResult>;
10
+ declare const screenshot: (widget: Gtk.Widget, options?: ScreenshotOptions) => Promise<ScreenshotResult>;
11
11
  /**
12
12
  * Prints a clickable file URL for a saved screenshot to the console.
13
13
  *
14
14
  * @param filepath Absolute path of the saved screenshot file.
15
15
  */
16
- export declare const logScreenshotPath: (filepath: string) => void;
16
+ declare const logScreenshotPath: (filepath: string) => void;
17
17
  /**
18
18
  * Captures a screenshot of a toplevel window, writes it to a temporary file,
19
19
  * logs its path, and returns the image data.
@@ -23,5 +23,6 @@ export declare const logScreenshotPath: (filepath: string) => void;
23
23
  * @param options Optional scale, timeout, and retry interval.
24
24
  * @returns The base64-encoded PNG data along with its mime type and dimensions.
25
25
  */
26
- export declare const captureAndSaveScreenshot: (selector?: WindowSelector, options?: ScreenshotOptions) => Promise<ScreenshotResult>;
26
+ declare const captureAndSaveScreenshot: (selector?: WindowSelector, options?: ScreenshotOptions) => Promise<ScreenshotResult>;
27
+ export { screenshot, logScreenshotPath, captureAndSaveScreenshot };
27
28
  //# sourceMappingURL=screenshot.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"screenshot.d.ts","sourceRoot":"","sources":["../src/screenshot.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,GAAG,MAAM,cAAc,CAAC;AACpC,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAuDtF;;;;;;GAMG;AACH,eAAO,MAAM,UAAU,WAAkB,GAAG,CAAC,MAAM,YAAY,iBAAiB,KAAG,OAAO,CAAC,gBAAgB,CAW1G,CAAC;AAiDF;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,aAAc,MAAM,KAAG,IAEpD,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,wBAAwB,cACtB,cAAc,YACf,iBAAiB,KAC5B,OAAO,CAAC,gBAAgB,CAK1B,CAAC"}
1
+ {"version":3,"file":"screenshot.d.ts","sourceRoot":"","sources":["../src/screenshot.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,GAAG,MAAM,cAAc,CAAC;AAIpC,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAuDtF;;;;;;GAMG;AACH,QAAA,MAAM,UAAU,GAAU,QAAQ,GAAG,CAAC,MAAM,EAAE,UAAU,iBAAiB,KAAG,OAAO,CAAC,gBAAgB,CAWnG,CAAC;AAqEF;;;;GAIG;AACH,QAAA,MAAM,iBAAiB,GAAI,UAAU,MAAM,KAAG,IAE7C,CAAC;AAEF;;;;;;;;GAQG;AACH,QAAA,MAAM,wBAAwB,GAC1B,WAAW,cAAc,EACzB,UAAU,iBAAiB,KAC5B,OAAO,CAAC,gBAAgB,CAM1B,CAAC;AAEF,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,CAAC"}
@@ -1,14 +1,15 @@
1
- import { existsSync, mkdirSync, writeFileSync } from "node:fs";
2
- import { tmpdir } from "node:os";
3
- import { join } from "node:path";
4
1
  import * as Gsk from "@gtkx/gi/gsk";
5
2
  import * as Gtk from "@gtkx/gi/gtk";
3
+ import { mkdtempSync, writeFileSync } from "node:fs";
4
+ import { tmpdir } from "node:os";
5
+ import { join } from "node:path";
6
6
  import { waitFor } from "./wait-for.js";
7
+ const DEFAULT_SCREENSHOT_INTERVAL = 10;
7
8
  const bytesToBase64 = (bytes) => {
8
9
  return Buffer.from(bytes).toString("base64");
9
10
  };
10
- const DEFAULT_SCREENSHOT_INTERVAL = 10;
11
- const describeWidgetState = (widget) => `realized=${widget.getRealized()} mapped=${widget.getMapped()} visible=${widget.getVisible()}`;
11
+ const describeWidgetState = (widget) => `realized=${String(widget.getRealized())} mapped=${String(widget.getMapped())} ` +
12
+ `visible=${String(widget.getVisible())}`;
12
13
  const captureSnapshot = (widget, scale) => {
13
14
  const paintable = new Gtk.WidgetPaintable({ widget });
14
15
  const width = paintable.getIntrinsicWidth();
@@ -51,54 +52,59 @@ const captureSnapshot = (widget, scale) => {
51
52
  * @param options Optional scale, timeout, and retry interval.
52
53
  * @returns The base64-encoded PNG data along with its mime type and dimensions.
53
54
  */
54
- export const screenshot = async (widget, options) => {
55
+ const screenshot = async (widget, options) => {
55
56
  const scale = options?.scale ?? 1;
56
57
  if (!Number.isFinite(scale) || scale <= 0) {
57
- throw new Error(`Screenshot scale must be a positive number, got ${scale}`);
58
+ throw new Error(`Screenshot scale must be a positive number, got ${String(scale)}`);
58
59
  }
59
60
  return waitFor(() => captureSnapshot(widget, scale), {
60
61
  timeout: options?.timeout,
61
62
  interval: options?.interval ?? DEFAULT_SCREENSHOT_INTERVAL,
62
63
  });
63
64
  };
65
+ const firstToplevelWindow = (windows) => {
66
+ const [first] = windows;
67
+ if (!(first instanceof Gtk.Window)) {
68
+ throw new TypeError("First toplevel is not a Window");
69
+ }
70
+ return first;
71
+ };
72
+ const windowAtIndex = (windows, index) => {
73
+ const indexed = windows[index];
74
+ if (!(indexed instanceof Gtk.Window)) {
75
+ throw new TypeError(`Window at index ${String(index)} not found`);
76
+ }
77
+ return indexed;
78
+ };
79
+ const isWindow = (widget) => widget instanceof Gtk.Window;
80
+ const hasMatchingTitle = (window, selector) => {
81
+ const title = window.getTitle() ?? "";
82
+ return selector instanceof RegExp ? selector.test(title) : title.includes(selector);
83
+ };
84
+ const describeTitleSelector = (selector) => selector instanceof RegExp ? selector.toString() : `"${selector}"`;
85
+ const windowByTitle = (windows, selector) => {
86
+ const found = windows.filter(isWindow).find((window) => hasMatchingTitle(window, selector));
87
+ if (!found) {
88
+ throw new Error(`No window found with title matching ${describeTitleSelector(selector)}`);
89
+ }
90
+ return found;
91
+ };
64
92
  const resolveWindow = (selector) => {
65
93
  const windows = Gtk.Window.listToplevels();
66
94
  if (windows.length === 0) {
67
95
  throw new Error("No windows available for screenshot");
68
96
  }
69
97
  if (selector === undefined) {
70
- const [first] = windows;
71
- if (!(first instanceof Gtk.Window)) {
72
- throw new TypeError("First toplevel is not a Window");
73
- }
74
- return first;
98
+ return firstToplevelWindow(windows);
75
99
  }
76
100
  if (typeof selector === "number") {
77
- const indexed = windows[selector];
78
- if (!(indexed instanceof Gtk.Window)) {
79
- throw new TypeError(`Window at index ${selector} not found`);
80
- }
81
- return indexed;
101
+ return windowAtIndex(windows, selector);
82
102
  }
83
- const isRegex = selector instanceof RegExp;
84
- const found = windows.find((w) => {
85
- if (!(w instanceof Gtk.Window))
86
- return false;
87
- const title = w.getTitle() ?? "";
88
- return isRegex ? selector.test(title) : title.includes(selector);
89
- });
90
- if (!found) {
91
- const pattern = isRegex ? selector.toString() : `"${selector}"`;
92
- throw new Error(`No window found with title matching ${pattern}`);
93
- }
94
- return found;
103
+ return windowByTitle(windows, selector);
95
104
  };
96
105
  const saveScreenshotToTempFile = (result) => {
97
- const dir = join(tmpdir(), "gtkx-screenshots");
98
- if (!existsSync(dir)) {
99
- mkdirSync(dir, { recursive: true });
100
- }
101
- const filepath = join(dir, `${Date.now()}-screenshot.png`);
106
+ const dir = mkdtempSync(join(tmpdir(), "gtkx-screenshots-"));
107
+ const filepath = join(dir, "screenshot.png");
102
108
  writeFileSync(filepath, Buffer.from(result.data, "base64"));
103
109
  return filepath;
104
110
  };
@@ -107,7 +113,7 @@ const saveScreenshotToTempFile = (result) => {
107
113
  *
108
114
  * @param filepath Absolute path of the saved screenshot file.
109
115
  */
110
- export const logScreenshotPath = (filepath) => {
116
+ const logScreenshotPath = (filepath) => {
111
117
  console.log(`Screenshot saved: file://${filepath}`);
112
118
  };
113
119
  /**
@@ -119,10 +125,11 @@ export const logScreenshotPath = (filepath) => {
119
125
  * @param options Optional scale, timeout, and retry interval.
120
126
  * @returns The base64-encoded PNG data along with its mime type and dimensions.
121
127
  */
122
- export const captureAndSaveScreenshot = async (selector, options) => {
128
+ const captureAndSaveScreenshot = async (selector, options) => {
123
129
  const target = resolveWindow(selector);
124
130
  const result = await screenshot(target, options);
125
131
  logScreenshotPath(saveScreenshotToTempFile(result));
126
132
  return result;
127
133
  };
134
+ export { screenshot, logScreenshotPath, captureAndSaveScreenshot };
128
135
  //# sourceMappingURL=screenshot.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"screenshot.js","sourceRoot":"","sources":["../src/screenshot.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, mkdirSync, writeFileSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport * as Gsk from \"@gtkx/gi/gsk\";\nimport * as Gtk from \"@gtkx/gi/gtk\";\nimport type { ScreenshotOptions, ScreenshotResult, WindowSelector } from \"./types.js\";\nimport { waitFor } from \"./wait-for.js\";\n\nconst bytesToBase64 = (bytes: number[]): string => {\n return Buffer.from(bytes).toString(\"base64\");\n};\n\nconst DEFAULT_SCREENSHOT_INTERVAL = 10;\n\nconst describeWidgetState = (widget: Gtk.Widget): string =>\n `realized=${widget.getRealized()} mapped=${widget.getMapped()} visible=${widget.getVisible()}`;\n\nconst captureSnapshot = (widget: Gtk.Widget, scale: number): ScreenshotResult => {\n const paintable = new Gtk.WidgetPaintable({ widget });\n const width = paintable.getIntrinsicWidth();\n const height = paintable.getIntrinsicHeight();\n\n if (width <= 0 || height <= 0) {\n throw new Error(`Widget has no size: ensure it is realized and visible (${describeWidgetState(widget)})`);\n }\n\n const snapshot = new Gtk.Snapshot();\n snapshot.scale(scale, scale);\n paintable.snapshot(snapshot, width, height);\n const renderNode = snapshot.toNode();\n\n if (!renderNode) {\n throw new Error(`Widget produced no render content (${describeWidgetState(widget)})`);\n }\n\n const display = widget.getDisplay();\n\n const renderer = new Gsk.CairoRenderer();\n renderer.realizeForDisplay(display);\n\n try {\n const texture = renderer.renderTexture(renderNode, null);\n const pngBytes = texture.saveToPngBytes();\n const data = pngBytes.getData();\n\n if (!data) {\n throw new Error(\"Failed to serialize screenshot to PNG\");\n }\n\n return {\n data: bytesToBase64(data),\n mimeType: \"image/png\",\n width: Math.round(width * scale),\n height: Math.round(height * scale),\n };\n } finally {\n renderer.unrealize();\n }\n};\n\n/**\n * Captures a PNG snapshot of a widget, retrying until it has a paintable size.\n *\n * @param widget The widget to render to an image.\n * @param options Optional scale, timeout, and retry interval.\n * @returns The base64-encoded PNG data along with its mime type and dimensions.\n */\nexport const screenshot = async (widget: Gtk.Widget, options?: ScreenshotOptions): Promise<ScreenshotResult> => {\n const scale = options?.scale ?? 1;\n\n if (!Number.isFinite(scale) || scale <= 0) {\n throw new Error(`Screenshot scale must be a positive number, got ${scale}`);\n }\n\n return waitFor(() => captureSnapshot(widget, scale), {\n timeout: options?.timeout,\n interval: options?.interval ?? DEFAULT_SCREENSHOT_INTERVAL,\n });\n};\n\nconst resolveWindow = (selector?: WindowSelector): Gtk.Window => {\n const windows = Gtk.Window.listToplevels();\n\n if (windows.length === 0) {\n throw new Error(\"No windows available for screenshot\");\n }\n\n if (selector === undefined) {\n const [first] = windows;\n if (!(first instanceof Gtk.Window)) {\n throw new TypeError(\"First toplevel is not a Window\");\n }\n return first;\n }\n\n if (typeof selector === \"number\") {\n const indexed = windows[selector];\n if (!(indexed instanceof Gtk.Window)) {\n throw new TypeError(`Window at index ${selector} not found`);\n }\n return indexed;\n }\n\n const isRegex = selector instanceof RegExp;\n const found = windows.find((w): w is Gtk.Window => {\n if (!(w instanceof Gtk.Window)) return false;\n const title = w.getTitle() ?? \"\";\n return isRegex ? selector.test(title) : title.includes(selector);\n });\n\n if (!found) {\n const pattern = isRegex ? selector.toString() : `\"${selector}\"`;\n throw new Error(`No window found with title matching ${pattern}`);\n }\n return found;\n};\n\nconst saveScreenshotToTempFile = (result: ScreenshotResult): string => {\n const dir = join(tmpdir(), \"gtkx-screenshots\");\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n const filepath = join(dir, `${Date.now()}-screenshot.png`);\n writeFileSync(filepath, Buffer.from(result.data, \"base64\"));\n return filepath;\n};\n\n/**\n * Prints a clickable file URL for a saved screenshot to the console.\n *\n * @param filepath Absolute path of the saved screenshot file.\n */\nexport const logScreenshotPath = (filepath: string): void => {\n console.log(`Screenshot saved: file://${filepath}`);\n};\n\n/**\n * Captures a screenshot of a toplevel window, writes it to a temporary file,\n * logs its path, and returns the image data.\n *\n * @param selector Chooses the window by index, title substring, or regular\n * expression; defaults to the first toplevel.\n * @param options Optional scale, timeout, and retry interval.\n * @returns The base64-encoded PNG data along with its mime type and dimensions.\n */\nexport const captureAndSaveScreenshot = async (\n selector?: WindowSelector,\n options?: ScreenshotOptions,\n): Promise<ScreenshotResult> => {\n const target = resolveWindow(selector);\n const result = await screenshot(target, options);\n logScreenshotPath(saveScreenshotToTempFile(result));\n return result;\n};\n"]}
1
+ {"version":3,"file":"screenshot.js","sourceRoot":"","sources":["../src/screenshot.ts"],"names":[],"mappings":"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","sourcesContent":["import * as Gsk from \"@gtkx/gi/gsk\";\nimport * as Gtk from \"@gtkx/gi/gtk\";\nimport { mkdtempSync, writeFileSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { ScreenshotOptions, ScreenshotResult, WindowSelector } from \"./types.js\";\nimport { waitFor } from \"./wait-for.js\";\n\nconst DEFAULT_SCREENSHOT_INTERVAL = 10;\n\nconst bytesToBase64 = (bytes: number[]): string => {\n return Buffer.from(bytes).toString(\"base64\");\n};\n\nconst describeWidgetState = (widget: Gtk.Widget): string =>\n `realized=${String(widget.getRealized())} mapped=${String(widget.getMapped())} ` +\n `visible=${String(widget.getVisible())}`;\n\nconst captureSnapshot = (widget: Gtk.Widget, scale: number): ScreenshotResult => {\n const paintable = new Gtk.WidgetPaintable({ widget });\n const width = paintable.getIntrinsicWidth();\n const height = paintable.getIntrinsicHeight();\n\n if (width <= 0 || height <= 0) {\n throw new Error(`Widget has no size: ensure it is realized and visible (${describeWidgetState(widget)})`);\n }\n\n const snapshot = new Gtk.Snapshot();\n snapshot.scale(scale, scale);\n paintable.snapshot(snapshot, width, height);\n const renderNode = snapshot.toNode();\n\n if (!renderNode) {\n throw new Error(`Widget produced no render content (${describeWidgetState(widget)})`);\n }\n\n const display = widget.getDisplay();\n const renderer = new Gsk.CairoRenderer();\n renderer.realizeForDisplay(display);\n\n try {\n const texture = renderer.renderTexture(renderNode, null);\n const pngBytes = texture.saveToPngBytes();\n const data = pngBytes.getData();\n\n if (!data) {\n throw new Error(\"Failed to serialize screenshot to PNG\");\n }\n\n return {\n data: bytesToBase64(data),\n mimeType: \"image/png\",\n width: Math.round(width * scale),\n height: Math.round(height * scale),\n };\n } finally {\n renderer.unrealize();\n }\n};\n\n/**\n * Captures a PNG snapshot of a widget, retrying until it has a paintable size.\n *\n * @param widget The widget to render to an image.\n * @param options Optional scale, timeout, and retry interval.\n * @returns The base64-encoded PNG data along with its mime type and dimensions.\n */\nconst screenshot = async (widget: Gtk.Widget, options?: ScreenshotOptions): Promise<ScreenshotResult> => {\n const scale = options?.scale ?? 1;\n\n if (!Number.isFinite(scale) || scale <= 0) {\n throw new Error(`Screenshot scale must be a positive number, got ${String(scale)}`);\n }\n\n return waitFor(() => captureSnapshot(widget, scale), {\n timeout: options?.timeout,\n interval: options?.interval ?? DEFAULT_SCREENSHOT_INTERVAL,\n });\n};\n\nconst firstToplevelWindow = (windows: Gtk.Widget[]): Gtk.Window => {\n const [first] = windows;\n\n if (!(first instanceof Gtk.Window)) {\n throw new TypeError(\"First toplevel is not a Window\");\n }\n\n return first;\n};\n\nconst windowAtIndex = (windows: Gtk.Widget[], index: number): Gtk.Window => {\n const indexed = windows[index];\n\n if (!(indexed instanceof Gtk.Window)) {\n throw new TypeError(`Window at index ${String(index)} not found`);\n }\n\n return indexed;\n};\n\nconst isWindow = (widget: Gtk.Widget): widget is Gtk.Window => widget instanceof Gtk.Window;\n\nconst hasMatchingTitle = (window: Gtk.Window, selector: string | RegExp): boolean => {\n const title = window.getTitle() ?? \"\";\n\n return selector instanceof RegExp ? selector.test(title) : title.includes(selector);\n};\n\nconst describeTitleSelector = (selector: string | RegExp): string =>\n selector instanceof RegExp ? selector.toString() : `\"${selector}\"`;\n\nconst windowByTitle = (windows: Gtk.Widget[], selector: string | RegExp): Gtk.Window => {\n const found = windows.filter(isWindow).find((window) => hasMatchingTitle(window, selector));\n\n if (!found) {\n throw new Error(`No window found with title matching ${describeTitleSelector(selector)}`);\n }\n\n return found;\n};\n\nconst resolveWindow = (selector?: WindowSelector): Gtk.Window => {\n const windows = Gtk.Window.listToplevels();\n\n if (windows.length === 0) {\n throw new Error(\"No windows available for screenshot\");\n }\n\n if (selector === undefined) {\n return firstToplevelWindow(windows);\n }\n\n if (typeof selector === \"number\") {\n return windowAtIndex(windows, selector);\n }\n\n return windowByTitle(windows, selector);\n};\n\nconst saveScreenshotToTempFile = (result: ScreenshotResult): string => {\n const dir = mkdtempSync(join(tmpdir(), \"gtkx-screenshots-\"));\n const filepath = join(dir, \"screenshot.png\");\n writeFileSync(filepath, Buffer.from(result.data, \"base64\"));\n\n return filepath;\n};\n\n/**\n * Prints a clickable file URL for a saved screenshot to the console.\n *\n * @param filepath Absolute path of the saved screenshot file.\n */\nconst logScreenshotPath = (filepath: string): void => {\n console.log(`Screenshot saved: file://${filepath}`);\n};\n\n/**\n * Captures a screenshot of a toplevel window, writes it to a temporary file,\n * logs its path, and returns the image data.\n *\n * @param selector Chooses the window by index, title substring, or regular\n * expression; defaults to the first toplevel.\n * @param options Optional scale, timeout, and retry interval.\n * @returns The base64-encoded PNG data along with its mime type and dimensions.\n */\nconst captureAndSaveScreenshot = async (\n selector?: WindowSelector,\n options?: ScreenshotOptions,\n): Promise<ScreenshotResult> => {\n const target = resolveWindow(selector);\n const result = await screenshot(target, options);\n logScreenshotPath(saveScreenshotToTempFile(result));\n\n return result;\n};\n\nexport { screenshot, logScreenshotPath, captureAndSaveScreenshot };\n"]}
@@ -1,10 +1,13 @@
1
1
  import * as Gtk from "@gtkx/gi/gtk";
2
2
  /** The query variant a suggestion targets. */
3
- export type Variant = "get" | "getAll" | "query" | "queryAll" | "find" | "findAll";
3
+ type Variant = "get" | "getAll" | "query" | "queryAll" | "find" | "findAll";
4
4
  /** The query family a suggestion targets. */
5
- export type Method = "Role" | "LabelText" | "PlaceholderText" | "Text" | "DisplayValue" | "Name";
6
- /** A suggested query for reaching a widget, including its family, variant, method name, and a `toString` that renders the full call. */
7
- export type Suggestion = {
5
+ type Method = "Role" | "LabelText" | "PlaceholderText" | "Text" | "DisplayValue" | "Name";
6
+ /**
7
+ * A suggested query for reaching a widget, including its family, variant, method name, and a
8
+ * `toString` that renders the full call.
9
+ */
10
+ type Suggestion = {
8
11
  queryName: Method;
9
12
  /** The full query function name, such as `getByRole`. */
10
13
  queryMethod: string;
@@ -12,11 +15,13 @@ export type Suggestion = {
12
15
  toString: () => string;
13
16
  };
14
17
  /**
15
- * Computes the recommended query for reaching the given widget, preferring role, then label text, placeholder text, text, display value, and name.
18
+ * Computes the recommended query for reaching the given widget, preferring role, then label text,
19
+ * placeholder text, text, display value, and name.
16
20
  * @param widget Widget to build a suggestion for.
17
21
  * @param variant Query variant the suggestion should use.
18
22
  * @param method Restrict the suggestion to a specific query family instead of choosing by priority.
19
23
  * @returns The best available suggestion, or undefined when no query applies.
20
24
  */
21
- export declare const getSuggestedQuery: (widget: Gtk.Widget, variant?: Variant, method?: Method) => Suggestion | undefined;
25
+ declare const getSuggestedQuery: (widget: Gtk.Widget, variant?: Variant, method?: Method) => Suggestion | undefined;
26
+ export { getSuggestedQuery, type Variant, type Method, type Suggestion };
22
27
  //# sourceMappingURL=suggestions.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"suggestions.d.ts","sourceRoot":"","sources":["../src/suggestions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,GAAG,MAAM,cAAc,CAAC;AAYpC,8CAA8C;AAC9C,MAAM,MAAM,OAAO,GAAG,KAAK,GAAG,QAAQ,GAAG,OAAO,GAAG,UAAU,GAAG,MAAM,GAAG,SAAS,CAAC;AAEnF,6CAA6C;AAC7C,MAAM,MAAM,MAAM,GAAG,MAAM,GAAG,WAAW,GAAG,iBAAiB,GAAG,MAAM,GAAG,cAAc,GAAG,MAAM,CAAC;AAEjG,wIAAwI;AACxI,MAAM,MAAM,UAAU,GAAG;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,yDAAyD;IACzD,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,MAAM,CAAC;CAC1B,CAAC;AA6BF;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,WAClB,GAAG,CAAC,MAAM,YACT,OAAO,WACP,MAAM,KAChB,UAAU,GAAG,SAqBf,CAAC"}
1
+ {"version":3,"file":"suggestions.d.ts","sourceRoot":"","sources":["../src/suggestions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,GAAG,MAAM,cAAc,CAAC;AAYpC,8CAA8C;AAC9C,KAAK,OAAO,GAAG,KAAK,GAAG,QAAQ,GAAG,OAAO,GAAG,UAAU,GAAG,MAAM,GAAG,SAAS,CAAC;AAC5E,6CAA6C;AAC7C,KAAK,MAAM,GAAG,MAAM,GAAG,WAAW,GAAG,iBAAiB,GAAG,MAAM,GAAG,cAAc,GAAG,MAAM,CAAC;AAE1F;;;GAGG;AACH,KAAK,UAAU,GAAG;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,yDAAyD;IACzD,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,MAAM,CAAC;CAC1B,CAAC;AAsCF;;;;;;;GAOG;AACH,QAAA,MAAM,iBAAiB,GACnB,QAAQ,GAAG,CAAC,MAAM,EAClB,UAAS,OAAe,EACxB,SAAS,MAAM,KAChB,UAAU,GAAG,SA0Bf,CAAC;AAEF,OAAO,EAAE,iBAAiB,EAAE,KAAK,OAAO,EAAE,KAAK,MAAM,EAAE,KAAK,UAAU,EAAE,CAAC"}
@@ -12,8 +12,9 @@ const makeSuggestion = (queryName, variant, argsText) => {
12
12
  };
13
13
  const roleSuggestion = (widget, variant) => {
14
14
  const role = widget.getAccessibleRole();
15
- if (role === Gtk.AccessibleRole.NONE || role === Gtk.AccessibleRole.GENERIC)
15
+ if (role === Gtk.AccessibleRole.NONE || role === Gtk.AccessibleRole.GENERIC) {
16
16
  return undefined;
17
+ }
17
18
  const roleText = `Gtk.AccessibleRole.${formatRole(role).toUpperCase()}`;
18
19
  const name = getWidgetAccessibleName(widget);
19
20
  if (name === null) {
@@ -22,18 +23,20 @@ const roleSuggestion = (widget, variant) => {
22
23
  return makeSuggestion("Role", variant, `${roleText}, { name: '${name}' }`);
23
24
  };
24
25
  const textSuggestion = (queryName, variant, value) => {
25
- if (value === null)
26
+ if (value === null) {
26
27
  return undefined;
28
+ }
27
29
  return makeSuggestion(queryName, variant, `'${value}'`);
28
30
  };
29
31
  /**
30
- * Computes the recommended query for reaching the given widget, preferring role, then label text, placeholder text, text, display value, and name.
32
+ * Computes the recommended query for reaching the given widget, preferring role, then label text,
33
+ * placeholder text, text, display value, and name.
31
34
  * @param widget Widget to build a suggestion for.
32
35
  * @param variant Query variant the suggestion should use.
33
36
  * @param method Restrict the suggestion to a specific query family instead of choosing by priority.
34
37
  * @returns The best available suggestion, or undefined when no query applies.
35
38
  */
36
- export const getSuggestedQuery = (widget, variant = "get", method) => {
39
+ const getSuggestedQuery = (widget, variant = "get", method) => {
37
40
  const builders = {
38
41
  Role: () => roleSuggestion(widget, variant),
39
42
  LabelText: () => textSuggestion("LabelText", variant, getWidgetOwnLabel(widget) ?? getWidgetLabelledByText(widget)),
@@ -48,9 +51,11 @@ export const getSuggestedQuery = (widget, variant = "get", method) => {
48
51
  const priority = ["Role", "LabelText", "PlaceholderText", "Text", "DisplayValue", "Name"];
49
52
  for (const candidate of priority) {
50
53
  const suggestion = builders[candidate]();
51
- if (suggestion)
54
+ if (suggestion) {
52
55
  return suggestion;
56
+ }
53
57
  }
54
58
  return undefined;
55
59
  };
60
+ export { getSuggestedQuery };
56
61
  //# sourceMappingURL=suggestions.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"suggestions.js","sourceRoot":"","sources":["../src/suggestions.ts"],"names":[],"mappings":"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","sourcesContent":["import * as Gtk from \"@gtkx/gi/gtk\";\nimport { formatRole } from \"./role-helpers.js\";\nimport {\n getWidgetAccessibleName,\n getWidgetDisplayValue,\n getWidgetLabelledByText,\n getWidgetLabelText,\n getWidgetName,\n getWidgetOwnLabel,\n getWidgetPlaceholderText,\n} from \"./widget-accessible-properties.js\";\n\n/** The query variant a suggestion targets. */\nexport type Variant = \"get\" | \"getAll\" | \"query\" | \"queryAll\" | \"find\" | \"findAll\";\n\n/** The query family a suggestion targets. */\nexport type Method = \"Role\" | \"LabelText\" | \"PlaceholderText\" | \"Text\" | \"DisplayValue\" | \"Name\";\n\n/** A suggested query for reaching a widget, including its family, variant, method name, and a `toString` that renders the full call. */\nexport type Suggestion = {\n queryName: Method;\n /** The full query function name, such as `getByRole`. */\n queryMethod: string;\n variant: Variant;\n toString: () => string;\n};\n\nconst makeSuggestion = (queryName: Method, variant: Variant, argsText: string): Suggestion => {\n const queryMethod = `${variant}By${queryName}`;\n return {\n queryName,\n queryMethod,\n variant,\n toString: () => `${queryMethod}(${argsText})`,\n };\n};\n\nconst roleSuggestion = (widget: Gtk.Widget, variant: Variant): Suggestion | undefined => {\n const role = widget.getAccessibleRole();\n if (role === Gtk.AccessibleRole.NONE || role === Gtk.AccessibleRole.GENERIC) return undefined;\n\n const roleText = `Gtk.AccessibleRole.${formatRole(role).toUpperCase()}`;\n const name = getWidgetAccessibleName(widget);\n if (name === null) {\n return makeSuggestion(\"Role\", variant, roleText);\n }\n return makeSuggestion(\"Role\", variant, `${roleText}, { name: '${name}' }`);\n};\n\nconst textSuggestion = (queryName: Method, variant: Variant, value: string | null): Suggestion | undefined => {\n if (value === null) return undefined;\n return makeSuggestion(queryName, variant, `'${value}'`);\n};\n\n/**\n * Computes the recommended query for reaching the given widget, preferring role, then label text, placeholder text, text, display value, and name.\n * @param widget Widget to build a suggestion for.\n * @param variant Query variant the suggestion should use.\n * @param method Restrict the suggestion to a specific query family instead of choosing by priority.\n * @returns The best available suggestion, or undefined when no query applies.\n */\nexport const getSuggestedQuery = (\n widget: Gtk.Widget,\n variant: Variant = \"get\",\n method?: Method,\n): Suggestion | undefined => {\n const builders: Record<Method, () => Suggestion | undefined> = {\n Role: () => roleSuggestion(widget, variant),\n LabelText: () =>\n textSuggestion(\"LabelText\", variant, getWidgetOwnLabel(widget) ?? getWidgetLabelledByText(widget)),\n PlaceholderText: () => textSuggestion(\"PlaceholderText\", variant, getWidgetPlaceholderText(widget)),\n Text: () => textSuggestion(\"Text\", variant, getWidgetLabelText(widget)),\n DisplayValue: () => textSuggestion(\"DisplayValue\", variant, getWidgetDisplayValue(widget)),\n Name: () => textSuggestion(\"Name\", variant, getWidgetName(widget)),\n };\n\n if (method) {\n return builders[method]();\n }\n\n const priority: Method[] = [\"Role\", \"LabelText\", \"PlaceholderText\", \"Text\", \"DisplayValue\", \"Name\"];\n for (const candidate of priority) {\n const suggestion = builders[candidate]();\n if (suggestion) return suggestion;\n }\n return undefined;\n};\n"]}
1
+ {"version":3,"file":"suggestions.js","sourceRoot":"","sources":["../src/suggestions.ts"],"names":[],"mappings":"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","sourcesContent":["import * as Gtk from \"@gtkx/gi/gtk\";\nimport { formatRole } from \"./role-helpers.js\";\nimport {\n getWidgetAccessibleName,\n getWidgetDisplayValue,\n getWidgetLabelledByText,\n getWidgetLabelText,\n getWidgetName,\n getWidgetOwnLabel,\n getWidgetPlaceholderText,\n} from \"./widget-accessible-properties.js\";\n\n/** The query variant a suggestion targets. */\ntype Variant = \"get\" | \"getAll\" | \"query\" | \"queryAll\" | \"find\" | \"findAll\";\n/** The query family a suggestion targets. */\ntype Method = \"Role\" | \"LabelText\" | \"PlaceholderText\" | \"Text\" | \"DisplayValue\" | \"Name\";\n\n/**\n * A suggested query for reaching a widget, including its family, variant, method name, and a\n * `toString` that renders the full call.\n */\ntype Suggestion = {\n queryName: Method;\n /** The full query function name, such as `getByRole`. */\n queryMethod: string;\n variant: Variant;\n toString: () => string;\n};\n\nconst makeSuggestion = (queryName: Method, variant: Variant, argsText: string): Suggestion => {\n const queryMethod = `${variant}By${queryName}`;\n\n return {\n queryName,\n queryMethod,\n variant,\n toString: () => `${queryMethod}(${argsText})`,\n };\n};\n\nconst roleSuggestion = (widget: Gtk.Widget, variant: Variant): Suggestion | undefined => {\n const role = widget.getAccessibleRole();\n\n if (role === Gtk.AccessibleRole.NONE || role === Gtk.AccessibleRole.GENERIC) {\n return undefined;\n }\n\n const roleText = `Gtk.AccessibleRole.${formatRole(role).toUpperCase()}`;\n const name = getWidgetAccessibleName(widget);\n\n if (name === null) {\n return makeSuggestion(\"Role\", variant, roleText);\n }\n\n return makeSuggestion(\"Role\", variant, `${roleText}, { name: '${name}' }`);\n};\n\nconst textSuggestion = (queryName: Method, variant: Variant, value: string | null): Suggestion | undefined => {\n if (value === null) {\n return undefined;\n }\n\n return makeSuggestion(queryName, variant, `'${value}'`);\n};\n\n/**\n * Computes the recommended query for reaching the given widget, preferring role, then label text,\n * placeholder text, text, display value, and name.\n * @param widget Widget to build a suggestion for.\n * @param variant Query variant the suggestion should use.\n * @param method Restrict the suggestion to a specific query family instead of choosing by priority.\n * @returns The best available suggestion, or undefined when no query applies.\n */\nconst getSuggestedQuery = (\n widget: Gtk.Widget,\n variant: Variant = \"get\",\n method?: Method,\n): Suggestion | undefined => {\n const builders: Record<Method, () => Suggestion | undefined> = {\n Role: () => roleSuggestion(widget, variant),\n LabelText: () =>\n textSuggestion(\"LabelText\", variant, getWidgetOwnLabel(widget) ?? getWidgetLabelledByText(widget)),\n PlaceholderText: () => textSuggestion(\"PlaceholderText\", variant, getWidgetPlaceholderText(widget)),\n Text: () => textSuggestion(\"Text\", variant, getWidgetLabelText(widget)),\n DisplayValue: () => textSuggestion(\"DisplayValue\", variant, getWidgetDisplayValue(widget)),\n Name: () => textSuggestion(\"Name\", variant, getWidgetName(widget)),\n };\n\n if (method) {\n return builders[method]();\n }\n\n const priority: Method[] = [\"Role\", \"LabelText\", \"PlaceholderText\", \"Text\", \"DisplayValue\", \"Name\"];\n\n for (const candidate of priority) {\n const suggestion = builders[candidate]();\n\n if (suggestion) {\n return suggestion;\n }\n }\n\n return undefined;\n};\n\nexport { getSuggestedQuery, type Variant, type Method, type Suggestion };\n"]}
@@ -1,15 +1,15 @@
1
1
  import * as Gtk from "@gtkx/gi/gtk";
2
- export declare const TOPLEVELS: unique symbol;
3
2
  type QueryContainer = Gtk.Widget | Gtk.EventController | Gtk.LayoutManager | Gtk.ListItem | Gtk.ListHeader;
4
3
  /**
5
4
  * A scope that queries and traversal can run against: a widget, an event
6
5
  * controller, a layout manager, a list item or header, an application, or the
7
6
  * sentinel representing all current toplevel windows.
8
7
  */
9
- export type Container = QueryContainer | Gtk.Application | typeof TOPLEVELS;
10
- export declare const descendants: (widget: Gtk.Widget) => Generator<Gtk.Widget>;
11
- export declare const roots: (container: Container) => Generator<Gtk.Widget>;
12
- export declare const traverse: (container: Container) => Generator<Gtk.Widget>;
13
- export declare const findAll: (container: Container, predicate: (node: Gtk.Widget) => boolean) => Gtk.Widget[];
14
- export {};
8
+ type Container = QueryContainer | Gtk.Application | typeof TOPLEVELS;
9
+ declare const TOPLEVELS: unique symbol;
10
+ declare const descendants: (widget: Gtk.Widget) => Generator<Gtk.Widget>;
11
+ declare const roots: (container: Container) => Generator<Gtk.Widget>;
12
+ declare const traverse: (container: Container) => Generator<Gtk.Widget>;
13
+ declare const findAll: (container: Container, isMatch: (node: Gtk.Widget) => boolean) => Gtk.Widget[];
14
+ export { TOPLEVELS, descendants, roots, traverse, findAll, type Container };
15
15
  //# sourceMappingURL=traversal.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"traversal.d.ts","sourceRoot":"","sources":["../src/traversal.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,GAAG,MAAM,cAAc,CAAC;AAEpC,eAAO,MAAM,SAAS,EAAE,OAAO,MAAiC,CAAC;AAEjE,KAAK,cAAc,GAAG,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,eAAe,GAAG,GAAG,CAAC,aAAa,GAAG,GAAG,CAAC,QAAQ,GAAG,GAAG,CAAC,UAAU,CAAC;AAE3G;;;;GAIG;AACH,MAAM,MAAM,SAAS,GAAG,cAAc,GAAG,GAAG,CAAC,WAAW,GAAG,OAAO,SAAS,CAAC;AAc5E,eAAO,MAAM,WAAW,WAAsB,GAAG,CAAC,MAAM,KAAG,SAAS,CAAC,GAAG,CAAC,MAAM,CAI9E,CAAC;AAUF,eAAO,MAAM,KAAK,cAAyB,SAAS,KAAG,SAAS,CAAC,GAAG,CAAC,MAAM,CAO1E,CAAC;AAEF,eAAO,MAAM,QAAQ,cAAyB,SAAS,KAAG,SAAS,CAAC,GAAG,CAAC,MAAM,CAI7E,CAAC;AAEF,eAAO,MAAM,OAAO,cAAe,SAAS,aAAa,CAAC,IAAI,EAAE,GAAG,CAAC,MAAM,KAAK,OAAO,KAAG,GAAG,CAAC,MAAM,EAQlG,CAAC"}
1
+ {"version":3,"file":"traversal.d.ts","sourceRoot":"","sources":["../src/traversal.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,GAAG,MAAM,cAAc,CAAC;AAEpC,KAAK,cAAc,GAAG,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,eAAe,GAAG,GAAG,CAAC,aAAa,GAAG,GAAG,CAAC,QAAQ,GAAG,GAAG,CAAC,UAAU,CAAC;AAC3G;;;;GAIG;AACH,KAAK,SAAS,GAAG,cAAc,GAAG,GAAG,CAAC,WAAW,GAAG,OAAO,SAAS,CAAC;AAErE,QAAA,MAAM,SAAS,EAAE,OAAO,MAAiC,CAAC;AAc1D,QAAA,MAAM,WAAW,GAAc,QAAQ,GAAG,CAAC,MAAM,KAAG,SAAS,CAAC,GAAG,CAAC,MAAM,CAIvE,CAAC;AAsBF,QAAA,MAAM,KAAK,GAAc,WAAW,SAAS,KAAG,SAAS,CAAC,GAAG,CAAC,MAAM,CAYnE,CAAC;AAEF,QAAA,MAAM,QAAQ,GAAc,WAAW,SAAS,KAAG,SAAS,CAAC,GAAG,CAAC,MAAM,CAItE,CAAC;AAEF,QAAA,MAAM,OAAO,GAAI,WAAW,SAAS,EAAE,SAAS,CAAC,IAAI,EAAE,GAAG,CAAC,MAAM,KAAK,OAAO,KAAG,GAAG,CAAC,MAAM,EAUzF,CAAC;AAEF,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,SAAS,EAAE,CAAC"}
package/dist/traversal.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as Gtk from "@gtkx/gi/gtk";
2
- export const TOPLEVELS = Symbol("gtkx.toplevels");
2
+ const TOPLEVELS = Symbol("gtkx.toplevels");
3
3
  const isApplication = (container) => container instanceof Gtk.Application;
4
4
  const traverseWidgetTree = function* (root) {
5
5
  yield root;
@@ -9,43 +9,49 @@ const traverseWidgetTree = function* (root) {
9
9
  child = child.getNextSibling();
10
10
  }
11
11
  };
12
- export const descendants = function* (widget) {
12
+ const descendants = function* (widget) {
13
13
  const tree = traverseWidgetTree(widget);
14
14
  tree.next();
15
15
  yield* tree;
16
16
  };
17
17
  const resolveRoot = (container) => {
18
- if (container instanceof Gtk.Widget)
18
+ if (container instanceof Gtk.Widget) {
19
19
  return container;
20
- if (container instanceof Gtk.EventController)
20
+ }
21
+ if (container instanceof Gtk.EventController) {
21
22
  return container.getWidget();
22
- if (container instanceof Gtk.LayoutManager)
23
+ }
24
+ if (container instanceof Gtk.LayoutManager) {
23
25
  return container.getWidget();
24
- if (container instanceof Gtk.ListItem || container instanceof Gtk.ListHeader)
26
+ }
27
+ if (container instanceof Gtk.ListItem || container instanceof Gtk.ListHeader) {
25
28
  return container.getChild();
29
+ }
26
30
  return null;
27
31
  };
28
- export const roots = function* (container) {
32
+ const roots = function* (container) {
29
33
  if (container === TOPLEVELS || isApplication(container)) {
30
34
  yield* Gtk.Window.listToplevels();
31
35
  return;
32
36
  }
33
37
  const root = resolveRoot(container);
34
- if (root)
38
+ if (root) {
35
39
  yield root;
40
+ }
36
41
  };
37
- export const traverse = function* (container) {
42
+ const traverse = function* (container) {
38
43
  for (const root of roots(container)) {
39
44
  yield* traverseWidgetTree(root);
40
45
  }
41
46
  };
42
- export const findAll = (container, predicate) => {
47
+ const findAll = (container, isMatch) => {
43
48
  const results = [];
44
49
  for (const node of traverse(container)) {
45
- if (predicate(node)) {
50
+ if (isMatch(node)) {
46
51
  results.push(node);
47
52
  }
48
53
  }
49
54
  return results;
50
55
  };
56
+ export { TOPLEVELS, descendants, roots, traverse, findAll };
51
57
  //# sourceMappingURL=traversal.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"traversal.js","sourceRoot":"","sources":["../src/traversal.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,GAAG,MAAM,cAAc,CAAC;AAEpC,MAAM,CAAC,MAAM,SAAS,GAAkB,MAAM,CAAC,gBAAgB,CAAC,CAAC;AAWjE,MAAM,aAAa,GAAG,CAAC,SAAoB,EAAgC,EAAE,CAAC,SAAS,YAAY,GAAG,CAAC,WAAW,CAAC;AAEnH,MAAM,kBAAkB,GAAG,QAAQ,CAAC,EAAE,IAAgB;IAClD,MAAM,IAAI,CAAC;IAEX,IAAI,KAAK,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC;IACjC,OAAO,KAAK,EAAE,CAAC;QACX,KAAK,CAAC,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC;QACjC,KAAK,GAAG,KAAK,CAAC,cAAc,EAAE,CAAC;IACnC,CAAC;AACL,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,WAAW,GAAG,QAAQ,CAAC,EAAE,MAAkB;IACpD,MAAM,IAAI,GAAG,kBAAkB,CAAC,MAAM,CAAC,CAAC;IACxC,IAAI,CAAC,IAAI,EAAE,CAAC;IACZ,KAAK,CAAC,CAAC,IAAI,CAAC;AAChB,CAAC,CAAC;AAEF,MAAM,WAAW,GAAG,CAAC,SAAyB,EAAqB,EAAE;IACjE,IAAI,SAAS,YAAY,GAAG,CAAC,MAAM;QAAE,OAAO,SAAS,CAAC;IACtD,IAAI,SAAS,YAAY,GAAG,CAAC,eAAe;QAAE,OAAO,SAAS,CAAC,SAAS,EAAE,CAAC;IAC3E,IAAI,SAAS,YAAY,GAAG,CAAC,aAAa;QAAE,OAAO,SAAS,CAAC,SAAS,EAAE,CAAC;IACzE,IAAI,SAAS,YAAY,GAAG,CAAC,QAAQ,IAAI,SAAS,YAAY,GAAG,CAAC,UAAU;QAAE,OAAO,SAAS,CAAC,QAAQ,EAAE,CAAC;IAC1G,OAAO,IAAI,CAAC;AAChB,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,KAAK,GAAG,QAAQ,CAAC,EAAE,SAAoB;IAChD,IAAI,SAAS,KAAK,SAAS,IAAI,aAAa,CAAC,SAAS,CAAC,EAAE,CAAC;QACtD,KAAK,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,aAAa,EAAE,CAAC;QAClC,OAAO;IACX,CAAC;IACD,MAAM,IAAI,GAAG,WAAW,CAAC,SAAS,CAAC,CAAC;IACpC,IAAI,IAAI;QAAE,MAAM,IAAI,CAAC;AACzB,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,QAAQ,GAAG,QAAQ,CAAC,EAAE,SAAoB;IACnD,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC;QAClC,KAAK,CAAC,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC;IACpC,CAAC;AACL,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,SAAoB,EAAE,SAAwC,EAAgB,EAAE;IACpG,MAAM,OAAO,GAAiB,EAAE,CAAC;IACjC,KAAK,MAAM,IAAI,IAAI,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;QACrC,IAAI,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;YAClB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACvB,CAAC;IACL,CAAC;IACD,OAAO,OAAO,CAAC;AACnB,CAAC,CAAC","sourcesContent":["import * as Gtk from \"@gtkx/gi/gtk\";\n\nexport const TOPLEVELS: unique symbol = Symbol(\"gtkx.toplevels\");\n\ntype QueryContainer = Gtk.Widget | Gtk.EventController | Gtk.LayoutManager | Gtk.ListItem | Gtk.ListHeader;\n\n/**\n * A scope that queries and traversal can run against: a widget, an event\n * controller, a layout manager, a list item or header, an application, or the\n * sentinel representing all current toplevel windows.\n */\nexport type Container = QueryContainer | Gtk.Application | typeof TOPLEVELS;\n\nconst isApplication = (container: Container): container is Gtk.Application => container instanceof Gtk.Application;\n\nconst traverseWidgetTree = function* (root: Gtk.Widget): Generator<Gtk.Widget> {\n yield root;\n\n let child = root.getFirstChild();\n while (child) {\n yield* traverseWidgetTree(child);\n child = child.getNextSibling();\n }\n};\n\nexport const descendants = function* (widget: Gtk.Widget): Generator<Gtk.Widget> {\n const tree = traverseWidgetTree(widget);\n tree.next();\n yield* tree;\n};\n\nconst resolveRoot = (container: QueryContainer): Gtk.Widget | null => {\n if (container instanceof Gtk.Widget) return container;\n if (container instanceof Gtk.EventController) return container.getWidget();\n if (container instanceof Gtk.LayoutManager) return container.getWidget();\n if (container instanceof Gtk.ListItem || container instanceof Gtk.ListHeader) return container.getChild();\n return null;\n};\n\nexport const roots = function* (container: Container): Generator<Gtk.Widget> {\n if (container === TOPLEVELS || isApplication(container)) {\n yield* Gtk.Window.listToplevels();\n return;\n }\n const root = resolveRoot(container);\n if (root) yield root;\n};\n\nexport const traverse = function* (container: Container): Generator<Gtk.Widget> {\n for (const root of roots(container)) {\n yield* traverseWidgetTree(root);\n }\n};\n\nexport const findAll = (container: Container, predicate: (node: Gtk.Widget) => boolean): Gtk.Widget[] => {\n const results: Gtk.Widget[] = [];\n for (const node of traverse(container)) {\n if (predicate(node)) {\n results.push(node);\n }\n }\n return results;\n};\n"]}
1
+ {"version":3,"file":"traversal.js","sourceRoot":"","sources":["../src/traversal.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,GAAG,MAAM,cAAc,CAAC;AAUpC,MAAM,SAAS,GAAkB,MAAM,CAAC,gBAAgB,CAAC,CAAC;AAE1D,MAAM,aAAa,GAAG,CAAC,SAAoB,EAAgC,EAAE,CAAC,SAAS,YAAY,GAAG,CAAC,WAAW,CAAC;AAEnH,MAAM,kBAAkB,GAAG,QAAQ,CAAC,EAAE,IAAgB;IAClD,MAAM,IAAI,CAAC;IACX,IAAI,KAAK,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC;IAEjC,OAAO,KAAK,EAAE,CAAC;QACX,KAAK,CAAC,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC;QACjC,KAAK,GAAG,KAAK,CAAC,cAAc,EAAE,CAAC;IACnC,CAAC;AACL,CAAC,CAAC;AAEF,MAAM,WAAW,GAAG,QAAQ,CAAC,EAAE,MAAkB;IAC7C,MAAM,IAAI,GAAG,kBAAkB,CAAC,MAAM,CAAC,CAAC;IACxC,IAAI,CAAC,IAAI,EAAE,CAAC;IACZ,KAAK,CAAC,CAAC,IAAI,CAAC;AAChB,CAAC,CAAC;AAEF,MAAM,WAAW,GAAG,CAAC,SAAyB,EAAqB,EAAE;IACjE,IAAI,SAAS,YAAY,GAAG,CAAC,MAAM,EAAE,CAAC;QAClC,OAAO,SAAS,CAAC;IACrB,CAAC;IAED,IAAI,SAAS,YAAY,GAAG,CAAC,eAAe,EAAE,CAAC;QAC3C,OAAO,SAAS,CAAC,SAAS,EAAE,CAAC;IACjC,CAAC;IAED,IAAI,SAAS,YAAY,GAAG,CAAC,aAAa,EAAE,CAAC;QACzC,OAAO,SAAS,CAAC,SAAS,EAAE,CAAC;IACjC,CAAC;IAED,IAAI,SAAS,YAAY,GAAG,CAAC,QAAQ,IAAI,SAAS,YAAY,GAAG,CAAC,UAAU,EAAE,CAAC;QAC3E,OAAO,SAAS,CAAC,QAAQ,EAAE,CAAC;IAChC,CAAC;IAED,OAAO,IAAI,CAAC;AAChB,CAAC,CAAC;AAEF,MAAM,KAAK,GAAG,QAAQ,CAAC,EAAE,SAAoB;IACzC,IAAI,SAAS,KAAK,SAAS,IAAI,aAAa,CAAC,SAAS,CAAC,EAAE,CAAC;QACtD,KAAK,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,aAAa,EAAE,CAAC;QAElC,OAAO;IACX,CAAC;IAED,MAAM,IAAI,GAAG,WAAW,CAAC,SAAS,CAAC,CAAC;IAEpC,IAAI,IAAI,EAAE,CAAC;QACP,MAAM,IAAI,CAAC;IACf,CAAC;AACL,CAAC,CAAC;AAEF,MAAM,QAAQ,GAAG,QAAQ,CAAC,EAAE,SAAoB;IAC5C,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC;QAClC,KAAK,CAAC,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC;IACpC,CAAC;AACL,CAAC,CAAC;AAEF,MAAM,OAAO,GAAG,CAAC,SAAoB,EAAE,OAAsC,EAAgB,EAAE;IAC3F,MAAM,OAAO,GAAiB,EAAE,CAAC;IAEjC,KAAK,MAAM,IAAI,IAAI,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;QACrC,IAAI,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;YAChB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACvB,CAAC;IACL,CAAC;IAED,OAAO,OAAO,CAAC;AACnB,CAAC,CAAC;AAEF,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAkB,CAAC","sourcesContent":["import * as Gtk from \"@gtkx/gi/gtk\";\n\ntype QueryContainer = Gtk.Widget | Gtk.EventController | Gtk.LayoutManager | Gtk.ListItem | Gtk.ListHeader;\n/**\n * A scope that queries and traversal can run against: a widget, an event\n * controller, a layout manager, a list item or header, an application, or the\n * sentinel representing all current toplevel windows.\n */\ntype Container = QueryContainer | Gtk.Application | typeof TOPLEVELS;\n\nconst TOPLEVELS: unique symbol = Symbol(\"gtkx.toplevels\");\n\nconst isApplication = (container: Container): container is Gtk.Application => container instanceof Gtk.Application;\n\nconst traverseWidgetTree = function* (root: Gtk.Widget): Generator<Gtk.Widget> {\n yield root;\n let child = root.getFirstChild();\n\n while (child) {\n yield* traverseWidgetTree(child);\n child = child.getNextSibling();\n }\n};\n\nconst descendants = function* (widget: Gtk.Widget): Generator<Gtk.Widget> {\n const tree = traverseWidgetTree(widget);\n tree.next();\n yield* tree;\n};\n\nconst resolveRoot = (container: QueryContainer): Gtk.Widget | null => {\n if (container instanceof Gtk.Widget) {\n return container;\n }\n\n if (container instanceof Gtk.EventController) {\n return container.getWidget();\n }\n\n if (container instanceof Gtk.LayoutManager) {\n return container.getWidget();\n }\n\n if (container instanceof Gtk.ListItem || container instanceof Gtk.ListHeader) {\n return container.getChild();\n }\n\n return null;\n};\n\nconst roots = function* (container: Container): Generator<Gtk.Widget> {\n if (container === TOPLEVELS || isApplication(container)) {\n yield* Gtk.Window.listToplevels();\n\n return;\n }\n\n const root = resolveRoot(container);\n\n if (root) {\n yield root;\n }\n};\n\nconst traverse = function* (container: Container): Generator<Gtk.Widget> {\n for (const root of roots(container)) {\n yield* traverseWidgetTree(root);\n }\n};\n\nconst findAll = (container: Container, isMatch: (node: Gtk.Widget) => boolean): Gtk.Widget[] => {\n const results: Gtk.Widget[] = [];\n\n for (const node of traverse(container)) {\n if (isMatch(node)) {\n results.push(node);\n }\n }\n\n return results;\n};\n\nexport { TOPLEVELS, descendants, roots, traverse, findAll, type Container };\n"]}