@xiboplayer/renderer 0.7.0 → 0.7.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/package.json +4 -5
- package/src/index.d.ts +1 -0
- package/src/renderer-lite.js +23 -11
- package/src/renderer-lite.overlays.test.js +18 -9
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xiboplayer/renderer",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.1",
|
|
4
4
|
"description": "RendererLite - Fast, efficient XLF layout rendering engine",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.js",
|
|
@@ -11,11 +11,10 @@
|
|
|
11
11
|
"./layout": "./src/layout.js"
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"nanoevents": "^9.1.0",
|
|
15
14
|
"pdfjs-dist": "^4.10.38",
|
|
16
|
-
"@xiboplayer/cache": "0.7.
|
|
17
|
-
"@xiboplayer/
|
|
18
|
-
"@xiboplayer/
|
|
15
|
+
"@xiboplayer/cache": "0.7.1",
|
|
16
|
+
"@xiboplayer/schedule": "0.7.1",
|
|
17
|
+
"@xiboplayer/utils": "0.7.1"
|
|
19
18
|
},
|
|
20
19
|
"devDependencies": {
|
|
21
20
|
"vitest": "^2.0.0",
|
package/src/index.d.ts
CHANGED
package/src/renderer-lite.js
CHANGED
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
* ```
|
|
42
42
|
*/
|
|
43
43
|
|
|
44
|
-
import {
|
|
44
|
+
import { EventEmitter } from '@xiboplayer/utils';
|
|
45
45
|
import { createLogger, isDebug, PLAYER_API } from '@xiboplayer/utils';
|
|
46
46
|
import { parseLayoutDuration } from '@xiboplayer/schedule';
|
|
47
47
|
import { LayoutPool } from './layout-pool.js';
|
|
@@ -205,7 +205,7 @@ export class RendererLite {
|
|
|
205
205
|
this.log = createLogger('RendererLite', options.logLevel);
|
|
206
206
|
|
|
207
207
|
// Event emitter for lifecycle hooks
|
|
208
|
-
this.emitter =
|
|
208
|
+
this.emitter = new EventEmitter();
|
|
209
209
|
|
|
210
210
|
// State
|
|
211
211
|
this.currentLayout = null;
|
|
@@ -223,6 +223,10 @@ export class RendererLite {
|
|
|
223
223
|
this.layoutBlobUrls = new Map(); // layoutId => Set<blobUrl> (for lifecycle tracking)
|
|
224
224
|
this.audioOverlays = new Map(); // widgetId => [HTMLAudioElement] (audio overlays for widgets)
|
|
225
225
|
|
|
226
|
+
// Bound methods (avoid lambda allocation per call in startRegion/_advanceRegion)
|
|
227
|
+
this._stopWidgetBound = (rid, idx) => this.stopWidget(rid, idx);
|
|
228
|
+
this._renderWidgetBound = (rid, idx) => this.renderWidget(rid, idx);
|
|
229
|
+
|
|
226
230
|
// Scale state (for fitting layout to screen)
|
|
227
231
|
this.scaleFactor = 1;
|
|
228
232
|
this.offsetX = 0;
|
|
@@ -356,7 +360,7 @@ export class RendererLite {
|
|
|
356
360
|
* Event emitter interface (like XMR wrapper)
|
|
357
361
|
*/
|
|
358
362
|
on(event, callback) {
|
|
359
|
-
|
|
363
|
+
this.emitter.on(event, callback);
|
|
360
364
|
}
|
|
361
365
|
|
|
362
366
|
emit(event, ...args) {
|
|
@@ -953,8 +957,8 @@ export class RendererLite {
|
|
|
953
957
|
const isMain = regionMap === this.regions;
|
|
954
958
|
this._startRegionCycle(
|
|
955
959
|
region, regionId,
|
|
956
|
-
isMain ?
|
|
957
|
-
isMain ?
|
|
960
|
+
isMain ? this._renderWidgetBound : this._renderWidgetBound,
|
|
961
|
+
isMain ? this._stopWidgetBound : this._stopWidgetBound,
|
|
958
962
|
isMain ? () => this.checkLayoutComplete() : undefined
|
|
959
963
|
);
|
|
960
964
|
}
|
|
@@ -1200,7 +1204,7 @@ export class RendererLite {
|
|
|
1200
1204
|
|
|
1201
1205
|
// Stop all region timers and widgets, then reset to first widget
|
|
1202
1206
|
this._clearRegionTimers(this.regions);
|
|
1203
|
-
this._stopAllRegionWidgets(this.regions,
|
|
1207
|
+
this._stopAllRegionWidgets(this.regions, this._stopWidgetBound);
|
|
1204
1208
|
for (const [, region] of this.regions) {
|
|
1205
1209
|
region.currentIndex = 0;
|
|
1206
1210
|
region.complete = false;
|
|
@@ -1389,8 +1393,8 @@ export class RendererLite {
|
|
|
1389
1393
|
const region = this.regions.get(regionId);
|
|
1390
1394
|
this._startRegionCycle(
|
|
1391
1395
|
region, regionId,
|
|
1392
|
-
|
|
1393
|
-
|
|
1396
|
+
this._renderWidgetBound,
|
|
1397
|
+
this._stopWidgetBound,
|
|
1394
1398
|
() => {
|
|
1395
1399
|
this.log.info(`Region ${regionId} completed one full cycle`);
|
|
1396
1400
|
this.checkLayoutComplete();
|
|
@@ -3011,7 +3015,7 @@ export class RendererLite {
|
|
|
3011
3015
|
if (oldLayoutId && this.layoutPool.has(oldLayoutId)) {
|
|
3012
3016
|
// Stop all widgets before evicting (symmetric widgetEnd events)
|
|
3013
3017
|
this._clearRegionTimers(this.regions);
|
|
3014
|
-
this._stopAllRegionWidgets(this.regions,
|
|
3018
|
+
this._stopAllRegionWidgets(this.regions, this._stopWidgetBound);
|
|
3015
3019
|
// Old layout was preloaded — evict from pool (safe: removes its wrapper div)
|
|
3016
3020
|
this.layoutPool.evict(oldLayoutId);
|
|
3017
3021
|
} else {
|
|
@@ -3019,7 +3023,7 @@ export class RendererLite {
|
|
|
3019
3023
|
// Region elements live directly in this.container (not a wrapper),
|
|
3020
3024
|
// so we must remove them individually.
|
|
3021
3025
|
this._clearRegionTimers(this.regions);
|
|
3022
|
-
this._stopAllRegionWidgets(this.regions,
|
|
3026
|
+
this._stopAllRegionWidgets(this.regions, this._stopWidgetBound);
|
|
3023
3027
|
for (const [, region] of this.regions) {
|
|
3024
3028
|
// Release video/audio resources before removing from DOM
|
|
3025
3029
|
LayoutPool.releaseMediaElements(region.element);
|
|
@@ -3112,6 +3116,14 @@ export class RendererLite {
|
|
|
3112
3116
|
this.log.info(`Swapped to preloaded layout ${layoutId} (instant transition)`);
|
|
3113
3117
|
}
|
|
3114
3118
|
|
|
3119
|
+
/**
|
|
3120
|
+
* Get the currently showing layout ID.
|
|
3121
|
+
* @returns {number|null}
|
|
3122
|
+
*/
|
|
3123
|
+
getCurrentLayoutId() {
|
|
3124
|
+
return this.currentLayoutId;
|
|
3125
|
+
}
|
|
3126
|
+
|
|
3115
3127
|
/**
|
|
3116
3128
|
* Show a preloaded layout (swap from pool to visible).
|
|
3117
3129
|
* If no layoutId, shows the most recently preloaded layout.
|
|
@@ -3212,7 +3224,7 @@ export class RendererLite {
|
|
|
3212
3224
|
|
|
3213
3225
|
// Stop all regions — use helper to stop ALL started widgets (canvas fix)
|
|
3214
3226
|
this._clearRegionTimers(this.regions);
|
|
3215
|
-
this._stopAllRegionWidgets(this.regions,
|
|
3227
|
+
this._stopAllRegionWidgets(this.regions, this._stopWidgetBound);
|
|
3216
3228
|
for (const [, region] of this.regions) {
|
|
3217
3229
|
// Release video/audio resources before removing from DOM
|
|
3218
3230
|
LayoutPool.releaseMediaElements(region.element);
|
|
@@ -9,15 +9,24 @@
|
|
|
9
9
|
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
10
10
|
import { RendererLite } from './renderer-lite.js';
|
|
11
11
|
|
|
12
|
-
// Mock logger
|
|
13
|
-
vi.mock('@xiboplayer/utils', () =>
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
20
|
-
|
|
12
|
+
// Mock logger (keep real EventEmitter for RendererLite construction)
|
|
13
|
+
vi.mock('@xiboplayer/utils', () => {
|
|
14
|
+
// Inline minimal EventEmitter — avoids async importActual which breaks vitest mock hoisting
|
|
15
|
+
class EventEmitter {
|
|
16
|
+
constructor() { this._listeners = new Map(); }
|
|
17
|
+
on(event, cb) { if (!this._listeners.has(event)) this._listeners.set(event, []); this._listeners.get(event).push(cb); }
|
|
18
|
+
emit(event, ...args) { for (const cb of this._listeners.get(event) || []) cb(...args); }
|
|
19
|
+
}
|
|
20
|
+
return {
|
|
21
|
+
EventEmitter,
|
|
22
|
+
createLogger: () => ({
|
|
23
|
+
info: vi.fn(),
|
|
24
|
+
warn: vi.fn(),
|
|
25
|
+
error: vi.fn(),
|
|
26
|
+
debug: vi.fn()
|
|
27
|
+
})
|
|
28
|
+
};
|
|
29
|
+
});
|
|
21
30
|
|
|
22
31
|
describe('RendererLite - Overlay Rendering', () => {
|
|
23
32
|
let renderer;
|