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,41 +1,36 @@
1
1
  # Animation
2
2
 
3
- react-x11 has a built-in `transition` style property with a fixed easing
4
- curve — see [styling](../styling.md#transitions). Everything on this page
5
- exists to cover what that cannot express: springs with velocity carry-over,
6
- interruption, arbitrary easing curves, shape morphing, and keeping an element
7
- mounted while it animates out.
3
+ react-x11's built-in `transition` style property has a fixed easing curve
4
+ ([styling](../styling.md#transitions)). This page covers what it cannot
5
+ express: springs with velocity carry-over, interruption, arbitrary easing
6
+ curves, shape morphing, and keeping an element mounted while it animates
7
+ out.
8
8
 
9
- There is one constraint every entry shares: **there is no `opacity` style
10
- property**, and `validateStyle` throws on unknown properties. The classic
11
- fade does not port literally; animate a colour, a size or a position instead.
9
+ **There is no `opacity` style property**, and `validateStyle` throws on
10
+ unknown properties, so the classic fade does not port; animate a colour, a
11
+ size or a position instead.
12
12
 
13
13
  Two patterns recur. The cheap one is `onUpdate → setState → style prop`:
14
- every frame is a React render plus a commit, which react-x11 turns into a
15
- partial repaint of the node whose paint changed. Keep the animated component
16
- a leaf — a `<box>`, not the window root — and it is fine at spring frame
17
- rates. The expensive-to-set-up, cheap-to-run one is the
18
- [react-spring host](#react-spring), which applies frames imperatively and
19
- bypasses React entirely.
20
-
21
- Headless Node has no `requestAnimationFrame`. In a real app the right clock
22
- is the ntk window's `requestAnimationFrame`, reachable through a ref on
23
- `<window>`, which paces frames to X connection latency; a 16 ms
24
- `setInterval` works everywhere else.
25
-
26
- `framer-motion` is not on this page. Both its component layer and its bare
27
- value layer fail — see the
28
- [negative-results register](../ecosystem.md#render-time), which also explains
29
- why reaching past `motion.*` does not help.
14
+ each frame is a React render plus a commit, which react-x11 turns into a
15
+ partial repaint of the changed node. Keep the animated component a leaf — a
16
+ `<box>`, not the window root — and it holds spring frame rates. The other is
17
+ the [react-spring host](#react-spring): more setup, but it applies frames
18
+ imperatively and bypasses React.
19
+
20
+ Node has no global `requestAnimationFrame`. In an app, use the ntk window's
21
+ `requestAnimationFrame` (through a ref on `<window>`), which paces frames to
22
+ X connection latency; a 16 ms `setInterval` works everywhere else.
23
+
24
+ `framer-motion` fails at both its component and its value layer — see the
25
+ [negative-results register](../ecosystem.md#render-time).
30
26
 
31
27
  ## popmotion {#popmotion}
32
28
 
33
29
  **Out of the box.** popmotion@11.0.5.
34
30
 
35
- Framer's low-level animation toolkit: `animate()` tweens or springs a plain
36
- JS value and calls `onUpdate(v)` every frame. No components, no DOM — the
37
- value goes wherever you point it. This is the smallest way to get real
38
- springs, with velocity and overshoot, without adopting the react-spring host.
31
+ `animate()` tweens or springs a plain JS value and calls `onUpdate(v)`
32
+ every frame. The smallest way to get real springs, with velocity and
33
+ overshoot, without the react-spring host.
39
34
 
40
35
  ```jsx
41
36
  import React, { useEffect, useState } from 'react';
@@ -57,29 +52,25 @@ function Bar() {
57
52
  }
58
53
  ```
59
54
 
60
- popmotion's `framesync` detects the missing `requestAnimationFrame` and falls
61
- back to a 16.7 ms `setTimeout`, so it works headless with no configuration.
62
- For latency-adaptive pacing, `animate()` accepts a custom `driver` you can
63
- back with the ntk window's clock.
55
+ With no `requestAnimationFrame`, popmotion falls back to a 16.7 ms
56
+ `setTimeout`, so it needs no configuration. For latency-adaptive pacing,
57
+ pass `animate()` a custom `driver` backed by the ntk window's clock.
64
58
 
65
59
  - Springs on layout properties (width, margin) cost a yoga layout pass per
66
- frame — the same trade react-x11's own layout transitions make.
67
- - `animate` also interpolates colour strings (`'#f00' → '#00f'`), and the
68
- `rgba(...)` strings it emits are accepted by the paint path.
69
- - popmotion's `styler` and DOM helpers are irrelevant here. Use only the
70
- value APIs: `animate`, `spring`, `decay`, `mix`.
60
+ frame, as react-x11's own layout transitions do.
61
+ - `animate` interpolates colour strings (`'#f00' → '#00f'`), and the
62
+ `rgba(...)` strings it emits paint as-is.
63
+ - Use only the value APIs — `animate`, `spring`, `decay`, `mix` — not
64
+ `styler` or the DOM helpers.
71
65
 
72
66
  ## d3-ease {#d3-ease}
73
67
 
74
68
  **Out of the box.** d3-ease@3.0.1.
75
69
 
76
- Pure easing functions: `ease(t)` maps normalized time to normalized progress,
77
- and nothing else. Thirty-plus curves — cubic, elastic, bounce, back — each a
78
- few lines of maths with no environment dependencies. This is the zero-cost
79
- way to get the curves the built-in `transition` does not have.
80
-
81
- Compute `t` from wall time, pass `ease(t)` to an interpolator, push the
82
- result into a style prop.
70
+ Pure easing functions — `ease(t)` maps normalized time to progress — with
71
+ thirty-plus curves (cubic, elastic, bounce, back) the built-in `transition`
72
+ lacks. Compute `t` from wall time, pass `ease(t)` to an interpolator, push
73
+ the result into a style prop.
83
74
 
84
75
  ```jsx
85
76
  import React, { useEffect, useState } from 'react';
@@ -109,26 +100,19 @@ function Gauge() {
109
100
  }
110
101
  ```
111
102
 
112
- - ESM-only since v3 — `require()` fails, `import` works. react-x11 apps are
113
- ESM anyway.
103
+ - ESM-only since v3 — `import`, not `require()`.
114
104
  - It is only the curve; you own the clock and the interpolation.
115
- - Overshooting curves (elastic, back) produce values outside the from/to
116
- range. That is the point of them, but clamp before feeding anything
117
- react-x11 validates as non-negative.
105
+ - Overshooting curves (elastic, back) leave the from/to range; clamp before
106
+ feeding anything react-x11 validates as non-negative.
118
107
 
119
108
  ## d3-interpolate {#d3-interpolate}
120
109
 
121
110
  **Out of the box.** d3-interpolate@3.0.1.
122
111
 
123
112
  `interpolate(a, b)` returns `t => value` for numbers, colours in any CSS
124
- space, strings with embedded numbers, dates, arrays and nested objects. Pure
125
- maths, no DOM.
126
-
127
- Worth knowing: `interpolateRgb` emits `'rgb(r, g, b)'` strings and the paint
128
- path takes them as-is, so colour animation needs no conversion. react-x11 has
129
- an internal interpolator for its own `transition` styles, but it is not
130
- exported and handles only what transitions need — d3-interpolate is the
131
- general tool for hand-rolled timelines.
113
+ space, strings with embedded numbers, dates, arrays and nested objects.
114
+ `interpolateRgb` emits `'rgb(r, g, b)'` strings, which paint as-is. (The
115
+ interpolator behind react-x11's `transition` is not exported.)
132
116
 
133
117
  ```jsx
134
118
  import { easeCubicOut } from 'd3-ease';
@@ -148,9 +132,8 @@ function Gauge() {
148
132
 
149
133
  - ESM-only since v3.
150
134
  - Generic `interpolate(a, b)` on objects interpolates _every_ numeric-looking
151
- key, so interpolating a whole style object will happily produce fractional
152
- values for properties that want integers. Interpolate the two or three
153
- properties you mean.
135
+ key, giving fractional values to properties that want integers.
136
+ Interpolate only the properties you mean.
154
137
  - There is no `opacity`; to fade, interpolate a colour's alpha into
155
138
  `backgroundColor: 'rgba(...)'`.
156
139
  - `interpolateArray` over polygon points feeding `<canvas onDraw>` is a good
@@ -160,14 +143,11 @@ function Gauge() {
160
143
 
161
144
  **Out of the box.** @tweenjs/tween.js@25.0.0.
162
145
 
163
- `new Tween(obj).to(target, ms).easing(...).onUpdate(...).start()`, and then
164
- _you_ call `tween.update(time)` on whatever clock you own. Since v18 there is
165
- no implicit global loop — nothing runs until you pump it.
166
-
167
- The explicit clock is the feature here: in a real app, pump from the ntk
168
- window's `requestAnimationFrame`, which paces updates to X connection
169
- latency. Use one `Group` per window if you run many tweens, so
170
- `group.update(now)` steps them all from the same tick.
146
+ `new Tween(obj).to(target, ms).easing(...).onUpdate(...).start()`, then
147
+ _you_ call `tween.update(time)` on your own clock — there is no implicit
148
+ global loop. In an app, pump from the ntk window's `requestAnimationFrame`;
149
+ with many tweens, one `Group` per window lets `group.update(now)` step them
150
+ all from the same tick.
171
151
 
172
152
  ```jsx
173
153
  import React, { useEffect, useState } from 'react';
@@ -200,11 +180,10 @@ function Panel() {
200
180
  }
201
181
  ```
202
182
 
203
- - Tweens numbers only, including numbers nested in objects and arrays.
204
- Colours need pre-decomposition into channels; use d3-interpolate or
205
- popmotion instead.
206
- - Nothing animates until something calls `update()`. Forgetting the pump is
207
- the classic first bug.
183
+ - Numbers only, including nested in objects and arrays. For colours use
184
+ d3-interpolate or popmotion.
185
+ - Nothing animates until something calls `update()` — the classic first
186
+ bug.
208
187
  - `onUpdate` mutates the source object, so spread it into fresh state
209
188
  (`setPos({ ...state })`) or React skips the re-render on identity.
210
189
 
@@ -212,11 +191,9 @@ function Panel() {
212
191
 
213
192
  **Out of the box.** flubber@0.4.2.
214
193
 
215
- Shape-morphing interpolators for SVG paths: `interpolate(from, to)` returns
194
+ Shape morphing for SVG paths: `interpolate(from, to)` returns
216
195
  `t => d-string`, handling ring alignment, point insertion and topology
217
- changes so intermediate frames look sensible instead of self-intersecting.
218
- Shape morphing is the one animation the `<svg>` host makes easy, and flubber
219
- is the standard tool for it.
196
+ changes so intermediate frames do not self-intersect.
220
197
 
221
198
  ```jsx
222
199
  import React, { useEffect, useState } from 'react';
@@ -247,12 +224,12 @@ function Morph() {
247
224
 
248
225
  - `import { interpolate } from 'flubber'` throws — it is a CJS package, so
249
226
  use the default-import form above.
250
- - Build the interpolator once (module scope or `useMemo`). Construction does
251
- the expensive ring matching; sampling is cheap.
227
+ - Build the interpolator once (module scope or `useMemo`); construction is
228
+ the expensive part.
252
229
  - Every frame is a full SVG parse and rasterize of the `source` string. Fine
253
- for icons and small figures; for large scenes prefer `<canvas onDraw>` and
254
- draw the interpolated points yourself.
255
- - Old but stable (0.4.2 since 2017) — finished rather than unmaintained.
230
+ for icons and small figures; for large scenes draw the interpolated points
231
+ in `<canvas onDraw>`.
232
+ - 0.4.2 since 2017 — finished rather than unmaintained.
256
233
 
257
234
  ## react-transition-group {#react-transition-group}
258
235
 
@@ -260,16 +237,12 @@ function Morph() {
260
237
 
261
238
  A transition _state machine_, not an animator: `<Transition in={bool}
262
239
  timeout={ms}>` walks its child through `entering`/`entered`/`exiting`/
263
- `exited` on a timer and keeps the child mounted during exit. You decide what
264
- each state looks like. It is the standard way to keep an element mounted
265
- while it animates out, which react-x11 has no built-in answer for.
240
+ `exited` on a timer and keeps it mounted during exit — the way to keep an
241
+ element mounted while it animates out, which react-x11 has no built-in for.
266
242
 
267
- Two rules. Always pass `nodeRef` — without it, v4 falls back to
268
- `ReactDOM.findDOMNode`, which would receive a react-x11 node it cannot
269
- understand. And map states to styles yourself, pairing with the built-in
270
- `transition` style so the state flip _tweens_ instead of snapping. That
271
- combination is the whole "CSSTransition" story here, since there are no class
272
- names.
243
+ Always pass `nodeRef` — without it, v4 falls back to `ReactDOM.findDOMNode`,
244
+ which cannot handle a react-x11 node. Map states to styles yourself, with the
245
+ built-in `transition` style so the flip _tweens_ instead of snapping.
273
246
 
274
247
  ```jsx
275
248
  import React, { useRef } from 'react';
@@ -301,14 +274,13 @@ function Panel({ shown }) {
301
274
  }
302
275
  ```
303
276
 
304
- - **`react-dom` must be present in `node_modules`.** `cjs/Transition.js`
305
- requires it at module scope for the `findDOMNode` fallback, even when
306
- `nodeRef` means it is never called. It is a peer dependency: install it, it
307
- will not be rendered with.
308
- - `CSSTransition` is pointless here — it toggles class names, and there are
309
- none. Use plain `Transition`.
310
- - `timeout` and your visual transition duration are independent numbers. Keep
311
- them equal, or the state flips mid-tween.
277
+ - **`react-dom` must be in `node_modules`.** `cjs/Transition.js` requires it
278
+ at module scope for the `findDOMNode` fallback, even when `nodeRef` means
279
+ it is never called. Install the peer; nothing renders through it.
280
+ - `CSSTransition` toggles class names, and there are none. Use plain
281
+ `Transition`.
282
+ - `timeout` and your transition duration are independent; keep them equal,
283
+ or the state flips mid-tween.
312
284
  - `addEndListener` works; call the provided `done` yourself.
313
285
 
314
286
  ## `@react-spring/core` {#react-spring}
@@ -316,23 +288,20 @@ function Panel({ shown }) {
316
288
  **Adapter, ~20 lines.** @react-spring/core@10.1.2,
317
289
  @react-spring/animated@10.1.2.
318
290
 
319
- The platform-independent half of react-spring: `useSpring`, `useTransition`,
320
- `useTrail`, `SpringValue`, `Controller`. `@react-spring/animated` provides
321
- `createHost`, the documented seam for building a non-DOM target — it is how
322
- `@react-spring/native` and `/three` exist.
323
-
324
- Springs, interruption with velocity carry-over, and `api.start()` are exactly
325
- what the built-in duration-only `transition` cannot express. Two public
291
+ The platform-independent half of react-spring: `useSpring`,
292
+ `useTransition`, `useTrail`, `SpringValue`, `Controller` — springs,
293
+ interruption with velocity carry-over, `api.start()`.
294
+ `@react-spring/animated` provides `createHost`, the documented seam for a
295
+ non-DOM target (how `@react-spring/native` and `/three` exist). Two public
326
296
  seams do the work:
327
297
 
328
- - `Globals.assign({ now, requestAnimationFrame })` points react-spring's
329
- frame loop at a clock.
298
+ - `Globals.assign({ now, requestAnimationFrame })` points the frame loop at
299
+ a clock.
330
300
  - `createHost(['box', …], { applyAnimatedValues })` produces `animated.box`.
331
- Every frame, react-spring calls `applyAnimatedValues(instance, props)`
332
- where `instance` is the react-x11 node your ref points at — and
333
- `node.applyProps(next, prev)` is the same imperative path the reconciler's
334
- own `commitUpdate` uses. Frames bypass React entirely: a component renders
335
- once while hundreds of frames apply.
301
+ Each frame, react-spring calls `applyAnimatedValues(instance, props)` with
302
+ the react-x11 node, and `node.applyProps(next, prev)` is the imperative
303
+ path the reconciler's `commitUpdate` uses. A component renders once while
304
+ hundreds of frames apply.
336
305
 
337
306
  ```jsx
338
307
  // spring-x11.js — the adapter
@@ -375,20 +344,17 @@ function Bar() {
375
344
  }
376
345
  ```
377
346
 
378
- - **Merge `style` one level deep in `applyAnimatedValues`** — this is the
379
- number-one silent failure. On animation frames react-spring calls
380
- `props.getValue(true)` for _animated leaves only_, so static keys
381
- (`height: 30` next to an animated `width`) are absent from the payload and
382
- a naive `{...prev, ...props}` merge wipes them. The box then paints
383
- nothing, with no error.
384
- - Hex, `rgb()` and `rgba()` colour strings interpolate out of the box. Named
385
- CSS colours are not enabled by default in `@react-spring/shared` — stick to
386
- hex and rgb.
347
+ - **Merge `style` one level deep in `applyAnimatedValues`** — the
348
+ number-one silent failure. Frames carry _animated leaves only_
349
+ (`props.getValue(true)`), so static keys (`height: 30` beside an animated
350
+ `width`) are absent, and a naive `{...prev, ...props}` merge wipes them:
351
+ the box paints nothing, with no error.
352
+ - Hex, `rgb()` and `rgba()` strings interpolate; named CSS colours are not
353
+ enabled by default in `@react-spring/shared`.
387
354
  - Do not use `useScroll`, `useResize`, `useInView` or `useReducedMotion` from
388
355
  core; they observe the DOM `window` and `matchMedia`.
389
356
  `Globals.assign({ skipAnimation })` is the manual reduced-motion switch.
390
357
  - Style arrays (`style={[a, b]}`) defeat the shallow merge. Flatten first —
391
358
  react-x11 exports `flattenStyle`.
392
- - Driving the clock from `setImmediate` runs frames as fast as the event loop
393
- turns, which is fine for tests and wasteful for apps. Use the ntk window
394
- clock or a 16 ms timeout.
359
+ - A `setImmediate` clock runs frames as fast as the event loop turns — fine
360
+ for tests, wasteful for apps. Use the ntk window clock or a 16 ms timeout.
@@ -1,33 +1,26 @@
1
1
  # Data fetching
2
2
 
3
3
  A react-x11 app is an ordinary Node process, so `fetch`, undici, `ky` and
4
- `graphql-request` all work unmodified. What needs care is the _cache_ layer
5
- on top, because every stale-while-revalidate library decides whether it is
6
- "on a server" by looking for a global `window` — and in a react-x11 process
7
- there isn't one.
8
-
9
- That check is the whole story on this page. Get it wrong and nothing throws;
10
- the app just stops refetching.
4
+ `graphql-request` work unmodified. The _cache_ layer on top needs care:
5
+ stale-while-revalidate libraries decide whether they are "on a server" by
6
+ looking for a global `window`, and a react-x11 process has none. Get it
7
+ wrong and nothing throws; the app just stops refetching.
11
8
 
12
9
  ## TanStack Query {#tanstack-query}
13
10
 
14
11
  **Adapter, ~12 lines.** @tanstack/react-query@5.101.4 (query-core 5.101.4).
15
12
 
16
- Query and mutation cache for server state: `useQuery` deduplicates, caches,
17
- retries and background-refetches; `useMutation` handles writes with
18
- invalidation. The core is renderer-agnostic, and the environment coupling
19
- lives in three small managers — `focusManager`, `onlineManager`,
20
- `environmentManager` — all replaceable.
21
-
22
- The trap is query-core's environment probe:
13
+ `useQuery` deduplicates, caches, retries and background-refetches;
14
+ `useMutation` handles writes with invalidation. The environment coupling
15
+ lives in three replaceable managers — `focusManager`, `onlineManager`,
16
+ `environmentManager`. The trap is query-core's probe:
23
17
 
24
18
  ```js
25
19
  // @tanstack/query-core src/utils.ts
26
20
  export const isServer = typeof window === 'undefined' || 'Deno' in globalThis;
27
21
  ```
28
22
 
29
- A react-x11 process has no `window`, so query-core decides it is doing SSR.
30
- Concretely:
23
+ With no `window`, query-core decides it is doing SSR:
31
24
 
32
25
  - `retry` defaults to **0** instead of 3;
33
26
  - stale timers are never scheduled and `refetchInterval` is disabled;
@@ -37,10 +30,8 @@ Concretely:
37
30
  `window.addEventListener('visibilitychange')`, so focus refetching is
38
31
  inert.
39
32
 
40
- The escape hatch shipped in v5: `environmentManager.setIsServer()`,
41
- re-exported from `@tanstack/react-query`. Pair it with react-x11's window
42
- focus events, which fire on the X `FocusIn`/`FocusOut` the window manager
43
- delivers.
33
+ The fix is `environmentManager.setIsServer()`, re-exported from
34
+ `@tanstack/react-query`, paired with react-x11's window focus events:
44
35
 
45
36
  ```jsx
46
37
  // query-x11.js — the whole adapter
@@ -89,29 +80,24 @@ function Weather() {
89
80
  );
90
81
  ```
91
82
 
92
- Focusing the window after being away now refetches stale queries, exactly
93
- like refocusing a browser tab.
83
+ Focusing the window now refetches stale queries, like refocusing a browser
84
+ tab.
94
85
 
95
86
  - **Call `setIsServer` before creating the `QueryClient`** — the managers
96
87
  are module singletons.
97
- - With several app windows in one process, switching between two of your own
98
- windows fires blur-then-focus, which counts as a focus event and refetches
99
- stale queries. Same semantics as switching browser tabs; raise `staleTime`
100
- if that is too eager.
101
- - `onlineManager` defaults to online under Node (there is no
102
- `navigator.onLine`). Wire it to NetworkManager over D-Bus if you want real
103
- offline handling.
88
+ - Switching between two of your own windows fires blur-then-focus, which
89
+ refetches stale queries; raise `staleTime` if that is too eager.
90
+ - `onlineManager` defaults to online under Node (no `navigator.onLine`).
91
+ Wire it to NetworkManager over D-Bus for real offline handling.
104
92
  - `@tanstack/react-query-devtools` is DOM-rendered and not usable.
105
93
 
106
94
  ## SWR {#swr}
107
95
 
108
96
  **Adapter, 1 line — but read the dead end first.** swr@2.4.2.
109
97
 
110
- Basic operation works unmodified: `useSWR` fetches on mount, caches, dedupes
111
- and re-renders through `useSyncExternalStore`.
112
-
113
- Focus and reconnect revalidation do not, and they fail silently. SWR
114
- computes its environment once at import time, with no override:
98
+ `useSWR` fetches on mount, caches, dedupes and re-renders unmodified. Focus
99
+ and reconnect revalidation fail silently: SWR computes its environment once
100
+ at import time, with no override:
115
101
 
116
102
  ```js
117
103
  // swr/dist/_internal/config-context-*.mjs
@@ -121,14 +107,11 @@ const IS_SERVER = !isWindowDefined || isLegacyDeno;
121
107
 
122
108
  Revalidation event sources are registered only `if (!IS_SERVER)` —
123
109
  _including_ a custom `initFocus`/`initReconnect` passed through the
124
- cache-provider config. So the "wire your own focus events via the provider
125
- option" approach from SWR's docs is dead code under Node: your `initFocus`
126
- is never called, and swr ends up with zero registered listeners. Unlike
127
- TanStack Query there is no `setIsServer`.
110
+ cache-provider config, so the provider approach from SWR's docs is dead code
111
+ under Node and swr registers zero listeners. There is no `setIsServer`.
128
112
 
129
- Two ways out. The simple one is to drive revalidation yourself with
130
- `mutate`; a key filter of `() => true` revalidates every key in the cache,
131
- so hang it off `<window onFocus>`:
113
+ Two ways out. The simple one: drive revalidation with `mutate`, whose key
114
+ filter `() => true` revalidates every cached key, from `<window onFocus>`:
132
115
 
133
116
  ```jsx
134
117
  import React from 'react';
@@ -155,8 +138,8 @@ function Weather() {
155
138
  );
156
139
  ```
157
140
 
158
- The thorough one is to make `IS_SERVER` false, which turns swr's own
159
- machinery back on. It has to happen **before the first import of swr**:
141
+ The thorough one: make `IS_SERVER` false, which turns swr's own machinery
142
+ back on. It has to happen **before the first import of swr**:
160
143
 
161
144
  ```js
162
145
  // swr-env.js — imported first, before anything that imports swr
@@ -178,24 +161,26 @@ export const fireFocus = () =>
178
161
  (listeners.get('focus') ?? []).forEach((fn) => fn());
179
162
  ```
180
163
 
181
- With that in place swr registers `visibilitychange`, `focus`, `online` and
182
- `offline` listeners and `revalidateOnFocus` behaves. Two gotchas:
164
+ swr then registers `visibilitychange`, `focus`, `online` and `offline`
165
+ listeners and `revalidateOnFocus` behaves. Two gotchas:
183
166
 
184
167
  - Fire only `'focus'`. An `'offline'` event sets swr's internal online flag
185
168
  false and silently suppresses the deferred revalidation.
186
- - `focusThrottleInterval` defaults to 5000 ms, so a focus that lands within
187
- five seconds of the last revalidation is dropped — which looks exactly
188
- like the shim not working. Lower it while you are testing the wiring.
169
+ - `focusThrottleInterval` defaults to 5000 ms, so a focus within five
170
+ seconds of the last revalidation is dropped — which looks exactly like the
171
+ shim not working. Lower it while testing the wiring.
172
+
173
+ Other caveats:
189
174
 
190
175
  - **`revalidateOnFocus` and `revalidateOnReconnect` are inert** unless you
191
176
  define the globals. Nothing warns you.
192
- - The global `mutate` import targets the _default_ cache. If you mount a
193
- custom `provider` via `<SWRConfig>`, calls to the global `mutate` silently
194
- miss it — use `useSWRConfig().mutate` from inside the tree.
177
+ - The global `mutate` targets the _default_ cache, so with a custom
178
+ `provider` via `<SWRConfig>` it silently misses; use
179
+ `useSWRConfig().mutate` from inside the tree.
195
180
  - Suspense mode throws
196
181
  `Fallback data is required when using Suspense in SSR.` because SWR
197
182
  believes it is on a server. Provide `fallbackData`, or avoid
198
183
  `suspense: true`.
199
184
  - `preload()` is an SSR-gated no-op under Node.
200
- - If you want focus revalidation with retries and cache GC done properly,
201
- prefer TanStack Query above — it ships a supported environment override.
185
+ - For focus revalidation with retries and cache GC done properly, prefer
186
+ TanStack Query — it ships a supported environment override.