react-x11 2.52.0 → 2.53.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 (112) hide show
  1. package/README.md +10 -10
  2. package/docs/README.md +160 -314
  3. package/docs/accessibility.md +176 -229
  4. package/docs/appearance.md +77 -176
  5. package/docs/click-to-component.md +18 -51
  6. package/docs/clipboard.md +40 -59
  7. package/docs/components.md +518 -1036
  8. package/docs/dbus.md +61 -133
  9. package/docs/debugging.md +152 -245
  10. package/docs/desktop-calendar.md +51 -88
  11. package/docs/desktop.md +222 -347
  12. package/docs/devtools.md +50 -167
  13. package/docs/drag-and-drop.md +126 -204
  14. package/docs/ecosystem/animation.md +91 -125
  15. package/docs/ecosystem/data-fetching.md +38 -53
  16. package/docs/ecosystem/dev-tooling.md +159 -202
  17. package/docs/ecosystem/forms.md +61 -97
  18. package/docs/ecosystem/headless.md +64 -89
  19. package/docs/ecosystem/i18n.md +18 -21
  20. package/docs/ecosystem/icons.md +93 -126
  21. package/docs/ecosystem/layout.md +42 -52
  22. package/docs/ecosystem/routing.md +18 -29
  23. package/docs/ecosystem/state.md +71 -88
  24. package/docs/ecosystem/testing.md +131 -157
  25. package/docs/ecosystem/theming.md +119 -165
  26. package/docs/ecosystem.md +145 -179
  27. package/docs/elements.md +1091 -1774
  28. package/docs/embedding.md +83 -132
  29. package/docs/events.md +412 -695
  30. package/docs/extending.md +784 -1326
  31. package/docs/eyedropper.md +63 -122
  32. package/docs/filedialog.md +71 -107
  33. package/docs/frame.md +195 -339
  34. package/docs/gl.md +65 -62
  35. package/docs/globalmenu.md +63 -146
  36. package/docs/macos.md +531 -3234
  37. package/docs/notifications.md +33 -47
  38. package/docs/packaging.md +166 -309
  39. package/docs/permissions.md +9 -14
  40. package/docs/react-features.md +81 -106
  41. package/docs/remote.md +32 -76
  42. package/docs/scale.md +72 -186
  43. package/docs/styling.md +329 -547
  44. package/docs/system.md +200 -280
  45. package/docs/testing.md +63 -83
  46. package/docs/typescript.md +6 -48
  47. package/docs/uri-schemes.md +67 -107
  48. package/docs/wayland-backend.md +200 -459
  49. package/docs/windows-integrations.md +75 -131
  50. package/docs/windows.md +343 -1367
  51. package/package.json +3 -2
  52. package/src/Reconciler.js +2 -2
  53. package/src/a11y.js +1 -1
  54. package/src/backend/context2d.js +3 -3
  55. package/src/bootstrap.js +1 -1
  56. package/src/cocoa/app.js +6 -6
  57. package/src/cocoa/bezels.js +1 -1
  58. package/src/cocoa/fonts.js +1 -1
  59. package/src/cocoa/globalmenu.js +1 -1
  60. package/src/cocoa/keymap.js +1 -1
  61. package/src/cocoa/panewindow.js +1 -1
  62. package/src/cocoa/player.js +1 -1
  63. package/src/cocoa/presenter.js +2 -2
  64. package/src/cocoa/promotion.js +1 -1
  65. package/src/cocoa/relaunch.js +1 -1
  66. package/src/cocoa/threaded.js +1 -1
  67. package/src/cocoa/video.js +1 -1
  68. package/src/cocoa/window.js +4 -4
  69. package/src/components/Menu.js +7 -2
  70. package/src/components/Switch.js +1 -1
  71. package/src/components/native.js +1 -1
  72. package/src/dnd.js +2 -2
  73. package/src/glnodes.js +1 -1
  74. package/src/grid.js +1 -1
  75. package/src/layouts.js +1 -1
  76. package/src/nodes/animation.js +1 -1
  77. package/src/nodes/invalidate.js +1 -1
  78. package/src/nodes/layouthost.js +1 -1
  79. package/src/nodes/video.js +19 -8
  80. package/src/nodes/window/droptarget.js +1 -1
  81. package/src/pacing.js +1 -1
  82. package/src/refresh/index.js +2 -1
  83. package/src/types/video.d.ts +5 -2
  84. package/src/videobackend.js +28 -0
  85. package/src/videoframes.js +29 -16
  86. package/src/wayland/app.js +27 -0
  87. package/src/wayland/video.js +376 -0
  88. package/src/win32/a11y.js +1 -1
  89. package/src/win32/app.js +2 -2
  90. package/src/win32/fonts.js +1 -1
  91. package/src/win32/glarea.js +4 -4
  92. package/src/win32/overlay.js +1 -1
  93. package/src/win32/panewindow.js +3 -3
  94. package/src/win32/shell.js +1 -1
  95. package/src/win32/window.js +2 -2
  96. package/src/windowid.js +1 -1
  97. package/src/xvideo.js +189 -0
  98. package/docs/architecture/accessibility.md +0 -470
  99. package/docs/architecture/animation.md +0 -707
  100. package/docs/architecture/container-queries.md +0 -525
  101. package/docs/architecture/custom-layout.md +0 -334
  102. package/docs/architecture/drag-and-drop.md +0 -941
  103. package/docs/architecture/element-layer-contents.md +0 -622
  104. package/docs/architecture/frame-pacing.md +0 -255
  105. package/docs/architecture/grid-layout.md +0 -358
  106. package/docs/architecture/protocol-efficiency.md +0 -357
  107. package/docs/architecture/video.md +0 -1149
  108. package/docs/architecture/windowed-regions.md +0 -854
  109. package/docs/glx.md +0 -137
  110. package/docs/wayland.md +0 -545
  111. package/docs/windows-embedding.md +0 -271
  112. package/docs/windows-gl.md +0 -221
@@ -1,14 +1,14 @@
1
1
  # Forms and validation
2
2
 
3
- There is no `<form>` element here, so nothing submits itself — wire
3
+ There is no `<form>` element, so nothing submits itself — wire
4
4
  `handleSubmit` to a `Button`'s `onPress` or to `<textinput onSubmit>`.
5
5
 
6
- Everything else follows from **one signature**. Every value control in the
7
- library — the host elements `<textinput>` and `<textarea>`, and the widgets
8
- `Checkbox`, `Switch`, `RadioGroup`, `Select` and `Slider` — calls
9
- `onChange(ev)` with a change event carrying `ev.value`, `ev.target.value`,
10
- `ev.target.name` and, for a checkbox, `ev.target.checked`. That is the shape
11
- every DOM form library reads, so a library's handler goes straight in:
6
+ Everything else follows from **one signature**. Every value control — the
7
+ host elements `<textinput>` and `<textarea>`, and the widgets `Checkbox`,
8
+ `Switch`, `RadioGroup`, `Select` and `Slider` — calls `onChange(ev)` with a
9
+ change event carrying `ev.value`, `ev.target.value`, `ev.target.name` and,
10
+ for a checkbox, `ev.target.checked`. That is the shape DOM form libraries
11
+ read, so a library's handler goes straight in, with no per-widget bridge:
12
12
 
13
13
  ```jsx
14
14
  <textinput name="host" value={f.values.host} onChange={f.handleChange} />
@@ -16,9 +16,6 @@ every DOM form library reads, so a library's handler goes straight in:
16
16
  <Select name="size" value={f.values.size} options={sizes} onChange={f.handleChange} />
17
17
  ```
18
18
 
19
- No adapter, no per-widget bridge, nothing to remember about which control
20
- you are wiring.
21
-
22
19
  | library | how it goes in |
23
20
  | ---------------------- | --------------------------------------------------------- |
24
21
  | `react-hook-form` | `{...register('host')}`, or `<Controller>` for a widget |
@@ -26,43 +23,36 @@ you are wiring.
26
23
  | `downshift` | `getInputProps()` spread onto a `<textinput>` |
27
24
  | `@tanstack/react-form` | `onChange={(ev) => field.handleChange(ev.value)}` — below |
28
25
 
29
- Every row was established by running the library headless against this
30
- renderer rather than by reading its documentation: react-hook-form@7.84.0,
26
+ Each row was run headless against the renderer: react-hook-form@7.84.0,
31
27
  formik@2.4.9, downshift@9.4.0, @tanstack/react-form@1.33.2.
32
28
 
33
29
  **The one thing to know**: a library whose field contract is
34
- value-in/value-out — TanStack Form is the one here — needs the value taken
35
- off the event, and a bare `onChange={field.handleChange}` stores the _event
36
- object_ as the field value with no error. See below.
30
+ value-in/value-out — TanStack Form here — needs the value taken off the
31
+ event; a bare `onChange={field.handleChange}` stores the _event object_ as
32
+ the field value with no error.
37
33
 
38
- **Where the line falls.** A control that takes a `name` is a form field and
39
- reports an event. `Tabs`, `Table` and the menus are not form fields
40
- and keep their plain callbacks
34
+ A control that takes a `name` is a form field and reports an event. `Tabs`,
35
+ `Table` and the menus are not, and keep their plain callbacks
41
36
  ([components](../components.md#the-change-event-and-name)).
42
37
 
43
38
  ## TanStack Form {#tanstack-form}
44
39
 
45
40
  **Out of the box.** @tanstack/react-form@1.33.2 (form-core 1.33.2).
46
41
 
47
- A headless, framework-agnostic form state manager: field values, dirty and
48
- touched tracking, sync and async validation (including Standard Schema, so
49
- zod/valibot/arktype plug in directly), and submission handling. Fields are
50
- value-in/value-out — no DOM events, no refs.
51
-
52
- The field contract is `field.state.value` + `field.handleChange(value)` —
53
- a **value, not an event**. react-x11's controls fire an event, so each field
54
- takes the value off it:
42
+ A headless form state manager: field values, dirty and touched tracking,
43
+ sync and async validation (Standard Schema, so zod/valibot/arktype plug in
44
+ directly), and submission. The field contract is `field.state.value` +
45
+ `field.handleChange(value)` — a **value, not an event** — so each field
46
+ takes the value off the control's event, the same expression a DOM app
47
+ writes, for a `<textinput>` and a `Checkbox` alike:
55
48
 
56
49
  ```jsx
57
50
  onChange={(ev) => field.handleChange(ev.value)}
58
51
  ```
59
52
 
60
- One expression, the same one a DOM app writes, and it is identical for a
61
- `<textinput>` and for a `Checkbox`.
62
-
63
- Watch for the bare form: `onChange={field.handleChange}` stores the event
64
- object as the field's value — no error, no warning, and validation sees an
65
- object where it expected a string.
53
+ The bare `onChange={field.handleChange}` stores the event object as the
54
+ field's value — no error, no warning, and validation sees an object where it
55
+ expected a string.
66
56
 
67
57
  ```jsx
68
58
  import React from 'react';
@@ -101,25 +91,19 @@ function ConnectForm({ connect }) {
101
91
  }
102
92
  ```
103
93
 
104
- - No `<form>` element exists, so there is no submit-on-Enter for free. Wire
105
- `form.handleSubmit()` to a button's `onPress` and/or a window-level Enter
106
- key handler.
107
- - The **widgets** are the exception: `Checkbox`, `Switch`, `RadioGroup`,
108
- `Select` and `Slider` still pass the next value first, so
109
- `onChange={field.handleChange}` is right there and needs no unwrap. Only
110
- the host elements `<textinput>`/`<textarea>` changed.
94
+ - There is no submit-on-Enter for free. Wire `form.handleSubmit()` to a
95
+ button's `onPress` and/or a window-level Enter key handler.
111
96
  - Validation errors from a zod schema are error _objects_
112
- (`errors[0].message`), not strings; a plain string validator returns
113
- strings. A `<text>` child must be a string, so render `.message`.
114
- - The package also ships SSR and Next helpers under subpaths; the root
115
- import used here pulls in no react-dom.
97
+ (`errors[0].message`); a plain string validator returns strings. A
98
+ `<text>` child must be a string, so render `.message`.
99
+ - The SSR and Next helpers live under subpaths; the root import pulls in no
100
+ react-dom.
116
101
 
117
102
  ## zod {#zod}
118
103
 
119
104
  **Out of the box.** zod@4.4.3.
120
105
 
121
- TypeScript-first schema validation. Pure computation, no DOM, nothing
122
- renderer-specific — the interesting part is only where it plugs in:
106
+ Schema validation, pure computation. Where it plugs in:
123
107
 
124
108
  - **TanStack Form** — pass a schema straight into a field's
125
109
  `validators: { onChange: z.string().min(3, 'too short') }`. Zod 4
@@ -157,49 +141,39 @@ function Settings({ raw }) {
157
141
 
158
142
  - Zod 4 field errors surface as issue objects; render `issue.message`, not
159
143
  the issue itself.
160
- - `valibot` and `arktype` are equally viable (also Standard Schema). zod is
161
- documented here because both verified form integrations used it.
162
- - Bundle-size arguments from the web world do not apply — an X11 app ships
163
- no bundle.
144
+ - `valibot` and `arktype` are equally viable (also Standard Schema).
145
+ - Bundle-size arguments from the web do not apply — an X11 app ships no
146
+ bundle.
164
147
 
165
148
  ## React Hook Form {#react-hook-form}
166
149
 
167
150
  **Out of the box, both APIs.** react-hook-form@7.84.0 with
168
151
  @hookform/resolvers@5.5.7.
169
152
 
170
- RHF's headline API is uncontrolled-first: `register()` returns DOM-shaped
171
- props (`{name, ref, onChange(e), onBlur(e)}`) and reads `e.target.value` off
172
- the event. That is exactly what `<textinput>` provides, so the terse spread
173
- works:
153
+ `register()` returns DOM-shaped props (`{name, ref, onChange(e),
154
+ onBlur(e)}`) and reads `e.target.value` off the event, which is what
155
+ `<textinput>` provides, so the spread works:
174
156
 
175
157
  ```jsx
176
158
  const { register, handleSubmit } = useForm({ defaultValues: { host: '' } });
177
159
  <textinput {...register('host')} />;
178
160
  ```
179
161
 
180
- Three things had to line up, and all three now do: `name` exists as a prop,
181
- `onChange` receives an event whose `target.value` is the new text, and the
182
- `ref` RHF attaches lands on a node whose `value` is **writable** — RHF
183
- assigns `ref.value = ''` while registering the field, which used to throw
184
- `TypeError: Cannot set property value of #<TextInputNode> which has only a
185
- getter` from inside the commit phase and take the render down with it.
186
-
187
- Verified working through `register()`: typing updates `getValues()`,
188
- `handleSubmit` receives them, `setValue(name, v)` writes through the ref so
189
- the field redraws, and `setFocus(name)` focuses the node.
190
-
191
- One asymmetry to know about: **`reset()` updates form state but does not
192
- clear an uncontrolled field's text.** RHF only writes values back through
193
- refs on the reset path when its `isWeb` probe
194
- (`typeof window !== 'undefined' && window.HTMLElement && document`) passes,
195
- which it never does here. `setValue()` is not gated that way and does write
196
- through. If you need `reset()` to clear the display, make the field
197
- controlled — `<Controller>`, below — or assign `ref.current.value = ''`
198
- yourself.
199
-
200
- `<Controller>` / `useController` still works, unchanged, and is the better
201
- choice when you want the field controlled. `field.onChange` accepts either a
202
- plain value or an event, so both of these are fine:
162
+ The `ref` RHF attaches lands on a node whose `value` is writable (RHF
163
+ assigns `ref.value = ''` while registering). Through `register()`, typing
164
+ updates `getValues()`, `handleSubmit` receives the values,
165
+ `setValue(name, v)` writes through the ref so the field redraws, and
166
+ `setFocus(name)` calls the node's `.focus()`.
167
+
168
+ **`reset()` updates form state but does not clear an uncontrolled field's
169
+ text.** RHF writes values back through refs on reset only when its `isWeb`
170
+ probe (`typeof window !== 'undefined' && window.HTMLElement && document`)
171
+ passes, which it never does here; `setValue()` is not gated. To have
172
+ `reset()` clear the display, make the field controlled with `<Controller>`,
173
+ or assign `ref.current.value = ''` yourself.
174
+
175
+ `<Controller>` / `useController` is the choice for a controlled field.
176
+ `field.onChange` accepts a plain value or an event, so both are fine:
203
177
 
204
178
  ```jsx
205
179
  onChange={(ev) => field.onChange(ev.target.value)} // explicit
@@ -246,11 +220,9 @@ function ConnectForm({ connect }) {
246
220
  }
247
221
  ```
248
222
 
249
- - A `Controller` field is controlled, so its subtree re-renders on every
250
- keystroke. `register()` does not, which is the same performance argument
251
- RHF makes on the web — and it now applies here too.
252
- - `setFocus(name)` works: it calls `.focus()` on whatever the ref holds, and
253
- a `<textinput>` node has one.
223
+ - A `Controller` field re-renders its subtree on every keystroke;
224
+ `register()` does not — the same performance argument RHF makes on the
225
+ web.
254
226
  - `handleSubmit` returns a function. Call it: `handleSubmit(fn)` in an
255
227
  `onPress` is the curried form, and there is no submit event to pass.
256
228
 
@@ -258,12 +230,7 @@ function ConnectForm({ connect }) {
258
230
 
259
231
  **Out of the box.** formik@2.4.9.
260
232
 
261
- Formik was the clearest casualty of the old `onChange(string)` contract, and
262
- it failed _silently_: `handleChange` treats a string argument as its curried
263
- field-name form, returns a handler, and never touches state — so spreading
264
- `getFieldProps` onto a `<textinput>` left `values` at its initial object and
265
- `touched` empty, with nothing logged. That is fixed by the event, not by an
266
- adapter. All three of Formik's field styles now work:
233
+ All three of Formik's field styles work:
267
234
 
268
235
  ```jsx
269
236
  import { Formik, Field, useFormik } from 'formik';
@@ -286,10 +253,7 @@ import { Formik, Field, useFormik } from 'formik';
286
253
  ```
287
254
 
288
255
  `handleChange` reads `target.name` to decide which field to write, so the
289
- `name` prop is not optional in any of these — that is what the synthetic
290
- event carries it for.
291
-
292
- The widgets need no bridge — same handler, same signature:
256
+ `name` prop is required in all of these. The widgets take the same handler:
293
257
 
294
258
  ```jsx
295
259
  <Checkbox
@@ -301,13 +265,13 @@ The widgets need no bridge — same handler, same signature:
301
265
  </Checkbox>
302
266
  ```
303
267
 
304
- `ev.target` is `{ type: 'checkbox', name, value, checked }`, which is exactly
305
- what `handleChange` destructures — including `type`, which is how it knows to
306
- take `checked` rather than `value`.
268
+ `ev.target` is `{ type: 'checkbox', name, value, checked }`, which is what
269
+ `handleChange` destructures — `type` is how it knows to take `checked`.
307
270
 
308
271
  - `<Formik>`'s render-prop form and `useFormik` both work; there is no
309
272
  `<form>` element, so call `submitForm()` (or `handleSubmit()`) yourself
310
273
  from a `Button` or from `<textinput onSubmit>`.
311
- - Passing a non-event object to `handleChange` — a hand-rolled fake event
312
- with no `target` — still throws `TypeError: Cannot read properties of
313
- undefined (reading 'type')` rather than no-opping. Pass the real event.
274
+ - Passing `handleChange` a non-event object — a hand-rolled fake event with
275
+ no `target` — throws
276
+ `TypeError: Cannot read properties of undefined (reading 'type')` rather
277
+ than no-opping. Pass the real event.
@@ -1,19 +1,15 @@
1
1
  # Headless components
2
2
 
3
- "Headless" is a promise that gets broken a lot. A library that returns DOM
4
- prop bags — `getInputProps()`, `useButton()` — is not headless in any sense
5
- that helps here; it is DOM-coupled with the rendering deferred. The two
6
- packages below are the real thing: they own state and collections, return
7
- plain values, and never touch a ref.
8
-
9
- Everything else that markets itself as headless is in the
10
- [negative-results register](../ecosystem.md#what-does-not-work) — Headless
11
- UI, the Radix primitives and downshift all fail, the last one silently until
12
- the first keystroke.
13
-
14
- For finished widgets, react-x11 ships its own: `Button`, `Checkbox`,
3
+ A library that returns DOM prop bags — `useButton()` and the like — is
4
+ DOM-coupled with the rendering deferred, not headless in a sense that helps
5
+ here. The two packages below own state and collections, return plain values,
6
+ and never touch a ref. Headless UI and the Radix primitives render DOM hosts
7
+ and fail ([register](../ecosystem.md#what-does-not-work)); downshift's
8
+ `getInputProps()` works on a `<textinput>` ([forms](forms.md)).
9
+
10
+ For finished widgets react-x11 ships its own: `Button`, `Checkbox`,
15
11
  `Radio`/`RadioGroup`, `Switch`, `ProgressBar`, `Select`, `Slider`, `Tooltip`,
16
- `Dialog`, `MenuBar`/`ContextMenu`, `Tabs`, `Table`, `SplitPane`. See
12
+ `Dialog`, `MenuBar`/`ContextMenu`, `Tabs`, `Table`, `SplitPane` — see
17
13
  [components](../components.md).
18
14
 
19
15
  ## react-stately {#react-stately}
@@ -25,13 +21,9 @@ The state-machine half of React Aria: `useListState`, `useTreeState`,
25
21
  `useComboBoxState`, `useMenuTriggerState`, `useNumberFieldState` and about
26
22
  thirty-five more, plus the `Item`/`Section` collection API. Each hook owns
27
23
  selection, disabled-key traversal, expansion and typeahead-ready collections
28
- — everything a widget needs _except_ rendering and event binding, which is
29
- exactly the part react-x11 supplies.
30
-
31
- There is no seam to bridge: the hooks take props, return state, and never
32
- touch a ref. A scan of the shipped `dist/` finds no runtime reference to
33
- `document`, `window` or `getBoundingClientRect` in any hook, and its
34
- dependencies (`@internationalized/*`, `@react-types/shared`,
24
+ — everything but rendering and event binding, which react-x11 supplies. No
25
+ hook references `document`, `window` or `getBoundingClientRect` at runtime,
26
+ and the dependencies (`@internationalized/*`, `@react-types/shared`,
35
27
  `use-sync-external-store`) are DOM-free.
36
28
 
37
29
  ```jsx
@@ -76,21 +68,18 @@ function Picker() {
76
68
  }
77
69
  ```
78
70
 
79
- - **Only `react-stately` is portable.** `react-aria` (the hooks that produce
80
- DOM props and bind DOM events) and `react-aria-components` are not — do not
81
- follow Adobe's docs into `useListBox` or `useButton`, which return DOM prop
82
- bags.
83
- - As of 3.48 the package is consolidated: the old `@react-stately/*` packages
84
- are bundled in, with per-hook subpath exports
85
- (`react-stately/useListState`) for lean imports.
86
- - The collection API wants `<Item>`/`<Section>` elements, or an `items` plus
87
- render-function pair. It is a compile target of its own, slightly
88
- ceremonious for a three-entry menu.
89
- - `useAsyncList` pulls in fetch-based loading state — fine under Node, but it
90
- is the one hook with I/O opinions.
91
- - Drag-and-drop hooks (`useDraggableCollectionState`) hold state fine, but
92
- everything that would _feed_ them events is DOM-side. Expect to write the
93
- event plumbing.
71
+ - **Only `react-stately` is portable.** `react-aria` (hooks that produce DOM
72
+ props and bind DOM events) and `react-aria-components` are not — do not
73
+ follow Adobe's docs into `useListBox` or `useButton`.
74
+ - Since 3.48 the old `@react-stately/*` packages are bundled in, with
75
+ per-hook subpath exports (`react-stately/useListState`) for lean imports.
76
+ - The collection API wants `<Item>`/`<Section>` elements, or `items` plus a
77
+ render function — ceremonious for a three-entry menu.
78
+ - `useAsyncList` carries fetch-based loading state — fine under Node, the
79
+ one hook with I/O opinions.
80
+ - Drag-and-drop hooks (`useDraggableCollectionState`) hold state, but
81
+ everything that would _feed_ them events is DOM-side; write the event
82
+ plumbing yourself.
94
83
 
95
84
  ## `@tanstack/react-table` {#tanstack-table}
96
85
 
@@ -98,21 +87,16 @@ function Picker() {
98
87
  react-store 0.11.1).
99
88
 
100
89
  A headless table engine: column defs, row models, sorting, filtering,
101
- grouping, pagination, column sizing, visibility and pinning — as pure state
102
- plus derived row models. It renders nothing; you map `getHeaderGroups()` and
103
- `getRowModel().rows` to whatever the host renderer draws.
104
-
105
- v9 talks about "adapters", and the word means the framework, not the
106
- renderer: `@tanstack/react-table` _is_ the React adapter, wiring the
107
- framework-agnostic core's store to React's subscription model. react-x11 is
108
- React, so that adapter is already ours and there is nothing renderer-shaped
109
- to write. It still never touches `react-dom` or `document` at runtime —
110
- though v9 moves two of this page's checks, see the react-dom and `flexRender`
111
- bullets below. `column.getToggleSortingHandler()` still goes straight onto a
112
- header `onClick`.
113
-
114
- v9's API is modular where v8's was monolithic: features and row models are
115
- registered up front through `tableFeatures`, and the hook is `useTable`.
90
+ grouping, pagination, column sizing, visibility and pinning, as pure state
91
+ plus derived row models. You map `getHeaderGroups()` and
92
+ `getRowModel().rows` to react-x11 elements.
93
+
94
+ v9's "adapters" are per framework, not per renderer: `@tanstack/react-table`
95
+ _is_ the React adapter, so there is nothing to write, and it never touches
96
+ `react-dom` or `document` at runtime (but see the react-dom and `flexRender`
97
+ bullets). `column.getToggleSortingHandler()` goes straight onto a header
98
+ `onClick`. Features and row models are registered up front through
99
+ `tableFeatures`, and the hook is `useTable`.
116
100
 
117
101
  ```jsx
118
102
  import React from 'react';
@@ -173,47 +157,38 @@ function Servers({ data }) {
173
157
  }
174
158
  ```
175
159
 
176
- Sorting is uncontrolled here — the table's own store holds it, and the
177
- header click re-renders through the adapter's subscription. Pass
178
- `state`/`onSortingChange` as before to control it, or a selector as
179
- `useTable`'s second argument to subscribe a component to only some slices.
180
-
181
- The natural pairing is inside the existing `Table` component, or raw
182
- `<box style={{ flexDirection: 'row' }}>` rows: TanStack owns the data logic,
183
- react-x11 owns painting.
160
+ Sorting is uncontrolled here — the table's store holds it, and the header
161
+ click re-renders through the adapter's subscription. Pass
162
+ `state`/`onSortingChange` to control it, or a selector as `useTable`'s
163
+ second argument to subscribe a component to only some slices. Render into
164
+ the built-in `Table` component or raw `<box style={{ flexDirection: 'row' }}>`
165
+ rows.
184
166
 
185
167
  - **`coreRowModel` is opt-in, and forgetting it fails silently.** Without
186
- `coreRowModel: createCoreRowModel()` in `tableFeatures`, the table
187
- constructs, headers render, and `getRowModel().rows` is `[]` — no error.
188
- The official examples lean on framework presets that include it; register
189
- it yourself.
168
+ `coreRowModel: createCoreRowModel()` in `tableFeatures`, headers render
169
+ and `getRowModel().rows` is `[]`, with no error. The official examples get
170
+ it from framework presets; register it yourself.
190
171
  - **npm installs a `react-dom` you must not keep.** `@tanstack/react-store`
191
- declares a `react-dom` _peer_ dependency, which npm 7+ auto-installs. The
192
- shipped code never imports it — grep test 2 on all four `@tanstack`
193
- packages finds one `"button"` in a doc comment, and this section was
194
- verified with `node_modules/react-dom` deleted — but a resident react-dom
195
- is exactly the [silent-failures](../ecosystem.md#silent-failures) hazard.
196
- Install with `--legacy-peer-deps`, or delete it and let `npm ls react-dom`
197
- stay empty.
198
- - **`flexRender` now returns a React element even for a plain string cell**
199
- (v8 returned the string itself). Inside `<text>` that is fine — the string
200
- arrives as a text chunk and paints identically — but a test or a11y walk
201
- that reads a `<text>` node's `children` prop now sees an element object.
202
- Read the rendered chunks, not the prop. A column def whose `cell` returns
203
- DOM elements still throws the renderer's unknown-element error; return
204
- strings or react-x11 elements.
205
- - Column _resizing_ helpers (`header.getResizeHandler`) still expect DOM
206
- mouse or touch events with `clientX`, and now fall back to a
207
- `document`-listener drag loop (injectable as `_contextDocument`). Simpler
208
- to ignore the helper: write your own drag handling off
209
- `onMouseDown`/`onMouseMove` — react-x11 events carry `x`/`y` — and call
172
+ declares a `react-dom` _peer_, which npm 7+ auto-installs. The shipped
173
+ code never imports it (test 2 finds one `"button"`, in a doc comment, and
174
+ the table works with `node_modules/react-dom` deleted), but a resident
175
+ react-dom is the [silent-failures](../ecosystem.md#silent-failures)
176
+ hazard. Install with `--legacy-peer-deps`, or delete it so
177
+ `npm ls react-dom` stays empty.
178
+ - **`flexRender` returns a React element even for a plain string cell** (v8
179
+ returned the string). Inside `<text>` it paints identically, but a test or
180
+ a11y walk reading a `<text>` node's `children` prop sees an element
181
+ object; read the rendered chunks. A `cell` that returns DOM elements
182
+ throws the unknown-element error; return strings or react-x11 elements.
183
+ - Column _resizing_ helpers (`header.getResizeHandler`) expect DOM mouse or
184
+ touch events with `clientX` and fall back to a `document`-listener drag
185
+ loop (injectable as `_contextDocument`). Simpler: handle the drag off
186
+ `onMouseDown`/`onMouseMove` (react-x11 events carry `x`/`y`) and call
210
187
  `table.setColumnSizing` (needs `columnSizingFeature` registered).
211
- - `useTable` probes `typeof window` to pick `useLayoutEffect`; under Node it
212
- gets the `useEffect` branch. That is a grep-test-3 hit of the harmless
213
- kind — timing, not a feature gate — and the sorted re-render above runs
214
- through it.
215
- - For thousands of rows, pair it with a virtualizer — see
216
- [layout](layout.md#tanstack-virtual). TanStack Table will happily hand you
217
- 10,000 row objects and let the renderer drown.
188
+ - `useTable` probes `typeof window` to pick `useLayoutEffect` and gets
189
+ `useEffect` under Node — a harmless test-3 hit (timing, not a feature
190
+ gate).
191
+ - For thousands of rows, pair it with a
192
+ [virtualizer](layout.md#tanstack-virtual).
218
193
  - State updates driven from outside a React event land one frame later than
219
- the call, the same as any react-x11 update.
194
+ the call, as with any react-x11 update.
@@ -4,16 +4,11 @@
4
4
 
5
5
  **Out of the box.** i18next@26.3.6, react-i18next@17.0.11.
6
6
 
7
- i18next is the de-facto JS i18n core — resources, interpolation, plurals,
8
- namespaces, backends — and react-i18next binds it to React with
9
- `useTranslation`, `<Trans>` for translations containing markup, and
10
- provider-driven re-render on language change.
11
-
12
- The core has no DOM in it and react-i18next renders whatever elements you
13
- hand it, so the whole stack runs unmodified: interpolation, count-based
14
- plurals, `<Trans>`, and `i18n.changeLanguage('de')` re-rendering the mounted
15
- tree in place. Node ships full ICU, so `Intl.NumberFormat` and
16
- `Intl.DateTimeFormat` formatting work too.
7
+ i18next (resources, interpolation, plurals, namespaces, backends) with
8
+ react-i18next's `useTranslation`, `<Trans>` and provider-driven re-render
9
+ runs unmodified: interpolation, count-based plurals, `<Trans>`, and
10
+ `i18n.changeLanguage('de')` re-rendering the mounted tree in place. Node
11
+ ships full ICU, so `Intl.NumberFormat` and `Intl.DateTimeFormat` work too.
17
12
 
18
13
  Two react-x11-specific rules:
19
14
 
@@ -72,18 +67,20 @@ function Status() {
72
67
  // i18n.changeLanguage('de') re-renders live
73
68
  ```
74
69
 
75
- For loading translations from disk, `i18next-fs-backend` is the natural
76
- desktop choice; `i18next-http-backend` also works, since Node has `fetch`.
70
+ To load translations from disk use `i18next-fs-backend`;
71
+ `i18next-http-backend` also works, since Node has `fetch`.
77
72
 
78
73
  ## What is missing below the string
79
74
 
80
- These are react-x11 gaps rather than i18next ones, and they matter more than
81
- the library choice:
75
+ React-x11 gaps rather than i18next ones, and they matter more than the
76
+ library choice:
82
77
 
83
- - **There is no X input method.** Dead keys and the Compose key are handled
84
- in the renderer ([events.md](../events.md#composition)), but there is no
85
- XIM/IBus integration, so a language that needs a real input method — CJK —
86
- can be _displayed_ but not typed.
87
- - `<Trans>` with the default `<strong>`- and `<em>`-style numbered tags in
88
- translations is fine as long as every referenced component is a `<text>`
89
- span. An accidental DOM tag fails loudly at render, which is the good case.
78
+ - **No input method on X11 or Cocoa.** Dead keys and the Compose key are
79
+ handled in the renderer ([events.md](../events.md#composition)), but
80
+ there is no XIM/IBus/Fcitx client on X11 and no `NSTextInputClient` on
81
+ Cocoa, so a language that needs one — CJK — can be _displayed_ but not
82
+ typed there. The Wayland and Windows backends drive the system input
83
+ method.
84
+ - `<Trans>` with `<strong>`/`<em>`-style numbered tags is fine as long as
85
+ every referenced component is a `<text>` span. An accidental DOM tag fails
86
+ loudly at render.