react-x11 0.0.1 → 2.0.0-canary.20260820101121.g8a4ce2b

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 (163) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +480 -0
  3. package/package.json +181 -8
  4. package/src/ClickToComponent.js +186 -0
  5. package/src/DevToolsIntegration.js +514 -0
  6. package/src/Reconciler.js +806 -0
  7. package/src/a11y.js +1299 -0
  8. package/src/acceleratorhooks.js +86 -0
  9. package/src/accelerators.js +128 -0
  10. package/src/activate.js +168 -0
  11. package/src/anchor.js +305 -0
  12. package/src/appcontext.js +161 -0
  13. package/src/appearance.js +748 -0
  14. package/src/appearancehooks.js +96 -0
  15. package/src/apphooks.js +73 -0
  16. package/src/application.js +748 -0
  17. package/src/atspi.js +1953 -0
  18. package/src/bus.js +545 -0
  19. package/src/bushooks.js +136 -0
  20. package/src/clientmessage.js +140 -0
  21. package/src/clipboard.js +121 -0
  22. package/src/components/Button.js +97 -0
  23. package/src/components/Calendar.js +612 -0
  24. package/src/components/Canvas3D.js +107 -0
  25. package/src/components/Checkbox.js +103 -0
  26. package/src/components/DatePicker.js +304 -0
  27. package/src/components/Dialog.js +195 -0
  28. package/src/components/FileDialog.js +491 -0
  29. package/src/components/Icon.js +329 -0
  30. package/src/components/Menu.js +1307 -0
  31. package/src/components/PasswordInput.js +434 -0
  32. package/src/components/ProgressBar.js +127 -0
  33. package/src/components/Radio.js +138 -0
  34. package/src/components/Select.js +456 -0
  35. package/src/components/Slider.js +234 -0
  36. package/src/components/SplitPane.js +203 -0
  37. package/src/components/Switch.js +127 -0
  38. package/src/components/Table.js +525 -0
  39. package/src/components/Tabs.js +211 -0
  40. package/src/components/Tooltip.js +522 -0
  41. package/src/components/Tree.js +311 -0
  42. package/src/components/anchor.js +251 -0
  43. package/src/components/change.js +34 -0
  44. package/src/components/dates.js +241 -0
  45. package/src/components/dnd.js +103 -0
  46. package/src/components/index.js +34 -0
  47. package/src/components/keys.js +18 -0
  48. package/src/components/scribble.js +247 -0
  49. package/src/components/theme.js +374 -0
  50. package/src/components/typeahead.js +66 -0
  51. package/src/compose.js +868 -0
  52. package/src/compositing.js +230 -0
  53. package/src/dbusmenu.js +384 -0
  54. package/src/debug.d.ts +63 -0
  55. package/src/debug.js +642 -0
  56. package/src/decorations.js +442 -0
  57. package/src/desktopsettings.js +209 -0
  58. package/src/desktopsettingshooks.js +62 -0
  59. package/src/dnd.js +1719 -0
  60. package/src/editmenu.js +272 -0
  61. package/src/errors.js +98 -0
  62. package/src/events.js +1591 -0
  63. package/src/filedialog.js +375 -0
  64. package/src/filedialoghooks.js +132 -0
  65. package/src/fonthooks.js +64 -0
  66. package/src/fonts.js +301 -0
  67. package/src/foreignnodes.js +519 -0
  68. package/src/frame/child.js +33 -0
  69. package/src/frame/childmain.js +274 -0
  70. package/src/frame/env.js +140 -0
  71. package/src/frame/index.js +444 -0
  72. package/src/frame/lifecycle.js +67 -0
  73. package/src/frame/protocol.js +179 -0
  74. package/src/frame3d.js +86 -0
  75. package/src/frames.js +73 -0
  76. package/src/geometry3d.js +225 -0
  77. package/src/glbackend.js +114 -0
  78. package/src/glnodes.js +425 -0
  79. package/src/globalmenu.js +683 -0
  80. package/src/host.d.ts +88 -0
  81. package/src/host.js +42 -0
  82. package/src/idle.js +498 -0
  83. package/src/idlehooks.js +100 -0
  84. package/src/index.d.ts +483 -0
  85. package/src/index.js +102 -0
  86. package/src/inputtime.js +206 -0
  87. package/src/jsx-dev-runtime.d.ts +21 -0
  88. package/src/jsx-dev-runtime.js +2 -0
  89. package/src/jsx-runtime.d.ts +39 -0
  90. package/src/jsx-runtime.js +6 -0
  91. package/src/keyboard.js +256 -0
  92. package/src/keyboardstate.js +286 -0
  93. package/src/keyboardstatehooks.js +58 -0
  94. package/src/keysyms.d.ts +149 -0
  95. package/src/keysyms.js +270 -0
  96. package/src/locale.js +170 -0
  97. package/src/localehooks.js +47 -0
  98. package/src/mat4.js +235 -0
  99. package/src/menuitem.js +223 -0
  100. package/src/node.d.ts +597 -0
  101. package/src/node.js +44 -0
  102. package/src/nodes.js +10045 -0
  103. package/src/ntk.d.ts +44 -0
  104. package/src/ntk.js +25 -0
  105. package/src/paintcache.js +366 -0
  106. package/src/palette.js +380 -0
  107. package/src/pastestate.js +66 -0
  108. package/src/pointer3d.js +158 -0
  109. package/src/portal.js +345 -0
  110. package/src/postprocess3d.js +606 -0
  111. package/src/priority.js +65 -0
  112. package/src/raycast3d.js +158 -0
  113. package/src/refresh/index.d.ts +40 -0
  114. package/src/refresh/index.js +122 -0
  115. package/src/refresh/loader.d.ts +37 -0
  116. package/src/refresh/loader.js +401 -0
  117. package/src/refresh/register.d.ts +5 -0
  118. package/src/refresh/register.js +13 -0
  119. package/src/registry.js +232 -0
  120. package/src/scene3d-shader.js +902 -0
  121. package/src/scene3d.js +1003 -0
  122. package/src/screens.js +745 -0
  123. package/src/screenshooks.js +77 -0
  124. package/src/startup.js +302 -0
  125. package/src/style.d.ts +126 -0
  126. package/src/style.js +33 -0
  127. package/src/styles.js +1544 -0
  128. package/src/svgnodes.js +301 -0
  129. package/src/testing/a11y.js +484 -0
  130. package/src/testing/components.js +414 -0
  131. package/src/testing/events.js +407 -0
  132. package/src/testing/harness.js +455 -0
  133. package/src/testing/index.d.ts +558 -0
  134. package/src/testing/index.js +85 -0
  135. package/src/testing/mock-app.js +436 -0
  136. package/src/testing/pixels.js +152 -0
  137. package/src/testing/queries.js +224 -0
  138. package/src/textrange.js +83 -0
  139. package/src/textselection.js +439 -0
  140. package/src/trace-registry.js +63 -0
  141. package/src/transfer.js +93 -0
  142. package/src/types/appearance.d.ts +84 -0
  143. package/src/types/application.d.ts +173 -0
  144. package/src/types/components.d.ts +972 -0
  145. package/src/types/dbus.d.ts +177 -0
  146. package/src/types/elements.d.ts +1014 -0
  147. package/src/types/events.d.ts +507 -0
  148. package/src/types/filedialog.d.ts +238 -0
  149. package/src/types/fonts.d.ts +124 -0
  150. package/src/types/frame.d.ts +146 -0
  151. package/src/types/globalmenu.d.ts +42 -0
  152. package/src/types/nodes.d.ts +199 -0
  153. package/src/types/style.d.ts +421 -0
  154. package/src/types/system.d.ts +265 -0
  155. package/src/windowid.js +151 -0
  156. package/src/windowstate.js +393 -0
  157. package/src/xsettings.js +341 -0
  158. package/src/yoga.d.ts +55 -0
  159. package/src/yoga.js +135 -0
  160. package/.npmignore +0 -17
  161. package/combobox.jsx +0 -69
  162. package/react-x11.js +0 -119
  163. package/test-canvas.js +0 -98
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2017 Andrey Sidorov
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1 +1,481 @@
1
1
  # react-x11
2
+
3
+ [![CI](https://github.com/sidorares/react-x11/actions/workflows/ci.yml/badge.svg)](https://github.com/sidorares/react-x11/actions/workflows/ci.yml)
4
+
5
+ **[Documentation](https://sidorares.github.io/react-x11/)** ·
6
+ **[Playground](https://sidorares.github.io/react-x11/playground)** — edit
7
+ react-x11 and watch it render, in your browser, against a JavaScript X server
8
+ running on the page.
9
+
10
+ A React renderer whose host environment is an [X11
11
+ server](https://www.x.org/wiki/Documentation/). React's job in a renderer is
12
+ to compute what changed; the renderer's job is to turn that into side effects
13
+ on some host — in react-dom those are DOM mutations, here they are **X11
14
+ protocol requests** written to a socket. There is no DOM, no HTML and no
15
+ browser engine underneath: this is not Electron with a different skin, and
16
+ `<div>` is not an element that exists. Build GUI programs for the X Window
17
+ environment (a linux desktop, or macOS +
18
+ [XQuartz](https://www.xquartz.org/)) with your React / React Native
19
+ experience — flexbox layout, components, hooks, synthetic events.
20
+
21
+ Everything is JavaScript all the way down: ntk /
22
+ [node-x11](https://github.com/sidorares/node-x11) implement the X11 protocol
23
+ in pure JS (think xlib rewritten in node.js), layout is
24
+ [yoga-layout](https://www.npmjs.com/package/yoga-layout), text shaping is
25
+ [fontkit](https://github.com/foliojs/fontkit). `npm install` never compiles
26
+ anything — and `npm test` doesn't even need an X server (node-x11 ships an
27
+ in-process pure-JS X server that the tests render into and read pixels back
28
+ from; every screenshot below was rendered that way too, by driving the real
29
+ examples through the real event pipeline).
30
+
31
+ ### The wire carries drawing, not pixels
32
+
33
+ react-x11 does not rasterize a frame on the client and ship the buffer
34
+ across. React reconciles the component tree, the renderer turns that diff
35
+ into **drawing operations** — rounded rectangles, composited gradients, clip
36
+ regions, runs of glyph indices — and the X server executes them. The server
37
+ owns the pixels; the client never had them.
38
+
39
+ That is what X's RENDER extension is for. Text is shaped once and its glyphs
40
+ uploaded once, so drawing a line afterwards names them by index, about a byte
41
+ per glyph; gradients, scaling, alpha compositing and clipping are single
42
+ server-side requests rather than loops over a pixel array; nothing is read
43
+ back. An update costs what the _drawing_ costs, not what the window's area
44
+ costs. Going full-screen on a 4K panel does not multiply your bandwidth,
45
+ because you were not sending pixels at 1080p either — which is why this
46
+ stays comfortable on a display forwarded over ssh. Mounting a window with
47
+ forty rows and their labels is 86 requests and 3.6 KB on the wire.
48
+
49
+ Because that is the design it is measured rather than assumed: `npm run
50
+ bench` reports requests, bytes, replies, RENDER composites and the pixel area
51
+ those composites touch, against a checked-in baseline.
52
+
53
+ ### Where this fits
54
+
55
+ X11 is the wire protocol, which means the display can be somewhere the
56
+ program is not, and the program can be somewhere a browser engine cannot go.
57
+ That is the shape of the problem this is good at:
58
+
59
+ - **the display is elsewhere** — a headless server over `ssh -X`, a
60
+ container pointed at the host, a thin client, an X terminal, a
61
+ deliberately dumb workstation. A whole window appears for under four
62
+ kilobytes, because what crosses the link is drawing rather than pixels
63
+ ([docs/remote.md](docs/remote.md));
64
+ - **the machine cannot afford a browser engine** — a kiosk, an appliance, an
65
+ instrument panel, an ARM board with 512 MB, a locked-down box where
66
+ installing must not compile anything and root is not on offer;
67
+ - **you want the UI in the same process as the rest of your program** —
68
+ `fs`, `serialport`, `pg` and your components in one heap, one event loop,
69
+ no IPC bridge and no second bundler;
70
+ - **you want GUI tests that run in CI with no display server** — `npm test`
71
+ here renders real pixels through the real protocol into node-x11's
72
+ in-process X server, on a machine with no `$DISPLAY`, on macOS. That
73
+ harness is published as `react-x11/test`
74
+ ([docs/testing.md](docs/testing.md)).
75
+
76
+ And the shape it is not good at, so you can stop here rather than in week
77
+ three:
78
+
79
+ - **three platforms.** X11 only. macOS means XQuartz — a separate install, a
80
+ non-native look and no menu bar integration. Windows is out; if you need
81
+ Windows, use Electron or Tauri.
82
+ - **native Wayland.** There is no Wayland backend and there is not going to
83
+ be one; that would be a different renderer, not a flag. Ordinary
84
+ application windows work fine on a Wayland desktop through Xwayland, which
85
+ is not going away — but the desktop-shell half of X11 (panel struts,
86
+ global key grabs, screen capture, and the window-manager example below)
87
+ needs a real X session.
88
+ - **reusing web components.** There is no DOM. Your MUI, your Tailwind and
89
+ your `recharts` do not come with you; the state, data-fetching, validation
90
+ and math libraries mostly do. [docs/ecosystem.md](docs/ecosystem.md) says
91
+ which is which, and what the failure looks like when it is the wrong one.
92
+ - **rendering HTML.** There is no HTML element and no webview. Rich
93
+ documents are markdown, through `<Markdown>` in
94
+ [`@react-x11/components`](https://github.com/sidorares/react-x11-components).
95
+ - **text entry outside Latin.** See [Known issues](#known-issues).
96
+
97
+ | `examples/dashboard.jsx` — context theming, hooks | `examples/tasks.jsx` — useReducer, textinput, scrolling |
98
+ | ------------------------------------------------- | ------------------------------------------------------- |
99
+ | ![dashboard](docs/img/dashboard.png) | ![tasks](docs/img/tasks.png) |
100
+
101
+ | `examples/form.jsx` — textinput + Select | the open Select menu (a real `<popup>` window) |
102
+ | ---------------------------------------- | ---------------------------------------------- |
103
+ | ![form](docs/img/form.png) | ![select menu](docs/img/select-menu.png) |
104
+
105
+ `examples/three.jsx` — a react-three-fiber-shaped scene over **indirect
106
+ GLX**: `<mesh>`, geometries, materials, lights and a texture, drawn by
107
+ sending the GL protocol over the X connection. No native bindings, no GPU
108
+ driver bindings — the same "JavaScript all the way down" story as the rest.
109
+
110
+ ![3D over indirect GLX](docs/img/three.png)
111
+
112
+ ## Quick start
113
+
114
+ ```sh
115
+ npm install react-x11 react
116
+ ```
117
+
118
+ ```jsx
119
+ import React, { useState } from 'react';
120
+ import { createRoot } from 'react-x11';
121
+
122
+ function Counter() {
123
+ const [n, setN] = useState(0);
124
+ return (
125
+ <window
126
+ width={240}
127
+ height={120}
128
+ title="counter"
129
+ style={{ backgroundColor: '#f4f4f4' }}
130
+ >
131
+ <box
132
+ style={{
133
+ flexGrow: 1,
134
+ alignItems: 'center',
135
+ justifyContent: 'center',
136
+ gap: 10,
137
+ }}
138
+ >
139
+ <text style={{ fontSize: 24 }}>{String(n)}</text>
140
+ <box
141
+ style={{
142
+ backgroundColor: '#2980b9',
143
+ borderRadius: 6,
144
+ padding: 8,
145
+ cursor: 'pointer',
146
+ ':hover': { backgroundColor: '#1f6693' },
147
+ }}
148
+ onClick={() => setN(n + 1)}
149
+ >
150
+ <text style={{ color: 'white' }}>+1</text>
151
+ </box>
152
+ </box>
153
+ </window>
154
+ );
155
+ }
156
+
157
+ const root = await createRoot(); // connects via $DISPLAY
158
+ root.render(<Counter />);
159
+ ```
160
+
161
+ Run it with `tsx` or any JSX-capable loader — or skip JSX entirely with
162
+ `React.createElement` (see
163
+ [`examples/simple-nojsx.js`](examples/simple-nojsx.js), plain node, no build
164
+ step).
165
+
166
+ ## Elements
167
+
168
+ Only `<window>`, `<popup>` and `<glarea>` map to real X11 windows.
169
+ Everything else is laid out by yoga and drawn client-side into the window's
170
+ double-buffered 2d context — see [NEXT_STEPS.md](NEXT_STEPS.md) for the architecture rationale
171
+ and [docs/](docs/README.md) for the full API reference.
172
+
173
+ | element | what it is |
174
+ | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
175
+ | `<window>` | a real X11 window; the flex, paint and event root |
176
+ | `<popup>` | an override-redirect window at screen coordinates — menus, tooltips, dropdowns |
177
+ | `<box>` | flex container: layout props → yoga, plus backgrounds, borders (solid/dashed, radius), overflow clipping, zIndex. `overflow: 'scroll'` makes it a wheel-scrollable viewport with a drawn scrollbar |
178
+ | `<text>` | shaped, wrapped text (bidi, ligatures, font fallback); nested `<text>` elements are style spans |
179
+ | `<textinput>` | single-line editor: caret/selection via ntk's TextLayout caret API, clipboard (CLIPBOARD + X11 PRIMARY), word select, undo/redo, right-click menu |
180
+ | `<image>` | PNG/JPEG from `src`, natural-size aware |
181
+ | `<canvas>` | escape hatch: `onDraw={(ctx, {width, height}) => …}` with ntk's canvas-like 2d context (XRender-backed) |
182
+ | `<svg>` | static SVG through ntk SvgView, sized like `<image>` |
183
+ | `<glarea>` | an OpenGL surface over indirect GLX; the 3D scene below lives inside it |
184
+
185
+ Widget **components** (plain React on top of the primitives, themable via
186
+ `ThemeProvider`): `Button`, `Checkbox`, `Radio`/`RadioGroup`, `Switch`,
187
+ `Slider`, `ProgressBar`, `Select`, `Tooltip`, `MenuBar`/`ContextMenu`,
188
+ `Calendar`/`DatePicker` — one date or a range, with days blockable and a
189
+ seam for drawing into the cells — `PasswordInput`, whose mask is a scribble
190
+ that moves on every keystroke rather than a countable row of bullets —
191
+ `Dialog` — a modal built on
192
+ `<popup trapFocus>`, which traps Tab and restores focus when it closes —
193
+ plus the two containers an application window is built from: `Tabs`, `Tree`
194
+ and `SplitPane`. See
195
+ [docs/components.md](docs/components.md).
196
+
197
+ **The menu bar goes where the desktop keeps menus.** On Plasma, on a panel
198
+ running `vala-panel-appmenu`, or under one of the GNOME extensions that add a
199
+ menu bar, `<MenuBar menus={…}/>` hands its menu to the panel over
200
+ `com.canonical.dbusmenu` and stops drawing one; everywhere else it draws the
201
+ bar itself. No configuration, and the same line of JSX either way — the item
202
+ descriptors are dbusmenu's own vocabulary, so the array that draws the menu is
203
+ the array that serialises. `npm run globalmenu:host` is a panel in a terminal,
204
+ for seeing it work on a desktop that has none. See
205
+ [docs/globalmenu.md](docs/globalmenu.md).
206
+
207
+ **`myapp://` links open the app that is already running.**
208
+ `registerApplication({ appId, schemes })` before `createRoot()` owns the app's
209
+ name on the bus, exports `org.freedesktop.Application`, and — on the half of
210
+ all desktops where `xdg-open` ignores D-Bus activation and spawns a second copy
211
+ with the URI in `argv` — forwards it to the first copy and tells that copy to
212
+ exit. `useAppOpen()` receives the link, buffered and replayed if it arrived
213
+ before the tree mounted, and `activateWindow()` brings the window forward with
214
+ the launch's own timestamp, without which a window manager declines and the
215
+ taskbar entry just blinks. The OAuth redirect this is for has an easier answer
216
+ that needs none of it, and the page says so first. See
217
+ [docs/uri-schemes.md](docs/uri-schemes.md).
218
+
219
+ **Drag and drop** works with the rest of the desktop: `<box dropAccept={['files']}
220
+ onDrop={…}>` takes a file dragged out of Nautilus, and `<box draggable dragData={…}>`
221
+ can be dropped into a file manager or an editor. Drags that stay inside the
222
+ app never touch the wire and hand their payload over by reference, so a
223
+ reorderable list and a cross-application file drop are the same two props.
224
+ See [docs/drag-and-drop.md](docs/drag-and-drop.md).
225
+
226
+ ### 3D
227
+
228
+ Inside a `<glarea>` (or the `Canvas3D` component) the children are scene
229
+ elements with react-three-fiber's names:
230
+
231
+ ```jsx
232
+ <Canvas3D style={{ flexGrow: 1 }} camera={{ position: [0, 2, 6], fov: 45 }}>
233
+ <ambientLight intensity={0.35} />
234
+ <pointLight position={[5, 6, 6]} />
235
+ <mesh rotation={[0.5, 0.4, 0]} onClick={() => pick()}>
236
+ <boxGeometry args={[1.4, 1.4, 1.4]} />
237
+ <meshPhongMaterial color="#2980b9" shininess={60} />
238
+ </mesh>
239
+ </Canvas3D>
240
+ ```
241
+
242
+ `<mesh>`, `<group>`, box/plane/sphere/cylinder/torus geometries,
243
+ `<bufferGeometry>`, basic/Lambert/Phong materials with textures, four light
244
+ types, and pointer events resolved by client-side raycasting. Each geometry
245
+ is compiled into a **server-side display list** once, so a frame costs
246
+ matrices plus one `CallList` per mesh whatever the triangle count. What the
247
+ protocol cannot do — shaders, instancing, post-processing, shadows — throws
248
+ with the reason rather than half-working. See
249
+ [docs/elements.md](docs/elements.md#3d-scene-mesh-group-geometries-materials)
250
+ and [docs/glx.md](docs/glx.md).
251
+
252
+ Events are synthetic with capture/bubble phases and hit testing over the
253
+ drawn tree: `onClick` (with DOM-style `detail` click counting),
254
+ `onMouseDown/Up/Move`, `onMouseEnter/Leave`, `onWheel`, `onKeyDown/Up`,
255
+ `focusable` + `onFocus`/`onBlur` + Tab traversal, and a `cursor` prop.
256
+ User handlers run before element default actions and can
257
+ `ev.preventDefault()` — stopping a `<textinput>` from editing or a
258
+ a scroll container from scrolling, like the DOM.
259
+
260
+ ## Examples
261
+
262
+ All need an X server — but "an X server" is a broader thing than it sounds:
263
+ your Linux desktop, XQuartz on macOS, `Xvfb` for automation, `Xephyr` for a
264
+ disposable screen, a VNC server, or your own display reached over `ssh -X`
265
+ from wherever the program actually runs — see
266
+ [docs/remote.md](docs/remote.md), which is the case this architecture is
267
+ categorically better at.
268
+ [`examples/README.md`](examples/README.md) describes each one and how to
269
+ explore them:
270
+
271
+ ```sh
272
+ npm run examples:simple # hello world (JSX via tsx)
273
+ npm run examples:app # the showcase: Tabs + SplitPane hosting the rest
274
+ npm run examples:theming # three themes x light/dark, and a size query
275
+ npm run examples:simple-nojsx # the same, plain node — no build step
276
+ npm run examples:xeyes # canvas drawing + hooks
277
+ npm run examples:dashboard # context theming, custom hooks, components
278
+ npm run examples:tasks # useReducer, textinput, scrolling
279
+ npm run examples:menu # right-click context menu via <popup>
280
+ npm run examples:transparent # rounded translucent <popup transparent>
281
+ npm run examples:form # <textinput> + Select dropdowns
282
+ npm run examples:datepicker # Calendar/DatePicker: ranges, blocked days, events
283
+ npm run examples:password # PasswordInput: the scribble mask, and a custom one
284
+ npm run examples:gl # raw GL in a <glarea> (display-list cube)
285
+ npm run examples:three # <Canvas3D> scene: meshes, lights, textures
286
+ npm run examples:wm # a reparenting window manager (see below)
287
+ ```
288
+
289
+ The two GL examples additionally need a server with **indirect GLX**
290
+ enabled (`+iglx` / `AllowIndirectGLX` — off by default on many).
291
+
292
+ ### Window manager
293
+
294
+ `examples:wm` is a real reparenting window manager: it takes over the root
295
+ window, puts every application's window inside a frame it draws, and moves,
296
+ resizes, focuses and closes them. The frames are ordinary react-x11
297
+ `<window>`s — the titlebar, the buttons and the eight resize handles are
298
+ components with `onMouseDown` handlers, and the application is a foreign X
299
+ window reparented inside.
300
+
301
+ Only one window manager may own a display, so run it against a nested
302
+ server rather than the one managing your desktop:
303
+
304
+ ```sh
305
+ Xephyr :10 -screen 1200x800 &
306
+ DISPLAY=:10 npm run examples:wm
307
+ DISPLAY=:10 xterm & # give it something to manage
308
+ ```
309
+
310
+ To have it manage your real session instead — including replacing
311
+ `quartz-wm` on macOS, which XQuartz has a hook for — see
312
+ [`examples/README.md`](examples/README.md#the-window-manager).
313
+
314
+ ### Hot reloading
315
+
316
+ ```sh
317
+ npm run examples:tasks:hot # then edit examples/tasks.jsx while it runs
318
+ ```
319
+
320
+ Runs the tasks example under the supported hot-reload entry point,
321
+ `react-x11/refresh` (`node --import react-x11/refresh/register`, Node ≥
322
+ 22.15), with React **Fast Refresh**: saving `examples/tasks.jsx` updates
323
+ the edited components in place. The X11 connection, the mounted window,
324
+ and component state — the task list, even half-typed text in the input —
325
+ survive the reload (a component whose hook signature changed remounts
326
+ alone). Any app adopts it the same way — no accept handlers, no loader
327
+ files to copy; see
328
+ [docs/ecosystem/dev-tooling.md](docs/ecosystem/dev-tooling.md#react-refresh).
329
+
330
+ ## React DevTools
331
+
332
+ ```sh
333
+ npx react-devtools # 1. start the standalone UI
334
+ REACT_X11_DEVTOOLS=1 npm run examples:dashboard # 2. run any example with the bridge on
335
+ ```
336
+
337
+ The component tree, props and hook state show up live in the DevTools
338
+ window, and editing any of them re-renders the app. Hovering an element in
339
+ the tree tints its rect in the X11 window; the toolbar's crosshair picks an
340
+ element by clicking it in the app; "highlight updates when components
341
+ render" outlines the rects that just re-rendered; the style editor edits a
342
+ selected element's style live; and the Profiler can restart the app to
343
+ record its mount. `REACT_X11_DEVTOOLS_HOST` / `REACT_X11_DEVTOOLS_PORT`
344
+ override the default `localhost:8097`. See
345
+ [docs/devtools.md](docs/devtools.md).
346
+
347
+ Two more debugging aids:
348
+
349
+ - `REACT_X11_DEBUG_LAYOUT=1` outlines every laid-out node (color = tree
350
+ depth) — handy when a flexbox doesn't do what you expect.
351
+ - refs give you the retained node (`abs` rect, `scrollTo`, …) for drawn
352
+ elements, or the live [ntk](https://github.com/sidorares/ntk) window for
353
+ `<window>`/`<popup>` — the whole ntk API is a ref away.
354
+
355
+ ## Click to component
356
+
357
+ ```sh
358
+ REACT_X11_EDITOR=code npm run examples:tasks
359
+ ```
360
+
361
+ Alt+Click any rendered element (Option+Click on macOS/XQuartz) to open the
362
+ JSX line that created it in your editor. `REACT_X11_EDITOR` picks the CLI
363
+ and enables the feature in one go; `REACT_X11_CLICK_TO_COMPONENT=1` does
364
+ the same with the default editor
365
+ (`cursor`). See [docs/click-to-component.md](docs/click-to-component.md).
366
+
367
+ ## TypeScript
368
+
369
+ Types ship with the package — no `@types/react-x11` to install. Point JSX at
370
+ react-x11 and the X11 elements type-check:
371
+
372
+ ```jsonc
373
+ // tsconfig.json
374
+ {
375
+ "compilerOptions": {
376
+ "jsx": "react-jsx",
377
+ "jsxImportSource": "react-x11",
378
+ },
379
+ }
380
+ ```
381
+
382
+ ```tsx
383
+ import { createRoot, createStyles, Button } from 'react-x11';
384
+ import type { MouseEvent } from 'react-x11';
385
+
386
+ const s = createStyles({
387
+ root: { flexGrow: 1, padding: 12, gap: 8 },
388
+ // ^ flexDirection: 'sideways' would not compile,
389
+ // and neither would a layout property inside ':hover'
390
+ });
391
+
392
+ <window title="hi" width={320} height={200} style={s.root}>
393
+ <box onClick={(ev: MouseEvent) => console.log(ev.detail)}>
394
+ <text style={{ fontSize: 18 }}>hello</text>
395
+ </box>
396
+ <Button primary onPress={() => {}}>
397
+ ok
398
+ </Button>
399
+ </window>;
400
+ ```
401
+
402
+ `jsxImportSource` is what makes `<div>` an error rather than something that
403
+ compiles and then throws at runtime: react-x11 owns the JSX namespace
404
+ instead of adding to React's, which it could not do anyway — `text`,
405
+ `image`, `canvas`, `html` and `svg` are all DOM element names too, with
406
+ incompatible props.
407
+
408
+ Style props, element props, event objects, ref types and every widget are
409
+ typed; `style` accepts the same nested arrays with falsy entries the runtime
410
+ does. See [docs/typescript.md](docs/typescript.md).
411
+
412
+ ## Developing
413
+
414
+ ```sh
415
+ npm test # hermetic: mock smoke tests + in-process X server pixels
416
+ npm run typecheck # tsc over the declarations and the type tests
417
+ npm run lint # ESLint
418
+ npm run format # Prettier
419
+ npm run screenshots # regenerate docs/img/*.png headlessly (no X server)
420
+ npm run docs:dev # the documentation site (npm install in website/ first)
421
+ ```
422
+
423
+ `docs/img/three.png` is the exception: the headless path has no GL, so the
424
+ 3D shot is captured by hand from `npm run examples:three` on a real server
425
+ with indirect GLX.
426
+
427
+ See [AGENTS.md](AGENTS.md) for architecture notes and contributor/agent
428
+ guidance, [docs/](docs/README.md) for API documentation, and
429
+ [NEXT_STEPS.md](NEXT_STEPS.md) for the roadmap.
430
+
431
+ The [documentation site](https://sidorares.github.io/react-x11/) lives in
432
+ [`website/`](website/) and renders `docs/` rather than repeating it — edit
433
+ `docs/` and the site follows. Its playground bundles this repo's `src/` for
434
+ the browser, so `npm run docs:test` fails when a demo stops matching the API.
435
+
436
+ ## Known issues
437
+
438
+ Two that are worth knowing before you hit them, both predating the current
439
+ release and both tracked:
440
+
441
+ - **`sans-serif` can resolve to a CJK font on macOS**
442
+ ([#86](https://github.com/sidorares/react-x11/issues/86)). Font families
443
+ are resolved by shelling out to `fc-match`, which follows `PATH`;
444
+ Homebrew's fontconfig ships no macOS system-font aliases and answers
445
+ Hiragino Sans. Latin looks fine, Cyrillic comes out on full-width
446
+ advances. Put `/opt/X11/bin` first on `PATH` until this is fixed.
447
+ - **AltGr levels do not work, and there is no input method.** `@` on a
448
+ German layout, `€`, `ł`, `ã` on US-International: the core keyboard map
449
+ cannot say whether the third and fourth keysyms on a key are a second
450
+ layout or a third and fourth level, so they are read as a layout and left
451
+ alone. Dead keys, Compose and layout switching all work
452
+ ([docs/events.md](docs/events.md)); what is absent is an ibus/fcitx/XIM
453
+ backend, so CJK is not partially working — it is structurally absent.
454
+
455
+ ## Security
456
+
457
+ X11 has no isolation between clients: any program on a display can read any
458
+ other program's window contents, grab the keyboard, and synthesize or record
459
+ input. That is the 1987 design, not a gap in this library, and it cuts both
460
+ ways — do not run untrusted programs on a display you use, and do not treat
461
+ a react-x11 window as a confidential surface. Your `$XAUTHORITY` cookie is a
462
+ bearer token; treat it like a password.
463
+
464
+ `ssh -X` runs your app as an untrusted client and restricts most of the
465
+ above; `ssh -Y` turns the restrictions off. **Prefer `-X` — react-x11 should
466
+ work under it, and if it does not, that is a bug worth filing.**
467
+
468
+ The full threat model, including what react-x11 does and does not defend
469
+ against, is [docs/security.md](docs/security.md). To report something,
470
+ [SECURITY.md](SECURITY.md).
471
+
472
+ ## See also
473
+
474
+ - [awesome-react-renderer](https://github.com/chentsulin/awesome-react-renderer)
475
+ — the catalogue of React renderers, which is how most people find things
476
+ like this.
477
+ - [ntk](https://github.com/sidorares/ntk) — the toolkit underneath: windows,
478
+ the XRender-backed 2d context, the text stack, the frame clock.
479
+ - [node-x11](https://github.com/sidorares/node-x11) — the X11 protocol in
480
+ JavaScript, including the in-process X server the tests and the playground
481
+ run against.