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.
- package/README.md +10 -10
- package/docs/README.md +160 -314
- package/docs/accessibility.md +176 -229
- package/docs/appearance.md +77 -176
- package/docs/click-to-component.md +18 -51
- package/docs/clipboard.md +40 -59
- package/docs/components.md +518 -1036
- package/docs/dbus.md +61 -133
- package/docs/debugging.md +152 -245
- package/docs/desktop-calendar.md +51 -88
- package/docs/desktop.md +222 -347
- package/docs/devtools.md +50 -167
- package/docs/drag-and-drop.md +126 -204
- package/docs/ecosystem/animation.md +91 -125
- package/docs/ecosystem/data-fetching.md +38 -53
- package/docs/ecosystem/dev-tooling.md +159 -202
- package/docs/ecosystem/forms.md +61 -97
- package/docs/ecosystem/headless.md +64 -89
- package/docs/ecosystem/i18n.md +18 -21
- package/docs/ecosystem/icons.md +93 -126
- package/docs/ecosystem/layout.md +42 -52
- package/docs/ecosystem/routing.md +18 -29
- package/docs/ecosystem/state.md +71 -88
- package/docs/ecosystem/testing.md +131 -157
- package/docs/ecosystem/theming.md +119 -165
- package/docs/ecosystem.md +145 -179
- package/docs/elements.md +1091 -1774
- package/docs/embedding.md +83 -132
- package/docs/events.md +412 -695
- package/docs/extending.md +784 -1326
- package/docs/eyedropper.md +63 -122
- package/docs/filedialog.md +71 -107
- package/docs/frame.md +195 -339
- package/docs/gl.md +65 -62
- package/docs/globalmenu.md +63 -146
- package/docs/macos.md +531 -3234
- package/docs/notifications.md +33 -47
- package/docs/packaging.md +166 -309
- package/docs/permissions.md +9 -14
- package/docs/react-features.md +81 -106
- package/docs/remote.md +32 -76
- package/docs/scale.md +72 -186
- package/docs/styling.md +329 -547
- package/docs/system.md +200 -280
- package/docs/testing.md +63 -83
- package/docs/typescript.md +6 -48
- package/docs/uri-schemes.md +67 -107
- package/docs/wayland-backend.md +200 -459
- package/docs/windows-integrations.md +75 -131
- package/docs/windows.md +343 -1367
- package/package.json +3 -2
- package/src/Reconciler.js +2 -2
- package/src/a11y.js +1 -1
- package/src/backend/context2d.js +3 -3
- package/src/bootstrap.js +1 -1
- package/src/cocoa/app.js +6 -6
- package/src/cocoa/bezels.js +1 -1
- package/src/cocoa/fonts.js +1 -1
- package/src/cocoa/globalmenu.js +1 -1
- package/src/cocoa/keymap.js +1 -1
- package/src/cocoa/panewindow.js +1 -1
- package/src/cocoa/player.js +1 -1
- package/src/cocoa/presenter.js +2 -2
- package/src/cocoa/promotion.js +1 -1
- package/src/cocoa/relaunch.js +1 -1
- package/src/cocoa/threaded.js +1 -1
- package/src/cocoa/video.js +1 -1
- package/src/cocoa/window.js +4 -4
- package/src/components/Menu.js +7 -2
- package/src/components/Switch.js +1 -1
- package/src/components/native.js +1 -1
- package/src/dnd.js +2 -2
- package/src/glnodes.js +1 -1
- package/src/grid.js +1 -1
- package/src/layouts.js +1 -1
- package/src/nodes/animation.js +1 -1
- package/src/nodes/invalidate.js +1 -1
- package/src/nodes/layouthost.js +1 -1
- package/src/nodes/video.js +19 -8
- package/src/nodes/window/droptarget.js +1 -1
- package/src/pacing.js +1 -1
- package/src/refresh/index.js +2 -1
- package/src/types/video.d.ts +5 -2
- package/src/videobackend.js +28 -0
- package/src/videoframes.js +29 -16
- package/src/wayland/app.js +27 -0
- package/src/wayland/video.js +376 -0
- package/src/win32/a11y.js +1 -1
- package/src/win32/app.js +2 -2
- package/src/win32/fonts.js +1 -1
- package/src/win32/glarea.js +4 -4
- package/src/win32/overlay.js +1 -1
- package/src/win32/panewindow.js +3 -3
- package/src/win32/shell.js +1 -1
- package/src/win32/window.js +2 -2
- package/src/windowid.js +1 -1
- package/src/xvideo.js +189 -0
- package/docs/architecture/accessibility.md +0 -470
- package/docs/architecture/animation.md +0 -707
- package/docs/architecture/container-queries.md +0 -525
- package/docs/architecture/custom-layout.md +0 -334
- package/docs/architecture/drag-and-drop.md +0 -941
- package/docs/architecture/element-layer-contents.md +0 -622
- package/docs/architecture/frame-pacing.md +0 -255
- package/docs/architecture/grid-layout.md +0 -358
- package/docs/architecture/protocol-efficiency.md +0 -357
- package/docs/architecture/video.md +0 -1149
- package/docs/architecture/windowed-regions.md +0 -854
- package/docs/glx.md +0 -137
- package/docs/wayland.md +0 -545
- package/docs/windows-embedding.md +0 -271
- package/docs/windows-gl.md +0 -221
package/docs/appearance.md
CHANGED
|
@@ -46,61 +46,36 @@ const { accent } = useSystemAppearance();
|
|
|
46
46
|
<box style={{ borderColor: accent ?? '#2980b9' }} />;
|
|
47
47
|
```
|
|
48
48
|
|
|
49
|
-
`accentText` is the ink the desktop puts on that fill
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
with `selectedContentBackgroundColor`, a darker cut of it — same hue,
|
|
59
|
-
lightness 0.54 → 0.40 in dark and 0.45 in light for the orange, hand-tuned per
|
|
60
|
-
accent rather than computed from it. The built-in palette uses it as
|
|
61
|
-
`hoverBackground`; a menu lit in the raw accent beside a native one reads as
|
|
62
|
-
too bright.
|
|
49
|
+
`accentText` is the ink the desktop puts on that fill. Only macOS names one —
|
|
50
|
+
white, on every accent it offers, as its native controls draw it. Elsewhere
|
|
51
|
+
it is null, and the palette picks the legible ink by contrast, as it does for
|
|
52
|
+
any theme that names a fill and stops there.
|
|
53
|
+
|
|
54
|
+
`selection` is the fill under a selected menu or list row, which macOS draws
|
|
55
|
+
not in the accent but in a darker cut of it (`selectedContentBackgroundColor`).
|
|
56
|
+
The built-in palette uses it as `hoverBackground`; a menu lit in the raw
|
|
57
|
+
accent beside a native one reads as too bright.
|
|
63
58
|
|
|
64
59
|
## The desktop's palette
|
|
65
60
|
|
|
66
|
-
On macOS the rung reads the
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
| `surfaceHover` | the second `alternatingContentBackgroundColors` | composited over `surface` |
|
|
85
|
-
| `text`, `textMuted` | `labelColor`, `secondaryLabelColor` | black or white at 85% and 50–55%, over the ground |
|
|
86
|
-
| `border` | `separatorColor` | 10% ink over the ground |
|
|
87
|
-
| `focusRing`, `borderFocus` | `keyboardFocusIndicatorColor` | the 50% ring over the ground |
|
|
88
|
-
| `track` | `unemphasizedSelectedContentBackgroundColor` | as is |
|
|
89
|
-
| `accent`, `accentText` | `controlAccentColor`, `alternateSelectedControlTextColor` | as is; the ink is white for all eight |
|
|
90
|
-
| `accentHover`, `accentActive` | the accent with the pressed and deep-pressed effects | as is — darker in light, lighter in dark |
|
|
91
|
-
| `hoverBackground`, `hoverText` | `selectedContentBackgroundColor`, the same ink | as is |
|
|
92
|
-
| `selection` | `selectedTextBackgroundColor` | the Highlight colour; dark is each channel less 116/255 |
|
|
93
|
-
| `caret` | `textInsertionPointColor` | the accent of the Highlight colour's name |
|
|
94
|
-
| `link` | `linkColor` | as is, never the accent |
|
|
95
|
-
| `danger`, `dangerHover` | `systemRedColor`, pressed | as is |
|
|
96
|
-
| `success`, `warning`, `info` | `systemGreenColor`, `systemOrangeColor`, `systemBlueColor` | orange for warning: yellow fails as letters |
|
|
97
|
-
|
|
98
|
-
AppKit's inks are translucent and every token here is a colour, so each is
|
|
99
|
-
flattened over the window ground on the way in. The status inks and the
|
|
100
|
-
pressed steps a table does not name come from `resolveTheme`, as for any
|
|
101
|
-
theme. What this cannot see is desktop tinting: a dark window on macOS is
|
|
102
|
-
`#323232` with a little of the wallpaper in it, and the wallpaper is not in
|
|
103
|
-
any colour AppKit will hand out.
|
|
61
|
+
On macOS the rung also reads the semantic colours AppKit paints its own
|
|
62
|
+
windows and controls with, in the appearance the desktop is in. They arrive
|
|
63
|
+
as `palette`, already in react-x11's vocabulary, and the built-in palette
|
|
64
|
+
merges them over the scheme's own the way a `<ThemeProvider>` value merges —
|
|
65
|
+
so an app that says nothing about colour comes up in the desktop's greys,
|
|
66
|
+
inks and status colours, and follows every retune macOS ships.
|
|
67
|
+
|
|
68
|
+
The tokens it sets: `background`, `surface`, `surfaceHover`, `text`,
|
|
69
|
+
`textMuted`, `border`, `focusRing`, `borderFocus`, `track`, `accent`,
|
|
70
|
+
`accentText`, `accentHover`, `accentActive`, `hoverBackground`, `hoverText`,
|
|
71
|
+
`selection`, `caret`, `link`, `danger`, `dangerHover`, `success`, `warning`
|
|
72
|
+
and `info`. The accent family follows the user's **Accent colour**;
|
|
73
|
+
`selection` and `caret` follow the **Highlight colour**, a separate setting;
|
|
74
|
+
`link` is never the accent, and `warning` is orange, since yellow fails as
|
|
75
|
+
letters. Every token is an opaque colour, flattened over the window ground,
|
|
76
|
+
and the status inks and pressed steps it does not name come from
|
|
77
|
+
`resolveTheme`, as for any theme. Desktop tinting — the little of the wallpaper in a dark window — is
|
|
78
|
+
not in any colour AppKit hands out, so it is not reproduced.
|
|
104
79
|
|
|
105
80
|
The portal names none of this, so on Linux the built-in palette stands and
|
|
106
81
|
only the accent is taken.
|
|
@@ -113,11 +88,10 @@ visible flash.
|
|
|
113
88
|
|
|
114
89
|
So **the answer from last time is on disk**, in
|
|
115
90
|
`$XDG_CACHE_HOME/react-x11/appearance.json`, and it is read synchronously
|
|
116
|
-
before the first render — 0.1 ms, no
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
nearly always the fresh one.
|
|
91
|
+
before the first render — 0.1 ms, no await. These values change a few times
|
|
92
|
+
in a machine's life, so the remembered answer is almost always the right
|
|
93
|
+
one; the ladder revalidates it in the background and replaces it if it
|
|
94
|
+
moved.
|
|
121
95
|
|
|
122
96
|
`source` is how you tell them apart:
|
|
123
97
|
|
|
@@ -132,8 +106,7 @@ run 2 {colorScheme: 'dark', accent: '#ed5b00', source: 'cache'} ←
|
|
|
132
106
|
```
|
|
133
107
|
|
|
134
108
|
Revalidation costs one re-render even when nothing moved, because `source`
|
|
135
|
-
itself changed.
|
|
136
|
-
desktop had actually answered would be worth less than the render it saved.
|
|
109
|
+
itself changed.
|
|
137
110
|
|
|
138
111
|
That leaves the very first launch on a machine, where there is nothing to
|
|
139
112
|
remember. An app that must be exact rather than probably right waits:
|
|
@@ -146,12 +119,9 @@ root.render(<App />); // useSystemAppearance() is already correct
|
|
|
146
119
|
```
|
|
147
120
|
|
|
148
121
|
`systemAppearance()` is the imperative twin of the hook, and never rejects.
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
probe 124 ms. `dbus-native`'s import is CPU-bound, so it does not hide behind
|
|
153
|
-
ntk's startup — and an app that never asks what colour the desktop is should
|
|
154
|
-
not pay 40 ms to find out.
|
|
122
|
+
It is not part of `createRoot()` because the probe adds about 40 ms to a cold
|
|
123
|
+
start, which an app that never asks what colour the desktop is should not
|
|
124
|
+
pay.
|
|
155
125
|
|
|
156
126
|
`REACT_X11_NO_APPEARANCE_CACHE=1` turns the file off for a process that must
|
|
157
127
|
not touch the disk.
|
|
@@ -219,14 +189,12 @@ is dark, for a design whose two schemes are not one recolour of the other:
|
|
|
219
189
|
<ThemeProvider value={{ background: '#fffdf7' }} dark={{ background: '#141210' }}>
|
|
220
190
|
```
|
|
221
191
|
|
|
222
|
-
**The accent is followed on the same terms as the scheme
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
own colour by naming it; a following provider's `value` wins over the desktop
|
|
229
|
-
token for token, so name the family rather than the one colour:
|
|
192
|
+
**The accent is followed on the same terms as the scheme**: on a desktop
|
|
193
|
+
that has an accent, every control beside the app is already that colour — on
|
|
194
|
+
the Cocoa backend the app's own native checkboxes and default buttons are
|
|
195
|
+
drawn by AppKit in it. A following provider's `value` wins over the desktop
|
|
196
|
+
token for token, so a brand keeps its own colour by naming the family rather
|
|
197
|
+
than the one colour:
|
|
230
198
|
|
|
231
199
|
```jsx
|
|
232
200
|
<ThemeProvider value={{ accent: '#e17055', accentHover: '#c0563a', hoverBackground: '#e17055' }}>
|
|
@@ -239,23 +207,10 @@ they say in any accent.
|
|
|
239
207
|
|
|
240
208
|
### Resizing
|
|
241
209
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
on every drag of the corner
|
|
246
|
-
|
|
247
|
-
There are two copies of that colour, because a window here is double-buffered:
|
|
248
|
-
the X **attribute** the server paints exposed area with, and the colour ntk
|
|
249
|
-
clears the part of its backing store that a grow adds. react-x11 sets both —
|
|
250
|
-
to the window's own `backgroundColor`, or the palette's — and keeps them in
|
|
251
|
-
step when either moves. Nothing to do; it is worth knowing only because they
|
|
252
|
-
are the pieces of the palette that live outside this renderer.
|
|
253
|
-
|
|
254
|
-
Needs **ntk >= 6.6.1**, where `setBackgroundPixel` sets the pair together.
|
|
255
|
-
Before that the backing store cleared to the screen's white whatever the
|
|
256
|
-
window said, so enlarging a dark window flashed a white strip — and it
|
|
257
|
-
survived, because a grow inside the pixmap's headroom reallocates nothing and
|
|
258
|
-
so nothing damages it.
|
|
210
|
+
Dragging a window larger exposes area before the app can have drawn it.
|
|
211
|
+
react-x11 fills it with the window's own `backgroundColor`, or the
|
|
212
|
+
palette's, and keeps that in step when either moves, so a dark window does
|
|
213
|
+
not flash a bright rectangle on every drag of the corner. Nothing to do.
|
|
259
214
|
|
|
260
215
|
### Testing
|
|
261
216
|
|
|
@@ -302,49 +257,32 @@ new to learn. See [styling](styling.md) for what a transition covers.
|
|
|
302
257
|
|
|
303
258
|
## The ladder
|
|
304
259
|
|
|
305
|
-
There is no cross-toolkit palette protocol
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
correction. Live over `PropertyNotify`.
|
|
332
|
-
|
|
333
|
-
`RESOURCE_MANAGER` is not a rung. It is where `Xft.dpi`, `Xft.rgba` and
|
|
334
|
-
`Xcursor.*` live — font and cursor rendering — and there has never been an X
|
|
335
|
-
resource for colour scheme, accent or contrast.
|
|
336
|
-
|
|
337
|
-
**The first rung that answers owns all four values.** The rungs disagree:
|
|
338
|
-
measured on one GNOME session, at one moment, the portal reported
|
|
339
|
-
`reduced-motion: 0` while GNOME's own `enable-animations` was `false`. Taking
|
|
340
|
-
the best-answered field from each would describe a desktop that does not
|
|
341
|
-
exist.
|
|
342
|
-
|
|
343
|
-
Nothing here holds the process open. The `SettingChanged` subscription leaves
|
|
344
|
-
the match rule on the shared connection and releases its reference, so the
|
|
345
|
-
socket goes back to `unref()`d: an app whose windows have closed still exits,
|
|
346
|
-
and an app with a window on screen is awake anyway and gets the signal. The
|
|
347
|
-
macOS child is spawned `unref()`d and killed on exit.
|
|
260
|
+
There is no cross-toolkit palette protocol: what a desktop exposes is a
|
|
261
|
+
shared theme _name_ and four standardised appearance _values_. Those four
|
|
262
|
+
are read from the best source the machine has:
|
|
263
|
+
|
|
264
|
+
1. **`org.freedesktop.portal.Settings`** — the real contract on Linux, and
|
|
265
|
+
the only Linux source with an accent colour; live over `SettingChanged`.
|
|
266
|
+
libadwaita, Qt 6.5+, Firefox and Electron all read this one.
|
|
267
|
+
2. **macOS** — only where the _process_ is running on a Mac, on either
|
|
268
|
+
backend; before XSETTINGS, because on a Mac the Mac's own preference is
|
|
269
|
+
the one the user set. A _Linux_ process drawing to an XQuartz display
|
|
270
|
+
never reaches this rung: it cannot read that Mac's settings.
|
|
271
|
+
3. **Windows** — answered by the win32 backend itself: light or dark, the
|
|
272
|
+
accent, contrast and reduced motion, live. Before XSETTINGS for the same
|
|
273
|
+
reason as macOS, and only a tree on the win32 backend reaches it.
|
|
274
|
+
4. **XSETTINGS** — X11 only. "Is this dark" comes down to a `-dark` suffix
|
|
275
|
+
on `Net/ThemeName`, so it is a fallback and never a correction. Live.
|
|
276
|
+
|
|
277
|
+
`RESOURCE_MANAGER` is not a rung: there has never been an X resource for
|
|
278
|
+
colour scheme, accent or contrast.
|
|
279
|
+
|
|
280
|
+
**The first rung that answers owns all four values.** The rungs can
|
|
281
|
+
disagree, and taking the best-answered field from each would describe a
|
|
282
|
+
desktop that does not exist.
|
|
283
|
+
|
|
284
|
+
Nothing here holds the process open: an app whose windows have closed still
|
|
285
|
+
exits.
|
|
348
286
|
|
|
349
287
|
### Not climbing it at all
|
|
350
288
|
|
|
@@ -371,46 +309,9 @@ off along with the other two integrations that talk to the session bus.
|
|
|
371
309
|
| `contrast` | yes | yes | from the theme name |
|
|
372
310
|
| `reducedMotion` | version 2 of the interface | yes | rarely — see below |
|
|
373
311
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
**1 is dark and 2 is light** in the portal's `color-scheme`. The ordering
|
|
382
|
-
reads backwards, and getting it wrong inverts the appearance of every desktop
|
|
383
|
-
that expressed a preference — which is precisely the set of machines that
|
|
384
|
-
care.
|
|
385
|
-
|
|
386
|
-
**The accent colour is a `(ddd)` of sRGB floats in [0, 1]**, and "unset" is
|
|
387
|
-
spelled as values outside that range. It is converted to a `'#rrggbb'` string
|
|
388
|
-
here, because every style in this renderer takes a CSS colour and
|
|
389
|
-
`rgb(0.93, 0.36, 0)` is black.
|
|
390
|
-
|
|
391
|
-
**The subscription goes on before the read.** A change landing between the
|
|
392
|
-
read and the match rule is lost and nothing corrects it — the app stays stale
|
|
393
|
-
for its whole lifetime. It is the same shape as the portal `Request` race in
|
|
394
|
-
[the file dialog](filedialog.md).
|
|
395
|
-
|
|
396
|
-
**On macOS the frameworks are read, not `defaults`.** Three of the four
|
|
397
|
-
plausible `defaults` keys — `AppleHighlightColor`, `AppleAccentColor`,
|
|
398
|
-
`com.apple.universalaccess increaseContrast` — do not exist until the user
|
|
399
|
-
changes that setting, so "key not found" is the normal answer rather than the
|
|
400
|
-
error case, and `AppleAccentColor` is an index into a table that has to be
|
|
401
|
-
maintained by hand. `NSColor.controlAccentColor` is the colour itself, with
|
|
402
|
-
Multicolor already resolved, `alternateSelectedControlTextColor` is the ink
|
|
403
|
-
AppKit writes on it, `selectedContentBackgroundColor` is the shade under a
|
|
404
|
-
selected row (both resolved in the appearance the desktop is in, since a
|
|
405
|
-
dynamic colour in a bare `osascript` resolves as Aqua otherwise), and
|
|
406
|
-
`NSWorkspace` answers the two accessibility flags directly.
|
|
407
|
-
|
|
408
|
-
**A process reads the colours once.** `controlAccentColor` is resolved on
|
|
409
|
-
first use and cached for the life of the process: after the user picks another
|
|
410
|
-
accent, the same process still answers the old one, with or without an
|
|
411
|
-
`NSApplication`. So the watcher prints its values once and, on the change
|
|
412
|
-
notifications, exits — and react-x11 spawns another, whose first read is
|
|
413
|
-
fresh. A watcher that re-read in place re-announced the same values, and a
|
|
414
|
-
running app never saw the change. The Cocoa backend
|
|
415
|
-
reads the same child: it is one source for both backends, and the accent it
|
|
416
|
-
reports is the one AppKit draws the native bezels in.
|
|
312
|
+
On GNOME the XSETTINGS rung reports no reduced motion:
|
|
313
|
+
`Gtk/EnableAnimations` is in GTK's key list, but gnome-settings-daemon does
|
|
314
|
+
not export it.
|
|
315
|
+
|
|
316
|
+
Design notes — how each source is read, and the macOS colour mapping:
|
|
317
|
+
[design/appearance.md](../design/appearance.md).
|
|
@@ -54,60 +54,27 @@ when DevTools is absent, which is the usual case.
|
|
|
54
54
|
`REACT_X11_EDITOR` picks the editor (`cursor` by default): `code`,
|
|
55
55
|
`code-insiders`, `windsurf`, `vim`, `nvim`, or any other value, treated as a
|
|
56
56
|
custom URI scheme. `react-x11 dev`'s error window opens a line the same way,
|
|
57
|
-
through the same variable
|
|
57
|
+
through the same variable.
|
|
58
58
|
|
|
59
59
|
## How it works
|
|
60
60
|
|
|
61
|
-
|
|
62
|
-
`REACT_X11_CLICK_TO_COMPONENT`
|
|
63
|
-
`
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
is just parsing stack-trace text and skipping the frames React and this
|
|
79
|
-
renderer add on top of the call site (`react`, `react-reconciler`,
|
|
80
|
-
`scheduler`, `react-x11`'s own `src/`) — no source-map library required
|
|
81
|
-
(this is the one place
|
|
82
|
-
the design diverges from a browser DOM version of the same idea: a
|
|
83
|
-
browser doesn't remap `Error.stack` for you, so a tool like
|
|
84
|
-
[show-component](https://github.com/sidorares/show-component) has to
|
|
85
|
-
resolve source maps itself at runtime).
|
|
86
|
-
|
|
87
|
-
Frames from _other_ packages are deliberately kept rather than skipped: a
|
|
88
|
-
call site inside an installed component is a real location, it just isn't
|
|
89
|
-
one in your source, and telling those apart is what lets the resolver climb
|
|
90
|
-
the `_debugOwner` chain to the line that is. Skipping every `node_modules`
|
|
91
|
-
frame instead walks past the library component entirely and lands on
|
|
92
|
-
whichever frame of yours happens to be deeper in the render stack — usually
|
|
93
|
-
the `render()` call at startup, which looks like an answer and isn't.
|
|
94
|
-
|
|
95
|
-
Alt+Click is recognized in `EventManager._onMouseDown`
|
|
96
|
-
(`src/events.js`) — `native.buttons & MOD.Alt` is X11's `Mod1Mask` bit, the
|
|
97
|
-
same `buttons` bitmask every synthetic event's `altKey` reads — ahead of the
|
|
98
|
-
normal press handling, so it never also starts a drag or moves focus.
|
|
99
|
-
|
|
100
|
-
GUI editors (cursor/code/code-insiders/windsurf, or any unrecognized
|
|
101
|
-
`REACT_X11_EDITOR` value treated as a raw scheme) are opened by navigating
|
|
102
|
-
their registered URI scheme — `open cursor://file/abs/path:line:col` on
|
|
103
|
-
macOS, `xdg-open` elsewhere — the same idea show-component uses in a
|
|
104
|
-
browser via `window.location.href`, just handed to the OS instead. This is
|
|
105
|
-
noticeably faster than spawning the editor's CLI shim directly: a CLI like
|
|
106
|
-
`cursor -g ...` is a wrapper script that re-detects and re-execs the real
|
|
107
|
-
app on every call, while the URI scheme is delivered straight to the
|
|
108
|
-
already-running instance by Launch Services / xdg-open. vim and nvim have
|
|
109
|
-
no URI scheme to navigate, so those are still spawned directly with a
|
|
110
|
-
`+call cursor(line, column)` argument.
|
|
61
|
+
It is installed when `REACT_X11_CLICK_TO_COMPONENT` or `REACT_X11_EDITOR` is
|
|
62
|
+
set, unless `REACT_X11_CLICK_TO_COMPONENT=0` — the way to turn it off under
|
|
63
|
+
`react-x11 dev`, which sets it to `1` when it is unset. No babel/jsx-source
|
|
64
|
+
plugin and no build step: React 19's development-mode call-site stacks and
|
|
65
|
+
owner chain are all it reads.
|
|
66
|
+
|
|
67
|
+
- **Locations are as good as the stack.** It reads the source location off
|
|
68
|
+
`Error.stack`, so run with Node's `--enable-source-maps` (on in the `tsx`
|
|
69
|
+
examples) or locations point at compiled output.
|
|
70
|
+
- **Alt+Click is taken before normal press handling**, so it never also
|
|
71
|
+
starts a drag or moves focus.
|
|
72
|
+
- **GUI editors are opened through their URI scheme** —
|
|
73
|
+
`open cursor://file/abs/path:line:col` on macOS, `xdg-open` elsewhere — which
|
|
74
|
+
reaches the already-running instance; vim and nvim are spawned with a
|
|
75
|
+
`+call cursor(line, column)` argument.
|
|
76
|
+
|
|
77
|
+
Design notes: [design/click-to-component.md](../design/click-to-component.md#how-it-works).
|
|
111
78
|
|
|
112
79
|
## Caveats
|
|
113
80
|
|
package/docs/clipboard.md
CHANGED
|
@@ -20,20 +20,17 @@ middle-click paste, select-to-own. This page is for everything else: a
|
|
|
20
20
|
canvas that copies an image, a list that pastes files, a document view with
|
|
21
21
|
its own Copy item.
|
|
22
22
|
|
|
23
|
-
> **On the Cocoa backend** the clipboard is `NSPasteboard.general
|
|
24
|
-
>
|
|
25
|
-
>
|
|
26
|
-
> _pasteboard the system holds_. Everything below works there — writing,
|
|
23
|
+
> **On the Cocoa backend** the clipboard is `NSPasteboard.general`: a
|
|
24
|
+
> pasteboard the system holds, rather than X's _ownership of a selection_
|
|
25
|
+
> negotiated per paste. Everything below works there — writing,
|
|
27
26
|
> [reading](#reading) any type the pasteboard carries, `targets()` and
|
|
28
27
|
> [`watch()`](#knowing-when-it-changes) — with three differences, each
|
|
29
|
-
> described where it applies: [`PRIMARY`](#primary-is-x11-only) is X11-only
|
|
30
|
-
> rather than missing, since nothing on macOS is filled by selecting text;
|
|
28
|
+
> described where it applies: [`PRIMARY`](#primary-is-x11-only) is X11-only;
|
|
31
29
|
> `targets()` names the pasteboard's types rather than an owner's atoms; and
|
|
32
|
-
> `watch()`
|
|
33
|
-
>
|
|
34
|
-
>
|
|
35
|
-
>
|
|
36
|
-
> dropped.
|
|
30
|
+
> `watch()` polls the pasteboard's change count. A
|
|
31
|
+
> [multi-flavour payload](#writing) is written whole when the bridge has
|
|
32
|
+
> `pasteboardWrite`; an older bridge keeps the text and says once, in
|
|
33
|
+
> development, what it dropped.
|
|
37
34
|
|
|
38
35
|
## Two clipboards
|
|
39
36
|
|
|
@@ -53,9 +50,8 @@ which is what the built-in text controls do.
|
|
|
53
50
|
Two consequences worth knowing, because neither matches a browser:
|
|
54
51
|
|
|
55
52
|
- **The data lives in this process.** Nothing is stored by the server, so
|
|
56
|
-
when the app exits the clipboard goes with it
|
|
57
|
-
|
|
58
|
-
yet.
|
|
53
|
+
when the app exits the clipboard goes with it (normal X behaviour; there is
|
|
54
|
+
no clipboard-manager handoff).
|
|
59
55
|
- **Someone else copying takes it away.** Ownership is exclusive; there is
|
|
60
56
|
no "clipboard history" to fall back on.
|
|
61
57
|
|
|
@@ -71,22 +67,15 @@ your own — is one nobody can paste from:
|
|
|
71
67
|
X's answers for a selection nobody owns;
|
|
72
68
|
- `clear()` leaves the pasteboard alone, and `watch()` never fires.
|
|
73
69
|
|
|
74
|
-
The **win32** backend answers every selection but `CLIPBOARD` the same way
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
`PRIMARY` on X11, and on macOS none of them replaces what the user copied
|
|
84
|
-
somewhere else. Code written to the X11 convention needs no platform check.
|
|
85
|
-
|
|
86
|
-
`PRIMARY` is deliberately not kept inside the app instead. Its point is the
|
|
87
|
-
paste in _another_ application; all an in-process copy could keep is a
|
|
88
|
-
middle click that types into a text field, which no Mac text field does. An
|
|
89
|
-
app that wants select-to-copy on macOS anyway — a terminal emulator, say —
|
|
70
|
+
The **win32** backend answers every selection but `CLIPBOARD` the same way
|
|
71
|
+
(and writes only text there so far). The **Wayland** backend has `PRIMARY`
|
|
72
|
+
too, over the primary-selection protocol where the compositor offers it — so
|
|
73
|
+
"X11-only" here means "not on macOS or Windows".
|
|
74
|
+
|
|
75
|
+
So selecting is never copying there: a drag across a `selectable` surface, a
|
|
76
|
+
select-all or shift+arrows takes `PRIMARY` on X11 and leaves the macOS
|
|
77
|
+
pasteboard alone. Code written to the X11 convention needs no platform check.
|
|
78
|
+
An app that wants select-to-copy on macOS anyway — a terminal emulator, say —
|
|
90
79
|
can write `CLIPBOARD` itself: a surface's `selectedText()` is the text, and
|
|
91
80
|
`onSelectionChange` says when it moved.
|
|
92
81
|
|
|
@@ -129,9 +118,8 @@ text-ish types to a string. It answers `null` when the owner has nothing of
|
|
|
129
118
|
that kind, because "is there an image on the clipboard?" is a question, not
|
|
130
119
|
an error.
|
|
131
120
|
|
|
132
|
-
The
|
|
133
|
-
|
|
134
|
-
of the desktop:
|
|
121
|
+
The same payload arrives under different names depending on who copied it,
|
|
122
|
+
so a group matches all of them:
|
|
135
123
|
|
|
136
124
|
| group | matches |
|
|
137
125
|
| --------- | ---------------------------------------------------------------------------------------------------------- |
|
|
@@ -139,28 +127,21 @@ of the desktop:
|
|
|
139
127
|
| `'files'` | `text/uri-list`, `application/x-kde4-urilist`, `x-special/gnome-copied-files` |
|
|
140
128
|
| `'uris'` | `text/uri-list`, `text/x-moz-url`, `_NETSCAPE_URL` |
|
|
141
129
|
|
|
142
|
-
This is the same table [drag and drop](drag-and-drop.md) uses
|
|
143
|
-
|
|
144
|
-
it offers on a copy. Groups do not overlap — a Firefox link is
|
|
130
|
+
This is the same table [drag and drop](drag-and-drop.md) uses. Groups do not
|
|
131
|
+
overlap — a Firefox link is
|
|
145
132
|
`text/x-moz-url`, which is in `uris`, not `text`.
|
|
146
133
|
|
|
147
134
|
On the Cocoa backend `targets()` lists the pasteboard's types in these
|
|
148
135
|
names: a type with a MIME type under that (`public.png` is `image/png`,
|
|
149
136
|
Finder's `public.file-url` is `text/uri-list`), text under both its X and
|
|
150
|
-
MIME names, and any other type under its own identifier.
|
|
151
|
-
|
|
152
|
-
[`org.nspasteboard.ConcealedType`](http://nspasteboard.org)
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
`
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
Text is read through the bridge's own text verb. Anything else is read from
|
|
159
|
-
the pasteboard's items: through `pasteboardItems`/`pasteboardItemData` on a
|
|
160
|
-
bridge that has them, and otherwise through `osascript -l JavaScript`, one
|
|
161
|
-
short-lived child of about 60 ms per `targets()` or `read()`. The type list
|
|
162
|
-
is kept against the pasteboard's change count, so asking again with nothing
|
|
163
|
-
copied in between costs nothing.
|
|
137
|
+
MIME names, and any other type under its own identifier. So an app can honour
|
|
138
|
+
a password manager: a clipboard history should skip any copy whose
|
|
139
|
+
`targets()` include [`org.nspasteboard.ConcealedType`](http://nspasteboard.org)
|
|
140
|
+
or `org.nspasteboard.TransientType`. Files copied in Finder are one item each,
|
|
141
|
+
and `read('files')` reads them as one uri-list. Reading anything but text
|
|
142
|
+
costs an `osascript` child (about 60 ms per `targets()` or `read()`) on a
|
|
143
|
+
bridge without `pasteboardItems`; asking again with nothing copied in between
|
|
144
|
+
costs nothing.
|
|
164
145
|
|
|
165
146
|
`readFiles()` is `read('files')` with the RFC 2483 parsing applied: CRLF
|
|
166
147
|
separators, `#` comment lines, percent-decoding. `path` is present only for
|
|
@@ -195,15 +176,13 @@ something changes. `owner === 0` means nothing is on the clipboard, which is
|
|
|
195
176
|
the case a Paste menu item wants. It rejects on a server without XFixes;
|
|
196
177
|
every X server since about 2004 has it.
|
|
197
178
|
|
|
198
|
-
On the Cocoa backend nothing announces a copy, so `watch()`
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
inside one poll are one change, and the app's own copies are changes too, as
|
|
206
|
-
they are on X11. It rejects on a bridge without `pasteboardChangeCount`.
|
|
179
|
+
On the Cocoa backend nothing announces a copy, so `watch()` polls the
|
|
180
|
+
pasteboard's change count every 250 ms while anything is watching (cheap; it
|
|
181
|
+
stops with the last unsubscribe). No window owns the pasteboard, so `owner` is
|
|
182
|
+
`-1` when it holds anything; `ev.changeCount` is the pasteboard's own count.
|
|
183
|
+
Two copies inside one poll are one change, and the app's own copies are
|
|
184
|
+
changes too, as they are on X11. It rejects on a bridge without
|
|
185
|
+
`pasteboardChangeCount`.
|
|
207
186
|
|
|
208
187
|
## Timestamps
|
|
209
188
|
|
|
@@ -279,3 +258,5 @@ raw protocol. See ntk's own clipboard documentation for the ICCCM details.
|
|
|
279
258
|
a bare `writeText()` offers both.
|
|
280
259
|
- **No image decoding.** `read('image/png')` hands back the bytes; turning
|
|
281
260
|
them into something drawable is yours.
|
|
261
|
+
|
|
262
|
+
Design notes: [design/clipboard.md](../design/clipboard.md).
|