@gtkx/react 1.0.0-rc.2 → 1.0.0-rc.4

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 (173) hide show
  1. package/README.md +4 -5
  2. package/dist/adw/config.d.ts +1 -0
  3. package/dist/adw/config.d.ts.map +1 -1
  4. package/dist/adw/config.js +1 -0
  5. package/dist/adw/config.js.map +1 -1
  6. package/dist/adw/dialog.d.ts +1 -0
  7. package/dist/adw/dialog.d.ts.map +1 -1
  8. package/dist/adw/dialog.js +1 -0
  9. package/dist/adw/dialog.js.map +1 -1
  10. package/dist/adw/element-behaviors.js +8 -39
  11. package/dist/adw/element-behaviors.js.map +1 -1
  12. package/dist/adw/element-config.d.ts +7 -4
  13. package/dist/adw/element-config.d.ts.map +1 -1
  14. package/dist/adw/element-config.js +32 -41
  15. package/dist/adw/element-config.js.map +1 -1
  16. package/dist/adw/index.d.ts +1 -0
  17. package/dist/adw/index.d.ts.map +1 -1
  18. package/dist/adw/index.js +1 -0
  19. package/dist/adw/index.js.map +1 -1
  20. package/dist/adw/prop-types.d.ts +1 -1
  21. package/dist/adw/prop-types.d.ts.map +1 -1
  22. package/dist/adw/prop-types.js.map +1 -1
  23. package/dist/bootstrap.js +1 -1
  24. package/dist/bootstrap.js.map +1 -1
  25. package/dist/components/application.d.ts +1 -0
  26. package/dist/components/application.d.ts.map +1 -1
  27. package/dist/components/application.js +1 -0
  28. package/dist/components/application.js.map +1 -1
  29. package/dist/components/element.d.ts +0 -1
  30. package/dist/components/element.d.ts.map +1 -1
  31. package/dist/components/element.js +0 -1
  32. package/dist/components/element.js.map +1 -1
  33. package/dist/components/window.d.ts +1 -0
  34. package/dist/components/window.d.ts.map +1 -1
  35. package/dist/components/window.js +14 -2
  36. package/dist/components/window.js.map +1 -1
  37. package/dist/config.d.ts +5 -2
  38. package/dist/config.d.ts.map +1 -1
  39. package/dist/config.js +3 -1
  40. package/dist/config.js.map +1 -1
  41. package/dist/element-behaviors.js +0 -1
  42. package/dist/element-behaviors.js.map +1 -1
  43. package/dist/element-config.d.ts +4 -3
  44. package/dist/element-config.d.ts.map +1 -1
  45. package/dist/element-config.js +12 -8
  46. package/dist/element-config.js.map +1 -1
  47. package/dist/hooks/use-bind-setting.d.ts +8 -7
  48. package/dist/hooks/use-bind-setting.d.ts.map +1 -1
  49. package/dist/hooks/use-bind-setting.js +2 -7
  50. package/dist/hooks/use-bind-setting.js.map +1 -1
  51. package/dist/hooks/use-parent-window.d.ts +3 -1
  52. package/dist/hooks/use-parent-window.d.ts.map +1 -1
  53. package/dist/hooks/use-parent-window.js +3 -1
  54. package/dist/hooks/use-parent-window.js.map +1 -1
  55. package/dist/hooks/use-property.d.ts +2 -2
  56. package/dist/hooks/use-property.d.ts.map +1 -1
  57. package/dist/hooks/use-property.js +1 -2
  58. package/dist/hooks/use-property.js.map +1 -1
  59. package/dist/hooks/use-setting.d.ts +2 -3
  60. package/dist/hooks/use-setting.d.ts.map +1 -1
  61. package/dist/hooks/use-setting.js +2 -3
  62. package/dist/hooks/use-setting.js.map +1 -1
  63. package/dist/hooks/use-signal.d.ts +17 -7
  64. package/dist/hooks/use-signal.d.ts.map +1 -1
  65. package/dist/hooks/use-signal.js +10 -8
  66. package/dist/hooks/use-signal.js.map +1 -1
  67. package/dist/index.d.ts +10 -2
  68. package/dist/index.d.ts.map +1 -1
  69. package/dist/index.js +8 -0
  70. package/dist/index.js.map +1 -1
  71. package/dist/internal.d.ts +5 -3
  72. package/dist/internal.d.ts.map +1 -1
  73. package/dist/internal.js +3 -3
  74. package/dist/internal.js.map +1 -1
  75. package/dist/prop-types.d.ts +60 -0
  76. package/dist/prop-types.d.ts.map +1 -1
  77. package/dist/prop-types.js.map +1 -1
  78. package/dist/reconciler/apply-props.d.ts.map +1 -1
  79. package/dist/reconciler/apply-props.js +12 -6
  80. package/dist/reconciler/apply-props.js.map +1 -1
  81. package/dist/reconciler/behaviors.d.ts +0 -15
  82. package/dist/reconciler/behaviors.d.ts.map +1 -1
  83. package/dist/reconciler/behaviors.js +12 -22
  84. package/dist/reconciler/behaviors.js.map +1 -1
  85. package/dist/reconciler/host-config.d.ts +1 -1
  86. package/dist/reconciler/host-config.d.ts.map +1 -1
  87. package/dist/reconciler/host-config.js +2 -6
  88. package/dist/reconciler/host-config.js.map +1 -1
  89. package/dist/reconciler/instance.js +1 -1
  90. package/dist/reconciler/instance.js.map +1 -1
  91. package/dist/reconciler/metadata.d.ts +1 -1
  92. package/dist/reconciler/metadata.d.ts.map +1 -1
  93. package/dist/reconciler/metadata.js +7 -7
  94. package/dist/reconciler/metadata.js.map +1 -1
  95. package/dist/reconciler/node.d.ts +2 -2
  96. package/dist/reconciler/node.d.ts.map +1 -1
  97. package/dist/reconciler/node.js +1 -1
  98. package/dist/reconciler/node.js.map +1 -1
  99. package/dist/reconciler/placement.d.ts.map +1 -1
  100. package/dist/reconciler/placement.js +28 -12
  101. package/dist/reconciler/placement.js.map +1 -1
  102. package/dist/reconciler/registry.d.ts +56 -29
  103. package/dist/reconciler/registry.d.ts.map +1 -1
  104. package/dist/reconciler/registry.js +27 -26
  105. package/dist/reconciler/registry.js.map +1 -1
  106. package/dist/reconciler/root-element.d.ts +0 -1
  107. package/dist/reconciler/root-element.d.ts.map +1 -1
  108. package/dist/reconciler/root-element.js +0 -1
  109. package/dist/reconciler/root-element.js.map +1 -1
  110. package/dist/reconciler/root.d.ts +8 -22
  111. package/dist/reconciler/root.d.ts.map +1 -1
  112. package/dist/reconciler/root.js +5 -23
  113. package/dist/reconciler/root.js.map +1 -1
  114. package/dist/reconciler/signals.d.ts +3 -4
  115. package/dist/reconciler/signals.d.ts.map +1 -1
  116. package/dist/reconciler/signals.js +14 -26
  117. package/dist/reconciler/signals.js.map +1 -1
  118. package/dist/reconciler/text.d.ts.map +1 -1
  119. package/dist/reconciler/text.js +18 -13
  120. package/dist/reconciler/text.js.map +1 -1
  121. package/dist/utils/accessible-metadata.d.ts +0 -7
  122. package/dist/utils/accessible-metadata.d.ts.map +1 -1
  123. package/dist/utils/accessible-metadata.js +0 -7
  124. package/dist/utils/accessible-metadata.js.map +1 -1
  125. package/dist/utils/accessible-props.d.ts +49 -1
  126. package/dist/utils/accessible-props.d.ts.map +1 -1
  127. package/dist/utils/accessible-props.js.map +1 -1
  128. package/dist/utils/ref-prop.d.ts +0 -6
  129. package/dist/utils/ref-prop.d.ts.map +1 -1
  130. package/dist/utils/ref-prop.js +0 -6
  131. package/dist/utils/ref-prop.js.map +1 -1
  132. package/dist/utils/settings.d.ts +6 -0
  133. package/dist/utils/settings.d.ts.map +1 -1
  134. package/dist/utils/settings.js.map +1 -1
  135. package/env.d.ts +11 -4
  136. package/package.json +5 -5
  137. package/src/adw/config.ts +1 -0
  138. package/src/adw/dialog.tsx +1 -0
  139. package/src/adw/element-behaviors.ts +8 -39
  140. package/src/adw/element-config.ts +33 -41
  141. package/src/adw/index.ts +1 -0
  142. package/src/adw/prop-types.ts +1 -1
  143. package/src/bootstrap.ts +1 -1
  144. package/src/components/application.tsx +1 -0
  145. package/src/components/element.tsx +0 -1
  146. package/src/components/window.tsx +16 -2
  147. package/src/config.ts +5 -2
  148. package/src/element-behaviors.ts +0 -1
  149. package/src/element-config.ts +12 -8
  150. package/src/hooks/use-bind-setting.ts +8 -7
  151. package/src/hooks/use-parent-window.ts +3 -1
  152. package/src/hooks/use-property.ts +2 -2
  153. package/src/hooks/use-setting.ts +2 -3
  154. package/src/hooks/use-signal.ts +20 -10
  155. package/src/index.ts +10 -2
  156. package/src/internal.ts +5 -3
  157. package/src/prop-types.ts +86 -5
  158. package/src/reconciler/apply-props.ts +12 -6
  159. package/src/reconciler/behaviors.ts +14 -23
  160. package/src/reconciler/host-config.ts +3 -8
  161. package/src/reconciler/instance.ts +1 -1
  162. package/src/reconciler/metadata.ts +8 -8
  163. package/src/reconciler/node.ts +3 -3
  164. package/src/reconciler/placement.ts +33 -13
  165. package/src/reconciler/registry.ts +70 -41
  166. package/src/reconciler/root-element.ts +0 -1
  167. package/src/reconciler/root.ts +9 -26
  168. package/src/reconciler/signals.ts +16 -37
  169. package/src/reconciler/text.ts +21 -14
  170. package/src/utils/accessible-metadata.ts +0 -7
  171. package/src/utils/accessible-props.ts +49 -1
  172. package/src/utils/ref-prop.ts +0 -6
  173. package/src/utils/settings.ts +6 -0
@@ -6,22 +6,23 @@ import type { SettingsSchema, SettingsSchemaKeys } from "../utils/settings.js";
6
6
  import { type RefProp, resolveRefProp } from "../utils/ref-prop.js";
7
7
  import { useSettings } from "./use-setting.js";
8
8
 
9
+ /** Options for {@link useBindSetting}. */
9
10
  type UseBindSettingOptions<K extends SettingsSchemaKeys> = {
11
+ /** Schema the settings object is opened from. */
10
12
  schema: SettingsSchema<K>;
13
+ /** Key of that schema to bind. */
11
14
  key: keyof K & string;
15
+ /** Object holding the property, given directly or as a ref; nothing is bound while it is absent. */
12
16
  object: RefProp<GObject.Object>;
17
+ /** camelCase name of the property to keep in sync with the key. */
13
18
  property: string;
19
+ /** Direction and conversion behavior of the bind; defaults to `Gio.SettingsBindFlags.DEFAULT`. */
14
20
  flags?: Gio.SettingsBindFlags;
15
21
  };
16
22
 
17
23
  /**
18
- * Binds a GSettings key to a property of a GObject, keeping the two in sync for the object's lifetime.
19
- *
20
- * @param options.schema The schema reference identifying the settings backend.
21
- * @param options.key The key within the schema to bind.
22
- * @param options.object The GObject whose property is bound to the setting.
23
- * @param options.property The name of the object property to bind.
24
- * @param options.flags Flags controlling the binding's direction and behaviour.
24
+ * Binds a GSettings key to a property of a GObject, keeping the two in sync until the component unmounts.
25
+ * `property` is given in camelCase, and `flags` defaults to `Gio.SettingsBindFlags.DEFAULT`, a two-way bind.
25
26
  */
26
27
  function useBindSetting<K extends SettingsSchemaKeys>({
27
28
  schema,
@@ -4,7 +4,9 @@ import { type Context, createContext, use } from "react";
4
4
  const ParentWindowContext: Context<Gtk.Window | null> = createContext<Gtk.Window | null>(null);
5
5
 
6
6
  /**
7
- * Returns the `Gtk.Window` provided by the nearest window ancestor, or `null` when there is none.
7
+ * Returns the `Gtk.Window` of the nearest window ancestor. This is `null` on the first render even inside a
8
+ * window, because a window provides itself only once its GObject exists, and stays `null` outside one. The
9
+ * component re-renders when it resolves, so derive from the value rather than reading it once.
8
10
  */
9
11
  const useParentWindow = (): Gtk.Window | null => use(ParentWindowContext);
10
12
 
@@ -4,14 +4,14 @@ import type { RefProp } from "../utils/ref-prop.js";
4
4
  import { useObjectValue } from "./use-object-value.js";
5
5
 
6
6
  type Properties<T extends GObject.Object> = NonNullable<T["__properties__"]>;
7
+ /** Every property `T` declares that is also readable off the instance, in camelCase. */
7
8
  type PropertyName<T extends GObject.Object> = keyof Properties<T> & keyof T;
8
9
 
9
10
  /**
10
11
  * Subscribes to a GObject property and returns its current value, re-rendering when the property changes.
11
12
  *
12
- * @param object The GObject (or ref to one) whose property to observe.
13
13
  * @param propertyName The camelCase name of a readable property on the object.
14
- * @returns The current property value, or `undefined` when the object is not resolved.
14
+ * @returns The current value, or `undefined` while the object is null or an unresolved ref.
15
15
  */
16
16
  function useProperty<T extends GObject.Object, P extends PropertyName<T>>(
17
17
  object: RefProp<T>,
@@ -16,9 +16,8 @@ const useSettings = <K extends SettingsSchemaKeys>({ id, path }: UseSettingsProp
16
16
  /**
17
17
  * Reads and writes a single key of a GSettings schema, re-rendering when the stored value changes.
18
18
  *
19
- * @param schema The schema reference identifying the settings backend.
20
- * @param key The key within the schema to read and write.
21
- * @returns A tuple of the current value and a setter that persists a new value.
19
+ * @returns The current value, and a setter that writes a new one back to GSettings.
20
+ * @throws When the key is not declared in the schema.
22
21
  */
23
22
  function useSetting<K extends SettingsSchemaKeys, P extends keyof K>(
24
23
  schema: SettingsSchema<K>,
@@ -4,8 +4,13 @@ import { useEffectEvent, useLayoutEffect } from "react";
4
4
  import { type RefProp, resolveRefProp } from "../utils/ref-prop.js";
5
5
 
6
6
  type Signals<T extends GObject.Object> = NonNullable<T["__signals__"]>;
7
+ /** Every signal name `T` declares, on its own or narrowed by a `::detail` suffix. */
7
8
  type SignalName<T extends GObject.Object> = keyof Signals<T> | `${keyof Signals<T> & string}::${string}`;
8
9
 
10
+ /**
11
+ * The handler signature `T` declares for signal `S`, looked up through any `::detail` suffix and
12
+ * falling back to an untyped `SignalHandler` when the object declares no such signal.
13
+ */
9
14
  type TypedSignalHandler<T extends GObject.Object, S extends string> = S extends keyof Signals<T>
10
15
  ? Signals<T>[S]
11
16
  : S extends `${infer TBase}::${string}`
@@ -14,9 +19,12 @@ type TypedSignalHandler<T extends GObject.Object, S extends string> = S extends
14
19
  : SignalHandler
15
20
  : SignalHandler;
16
21
 
22
+ /** Options for {@link useSignal}. */
17
23
  type UseSignalOptions = {
18
- after?: boolean;
19
- immediate?: boolean;
24
+ /** Runs the handler after the object's own default handler rather than before it. */
25
+ isAfter?: boolean;
26
+ /** Invokes the handler once, with no arguments, as soon as the signal is connected. */
27
+ isImmediate?: boolean;
20
28
  };
21
29
 
22
30
  /**
@@ -25,16 +33,18 @@ type UseSignalOptions = {
25
33
  * Each emission runs the handler from the latest render, so it does not have to be stable and a
26
34
  * changing handler never reconnects the signal.
27
35
  *
28
- * @param object The GObject (or ref to one) to connect to.
29
- * @param signal The signal name, optionally with a detail suffix.
30
- * @param handler The callback invoked when the signal is emitted.
31
- * @param options Connection options such as running after the default handler or invoking immediately.
36
+ * On React 19.2 this does not hold inside a component wrapped in `memo` or `forwardRef`, where every emission
37
+ * runs the handler captured on the first render. Keep the calling component unwrapped, or read the values the
38
+ * handler needs off the GObject itself. React fixes this on the 19.3 line.
39
+ *
40
+ * @param signal The signal name, optionally with a `::detail` suffix.
41
+ * @param options `isAfter` runs the handler after the default handler; `isImmediate` also invokes it on connect.
32
42
  */
33
43
  function useSignal<T extends GObject.Object, S extends SignalName<T> & string>(
34
44
  object: RefProp<T>,
35
45
  signal: S,
36
46
  handler: TypedSignalHandler<T, S>,
37
- { after = false, immediate = false }: UseSignalOptions = {},
47
+ { isAfter = false, isImmediate = false }: UseSignalOptions = {},
38
48
  ): void {
39
49
  const emit = useEffectEvent(handler as SignalHandler);
40
50
 
@@ -45,16 +55,16 @@ function useSignal<T extends GObject.Object, S extends SignalName<T> & string>(
45
55
  return;
46
56
  }
47
57
 
48
- resolved.on(signal, emit, after);
58
+ resolved.on(signal, emit, isAfter);
49
59
 
50
- if (immediate) {
60
+ if (isImmediate) {
51
61
  emit();
52
62
  }
53
63
 
54
64
  return () => {
55
65
  resolved.off(signal, emit);
56
66
  };
57
- }, [object, signal, after, immediate]);
67
+ }, [object, signal, isAfter, isImmediate]);
58
68
  }
59
69
 
60
70
  export { useSignal, type SignalName, type TypedSignalHandler };
package/src/index.ts CHANGED
@@ -1,14 +1,22 @@
1
1
  import "./bootstrap.js";
2
2
 
3
+ /** @public */
3
4
  export { useApplication } from "./hooks/use-application.js";
5
+ /** @public */
4
6
  export { useBindSetting } from "./hooks/use-bind-setting.js";
7
+ /** @public */
5
8
  export { useParentWindow } from "./hooks/use-parent-window.js";
9
+ /** @public */
6
10
  export { useProperty } from "./hooks/use-property.js";
11
+ /** @public */
7
12
  export { useSetting } from "./hooks/use-setting.js";
13
+ /** @public */
8
14
  export { useSignal } from "./hooks/use-signal.js";
9
- export type { MenuItem, VflConstraints } from "./prop-types.js";
15
+ /** @public */
10
16
  export { type RootElement, rootElement } from "./reconciler/root-element.js";
17
+ /** @public */
11
18
  export { createPortal, createRoot, quit, type Root } from "./reconciler/root.js";
19
+ /** @public */
12
20
  export type { AccessibleProps } from "./utils/accessible-props.js";
21
+ /** @public */
13
22
  export type { RefProp } from "./utils/ref-prop.js";
14
- export type { SettingsSchema, SettingsSchemaKeys, SettingValue } from "./utils/settings.js";
package/src/internal.ts CHANGED
@@ -1,13 +1,15 @@
1
1
  import "./bootstrap.js";
2
2
 
3
+ /** @internal */
3
4
  export { createApplicationComponent } from "./components/application.js";
4
5
  export { createElementComponent } from "./components/element.js";
6
+ /** @internal */
5
7
  export { createWindowComponent } from "./components/window.js";
6
- export { ApplicationContext } from "./hooks/use-application.js";
7
8
  export { useMergedRef } from "./hooks/use-merged-refs.js";
8
- export { useObjectValue } from "./hooks/use-object-value.js";
9
9
  export type * from "./prop-types.js";
10
+ export type { ModuleExport } from "./reconciler/registry.js";
10
11
  export { isRootElement } from "./reconciler/root-element.js";
11
12
  export { createReconcilerRoot, type ReconcilerRoot, setReconcilerErrorHandler } from "./reconciler/root.js";
13
+ export { applyWrite } from "./reconciler/signals.js";
12
14
  export { getAccessibleMetadata } from "./utils/accessible-metadata.js";
13
- export { type RefProp, resolveRefProp } from "./utils/ref-prop.js";
15
+ export type { SettingsSchema, SettingsSchemaKeys, SettingValue } from "./utils/settings.js";
package/src/prop-types.ts CHANGED
@@ -5,110 +5,191 @@ import type { ReactNode } from "react";
5
5
 
6
6
  /** One entry of a `GMenu`'s `items` prop; `submenu` and `section` nest further menus. */
7
7
  type MenuItem = {
8
+ /** Text shown for the entry, or the heading of the submenu or section it introduces. */
8
9
  label?: string | null;
10
+ /** Detailed action name the entry activates, ignored when `submenu` or `section` is given. */
9
11
  action?: string | null;
12
+ /** Entries of a separate menu the labelled entry opens. */
10
13
  submenu?: MenuItem[];
14
+ /** Entries appended below the label as an inline section, used only when `submenu` is absent. */
11
15
  section?: MenuItem[];
12
16
  };
13
17
 
14
18
  /** One Visual Format Language block applied to a `Gtk.ConstraintLayout`. */
15
19
  type VflConstraints = {
20
+ /** Format lines, each describing every constraint on one row or column. */
16
21
  lines: string[];
22
+ /** Default horizontal spacing the `-` operator stands for; defaults to 0. */
17
23
  hspacing?: number;
24
+ /** Default vertical spacing the `-` operator stands for; defaults to 0. */
18
25
  vspacing?: number;
26
+ /** Targets the view names in `lines` refer to; defaults to none. */
19
27
  views?: Map<string, Gtk.ConstraintTarget>;
20
28
  };
21
29
 
22
30
  /** One mark on a `Gtk.Scale`. */
23
- type ScaleMark = { value?: number; position: Gtk.PositionType; markup?: string | null };
31
+ type ScaleMark = {
32
+ /** Point on the scale the mark is drawn at; defaults to 0. */
33
+ value?: number;
34
+ /** Side of the scale the mark and its label sit on. */
35
+ position: Gtk.PositionType;
36
+ /** Pango markup drawn beside the mark, or `null` for an unlabelled one. */
37
+ markup?: string | null;
38
+ };
39
+
24
40
  /** One labelled offset on a `Gtk.LevelBar`. */
25
- type LevelBarOffset = { name: string; value?: number };
41
+ type LevelBarOffset = {
42
+ /** Style class applied to the bar's blocks while the value falls in this offset's interval. */
43
+ name: string;
44
+ /** Upper bound of that interval; defaults to 0. */
45
+ value?: number;
46
+ };
47
+
26
48
  /** One credit section on a `Gtk.AboutDialog`. */
27
- type CreditSection = { sectionName: string; people: string[] };
49
+ type CreditSection = {
50
+ /** Heading the names are listed under. */
51
+ sectionName: string;
52
+ /** Names listed in the section. */
53
+ people: string[];
54
+ };
55
+
28
56
  /** One accelerator binding on a `Gtk.Application`. */
29
- type ActionAccel = { detailedActionName: string; accels: string[] };
57
+ type ActionAccel = {
58
+ /** Action the accelerators activate, such as `app.quit` or `win.open('file')`. */
59
+ detailedActionName: string;
60
+ /** Accelerators in `Gtk.acceleratorParse` syntax, cleared when the entry goes away. */
61
+ accels: string[];
62
+ };
63
+
30
64
  /** The drag icon of a `Gtk.DragSource`, with its hotspot. */
31
- type DragSourceIcon = { paintable?: Gdk.Paintable | null; hotX?: number; hotY?: number };
65
+ type DragSourceIcon = {
66
+ /** Image shown under the pointer during the drag; defaults to none. */
67
+ paintable?: Gdk.Paintable | null;
68
+ /** Horizontal offset of the pointer within the image; defaults to 0. */
69
+ hotX?: number;
70
+ /** Vertical offset of the pointer within the image; defaults to 0. */
71
+ hotY?: number;
72
+ };
32
73
 
33
74
  /** Props of an element that accepts children. */
34
75
  type ChildrenProps = {
76
+ /** Elements attached to the element's default child slot, or its text for elements that hold text. */
35
77
  children?: ReactNode;
36
78
  };
37
79
 
80
+ /** Objects a widget takes through a method call rather than a property. */
38
81
  type GtkWidgetProps = {
82
+ /** `Gtk.EventController` elements added to the widget. */
39
83
  controllers?: ReactNode | null | undefined;
84
+ /** `Gio.ActionGroup` elements inserted into the widget, each under its own `prefix`. */
40
85
  actionGroups?: ReactNode | null | undefined;
41
86
  } & ChildrenProps;
42
87
 
88
+ /** Props of an action group placed in a widget's `actionGroups` slot. */
43
89
  type GActionGroupProps = {
90
+ /** Prefix the group's actions are addressed by, such as `win`; defaults to the empty string. */
44
91
  prefix?: string | null | undefined;
45
92
  };
46
93
 
94
+ /** Props of an element implementing `Gio.ActionMap`. */
47
95
  type GActionMapProps = {
96
+ /** `Gio.Action` elements added to the map, removed again by their `name`. */
48
97
  actions?: ReactNode | null | undefined;
49
98
  };
50
99
 
100
+ /** Props of a `Gio.Menu` element. */
51
101
  type GMenuProps = {
102
+ /** Entries the menu is rebuilt from whenever they change. */
52
103
  items?: MenuItem[] | null | undefined;
53
104
  };
54
105
 
106
+ /** Props of a `Gtk.ShortcutController` element. */
55
107
  type GtkShortcutControllerProps = {
108
+ /** `Gtk.Shortcut` elements the controller watches for. */
56
109
  shortcuts?: ReactNode | null | undefined;
57
110
  };
58
111
 
112
+ /** Props of a `Gtk.Overlay` element, whose `children` is the widget the overlays sit above. */
59
113
  type GtkOverlayProps = {
114
+ /** Widgets stacked over the main child. */
60
115
  overlays?: ReactNode | null | undefined;
61
116
  } & ChildrenProps;
62
117
 
118
+ /** Props of a `Gtk.ConstraintLayout` element. */
63
119
  type GtkConstraintLayoutProps = {
120
+ /** `Gtk.Constraint` elements added to the layout. */
64
121
  constraints?: ReactNode | null | undefined;
122
+ /** `Gtk.ConstraintGuide` elements added to the layout as invisible spacers. */
65
123
  guides?: ReactNode | null | undefined;
124
+ /** Visual Format Language blocks whose constraints are added alongside `constraints`. */
66
125
  vfl?: VflConstraints[] | null | undefined;
67
126
  };
68
127
 
69
128
  /** Props of `GtkTextChildAnchor`, which embeds either a child widget or a paintable in a text buffer. */
70
129
  type GtkTextChildAnchorProps = {
130
+ /** Image inserted into the buffer instead of an anchored widget; giving both is an error. */
71
131
  paintable?: Gdk.Paintable | null | undefined;
72
132
  } & ChildrenProps;
73
133
 
134
+ /** Props of a `Gtk.HeaderBar` or `Gtk.ActionBar` element. */
74
135
  type GtkHeaderBarProps = {
136
+ /** Widgets packed at the start of the bar. */
75
137
  start?: ReactNode | null | undefined;
138
+ /** Widgets packed at the end of the bar. */
76
139
  end?: ReactNode | null | undefined;
77
140
  };
78
141
 
142
+ /** Props of a `Gtk.Scale` element. */
79
143
  type GtkScaleProps = {
144
+ /** Marks drawn along the scale, cleared and re-added whenever the list changes. */
80
145
  marks?: ScaleMark[] | null | undefined;
81
146
  };
82
147
 
148
+ /** Props of a `Gtk.Calendar` element. */
83
149
  type GtkCalendarProps = {
150
+ /** Days of the shown month drawn as marked, cleared and re-marked whenever the list changes. */
84
151
  markedDays?: number[] | null | undefined;
85
152
  };
86
153
 
154
+ /** Props of a `Gtk.LevelBar` element. */
87
155
  type GtkLevelBarProps = {
156
+ /** Offsets that split the bar's range into differently styled intervals. */
88
157
  offsets?: LevelBarOffset[] | null | undefined;
89
158
  };
90
159
 
160
+ /** Props of a `Gtk.SizeGroup` element. */
91
161
  type GtkSizeGroupProps = {
162
+ /** Widgets the group keeps at a common size. */
92
163
  widgets?: Gtk.Widget[] | null | undefined;
93
164
  };
94
165
 
166
+ /** Props of a `Gtk.AboutDialog` element. */
95
167
  type GtkAboutDialogProps = {
168
+ /** Extra sections appended to the dialog's credits, which GTK offers no way to remove. */
96
169
  creditSections?: CreditSection[] | null | undefined;
97
170
  };
98
171
 
172
+ /** Props of a `Gtk.Application` element, whose `children` are the windows it owns. */
99
173
  type GtkApplicationProps = {
174
+ /** Accelerators bound to the application's actions. */
100
175
  actionAccels?: ActionAccel[] | null | undefined;
101
176
  } & ChildrenProps;
102
177
 
178
+ /** Props of a `Gtk.DropTarget` element. */
103
179
  type GtkDropTargetProps = {
180
+ /** GTypes the target accepts a drop of. */
104
181
  types?: GObject.Type[] | null | undefined;
105
182
  };
106
183
 
184
+ /** Props of a `Gtk.DrawingArea` element. */
107
185
  type GtkDrawingAreaProps = {
186
+ /** Callback that draws the area's contents; setting it queues a redraw. */
108
187
  drawFunc?: Gtk.DrawingAreaDrawFunc | null | undefined;
109
188
  };
110
189
 
190
+ /** Props of a `Gtk.DragSource` element. */
111
191
  type GtkDragSourceProps = {
192
+ /** Icon shown under the pointer while a drag started from this source is in flight. */
112
193
  icon?: DragSourceIcon | null | undefined;
113
194
  };
114
195
 
@@ -6,7 +6,7 @@ import type { ElementBehavior, Props } from "./registry.js";
6
6
  import { applyAccessibleProps, isAccessibleProp } from "../utils/accessible-props.js";
7
7
  import { type TypeInfo, typeInfoFor } from "./metadata.js";
8
8
  import { type ElementNode, getOrCreateContext, type SignalTarget } from "./node.js";
9
- import { connectHandler, disconnectHandler } from "./signals.js";
9
+ import { applyWrite, connectHandler, disconnectHandler } from "./signals.js";
10
10
  import { bufferText, hasSameText, isContentPaintableProp, markTextDirty, TEXT_PROP } from "./text.js";
11
11
 
12
12
  type PropDelta = { name: string; value: unknown; prevValue: unknown };
@@ -43,7 +43,9 @@ const writeValue = (object: GObject.Object, name: string, value: unknown): void
43
43
  return;
44
44
  }
45
45
 
46
- Reflect.set(object, name, value);
46
+ applyWrite(() => {
47
+ Reflect.set(object, name, value);
48
+ });
47
49
  };
48
50
 
49
51
  const resetPlain = (object: GObject.Object, info: TypeInfo, name: string): void => {
@@ -65,9 +67,11 @@ const applyBufferText = (buffer: Gtk.TextBuffer, text: string): void => {
65
67
  return;
66
68
  }
67
69
 
68
- buffer.beginIrreversibleAction();
69
- buffer.setText(text, -1);
70
- buffer.endIrreversibleAction();
70
+ applyWrite(() => {
71
+ buffer.beginIrreversibleAction();
72
+ buffer.setText(text, -1);
73
+ buffer.endIrreversibleAction();
74
+ });
71
75
  };
72
76
 
73
77
  const isBufferText = (node: ElementNode, name: string): node is ElementNode & { object: Gtk.TextBuffer } =>
@@ -169,7 +173,9 @@ const restoreActionableSensitivity = (node: ElementNode, info: TypeInfo, prev: P
169
173
  const desired = "sensitive" in next ? next.sensitive : info.defaults.sensitive;
170
174
 
171
175
  if (typeof desired === "boolean") {
172
- Reflect.set(node.object, "sensitive", desired);
176
+ applyWrite(() => {
177
+ Reflect.set(node.object, "sensitive", desired);
178
+ });
173
179
  }
174
180
  };
175
181
 
@@ -3,6 +3,7 @@ import type * as Gtk from "@gtkx/gi/gtk";
3
3
  import { getInstanceType, TYPE_INVALID, typeFromName, typeIsA } from "@gtkx/runtime";
4
4
  import { getOrInsert, isDeepEqual, structuredClone } from "@gtkx/utils";
5
5
  import type { DetachInfo, ElementBehavior, PlaceInfo, Props } from "./registry.js";
6
+ import { applyWrite } from "./signals.js";
6
7
  import { hasSameText } from "./text.js";
7
8
 
8
9
  type SlotHooks<P extends GObject.Object, C extends GObject.Object> = {
@@ -22,7 +23,7 @@ type ListHooks<P extends GObject.Object, I, H> = {
22
23
 
23
24
  type ListEntry = { item: unknown; handle: unknown };
24
25
  type ListState = { snapshot: unknown[]; entries: ListEntry[] };
25
- type DeferredState = { desired: unknown; present: boolean; applied: unknown };
26
+ type DeferredState = { desired: unknown; isPresent: boolean; applied: unknown };
26
27
  type CanApply<P extends GObject.Object, V> = (object: P, value: V) => boolean;
27
28
  type ChildSetter = GObject.Object & { setChild: (child: Gtk.Widget | null) => void };
28
29
  type ContentSetter<C extends Gtk.Widget> = GObject.Object & { setContent: (content: C | null) => void };
@@ -66,7 +67,6 @@ const slotAttach =
66
67
  return attach(object as P, child as C, info) ?? true;
67
68
  };
68
69
 
69
- /** Builds a behavior for a named child slot holding children of `childType`, claiming matches only. */
70
70
  const slot = <P extends GObject.Object, C extends GObject.Object>(
71
71
  slotName: string,
72
72
  childType: string,
@@ -93,7 +93,6 @@ const slot = <P extends GObject.Object, C extends GObject.Object>(
93
93
  return behavior;
94
94
  };
95
95
 
96
- /** Builds a scalar-prop behavior that invokes `apply` whenever the value changes, and claims the prop. */
97
96
  const value = <P extends GObject.Object, V>(
98
97
  prop: string,
99
98
  apply: ValueApply<P, V>,
@@ -129,11 +128,6 @@ const teardownList = <P extends GObject.Object, I, H>(
129
128
  }
130
129
  };
131
130
 
132
- /**
133
- * Builds an array-prop behavior that adds, removes, and clears its items, reapplying on structural
134
- * change. `add` may return a handle that the same item's later `remove` receives, for items whose
135
- * teardown needs what `add` produced (as VFL constraints need the objects the layout created).
136
- */
137
131
  const list = <P extends GObject.Object, I, H = void>(
138
132
  prop: string,
139
133
  hooks: ListHooks<P, I, H>,
@@ -141,7 +135,7 @@ const list = <P extends GObject.Object, I, H = void>(
141
135
  const { add } = hooks;
142
136
 
143
137
  return {
144
- createContext: (): ListState => ({ snapshot: [], entries: [] }),
138
+ initialize: (): ListState => ({ snapshot: [], entries: [] }),
145
139
  update: (object, _prev, next, context) => {
146
140
  const state = context as ListState;
147
141
  const raw = next[prop];
@@ -168,7 +162,7 @@ const flushDeferred = <P extends GObject.Object, V>(
168
162
  ): void => {
169
163
  const state = context as DeferredState;
170
164
 
171
- if (!state.present || Object.is(state.applied, state.desired)) {
165
+ if (!state.isPresent || Object.is(state.applied, state.desired)) {
172
166
  return;
173
167
  }
174
168
 
@@ -176,21 +170,23 @@ const flushDeferred = <P extends GObject.Object, V>(
176
170
  return;
177
171
  }
178
172
 
179
- Reflect.set(object, prop, state.desired);
173
+ applyWrite(() => {
174
+ Reflect.set(object, prop, state.desired);
175
+ });
176
+
180
177
  state.applied = state.desired;
181
178
  };
182
179
 
183
- /** Builds a behavior for a prop applied after the surrounding commit, deferred until `canApply` returns true. */
184
180
  const deferred = <P extends GObject.Object, V>(
185
181
  prop: string,
186
182
  canApply?: CanApply<P, V>,
187
183
  ): ElementBehavior<P> => ({
188
184
  deferred: [prop],
189
- createContext: (): DeferredState => ({ desired: undefined, present: false, applied: undefined }),
185
+ initialize: (): DeferredState => ({ desired: undefined, isPresent: false, applied: undefined }),
190
186
  update: (_object, _prev, next, context) => {
191
187
  const state = context as DeferredState;
192
188
  state.desired = next[prop];
193
- state.present = next[prop] !== undefined;
189
+ state.isPresent = next[prop] !== undefined;
194
190
 
195
191
  return [prop];
196
192
  },
@@ -199,15 +195,15 @@ const deferred = <P extends GObject.Object, V>(
199
195
  },
200
196
  });
201
197
 
202
- /** Builds a behavior for a text prop kept in controlled-input sync: set when provided, never reset. */
203
198
  const controlledText = (prop: string): ElementBehavior =>
204
199
  value(prop, (object, next) => {
205
200
  if (!hasSameText(object, prop, next)) {
206
- Reflect.set(object, prop, next);
201
+ applyWrite(() => {
202
+ Reflect.set(object, prop, next);
203
+ });
207
204
  }
208
205
  });
209
206
 
210
- /** Behavior for a container that installs its single child with `setChild`. */
211
207
  const childSetterSlot = <P extends ChildSetter>(): ElementBehavior<P> =>
212
208
  slot<P, Gtk.Widget>("children", "GtkWidget", {
213
209
  attach: (parent, child) => {
@@ -218,7 +214,6 @@ const childSetterSlot = <P extends ChildSetter>(): ElementBehavior<P> =>
218
214
  },
219
215
  });
220
216
 
221
- /** Behavior for a container that installs its single child with `setContent`. */
222
217
  const contentSetterSlot = <P extends ContentSetter<C>, C extends Gtk.Widget = Gtk.Widget>(
223
218
  childType = "GtkWidget",
224
219
  ): ElementBehavior<P> =>
@@ -231,7 +226,6 @@ const contentSetterSlot = <P extends ContentSetter<C>, C extends Gtk.Widget = Gt
231
226
  },
232
227
  });
233
228
 
234
- /** Behavior for a `GtkBox`-style container that orders children by sibling. */
235
229
  const boxSlot = <P extends BoxLike>(): ElementBehavior<P> =>
236
230
  slot<P, Gtk.Widget>("children", "GtkWidget", {
237
231
  attach: (box, child, info) => box.insertChildAfter(child, info.sibling as Gtk.Widget | null),
@@ -243,7 +237,6 @@ const boxSlot = <P extends BoxLike>(): ElementBehavior<P> =>
243
237
  },
244
238
  });
245
239
 
246
- /** Behavior for a container whose children are added and removed by a pair of methods. */
247
240
  const addRemoveSlot = <C extends GObject.Object, P extends GObject.Object>(
248
241
  slotName: string,
249
242
  childType: string,
@@ -251,7 +244,6 @@ const addRemoveSlot = <C extends GObject.Object, P extends GObject.Object>(
251
244
  remove: (parent: P, child: C) => void,
252
245
  ): ElementBehavior => slot<P, C>(slotName, childType, { attach: add, detach: remove });
253
246
 
254
- /** Behavior for a `children` slot whose attach call returns the page object the container adopts. */
255
247
  const adoptedChildrenSlot = <P extends GObject.Object, C extends GObject.Object>(
256
248
  childType: string,
257
249
  add: (parent: P, item: C) => unknown,
@@ -294,7 +286,6 @@ const removeWrappedRow = (
294
286
  }
295
287
  };
296
288
 
297
- /** Behavior for an index-placed container that wraps each child in `Wrapper` before adding it. */
298
289
  const wrappingIndexedSlot = <W extends Gtk.Widget, P extends IndexedInserter>(
299
290
  Wrapper: new (props: Props) => W,
300
291
  setChild: (wrapper: W, inner: Gtk.Widget) => void,
@@ -306,7 +297,7 @@ const wrappingIndexedSlot = <W extends Gtk.Widget, P extends IndexedInserter>(
306
297
  removeWrappedRow(Wrapper, parent, child, info.context as RowCache);
307
298
  },
308
299
  }),
309
- createContext: (): RowCache => new WeakMap(),
300
+ initialize: (): RowCache => new WeakMap(),
310
301
  });
311
302
 
312
303
  export {
@@ -32,7 +32,7 @@ import {
32
32
  type TextNode,
33
33
  } from "./node.js";
34
34
  import { isRootElement, type RootElement } from "./root-element.js";
35
- import { beginSuppression, disconnectAllHandlers, endSuppression } from "./signals.js";
35
+ import { disconnectAllHandlers } from "./signals.js";
36
36
  import {
37
37
  didUpdateTextSurgically,
38
38
  enclosingHost,
@@ -41,7 +41,7 @@ import {
41
41
  validateContentMix,
42
42
  } from "./text.js";
43
43
 
44
- /** A top-level container an element tree can be mounted into. */
44
+ /** What a tree is rendered into: any GObject, or the {@link RootElement} marker to render at the top level. */
45
45
  type Container = RootElement | GObject.Object;
46
46
 
47
47
  type PriorityTracker = {
@@ -88,15 +88,10 @@ const hostConfig = {
88
88
  getRootHostContext: (): Record<string, never> => HOST_CONTEXT,
89
89
  getChildHostContext: (parent: Record<string, never>): Record<string, never> => parent,
90
90
  getPublicInstance: (instance: Instance): object => getPublicInstance(instance),
91
- prepareForCommit: (): null => {
92
- beginSuppression();
93
-
94
- return null;
95
- },
91
+ prepareForCommit: (): null => null,
96
92
  resetAfterCommit: (): void => {
97
93
  flushTextHosts();
98
94
  flushBehaviors();
99
- endSuppression();
100
95
  },
101
96
  preparePortalMount: (): void => undefined,
102
97
  clearContainer: (): void => undefined,
@@ -69,7 +69,7 @@ const createObject = (typeName: string, type: bigint, input: Props): GObject.Obj
69
69
  const resolveElementNode = (typeName: string, props: Props, dispatch: Dispatch): ElementNode | LazyNode => {
70
70
  const info = typeInfoFor(typeName);
71
71
 
72
- if (info.lazy) {
72
+ if (info.isLazy) {
73
73
  return createLazyNode(typeName, props, dispatch);
74
74
  }
75
75