react-x11 2.48.0 → 2.50.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 +9 -8
- package/package.json +7 -4
- package/src/ClickToComponent.js +11 -132
- package/src/Reconciler.js +4 -1
- package/src/cli/dev.js +578 -0
- package/src/cli/errorwindow.js +368 -0
- package/src/cli/index.js +81 -0
- package/src/cocoa/app.js +12 -3
- package/src/cocoa/presenter.js +3 -1
- package/src/cocoa/promotion.js +10 -11
- package/src/cocoa/relaunch.js +110 -13
- package/src/currentcolor.js +28 -0
- package/src/decorations.js +21 -9
- package/src/editor.js +151 -0
- package/src/errors.js +8 -0
- package/src/foreignnodes.js +8 -1
- package/src/gloverlay.js +2 -1
- package/src/node.d.ts +5 -0
- package/src/nodes/animation.js +31 -3
- package/src/nodes/boxpaint.js +34 -8
- package/src/nodes/cascade.js +50 -3
- package/src/nodes/damage.js +2 -0
- package/src/nodes/layout.js +9 -17
- package/src/nodes/moveblit.js +5 -5
- package/src/nodes/node.js +8 -0
- package/src/nodes/paint.js +4 -2
- package/src/nodes/queries.js +46 -34
- package/src/nodes/scrollblit.js +10 -7
- package/src/nodes/styling.js +176 -21
- package/src/nodes/window/capabilities.js +1 -1
- package/src/nodes/window/floors.js +63 -18
- package/src/nodes/window/size.js +94 -19
- package/src/refresh/loader.d.ts +16 -0
- package/src/refresh/loader.js +84 -14
- package/src/refresh/register.js +8 -5
- package/src/shorthands.js +1266 -0
- package/src/styles.js +370 -131
- package/src/types/style.d.ts +163 -39
package/README.md
CHANGED
|
@@ -446,14 +446,15 @@ To have it manage your real session instead — including replacing
|
|
|
446
446
|
npm run examples:tasks:hot # then edit examples/tasks.jsx while it runs
|
|
447
447
|
```
|
|
448
448
|
|
|
449
|
-
Runs the tasks example under
|
|
450
|
-
`
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
component
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
449
|
+
Runs the tasks example under `react-x11 dev`, with React **Fast Refresh**
|
|
450
|
+
(Node ≥ 22.15): saving `examples/tasks.jsx` updates the edited components
|
|
451
|
+
in place. The connection, the mounted window, and component state — the
|
|
452
|
+
task list, even half-typed text in the input — survive the reload (a
|
|
453
|
+
component whose hook signature changed remounts alone). Any app adopts it
|
|
454
|
+
the same way, with no accept handlers and no entry file to write:
|
|
455
|
+
`npx react-x11 dev app.jsx` mounts the file's default export, in a window
|
|
456
|
+
of its own while the tree renders none, and offers to install the toolchain
|
|
457
|
+
on its first run; see
|
|
457
458
|
[docs/ecosystem/dev-tooling.md](docs/ecosystem/dev-tooling.md#react-refresh).
|
|
458
459
|
|
|
459
460
|
## React DevTools
|
package/package.json
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-x11",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.50.0",
|
|
4
4
|
"description": "react renderer with X11 as a target",
|
|
5
5
|
"main": "./src/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"react-x11": "./src/cli/index.js"
|
|
8
|
+
},
|
|
6
9
|
"files": [
|
|
7
10
|
"src"
|
|
8
11
|
],
|
|
@@ -26,7 +29,7 @@
|
|
|
26
29
|
"examples:xeyes": "tsx examples/xeyes.jsx",
|
|
27
30
|
"examples:dashboard": "tsx examples/dashboard.jsx",
|
|
28
31
|
"examples:tasks": "tsx examples/tasks.jsx",
|
|
29
|
-
"examples:tasks:hot": "node
|
|
32
|
+
"examples:tasks:hot": "node src/cli/index.js dev examples/tasks.jsx",
|
|
30
33
|
"examples:menu": "tsx examples/menu.jsx",
|
|
31
34
|
"examples:notify": "tsx examples/notify.jsx",
|
|
32
35
|
"examples:monitor": "tsx examples/monitor.jsx",
|
|
@@ -118,7 +121,7 @@
|
|
|
118
121
|
"peerDependencies": {
|
|
119
122
|
"@babel/core": "^8.0.0",
|
|
120
123
|
"@babel/plugin-transform-react-jsx": "^8.0.0",
|
|
121
|
-
"hot-module-replacement": "^4.0.
|
|
124
|
+
"hot-module-replacement": "^4.0.2",
|
|
122
125
|
"react": "^19.3.0",
|
|
123
126
|
"react-refresh": "^0.19.0"
|
|
124
127
|
},
|
|
@@ -150,7 +153,7 @@
|
|
|
150
153
|
"eslint": "^9.32.0",
|
|
151
154
|
"eslint-plugin-react": "^7.37.5",
|
|
152
155
|
"globals": "^17.8.0",
|
|
153
|
-
"hot-module-replacement": "^4.0.
|
|
156
|
+
"hot-module-replacement": "^4.0.2",
|
|
154
157
|
"katex": "^0.19.0",
|
|
155
158
|
"prettier": "^3.6.0",
|
|
156
159
|
"react": "^19.3.0",
|
package/src/ClickToComponent.js
CHANGED
|
@@ -10,40 +10,9 @@
|
|
|
10
10
|
// loader both enable) has already rewritten that Error's stack to point at
|
|
11
11
|
// original source — we only have to parse the stack text and skip the
|
|
12
12
|
// frames inside React/the reconciler itself.
|
|
13
|
-
import { spawn } from 'node:child_process';
|
|
14
|
-
import { dirname, sep } from 'node:path';
|
|
15
|
-
import { fileURLToPath } from 'node:url';
|
|
16
13
|
import { setClickToComponentHandler } from './events.js';
|
|
17
14
|
import { selectInDevTools } from './DevToolsIntegration.js';
|
|
18
|
-
|
|
19
|
-
const STACK_FRAME = /^\s*at\s+(?:(.+?)\s+\()?(.+?):(\d+):(\d+)\)?\s*$/;
|
|
20
|
-
|
|
21
|
-
// The frames to walk past before the JSX call site itself: React captures
|
|
22
|
-
// the Error inside `jsxDEV`/`createElement`, and an element created by this
|
|
23
|
-
// renderer (or by node internals) is never what a click means. `SELF_DIR` is
|
|
24
|
-
// whichever copy of react-x11 is running — the repo's own `src/` for the
|
|
25
|
-
// examples and tests, an installed `node_modules/react-x11/src` for an app.
|
|
26
|
-
//
|
|
27
|
-
// Everything *else* in `node_modules` is deliberately not skipped here: a
|
|
28
|
-
// frame inside an installed package is a real call site, it is just not one
|
|
29
|
-
// in the application's source, and telling the two apart is what lets
|
|
30
|
-
// `resolveOwnedLocation` climb to the owner that is. Skipping every
|
|
31
|
-
// `node_modules` frame instead would walk straight past the library
|
|
32
|
-
// component and land on whatever application frame happens to be deeper in
|
|
33
|
-
// the render stack — usually the `render()` call at startup, which looks
|
|
34
|
-
// like an answer and isn't.
|
|
35
|
-
const REACT_RUNTIME =
|
|
36
|
-
/[\\/]node_modules[\\/](react|react-dom|react-reconciler|scheduler)[\\/]/;
|
|
37
|
-
const SELF_DIR = dirname(fileURLToPath(import.meta.url)) + sep;
|
|
38
|
-
|
|
39
|
-
function isInternalFrame(file) {
|
|
40
|
-
return (
|
|
41
|
-
file.startsWith('node:') ||
|
|
42
|
-
file === '<anonymous>' ||
|
|
43
|
-
file.startsWith(SELF_DIR) ||
|
|
44
|
-
REACT_RUNTIME.test(file)
|
|
45
|
-
);
|
|
46
|
-
}
|
|
15
|
+
import { openInEditor, stackFrames } from './editor.js';
|
|
47
16
|
|
|
48
17
|
/** The first stack frame outside React/the reconciler/node internals — the
|
|
49
18
|
* JSX call site for whatever element this Error was captured at, wherever it
|
|
@@ -52,25 +21,16 @@ function isInternalFrame(file) {
|
|
|
52
21
|
* Exported because `react-x11/test`'s `sourceOf` answers the same question
|
|
53
22
|
* for a test that a click answers for an editor. */
|
|
54
23
|
export function resolveLocation(debugStack) {
|
|
55
|
-
const
|
|
56
|
-
if (!
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
return {
|
|
66
|
-
functionName,
|
|
67
|
-
file,
|
|
68
|
-
line: Number(lineStr),
|
|
69
|
-
column: Number(columnStr),
|
|
70
|
-
installed: file.includes(`${sep}node_modules${sep}`),
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
return null;
|
|
24
|
+
const frame = stackFrames(debugStack?.stack).find((f) => !f.internal);
|
|
25
|
+
if (!frame) return null;
|
|
26
|
+
const { functionName, file, line, column, installed } = frame;
|
|
27
|
+
return {
|
|
28
|
+
functionName: functionName ?? undefined,
|
|
29
|
+
file,
|
|
30
|
+
line,
|
|
31
|
+
column,
|
|
32
|
+
installed,
|
|
33
|
+
};
|
|
74
34
|
}
|
|
75
35
|
|
|
76
36
|
/** The nearest source location a click can mean: the clicked element's own
|
|
@@ -132,87 +92,6 @@ function logOwnerChain(fiber) {
|
|
|
132
92
|
}
|
|
133
93
|
}
|
|
134
94
|
|
|
135
|
-
// GUI editors: navigate their registered URI scheme (same idea as
|
|
136
|
-
// https://github.com/sidorares/show-component, minus the browser) instead of
|
|
137
|
-
// spawning their CLI shim. The CLI wrapper (e.g. `cursor -g ...`) round-trips
|
|
138
|
-
// through a shell script that re-detects and re-execs the real app, which is
|
|
139
|
-
// visibly slower than the OS handing the URL straight to the already-running
|
|
140
|
-
// instance via Launch Services (macOS) / xdg-open (Linux).
|
|
141
|
-
const EDITOR_SCHEMES = {
|
|
142
|
-
cursor: 'cursor',
|
|
143
|
-
code: 'vscode',
|
|
144
|
-
vscode: 'vscode',
|
|
145
|
-
'code-insiders': 'vscode-insiders',
|
|
146
|
-
'vscode-insiders': 'vscode-insiders',
|
|
147
|
-
windsurf: 'windsurf',
|
|
148
|
-
};
|
|
149
|
-
|
|
150
|
-
// Terminal editors have no URI scheme to navigate — spawn them directly.
|
|
151
|
-
const EDITOR_CLI = {
|
|
152
|
-
vim: (file, line, column) => [
|
|
153
|
-
'vim',
|
|
154
|
-
[`+call cursor(${line},${column})`, file],
|
|
155
|
-
],
|
|
156
|
-
nvim: (file, line, column) => [
|
|
157
|
-
'nvim',
|
|
158
|
-
[`+call cursor(${line},${column})`, file],
|
|
159
|
-
],
|
|
160
|
-
};
|
|
161
|
-
|
|
162
|
-
const OPEN_URI_COMMAND = process.platform === 'darwin' ? 'open' : 'xdg-open';
|
|
163
|
-
|
|
164
|
-
function editorUri(scheme, file, line, column) {
|
|
165
|
-
return `${scheme}://file${encodeURI(file)}:${line}:${column}`;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
function openInEditor({ file, line, column }) {
|
|
169
|
-
const name = process.env.REACT_X11_EDITOR || 'cursor';
|
|
170
|
-
let bin, args;
|
|
171
|
-
if (EDITOR_CLI[name]) {
|
|
172
|
-
[bin, args] = EDITOR_CLI[name](file, line, column);
|
|
173
|
-
} else {
|
|
174
|
-
// an unrecognized name is assumed to be a URI scheme itself, so any
|
|
175
|
-
// editor that registers one works without a matching entry above
|
|
176
|
-
const scheme = EDITOR_SCHEMES[name] || name;
|
|
177
|
-
bin = OPEN_URI_COMMAND;
|
|
178
|
-
args = [editorUri(scheme, file, line, column)];
|
|
179
|
-
}
|
|
180
|
-
// stderr is kept (rather than the whole stdio ignored) for one reason:
|
|
181
|
-
// `open`/`xdg-open` exit non-zero and print *there* when nothing claims
|
|
182
|
-
// the scheme — an editor that isn't installed, or one whose URL handler
|
|
183
|
-
// was never registered. Swallowing that is what makes click-to-component
|
|
184
|
-
// look like it only logs: the location resolves, the console line prints,
|
|
185
|
-
// and the editor silently never opens.
|
|
186
|
-
const child = spawn(bin, args, {
|
|
187
|
-
detached: true,
|
|
188
|
-
stdio: ['ignore', 'ignore', 'pipe'],
|
|
189
|
-
});
|
|
190
|
-
let stderr = '';
|
|
191
|
-
// ...and unref'd, so keeping it does not keep the process alive: a pipe is
|
|
192
|
-
// a ref'd handle, and an editor that outlives the app it was launched from
|
|
193
|
-
// is the normal case, not a reason to hold the event loop open.
|
|
194
|
-
child.stderr.unref();
|
|
195
|
-
child.stderr.on('data', (chunk) => {
|
|
196
|
-
stderr += chunk;
|
|
197
|
-
});
|
|
198
|
-
child.on('error', (err) => {
|
|
199
|
-
console.warn(
|
|
200
|
-
`react-x11: click-to-component could not launch "${bin}" (${err.code ?? err.message}). ` +
|
|
201
|
-
'Set REACT_X11_EDITOR to your editor CLI (cursor, code, code-insiders, windsurf, vim, nvim).',
|
|
202
|
-
);
|
|
203
|
-
});
|
|
204
|
-
child.on('exit', (code) => {
|
|
205
|
-
if (!code) return;
|
|
206
|
-
console.warn(
|
|
207
|
-
`react-x11: click-to-component — \`${bin} ${args.join(' ')}\` exited ${code}. ` +
|
|
208
|
-
(stderr.trim() ? `${stderr.trim()} ` : '') +
|
|
209
|
-
`Set REACT_X11_EDITOR to the editor you actually use (currently "${name}"; ` +
|
|
210
|
-
'cursor, code, code-insiders, windsurf, vim, nvim, or any registered URI scheme).',
|
|
211
|
-
);
|
|
212
|
-
});
|
|
213
|
-
child.unref();
|
|
214
|
-
}
|
|
215
|
-
|
|
216
95
|
/** Briefly tint the clicked node so there is visual confirmation of what was
|
|
217
96
|
* resolved, reusing the same overlay the DevTools hover highlight paints. */
|
|
218
97
|
function flashHighlight(node) {
|
package/src/Reconciler.js
CHANGED
|
@@ -44,6 +44,7 @@ import { hasDropProps } from './dnd.js';
|
|
|
44
44
|
import { AppProvider } from './appcontext.js';
|
|
45
45
|
import {
|
|
46
46
|
defaultRootHandlers,
|
|
47
|
+
NOT_IN_WINDOW,
|
|
47
48
|
setErrorHandler,
|
|
48
49
|
STRICT_TOKENS,
|
|
49
50
|
} from './errors.js';
|
|
@@ -138,11 +139,13 @@ const ROOT_TYPES = new Set(['window', 'popup', THEME_SCOPE]);
|
|
|
138
139
|
* `<box>` the app never wrote (#584).
|
|
139
140
|
*/
|
|
140
141
|
function notAtRoot(type) {
|
|
141
|
-
|
|
142
|
+
const err = new Error(
|
|
142
143
|
`react-x11: <${type}> must be inside a <window> — it is at the root of ` +
|
|
143
144
|
'the tree, which holds only <window> and <popup> elements. Render it ' +
|
|
144
145
|
`inside one: <window><${type} …/></window>.`,
|
|
145
146
|
);
|
|
147
|
+
err.code = NOT_IN_WINDOW;
|
|
148
|
+
return err;
|
|
146
149
|
}
|
|
147
150
|
|
|
148
151
|
const HostConfig = {
|