@3dsource/angular-unreal-module 0.0.112 → 0.0.113-dev.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.
@@ -1,17 +1,17 @@
1
1
  import * as i0 from '@angular/core';
2
- import { InjectionToken, inject, ChangeDetectionStrategy, Component, Injectable, DestroyRef, effect, untracked, signal, Pipe, input, ElementRef, viewChild, afterNextRender, HostListener, computed, output, makeEnvironmentProviders, provideEnvironmentInitializer } from '@angular/core';
2
+ import { InjectionToken, inject, ChangeDetectionStrategy, Component, Injectable, DestroyRef, effect, untracked, signal, computed, Pipe, input, ElementRef, viewChild, afterNextRender, HostListener, output, makeEnvironmentProviders, provideEnvironmentInitializer } from '@angular/core';
3
3
  import { SourceButtonComponent, SourceLoadingComponent } from '@3dsource/source-ui-native';
4
- import { merge, map, scan, concatMap, of, animationFrameScheduler, interval, Observable, debounceTime, switchMap, take, fromEvent, EMPTY, timer, timeout, mergeMap, Subject, skip, share, from, throwError, defer, retry, tap as tap$1, startWith, takeUntil as takeUntil$1, throttleTime, asyncScheduler, filter as filter$1, withLatestFrom as withLatestFrom$1, pairwise, Subscription, bufferTime, BehaviorSubject, combineLatestWith, distinctUntilChanged as distinctUntilChanged$1, concat } from 'rxjs';
4
+ import { merge, map, scan, concatMap, of, animationFrameScheduler, interval, Observable, skip, switchMap, concat, delay, debounceTime, timer, take, timeout, mergeMap, EMPTY, Subject, share, from, fromEvent, throwError, defer, retry, tap as tap$1, takeUntil as takeUntil$1, NEVER, startWith, throttleTime, asyncScheduler, filter as filter$1, withLatestFrom as withLatestFrom$1, pairwise, Subscription, bufferTime, combineLatestWith, distinctUntilChanged as distinctUntilChanged$1 } from 'rxjs';
5
5
  import { createAction, props, createReducer, on, createFeature, createSelector, Store, provideState } from '@ngrx/store';
6
6
  import { Actions, createEffect, ofType, provideEffects } from '@ngrx/effects';
7
- import { map as map$1, distinctUntilChanged, takeWhile, filter, tap, withLatestFrom, exhaustMap, catchError, takeUntil, switchMap as switchMap$1, timeout as timeout$1, debounceTime as debounceTime$1, take as take$1, startWith as startWith$1, delay, skip as skip$1 } from 'rxjs/operators';
8
- import { Logger, calculateMedian, clampf, lerp, getCanvasCached, getSnapshot, fitIntoRectangle, whereNot, Truthy, Falsy, tapLog, generateUuid, COLOR_CODES, where, KeyboardNumericCode, InvertedKeyMap, Semaphore, HEXtoRGB, RGBtoHSV, inverseLerp, HSVtoRGB, RGBtoHEX, fpIsASameAsB } from '@3dsource/utils';
7
+ import { map as map$1, distinctUntilChanged, takeWhile, filter, tap, takeUntil, catchError, withLatestFrom, exhaustMap, switchMap as switchMap$1, timeout as timeout$1, debounceTime as debounceTime$1, take as take$1, startWith as startWith$1 } from 'rxjs/operators';
8
+ import { Logger, calculateMedian, clampf, lerp, getCanvasCached, getSnapshot, fitIntoRectangle, whereNot, Truthy, tapLog, Falsy, generateUuid, COLOR_CODES, where, KeyboardNumericCode, InvertedKeyMap, Semaphore, HEXtoRGB, RGBtoHSV, inverseLerp, HSVtoRGB, RGBtoHEX, fpIsASameAsB } from '@3dsource/utils';
9
9
  import { concatLatestFrom } from '@ngrx/operators';
10
10
  import { DialogRef, DIALOG_DATA, Dialog } from '@angular/cdk/dialog';
11
11
  import { HttpClient } from '@angular/common/http';
12
12
  import { ScrollStrategyOptions } from '@angular/cdk/overlay';
13
13
  import { MetaBoxCommand, EPayloadVersion } from '@3dsource/types-unreal';
14
- import { toSignal, takeUntilDestroyed, toObservable } from '@angular/core/rxjs-interop';
14
+ import { toSignal, toObservable, takeUntilDestroyed } from '@angular/core/rxjs-interop';
15
15
  import { v4 } from 'uuid';
16
16
  import { DomSanitizer } from '@angular/platform-browser';
17
17
  import { NgOptimizedImage, AsyncPipe, JsonPipe } from '@angular/common';
@@ -116,10 +116,92 @@ const InputOptions = {
116
116
  suppressBrowserKeys: true,
117
117
  };
118
118
 
119
+ const WSCloseCode_NORMAL_CLOSURE = 3000;
120
+ const WSCloseCode_NORMAL_AFK_TIMEOUT = 3001;
121
+ const WSCloseCode_NORMAL_MANUAL_DISCONNECT = 3002;
122
+ const WSCloseCode_FORCE_CIRRUS_CLOSE = 3003;
123
+ const WSCloseCode_UNKNOWN = 1000;
124
+ const WSCloseCode_CIRRUS_PLAYER_DISCONNECTED = 1001;
125
+ const WSCloseCode_CIRRUS_ABNORMAL_CLOSURE = 1006;
126
+ const WSCloseCode_CIRRUS_MAX_PLAYERS_ERROR = 1013;
127
+ const WSCloseCode_CIRRUS_STREAMER_KIKED_PLAYER = 1011;
128
+ const WSCloseCodes = [
129
+ WSCloseCode_NORMAL_CLOSURE,
130
+ WSCloseCode_NORMAL_AFK_TIMEOUT,
131
+ WSCloseCode_NORMAL_MANUAL_DISCONNECT,
132
+ WSCloseCode_FORCE_CIRRUS_CLOSE,
133
+ WSCloseCode_UNKNOWN,
134
+ WSCloseCode_CIRRUS_PLAYER_DISCONNECTED,
135
+ WSCloseCode_CIRRUS_ABNORMAL_CLOSURE,
136
+ WSCloseCode_CIRRUS_MAX_PLAYERS_ERROR,
137
+ WSCloseCode_CIRRUS_STREAMER_KIKED_PLAYER,
138
+ ];
139
+
140
+ const DisconnectReason = {
141
+ Afk: 'Afk',
142
+ None: 'None',
143
+ Destroy: 'Destroy',
144
+ DataChannelClosed: 'DataChannelClosed',
145
+ DataChannelTimeout: 'DataChannelTimeout',
146
+ WebRTCError: 'WebRTCError',
147
+ WebSocketError: 'WebSocketError',
148
+ WebSocketClose: 'WebSocketClose',
149
+ DropConnection: 'DropConnection',
150
+ OrchestrationPlayerDisconnected: 'OrchestrationPlayerDisconnected',
151
+ OrchestrationStreamerDisconnected: 'OrchestrationStreamerDisconnected',
152
+ IceConnectionFailed: 'IceConnectionFailed',
153
+ ReconnectFailed: 'ReconnectFailed',
154
+ NewConnectionAttempt: 'NewConnectionAttempt',
155
+ AbortEstablishingConnection: 'AbortEstablishingConnection',
156
+ };
157
+
158
+ /*
159
+ export interface RequestReservation extends InstanceMessageBase {
160
+ type: 'requestReservation';
161
+ }
162
+ */
163
+ const OrchestrationMessageTypes = {
164
+ ping: 'ping',
165
+ error: 'error',
166
+ offer: 'offer',
167
+ answer: 'answer',
168
+ ssInfo: 'ssInfo',
169
+ config: 'config',
170
+ playerCount: 'playerCount',
171
+ iceCandidate: 'iceCandidate',
172
+ streamerList: 'streamerList',
173
+ requestStream: 'requestStream',
174
+ listStreamers: 'listStreamers',
175
+ instanceReady: 'instanceReady',
176
+ p2pEstablished: 'p2pEstablished',
177
+ instanceReserved: 'instanceReserved',
178
+ playerDisconnected: 'playerDisconnected',
179
+ streamPreparedForUserInteraction: 'streamPreparedForUserInteraction',
180
+ streamerDisconnected: 'streamerDisconnected',
181
+ interruptClientStream: 'interruptClientStream',
182
+ };
183
+
184
+ /** Disconnect reasons that should trigger remote connection cleanup. */
185
+ const DESTROY_DISCONNECT_REASONS = [
186
+ DisconnectReason.Afk,
187
+ DisconnectReason.DataChannelClosed,
188
+ DisconnectReason.DataChannelTimeout,
189
+ DisconnectReason.WebSocketError,
190
+ DisconnectReason.WebSocketClose,
191
+ DisconnectReason.WebRTCError,
192
+ DisconnectReason.DropConnection,
193
+ DisconnectReason.ReconnectFailed,
194
+ DisconnectReason.OrchestrationPlayerDisconnected,
195
+ DisconnectReason.OrchestrationStreamerDisconnected,
196
+ ];
197
+
119
198
  const DEBOUNCE_TO_MANY_RESIZE_CALLS = 100;
120
199
  const SAME_SIZE_THRESHOLD = 1.01;
200
+ // Minimum container dimension in px — prevents division by zero in resize calculations
201
+ const MIN_CONTAINER_SIZE = 2;
121
202
  const MINIMAL_FPS = 6;
122
203
  const STREAMING_VIDEO_ID = 'streamingVideo';
204
+ const STREAMING_AUDIO_ID = 'streamingAudio';
123
205
  const CONSOLE_COMMAND_ENABLE_MESSAGES = 'EnableAllScreenMessages';
124
206
  const CONSOLE_COMMAND_DISABLE_MESSAGES = 'DisableAllScreenMessages';
125
207
  const CONSOLE_COMMAND_PIXEL_QUALITY = 'PixelStreaming.FreezeFrameQuality 95';
@@ -129,7 +211,7 @@ const WS_TIMEOUT = 2000;
129
211
  const POLLING_TIME = 4000;
130
212
  const WS_OPEN_STATE = 1;
131
213
  const DEFAULT_AFK_TIMEOUT_PERIOD = 15;
132
- const DEFAULT_AFK_TIMEOUT = 120;
214
+ const DEFAULT_AFK_TIMEOUT = 10;
133
215
  const DATA_CHANNEL_CONNECTION_TIMEOUT = 8000; //ms;
134
216
  const SIGNALLING_PERCENT_VALUE = 56;
135
217
  const SCREEN_LOCKER_CONTAINER_ID = '3dsource_start_screen';
@@ -152,7 +234,6 @@ const commandCompleted = createAction(scoped$1 `command completed`, props());
152
234
  const scoped = (templateString) => `[UNREAL] ${templateString[0]}`;
153
235
  const trackMixpanelEvent = createAction(scoped `track mixpanel event`, props());
154
236
  const trackTelemetryData = createAction(scoped `track telemetry data`, props());
155
- const changeLowBandwidth = createAction(scoped `change low bandwidth`, props());
156
237
  const setMaxFps = createAction(scoped `change fps`, props());
157
238
  const destroyRemoteConnections = createAction(scoped `destroyRemoteConnections`, props());
158
239
  const destroyUnrealScene = createAction(scoped `destroyUnrealScene`);
@@ -171,10 +252,6 @@ const setIntroVideoSrc = createAction(scoped `set intro video src`, props());
171
252
  const resetIntroSrc = createAction(scoped `reset intro src`);
172
253
  const setFreezeFrameFromVideo = createAction(scoped `set freeze frame from video`, props());
173
254
  const setEstablishingConnection = createAction(scoped `set establishing connection`, props());
174
- /**
175
- * @deprecated Use `dataChannelReady` instead. Currently, this action is not working.
176
- */
177
- const setDataChannelConnected = createAction(scoped `set data channel connected`, props());
178
255
  const dataChannelConnected = createAction(scoped `data channel connected`, props());
179
256
  const dataChannelReady = createAction(scoped `dataChannelReady`);
180
257
  const setConfig = createAction(scoped `set config`, props());
@@ -236,11 +313,7 @@ const setAfkTimerVisible = createAction(scoped `set afk timer visible`);
236
313
  const setAfkTimerHide = createAction(scoped `set afk timer hide`);
237
314
  const setAfkTimerCountdown = createAction(scoped `set afk timer count down`, props());
238
315
  const showUnrealErrorMessage = createAction(scoped `show unreal error message`, props());
239
- const initSignalling = createAction(scoped `init signalling`, (data = {
240
- resetDisconnectionReason: true,
241
- }) => ({
242
- resetDisconnectionReason: data.resetDisconnectionReason,
243
- }));
316
+ const initSignalling = createAction(scoped `init signalling`, (resetDisconnectionReason = true) => ({ resetDisconnectionReason }));
244
317
  const startStream = createAction(scoped `startStream`, props());
245
318
  const resetConfig = createAction(scoped `reset config`);
246
319
  const resetAfk = createAction(scoped `reset afk action`);
@@ -261,83 +334,20 @@ const iceConnectionFailed = createAction(scoped `ice connection failed`, props()
261
334
  const forceResizeViewport = createAction(scoped `force resize viewport`);
262
335
  const videoAdaptedToContainer = createAction(scoped `video adapted to container`);
263
336
  const videoStreamInitialized = createAction(scoped `video stream initialized`);
264
- const setKalmanSettings = createAction(scoped `set kalman settings`, props());
265
337
  const dropDC = createAction(scoped `drop data channel`);
266
338
  const dropPC = createAction(scoped `drop peer connection`);
267
-
268
- const WSCloseCode_NORMAL_CLOSURE = 3000;
269
- const WSCloseCode_NORMAL_AFK_TIMEOUT = 3001;
270
- const WSCloseCode_NORMAL_MANUAL_DISCONNECT = 3002;
271
- const WSCloseCode_FORCE_CIRRUS_CLOSE = 3003;
272
- const WSCloseCode_UNKNOWN = 1000;
273
- const WSCloseCode_CIRRUS_PLAYER_DISCONNECTED = 1001;
274
- const WSCloseCode_CIRRUS_ABNORMAL_CLOSURE = 1006;
275
- const WSCloseCode_CIRRUS_MAX_PLAYERS_ERROR = 1013;
276
- const WSCloseCode_CIRRUS_STREAMER_KIKED_PLAYER = 1011;
277
- const WSCloseCodes = [
278
- WSCloseCode_NORMAL_CLOSURE,
279
- WSCloseCode_NORMAL_AFK_TIMEOUT,
280
- WSCloseCode_NORMAL_MANUAL_DISCONNECT,
281
- WSCloseCode_FORCE_CIRRUS_CLOSE,
282
- WSCloseCode_UNKNOWN,
283
- WSCloseCode_CIRRUS_PLAYER_DISCONNECTED,
284
- WSCloseCode_CIRRUS_ABNORMAL_CLOSURE,
285
- WSCloseCode_CIRRUS_MAX_PLAYERS_ERROR,
286
- WSCloseCode_CIRRUS_STREAMER_KIKED_PLAYER,
287
- ];
288
-
289
- const DisconnectReason = {
290
- Afk: 'Afk',
291
- None: 'None',
292
- Destroy: 'Destroy',
293
- DataChannelClosed: 'DataChannelClosed',
294
- DataChannelTimeout: 'DataChannelTimeout',
295
- WebRTCError: 'WebRTCError',
296
- WebSocketError: 'WebSocketError',
297
- WebSocketClose: 'WebSocketClose',
298
- DropConnection: 'DropConnection',
299
- OrchestrationPlayerDisconnected: 'OrchestrationPlayerDisconnected',
300
- OrchestrationStreamerDisconnected: 'OrchestrationStreamerDisconnected',
301
- IceConnectionFailed: 'IceConnectionFailed',
302
- ReconnectFailed: 'ReconnectFailed',
303
- NewConnectionAttempt: 'NewConnectionAttempt',
304
- AbortEstablishingConnection: 'AbortEstablishingConnection',
305
- };
306
-
307
- /*
308
- export interface RequestReservation extends InstanceMessageBase {
309
- type: 'requestReservation';
310
- }
311
- */
312
- const OrchestrationMessageTypes = {
313
- ping: 'ping',
314
- error: 'error',
315
- offer: 'offer',
316
- answer: 'answer',
317
- ssInfo: 'ssInfo',
318
- config: 'config',
319
- playerCount: 'playerCount',
320
- iceCandidate: 'iceCandidate',
321
- streamerList: 'streamerList',
322
- requestStream: 'requestStream',
323
- listStreamers: 'listStreamers',
324
- instanceReady: 'instanceReady',
325
- p2pEstablished: 'p2pEstablished',
326
- instanceReserved: 'instanceReserved',
327
- playerDisconnected: 'playerDisconnected',
328
- streamPreparedForUserInteraction: 'streamPreparedForUserInteraction',
329
- streamerDisconnected: 'streamerDisconnected',
330
- interruptClientStream: 'interruptClientStream',
331
- };
339
+ const showLoadingProgress = createAction(scoped `show loading progress`, props());
340
+ const showResumeStreamPopup = createAction(scoped `show resume stream popup`, props());
341
+ const showUserInactivityTimeoutPopup = createAction(scoped `show user inactivity timeout popup`, props());
342
+ const setUserInactivityTimeout = createAction(scoped `set user inactivity timeout`, props());
343
+ const resetUserInactivityTimeout = createAction(scoped `reset user inactivity timeout`);
332
344
 
333
345
  const initialState$1 = {
334
346
  streamRequestContext: null,
335
347
  environmentId: null,
336
- lowBandwidthStats: undefined,
337
348
  isFirstSuccessLoad: false,
338
349
  isAfkTimerVisible: false,
339
350
  afkCountdown: 0,
340
- lowBandwidth: false,
341
351
  cirrusConnected: false,
342
352
  establishingConnection: false,
343
353
  isReconnecting: false,
@@ -347,7 +357,6 @@ const initialState$1 = {
347
357
  statusPercentSignallingServer: null,
348
358
  statusMessage: null,
349
359
  errorMessage: null,
350
- ssInfo: null,
351
360
  ssData: null,
352
361
  streamResolution: { width: null, height: null },
353
362
  freezeFrameFromVideo: { dataUrl: null, progress: null },
@@ -363,6 +372,7 @@ const initialState$1 = {
363
372
  autoStart: true,
364
373
  warnTimeout: DEFAULT_AFK_TIMEOUT,
365
374
  },
375
+ userInactivityTimeout: DEFAULT_AFK_TIMEOUT,
366
376
  matchUrls: [],
367
377
  streamClientCompanyId: '',
368
378
  streamViewId: 'default',
@@ -370,15 +380,23 @@ const initialState$1 = {
370
380
  imageIntroSrc: null,
371
381
  imageLoadingSrc: '',
372
382
  analyticsEvent: null,
383
+ showLoadingProgress: true,
384
+ showResumeStreamPopup: true,
385
+ showAfkTimeoutPopup: true,
373
386
  };
374
- const unrealReducer = createReducer(initialState$1, on(changeLowBandwidth, (state, { lowBandwidth, stats }) => ({
375
- ...state,
376
- lowBandwidth: lowBandwidth,
377
- lowBandwidthStats: lowBandwidth ? stats : undefined,
378
- })), on(changeStatusMainVideoOnScene, (state, { isVideoPlaying }) => {
379
- return { ...state, isVideoPlaying: isVideoPlaying };
387
+ const unrealReducer = createReducer(initialState$1, on(changeStatusMainVideoOnScene, (state, { isVideoPlaying }) => {
388
+ return { ...state, isVideoPlaying };
380
389
  }), on(setAwsInstance, (state, { instanceName, wsUrl, pollingUrl }) => {
381
- return { ...state, awsInstance: { instanceName, wsUrl, pollingUrl } };
390
+ return {
391
+ ...state,
392
+ awsInstance: {
393
+ ...state.awsInstance,
394
+ instanceName,
395
+ wsUrl,
396
+ pollingUrl,
397
+ progressComplete: 0,
398
+ },
399
+ };
382
400
  }), on(setViewportReady, (state) => {
383
401
  return {
384
402
  ...state,
@@ -388,12 +406,8 @@ const unrealReducer = createReducer(initialState$1, on(changeLowBandwidth, (stat
388
406
  };
389
407
  }), on(setViewportNotReady, (state) => {
390
408
  return { ...state, viewportReady: false };
391
- }), on(updateCirrusInfo, (state, { ssInfo, ssData }) => {
392
- return {
393
- ...state,
394
- ssInfo: ssInfo, // For back compatibility
395
- ssData: ssData, // Contains all the data from the ssInfo as an object
396
- };
409
+ }), on(updateCirrusInfo, (state, { ssData }) => {
410
+ return { ...state, ssData: ssData };
397
411
  }), on(changeStreamResolutionSuccessAction, (state, { width, height }) => {
398
412
  return { ...state, streamResolution: { width: width, height: height } };
399
413
  }), on(setFreezeFrame, (state, freezeFrame) => {
@@ -409,15 +423,11 @@ const unrealReducer = createReducer(initialState$1, on(changeLowBandwidth, (stat
409
423
  },
410
424
  };
411
425
  }), on(disconnectStream, (state, errorMessage) => {
412
- if (state.dataChannelConnected ||
413
- state.disconnectReason === DisconnectReason.Destroy) {
426
+ const alreadyHandled = state.dataChannelConnected ||
427
+ state.disconnectReason === DisconnectReason.Destroy;
428
+ if (alreadyHandled)
414
429
  return state;
415
- }
416
- return {
417
- ...state,
418
- errorMessage,
419
- statusMessage: null,
420
- };
430
+ return { ...state, errorMessage, statusMessage: null };
421
431
  }), on(setFreezeFrameFromVideo, (state, freezeFrameFromVideo) => {
422
432
  return {
423
433
  ...state,
@@ -441,12 +451,21 @@ const unrealReducer = createReducer(initialState$1, on(changeLowBandwidth, (stat
441
451
  };
442
452
  }), on(setConfig, startStream, (state, { config }) => {
443
453
  return { ...state, streamConfig: { ...state.streamConfig, ...config } };
444
- }), on(resetConfig, (state) => {
445
- return { ...state, streamConfig: initialState$1.streamConfig };
446
454
  }), on(resetWarnTimeout, (state) => {
447
455
  return {
448
456
  ...state,
449
- streamConfig: { ...state.streamConfig, warnTimeout: DEFAULT_AFK_TIMEOUT },
457
+ streamConfig: {
458
+ ...state.streamConfig,
459
+ warnTimeout: state.userInactivityTimeout,
460
+ },
461
+ };
462
+ }), on(resetConfig, (state) => {
463
+ return {
464
+ ...state,
465
+ streamConfig: {
466
+ ...initialState$1.streamConfig,
467
+ warnTimeout: state.userInactivityTimeout,
468
+ },
450
469
  };
451
470
  }), on(initSignalling, (state, { resetDisconnectionReason = true }) => {
452
471
  return {
@@ -522,6 +541,18 @@ const unrealReducer = createReducer(initialState$1, on(changeLowBandwidth, (stat
522
541
  return { ...state, analyticsEvent: event };
523
542
  }), on(setCirrusConnected, (state) => {
524
543
  return { ...state, cirrusConnected: true };
544
+ }), on(showLoadingProgress, (state, { visible }) => {
545
+ return { ...state, showLoadingProgress: visible };
546
+ }), on(showResumeStreamPopup, (state, { visible }) => {
547
+ return { ...state, showResumeStreamPopup: visible };
548
+ }), on(showUserInactivityTimeoutPopup, (state, { visible }) => {
549
+ return { ...state, showAfkTimeoutPopup: visible };
550
+ }), on(setUserInactivityTimeout, (state, { timeout }) => {
551
+ return {
552
+ ...state,
553
+ userInactivityTimeout: timeout,
554
+ streamConfig: { ...state.streamConfig, warnTimeout: timeout },
555
+ };
525
556
  }), on(setCirrusDisconnected, (state) => {
526
557
  return {
527
558
  ...initialState$1,
@@ -537,6 +568,7 @@ const unrealReducer = createReducer(initialState$1, on(changeLowBandwidth, (stat
537
568
  environmentId: state.environmentId,
538
569
  streamRequestContext: state.streamRequestContext,
539
570
  streamConfig: state.streamConfig,
571
+ userInactivityTimeout: state.userInactivityTimeout,
540
572
  };
541
573
  }), on(dropConnection, (state) => {
542
574
  return { ...state, establishingConnection: false };
@@ -608,7 +640,7 @@ function PlayerCountHandler(msg) {
608
640
  }
609
641
 
610
642
  function SSInfoHandler(msg) {
611
- this.store.dispatch(updateCirrusInfo({ ssInfo: msg.message, ssData: msg.data }));
643
+ this.store.dispatch(updateCirrusInfo({ ssData: msg.data }));
612
644
  return null;
613
645
  }
614
646
 
@@ -1055,63 +1087,6 @@ function getResizeValuesUniversal(params) {
1055
1087
  return { w: makeEven(w), h: makeEven(h), x: 0, y: 0, scale: w / maxSpace.w };
1056
1088
  }
1057
1089
 
1058
- //TODO: Remove this legacy method after confirming that `getResizeValuesUniversal`
1059
- // works well in all scenarios and updating any dependent code to use it instead.
1060
- function getResizeValuesUniversalOld(params) {
1061
- const { pixelRatio, containerWidth, containerHeight, maxResolution } = params;
1062
- const maxSpace = {
1063
- w: containerWidth * pixelRatio,
1064
- h: containerHeight * pixelRatio,
1065
- };
1066
- const isVertical = containerWidth < containerHeight;
1067
- let maxH = FULL_HD_HEIGHT;
1068
- let maxW = FULL_HD_WIDTH;
1069
- if (maxResolution) {
1070
- maxH = FULL_HD_HEIGHT * 2;
1071
- maxW = FULL_HD_WIDTH * 2;
1072
- }
1073
- const maxLimits = isVertical
1074
- ? { w: maxH, h: maxW, x: 0, y: 0 }
1075
- : { w: maxW, h: maxH, x: 0, y: 0 };
1076
- const props = fitIntoRectangle({ x: 0, y: 0, w: containerWidth, h: containerHeight }, { x: 0, y: 0, ...maxSpace });
1077
- let out = { ...props };
1078
- const fitIntoMax = fitIntoRectangle(props, maxLimits);
1079
- if (fitIntoMax.scale < 1) {
1080
- // If reducing size to 1920x1080 is required, do it
1081
- out = fitIntoMax;
1082
- }
1083
- else {
1084
- // Otherwise, use the lowest resolution because the layout size is smaller than 1920x1080
1085
- out.scale = 1;
1086
- }
1087
- /**
1088
- * Ensure that the size is always even.
1089
- */
1090
- out.w = makeEven(out.w);
1091
- out.h = makeEven(out.h);
1092
- return { ...out };
1093
- }
1094
- function getResizeStudioValuesOld(params) {
1095
- const { pixelRatio, containerWidth, containerHeight, studioWidth, studioHeight, } = params;
1096
- const maxSpace = {
1097
- w: containerWidth * pixelRatio,
1098
- h: containerHeight * pixelRatio,
1099
- };
1100
- const videoSizePixelPerfect = fitIntoRectangle({ x: 0, y: 0, w: studioWidth, h: studioHeight }, { x: 0, y: 0, ...maxSpace });
1101
- const out = fitIntoRectangle(videoSizePixelPerfect, {
1102
- x: 0,
1103
- y: 0,
1104
- w: Math.min(maxSpace.w, FULL_HD_WIDTH),
1105
- h: Math.min(maxSpace.h, FULL_HD_HEIGHT),
1106
- });
1107
- /**
1108
- * Ensure that the size is always even.
1109
- */
1110
- out.w = makeEven(out.w);
1111
- out.h = makeEven(out.h);
1112
- return out;
1113
- }
1114
-
1115
1090
  const removeExileCommands = (state, idToRemove) => {
1116
1091
  const exileTimeout = 10000;
1117
1092
  const out = { ...state };
@@ -1119,11 +1094,11 @@ const removeExileCommands = (state, idToRemove) => {
1119
1094
  out.totalCommandsCompleted++;
1120
1095
  const time = new Date().getTime();
1121
1096
  const markForDelete = [];
1122
- Object.entries(out.commandsInProgress).forEach(([, { id, timeStamp }]) => {
1097
+ for (const { id, timeStamp } of out.commandsInProgress) {
1123
1098
  if (time - timeStamp > exileTimeout) {
1124
1099
  markForDelete.push(id);
1125
1100
  }
1126
- });
1101
+ }
1127
1102
  out.totalCommandsCompleted += markForDelete.length;
1128
1103
  out.commandsInProgress = out.commandsInProgress.filter((command) => !markForDelete.includes(command.id));
1129
1104
  return out;
@@ -1159,9 +1134,9 @@ const commandsFeature = createFeature({
1159
1134
  const selectCommandsInProgress = createSelector(commandsFeature.selectCommandsInProgress, (commandsInProgress) => commandsInProgress?.length ?? 0);
1160
1135
  const selectCommandProgress = createSelector(commandsFeature.selectTotalCommandsStarted, commandsFeature.selectTotalCommandsCompleted, (totalCommandsStarted, totalCommandsCompleted) => totalCommandsStarted ? totalCommandsCompleted / totalCommandsStarted : 0);
1161
1136
 
1162
- const selectFreezeFrameDataUrlFromVideo = createSelector(unrealFeature.selectFreezeFrameFromVideo, unrealFeature.selectLowBandwidth, (freezeFrameFromVideo, isLow) => isLow ? null : freezeFrameFromVideo?.dataUrl || null);
1163
- const selectFreezeFrameProgressMessageFromVideo = createSelector(unrealFeature.selectFreezeFrameFromVideo, unrealFeature.selectLowBandwidth, (freezeFrameFromVideo, isLow) => isLow ? null : freezeFrameFromVideo?.progress || null);
1164
- const selectFreezeFrameDataUrl = createSelector(unrealFeature.selectFreezeFrame, unrealFeature.selectLowBandwidth, (freezeFrame, isLow) => freezeFrame?.dataUrl && isLow ? freezeFrame?.dataUrl : null);
1137
+ const selectFreezeFrameDataUrlFromVideo = createSelector(unrealFeature.selectFreezeFrameFromVideo, (freezeFrameFromVideo) => freezeFrameFromVideo?.dataUrl || null);
1138
+ const selectFreezeFrameProgressMessageFromVideo = createSelector(unrealFeature.selectFreezeFrameFromVideo, (freezeFrameFromVideo) => freezeFrameFromVideo?.progress || null);
1139
+ const selectFreezeFrameDataUrl = createSelector(unrealFeature.selectFreezeFrame, (freezeFrame) => (freezeFrame?.dataUrl ? freezeFrame?.dataUrl : null));
1165
1140
  const selectIsFreezeFrameLoading = createSelector(unrealFeature.selectFreezeFrame, (freezeFrame) => freezeFrame?.progress
1166
1141
  ? freezeFrame.progress > 0 && freezeFrame.progress < 1
1167
1142
  : false);
@@ -1170,50 +1145,55 @@ const selectStreamConfig = createSelector(unrealFeature.selectStreamConfig, (con
1170
1145
  const selectWarnTimeout = createSelector(selectStreamConfig, (config) => config?.warnTimeout || DEFAULT_AFK_TIMEOUT);
1171
1146
  const selectIsExistMatchUrls = createSelector(unrealFeature.selectMatchUrls, (urls) => urls?.length > 0);
1172
1147
  const selectIsAutostart = createSelector(selectStreamConfig, (config) => config?.autoStart);
1173
- const selectShowReconnectPopup = createSelector(unrealFeature.selectCirrusConnected, unrealFeature.selectEstablishingConnection, (connected, establishing) => !connected && !establishing);
1148
+ const selectShowReconnectPopup = createSelector(unrealFeature.selectCirrusConnected, unrealFeature.selectEstablishingConnection, (cirrusConnected, establishingConnection) => !cirrusConnected && !establishingConnection);
1174
1149
  const selectShowLoader = createSelector(selectShowReconnectPopup, unrealFeature.selectViewportReady, (showPopup, viewportReady) => !showPopup && !viewportReady);
1175
1150
  const selectIsVideoPlayingAndDataChannelConnected = createSelector(unrealFeature.selectIsVideoPlaying, unrealFeature.selectDataChannelConnected, (isVideoPlaying, isDataChannelConnected) => isVideoPlaying && isDataChannelConnected);
1176
- const selectSignalingParameters = createSelector(unrealFeature.selectSsData, unrealFeature.selectAwsInstance, unrealFeature.selectSsInfo, (data, instance, ssInfo) => {
1151
+ const selectSignalingParameters = createSelector(unrealFeature.selectSsData, unrealFeature.selectAwsInstance, (data, instance) => {
1177
1152
  const info = [];
1178
1153
  if (data) {
1179
1154
  info.push(`${data.cirrusVersion}, ${data.branchInfo}, ${data.instanceType}`);
1180
1155
  info.push(`Unreal App Version: ${data.streamerVersion}`);
1181
1156
  info.push(`APP: ${data.streamPath}${data.streamApp}`);
1182
1157
  }
1183
- else {
1184
- info.push(ssInfo);
1185
- }
1186
1158
  info.push(`Polling: ${instance.pollingUrl}`);
1187
1159
  info.push(`WS: ${instance.wsUrl}`);
1188
1160
  return info.join('<br>');
1189
1161
  });
1162
+ const selectClientAndViewIds = createSelector(unrealFeature.selectStreamClientCompanyId, unrealFeature.selectStreamViewId, (clientId, viewId) => ({ clientId, viewId }));
1163
+ const selectIsFullyConnected = createSelector(unrealFeature.selectViewportReady, unrealFeature.selectDataChannelConnected, unrealFeature.selectCirrusConnected, (viewportReady, dataChannelConnected, cirrusConnected) => viewportReady && dataChannelConnected && cirrusConnected);
1164
+ const selectIsLoaderScreenVisible = createSelector(selectIsFullyConnected, (fullyConnected) => !fullyConnected);
1165
+ const selectCirrusConnectionId = createSelector(unrealFeature.selectSsData, (data) => data?.connectionId);
1190
1166
  /**
1191
- * Returns Scene load progress as a float value in between 0-1
1167
+ * Returns Scene load progress as a float value between 0 and 1.
1168
+ *
1169
+ * Progress is split into two phases:
1170
+ * 1. **Signaling** (0 → ~56%): connection polling or orchestration progress.
1171
+ * Starts at 0.1%, so the bar is never visually empty while connecting.
1172
+ * 2. **Commands** (~56% → 100%): Unreal command progress after the data channel connects.
1173
+ *
1174
+ * The split point bumps by +1% once the WebSocket URL is known,
1175
+ * producing a small visual step that indicates the WS handshake succeeded.
1192
1176
  */
1193
1177
  const selectTotalProgress = createSelector(unrealFeature.selectStatusPercentSignallingServer, selectCommandProgress, unrealFeature.selectAwsInstance, unrealFeature.selectDataChannelConnected, (statusPercentSignallingServer, commandProgress, awsInstance, dataChannelConnected) => {
1194
- // TODO make better mechanism to detect is new orchestration
1195
- const isNewOrchestration = String(awsInstance?.wsUrl).match(/^wss:\/\/orchestration-websocket/i);
1196
- const newOrchestrationProgressComplete = dataChannelConnected
1197
- ? 100
1198
- : (awsInstance?.progressComplete || 0) * 100;
1199
- /**
1200
- * Set 57% {@link SIGNALLING_PERCENT_VALUE} immediately when signalingServer?.signalingUrl is available
1201
- * Up to 56% {@link SIGNALLING_PERCENT_VALUE} - 1 of the progress is reserved for signaling server polling
1202
- */
1203
- const signalingPercentValue = isNewOrchestration
1204
- ? newOrchestrationProgressComplete
1205
- : statusPercentSignallingServer;
1206
1178
  const SIGNALLING_MAX = SIGNALLING_PERCENT_VALUE / 100;
1179
+ const ORCHESTRATION_WS_PATTERN = /^wss:\/\/orchestration-websocket/i;
1180
+ const isNewOrchestration = ORCHESTRATION_WS_PATTERN.test(awsInstance?.wsUrl ?? '');
1181
+ // Orchestration progress is 0–1 from the backend; map to 0–1 directly.
1182
+ // Once the data channel is connected, signaling is fully complete (1.0).
1183
+ const signalingProgress = isNewOrchestration
1184
+ ? dataChannelConnected
1185
+ ? 1
1186
+ : (awsInstance?.progressComplete ?? 0)
1187
+ : (statusPercentSignallingServer ?? 0) / 100;
1188
+ // +1% bump once WS URL is known (visual step for WS handshake)
1207
1189
  const splitPoint = awsInstance?.wsUrl
1208
1190
  ? SIGNALLING_MAX + 0.01
1209
1191
  : SIGNALLING_MAX;
1210
- const progressSignaling = lerp(0.001, splitPoint, (signalingPercentValue ?? 0) / 100);
1192
+ // 0.001 minimum so the bar is never completely empty while loading
1193
+ const progressSignaling = lerp(0.001, splitPoint, signalingProgress);
1211
1194
  const progressRest = lerp(0, 1 - splitPoint, commandProgress);
1212
1195
  return clampf(0, 1, progressSignaling + progressRest);
1213
1196
  });
1214
- const selectClientAndViewIds = createSelector(unrealFeature.selectStreamClientCompanyId, unrealFeature.selectStreamViewId, (clientId, viewId) => ({ clientId, viewId }));
1215
- const selectIsLoaderScreenVisible = createSelector(unrealFeature.selectViewportReady, unrealFeature.selectDataChannelConnected, unrealFeature.selectCirrusConnected, (viewReady, cirrusConnected, dataChannelConnected) => !viewReady || !cirrusConnected || !dataChannelConnected);
1216
- const selectCirrusConnectionId = createSelector(unrealFeature.selectSsData, (data) => data?.connectionId);
1217
1197
 
1218
1198
  class UnrealErrorModalComponent {
1219
1199
  constructor() {
@@ -1224,38 +1204,14 @@ class UnrealErrorModalComponent {
1224
1204
  close() {
1225
1205
  this.#dialogRef.close();
1226
1206
  }
1227
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: UnrealErrorModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1228
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.10", type: UnrealErrorModalComponent, isStandalone: true, selector: "app-unreal-error-modal", ngImport: i0, template: "<div class=\"src-modal src-modal--small\">\n <header class=\"src-modal__header\">\n <div\n [attr.data-testid]=\"'unreal-error-header-title'\"\n class=\"src-modal__title\"\n >\n Warning\n </div>\n </header>\n <div class=\"src-modal__body\">\n <div>{{ dialogData.content }}</div>\n </div>\n <div class=\"src-modal__footer\">\n <src-button (onClick)=\"close()\">Ok</src-button>\n </div>\n</div>\n", styles: [".src-modal__header{padding:16px 24px 0}\n"], dependencies: [{ kind: "component", type: SourceButtonComponent, selector: "src-button", inputs: ["type", "appearance", "weight", "size", "context", "customClass", "isFullWidth", "isPressed", "isDisabled", "isLoading", "isInverted", "srcButtonConfig", "formID", "data-testid", "ariaLabel"], outputs: ["onClick", "onSubmit"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1207
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: UnrealErrorModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1208
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.11", type: UnrealErrorModalComponent, isStandalone: true, selector: "app-unreal-error-modal", ngImport: i0, template: "<div class=\"src-modal src-modal--small\">\n <header class=\"src-modal__header\">\n <div\n [attr.data-testid]=\"'unreal-error-header-title'\"\n class=\"src-modal__title\"\n >\n Warning\n </div>\n </header>\n <div class=\"src-modal__body\">\n <div>{{ dialogData.content }}</div>\n </div>\n <div class=\"src-modal__footer\">\n <src-button (onClick)=\"close()\">Ok</src-button>\n </div>\n</div>\n", styles: [".src-modal__header{padding:16px 24px 0}\n"], dependencies: [{ kind: "component", type: SourceButtonComponent, selector: "src-button", inputs: ["type", "appearance", "weight", "size", "context", "customClass", "isFullWidth", "isPressed", "isDisabled", "isLoading", "isInverted", "srcButtonConfig", "formID", "data-testid", "ariaLabel"], outputs: ["onClick", "onSubmit"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1229
1209
  }
1230
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: UnrealErrorModalComponent, decorators: [{
1210
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: UnrealErrorModalComponent, decorators: [{
1231
1211
  type: Component,
1232
1212
  args: [{ selector: 'app-unreal-error-modal', changeDetection: ChangeDetectionStrategy.OnPush, imports: [SourceButtonComponent], template: "<div class=\"src-modal src-modal--small\">\n <header class=\"src-modal__header\">\n <div\n [attr.data-testid]=\"'unreal-error-header-title'\"\n class=\"src-modal__title\"\n >\n Warning\n </div>\n </header>\n <div class=\"src-modal__body\">\n <div>{{ dialogData.content }}</div>\n </div>\n <div class=\"src-modal__footer\">\n <src-button (onClick)=\"close()\">Ok</src-button>\n </div>\n</div>\n", styles: [".src-modal__header{padding:16px 24px 0}\n"] }]
1233
1213
  }] });
1234
1214
 
1235
- /**
1236
- * Default LBM Filter Parameters
1237
- */
1238
- const DefaultFilterModel = {
1239
- minimumBitrate: 1600,
1240
- yellowFlag: 30,
1241
- redFlag: 50,
1242
- minimumFps: 5,
1243
- monitoringDelayTime: 5000,
1244
- initialBitrateEstimate: 1600,
1245
- initialErrorCovariance: 1,
1246
- processNoise: 3,
1247
- measurementNoise: 500,
1248
- panelOpen: false,
1249
- };
1250
- /**
1251
- * Global LBM Filter Parameters
1252
- */
1253
- const FilterModel = { ...DefaultFilterModel };
1254
- /**
1255
- * Bitrate Monitor Static Class
1256
- */
1257
- const BITRATE_MONITOR = new DataFlowMonitor(FilterModel.yellowFlag, FilterModel.redFlag, 20);
1258
-
1259
1215
  class UnrealEffects {
1260
1216
  constructor() {
1261
1217
  this.actions$ = inject(Actions);
@@ -1276,7 +1232,6 @@ class UnrealEffects {
1276
1232
  this.connectionCompleted$ = this.store
1277
1233
  .select(unrealFeature.selectDataChannelConnected)
1278
1234
  .pipe(filter(Truthy));
1279
- // ==================== PEER RECONNECTION EFFECTS ====================
1280
1235
  this.reconnectConfig = {
1281
1236
  enabled: this.unrealInitialConfig?.reconnect?.enabled ?? DEFAULT_RECONNECT_ENABLED,
1282
1237
  maxAttempts: this.unrealInitialConfig?.reconnect?.maxAttempts ??
@@ -1289,29 +1244,14 @@ class UnrealEffects {
1289
1244
  DEFAULT_RECONNECT_ON_DATACHANNEL_CLOSE,
1290
1245
  };
1291
1246
  this.disconnectReasonHandling$ = createEffect(() => {
1292
- const destroyReasons = [
1293
- DisconnectReason.Afk,
1294
- DisconnectReason.DataChannelClosed,
1295
- DisconnectReason.DataChannelTimeout,
1296
- DisconnectReason.WebSocketError,
1297
- DisconnectReason.WebSocketClose,
1298
- DisconnectReason.WebRTCError,
1299
- DisconnectReason.DropConnection,
1300
- DisconnectReason.ReconnectFailed,
1301
- DisconnectReason.OrchestrationPlayerDisconnected,
1302
- DisconnectReason.OrchestrationStreamerDisconnected,
1303
- ];
1304
1247
  return this.actions$.pipe(ofType(disconnectStream), filter(({ reason }) => {
1305
- // Skip DataChannelClosed if peer reconnection is enabled and signaling is connected
1306
- // The peer reconnection effect will handle this case
1248
+ // Skip DataChannelClosed if peer reconnection will handle this case
1307
1249
  if (reason === DisconnectReason.DataChannelClosed &&
1308
- this.reconnectConfig.enabled &&
1309
- this.reconnectConfig.onDataChannelClose &&
1310
- this.signallingService.isConnected()) {
1250
+ this.canPeerReconnect()) {
1311
1251
  Logger.info('[Reconnect] Skipping destroy for DataChannelClosed - peer reconnection will handle');
1312
1252
  return false;
1313
1253
  }
1314
- return destroyReasons.some((r) => r === reason);
1254
+ return DESTROY_DISCONNECT_REASONS.includes(reason);
1315
1255
  }), tap(({ reason, message }) => {
1316
1256
  Logger.log(`Disconnect Call=> reason:'${reason}', message:'${message}'`);
1317
1257
  }), map(({ reason }) => destroyRemoteConnections({ reason })));
@@ -1331,11 +1271,6 @@ class UnrealEffects {
1331
1271
  this.webRtcPlayerService.closePC();
1332
1272
  }), filter(({ reason }) => reason === DisconnectReason.DropConnection), map(() => dropConnection()));
1333
1273
  });
1334
- this.webrtcErrorModalComponent$ = createEffect(() => {
1335
- return this.actions$.pipe(ofType(disconnectStream), filter(({ reason }) => reason === DisconnectReason.WebRTCError), debounceTime(400), switchMap(() => this.store
1336
- .select(unrealFeature.selectDataChannelConnected)
1337
- .pipe(take(1), filter(Falsy))), tap(() => this.dialog.open(WebrtcErrorModalComponent)));
1338
- }, { dispatch: false });
1339
1274
  this.changeStreamResolution$ = createEffect(() => {
1340
1275
  return this.actions$.pipe(ofType(changeStreamResolutionAction), filter((size) => !!(size?.width && size?.height)), concatLatestFrom(() => this.store.select(unrealFeature.selectStreamResolution)), filter(([newSizes, savedSizes]) => !(newSizes.width === savedSizes.width &&
1341
1276
  newSizes.height === savedSizes.height)), map(([sizes]) => changeStreamResolutionSuccessAction({
@@ -1343,49 +1278,25 @@ class UnrealEffects {
1343
1278
  height: sizes.height,
1344
1279
  })));
1345
1280
  });
1346
- this.forceLBMOff$ = createEffect(() => {
1347
- return fromEvent(document, 'visibilitychange').pipe(map(() => document.visibilityState === 'visible'), filter(Truthy), withLatestFrom(this.store.select(unrealFeature.selectLowBandwidth)), filter(([, isLowBandwidth]) => isLowBandwidth), map(() => changeLowBandwidth({ lowBandwidth: false })));
1348
- });
1349
- this.resetFreezeFrameOnLowBandwidthTriggered$ = createEffect(() => {
1350
- return this.actions$.pipe(ofType(changeLowBandwidth), map(({ lowBandwidth }) => lowBandwidth), distinctUntilChanged(), map(() => setFreezeFrame({ dataUrl: null, progress: null })));
1351
- });
1352
- this.sendLbmStatistics$ = createEffect(() => {
1353
- return this.actions$.pipe(ofType(changeLowBandwidth), filter(({ lowBandwidth }) => lowBandwidth), withLatestFrom(this.store.select(unrealFeature.selectAwsInstance), this.store.select(unrealFeature.selectStreamClientCompanyId), this.store.select(unrealFeature.selectStreamViewId)), take(1), exhaustMap(([, instance, companyId, viewId]) => this.http
1354
- .post(this.unrealInitialConfig?.customErrorsEndpoint || '', {
1355
- message: {
1356
- type: 'front',
1357
- error: 'low_bandwidth_mode',
1358
- signalling: instance.pollingUrl,
1359
- },
1360
- client: instance.pollingUrl,
1361
- session_uuid: companyId,
1362
- session_uuid2: viewId,
1363
- })
1364
- .pipe(catchError(() => EMPTY))));
1365
- }, { dispatch: false });
1366
1281
  this.resetAfk$ = createEffect(() => {
1367
- return this.actions$.pipe(ofType(setConfig, resetWarnTimeout, resetConfig), map(() => resetAfk()));
1282
+ return this.actions$.pipe(ofType(setConfig, setUserInactivityTimeout, resetWarnTimeout, resetConfig), map(() => resetAfk()));
1368
1283
  });
1369
1284
  this.abortEstablishingConnection$ = createEffect(() => {
1370
- const viewportReady$ = this.store
1371
- .select(unrealFeature.selectViewportReady)
1372
- .pipe(filter(Truthy), map(() => abortEstablishingConnection()));
1373
- const disconnectEvents$ = this.actions$.pipe(ofType(setCirrusDisconnected, destroyUnrealScene, dropConnection), map(() => abortEstablishingConnection()));
1374
- return merge(viewportReady$, disconnectEvents$).pipe(tapLog('ABORT'));
1285
+ return merge(this.store.select(unrealFeature.selectViewportReady).pipe(filter(Truthy)), this.actions$.pipe(ofType(setCirrusDisconnected, destroyUnrealScene, dropConnection))).pipe(map(() => abortEstablishingConnection()));
1375
1286
  });
1376
- this.setMaxFps$ = createEffect(() => {
1377
- return this.actions$.pipe(ofType(setMaxFps), tap(({ maxFps }) => {
1378
- this.commandsSender.sendCommandToUnreal({
1379
- command: MetaBoxCommand.FExecuteConsoleCommandCommand,
1380
- payload: { value: `t.MaxFPS ${maxFps}` },
1381
- });
1382
- }));
1383
- }, { dispatch: false });
1384
- this.setKalmanSettings$ = createEffect(() => {
1385
- return this.actions$.pipe(ofType(setKalmanSettings), tap(({ settings }) => {
1386
- this.videoService.kalmanFilter1D.config(settings);
1387
- BITRATE_MONITOR.config(settings);
1388
- }));
1287
+ this.iframeMessaging$ = createEffect(() => {
1288
+ const cirrusConnected$ = this.store
1289
+ .select(unrealFeature.selectCirrusConnected)
1290
+ .pipe(skip(1));
1291
+ // On disconnect: immediately show 'Connection closed.', nullify after 2s
1292
+ const cirrusStatus$ = cirrusConnected$.pipe(switchMap((connected) => connected
1293
+ ? of('Creating Socket...')
1294
+ : concat(of('Connection closed.'), of(null).pipe(delay(2000)))));
1295
+ // Skip initial false; emit null on every viewport state change
1296
+ const viewportReset$ = this.store
1297
+ .select(unrealFeature.selectViewportReady)
1298
+ .pipe(skip(1), delay(500), map(() => null));
1299
+ return merge(cirrusStatus$, this.store.select(unrealFeature.selectStatusMessage), viewportReset$).pipe(debounceTime(0), distinctUntilChanged(), tapLog('STATUS=>'), tap((status) => this.postToScreenLocker(status)));
1389
1300
  }, { dispatch: false });
1390
1301
  this.setSignalingTo100$ = createEffect(() => {
1391
1302
  return this.actions$.pipe(ofType(setAwsInstance), map(() => setStatusPercentSignallingServer({ percent: 100 })));
@@ -1448,27 +1359,10 @@ class UnrealEffects {
1448
1359
  data: { content },
1449
1360
  }).closed), map(() => showUnrealErrorMessage({ code: null })));
1450
1361
  });
1451
- this.sendStatisticsToUnreal$ = createEffect(() => {
1452
- return this.actions$.pipe(ofType(showUnrealErrorMessage), filter(({ code }) => code === 404), withLatestFrom(this.store.select(unrealFeature.selectAwsInstance), this.store.select(unrealFeature.selectStreamClientCompanyId), this.store.select(unrealFeature.selectStreamViewId)), switchMap(([action, instance, companyId, viewId]) => this.http
1453
- .post(this.unrealInitialConfig?.customErrorsEndpoint || '', {
1454
- client: instance.pollingUrl,
1455
- session_uuid: companyId,
1456
- session_uuid2: viewId,
1457
- message: {
1458
- type: 'front',
1459
- error: 'pak_not_found',
1460
- info: action?.error ?? '',
1461
- },
1462
- })
1463
- .pipe(tap(() => Logger.log(`successfully send to ${this.unrealInitialConfig?.customErrorsEndpoint}`)), catchError(() => {
1464
- Logger.error(`get error from ${this.unrealInitialConfig?.customErrorsEndpoint}`);
1465
- return EMPTY;
1466
- }))));
1467
- }, { dispatch: false });
1468
1362
  this.unrealReceiverReady$ = createEffect(() => {
1469
1363
  return this.actions$.pipe(ofType(dataChannelConnected), concatLatestFrom(() => this.store.select(unrealFeature.selectIsReconnecting)), filter(([, isReconnecting]) => !isReconnecting), map(() => dataChannelReady()));
1470
1364
  });
1471
- this.initAppBySetDataChannelConnected$ = createEffect(() => {
1365
+ this.initAppByDataChannelReady$ = createEffect(() => {
1472
1366
  return this.actions$.pipe(ofType(dataChannelReady), tap(() => {
1473
1367
  this.commandsSender.sendCommandToUnreal({
1474
1368
  command: MetaBoxCommand.FExecuteConsoleCommandCommand,
@@ -1479,10 +1373,13 @@ class UnrealEffects {
1479
1373
  payload: { value: this.isDevMode },
1480
1374
  });
1481
1375
  this.commandTelemetryService.trackStartCommand('EXT-SetViewportReady');
1482
- }), switchMap(() => this.store.select(selectCommandsInProgress).pipe(debounceTime(100), // !IMPORTANT last command detection time
1483
- take(1), switchMap(() => this.unrealCallbackService.observeCommandResponse({ command: MetaBoxCommand.FLoopBackCommand }, (data) => this.commandsSender.sendCommandToUnreal(data))), switchMap(() => this.videoService.videoStats$), filter((videoStats) => !!videoStats.aggregatedStats.framesPerSecond &&
1484
- videoStats.aggregatedStats.framesPerSecond > MINIMAL_FPS &&
1485
- (videoStats.aggregatedStats.framesDecoded || 0) > 15), take(1), map(() => setLoopBackCommandIsCompleted()))));
1376
+ }), switchMap(() =>
1377
+ // Wait for initial commands to be sent before sending the loopback.
1378
+ // commandStarted fires for each sent command; debounce ensures
1379
+ // we proceed only after no more commands are started for 100 ms.
1380
+ this.actions$.pipe(ofType(commandStarted), debounceTime(100), take(1), switchMap(() => this.unrealCallbackService.observeCommandResponse({ command: MetaBoxCommand.FLoopBackCommand }, (data) => this.commandsSender.sendCommandToUnreal(data))), switchMap(() => this.videoService.videoStats$), filter(({ aggregatedStats: s }) => !!s.framesPerSecond &&
1381
+ s.framesPerSecond > MINIMAL_FPS &&
1382
+ (s.framesDecoded ?? 0) > 15), take(1), map(() => setLoopBackCommandIsCompleted()))));
1486
1383
  });
1487
1384
  this.setViewportReadyBySetLoopBackCommandIsCompleted$ = createEffect(() => {
1488
1385
  return this.actions$.pipe(ofType(setLoopBackCommandIsCompleted, reconnectPeerSuccess), tap(() => this.commandTelemetryService.trackStopCommand('EXT-SetViewportReady', false, { ready: true })), map(() => setViewportReady()));
@@ -1490,16 +1387,9 @@ class UnrealEffects {
1490
1387
  this.startStream$ = createEffect(() => {
1491
1388
  return this.actions$.pipe(ofType(startStream), map(() => initSignalling()));
1492
1389
  });
1493
- this.connectToSignaling$ = createEffect(() => {
1494
- return this.actions$.pipe(ofType(initSignalling), withLatestFrom(this.store.select(unrealFeature.selectMatchUrls), this.store.select(selectIsAutostart), this.store.select(unrealFeature.selectCirrusConnected)), filter(([, , autoStart, cirrusConnected]) => {
1495
- if (cirrusConnected) {
1496
- Logger.warn(`SIGNALING REQUEST can't be called when cirrus connection is already established.`);
1497
- }
1498
- return autoStart && !cirrusConnected;
1499
- }), map(([, matchMakerUrls]) => [...matchMakerUrls].filter(Truthy)), filter((urls) => urls.length > 0),
1500
- // Ensure a single in-flight request per attempt.
1501
- exhaustMap((urls) => this.signallingService.connectToSignaling(urls)));
1502
- }, { dispatch: false });
1390
+ this.resetAfkOnVideoStart$ = createEffect(() => {
1391
+ return this.actions$.pipe(ofType(videoStreamInitialized), filter(() => !!this.videoService.video?.paused), tap(() => Logger.info('Simulate Press Play')), map(() => resetAfk()));
1392
+ });
1503
1393
  /**
1504
1394
  * Effect: `forceViewportNotReady$`
1505
1395
  *
@@ -1527,9 +1417,7 @@ class UnrealEffects {
1527
1417
  */
1528
1418
  this.handleDataChannelClosed$ = createEffect(() => {
1529
1419
  return this.actions$.pipe(ofType(disconnectStream), filter(({ reason }) => reason === DisconnectReason.DataChannelClosed &&
1530
- this.reconnectConfig.enabled &&
1531
- this.reconnectConfig.onDataChannelClose &&
1532
- this.signallingService.isConnected()), tap(() => {
1420
+ this.canPeerReconnect()), tap(() => {
1533
1421
  Logger.warn('[Reconnect] DataChannel closed, initiating peer reconnection');
1534
1422
  }), map(() => reconnectPeer({
1535
1423
  attempt: 1,
@@ -1584,9 +1472,7 @@ class UnrealEffects {
1584
1472
  }));
1585
1473
  }
1586
1474
  }));
1587
- }),
1588
- // Cancel if a scene is destroyed
1589
- takeUntil(this.actions$.pipe(ofType(destroyUnrealScene), take(1))))));
1475
+ }), takeUntil(this.actions$.pipe(ofType(destroyUnrealScene), take(1))))));
1590
1476
  });
1591
1477
  /**
1592
1478
  * Effect: `handlePeerReconnectFailed$`
@@ -1602,21 +1488,66 @@ class UnrealEffects {
1602
1488
  message: reason,
1603
1489
  })));
1604
1490
  });
1605
- /**
1606
- * Effect: `logPeerReconnectSuccess$`
1607
- *
1608
- * Logs successful peer reconnection for telemetry.
1609
- */
1610
- this.logPeerReconnectSuccess$ = createEffect(() => {
1611
- return this.actions$.pipe(ofType(reconnectPeerSuccess), tap(() => {
1612
- Logger.info('[Reconnect] Peer connection restored successfully');
1491
+ this.setMaxFps$ = createEffect(() => {
1492
+ return this.actions$.pipe(ofType(setMaxFps), tap(({ maxFps }) => {
1493
+ this.commandsSender.sendCommandToUnreal({
1494
+ command: MetaBoxCommand.FExecuteConsoleCommandCommand,
1495
+ payload: { value: `t.MaxFPS ${maxFps}` },
1496
+ });
1613
1497
  }));
1614
1498
  }, { dispatch: false });
1499
+ this.webrtcErrorModalComponent$ = createEffect(() => {
1500
+ return this.actions$.pipe(ofType(disconnectStream), filter(({ reason }) => reason === DisconnectReason.WebRTCError), debounceTime(400), switchMap(() => this.store
1501
+ .select(unrealFeature.selectDataChannelConnected)
1502
+ .pipe(take(1), filter(Falsy))), tap(() => this.dialog.open(WebrtcErrorModalComponent)));
1503
+ }, { dispatch: false });
1504
+ this.sendStatisticsToUnreal$ = createEffect(() => {
1505
+ return this.actions$.pipe(ofType(showUnrealErrorMessage), filter(({ code }) => code === 404), withLatestFrom(this.store.select(unrealFeature.selectAwsInstance), this.store.select(unrealFeature.selectStreamClientCompanyId), this.store.select(unrealFeature.selectStreamViewId)), switchMap(([action, instance, companyId, viewId]) => this.http
1506
+ .post(this.unrealInitialConfig?.customErrorsEndpoint || '', {
1507
+ client: instance.pollingUrl,
1508
+ session_uuid: companyId,
1509
+ session_uuid2: viewId,
1510
+ message: {
1511
+ type: 'front',
1512
+ error: 'pak_not_found',
1513
+ info: action?.error ?? '',
1514
+ },
1515
+ })
1516
+ .pipe(tap(() => Logger.log(`successfully send to ${this.unrealInitialConfig?.customErrorsEndpoint}`)), catchError(() => {
1517
+ Logger.error(`get error from ${this.unrealInitialConfig?.customErrorsEndpoint}`);
1518
+ return EMPTY;
1519
+ }))));
1520
+ }, { dispatch: false });
1521
+ this.connectToSignaling$ = createEffect(() => {
1522
+ return this.actions$.pipe(ofType(initSignalling), withLatestFrom(this.store.select(unrealFeature.selectMatchUrls), this.store.select(selectIsAutostart), this.store.select(unrealFeature.selectCirrusConnected)), filter(([, , autoStart, cirrusConnected]) => {
1523
+ if (cirrusConnected) {
1524
+ Logger.warn(`SIGNALING REQUEST can't be called when cirrus connection is already established.`);
1525
+ }
1526
+ return autoStart && !cirrusConnected;
1527
+ }), map(([, matchMakerUrls]) => [...matchMakerUrls].filter(Truthy)), filter((urls) => urls.length > 0),
1528
+ // Ensure a single in-flight request per attempt.
1529
+ exhaustMap((urls) => this.signallingService.connectToSignaling(urls)));
1530
+ }, { dispatch: false });
1531
+ }
1532
+ canPeerReconnect() {
1533
+ return (this.reconnectConfig.enabled &&
1534
+ this.reconnectConfig.onDataChannelClose &&
1535
+ this.signallingService.isConnected());
1536
+ }
1537
+ postToScreenLocker(text) {
1538
+ try {
1539
+ const el = document.getElementById(this.unrealInitialConfig?.screenLockerContainerId ??
1540
+ SCREEN_LOCKER_CONTAINER_ID);
1541
+ el?.contentWindow?.postMessage(text, '*');
1542
+ }
1543
+ catch {
1544
+ // Fails silently if cross-origin
1545
+ }
1615
1546
  }
1616
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: UnrealEffects, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1617
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: UnrealEffects }); }
1547
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: UnrealEffects, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1548
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: UnrealEffects }); }
1618
1549
  }
1619
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: UnrealEffects, decorators: [{
1550
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: UnrealEffects, decorators: [{
1620
1551
  type: Injectable
1621
1552
  }] });
1622
1553
 
@@ -1630,10 +1561,10 @@ class CommandsEffects {
1630
1561
  : json?.correlationId), filter(Truthy), map((id) => commandCompleted({ id })))));
1631
1562
  });
1632
1563
  }
1633
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: CommandsEffects, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1634
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: CommandsEffects }); }
1564
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: CommandsEffects, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1565
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: CommandsEffects }); }
1635
1566
  }
1636
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: CommandsEffects, decorators: [{
1567
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: CommandsEffects, decorators: [{
1637
1568
  type: Injectable
1638
1569
  }] });
1639
1570
 
@@ -1646,7 +1577,7 @@ class SubService {
1646
1577
  this.disconnect$ = this.store
1647
1578
  .select(unrealFeature.selectCirrusConnected)
1648
1579
  .pipe(skip(1), filter(Falsy), share());
1649
- this._destroy = this.destroyRef.onDestroy(() => {
1580
+ this.destroyRef.onDestroy(() => {
1650
1581
  this.destroy$.next();
1651
1582
  this.destroy$.complete();
1652
1583
  });
@@ -1657,7 +1588,7 @@ class AFKService extends SubService {
1657
1588
  constructor() {
1658
1589
  super();
1659
1590
  this.active = false; // Whether the AFK system is currently looking for inactivity.
1660
- this.countdown = 0; // The inactivity warning overlay has a countdown to show time until disconnect.
1591
+ this.countdown = 0;
1661
1592
  this.warnTimeout = this.store.selectSignal(selectWarnTimeout);
1662
1593
  this.isViewportReady = this.store.selectSignal(unrealFeature.selectViewportReady);
1663
1594
  this.unrealConfig = inject(UNREAL_CONFIG, { optional: true });
@@ -1665,6 +1596,8 @@ class AFKService extends SubService {
1665
1596
  this.afkResetTrigger = toSignal(merge(this.store.select(selectWarnTimeout), this.actions$.pipe(ofType(resetAfk))));
1666
1597
  /** Ticks when cirrus disconnects */
1667
1598
  this.disconnected = toSignal(this.disconnect$);
1599
+ /** Ticks when resetUserInactivityTimeout action fires */
1600
+ this.resetUserInactivityTrigger = toSignal(this.actions$.pipe(ofType(resetUserInactivityTimeout)));
1668
1601
  this.destroyRef.onDestroy(() => this.clearTimers());
1669
1602
  if (this.unrealConfig?.playwright) {
1670
1603
  return;
@@ -1692,6 +1625,11 @@ class AFKService extends SubService {
1692
1625
  untracked(() => this.stop());
1693
1626
  }
1694
1627
  });
1628
+ // Full reset (hide overlay + restart warning timer) on external request
1629
+ effect(() => {
1630
+ this.resetUserInactivityTrigger();
1631
+ untracked(() => this.reset());
1632
+ });
1695
1633
  }
1696
1634
  hideOverlay() {
1697
1635
  this.store.dispatch(setAfkTimerHide());
@@ -1762,18 +1700,35 @@ class AFKService extends SubService {
1762
1700
  }
1763
1701
  }, 1000);
1764
1702
  }
1765
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: AFKService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1766
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: AFKService }); }
1703
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: AFKService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1704
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: AFKService }); }
1767
1705
  }
1768
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: AFKService, decorators: [{
1706
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: AFKService, decorators: [{
1769
1707
  type: Injectable
1770
1708
  }], ctorParameters: () => [] });
1771
1709
 
1710
+ class LoadingProgressService {
1711
+ constructor() {
1712
+ this.#store = inject(Store);
1713
+ this.loaderVisible = this.#store.selectSignal(selectShowLoader);
1714
+ this.percents = toSignal(this.#store
1715
+ .select(selectTotalProgress)
1716
+ .pipe(keepMaxUntilReset(toObservable(this.loaderVisible).pipe(filter(Falsy))), floatToSmoothPercents(), distinctUntilChanged()));
1717
+ }
1718
+ #store;
1719
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: LoadingProgressService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1720
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: LoadingProgressService }); }
1721
+ }
1722
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: LoadingProgressService, decorators: [{
1723
+ type: Injectable
1724
+ }] });
1725
+
1772
1726
  class FreezeFrameService extends SubService {
1773
1727
  constructor() {
1774
1728
  super();
1775
1729
  this.receiving = false;
1776
1730
  this.size = 0;
1731
+ this.jpeg = undefined;
1777
1732
  this.freezeFrameOverlay = new Image();
1778
1733
  this.unrealConfig = inject(UNREAL_CONFIG, { optional: true });
1779
1734
  this.init();
@@ -1853,13 +1808,37 @@ class FreezeFrameService extends SubService {
1853
1808
  progress: 1,
1854
1809
  }));
1855
1810
  }
1856
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: FreezeFrameService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1857
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: FreezeFrameService }); }
1811
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: FreezeFrameService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1812
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: FreezeFrameService }); }
1858
1813
  }
1859
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: FreezeFrameService, decorators: [{
1814
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: FreezeFrameService, decorators: [{
1860
1815
  type: Injectable
1861
1816
  }], ctorParameters: () => [] });
1862
1817
 
1818
+ /**
1819
+ * Default LBM Filter Parameters
1820
+ */
1821
+ const DefaultFilterModel = {
1822
+ minimumBitrate: 1600,
1823
+ yellowFlag: 30,
1824
+ redFlag: 50,
1825
+ minimumFps: 5,
1826
+ monitoringDelayTime: 5000,
1827
+ initialBitrateEstimate: 1600,
1828
+ initialErrorCovariance: 1,
1829
+ processNoise: 3,
1830
+ measurementNoise: 500,
1831
+ panelOpen: false,
1832
+ };
1833
+ /**
1834
+ * Global LBM Filter Parameters
1835
+ */
1836
+ const FilterModel = { ...DefaultFilterModel };
1837
+ /**
1838
+ * Bitrate Monitor Static Class
1839
+ */
1840
+ const BITRATE_MONITOR = new DataFlowMonitor(FilterModel.yellowFlag, FilterModel.redFlag, 20);
1841
+
1863
1842
  class VideoService extends SubService {
1864
1843
  constructor() {
1865
1844
  super(...arguments);
@@ -1902,7 +1881,7 @@ class VideoService extends SubService {
1902
1881
  this.videoTrack$.next({ stream, pcClient });
1903
1882
  }
1904
1883
  else {
1905
- console.error('Video element is not defined.');
1884
+ Logger.error('Video element is not defined.');
1906
1885
  }
1907
1886
  }
1908
1887
  attachAudioStream(stream) {
@@ -1979,7 +1958,7 @@ class VideoService extends SubService {
1979
1958
  catch (err) {
1980
1959
  // Handle InvalidStateError or any transient WebRTC errors gracefully
1981
1960
  if (err instanceof DOMException && err.name === 'InvalidStateError') {
1982
- console.warn('Peer connection is closed or unusable, skipping stats.');
1961
+ Logger.warn('Peer connection is closed or unusable, skipping stats.');
1983
1962
  return null;
1984
1963
  }
1985
1964
  throw err;
@@ -2121,7 +2100,7 @@ class VideoService extends SubService {
2121
2100
  }
2122
2101
  createWebRtcAudio() {
2123
2102
  const audio = new Audio();
2124
- audio.id = 'streamingAudio';
2103
+ audio.id = STREAMING_AUDIO_ID;
2125
2104
  this.audio = audio;
2126
2105
  }
2127
2106
  playAudio(audio) {
@@ -2132,10 +2111,10 @@ class VideoService extends SubService {
2132
2111
  error: () => ({}),
2133
2112
  });
2134
2113
  }
2135
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: VideoService, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
2136
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: VideoService }); }
2114
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: VideoService, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
2115
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: VideoService }); }
2137
2116
  }
2138
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: VideoService, decorators: [{
2117
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: VideoService, decorators: [{
2139
2118
  type: Injectable
2140
2119
  }] });
2141
2120
 
@@ -2341,10 +2320,10 @@ class CommandTelemetryService {
2341
2320
  this.trackTime(out);
2342
2321
  funcToDecorate(out);
2343
2322
  }
2344
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: CommandTelemetryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
2345
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: CommandTelemetryService }); }
2323
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: CommandTelemetryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
2324
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: CommandTelemetryService }); }
2346
2325
  }
2347
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: CommandTelemetryService, decorators: [{
2326
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: CommandTelemetryService, decorators: [{
2348
2327
  type: Injectable
2349
2328
  }], ctorParameters: () => [] });
2350
2329
 
@@ -2594,7 +2573,9 @@ class SignallingService extends SubService {
2594
2573
  return;
2595
2574
  }
2596
2575
  this.setHandlersFromStream();
2597
- this.action$.pipe(ofType(dataChannelReady)).subscribe(() => {
2576
+ this.action$
2577
+ .pipe(ofType(dataChannelReady), takeUntil(this.abort$))
2578
+ .subscribe(() => {
2598
2579
  this.send({
2599
2580
  type: 'p2pEstablished',
2600
2581
  source: 'front',
@@ -2604,7 +2585,7 @@ class SignallingService extends SubService {
2604
2585
  this.action$
2605
2586
  .pipe(ofType(dataChannelReady), switchMap(() => this.store
2606
2587
  .select(unrealFeature.selectViewportReady)
2607
- .pipe(filter(Truthy), take(1))))
2588
+ .pipe(filter(Truthy), take(1))), takeUntil(this.abort$))
2608
2589
  .subscribe(() => {
2609
2590
  this.send({
2610
2591
  type: 'streamPreparedForUserInteraction',
@@ -2636,10 +2617,10 @@ class SignallingService extends SubService {
2636
2617
  },
2637
2618
  // count: 10, // Optional: cap the number of attempts.
2638
2619
  }),
2639
- // abort$ sits OUTSIDE retry so it kills the entire retry loop,
2620
+ // abort$ sits OUTSIDE retry, so it kills the entire retry loop,
2640
2621
  // not just the current attempt. Each inner createWebSocket also
2641
2622
  // receives abort$ independently, so mid-connection attempts are
2642
- // cancelled too.
2623
+ // canceled too.
2643
2624
  takeUntil(this.abort$));
2644
2625
  }
2645
2626
  startEstablishingConnection() {
@@ -2665,7 +2646,7 @@ class SignallingService extends SubService {
2665
2646
  *
2666
2647
  * 1. **Telemetry** — Starts a `getSignaling` span for end-to-end timing.
2667
2648
  * 2. **Candidate URL selection** — A stateful generator (`urlGen`) is created
2668
- * from `urlsPool`. Each resubscription (e.g. via `retry`) advances to the next URL.
2649
+ * from `urlsPool`. Each resubscription (e.g., via `retry`) advances to the next URL.
2669
2650
  * 3. **Short-circuit for WS URLs** — If the picked candidate already matches
2670
2651
  * `ws://` or `wss://`, emit `{ wsUrl, pollingUrl: null, instanceName: null }`
2671
2652
  * immediately and complete (no HTTP request).
@@ -2677,7 +2658,7 @@ class SignallingService extends SubService {
2677
2658
  * while the request is in flight are **ignored** until completion, preventing overlap.
2678
2659
  * 6. **Per-attempt timeout** — `timeout(WS_TIMEOUT)` caps how long we wait for a response.
2679
2660
  * On timeout, the attempt errors and the outer `retry` schedules the next URL.
2680
- * 7. **Response validation & progress reporting** — A `filter`:
2661
+ * 7. **Response validation and progress reporting** — A `filter`:
2681
2662
  * - Calls `TelemetryStop('getSignaling', { ...data, multi: true })` on receipt.
2682
2663
  * - If `data.signallingServer === ''` or `data.error`:
2683
2664
  * shows a status message, optionally dispatches `setStatusPercentSignallingServer`
@@ -2694,7 +2675,7 @@ class SignallingService extends SubService {
2694
2675
  *
2695
2676
  * ### Concurrency semantics
2696
2677
  *
2697
- * - `exhaustMap` guarantees a **single** HTTP request per attempt; subsequent
2678
+ * - `exhaustMap` guarantees a **single** HTTP request per attempt; later
2698
2679
  * `{clientId, viewId}` emissions are ignored until the current request completes.
2699
2680
  *
2700
2681
  * ### Telemetry semantics
@@ -2728,7 +2709,7 @@ class SignallingService extends SubService {
2728
2709
  * @remarks
2729
2710
  * - The URL generator (`urlGen`) is created **outside** `defer`, so resubscriptions
2730
2711
  * advance the pool. Creating it inside `defer` would restart from the first URL each retry.
2731
- * - `location.protocol` is used to derive `ws` vs `wss`. If this may run in non-browser
2712
+ * - `location.protocol` is used to derive `ws` vs. `wss`. If this may run in non-browser
2732
2713
  * contexts (e.g., SSR), guard or abstract this logic.
2733
2714
  * - Consider capping retries with `count` in `retry({ count, delay })` when appropriate.
2734
2715
  *
@@ -2761,7 +2742,7 @@ class SignallingService extends SubService {
2761
2742
  };
2762
2743
  }));
2763
2744
  }
2764
- // Old (HTTP-polling) orchestration flow — mark the roundtrip so
2745
+ // Old (HTTP-polling) orchestration flow — mark the round trip so
2765
2746
  // the signaling-phase timeline can show it as a distinct segment.
2766
2747
  this.store.dispatch(pollingStarted());
2767
2748
  return this.store.select(selectClientAndViewIds).pipe(
@@ -2924,10 +2905,10 @@ class SignallingService extends SubService {
2924
2905
  Logger.log('Start Getting Region');
2925
2906
  return this.regionsPingService.getFastest().pipe(take(1));
2926
2907
  }
2927
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: SignallingService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
2928
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: SignallingService }); }
2908
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: SignallingService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
2909
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: SignallingService }); }
2929
2910
  }
2930
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: SignallingService, decorators: [{
2911
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: SignallingService, decorators: [{
2931
2912
  type: Injectable
2932
2913
  }], ctorParameters: () => [] });
2933
2914
 
@@ -2935,6 +2916,8 @@ class WebRtcPlayerService extends SubService {
2935
2916
  constructor() {
2936
2917
  super();
2937
2918
  this.onDataChannelMessage$ = new Subject();
2919
+ this.pcClient = null;
2920
+ this.dcClient = null;
2938
2921
  this.signallingSrv = inject(SignallingService);
2939
2922
  this.videoService = inject(VideoService);
2940
2923
  this.commandTelemetryService = inject(CommandTelemetryService);
@@ -2948,29 +2931,37 @@ class WebRtcPlayerService extends SubService {
2948
2931
  this.iceCandidateBuffer = [];
2949
2932
  this.isReconnecting = this.store.selectSignal(unrealFeature.selectIsReconnecting);
2950
2933
  this.init();
2951
- this.actions$.pipe(ofType(dropDC)).subscribe(() => this.dcClient?.close());
2952
- this.actions$.pipe(ofType(dropPC)).subscribe(() => this.pcClient?.close());
2934
+ this.actions$
2935
+ .pipe(ofType(dropDC), takeUntil(this.destroy$))
2936
+ .subscribe(() => this.dcClient?.close());
2937
+ this.actions$
2938
+ .pipe(ofType(dropPC), takeUntil(this.destroy$))
2939
+ .subscribe(() => this.pcClient?.close());
2953
2940
  }
2954
2941
  init() {
2955
- this.signallingSrv.onConfig$.subscribe((config) => {
2942
+ this.signallingSrv.onConfig$
2943
+ .pipe(takeUntil(this.destroy$))
2944
+ .subscribe((config) => {
2956
2945
  this.videoService.create();
2957
2946
  this.setConfig(config);
2958
2947
  });
2959
2948
  // Server-initiated offer: set remote description and respond with answer
2960
- this.signallingSrv.onOffer$.pipe(filter(Truthy)).subscribe((offer) => {
2949
+ this.signallingSrv.onOffer$
2950
+ .pipe(filter(Truthy), takeUntil(this.destroy$))
2951
+ .subscribe((offer) => {
2961
2952
  void this.handleServerOffer(offer);
2962
2953
  });
2963
2954
  // Answer to a client-initiated offer
2964
2955
  this.signallingSrv.onWebRtcAnswer$
2965
- .pipe(filter(Truthy))
2956
+ .pipe(filter(Truthy), takeUntil(this.destroy$))
2966
2957
  .subscribe((answer) => {
2967
2958
  void this.handleServerAnswer(answer);
2968
2959
  });
2969
2960
  // ICE candidates — buffered until remote description is set
2970
- this.signallingSrv.onWebRtcIce$.subscribe((candidate) => {
2971
- this.commandTelemetryService.trackStopCommand('EXT-iceCandidate', true, {
2972
- multi: true,
2973
- });
2961
+ this.signallingSrv.onWebRtcIce$
2962
+ .pipe(takeUntil(this.destroy$))
2963
+ .subscribe((candidate) => {
2964
+ this.commandTelemetryService.trackStopCommand('EXT-iceCandidate', true, { multi: true });
2974
2965
  if (this.remoteDescriptionSet) {
2975
2966
  this.handleCandidateFromServer(candidate);
2976
2967
  }
@@ -2995,11 +2986,15 @@ class WebRtcPlayerService extends SubService {
2995
2986
  if (!this.pcClient) {
2996
2987
  this.setupPeerConnection(this.cfg);
2997
2988
  }
2989
+ const pc = this.pcClient;
2990
+ if (!pc) {
2991
+ return;
2992
+ }
2998
2993
  // Offer already defines media transceivers — don't add extras
2999
- await this.pcClient?.setRemoteDescription(offer);
2994
+ await pc.setRemoteDescription(offer);
3000
2995
  this.remoteDescriptionSet = true;
3001
2996
  this.flushIceCandidateBuffer();
3002
- await this.createAnswer(this.pcClient);
2997
+ await this.createAnswer(pc);
3003
2998
  }
3004
2999
  catch (e) {
3005
3000
  Logger.error('Failed to handle server offer', e);
@@ -3044,10 +3039,16 @@ class WebRtcPlayerService extends SubService {
3044
3039
  if (!this.pcClient) {
3045
3040
  this.setupPeerConnection(this.cfg);
3046
3041
  }
3042
+ const pc = this.pcClient;
3043
+ if (!pc) {
3044
+ return;
3045
+ }
3047
3046
  // Client offers: transceivers first, then DC, then offer
3048
- await this.setupTransceiversAsync(this.pcClient);
3049
- this.dcClient = this.createDataChannel(this.pcClient, 'UnrealDcConnection', { ordered: true });
3050
- await this.createOffer(this.pcClient);
3047
+ await this.setupTransceiversAsync(pc);
3048
+ this.dcClient = this.createDataChannel(pc, 'UnrealDcConnection', {
3049
+ ordered: true,
3050
+ });
3051
+ await this.createOffer(pc);
3051
3052
  }
3052
3053
  catch (e) {
3053
3054
  Logger.error('Failed to create WebRTC offer', e);
@@ -3106,7 +3107,7 @@ class WebRtcPlayerService extends SubService {
3106
3107
  * RFC 6184 that encodes three H.264 parameters:
3107
3108
  * - **Profile IDC** (`XX`) — the H.264 profile.
3108
3109
  * - **Profile IOP** (`YY`) — constraint flags that further narrow the profile.
3109
- * - **Level IDC** (`ZZ`) — the level, which caps resolution/framerate/bitrate.
3110
+ * - **Level IDC** (`ZZ`) — the level which caps resolution/framerate/bitrate.
3110
3111
  *
3111
3112
  * ### H.264 profiles relevant to Unreal Engine Pixel Streaming
3112
3113
  *
@@ -3124,12 +3125,12 @@ class WebRtcPlayerService extends SubService {
3124
3125
  * | `f4001f` | High 4:4:4 Predictive | 3.1 | Lossless capable; rare in browsers |
3125
3126
  *
3126
3127
  * UE Pixel Streaming defaults to Constrained Baseline (`42e01f`) or High (`6400xx`)
3127
- * depending on engine version and `WebRTC.H264Profile` CVar.
3128
+ * depending on the engine version and `WebRTC.H264Profile` CVar.
3128
3129
  *
3129
3130
  * We prioritize `42e01f` because it guarantees hardware decoding on
3130
3131
  * virtually all client devices (desktop, mobile, embedded), avoiding
3131
3132
  * fallback to software decoding or negotiation of a higher profile
3132
- * that the client GPU may not accelerate.
3133
+ * that the client GPU may not speed up.
3133
3134
  */
3134
3135
  prioritizeH264Profile(sdp, profileLevelId) {
3135
3136
  const lines = sdp.split('\r\n');
@@ -3472,10 +3473,10 @@ class WebRtcPlayerService extends SubService {
3472
3473
  Logger.error("Couldn't create answer", e);
3473
3474
  }
3474
3475
  }
3475
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: WebRtcPlayerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
3476
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: WebRtcPlayerService }); }
3476
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: WebRtcPlayerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
3477
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: WebRtcPlayerService }); }
3477
3478
  }
3478
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: WebRtcPlayerService, decorators: [{
3479
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: WebRtcPlayerService, decorators: [{
3479
3480
  type: Injectable
3480
3481
  }], ctorParameters: () => [] });
3481
3482
 
@@ -3538,7 +3539,7 @@ class FileReceiverService extends SubService {
3538
3539
  }
3539
3540
  this.store
3540
3541
  .select(unrealFeature.selectViewportReady)
3541
- .pipe(filter(Truthy))
3542
+ .pipe(filter(Truthy), takeUntil(this.destroy$))
3542
3543
  .subscribe(() => this.reset());
3543
3544
  }
3544
3545
  reset() {
@@ -3663,7 +3664,7 @@ class AggregatorService extends SubService {
3663
3664
  initialize() {
3664
3665
  this.store
3665
3666
  .select(unrealFeature.selectCirrusConnected)
3666
- .pipe(distinctUntilChanged())
3667
+ .pipe(distinctUntilChanged(), takeUntil(this.destroy$))
3667
3668
  .subscribe((connected) => {
3668
3669
  this.resetResponseList();
3669
3670
  if (connected) {
@@ -3673,7 +3674,9 @@ class AggregatorService extends SubService {
3673
3674
  const unlockViewport$ = this.store
3674
3675
  .select(unrealFeature.selectViewportReady)
3675
3676
  .pipe(filter(Truthy));
3676
- merge(unlockViewport$, this.disconnect$).subscribe(() => {
3677
+ merge(unlockViewport$, this.disconnect$)
3678
+ .pipe(takeUntil(this.destroy$))
3679
+ .subscribe(() => {
3677
3680
  this.removeLoadScreen();
3678
3681
  });
3679
3682
  if (!this.selectStreamConfig()?.autoStart ||
@@ -3685,22 +3688,6 @@ class AggregatorService extends SubService {
3685
3688
  addResponseEventListener(name, listener) {
3686
3689
  this.responseEventListeners.set(name, listener);
3687
3690
  }
3688
- showOnScreenKeyboard(command) {
3689
- if (command.showOnScreenKeyboard) {
3690
- // Show the 'edit text' button.
3691
- //editTextButton.classList.remove('hiddenState');
3692
- // Place the 'edit text' button near the UE4 input widget.
3693
- //let pos = unquantizeAndDenormalizeUnsigned(command.x, command.y);
3694
- //editTextButton.style.top = pos.y.toString() + 'px';
3695
- //editTextButton.style.left = (pos.x - 40).toString() + 'px';
3696
- }
3697
- else {
3698
- // Hide the 'edit text' button.
3699
- //editTextButton.classList.add('hiddenState');
3700
- // Hide the on-screen keyboard.
3701
- // hiddenInput.blur();
3702
- }
3703
- }
3704
3691
  removeLoadScreen() {
3705
3692
  document
3706
3693
  .getElementById(this.unrealConfig?.screenLockerContainerId ||
@@ -3768,12 +3755,8 @@ class AggregatorService extends SubService {
3768
3755
  break;
3769
3756
  }
3770
3757
  case ToClientMessage.Command: {
3771
- const commandAsString = decodeData(anyData);
3772
- Logger.colored(...COLOR_CODES.FROM_UNREAL, commandAsString);
3773
- const command = JSON.parse(commandAsString);
3774
- if (command.command === 'onScreenKeyboard') {
3775
- this.showOnScreenKeyboard(command);
3776
- }
3758
+ const command = JSON.parse(decodeData(anyData));
3759
+ Logger.colored(...COLOR_CODES.FROM_UNREAL, command);
3777
3760
  break;
3778
3761
  }
3779
3762
  case ToClientMessage.FileExtension:
@@ -3819,10 +3802,10 @@ class AggregatorService extends SubService {
3819
3802
  Logger.warn(`unrecognized data received, packet ID ${view[0]}`);
3820
3803
  }
3821
3804
  }
3822
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: AggregatorService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
3823
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: AggregatorService }); }
3805
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: AggregatorService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
3806
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: AggregatorService }); }
3824
3807
  }
3825
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: AggregatorService, decorators: [{
3808
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: AggregatorService, decorators: [{
3826
3809
  type: Injectable
3827
3810
  }], ctorParameters: () => [] });
3828
3811
 
@@ -3873,18 +3856,12 @@ class ConsoleExtensionsService extends SubService {
3873
3856
  return true;
3874
3857
  };
3875
3858
  window.setResolution = ({ w, h }) => {
3859
+ const resolution = { x: w, y: h };
3876
3860
  this.#commandsSender.sendCommandToUnreal({
3877
3861
  command: MetaBoxCommand.FChangeResolutionCommand,
3878
- payload: { resolution: { x: w, y: h } },
3862
+ payload: { resolution },
3879
3863
  });
3880
- return 'LowBandwidth => toggled';
3881
- };
3882
- window.toggleBandwidth = () => {
3883
- this.store
3884
- .select(unrealFeature.selectLowBandwidth)
3885
- .pipe(take(1))
3886
- .subscribe((isLow) => this.store.dispatch(changeLowBandwidth({ lowBandwidth: !isLow })));
3887
- return 'LowBandwidth => toggled';
3864
+ return `Resolution set to ${resolution.x}x${resolution.y}`;
3888
3865
  };
3889
3866
  window.setMaxFps = (maxFps) => {
3890
3867
  this.store.dispatch(setMaxFps({ maxFps }));
@@ -3910,10 +3887,10 @@ class ConsoleExtensionsService extends SubService {
3910
3887
  Logger.info('setWarnTime() => set time to appear the AFK window.');
3911
3888
  Logger.info('emitCommand(command) => send command to Unreal Engine.');
3912
3889
  }
3913
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: ConsoleExtensionsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
3914
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: ConsoleExtensionsService }); }
3890
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: ConsoleExtensionsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
3891
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: ConsoleExtensionsService }); }
3915
3892
  }
3916
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: ConsoleExtensionsService, decorators: [{
3893
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: ConsoleExtensionsService, decorators: [{
3917
3894
  type: Injectable
3918
3895
  }], ctorParameters: () => [] });
3919
3896
 
@@ -3929,10 +3906,10 @@ class DevModeService {
3929
3906
  localStorage.removeItem('devMode');
3930
3907
  }
3931
3908
  }
3932
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: DevModeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
3933
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: DevModeService, providedIn: 'root' }); }
3909
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: DevModeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
3910
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: DevModeService, providedIn: 'root' }); }
3934
3911
  }
3935
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: DevModeService, decorators: [{
3912
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: DevModeService, decorators: [{
3936
3913
  type: Injectable,
3937
3914
  args: [{
3938
3915
  providedIn: 'root',
@@ -3959,7 +3936,9 @@ class UnrealCommunicatorService {
3959
3936
  // of the store selector. The selector reads the current state and can fire on
3960
3937
  // stale `true` from a previous session when switching components (Angular
3961
3938
  // creates the new component before destroying the old one).
3962
- this.actions$.pipe(ofType(dataChannelConnected)).subscribe(() => {
3939
+ this.actions$
3940
+ .pipe(ofType(dataChannelConnected), takeUntil$1(this.destroy$))
3941
+ .subscribe(() => {
3963
3942
  this.requestInitialSettings();
3964
3943
  this.requestQualityControl();
3965
3944
  });
@@ -4043,10 +4022,10 @@ class UnrealCommunicatorService {
4043
4022
  requestQualityControl() {
4044
4023
  this.sendInputData(new Uint8Array([MessageType.RequestQualityControl]).buffer);
4045
4024
  }
4046
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: UnrealCommunicatorService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
4047
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: UnrealCommunicatorService }); }
4025
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: UnrealCommunicatorService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
4026
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: UnrealCommunicatorService }); }
4048
4027
  }
4049
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: UnrealCommunicatorService, decorators: [{
4028
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: UnrealCommunicatorService, decorators: [{
4050
4029
  type: Injectable
4051
4030
  }], ctorParameters: () => [] });
4052
4031
 
@@ -4075,7 +4054,15 @@ class InputService extends SubService {
4075
4054
  KeyboardNumericCode.NumpadDecimal,
4076
4055
  ];
4077
4056
  this.availableKeys = this.defaultKeys;
4057
+ this.options = InputOptions;
4078
4058
  this.reInit$ = new Subject();
4059
+ this.deactivatedVideoTrigger$ = NEVER;
4060
+ this.normalizeAndQuantizeUnsigned = (_x, _y) => ({
4061
+ inRange: false,
4062
+ x: 65535,
4063
+ y: 65535,
4064
+ });
4065
+ this.normalizeAndQuantizeSigned = (_x, _y) => ({ x: 0, y: 0 });
4079
4066
  this.videoBoundingRect = new DOMRect(0, 0, 1, 1);
4080
4067
  this.fingerIds = new Map();
4081
4068
  this.maxByteValue = 255;
@@ -4098,11 +4085,11 @@ class InputService extends SubService {
4098
4085
  const visibilityVisibleTrigger$ = fromEvent(document, 'visibilitychange').pipe(map$1(() => document.visibilityState === 'visible'), filter(Truthy), startWith(true));
4099
4086
  this.deactivatedVideoTrigger$ = merge(this.disconnect$, this.reInit$, visibilityHiddenTrigger$);
4100
4087
  merge(visibilityVisibleTrigger$, this.store.select(unrealFeature.selectViewportReady).pipe(filter(Truthy)))
4101
- .pipe(filter(() => !!this.video))
4088
+ .pipe(filter(() => !!this.video), takeUntil$1(this.destroy$))
4102
4089
  .subscribe(() => this.setup());
4103
4090
  }
4104
4091
  useKeyboardKeys(keys) {
4105
- this.availableKeys = keys || [];
4092
+ this.availableKeys = keys;
4106
4093
  }
4107
4094
  get video() {
4108
4095
  return this.videoService.video;
@@ -4164,57 +4151,53 @@ class InputService extends SubService {
4164
4151
  };
4165
4152
  }
4166
4153
  setupNormalizeAndQuantize(videoElement) {
4167
- if (videoElement) {
4168
- this.videoBoundingRect = videoElement.getBoundingClientRect();
4169
- this.offsetParams = this.getObjectFitOffset(videoElement);
4170
- // Unsigned XY positions are the ratio (0.0..1.0) along a viewport axis,
4171
- // quantized into an uint16 (0..65536).
4172
- // Signed XY deltas are the ratio (-1.0..1.0) along a viewport axis,
4173
- // quantized into an int16 (-32767..32767).
4174
- // This allows the browser viewport and client viewport to have a different
4175
- // size.
4176
- // Hack: Currently we set an out-of-range position to an extreme (65535)
4177
- // as we can't yet accurately detect mouse enter and leave events
4178
- // precisely inside a video with an aspect ratio which causes mattes.
4179
- this.normalizeAndQuantizeUnsigned = (x, y) => {
4180
- const normalizedX = x / videoElement.clientWidth;
4181
- const normalizedY = y / videoElement.clientHeight;
4182
- if (normalizedX < 0.0 ||
4183
- normalizedX > 1.0 ||
4184
- normalizedY < 0.0 ||
4185
- normalizedY > 1.0) {
4186
- return {
4187
- inRange: false,
4188
- x: 65535,
4189
- y: 65535,
4190
- };
4191
- }
4192
- else {
4193
- return {
4194
- inRange: true,
4195
- x: normalizedX * 65536,
4196
- y: normalizedY * 65536,
4197
- };
4198
- }
4199
- };
4200
- // Signed.
4201
- this.normalizeAndQuantizeSigned = (x, y) => {
4202
- const normalizedX = x / (0.5 * videoElement.clientWidth);
4203
- const normalizedY = y / (0.5 * videoElement.clientHeight);
4154
+ this.videoBoundingRect = videoElement.getBoundingClientRect();
4155
+ this.offsetParams = this.getObjectFitOffset(videoElement);
4156
+ // Unsigned XY positions are the ratio (0.0..1.0) along a viewport axis,
4157
+ // quantized into an uint16 (0..65536).
4158
+ // Signed XY deltas are the ratio (-1.0..1.0) along a viewport axis,
4159
+ // quantized into an int16 (-32767..32767).
4160
+ // This allows the browser viewport and client viewport to have a different
4161
+ // size.
4162
+ // Hack: Currently, we set an out-of-range position to an extreme (65535)
4163
+ // as we can't yet accurately detect mouse enter and leave events
4164
+ // precisely inside a video with an aspect ratio which causes mattes.
4165
+ this.normalizeAndQuantizeUnsigned = (x, y) => {
4166
+ const normalizedX = x / videoElement.clientWidth;
4167
+ const normalizedY = y / videoElement.clientHeight;
4168
+ if (normalizedX < 0.0 ||
4169
+ normalizedX > 1.0 ||
4170
+ normalizedY < 0.0 ||
4171
+ normalizedY > 1.0) {
4172
+ return {
4173
+ inRange: false,
4174
+ x: 65535,
4175
+ y: 65535,
4176
+ };
4177
+ }
4178
+ else {
4204
4179
  return {
4205
- x: normalizedX * 32767,
4206
- y: normalizedY * 32767,
4180
+ inRange: true,
4181
+ x: normalizedX * 65536,
4182
+ y: normalizedY * 65536,
4207
4183
  };
4184
+ }
4185
+ };
4186
+ // Signed.
4187
+ this.normalizeAndQuantizeSigned = (x, y) => {
4188
+ const normalizedX = x / (0.5 * videoElement.clientWidth);
4189
+ const normalizedY = y / (0.5 * videoElement.clientHeight);
4190
+ return {
4191
+ x: normalizedX * 32767,
4192
+ y: normalizedY * 32767,
4208
4193
  };
4209
- }
4194
+ };
4210
4195
  }
4211
4196
  sendInputData(data) {
4212
4197
  this.commandsSender.sendInputData(data);
4213
4198
  }
4214
4199
  registerInputs(video) {
4215
- merge(this.store
4216
- .select(unrealFeature.selectLowBandwidth)
4217
- .pipe(distinctUntilChanged(), filter(Truthy)), fromEvent(window, 'blur'))
4200
+ fromEvent(window, 'blur')
4218
4201
  .pipe(filter(() => !!video), takeUntil$1(this.deactivatedVideoTrigger$))
4219
4202
  .subscribe(() => this.releaseMouseButtons(255, 0, 0));
4220
4203
  this.registerMouseEnterAndLeaveEvents(video);
@@ -4248,11 +4231,12 @@ class InputService extends SubService {
4248
4231
  this.fingerIds.set(touch.identifier, finger);
4249
4232
  };
4250
4233
  const forgetTouch = (touch) => {
4251
- this.fingers.push(this.fingerIds.get(touch.identifier));
4234
+ const finger = this.fingerIds.get(touch.identifier);
4235
+ if (finger) {
4236
+ this.fingers.push(finger);
4237
+ }
4252
4238
  // Sort array back into descending order. This means if finger '1' were to lift after finger '0', we would ensure that 0 will be the first index to pop
4253
- this.fingers.sort(function (a, b) {
4254
- return b - a;
4255
- });
4239
+ this.fingers.sort((a, b) => b - a);
4256
4240
  this.fingerIds.delete(touch.identifier);
4257
4241
  };
4258
4242
  const emitTouchData = (type, touches) => {
@@ -4263,21 +4247,24 @@ class InputService extends SubService {
4263
4247
  const touch = touches[t];
4264
4248
  const { x, y } = this.getTouchPositionWithOffset(touch);
4265
4249
  Logger.info(`F${this.fingerIds.get(touch.identifier)}=(${x}, ${y})`);
4266
- const coord = this.normalizeAndQuantizeUnsigned(x, y);
4250
+ const normalizeCoordinates = this.normalizeAndQuantizeUnsigned(x, y);
4267
4251
  const data = new DataView(new ArrayBuffer(9));
4268
4252
  data.setUint8(0, type);
4269
4253
  data.setUint8(1, numTouches);
4270
- data.setUint16(byte, coord.x, true);
4254
+ data.setUint16(byte, normalizeCoordinates.x, true);
4271
4255
  byte += 2;
4272
- data.setUint16(byte, coord.y, true);
4256
+ data.setUint16(byte, normalizeCoordinates.y, true);
4273
4257
  byte += 2;
4274
- data.setUint8(byte, this.fingerIds.get(touch.identifier));
4258
+ const finger = this.fingerIds.get(touch.identifier);
4259
+ if (finger) {
4260
+ data.setUint8(byte, finger);
4261
+ }
4275
4262
  byte += 1;
4276
4263
  // force is between 0.0 and 1.0, so quantize into byte.
4277
4264
  // iOs bug: Forced to be always 0.5 on all devices as we do not use forced touch any way;/
4278
4265
  data.setUint8(byte, this.maxByteValue * 0.5);
4279
4266
  byte += 1;
4280
- data.setUint8(byte, coord.inRange ? 1 : 0);
4267
+ data.setUint8(byte, normalizeCoordinates.inRange ? 1 : 0);
4281
4268
  byte += 1;
4282
4269
  this.sendInputData(data.buffer);
4283
4270
  }
@@ -4320,42 +4307,42 @@ class InputService extends SubService {
4320
4307
  window.focus();
4321
4308
  }
4322
4309
  emitMouseMove(x, y, deltaX, deltaY) {
4323
- const coord = this.normalizeAndQuantizeUnsigned(x, y);
4310
+ const normalizeCoordinates = this.normalizeAndQuantizeUnsigned(x, y);
4324
4311
  const delta = this.normalizeAndQuantizeSigned(deltaX, deltaY);
4325
- const Data = new DataView(new ArrayBuffer(9));
4326
- Data.setUint8(0, MessageType.MouseMove);
4327
- Data.setUint16(1, coord.x, true);
4328
- Data.setUint16(3, coord.y, true);
4329
- Data.setInt16(5, delta.x, true);
4330
- Data.setInt16(7, delta.y, true);
4331
- this.sendInputData(Data.buffer);
4312
+ const data = new DataView(new ArrayBuffer(9));
4313
+ data.setUint8(0, MessageType.MouseMove);
4314
+ data.setUint16(1, normalizeCoordinates.x, true);
4315
+ data.setUint16(3, normalizeCoordinates.y, true);
4316
+ data.setInt16(5, delta.x, true);
4317
+ data.setInt16(7, delta.y, true);
4318
+ this.sendInputData(data.buffer);
4332
4319
  }
4333
4320
  emitMouseDown(button, x, y) {
4334
- const coord = this.normalizeAndQuantizeUnsigned(x, y);
4335
- const Data = new DataView(new ArrayBuffer(6));
4336
- Data.setUint8(0, MessageType.MouseDown);
4337
- Data.setUint8(1, button);
4338
- Data.setUint16(2, coord.x, true);
4339
- Data.setUint16(4, coord.y, true);
4340
- this.sendInputData(Data.buffer);
4321
+ const normalizeCoordinates = this.normalizeAndQuantizeUnsigned(x, y);
4322
+ const data = new DataView(new ArrayBuffer(6));
4323
+ data.setUint8(0, MessageType.MouseDown);
4324
+ data.setUint8(1, button);
4325
+ data.setUint16(2, normalizeCoordinates.x, true);
4326
+ data.setUint16(4, normalizeCoordinates.y, true);
4327
+ this.sendInputData(data.buffer);
4341
4328
  }
4342
4329
  emitMouseUp(button, x, y) {
4343
- const coord = this.normalizeAndQuantizeUnsigned(x, y);
4344
- const Data = new DataView(new ArrayBuffer(6));
4345
- Data.setUint8(0, MessageType.MouseUp);
4346
- Data.setUint8(1, button);
4347
- Data.setUint16(2, coord.x, true);
4348
- Data.setUint16(4, coord.y, true);
4349
- this.sendInputData(Data.buffer);
4330
+ const normalizeCoordinates = this.normalizeAndQuantizeUnsigned(x, y);
4331
+ const data = new DataView(new ArrayBuffer(6));
4332
+ data.setUint8(0, MessageType.MouseUp);
4333
+ data.setUint8(1, button);
4334
+ data.setUint16(2, normalizeCoordinates.x, true);
4335
+ data.setUint16(4, normalizeCoordinates.y, true);
4336
+ this.sendInputData(data.buffer);
4350
4337
  }
4351
4338
  emitMouseWheel(delta, x, y) {
4352
- const coord = this.normalizeAndQuantizeUnsigned(x, y);
4353
- const Data = new DataView(new ArrayBuffer(7));
4354
- Data.setUint8(0, MessageType.MouseWheel);
4355
- Data.setInt16(1, delta, true);
4356
- Data.setUint16(3, coord.x, true);
4357
- Data.setUint16(5, coord.y, true);
4358
- this.sendInputData(Data.buffer);
4339
+ const normalizeCoordinates = this.normalizeAndQuantizeUnsigned(x, y);
4340
+ const data = new DataView(new ArrayBuffer(7));
4341
+ data.setUint8(0, MessageType.MouseWheel);
4342
+ data.setInt16(1, delta, true);
4343
+ data.setUint16(3, normalizeCoordinates.x, true);
4344
+ data.setUint16(5, normalizeCoordinates.y, true);
4345
+ this.sendInputData(data.buffer);
4359
4346
  }
4360
4347
  releaseMouseButtons(buttons, x, y) {
4361
4348
  if (buttons & MouseButtonsMask.PrimaryButton) {
@@ -4374,7 +4361,7 @@ class InputService extends SubService {
4374
4361
  this.emitMouseUp(MouseButton.FifthButton, x, y);
4375
4362
  }
4376
4363
  }
4377
- // If the user has any mouse buttons pressed then press them again.
4364
+ // If the user has any mouse buttons pressed, then press them again.
4378
4365
  pressMouseButtons(buttons, x, y) {
4379
4366
  if (buttons & MouseButtonsMask.PrimaryButton) {
4380
4367
  this.emitMouseDown(MouseButton.MainButton, x, y);
@@ -4393,58 +4380,53 @@ class InputService extends SubService {
4393
4380
  }
4394
4381
  }
4395
4382
  registerHoveringMouseEvents(htmlVideoElement) {
4396
- htmlVideoElement.onmousemove = (e) => {
4383
+ fromEvent(htmlVideoElement, 'mousemove')
4384
+ .pipe(takeUntil$1(this.deactivatedVideoTrigger$))
4385
+ .subscribe((e) => {
4397
4386
  const { x, y } = this.getMousePositionWithOffset(e);
4398
4387
  this.emitMouseMove(x, y, e.movementX, e.movementY);
4399
4388
  e.preventDefault();
4400
- };
4401
- htmlVideoElement.onmousedown = (e) => {
4389
+ });
4390
+ fromEvent(htmlVideoElement, 'mousedown')
4391
+ .pipe(takeUntil$1(this.deactivatedVideoTrigger$))
4392
+ .subscribe((e) => {
4402
4393
  const { x, y } = this.getMousePositionWithOffset(e);
4403
4394
  this.focusWindow();
4404
4395
  this.emitMouseDown(e.button, x, y);
4405
4396
  e.preventDefault();
4406
- };
4407
- htmlVideoElement.onmouseup = (e) => {
4397
+ });
4398
+ fromEvent(htmlVideoElement, 'mouseup')
4399
+ .pipe(takeUntil$1(this.deactivatedVideoTrigger$))
4400
+ .subscribe((e) => {
4408
4401
  const { x, y } = this.getMousePositionWithOffset(e);
4409
4402
  this.emitMouseUp(e.button, x, y);
4410
4403
  e.preventDefault();
4411
- };
4412
- // When the context menu is shown, then it is safest to release the button
4413
- // pressed when the event happened. This will guarantee we will
4414
- // get at least one mouse up corresponding to a mouse down event. Otherwise,
4415
- // the mouse can get stuck.
4416
- // https://github.com/facebook/react/issues/5531
4417
- // playerElement.oncontextmenu = (e) => {
4418
- // e.preventDefault();
4419
- // this.emitMouseUp(e.button, e.offsetX, e.offsetY);
4420
- // };
4404
+ });
4421
4405
  fromEvent(htmlVideoElement, 'wheel')
4422
4406
  .pipe(takeUntil$1(this.deactivatedVideoTrigger$))
4423
4407
  .subscribe((e) => {
4424
4408
  this.emitMouseWheel(e.deltaY * -120, e.offsetX, e.offsetY);
4425
4409
  e.preventDefault();
4426
4410
  });
4427
- htmlVideoElement.pressMouseButtons = (e) => {
4428
- this.pressMouseButtons(e.buttons, e.offsetX, e.offsetY);
4429
- };
4430
- htmlVideoElement.releaseMouseButtons = (e) => {
4431
- this.releaseMouseButtons(e.buttons, e.offsetX, e.offsetY);
4432
- };
4433
4411
  }
4434
4412
  registerMouseEnterAndLeaveEvents(htmlVideoElement) {
4435
- htmlVideoElement.onmouseenter = (e) => {
4413
+ fromEvent(htmlVideoElement, 'mouseenter')
4414
+ .pipe(takeUntil$1(this.deactivatedVideoTrigger$))
4415
+ .subscribe((e) => {
4436
4416
  this.focusWindow();
4437
- const Data = new DataView(new ArrayBuffer(1));
4438
- Data.setUint8(0, MessageType.MouseEnter);
4439
- this.sendInputData(Data.buffer);
4440
- htmlVideoElement.pressMouseButtons(e);
4441
- };
4442
- htmlVideoElement.onmouseleave = (e) => {
4443
- const Data = new DataView(new ArrayBuffer(1));
4444
- Data.setUint8(0, MessageType.MouseLeave);
4445
- this.sendInputData(Data.buffer);
4446
- htmlVideoElement.releaseMouseButtons(e);
4447
- };
4417
+ const data = new DataView(new ArrayBuffer(1));
4418
+ data.setUint8(0, MessageType.MouseEnter);
4419
+ this.sendInputData(data.buffer);
4420
+ this.pressMouseButtons(e.buttons, e.offsetX, e.offsetY);
4421
+ });
4422
+ fromEvent(htmlVideoElement, 'mouseleave')
4423
+ .pipe(takeUntil$1(this.deactivatedVideoTrigger$))
4424
+ .subscribe((e) => {
4425
+ const data = new DataView(new ArrayBuffer(1));
4426
+ data.setUint8(0, MessageType.MouseLeave);
4427
+ this.sendInputData(data.buffer);
4428
+ this.releaseMouseButtons(e.buttons, e.offsetX, e.offsetY);
4429
+ });
4448
4430
  }
4449
4431
  // Browser keys do not have a charCode so we only need to test keyCode.
4450
4432
  isKeyCodeBrowserKey(keyCode) {
@@ -4638,10 +4620,10 @@ class RegionsPingService {
4638
4620
  throw error;
4639
4621
  }
4640
4622
  }
4641
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: RegionsPingService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
4642
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: RegionsPingService }); }
4623
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: RegionsPingService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
4624
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: RegionsPingService }); }
4643
4625
  }
4644
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: RegionsPingService, decorators: [{
4626
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: RegionsPingService, decorators: [{
4645
4627
  type: Injectable
4646
4628
  }] });
4647
4629
 
@@ -4671,11 +4653,9 @@ class StreamStatusTelemetryService {
4671
4653
  return this.videoService.videoStats$.pipe(throttleTime(5000, asyncScheduler, {
4672
4654
  leading: true,
4673
4655
  trailing: true,
4674
- }), withLatestFrom(this.store.select(unrealFeature.selectAwsInstance), this.store.select(unrealFeature.selectLowBandwidth), this.store.select(unrealFeature.selectLowBandwidthStats), signalingReadyTime$, viewPortReadyTime$), map$1(([data, signalingServer, isLowBandwidth, lbmStats, signalingReadyTime, viewPortReadyTime,]) => this.mapEventData({
4656
+ }), withLatestFrom(this.store.select(unrealFeature.selectAwsInstance), signalingReadyTime$, viewPortReadyTime$), map$1(([data, signalingServer, signalingReadyTime, viewPortReadyTime,]) => this.mapEventData({
4675
4657
  data,
4676
4658
  signalingServer,
4677
- isLowBandwidth,
4678
- lbmStats,
4679
4659
  startTimeUtc,
4680
4660
  signalingReadyTime,
4681
4661
  viewPortReadyTime,
@@ -4692,7 +4672,7 @@ class StreamStatusTelemetryService {
4692
4672
  return readyState$.pipe(filter(Boolean), take$1(1), map$1(() => Date.now() - startedAt), startWith$1(undefined));
4693
4673
  }
4694
4674
  mapEventData(params) {
4695
- const { data, signalingServer, isLowBandwidth, lbmStats, startTimeUtc, signalingReadyTime, viewPortReadyTime, } = params;
4675
+ const { data, signalingServer, startTimeUtc, signalingReadyTime, viewPortReadyTime, } = params;
4696
4676
  const { bitrate, VideoEncoderQP, framesPerSecond, frameWidth, frameHeight, jitter, packetsLost, currentRoundTripTime, runTime, } = data.aggregatedStats;
4697
4677
  return {
4698
4678
  startTimeUtc,
@@ -4701,7 +4681,6 @@ class StreamStatusTelemetryService {
4701
4681
  runTime,
4702
4682
  frameWidth,
4703
4683
  frameHeight,
4704
- isLowBandwidth,
4705
4684
  client: signalingServer.wsUrl,
4706
4685
  pixelRatio: window.devicePixelRatio,
4707
4686
  framesPerSecond,
@@ -4709,7 +4688,6 @@ class StreamStatusTelemetryService {
4709
4688
  packetsLost,
4710
4689
  instanceName: signalingServer.instanceName,
4711
4690
  bitrate,
4712
- lbmStats,
4713
4691
  rtt: currentRoundTripTime,
4714
4692
  VideoEncoderQP,
4715
4693
  };
@@ -4717,10 +4695,10 @@ class StreamStatusTelemetryService {
4717
4695
  trackEventToMixPanel(data) {
4718
4696
  this.store.dispatch(trackMixpanelEvent({ event: 'streamStatus', data }));
4719
4697
  }
4720
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: StreamStatusTelemetryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
4721
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: StreamStatusTelemetryService }); }
4698
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: StreamStatusTelemetryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
4699
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: StreamStatusTelemetryService }); }
4722
4700
  }
4723
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: StreamStatusTelemetryService, decorators: [{
4701
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: StreamStatusTelemetryService, decorators: [{
4724
4702
  type: Injectable
4725
4703
  }], ctorParameters: () => [] });
4726
4704
 
@@ -4953,10 +4931,10 @@ class StreamStatusTelemetryService2 {
4953
4931
  ],
4954
4932
  },
4955
4933
  ];
4956
- if (this.unrealConfig?.playwright)
4957
- return;
4958
- if (!this.unrealConfig?.streamTelemetryV2)
4934
+ if (this.unrealConfig?.playwright ||
4935
+ !this.unrealConfig?.streamTelemetryV2) {
4959
4936
  return;
4937
+ }
4960
4938
  this.init();
4961
4939
  }
4962
4940
  getOrCreateUserId() {
@@ -5372,10 +5350,10 @@ class StreamStatusTelemetryService2 {
5372
5350
  data: { userId: this.userId, ...data },
5373
5351
  }));
5374
5352
  }
5375
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: StreamStatusTelemetryService2, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
5376
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: StreamStatusTelemetryService2 }); }
5353
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: StreamStatusTelemetryService2, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
5354
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: StreamStatusTelemetryService2 }); }
5377
5355
  }
5378
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: StreamStatusTelemetryService2, decorators: [{
5356
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: StreamStatusTelemetryService2, decorators: [{
5379
5357
  type: Injectable
5380
5358
  }], ctorParameters: () => [] });
5381
5359
  // ──────────────────────────────────────────────────────────────────
@@ -5457,30 +5435,29 @@ class FileHandlerService {
5457
5435
  setTimeout(() => sender(out), 0);
5458
5436
  return observable;
5459
5437
  }
5460
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: FileHandlerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
5461
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: FileHandlerService }); }
5438
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: FileHandlerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
5439
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: FileHandlerService }); }
5462
5440
  }
5463
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: FileHandlerService, decorators: [{
5441
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: FileHandlerService, decorators: [{
5464
5442
  type: Injectable
5465
5443
  }] });
5466
5444
 
5467
5445
  class AnalyticsService extends SubService {
5468
- #store = inject(Store);
5469
5446
  /** Signals monitorMouse inner subscriptions to stop (viewport lost / new session). */
5470
5447
  #stopMouse$ = new Subject();
5471
5448
  sendEventToMixPanel(name, params = {}) {
5472
5449
  Logger.log('Analytics Event:', name, params);
5473
- this.#store.dispatch(saveAnalyticsEvent({ event: { name, params } }));
5450
+ this.store.dispatch(saveAnalyticsEvent({ event: { name, params } }));
5474
5451
  }
5475
5452
  constructor() {
5476
5453
  super();
5477
- const abortSignal$ = this.#store
5454
+ const abortSignal$ = this.store
5478
5455
  .select(unrealFeature.selectViewportReady)
5479
5456
  .pipe(filter$1(Falsy), map(() => void 0));
5480
- this.#store
5457
+ this.store
5481
5458
  .select(unrealFeature.selectViewportReady)
5482
- .pipe(distinctUntilChanged(), filter$1(Truthy), tap(() => this.monitorMouse(abortSignal$)))
5483
- .subscribe();
5459
+ .pipe(distinctUntilChanged(), filter$1(Truthy), takeUntil$1(this.destroy$))
5460
+ .subscribe(() => this.monitorMouse(abortSignal$));
5484
5461
  }
5485
5462
  monitorMouse(abortSignal$) {
5486
5463
  this.#stopMouse$.next();
@@ -5549,10 +5526,10 @@ class AnalyticsService extends SubService {
5549
5526
  });
5550
5527
  });
5551
5528
  }
5552
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: AnalyticsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
5553
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: AnalyticsService }); }
5529
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: AnalyticsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
5530
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: AnalyticsService }); }
5554
5531
  }
5555
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: AnalyticsService, decorators: [{
5532
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: AnalyticsService, decorators: [{
5556
5533
  type: Injectable
5557
5534
  }], ctorParameters: () => [] });
5558
5535
 
@@ -5919,10 +5896,10 @@ class FpsMonitorService {
5919
5896
  }
5920
5897
  return { fps: fps / len, bitrate: bitrate / len, qp: qp / len };
5921
5898
  }
5922
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: FpsMonitorService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
5923
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: FpsMonitorService }); }
5899
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: FpsMonitorService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
5900
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: FpsMonitorService }); }
5924
5901
  }
5925
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: FpsMonitorService, decorators: [{
5902
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: FpsMonitorService, decorators: [{
5926
5903
  type: Injectable
5927
5904
  }], ctorParameters: () => [] });
5928
5905
 
@@ -5953,12 +5930,7 @@ class UnrealCallbackService {
5953
5930
  * ```
5954
5931
  */
5955
5932
  fromUnrealCallback(command) {
5956
- return this.actions$.pipe(ofType(unrealCommandCallback), map$1(({ json }) => {
5957
- if ('commandCallback' in json) {
5958
- return json.commandCallback;
5959
- }
5960
- return json;
5961
- }), filter((json) => json.command === command), map$1((data) => ({
5933
+ return this.actions$.pipe(ofType(unrealCommandCallback), map$1(({ json }) => 'commandCallback' in json ? json.commandCallback : json), filter((json) => json.command === command), map$1((data) => ({
5962
5934
  command: data.command,
5963
5935
  payload: 'payload' in data ? data.payload : null,
5964
5936
  })));
@@ -5993,10 +5965,10 @@ class UnrealCallbackService {
5993
5965
  setTimeout(() => sender({ ...data, correlationId }), 0);
5994
5966
  return observable;
5995
5967
  }
5996
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: UnrealCallbackService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
5997
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: UnrealCallbackService }); }
5968
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: UnrealCallbackService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
5969
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: UnrealCallbackService }); }
5998
5970
  }
5999
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: UnrealCallbackService, decorators: [{
5971
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: UnrealCallbackService, decorators: [{
6000
5972
  type: Injectable
6001
5973
  }] });
6002
5974
 
@@ -6005,19 +5977,21 @@ class AfkTimeoutModalComponent {
6005
5977
  this.#store = inject(Store);
6006
5978
  this.#afkService = inject(AFKService);
6007
5979
  this.isAfkTimerVisible = this.#store.selectSignal(unrealFeature.selectIsAfkTimerVisible);
5980
+ this.showAfkTimeoutPopup = this.#store.selectSignal(unrealFeature.selectShowAfkTimeoutPopup);
6008
5981
  this.afkCountdown = this.#store.selectSignal(unrealFeature.selectAfkCountdown);
5982
+ this.isShowAfkPopup = computed(() => this.isAfkTimerVisible() && this.showAfkTimeoutPopup(), ...(ngDevMode ? [{ debugName: "isShowAfkPopup" }] : /* istanbul ignore next */ []));
6009
5983
  }
6010
5984
  #store;
6011
5985
  #afkService;
6012
5986
  resetAfk() {
6013
5987
  this.#afkService.reset();
6014
5988
  }
6015
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: AfkTimeoutModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
6016
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.10", type: AfkTimeoutModalComponent, isStandalone: true, selector: "app-afk-timeout-modal", ngImport: i0, template: "@if (isAfkTimerVisible()) {\n <div (click)=\"resetAfk()\" class=\"clickableState\" id=\"videoPlayOverlay\">\n <div class=\"resume-box\">\n <div aria-hidden=\"true\" class=\"resume-box__pic\" role=\"presentation\">\n <div [innerHTML]=\"afkCountdown()\" class=\"text-number\"></div>\n </div>\n <div class=\"resume-box__text\">\n <h3 class=\"resume-box__heading\">Session will time out soon</h3>\n <p>\n No activity detected. Press 'Continue' if you wish to keep your\n session active\n </p>\n </div>\n <src-button\n [isFullWidth]=\"true\"\n [data-testid]=\"'continue-session'\"\n weight=\"primary\"\n size=\"lg\"\n >\n Continue\n </src-button>\n </div>\n </div>\n}\n", styles: ["#videoPlayOverlay{position:absolute;z-index:30;top:0;width:100%;height:100%;font-size:1.8em;font-family:var(--src-font-family-body);background-color:#646464b3}.clickableState{display:flex;justify-content:center;align-items:center;cursor:pointer}.textDisplayState{display:flex}.hiddenState{display:none}.resume-box{width:340px;padding:32px 20px 20px;flex-direction:column;align-items:center;border-radius:var(--src-border-rounded-parent, 8px);background:var(--src-surface-container-main, #fff);box-shadow:0 26px 80px #0003,0 0 1px #0003}.resume-box .resume-box__pic{width:72px;height:72px;margin:0 auto 22px;border-radius:48px;background:#ecf0f2;padding:12px;display:flex;align-items:center;justify-content:center}.resume-box .resume-box__pic .text-number{color:var(--src-colors-text-default, #1f2937);text-align:center;font-family:var(--src-font-family-body);font-size:30px;font-style:normal;font-weight:400;line-height:24px}.resume-box__text{margin-bottom:18px}.resume-box__text p{text-align:center;font-family:var(--src-font-family-body);font-size:var(--src-font-size-sm, 14px);font-style:normal;font-weight:400;line-height:24px;color:var(--src-text-body-secondary, #6b7280)}.resume-box__text .resume-box__heading{color:var(--src-text-body-main, #1f2937);text-align:center;font-family:var(--src-font-family-body);font-size:18px;font-style:normal;font-weight:500;line-height:26px;margin-bottom:8px}\n"], dependencies: [{ kind: "component", type: SourceButtonComponent, selector: "src-button", inputs: ["type", "appearance", "weight", "size", "context", "customClass", "isFullWidth", "isPressed", "isDisabled", "isLoading", "isInverted", "srcButtonConfig", "formID", "data-testid", "ariaLabel"], outputs: ["onClick", "onSubmit"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
5989
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: AfkTimeoutModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
5990
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: AfkTimeoutModalComponent, isStandalone: true, selector: "app-afk-timeout-modal", ngImport: i0, template: "@if (isShowAfkPopup()) {\n <div (click)=\"resetAfk()\" class=\"clickableState\" id=\"videoPlayOverlay\">\n <div class=\"resume-box\">\n <div aria-hidden=\"true\" class=\"resume-box__pic\" role=\"presentation\">\n <div [innerHTML]=\"afkCountdown()\" class=\"text-number\"></div>\n </div>\n <div class=\"resume-box__text\">\n <h3 class=\"resume-box__heading\">Session will time out soon</h3>\n <p>\n No activity detected. Press 'Continue' if you wish to keep your\n session active\n </p>\n </div>\n <src-button\n [isFullWidth]=\"true\"\n [data-testid]=\"'continue-session'\"\n weight=\"primary\"\n size=\"lg\"\n >\n Continue\n </src-button>\n </div>\n </div>\n}\n", styles: ["#videoPlayOverlay{position:absolute;z-index:30;top:0;width:100%;height:100%;font-size:1.8em;font-family:var(--src-font-family-body);background-color:#646464b3}.clickableState{display:flex;justify-content:center;align-items:center;cursor:pointer}.textDisplayState{display:flex}.hiddenState{display:none}.resume-box{width:340px;padding:32px 20px 20px;flex-direction:column;align-items:center;border-radius:var(--src-border-rounded-parent, 8px);background:var(--src-surface-container-main, #fff);box-shadow:0 26px 80px #0003,0 0 1px #0003}.resume-box .resume-box__pic{width:72px;height:72px;margin:0 auto 22px;border-radius:48px;background:#ecf0f2;padding:12px;display:flex;align-items:center;justify-content:center}.resume-box .resume-box__pic .text-number{color:var(--src-colors-text-default, #1f2937);text-align:center;font-family:var(--src-font-family-body);font-size:30px;font-style:normal;font-weight:400;line-height:24px}.resume-box__text{margin-bottom:18px}.resume-box__text p{text-align:center;font-family:var(--src-font-family-body);font-size:var(--src-font-size-sm, 14px);font-style:normal;font-weight:400;line-height:24px;color:var(--src-text-body-secondary, #6b7280)}.resume-box__text .resume-box__heading{color:var(--src-text-body-main, #1f2937);text-align:center;font-family:var(--src-font-family-body);font-size:18px;font-style:normal;font-weight:500;line-height:26px;margin-bottom:8px}\n"], dependencies: [{ kind: "component", type: SourceButtonComponent, selector: "src-button", inputs: ["type", "appearance", "weight", "size", "context", "customClass", "isFullWidth", "isPressed", "isDisabled", "isLoading", "isInverted", "srcButtonConfig", "formID", "data-testid", "ariaLabel"], outputs: ["onClick", "onSubmit"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
6017
5991
  }
6018
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: AfkTimeoutModalComponent, decorators: [{
5992
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: AfkTimeoutModalComponent, decorators: [{
6019
5993
  type: Component,
6020
- args: [{ selector: 'app-afk-timeout-modal', changeDetection: ChangeDetectionStrategy.OnPush, imports: [SourceButtonComponent], template: "@if (isAfkTimerVisible()) {\n <div (click)=\"resetAfk()\" class=\"clickableState\" id=\"videoPlayOverlay\">\n <div class=\"resume-box\">\n <div aria-hidden=\"true\" class=\"resume-box__pic\" role=\"presentation\">\n <div [innerHTML]=\"afkCountdown()\" class=\"text-number\"></div>\n </div>\n <div class=\"resume-box__text\">\n <h3 class=\"resume-box__heading\">Session will time out soon</h3>\n <p>\n No activity detected. Press 'Continue' if you wish to keep your\n session active\n </p>\n </div>\n <src-button\n [isFullWidth]=\"true\"\n [data-testid]=\"'continue-session'\"\n weight=\"primary\"\n size=\"lg\"\n >\n Continue\n </src-button>\n </div>\n </div>\n}\n", styles: ["#videoPlayOverlay{position:absolute;z-index:30;top:0;width:100%;height:100%;font-size:1.8em;font-family:var(--src-font-family-body);background-color:#646464b3}.clickableState{display:flex;justify-content:center;align-items:center;cursor:pointer}.textDisplayState{display:flex}.hiddenState{display:none}.resume-box{width:340px;padding:32px 20px 20px;flex-direction:column;align-items:center;border-radius:var(--src-border-rounded-parent, 8px);background:var(--src-surface-container-main, #fff);box-shadow:0 26px 80px #0003,0 0 1px #0003}.resume-box .resume-box__pic{width:72px;height:72px;margin:0 auto 22px;border-radius:48px;background:#ecf0f2;padding:12px;display:flex;align-items:center;justify-content:center}.resume-box .resume-box__pic .text-number{color:var(--src-colors-text-default, #1f2937);text-align:center;font-family:var(--src-font-family-body);font-size:30px;font-style:normal;font-weight:400;line-height:24px}.resume-box__text{margin-bottom:18px}.resume-box__text p{text-align:center;font-family:var(--src-font-family-body);font-size:var(--src-font-size-sm, 14px);font-style:normal;font-weight:400;line-height:24px;color:var(--src-text-body-secondary, #6b7280)}.resume-box__text .resume-box__heading{color:var(--src-text-body-main, #1f2937);text-align:center;font-family:var(--src-font-family-body);font-size:18px;font-style:normal;font-weight:500;line-height:26px;margin-bottom:8px}\n"] }]
5994
+ args: [{ selector: 'app-afk-timeout-modal', changeDetection: ChangeDetectionStrategy.OnPush, imports: [SourceButtonComponent], template: "@if (isShowAfkPopup()) {\n <div (click)=\"resetAfk()\" class=\"clickableState\" id=\"videoPlayOverlay\">\n <div class=\"resume-box\">\n <div aria-hidden=\"true\" class=\"resume-box__pic\" role=\"presentation\">\n <div [innerHTML]=\"afkCountdown()\" class=\"text-number\"></div>\n </div>\n <div class=\"resume-box__text\">\n <h3 class=\"resume-box__heading\">Session will time out soon</h3>\n <p>\n No activity detected. Press 'Continue' if you wish to keep your\n session active\n </p>\n </div>\n <src-button\n [isFullWidth]=\"true\"\n [data-testid]=\"'continue-session'\"\n weight=\"primary\"\n size=\"lg\"\n >\n Continue\n </src-button>\n </div>\n </div>\n}\n", styles: ["#videoPlayOverlay{position:absolute;z-index:30;top:0;width:100%;height:100%;font-size:1.8em;font-family:var(--src-font-family-body);background-color:#646464b3}.clickableState{display:flex;justify-content:center;align-items:center;cursor:pointer}.textDisplayState{display:flex}.hiddenState{display:none}.resume-box{width:340px;padding:32px 20px 20px;flex-direction:column;align-items:center;border-radius:var(--src-border-rounded-parent, 8px);background:var(--src-surface-container-main, #fff);box-shadow:0 26px 80px #0003,0 0 1px #0003}.resume-box .resume-box__pic{width:72px;height:72px;margin:0 auto 22px;border-radius:48px;background:#ecf0f2;padding:12px;display:flex;align-items:center;justify-content:center}.resume-box .resume-box__pic .text-number{color:var(--src-colors-text-default, #1f2937);text-align:center;font-family:var(--src-font-family-body);font-size:30px;font-style:normal;font-weight:400;line-height:24px}.resume-box__text{margin-bottom:18px}.resume-box__text p{text-align:center;font-family:var(--src-font-family-body);font-size:var(--src-font-size-sm, 14px);font-style:normal;font-weight:400;line-height:24px;color:var(--src-text-body-secondary, #6b7280)}.resume-box__text .resume-box__heading{color:var(--src-text-body-main, #1f2937);text-align:center;font-family:var(--src-font-family-body);font-size:18px;font-style:normal;font-weight:500;line-height:26px;margin-bottom:8px}\n"] }]
6021
5995
  }] });
6022
5996
 
6023
5997
  class FreezeFrameComponent {
@@ -6027,10 +6001,10 @@ class FreezeFrameComponent {
6027
6001
  this.combinedFreeze = this.#store.selectSignal(selectFreezeFrameCombinedDataUrl);
6028
6002
  }
6029
6003
  #store;
6030
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: FreezeFrameComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
6031
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.10", type: FreezeFrameComponent, isStandalone: true, selector: "app-freeze-frame", ngImport: i0, template: "@let image = combinedFreeze();\n@let progress = freezeFrameProgressMessageFromVideo();\n\n<div class=\"freeze-images\">\n @if (image) {\n <img [src]=\"image\" class=\"videoStillImage\" alt=\"freezeFrameImage\" />\n }\n\n @if (progress) {\n <div class=\"progress-status\">\n {{ progress }}\n </div>\n }\n</div>\n", styles: [".freeze-images{pointer-events:none;position:absolute;left:0;top:0;width:100%;height:100%;z-index:1}.freeze-images .freezeFrameOverlay{top:0;left:0;width:100%;height:100%;position:absolute;object-fit:cover;object-position:center}.freeze-images .videoStillImage{position:absolute;z-index:1;top:0;left:0;width:100%;height:100%;object-fit:cover}.freeze-images .progress-status{position:absolute;top:50%;left:50%;z-index:1;display:flex;flex-direction:column;justify-content:center;align-items:center;width:auto;height:auto;margin:0;padding:10px;background-color:#ffffffb3;border:1px solid grey;transform:translate(-50%,-50%)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
6004
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: FreezeFrameComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
6005
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: FreezeFrameComponent, isStandalone: true, selector: "app-freeze-frame", ngImport: i0, template: "@let image = combinedFreeze();\n@let progress = freezeFrameProgressMessageFromVideo();\n\n<div class=\"freeze-images\">\n @if (image) {\n <img [src]=\"image\" class=\"videoStillImage\" alt=\"freezeFrameImage\" />\n }\n\n @if (progress) {\n <div class=\"progress-status\">\n {{ progress }}\n </div>\n }\n</div>\n", styles: [".freeze-images{pointer-events:none;position:absolute;left:0;top:0;width:100%;height:100%;z-index:1}.freeze-images .freezeFrameOverlay{top:0;left:0;width:100%;height:100%;position:absolute;object-fit:cover;object-position:center}.freeze-images .videoStillImage{position:absolute;z-index:1;top:0;left:0;width:100%;height:100%;object-fit:cover}.freeze-images .progress-status{position:absolute;top:50%;left:50%;z-index:1;display:flex;flex-direction:column;justify-content:center;align-items:center;width:auto;height:auto;margin:0;padding:10px;background-color:#ffffffb3;border:1px solid grey;transform:translate(-50%,-50%)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
6032
6006
  }
6033
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: FreezeFrameComponent, decorators: [{
6007
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: FreezeFrameComponent, decorators: [{
6034
6008
  type: Component,
6035
6009
  args: [{ selector: 'app-freeze-frame', changeDetection: ChangeDetectionStrategy.OnPush, template: "@let image = combinedFreeze();\n@let progress = freezeFrameProgressMessageFromVideo();\n\n<div class=\"freeze-images\">\n @if (image) {\n <img [src]=\"image\" class=\"videoStillImage\" alt=\"freezeFrameImage\" />\n }\n\n @if (progress) {\n <div class=\"progress-status\">\n {{ progress }}\n </div>\n }\n</div>\n", styles: [".freeze-images{pointer-events:none;position:absolute;left:0;top:0;width:100%;height:100%;z-index:1}.freeze-images .freezeFrameOverlay{top:0;left:0;width:100%;height:100%;position:absolute;object-fit:cover;object-position:center}.freeze-images .videoStillImage{position:absolute;z-index:1;top:0;left:0;width:100%;height:100%;object-fit:cover}.freeze-images .progress-status{position:absolute;top:50%;left:50%;z-index:1;display:flex;flex-direction:column;justify-content:center;align-items:center;width:auto;height:auto;margin:0;padding:10px;background-color:#ffffffb3;border:1px solid grey;transform:translate(-50%,-50%)}\n"] }]
6036
6010
  }] });
@@ -6058,10 +6032,10 @@ class SafePipe {
6058
6032
  throw new Error(`Invalid safe type specified: ${type}`);
6059
6033
  }
6060
6034
  }
6061
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: SafePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
6062
- static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.2.10", ngImport: i0, type: SafePipe, isStandalone: true, name: "safe" }); }
6035
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: SafePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
6036
+ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.2.11", ngImport: i0, type: SafePipe, isStandalone: true, name: "safe" }); }
6063
6037
  }
6064
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: SafePipe, decorators: [{
6038
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: SafePipe, decorators: [{
6065
6039
  type: Pipe,
6066
6040
  args: [{
6067
6041
  name: 'safe',
@@ -6073,10 +6047,10 @@ class LowBandwidthModalComponent {
6073
6047
  close(value) {
6074
6048
  this.#dialogRef.close(value || false);
6075
6049
  }
6076
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: LowBandwidthModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
6077
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.10", type: LowBandwidthModalComponent, isStandalone: true, selector: "app-low-bandwidth-modal", ngImport: i0, template: "<div [attr.data-testid]=\"'low-bandwidth'\" class=\"src-modal src-modal--lbm\">\n <header class=\"src-modal__header\">\n <h6 [attr.data-testid]=\"'title'\" class=\"src-modal__title\">\n Unstable Connection\n </h6>\n </header>\n <section class=\"src-modal__body\">\n <div\n [innerHtml]=\"\n 'Fluid Interactivity Modes were disabled due to an unstable connection. Showcase Gallery Mode is enabled. To regain full functionality, switch to Interactive Mode.'\n | safe: 'html'\n \"\n class=\"src-modal__scroll-box\"\n ></div>\n </section>\n <footer class=\"src-modal__footer\">\n <div class=\"src-modal__buttons\">\n <src-button\n (onClick)=\"close(true)\"\n [data-testid]=\"'switch-to-interactive-mode'\"\n >\n Switch\n </src-button>\n\n <src-button\n (onClick)=\"close()\"\n [data-testid]=\"'close-lbm-modal'\"\n weight=\"primary\"\n >\n Ok\n </src-button>\n </div>\n </footer>\n</div>\n", styles: [".src-modal--lbm{width:360px}.src-modal--lbm .src-modal__body{font-size:14px;line-height:24px}.src-modal--lbm .src-modal__buttons{display:flex;gap:8px}\n"], dependencies: [{ kind: "component", type: SourceButtonComponent, selector: "src-button", inputs: ["type", "appearance", "weight", "size", "context", "customClass", "isFullWidth", "isPressed", "isDisabled", "isLoading", "isInverted", "srcButtonConfig", "formID", "data-testid", "ariaLabel"], outputs: ["onClick", "onSubmit"] }, { kind: "pipe", type: SafePipe, name: "safe" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
6050
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: LowBandwidthModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
6051
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.11", type: LowBandwidthModalComponent, isStandalone: true, selector: "app-low-bandwidth-modal", ngImport: i0, template: "<div [attr.data-testid]=\"'low-bandwidth'\" class=\"src-modal src-modal--lbm\">\n <header class=\"src-modal__header\">\n <h6 [attr.data-testid]=\"'title'\" class=\"src-modal__title\">\n Unstable Connection\n </h6>\n </header>\n <section class=\"src-modal__body\">\n <div\n [innerHtml]=\"\n 'Fluid Interactivity Modes were disabled due to an unstable connection. Showcase Gallery Mode is enabled. To regain full functionality, switch to Interactive Mode.'\n | safe: 'html'\n \"\n class=\"src-modal__scroll-box\"\n ></div>\n </section>\n <footer class=\"src-modal__footer\">\n <div class=\"src-modal__buttons\">\n <src-button\n (onClick)=\"close(true)\"\n [data-testid]=\"'switch-to-interactive-mode'\"\n >\n Switch\n </src-button>\n\n <src-button\n (onClick)=\"close()\"\n [data-testid]=\"'close-lbm-modal'\"\n weight=\"primary\"\n >\n Ok\n </src-button>\n </div>\n </footer>\n</div>\n", styles: [".src-modal--lbm{width:360px}.src-modal--lbm .src-modal__body{font-size:14px;line-height:24px}.src-modal--lbm .src-modal__buttons{display:flex;gap:8px}\n"], dependencies: [{ kind: "component", type: SourceButtonComponent, selector: "src-button", inputs: ["type", "appearance", "weight", "size", "context", "customClass", "isFullWidth", "isPressed", "isDisabled", "isLoading", "isInverted", "srcButtonConfig", "formID", "data-testid", "ariaLabel"], outputs: ["onClick", "onSubmit"] }, { kind: "pipe", type: SafePipe, name: "safe" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
6078
6052
  }
6079
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: LowBandwidthModalComponent, decorators: [{
6053
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: LowBandwidthModalComponent, decorators: [{
6080
6054
  type: Component,
6081
6055
  args: [{ selector: 'app-low-bandwidth-modal', changeDetection: ChangeDetectionStrategy.OnPush, imports: [SafePipe, SourceButtonComponent], template: "<div [attr.data-testid]=\"'low-bandwidth'\" class=\"src-modal src-modal--lbm\">\n <header class=\"src-modal__header\">\n <h6 [attr.data-testid]=\"'title'\" class=\"src-modal__title\">\n Unstable Connection\n </h6>\n </header>\n <section class=\"src-modal__body\">\n <div\n [innerHtml]=\"\n 'Fluid Interactivity Modes were disabled due to an unstable connection. Showcase Gallery Mode is enabled. To regain full functionality, switch to Interactive Mode.'\n | safe: 'html'\n \"\n class=\"src-modal__scroll-box\"\n ></div>\n </section>\n <footer class=\"src-modal__footer\">\n <div class=\"src-modal__buttons\">\n <src-button\n (onClick)=\"close(true)\"\n [data-testid]=\"'switch-to-interactive-mode'\"\n >\n Switch\n </src-button>\n\n <src-button\n (onClick)=\"close()\"\n [data-testid]=\"'close-lbm-modal'\"\n weight=\"primary\"\n >\n Ok\n </src-button>\n </div>\n </footer>\n</div>\n", styles: [".src-modal--lbm{width:360px}.src-modal--lbm .src-modal__body{font-size:14px;line-height:24px}.src-modal--lbm .src-modal__buttons{display:flex;gap:8px}\n"] }]
6082
6056
  }] });
@@ -6088,15 +6062,15 @@ class ImageLoadingSrcComponent {
6088
6062
  this.imageLoadingSrc = this.#store.selectSignal(unrealFeature.selectImageLoadingSrc);
6089
6063
  }
6090
6064
  #store;
6091
- imageError() {
6065
+ loadingImageError() {
6092
6066
  this.#store.dispatch(setLoadingImageSrc({ src: '' }));
6093
6067
  }
6094
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: ImageLoadingSrcComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
6095
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.10", type: ImageLoadingSrcComponent, isStandalone: true, selector: "app-image-loading-src", ngImport: i0, template: "@let image = imageLoadingSrc();\n@if (isLoaderScreenVisible()) {\n <div class=\"preload\">\n <div class=\"back\">\n @if (image) {\n <img\n (error)=\"imageError()\"\n [ngSrc]=\"image\"\n fill\n loading=\"lazy\"\n alt=\"image loading src\"\n />\n }\n </div>\n </div>\n}\n", styles: [".preload{position:absolute;top:0;left:0;width:100%;height:100%;text-align:center}.preload .back{position:absolute;top:0;left:0;display:flex;justify-content:center;align-items:center;width:100%;height:100%}.preload .back img{width:100%;height:100%;object-fit:cover}.preload .back{background-color:#fff}\n"], dependencies: [{ kind: "directive", type: NgOptimizedImage, selector: "img[ngSrc]", inputs: ["ngSrc", "ngSrcset", "sizes", "width", "height", "decoding", "loading", "priority", "loaderParams", "disableOptimizedSrcset", "fill", "placeholder", "placeholderConfig", "src", "srcset"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
6068
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: ImageLoadingSrcComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
6069
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: ImageLoadingSrcComponent, isStandalone: true, selector: "app-image-loading-src", ngImport: i0, template: "@let image = imageLoadingSrc();\n@if (isLoaderScreenVisible()) {\n <div class=\"preload\">\n <div class=\"back\">\n @if (image) {\n <img\n (error)=\"loadingImageError()\"\n [ngSrc]=\"image\"\n fill\n loading=\"lazy\"\n alt=\"image loading src\"\n />\n }\n </div>\n </div>\n}\n", styles: [".preload{position:absolute;top:0;left:0;width:100%;height:100%;text-align:center}.preload .back{position:absolute;top:0;left:0;display:flex;justify-content:center;align-items:center;width:100%;height:100%}.preload .back img{width:100%;height:100%;object-fit:cover}.preload .back{background-color:#fff}\n"], dependencies: [{ kind: "directive", type: NgOptimizedImage, selector: "img[ngSrc]", inputs: ["ngSrc", "ngSrcset", "sizes", "width", "height", "decoding", "loading", "priority", "loaderParams", "disableOptimizedSrcset", "fill", "placeholder", "placeholderConfig", "src", "srcset"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
6096
6070
  }
6097
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: ImageLoadingSrcComponent, decorators: [{
6071
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: ImageLoadingSrcComponent, decorators: [{
6098
6072
  type: Component,
6099
- args: [{ selector: 'app-image-loading-src', changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgOptimizedImage], template: "@let image = imageLoadingSrc();\n@if (isLoaderScreenVisible()) {\n <div class=\"preload\">\n <div class=\"back\">\n @if (image) {\n <img\n (error)=\"imageError()\"\n [ngSrc]=\"image\"\n fill\n loading=\"lazy\"\n alt=\"image loading src\"\n />\n }\n </div>\n </div>\n}\n", styles: [".preload{position:absolute;top:0;left:0;width:100%;height:100%;text-align:center}.preload .back{position:absolute;top:0;left:0;display:flex;justify-content:center;align-items:center;width:100%;height:100%}.preload .back img{width:100%;height:100%;object-fit:cover}.preload .back{background-color:#fff}\n"] }]
6073
+ args: [{ selector: 'app-image-loading-src', changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgOptimizedImage], template: "@let image = imageLoadingSrc();\n@if (isLoaderScreenVisible()) {\n <div class=\"preload\">\n <div class=\"back\">\n @if (image) {\n <img\n (error)=\"loadingImageError()\"\n [ngSrc]=\"image\"\n fill\n loading=\"lazy\"\n alt=\"image loading src\"\n />\n }\n </div>\n </div>\n}\n", styles: [".preload{position:absolute;top:0;left:0;width:100%;height:100%;text-align:center}.preload .back{position:absolute;top:0;left:0;display:flex;justify-content:center;align-items:center;width:100%;height:100%}.preload .back img{width:100%;height:100%;object-fit:cover}.preload .back{background-color:#fff}\n"] }]
6100
6074
  }] });
6101
6075
 
6102
6076
  class StatGraphComponent {
@@ -6170,10 +6144,10 @@ class StatGraphComponent {
6170
6144
  }
6171
6145
  this.draw();
6172
6146
  }
6173
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: StatGraphComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
6174
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.2.10", type: StatGraphComponent, isStandalone: true, selector: "app-stat-graph", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, stat: { classPropertyName: "stat", publicName: "stat", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, tickStep: { classPropertyName: "tickStep", publicName: "tickStep", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "window:resize": "resize()" } }, viewQueries: [{ propertyName: "graph", first: true, predicate: ["graph"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"content\">\n <p>{{ label() }}: {{ current() }} | Min:{{ min() }} | Max:{{ max() }}</p>\n <canvas #graph class=\"graph\"></canvas>\n</div>\n", styles: [".content{width:100%;height:100%}.content p{color:#fff;margin:5px 0 0;height:25px}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
6147
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: StatGraphComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
6148
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.2.11", type: StatGraphComponent, isStandalone: true, selector: "app-stat-graph", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, stat: { classPropertyName: "stat", publicName: "stat", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, tickStep: { classPropertyName: "tickStep", publicName: "tickStep", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "window:resize": "resize()" } }, viewQueries: [{ propertyName: "graph", first: true, predicate: ["graph"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"content\">\n <p>{{ label() }}: {{ current() }} | Min:{{ min() }} | Max:{{ max() }}</p>\n <canvas #graph class=\"graph\"></canvas>\n</div>\n", styles: [".content{width:100%;height:100%}.content p{color:#fff;margin:5px 0 0;height:25px}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
6175
6149
  }
6176
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: StatGraphComponent, decorators: [{
6150
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: StatGraphComponent, decorators: [{
6177
6151
  type: Component,
6178
6152
  args: [{ selector: 'app-stat-graph', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"content\">\n <p>{{ label() }}: {{ current() }} | Min:{{ min() }} | Max:{{ max() }}</p>\n <canvas #graph class=\"graph\"></canvas>\n</div>\n", styles: [".content{width:100%;height:100%}.content p{color:#fff;margin:5px 0 0;height:25px}\n"] }]
6179
6153
  }], ctorParameters: () => [], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], stat: [{ type: i0.Input, args: [{ isSignal: true, alias: "stat", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], tickStep: [{ type: i0.Input, args: [{ isSignal: true, alias: "tickStep", required: false }] }], graph: [{ type: i0.ViewChild, args: ['graph', { isSignal: true }] }], resize: [{
@@ -6183,83 +6157,22 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImpo
6183
6157
 
6184
6158
  class UnrealStatusComponent {
6185
6159
  constructor() {
6186
- this.unrealInitialConfig = inject(UNREAL_CONFIG);
6187
- this.destroyRef = inject(DestroyRef);
6188
- this.store = inject(Store);
6189
- this.resetPercentageSignal$ = this.store
6190
- .select(selectShowLoader)
6191
- .pipe(filter(Falsy));
6192
- this.showLoader = this.store.selectSignal(selectShowLoader);
6193
- /**
6194
- * An observable that emits smoothed percentage values from 0 to 100.
6195
- */
6196
- this.messagePercents = toSignal(this.store.select(selectTotalProgress).pipe(keepMaxUntilReset(this.resetPercentageSignal$), floatToSmoothPercents(), distinctUntilChanged(), map$1((progress) => progress || 0)));
6197
- this.awsInstance = this.store.selectSignal(unrealFeature.selectAwsInstance);
6198
- }
6199
- ngOnInit() {
6200
- /**
6201
- * A subject that emits null values to trigger nullification.
6202
- */
6203
- const nullify$ = new Subject();
6204
- /**
6205
- * An observable that emits null values with a delay of 2000 milliseconds.
6206
- */
6207
- const nullifyObs$ = nullify$
6208
- .asObservable()
6209
- .pipe(delay(2000));
6210
- /**
6211
- * An observable that emits the status message from the Unreal feature store.
6212
- */
6213
- const signalingStatus$ = this.store.select(unrealFeature.selectStatusMessage);
6214
- /**
6215
- * An observable that emits connection status messages based on the Cirrus connection state.
6216
- * Then call side effect to nullify the message.
6217
- */
6218
- const cirrusConnected$ = this.store
6219
- .select(unrealFeature.selectCirrusConnected)
6220
- .pipe(skip(1), switchMap((connected) => connected
6221
- ? of('Creating Socket...')
6222
- : of('Connection closed.').pipe(tap(() => nullify$.next(null)))));
6223
- /**
6224
- * An observable that emits the viewport visibility state.
6225
- */
6226
- const viewportTrigger$ = this.store.select(unrealFeature.selectViewportReady);
6227
- /**
6228
- * An observable that will nullify any message displayed after 500 mSec, when viewport changes its state.
6229
- */
6230
- const streamToggle$ = merge(viewportTrigger$.pipe(filter(Falsy), skip(1)), viewportTrigger$.pipe(filter(Truthy))).pipe(delay(500), map$1(() => null));
6231
- /**
6232
- * A subscription that merges various status observables and emits debounced status via postMessage.
6233
- */
6234
- merge(nullifyObs$, cirrusConnected$, signalingStatus$, streamToggle$)
6235
- .pipe(tapLog('STATUS=>'), debounceTime$1(0), takeUntilDestroyed(this.destroyRef))
6236
- .subscribe((status) => this.sendMessageToIndex(status));
6237
- }
6238
- /**
6239
- * Sends a message to the embedded iframe identified by the default value '3dsource_start_screen' ID.
6240
- * This function tries to post a message to the iframe's content window. If the content
6241
- * window is not accessible (e.g., due to cross-origin policies), the function fails silently.
6242
- *
6243
- * @param {string} text The message to be sent to the iframe. This could be a status update
6244
- * or any other relevant information that the iframe needs to process.
6245
- */
6246
- sendMessageToIndex(text) {
6247
- const objectElement = document.getElementById(this.unrealInitialConfig?.screenLockerContainerId ||
6248
- SCREEN_LOCKER_CONTAINER_ID);
6249
- try {
6250
- const objectDocument = objectElement.contentWindow;
6251
- objectDocument?.postMessage(text, '*');
6252
- }
6253
- catch {
6254
- // Fails silently if the iframe's content window cannot be accessed
6255
- }
6160
+ this.#store = inject(Store);
6161
+ this.#loadingProgress = inject(LoadingProgressService);
6162
+ this.awsInstance = this.#store.selectSignal(unrealFeature.selectAwsInstance);
6163
+ this.messagePercents = this.#loadingProgress.percents;
6164
+ this.showLoader = this.#loadingProgress.loaderVisible;
6165
+ this.showLoadingProgress = this.#store.selectSignal(unrealFeature.selectShowLoadingProgress);
6166
+ this.isShowLoadingPercentsPopup = computed(() => this.showLoader() && this.showLoadingProgress(), ...(ngDevMode ? [{ debugName: "isShowLoadingPercentsPopup" }] : /* istanbul ignore next */ []));
6256
6167
  }
6257
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: UnrealStatusComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
6258
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.10", type: UnrealStatusComponent, isStandalone: true, selector: "app-unreal-status", ngImport: i0, template: "@if (showLoader()) {\n <div class=\"status-box\">\n @if (awsInstance()?.message) {\n <p>{{ awsInstance()?.message }}</p>\n } @else {\n <div class=\"status-box__message\">\n <src-loading\n [size]=\"30\"\n [backgroundStrokeColor]=\"'#E5E7EB'\"\n [progressStrokeColor]=\"'#1F2937'\"\n />\n {{ messagePercents() }}%\n </div>\n }\n </div>\n}\n", styles: ["app-stat-graph{height:30px;width:100%}.status-box{position:absolute;top:80%;left:50%;transform:translate(-50%,-50%);z-index:5;display:flex;justify-content:center;align-items:center;width:auto;max-width:500px;margin:0;background:var(--src-surface-container-main, #fff);border:none;border-radius:var(--src-border-rounded-full, 9999px);box-shadow:0 8px 20px #1718181f,0 3px 6px #17181814;pointer-events:none}.status-box p{padding:10px 15px;margin:0;text-align:center}.status-box__message{display:flex;align-items:center;padding:12px 20px 12px 16px;color:var(--src-text-body-main, #1f2937);font-size:16px;font-weight:400;line-height:24px}.status-box__message .status-box__message_text{display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}.status-box__message src-loading{margin-right:12px}\n"], dependencies: [{ kind: "component", type: SourceLoadingComponent, selector: "src-loading", inputs: ["size", "progress", "lineCap", "backgroundStrokeColor", "progressStrokeColor", "strokeWidth", "data-testid", "role", "ariaLabel", "ariaValueNow", "ariaValueMin", "ariaValueMax"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
6168
+ #store;
6169
+ #loadingProgress;
6170
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: UnrealStatusComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
6171
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: UnrealStatusComponent, isStandalone: true, selector: "app-unreal-status", ngImport: i0, template: "@if (isShowLoadingPercentsPopup()) {\n @let _awsInstance = awsInstance();\n <div class=\"status-box\">\n @if (_awsInstance?.message) {\n <p>{{ _awsInstance?.message }}</p>\n } @else {\n <div class=\"status-box__message\">\n <src-loading\n [size]=\"30\"\n [backgroundStrokeColor]=\"'#E5E7EB'\"\n [progressStrokeColor]=\"'#1F2937'\"\n />\n {{ messagePercents() }}%\n </div>\n }\n </div>\n}\n", styles: ["app-stat-graph{height:30px;width:100%}.status-box{position:absolute;top:80%;left:50%;transform:translate(-50%,-50%);z-index:5;display:flex;justify-content:center;align-items:center;width:auto;max-width:500px;margin:0;background:var(--src-surface-container-main, #fff);border:none;border-radius:var(--src-border-rounded-full, 9999px);box-shadow:0 8px 20px #1718181f,0 3px 6px #17181814;pointer-events:none}.status-box p{padding:10px 15px;margin:0;text-align:center}.status-box__message{display:flex;align-items:center;padding:12px 20px 12px 16px;color:var(--src-text-body-main, #1f2937);font-size:16px;font-weight:400;line-height:24px}.status-box__message .status-box__message_text{display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}.status-box__message src-loading{margin-right:12px}\n"], dependencies: [{ kind: "component", type: SourceLoadingComponent, selector: "src-loading", inputs: ["size", "progress", "lineCap", "backgroundStrokeColor", "progressStrokeColor", "strokeWidth", "data-testid", "role", "ariaLabel", "ariaValueNow", "ariaValueMin", "ariaValueMax"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
6259
6172
  }
6260
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: UnrealStatusComponent, decorators: [{
6173
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: UnrealStatusComponent, decorators: [{
6261
6174
  type: Component,
6262
- args: [{ selector: 'app-unreal-status', changeDetection: ChangeDetectionStrategy.OnPush, imports: [SourceLoadingComponent], template: "@if (showLoader()) {\n <div class=\"status-box\">\n @if (awsInstance()?.message) {\n <p>{{ awsInstance()?.message }}</p>\n } @else {\n <div class=\"status-box__message\">\n <src-loading\n [size]=\"30\"\n [backgroundStrokeColor]=\"'#E5E7EB'\"\n [progressStrokeColor]=\"'#1F2937'\"\n />\n {{ messagePercents() }}%\n </div>\n }\n </div>\n}\n", styles: ["app-stat-graph{height:30px;width:100%}.status-box{position:absolute;top:80%;left:50%;transform:translate(-50%,-50%);z-index:5;display:flex;justify-content:center;align-items:center;width:auto;max-width:500px;margin:0;background:var(--src-surface-container-main, #fff);border:none;border-radius:var(--src-border-rounded-full, 9999px);box-shadow:0 8px 20px #1718181f,0 3px 6px #17181814;pointer-events:none}.status-box p{padding:10px 15px;margin:0;text-align:center}.status-box__message{display:flex;align-items:center;padding:12px 20px 12px 16px;color:var(--src-text-body-main, #1f2937);font-size:16px;font-weight:400;line-height:24px}.status-box__message .status-box__message_text{display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}.status-box__message src-loading{margin-right:12px}\n"] }]
6175
+ args: [{ selector: 'app-unreal-status', changeDetection: ChangeDetectionStrategy.OnPush, imports: [SourceLoadingComponent], template: "@if (isShowLoadingPercentsPopup()) {\n @let _awsInstance = awsInstance();\n <div class=\"status-box\">\n @if (_awsInstance?.message) {\n <p>{{ _awsInstance?.message }}</p>\n } @else {\n <div class=\"status-box__message\">\n <src-loading\n [size]=\"30\"\n [backgroundStrokeColor]=\"'#E5E7EB'\"\n [progressStrokeColor]=\"'#1F2937'\"\n />\n {{ messagePercents() }}%\n </div>\n }\n </div>\n}\n", styles: ["app-stat-graph{height:30px;width:100%}.status-box{position:absolute;top:80%;left:50%;transform:translate(-50%,-50%);z-index:5;display:flex;justify-content:center;align-items:center;width:auto;max-width:500px;margin:0;background:var(--src-surface-container-main, #fff);border:none;border-radius:var(--src-border-rounded-full, 9999px);box-shadow:0 8px 20px #1718181f,0 3px 6px #17181814;pointer-events:none}.status-box p{padding:10px 15px;margin:0;text-align:center}.status-box__message{display:flex;align-items:center;padding:12px 20px 12px 16px;color:var(--src-text-body-main, #1f2937);font-size:16px;font-weight:400;line-height:24px}.status-box__message .status-box__message_text{display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}.status-box__message src-loading{margin-right:12px}\n"] }]
6263
6176
  }] });
6264
6177
 
6265
6178
  class VideoStatsComponent {
@@ -6270,43 +6183,19 @@ class VideoStatsComponent {
6270
6183
  this.isDevMode = inject(DevModeService).isDevMode;
6271
6184
  this.obs$ = this.videoService.videoStats$;
6272
6185
  this.viewportReady = this.store.selectSignal(unrealFeature.selectViewportReady);
6273
- this.ssInfo = this.store.selectSignal(selectSignalingParameters);
6274
- this.fpsTick = toSignal(this.obs$.pipe(map$1((stat) => ({
6275
- value: stat.aggregatedStats.framesPerSecond || 0,
6276
- }))));
6277
- this.fpsAvgTick = toSignal(this.obs$.pipe(map$1((stat) => ({
6278
- value: stat.aggregatedStats.avgFrameRate,
6279
- }))));
6280
- this.bitrateTick = toSignal(this.obs$.pipe(map$1((stat) => ({
6281
- value: stat.aggregatedStats.bitrate,
6282
- }))));
6283
- this.videoQP = toSignal(this.obs$.pipe(map$1((stat) => ({
6284
- value: stat.aggregatedStats.VideoEncoderQP,
6285
- }))));
6286
- this.bitrateAverageTick = toSignal(this.obs$.pipe(map$1((stat) => ({
6287
- value: stat.aggregatedStats.avgBitrate,
6288
- }))));
6186
+ this.ssData = this.store.selectSignal(selectSignalingParameters);
6187
+ this.fpsTick = toSignal(this.obs$.pipe(map$1((stat) => ({ value: stat.aggregatedStats.framesPerSecond || 0 }))));
6188
+ this.fpsAvgTick = toSignal(this.obs$.pipe(map$1((stat) => ({ value: stat.aggregatedStats.avgFrameRate }))));
6189
+ this.bitrateTick = toSignal(this.obs$.pipe(map$1((stat) => ({ value: stat.aggregatedStats.bitrate }))));
6190
+ this.videoQP = toSignal(this.obs$.pipe(map$1((stat) => ({ value: stat.aggregatedStats.VideoEncoderQP }))));
6191
+ this.bitrateAverageTick = toSignal(this.obs$.pipe(map$1((stat) => ({ value: stat.aggregatedStats.avgBitrate }))));
6289
6192
  this.videoStatus = toSignal(this.obs$.pipe(map$1((event) => this.isDevMode &&
6290
6193
  {
6291
6194
  ...event.aggregatedStats,
6292
6195
  quality: event.quality,
6293
- }), filter(Boolean), map$1((data) => {
6294
- return this.fields
6295
- .map((key) => {
6296
- return {
6297
- key,
6298
- value: data[key],
6299
- };
6300
- })
6301
- .filter((r) => !this.elementsToShow.includes(r.key));
6302
- })));
6303
- this.graphList = [];
6304
- this.idKey = 'com.source3d.unreal.stat';
6305
- this.elementsToShow = [];
6306
- this.trigger$ = new BehaviorSubject(undefined);
6307
- this.getFields();
6308
- }
6309
- ngOnInit() {
6196
+ }), filter(Boolean), map$1((data) => this.fields
6197
+ .map((key) => ({ key, value: data[key] }))
6198
+ .filter((r) => !this.elementsToShow().includes(r.key)))));
6310
6199
  this.fields = [
6311
6200
  'kalmanBitrate',
6312
6201
  'bitrateDrop',
@@ -6330,26 +6219,21 @@ class VideoStatsComponent {
6330
6219
  'type',
6331
6220
  'codecs',
6332
6221
  'selectedPair',
6333
- //'pliCount',
6334
- //'sliCount',
6335
- //'transportId',
6336
- //'id',
6337
6222
  ];
6338
- this.updateGraph();
6339
- }
6340
- updateGraph() {
6341
- this.graphList = this.fields.map((key) => {
6342
- const stat = this.obs$.pipe(map$1((val) => {
6343
- const value = val.aggregatedStats[key];
6344
- return { value };
6345
- }));
6346
- return {
6347
- key,
6348
- color: Math.floor(Math.random() * 16777216).toString(),
6349
- stat,
6350
- };
6351
- });
6352
- this.graphList$ = of(this.graphList).pipe(combineLatestWith(this.trigger$), map$1(([graphList]) => graphList.filter((r) => this.elementsToShow.includes(r.key))));
6223
+ this.elementsToShow = signal([], ...(ngDevMode ? [{ debugName: "elementsToShow" }] : /* istanbul ignore next */ []));
6224
+ this.idKey = 'com.source3d.unreal.stat';
6225
+ this.graphListItems = this.fields.map((key) => ({
6226
+ key,
6227
+ color: '#' +
6228
+ Math.floor(Math.random() * 16777216)
6229
+ .toString(16)
6230
+ .padStart(6, '0'),
6231
+ stat: this.obs$.pipe(map$1((val) => ({
6232
+ value: val.aggregatedStats[key],
6233
+ }))),
6234
+ }));
6235
+ this.graphList = computed(() => this.graphListItems.filter((r) => this.elementsToShow().includes(r.key)), ...(ngDevMode ? [{ debugName: "graphList" }] : /* istanbul ignore next */ []));
6236
+ this.restoreFromStorage();
6353
6237
  }
6354
6238
  toggle() {
6355
6239
  this.collapsed.update((collapsed) => !collapsed);
@@ -6361,91 +6245,78 @@ class VideoStatsComponent {
6361
6245
  (typeof value === 'number' && isNaN(value))) {
6362
6246
  return;
6363
6247
  }
6364
- if (this.elementsToShow.includes(el)) {
6365
- this.elementsToShow = this.elementsToShow.filter((item) => item !== el);
6366
- }
6367
- else {
6368
- this.elementsToShow.push(el);
6369
- }
6248
+ this.elementsToShow.update((current) => current.includes(el)
6249
+ ? current.filter((item) => item !== el)
6250
+ : [...current, el]);
6370
6251
  this.writeFields();
6371
- this.trigger$.next();
6372
6252
  }
6373
- getFields() {
6253
+ restoreFromStorage() {
6374
6254
  try {
6375
- const { elements, collapsed } = JSON.parse(localStorage.getItem(this.idKey)) || [];
6376
- this.elementsToShow = elements || [];
6377
- this.collapsed.set(collapsed);
6255
+ const { elements, collapsed } = JSON.parse(localStorage.getItem(this.idKey)) || {};
6256
+ this.elementsToShow.set(elements || []);
6257
+ this.collapsed.set(collapsed ?? false);
6378
6258
  }
6379
6259
  catch {
6380
- this.elementsToShow = [];
6260
+ this.elementsToShow.set([]);
6381
6261
  }
6382
6262
  }
6383
6263
  writeFields() {
6384
6264
  localStorage.setItem(this.idKey, JSON.stringify({
6385
6265
  collapsed: this.collapsed(),
6386
- elements: this.elementsToShow,
6266
+ elements: this.elementsToShow(),
6387
6267
  }));
6388
6268
  }
6389
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: VideoStatsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
6390
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.10", type: VideoStatsComponent, isStandalone: true, selector: "app-video-stats", ngImport: i0, template: "@if (videoStatus() && viewportReady()) {\n <div class=\"settings-container\">\n <button (click)=\"toggle()\" class=\"gear-button\">Stats</button>\n <div (click)=\"toggle()\" [class.min]=\"collapsed()\" id=\"aggregatedStats\">\n <div class=\"forNerds\">\n <div (click)=\"$event.stopPropagation()\" class=\"static\">\n <li>\n <app-stat-graph\n [stat]=\"fpsTick()!\"\n label=\"FPS (higher is better)\"\n />\n </li>\n <li>\n <app-stat-graph\n [stat]=\"videoQP()!\"\n label=\"QP (lower is better)\"\n color=\"#D5ff07\"\n />\n </li>\n <li>\n <app-stat-graph\n [stat]=\"bitrateTick()!\"\n label=\"Bitrate\"\n color=\"#D57F07\"\n />\n </li>\n </div>\n <div>\n @for (graph of graphList$ | async; track graph) {\n <li (click)=\"toggleGraph($event, graph.key, 0)\" class=\"graph\">\n <app-stat-graph\n [label]=\"graph.key\"\n [stat]=\"(graph.stat | async)!\"\n [color]=\"graph.color\"\n />\n </li>\n }\n </div>\n </div>\n\n @for (el of videoStatus(); track el.key + $index) {\n <div (click)=\"toggleGraph($event, el.key, el.value)\" class=\"stat\">\n <span>{{ el.key }}: </span>{{ el.value | json }}\n </div>\n }\n </div>\n </div>\n @if (!collapsed()) {\n <div [innerHTML]=\"ssInfo() | safe: 'html'\" class=\"ssInfo\"></div>\n }\n}\n", styles: [".settings-container{position:absolute;top:65px;left:10px;z-index:1000;bottom:10px}.gear-button{left:0;width:50px;position:absolute;background:none;cursor:pointer;font-size:10px;font-weight:500;border:1px solid;border-radius:9px;background:#0162cc;color:#fff;padding:4px}.gear-button>*{color:#fff;filter:drop-shadow(1px 1px 1px rgb(0,0,0))}#aggregatedStats{max-height:calc(100% - 25px);overflow-y:auto;margin-top:25px;left:20px;z-index:31;max-width:400px;padding:10px;background-color:#fff3;border:1px solid grey}#aggregatedStats>*{color:#fff;text-shadow:-1px -1px 0 #000,1px -1px 0 #000,-1px 1px 0 #000,1px 1px 0 #000}#aggregatedStats:empty{display:none}#aggregatedStats button{position:absolute;right:5px;top:5px}#aggregatedStats.min{display:none}#aggregatedStats .forNerds{width:100%}#aggregatedStats .forNerds li{list-style-type:none;height:60px}#aggregatedStats .forNerds li app-stat-graph{display:block;width:100%;height:100%}#aggregatedStats .stat span{font-weight:700}#aggregatedStats .stat,#aggregatedStats .graph{cursor:pointer}#aggregatedStats .static{pointer-events:all}.ssInfo{position:absolute;top:0;left:50%;transform:translate(-50%);color:#fff;text-shadow:-1px -1px 0 #000,1px -1px 0 #000,-1px 1px 0 #000,1px 1px 0 #000;text-align:center;width:80%;pointer-events:none}\n"], dependencies: [{ kind: "component", type: StatGraphComponent, selector: "app-stat-graph", inputs: ["label", "stat", "color", "tickStep"] }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: JsonPipe, name: "json" }, { kind: "pipe", type: SafePipe, name: "safe" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
6269
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: VideoStatsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
6270
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: VideoStatsComponent, isStandalone: true, selector: "app-video-stats", ngImport: i0, template: "@if (videoStatus() && viewportReady()) {\n <div class=\"settings-container\">\n <button (click)=\"toggle()\" class=\"gear-button\">Stats</button>\n <div (click)=\"toggle()\" [class.min]=\"collapsed()\" id=\"aggregatedStats\">\n <div class=\"forNerds\">\n <div (click)=\"$event.stopPropagation()\" class=\"static\">\n <li>\n <app-stat-graph\n [stat]=\"fpsTick()!\"\n label=\"FPS (higher is better)\"\n />\n </li>\n <li>\n <app-stat-graph\n [stat]=\"videoQP()!\"\n label=\"QP (lower is better)\"\n color=\"#D5ff07\"\n />\n </li>\n <li>\n <app-stat-graph\n [stat]=\"bitrateTick()!\"\n label=\"Bitrate\"\n color=\"#D57F07\"\n />\n </li>\n </div>\n <div>\n @for (graph of graphList(); track graph.key) {\n <li (click)=\"toggleGraph($event, graph.key, 0)\" class=\"graph\">\n <app-stat-graph\n [label]=\"graph.key\"\n [stat]=\"(graph.stat | async)!\"\n [color]=\"graph.color\"\n />\n </li>\n }\n </div>\n </div>\n\n @for (el of videoStatus(); track el.key + $index) {\n <div (click)=\"toggleGraph($event, el.key, el.value)\" class=\"stat\">\n <span>{{ el.key }}: </span>{{ el.value | json }}\n </div>\n }\n </div>\n </div>\n @if (!collapsed()) {\n <div class=\"ssInfo\">\n @for (line of ssData(); track line) {\n <div>{{ line }}</div>\n }\n </div>\n }\n}\n", styles: [".settings-container{position:absolute;top:65px;left:10px;z-index:1000;bottom:10px}.gear-button{left:0;width:50px;position:absolute;background:none;cursor:pointer;font-size:10px;font-weight:500;border:1px solid;border-radius:9px;background:#0162cc;color:#fff;padding:4px}.gear-button>*{color:#fff;filter:drop-shadow(1px 1px 1px rgb(0,0,0))}#aggregatedStats{max-height:calc(100% - 25px);overflow-y:auto;margin-top:25px;left:20px;z-index:31;max-width:400px;padding:10px;background-color:#fff3;border:1px solid grey}#aggregatedStats>*{color:#fff;text-shadow:-1px -1px 0 #000,1px -1px 0 #000,-1px 1px 0 #000,1px 1px 0 #000}#aggregatedStats:empty{display:none}#aggregatedStats button{position:absolute;right:5px;top:5px}#aggregatedStats.min{display:none}#aggregatedStats .forNerds{width:100%}#aggregatedStats .forNerds li{list-style-type:none;height:60px}#aggregatedStats .forNerds li app-stat-graph{display:block;width:100%;height:100%}#aggregatedStats .stat span{font-weight:700}#aggregatedStats .stat,#aggregatedStats .graph{cursor:pointer}#aggregatedStats .static{pointer-events:all}.ssInfo{position:absolute;top:0;left:50%;transform:translate(-50%);color:#fff;text-shadow:-1px -1px 0 #000,1px -1px 0 #000,-1px 1px 0 #000,1px 1px 0 #000;text-align:center;width:80%;pointer-events:none}\n"], dependencies: [{ kind: "component", type: StatGraphComponent, selector: "app-stat-graph", inputs: ["label", "stat", "color", "tickStep"] }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: JsonPipe, name: "json" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
6391
6271
  }
6392
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: VideoStatsComponent, decorators: [{
6272
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: VideoStatsComponent, decorators: [{
6393
6273
  type: Component,
6394
- args: [{ selector: 'app-video-stats', changeDetection: ChangeDetectionStrategy.OnPush, imports: [StatGraphComponent, AsyncPipe, JsonPipe, SafePipe], template: "@if (videoStatus() && viewportReady()) {\n <div class=\"settings-container\">\n <button (click)=\"toggle()\" class=\"gear-button\">Stats</button>\n <div (click)=\"toggle()\" [class.min]=\"collapsed()\" id=\"aggregatedStats\">\n <div class=\"forNerds\">\n <div (click)=\"$event.stopPropagation()\" class=\"static\">\n <li>\n <app-stat-graph\n [stat]=\"fpsTick()!\"\n label=\"FPS (higher is better)\"\n />\n </li>\n <li>\n <app-stat-graph\n [stat]=\"videoQP()!\"\n label=\"QP (lower is better)\"\n color=\"#D5ff07\"\n />\n </li>\n <li>\n <app-stat-graph\n [stat]=\"bitrateTick()!\"\n label=\"Bitrate\"\n color=\"#D57F07\"\n />\n </li>\n </div>\n <div>\n @for (graph of graphList$ | async; track graph) {\n <li (click)=\"toggleGraph($event, graph.key, 0)\" class=\"graph\">\n <app-stat-graph\n [label]=\"graph.key\"\n [stat]=\"(graph.stat | async)!\"\n [color]=\"graph.color\"\n />\n </li>\n }\n </div>\n </div>\n\n @for (el of videoStatus(); track el.key + $index) {\n <div (click)=\"toggleGraph($event, el.key, el.value)\" class=\"stat\">\n <span>{{ el.key }}: </span>{{ el.value | json }}\n </div>\n }\n </div>\n </div>\n @if (!collapsed()) {\n <div [innerHTML]=\"ssInfo() | safe: 'html'\" class=\"ssInfo\"></div>\n }\n}\n", styles: [".settings-container{position:absolute;top:65px;left:10px;z-index:1000;bottom:10px}.gear-button{left:0;width:50px;position:absolute;background:none;cursor:pointer;font-size:10px;font-weight:500;border:1px solid;border-radius:9px;background:#0162cc;color:#fff;padding:4px}.gear-button>*{color:#fff;filter:drop-shadow(1px 1px 1px rgb(0,0,0))}#aggregatedStats{max-height:calc(100% - 25px);overflow-y:auto;margin-top:25px;left:20px;z-index:31;max-width:400px;padding:10px;background-color:#fff3;border:1px solid grey}#aggregatedStats>*{color:#fff;text-shadow:-1px -1px 0 #000,1px -1px 0 #000,-1px 1px 0 #000,1px 1px 0 #000}#aggregatedStats:empty{display:none}#aggregatedStats button{position:absolute;right:5px;top:5px}#aggregatedStats.min{display:none}#aggregatedStats .forNerds{width:100%}#aggregatedStats .forNerds li{list-style-type:none;height:60px}#aggregatedStats .forNerds li app-stat-graph{display:block;width:100%;height:100%}#aggregatedStats .stat span{font-weight:700}#aggregatedStats .stat,#aggregatedStats .graph{cursor:pointer}#aggregatedStats .static{pointer-events:all}.ssInfo{position:absolute;top:0;left:50%;transform:translate(-50%);color:#fff;text-shadow:-1px -1px 0 #000,1px -1px 0 #000,-1px 1px 0 #000,1px 1px 0 #000;text-align:center;width:80%;pointer-events:none}\n"] }]
6274
+ args: [{ selector: 'app-video-stats', changeDetection: ChangeDetectionStrategy.OnPush, imports: [StatGraphComponent, AsyncPipe, JsonPipe], template: "@if (videoStatus() && viewportReady()) {\n <div class=\"settings-container\">\n <button (click)=\"toggle()\" class=\"gear-button\">Stats</button>\n <div (click)=\"toggle()\" [class.min]=\"collapsed()\" id=\"aggregatedStats\">\n <div class=\"forNerds\">\n <div (click)=\"$event.stopPropagation()\" class=\"static\">\n <li>\n <app-stat-graph\n [stat]=\"fpsTick()!\"\n label=\"FPS (higher is better)\"\n />\n </li>\n <li>\n <app-stat-graph\n [stat]=\"videoQP()!\"\n label=\"QP (lower is better)\"\n color=\"#D5ff07\"\n />\n </li>\n <li>\n <app-stat-graph\n [stat]=\"bitrateTick()!\"\n label=\"Bitrate\"\n color=\"#D57F07\"\n />\n </li>\n </div>\n <div>\n @for (graph of graphList(); track graph.key) {\n <li (click)=\"toggleGraph($event, graph.key, 0)\" class=\"graph\">\n <app-stat-graph\n [label]=\"graph.key\"\n [stat]=\"(graph.stat | async)!\"\n [color]=\"graph.color\"\n />\n </li>\n }\n </div>\n </div>\n\n @for (el of videoStatus(); track el.key + $index) {\n <div (click)=\"toggleGraph($event, el.key, el.value)\" class=\"stat\">\n <span>{{ el.key }}: </span>{{ el.value | json }}\n </div>\n }\n </div>\n </div>\n @if (!collapsed()) {\n <div class=\"ssInfo\">\n @for (line of ssData(); track line) {\n <div>{{ line }}</div>\n }\n </div>\n }\n}\n", styles: [".settings-container{position:absolute;top:65px;left:10px;z-index:1000;bottom:10px}.gear-button{left:0;width:50px;position:absolute;background:none;cursor:pointer;font-size:10px;font-weight:500;border:1px solid;border-radius:9px;background:#0162cc;color:#fff;padding:4px}.gear-button>*{color:#fff;filter:drop-shadow(1px 1px 1px rgb(0,0,0))}#aggregatedStats{max-height:calc(100% - 25px);overflow-y:auto;margin-top:25px;left:20px;z-index:31;max-width:400px;padding:10px;background-color:#fff3;border:1px solid grey}#aggregatedStats>*{color:#fff;text-shadow:-1px -1px 0 #000,1px -1px 0 #000,-1px 1px 0 #000,1px 1px 0 #000}#aggregatedStats:empty{display:none}#aggregatedStats button{position:absolute;right:5px;top:5px}#aggregatedStats.min{display:none}#aggregatedStats .forNerds{width:100%}#aggregatedStats .forNerds li{list-style-type:none;height:60px}#aggregatedStats .forNerds li app-stat-graph{display:block;width:100%;height:100%}#aggregatedStats .stat span{font-weight:700}#aggregatedStats .stat,#aggregatedStats .graph{cursor:pointer}#aggregatedStats .static{pointer-events:all}.ssInfo{position:absolute;top:0;left:50%;transform:translate(-50%);color:#fff;text-shadow:-1px -1px 0 #000,1px -1px 0 #000,-1px 1px 0 #000,1px 1px 0 #000;text-align:center;width:80%;pointer-events:none}\n"] }]
6395
6275
  }], ctorParameters: () => [] });
6396
6276
 
6397
6277
  class ResumeStreamButtonComponent {
6278
+ #store;
6398
6279
  constructor() {
6399
- this.actions$ = inject(Actions);
6400
6280
  this.#store = inject(Store);
6401
- this.#videoService = inject(VideoService);
6402
- this.#destroyRef = inject(DestroyRef);
6403
- this.showReconnectPopup = this.#store.selectSignal(selectShowReconnectPopup);
6404
- this.isLoaderScreenVisible = this.#store.selectSignal(selectIsLoaderScreenVisible);
6405
6281
  this.streamConfig = this.#store.selectSignal(selectStreamConfig);
6406
- this.isExistMatchUrls = this.#store.selectSignal(selectIsExistMatchUrls);
6407
6282
  this.isSecondStart = signal(!!(this.streamConfig()?.autoStart && location.href.match(/^https/gi)), ...(ngDevMode ? [{ debugName: "isSecondStart" }] : /* istanbul ignore next */ []));
6283
+ this.isLoaderScreenVisible = this.#store.selectSignal(selectIsLoaderScreenVisible);
6284
+ this.showReconnectPopup = this.#store.selectSignal(selectShowReconnectPopup);
6285
+ this.isExistMatchUrls = this.#store.selectSignal(selectIsExistMatchUrls);
6286
+ this.showResumeStreamPopup = this.#store.selectSignal(unrealFeature.selectShowResumeStreamPopup);
6408
6287
  this.isShowResumeButton = computed(() => this.isLoaderScreenVisible() &&
6409
6288
  this.showReconnectPopup() &&
6410
- this.isExistMatchUrls(), ...(ngDevMode ? [{ debugName: "isShowResumeButton" }] : /* istanbul ignore next */ []));
6411
- }
6412
- #store;
6413
- #videoService;
6414
- #destroyRef;
6415
- ngOnInit() {
6416
- this.#store
6417
- .select(unrealFeature.selectCirrusConnected)
6418
- .pipe(skip$1(1), filter(Falsy), takeUntilDestroyed(this.#destroyRef))
6419
- .subscribe(() => this.onDisconnect());
6420
- }
6421
- ngAfterViewInit() {
6422
- this.actions$
6423
- .pipe(ofType(videoStreamInitialized), takeUntilDestroyed(this.#destroyRef), filter(() => !!this.#videoService.video?.paused))
6424
- .subscribe(() => {
6425
- Logger.info('Simulate Press Play');
6426
- this.#videoService.play();
6427
- this.#store.dispatch(resetAfk());
6289
+ this.isExistMatchUrls() &&
6290
+ this.showResumeStreamPopup(), ...(ngDevMode ? [{ debugName: "isShowResumeButton" }] : /* istanbul ignore next */ []));
6291
+ this.cirrusConnected = this.#store.selectSignal(unrealFeature.selectCirrusConnected);
6292
+ let wasConnected = this.cirrusConnected();
6293
+ effect(() => {
6294
+ const connected = this.cirrusConnected();
6295
+ if (!connected && wasConnected) {
6296
+ this.disconnect();
6297
+ }
6298
+ wasConnected = connected;
6428
6299
  });
6429
6300
  }
6430
6301
  connect() {
6431
6302
  this.#store.dispatch(startStream({ config: { autoStart: true } }));
6432
6303
  }
6433
- onDisconnect() {
6304
+ disconnect() {
6434
6305
  this.isSecondStart.set(true);
6435
6306
  }
6436
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: ResumeStreamButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
6437
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.10", type: ResumeStreamButtonComponent, isStandalone: true, selector: "app-resume-stream-button", ngImport: i0, template: "@if (isShowResumeButton()) {\n <div class=\"resume-box\">\n @if (isSecondStart()) {\n <div class=\"resume-box__text\">\n Your stream has been paused due to inactivity\n </div>\n }\n\n <src-button\n (onClick)=\"connect()\"\n [isFullWidth]=\"true\"\n [data-testid]=\"'connect-button'\"\n size=\"lg\"\n weight=\"primary\"\n class=\"connect-button\"\n >\n {{ isSecondStart() ? 'Resume' : 'Start' }}\n </src-button>\n </div>\n}\n", styles: [".resume-box{padding:16px;position:absolute;bottom:0;left:50%;gap:12px;z-index:6;display:flex;transform:translate(-50%,-50%);flex-direction:column;align-items:center;border-radius:var(--src-border-rounded-parent, 8px);background:var(--src-surface-container-main, #fff);box-shadow:0 26px 80px #0003,0 0 1px #0003}.resume-box .connect-button{width:100%}.resume-box .resume-box__text{color:var(--src-color-grey-500, #6b7280);text-align:center;font-family:var(--src-font-family-body);font-size:14px;font-style:normal;font-weight:400;line-height:24px}\n"], dependencies: [{ kind: "component", type: SourceButtonComponent, selector: "src-button", inputs: ["type", "appearance", "weight", "size", "context", "customClass", "isFullWidth", "isPressed", "isDisabled", "isLoading", "isInverted", "srcButtonConfig", "formID", "data-testid", "ariaLabel"], outputs: ["onClick", "onSubmit"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
6307
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: ResumeStreamButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
6308
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: ResumeStreamButtonComponent, isStandalone: true, selector: "app-resume-stream-button", ngImport: i0, template: "@if (isShowResumeButton()) {\n <div class=\"resume-box\">\n @if (isSecondStart()) {\n <div class=\"resume-box__text\">\n Your stream has been paused due to inactivity\n </div>\n }\n\n <src-button\n (onClick)=\"connect()\"\n [isFullWidth]=\"true\"\n [data-testid]=\"'connect-button'\"\n size=\"lg\"\n weight=\"primary\"\n class=\"connect-button\"\n >\n {{ isSecondStart() ? 'Resume' : 'Start' }}\n </src-button>\n </div>\n}\n", styles: [".resume-box{padding:16px;position:absolute;bottom:0;left:50%;gap:12px;z-index:6;display:flex;transform:translate(-50%,-50%);flex-direction:column;align-items:center;border-radius:var(--src-border-rounded-parent, 8px);background:var(--src-surface-container-main, #fff);box-shadow:0 26px 80px #0003,0 0 1px #0003}.resume-box .connect-button{width:100%}.resume-box .resume-box__text{color:var(--src-color-grey-500, #6b7280);text-align:center;font-family:var(--src-font-family-body);font-size:14px;font-style:normal;font-weight:400;line-height:24px}\n"], dependencies: [{ kind: "component", type: SourceButtonComponent, selector: "src-button", inputs: ["type", "appearance", "weight", "size", "context", "customClass", "isFullWidth", "isPressed", "isDisabled", "isLoading", "isInverted", "srcButtonConfig", "formID", "data-testid", "ariaLabel"], outputs: ["onClick", "onSubmit"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
6438
6309
  }
6439
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: ResumeStreamButtonComponent, decorators: [{
6310
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: ResumeStreamButtonComponent, decorators: [{
6440
6311
  type: Component,
6441
6312
  args: [{ selector: 'app-resume-stream-button', changeDetection: ChangeDetectionStrategy.OnPush, imports: [SourceButtonComponent], template: "@if (isShowResumeButton()) {\n <div class=\"resume-box\">\n @if (isSecondStart()) {\n <div class=\"resume-box__text\">\n Your stream has been paused due to inactivity\n </div>\n }\n\n <src-button\n (onClick)=\"connect()\"\n [isFullWidth]=\"true\"\n [data-testid]=\"'connect-button'\"\n size=\"lg\"\n weight=\"primary\"\n class=\"connect-button\"\n >\n {{ isSecondStart() ? 'Resume' : 'Start' }}\n </src-button>\n </div>\n}\n", styles: [".resume-box{padding:16px;position:absolute;bottom:0;left:50%;gap:12px;z-index:6;display:flex;transform:translate(-50%,-50%);flex-direction:column;align-items:center;border-radius:var(--src-border-rounded-parent, 8px);background:var(--src-surface-container-main, #fff);box-shadow:0 26px 80px #0003,0 0 1px #0003}.resume-box .connect-button{width:100%}.resume-box .resume-box__text{color:var(--src-color-grey-500, #6b7280);text-align:center;font-family:var(--src-font-family-body);font-size:14px;font-style:normal;font-weight:400;line-height:24px}\n"] }]
6442
- }] });
6313
+ }], ctorParameters: () => [] });
6443
6314
 
6444
6315
  class IntroSrcComponent {
6445
6316
  #store;
6446
6317
  constructor() {
6447
- this.videoElement = viewChild('videoPlayerCover', ...(ngDevMode ? [{ debugName: "videoElement" }] : /* istanbul ignore next */ []));
6448
6318
  this.#store = inject(Store);
6319
+ this.videoElement = viewChild('videoPlayerCover', ...(ngDevMode ? [{ debugName: "videoElement" }] : /* istanbul ignore next */ []));
6449
6320
  this.imageIntroSrc = this.#store.selectSignal(unrealFeature.selectImageIntroSrc);
6450
6321
  this.videoIntroSrc = this.#store.selectSignal(unrealFeature.selectVideoIntroSrc);
6451
6322
  this.viewportReady = this.#store.selectSignal(unrealFeature.selectViewportReady);
@@ -6469,6 +6340,9 @@ class IntroSrcComponent {
6469
6340
  });
6470
6341
  });
6471
6342
  }
6343
+ loadingImageError() {
6344
+ this.#store.dispatch(resetIntroSrc());
6345
+ }
6472
6346
  createVideo(src, videoContainer) {
6473
6347
  if (typeof src !== 'string') {
6474
6348
  return;
@@ -6499,15 +6373,12 @@ class IntroSrcComponent {
6499
6373
  sourceElement.addEventListener('error', () => this.#store.dispatch(resetIntroSrc()));
6500
6374
  videoContainer.appendChild(videoElement);
6501
6375
  }
6502
- imageError() {
6503
- this.#store.dispatch(resetIntroSrc());
6504
- }
6505
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: IntroSrcComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
6506
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.10", type: IntroSrcComponent, isStandalone: true, selector: "app-intro-src", viewQueries: [{ propertyName: "videoElement", first: true, predicate: ["videoPlayerCover"], descendants: true, isSignal: true }], ngImport: i0, template: "@let image = imageIntroSrc();\n@if (image) {\n <div class=\"backImage\">\n <img (error)=\"imageError()\" [ngSrc]=\"image\" fill alt=\"imageSrc\" />\n </div>\n} @else if (videoIntroSrc()) {\n <div #videoPlayerCover class=\"backVideo\"></div>\n}\n", styles: [".backVideo{position:absolute;top:0;left:0;display:flex;justify-content:center;align-items:center;width:100%;height:100%;z-index:1}.backVideo video{position:absolute;width:100%;height:100%;object-fit:cover}.backImage{position:absolute;top:0;left:0;display:flex;justify-content:center;align-items:center;width:100%;height:100%;z-index:1}.backImage img{position:absolute;width:100%;height:100%;object-fit:cover}\n"], dependencies: [{ kind: "directive", type: NgOptimizedImage, selector: "img[ngSrc]", inputs: ["ngSrc", "ngSrcset", "sizes", "width", "height", "decoding", "loading", "priority", "loaderParams", "disableOptimizedSrcset", "fill", "placeholder", "placeholderConfig", "src", "srcset"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
6376
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: IntroSrcComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
6377
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: IntroSrcComponent, isStandalone: true, selector: "app-intro-src", viewQueries: [{ propertyName: "videoElement", first: true, predicate: ["videoPlayerCover"], descendants: true, isSignal: true }], ngImport: i0, template: "@let image = imageIntroSrc();\n@if (image) {\n <div class=\"backImage\">\n <img (error)=\"loadingImageError()\" [ngSrc]=\"image\" fill alt=\"imageSrc\" />\n </div>\n} @else if (videoIntroSrc()) {\n <div #videoPlayerCover class=\"backVideo\"></div>\n}\n", styles: [".backVideo{position:absolute;top:0;left:0;display:flex;justify-content:center;align-items:center;width:100%;height:100%;z-index:1}.backVideo video{position:absolute;width:100%;height:100%;object-fit:cover}.backImage{position:absolute;top:0;left:0;display:flex;justify-content:center;align-items:center;width:100%;height:100%;z-index:1}.backImage img{position:absolute;width:100%;height:100%;object-fit:cover}\n"], dependencies: [{ kind: "directive", type: NgOptimizedImage, selector: "img[ngSrc]", inputs: ["ngSrc", "ngSrcset", "sizes", "width", "height", "decoding", "loading", "priority", "loaderParams", "disableOptimizedSrcset", "fill", "placeholder", "placeholderConfig", "src", "srcset"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
6507
6378
  }
6508
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: IntroSrcComponent, decorators: [{
6379
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: IntroSrcComponent, decorators: [{
6509
6380
  type: Component,
6510
- args: [{ selector: 'app-intro-src', imports: [NgOptimizedImage], changeDetection: ChangeDetectionStrategy.OnPush, template: "@let image = imageIntroSrc();\n@if (image) {\n <div class=\"backImage\">\n <img (error)=\"imageError()\" [ngSrc]=\"image\" fill alt=\"imageSrc\" />\n </div>\n} @else if (videoIntroSrc()) {\n <div #videoPlayerCover class=\"backVideo\"></div>\n}\n", styles: [".backVideo{position:absolute;top:0;left:0;display:flex;justify-content:center;align-items:center;width:100%;height:100%;z-index:1}.backVideo video{position:absolute;width:100%;height:100%;object-fit:cover}.backImage{position:absolute;top:0;left:0;display:flex;justify-content:center;align-items:center;width:100%;height:100%;z-index:1}.backImage img{position:absolute;width:100%;height:100%;object-fit:cover}\n"] }]
6381
+ args: [{ selector: 'app-intro-src', imports: [NgOptimizedImage], changeDetection: ChangeDetectionStrategy.OnPush, template: "@let image = imageIntroSrc();\n@if (image) {\n <div class=\"backImage\">\n <img (error)=\"loadingImageError()\" [ngSrc]=\"image\" fill alt=\"imageSrc\" />\n </div>\n} @else if (videoIntroSrc()) {\n <div #videoPlayerCover class=\"backVideo\"></div>\n}\n", styles: [".backVideo{position:absolute;top:0;left:0;display:flex;justify-content:center;align-items:center;width:100%;height:100%;z-index:1}.backVideo video{position:absolute;width:100%;height:100%;object-fit:cover}.backImage{position:absolute;top:0;left:0;display:flex;justify-content:center;align-items:center;width:100%;height:100%;z-index:1}.backImage img{position:absolute;width:100%;height:100%;object-fit:cover}\n"] }]
6511
6382
  }], ctorParameters: () => [], propDecorators: { videoElement: [{ type: i0.ViewChild, args: ['videoPlayerCover', { isSignal: true }] }] } });
6512
6383
 
6513
6384
  class UnrealSceneComponent {
@@ -6527,62 +6398,57 @@ class UnrealSceneComponent {
6527
6398
  onMouseOut() {
6528
6399
  this.changeMouseOverScene.emit(false);
6529
6400
  }
6401
+ onContextMenu(event) {
6402
+ event.preventDefault();
6403
+ }
6530
6404
  constructor() {
6531
- this.isStudio = input(false, ...(ngDevMode ? [{ debugName: "isStudio" }] : /* istanbul ignore next */ []));
6532
6405
  this.useContainerAsSizeProvider = input(true, ...(ngDevMode ? [{ debugName: "useContainerAsSizeProvider" }] : /* istanbul ignore next */ []));
6533
- this.studioResolutionSize = input({
6406
+ this.isStudio = input(false, ...(ngDevMode ? [{ debugName: "isStudio" }] : /* istanbul ignore next */ []));
6407
+ this.resolutionSize = input({
6534
6408
  width: FULL_HD_WIDTH,
6535
6409
  height: FULL_HD_HEIGHT,
6536
- }, ...(ngDevMode ? [{ debugName: "studioResolutionSize" }] : /* istanbul ignore next */ []));
6537
- this.studioResolutionValues = toObservable(computed(() => {
6538
- const values = this.studioResolutionSize();
6539
- return { width: values.width, height: values.height };
6540
- }));
6541
- this.videoElement = viewChild('videoContainer', ...(ngDevMode ? [{ debugName: "videoElement" }] : /* istanbul ignore next */ []));
6410
+ }, ...(ngDevMode ? [{ debugName: "resolutionSize" }] : /* istanbul ignore next */ []));
6542
6411
  this.changeMouseOverScene = output();
6412
+ this.videoElement = viewChild('videoContainer', ...(ngDevMode ? [{ debugName: "videoElement" }] : /* istanbul ignore next */ []));
6413
+ this.resolutionValues$ = toObservable(this.resolutionSize);
6543
6414
  this.width = FULL_HD_WIDTH;
6544
6415
  this.height = FULL_HD_HEIGHT;
6545
6416
  this.actions$ = inject(Actions);
6546
6417
  this.#store = inject(Store);
6547
6418
  this.isExistMatchUrls = this.#store.selectSignal(selectIsExistMatchUrls);
6548
6419
  this.isDevMode = inject(DevModeService).isDevMode;
6549
- this.lightMode = this.#store.selectSignal(unrealFeature.selectLowBandwidth);
6550
6420
  this.isFreezeFrameLoading = this.#store.selectSignal(selectIsFreezeFrameLoading);
6551
6421
  this.maxResolution = Truthy(new URLSearchParams(location.search).get('maxresolution'));
6552
6422
  this.commandsSender = inject(UnrealCommunicatorService);
6553
6423
  this.videoService = inject(VideoService);
6554
- this.element = inject(ElementRef);
6555
6424
  this.destroyRef = inject(DestroyRef);
6556
6425
  this.destroyRef.onDestroy(() => this.#store.dispatch(destroyUnrealScene()));
6557
6426
  effect(() => {
6558
6427
  const videoElement = this.videoElement();
6559
6428
  const isExistMatchUrls = this.isExistMatchUrls();
6560
- if (videoElement && isExistMatchUrls) {
6561
- this.#store.dispatch(initSignalling());
6562
- }
6429
+ untracked(() => {
6430
+ if (videoElement && isExistMatchUrls) {
6431
+ this.#store.dispatch(initSignalling());
6432
+ }
6433
+ });
6563
6434
  });
6564
6435
  afterNextRender({
6565
6436
  read: () => {
6566
6437
  const videoElement = this.videoElement();
6567
6438
  if (videoElement) {
6568
6439
  this.videoService.setContainer(videoElement.nativeElement);
6569
- if (this.element?.nativeElement) {
6570
- fromEvent(this.element?.nativeElement, 'contextmenu')
6571
- .pipe(takeUntilDestroyed(this.destroyRef))
6572
- .subscribe((event) => event.preventDefault());
6573
- }
6574
6440
  this.listenResizeValues();
6575
6441
  }
6576
6442
  },
6577
6443
  });
6578
6444
  }
6579
6445
  listenResizeValues() {
6580
- const customSizeInputChanged$ = this.studioResolutionValues.pipe(filter(Truthy), distinctUntilChanged(), filter(({ width, height }) => this.width !== width || this.height !== height), map$1(({ width, height }) => ({ width, height })), tap(({ width, height }) => {
6446
+ const customSizeInputChanged$ = this.resolutionValues$.pipe(filter(({ width, height }) => this.width !== width || this.height !== height), tap(({ width, height }) => {
6581
6447
  this.width = width;
6582
6448
  this.height = height;
6583
6449
  }));
6584
6450
  const resizeRequired$ = merge(this.actions$.pipe(ofType(videoStreamInitialized)), fromEvent(window, 'resize'));
6585
- const videoStreamSizeChanged$ = this.videoService?.videoStats$.pipe(filter(() => !this.isFreezeFrameLoading()), map$1((stats) => ({
6451
+ const videoStreamSizeChanged$ = this.videoService.videoStats$.pipe(filter(() => !this.isFreezeFrameLoading()), map$1((stats) => ({
6586
6452
  w: stats.aggregatedStats.frameWidth || 0,
6587
6453
  h: stats.aggregatedStats.frameHeight || 0,
6588
6454
  })), distinctUntilChanged((previous, current) => fpIsASameAsB(previous.w, current.w, SAME_SIZE_THRESHOLD) &&
@@ -6590,12 +6456,17 @@ class UnrealSceneComponent {
6590
6456
  merge(customSizeInputChanged$, resizeRequired$, videoStreamSizeChanged$)
6591
6457
  .pipe(debounceTime(DEBOUNCE_TO_MANY_RESIZE_CALLS), switchMap(() => {
6592
6458
  const key = Math.random();
6593
- // Monitoring the size of the video stream
6459
+ // Poll 3 times to capture late-arriving DOM measurements after layout settles
6594
6460
  return timer(0, 300).pipe(take(3), map$1(() => key));
6595
6461
  }), map$1((key) => ({ key, ...this.resizeValues })), distinctUntilChanged((previous, current) => fpIsASameAsB(previous.w, current.w, SAME_SIZE_THRESHOLD) &&
6596
6462
  fpIsASameAsB(previous.h, current.h, SAME_SIZE_THRESHOLD) &&
6597
- previous.key === current.key), tap((size) => this.adaptVideo(size)), distinctUntilChanged((previous, current) => fpIsASameAsB(previous.w, current.w, SAME_SIZE_THRESHOLD) &&
6598
- fpIsASameAsB(previous.h, current.h, SAME_SIZE_THRESHOLD)), combineLatestWith(this.#store
6463
+ previous.key === current.key), tap((size) => this.adaptVideo(size)),
6464
+ // Key excluded: only dispatch when actual pixel dimensions change
6465
+ distinctUntilChanged((previous, current) => fpIsASameAsB(previous.w, current.w, SAME_SIZE_THRESHOLD) &&
6466
+ fpIsASameAsB(previous.h, current.h, SAME_SIZE_THRESHOLD)),
6467
+ // Holds dispatch until data channel is up; retains true after disconnect
6468
+ // so resizes still reach Unreal — cleanup is handled by destroyUnrealScene
6469
+ combineLatestWith(this.#store
6599
6470
  .select(unrealFeature.selectDataChannelConnected)
6600
6471
  .pipe(filter(Truthy))), takeUntilDestroyed(this.destroyRef))
6601
6472
  .subscribe(([size]) => {
@@ -6623,11 +6494,13 @@ class UnrealSceneComponent {
6623
6494
  this.#store.dispatch(videoAdaptedToContainer());
6624
6495
  }
6625
6496
  getResizeValuesUniversal() {
6626
- let containerWidth = window.innerWidth || 2;
6627
- let containerHeight = window.innerHeight || 2;
6497
+ let containerWidth = window.innerWidth || MIN_CONTAINER_SIZE;
6498
+ let containerHeight = window.innerHeight || MIN_CONTAINER_SIZE;
6628
6499
  if (this.useContainerAsSizeProvider()) {
6629
- containerWidth = this.videoElement()?.nativeElement.offsetWidth || 2;
6630
- containerHeight = this.videoElement()?.nativeElement.offsetHeight || 2;
6500
+ containerWidth =
6501
+ this.videoElement()?.nativeElement.offsetWidth || MIN_CONTAINER_SIZE;
6502
+ containerHeight =
6503
+ this.videoElement()?.nativeElement.offsetHeight || MIN_CONTAINER_SIZE;
6631
6504
  }
6632
6505
  const pixelRatio = this.pixelRatio;
6633
6506
  return getResizeValuesUniversal({
@@ -6641,8 +6514,8 @@ class UnrealSceneComponent {
6641
6514
  const videoElement = this.videoElement()?.nativeElement;
6642
6515
  return getResizeStudioValues({
6643
6516
  pixelRatio: this.pixelRatio,
6644
- containerWidth: videoElement?.offsetWidth || 2,
6645
- containerHeight: videoElement?.offsetHeight || 2,
6517
+ containerWidth: videoElement?.offsetWidth || MIN_CONTAINER_SIZE,
6518
+ containerHeight: videoElement?.offsetHeight || MIN_CONTAINER_SIZE,
6646
6519
  studioWidth: this.width,
6647
6520
  studioHeight: this.height,
6648
6521
  maxResolution: this.maxResolution,
@@ -6659,10 +6532,10 @@ class UnrealSceneComponent {
6659
6532
  },
6660
6533
  });
6661
6534
  }
6662
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: UnrealSceneComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
6663
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.10", type: UnrealSceneComponent, isStandalone: true, selector: "app-unreal-scene", inputs: { isStudio: { classPropertyName: "isStudio", publicName: "isStudio", isSignal: true, isRequired: false, transformFunction: null }, useContainerAsSizeProvider: { classPropertyName: "useContainerAsSizeProvider", publicName: "useContainerAsSizeProvider", isSignal: true, isRequired: false, transformFunction: null }, studioResolutionSize: { classPropertyName: "studioResolutionSize", publicName: "studioResolutionSize", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { changeMouseOverScene: "changeMouseOverScene" }, host: { listeners: { "mouseover": "onMouseOver()", "mouseout": "onMouseOut()" } }, viewQueries: [{ propertyName: "videoElement", first: true, predicate: ["videoContainer"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"frame unreal-container\">\n <div #videoContainer [class.lightModeCursor]=\"lightMode()\" id=\"player\"></div>\n\n <app-intro-src />\n <app-image-loading-src />\n <app-resume-stream-button />\n <app-unreal-status />\n <app-freeze-frame />\n <app-afk-timeout-modal />\n\n <ng-content select=\"app-pdf\"></ng-content>\n <ng-content select=\"app-show-case\"></ng-content>\n <ng-content select=\"app-low-bandwidth-detector\"></ng-content>\n\n @if (isDevMode) {\n <app-video-stats />\n }\n</div>\n", styles: [".unreal-container{position:absolute;top:0;left:0;width:100%;height:100%;background-color:#fff}.unreal-container #player{position:absolute;width:100%;height:100%;display:flex;justify-content:center;align-items:center;overflow:hidden}.unreal-container.select{cursor:url('data:image/svg+xml,<svg width=\"24\" height=\"24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M15.15 21.375a1.423 1.423 0 0 1-1.15.063 1.487 1.487 0 0 1-.85-.788l-3-6.45-2.325 3.25c-.283.4-.658.525-1.125.375-.467-.15-.7-.466-.7-.95V4.05c0-.417.188-.717.563-.9.375-.183.729-.142 1.062.125l10.1 7.95c.383.284.496.65.337 1.1-.158.45-.479.675-.962.675h-4.2l2.975 6.375c.183.384.204.767.063 1.15a1.487 1.487 0 0 1-.788.85Z\" fill=\"%23000\"/><path d=\"m12.697 20.861.002.005c.237.495.617.852 1.128 1.04.515.191 1.039.16 1.539-.08a1.987 1.987 0 0 0 1.04-1.128 1.922 1.922 0 0 0-.079-1.536v-.003L13.684 13.5H17.1c.32 0 .628-.075.89-.26.263-.184.438-.447.544-.749.106-.3.135-.617.04-.925a1.458 1.458 0 0 0-.545-.738L7.936 2.883a1.51 1.51 0 0 0-.768-.336 1.471 1.471 0 0 0-.825.154 1.495 1.495 0 0 0-.626.547 1.492 1.492 0 0 0-.217.802v12.825c0 .324.08.634.272.897.193.261.467.43.775.53.31.099.632.12.942.016.31-.103.555-.313.743-.578h.001l1.825-2.552 2.639 5.673Z\" stroke=\"%23fff\" stroke-opacity=\".7\"/></svg>') 6 2,auto}.unreal-container.orbit{cursor:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" fill=\"none\"><path fill=\"%23000\" d=\"M11.998 21.6c-1.966 0-3.737-.53-5.312-1.588-1.575-1.058-2.738-2.437-3.488-4.137a.852.852 0 0 1 .013-.713.867.867 0 0 1 .512-.487.86.86 0 0 1 .688.037c.225.109.387.28.487.513.617 1.35 1.559 2.45 2.825 3.3 1.267.85 2.692 1.275 4.275 1.275 1.384 0 2.646-.33 3.788-.988a7.985 7.985 0 0 0 2.762-2.612h-.85a.873.873 0 0 1-.64-.257.863.863 0 0 1-.26-.638.88.88 0 0 1 .26-.643.864.864 0 0 1 .64-.262h3c.255 0 .47.086.642.259a.87.87 0 0 1 .258.64v3a.874.874 0 0 1-.257.642.863.863 0 0 1-.637.259.88.88 0 0 1-.643-.259.865.865 0 0 1-.263-.641v-.725a9.95 9.95 0 0 1-3.35 2.925c-1.35.733-2.833 1.1-4.45 1.1Zm0-17.4c-1.383 0-2.645.33-3.787.987A7.985 7.985 0 0 0 5.448 7.8h.85c.255 0 .47.086.642.257a.863.863 0 0 1 .258.638.88.88 0 0 1-.258.642.864.864 0 0 1-.642.263h-3a.87.87 0 0 1-.64-.259.87.87 0 0 1-.26-.641v-3c0-.255.086-.469.258-.641a.863.863 0 0 1 .637-.26.88.88 0 0 1 .643.26.864.864 0 0 1 .262.64v.726A9.95 9.95 0 0 1 7.548 3.5c1.35-.733 2.834-1.1 4.45-1.1 2 0 3.788.542 5.363 1.625 1.575 1.083 2.736 2.49 3.482 4.22a.724.724 0 0 1-.045.646.926.926 0 0 1-.525.437.87.87 0 0 1-.687-.04.977.977 0 0 1-.488-.513c-.616-1.35-1.558-2.45-2.825-3.3-1.266-.85-2.691-1.275-4.275-1.275Zm0 10.8a2.893 2.893 0 0 1-2.125-.875A2.893 2.893 0 0 1 8.998 12c0-.833.292-1.542.875-2.125A2.893 2.893 0 0 1 11.998 9c.834 0 1.542.292 2.125.875.584.583.875 1.292.875 2.125s-.291 1.542-.875 2.125a2.893 2.893 0 0 1-2.125.875Z\"/><path stroke=\"%23fff\" stroke-opacity=\".7\" d=\"m2.739 16.072.002.005c.789 1.788 2.014 3.24 3.666 4.35 1.66 1.116 3.53 1.673 5.591 1.673 1.696 0 3.262-.386 4.69-1.16a10.54 10.54 0 0 0 2.714-2.097c.069.167.171.32.308.454.272.268.613.403.994.403.38 0 .722-.135.992-.406s.402-.614.402-.994v-3c0-.381-.134-.724-.405-.995a1.37 1.37 0 0 0-.995-.405h-3c-.383 0-.727.137-.997.411a1.38 1.38 0 0 0-.403.994c0 .38.135.723.407.992.238.237.532.369.858.397a7.552 7.552 0 0 1-2.027 1.685c-1.062.613-2.237.92-3.538.92-1.488 0-2.815-.397-3.996-1.19-1.19-.798-2.07-1.826-2.647-3.087-.147-.34-.393-.6-.727-.76a1.36 1.36 0 0 0-1.073-.058c-.37.133-.64.397-.8.754a1.351 1.351 0 0 0-.016 1.114Zm4.553-8.37a1.36 1.36 0 0 0-.858-.396A7.552 7.552 0 0 1 8.46 5.62c1.062-.613 2.237-.921 3.537-.921 1.489 0 2.816.398 3.997 1.19 1.19.8 2.07 1.827 2.646 3.088.148.34.394.6.728.76a1.37 1.37 0 0 0 1.068.063c.347-.12.62-.346.799-.668.187-.338.215-.71.072-1.072l-.003-.007-.003-.007c-.784-1.818-2.006-3.298-3.658-4.434C15.982 2.47 14.094 1.9 11.998 1.9c-1.695 0-3.261.385-4.688 1.16a10.536 10.536 0 0 0-2.715 2.096 1.364 1.364 0 0 0-.308-.453 1.38 1.38 0 0 0-.994-.403c-.38 0-.722.135-.992.406s-.403.614-.403.994v3c0 .38.135.724.406.995.27.27.614.405.994.405h3c.384 0 .727-.137.998-.412a1.38 1.38 0 0 0 .402-.993c0-.38-.135-.723-.406-.992ZM9.52 14.48a3.393 3.393 0 0 0 2.478 1.02c.965 0 1.8-.342 2.479-1.02a3.392 3.392 0 0 0 1.021-2.48c0-.963-.343-1.8-1.021-2.478A3.392 3.392 0 0 0 11.998 8.5c-.964 0-1.8.343-2.478 1.021A3.392 3.392 0 0 0 8.498 12c0 .964.344 1.8 1.022 2.479Z\"/></svg>') 12 12,auto}.unreal-container.pan{cursor:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" fill=\"none\"><path fill=\"%23000\" d=\"M11.997 10.075a1.01 1.01 0 0 1-.738-.313 1.008 1.008 0 0 1-.312-.737V6.15l-.95.95a.998.998 0 0 1-.763.312 1.09 1.09 0 0 1-.763-.337 1.06 1.06 0 0 1-.324-.775c0-.3.108-.558.324-.775l2.776-2.775c.117-.116.237-.2.362-.25a1.04 1.04 0 0 1 .775 0c.125.05.246.134.363.25l2.8 2.8c.216.217.32.47.313.762a1.096 1.096 0 0 1-.338.763 1.058 1.058 0 0 1-.775.325c-.3 0-.558-.108-.775-.325l-.925-.925v2.875c0 .283-.104.53-.312.737a1.01 1.01 0 0 1-.738.313Zm0 11.5a1.04 1.04 0 0 1-.388-.075 1.103 1.103 0 0 1-.362-.25l-2.8-2.8a1.002 1.002 0 0 1-.313-.762c.009-.292.121-.546.337-.763.217-.217.475-.325.775-.325.3 0 .559.108.775.325l.926.925v-2.875c0-.283.104-.529.312-.737a1.01 1.01 0 0 1 .738-.313c.283 0 .529.104.738.313.208.208.312.454.312.737v2.875l.95-.95a.998.998 0 0 1 .762-.312c.292.008.546.12.763.337.216.217.324.475.324.775 0 .3-.108.559-.324.775l-2.775 2.775c-.117.116-.238.2-.363.25a1.04 1.04 0 0 1-.387.075Zm4.925-6.05a1.058 1.058 0 0 1-.326-.774c0-.3.109-.559.326-.776l.925-.925H14.97c-.283 0-.528-.104-.736-.312a1.01 1.01 0 0 1-.313-.738c0-.283.104-.53.313-.738.208-.208.453-.312.737-.312h2.875l-.95-.95a.998.998 0 0 1-.313-.762 1.09 1.09 0 0 1 .338-.764 1.06 1.06 0 0 1 .775-.324c.3 0 .558.108.775.324l2.774 2.776c.117.117.2.238.251.363a1.04 1.04 0 0 1 0 .775c-.05.124-.134.245-.25.362l-2.8 2.8c-.217.216-.471.32-.762.313a1.096 1.096 0 0 1-.763-.337Zm-11.4 0L2.746 12.75a1.103 1.103 0 0 1-.25-.362 1.04 1.04 0 0 1 0-.775c.05-.125.133-.246.25-.363l2.8-2.8c.216-.216.47-.32.762-.313.292.01.546.121.763.337.217.217.325.476.325.776 0 .3-.108.558-.325.775l-.925.925h2.875c.283 0 .529.104.737.312.208.209.313.455.313.738s-.105.53-.313.738a1.008 1.008 0 0 1-.737.312H6.147l.95.95c.217.217.32.471.312.762a1.09 1.09 0 0 1-.337.764 1.06 1.06 0 0 1-.775.324c-.3 0-.559-.108-.776-.324Z\"/><path stroke=\"%23fff\" stroke-opacity=\".7\" d=\"M9.22 7.912c.436.013.821-.15 1.13-.458l.097-.097v1.668c0 .422.16.793.458 1.09.298.299.67.46 1.092.46.422 0 .793-.161 1.091-.46.298-.297.459-.668.459-1.09V7.357l.071.072c.309.308.693.471 1.13.471.435 0 .82-.163 1.128-.471.301-.303.47-.676.483-1.102v-.001a1.501 1.501 0 0 0-.46-1.13l-2.799-2.8a1.6 1.6 0 0 0-.528-.36l-.003-.001a1.54 1.54 0 0 0-1.145 0l-.002.001a1.6 1.6 0 0 0-.529.36L8.118 5.172l-.001.001a1.56 1.56 0 0 0-.47 1.128c0 .435.163.82.47 1.128.303.303.676.472 1.103.484Zm0 0 .014-.5-.015.5Zm-1.311 6.865a1.497 1.497 0 0 0-.458-1.13l-.097-.097h1.668c.421 0 .793-.16 1.09-.458.298-.299.46-.67.46-1.092a1.51 1.51 0 0 0-.46-1.091 1.507 1.507 0 0 0-1.09-.459H7.354l.071-.072c.309-.308.472-.692.472-1.128 0-.436-.163-.82-.472-1.13a1.596 1.596 0 0 0-1.101-.482h-.002a1.501 1.501 0 0 0-1.129.459l-2.8 2.8a1.6 1.6 0 0 0-.36.528l-.001.003a1.54 1.54 0 0 0 0 1.144v.003c.081.2.208.375.362.529l2.774 2.775c.309.308.693.47 1.129.47.435 0 .82-.162 1.128-.47.303-.302.472-.676.484-1.102Zm0 0-.5-.014.5.015v-.001Zm3.038-8.627-.5.5.5.207V6.15Zm.475 15.814.002.001a1.54 1.54 0 0 0 1.145 0h.002c.2-.081.375-.208.53-.362l2.775-2.774a1.56 1.56 0 0 0 .47-1.129c0-.435-.162-.82-.47-1.128a1.589 1.589 0 0 0-1.103-.484 1.497 1.497 0 0 0-1.13.458l-.096.097v-1.668c0-.421-.161-.793-.459-1.09a1.509 1.509 0 0 0-1.091-.46c-.422 0-.794.161-1.092.46a1.507 1.507 0 0 0-.458 1.09v1.668l-.072-.072a1.558 1.558 0 0 0-1.129-.471c-.436 0-.82.163-1.128.471l-.001.001c-.301.302-.47.675-.483 1.101v.001c-.012.437.151.821.46 1.13l2.8 2.8c.153.153.328.28.528.36Zm5.146-6.085c.303.302.676.47 1.102.484a1.5 1.5 0 0 0 1.13-.46l2.8-2.8a1.6 1.6 0 0 0 .36-.528l.002-.003a1.54 1.54 0 0 0 0-1.144l-.001-.003a1.6 1.6 0 0 0-.361-.529l-2.774-2.775h-.001a1.56 1.56 0 0 0-1.128-.47c-.436 0-.82.162-1.128.47h-.001a1.59 1.59 0 0 0-.484 1.102c-.012.437.15.822.459 1.13l.097.097h-1.668c-.422 0-.793.16-1.09.459-.299.298-.46.67-.46 1.091 0 .422.161.793.46 1.092.297.297.668.458 1.09.458h1.668l-.072.072a1.558 1.558 0 0 0-.472 1.129c0 .435.164.82.472 1.128ZM6.147 13.05l.5.5.207-.5h-.707Z\"/></svg>') 12 12,auto}.unreal-container.dolly{cursor:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" fill=\"none\"><mask id=\"a\" width=\"24\" height=\"24\" x=\"0\" y=\"0\" maskUnits=\"userSpaceOnUse\" style=\"mask-type:alpha\"><path fill=\"%23127392\" d=\"M0 0h24v24H0z\"/></mask><g mask=\"url(%23a)\"><path fill=\"%23000\" d=\"M12.002 20.575a1.026 1.026 0 0 1-.75-.325l-2.55-2.55a.933.933 0 0 1-.287-.713c.008-.275.113-.513.313-.713.2-.2.441-.3.725-.3.283 0 .524.1.724.3l.776.776V6.95l-.8.8c-.2.2-.438.296-.712.288a1.018 1.018 0 0 1-.713-.312c-.2-.2-.3-.442-.3-.726s.1-.525.3-.725l2.524-2.525a1.026 1.026 0 0 1 1.5 0l2.55 2.55c.2.2.296.438.289.713a1.023 1.023 0 0 1-.314.713c-.2.2-.441.3-.724.3a.988.988 0 0 1-.725-.3l-.775-.776v10.1l.8-.8c.2-.2.437-.296.712-.288.275.008.512.112.712.312.2.2.3.442.3.726s-.1.525-.3.725l-2.524 2.525a1.026 1.026 0 0 1-.75.325Z\"/><path stroke=\"%23fff\" stroke-opacity=\".7\" d=\"M13.553 8.153c.28.245.62.373 1 .373.415 0 .784-.153 1.078-.447.287-.287.446-.644.46-1.05v-.002a1.432 1.432 0 0 0-.435-1.08l-2.55-2.55-.354.353.354-.354a1.526 1.526 0 0 0-2.207 0L8.374 5.921A1.487 1.487 0 0 0 7.928 7c0 .416.152.785.446 1.08.288.287.645.446 1.052.457l4.127-.384Zm0 0v7.693m0-7.693a1.667 1.667 0 0 1-.079-.074l-3.021.075v7.693a1.482 1.482 0 0 0-1-.373c-.416 0-.785.153-1.079.447a1.522 1.522 0 0 0-.46 1.05v.002c-.011.417.14.786.435 1.08l2.55 2.55a1.527 1.527 0 0 0 2.207 0l2.525-2.524c.294-.294.446-.663.446-1.079 0-.416-.152-.785-.446-1.08a1.518 1.518 0 0 0-1.052-.457m0 0-.014.5.014-.5Zm0 0a1.424 1.424 0 0 0-1.027.383m0 0a1.583 1.583 0 0 0-.053.05l.354.354m-.3-.404v.104l.3.3m0 0-.3-.3v.6l.3-.3Z\"/></g></svg>%0A') 12 12,auto}.unreal-container.disabled:before{content:\"\";position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}#playerUI{position:absolute;z-index:10;width:100%}#statsContainer{display:none;text-align:left;background-color:#000c}#stats{padding:6px;font-weight:700;font-size:14px;color:#0f0}canvas{position:absolute;image-rendering:crisp-edges}#overlay{position:absolute;top:0;right:2%;z-index:100;padding:4px;border-top-width:0;-webkit-border-bottom-right-radius:5px;-moz-border-radius-bottomright:5px;border-bottom-right-radius:5px;-webkit-border-bottom-left-radius:5px;-moz-border-radius-bottomleft:5px;border-bottom-left-radius:5px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-touch-callout:none}.overlay{font-weight:lighter;font-family:var(--src-font-family-body)}#overlayButton:hover{cursor:pointer}#overlayButton{font-size:40px;text-align:right}#overlaySettings{display:none;width:300px;padding:4px}#videoMessageOverlay{position:absolute;z-index:20;width:100%;margin:auto;font-size:1.8em;font-family:var(--src-font-family-body)}#playButton{display:inline-block;height:auto}img#playButton{width:10%;max-width:241px}#UIInteraction{position:fixed}#UIInteractionButtonBoundary{padding:2px}#UIInteractionButton{cursor:pointer}#hiddenInput{position:absolute;left:-10%;width:0;opacity:0}#editTextButton{position:absolute;width:40px;height:40px}\n"], dependencies: [{ kind: "component", type: AfkTimeoutModalComponent, selector: "app-afk-timeout-modal" }, { kind: "component", type: UnrealStatusComponent, selector: "app-unreal-status" }, { kind: "component", type: FreezeFrameComponent, selector: "app-freeze-frame" }, { kind: "component", type: VideoStatsComponent, selector: "app-video-stats" }, { kind: "component", type: ResumeStreamButtonComponent, selector: "app-resume-stream-button" }, { kind: "component", type: ImageLoadingSrcComponent, selector: "app-image-loading-src" }, { kind: "component", type: IntroSrcComponent, selector: "app-intro-src" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
6535
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: UnrealSceneComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
6536
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: UnrealSceneComponent, isStandalone: true, selector: "app-unreal-scene", inputs: { useContainerAsSizeProvider: { classPropertyName: "useContainerAsSizeProvider", publicName: "useContainerAsSizeProvider", isSignal: true, isRequired: false, transformFunction: null }, isStudio: { classPropertyName: "isStudio", publicName: "isStudio", isSignal: true, isRequired: false, transformFunction: null }, resolutionSize: { classPropertyName: "resolutionSize", publicName: "resolutionSize", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { changeMouseOverScene: "changeMouseOverScene" }, host: { listeners: { "mouseover": "onMouseOver()", "mouseout": "onMouseOut()", "contextmenu": "onContextMenu($event)" } }, viewQueries: [{ propertyName: "videoElement", first: true, predicate: ["videoContainer"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"frame unreal-container\">\n <div #videoContainer id=\"player\"></div>\n\n <app-intro-src />\n <app-image-loading-src />\n <app-resume-stream-button />\n <app-unreal-status />\n <app-freeze-frame />\n <app-afk-timeout-modal />\n\n <ng-content select=\"app-pdf\"></ng-content>\n <ng-content select=\"app-show-case\"></ng-content>\n\n @if (isDevMode) {\n <app-video-stats />\n }\n</div>\n", styles: [".unreal-container{position:absolute;top:0;left:0;width:100%;height:100%;background-color:#fff}.unreal-container #player{position:absolute;width:100%;height:100%;display:flex;justify-content:center;align-items:center;overflow:hidden}.unreal-container.select{cursor:url('data:image/svg+xml,<svg width=\"24\" height=\"24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M15.15 21.375a1.423 1.423 0 0 1-1.15.063 1.487 1.487 0 0 1-.85-.788l-3-6.45-2.325 3.25c-.283.4-.658.525-1.125.375-.467-.15-.7-.466-.7-.95V4.05c0-.417.188-.717.563-.9.375-.183.729-.142 1.062.125l10.1 7.95c.383.284.496.65.337 1.1-.158.45-.479.675-.962.675h-4.2l2.975 6.375c.183.384.204.767.063 1.15a1.487 1.487 0 0 1-.788.85Z\" fill=\"%23000\"/><path d=\"m12.697 20.861.002.005c.237.495.617.852 1.128 1.04.515.191 1.039.16 1.539-.08a1.987 1.987 0 0 0 1.04-1.128 1.922 1.922 0 0 0-.079-1.536v-.003L13.684 13.5H17.1c.32 0 .628-.075.89-.26.263-.184.438-.447.544-.749.106-.3.135-.617.04-.925a1.458 1.458 0 0 0-.545-.738L7.936 2.883a1.51 1.51 0 0 0-.768-.336 1.471 1.471 0 0 0-.825.154 1.495 1.495 0 0 0-.626.547 1.492 1.492 0 0 0-.217.802v12.825c0 .324.08.634.272.897.193.261.467.43.775.53.31.099.632.12.942.016.31-.103.555-.313.743-.578h.001l1.825-2.552 2.639 5.673Z\" stroke=\"%23fff\" stroke-opacity=\".7\"/></svg>') 6 2,auto}.unreal-container.orbit{cursor:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" fill=\"none\"><path fill=\"%23000\" d=\"M11.998 21.6c-1.966 0-3.737-.53-5.312-1.588-1.575-1.058-2.738-2.437-3.488-4.137a.852.852 0 0 1 .013-.713.867.867 0 0 1 .512-.487.86.86 0 0 1 .688.037c.225.109.387.28.487.513.617 1.35 1.559 2.45 2.825 3.3 1.267.85 2.692 1.275 4.275 1.275 1.384 0 2.646-.33 3.788-.988a7.985 7.985 0 0 0 2.762-2.612h-.85a.873.873 0 0 1-.64-.257.863.863 0 0 1-.26-.638.88.88 0 0 1 .26-.643.864.864 0 0 1 .64-.262h3c.255 0 .47.086.642.259a.87.87 0 0 1 .258.64v3a.874.874 0 0 1-.257.642.863.863 0 0 1-.637.259.88.88 0 0 1-.643-.259.865.865 0 0 1-.263-.641v-.725a9.95 9.95 0 0 1-3.35 2.925c-1.35.733-2.833 1.1-4.45 1.1Zm0-17.4c-1.383 0-2.645.33-3.787.987A7.985 7.985 0 0 0 5.448 7.8h.85c.255 0 .47.086.642.257a.863.863 0 0 1 .258.638.88.88 0 0 1-.258.642.864.864 0 0 1-.642.263h-3a.87.87 0 0 1-.64-.259.87.87 0 0 1-.26-.641v-3c0-.255.086-.469.258-.641a.863.863 0 0 1 .637-.26.88.88 0 0 1 .643.26.864.864 0 0 1 .262.64v.726A9.95 9.95 0 0 1 7.548 3.5c1.35-.733 2.834-1.1 4.45-1.1 2 0 3.788.542 5.363 1.625 1.575 1.083 2.736 2.49 3.482 4.22a.724.724 0 0 1-.045.646.926.926 0 0 1-.525.437.87.87 0 0 1-.687-.04.977.977 0 0 1-.488-.513c-.616-1.35-1.558-2.45-2.825-3.3-1.266-.85-2.691-1.275-4.275-1.275Zm0 10.8a2.893 2.893 0 0 1-2.125-.875A2.893 2.893 0 0 1 8.998 12c0-.833.292-1.542.875-2.125A2.893 2.893 0 0 1 11.998 9c.834 0 1.542.292 2.125.875.584.583.875 1.292.875 2.125s-.291 1.542-.875 2.125a2.893 2.893 0 0 1-2.125.875Z\"/><path stroke=\"%23fff\" stroke-opacity=\".7\" d=\"m2.739 16.072.002.005c.789 1.788 2.014 3.24 3.666 4.35 1.66 1.116 3.53 1.673 5.591 1.673 1.696 0 3.262-.386 4.69-1.16a10.54 10.54 0 0 0 2.714-2.097c.069.167.171.32.308.454.272.268.613.403.994.403.38 0 .722-.135.992-.406s.402-.614.402-.994v-3c0-.381-.134-.724-.405-.995a1.37 1.37 0 0 0-.995-.405h-3c-.383 0-.727.137-.997.411a1.38 1.38 0 0 0-.403.994c0 .38.135.723.407.992.238.237.532.369.858.397a7.552 7.552 0 0 1-2.027 1.685c-1.062.613-2.237.92-3.538.92-1.488 0-2.815-.397-3.996-1.19-1.19-.798-2.07-1.826-2.647-3.087-.147-.34-.393-.6-.727-.76a1.36 1.36 0 0 0-1.073-.058c-.37.133-.64.397-.8.754a1.351 1.351 0 0 0-.016 1.114Zm4.553-8.37a1.36 1.36 0 0 0-.858-.396A7.552 7.552 0 0 1 8.46 5.62c1.062-.613 2.237-.921 3.537-.921 1.489 0 2.816.398 3.997 1.19 1.19.8 2.07 1.827 2.646 3.088.148.34.394.6.728.76a1.37 1.37 0 0 0 1.068.063c.347-.12.62-.346.799-.668.187-.338.215-.71.072-1.072l-.003-.007-.003-.007c-.784-1.818-2.006-3.298-3.658-4.434C15.982 2.47 14.094 1.9 11.998 1.9c-1.695 0-3.261.385-4.688 1.16a10.536 10.536 0 0 0-2.715 2.096 1.364 1.364 0 0 0-.308-.453 1.38 1.38 0 0 0-.994-.403c-.38 0-.722.135-.992.406s-.403.614-.403.994v3c0 .38.135.724.406.995.27.27.614.405.994.405h3c.384 0 .727-.137.998-.412a1.38 1.38 0 0 0 .402-.993c0-.38-.135-.723-.406-.992ZM9.52 14.48a3.393 3.393 0 0 0 2.478 1.02c.965 0 1.8-.342 2.479-1.02a3.392 3.392 0 0 0 1.021-2.48c0-.963-.343-1.8-1.021-2.478A3.392 3.392 0 0 0 11.998 8.5c-.964 0-1.8.343-2.478 1.021A3.392 3.392 0 0 0 8.498 12c0 .964.344 1.8 1.022 2.479Z\"/></svg>') 12 12,auto}.unreal-container.pan{cursor:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" fill=\"none\"><path fill=\"%23000\" d=\"M11.997 10.075a1.01 1.01 0 0 1-.738-.313 1.008 1.008 0 0 1-.312-.737V6.15l-.95.95a.998.998 0 0 1-.763.312 1.09 1.09 0 0 1-.763-.337 1.06 1.06 0 0 1-.324-.775c0-.3.108-.558.324-.775l2.776-2.775c.117-.116.237-.2.362-.25a1.04 1.04 0 0 1 .775 0c.125.05.246.134.363.25l2.8 2.8c.216.217.32.47.313.762a1.096 1.096 0 0 1-.338.763 1.058 1.058 0 0 1-.775.325c-.3 0-.558-.108-.775-.325l-.925-.925v2.875c0 .283-.104.53-.312.737a1.01 1.01 0 0 1-.738.313Zm0 11.5a1.04 1.04 0 0 1-.388-.075 1.103 1.103 0 0 1-.362-.25l-2.8-2.8a1.002 1.002 0 0 1-.313-.762c.009-.292.121-.546.337-.763.217-.217.475-.325.775-.325.3 0 .559.108.775.325l.926.925v-2.875c0-.283.104-.529.312-.737a1.01 1.01 0 0 1 .738-.313c.283 0 .529.104.738.313.208.208.312.454.312.737v2.875l.95-.95a.998.998 0 0 1 .762-.312c.292.008.546.12.763.337.216.217.324.475.324.775 0 .3-.108.559-.324.775l-2.775 2.775c-.117.116-.238.2-.363.25a1.04 1.04 0 0 1-.387.075Zm4.925-6.05a1.058 1.058 0 0 1-.326-.774c0-.3.109-.559.326-.776l.925-.925H14.97c-.283 0-.528-.104-.736-.312a1.01 1.01 0 0 1-.313-.738c0-.283.104-.53.313-.738.208-.208.453-.312.737-.312h2.875l-.95-.95a.998.998 0 0 1-.313-.762 1.09 1.09 0 0 1 .338-.764 1.06 1.06 0 0 1 .775-.324c.3 0 .558.108.775.324l2.774 2.776c.117.117.2.238.251.363a1.04 1.04 0 0 1 0 .775c-.05.124-.134.245-.25.362l-2.8 2.8c-.217.216-.471.32-.762.313a1.096 1.096 0 0 1-.763-.337Zm-11.4 0L2.746 12.75a1.103 1.103 0 0 1-.25-.362 1.04 1.04 0 0 1 0-.775c.05-.125.133-.246.25-.363l2.8-2.8c.216-.216.47-.32.762-.313.292.01.546.121.763.337.217.217.325.476.325.776 0 .3-.108.558-.325.775l-.925.925h2.875c.283 0 .529.104.737.312.208.209.313.455.313.738s-.105.53-.313.738a1.008 1.008 0 0 1-.737.312H6.147l.95.95c.217.217.32.471.312.762a1.09 1.09 0 0 1-.337.764 1.06 1.06 0 0 1-.775.324c-.3 0-.559-.108-.776-.324Z\"/><path stroke=\"%23fff\" stroke-opacity=\".7\" d=\"M9.22 7.912c.436.013.821-.15 1.13-.458l.097-.097v1.668c0 .422.16.793.458 1.09.298.299.67.46 1.092.46.422 0 .793-.161 1.091-.46.298-.297.459-.668.459-1.09V7.357l.071.072c.309.308.693.471 1.13.471.435 0 .82-.163 1.128-.471.301-.303.47-.676.483-1.102v-.001a1.501 1.501 0 0 0-.46-1.13l-2.799-2.8a1.6 1.6 0 0 0-.528-.36l-.003-.001a1.54 1.54 0 0 0-1.145 0l-.002.001a1.6 1.6 0 0 0-.529.36L8.118 5.172l-.001.001a1.56 1.56 0 0 0-.47 1.128c0 .435.163.82.47 1.128.303.303.676.472 1.103.484Zm0 0 .014-.5-.015.5Zm-1.311 6.865a1.497 1.497 0 0 0-.458-1.13l-.097-.097h1.668c.421 0 .793-.16 1.09-.458.298-.299.46-.67.46-1.092a1.51 1.51 0 0 0-.46-1.091 1.507 1.507 0 0 0-1.09-.459H7.354l.071-.072c.309-.308.472-.692.472-1.128 0-.436-.163-.82-.472-1.13a1.596 1.596 0 0 0-1.101-.482h-.002a1.501 1.501 0 0 0-1.129.459l-2.8 2.8a1.6 1.6 0 0 0-.36.528l-.001.003a1.54 1.54 0 0 0 0 1.144v.003c.081.2.208.375.362.529l2.774 2.775c.309.308.693.47 1.129.47.435 0 .82-.162 1.128-.47.303-.302.472-.676.484-1.102Zm0 0-.5-.014.5.015v-.001Zm3.038-8.627-.5.5.5.207V6.15Zm.475 15.814.002.001a1.54 1.54 0 0 0 1.145 0h.002c.2-.081.375-.208.53-.362l2.775-2.774a1.56 1.56 0 0 0 .47-1.129c0-.435-.162-.82-.47-1.128a1.589 1.589 0 0 0-1.103-.484 1.497 1.497 0 0 0-1.13.458l-.096.097v-1.668c0-.421-.161-.793-.459-1.09a1.509 1.509 0 0 0-1.091-.46c-.422 0-.794.161-1.092.46a1.507 1.507 0 0 0-.458 1.09v1.668l-.072-.072a1.558 1.558 0 0 0-1.129-.471c-.436 0-.82.163-1.128.471l-.001.001c-.301.302-.47.675-.483 1.101v.001c-.012.437.151.821.46 1.13l2.8 2.8c.153.153.328.28.528.36Zm5.146-6.085c.303.302.676.47 1.102.484a1.5 1.5 0 0 0 1.13-.46l2.8-2.8a1.6 1.6 0 0 0 .36-.528l.002-.003a1.54 1.54 0 0 0 0-1.144l-.001-.003a1.6 1.6 0 0 0-.361-.529l-2.774-2.775h-.001a1.56 1.56 0 0 0-1.128-.47c-.436 0-.82.162-1.128.47h-.001a1.59 1.59 0 0 0-.484 1.102c-.012.437.15.822.459 1.13l.097.097h-1.668c-.422 0-.793.16-1.09.459-.299.298-.46.67-.46 1.091 0 .422.161.793.46 1.092.297.297.668.458 1.09.458h1.668l-.072.072a1.558 1.558 0 0 0-.472 1.129c0 .435.164.82.472 1.128ZM6.147 13.05l.5.5.207-.5h-.707Z\"/></svg>') 12 12,auto}.unreal-container.dolly{cursor:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" fill=\"none\"><mask id=\"a\" width=\"24\" height=\"24\" x=\"0\" y=\"0\" maskUnits=\"userSpaceOnUse\" style=\"mask-type:alpha\"><path fill=\"%23127392\" d=\"M0 0h24v24H0z\"/></mask><g mask=\"url(%23a)\"><path fill=\"%23000\" d=\"M12.002 20.575a1.026 1.026 0 0 1-.75-.325l-2.55-2.55a.933.933 0 0 1-.287-.713c.008-.275.113-.513.313-.713.2-.2.441-.3.725-.3.283 0 .524.1.724.3l.776.776V6.95l-.8.8c-.2.2-.438.296-.712.288a1.018 1.018 0 0 1-.713-.312c-.2-.2-.3-.442-.3-.726s.1-.525.3-.725l2.524-2.525a1.026 1.026 0 0 1 1.5 0l2.55 2.55c.2.2.296.438.289.713a1.023 1.023 0 0 1-.314.713c-.2.2-.441.3-.724.3a.988.988 0 0 1-.725-.3l-.775-.776v10.1l.8-.8c.2-.2.437-.296.712-.288.275.008.512.112.712.312.2.2.3.442.3.726s-.1.525-.3.725l-2.524 2.525a1.026 1.026 0 0 1-.75.325Z\"/><path stroke=\"%23fff\" stroke-opacity=\".7\" d=\"M13.553 8.153c.28.245.62.373 1 .373.415 0 .784-.153 1.078-.447.287-.287.446-.644.46-1.05v-.002a1.432 1.432 0 0 0-.435-1.08l-2.55-2.55-.354.353.354-.354a1.526 1.526 0 0 0-2.207 0L8.374 5.921A1.487 1.487 0 0 0 7.928 7c0 .416.152.785.446 1.08.288.287.645.446 1.052.457l4.127-.384Zm0 0v7.693m0-7.693a1.667 1.667 0 0 1-.079-.074l-3.021.075v7.693a1.482 1.482 0 0 0-1-.373c-.416 0-.785.153-1.079.447a1.522 1.522 0 0 0-.46 1.05v.002c-.011.417.14.786.435 1.08l2.55 2.55a1.527 1.527 0 0 0 2.207 0l2.525-2.524c.294-.294.446-.663.446-1.079 0-.416-.152-.785-.446-1.08a1.518 1.518 0 0 0-1.052-.457m0 0-.014.5.014-.5Zm0 0a1.424 1.424 0 0 0-1.027.383m0 0a1.583 1.583 0 0 0-.053.05l.354.354m-.3-.404v.104l.3.3m0 0-.3-.3v.6l.3-.3Z\"/></g></svg>%0A') 12 12,auto}.unreal-container.disabled:before{content:\"\";position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}#playerUI{position:absolute;z-index:10;width:100%}#statsContainer{display:none;text-align:left;background-color:#000c}#stats{padding:6px;font-weight:700;font-size:14px;color:#0f0}canvas{position:absolute;image-rendering:crisp-edges}#overlay{position:absolute;top:0;right:2%;z-index:100;padding:4px;border-top-width:0;-webkit-border-bottom-right-radius:5px;-moz-border-radius-bottomright:5px;border-bottom-right-radius:5px;-webkit-border-bottom-left-radius:5px;-moz-border-radius-bottomleft:5px;border-bottom-left-radius:5px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-touch-callout:none}.overlay{font-weight:lighter;font-family:var(--src-font-family-body)}#overlayButton:hover{cursor:pointer}#overlayButton{font-size:40px;text-align:right}#overlaySettings{display:none;width:300px;padding:4px}#videoMessageOverlay{position:absolute;z-index:20;width:100%;margin:auto;font-size:1.8em;font-family:var(--src-font-family-body)}#playButton{display:inline-block;height:auto}img#playButton{width:10%;max-width:241px}#UIInteraction{position:fixed}#UIInteractionButtonBoundary{padding:2px}#UIInteractionButton{cursor:pointer}#hiddenInput{position:absolute;left:-10%;width:0;opacity:0}#editTextButton{position:absolute;width:40px;height:40px}\n"], dependencies: [{ kind: "component", type: AfkTimeoutModalComponent, selector: "app-afk-timeout-modal" }, { kind: "component", type: UnrealStatusComponent, selector: "app-unreal-status" }, { kind: "component", type: FreezeFrameComponent, selector: "app-freeze-frame" }, { kind: "component", type: VideoStatsComponent, selector: "app-video-stats" }, { kind: "component", type: ResumeStreamButtonComponent, selector: "app-resume-stream-button" }, { kind: "component", type: ImageLoadingSrcComponent, selector: "app-image-loading-src" }, { kind: "component", type: IntroSrcComponent, selector: "app-intro-src" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
6664
6537
  }
6665
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: UnrealSceneComponent, decorators: [{
6538
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: UnrealSceneComponent, decorators: [{
6666
6539
  type: Component,
6667
6540
  args: [{ selector: 'app-unreal-scene', changeDetection: ChangeDetectionStrategy.OnPush, imports: [
6668
6541
  AfkTimeoutModalComponent,
@@ -6672,13 +6545,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImpo
6672
6545
  ResumeStreamButtonComponent,
6673
6546
  ImageLoadingSrcComponent,
6674
6547
  IntroSrcComponent,
6675
- ], template: "<div class=\"frame unreal-container\">\n <div #videoContainer [class.lightModeCursor]=\"lightMode()\" id=\"player\"></div>\n\n <app-intro-src />\n <app-image-loading-src />\n <app-resume-stream-button />\n <app-unreal-status />\n <app-freeze-frame />\n <app-afk-timeout-modal />\n\n <ng-content select=\"app-pdf\"></ng-content>\n <ng-content select=\"app-show-case\"></ng-content>\n <ng-content select=\"app-low-bandwidth-detector\"></ng-content>\n\n @if (isDevMode) {\n <app-video-stats />\n }\n</div>\n", styles: [".unreal-container{position:absolute;top:0;left:0;width:100%;height:100%;background-color:#fff}.unreal-container #player{position:absolute;width:100%;height:100%;display:flex;justify-content:center;align-items:center;overflow:hidden}.unreal-container.select{cursor:url('data:image/svg+xml,<svg width=\"24\" height=\"24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M15.15 21.375a1.423 1.423 0 0 1-1.15.063 1.487 1.487 0 0 1-.85-.788l-3-6.45-2.325 3.25c-.283.4-.658.525-1.125.375-.467-.15-.7-.466-.7-.95V4.05c0-.417.188-.717.563-.9.375-.183.729-.142 1.062.125l10.1 7.95c.383.284.496.65.337 1.1-.158.45-.479.675-.962.675h-4.2l2.975 6.375c.183.384.204.767.063 1.15a1.487 1.487 0 0 1-.788.85Z\" fill=\"%23000\"/><path d=\"m12.697 20.861.002.005c.237.495.617.852 1.128 1.04.515.191 1.039.16 1.539-.08a1.987 1.987 0 0 0 1.04-1.128 1.922 1.922 0 0 0-.079-1.536v-.003L13.684 13.5H17.1c.32 0 .628-.075.89-.26.263-.184.438-.447.544-.749.106-.3.135-.617.04-.925a1.458 1.458 0 0 0-.545-.738L7.936 2.883a1.51 1.51 0 0 0-.768-.336 1.471 1.471 0 0 0-.825.154 1.495 1.495 0 0 0-.626.547 1.492 1.492 0 0 0-.217.802v12.825c0 .324.08.634.272.897.193.261.467.43.775.53.31.099.632.12.942.016.31-.103.555-.313.743-.578h.001l1.825-2.552 2.639 5.673Z\" stroke=\"%23fff\" stroke-opacity=\".7\"/></svg>') 6 2,auto}.unreal-container.orbit{cursor:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" fill=\"none\"><path fill=\"%23000\" d=\"M11.998 21.6c-1.966 0-3.737-.53-5.312-1.588-1.575-1.058-2.738-2.437-3.488-4.137a.852.852 0 0 1 .013-.713.867.867 0 0 1 .512-.487.86.86 0 0 1 .688.037c.225.109.387.28.487.513.617 1.35 1.559 2.45 2.825 3.3 1.267.85 2.692 1.275 4.275 1.275 1.384 0 2.646-.33 3.788-.988a7.985 7.985 0 0 0 2.762-2.612h-.85a.873.873 0 0 1-.64-.257.863.863 0 0 1-.26-.638.88.88 0 0 1 .26-.643.864.864 0 0 1 .64-.262h3c.255 0 .47.086.642.259a.87.87 0 0 1 .258.64v3a.874.874 0 0 1-.257.642.863.863 0 0 1-.637.259.88.88 0 0 1-.643-.259.865.865 0 0 1-.263-.641v-.725a9.95 9.95 0 0 1-3.35 2.925c-1.35.733-2.833 1.1-4.45 1.1Zm0-17.4c-1.383 0-2.645.33-3.787.987A7.985 7.985 0 0 0 5.448 7.8h.85c.255 0 .47.086.642.257a.863.863 0 0 1 .258.638.88.88 0 0 1-.258.642.864.864 0 0 1-.642.263h-3a.87.87 0 0 1-.64-.259.87.87 0 0 1-.26-.641v-3c0-.255.086-.469.258-.641a.863.863 0 0 1 .637-.26.88.88 0 0 1 .643.26.864.864 0 0 1 .262.64v.726A9.95 9.95 0 0 1 7.548 3.5c1.35-.733 2.834-1.1 4.45-1.1 2 0 3.788.542 5.363 1.625 1.575 1.083 2.736 2.49 3.482 4.22a.724.724 0 0 1-.045.646.926.926 0 0 1-.525.437.87.87 0 0 1-.687-.04.977.977 0 0 1-.488-.513c-.616-1.35-1.558-2.45-2.825-3.3-1.266-.85-2.691-1.275-4.275-1.275Zm0 10.8a2.893 2.893 0 0 1-2.125-.875A2.893 2.893 0 0 1 8.998 12c0-.833.292-1.542.875-2.125A2.893 2.893 0 0 1 11.998 9c.834 0 1.542.292 2.125.875.584.583.875 1.292.875 2.125s-.291 1.542-.875 2.125a2.893 2.893 0 0 1-2.125.875Z\"/><path stroke=\"%23fff\" stroke-opacity=\".7\" d=\"m2.739 16.072.002.005c.789 1.788 2.014 3.24 3.666 4.35 1.66 1.116 3.53 1.673 5.591 1.673 1.696 0 3.262-.386 4.69-1.16a10.54 10.54 0 0 0 2.714-2.097c.069.167.171.32.308.454.272.268.613.403.994.403.38 0 .722-.135.992-.406s.402-.614.402-.994v-3c0-.381-.134-.724-.405-.995a1.37 1.37 0 0 0-.995-.405h-3c-.383 0-.727.137-.997.411a1.38 1.38 0 0 0-.403.994c0 .38.135.723.407.992.238.237.532.369.858.397a7.552 7.552 0 0 1-2.027 1.685c-1.062.613-2.237.92-3.538.92-1.488 0-2.815-.397-3.996-1.19-1.19-.798-2.07-1.826-2.647-3.087-.147-.34-.393-.6-.727-.76a1.36 1.36 0 0 0-1.073-.058c-.37.133-.64.397-.8.754a1.351 1.351 0 0 0-.016 1.114Zm4.553-8.37a1.36 1.36 0 0 0-.858-.396A7.552 7.552 0 0 1 8.46 5.62c1.062-.613 2.237-.921 3.537-.921 1.489 0 2.816.398 3.997 1.19 1.19.8 2.07 1.827 2.646 3.088.148.34.394.6.728.76a1.37 1.37 0 0 0 1.068.063c.347-.12.62-.346.799-.668.187-.338.215-.71.072-1.072l-.003-.007-.003-.007c-.784-1.818-2.006-3.298-3.658-4.434C15.982 2.47 14.094 1.9 11.998 1.9c-1.695 0-3.261.385-4.688 1.16a10.536 10.536 0 0 0-2.715 2.096 1.364 1.364 0 0 0-.308-.453 1.38 1.38 0 0 0-.994-.403c-.38 0-.722.135-.992.406s-.403.614-.403.994v3c0 .38.135.724.406.995.27.27.614.405.994.405h3c.384 0 .727-.137.998-.412a1.38 1.38 0 0 0 .402-.993c0-.38-.135-.723-.406-.992ZM9.52 14.48a3.393 3.393 0 0 0 2.478 1.02c.965 0 1.8-.342 2.479-1.02a3.392 3.392 0 0 0 1.021-2.48c0-.963-.343-1.8-1.021-2.478A3.392 3.392 0 0 0 11.998 8.5c-.964 0-1.8.343-2.478 1.021A3.392 3.392 0 0 0 8.498 12c0 .964.344 1.8 1.022 2.479Z\"/></svg>') 12 12,auto}.unreal-container.pan{cursor:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" fill=\"none\"><path fill=\"%23000\" d=\"M11.997 10.075a1.01 1.01 0 0 1-.738-.313 1.008 1.008 0 0 1-.312-.737V6.15l-.95.95a.998.998 0 0 1-.763.312 1.09 1.09 0 0 1-.763-.337 1.06 1.06 0 0 1-.324-.775c0-.3.108-.558.324-.775l2.776-2.775c.117-.116.237-.2.362-.25a1.04 1.04 0 0 1 .775 0c.125.05.246.134.363.25l2.8 2.8c.216.217.32.47.313.762a1.096 1.096 0 0 1-.338.763 1.058 1.058 0 0 1-.775.325c-.3 0-.558-.108-.775-.325l-.925-.925v2.875c0 .283-.104.53-.312.737a1.01 1.01 0 0 1-.738.313Zm0 11.5a1.04 1.04 0 0 1-.388-.075 1.103 1.103 0 0 1-.362-.25l-2.8-2.8a1.002 1.002 0 0 1-.313-.762c.009-.292.121-.546.337-.763.217-.217.475-.325.775-.325.3 0 .559.108.775.325l.926.925v-2.875c0-.283.104-.529.312-.737a1.01 1.01 0 0 1 .738-.313c.283 0 .529.104.738.313.208.208.312.454.312.737v2.875l.95-.95a.998.998 0 0 1 .762-.312c.292.008.546.12.763.337.216.217.324.475.324.775 0 .3-.108.559-.324.775l-2.775 2.775c-.117.116-.238.2-.363.25a1.04 1.04 0 0 1-.387.075Zm4.925-6.05a1.058 1.058 0 0 1-.326-.774c0-.3.109-.559.326-.776l.925-.925H14.97c-.283 0-.528-.104-.736-.312a1.01 1.01 0 0 1-.313-.738c0-.283.104-.53.313-.738.208-.208.453-.312.737-.312h2.875l-.95-.95a.998.998 0 0 1-.313-.762 1.09 1.09 0 0 1 .338-.764 1.06 1.06 0 0 1 .775-.324c.3 0 .558.108.775.324l2.774 2.776c.117.117.2.238.251.363a1.04 1.04 0 0 1 0 .775c-.05.124-.134.245-.25.362l-2.8 2.8c-.217.216-.471.32-.762.313a1.096 1.096 0 0 1-.763-.337Zm-11.4 0L2.746 12.75a1.103 1.103 0 0 1-.25-.362 1.04 1.04 0 0 1 0-.775c.05-.125.133-.246.25-.363l2.8-2.8c.216-.216.47-.32.762-.313.292.01.546.121.763.337.217.217.325.476.325.776 0 .3-.108.558-.325.775l-.925.925h2.875c.283 0 .529.104.737.312.208.209.313.455.313.738s-.105.53-.313.738a1.008 1.008 0 0 1-.737.312H6.147l.95.95c.217.217.32.471.312.762a1.09 1.09 0 0 1-.337.764 1.06 1.06 0 0 1-.775.324c-.3 0-.559-.108-.776-.324Z\"/><path stroke=\"%23fff\" stroke-opacity=\".7\" d=\"M9.22 7.912c.436.013.821-.15 1.13-.458l.097-.097v1.668c0 .422.16.793.458 1.09.298.299.67.46 1.092.46.422 0 .793-.161 1.091-.46.298-.297.459-.668.459-1.09V7.357l.071.072c.309.308.693.471 1.13.471.435 0 .82-.163 1.128-.471.301-.303.47-.676.483-1.102v-.001a1.501 1.501 0 0 0-.46-1.13l-2.799-2.8a1.6 1.6 0 0 0-.528-.36l-.003-.001a1.54 1.54 0 0 0-1.145 0l-.002.001a1.6 1.6 0 0 0-.529.36L8.118 5.172l-.001.001a1.56 1.56 0 0 0-.47 1.128c0 .435.163.82.47 1.128.303.303.676.472 1.103.484Zm0 0 .014-.5-.015.5Zm-1.311 6.865a1.497 1.497 0 0 0-.458-1.13l-.097-.097h1.668c.421 0 .793-.16 1.09-.458.298-.299.46-.67.46-1.092a1.51 1.51 0 0 0-.46-1.091 1.507 1.507 0 0 0-1.09-.459H7.354l.071-.072c.309-.308.472-.692.472-1.128 0-.436-.163-.82-.472-1.13a1.596 1.596 0 0 0-1.101-.482h-.002a1.501 1.501 0 0 0-1.129.459l-2.8 2.8a1.6 1.6 0 0 0-.36.528l-.001.003a1.54 1.54 0 0 0 0 1.144v.003c.081.2.208.375.362.529l2.774 2.775c.309.308.693.47 1.129.47.435 0 .82-.162 1.128-.47.303-.302.472-.676.484-1.102Zm0 0-.5-.014.5.015v-.001Zm3.038-8.627-.5.5.5.207V6.15Zm.475 15.814.002.001a1.54 1.54 0 0 0 1.145 0h.002c.2-.081.375-.208.53-.362l2.775-2.774a1.56 1.56 0 0 0 .47-1.129c0-.435-.162-.82-.47-1.128a1.589 1.589 0 0 0-1.103-.484 1.497 1.497 0 0 0-1.13.458l-.096.097v-1.668c0-.421-.161-.793-.459-1.09a1.509 1.509 0 0 0-1.091-.46c-.422 0-.794.161-1.092.46a1.507 1.507 0 0 0-.458 1.09v1.668l-.072-.072a1.558 1.558 0 0 0-1.129-.471c-.436 0-.82.163-1.128.471l-.001.001c-.301.302-.47.675-.483 1.101v.001c-.012.437.151.821.46 1.13l2.8 2.8c.153.153.328.28.528.36Zm5.146-6.085c.303.302.676.47 1.102.484a1.5 1.5 0 0 0 1.13-.46l2.8-2.8a1.6 1.6 0 0 0 .36-.528l.002-.003a1.54 1.54 0 0 0 0-1.144l-.001-.003a1.6 1.6 0 0 0-.361-.529l-2.774-2.775h-.001a1.56 1.56 0 0 0-1.128-.47c-.436 0-.82.162-1.128.47h-.001a1.59 1.59 0 0 0-.484 1.102c-.012.437.15.822.459 1.13l.097.097h-1.668c-.422 0-.793.16-1.09.459-.299.298-.46.67-.46 1.091 0 .422.161.793.46 1.092.297.297.668.458 1.09.458h1.668l-.072.072a1.558 1.558 0 0 0-.472 1.129c0 .435.164.82.472 1.128ZM6.147 13.05l.5.5.207-.5h-.707Z\"/></svg>') 12 12,auto}.unreal-container.dolly{cursor:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" fill=\"none\"><mask id=\"a\" width=\"24\" height=\"24\" x=\"0\" y=\"0\" maskUnits=\"userSpaceOnUse\" style=\"mask-type:alpha\"><path fill=\"%23127392\" d=\"M0 0h24v24H0z\"/></mask><g mask=\"url(%23a)\"><path fill=\"%23000\" d=\"M12.002 20.575a1.026 1.026 0 0 1-.75-.325l-2.55-2.55a.933.933 0 0 1-.287-.713c.008-.275.113-.513.313-.713.2-.2.441-.3.725-.3.283 0 .524.1.724.3l.776.776V6.95l-.8.8c-.2.2-.438.296-.712.288a1.018 1.018 0 0 1-.713-.312c-.2-.2-.3-.442-.3-.726s.1-.525.3-.725l2.524-2.525a1.026 1.026 0 0 1 1.5 0l2.55 2.55c.2.2.296.438.289.713a1.023 1.023 0 0 1-.314.713c-.2.2-.441.3-.724.3a.988.988 0 0 1-.725-.3l-.775-.776v10.1l.8-.8c.2-.2.437-.296.712-.288.275.008.512.112.712.312.2.2.3.442.3.726s-.1.525-.3.725l-2.524 2.525a1.026 1.026 0 0 1-.75.325Z\"/><path stroke=\"%23fff\" stroke-opacity=\".7\" d=\"M13.553 8.153c.28.245.62.373 1 .373.415 0 .784-.153 1.078-.447.287-.287.446-.644.46-1.05v-.002a1.432 1.432 0 0 0-.435-1.08l-2.55-2.55-.354.353.354-.354a1.526 1.526 0 0 0-2.207 0L8.374 5.921A1.487 1.487 0 0 0 7.928 7c0 .416.152.785.446 1.08.288.287.645.446 1.052.457l4.127-.384Zm0 0v7.693m0-7.693a1.667 1.667 0 0 1-.079-.074l-3.021.075v7.693a1.482 1.482 0 0 0-1-.373c-.416 0-.785.153-1.079.447a1.522 1.522 0 0 0-.46 1.05v.002c-.011.417.14.786.435 1.08l2.55 2.55a1.527 1.527 0 0 0 2.207 0l2.525-2.524c.294-.294.446-.663.446-1.079 0-.416-.152-.785-.446-1.08a1.518 1.518 0 0 0-1.052-.457m0 0-.014.5.014-.5Zm0 0a1.424 1.424 0 0 0-1.027.383m0 0a1.583 1.583 0 0 0-.053.05l.354.354m-.3-.404v.104l.3.3m0 0-.3-.3v.6l.3-.3Z\"/></g></svg>%0A') 12 12,auto}.unreal-container.disabled:before{content:\"\";position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}#playerUI{position:absolute;z-index:10;width:100%}#statsContainer{display:none;text-align:left;background-color:#000c}#stats{padding:6px;font-weight:700;font-size:14px;color:#0f0}canvas{position:absolute;image-rendering:crisp-edges}#overlay{position:absolute;top:0;right:2%;z-index:100;padding:4px;border-top-width:0;-webkit-border-bottom-right-radius:5px;-moz-border-radius-bottomright:5px;border-bottom-right-radius:5px;-webkit-border-bottom-left-radius:5px;-moz-border-radius-bottomleft:5px;border-bottom-left-radius:5px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-touch-callout:none}.overlay{font-weight:lighter;font-family:var(--src-font-family-body)}#overlayButton:hover{cursor:pointer}#overlayButton{font-size:40px;text-align:right}#overlaySettings{display:none;width:300px;padding:4px}#videoMessageOverlay{position:absolute;z-index:20;width:100%;margin:auto;font-size:1.8em;font-family:var(--src-font-family-body)}#playButton{display:inline-block;height:auto}img#playButton{width:10%;max-width:241px}#UIInteraction{position:fixed}#UIInteractionButtonBoundary{padding:2px}#UIInteractionButton{cursor:pointer}#hiddenInput{position:absolute;left:-10%;width:0;opacity:0}#editTextButton{position:absolute;width:40px;height:40px}\n"] }]
6676
- }], ctorParameters: () => [], propDecorators: { isStudio: [{ type: i0.Input, args: [{ isSignal: true, alias: "isStudio", required: false }] }], useContainerAsSizeProvider: [{ type: i0.Input, args: [{ isSignal: true, alias: "useContainerAsSizeProvider", required: false }] }], studioResolutionSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "studioResolutionSize", required: false }] }], videoElement: [{ type: i0.ViewChild, args: ['videoContainer', { isSignal: true }] }], changeMouseOverScene: [{ type: i0.Output, args: ["changeMouseOverScene"] }], onMouseOver: [{
6548
+ ], template: "<div class=\"frame unreal-container\">\n <div #videoContainer id=\"player\"></div>\n\n <app-intro-src />\n <app-image-loading-src />\n <app-resume-stream-button />\n <app-unreal-status />\n <app-freeze-frame />\n <app-afk-timeout-modal />\n\n <ng-content select=\"app-pdf\"></ng-content>\n <ng-content select=\"app-show-case\"></ng-content>\n\n @if (isDevMode) {\n <app-video-stats />\n }\n</div>\n", styles: [".unreal-container{position:absolute;top:0;left:0;width:100%;height:100%;background-color:#fff}.unreal-container #player{position:absolute;width:100%;height:100%;display:flex;justify-content:center;align-items:center;overflow:hidden}.unreal-container.select{cursor:url('data:image/svg+xml,<svg width=\"24\" height=\"24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M15.15 21.375a1.423 1.423 0 0 1-1.15.063 1.487 1.487 0 0 1-.85-.788l-3-6.45-2.325 3.25c-.283.4-.658.525-1.125.375-.467-.15-.7-.466-.7-.95V4.05c0-.417.188-.717.563-.9.375-.183.729-.142 1.062.125l10.1 7.95c.383.284.496.65.337 1.1-.158.45-.479.675-.962.675h-4.2l2.975 6.375c.183.384.204.767.063 1.15a1.487 1.487 0 0 1-.788.85Z\" fill=\"%23000\"/><path d=\"m12.697 20.861.002.005c.237.495.617.852 1.128 1.04.515.191 1.039.16 1.539-.08a1.987 1.987 0 0 0 1.04-1.128 1.922 1.922 0 0 0-.079-1.536v-.003L13.684 13.5H17.1c.32 0 .628-.075.89-.26.263-.184.438-.447.544-.749.106-.3.135-.617.04-.925a1.458 1.458 0 0 0-.545-.738L7.936 2.883a1.51 1.51 0 0 0-.768-.336 1.471 1.471 0 0 0-.825.154 1.495 1.495 0 0 0-.626.547 1.492 1.492 0 0 0-.217.802v12.825c0 .324.08.634.272.897.193.261.467.43.775.53.31.099.632.12.942.016.31-.103.555-.313.743-.578h.001l1.825-2.552 2.639 5.673Z\" stroke=\"%23fff\" stroke-opacity=\".7\"/></svg>') 6 2,auto}.unreal-container.orbit{cursor:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" fill=\"none\"><path fill=\"%23000\" d=\"M11.998 21.6c-1.966 0-3.737-.53-5.312-1.588-1.575-1.058-2.738-2.437-3.488-4.137a.852.852 0 0 1 .013-.713.867.867 0 0 1 .512-.487.86.86 0 0 1 .688.037c.225.109.387.28.487.513.617 1.35 1.559 2.45 2.825 3.3 1.267.85 2.692 1.275 4.275 1.275 1.384 0 2.646-.33 3.788-.988a7.985 7.985 0 0 0 2.762-2.612h-.85a.873.873 0 0 1-.64-.257.863.863 0 0 1-.26-.638.88.88 0 0 1 .26-.643.864.864 0 0 1 .64-.262h3c.255 0 .47.086.642.259a.87.87 0 0 1 .258.64v3a.874.874 0 0 1-.257.642.863.863 0 0 1-.637.259.88.88 0 0 1-.643-.259.865.865 0 0 1-.263-.641v-.725a9.95 9.95 0 0 1-3.35 2.925c-1.35.733-2.833 1.1-4.45 1.1Zm0-17.4c-1.383 0-2.645.33-3.787.987A7.985 7.985 0 0 0 5.448 7.8h.85c.255 0 .47.086.642.257a.863.863 0 0 1 .258.638.88.88 0 0 1-.258.642.864.864 0 0 1-.642.263h-3a.87.87 0 0 1-.64-.259.87.87 0 0 1-.26-.641v-3c0-.255.086-.469.258-.641a.863.863 0 0 1 .637-.26.88.88 0 0 1 .643.26.864.864 0 0 1 .262.64v.726A9.95 9.95 0 0 1 7.548 3.5c1.35-.733 2.834-1.1 4.45-1.1 2 0 3.788.542 5.363 1.625 1.575 1.083 2.736 2.49 3.482 4.22a.724.724 0 0 1-.045.646.926.926 0 0 1-.525.437.87.87 0 0 1-.687-.04.977.977 0 0 1-.488-.513c-.616-1.35-1.558-2.45-2.825-3.3-1.266-.85-2.691-1.275-4.275-1.275Zm0 10.8a2.893 2.893 0 0 1-2.125-.875A2.893 2.893 0 0 1 8.998 12c0-.833.292-1.542.875-2.125A2.893 2.893 0 0 1 11.998 9c.834 0 1.542.292 2.125.875.584.583.875 1.292.875 2.125s-.291 1.542-.875 2.125a2.893 2.893 0 0 1-2.125.875Z\"/><path stroke=\"%23fff\" stroke-opacity=\".7\" d=\"m2.739 16.072.002.005c.789 1.788 2.014 3.24 3.666 4.35 1.66 1.116 3.53 1.673 5.591 1.673 1.696 0 3.262-.386 4.69-1.16a10.54 10.54 0 0 0 2.714-2.097c.069.167.171.32.308.454.272.268.613.403.994.403.38 0 .722-.135.992-.406s.402-.614.402-.994v-3c0-.381-.134-.724-.405-.995a1.37 1.37 0 0 0-.995-.405h-3c-.383 0-.727.137-.997.411a1.38 1.38 0 0 0-.403.994c0 .38.135.723.407.992.238.237.532.369.858.397a7.552 7.552 0 0 1-2.027 1.685c-1.062.613-2.237.92-3.538.92-1.488 0-2.815-.397-3.996-1.19-1.19-.798-2.07-1.826-2.647-3.087-.147-.34-.393-.6-.727-.76a1.36 1.36 0 0 0-1.073-.058c-.37.133-.64.397-.8.754a1.351 1.351 0 0 0-.016 1.114Zm4.553-8.37a1.36 1.36 0 0 0-.858-.396A7.552 7.552 0 0 1 8.46 5.62c1.062-.613 2.237-.921 3.537-.921 1.489 0 2.816.398 3.997 1.19 1.19.8 2.07 1.827 2.646 3.088.148.34.394.6.728.76a1.37 1.37 0 0 0 1.068.063c.347-.12.62-.346.799-.668.187-.338.215-.71.072-1.072l-.003-.007-.003-.007c-.784-1.818-2.006-3.298-3.658-4.434C15.982 2.47 14.094 1.9 11.998 1.9c-1.695 0-3.261.385-4.688 1.16a10.536 10.536 0 0 0-2.715 2.096 1.364 1.364 0 0 0-.308-.453 1.38 1.38 0 0 0-.994-.403c-.38 0-.722.135-.992.406s-.403.614-.403.994v3c0 .38.135.724.406.995.27.27.614.405.994.405h3c.384 0 .727-.137.998-.412a1.38 1.38 0 0 0 .402-.993c0-.38-.135-.723-.406-.992ZM9.52 14.48a3.393 3.393 0 0 0 2.478 1.02c.965 0 1.8-.342 2.479-1.02a3.392 3.392 0 0 0 1.021-2.48c0-.963-.343-1.8-1.021-2.478A3.392 3.392 0 0 0 11.998 8.5c-.964 0-1.8.343-2.478 1.021A3.392 3.392 0 0 0 8.498 12c0 .964.344 1.8 1.022 2.479Z\"/></svg>') 12 12,auto}.unreal-container.pan{cursor:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" fill=\"none\"><path fill=\"%23000\" d=\"M11.997 10.075a1.01 1.01 0 0 1-.738-.313 1.008 1.008 0 0 1-.312-.737V6.15l-.95.95a.998.998 0 0 1-.763.312 1.09 1.09 0 0 1-.763-.337 1.06 1.06 0 0 1-.324-.775c0-.3.108-.558.324-.775l2.776-2.775c.117-.116.237-.2.362-.25a1.04 1.04 0 0 1 .775 0c.125.05.246.134.363.25l2.8 2.8c.216.217.32.47.313.762a1.096 1.096 0 0 1-.338.763 1.058 1.058 0 0 1-.775.325c-.3 0-.558-.108-.775-.325l-.925-.925v2.875c0 .283-.104.53-.312.737a1.01 1.01 0 0 1-.738.313Zm0 11.5a1.04 1.04 0 0 1-.388-.075 1.103 1.103 0 0 1-.362-.25l-2.8-2.8a1.002 1.002 0 0 1-.313-.762c.009-.292.121-.546.337-.763.217-.217.475-.325.775-.325.3 0 .559.108.775.325l.926.925v-2.875c0-.283.104-.529.312-.737a1.01 1.01 0 0 1 .738-.313c.283 0 .529.104.738.313.208.208.312.454.312.737v2.875l.95-.95a.998.998 0 0 1 .762-.312c.292.008.546.12.763.337.216.217.324.475.324.775 0 .3-.108.559-.324.775l-2.775 2.775c-.117.116-.238.2-.363.25a1.04 1.04 0 0 1-.387.075Zm4.925-6.05a1.058 1.058 0 0 1-.326-.774c0-.3.109-.559.326-.776l.925-.925H14.97c-.283 0-.528-.104-.736-.312a1.01 1.01 0 0 1-.313-.738c0-.283.104-.53.313-.738.208-.208.453-.312.737-.312h2.875l-.95-.95a.998.998 0 0 1-.313-.762 1.09 1.09 0 0 1 .338-.764 1.06 1.06 0 0 1 .775-.324c.3 0 .558.108.775.324l2.774 2.776c.117.117.2.238.251.363a1.04 1.04 0 0 1 0 .775c-.05.124-.134.245-.25.362l-2.8 2.8c-.217.216-.471.32-.762.313a1.096 1.096 0 0 1-.763-.337Zm-11.4 0L2.746 12.75a1.103 1.103 0 0 1-.25-.362 1.04 1.04 0 0 1 0-.775c.05-.125.133-.246.25-.363l2.8-2.8c.216-.216.47-.32.762-.313.292.01.546.121.763.337.217.217.325.476.325.776 0 .3-.108.558-.325.775l-.925.925h2.875c.283 0 .529.104.737.312.208.209.313.455.313.738s-.105.53-.313.738a1.008 1.008 0 0 1-.737.312H6.147l.95.95c.217.217.32.471.312.762a1.09 1.09 0 0 1-.337.764 1.06 1.06 0 0 1-.775.324c-.3 0-.559-.108-.776-.324Z\"/><path stroke=\"%23fff\" stroke-opacity=\".7\" d=\"M9.22 7.912c.436.013.821-.15 1.13-.458l.097-.097v1.668c0 .422.16.793.458 1.09.298.299.67.46 1.092.46.422 0 .793-.161 1.091-.46.298-.297.459-.668.459-1.09V7.357l.071.072c.309.308.693.471 1.13.471.435 0 .82-.163 1.128-.471.301-.303.47-.676.483-1.102v-.001a1.501 1.501 0 0 0-.46-1.13l-2.799-2.8a1.6 1.6 0 0 0-.528-.36l-.003-.001a1.54 1.54 0 0 0-1.145 0l-.002.001a1.6 1.6 0 0 0-.529.36L8.118 5.172l-.001.001a1.56 1.56 0 0 0-.47 1.128c0 .435.163.82.47 1.128.303.303.676.472 1.103.484Zm0 0 .014-.5-.015.5Zm-1.311 6.865a1.497 1.497 0 0 0-.458-1.13l-.097-.097h1.668c.421 0 .793-.16 1.09-.458.298-.299.46-.67.46-1.092a1.51 1.51 0 0 0-.46-1.091 1.507 1.507 0 0 0-1.09-.459H7.354l.071-.072c.309-.308.472-.692.472-1.128 0-.436-.163-.82-.472-1.13a1.596 1.596 0 0 0-1.101-.482h-.002a1.501 1.501 0 0 0-1.129.459l-2.8 2.8a1.6 1.6 0 0 0-.36.528l-.001.003a1.54 1.54 0 0 0 0 1.144v.003c.081.2.208.375.362.529l2.774 2.775c.309.308.693.47 1.129.47.435 0 .82-.162 1.128-.47.303-.302.472-.676.484-1.102Zm0 0-.5-.014.5.015v-.001Zm3.038-8.627-.5.5.5.207V6.15Zm.475 15.814.002.001a1.54 1.54 0 0 0 1.145 0h.002c.2-.081.375-.208.53-.362l2.775-2.774a1.56 1.56 0 0 0 .47-1.129c0-.435-.162-.82-.47-1.128a1.589 1.589 0 0 0-1.103-.484 1.497 1.497 0 0 0-1.13.458l-.096.097v-1.668c0-.421-.161-.793-.459-1.09a1.509 1.509 0 0 0-1.091-.46c-.422 0-.794.161-1.092.46a1.507 1.507 0 0 0-.458 1.09v1.668l-.072-.072a1.558 1.558 0 0 0-1.129-.471c-.436 0-.82.163-1.128.471l-.001.001c-.301.302-.47.675-.483 1.101v.001c-.012.437.151.821.46 1.13l2.8 2.8c.153.153.328.28.528.36Zm5.146-6.085c.303.302.676.47 1.102.484a1.5 1.5 0 0 0 1.13-.46l2.8-2.8a1.6 1.6 0 0 0 .36-.528l.002-.003a1.54 1.54 0 0 0 0-1.144l-.001-.003a1.6 1.6 0 0 0-.361-.529l-2.774-2.775h-.001a1.56 1.56 0 0 0-1.128-.47c-.436 0-.82.162-1.128.47h-.001a1.59 1.59 0 0 0-.484 1.102c-.012.437.15.822.459 1.13l.097.097h-1.668c-.422 0-.793.16-1.09.459-.299.298-.46.67-.46 1.091 0 .422.161.793.46 1.092.297.297.668.458 1.09.458h1.668l-.072.072a1.558 1.558 0 0 0-.472 1.129c0 .435.164.82.472 1.128ZM6.147 13.05l.5.5.207-.5h-.707Z\"/></svg>') 12 12,auto}.unreal-container.dolly{cursor:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" fill=\"none\"><mask id=\"a\" width=\"24\" height=\"24\" x=\"0\" y=\"0\" maskUnits=\"userSpaceOnUse\" style=\"mask-type:alpha\"><path fill=\"%23127392\" d=\"M0 0h24v24H0z\"/></mask><g mask=\"url(%23a)\"><path fill=\"%23000\" d=\"M12.002 20.575a1.026 1.026 0 0 1-.75-.325l-2.55-2.55a.933.933 0 0 1-.287-.713c.008-.275.113-.513.313-.713.2-.2.441-.3.725-.3.283 0 .524.1.724.3l.776.776V6.95l-.8.8c-.2.2-.438.296-.712.288a1.018 1.018 0 0 1-.713-.312c-.2-.2-.3-.442-.3-.726s.1-.525.3-.725l2.524-2.525a1.026 1.026 0 0 1 1.5 0l2.55 2.55c.2.2.296.438.289.713a1.023 1.023 0 0 1-.314.713c-.2.2-.441.3-.724.3a.988.988 0 0 1-.725-.3l-.775-.776v10.1l.8-.8c.2-.2.437-.296.712-.288.275.008.512.112.712.312.2.2.3.442.3.726s-.1.525-.3.725l-2.524 2.525a1.026 1.026 0 0 1-.75.325Z\"/><path stroke=\"%23fff\" stroke-opacity=\".7\" d=\"M13.553 8.153c.28.245.62.373 1 .373.415 0 .784-.153 1.078-.447.287-.287.446-.644.46-1.05v-.002a1.432 1.432 0 0 0-.435-1.08l-2.55-2.55-.354.353.354-.354a1.526 1.526 0 0 0-2.207 0L8.374 5.921A1.487 1.487 0 0 0 7.928 7c0 .416.152.785.446 1.08.288.287.645.446 1.052.457l4.127-.384Zm0 0v7.693m0-7.693a1.667 1.667 0 0 1-.079-.074l-3.021.075v7.693a1.482 1.482 0 0 0-1-.373c-.416 0-.785.153-1.079.447a1.522 1.522 0 0 0-.46 1.05v.002c-.011.417.14.786.435 1.08l2.55 2.55a1.527 1.527 0 0 0 2.207 0l2.525-2.524c.294-.294.446-.663.446-1.079 0-.416-.152-.785-.446-1.08a1.518 1.518 0 0 0-1.052-.457m0 0-.014.5.014-.5Zm0 0a1.424 1.424 0 0 0-1.027.383m0 0a1.583 1.583 0 0 0-.053.05l.354.354m-.3-.404v.104l.3.3m0 0-.3-.3v.6l.3-.3Z\"/></g></svg>%0A') 12 12,auto}.unreal-container.disabled:before{content:\"\";position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}#playerUI{position:absolute;z-index:10;width:100%}#statsContainer{display:none;text-align:left;background-color:#000c}#stats{padding:6px;font-weight:700;font-size:14px;color:#0f0}canvas{position:absolute;image-rendering:crisp-edges}#overlay{position:absolute;top:0;right:2%;z-index:100;padding:4px;border-top-width:0;-webkit-border-bottom-right-radius:5px;-moz-border-radius-bottomright:5px;border-bottom-right-radius:5px;-webkit-border-bottom-left-radius:5px;-moz-border-radius-bottomleft:5px;border-bottom-left-radius:5px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-touch-callout:none}.overlay{font-weight:lighter;font-family:var(--src-font-family-body)}#overlayButton:hover{cursor:pointer}#overlayButton{font-size:40px;text-align:right}#overlaySettings{display:none;width:300px;padding:4px}#videoMessageOverlay{position:absolute;z-index:20;width:100%;margin:auto;font-size:1.8em;font-family:var(--src-font-family-body)}#playButton{display:inline-block;height:auto}img#playButton{width:10%;max-width:241px}#UIInteraction{position:fixed}#UIInteractionButtonBoundary{padding:2px}#UIInteractionButton{cursor:pointer}#hiddenInput{position:absolute;left:-10%;width:0;opacity:0}#editTextButton{position:absolute;width:40px;height:40px}\n"] }]
6549
+ }], ctorParameters: () => [], propDecorators: { useContainerAsSizeProvider: [{ type: i0.Input, args: [{ isSignal: true, alias: "useContainerAsSizeProvider", required: false }] }], isStudio: [{ type: i0.Input, args: [{ isSignal: true, alias: "isStudio", required: false }] }], resolutionSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "resolutionSize", required: false }] }], changeMouseOverScene: [{ type: i0.Output, args: ["changeMouseOverScene"] }], videoElement: [{ type: i0.ViewChild, args: ['videoContainer', { isSignal: true }] }], onMouseOver: [{
6677
6550
  type: HostListener,
6678
6551
  args: ['mouseover']
6679
6552
  }], onMouseOut: [{
6680
6553
  type: HostListener,
6681
6554
  args: ['mouseout']
6555
+ }], onContextMenu: [{
6556
+ type: HostListener,
6557
+ args: ['contextmenu', ['$event']]
6682
6558
  }] } });
6683
6559
 
6684
6560
  class WebrtcErrorModalComponent {
@@ -6689,9 +6565,6 @@ class WebrtcErrorModalComponent {
6689
6565
  }
6690
6566
  #dialogRef;
6691
6567
  #store;
6692
- close() {
6693
- this.#dialogRef.close();
6694
- }
6695
6568
  closeModalWithCirrusDisconnect() {
6696
6569
  if (!this.isCirrusConnected()) {
6697
6570
  this.#store.dispatch(disconnectStream({
@@ -6701,22 +6574,23 @@ class WebrtcErrorModalComponent {
6701
6574
  }
6702
6575
  this.close();
6703
6576
  }
6704
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: WebrtcErrorModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
6705
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.10", type: WebrtcErrorModalComponent, isStandalone: true, selector: "app-webrtc-error-modal", ngImport: i0, template: "<div [attr.data-testid]=\"'webrtc-error-modal'\" class=\"src-modal\">\n <header class=\"src-modal__header\"></header>\n <section class=\"src-modal__body\">\n <div [attr.data-testid]=\"'title'\" class=\"src-modal__title\">\n WebRTC error\n </div>\n <div style=\"text-align: left\">\n An internet connection type (WebRTC) appears to be blocked either by your\n browser settings or your current network. If WebRTC is blocked on your\n browser you may be able to adjust these settings yourself based on\n instructions here:\n <a href=\"https://myownconference.com/blog/en/webrtc/\" target=\"blank\"\n >https://myownconference.com/blog/en/webrtc/</a\n >\n <br /><br />\n Trying a different web browser may help confirm this as well. If WebRTC is\n blocked by your network, try switching to a different network if possible\n or contact your network administrator.<br /><br />\n WebRTC is common, safe and increasingly utilised method for streaming real\n time 3D experiences via a web browser. It typically consumes no more\n bandwidth than streaming an HD video.\n </div>\n </section>\n <footer class=\"src-modal__footer\">\n <src-button\n (onClick)=\"closeModalWithCirrusDisconnect()\"\n [data-testid]=\"'close-webrtc-error-modal'\"\n weight=\"primary\"\n >\n Ok\n </src-button>\n </footer>\n</div>\n", styles: [".src-modal{--modalBodyPadding: 20px 8px 20px 20px;display:grid;grid-template-columns:minmax(0,1fr);grid-template-rows:auto minmax(0,1fr) auto}.src-modal__body{width:100%}.src-modal__body p{color:var(--src-text-body-main, #1f2937);font-size:14px;font-style:normal;font-weight:400;line-height:24px;margin-top:0}.src-modal__body a{word-wrap:break-word;white-space:normal}\n"], dependencies: [{ kind: "component", type: SourceButtonComponent, selector: "src-button", inputs: ["type", "appearance", "weight", "size", "context", "customClass", "isFullWidth", "isPressed", "isDisabled", "isLoading", "isInverted", "srcButtonConfig", "formID", "data-testid", "ariaLabel"], outputs: ["onClick", "onSubmit"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
6577
+ close() {
6578
+ this.#dialogRef.close();
6579
+ }
6580
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: WebrtcErrorModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
6581
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.11", type: WebrtcErrorModalComponent, isStandalone: true, selector: "app-webrtc-error-modal", ngImport: i0, template: "<div [attr.data-testid]=\"'webrtc-error-modal'\" class=\"src-modal\">\n <header class=\"src-modal__header\"></header>\n <section class=\"src-modal__body\">\n <div [attr.data-testid]=\"'title'\" class=\"src-modal__title\">\n WebRTC error\n </div>\n <div style=\"text-align: left\">\n An internet connection type (WebRTC) appears to be blocked either by your\n browser settings or your current network. If WebRTC is blocked on your\n browser you may be able to adjust these settings yourself based on\n instructions here:\n <a href=\"https://myownconference.com/blog/en/webrtc/\" target=\"blank\"\n >https://myownconference.com/blog/en/webrtc/</a\n >\n <br /><br />\n Trying a different web browser may help confirm this as well. If WebRTC is\n blocked by your network, try switching to a different network if possible\n or contact your network administrator.<br /><br />\n WebRTC is common, safe and increasingly utilised method for streaming real\n time 3D experiences via a web browser. It typically consumes no more\n bandwidth than streaming an HD video.\n </div>\n </section>\n <footer class=\"src-modal__footer\">\n <src-button\n (onClick)=\"closeModalWithCirrusDisconnect()\"\n [data-testid]=\"'close-webrtc-error-modal'\"\n weight=\"primary\"\n >\n Ok\n </src-button>\n </footer>\n</div>\n", styles: [".src-modal{--modalBodyPadding: 20px 8px 20px 20px;display:grid;grid-template-columns:minmax(0,1fr);grid-template-rows:auto minmax(0,1fr) auto}.src-modal__body{width:100%}.src-modal__body p{color:var(--src-text-body-main, #1f2937);font-size:14px;font-style:normal;font-weight:400;line-height:24px;margin-top:0}.src-modal__body a{word-wrap:break-word;white-space:normal}\n"], dependencies: [{ kind: "component", type: SourceButtonComponent, selector: "src-button", inputs: ["type", "appearance", "weight", "size", "context", "customClass", "isFullWidth", "isPressed", "isDisabled", "isLoading", "isInverted", "srcButtonConfig", "formID", "data-testid", "ariaLabel"], outputs: ["onClick", "onSubmit"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
6706
6582
  }
6707
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: WebrtcErrorModalComponent, decorators: [{
6583
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: WebrtcErrorModalComponent, decorators: [{
6708
6584
  type: Component,
6709
6585
  args: [{ selector: 'app-webrtc-error-modal', changeDetection: ChangeDetectionStrategy.OnPush, imports: [SourceButtonComponent], template: "<div [attr.data-testid]=\"'webrtc-error-modal'\" class=\"src-modal\">\n <header class=\"src-modal__header\"></header>\n <section class=\"src-modal__body\">\n <div [attr.data-testid]=\"'title'\" class=\"src-modal__title\">\n WebRTC error\n </div>\n <div style=\"text-align: left\">\n An internet connection type (WebRTC) appears to be blocked either by your\n browser settings or your current network. If WebRTC is blocked on your\n browser you may be able to adjust these settings yourself based on\n instructions here:\n <a href=\"https://myownconference.com/blog/en/webrtc/\" target=\"blank\"\n >https://myownconference.com/blog/en/webrtc/</a\n >\n <br /><br />\n Trying a different web browser may help confirm this as well. If WebRTC is\n blocked by your network, try switching to a different network if possible\n or contact your network administrator.<br /><br />\n WebRTC is common, safe and increasingly utilised method for streaming real\n time 3D experiences via a web browser. It typically consumes no more\n bandwidth than streaming an HD video.\n </div>\n </section>\n <footer class=\"src-modal__footer\">\n <src-button\n (onClick)=\"closeModalWithCirrusDisconnect()\"\n [data-testid]=\"'close-webrtc-error-modal'\"\n weight=\"primary\"\n >\n Ok\n </src-button>\n </footer>\n</div>\n", styles: [".src-modal{--modalBodyPadding: 20px 8px 20px 20px;display:grid;grid-template-columns:minmax(0,1fr);grid-template-rows:auto minmax(0,1fr) auto}.src-modal__body{width:100%}.src-modal__body p{color:var(--src-text-body-main, #1f2937);font-size:14px;font-style:normal;font-weight:400;line-height:24px;margin-top:0}.src-modal__body a{word-wrap:break-word;white-space:normal}\n"] }]
6710
6586
  }] });
6711
6587
 
6712
6588
  class FilterSettingsComponent {
6713
6589
  constructor() {
6714
- this.#store = inject(Store);
6715
6590
  this.storageKey = 'LBMFilterSettings';
6716
6591
  this.defaultFilterModel = { ...DefaultFilterModel };
6717
6592
  this.settings = { ...DefaultFilterModel };
6718
6593
  }
6719
- #store;
6720
6594
  ngOnInit() {
6721
6595
  const savedSettings = localStorage.getItem(this.storageKey);
6722
6596
  if (savedSettings) {
@@ -6742,7 +6616,6 @@ class FilterSettingsComponent {
6742
6616
  }
6743
6617
  Object.assign(this.settings, settings);
6744
6618
  Object.assign(FilterModel, settings);
6745
- this.#store.dispatch(setKalmanSettings({ settings }));
6746
6619
  }
6747
6620
  reset() {
6748
6621
  this.dispatch({
@@ -6750,23 +6623,23 @@ class FilterSettingsComponent {
6750
6623
  panelOpen: this.settings.panelOpen,
6751
6624
  });
6752
6625
  }
6753
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: FilterSettingsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
6754
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.10", type: FilterSettingsComponent, isStandalone: true, selector: "app-filter-settings", ngImport: i0, template: "<div class=\"settings-container\">\n <button (click)=\"togglePanel()\" class=\"gear-button\">LBM</button>\n @if (settings.panelOpen) {\n <div class=\"settings-panel\">\n <h3>LBM Trigger Settings</h3>\n <h5>Data Flow Monitor</h5>\n <form>\n <label>\n Monitoring delay:\n <input\n [(ngModel)]=\"settings.monitoringDelayTime\"\n [placeholder]=\"defaultFilterModel.monitoringDelayTime\"\n type=\"number\"\n name=\"monitoringDelayTime\"\n />\n </label>\n\n <label>\n Minimum Bitrate (kbit/s):\n <input\n [(ngModel)]=\"settings.minimumBitrate\"\n [placeholder]=\"defaultFilterModel.minimumBitrate\"\n type=\"number\"\n name=\"minimumBitrate\"\n />\n </label>\n <label>\n Yellow Flag (%):\n <input\n [(ngModel)]=\"settings.yellowFlag\"\n [placeholder]=\"defaultFilterModel.yellowFlag\"\n type=\"number\"\n name=\"yellowFlag\"\n />\n </label>\n <label>\n Red Flag (%):\n <input\n [(ngModel)]=\"settings.redFlag\"\n [placeholder]=\"defaultFilterModel.redFlag\"\n type=\"number\"\n name=\"redFlag\"\n />\n </label>\n <label>\n Minimum FPS:\n <input\n [(ngModel)]=\"settings.minimumFps\"\n [placeholder]=\"defaultFilterModel.minimumFps\"\n type=\"number\"\n name=\"minimumFps\"\n />\n </label>\n <hr />\n <h5>Kalman Filter</h5>\n <label>\n Initial Bitrate Estimate (kbit/s):\n <input\n [(ngModel)]=\"settings.initialBitrateEstimate\"\n [placeholder]=\"defaultFilterModel.initialBitrateEstimate\"\n type=\"number\"\n name=\"initialBitrateEstimate\"\n />\n </label>\n <label>\n Initial Error Covariance:\n <input\n [(ngModel)]=\"settings.initialErrorCovariance\"\n [placeholder]=\"defaultFilterModel.initialErrorCovariance\"\n type=\"number\"\n name=\"initialErrorCovariance\"\n />\n </label>\n <label>\n Process Noise (Q):\n <input\n [(ngModel)]=\"settings.processNoise\"\n [placeholder]=\"defaultFilterModel.processNoise\"\n type=\"number\"\n name=\"processNoise\"\n />\n </label>\n <label>\n Measurement Noise (R):\n <input\n [(ngModel)]=\"settings.measurementNoise\"\n [placeholder]=\"defaultFilterModel.measurementNoise\"\n type=\"number\"\n name=\"measurementNoise\"\n />\n </label>\n </form>\n <br />\n <br />\n <div class=\"apply-button-container\">\n <button (click)=\"reset()\">Reset</button>\n <button (click)=\"saveSettings()\">Apply</button>\n </div>\n </div>\n }\n</div>\n", styles: [".settings-container{position:absolute;top:65px;right:10px;z-index:1000}.gear-button{right:0;width:50px;position:absolute;cursor:pointer;font-size:10px;background:none;font-weight:500;border:1px solid;border-radius:9px;background:#0162cc;color:#fff;padding:4px}.gear-button>*{color:#e6f419;filter:drop-shadow(1px 1px 1px rgb(0,0,0))}.settings-panel{font-size:13px;background:#fff;border:1px solid #ccc;padding:15px;margin-top:25px;border-radius:4px;box-shadow:0 2px 8px #0003}.settings-panel h3{margin-top:0}.settings-panel form{display:flex;flex-direction:column}.settings-panel label{margin-bottom:10px;display:flex;flex-direction:column}.settings-panel input{padding:4px;margin-top:4px;border:1px solid #ccc;border-radius:2px}.apply-button-container{position:absolute;bottom:15px;right:15px}.apply-button-container button{padding:6px 12px;border:none;background-color:#1976d2;color:#fff;border-radius:4px;cursor:pointer;margin:2px}.apply-button-container button:hover{background-color:#1565c0}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
6626
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: FilterSettingsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
6627
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: FilterSettingsComponent, isStandalone: true, selector: "app-filter-settings", ngImport: i0, template: "<div class=\"settings-container\">\n <button (click)=\"togglePanel()\" class=\"gear-button\">LBM</button>\n @if (settings.panelOpen) {\n <div class=\"settings-panel\">\n <h3>LBM Trigger Settings</h3>\n <h5>Data Flow Monitor</h5>\n <form>\n <label>\n Monitoring delay:\n <input\n [(ngModel)]=\"settings.monitoringDelayTime\"\n [placeholder]=\"defaultFilterModel.monitoringDelayTime\"\n type=\"number\"\n name=\"monitoringDelayTime\"\n />\n </label>\n\n <label>\n Minimum Bitrate (kbit/s):\n <input\n [(ngModel)]=\"settings.minimumBitrate\"\n [placeholder]=\"defaultFilterModel.minimumBitrate\"\n type=\"number\"\n name=\"minimumBitrate\"\n />\n </label>\n <label>\n Yellow Flag (%):\n <input\n [(ngModel)]=\"settings.yellowFlag\"\n [placeholder]=\"defaultFilterModel.yellowFlag\"\n type=\"number\"\n name=\"yellowFlag\"\n />\n </label>\n <label>\n Red Flag (%):\n <input\n [(ngModel)]=\"settings.redFlag\"\n [placeholder]=\"defaultFilterModel.redFlag\"\n type=\"number\"\n name=\"redFlag\"\n />\n </label>\n <label>\n Minimum FPS:\n <input\n [(ngModel)]=\"settings.minimumFps\"\n [placeholder]=\"defaultFilterModel.minimumFps\"\n type=\"number\"\n name=\"minimumFps\"\n />\n </label>\n <hr />\n <h5>Kalman Filter</h5>\n <label>\n Initial Bitrate Estimate (kbit/s):\n <input\n [(ngModel)]=\"settings.initialBitrateEstimate\"\n [placeholder]=\"defaultFilterModel.initialBitrateEstimate\"\n type=\"number\"\n name=\"initialBitrateEstimate\"\n />\n </label>\n <label>\n Initial Error Covariance:\n <input\n [(ngModel)]=\"settings.initialErrorCovariance\"\n [placeholder]=\"defaultFilterModel.initialErrorCovariance\"\n type=\"number\"\n name=\"initialErrorCovariance\"\n />\n </label>\n <label>\n Process Noise (Q):\n <input\n [(ngModel)]=\"settings.processNoise\"\n [placeholder]=\"defaultFilterModel.processNoise\"\n type=\"number\"\n name=\"processNoise\"\n />\n </label>\n <label>\n Measurement Noise (R):\n <input\n [(ngModel)]=\"settings.measurementNoise\"\n [placeholder]=\"defaultFilterModel.measurementNoise\"\n type=\"number\"\n name=\"measurementNoise\"\n />\n </label>\n </form>\n <br />\n <br />\n <div class=\"apply-button-container\">\n <button (click)=\"reset()\">Reset</button>\n <button (click)=\"saveSettings()\">Apply</button>\n </div>\n </div>\n }\n</div>\n", styles: [".settings-container{position:absolute;top:65px;right:10px;z-index:1000}.gear-button{right:0;width:50px;position:absolute;cursor:pointer;font-size:10px;background:none;font-weight:500;border:1px solid;border-radius:9px;background:#0162cc;color:#fff;padding:4px}.gear-button>*{color:#e6f419;filter:drop-shadow(1px 1px 1px rgb(0,0,0))}.settings-panel{font-size:13px;background:#fff;border:1px solid #ccc;padding:15px;margin-top:25px;border-radius:4px;box-shadow:0 2px 8px #0003}.settings-panel h3{margin-top:0}.settings-panel form{display:flex;flex-direction:column}.settings-panel label{margin-bottom:10px;display:flex;flex-direction:column}.settings-panel input{padding:4px;margin-top:4px;border:1px solid #ccc;border-radius:2px}.apply-button-container{position:absolute;bottom:15px;right:15px}.apply-button-container button{padding:6px 12px;border:none;background-color:#1976d2;color:#fff;border-radius:4px;cursor:pointer;margin:2px}.apply-button-container button:hover{background-color:#1565c0}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
6755
6628
  }
6756
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: FilterSettingsComponent, decorators: [{
6629
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: FilterSettingsComponent, decorators: [{
6757
6630
  type: Component,
6758
6631
  args: [{ selector: 'app-filter-settings', imports: [FormsModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"settings-container\">\n <button (click)=\"togglePanel()\" class=\"gear-button\">LBM</button>\n @if (settings.panelOpen) {\n <div class=\"settings-panel\">\n <h3>LBM Trigger Settings</h3>\n <h5>Data Flow Monitor</h5>\n <form>\n <label>\n Monitoring delay:\n <input\n [(ngModel)]=\"settings.monitoringDelayTime\"\n [placeholder]=\"defaultFilterModel.monitoringDelayTime\"\n type=\"number\"\n name=\"monitoringDelayTime\"\n />\n </label>\n\n <label>\n Minimum Bitrate (kbit/s):\n <input\n [(ngModel)]=\"settings.minimumBitrate\"\n [placeholder]=\"defaultFilterModel.minimumBitrate\"\n type=\"number\"\n name=\"minimumBitrate\"\n />\n </label>\n <label>\n Yellow Flag (%):\n <input\n [(ngModel)]=\"settings.yellowFlag\"\n [placeholder]=\"defaultFilterModel.yellowFlag\"\n type=\"number\"\n name=\"yellowFlag\"\n />\n </label>\n <label>\n Red Flag (%):\n <input\n [(ngModel)]=\"settings.redFlag\"\n [placeholder]=\"defaultFilterModel.redFlag\"\n type=\"number\"\n name=\"redFlag\"\n />\n </label>\n <label>\n Minimum FPS:\n <input\n [(ngModel)]=\"settings.minimumFps\"\n [placeholder]=\"defaultFilterModel.minimumFps\"\n type=\"number\"\n name=\"minimumFps\"\n />\n </label>\n <hr />\n <h5>Kalman Filter</h5>\n <label>\n Initial Bitrate Estimate (kbit/s):\n <input\n [(ngModel)]=\"settings.initialBitrateEstimate\"\n [placeholder]=\"defaultFilterModel.initialBitrateEstimate\"\n type=\"number\"\n name=\"initialBitrateEstimate\"\n />\n </label>\n <label>\n Initial Error Covariance:\n <input\n [(ngModel)]=\"settings.initialErrorCovariance\"\n [placeholder]=\"defaultFilterModel.initialErrorCovariance\"\n type=\"number\"\n name=\"initialErrorCovariance\"\n />\n </label>\n <label>\n Process Noise (Q):\n <input\n [(ngModel)]=\"settings.processNoise\"\n [placeholder]=\"defaultFilterModel.processNoise\"\n type=\"number\"\n name=\"processNoise\"\n />\n </label>\n <label>\n Measurement Noise (R):\n <input\n [(ngModel)]=\"settings.measurementNoise\"\n [placeholder]=\"defaultFilterModel.measurementNoise\"\n type=\"number\"\n name=\"measurementNoise\"\n />\n </label>\n </form>\n <br />\n <br />\n <div class=\"apply-button-container\">\n <button (click)=\"reset()\">Reset</button>\n <button (click)=\"saveSettings()\">Apply</button>\n </div>\n </div>\n }\n</div>\n", styles: [".settings-container{position:absolute;top:65px;right:10px;z-index:1000}.gear-button{right:0;width:50px;position:absolute;cursor:pointer;font-size:10px;background:none;font-weight:500;border:1px solid;border-radius:9px;background:#0162cc;color:#fff;padding:4px}.gear-button>*{color:#e6f419;filter:drop-shadow(1px 1px 1px rgb(0,0,0))}.settings-panel{font-size:13px;background:#fff;border:1px solid #ccc;padding:15px;margin-top:25px;border-radius:4px;box-shadow:0 2px 8px #0003}.settings-panel h3{margin-top:0}.settings-panel form{display:flex;flex-direction:column}.settings-panel label{margin-bottom:10px;display:flex;flex-direction:column}.settings-panel input{padding:4px;margin-top:4px;border:1px solid #ccc;border-radius:2px}.apply-button-container{position:absolute;bottom:15px;right:15px}.apply-button-container button{padding:6px 12px;border:none;background-color:#1976d2;color:#fff;border-radius:4px;cursor:pointer;margin:2px}.apply-button-container button:hover{background-color:#1565c0}\n"] }]
6759
6632
  }] });
6760
6633
 
6761
6634
  class LowBandwidthDetectorComponent {
6762
6635
  constructor() {
6763
- this.store = inject(Store);
6636
+ this.#store = inject(Store);
6764
6637
  this.dialog = inject(Dialog);
6765
6638
  this.destroyRef = inject(DestroyRef);
6766
6639
  this.videoService = inject(VideoService);
6767
6640
  this.isDevMode = inject(DevModeService).isDevMode;
6768
- this.viewportReady$ = this.store.select(unrealFeature.selectViewportReady);
6769
- this.isLowBandwidth$ = this.store.select(unrealFeature.selectLowBandwidth);
6641
+ this.viewportReady$ = this.#store.select(unrealFeature.selectViewportReady);
6642
+ this.isLowBandwidth$ = of(true);
6770
6643
  this.lowBandwidthTrigger$ = this.isLowBandwidth$.pipe(distinctUntilChanged$1(), filter(Truthy));
6771
6644
  this.highBandwidthTrigger$ = this.isLowBandwidth$.pipe(distinctUntilChanged$1(), filter(Falsy));
6772
6645
  this.viewPortDestroyedTrigger$ = this.viewportReady$.pipe(distinctUntilChanged$1(), filter(Falsy));
@@ -6788,6 +6661,7 @@ class LowBandwidthDetectorComponent {
6788
6661
  this.isIndicatorExpanded = signal(false, ...(ngDevMode ? [{ debugName: "isIndicatorExpanded" }] : /* istanbul ignore next */ []));
6789
6662
  this.isIndicatorExpandedTrigger$ = toObservable(this.isIndicatorExpanded).pipe(skip(1), distinctUntilChanged$1(), filter(Falsy));
6790
6663
  }
6664
+ #store;
6791
6665
  ngOnInit() {
6792
6666
  this.lowBandwidthTrigger$
6793
6667
  .pipe(takeUntilDestroyed(this.destroyRef))
@@ -6827,15 +6701,13 @@ class LowBandwidthDetectorComponent {
6827
6701
  else {
6828
6702
  reason = `Speed < ${FilterModel.minimumBitrate}`;
6829
6703
  }
6830
- Logger.info(`LBM DETECTED: ${reason}, switching to low bandwidth; `, dataFlowResult.message);
6831
- this.store.dispatch(changeLowBandwidth({
6832
- lowBandwidth: true,
6704
+ Logger.info(`LBM DETECTED: ${reason}, switching to low bandwidth; `, dataFlowResult.message, {
6833
6705
  stats: {
6834
6706
  dataFlowResult,
6835
6707
  reason,
6836
6708
  triggerTime: this.aggregatedStats()?.runTime ?? 0,
6837
6709
  },
6838
- }));
6710
+ });
6839
6711
  }
6840
6712
  }
6841
6713
  toggleIndicator(value) {
@@ -6855,13 +6727,12 @@ class LowBandwidthDetectorComponent {
6855
6727
  .closed.pipe(filter(Truthy))
6856
6728
  .subscribe(() => {
6857
6729
  this.toggleIndicator(false);
6858
- this.store.dispatch(changeLowBandwidth({ lowBandwidth: false }));
6859
6730
  });
6860
6731
  }
6861
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: LowBandwidthDetectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
6862
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.10", type: LowBandwidthDetectorComponent, isStandalone: true, selector: "app-low-bandwidth-detector", ngImport: i0, template: "@if (isReducedQuality() || isLowBandwidth()) {\n <div\n [class.expanded]=\"isIndicatorExpanded() && isLowBandwidth()\"\n class=\"lbm-indicator freeze-loader\"\n >\n <div (click)=\"toggleIndicator(true)\" class=\"lbm-icon\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 20 20\">\n <path\n fill=\"#85888E\"\n d=\"M7.189 3.605c-.73.145-1.438.35-2.126.614A14.412 14.412 0 0 0 .896 6.666a1.08 1.08 0 0 0-.375.844c0 .34.118.629.354.865s.524.36.865.375c.34.014.65-.09.927-.313a11.89 11.89 0 0 1 3.385-1.916 10.94 10.94 0 0 1 1.235-.375l-.098-2.541ZM7.385 8.708a9.107 9.107 0 0 0-2.906 1.48c-.264.194-.402.464-.416.812-.014.347.104.646.354.896.236.236.524.364.864.385.34.02.664-.073.97-.281.422-.29.878-.53 1.368-.721a2.545 2.545 0 0 1-.166-.814l-.068-1.757ZM12.386 11.267c.094-.25.15-.52.161-.802l.068-1.755a9.019 9.019 0 0 1 2.927 1.52c.264.193.4.46.406.801.007.34-.114.635-.364.885a1.213 1.213 0 0 1-.865.365A1.614 1.614 0 0 1 13.77 12a6.574 6.574 0 0 0-1.385-.733ZM12.713 6.146l.098-2.542c.73.146 1.438.351 2.127.615 1.541.59 2.93 1.406 4.166 2.447.25.223.379.5.386.834.007.333-.115.625-.365.875a1.253 1.253 0 0 1-.864.375c-.34.014-.65-.09-.927-.313a11.892 11.892 0 0 0-3.386-1.916 10.94 10.94 0 0 0-1.235-.375ZM8.813 16.187c.32.32.715.48 1.187.48.472 0 .868-.16 1.188-.48.32-.32.479-.715.479-1.187 0-.473-.16-.868-.48-1.188-.319-.32-.715-.479-1.187-.479-.472 0-.868.16-1.187.48-.32.319-.48.714-.48 1.187 0 .472.16.868.48 1.187Z\"\n />\n <path\n fill=\"#fff\"\n fill-rule=\"evenodd\"\n d=\"M10 .833c-.91 0-1.637.756-1.602 1.665l.304 7.92a1.299 1.299 0 0 0 2.596 0l.305-7.92A1.604 1.604 0 0 0 10 .833ZM8.813 16.187c.32.32.715.48 1.187.48.472 0 .868-.16 1.188-.48.32-.32.479-.715.479-1.187 0-.473-.16-.868-.48-1.188-.319-.32-.715-.479-1.187-.479-.472 0-.868.16-1.187.48-.32.319-.48.714-.48 1.187 0 .472.16.868.48 1.187Z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n </div>\n\n <div\n [class.lbm-message--open]=\"isLowBandwidth() && isIndicatorExpanded()\"\n class=\"lbm-message\"\n >\n <p class=\"lbm-description\">\n Fluid Interactivity Modes were disabled due to an unstable connection.\n\n <button\n (click)=\"openLBMDialog()\"\n [attr.data-testid]=\"'learn-more-lbm'\"\n type=\"button\"\n class=\"lbm-learn-more\"\n >\n Learn more\n </button>\n </p>\n <button\n (click)=\"toggleIndicator(false)\"\n [attr.data-testid]=\"'close-lbm-indicator'\"\n type=\"button\"\n class=\"lbm-close\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 20 20\"\n fill=\"none\"\n >\n <path\n d=\"M10.0001 11.2289L6.68618 14.5428C6.51951 14.7095 6.31818 14.7895 6.08218 14.7828C5.84618 14.7755 5.64485 14.6885 5.47818 14.5218C5.31151 14.3552 5.22818 14.1505 5.22818 13.9078C5.22818 13.6645 5.31151 13.4595 5.47818 13.2928L8.77107 9.99992L5.45718 6.68603C5.29051 6.51936 5.21051 6.3147 5.21718 6.07203C5.22451 5.8287 5.31151 5.6237 5.47818 5.45703C5.64485 5.29036 5.84951 5.20703 6.09218 5.20703C6.33551 5.20703 6.54051 5.29036 6.70718 5.45703L10.0001 8.77092L13.314 5.45703C13.4806 5.29036 13.6853 5.20703 13.928 5.20703C14.1713 5.20703 14.3763 5.29036 14.543 5.45703C14.7096 5.6237 14.793 5.8287 14.793 6.07203C14.793 6.3147 14.7096 6.51936 14.543 6.68603L11.2291 9.99992L14.543 13.3138C14.7096 13.4805 14.793 13.6818 14.793 13.9178C14.793 14.1538 14.7096 14.3552 14.543 14.5218C14.3763 14.6885 14.1713 14.7718 13.928 14.7718C13.6853 14.7718 13.4806 14.6885 13.314 14.5218L10.0001 11.2289Z\"\n fill=\"white\"\n />\n </svg>\n </button>\n </div>\n </div>\n}\n\n@if (isDevMode) {\n <app-filter-settings />\n}\n", styles: [".freeze-loader,.lbm-indicator{--lbmPositionTop: 18px;--lbmPositionLeft: 18px;--lbmPositionRight: 18px;--lbmIndicatorSize: 36px;--lbmIndicatorBorderRadius: 8px;--lbmIndicatorCloseSize: 20px;--lbmPadding: 10px;--lbmTextSize: 13px;--lbmLineHeight: 20px;--lbmTextColor: #ffffff;--lbmBackgroundColor: rgba(17, 24, 39, .7);position:absolute;top:var(--lbmPositionTop);left:var(--lbmPositionLeft);z-index:10;display:flex;justify-content:center;align-items:flex-start;min-width:var(--lbmIndicatorSize);min-height:var(--lbmIndicatorSize);overflow:hidden}.freeze-loader.expanded,.lbm-indicator.expanded{--lbmPositionTop: 8px;--lbmPositionLeft: 8px;--lbmPositionRight: 8px;width:auto;max-width:calc(100% - var(--lbmPositionLeft) - var(--lbmPositionRight));padding:var(--lbmPadding);background:var(--lbmBackgroundColor);-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);border-radius:var(--lbmIndicatorBorderRadius);gap:12px}.freeze-loader .lbm-icon,.lbm-indicator .lbm-icon{display:flex;width:var(--lbmIndicatorSize);height:var(--lbmIndicatorSize);padding:8px;color:#fff;background-color:var(--lbmBackgroundColor);border-radius:50%;flex-shrink:0;cursor:pointer}.freeze-loader .lbm-icon svg,.freeze-loader .lbm-icon img,.lbm-indicator .lbm-icon svg,.lbm-indicator .lbm-icon img{width:100%;height:100%}.freeze-loader .lbm-close,.lbm-indicator .lbm-close{position:absolute;top:0;right:0;width:var(--lbmIndicatorCloseSize);height:var(--lbmIndicatorCloseSize);background-color:transparent;border:none;padding:0;cursor:pointer}.freeze-loader .lbm-message,.lbm-indicator .lbm-message{position:relative;opacity:0;height:0;max-width:0;padding-right:24px;pointer-events:none;visibility:hidden;animation:closeMessage .3s forwards}.freeze-loader .lbm-message--open,.lbm-indicator .lbm-message--open{position:relative;visibility:visible;display:flex;align-items:center;align-self:center;pointer-events:all;gap:12px;max-width:fit-content;height:auto;opacity:1;animation:openMessage .3s forwards}.freeze-loader .lbm-description,.lbm-indicator .lbm-description{margin:0;font-size:var(--lbmTextSize);font-weight:400;line-height:var(--lbmLineHeight);color:var(--lbmTextColor)}.freeze-loader .lbm-learn-more,.lbm-indicator .lbm-learn-more{display:inline-flex;padding:0;font-size:var(--lbmTextSize);font-weight:400;line-height:var(--lbmLineHeight);color:var(--lbmTextColor);text-decoration-line:underline;background-color:transparent;border:none;cursor:pointer}.freeze-loader .lbm-learn-more:hover,.lbm-indicator .lbm-learn-more:hover{text-decoration:none}@keyframes openMessage{0%{opacity:0;max-width:0;height:0}10%{height:auto;max-width:fit-content}to{opacity:1;height:auto;max-width:fit-content}}@keyframes closeMessage{0%{opacity:1;max-width:fit-content}to{opacity:0;max-width:0}}\n"], dependencies: [{ kind: "component", type: FilterSettingsComponent, selector: "app-filter-settings" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
6732
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: LowBandwidthDetectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
6733
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.11", type: LowBandwidthDetectorComponent, isStandalone: true, selector: "app-low-bandwidth-detector", ngImport: i0, template: "@if (isReducedQuality() || isLowBandwidth()) {\n <div\n [class.expanded]=\"isIndicatorExpanded() && isLowBandwidth()\"\n class=\"lbm-indicator freeze-loader\"\n >\n <div (click)=\"toggleIndicator(true)\" class=\"lbm-icon\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 20 20\">\n <path\n fill=\"#85888E\"\n d=\"M7.189 3.605c-.73.145-1.438.35-2.126.614A14.412 14.412 0 0 0 .896 6.666a1.08 1.08 0 0 0-.375.844c0 .34.118.629.354.865s.524.36.865.375c.34.014.65-.09.927-.313a11.89 11.89 0 0 1 3.385-1.916 10.94 10.94 0 0 1 1.235-.375l-.098-2.541ZM7.385 8.708a9.107 9.107 0 0 0-2.906 1.48c-.264.194-.402.464-.416.812-.014.347.104.646.354.896.236.236.524.364.864.385.34.02.664-.073.97-.281.422-.29.878-.53 1.368-.721a2.545 2.545 0 0 1-.166-.814l-.068-1.757ZM12.386 11.267c.094-.25.15-.52.161-.802l.068-1.755a9.019 9.019 0 0 1 2.927 1.52c.264.193.4.46.406.801.007.34-.114.635-.364.885a1.213 1.213 0 0 1-.865.365A1.614 1.614 0 0 1 13.77 12a6.574 6.574 0 0 0-1.385-.733ZM12.713 6.146l.098-2.542c.73.146 1.438.351 2.127.615 1.541.59 2.93 1.406 4.166 2.447.25.223.379.5.386.834.007.333-.115.625-.365.875a1.253 1.253 0 0 1-.864.375c-.34.014-.65-.09-.927-.313a11.892 11.892 0 0 0-3.386-1.916 10.94 10.94 0 0 0-1.235-.375ZM8.813 16.187c.32.32.715.48 1.187.48.472 0 .868-.16 1.188-.48.32-.32.479-.715.479-1.187 0-.473-.16-.868-.48-1.188-.319-.32-.715-.479-1.187-.479-.472 0-.868.16-1.187.48-.32.319-.48.714-.48 1.187 0 .472.16.868.48 1.187Z\"\n />\n <path\n fill=\"#fff\"\n fill-rule=\"evenodd\"\n d=\"M10 .833c-.91 0-1.637.756-1.602 1.665l.304 7.92a1.299 1.299 0 0 0 2.596 0l.305-7.92A1.604 1.604 0 0 0 10 .833ZM8.813 16.187c.32.32.715.48 1.187.48.472 0 .868-.16 1.188-.48.32-.32.479-.715.479-1.187 0-.473-.16-.868-.48-1.188-.319-.32-.715-.479-1.187-.479-.472 0-.868.16-1.187.48-.32.319-.48.714-.48 1.187 0 .472.16.868.48 1.187Z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n </div>\n\n <div\n [class.lbm-message--open]=\"isLowBandwidth() && isIndicatorExpanded()\"\n class=\"lbm-message\"\n >\n <p class=\"lbm-description\">\n Fluid Interactivity Modes were disabled due to an unstable connection.\n\n <button\n (click)=\"openLBMDialog()\"\n [attr.data-testid]=\"'learn-more-lbm'\"\n type=\"button\"\n class=\"lbm-learn-more\"\n >\n Learn more\n </button>\n </p>\n <button\n (click)=\"toggleIndicator(false)\"\n [attr.data-testid]=\"'close-lbm-indicator'\"\n type=\"button\"\n class=\"lbm-close\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 20 20\"\n fill=\"none\"\n >\n <path\n d=\"M10.0001 11.2289L6.68618 14.5428C6.51951 14.7095 6.31818 14.7895 6.08218 14.7828C5.84618 14.7755 5.64485 14.6885 5.47818 14.5218C5.31151 14.3552 5.22818 14.1505 5.22818 13.9078C5.22818 13.6645 5.31151 13.4595 5.47818 13.2928L8.77107 9.99992L5.45718 6.68603C5.29051 6.51936 5.21051 6.3147 5.21718 6.07203C5.22451 5.8287 5.31151 5.6237 5.47818 5.45703C5.64485 5.29036 5.84951 5.20703 6.09218 5.20703C6.33551 5.20703 6.54051 5.29036 6.70718 5.45703L10.0001 8.77092L13.314 5.45703C13.4806 5.29036 13.6853 5.20703 13.928 5.20703C14.1713 5.20703 14.3763 5.29036 14.543 5.45703C14.7096 5.6237 14.793 5.8287 14.793 6.07203C14.793 6.3147 14.7096 6.51936 14.543 6.68603L11.2291 9.99992L14.543 13.3138C14.7096 13.4805 14.793 13.6818 14.793 13.9178C14.793 14.1538 14.7096 14.3552 14.543 14.5218C14.3763 14.6885 14.1713 14.7718 13.928 14.7718C13.6853 14.7718 13.4806 14.6885 13.314 14.5218L10.0001 11.2289Z\"\n fill=\"white\"\n />\n </svg>\n </button>\n </div>\n </div>\n}\n\n@if (isDevMode) {\n <app-filter-settings />\n}\n", styles: [".freeze-loader,.lbm-indicator{--lbmPositionTop: 18px;--lbmPositionLeft: 18px;--lbmPositionRight: 18px;--lbmIndicatorSize: 36px;--lbmIndicatorBorderRadius: 8px;--lbmIndicatorCloseSize: 20px;--lbmPadding: 10px;--lbmTextSize: 13px;--lbmLineHeight: 20px;--lbmTextColor: #ffffff;--lbmBackgroundColor: rgba(17, 24, 39, .7);position:absolute;top:var(--lbmPositionTop);left:var(--lbmPositionLeft);z-index:10;display:flex;justify-content:center;align-items:flex-start;min-width:var(--lbmIndicatorSize);min-height:var(--lbmIndicatorSize);overflow:hidden}.freeze-loader.expanded,.lbm-indicator.expanded{--lbmPositionTop: 8px;--lbmPositionLeft: 8px;--lbmPositionRight: 8px;width:auto;max-width:calc(100% - var(--lbmPositionLeft) - var(--lbmPositionRight));padding:var(--lbmPadding);background:var(--lbmBackgroundColor);-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);border-radius:var(--lbmIndicatorBorderRadius);gap:12px}.freeze-loader .lbm-icon,.lbm-indicator .lbm-icon{display:flex;width:var(--lbmIndicatorSize);height:var(--lbmIndicatorSize);padding:8px;color:#fff;background-color:var(--lbmBackgroundColor);border-radius:50%;flex-shrink:0;cursor:pointer}.freeze-loader .lbm-icon svg,.freeze-loader .lbm-icon img,.lbm-indicator .lbm-icon svg,.lbm-indicator .lbm-icon img{width:100%;height:100%}.freeze-loader .lbm-close,.lbm-indicator .lbm-close{position:absolute;top:0;right:0;width:var(--lbmIndicatorCloseSize);height:var(--lbmIndicatorCloseSize);background-color:transparent;border:none;padding:0;cursor:pointer}.freeze-loader .lbm-message,.lbm-indicator .lbm-message{position:relative;opacity:0;height:0;max-width:0;padding-right:24px;pointer-events:none;visibility:hidden;animation:closeMessage .3s forwards}.freeze-loader .lbm-message--open,.lbm-indicator .lbm-message--open{position:relative;visibility:visible;display:flex;align-items:center;align-self:center;pointer-events:all;gap:12px;max-width:fit-content;height:auto;opacity:1;animation:openMessage .3s forwards}.freeze-loader .lbm-description,.lbm-indicator .lbm-description{margin:0;font-size:var(--lbmTextSize);font-weight:400;line-height:var(--lbmLineHeight);color:var(--lbmTextColor)}.freeze-loader .lbm-learn-more,.lbm-indicator .lbm-learn-more{display:inline-flex;padding:0;font-size:var(--lbmTextSize);font-weight:400;line-height:var(--lbmLineHeight);color:var(--lbmTextColor);text-decoration-line:underline;background-color:transparent;border:none;cursor:pointer}.freeze-loader .lbm-learn-more:hover,.lbm-indicator .lbm-learn-more:hover{text-decoration:none}@keyframes openMessage{0%{opacity:0;max-width:0;height:0}10%{height:auto;max-width:fit-content}to{opacity:1;height:auto;max-width:fit-content}}@keyframes closeMessage{0%{opacity:1;max-width:fit-content}to{opacity:0;max-width:0}}\n"], dependencies: [{ kind: "component", type: FilterSettingsComponent, selector: "app-filter-settings" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
6863
6734
  }
6864
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: LowBandwidthDetectorComponent, decorators: [{
6735
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.11", ngImport: i0, type: LowBandwidthDetectorComponent, decorators: [{
6865
6736
  type: Component,
6866
6737
  args: [{ selector: 'app-low-bandwidth-detector', changeDetection: ChangeDetectionStrategy.OnPush, imports: [FilterSettingsComponent], template: "@if (isReducedQuality() || isLowBandwidth()) {\n <div\n [class.expanded]=\"isIndicatorExpanded() && isLowBandwidth()\"\n class=\"lbm-indicator freeze-loader\"\n >\n <div (click)=\"toggleIndicator(true)\" class=\"lbm-icon\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 20 20\">\n <path\n fill=\"#85888E\"\n d=\"M7.189 3.605c-.73.145-1.438.35-2.126.614A14.412 14.412 0 0 0 .896 6.666a1.08 1.08 0 0 0-.375.844c0 .34.118.629.354.865s.524.36.865.375c.34.014.65-.09.927-.313a11.89 11.89 0 0 1 3.385-1.916 10.94 10.94 0 0 1 1.235-.375l-.098-2.541ZM7.385 8.708a9.107 9.107 0 0 0-2.906 1.48c-.264.194-.402.464-.416.812-.014.347.104.646.354.896.236.236.524.364.864.385.34.02.664-.073.97-.281.422-.29.878-.53 1.368-.721a2.545 2.545 0 0 1-.166-.814l-.068-1.757ZM12.386 11.267c.094-.25.15-.52.161-.802l.068-1.755a9.019 9.019 0 0 1 2.927 1.52c.264.193.4.46.406.801.007.34-.114.635-.364.885a1.213 1.213 0 0 1-.865.365A1.614 1.614 0 0 1 13.77 12a6.574 6.574 0 0 0-1.385-.733ZM12.713 6.146l.098-2.542c.73.146 1.438.351 2.127.615 1.541.59 2.93 1.406 4.166 2.447.25.223.379.5.386.834.007.333-.115.625-.365.875a1.253 1.253 0 0 1-.864.375c-.34.014-.65-.09-.927-.313a11.892 11.892 0 0 0-3.386-1.916 10.94 10.94 0 0 0-1.235-.375ZM8.813 16.187c.32.32.715.48 1.187.48.472 0 .868-.16 1.188-.48.32-.32.479-.715.479-1.187 0-.473-.16-.868-.48-1.188-.319-.32-.715-.479-1.187-.479-.472 0-.868.16-1.187.48-.32.319-.48.714-.48 1.187 0 .472.16.868.48 1.187Z\"\n />\n <path\n fill=\"#fff\"\n fill-rule=\"evenodd\"\n d=\"M10 .833c-.91 0-1.637.756-1.602 1.665l.304 7.92a1.299 1.299 0 0 0 2.596 0l.305-7.92A1.604 1.604 0 0 0 10 .833ZM8.813 16.187c.32.32.715.48 1.187.48.472 0 .868-.16 1.188-.48.32-.32.479-.715.479-1.187 0-.473-.16-.868-.48-1.188-.319-.32-.715-.479-1.187-.479-.472 0-.868.16-1.187.48-.32.319-.48.714-.48 1.187 0 .472.16.868.48 1.187Z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n </div>\n\n <div\n [class.lbm-message--open]=\"isLowBandwidth() && isIndicatorExpanded()\"\n class=\"lbm-message\"\n >\n <p class=\"lbm-description\">\n Fluid Interactivity Modes were disabled due to an unstable connection.\n\n <button\n (click)=\"openLBMDialog()\"\n [attr.data-testid]=\"'learn-more-lbm'\"\n type=\"button\"\n class=\"lbm-learn-more\"\n >\n Learn more\n </button>\n </p>\n <button\n (click)=\"toggleIndicator(false)\"\n [attr.data-testid]=\"'close-lbm-indicator'\"\n type=\"button\"\n class=\"lbm-close\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 20 20\"\n fill=\"none\"\n >\n <path\n d=\"M10.0001 11.2289L6.68618 14.5428C6.51951 14.7095 6.31818 14.7895 6.08218 14.7828C5.84618 14.7755 5.64485 14.6885 5.47818 14.5218C5.31151 14.3552 5.22818 14.1505 5.22818 13.9078C5.22818 13.6645 5.31151 13.4595 5.47818 13.2928L8.77107 9.99992L5.45718 6.68603C5.29051 6.51936 5.21051 6.3147 5.21718 6.07203C5.22451 5.8287 5.31151 5.6237 5.47818 5.45703C5.64485 5.29036 5.84951 5.20703 6.09218 5.20703C6.33551 5.20703 6.54051 5.29036 6.70718 5.45703L10.0001 8.77092L13.314 5.45703C13.4806 5.29036 13.6853 5.20703 13.928 5.20703C14.1713 5.20703 14.3763 5.29036 14.543 5.45703C14.7096 5.6237 14.793 5.8287 14.793 6.07203C14.793 6.3147 14.7096 6.51936 14.543 6.68603L11.2291 9.99992L14.543 13.3138C14.7096 13.4805 14.793 13.6818 14.793 13.9178C14.793 14.1538 14.7096 14.3552 14.543 14.5218C14.3763 14.6885 14.1713 14.7718 13.928 14.7718C13.6853 14.7718 13.4806 14.6885 13.314 14.5218L10.0001 11.2289Z\"\n fill=\"white\"\n />\n </svg>\n </button>\n </div>\n </div>\n}\n\n@if (isDevMode) {\n <app-filter-settings />\n}\n", styles: [".freeze-loader,.lbm-indicator{--lbmPositionTop: 18px;--lbmPositionLeft: 18px;--lbmPositionRight: 18px;--lbmIndicatorSize: 36px;--lbmIndicatorBorderRadius: 8px;--lbmIndicatorCloseSize: 20px;--lbmPadding: 10px;--lbmTextSize: 13px;--lbmLineHeight: 20px;--lbmTextColor: #ffffff;--lbmBackgroundColor: rgba(17, 24, 39, .7);position:absolute;top:var(--lbmPositionTop);left:var(--lbmPositionLeft);z-index:10;display:flex;justify-content:center;align-items:flex-start;min-width:var(--lbmIndicatorSize);min-height:var(--lbmIndicatorSize);overflow:hidden}.freeze-loader.expanded,.lbm-indicator.expanded{--lbmPositionTop: 8px;--lbmPositionLeft: 8px;--lbmPositionRight: 8px;width:auto;max-width:calc(100% - var(--lbmPositionLeft) - var(--lbmPositionRight));padding:var(--lbmPadding);background:var(--lbmBackgroundColor);-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);border-radius:var(--lbmIndicatorBorderRadius);gap:12px}.freeze-loader .lbm-icon,.lbm-indicator .lbm-icon{display:flex;width:var(--lbmIndicatorSize);height:var(--lbmIndicatorSize);padding:8px;color:#fff;background-color:var(--lbmBackgroundColor);border-radius:50%;flex-shrink:0;cursor:pointer}.freeze-loader .lbm-icon svg,.freeze-loader .lbm-icon img,.lbm-indicator .lbm-icon svg,.lbm-indicator .lbm-icon img{width:100%;height:100%}.freeze-loader .lbm-close,.lbm-indicator .lbm-close{position:absolute;top:0;right:0;width:var(--lbmIndicatorCloseSize);height:var(--lbmIndicatorCloseSize);background-color:transparent;border:none;padding:0;cursor:pointer}.freeze-loader .lbm-message,.lbm-indicator .lbm-message{position:relative;opacity:0;height:0;max-width:0;padding-right:24px;pointer-events:none;visibility:hidden;animation:closeMessage .3s forwards}.freeze-loader .lbm-message--open,.lbm-indicator .lbm-message--open{position:relative;visibility:visible;display:flex;align-items:center;align-self:center;pointer-events:all;gap:12px;max-width:fit-content;height:auto;opacity:1;animation:openMessage .3s forwards}.freeze-loader .lbm-description,.lbm-indicator .lbm-description{margin:0;font-size:var(--lbmTextSize);font-weight:400;line-height:var(--lbmLineHeight);color:var(--lbmTextColor)}.freeze-loader .lbm-learn-more,.lbm-indicator .lbm-learn-more{display:inline-flex;padding:0;font-size:var(--lbmTextSize);font-weight:400;line-height:var(--lbmLineHeight);color:var(--lbmTextColor);text-decoration-line:underline;background-color:transparent;border:none;cursor:pointer}.freeze-loader .lbm-learn-more:hover,.lbm-indicator .lbm-learn-more:hover{text-decoration:none}@keyframes openMessage{0%{opacity:0;max-width:0;height:0}10%{height:auto;max-width:fit-content}to{opacity:1;height:auto;max-width:fit-content}}@keyframes closeMessage{0%{opacity:1;max-width:fit-content}to{opacity:0;max-width:0}}\n"] }]
6867
6738
  }] });
@@ -6889,6 +6760,7 @@ function provideUnrealModule() {
6889
6760
  StreamStatusTelemetryService2,
6890
6761
  FpsMonitorService,
6891
6762
  WebRtcPlayerService,
6763
+ LoadingProgressService,
6892
6764
  provideEnvironmentInitializer(() => {
6893
6765
  inject(AggregatorService);
6894
6766
  inject(InputService);
@@ -6907,5 +6779,5 @@ function provideUnrealModule() {
6907
6779
  * Generated bundle index. Do not edit.
6908
6780
  */
6909
6781
 
6910
- export { AFKService, AfkTimeoutModalComponent, AggregatorService, AnalyticsService, AnswerHandler, CONSOLE_COMMAND_DISABLE_MESSAGES, CONSOLE_COMMAND_ENABLE_MESSAGES, CONSOLE_COMMAND_PIXEL_QUALITY, CommandTelemetryService, CommandsEffects, ConfigHandler, ConsoleExtensionsService, ControlScheme, DATA_CHANNEL_CONNECTION_TIMEOUT, DEBOUNCE_TO_MANY_RESIZE_CALLS, DEFAULT_AFK_TIMEOUT, DEFAULT_AFK_TIMEOUT_PERIOD, DEFAULT_RECONNECT_DELAY_MS, DEFAULT_RECONNECT_ENABLED, DEFAULT_RECONNECT_MAX_ATTEMPTS, DEFAULT_RECONNECT_ON_DATACHANNEL_CLOSE, DEFAULT_RECONNECT_ON_ICE_FAILURE, DataFlowMonitor, DevModeService, DisconnectReason, FULL_HD_HEIGHT, FULL_HD_WIDTH, FileHandlerService, FileReceiverService, FilterSettingsComponent, FpsMonitorService, FreezeFrameComponent, FreezeFrameService, IceCandidateHandler, ImageLoadingSrcComponent, InputOptions, InputService, InstanceReadyHandler, InstanceReservedHandler, IntroSrcComponent, KalmanFilter1D, LatencyTimings, LowBandwidthDetectorComponent, LowBandwidthModalComponent, MINIMAL_FPS, MessageType, MouseButton, MouseButtonsMask, OnCloseHandler, OnErrorHandler, OnMessageHandler, OnOpenHandler, OrchestrationMessageTypes, POLLING_TIME, PingHandler, PlayerCountHandler, RegionsPingService, SAME_SIZE_THRESHOLD, SCREEN_LOCKER_CONTAINER_ID, SIGNALLING_PERCENT_VALUE, SSInfoHandler, STREAMING_VIDEO_ID, SafePipe, SignallingService, SpecialKeyCodes, StatGraphComponent, StreamStatusTelemetryService, StreamStatusTelemetryService2, SubService, ToClientMessage, UNREAL_CONFIG, UnrealCallbackService, UnrealCommunicatorService, UnrealEffects, UnrealSceneComponent, UnrealStatusMessage, VideoService, VideoStatsComponent, WSCloseCode_CIRRUS_ABNORMAL_CLOSURE, WSCloseCode_CIRRUS_MAX_PLAYERS_ERROR, WSCloseCode_CIRRUS_PLAYER_DISCONNECTED, WSCloseCode_CIRRUS_STREAMER_KIKED_PLAYER, WSCloseCode_FORCE_CIRRUS_CLOSE, WSCloseCode_NORMAL_AFK_TIMEOUT, WSCloseCode_NORMAL_CLOSURE, WSCloseCode_NORMAL_MANUAL_DISCONNECT, WSCloseCode_UNKNOWN, WSCloseCodes, WS_OPEN_STATE, WS_TIMEOUT, WebRtcPlayerService, WebrtcErrorModalComponent, abortEstablishingConnection, answerSent, changeLowBandwidth, changeStatusMainVideoOnScene, changeStreamResolutionAction, changeStreamResolutionSuccessAction, commandCompleted, commandStarted, commandsFeature, commandsReducer, dataChannelConnected, dataChannelReady, decodeData, destroyRemoteConnections, destroyUnrealScene, disconnectStream, dispatchResize, dropConnection, dropDC, dropPC, floatToSmoothPercents, forceResizeViewport, fromResizeObserver, getActiveUrl, getImageFromVideoStream, getResizeStudioValues, getResizeStudioValuesOld, getResizeValuesUniversal, getResizeValuesUniversalOld, getRtcErrorMessage, iceConnectionFailed, initSignalling, instanceReady, keepMaxUntilReset, makeEven, pollingEnded, pollingStarted, provideUnrealModule, qpToQuality, reconnectPeer, reconnectPeerFailed, reconnectPeerSuccess, regionPingFetchEnd, regionPingFetchStart, regionResolved, resetAfk, resetConfig, resetDataChannelForReconnect, resetIntroSrc, resetWarnTimeout, saveAnalyticsEvent, selectCirrusConnectionId, selectClientAndViewIds, selectCommandProgress, selectCommandsInProgress, selectFreezeFrameCombinedDataUrl, selectFreezeFrameDataUrl, selectFreezeFrameDataUrlFromVideo, selectFreezeFrameProgressMessageFromVideo, selectIsAutostart, selectIsExistMatchUrls, selectIsFreezeFrameLoading, selectIsLoaderScreenVisible, selectIsVideoPlayingAndDataChannelConnected, selectShowLoader, selectShowReconnectPopup, selectSignalingParameters, selectStreamConfig, selectTotalProgress, selectWarnTimeout, setAfkTimerCountdown, setAfkTimerHide, setAfkTimerVisible, setAwsInstance, setCirrusConnected, setCirrusDisconnected, setConfig, setDataChannelConnected, setEstablishingConnection, setFreezeFrame, setFreezeFrameFromVideo, setIntroImageSrc, setIntroVideoSrc, setKalmanSettings, setLoadingImageSrc, setLoopBackCommandIsCompleted, setMaxFps, setOrchestrationContext, setOrchestrationMessage, setOrchestrationParameters, setOrchestrationProgress, setSignalingName, setStatusMessage, setStatusPercentSignallingServer, setStreamClientCompanyId, setStreamViewId, setUnrealPlaywrightConfig, setViewportNotReady, setViewportReady, showUnrealErrorMessage, smoothTransition, startStream, trackMixpanelEvent, trackTelemetryData, unrealCommandCallback, unrealFeature, unrealReducer, updateCirrusInfo, videoAdaptedToContainer, videoStreamInitialized };
6782
+ export { AFKService, AfkTimeoutModalComponent, AggregatorService, AnalyticsService, AnswerHandler, CONSOLE_COMMAND_DISABLE_MESSAGES, CONSOLE_COMMAND_ENABLE_MESSAGES, CONSOLE_COMMAND_PIXEL_QUALITY, CommandTelemetryService, CommandsEffects, ConfigHandler, ConsoleExtensionsService, ControlScheme, DATA_CHANNEL_CONNECTION_TIMEOUT, DEBOUNCE_TO_MANY_RESIZE_CALLS, DEFAULT_AFK_TIMEOUT, DEFAULT_AFK_TIMEOUT_PERIOD, DEFAULT_RECONNECT_DELAY_MS, DEFAULT_RECONNECT_ENABLED, DEFAULT_RECONNECT_MAX_ATTEMPTS, DEFAULT_RECONNECT_ON_DATACHANNEL_CLOSE, DEFAULT_RECONNECT_ON_ICE_FAILURE, DESTROY_DISCONNECT_REASONS, DataFlowMonitor, DevModeService, DisconnectReason, FULL_HD_HEIGHT, FULL_HD_WIDTH, FileHandlerService, FileReceiverService, FilterSettingsComponent, FpsMonitorService, FreezeFrameComponent, FreezeFrameService, IceCandidateHandler, ImageLoadingSrcComponent, InputOptions, InputService, InstanceReadyHandler, InstanceReservedHandler, IntroSrcComponent, KalmanFilter1D, LatencyTimings, LoadingProgressService, LowBandwidthDetectorComponent, LowBandwidthModalComponent, MINIMAL_FPS, MIN_CONTAINER_SIZE, MessageType, MouseButton, MouseButtonsMask, OnCloseHandler, OnErrorHandler, OnMessageHandler, OnOpenHandler, OrchestrationMessageTypes, POLLING_TIME, PingHandler, PlayerCountHandler, RegionsPingService, SAME_SIZE_THRESHOLD, SCREEN_LOCKER_CONTAINER_ID, SIGNALLING_PERCENT_VALUE, SSInfoHandler, STREAMING_AUDIO_ID, STREAMING_VIDEO_ID, SafePipe, SignallingService, SpecialKeyCodes, StatGraphComponent, StreamStatusTelemetryService, StreamStatusTelemetryService2, SubService, ToClientMessage, UNREAL_CONFIG, UnrealCallbackService, UnrealCommunicatorService, UnrealEffects, UnrealSceneComponent, UnrealStatusMessage, VideoService, VideoStatsComponent, WSCloseCode_CIRRUS_ABNORMAL_CLOSURE, WSCloseCode_CIRRUS_MAX_PLAYERS_ERROR, WSCloseCode_CIRRUS_PLAYER_DISCONNECTED, WSCloseCode_CIRRUS_STREAMER_KIKED_PLAYER, WSCloseCode_FORCE_CIRRUS_CLOSE, WSCloseCode_NORMAL_AFK_TIMEOUT, WSCloseCode_NORMAL_CLOSURE, WSCloseCode_NORMAL_MANUAL_DISCONNECT, WSCloseCode_UNKNOWN, WSCloseCodes, WS_OPEN_STATE, WS_TIMEOUT, WebRtcPlayerService, WebrtcErrorModalComponent, abortEstablishingConnection, answerSent, changeStatusMainVideoOnScene, changeStreamResolutionAction, changeStreamResolutionSuccessAction, commandCompleted, commandStarted, commandsFeature, commandsReducer, dataChannelConnected, dataChannelReady, decodeData, destroyRemoteConnections, destroyUnrealScene, disconnectStream, dispatchResize, dropConnection, dropDC, dropPC, floatToSmoothPercents, forceResizeViewport, fromResizeObserver, getActiveUrl, getImageFromVideoStream, getResizeStudioValues, getResizeValuesUniversal, getRtcErrorMessage, iceConnectionFailed, initSignalling, instanceReady, keepMaxUntilReset, makeEven, pollingEnded, pollingStarted, provideUnrealModule, qpToQuality, reconnectPeer, reconnectPeerFailed, reconnectPeerSuccess, regionPingFetchEnd, regionPingFetchStart, regionResolved, resetAfk, resetConfig, resetDataChannelForReconnect, resetIntroSrc, resetUserInactivityTimeout, resetWarnTimeout, saveAnalyticsEvent, selectCirrusConnectionId, selectClientAndViewIds, selectCommandProgress, selectCommandsInProgress, selectFreezeFrameCombinedDataUrl, selectFreezeFrameDataUrl, selectFreezeFrameDataUrlFromVideo, selectFreezeFrameProgressMessageFromVideo, selectIsAutostart, selectIsExistMatchUrls, selectIsFreezeFrameLoading, selectIsFullyConnected, selectIsLoaderScreenVisible, selectIsVideoPlayingAndDataChannelConnected, selectShowLoader, selectShowReconnectPopup, selectSignalingParameters, selectStreamConfig, selectTotalProgress, selectWarnTimeout, setAfkTimerCountdown, setAfkTimerHide, setAfkTimerVisible, setAwsInstance, setCirrusConnected, setCirrusDisconnected, setConfig, setEstablishingConnection, setFreezeFrame, setFreezeFrameFromVideo, setIntroImageSrc, setIntroVideoSrc, setLoadingImageSrc, setLoopBackCommandIsCompleted, setMaxFps, setOrchestrationContext, setOrchestrationMessage, setOrchestrationParameters, setOrchestrationProgress, setSignalingName, setStatusMessage, setStatusPercentSignallingServer, setStreamClientCompanyId, setStreamViewId, setUnrealPlaywrightConfig, setUserInactivityTimeout, setViewportNotReady, setViewportReady, showLoadingProgress, showResumeStreamPopup, showUnrealErrorMessage, showUserInactivityTimeoutPopup, smoothTransition, startStream, trackMixpanelEvent, trackTelemetryData, unrealCommandCallback, unrealFeature, unrealReducer, updateCirrusInfo, videoAdaptedToContainer, videoStreamInitialized };
6911
6783
  //# sourceMappingURL=3dsource-angular-unreal-module.mjs.map