react-x11 2.47.5 → 2.48.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-x11",
3
- "version": "2.47.5",
3
+ "version": "2.48.0",
4
4
  "description": "react renderer with X11 as a target",
5
5
  "main": "./src/index.js",
6
6
  "files": [
@@ -109,7 +109,7 @@
109
109
  "yoga-layout": "^3.2.1"
110
110
  },
111
111
  "optionalDependencies": {
112
- "@windowkit/appkit": "^0.28.0",
112
+ "@windowkit/appkit": "^0.29.0",
113
113
  "@windowkit/wayland": "^3.1.1",
114
114
  "@windowkit/win32": "^0.0.9",
115
115
  "dbus-native": "^0.16.0",
package/src/cocoa/app.js CHANGED
@@ -71,6 +71,9 @@ const PUMP_INTERVAL_MS = 8;
71
71
  // to 100, docs/windows.md §"Resize"). `createRoot({ cocoa: { resizeWait } })`
72
72
  // sets it; 0 is no handshake at all.
73
73
  const RESIZE_WAIT_MS = 50;
74
+ // What `createRoot({ cocoa: { paneStretch } })` and
75
+ // REACT_X11_COCOA_PANE_STRETCH take (src/cocoa/panehost.js, `_stretch`).
76
+ const PANE_STRETCH = ['behind', 'never', 'always'];
74
77
  // How often the screen layout is re-read while something is watching it, in
75
78
  // ms. There is no event to wait for — the bridge keeps its `NSScreen` copy
76
79
  // current on macOS's notification and emits nothing (#617) — so
@@ -149,6 +152,24 @@ export class CocoaApp {
149
152
  );
150
153
  }
151
154
  this._resizeWait = resizeWait;
155
+ // How a `<Frame>` pane's last frame is shown while the frame of the
156
+ // size its layer was just given is on its way (src/cocoa/panehost.js,
157
+ // `_stretch`): 'behind' stretches it across once the pane falls behind
158
+ // `resizeWait`, 'never' keeps it at its size however late the pane is,
159
+ // and 'always' stretches it both ways. Apart from `resizeWait`, which
160
+ // is also how long AppKit holds the window's edge for a frame, so a
161
+ // pane that is never to be stretched would otherwise need a budget
162
+ // that held the drag back as long.
163
+ const paneStretch =
164
+ options.cocoa?.paneStretch ??
165
+ (process.env.REACT_X11_COCOA_PANE_STRETCH || 'behind');
166
+ if (!PANE_STRETCH.includes(paneStretch)) {
167
+ throw new TypeError(
168
+ 'react-x11: cocoa.paneStretch (REACT_X11_COCOA_PANE_STRETCH) is ' +
169
+ `one of ${PANE_STRETCH.join(', ')} — got ${paneStretch}.`,
170
+ );
171
+ }
172
+ this._paneStretch = paneStretch;
152
173
 
153
174
  // The activation policy has to be fixed before the app finishes
154
175
  // launching — a Regular launch registers a Dock tile, so an agent app
package/src/cocoa/dnd.js CHANGED
@@ -34,7 +34,8 @@
34
34
  // the pasteboard, so an in-app drop still gets `e.items` by reference and
35
35
  // `e.source === 'internal'`, the contract the X11 transport keeps. Other
36
36
  // applications read the pasteboard, where a thunk is a promise the bridge
37
- // asks `provide` to keep.
37
+ // asks `provide` to keep — on the main thread. On a worker the bridge takes
38
+ // no `provide`, and a thunk resolves as the drag begins (`dragSpec`).
38
39
  //
39
40
  // ## Types
40
41
  //
@@ -223,6 +224,16 @@ export const wire = (value) =>
223
224
  * to keep. The files thunk alone resolves now, because the items cannot
224
225
  * be counted without it.
225
226
  *
227
+ * Unless the app is on a worker (`upFront`, threaded mode): AppKit asks a
228
+ * provider from inside its own callback on the main thread, which a worker
229
+ * cannot answer in time, so the bridge refuses `provide` there with a
230
+ * TypeError. That was every drag on a Mac, since an app runs on a worker
231
+ * by default. There every thunk resolves now, as the win32 backend has
232
+ * them, and this is the external promotion `_resolve` caches for: an
233
+ * in-app drop is handed the same value by reference. A value that resolves
234
+ * to nothing, or that JSON cannot carry (a cycle, a node), is left off
235
+ * the pasteboard rather than failing the drag; an in-app drop still has it.
236
+ *
226
237
  * No image, and so no slide-back. The picture of the drag is the app's own
227
238
  * `<popup dragPreview>`; what the system session carries is a blank 1×1.
228
239
  * Left at AppKit's default, a refused or cancelled drop plays
@@ -236,7 +247,7 @@ export const wire = (value) =>
236
247
  * to animate from `onDragEnd`. Not a seam: there is no system image for
237
248
  * a caller to want slid back.
238
249
  */
239
- export function dragSpec(session, native, scale) {
250
+ export function dragSpec(session, native, scale, { upFront = false } = {}) {
240
251
  const types = session.types;
241
252
  const reverse = new Map(); // uti -> react-x11 type
242
253
  const items = [];
@@ -256,7 +267,17 @@ export function dragSpec(session, native, scale) {
256
267
  if (!uti || uti in first) continue;
257
268
  reverse.set(uti, type);
258
269
  const raw = session._data[type];
259
- first[uti] = typeof raw === 'function' ? null : wire(raw);
270
+ if (!upFront) {
271
+ first[uti] = typeof raw === 'function' ? null : wire(raw);
272
+ continue;
273
+ }
274
+ const value = session._resolve(type);
275
+ if (value == null) continue;
276
+ try {
277
+ first[uti] = wire(value);
278
+ } catch {
279
+ // not something the pasteboard can carry; the in-app drop has it
280
+ }
260
281
  }
261
282
  if (Object.keys(first).length) {
262
283
  if (items.length) Object.assign(items[0], first);
@@ -269,7 +290,11 @@ export function dragSpec(session, native, scale) {
269
290
  items,
270
291
  operations: session.actions,
271
292
  slideBack: false,
272
- provide: (uti) => wire(session._resolve(reverse.get(uti) ?? uti)),
293
+ ...(upFront
294
+ ? null
295
+ : {
296
+ provide: (uti) => wire(session._resolve(reverse.get(uti) ?? uti)),
297
+ }),
273
298
  };
274
299
  }
275
300
 
@@ -91,7 +91,8 @@ class PaneLayer {
91
91
  * and a smeared strip. The budget is the window's live-resize
92
92
  * handshake's (`createRoot({ cocoa: { resizeWait } })`), how long a frame
93
93
  * of a new size may take before the old one is shown in its place: 0
94
- * stretches always, both ways, as before.
94
+ * stretches always, both ways, as before. And the app may say otherwise
95
+ * whatever the budget (`_stretch`).
95
96
  *
96
97
  * Down the layer the frame is at its size whatever this says. A width
97
98
  * moves where a page's lines break, and what comes is a page laid out
@@ -116,8 +117,9 @@ class PaneLayer {
116
117
  * decides the rest (`_answered`).
117
118
  */
118
119
  _anchoredNow() {
120
+ const stretch = this._stretch();
121
+ if (stretch !== 'behind') return stretch === 'never';
119
122
  const budget = this.app._resizeWait ?? RESIZE_WAIT_MS;
120
- if (!(budget > 0)) return false;
121
123
  // a size asked longer ago than the budget, and no frame of any since
122
124
  const oldest = this._asked[0];
123
125
  if (oldest && now() - Math.max(oldest.at, this._answeredAt) > budget) {
@@ -126,6 +128,23 @@ class PaneLayer {
126
128
  return !this._behind;
127
129
  }
128
130
 
131
+ /**
132
+ * When the frame the layer holds is stretched to bounds that are not its
133
+ * size: `'behind'`, across, once the pane falls behind the budget
134
+ * (`_anchoredNow`); `'never'`, kept at its size and its edge carried over
135
+ * however late the pane is; `'always'`, both ways, Core Animation's own.
136
+ * The app's `cocoa.paneStretch` (REACT_X11_COCOA_PANE_STRETCH), and
137
+ * `'behind'` where it names none, or `'always'` at a budget of 0, which
138
+ * waits for no frame. An app that says `'never'` was not served by the
139
+ * budget: as long as AppKit holds the window's edge for a frame, so a
140
+ * budget a slow page never misses held every step of the drag back.
141
+ */
142
+ _stretch() {
143
+ const stretch = this.app._paneStretch ?? 'behind';
144
+ if (stretch !== 'behind') return stretch;
145
+ return (this.app._resizeWait ?? RESIZE_WAIT_MS) > 0 ? 'behind' : 'always';
146
+ }
147
+
129
148
  /**
130
149
  * The props that show the frame the layer holds in bounds that are not
131
150
  * its size, where they changed. At its size and from the top left, as a
@@ -138,7 +157,8 @@ class PaneLayer {
138
157
  * the frame is stretched, and down it is still at its size
139
158
  * (`_anchoredNow`): the whole of each row stretched to the new width,
140
159
  * cropped or with its last row carried down. Both rects are the whole
141
- * when the frame is the layer's size, and at a budget of 0.
160
+ * when the frame is the layer's size, and where it is stretched always
161
+ * (`_stretch`).
142
162
  *
143
163
  * Anchored by gravity alone, the strip the frame did not cover showed
144
164
  * whatever was under the pane, which is the `<Frame>`'s background: in
@@ -159,8 +179,7 @@ class PaneLayer {
159
179
  const rect = this._rect;
160
180
  let crop = null;
161
181
  let edge = null;
162
- const budget = this.app._resizeWait ?? RESIZE_WAIT_MS;
163
- if (frame && rect && budget > 0) {
182
+ if (frame && rect && this._stretch() !== 'always') {
164
183
  // across at its size while the pane keeps up, and down always; an
165
184
  // axis stretched is shown whole, the frame's every column across
166
185
  const across = this._anchoredNow();
@@ -520,7 +520,9 @@ export class CocoaWindow {
520
520
  if (this.destroyed) return null;
521
521
  const began = this._native.beginDrag(
522
522
  this._h,
523
- dragSpec(session, this._native, this.scale),
523
+ dragSpec(session, this._native, this.scale, {
524
+ upFront: this.app._threaded,
525
+ }),
524
526
  );
525
527
  this.app._afterInput();
526
528
  return began;
package/src/index.d.ts CHANGED
@@ -306,6 +306,11 @@ export interface RootOptions {
306
306
  * the next comes, and past it, it is stretched across to the pane's new
307
307
  * width and stays at its size down it; at 0 every one is stretched both
308
308
  * ways.
309
+ * `paneStretch` says when a `<Frame>` pane's last frame is stretched
310
+ * instead, whatever `resizeWait` is: `'behind'` (the default) as above,
311
+ * `'never'` keeps it at its size however late the pane's next frame is,
312
+ * and `'always'` stretches it both ways. `REACT_X11_COCOA_PANE_STRETCH`
313
+ * says the same from the environment.
309
314
  * `screenPoll` is how often the screen layout is re-read while a
310
315
  * {@link useScreens} subscriber is mounted, in ms — 500 by default, 0 for
311
316
  * never. macOS has no event for a display plugged in, unplugged or
@@ -333,6 +338,7 @@ export interface RootOptions {
333
338
  frameInterval?: number;
334
339
  pumpInterval?: number;
335
340
  resizeWait?: number;
341
+ paneStretch?: 'behind' | 'never' | 'always';
336
342
  screenPoll?: number;
337
343
  appName?: string;
338
344
  activationPolicy?: 'regular' | 'accessory' | 'prohibited';