@napolab/texture-bridge-renderer 0.4.1 → 0.5.1

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/dist/index.cjs CHANGED
@@ -121,6 +121,11 @@ var FpsCounter = class {
121
121
  this.count = 0;
122
122
  this.lastTime = Date.now();
123
123
  }
124
+ /** Reset the counter, discarding any accumulated frames and time. */
125
+ reset() {
126
+ this.count = 0;
127
+ this.lastTime = Date.now();
128
+ }
124
129
  /** Call on every frame. Returns FPS when 1 second has elapsed, otherwise null. */
125
130
  tick() {
126
131
  this.count++;
@@ -238,4 +243,112 @@ async function createTextureBridge(options) {
238
243
  }
239
244
 
240
245
  //#endregion
241
- exports.createTextureBridge = createTextureBridge;
246
+ //#region src/receiver.ts
247
+ var TextureReceiverBridgeImpl = class extends events.EventEmitter {
248
+ constructor(receiver, pollIntervalMs) {
249
+ super();
250
+ this.fpsCounter = new FpsCounter();
251
+ this._disposed = false;
252
+ this._timer = null;
253
+ this.receiver = receiver;
254
+ this.pollIntervalMs = pollIntervalMs;
255
+ }
256
+ get isDisposed() {
257
+ return this._disposed;
258
+ }
259
+ start() {
260
+ if (this._disposed || this._timer) return;
261
+ this.fpsCounter.reset();
262
+ this._timer = setInterval(() => this._poll(), this.pollIntervalMs);
263
+ }
264
+ stop() {
265
+ if (this._timer) {
266
+ clearInterval(this._timer);
267
+ this._timer = null;
268
+ }
269
+ }
270
+ dispose() {
271
+ if (this._disposed) return;
272
+ this._disposed = true;
273
+ this.stop();
274
+ this.receiver.stop();
275
+ this.emit("disposed");
276
+ this.removeAllListeners();
277
+ }
278
+ _poll() {
279
+ if (this._disposed) return;
280
+ try {
281
+ if (!this.receiver.hasNewFrame()) return;
282
+ const frame = this.receiver.receiveFrame();
283
+ if (!frame) return;
284
+ this.emit("frame", frame);
285
+ const fps = this.fpsCounter.tick();
286
+ if (fps !== null) this.emit("fps", fps);
287
+ } catch (err) {
288
+ const error = err instanceof Error ? err : new Error(String(err));
289
+ this.emit("error", error);
290
+ }
291
+ }
292
+ };
293
+ function createTextureReceiver(options) {
294
+ const { senderName, appName, serverUuid, pollIntervalMs = 16 } = options;
295
+ return new TextureReceiverBridgeImpl(new _napolab_texture_bridge_core.TextureReceiver(senderName, appName, serverUuid), pollIntervalMs);
296
+ }
297
+
298
+ //#endregion
299
+ //#region src/discovery.ts
300
+ var SenderDiscovery = class extends events.EventEmitter {
301
+ constructor(..._args) {
302
+ super(..._args);
303
+ this._senders = [];
304
+ this._timer = null;
305
+ this._disposed = false;
306
+ }
307
+ get isDisposed() {
308
+ return this._disposed;
309
+ }
310
+ start(intervalMs = 1e3) {
311
+ if (this._disposed || this._timer) return;
312
+ this._timer = setInterval(() => this._refresh(), intervalMs);
313
+ }
314
+ stop() {
315
+ if (this._timer) {
316
+ clearInterval(this._timer);
317
+ this._timer = null;
318
+ }
319
+ }
320
+ getSenders() {
321
+ return [...this._senders];
322
+ }
323
+ dispose() {
324
+ if (this._disposed) return;
325
+ this._disposed = true;
326
+ this.stop();
327
+ this.removeAllListeners();
328
+ }
329
+ _refresh() {
330
+ if (this._disposed) return;
331
+ try {
332
+ const current = (0, _napolab_texture_bridge_core.listSenders)();
333
+ const prev = this._senders;
334
+ const added = current.filter((c) => !prev.some((p) => this._isSame(c, p)));
335
+ const removed = prev.filter((p) => !current.some((c) => this._isSame(c, p)));
336
+ this._senders = current;
337
+ if (added.length > 0) this.emit("added", added);
338
+ if (removed.length > 0) this.emit("removed", removed);
339
+ if (added.length > 0 || removed.length > 0) this.emit("updated", current);
340
+ } catch (err) {
341
+ const error = err instanceof Error ? err : new Error(String(err));
342
+ this.emit("error", error);
343
+ }
344
+ }
345
+ _isSame(a, b) {
346
+ if (a.uuid && b.uuid) return a.uuid === b.uuid;
347
+ return a.name === b.name && a.appName === b.appName;
348
+ }
349
+ };
350
+
351
+ //#endregion
352
+ exports.SenderDiscovery = SenderDiscovery;
353
+ exports.createTextureBridge = createTextureBridge;
354
+ exports.createTextureReceiver = createTextureReceiver;
package/dist/index.d.cts CHANGED
@@ -1,4 +1,6 @@
1
1
  import { BrowserWindow } from "electron";
2
+ import { ReceivedFrame, SenderInfo } from "@napolab/texture-bridge-core";
3
+ import { EventEmitter } from "events";
2
4
 
3
5
  //#region src/types.d.ts
4
6
  /** Options for the preview window */
@@ -63,4 +65,48 @@ interface TextureBridge {
63
65
  */
64
66
  declare function createTextureBridge(options: TextureBridgeOptions): Promise<TextureBridge>;
65
67
  //#endregion
66
- export { type BridgeEvents, type PreviewOptions, type TextureBridge, type TextureBridgeOptions, createTextureBridge };
68
+ //#region src/receiver.d.ts
69
+ interface TextureReceiverBridgeOptions {
70
+ senderName: string;
71
+ appName?: string;
72
+ serverUuid?: string;
73
+ pollIntervalMs?: number;
74
+ }
75
+ interface ReceiverBridgeEvents {
76
+ frame: [frame: ReceivedFrame];
77
+ fps: [fps: number];
78
+ error: [error: Error];
79
+ disposed: [];
80
+ }
81
+ interface TextureReceiverBridge {
82
+ on<K extends keyof ReceiverBridgeEvents>(event: K, listener: (...args: ReceiverBridgeEvents[K]) => void): this;
83
+ off<K extends keyof ReceiverBridgeEvents>(event: K, listener: (...args: ReceiverBridgeEvents[K]) => void): this;
84
+ once<K extends keyof ReceiverBridgeEvents>(event: K, listener: (...args: ReceiverBridgeEvents[K]) => void): this;
85
+ start(): void;
86
+ stop(): void;
87
+ dispose(): void;
88
+ readonly isDisposed: boolean;
89
+ }
90
+ declare function createTextureReceiver(options: TextureReceiverBridgeOptions): TextureReceiverBridge;
91
+ //#endregion
92
+ //#region src/discovery.d.ts
93
+ interface SenderDiscoveryEvents {
94
+ updated: [senders: SenderInfo[]];
95
+ added: [senders: SenderInfo[]];
96
+ removed: [senders: SenderInfo[]];
97
+ error: [error: Error];
98
+ }
99
+ declare class SenderDiscovery extends EventEmitter {
100
+ private _senders;
101
+ private _timer;
102
+ private _disposed;
103
+ get isDisposed(): boolean;
104
+ start(intervalMs?: number): void;
105
+ stop(): void;
106
+ getSenders(): SenderInfo[];
107
+ dispose(): void;
108
+ private _refresh;
109
+ private _isSame;
110
+ }
111
+ //#endregion
112
+ export { type BridgeEvents, type PreviewOptions, type ReceiverBridgeEvents, SenderDiscovery, type SenderDiscoveryEvents, type TextureBridge, type TextureBridgeOptions, type TextureReceiverBridge, type TextureReceiverBridgeOptions, createTextureBridge, createTextureReceiver };
package/dist/index.d.mts CHANGED
@@ -1,4 +1,6 @@
1
+ import { EventEmitter } from "events";
1
2
  import { BrowserWindow } from "electron";
3
+ import { ReceivedFrame, SenderInfo } from "@napolab/texture-bridge-core";
2
4
 
3
5
  //#region src/types.d.ts
4
6
  /** Options for the preview window */
@@ -63,4 +65,48 @@ interface TextureBridge {
63
65
  */
64
66
  declare function createTextureBridge(options: TextureBridgeOptions): Promise<TextureBridge>;
65
67
  //#endregion
66
- export { type BridgeEvents, type PreviewOptions, type TextureBridge, type TextureBridgeOptions, createTextureBridge };
68
+ //#region src/receiver.d.ts
69
+ interface TextureReceiverBridgeOptions {
70
+ senderName: string;
71
+ appName?: string;
72
+ serverUuid?: string;
73
+ pollIntervalMs?: number;
74
+ }
75
+ interface ReceiverBridgeEvents {
76
+ frame: [frame: ReceivedFrame];
77
+ fps: [fps: number];
78
+ error: [error: Error];
79
+ disposed: [];
80
+ }
81
+ interface TextureReceiverBridge {
82
+ on<K extends keyof ReceiverBridgeEvents>(event: K, listener: (...args: ReceiverBridgeEvents[K]) => void): this;
83
+ off<K extends keyof ReceiverBridgeEvents>(event: K, listener: (...args: ReceiverBridgeEvents[K]) => void): this;
84
+ once<K extends keyof ReceiverBridgeEvents>(event: K, listener: (...args: ReceiverBridgeEvents[K]) => void): this;
85
+ start(): void;
86
+ stop(): void;
87
+ dispose(): void;
88
+ readonly isDisposed: boolean;
89
+ }
90
+ declare function createTextureReceiver(options: TextureReceiverBridgeOptions): TextureReceiverBridge;
91
+ //#endregion
92
+ //#region src/discovery.d.ts
93
+ interface SenderDiscoveryEvents {
94
+ updated: [senders: SenderInfo[]];
95
+ added: [senders: SenderInfo[]];
96
+ removed: [senders: SenderInfo[]];
97
+ error: [error: Error];
98
+ }
99
+ declare class SenderDiscovery extends EventEmitter {
100
+ private _senders;
101
+ private _timer;
102
+ private _disposed;
103
+ get isDisposed(): boolean;
104
+ start(intervalMs?: number): void;
105
+ stop(): void;
106
+ getSenders(): SenderInfo[];
107
+ dispose(): void;
108
+ private _refresh;
109
+ private _isSame;
110
+ }
111
+ //#endregion
112
+ export { type BridgeEvents, type PreviewOptions, type ReceiverBridgeEvents, SenderDiscovery, type SenderDiscoveryEvents, type TextureBridge, type TextureBridgeOptions, type TextureReceiverBridge, type TextureReceiverBridgeOptions, createTextureBridge, createTextureReceiver };
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { EventEmitter } from "events";
2
2
  import { BrowserWindow, app, ipcMain, sharedTexture } from "electron";
3
- import { TextureSender, sendTextureFromPaintEvent } from "@napolab/texture-bridge-core";
3
+ import { TextureReceiver, TextureSender, listSenders, sendTextureFromPaintEvent } from "@napolab/texture-bridge-core";
4
4
  import path from "path";
5
5
 
6
6
  //#region src/preview-manager.ts
@@ -92,6 +92,11 @@ var FpsCounter = class {
92
92
  this.count = 0;
93
93
  this.lastTime = Date.now();
94
94
  }
95
+ /** Reset the counter, discarding any accumulated frames and time. */
96
+ reset() {
97
+ this.count = 0;
98
+ this.lastTime = Date.now();
99
+ }
95
100
  /** Call on every frame. Returns FPS when 1 second has elapsed, otherwise null. */
96
101
  tick() {
97
102
  this.count++;
@@ -209,4 +214,110 @@ async function createTextureBridge(options) {
209
214
  }
210
215
 
211
216
  //#endregion
212
- export { createTextureBridge };
217
+ //#region src/receiver.ts
218
+ var TextureReceiverBridgeImpl = class extends EventEmitter {
219
+ constructor(receiver, pollIntervalMs) {
220
+ super();
221
+ this.fpsCounter = new FpsCounter();
222
+ this._disposed = false;
223
+ this._timer = null;
224
+ this.receiver = receiver;
225
+ this.pollIntervalMs = pollIntervalMs;
226
+ }
227
+ get isDisposed() {
228
+ return this._disposed;
229
+ }
230
+ start() {
231
+ if (this._disposed || this._timer) return;
232
+ this.fpsCounter.reset();
233
+ this._timer = setInterval(() => this._poll(), this.pollIntervalMs);
234
+ }
235
+ stop() {
236
+ if (this._timer) {
237
+ clearInterval(this._timer);
238
+ this._timer = null;
239
+ }
240
+ }
241
+ dispose() {
242
+ if (this._disposed) return;
243
+ this._disposed = true;
244
+ this.stop();
245
+ this.receiver.stop();
246
+ this.emit("disposed");
247
+ this.removeAllListeners();
248
+ }
249
+ _poll() {
250
+ if (this._disposed) return;
251
+ try {
252
+ if (!this.receiver.hasNewFrame()) return;
253
+ const frame = this.receiver.receiveFrame();
254
+ if (!frame) return;
255
+ this.emit("frame", frame);
256
+ const fps = this.fpsCounter.tick();
257
+ if (fps !== null) this.emit("fps", fps);
258
+ } catch (err) {
259
+ const error = err instanceof Error ? err : new Error(String(err));
260
+ this.emit("error", error);
261
+ }
262
+ }
263
+ };
264
+ function createTextureReceiver(options) {
265
+ const { senderName, appName, serverUuid, pollIntervalMs = 16 } = options;
266
+ return new TextureReceiverBridgeImpl(new TextureReceiver(senderName, appName, serverUuid), pollIntervalMs);
267
+ }
268
+
269
+ //#endregion
270
+ //#region src/discovery.ts
271
+ var SenderDiscovery = class extends EventEmitter {
272
+ constructor(..._args) {
273
+ super(..._args);
274
+ this._senders = [];
275
+ this._timer = null;
276
+ this._disposed = false;
277
+ }
278
+ get isDisposed() {
279
+ return this._disposed;
280
+ }
281
+ start(intervalMs = 1e3) {
282
+ if (this._disposed || this._timer) return;
283
+ this._timer = setInterval(() => this._refresh(), intervalMs);
284
+ }
285
+ stop() {
286
+ if (this._timer) {
287
+ clearInterval(this._timer);
288
+ this._timer = null;
289
+ }
290
+ }
291
+ getSenders() {
292
+ return [...this._senders];
293
+ }
294
+ dispose() {
295
+ if (this._disposed) return;
296
+ this._disposed = true;
297
+ this.stop();
298
+ this.removeAllListeners();
299
+ }
300
+ _refresh() {
301
+ if (this._disposed) return;
302
+ try {
303
+ const current = listSenders();
304
+ const prev = this._senders;
305
+ const added = current.filter((c) => !prev.some((p) => this._isSame(c, p)));
306
+ const removed = prev.filter((p) => !current.some((c) => this._isSame(c, p)));
307
+ this._senders = current;
308
+ if (added.length > 0) this.emit("added", added);
309
+ if (removed.length > 0) this.emit("removed", removed);
310
+ if (added.length > 0 || removed.length > 0) this.emit("updated", current);
311
+ } catch (err) {
312
+ const error = err instanceof Error ? err : new Error(String(err));
313
+ this.emit("error", error);
314
+ }
315
+ }
316
+ _isSame(a, b) {
317
+ if (a.uuid && b.uuid) return a.uuid === b.uuid;
318
+ return a.name === b.name && a.appName === b.appName;
319
+ }
320
+ };
321
+
322
+ //#endregion
323
+ export { SenderDiscovery, createTextureBridge, createTextureReceiver };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@napolab/texture-bridge-renderer",
3
- "version": "0.4.1",
3
+ "version": "0.5.1",
4
4
  "description": "High-level factory API for GPU texture bridge (BrowserWindow + Preview + Sender)",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.mjs",
@@ -25,7 +25,7 @@
25
25
  "./package.json": "./package.json"
26
26
  },
27
27
  "dependencies": {
28
- "@napolab/texture-bridge-core": "0.4.1"
28
+ "@napolab/texture-bridge-core": "0.5.1"
29
29
  },
30
30
  "peerDependencies": {
31
31
  "electron": ">=40.0.0"
@@ -53,6 +53,7 @@
53
53
  ],
54
54
  "scripts": {
55
55
  "build": "tsdown src/index.ts src/client/index.ts src/client/worker-protocol.ts --format cjs,esm --dts && cp -r src/assets dist/assets",
56
+ "test": "vitest run",
56
57
  "typecheck": "tsgo --noEmit"
57
58
  }
58
59
  }