react-x11 1.2.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (158) hide show
  1. package/README.md +330 -81
  2. package/package.json +132 -14
  3. package/src/ClickToComponent.js +9 -2
  4. package/src/DevToolsIntegration.js +440 -32
  5. package/src/Reconciler.js +463 -165
  6. package/src/a11y.js +1299 -0
  7. package/src/acceleratorhooks.js +86 -0
  8. package/src/accelerators.js +128 -0
  9. package/src/activate.js +168 -0
  10. package/src/anchor.js +360 -0
  11. package/src/appcontext.js +161 -0
  12. package/src/appearance.js +748 -0
  13. package/src/appearancehooks.js +96 -0
  14. package/src/apphooks.js +73 -0
  15. package/src/application.js +748 -0
  16. package/src/atspi.js +1953 -0
  17. package/src/bus.js +545 -0
  18. package/src/bushooks.js +136 -0
  19. package/src/clientmessage.js +140 -0
  20. package/src/clipboard.js +121 -0
  21. package/src/components/Button.js +148 -33
  22. package/src/components/Checkbox.js +66 -32
  23. package/src/components/Dialog.js +96 -29
  24. package/src/components/FileDialog.js +491 -0
  25. package/src/components/Icon.js +329 -0
  26. package/src/components/Menu.js +797 -133
  27. package/src/components/PasswordInput.js +434 -0
  28. package/src/components/ProgressBar.js +91 -12
  29. package/src/components/Radio.js +78 -35
  30. package/src/components/Select.js +255 -65
  31. package/src/components/Slider.js +102 -41
  32. package/src/components/SplitPane.js +206 -0
  33. package/src/components/Switch.js +100 -22
  34. package/src/components/Table.js +529 -0
  35. package/src/components/Tabs.js +211 -0
  36. package/src/components/Tooltip.js +528 -54
  37. package/src/components/anchor.js +154 -107
  38. package/src/components/change.js +34 -0
  39. package/src/components/dnd.js +103 -0
  40. package/src/components/index.js +16 -3
  41. package/src/components/keys.js +17 -20
  42. package/src/components/scribble.js +247 -0
  43. package/src/components/theme.js +350 -42
  44. package/src/components/typeahead.js +12 -2
  45. package/src/compose.js +868 -0
  46. package/src/compositing.js +224 -0
  47. package/src/dbusmenu.js +384 -0
  48. package/src/debug.d.ts +63 -0
  49. package/src/debug.js +642 -0
  50. package/src/decorations.js +486 -0
  51. package/src/desktopsettings.js +209 -0
  52. package/src/desktopsettingshooks.js +62 -0
  53. package/src/dnd.js +1725 -0
  54. package/src/editmenu.js +272 -0
  55. package/src/errors.js +98 -0
  56. package/src/events.js +1406 -162
  57. package/src/extensions.js +45 -0
  58. package/src/filedialog.js +375 -0
  59. package/src/filedialoghooks.js +132 -0
  60. package/src/fonthooks.js +64 -0
  61. package/src/fonts.js +301 -0
  62. package/src/foreignnodes.js +519 -0
  63. package/src/frame/child.js +33 -0
  64. package/src/frame/childmain.js +274 -0
  65. package/src/frame/env.js +140 -0
  66. package/src/frame/index.js +444 -0
  67. package/src/frame/lifecycle.js +67 -0
  68. package/src/frame/protocol.js +179 -0
  69. package/src/frames.js +73 -0
  70. package/src/glbackend.js +114 -0
  71. package/src/glnodes.js +125 -67
  72. package/src/globalmenu.js +683 -0
  73. package/src/host.d.ts +88 -0
  74. package/src/host.js +42 -0
  75. package/src/idle.js +490 -0
  76. package/src/idlehooks.js +100 -0
  77. package/src/imagesource.js +349 -0
  78. package/src/index.d.ts +475 -0
  79. package/src/index.js +83 -9
  80. package/src/inputtime.js +206 -0
  81. package/src/jsx-dev-runtime.d.ts +21 -0
  82. package/src/jsx-dev-runtime.js +2 -0
  83. package/src/jsx-runtime.d.ts +39 -0
  84. package/src/jsx-runtime.js +6 -0
  85. package/src/keyboard.js +256 -0
  86. package/src/keyboardstate.js +278 -0
  87. package/src/keyboardstatehooks.js +58 -0
  88. package/src/keysyms.d.ts +149 -0
  89. package/src/keysyms.js +270 -0
  90. package/src/locale.js +170 -0
  91. package/src/localehooks.js +47 -0
  92. package/src/menuitem.js +223 -0
  93. package/src/node.d.ts +597 -0
  94. package/src/node.js +44 -0
  95. package/src/nodes.js +9546 -690
  96. package/src/ntk.d.ts +44 -0
  97. package/src/ntk.js +25 -0
  98. package/src/paintcache.js +366 -0
  99. package/src/palette.js +380 -0
  100. package/src/pastestate.js +66 -0
  101. package/src/portal.js +461 -0
  102. package/src/priority.js +26 -0
  103. package/src/refresh/index.d.ts +40 -0
  104. package/src/refresh/index.js +122 -0
  105. package/src/refresh/loader.d.ts +37 -0
  106. package/src/refresh/loader.js +401 -0
  107. package/src/refresh/register.d.ts +5 -0
  108. package/src/refresh/register.js +13 -0
  109. package/src/registry.js +232 -0
  110. package/src/scale.js +626 -0
  111. package/src/scalehooks.js +27 -0
  112. package/src/screencolor.js +640 -0
  113. package/src/screencolorhooks.js +101 -0
  114. package/src/screens.js +754 -0
  115. package/src/screenshooks.js +137 -0
  116. package/src/startup.js +302 -0
  117. package/src/style.d.ts +126 -0
  118. package/src/style.js +33 -0
  119. package/src/styles.js +1482 -6
  120. package/src/svgnodes.js +306 -0
  121. package/src/testing/a11y.js +484 -0
  122. package/src/testing/components.js +414 -0
  123. package/src/testing/events.js +407 -0
  124. package/src/testing/harness.js +455 -0
  125. package/src/testing/index.d.ts +558 -0
  126. package/src/testing/index.js +85 -0
  127. package/src/testing/mock-app.js +463 -0
  128. package/src/testing/pixels.js +152 -0
  129. package/src/testing/queries.js +224 -0
  130. package/src/textrange.js +83 -0
  131. package/src/textselection.js +439 -0
  132. package/src/trace-registry.js +63 -0
  133. package/src/transfer.js +93 -0
  134. package/src/types/appearance.d.ts +84 -0
  135. package/src/types/application.d.ts +173 -0
  136. package/src/types/components.d.ts +820 -0
  137. package/src/types/dbus.d.ts +177 -0
  138. package/src/types/elements.d.ts +897 -0
  139. package/src/types/events.d.ts +549 -0
  140. package/src/types/filedialog.d.ts +260 -0
  141. package/src/types/fonts.d.ts +124 -0
  142. package/src/types/frame.d.ts +146 -0
  143. package/src/types/globalmenu.d.ts +42 -0
  144. package/src/types/nodes.d.ts +199 -0
  145. package/src/types/screencolor.d.ts +84 -0
  146. package/src/types/style.d.ts +434 -0
  147. package/src/types/system.d.ts +287 -0
  148. package/src/windowid.js +151 -0
  149. package/src/windowstate.js +393 -0
  150. package/src/xsettings.js +336 -0
  151. package/src/yoga.d.ts +55 -0
  152. package/src/yoga.js +135 -0
  153. package/src/components/Canvas3D.js +0 -28
  154. package/src/geometry3d.js +0 -223
  155. package/src/pointer3d.js +0 -158
  156. package/src/raycast3d.js +0 -146
  157. package/src/richnodes.js +0 -436
  158. package/src/scene3d.js +0 -683
package/src/node.d.ts ADDED
@@ -0,0 +1,597 @@
1
+ /**
2
+ * `react-x11/node` — the base class a registered element subclasses.
3
+ *
4
+ * Typed as the surface a subclass legitimately uses. It is deliberately
5
+ * narrower than the runtime class: everything underscore-prefixed is
6
+ * internal and may change in a patch release. docs/extending.md is the
7
+ * contract in prose.
8
+ */
9
+ import type {
10
+ Rect,
11
+ NtkApp,
12
+ TextPosition,
13
+ TextSelectionSnapshot,
14
+ } from './types/nodes.js';
15
+ import type {
16
+ FontStyle,
17
+ FontWeight,
18
+ Style,
19
+ TextRendering,
20
+ } from './types/style.js';
21
+ import type {
22
+ CompositionEvent,
23
+ KeyboardEvent,
24
+ MouseEvent,
25
+ WheelEvent,
26
+ } from './types/events.js';
27
+
28
+ /** ntk's 2d context. Typed loosely — it is ntk's API, not ours. */
29
+ export type Context2D = unknown;
30
+
31
+ /** How an axis is bounded when layout asks an element for its size.
32
+ * `'exactly'` — the style decided this axis; `'at-most'` — that many pixels
33
+ * are on offer; `'unconstrained'` — nothing bounds it. */
34
+ export type MeasureMode = 'exactly' | 'at-most' | 'unconstrained';
35
+
36
+ /** The question `measureContent` answers. */
37
+ export interface MeasureConstraints {
38
+ /** Pixels on offer across, per `widthMode`. `Infinity` when unbounded, so
39
+ * `Math.min(preferred, width)` is right in every mode. */
40
+ width: number;
41
+ /** Pixels on offer down, per `heightMode`. `Infinity` when unbounded. */
42
+ height: number;
43
+ widthMode: MeasureMode;
44
+ heightMode: MeasureMode;
45
+ }
46
+
47
+ /** What `measureContent` answers with. */
48
+ export interface MeasuredSize {
49
+ width: number;
50
+ height: number;
51
+ }
52
+
53
+ /**
54
+ * A resolved text style — the base style `app.fonts.layout` takes, which is
55
+ * ntk's vocabulary rather than the style vocabulary: `family` for
56
+ * `fontFamily`, `variations` for `fontVariationSettings`.
57
+ */
58
+ export interface TextStyle {
59
+ /** A CSS-style family list, as `fontFamily` is written. */
60
+ family: string;
61
+ size: number;
62
+ weight: FontWeight;
63
+ style: FontStyle;
64
+ /** A variable font's axes, `{ wght: 460 }` — undefined unless a style or
65
+ * the cascade above it named one. */
66
+ variations: Record<string, number> | undefined;
67
+ textRendering: TextRendering | undefined;
68
+ color: string;
69
+ }
70
+
71
+ /**
72
+ * What an element holding text tells an assistive technology
73
+ * (`a11yTextState`). Offsets are **code points**, counted the way
74
+ * `Array.from(value).length` counts, and index `value` — the string the
75
+ * element *draws*, an open composition included.
76
+ */
77
+ export interface A11yTextState {
78
+ /** The text as drawn. */
79
+ value: string;
80
+ /** Where the caret is. Defaults to `selectionEnd`. */
81
+ caret?: number;
82
+ /** The selected range; equal offsets mean a bare caret. Default 0. */
83
+ selectionStart?: number;
84
+ selectionEnd?: number;
85
+ /** Whether this is text the user edits — an editor rather than a viewer.
86
+ * Sets the EDITABLE state and, with `a11yReplaceText`, the AT-SPI
87
+ * EditableText interface. */
88
+ editable?: boolean;
89
+ /** One line or many. Left unsaid, neither state is claimed rather than
90
+ * guessed from the current value. */
91
+ multiline?: boolean;
92
+ /** The part of `value` that is an open composition — a dead key, a
93
+ * half-typed Compose sequence — so a reader can tell it from the
94
+ * character it commits. `offset` is where it starts in `value`. */
95
+ preedit?: { offset: number; text: string } | null;
96
+ }
97
+
98
+ /**
99
+ * One thing an element drew, as an assistive technology should meet it
100
+ * (`a11yScene`). Everything but `id` and `rect` is optional, and what is
101
+ * left out is simply not claimed.
102
+ */
103
+ export interface A11ySceneItem {
104
+ /** This item's name for the whole time it is on screen. Identity is
105
+ * matched on it frame to frame, so a fresh id per frame is a child that
106
+ * is destroyed and recreated under every screen reader holding it. */
107
+ id: string;
108
+ /** Where it is drawn, in the owning window's coordinates — the same
109
+ * space as `abs` and a mouse event's `x`/`y`. Where AT focus draws and
110
+ * what a magnifier follows. */
111
+ rect: Rect;
112
+ /** The ARIA role it plays. `'group'` when it says none, which is
113
+ * audible and promises nothing. A role that promises activation
114
+ * (`button`, `option`, `treeitem`, …) gives it an AT-SPI action, and so
115
+ * does the element implementing `a11ySceneAction`. */
116
+ role?: string;
117
+ /** What a screen reader says. Anything unnamed is announced as having no
118
+ * accessible name, which is the defect being loud rather than hidden. */
119
+ name?: string;
120
+ description?: string;
121
+ /** Whether an AT may put its focus here. Default true. */
122
+ focusable?: boolean;
123
+ states?: {
124
+ selected?: boolean;
125
+ checked?: boolean | 'mixed';
126
+ expanded?: boolean;
127
+ disabled?: boolean;
128
+ busy?: boolean;
129
+ /** The element's own keyboard cursor — the item arrow keys would act
130
+ * on. Element-internal focus never reaches the window's focus manager,
131
+ * so this is the only way it is reported. */
132
+ focused?: boolean;
133
+ };
134
+ /** Anything else this item would carry as an element: `aria-level`,
135
+ * `aria-posinset`/`aria-setsize` (Orca's "3 of 7"), `aria-valuenow`,
136
+ * `aria-keyshortcuts`, `aria-hidden`. Read exactly as they are on a
137
+ * `<box>`; the fields above win where they overlap. */
138
+ props?: Record<string, unknown>;
139
+ /** A scene with structure — series and points, groups and nodes. Ids are
140
+ * matched within their parent, so they only have to be unique there. */
141
+ children?: A11ySceneItem[];
142
+ }
143
+
144
+ /** What an assistive technology asks an element to do with one of the
145
+ * things it drew. */
146
+ export type A11ySceneAction = 'activate' | 'focus' | 'scroll';
147
+
148
+ /**
149
+ * The `measureContent` body for content with a natural size and an aspect
150
+ * ratio to keep — `<image>` and `<svg>` are written on it:
151
+ *
152
+ * ```js
153
+ * measureContent(constraints) {
154
+ * return intrinsicSize({ width: 320, height: 200 }, constraints);
155
+ * }
156
+ * ```
157
+ */
158
+ export declare function intrinsicSize(
159
+ natural: MeasuredSize,
160
+ constraints: MeasureConstraints,
161
+ ): MeasuredSize;
162
+
163
+ /**
164
+ * How long a caret stays in each of its two states, in milliseconds — what
165
+ * `<textinput>` blinks at. An element that draws its own caret uses this
166
+ * rather than a number of its own, so two carets on one screen are in step.
167
+ */
168
+ export declare const CARET_BLINK_MS: number;
169
+
170
+ export declare class Node {
171
+ constructor(
172
+ kind: string,
173
+ props: Record<string, unknown>,
174
+ app: NtkApp,
175
+ options?: { yoga?: boolean },
176
+ );
177
+
178
+ /** Element name. Must equal the name it was registered under. */
179
+ readonly kind: string;
180
+ /** The current props. Replaced wholesale by `applyProps`. */
181
+ readonly props: Record<string, unknown>;
182
+ /** The ntk connection this tree renders through. */
183
+ readonly app: NtkApp;
184
+ readonly parent: Node | null;
185
+ readonly children: Node[];
186
+ /** The owning `<window>` node, once attached. */
187
+ readonly root: Node | null;
188
+ readonly destroyed: boolean;
189
+ /** Position and size within the owning window, valid after layout. */
190
+ readonly abs: Rect;
191
+ /** `abs` inset by the border and the padding — where the content goes,
192
+ * and where every built-in draws. The insets come off the layout, which
193
+ * is where percentages and the per-side overrides have already been
194
+ * resolved, so this is not arithmetic to redo from `style`. */
195
+ contentBox(): Rect;
196
+ /** The text style this node resolves to, in the shape `app.fonts.layout`
197
+ * takes: the style's font properties over what the palette says text
198
+ * with none of its own is set in. An element that draws text inherits
199
+ * exactly what `<text>` inherits by asking. */
200
+ resolvedTextStyle(): TextStyle;
201
+ /** The flattened `style` prop with the active state blocks overlaid.
202
+ * Everything that paints or lays out reads this, never `props`. */
203
+ readonly style: Style;
204
+ /** The nearest `theme` at or above this node. */
205
+ readonly theme: Record<string, unknown> | null;
206
+ /** Which way this node reads, resolved — `'ltr'` or `'rtl'`, never
207
+ * `'inherit'`. Yoga has already mirrored the boxes, so this is for what an
208
+ * element draws *inside* one: which side a mark sits on, which way a
209
+ * chevron points, what a pointer coordinate means. */
210
+ readonly direction: 'ltr' | 'rtl';
211
+ /** Style names this element owns as its own semantics. Registered
212
+ * elements declare these to `registerElement` instead of overriding. */
213
+ readonly semanticNames: ReadonlySet<string>;
214
+ /** Prop names whose damage this element's own `applyProps` claims, and
215
+ * which `paintChanged` therefore does not damage the whole node for.
216
+ * Empty unless declared — registered elements declare theirs to
217
+ * `registerElement`, so the common case needs no subclass. */
218
+ readonly selfDamagedProps: ReadonlySet<string>;
219
+
220
+ // --- the text this element answers for (docs/extending.md) ---------------
221
+ //
222
+ // Four questions in one index space and one coordinate space: characters
223
+ // are **code points**, rectangles are in the owning window's coordinates —
224
+ // the same ones `abs`, `contentBox()` and a mouse event's `x`/`y` use.
225
+ // An element that answers them can be selected across by a `selectable`
226
+ // ancestor with no registration of any kind.
227
+
228
+ /** This element's text, or null when it has none. The default. */
229
+ textContent(): string | null;
230
+ /** The character boundary nearest a point. Clamps to the ends. */
231
+ textIndexAt(x: number, y: number): number;
232
+ /** Where a caret at this index stands — a zero-width rect from the top of
233
+ * the glyphs to the bottom of them. */
234
+ textCaretRect(index: number): Rect | null;
235
+ /** The bands a highlight over `[start, end)` fills: one per line, and one
236
+ * per direction run within a line — a range that crosses from Latin into
237
+ * Arabic is two stretches of pixels, not the span between them. */
238
+ textRangeRects(start: number, end: number): Rect[];
239
+ /** The part of this element's text the document selection covers, or
240
+ * null. An element that paints its own text fills `textRangeRects` with
241
+ * `selectionColor` under the glyphs while this is set; that is the whole
242
+ * contract for taking part in a selection. */
243
+ readonly selectionRange: { start: number; end: number } | null;
244
+ /** What to fill those rectangles with while `selectionRange` is set. */
245
+ readonly selectionColor: string | null;
246
+ /** An element with a selection of its own — an editor. A `selectable`
247
+ * document around it skips its subtree whole and leaves its presses
248
+ * alone. `<textinput>` sets it. */
249
+ hasOwnSelection: boolean;
250
+
251
+ // --- being a selection surface -------------------------------------------
252
+ //
253
+ // The base class implements these for every node, which is what lets a
254
+ // registered element be passed anywhere a `DrawnNode` is taken — the
255
+ // interface in `types/nodes.d.ts` lists them, so leaving them out here
256
+ // made a subclass stop being one.
257
+
258
+ /** The selection this element owns, or null when it is not `selectable`.
259
+ * A snapshot: read it again after a change. */
260
+ readonly textSelection: TextSelectionSnapshot | null;
261
+ /** Select everything in this surface, and take PRIMARY with it. */
262
+ selectAll(): this;
263
+ /** Drop the selection. PRIMARY is left where it is: the text stays
264
+ * pasteable, which is what every other X client does. */
265
+ clearSelection(): this;
266
+ /** What a copy would put on the clipboard. */
267
+ selectedText(): string;
268
+ /** Set both ends by hand. `setSelection(null)` clears. */
269
+ setSelection(anchor: TextPosition | null, focus?: TextPosition | null): this;
270
+
271
+ /** Draw. A subclass calls `super.paint(ctx)` first, for the background,
272
+ * border and clip, then draws inside `this.abs`. */
273
+ paint(ctx: Context2D): void;
274
+ /**
275
+ * The rect this paint pass is repainting, or null when it is repainting
276
+ * the whole window — and null outside a paint, which means the same
277
+ * thing: nothing is bounding you, so draw everything.
278
+ *
279
+ * An element whose node is one node never needs it; being painted at all
280
+ * means it is inside the pass. An element that draws a **scene** into one
281
+ * node culls against it the way core culls the tree, instead of redrawing
282
+ * the scene into a clip that throws most of it away. Window coordinates,
283
+ * the same space as `abs`; read-only.
284
+ */
285
+ paintDamage(): Rect | null;
286
+ /**
287
+ * "The pixels in `rect` moved by (dx, dy); the rest of it is new" — for an
288
+ * element with a viewport of its own, whose pan is a scroll in every way
289
+ * but the bookkeeping.
290
+ *
291
+ * Claims `rect`, and arms the frame to blit the surviving band inside the
292
+ * backing store instead — after which the claim is narrowed to the band
293
+ * the shift exposed, which is what `paintDamage()` hands the paint. The
294
+ * element draws the strip and nothing else, without asking whether the
295
+ * blit happened.
296
+ *
297
+ * `dx`/`dy` are how far the **pixels** moved, the sense `Surface.copyWithin`
298
+ * uses rather than a scroll offset's, and whole. `rect` is in window
299
+ * coordinates and inside this node. The return says whether the frame is
300
+ * still a blit candidate; the gates that matter close at frame time, and
301
+ * every one of them falls back to repainting `rect`.
302
+ */
303
+ scrollContents(rect: Rect, dx: number, dy: number): boolean;
304
+ /**
305
+ * Did anything this node draws change? True damages the whole node, false
306
+ * contributes no damage at all, and the default answers true for any prop
307
+ * that is not identical to the one it replaced — conservative, because a
308
+ * wrong "no" is a stale pixel nothing comes back to fix.
309
+ *
310
+ * Overridden by an element that claims its own damage and can say so only
311
+ * by looking at the values; an element that can say so by *name* declares
312
+ * `selfDamagedProps` instead. Either way, call `super.paintChanged` for
313
+ * everything the element does not know about — `style` is compared by the
314
+ * caller and is never yours to excuse.
315
+ */
316
+ paintChanged(
317
+ nextProps: Record<string, unknown>,
318
+ prevProps: Record<string, unknown>,
319
+ ): boolean;
320
+ /**
321
+ * Implemented by an element whose size comes from its content — a gauge, a
322
+ * chart, a terminal, an editor. Called during layout whenever the box's
323
+ * size is the element's to give, and by the content-floor pass behind
324
+ * `minWidth: 'auto'` with `{ width: 0, widthMode: 'at-most' }`, whose
325
+ * answer is the smallest size the content can be drawn at.
326
+ *
327
+ * Must be a **method on the class**: the base constructor is what wires it
328
+ * to layout. An element that implements it may not have children, and it
329
+ * has to answer from what it can read at any time (props, loaded data) —
330
+ * never from `this.abs`, which is the result of the layout doing the
331
+ * asking. Call `invalidateMeasure()` when the answer would change.
332
+ */
333
+ measureContent?(constraints: MeasureConstraints): MeasuredSize;
334
+ /** The inputs to `measureContent` changed — a prop it reads, data that
335
+ * arrived — so the next layout has to ask again. `reason` joins the closed
336
+ * set the diagnostics print. */
337
+ invalidateMeasure(reason?: string): void;
338
+ /**
339
+ * The element's own behaviour for a key, run **after** the application's
340
+ * `onKeyDown` handlers and not at all if one of them called
341
+ * `preventDefault()` — the ordering that lets an app veto or extend an
342
+ * interactive element without knowing how it is built.
343
+ *
344
+ * Tab arrives here like any other key. Consuming it — an editor that
345
+ * indents — means calling `ev.preventDefault()`, which suppresses the
346
+ * default action left after this one: the focus cycle. An element that
347
+ * takes Tab owes the user a way back out; see docs/extending.md.
348
+ */
349
+ defaultKeyDown?(ev: KeyboardEvent): void;
350
+ /**
351
+ * Text the user is still typing: a dead key waiting for its letter, or a
352
+ * Compose sequence half entered. `ev.type` is `compositionStart`,
353
+ * `compositionUpdate` or `compositionEnd`, and `ev.data` is what to show
354
+ * — or, at the end, the text that was committed.
355
+ *
356
+ * An element that implements this shows the preedit at its caret and
357
+ * inserts only what the end carries; the keys that made it never reach
358
+ * `defaultKeyDown`. See docs/extending.md.
359
+ */
360
+ defaultComposition?(ev: CompositionEvent): void;
361
+ /**
362
+ * The element's own behaviour for a press, after `onMouseDown` handlers:
363
+ * placing a caret, grabbing a scrollbar thumb, arming a drag.
364
+ *
365
+ * The base class implements this (and the two below, and `defaultKeyDown`)
366
+ * to hand the gesture to the nearest `selectable` ancestor, so a press on
367
+ * anything inside a document reaches the selection. An element that
368
+ * overrides it and does not call `super` is, by that alone, not part of
369
+ * one — which is right for anything that edits, and wrong for anything
370
+ * that only draws.
371
+ */
372
+ defaultMouseDown?(ev: MouseEvent): void;
373
+ /** Pointer motion while this element holds the press — it keeps receiving
374
+ * these wherever the pointer goes, as `onMouseMove` does not. */
375
+ defaultMouseDrag?(ev: MouseEvent): void;
376
+ /** The press this element received has been released. */
377
+ defaultMouseUp?(ev: MouseEvent): void;
378
+ /**
379
+ * Plain pointer motion over this element, with no button down — the hook
380
+ * for an element that paints its own hover state, a scene where the node
381
+ * or handle under the pointer lights up.
382
+ *
383
+ * The element under the pointer gets it, so the first one after the
384
+ * pointer arrives is also the enter. Not delivered while a pointer
385
+ * capture is in force: that motion belongs to the gesture, and
386
+ * `defaultMouseDrag` is where it goes.
387
+ */
388
+ defaultMouseMove?(ev: MouseEvent): void;
389
+ /** The pointer left this element — clear what `defaultMouseMove` lit. A
390
+ * node that unmounts while hovered is *forgotten* rather than left, the
391
+ * way focus is, so a subtree torn down under the pointer never sees it. */
392
+ defaultMouseLeave?(ev: MouseEvent): void;
393
+ /**
394
+ * The wheel over this element, before the scroll chain gets it: a pane
395
+ * whose wheel is a zoom about the pointer rather than a scroll, which
396
+ * needs `ev.x`/`ev.y` and the modifiers as much as the deltas.
397
+ *
398
+ * `ev.preventDefault()` consumes it, and the chain then never runs — the
399
+ * answer for a gesture that is handled whether or not anything moved. An
400
+ * element whose wheel really is a scroll implements `canScroll`/`scrollBy`
401
+ * instead and joins the chain. Deltas are pixels, fractional where the
402
+ * device measured a fraction: the whole-pixel rule is the chain's.
403
+ */
404
+ defaultWheel?(ev: WheelEvent): void;
405
+ /** Right-click, after `onContextMenu` handlers: open the element's own
406
+ * menu. Separate from the press so suppressing the menu does not also
407
+ * give up the caret placement `defaultMouseDown` did. */
408
+ defaultContextMenu?(ev: MouseEvent): void;
409
+ /** This element became the focused node, or its window regained the X
410
+ * focus while it was: start a caret blinking, arm an IME. No event —
411
+ * focus is a state, and `onFocus` is where the app hears about it. */
412
+ defaultFocus?(): void;
413
+ /** Focus left, or the window lost it: stop whatever `defaultFocus`
414
+ * started. A node that unmounts while focused is *forgotten* rather than
415
+ * blurred, so anything with a lifetime — a blink timer — is released in
416
+ * `destroySubtree` as well, the way `<textinput>` does. */
417
+ defaultBlur?(): void;
418
+ /** Whether this element is a tab stop and a focus target with nothing in
419
+ * the props saying so — `<textinput>` is, a `<box>` with something to
420
+ * scroll is. `focusable`/`tabIndex` props override it either way. An
421
+ * element with default actions for keys has to set this, or nothing will
422
+ * ever focus it. */
423
+ focusableByDefault?: boolean;
424
+ /** Whether dead keys and Compose run while this element has focus.
425
+ * Defaults to on. Set it false for an element that forwards raw key
426
+ * events to something with an input method of its own — `<foreign>` does,
427
+ * and composing on this side would swallow the dead key on its way to the
428
+ * embedded client. */
429
+ composes?: boolean;
430
+ /** The cursor to show over this element when nothing in the style says
431
+ * otherwise — `'text'` for something editable. A `cursor` style wins. */
432
+ defaultCursor?: string;
433
+ /**
434
+ * The ARIA role this element is when the application writes none — the
435
+ * registered-element counterpart of `<textinput>` defaulting to
436
+ * `textbox`. A `role` prop still wins, and an unknown name warns in
437
+ * development. Assign it in the constructor.
438
+ */
439
+ a11yRole?: string;
440
+ /**
441
+ * What text this element holds, for the AT-SPI `Text` interface and the
442
+ * test spy (docs/accessibility.md). `null` when it holds none. Implement
443
+ * it and an editor, a viewer or a terminal is readable, navigable and
444
+ * announced through the same paths `<textinput>` uses.
445
+ *
446
+ * Called several times per change, so answer from state the element
447
+ * already holds — never a shaping pass or a copy of a buffer.
448
+ */
449
+ a11yTextState?(): A11yTextState | null;
450
+ /**
451
+ * An assistive technology moved the caret or the selection: `start` and
452
+ * `end` are code-point offsets into the reported `value`, and the caret
453
+ * belongs at `end`. Return false to refuse. Without it an AT can read
454
+ * this element but not navigate it.
455
+ */
456
+ a11ySetSelection?(start: number, end: number): boolean;
457
+ /**
458
+ * An assistive technology edited the text: replace `[start, end)` with
459
+ * `text`. Insert, delete and replace-everything all arrive here. Only an
460
+ * element that implements this exposes AT-SPI's `EditableText`, so an
461
+ * editor without it is read but never typed into.
462
+ */
463
+ a11yReplaceText?(start: number, end: number, text: string): boolean;
464
+ /**
465
+ * The text reported by `a11yTextState()` may have moved — an edit, a
466
+ * caret move, a selection change, a composition. Free when no assistive
467
+ * technology is listening, so call it from every path that changes the
468
+ * text rather than guarding it.
469
+ */
470
+ notifyA11yTextChanged(): void;
471
+ /**
472
+ * The interactive things this element draws, as accessible children
473
+ * (docs/extending.md). Without it a scene of any size is one accessible
474
+ * — "Flow graph, group" — and nothing in it can be found, named or
475
+ * activated.
476
+ *
477
+ * Called once per question asked about this element's children, so
478
+ * answer from state the element already holds: no layout pass, no copy.
479
+ */
480
+ a11yScene?(): A11ySceneItem[];
481
+ /**
482
+ * An assistive technology acted on one of them: `id` is the one this
483
+ * element gave it. Return `true` to claim the action; anything else
484
+ * falls back to core — a synthetic click at the item's rect for
485
+ * `activate`, focusing the element for `focus`, revealing the element
486
+ * for `scroll` — so an element only answers what it has a better answer
487
+ * for.
488
+ */
489
+ a11ySceneAction?(id: string, action: A11ySceneAction): boolean | void;
490
+ /**
491
+ * The scene reported by `a11yScene()` has changed — an item added or
492
+ * removed, one selected, the keyboard cursor moved. A commit re-reads it
493
+ * on its own, so this is for what the element does between commits: a
494
+ * drag, an animation, its own arrow keys. Free when nothing is
495
+ * listening.
496
+ */
497
+ notifyA11ySceneChanged(): void;
498
+ /** Props changed. A subclass calls `super.applyProps(next, prev)`. */
499
+ applyProps(
500
+ nextProps: Record<string, unknown>,
501
+ prevProps: Record<string, unknown>,
502
+ ): void;
503
+ /** Take the keyboard focus, as clicking this node would: focus moves
504
+ * here, `onBlur` fires on whatever had it, `onFocus` here. Also pulls the
505
+ * X input focus back to the window if the window manager gave it away.
506
+ * Returns this node, so an element can hand it out of a method. */
507
+ focus(): this;
508
+ /** Give up focus, leaving the window with nothing focused. */
509
+ blur(): this;
510
+ /** Whether this node has the owning window's focus. */
511
+ readonly focused: boolean;
512
+ /** Whether focus is on this node or inside it — CSS `:focus-within`. A
513
+ * `<popup>` counts as inside the node it hangs off in the JSX tree. */
514
+ readonly focusWithin: boolean;
515
+ /** Whether `node` is this node or a descendant of it (DOM `contains`). */
516
+ contains(node: Node | null): boolean;
517
+ /** The deepest node containing the point, or null. */
518
+ hitTest(x: number, y: number): Node | null;
519
+ containsPoint(x: number, y: number): boolean;
520
+ /** Drawn, visible children in paint order. */
521
+ paintOrder(): Node[];
522
+ insertBefore(child: Node, beforeChild: Node | null): void;
523
+ removeChild(child: Node): void;
524
+ destroySubtree(): void;
525
+ /** Ask the owning window to repaint — every node has this; the window
526
+ * node is where it lands. `damage` is what changed: pass `this`, or a
527
+ * rect when you know a tighter one, because an invalidation with no bound
528
+ * repaints the whole window. `reason` joins the closed set the
529
+ * diagnostics print (docs/debugging.md). */
530
+ invalidate(
531
+ layout?: boolean,
532
+ damage?: Node | Rect | null,
533
+ reason?: string,
534
+ ): void;
535
+ getClientRects(): Rect[];
536
+ /** React Native's measure contract — position in the parent, size, then
537
+ * position in the window — which DevTools' style editor calls to draw
538
+ * the box model. Calls back with nothing for a node with no laid-out
539
+ * rect. */
540
+ measure(
541
+ callback: (
542
+ x?: number,
543
+ y?: number,
544
+ width?: number,
545
+ height?: number,
546
+ left?: number,
547
+ top?: number,
548
+ ) => void,
549
+ ): void;
550
+ }
551
+
552
+ /**
553
+ * Scrolling as a mixin: `class Pane extends Scrollable(Node)` gives a
554
+ * registered element the same `overflow: 'scroll'` behaviour `<box>` and
555
+ * `<window>` have — wheel, keys, bars, and the AT-SPI scroll-pane role.
556
+ *
557
+ * An element whose content is **painted rather than laid out** overrides
558
+ * `measureScrollContent`; everything else follows from the numbers it
559
+ * returns. See docs/extending.md.
560
+ */
561
+ export declare function Scrollable<T extends typeof Node>(
562
+ Base: T,
563
+ ): T & {
564
+ new (...args: any[]): {
565
+ scrollX: number;
566
+ scrollY: number;
567
+ contentWidth: number;
568
+ contentHeight: number;
569
+ isScroller(): boolean;
570
+ scrollTo(to: number | { x?: number; y?: number }): void;
571
+ scrollBy(by: number | { x?: number; y?: number }): void;
572
+ /** Chain membership: has this node room to move on the axis the delta
573
+ * names? The wheel asks it before scrolling this node rather than the
574
+ * next one out. */
575
+ canScroll(dx: number, dy: number): boolean;
576
+ /** How far the content reaches. The default walks the children;
577
+ * override it when the content is pixels this element paints. */
578
+ measureScrollContent(): { width: number; height: number };
579
+ scrollIntoView(node: Node): void;
580
+ };
581
+ };
582
+
583
+ export declare class BoxNode extends Node {
584
+ constructor(props: Record<string, unknown>, app: NtkApp);
585
+ }
586
+
587
+ export declare class TextNode extends Node {}
588
+ export declare class ImageNode extends Node {}
589
+ export declare class CanvasNode extends Node {}
590
+ export declare class TextInputNode extends Node {}
591
+ export declare class TextAreaNode extends TextInputNode {}
592
+ export declare class WindowNode extends Node {}
593
+ export declare class PopupNode extends WindowNode {}
594
+ /** The precedents for an element owning a real child X window — a surface of
595
+ * its own, and one holding somebody else's window. */
596
+ export declare class GlAreaNode extends Node {}
597
+ export declare class ForeignNode extends Node {}
package/src/node.js ADDED
@@ -0,0 +1,44 @@
1
+ // `react-x11/node` — the base class a registered element subclasses, plus
2
+ // the built-in nodes worth extending or reading as worked examples.
3
+ //
4
+ // The contract a subclass has to keep is written down in docs/extending.md.
5
+ // The short version: `Node` already implements the whole reconciler-facing
6
+ // surface (`insertBefore`, `removeChild`, `applyProps`, `destroySubtree`,
7
+ // layout, hit testing, and a `paint` that draws background, border and
8
+ // clip), so an element that only draws needs a constructor that names its
9
+ // kind and a `paint` that calls `super.paint(ctx)` first. An element whose
10
+ // *size* comes from its content adds `measureContent`, and says when that
11
+ // answer moves with `invalidateMeasure`. An element that *behaves* — one
12
+ // that edits, drags a caret or answers chords — adds the `default*` methods:
13
+ // they run after the application's own handlers and not at all if one of
14
+ // those called `preventDefault`, which is the ordering that makes an
15
+ // interactive element composable rather than something to work around.
16
+ export {
17
+ Node,
18
+ // `class MyPane extends Scrollable(Node)` — the same mixin <box> and
19
+ // <window> use, so a registered element can honour `overflow: 'scroll'`
20
+ // with the wheel, the keys, the bars and the a11y role already wired.
21
+ Scrollable,
22
+ // The `measureContent` body of an element whose content has a size of its
23
+ // own and an aspect ratio to keep — what <image> and <svg> answer with.
24
+ intrinsicSize,
25
+ // The cadence a caret blinks at, so an element that draws one is in step
26
+ // with `<textinput>` rather than a few tens of milliseconds beside it.
27
+ CARET_BLINK_MS,
28
+ BoxNode,
29
+ TextNode,
30
+ ImageNode,
31
+ CanvasNode,
32
+ TextInputNode,
33
+ TextAreaNode,
34
+ WindowNode,
35
+ PopupNode,
36
+ } from './nodes.js';
37
+
38
+ // The two precedents for an element that owns a real child X window rather
39
+ // than painting into its parent's: registered with `drawn: false`, realized
40
+ // by the owning WindowNode. docs/extending.md walks through them —
41
+ // `GlAreaNode` for a surface of one's own, `ForeignNode` for one that holds
42
+ // somebody else's window and therefore must never destroy it.
43
+ export { GlAreaNode } from './glnodes.js';
44
+ export { ForeignNode } from './foreignnodes.js';