react-x11 2.52.0 → 2.54.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 (115) 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 +1096 -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/imagedecode-worker.js +32 -0
  76. package/src/imagedecode.js +140 -2
  77. package/src/layouts.js +1 -1
  78. package/src/nodes/animation.js +1 -1
  79. package/src/nodes/invalidate.js +1 -1
  80. package/src/nodes/layouthost.js +1 -1
  81. package/src/nodes/video.js +19 -8
  82. package/src/nodes/window/droptarget.js +1 -1
  83. package/src/ntk.d.ts +12 -0
  84. package/src/pacing.js +1 -1
  85. package/src/refresh/index.js +2 -1
  86. package/src/types/video.d.ts +5 -2
  87. package/src/videobackend.js +28 -0
  88. package/src/videoframes.js +29 -16
  89. package/src/wayland/app.js +27 -0
  90. package/src/wayland/video.js +376 -0
  91. package/src/win32/a11y.js +1 -1
  92. package/src/win32/app.js +2 -2
  93. package/src/win32/fonts.js +1 -1
  94. package/src/win32/glarea.js +4 -4
  95. package/src/win32/overlay.js +1 -1
  96. package/src/win32/panewindow.js +3 -3
  97. package/src/win32/shell.js +1 -1
  98. package/src/win32/window.js +2 -2
  99. package/src/windowid.js +1 -1
  100. package/src/xvideo.js +189 -0
  101. package/docs/architecture/accessibility.md +0 -470
  102. package/docs/architecture/animation.md +0 -707
  103. package/docs/architecture/container-queries.md +0 -525
  104. package/docs/architecture/custom-layout.md +0 -334
  105. package/docs/architecture/drag-and-drop.md +0 -941
  106. package/docs/architecture/element-layer-contents.md +0 -622
  107. package/docs/architecture/frame-pacing.md +0 -255
  108. package/docs/architecture/grid-layout.md +0 -358
  109. package/docs/architecture/protocol-efficiency.md +0 -357
  110. package/docs/architecture/video.md +0 -1149
  111. package/docs/architecture/windowed-regions.md +0 -854
  112. package/docs/glx.md +0 -137
  113. package/docs/wayland.md +0 -545
  114. package/docs/windows-embedding.md +0 -271
  115. package/docs/windows-gl.md +0 -221
package/docs/devtools.md CHANGED
@@ -26,14 +26,12 @@ What you get:
26
26
 
27
27
  - the **component tree**, live as the app updates;
28
28
  - **props, hooks and state** when selecting a component, and editing any of
29
- them — the edit goes through the reconciler's own override path, so the
30
- app really re-renders;
29
+ them — the app really re-renders;
31
30
  - **highlight-on-hover** — hovering an element in the tree tints its rect
32
- in the X11 window;
31
+ in the window;
33
32
  - **"highlight updates when components render"** — the rects that just
34
- re-rendered are outlined, in DevTools' own colour ramp (teal for a first
35
- render through amber for one that keeps re-rendering), fading out on the
36
- backend's clock;
33
+ re-rendered are outlined in DevTools' own colour ramp (teal for a first
34
+ render through amber for one that keeps re-rendering), fading out;
37
35
  - the **element picker** — the crosshair in the DevTools toolbar. While it
38
36
  is on, the pointer belongs to DevTools: motion tints whatever is under
39
37
  it, a click selects that element in the tree, `Escape` gives up. The app
@@ -44,9 +42,8 @@ What you get:
44
42
  "host components") to have something with a style to select;
45
43
  - the Profiler's commit list and flamegraph, including **"reload and start
46
44
  profiling"**, which re-execs the app with profiling already on so the
47
- mount itself is recorded. **Not** its Timeline view, which needs
48
- `injectProfilingHooks` — this reconciler build does not expose it, so
49
- there is nothing to enable;
45
+ mount itself is recorded. **Not** its Timeline view, which this reconciler
46
+ build does not support;
50
47
  - **DevTools' own settings** — component filters, console patching — kept
51
48
  between runs, in `$XDG_CACHE_HOME/react-x11/devtools.json`
52
49
  (`~/Library/Caches/react-x11/devtools.json` on macOS).
@@ -61,23 +58,20 @@ X app runs on a headless box). `REACT_X11_DEVTOOLS_STATE` moves the
61
58
  settings file.
62
59
 
63
60
  DevTools has no network panel — nothing in its protocol carries network
64
- data, and no injection would change that. For that, `node --inspect` and
65
- `chrome://inspect` still work alongside all of this.
61
+ data. For that, `node --inspect` and `chrome://inspect` still work alongside
62
+ all of this.
66
63
 
67
64
  ## Opening the source from the DevTools panel
68
65
 
69
66
  The inspected-element panel already shows where a component was written —
70
- `file:///…/tasks.jsx:42:7`, from the same React 19 call-site stacks
71
- click-to-component reads, absolute and source-mapped. Making that line
72
- _clickable_ is a standalone-side setting, and it has two independent
73
- routes:
67
+ `file:///…/tasks.jsx:42:7`, absolute and source-mapped. Making that line
68
+ _clickable_ is a standalone-side setting, with two independent routes:
74
69
 
75
70
  - **Spawn an editor.** The standalone launches one itself, choosing it from
76
- `REACT_EDITOR` (a full command line, shell-quoted), else — on macOS — by
77
- looking through `ps x` for an editor that is already running, else
78
- `VISUAL`, else `EDITOR`. It knows each editor's argument style — vim
79
- takes `+42`, sublime and the JetBrains editors take `file:42` — so this
80
- is the route to reach for:
71
+ `REACT_EDITOR` (a full command line, shell-quoted), else — on macOS — an
72
+ editor already running, else `VISUAL`, else `EDITOR`. It knows each
73
+ editor's argument style (vim's `+42`, sublime's and JetBrains' `file:42`),
74
+ so this is the route to reach for:
81
75
 
82
76
  ```sh
83
77
  REACT_EDITOR=cursor npx react-devtools
@@ -85,158 +79,47 @@ routes:
85
79
 
86
80
  - **Open a URL.** ⚙ → General → "Open local files directly in your code
87
81
  editor", with the VS Code preset or a custom template using `{path}`,
88
- `{line}` and `{column}`. The frontend turns our `file://` source into a
89
- plain path before filling it in. The setting lives in the standalone's
90
- own storage, not the app's.
82
+ `{line}` and `{column}`. The setting lives in the standalone's own
83
+ storage, not the app's.
91
84
 
92
85
  Both are configured where the standalone runs, which may be a different
93
- machine from the app. Neither is something the app can inject: the source
94
- location is all the backend is asked for, and it is already sent.
95
- Click-to-component ([click-to-component.md](click-to-component.md)) is the
96
- app-side answer to the same question, and the one that works when DevTools
97
- runs somewhere without your editor on it.
86
+ machine from the app; the app cannot inject either.
87
+ [Click-to-component](click-to-component.md) is the app-side answer to the same
88
+ question, and the one that works when DevTools runs somewhere without your
89
+ editor on it.
98
90
 
99
91
  The same backend, minus the socket, powers `react-x11/test`'s component
100
92
  inspection: `inspect()`/`setHook` in
101
- [testing.md](testing.md#inspectnode-and-sethook) drive the renderer
102
- interface the backend registers at injection, entirely in-process — no
103
- standalone app, no `ws`, no env variable. If a test run does set
104
- `REACT_X11_DEVTOOLS=1`, the two share one renderer registration (see the
105
- guard in `connect()`).
106
-
107
- ## Watching the wire
108
-
109
- The bridge is a plain WebSocket carrying text frames of `{"event": …,
110
- "payload": …}` — no CDP, no binary framing. `scripts/devtools-proxy.mjs`
111
- sits in the middle of it and prints what goes past, decoding the packed
112
- `operations` arrays (the tree deltas) into readable lines:
113
-
114
- ```sh
115
- npx react-devtools # terminal 1 — the real UI on :8097
116
- npm run devtools:proxy # terminal 2 — :8098 -> :8097
117
- REACT_X11_DEVTOOLS=1 REACT_X11_DEVTOOLS_PORT=8098 npm run examples:dashboard
118
- ```
119
-
120
- ```
121
- 0.008 ▲ app operations 193B
122
- · renderer 1, root 1
123
- · add root 1
124
- · add 3 <Counter> (Function) under 2
125
- 0.291 ▼ devtools inspectElement {"id":3,"rendererID":1,…}
126
- ```
127
-
128
- `--only`/`--skip` filter by event name, `--full` prints whole payloads, and
129
- `--jsonl FILE` appends every frame for later analysis.
93
+ [testing.md](testing.md#inspectnode-and-sethook) run entirely in-process — no
94
+ standalone app, no `ws`, no env variable. A test run that also sets
95
+ `REACT_X11_DEVTOOLS=1` shares one renderer registration with them.
130
96
 
131
97
  ## How it works (and its sharp edges)
132
98
 
133
- `src/DevToolsIntegration.js`. When `REACT_X11_DEVTOOLS` is set, the hook is
134
- installed from `createRoot()` rather than at import time — a
135
- top-level await there would make every bundle of react-x11 ESM-only, which
136
- rules out a single executable ([packaging.md](packaging.md)). The ordering
137
- guarantee is the same: a commit can only follow a root, and there is no way
138
- to get one without awaiting the install. It connects the backend and
139
- registers the renderer via `injectIntoDevTools`.
140
-
141
- The backend never stops trying to connect — a retry timer while no UI
142
- listens, an open socket while one does — and either kept the process up
143
- after the app's last window closed. Its code runs in a scope
144
- (`inBackend`) where the timers it starts and the sockets it opens are
145
- unref'd, entered at our connect call, at its socket's close and error
146
- handlers, and at the timers it armed there; the globals are swapped only
147
- for that synchronous stretch, so the app's own `setTimeout` and
148
- `WebSocket` are untouched. The socket is `ws` from react-devtools-core's
149
- own dependencies, which is why `ws` is not a separate install.
150
-
151
- Under `react-x11 dev` the hook is installed earlier, before React Refresh
152
- loads: Refresh installs a stub hook where it finds none, the backend
153
- installs none where it finds one, and the stub cannot connect.
154
-
155
- Notes for maintainers:
156
-
157
- - **`injectIntoDevTools()` takes no arguments** in react-reconciler 0.34.
158
- What it reports comes from the host config — `rendererPackageName`,
159
- `rendererVersion`, `extraDevToolsConfig` — and an argument passed here is
160
- ignored without a word, which is how react-x11 shipped for a while with
161
- its renderer name and version quietly missing (#121). A test in
162
- `test/smoke.test.js` asserts what reaches the hook, so the next
163
- reconciler bump cannot repeat it.
164
- - react-devtools-core v7 requires `initialize()` before
165
- `connectToDevTools`, exposes its API on the **default export** under
166
- node ESM interop, and expects browser-ish globals (`self`, `window`) —
167
- all handled in `prepare()`.
168
- - The Highlighter only emits `showNativeHighlight` for host instances that
169
- implement `getClientRects()` returning a non-empty rect, and anything
170
- with `getClientRects` is measured at mount through
171
- `instance.ownerDocument.documentElement` — nodes therefore expose both
172
- (see `Node.getClientRects` / the `ownerDocument` stub in
173
- `src/nodes/`).
174
- - Highlight events arrive on the backend agent
175
- (`hook.reactDevtoolsAgent`, or the `'react-devtools'` hook event);
176
- `attachHighlightAgent` maps the public instance to its owning window
177
- node and paints a translucent overlay.
178
- - **What makes the native paths run at all** is
179
- `isReactNativeEnvironment()`, which the backend defines as
180
- `window.document == null`. That is true for a node process with
181
- `window = global`, so the backend takes React Native's route everywhere:
182
- it _asks the host_ to draw the highlight and the update outlines, and to
183
- drive the picker, instead of drawing into a DOM overlay of its own. Every
184
- injection below is one of those asks.
185
- - **Update outlines**: the backend counts updates, picks each rect's colour
186
- and expires it on its own clock, then emits `drawTraceUpdates`
187
- (`[{node, color}]` — the whole live set, every time) and
188
- `disableTraceUpdates`. `attachTraceUpdatesAgent` groups them by window
189
- and hands each root the rects; `WindowNode.setTraceUpdates` is a dumb
190
- overlay that replaces rather than accumulates. It schedules those redraws
191
- on `requestAnimationFrame`, which node does not have — `prepare()`
192
- installs an unref'd `setTimeout` shim, and without it the first traced
193
- commit throws inside the backend.
194
- - **The picker**: with no `window.addEventListener` to install, the backend
195
- emits `startInspectingNative` and waits. `attachPickerAgent` takes the
196
- pointer through `setInspectHandler` (events.js) — motion, press and
197
- release are answered and go no further — and calls `agent.selectNode()`
198
- then `agent.stopInspectingNative(true)`. Cancelling from the DevTools
199
- side arrives as a bridge message with no host-facing event, so the
200
- picker also listens on `agent._bridge` directly; without that a
201
- cancelled picker would keep swallowing the app's input.
202
- - **The style editor** needs three things and is offered only if it gets
203
- them: `resolveRNStyle` (our `flattenStyle`, minus state blocks, which are
204
- not editable values), `nativeStyleEditorValidAttributes`
205
- (`STYLE_PROP_NAMES`), and `instance.measure(cb)` on host instances for
206
- the box model — RN's contract, implemented by `Node.measure`. Edits
207
- arrive as `overrideValueAtPath` on `['style']` with the value wrapped in
208
- an array, which works here only because a react-x11 `style` prop already
209
- takes an array.
210
- - **Reload and profile** is gated on a backend check that asks whether
211
- synchronous XHR works, so `isReloadAndProfileSupported: true` is passed
212
- explicitly. Registering a renderer does not start profiling, either — the
213
- run that comes back from the restart starts it itself, before the first
214
- commit, which is the only reason to restart at all.
215
- - `localStorage`/`sessionStorage` are **defined** on the global rather than
216
- assigned: reading `global.localStorage` first is what makes node print
217
- its `--localstorage-file` warning on every DevTools run.
218
- - **Hook names and sources need a deeper stack than V8 keeps.** React reads
219
- each hook's location — and the custom-hook nesting the names come from —
220
- off a stack captured at the hook dispatch, and `Error.stackTraceLimit` is
221
- 10 by default. A loader leaves frames of its own between the hook and its
222
- call site (`tsx`, which every `npm run examples:*` goes through, costs
223
- about ten), so the identifying frames fall off the end: `hookSource`
224
- comes back `{fileName: null, lineNumber: null, …}`, the frontend logs
225
- "Hook source code location not found", and the element shows its hooks
226
- with no names and no source lines (#514). `prepare()` raises the limit to
227
- 50 when the bridge is enabled, and only upwards, so an app that asked for
228
- more keeps it. `test/devtools-hooks.test.js` squeezes the budget and
229
- asserts the names and sources still come back over the bridge.
230
- - **Selecting a component with hooks can blank the DevTools window.** That
231
- is the standalone app's own renderer process aborting, upstream of
232
- anything the host sends: it happens with every hook source complete, and
233
- under runtimes with no loader at all. Turning ⚙ → Components → "Always
234
- parse hook names for the selected element" off makes it intermittent
235
- rather than reliable; restarting `npx react-devtools` reconnects to the
236
- app, which is unaffected.
237
- - `test/devtools.test.js` drives the real backend over a socket for all of
238
- the above; the fake-agent tests in `test/smoke.test.js` prove the drawing
239
- but not that the backend still asks for it.
99
+ The bridge is installed by `createRoot()` when `REACT_X11_DEVTOOLS` is set (by
100
+ `react-x11 dev`, earlier, before React Refresh loads). Its socket is `ws` from
101
+ `react-devtools-core`'s own dependencies, so `ws` is not a separate install.
102
+ What an app can notice while it is on:
103
+
104
+ - **It does not keep the process alive.** The backend's retry timer and socket
105
+ are unref'd, so the process still exits when the app's last window closes;
106
+ the app's own `setTimeout` and `WebSocket` are untouched.
107
+ - **Browser-ish globals appear.** `self` and `window` are set to the global
108
+ where absent, `requestAnimationFrame`/`cancelAnimationFrame` are shimmed
109
+ where absent, and `localStorage`/`sessionStorage` are defined (the store
110
+ behind DevTools' settings file).
111
+ - **`Error.stackTraceLimit` is raised to 50** (never lowered), because React
112
+ reads hook names and sources off a stack captured at each hook call and a
113
+ loader such as `tsx` eats the default 10 frames. Without it, hooks show with
114
+ no names and no source lines.
115
+ - **Selecting a component with hooks can blank the DevTools window.** That is
116
+ the standalone app's own renderer process aborting, upstream of anything the
117
+ app sends. Turning ⚙ → Components → "Always parse hook names for the
118
+ selected element" off makes it intermittent rather than reliable;
119
+ restarting `npx react-devtools` reconnects to the app, which is unaffected.
120
+
121
+ Design notes and maintainer notes:
122
+ [design/devtools.md](../design/devtools.md#how-it-works-and-its-sharp-edges).
240
123
 
241
124
  ## Other debugging aids
242
125
 
@@ -245,6 +128,6 @@ Notes for maintainers:
245
128
  - Refs: drawn elements expose the retained node (`abs` rect, `scrollTo`,
246
129
  …); `<window>`/`<popup>` expose the live ntk window (`getContext('2d')`,
247
130
  `queryTree`, `setCursor`, …).
248
- - The in-process X server (`x11/lib/xserver`) renders headlessly and reads
249
- pixels back — see `test/integration.test.js` for the harness pattern,
250
- which is also how the README screenshots are generated.
131
+ - The in-process X server (node-x11's `lib/xserver`) renders headlessly and
132
+ reads pixels back; `react-x11/test` is built on it
133
+ ([testing.md](testing.md#pixels)).