react-x11 2.51.1 → 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 (121) hide show
  1. package/README.md +94 -61
  2. package/docs/README.md +256 -0
  3. package/docs/accessibility.md +575 -0
  4. package/docs/appearance.md +317 -0
  5. package/docs/click-to-component.md +92 -0
  6. package/docs/clipboard.md +262 -0
  7. package/docs/components.md +1231 -0
  8. package/docs/dbus.md +205 -0
  9. package/docs/debugging.md +371 -0
  10. package/docs/desktop-calendar.md +247 -0
  11. package/docs/desktop.md +523 -0
  12. package/docs/devtools.md +133 -0
  13. package/docs/drag-and-drop.md +585 -0
  14. package/docs/ecosystem/animation.md +360 -0
  15. package/docs/ecosystem/data-fetching.md +186 -0
  16. package/docs/ecosystem/dev-tooling.md +393 -0
  17. package/docs/ecosystem/forms.md +277 -0
  18. package/docs/ecosystem/headless.md +194 -0
  19. package/docs/ecosystem/i18n.md +86 -0
  20. package/docs/ecosystem/icons.md +377 -0
  21. package/docs/ecosystem/layout.md +206 -0
  22. package/docs/ecosystem/routing.md +135 -0
  23. package/docs/ecosystem/state.md +344 -0
  24. package/docs/ecosystem/testing.md +474 -0
  25. package/docs/ecosystem/theming.md +494 -0
  26. package/docs/ecosystem.md +439 -0
  27. package/docs/elements.md +2079 -0
  28. package/docs/embedding.md +239 -0
  29. package/docs/events.md +742 -0
  30. package/docs/extending.md +1700 -0
  31. package/docs/eyedropper.md +164 -0
  32. package/docs/filedialog.md +222 -0
  33. package/docs/frame.md +436 -0
  34. package/docs/gl.md +173 -0
  35. package/docs/globalmenu.md +195 -0
  36. package/docs/macos.md +592 -0
  37. package/docs/notifications.md +150 -0
  38. package/docs/packaging.md +468 -0
  39. package/docs/permissions.md +143 -0
  40. package/docs/react-features.md +405 -0
  41. package/docs/remote.md +179 -0
  42. package/docs/scale.md +225 -0
  43. package/docs/security.md +132 -0
  44. package/docs/styling.md +1710 -0
  45. package/docs/system.md +519 -0
  46. package/docs/testing.md +494 -0
  47. package/docs/typescript.md +75 -0
  48. package/docs/uri-schemes.md +340 -0
  49. package/docs/wayland-backend.md +332 -0
  50. package/docs/windows-integrations.md +177 -0
  51. package/docs/windows.md +380 -0
  52. package/package.json +11 -3
  53. package/src/Reconciler.js +2 -2
  54. package/src/a11y.js +1 -1
  55. package/src/backend/context2d.js +3 -3
  56. package/src/bootstrap.js +1 -1
  57. package/src/bus.js +39 -26
  58. package/src/cli/create.d.ts +43 -0
  59. package/src/cli/create.js +305 -0
  60. package/src/cli/env.js +484 -0
  61. package/src/cli/index.js +29 -2
  62. package/src/cli/templates/AGENTS.template.md +195 -0
  63. package/src/cli/templates/DESIGN.template.md +269 -0
  64. package/src/cocoa/app.js +6 -6
  65. package/src/cocoa/bezels.js +1 -1
  66. package/src/cocoa/fonts.js +1 -1
  67. package/src/cocoa/globalmenu.js +1 -1
  68. package/src/cocoa/keymap.js +1 -1
  69. package/src/cocoa/panewindow.js +1 -1
  70. package/src/cocoa/player.js +1 -1
  71. package/src/cocoa/presenter.js +2 -2
  72. package/src/cocoa/promotion.js +1 -1
  73. package/src/cocoa/relaunch.js +1 -1
  74. package/src/cocoa/threaded.js +1 -1
  75. package/src/cocoa/video.js +1 -1
  76. package/src/cocoa/window.js +4 -4
  77. package/src/components/Menu.js +7 -2
  78. package/src/components/Switch.js +1 -1
  79. package/src/components/native.js +1 -1
  80. package/src/desktopintegration.js +2 -1
  81. package/src/desktopsettings.js +193 -24
  82. package/src/dnd.js +2 -2
  83. package/src/filedialog.js +3 -3
  84. package/src/glnodes.js +1 -1
  85. package/src/grid.js +1 -1
  86. package/src/keyboardstate.js +28 -0
  87. package/src/layouts.js +1 -1
  88. package/src/node.d.ts +10 -10
  89. package/src/nodes/animation.js +1 -1
  90. package/src/nodes/invalidate.js +1 -1
  91. package/src/nodes/layouthost.js +1 -1
  92. package/src/nodes/video.js +19 -8
  93. package/src/nodes/window/droptarget.js +1 -1
  94. package/src/pacing.js +1 -1
  95. package/src/portal.js +31 -0
  96. package/src/refresh/index.js +2 -1
  97. package/src/screencolor.js +5 -10
  98. package/src/types/system.d.ts +8 -6
  99. package/src/types/video.d.ts +5 -2
  100. package/src/videobackend.js +28 -0
  101. package/src/videoframes.js +29 -16
  102. package/src/wayland/app.js +86 -0
  103. package/src/wayland/clipboard.js +14 -7
  104. package/src/wayland/connection.js +3 -0
  105. package/src/wayland/context2d.js +129 -10
  106. package/src/wayland/protocols/index.json +6 -0
  107. package/src/wayland/protocols/xdg-foreign-unstable-v2.json +1 -0
  108. package/src/wayland/seat.js +28 -1
  109. package/src/wayland/swapchain.js +23 -0
  110. package/src/wayland/video.js +376 -0
  111. package/src/wayland/xkb.js +137 -0
  112. package/src/win32/a11y.js +1 -1
  113. package/src/win32/app.js +2 -2
  114. package/src/win32/fonts.js +1 -1
  115. package/src/win32/glarea.js +4 -4
  116. package/src/win32/overlay.js +1 -1
  117. package/src/win32/panewindow.js +3 -3
  118. package/src/win32/shell.js +1 -1
  119. package/src/win32/window.js +2 -2
  120. package/src/windowid.js +1 -1
  121. package/src/xvideo.js +189 -0
package/src/cli/env.js ADDED
@@ -0,0 +1,484 @@
1
+ // Every REACT_X11_* variable the renderer reads, for `react-x11 help`.
2
+ //
3
+ // The renderer reads them, not this command: each one works the same in
4
+ // front of `react-x11 dev`, `node app.js` or a `.desktop` launcher, and the
5
+ // list is here because the CLI is the one place a developer can ask for it.
6
+ // test/cli-help.test.js holds it to src/: a variable read there and missing
7
+ // here fails, and so does one listed here that nothing reads any more.
8
+ //
9
+ // `value` is how the variable is set, shown after the name; `text` says what
10
+ // it does, with the default where there is one.
11
+
12
+ export const ENVIRONMENT = [
13
+ {
14
+ title: 'Backend and display',
15
+ vars: [
16
+ {
17
+ name: 'REACT_X11_BACKEND',
18
+ value: 'x11|cocoa|wayland|win32|auto',
19
+ text:
20
+ "The backend, over createRoot({ backend }) and the platform's " +
21
+ 'default: Cocoa on macOS, win32 on Windows, X11 elsewhere. Wayland ' +
22
+ 'is only ever used when it is named.',
23
+ },
24
+ {
25
+ name: 'REACT_X11_SCALE',
26
+ value: '<0.5–8>',
27
+ text:
28
+ 'Device pixels per logical pixel. Outranks GDK_SCALE, ' +
29
+ "QT_SCALE_FACTOR, the desktop's settings and even a numeric " +
30
+ 'createRoot({ scale }).',
31
+ },
32
+ {
33
+ name: 'REACT_X11_FRAME_RATE',
34
+ value: 'display|adaptive|throughput|<fps>',
35
+ text:
36
+ "Every window's frame rate, over its frameRate prop and " +
37
+ 'createRoot({ frameRate }).',
38
+ },
39
+ {
40
+ name: 'REACT_X11_TEXT_STRIP_BELOW',
41
+ value: '<px>',
42
+ text:
43
+ 'The size under which <text> paints as a strip of ink rather than ' +
44
+ 'glyphs, where createRoot({ textStripBelow }) is unset. Default 6; ' +
45
+ '0 paints glyphs at every size.',
46
+ },
47
+ {
48
+ name: 'REACT_X11_FONT_SOURCE',
49
+ value: '<dir|file>',
50
+ text:
51
+ 'X11: the fonts to use on a machine without fontconfig (no ' +
52
+ 'fc-match), where createRoot({ fontSource }) is unset — a ' +
53
+ 'directory of .ttf/.otf files or one font file.',
54
+ },
55
+ {
56
+ name: 'REACT_X11_THREADED',
57
+ value: '0',
58
+ text:
59
+ 'macOS: keep the app on the main thread, pumping AppKit, instead ' +
60
+ 'of moving it onto a worker.',
61
+ },
62
+ ],
63
+ },
64
+ {
65
+ title: 'Desktop integration',
66
+ vars: [
67
+ {
68
+ name: 'REACT_X11_A11Y',
69
+ value: '0|1',
70
+ text:
71
+ '0 turns the accessibility bridge off. Any other value turns it on ' +
72
+ 'even under NO_AT_BRIDGE, and says why when it cannot start.',
73
+ },
74
+ {
75
+ name: 'REACT_X11_A11Y_INTERVAL',
76
+ value: '<ms>',
77
+ text:
78
+ 'How often a stream of changes — a scroll, a value ticking — is ' +
79
+ 'pushed to assistive technology. Default 500; 0 pushes every ' +
80
+ 'change.',
81
+ },
82
+ {
83
+ name: 'REACT_X11_NO_GLOBAL_MENU',
84
+ value: '1',
85
+ text: "<MenuBar> always draws its own bar, never the desktop panel's.",
86
+ },
87
+ {
88
+ name: 'REACT_X11_NO_TRANSPARENCY',
89
+ value: '1',
90
+ text:
91
+ 'Pretend there is no compositor: transparent windows are created ' +
92
+ 'opaque.',
93
+ },
94
+ {
95
+ name: 'REACT_X11_NO_APPEARANCE_CACHE',
96
+ value: '1',
97
+ text:
98
+ "Do not keep the desktop's appearance in the cache directory, for a " +
99
+ 'process that must not touch the disk.',
100
+ },
101
+ ],
102
+ },
103
+ {
104
+ title: 'Development',
105
+ vars: [
106
+ {
107
+ name: 'REACT_X11_DEVTOOLS',
108
+ value: '1|0',
109
+ text:
110
+ 'Connect to React DevTools (npx react-devtools). On under ' +
111
+ 'react-x11 dev; 0 turns it off.',
112
+ },
113
+ {
114
+ name: 'REACT_X11_DEVTOOLS_HOST',
115
+ value: '<host>',
116
+ text: 'Where React DevTools listens. Default localhost.',
117
+ },
118
+ {
119
+ name: 'REACT_X11_DEVTOOLS_PORT',
120
+ value: '<port>',
121
+ text: 'The port React DevTools listens on. Default 8097.',
122
+ },
123
+ {
124
+ name: 'REACT_X11_DEVTOOLS_STATE',
125
+ value: '<file>',
126
+ text:
127
+ "Where DevTools' settings are kept between runs. Default " +
128
+ 'react-x11/devtools.json in the cache directory.',
129
+ },
130
+ {
131
+ name: 'REACT_X11_CLICK_TO_COMPONENT',
132
+ value: '1|0',
133
+ text:
134
+ "Alt+Click (Option+Click on macOS) opens an element's JSX line in " +
135
+ 'your editor. On under react-x11 dev; 0 turns it off.',
136
+ },
137
+ {
138
+ name: 'REACT_X11_EDITOR',
139
+ value: '<command>',
140
+ text:
141
+ 'The editor click-to-component and the dev error window open: ' +
142
+ 'cursor (the default), code, code-insiders, windsurf, vim, nvim. ' +
143
+ 'Setting it also turns click-to-component on.',
144
+ },
145
+ {
146
+ name: 'REACT_X11_STRICT_TOKENS',
147
+ value: '1',
148
+ text:
149
+ 'A $token the theme does not define throws, instead of being ' +
150
+ 'dropped and reported.',
151
+ },
152
+ ],
153
+ },
154
+ {
155
+ title: 'Diagnostics',
156
+ vars: [
157
+ {
158
+ name: 'REACT_X11_TRACE',
159
+ value: 'summary|requests|chrome:<file>',
160
+ text:
161
+ 'Record the X11 protocol: an opcode histogram at exit, a line per ' +
162
+ 'request, or Chrome Trace Event JSON. +stacks after requests or ' +
163
+ 'chrome names the JS call behind each request.',
164
+ },
165
+ {
166
+ name: 'REACT_X11_DEBUG_PAINT',
167
+ value: '1|full',
168
+ text:
169
+ "Stroke each frame's damage rects in a rotating colour. full also " +
170
+ 'warns, with the reasons and a stack, on every full-window repaint.',
171
+ },
172
+ {
173
+ name: 'REACT_X11_DEBUG_LAYOUT',
174
+ value: '1',
175
+ text: 'Outline every laid-out node, coloured by tree depth.',
176
+ },
177
+ {
178
+ name: 'REACT_X11_DEBUG_SCALE',
179
+ value: '1',
180
+ text: 'Print the evidence for the display scale, and the verdict.',
181
+ },
182
+ {
183
+ name: 'REACT_X11_PAINT_CACHE',
184
+ value: 'verify',
185
+ text:
186
+ 'Re-render every paint-cache hit and fail loudly where it differs ' +
187
+ 'from the entry: the check for a cache key that misses a ' +
188
+ 'dependency. Slow.',
189
+ },
190
+ {
191
+ name: 'REACT_X11_DEBUG_PAINT_CACHE',
192
+ value: '1',
193
+ text: "Log the paint cache's size, hits and misses every frame.",
194
+ },
195
+ {
196
+ name: 'REACT_X11_DEBUG_URI',
197
+ value: '1',
198
+ text:
199
+ 'Narrate URI-scheme handling, with queries and fragments stripped ' +
200
+ 'so it is safe to paste into an issue.',
201
+ },
202
+ ],
203
+ },
204
+ {
205
+ title: 'Optimisations off (each answers only to 1)',
206
+ intro:
207
+ 'For measuring one against the plain path on the same build, and as ' +
208
+ 'first aid when it misrenders.',
209
+ vars: [
210
+ {
211
+ name: 'REACT_X11_NO_PAINT_CACHE',
212
+ value: '1',
213
+ text: 'Paint every node live; no cached pixels.',
214
+ },
215
+ {
216
+ name: 'REACT_X11_NO_SCROLL_BLIT',
217
+ value: '1',
218
+ text:
219
+ 'A scroll, or a subtree that only moved, repaints instead of ' +
220
+ 'copying the pixels it already has.',
221
+ },
222
+ {
223
+ name: 'REACT_X11_NO_BOUNDS_CACHE',
224
+ value: '1',
225
+ text: "Recompute each subtree's paint reach on every pass.",
226
+ },
227
+ {
228
+ name: 'REACT_X11_NO_SCOPED_FLOORS',
229
+ value: '1',
230
+ text:
231
+ 'Measure content floors over the whole tree on every layout ' +
232
+ 'change.',
233
+ },
234
+ {
235
+ name: 'REACT_X11_NO_UNREAD_WIDTHS',
236
+ value: '1',
237
+ text:
238
+ "Measure every box's min-content width, read or not, in the width " +
239
+ 'passes.',
240
+ },
241
+ {
242
+ name: 'REACT_X11_NO_REFIT_HOLD',
243
+ value: '1',
244
+ text:
245
+ "An 'auto'-sized window lays out at its old size rather than wait " +
246
+ "for the server's answer to its resize.",
247
+ },
248
+ {
249
+ name: 'REACT_X11_NO_PANE_SHAPE',
250
+ value: '1',
251
+ text:
252
+ "X11: a <glarea> overlay pane stays the rectangle of its child's " +
253
+ 'reach instead of being cut to its ink.',
254
+ },
255
+ {
256
+ name: 'REACT_X11_NO_SHADOW_TILES',
257
+ value: '1',
258
+ text: 'macOS, Windows: draw every shadow live, not from cached tiles.',
259
+ },
260
+ {
261
+ name: 'REACT_X11_NO_STROKE_CHUNKING',
262
+ value: '1',
263
+ text:
264
+ 'macOS: hand each stroke to CoreGraphics whole, however many ' +
265
+ 'subpaths it has.',
266
+ },
267
+ ],
268
+ },
269
+ {
270
+ title: 'macOS (Cocoa)',
271
+ vars: [
272
+ {
273
+ name: 'REACT_X11_COCOA_PRESENTER',
274
+ value: 'surface|layers',
275
+ text:
276
+ 'How windows reach the screen: one surface (the default) or ' +
277
+ 'retained CALayers.',
278
+ },
279
+ {
280
+ name: 'REACT_X11_COCOA_PROMOTE',
281
+ value: '1|0',
282
+ text:
283
+ 'Whether the nodes that animate get layers of their own: 1 always, ' +
284
+ '0 never. Default on where the bridge draws in sRGB.',
285
+ },
286
+ {
287
+ name: 'REACT_X11_COCOA_PANE_STRETCH',
288
+ value: 'behind|never|always',
289
+ text:
290
+ "When a <Frame> pane's last frame is stretched while a resize waits " +
291
+ 'for the next. Default behind.',
292
+ },
293
+ {
294
+ name: 'REACT_X11_CALAYERS_PATH',
295
+ value: '<path>',
296
+ text:
297
+ 'Load the @windowkit/appkit bridge from a checkout or an extracted ' +
298
+ 'tarball instead of the installed package.',
299
+ },
300
+ ],
301
+ },
302
+ {
303
+ title: 'Wayland',
304
+ vars: [
305
+ {
306
+ name: 'REACT_X11_GL_DEVICE',
307
+ value: '<path>',
308
+ text:
309
+ 'The DRM render node to draw on (/dev/dri/renderD128), where ' +
310
+ 'glPolicy.devicePath is unset.',
311
+ },
312
+ {
313
+ name: 'REACT_X11_WAYLAND_FRAME_STALL_MS',
314
+ value: '<ms>',
315
+ text:
316
+ 'How long a frame callback may be outstanding before the window ' +
317
+ 'counts as not shown. Default 2000; 0 turns the watch off.',
318
+ },
319
+ {
320
+ name: 'REACT_X11_WAYLAND_TRACE',
321
+ value: '1',
322
+ text:
323
+ 'A line on stderr per presented frame, text-input event and ' +
324
+ 'eyedropper step.',
325
+ },
326
+ {
327
+ name: 'REACT_X11_WAYLAND_SNAPSHOT',
328
+ value: '<file.png>',
329
+ text:
330
+ "Write the first window's frame, read back from the GPU, to this " +
331
+ 'file: a screenshot where the desktop offers no screenshot API.',
332
+ },
333
+ {
334
+ name: 'REACT_X11_WAYLAND_SNAPSHOT_AT',
335
+ value: '<n>',
336
+ text: 'Which presented frame the snapshot is. Default 30.',
337
+ },
338
+ ],
339
+ },
340
+ {
341
+ title: 'Windows (win32)',
342
+ vars: [
343
+ {
344
+ name: 'REACT_X11_WIN32_PATH',
345
+ value: '<path>',
346
+ text:
347
+ 'Load the @windowkit/win32 bridge from a checkout instead of the ' +
348
+ 'installed package.',
349
+ },
350
+ {
351
+ name: 'REACT_X11_WIN32_DEBUG',
352
+ value: '1',
353
+ text:
354
+ 'Report every frame and what it drew into, the size each paragraph ' +
355
+ 'is shaped at, and drag and drop.',
356
+ },
357
+ {
358
+ name: 'REACT_X11_WIN32_FULL_REPAINT',
359
+ value: '1',
360
+ text: 'Paint the whole window every frame: the control for a damage bug.',
361
+ },
362
+ {
363
+ name: 'REACT_X11_TRACE_IME',
364
+ value: '1',
365
+ text: 'A line on stderr per input-method event.',
366
+ },
367
+ {
368
+ name: 'REACT_X11_TRACE_A11Y',
369
+ value: '1',
370
+ text: 'A line on stderr per UI Automation push and action.',
371
+ },
372
+ {
373
+ name: 'REACT_X11_GL_DEBUG',
374
+ value: '1',
375
+ text:
376
+ '<glarea>: check every GL call against glGetError, list the GL ' +
377
+ 'names asked for that are missing, and print frame timings every ' +
378
+ '30 swaps. The GL_ variables below need it.',
379
+ },
380
+ {
381
+ name: 'REACT_X11_GL_TOP',
382
+ value: '<n>',
383
+ text: 'How many of the most-called GL verbs it lists. Default 12.',
384
+ },
385
+ {
386
+ name: 'REACT_X11_GL_TRACE',
387
+ value: '<n>',
388
+ text: 'Print the first n GL calls, with their arguments.',
389
+ },
390
+ {
391
+ name: 'REACT_X11_GL_TRACE_AFTER',
392
+ value: '<swaps>',
393
+ text: 'Start that trace after this many frames. Default 0.',
394
+ },
395
+ {
396
+ name: 'REACT_X11_GL_PEEK',
397
+ value: '<n>',
398
+ text:
399
+ 'Read the stencil and the colour at the middle of the surface ' +
400
+ 'after each of the first n draws.',
401
+ },
402
+ {
403
+ name: 'REACT_X11_GL_FORCE_CLEAR',
404
+ value: '1',
405
+ text:
406
+ 'Clear the GL surface magenta, to tell it apart from the 2D layer ' +
407
+ 'behind it.',
408
+ },
409
+ ],
410
+ },
411
+ {
412
+ title: 'Set by react-x11 itself',
413
+ intro: 'Listed so they are not a mystery in an environment dump.',
414
+ vars: [
415
+ {
416
+ name: 'REACT_X11_FRAME',
417
+ value: '1',
418
+ text: "Set on a <Frame> pane's process by the host that forked it.",
419
+ },
420
+ {
421
+ name: 'REACT_X11_DEVTOOLS_PROFILING',
422
+ text:
423
+ "Carried across DevTools' Reload and start profiling: the " +
424
+ 'profiling to start with.',
425
+ },
426
+ {
427
+ name: 'REACT_X11_DEVTOOLS_SESSION',
428
+ text:
429
+ "Carried across the same restart: the session storage a browser's " +
430
+ 'reload would have kept.',
431
+ },
432
+ ],
433
+ },
434
+ ];
435
+
436
+ /**
437
+ * `text` broken into lines of at most `width` columns, each `indent`ed and
438
+ * passed through `style`. A `{ … }` stays whole, so `createRoot({ scale })`
439
+ * is never cut after its brace.
440
+ */
441
+ function wrap(text, width, indent, style = (s) => s) {
442
+ const lines = [];
443
+ let line = '';
444
+ for (const word of text.match(/(?:\{[^}]*\}|\S)+/g) ?? []) {
445
+ if (line && indent.length + line.length + 1 + word.length > width) {
446
+ lines.push(indent + style(line));
447
+ line = word;
448
+ } else {
449
+ line = line ? `${line} ${word}` : word;
450
+ }
451
+ }
452
+ if (line) lines.push(indent + style(line));
453
+ return lines.join('\n');
454
+ }
455
+
456
+ // SGR pairs, each closing only what it opened. Applied after the wrapping,
457
+ // which counts the columns of the plain text.
458
+ const STYLES = {
459
+ title: ['\x1b[1m', '\x1b[22m'],
460
+ name: ['\x1b[1;36m', '\x1b[39;22m'],
461
+ dim: ['\x1b[2m', '\x1b[22m'],
462
+ };
463
+
464
+ /**
465
+ * The list as `react-x11 help` prints it, wrapped to `width` columns. With
466
+ * `color`, group titles are bold, names bold cyan, and value forms and
467
+ * intros dim; the text is otherwise the same.
468
+ */
469
+ export function formatEnvironment(width = 80, { color = false } = {}) {
470
+ const paint = (style) => (text) =>
471
+ color ? STYLES[style][0] + text + STYLES[style][1] : text;
472
+ const out = ['Environment variables:'];
473
+ for (const group of ENVIRONMENT) {
474
+ out.push('', paint('title')(group.title));
475
+ if (group.intro) out.push(wrap(group.intro, width, ' ', paint('dim')));
476
+ for (const { name, value, text } of group.vars) {
477
+ out.push(
478
+ ` ${paint('name')(name)}${value ? paint('dim')(`=${value}`) : ''}`,
479
+ );
480
+ out.push(wrap(text, width, ' '));
481
+ }
482
+ }
483
+ return `${out.join('\n')}\n`;
484
+ }
package/src/cli/index.js CHANGED
@@ -1,17 +1,28 @@
1
1
  #!/usr/bin/env node
2
- // react-x11's command line, the `bin` of the package. One command so far:
2
+ // react-x11's command line, the `bin` of the package:
3
3
  //
4
4
  // react-x11 dev <file> [args…] hot reload with nothing to set up (./dev.js)
5
+ // react-x11 create <dir> a new app (./create.js; also what
6
+ // `npm create react-x11` runs)
7
+ // react-x11 help the usage, then every REACT_X11_* variable
8
+ // the renderer reads (./env.js)
5
9
  //
6
10
  // Arguments before the file belong to the command, and everything after it
7
11
  // to the app, which sees them in process.argv as `node <file> …args` would
8
12
  // have handed them over.
9
13
  import { readFileSync } from 'node:fs';
14
+ import { runCreate } from './create.js';
10
15
  import { DevError, runDev } from './dev.js';
16
+ import { formatEnvironment } from './env.js';
11
17
 
12
18
  const USAGE = `Usage: react-x11 dev <file> [args…]
19
+ react-x11 create <dir> [--no-install]
20
+ react-x11 help
13
21
 
14
22
  Commands:
23
+ create <dir> Write a new app into <dir> and install its dependencies;
24
+ --no-install writes the files only. \`npm create react-x11
25
+ <dir>\` does the same.
15
26
  dev <file> Run <file> with hot reload: saving it, or a .jsx module it
16
27
  imports, updates the running app in place. Its default
17
28
  export is mounted, inside a window of its own when it
@@ -21,6 +32,8 @@ Commands:
21
32
  source; REACT_X11_CLICK_TO_COMPONENT=0 turns that off.
22
33
  Connects to React DevTools (npx react-devtools) whenever
23
34
  it runs; REACT_X11_DEVTOOLS=0 turns that off.
35
+ help Show this help, then every REACT_X11_* environment
36
+ variable the renderer reads, with what it does.
24
37
 
25
38
  Options:
26
39
  -h, --help Show this help.
@@ -34,10 +47,23 @@ function version() {
34
47
 
35
48
  /** An exit code to finish with, or undefined while the app runs. */
36
49
  async function main([command, ...rest]) {
37
- if (command === '-h' || command === '--help' || command === 'help') {
50
+ if (command === '-h' || command === '--help') {
38
51
  process.stdout.write(USAGE);
39
52
  return 0;
40
53
  }
54
+ if (command === 'help') {
55
+ // A terminal's own width, within reason; 80 for a pipe or a file.
56
+ const columns = process.stdout.isTTY ? process.stdout.columns : 80;
57
+ const width = Math.min(100, Math.max(60, columns || 80));
58
+ // Colour where the terminal has it — hasColors() reads NO_COLOR and
59
+ // FORCE_COLOR — and into a pipe only when FORCE_COLOR asks for it.
60
+ const force = process.env.FORCE_COLOR;
61
+ const color = process.stdout.isTTY
62
+ ? process.stdout.hasColors()
63
+ : force !== undefined && force !== '0' && force !== 'false';
64
+ process.stdout.write(`${USAGE}\n${formatEnvironment(width, { color })}`);
65
+ return 0;
66
+ }
41
67
  if (command === '-v' || command === '--version') {
42
68
  process.stdout.write(`${version()}\n`);
43
69
  return 0;
@@ -46,6 +72,7 @@ async function main([command, ...rest]) {
46
72
  process.stderr.write(USAGE);
47
73
  return 2;
48
74
  }
75
+ if (command === 'create') return runCreate(rest);
49
76
  if (command !== 'dev') {
50
77
  process.stderr.write(`react-x11: no command "${command}".\n\n${USAGE}`);
51
78
  return 2;