@nativescript/cloudflare-desktop 0.1.0-alpha.1 → 0.1.0-alpha.2

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 CHANGED
@@ -30,7 +30,7 @@ AppKit holds control targets and delegates weakly. Keep them referenced from Jav
30
30
  - **Code.** The host joins the project's socket as `platform: 'macos'`, walks each new entry's module graph over HTTP and serves it to Node through `module.registerHooks`. Module URLs are versioned, so only changed modules are fetched. Node can't unload old versions; they stay in memory until the host restarts.
31
31
  - **Event loop.** Cocoa events are pumped from Node's timers, so sockets and `fetch` keep working.
32
32
  - **Memory.** Each frame's native objects are released as soon as it's sent (`interop.releaseNativeObject`, NativeScript/runtimes#103); runtimes without it get a garbage collection every few frames instead.
33
- - **Stream.** While a viewer watches, ScreenCaptureKit captures the project's window (only that window, through the current-process listing, macOS 14.4+) at up to 30 fps and at the window's point size (at most 800 px wide); frames go out as JPEG over `/p/<id>/stream/<stream>`, a relay separate from the project's hub, in chunks of at most 16 KB under a budget of about 4.8 Mbps with adaptive quality. Cloudflare cuts a Durable Object's WebSocket when messages are large or one Mac streams two windows at once, so the IDE only watches a window while its pane is on screen.
33
+ - **Stream.** While a viewer watches, ScreenCaptureKit captures the project's window (only that window, through the current-process listing, macOS 14.4+) at up to 30 fps and at the window's point size (at most 800 px wide); frames go out as JPEG over `/p/<id>/stream/<stream>`, a relay separate from the project's hub, in chunks of at most 16 KB under a budget of about 4.8 Mbps with adaptive quality. AppKit can repaint a still window many times a second, so a frame identical to the last one sent is skipped. Cloudflare cuts a Durable Object's WebSocket when messages are large or one Mac streams two windows at once, so the IDE only watches a window while its pane is on screen.
34
34
  - **Input.** Pointer, wheel and key events from the viewer are posted to the app's own event queue, so they reach only this app, never move the real cursor and need no Accessibility permission. A press is held until its release arrives, because a control's tracking loop blocks the JS thread in between: drags land on release, and press-and-hold isn't possible yet.
35
35
 
36
36
  ## Publishing
package/dist/stream.d.ts CHANGED
@@ -18,6 +18,8 @@ export declare class WindowStreamer {
18
18
  private budget;
19
19
  private budgetAt;
20
20
  private framed;
21
+ /** The last JPEG sent; repeats are skipped until a viewer joins. */
22
+ private sent?;
21
23
  private backoff;
22
24
  private closed;
23
25
  private timer?;
package/dist/stream.js CHANGED
@@ -87,6 +87,8 @@ export class WindowStreamer {
87
87
  budget = BYTES_PER_SECOND;
88
88
  budgetAt = 0;
89
89
  framed = false;
90
+ /** The last JPEG sent; repeats are skipped until a viewer joins. */
91
+ sent;
90
92
  backoff = 500;
91
93
  closed = false;
92
94
  timer;
@@ -112,6 +114,8 @@ export class WindowStreamer {
112
114
  return;
113
115
  }
114
116
  if (msg.type === 'viewers') {
117
+ if (msg.count > this.viewers)
118
+ this.sent = undefined;
115
119
  this.viewers = msg.count;
116
120
  void this.sync();
117
121
  }
@@ -274,6 +278,11 @@ export class WindowStreamer {
274
278
  held.push(jpeg);
275
279
  // Chunks are copies: the bytes are backed by the NSData, which is released right after.
276
280
  const bytes = new Uint8Array(interop.bufferFromData(jpeg));
281
+ // AppKit can repaint a still window many times a second, and ScreenCaptureKit
282
+ // reports each repaint as a complete frame with the whole window dirty.
283
+ if (this.sent && sameBytes(this.sent, bytes))
284
+ return;
285
+ this.sent = bytes;
277
286
  for (let at = 0; at < bytes.byteLength; at += FRAME_CHUNK_BYTES) {
278
287
  const payload = bytes.subarray(at, at + FRAME_CHUNK_BYTES);
279
288
  const chunk = new Uint8Array(payload.byteLength + 1);
@@ -318,6 +327,14 @@ export class WindowStreamer {
318
327
  return resized;
319
328
  }
320
329
  }
330
+ function sameBytes(a, b) {
331
+ if (a.byteLength !== b.byteLength)
332
+ return false;
333
+ for (let i = 0; i < a.byteLength; i++)
334
+ if (a[i] !== b[i])
335
+ return false;
336
+ return true;
337
+ }
321
338
  /**
322
339
  * Output pixels: the window's size in points, not its Retina backing size.
323
340
  * Viewers show it in a side pane, and 2x frames are four times the bytes,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nativescript/cloudflare-desktop",
3
- "version": "0.1.0-alpha.1",
3
+ "version": "0.1.0-alpha.2",
4
4
  "description": "Runs an ns-edge project as a macOS desktop app on the NativeScript Node-API runtime, hot-reloaded from the edge and streamed to the browser IDE",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",