@3dsource/angular-unreal-module 0.0.177 → 0.0.182

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.
@@ -4,10 +4,10 @@ import { NgTemplateOutlet, NgOptimizedImage, JsonPipe } from '@angular/common';
4
4
  import { createAction, props, createReducer, on, createFeature, createSelector, Store, provideState } from '@ngrx/store';
5
5
  import { Actions, ofType, createEffect, provideEffects } from '@ngrx/effects';
6
6
  import { Subject, skip, share, timer, of, take, defer, switchMap, from, throwError, Observable, EMPTY, retry, merge, timeout as timeout$1, repeat, BehaviorSubject, interval, map as map$1, takeUntil as takeUntil$1, fromEvent, tap as tap$1, throttleTime, delay, concat, debounceTime, mergeMap, NEVER, ignoreElements, startWith, scan, timestamp, pairwise, concatMap, animationFrameScheduler, shareReplay, firstValueFrom, asyncScheduler, filter as filter$1, withLatestFrom as withLatestFrom$1, Subscription, bufferTime, combineLatestWith } from 'rxjs';
7
- import { whereNot, lerp, clampf, Falsy, generateUuid, Truthy, Logger, tapLog, Semaphore, COLOR_CODES, getCanvasCached, getSnapshot, fitIntoRectangle, where, KeyboardNumericCode, InvertedKeyMap, HEXtoRGB, RGBtoHSV, inverseLerp, HSVtoRGB, RGBtoHEX, fpIsASameAsB } from '@3dsource/utils';
8
7
  import { UNREAL_CONFIG } from '@3dsource/angular-unreal-module/config';
9
8
  export { UNREAL_CONFIG } from '@3dsource/angular-unreal-module/config';
10
- import { filter, takeUntil, catchError, tap, map, timeout, finalize, first, switchMap as switchMap$1, distinctUntilChanged, withLatestFrom, exhaustMap, takeWhile, debounceTime as debounceTime$1, take as take$1, startWith as startWith$1 } from 'rxjs/operators';
9
+ import { tap, filter, takeUntil, catchError, map, timeout, finalize, first, switchMap as switchMap$1, distinctUntilChanged, withLatestFrom, exhaustMap, takeWhile, debounceTime as debounceTime$1, take as take$1, startWith as startWith$1 } from 'rxjs/operators';
10
+ import { v4 } from 'uuid';
11
11
  import { DialogRef, DIALOG_DATA, Dialog } from '@angular/cdk/dialog';
12
12
  import { HttpClient } from '@angular/common/http';
13
13
  import { ScrollStrategyOptions } from '@angular/cdk/overlay';
@@ -36,10 +36,10 @@ class UnrealButtonComponent {
36
36
  event.preventDefault();
37
37
  this.clicked.emit(event);
38
38
  }
39
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: UnrealButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
40
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.2", type: UnrealButtonComponent, isStandalone: true, selector: "app-unreal-button", inputs: { type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, weight: { classPropertyName: "weight", publicName: "weight", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, fullWidth: { classPropertyName: "fullWidth", publicName: "fullWidth", isSignal: true, isRequired: false, transformFunction: null }, isDisabled: { classPropertyName: "isDisabled", publicName: "isDisabled", isSignal: true, isRequired: false, transformFunction: null }, testID: { classPropertyName: "testID", publicName: "data-testid", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { clicked: "clicked" }, ngImport: i0, template: "<button\n (click)=\"handleClick($event)\"\n [attr.data-testid]=\"testID() || null\"\n [attr.aria-label]=\"ariaLabel()\"\n [class.unreal-button--primary]=\"weight() === 'primary'\"\n [class.unreal-button--lg]=\"size() === 'lg'\"\n [class.unreal-button--full-width]=\"fullWidth()\"\n [disabled]=\"isDisabled()\"\n [type]=\"type()\"\n class=\"unreal-button\"\n>\n <span><ng-content></ng-content></span>\n</button>\n", styles: [".unreal-button{--unrealButtonHeight: 36px;--unrealButtonPaddings: 0 12px;--unrealButtonBorderRadius: 6px;--unrealButtonFontSize: var(--src-font-size-sm, 14px);--unrealButtonLineHeight: var(--src-font-line-sm, 20px);--unrealButtonWidth: auto;--unrealButtonBgColor: transparent;--unrealButtonBorderColor: var(--src-border-button-basic, #e5e7eb);--unrealButtonBorder: 1px solid var(--unrealButtonBorderColor);--unrealButtonFontColor: var(--src-text-ui-secondary-main, #111827);--unrealButtonFontWeight: var(--src-font-weight-medium, 500);--unrealButtonFocusRingSize: 4px;--unrealButtonFocusRingColor: var( --src-shadow-light, rgba(148, 163, 184, .16) );--unrealButtonBoxShadow: 0 -1px 0 0 var(--src-shadow-hard, rgba(71, 85, 105, .24)) inset, 0 1px 0 0 var(--src-shadow-light, rgba(148, 163, 184, .16));position:relative;display:flex;justify-content:center;align-items:center;width:var(--unrealButtonWidth);height:var(--unrealButtonHeight);padding:var(--unrealButtonPaddings);font-weight:var(--unrealButtonFontWeight);font-size:var(--unrealButtonFontSize);font-style:normal;line-height:var(--unrealButtonLineHeight);text-align:center;color:var(--unrealButtonFontColor);letter-spacing:0;white-space:nowrap;border:none;background-color:var(--unrealButtonBgColor);border-radius:var(--unrealButtonBorderRadius);box-shadow:var(--unrealButtonBoxShadow);cursor:pointer}.unreal-button:after{content:\"\";position:absolute;inset:0;border-radius:var(--unrealButtonBorderRadius);border:var(--unrealButtonBorder)}.unreal-button>span{display:flex;justify-content:center;align-items:center;gap:var(--src-gap-md, 6px);width:auto;font-size:inherit;line-height:inherit;color:inherit;font-weight:inherit}.unreal-button:disabled{--unrealButtonBgColor: var( --src-ui-accent-disabled, rgba(148, 163, 184, .24) );--unrealButtonFontColor: var(--src-text-ui-secondary-disabled, #9ca3af);--unrealButtonBoxShadow: none;--unrealButtonBorder: none;--unrealButtonBorderColor: transparent;cursor:default;pointer-events:none}.unreal-button:focus-visible{--unrealButtonBorderColor: var(--src-border-button-basic-hover, #9ca3af);outline:none;box-shadow:0 0 0 var(--unrealButtonFocusRingSize) var(--unrealButtonFocusRingColor)}@media(pointer:fine){.unreal-button:hover{--unrealButtonBgColor: var( --src-ui-secondary-default-hover, rgba(148, 163, 184, .08) );--unrealButtonBorderColor: var(--src-border-button-basic-hover, #9ca3af)}}.unreal-button--primary{--unrealButtonBgColor: var(--unreal-accent, var(--src-ui-accent-default, #017bff));--unrealButtonBorder: none;--unrealButtonFontColor: var(--unreal-accent-text, var(--src-text-ui-primary-main, #fff));--unrealButtonFocusRingColor: var( --src-shadow-accent-light, rgba(1, 123, 255, .16) )}@media(pointer:fine){.unreal-button--primary:hover{--unrealButtonBgColor: var(--unreal-accent-hover, var(--src-ui-accent-default-hover, #016fe6));--unrealButtonFontColor: var(--unreal-accent-text, var(--src-text-ui-primary-main, #fff))}}.unreal-button--lg{--unrealButtonHeight: 44px;--unrealButtonPaddings: 0 16px;--unrealButtonBorderRadius: 8px;--unrealButtonFontSize: var(--src-font-size-base, 16px);--unrealButtonLineHeight: var(--src-font-line-base, 24px);--unrealButtonFocusRingSize: 6px;--unrealButtonBoxShadow: 0 -1px 0 0 var(--src-shadow-hard, rgba(71, 85, 105, .24)) inset, 0 2px 0 0 var(--src-shadow-light, rgba(148, 163, 184, .16))}.unreal-button--lg>span{gap:var(--src-gap-md, 8px)}.unreal-button--full-width{--unrealButtonWidth: 100%}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
39
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: UnrealButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
40
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.3", type: UnrealButtonComponent, isStandalone: true, selector: "app-unreal-button", inputs: { type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, weight: { classPropertyName: "weight", publicName: "weight", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, fullWidth: { classPropertyName: "fullWidth", publicName: "fullWidth", isSignal: true, isRequired: false, transformFunction: null }, isDisabled: { classPropertyName: "isDisabled", publicName: "isDisabled", isSignal: true, isRequired: false, transformFunction: null }, testID: { classPropertyName: "testID", publicName: "data-testid", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { clicked: "clicked" }, ngImport: i0, template: "<button\n (click)=\"handleClick($event)\"\n [attr.data-testid]=\"testID() || null\"\n [attr.aria-label]=\"ariaLabel()\"\n [class.unreal-button--primary]=\"weight() === 'primary'\"\n [class.unreal-button--lg]=\"size() === 'lg'\"\n [class.unreal-button--full-width]=\"fullWidth()\"\n [disabled]=\"isDisabled()\"\n [type]=\"type()\"\n class=\"unreal-button\"\n>\n <span><ng-content></ng-content></span>\n</button>\n", styles: [".unreal-button{--unrealButtonHeight: 36px;--unrealButtonPaddings: 0 12px;--unrealButtonBorderRadius: 6px;--unrealButtonFontSize: var(--src-font-size-sm, 14px);--unrealButtonLineHeight: var(--src-font-line-sm, 20px);--unrealButtonWidth: auto;--unrealButtonBgColor: transparent;--unrealButtonBorderColor: var(--src-border-button-basic, #e5e7eb);--unrealButtonBorder: 1px solid var(--unrealButtonBorderColor);--unrealButtonFontColor: var(--src-text-ui-secondary-main, #111827);--unrealButtonFontWeight: var(--src-font-weight-medium, 500);--unrealButtonFocusRingSize: 4px;--unrealButtonFocusRingColor: var( --src-shadow-light, rgba(148, 163, 184, .16) );--unrealButtonBoxShadow: 0 -1px 0 0 var(--src-shadow-hard, rgba(71, 85, 105, .24)) inset, 0 1px 0 0 var(--src-shadow-light, rgba(148, 163, 184, .16));position:relative;display:flex;justify-content:center;align-items:center;width:var(--unrealButtonWidth);height:var(--unrealButtonHeight);padding:var(--unrealButtonPaddings);font-weight:var(--unrealButtonFontWeight);font-size:var(--unrealButtonFontSize);font-style:normal;line-height:var(--unrealButtonLineHeight);text-align:center;color:var(--unrealButtonFontColor);letter-spacing:0;white-space:nowrap;border:none;background-color:var(--unrealButtonBgColor);border-radius:var(--unrealButtonBorderRadius);box-shadow:var(--unrealButtonBoxShadow);cursor:pointer}.unreal-button:after{content:\"\";position:absolute;inset:0;border-radius:var(--unrealButtonBorderRadius);border:var(--unrealButtonBorder)}.unreal-button>span{display:flex;justify-content:center;align-items:center;gap:var(--src-gap-md, 6px);width:auto;font-size:inherit;line-height:inherit;color:inherit;font-weight:inherit}.unreal-button:disabled{--unrealButtonBgColor: var( --src-ui-accent-disabled, rgba(148, 163, 184, .24) );--unrealButtonFontColor: var(--src-text-ui-secondary-disabled, #9ca3af);--unrealButtonBoxShadow: none;--unrealButtonBorder: none;--unrealButtonBorderColor: transparent;cursor:default;pointer-events:none}.unreal-button:focus-visible{--unrealButtonBorderColor: var(--src-border-button-basic-hover, #9ca3af);outline:none;box-shadow:0 0 0 var(--unrealButtonFocusRingSize) var(--unrealButtonFocusRingColor)}@media(pointer:fine){.unreal-button:hover{--unrealButtonBgColor: var( --src-ui-secondary-default-hover, rgba(148, 163, 184, .08) );--unrealButtonBorderColor: var(--src-border-button-basic-hover, #9ca3af)}}.unreal-button--primary{--unrealButtonBgColor: var(--unreal-accent, var(--src-ui-accent-default, #017bff));--unrealButtonBorder: none;--unrealButtonFontColor: var(--unreal-accent-text, var(--src-text-ui-primary-main, #fff));--unrealButtonFocusRingColor: var( --src-shadow-accent-light, rgba(1, 123, 255, .16) )}@media(pointer:fine){.unreal-button--primary:hover{--unrealButtonBgColor: var(--unreal-accent-hover, var(--src-ui-accent-default-hover, #016fe6));--unrealButtonFontColor: var(--unreal-accent-text, var(--src-text-ui-primary-main, #fff))}}.unreal-button--lg{--unrealButtonHeight: 44px;--unrealButtonPaddings: 0 16px;--unrealButtonBorderRadius: 8px;--unrealButtonFontSize: var(--src-font-size-base, 16px);--unrealButtonLineHeight: var(--src-font-line-base, 24px);--unrealButtonFocusRingSize: 6px;--unrealButtonBoxShadow: 0 -1px 0 0 var(--src-shadow-hard, rgba(71, 85, 105, .24)) inset, 0 2px 0 0 var(--src-shadow-light, rgba(148, 163, 184, .16))}.unreal-button--lg>span{gap:var(--src-gap-md, 8px)}.unreal-button--full-width{--unrealButtonWidth: 100%}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
41
41
  }
42
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: UnrealButtonComponent, decorators: [{
42
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: UnrealButtonComponent, decorators: [{
43
43
  type: Component,
44
44
  args: [{ selector: 'app-unreal-button', changeDetection: ChangeDetectionStrategy.OnPush, template: "<button\n (click)=\"handleClick($event)\"\n [attr.data-testid]=\"testID() || null\"\n [attr.aria-label]=\"ariaLabel()\"\n [class.unreal-button--primary]=\"weight() === 'primary'\"\n [class.unreal-button--lg]=\"size() === 'lg'\"\n [class.unreal-button--full-width]=\"fullWidth()\"\n [disabled]=\"isDisabled()\"\n [type]=\"type()\"\n class=\"unreal-button\"\n>\n <span><ng-content></ng-content></span>\n</button>\n", styles: [".unreal-button{--unrealButtonHeight: 36px;--unrealButtonPaddings: 0 12px;--unrealButtonBorderRadius: 6px;--unrealButtonFontSize: var(--src-font-size-sm, 14px);--unrealButtonLineHeight: var(--src-font-line-sm, 20px);--unrealButtonWidth: auto;--unrealButtonBgColor: transparent;--unrealButtonBorderColor: var(--src-border-button-basic, #e5e7eb);--unrealButtonBorder: 1px solid var(--unrealButtonBorderColor);--unrealButtonFontColor: var(--src-text-ui-secondary-main, #111827);--unrealButtonFontWeight: var(--src-font-weight-medium, 500);--unrealButtonFocusRingSize: 4px;--unrealButtonFocusRingColor: var( --src-shadow-light, rgba(148, 163, 184, .16) );--unrealButtonBoxShadow: 0 -1px 0 0 var(--src-shadow-hard, rgba(71, 85, 105, .24)) inset, 0 1px 0 0 var(--src-shadow-light, rgba(148, 163, 184, .16));position:relative;display:flex;justify-content:center;align-items:center;width:var(--unrealButtonWidth);height:var(--unrealButtonHeight);padding:var(--unrealButtonPaddings);font-weight:var(--unrealButtonFontWeight);font-size:var(--unrealButtonFontSize);font-style:normal;line-height:var(--unrealButtonLineHeight);text-align:center;color:var(--unrealButtonFontColor);letter-spacing:0;white-space:nowrap;border:none;background-color:var(--unrealButtonBgColor);border-radius:var(--unrealButtonBorderRadius);box-shadow:var(--unrealButtonBoxShadow);cursor:pointer}.unreal-button:after{content:\"\";position:absolute;inset:0;border-radius:var(--unrealButtonBorderRadius);border:var(--unrealButtonBorder)}.unreal-button>span{display:flex;justify-content:center;align-items:center;gap:var(--src-gap-md, 6px);width:auto;font-size:inherit;line-height:inherit;color:inherit;font-weight:inherit}.unreal-button:disabled{--unrealButtonBgColor: var( --src-ui-accent-disabled, rgba(148, 163, 184, .24) );--unrealButtonFontColor: var(--src-text-ui-secondary-disabled, #9ca3af);--unrealButtonBoxShadow: none;--unrealButtonBorder: none;--unrealButtonBorderColor: transparent;cursor:default;pointer-events:none}.unreal-button:focus-visible{--unrealButtonBorderColor: var(--src-border-button-basic-hover, #9ca3af);outline:none;box-shadow:0 0 0 var(--unrealButtonFocusRingSize) var(--unrealButtonFocusRingColor)}@media(pointer:fine){.unreal-button:hover{--unrealButtonBgColor: var( --src-ui-secondary-default-hover, rgba(148, 163, 184, .08) );--unrealButtonBorderColor: var(--src-border-button-basic-hover, #9ca3af)}}.unreal-button--primary{--unrealButtonBgColor: var(--unreal-accent, var(--src-ui-accent-default, #017bff));--unrealButtonBorder: none;--unrealButtonFontColor: var(--unreal-accent-text, var(--src-text-ui-primary-main, #fff));--unrealButtonFocusRingColor: var( --src-shadow-accent-light, rgba(1, 123, 255, .16) )}@media(pointer:fine){.unreal-button--primary:hover{--unrealButtonBgColor: var(--unreal-accent-hover, var(--src-ui-accent-default-hover, #016fe6));--unrealButtonFontColor: var(--unreal-accent-text, var(--src-text-ui-primary-main, #fff))}}.unreal-button--lg{--unrealButtonHeight: 44px;--unrealButtonPaddings: 0 16px;--unrealButtonBorderRadius: 8px;--unrealButtonFontSize: var(--src-font-size-base, 16px);--unrealButtonLineHeight: var(--src-font-line-base, 24px);--unrealButtonFocusRingSize: 6px;--unrealButtonBoxShadow: 0 -1px 0 0 var(--src-shadow-hard, rgba(71, 85, 105, .24)) inset, 0 2px 0 0 var(--src-shadow-light, rgba(148, 163, 184, .16))}.unreal-button--lg>span{gap:var(--src-gap-md, 8px)}.unreal-button--full-width{--unrealButtonWidth: 100%}\n"] }]
45
45
  }], propDecorators: { type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], weight: [{ type: i0.Input, args: [{ isSignal: true, alias: "weight", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], fullWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "fullWidth", required: false }] }], isDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "isDisabled", required: false }] }], testID: [{ type: i0.Input, args: [{ isSignal: true, alias: "data-testid", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], clicked: [{ type: i0.Output, args: ["clicked"] }] } });
@@ -307,6 +307,179 @@ const MouseButtonsMask = {
307
307
  FifthButton: 16, // Browser Forward button.
308
308
  };
309
309
 
310
+ /**
311
+ * `KeyboardEvent.code` → the Windows virtual-key code Unreal expects.
312
+ *
313
+ * Only keys whose `event.keyCode` cannot be trusted belong here. The browser
314
+ * already resolves `keyCode` correctly for everything else, so the table exists
315
+ * for two cases: browsers that disagree on a value (Gecko reports the
316
+ * punctuation keys by ASCII — `;` as 59, `=` as 61, `-` as 173) and keys Unreal
317
+ * numbers differently from the DOM (the right-hand modifiers, the Cmd/Win keys).
318
+ *
319
+ * The numpad digits, `NumpadDecimal` and `NumpadEnter` are the exception to the
320
+ * rule above, and the reason it is worded as a rule. A numpad key reports the
321
+ * same `code` in both NumLock states but a different virtual key — `Numpad4` is
322
+ * 37/`VK_LEFT` with NumLock off and 100/`VK_NUMPAD4` with it on. Their entries
323
+ * here hold the NumLock-*off* values, so an entry wins over the value the
324
+ * browser resolved and pins that state: with NumLock on the engine is told
325
+ * `VK_LEFT`. Read `event.keyCode` directly if you need the other state.
326
+ * `NumpadAdd`/`Subtract`/`Multiply`/`Divide` carry no such caveat — they do not
327
+ * change with NumLock.
328
+ *
329
+ * `SpecialKeyCodes` in `./buttons-codes` repeats seven of these entries with
330
+ * the same values. The two are kept apart because both are public API; merging
331
+ * them would be a breaking change.
332
+ *
333
+ * `InvertedKeyMap` is consequently lossy. `NumpadEnter` and `Numpad4/8/6/2`
334
+ * share their values with `Enter` and `Arrow*`, and the derivation keeps the
335
+ * last name written for a value, so those five cannot be reached through it —
336
+ * which is what makes them unnameable through the numeric form of
337
+ * `useKeyboardKeys()`. The `OSLeft`/`MetaLeft` and `OSRight`/`MetaRight` pairs
338
+ * share a value on purpose: they are legacy and current names for the same
339
+ * physical key.
340
+ */
341
+ const KeyboardNumericCode = {
342
+ Backspace: 8,
343
+ Tab: 9,
344
+ // The NumLock-*off* virtual keys. Declared before `Enter` and the arrows on
345
+ // purpose: those come later and so win the `InvertedKeyMap` derivation, which
346
+ // keeps 13 resolving to `Enter` and 37-40 to `Arrow*`.
347
+ Numpad5: 12,
348
+ NumpadEnter: 13,
349
+ Numpad9: 33,
350
+ Numpad3: 34,
351
+ Numpad1: 35,
352
+ Numpad7: 36,
353
+ Numpad4: 37,
354
+ Numpad8: 38,
355
+ Numpad6: 39,
356
+ Numpad2: 40,
357
+ Numpad0: 45,
358
+ NumpadDecimal: 46,
359
+ Enter: 13,
360
+ ShiftLeft: 16,
361
+ ControlLeft: 17,
362
+ AltLeft: 18,
363
+ Escape: 27,
364
+ Space: 32,
365
+ ArrowLeft: 37,
366
+ ArrowUp: 38,
367
+ ArrowRight: 39,
368
+ ArrowDown: 40,
369
+ Digit0: 48,
370
+ Digit1: 49,
371
+ Digit2: 50,
372
+ Digit3: 51,
373
+ Digit4: 52,
374
+ Digit5: 53,
375
+ Digit6: 54,
376
+ Digit7: 55,
377
+ Digit8: 56,
378
+ Digit9: 57,
379
+ KeyA: 65,
380
+ KeyB: 66,
381
+ KeyC: 67,
382
+ KeyD: 68,
383
+ KeyE: 69,
384
+ KeyF: 70,
385
+ KeyG: 71,
386
+ KeyH: 72,
387
+ KeyI: 73,
388
+ KeyJ: 74,
389
+ KeyK: 75,
390
+ KeyL: 76,
391
+ KeyM: 77,
392
+ KeyN: 78,
393
+ KeyO: 79,
394
+ KeyP: 80,
395
+ KeyQ: 81,
396
+ KeyR: 82,
397
+ KeyS: 83,
398
+ KeyT: 84,
399
+ KeyU: 85,
400
+ KeyV: 86,
401
+ KeyW: 87,
402
+ KeyX: 88,
403
+ KeyY: 89,
404
+ KeyZ: 90,
405
+ // Chrome/Safari report 91 for the left Cmd/Win key, Firefox on macOS 224.
406
+ // The right one shares keyCode 93 with ContextMenu on Chrome/Safari, making
407
+ // the two indistinguishable. Unreal numbers them 91 and 92.
408
+ OSLeft: 91,
409
+ MetaLeft: 91,
410
+ OSRight: 92,
411
+ MetaRight: 92,
412
+ ContextMenu: 93,
413
+ NumpadMultiply: 106,
414
+ NumpadAdd: 107,
415
+ NumpadSubtract: 109,
416
+ NumpadDivide: 111,
417
+ F1: 112,
418
+ F2: 113,
419
+ F3: 114,
420
+ F4: 115,
421
+ F5: 116,
422
+ F6: 117,
423
+ F7: 118,
424
+ F8: 119,
425
+ F9: 120,
426
+ F10: 121,
427
+ F11: 122,
428
+ F12: 123,
429
+ NumLock: 144,
430
+ ScrollLock: 145,
431
+ Semicolon: 186,
432
+ Equal: 187,
433
+ Comma: 188,
434
+ Minus: 189,
435
+ Period: 190,
436
+ Slash: 191,
437
+ Backquote: 192,
438
+ BracketLeft: 219,
439
+ Backslash: 220,
440
+ BracketRight: 221,
441
+ Quote: 222,
442
+ IntlBackslash: 226,
443
+ // Unreal keeps distinct codes for the right-hand modifiers; the DOM reports
444
+ // both sides of each pair under the same `keyCode`.
445
+ ShiftRight: 253,
446
+ ControlRight: 254,
447
+ AltRight: 255,
448
+ };
449
+ /**
450
+ * The `keyCode` a browser reports while an IME is composing (`VK_PROCESSKEY`).
451
+ *
452
+ * `event.code` keeps naming the *physical* key during composition — `KeyW`,
453
+ * `KeyA` and friends — so a consumer that forwards keys has to drop these
454
+ * events explicitly or typing CJK text turns into keystrokes.
455
+ */
456
+ const IME_PROCESS_KEY = 229;
457
+ /**
458
+ * Codes of the keys that are themselves modifiers. A modifier's own `keydown`
459
+ * already reports `ctrlKey`/`metaKey` as pressed, so a "browser shortcut in
460
+ * flight" check has to exempt them or `ControlRight` would block itself.
461
+ */
462
+ const MODIFIER_KEY_CODES = new Set([
463
+ 'ShiftLeft',
464
+ 'ShiftRight',
465
+ 'ControlLeft',
466
+ 'ControlRight',
467
+ 'AltLeft',
468
+ 'AltRight',
469
+ 'MetaLeft',
470
+ 'MetaRight',
471
+ 'OSLeft',
472
+ 'OSRight',
473
+ ]);
474
+ const InvertedKeyMapValues = {};
475
+ //For use for InvertedKeyMap[32] will return 'Space' and so on
476
+ const InvertedKeyMap = InvertedKeyMapValues;
477
+ Object.entries(KeyboardNumericCode).forEach(
478
+ // @ts-expect-error @typescript-eslint/ban-ts-comment
479
+ ([key, value]) => {
480
+ InvertedKeyMapValues[value] = key;
481
+ });
482
+
310
483
  const ControlScheme = {
311
484
  // A mouse can lock inside the WebRTC player, so the user can simply move the
312
485
  // mouse to control the orientation of the camera. The user presses the
@@ -491,8 +664,12 @@ const UnrealStatusMessage = {
491
664
  };
492
665
 
493
666
  const COMMAND_EXILE_TIMEOUT_MS = 10_000;
667
+ const normalizeId = (value) => String(value).trim().replace(/ +/gi, '_').toLowerCase();
494
668
  function exileExpiredCommands(commandsInProgress, idToRemove, now) {
495
- let remaining = commandsInProgress.filter(whereNot({ id: idToRemove }));
669
+ // Case-insensitive, like the `whereNot()` predicate this replaces: a caller
670
+ // supplies `correlationId`, and it round-trips through the engine.
671
+ const idKey = normalizeId(idToRemove);
672
+ let remaining = commandsInProgress.filter(({ id }) => normalizeId(id) !== idKey);
496
673
  const expired = remaining.filter(({ timeStamp }) => now - timeStamp > COMMAND_EXILE_TIMEOUT_MS);
497
674
  if (expired.length) {
498
675
  const expiredIds = new Set(expired.map(({ id }) => id));
@@ -825,6 +1002,488 @@ const unrealFeature = createFeature({
825
1002
  reducer: unrealReducer,
826
1003
  });
827
1004
 
1005
+ function fitIntoRectangle(objectRect, areaRect, round = true, fixedRatio = null) {
1006
+ const result = { x: 0, y: 0, w: 0, h: 0, scale: 1 };
1007
+ const ratio = objectRect.h / objectRect.w;
1008
+ if (objectRect.h > objectRect.w) {
1009
+ result.h = areaRect.h;
1010
+ result.w = areaRect.h / ratio;
1011
+ if (result.w > areaRect.w) {
1012
+ result.w = areaRect.w;
1013
+ result.h = areaRect.w * ratio;
1014
+ }
1015
+ }
1016
+ else {
1017
+ result.w = areaRect.w;
1018
+ result.h = areaRect.w * ratio;
1019
+ if (result.h > areaRect.h) {
1020
+ result.h = areaRect.h;
1021
+ result.w = areaRect.h / ratio;
1022
+ }
1023
+ }
1024
+ if (fixedRatio) {
1025
+ result.w = objectRect.w * fixedRatio;
1026
+ result.h = objectRect.h * fixedRatio;
1027
+ }
1028
+ const wOut = round ? Math.floor(result.w) : result.w;
1029
+ const hOut = round ? Math.floor(result.h) : result.h;
1030
+ return {
1031
+ x: areaRect.x + (areaRect.w - wOut) / 2,
1032
+ y: areaRect.y + (areaRect.h - hOut) / 2,
1033
+ w: wOut,
1034
+ h: hOut,
1035
+ scale: wOut / objectRect.w,
1036
+ };
1037
+ }
1038
+
1039
+ const canvasCacheObj = {};
1040
+ function getCanvasCached(id) {
1041
+ if (!canvasCacheObj[id]) {
1042
+ const canvas = document.createElement('canvas');
1043
+ const ctx = canvas.getContext('2d');
1044
+ if (!ctx) {
1045
+ throw new Error('Canvas 2D context is unavailable.');
1046
+ }
1047
+ canvasCacheObj[id] = {
1048
+ canvas,
1049
+ ctx,
1050
+ };
1051
+ }
1052
+ return canvasCacheObj[id];
1053
+ }
1054
+ function sourceDimensions(imageElement) {
1055
+ const naturalWidth = 'naturalWidth' in imageElement ? imageElement.naturalWidth : 0;
1056
+ const naturalHeight = 'naturalHeight' in imageElement ? imageElement.naturalHeight : 0;
1057
+ return {
1058
+ width: naturalWidth || imageElement.width,
1059
+ height: naturalHeight || imageElement.height,
1060
+ };
1061
+ }
1062
+ function canvasContext(canvas) {
1063
+ const context = canvas.getContext('2d');
1064
+ if (!context) {
1065
+ throw new Error('Canvas 2D context is unavailable.');
1066
+ }
1067
+ return context;
1068
+ }
1069
+ function getSnapshot2(imageElement, maxWidth = 1024, output = 'image/png', quality = 0.9) {
1070
+ const canvasHelper = document.createElement('canvas');
1071
+ const drawCTX = canvasContext(canvasHelper);
1072
+ const dimensions = sourceDimensions(imageElement);
1073
+ const prop = fitIntoRectangle({
1074
+ x: 0,
1075
+ y: 0,
1076
+ w: dimensions.width,
1077
+ h: dimensions.height,
1078
+ }, {
1079
+ x: 0,
1080
+ y: 0,
1081
+ w: maxWidth,
1082
+ h: dimensions.height,
1083
+ });
1084
+ const width = prop.w;
1085
+ const height = prop.h;
1086
+ canvasHelper.width = width;
1087
+ canvasHelper.height = height;
1088
+ drawCTX.save();
1089
+ drawCTX.clearRect(0, 0, width, height);
1090
+ drawCTX.drawImage(imageElement, 0, 0, width, height);
1091
+ drawCTX.restore();
1092
+ if (output === 'canvas') {
1093
+ return canvasHelper;
1094
+ }
1095
+ return canvasHelper.toDataURL(output, quality);
1096
+ }
1097
+ function getSnapshot(imageElement, maxWidth = 1024, output = 'image/png', quality = 0.9) {
1098
+ const canvasHelper = document.createElement('canvas');
1099
+ const drawCTX = canvasContext(canvasHelper);
1100
+ const dimensions = sourceDimensions(imageElement);
1101
+ const prop = fitIntoRectangle({
1102
+ x: 0,
1103
+ y: 0,
1104
+ w: dimensions.width,
1105
+ h: dimensions.height,
1106
+ }, {
1107
+ x: 0,
1108
+ y: 0,
1109
+ w: dimensions.width / 2,
1110
+ h: dimensions.height,
1111
+ });
1112
+ const width = prop.w;
1113
+ const height = prop.h;
1114
+ canvasHelper.width = width;
1115
+ canvasHelper.height = height;
1116
+ drawCTX.save();
1117
+ drawCTX.clearRect(0, 0, width, height);
1118
+ drawCTX.drawImage(imageElement, 0, 0, width, height);
1119
+ drawCTX.restore();
1120
+ if (width > maxWidth * 2) {
1121
+ return getSnapshot(canvasHelper, maxWidth, output, quality);
1122
+ }
1123
+ return getSnapshot2(imageElement, maxWidth, output, quality);
1124
+ }
1125
+
1126
+ /* eslint-disable prefer-const */
1127
+ /**
1128
+ * Colour-space conversions used by the dev stat graph overlay.
1129
+ *
1130
+ * Carried over verbatim; `HSVtoRGB` reassigns its locals, hence the
1131
+ * file-level `prefer-const` exemption.
1132
+ */
1133
+ function HEXtoRGB(color) {
1134
+ if (typeof color === 'string') {
1135
+ color = parseInt(color.replace('#', ''), 16);
1136
+ }
1137
+ const r = (color >> 16) & 255;
1138
+ const g = (color >> 8) & 255;
1139
+ const b = color & 255;
1140
+ return { r: r, g: g, b: b };
1141
+ }
1142
+ function RGBtoHEX(r, g, b) {
1143
+ let redColor = r.toString(16);
1144
+ let greenColor = g.toString(16);
1145
+ let blackColor = b.toString(16);
1146
+ if (redColor.length === 1) {
1147
+ redColor = '0' + redColor;
1148
+ }
1149
+ if (greenColor.length === 1) {
1150
+ greenColor = '0' + greenColor;
1151
+ }
1152
+ if (blackColor.length === 1) {
1153
+ blackColor = '0' + blackColor;
1154
+ }
1155
+ return (redColor + greenColor + blackColor).toUpperCase();
1156
+ }
1157
+ /**
1158
+ * RGB from the respective figures, HSV sequences in terms of returns.
1159
+ * RGB values are as follows.
1160
+ * R - a number from 0 to 255
1161
+ * G - a number from 0 to 255
1162
+ * B - a number from 0 to 255
1163
+ *
1164
+ * HSV values are as follows.
1165
+ * H - a number between 360-0
1166
+ * S - number between 0 and 1.0
1167
+ * V - number between 0 and 1.0
1168
+ *
1169
+ * Cannot compute, including alpha.
1170
+ * @ Param r the red (R) indicating the number (0x00 to 0xFF to)
1171
+ * @ Param g green (G) indicates the number (0x00 to 0xFF to)
1172
+ * @ Param b blue (B) shows the number (0x00 to 0xFF to)
1173
+ * @ Return HSV values as [H, S, V]
1174
+ **/
1175
+ function RGBtoHSV(r, g, b) {
1176
+ r /= 255;
1177
+ g /= 255;
1178
+ b /= 255;
1179
+ let h = 0;
1180
+ let s;
1181
+ let x, y;
1182
+ if (r >= g)
1183
+ x = r;
1184
+ else
1185
+ x = g;
1186
+ if (b > x)
1187
+ x = b;
1188
+ if (r <= g)
1189
+ y = r;
1190
+ else
1191
+ y = g;
1192
+ if (b < y)
1193
+ y = b;
1194
+ const v = x;
1195
+ const c = x - y;
1196
+ if (x === 0)
1197
+ s = 0;
1198
+ else
1199
+ s = c / x;
1200
+ if (s !== 0) {
1201
+ if (r === x) {
1202
+ h = (g - b) / c;
1203
+ }
1204
+ else {
1205
+ if (g === x) {
1206
+ h = 2 + (b - r) / c;
1207
+ }
1208
+ else {
1209
+ if (b === x) {
1210
+ h = 4 + (r - g) / c;
1211
+ }
1212
+ }
1213
+ }
1214
+ h = h * 60;
1215
+ if (h < 0)
1216
+ h = h + 360;
1217
+ }
1218
+ return [h, s, v];
1219
+ }
1220
+ /**
1221
+ * HSV from each of the RGB values to determine a return as a number[].
1222
+ * RGB values are as follows.
1223
+ * R - a number from 0 to 255
1224
+ * G - a number from 0 to 255
1225
+ * B - a number from 0 to 255
1226
+ *
1227
+ * HSV values are as follows.
1228
+ * H - a number between 360-0
1229
+ * S - number between 0 and 1.0
1230
+ * V - number between 0 and 1.0
1231
+ *
1232
+ * H is replaced with equivalent numbers in the range of the 360-0 that is out of range.
1233
+ * Cannot compute, including alpha.
1234
+ *
1235
+ * @ Param h hue (Hue) number that indicates (to 360-0)
1236
+ * @ Param s the saturation (Saturation) shows the number (0.0 to 1.0)
1237
+ * @ Param v lightness (Value) indicates the number (0.0 to 1.0)
1238
+ * @ Return RGB values into a number[] of [R, G, B]
1239
+ **/
1240
+ function HSVtoRGB(h, s, v) {
1241
+ let r = 0, g = 0, b = 0;
1242
+ let i, x, y, z;
1243
+ if (s < 0) {
1244
+ s = 0;
1245
+ }
1246
+ if (s > 1) {
1247
+ s = 1;
1248
+ }
1249
+ if (v < 0) {
1250
+ v = 0;
1251
+ }
1252
+ if (v > 1) {
1253
+ v = 1;
1254
+ }
1255
+ h = h % 360;
1256
+ if (h < 0) {
1257
+ h += 360;
1258
+ }
1259
+ h /= 60;
1260
+ i = h >> 0;
1261
+ x = v * (1 - s);
1262
+ y = v * (1 - s * (h - i));
1263
+ z = v * (1 - s * (1 - h + i));
1264
+ switch (i) {
1265
+ case 0:
1266
+ r = v;
1267
+ g = z;
1268
+ b = x;
1269
+ break;
1270
+ case 1:
1271
+ r = y;
1272
+ g = v;
1273
+ b = x;
1274
+ break;
1275
+ case 2:
1276
+ r = x;
1277
+ g = v;
1278
+ b = z;
1279
+ break;
1280
+ case 3:
1281
+ r = x;
1282
+ g = y;
1283
+ b = v;
1284
+ break;
1285
+ case 4:
1286
+ r = z;
1287
+ g = x;
1288
+ b = v;
1289
+ break;
1290
+ case 5:
1291
+ r = v;
1292
+ g = x;
1293
+ b = y;
1294
+ break;
1295
+ }
1296
+ return [(r * 255) >> 0, (g * 255) >> 0, (b * 255) >> 0];
1297
+ }
1298
+
1299
+ /** Console palette for the Unreal/Cirrus message log. */
1300
+ const COLOR_CODES = {
1301
+ TO_UNREAL: ['#ffffff', '#005912', `UNREAL <-`],
1302
+ TAP_LOG: ['#ffff00', '#1976d2', ' [TAPLOG]'],
1303
+ FROM_UNREAL: ['#ffff00', '#898989', 'UNREAL ->'],
1304
+ FROM_UNREAL_ERROR: ['#ffffff', '#ff0000', 'UNREAL ERROR ->'],
1305
+ FROM_CIRRUS: ['#ffffff', '#ef702b', 'CIRRUS ->'],
1306
+ FROM_CIRRUS_ERROR: ['#ffffff', '#ff0000', 'CIRRUS ->'],
1307
+ TO_CIRRUS: ['#ffffff', '#3c9738', 'CIRRUS <-'],
1308
+ };
1309
+
1310
+ function generateUuid() {
1311
+ return v4();
1312
+ }
1313
+
1314
+ /**
1315
+ * Truthiness with the string forms folded in: `'false'`, `'undefined'` and
1316
+ * `'null'` are falsy here, which `Boolean` would call true.
1317
+ */
1318
+ const Truthy = (value) => !!value && value !== 'false' && value !== 'undefined' && value !== 'null';
1319
+ /** The inverse of {@link Truthy}. */
1320
+ const Falsy = (value) => !Truthy(value);
1321
+
1322
+ /* eslint-disable no-console -- this file *is* the console wrapper; every call
1323
+ is gated behind `Logger.isDevMode`. */
1324
+ /**
1325
+ * Left-pads `input` to `size`, then trims to the last `size` characters — a
1326
+ * value longer than `size` is truncated, not left as-is.
1327
+ */
1328
+ function pad(input, size, symbol = '0') {
1329
+ let s = input + '';
1330
+ while (s.length < size) {
1331
+ s = symbol + s;
1332
+ }
1333
+ return s.slice(-size);
1334
+ }
1335
+ /** `UTC:HH:MM:SS.mmm` for the current instant. */
1336
+ function timeUTCToString() {
1337
+ const date = new Date();
1338
+ return `UTC:${pad(date.getUTCHours(), 2)}:${pad(date.getUTCMinutes(), 2)}:${pad(date.getUTCSeconds(), 2)}.${pad(date.getUTCMilliseconds(), 3)}`;
1339
+ }
1340
+ class Logger {
1341
+ // Guard the top-level `localStorage` access so the package stays importable
1342
+ // in non-browser contexts (Node CLI / SSR); off the browser dev mode is off.
1343
+ static { this.isDevMode = typeof localStorage !== 'undefined' &&
1344
+ Truthy(localStorage.getItem('devMode')); }
1345
+ static colored(color, background, ...args) {
1346
+ if (!Logger.isDevMode) {
1347
+ return;
1348
+ }
1349
+ const [first, ...rest] = args;
1350
+ console.log.apply(console, [
1351
+ `%c ${timeUTCToString()} ${first} `,
1352
+ `color: ${color}; background: ${background};`,
1353
+ ...rest,
1354
+ ]);
1355
+ }
1356
+ static log(...args) {
1357
+ if (!Logger.isDevMode) {
1358
+ return;
1359
+ }
1360
+ console.log.apply(console, [
1361
+ `%c ${timeUTCToString()} [ LOG ] `,
1362
+ 'color: black; background: #80ff80;',
1363
+ ...args,
1364
+ ]);
1365
+ }
1366
+ static warn(...args) {
1367
+ if (!Logger.isDevMode) {
1368
+ return;
1369
+ }
1370
+ console.warn.apply(console, [
1371
+ `%c ${timeUTCToString()} WARN `,
1372
+ 'color: black; background: #ffd500;',
1373
+ ...args,
1374
+ ]);
1375
+ }
1376
+ static info(...args) {
1377
+ if (!Logger.isDevMode) {
1378
+ return;
1379
+ }
1380
+ console.log.apply(console, [
1381
+ `%c ${timeUTCToString()} [ INFO ] `,
1382
+ 'color: white; background: blue;',
1383
+ ...args,
1384
+ ]);
1385
+ }
1386
+ static error(...args) {
1387
+ if (!Logger.isDevMode) {
1388
+ return;
1389
+ }
1390
+ console.error.apply(console, [
1391
+ `%c ${timeUTCToString()} ERROR `,
1392
+ 'color: white; background: red;',
1393
+ ...args,
1394
+ ]);
1395
+ }
1396
+ }
1397
+
1398
+ /** Clamps `value` between `min` and `max`; an inverted range yields `min`. */
1399
+ function clampf(min, max, value) {
1400
+ if (min > max) {
1401
+ return min;
1402
+ }
1403
+ return Math.min(max, Math.max(min, value));
1404
+ }
1405
+ /** Linear interpolation; `value` is clamped to the 0..1 range first. */
1406
+ function lerp(min, max, value) {
1407
+ value = clampf(0, 1, value);
1408
+ return value * (max - min) + min;
1409
+ }
1410
+ /**
1411
+ * The inverse of {@link lerp}: where `value` sits between `min` and `max`.
1412
+ *
1413
+ * Returns 1 when the range is empty, to avoid dividing by zero.
1414
+ */
1415
+ function inverseLerp(min, max, value, clampToNormal = false) {
1416
+ if (clampToNormal) {
1417
+ value = Math.min(max, value);
1418
+ value = Math.max(min, value);
1419
+ }
1420
+ const difference = max - min;
1421
+ if (difference === 0) {
1422
+ return 1;
1423
+ }
1424
+ const result = (value - min) / difference;
1425
+ if (clampToNormal) {
1426
+ return Math.min(1, Math.max(0, result));
1427
+ }
1428
+ return result;
1429
+ }
1430
+ const EPSILON = 0.01;
1431
+ /**
1432
+ * Floating-point equality within `Epsilon` (default `0.01`).
1433
+ *
1434
+ * Note the `||` fallback: passing `0` uses the default, it does not compare
1435
+ * exactly.
1436
+ */
1437
+ function fpIsASameAsB(A, B, Epsilon) {
1438
+ Epsilon = Epsilon || EPSILON;
1439
+ return Math.abs(A - B) < Epsilon;
1440
+ }
1441
+
1442
+ /**
1443
+ * A counting semaphore: `acquire()` resolves once a slot is free, `release()`
1444
+ * hands the slot to the longest-waiting caller.
1445
+ */
1446
+ class Semaphore {
1447
+ constructor(maxConcurrency) {
1448
+ this.maxConcurrency = maxConcurrency;
1449
+ this.tasks = [];
1450
+ this.currentCount = 0;
1451
+ }
1452
+ acquire() {
1453
+ return new Promise((resolve) => {
1454
+ if (this.currentCount < this.maxConcurrency) {
1455
+ this.currentCount++;
1456
+ resolve();
1457
+ }
1458
+ else {
1459
+ this.tasks.push(resolve);
1460
+ }
1461
+ });
1462
+ }
1463
+ release() {
1464
+ if (this.tasks.length > 0) {
1465
+ const nextTask = this.tasks.shift();
1466
+ if (nextTask) {
1467
+ nextTask();
1468
+ }
1469
+ }
1470
+ else if (this.currentCount > 0) {
1471
+ this.currentCount--;
1472
+ }
1473
+ }
1474
+ }
1475
+
1476
+ const tapLog = (text, ...args) => (source) => source.pipe(tap((data) => {
1477
+ Logger.colored(...COLOR_CODES.TAP_LOG, text, data, ...args);
1478
+ }));
1479
+
1480
+ // Internal only. Deliberately absent from src/public-api.ts — these are the
1481
+ // symbols this package used to pull from @3dsource/utils. Exporting them would
1482
+ // re-create the coupling this folder exists to remove.
1483
+ //
1484
+ // Import this barrel from production code ('../utils' / '../../utils'), never
1485
+ // the individual files: the specs mock the barrel as a unit.
1486
+
828
1487
  /**
829
1488
  * `'metabox'` — the default when `UNREAL_CONFIG` omits `mode` — speaks the full
830
1489
  * Metabox command protocol. `'default'` targets a stock Pixel Streaming
@@ -953,10 +1612,10 @@ class WebrtcErrorModalComponent {
953
1612
  close() {
954
1613
  this.#dialogRef.close();
955
1614
  }
956
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: WebrtcErrorModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
957
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.2", type: WebrtcErrorModalComponent, isStandalone: true, selector: "app-webrtc-error-modal", ngImport: i0, template: "<div [attr.data-testid]=\"'webrtc-error-modal'\" class=\"unreal-modal\">\n <header class=\"unreal-modal__header\"></header>\n <section class=\"unreal-modal__body\">\n <div [attr.data-testid]=\"'title'\" class=\"unreal-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=\"unreal-modal__footer\">\n <app-unreal-button\n (clicked)=\"closeModalWithCirrusDisconnect()\"\n [data-testid]=\"'close-webrtc-error-modal'\"\n weight=\"primary\"\n >\n Ok\n </app-unreal-button>\n </footer>\n</div>\n", styles: [".unreal-modal{--unrealModalWidth: 422px;--unrealModalMaxWidth: calc(100% - 16px) ;--unrealModalMaxHeight: 80vh;--unrealModalBg: var(--src-surface-container-main, #fff);--unrealModalBoxShadow: 0 16px 36px 4px var(--src-shadow-hard, rgba(71, 85, 105, .24)), 0 1px 4px 0 var(--src-shadow-light, rgba(148, 163, 184, .16));--unrealModalBorderRadius: var(--src-layout-radius-const-rounded-md, 12px);--unrealModalTitleSize: var(--src-font-size-base, 16px);--unrealModalTitleLineHeight: var(--src-font-line-base, 24px);--unrealModalTitleColor: var(--src-text-body-main, #111827);--unrealModalBodyPadding: var(--src-layout-padding-const-lg, 16px) var(--src-layout-padding-const-xl, 24px) var(--src-layout-padding-const-2xl, 32px) var(--src-layout-padding-const-xl, 24px);--unrealModalTitleBorder: none;--unrealModalFooterBorder: 1px solid var(--src-border-container-light, rgba(148, 163, 184, .16));display:grid;grid-template-columns:minmax(0,1fr);grid-template-rows:auto minmax(0,1fr) auto;width:var(--unrealModalWidth);max-width:var(--unrealModalMaxWidth);max-height:var(--unrealModalMaxHeight);margin:auto;background-color:var(--unrealModalBg);border-radius:var(--unrealModalBorderRadius);box-shadow:var(--unrealModalBoxShadow);color:var(--src-text-body-secondary, #4b5563);font-family:var(--src-font-family-header, Inter);font-size:var(--src-font-size-sm, 14px);font-style:normal;font-weight:400;line-height:var(--src-font-line-sm, 20px)}.unreal-modal .unreal-modal__header{position:relative;display:flex;padding:0;width:100%;border-bottom:var(--unrealModalTitleBorder)}.unreal-modal .unreal-modal__body{display:grid;grid-template-columns:minmax(0,1fr);grid-template-rows:auto minmax(0,1fr);gap:var(--src-layout-gap-const-md, 12px);padding:var(--unrealModalBodyPadding);overflow:auto;scrollbar-width:thin;scrollbar-color:var(--src-border-container-basic, rgba(148, 163, 184, .24)) transparent}.unreal-modal .unreal-modal__body .unreal-modal__title{grid-column:1/-1}.unreal-modal .unreal-modal__body::-webkit-scrollbar{width:4px}.unreal-modal .unreal-modal__body::-webkit-scrollbar-track{background:transparent}.unreal-modal .unreal-modal__body::-webkit-scrollbar-thumb{background-color:var(--src-border-container-basic, rgba(148, 163, 184, .24));border-radius:10px}.unreal-modal .unreal-modal__footer{display:flex;justify-content:flex-end;align-items:center;padding:var(--src-layout-padding-const-md, 12px) var(--src-layout-padding-const-xl, 24px) var(--src-layout-padding-const-lg, 16px) var(--src-layout-padding-const-xl, 24px);width:100%;border-top:var(--unrealModalFooterBorder);gap:var(--src-layout-gap-const-sm, 8px)}.unreal-modal .unreal-modal__title{font-size:var(--unrealModalTitleSize);font-weight:600;line-height:var(--unrealModalTitleLineHeight);color:var(--unrealModalTitleColor);word-break:break-word}.unreal-modal .unreal-modal__body{width:100%}.unreal-modal .unreal-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}.unreal-modal .unreal-modal__body a{word-wrap:break-word;white-space:normal}\n"], dependencies: [{ kind: "component", type: UnrealButtonComponent, selector: "app-unreal-button", inputs: ["type", "weight", "size", "fullWidth", "isDisabled", "data-testid", "ariaLabel"], outputs: ["clicked"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1615
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: WebrtcErrorModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1616
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.3", type: WebrtcErrorModalComponent, isStandalone: true, selector: "app-webrtc-error-modal", ngImport: i0, template: "<div [attr.data-testid]=\"'webrtc-error-modal'\" class=\"unreal-modal\">\n <header class=\"unreal-modal__header\"></header>\n <section class=\"unreal-modal__body\">\n <div [attr.data-testid]=\"'title'\" class=\"unreal-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=\"unreal-modal__footer\">\n <app-unreal-button\n (clicked)=\"closeModalWithCirrusDisconnect()\"\n [data-testid]=\"'close-webrtc-error-modal'\"\n weight=\"primary\"\n >\n Ok\n </app-unreal-button>\n </footer>\n</div>\n", styles: [".unreal-modal{--unrealModalWidth: 422px;--unrealModalMaxWidth: calc(100% - 16px) ;--unrealModalMaxHeight: 80vh;--unrealModalBg: var(--src-surface-container-main, #fff);--unrealModalBoxShadow: 0 16px 36px 4px var(--src-shadow-hard, rgba(71, 85, 105, .24)), 0 1px 4px 0 var(--src-shadow-light, rgba(148, 163, 184, .16));--unrealModalBorderRadius: var(--src-layout-radius-const-rounded-md, 12px);--unrealModalTitleSize: var(--src-font-size-base, 16px);--unrealModalTitleLineHeight: var(--src-font-line-base, 24px);--unrealModalTitleColor: var(--src-text-body-main, #111827);--unrealModalBodyPadding: var(--src-layout-padding-const-lg, 16px) var(--src-layout-padding-const-xl, 24px) var(--src-layout-padding-const-2xl, 32px) var(--src-layout-padding-const-xl, 24px);--unrealModalTitleBorder: none;--unrealModalFooterBorder: 1px solid var(--src-border-container-light, rgba(148, 163, 184, .16));display:grid;grid-template-columns:minmax(0,1fr);grid-template-rows:auto minmax(0,1fr) auto;width:var(--unrealModalWidth);max-width:var(--unrealModalMaxWidth);max-height:var(--unrealModalMaxHeight);margin:auto;background-color:var(--unrealModalBg);border-radius:var(--unrealModalBorderRadius);box-shadow:var(--unrealModalBoxShadow);color:var(--src-text-body-secondary, #4b5563);font-family:var(--src-font-family-header, Inter);font-size:var(--src-font-size-sm, 14px);font-style:normal;font-weight:400;line-height:var(--src-font-line-sm, 20px)}.unreal-modal .unreal-modal__header{position:relative;display:flex;padding:0;width:100%;border-bottom:var(--unrealModalTitleBorder)}.unreal-modal .unreal-modal__body{display:grid;grid-template-columns:minmax(0,1fr);grid-template-rows:auto minmax(0,1fr);gap:var(--src-layout-gap-const-md, 12px);padding:var(--unrealModalBodyPadding);overflow:auto;scrollbar-width:thin;scrollbar-color:var(--src-border-container-basic, rgba(148, 163, 184, .24)) transparent}.unreal-modal .unreal-modal__body .unreal-modal__title{grid-column:1/-1}.unreal-modal .unreal-modal__body::-webkit-scrollbar{width:4px}.unreal-modal .unreal-modal__body::-webkit-scrollbar-track{background:transparent}.unreal-modal .unreal-modal__body::-webkit-scrollbar-thumb{background-color:var(--src-border-container-basic, rgba(148, 163, 184, .24));border-radius:10px}.unreal-modal .unreal-modal__footer{display:flex;justify-content:flex-end;align-items:center;padding:var(--src-layout-padding-const-md, 12px) var(--src-layout-padding-const-xl, 24px) var(--src-layout-padding-const-lg, 16px) var(--src-layout-padding-const-xl, 24px);width:100%;border-top:var(--unrealModalFooterBorder);gap:var(--src-layout-gap-const-sm, 8px)}.unreal-modal .unreal-modal__title{font-size:var(--unrealModalTitleSize);font-weight:600;line-height:var(--unrealModalTitleLineHeight);color:var(--unrealModalTitleColor);word-break:break-word}.unreal-modal .unreal-modal__body{width:100%}.unreal-modal .unreal-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}.unreal-modal .unreal-modal__body a{word-wrap:break-word;white-space:normal}\n"], dependencies: [{ kind: "component", type: UnrealButtonComponent, selector: "app-unreal-button", inputs: ["type", "weight", "size", "fullWidth", "isDisabled", "data-testid", "ariaLabel"], outputs: ["clicked"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
958
1617
  }
959
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: WebrtcErrorModalComponent, decorators: [{
1618
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: WebrtcErrorModalComponent, decorators: [{
960
1619
  type: Component,
961
1620
  args: [{ selector: 'app-webrtc-error-modal', changeDetection: ChangeDetectionStrategy.OnPush, imports: [UnrealButtonComponent], template: "<div [attr.data-testid]=\"'webrtc-error-modal'\" class=\"unreal-modal\">\n <header class=\"unreal-modal__header\"></header>\n <section class=\"unreal-modal__body\">\n <div [attr.data-testid]=\"'title'\" class=\"unreal-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=\"unreal-modal__footer\">\n <app-unreal-button\n (clicked)=\"closeModalWithCirrusDisconnect()\"\n [data-testid]=\"'close-webrtc-error-modal'\"\n weight=\"primary\"\n >\n Ok\n </app-unreal-button>\n </footer>\n</div>\n", styles: [".unreal-modal{--unrealModalWidth: 422px;--unrealModalMaxWidth: calc(100% - 16px) ;--unrealModalMaxHeight: 80vh;--unrealModalBg: var(--src-surface-container-main, #fff);--unrealModalBoxShadow: 0 16px 36px 4px var(--src-shadow-hard, rgba(71, 85, 105, .24)), 0 1px 4px 0 var(--src-shadow-light, rgba(148, 163, 184, .16));--unrealModalBorderRadius: var(--src-layout-radius-const-rounded-md, 12px);--unrealModalTitleSize: var(--src-font-size-base, 16px);--unrealModalTitleLineHeight: var(--src-font-line-base, 24px);--unrealModalTitleColor: var(--src-text-body-main, #111827);--unrealModalBodyPadding: var(--src-layout-padding-const-lg, 16px) var(--src-layout-padding-const-xl, 24px) var(--src-layout-padding-const-2xl, 32px) var(--src-layout-padding-const-xl, 24px);--unrealModalTitleBorder: none;--unrealModalFooterBorder: 1px solid var(--src-border-container-light, rgba(148, 163, 184, .16));display:grid;grid-template-columns:minmax(0,1fr);grid-template-rows:auto minmax(0,1fr) auto;width:var(--unrealModalWidth);max-width:var(--unrealModalMaxWidth);max-height:var(--unrealModalMaxHeight);margin:auto;background-color:var(--unrealModalBg);border-radius:var(--unrealModalBorderRadius);box-shadow:var(--unrealModalBoxShadow);color:var(--src-text-body-secondary, #4b5563);font-family:var(--src-font-family-header, Inter);font-size:var(--src-font-size-sm, 14px);font-style:normal;font-weight:400;line-height:var(--src-font-line-sm, 20px)}.unreal-modal .unreal-modal__header{position:relative;display:flex;padding:0;width:100%;border-bottom:var(--unrealModalTitleBorder)}.unreal-modal .unreal-modal__body{display:grid;grid-template-columns:minmax(0,1fr);grid-template-rows:auto minmax(0,1fr);gap:var(--src-layout-gap-const-md, 12px);padding:var(--unrealModalBodyPadding);overflow:auto;scrollbar-width:thin;scrollbar-color:var(--src-border-container-basic, rgba(148, 163, 184, .24)) transparent}.unreal-modal .unreal-modal__body .unreal-modal__title{grid-column:1/-1}.unreal-modal .unreal-modal__body::-webkit-scrollbar{width:4px}.unreal-modal .unreal-modal__body::-webkit-scrollbar-track{background:transparent}.unreal-modal .unreal-modal__body::-webkit-scrollbar-thumb{background-color:var(--src-border-container-basic, rgba(148, 163, 184, .24));border-radius:10px}.unreal-modal .unreal-modal__footer{display:flex;justify-content:flex-end;align-items:center;padding:var(--src-layout-padding-const-md, 12px) var(--src-layout-padding-const-xl, 24px) var(--src-layout-padding-const-lg, 16px) var(--src-layout-padding-const-xl, 24px);width:100%;border-top:var(--unrealModalFooterBorder);gap:var(--src-layout-gap-const-sm, 8px)}.unreal-modal .unreal-modal__title{font-size:var(--unrealModalTitleSize);font-weight:600;line-height:var(--unrealModalTitleLineHeight);color:var(--unrealModalTitleColor);word-break:break-word}.unreal-modal .unreal-modal__body{width:100%}.unreal-modal .unreal-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}.unreal-modal .unreal-modal__body a{word-wrap:break-word;white-space:normal}\n"] }]
962
1621
  }] });
@@ -1179,10 +1838,10 @@ class CommandTelemetryService extends SubService {
1179
1838
  this.trackTime(out);
1180
1839
  funcToDecorate(out);
1181
1840
  }
1182
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: CommandTelemetryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1183
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: CommandTelemetryService, providedIn: 'root' }); }
1841
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: CommandTelemetryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1842
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: CommandTelemetryService, providedIn: 'root' }); }
1184
1843
  }
1185
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: CommandTelemetryService, decorators: [{
1844
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: CommandTelemetryService, decorators: [{
1186
1845
  type: Injectable,
1187
1846
  args: [{ providedIn: 'root' }]
1188
1847
  }], ctorParameters: () => [] });
@@ -1203,10 +1862,10 @@ class DevModeService {
1203
1862
  localStorage.removeItem('devMode');
1204
1863
  }
1205
1864
  }
1206
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: DevModeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1207
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: DevModeService, providedIn: 'root' }); }
1865
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: DevModeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1866
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: DevModeService, providedIn: 'root' }); }
1208
1867
  }
1209
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: DevModeService, decorators: [{
1868
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: DevModeService, decorators: [{
1210
1869
  type: Injectable,
1211
1870
  args: [{
1212
1871
  providedIn: 'root',
@@ -1525,10 +2184,10 @@ class RegionsPingService {
1525
2184
  }
1526
2185
  }
1527
2186
  }
1528
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: RegionsPingService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1529
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: RegionsPingService, providedIn: 'root' }); }
2187
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: RegionsPingService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
2188
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: RegionsPingService, providedIn: 'root' }); }
1530
2189
  }
1531
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: RegionsPingService, decorators: [{
2190
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: RegionsPingService, decorators: [{
1532
2191
  type: Injectable,
1533
2192
  args: [{ providedIn: 'root' }]
1534
2193
  }], ctorParameters: () => [] });
@@ -1571,101 +2230,25 @@ function withStreamRequestId(wsUrl, streamRequestId) {
1571
2230
  }
1572
2231
 
1573
2232
  /**
1574
- * Factory that creates a WebSocket connection and wraps it in an RxJS Observable.
1575
- *
1576
- * Returns a **single-value Observable**: it emits the open `WebSocket` instance
1577
- * once the connection is established, then completes. This design allows
1578
- * downstream operators (e.g. `tap` in `connectToCirrus`) to attach their own
1579
- * event handlers before any messages arrive.
1580
- *
1581
- * ## Ownership model
1582
- *
1583
- * The factory **transfers ownership** of the WebSocket to the consumer upon
1584
- * emission. Once `observer.next(ws)` fires, the consumer is responsible for
1585
- * the socket's lifecycle (attaching handlers, closing when done). The teardown
1586
- * logic will **never** close a socket that has been successfully delivered.
1587
- *
1588
- * ## Timeout
1589
- *
1590
- * The observable is piped through `timeout({ first: WS_TIMEOUT })`. If the
1591
- * WebSocket `open` event does not fire within `WS_TIMEOUT` milliseconds, the
1592
- * stream errors with a timeout message and the teardown disposes the socket.
1593
- *
1594
- * ## Teardown & leak prevention
1595
- *
1596
- * When the observable is unsubscribed (timeout, manual unsubscribe, or error),
1597
- * the teardown function runs and handles two cases:
1598
- *
1599
- * 1. **Socket was emitted (`emitted === true`):** The consumer owns the socket.
1600
- * Teardown only removes the internal event listeners — it does NOT close
1601
- * the socket. Closing it here would break downstream consumers like
1602
- * `connectToCirrus` that rely on the connection staying alive.
1603
- *
1604
- * 2. **Socket was NOT emitted (`emitted === false`):** The socket is orphaned
1605
- * (nobody has a reference to it). Teardown must close it to prevent a leak.
1606
- * However, calling `ws.close()` on a `CONNECTING` socket does not reliably
1607
- * abort the underlying HTTP upgrade request in all browsers — the TCP
1608
- * handshake may still complete asynchronously. To handle this edge case,
1609
- * teardown replaces the `open` listener with a one-shot handler that calls
1610
- * `ws.close()` if the connection succeeds after teardown has already run.
1611
- * This ensures the orphaned socket is closed regardless of timing:
1612
- *
1613
- * - If `close()` aborts the connection immediately — done.
1614
- * - If the browser lets the handshake complete anyway — the `open` handler
1615
- * fires and closes the now-connected socket, so it doesn't linger in
1616
- * the browser's network tab as a "Pending" request.
1617
- *
1618
- * ## Why `defer`?
1619
- *
1620
- * The outer `defer()` ensures a **new WebSocket** is created on each
1621
- * subscription. Without it, retry operators would reuse the same (possibly
1622
- * closed) socket instance. With `defer`, each retry gets a fresh connection.
1623
- *
1624
- * ## SSR guard
1625
- *
1626
- * If `window` or `WebSocket` is not available (server-side rendering),
1627
- * the function returns an error observable immediately without attempting
1628
- * a connection.
1629
- *
1630
- * ## Lifecycle flags
1631
- *
1632
- * - `opened` — set to `true` when the `open` event fires. Guards the `close`
1633
- * handler so it only errors the observable if the socket was closed *before*
1634
- * the connection was established (e.g. server rejected the upgrade).
1635
- *
1636
- * - `emitted` — set to `true` right before `observer.next(ws)`. Tells the
1637
- * teardown function whether the socket was delivered to the consumer.
1638
- * This is the flag that controls whether teardown closes the socket or
1639
- * leaves it for the consumer.
2233
+ * Creates a WebSocket and wraps it in a single-value Observable: emits the open
2234
+ * socket, then completes, so a downstream `tap` can attach handlers before any
2235
+ * message arrives.
1640
2236
  *
1641
- * ## Abort
2237
+ * Ownership transfers to the consumer on emission — teardown never closes a
2238
+ * socket it already delivered, because `connectToCirrus` needs that connection
2239
+ * to stay alive.
1642
2240
  *
1643
- * The factory does **not** accept an abort signal — cancellation is handled
1644
- * by the caller via standard RxJS unsubscription. When the caller's
1645
- * `takeUntil` (or manual `unsubscribe`) fires, the observable tears down
1646
- * and the orphaned socket is closed. For retry loops, place `takeUntil`
1647
- * at two levels:
2241
+ * An *undelivered* socket is orphaned and teardown must close it. `ws.close()`
2242
+ * on a `CONNECTING` socket does not reliably abort the HTTP upgrade in every
2243
+ * browser, so teardown also swaps in a one-shot `open` handler that closes the
2244
+ * socket if the handshake completes anyway.
1648
2245
  *
1649
- * 1. **Inside the per-attempt chain** (e.g. in `connectToCirrus`) — cancels
1650
- * the current in-flight WebSocket attempt and triggers teardown.
1651
- * 2. **Outside `retry`** (e.g. in `connectToSignaling`) — kills the entire
1652
- * retry loop so no further re-subscriptions occur.
2246
+ * The outer `defer` gives every subscription a fresh socket, so `retry` does not
2247
+ * reuse a closed one.
1653
2248
  *
1654
- * ## Usage
1655
- *
1656
- * ```typescript
1657
- * // In connectToCirrus — takeUntil cancels the current attempt:
1658
- * createWebSocket(wsUrl).pipe(
1659
- * takeUntil(abort$),
1660
- * tap(ws => addWsHandlers(ws)),
1661
- * );
1662
- *
1663
- * // In connectToSignaling — takeUntil outside retry kills the loop:
1664
- * defer(() => innerChain$).pipe(
1665
- * retry({ delay: ... }),
1666
- * takeUntil(abort$),
1667
- * );
1668
- * ```
2249
+ * There is no abort signal: cancellation is the caller's `takeUntil`, and it has
2250
+ * to sit at two levels — inside the per-attempt chain to cancel the in-flight
2251
+ * socket, and outside `retry` to stop the loop re-subscribing.
1669
2252
  *
1670
2253
  * @param wsUrl - The `ws://` or `wss://` URL to connect to.
1671
2254
  * @returns Observable that emits a single open WebSocket, then completes.
@@ -1673,17 +2256,18 @@ function withStreamRequestId(wsUrl, streamRequestId) {
1673
2256
  * does not support WebSocket.
1674
2257
  */
1675
2258
  function createWebSocket(wsUrl, timeoutMs = WS_TIMEOUT) {
1676
- // --- SSR guard: bail immediately if WebSocket API is unavailable ---
2259
+ // SSR guard.
1677
2260
  if (typeof window === 'undefined' || !('WebSocket' in window)) {
1678
2261
  return throwError(() => new Error("Your browser doesn't support WebSocket"));
1679
2262
  }
1680
2263
  Logger.info('Creating socket', wsUrl);
1681
2264
  return defer(() => new Observable((observer) => {
1682
2265
  const ws = new WebSocket(wsUrl);
1683
- // Lifecycle flags — see JSDoc above for detailed explanation.
2266
+ // `opened` guards `onClose` so it only errors on a close *before* the
2267
+ // connection was established; `emitted` tells teardown whether the
2268
+ // consumer already owns the socket.
1684
2269
  let opened = false;
1685
2270
  let emitted = false;
1686
- // --- Event handlers (attached during connection, removed in teardown) ---
1687
2271
  const onOpen = () => {
1688
2272
  opened = true;
1689
2273
  emitted = true;
@@ -1704,23 +2288,20 @@ function createWebSocket(wsUrl, timeoutMs = WS_TIMEOUT) {
1704
2288
  ws.addEventListener('open', onOpen);
1705
2289
  ws.addEventListener('error', onError);
1706
2290
  ws.addEventListener('close', onClose);
1707
- // --- Teardown: runs on unsubscribe, timeout, error, or complete ---
2291
+ // Teardown: unsubscribe, timeout, error, or complete.
1708
2292
  return () => {
1709
2293
  ws.removeEventListener('error', onError);
1710
2294
  ws.removeEventListener('close', onClose);
1711
2295
  if (!emitted &&
1712
2296
  (ws.readyState === WebSocket.CONNECTING ||
1713
2297
  ws.readyState === WebSocket.OPEN)) {
1714
- // Socket was never delivered — it's orphaned. Must close it.
1715
- // Replace the open listener with a cleanup handler: if the
1716
- // browser completes the handshake after this teardown, the
1717
- // handler will close the orphaned socket immediately.
2298
+ // Orphaned: close it, and cover the case where the browser
2299
+ // completes the handshake after this teardown anyway.
1718
2300
  ws.removeEventListener('open', onOpen);
1719
2301
  ws.addEventListener('open', () => ws.close());
1720
- // Some strict implementations (including `ws`) emit an `error`
1721
- // when a CONNECTING socket is closed. The observable is already
1722
- // torn down, so consume that expected lifecycle error and remove
1723
- // the temporary handlers once the socket closes.
2302
+ // Some implementations (including `ws`) emit `error` when a
2303
+ // CONNECTING socket is closed. The observable is already torn
2304
+ // down, so swallow it and drop the handlers on close.
1724
2305
  const onOrphanError = () => undefined;
1725
2306
  const onOrphanClose = () => {
1726
2307
  ws.removeEventListener('error', onOrphanError);
@@ -1731,8 +2312,7 @@ function createWebSocket(wsUrl, timeoutMs = WS_TIMEOUT) {
1731
2312
  ws.close();
1732
2313
  }
1733
2314
  else {
1734
- // Socket was delivered to consumer — they own it now.
1735
- // Just remove our internal listener; don't touch the socket.
2315
+ // Delivered — the consumer owns it; only drop our listener.
1736
2316
  ws.removeEventListener('open', onOpen);
1737
2317
  }
1738
2318
  };
@@ -2366,10 +2946,10 @@ class SignallingService extends SubService {
2366
2946
  Logger.log('Start Getting Region');
2367
2947
  return this.regionsPingService.getFastest().pipe(take(1));
2368
2948
  }
2369
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: SignallingService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
2370
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: SignallingService, providedIn: 'root' }); }
2949
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SignallingService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
2950
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SignallingService, providedIn: 'root' }); }
2371
2951
  }
2372
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: SignallingService, decorators: [{
2952
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SignallingService, decorators: [{
2373
2953
  type: Injectable,
2374
2954
  args: [{ providedIn: 'root' }]
2375
2955
  }], ctorParameters: () => [] });
@@ -2784,10 +3364,10 @@ class VideoService extends SubService {
2784
3364
  this.armGestureRetry(audio);
2785
3365
  }
2786
3366
  }
2787
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: VideoService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
2788
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: VideoService, providedIn: 'root' }); }
3367
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: VideoService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
3368
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: VideoService, providedIn: 'root' }); }
2789
3369
  }
2790
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: VideoService, decorators: [{
3370
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: VideoService, decorators: [{
2791
3371
  type: Injectable,
2792
3372
  args: [{ providedIn: 'root' }]
2793
3373
  }], ctorParameters: () => [] });
@@ -3453,10 +4033,10 @@ class WebRtcPlayerService extends SubService {
3453
4033
  Logger.error("Couldn't create answer", e);
3454
4034
  }
3455
4035
  }
3456
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: WebRtcPlayerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
3457
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: WebRtcPlayerService, providedIn: 'root' }); }
4036
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: WebRtcPlayerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
4037
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: WebRtcPlayerService, providedIn: 'root' }); }
3458
4038
  }
3459
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: WebRtcPlayerService, decorators: [{
4039
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: WebRtcPlayerService, decorators: [{
3460
4040
  type: Injectable,
3461
4041
  args: [{ providedIn: 'root' }]
3462
4042
  }], ctorParameters: () => [] });
@@ -3626,10 +4206,10 @@ class StreamAdoptionService {
3626
4206
  this.webRtc.adopt(parked.pc, parked.dc, parked.stream, parked.config ?? {}, parked.dcBuffer);
3627
4207
  return true;
3628
4208
  }
3629
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: StreamAdoptionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
3630
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: StreamAdoptionService, providedIn: 'root' }); }
4209
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: StreamAdoptionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
4210
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: StreamAdoptionService, providedIn: 'root' }); }
3631
4211
  }
3632
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: StreamAdoptionService, decorators: [{
4212
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: StreamAdoptionService, decorators: [{
3633
4213
  type: Injectable,
3634
4214
  args: [{ providedIn: 'root' }]
3635
4215
  }] });
@@ -3697,10 +4277,10 @@ class UnrealCallbackService {
3697
4277
  return observable;
3698
4278
  });
3699
4279
  }
3700
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: UnrealCallbackService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
3701
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: UnrealCallbackService, providedIn: 'root' }); }
4280
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: UnrealCallbackService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
4281
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: UnrealCallbackService, providedIn: 'root' }); }
3702
4282
  }
3703
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: UnrealCallbackService, decorators: [{
4283
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: UnrealCallbackService, decorators: [{
3704
4284
  type: Injectable,
3705
4285
  args: [{ providedIn: 'root' }]
3706
4286
  }] });
@@ -3817,10 +4397,10 @@ class UnrealCommunicatorService extends SubService {
3817
4397
  requestQualityControl() {
3818
4398
  this.sendInputData(new Uint8Array([MessageType.RequestQualityControl]).buffer);
3819
4399
  }
3820
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: UnrealCommunicatorService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
3821
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: UnrealCommunicatorService, providedIn: 'root' }); }
4400
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: UnrealCommunicatorService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
4401
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: UnrealCommunicatorService, providedIn: 'root' }); }
3822
4402
  }
3823
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: UnrealCommunicatorService, decorators: [{
4403
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: UnrealCommunicatorService, decorators: [{
3824
4404
  type: Injectable,
3825
4405
  args: [{ providedIn: 'root' }]
3826
4406
  }], ctorParameters: () => [] });
@@ -3834,10 +4414,10 @@ class UnrealErrorModalComponent {
3834
4414
  close() {
3835
4415
  this.#dialogRef.close();
3836
4416
  }
3837
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: UnrealErrorModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3838
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.2", type: UnrealErrorModalComponent, isStandalone: true, selector: "app-unreal-error-modal", ngImport: i0, template: "<div class=\"unreal-modal\">\n <header class=\"unreal-modal__header\">\n <div\n [attr.data-testid]=\"'unreal-error-header-title'\"\n class=\"unreal-modal__title\"\n >\n Warning\n </div>\n </header>\n <div class=\"unreal-modal__body\">\n <div>{{ dialogData.content }}</div>\n </div>\n <div class=\"unreal-modal__footer\">\n <app-unreal-button (clicked)=\"close()\">Ok</app-unreal-button>\n </div>\n</div>\n", styles: [".unreal-modal{--unrealModalWidth: 422px;--unrealModalMaxWidth: calc(100% - 16px) ;--unrealModalMaxHeight: 80vh;--unrealModalBg: var(--src-surface-container-main, #fff);--unrealModalBoxShadow: 0 16px 36px 4px var(--src-shadow-hard, rgba(71, 85, 105, .24)), 0 1px 4px 0 var(--src-shadow-light, rgba(148, 163, 184, .16));--unrealModalBorderRadius: var(--src-layout-radius-const-rounded-md, 12px);--unrealModalTitleSize: var(--src-font-size-base, 16px);--unrealModalTitleLineHeight: var(--src-font-line-base, 24px);--unrealModalTitleColor: var(--src-text-body-main, #111827);--unrealModalBodyPadding: var(--src-layout-padding-const-lg, 16px) var(--src-layout-padding-const-xl, 24px) var(--src-layout-padding-const-2xl, 32px) var(--src-layout-padding-const-xl, 24px);--unrealModalTitleBorder: none;--unrealModalFooterBorder: 1px solid var(--src-border-container-light, rgba(148, 163, 184, .16));display:grid;grid-template-columns:minmax(0,1fr);grid-template-rows:auto minmax(0,1fr) auto;width:var(--unrealModalWidth);max-width:var(--unrealModalMaxWidth);max-height:var(--unrealModalMaxHeight);margin:auto;background-color:var(--unrealModalBg);border-radius:var(--unrealModalBorderRadius);box-shadow:var(--unrealModalBoxShadow);color:var(--src-text-body-secondary, #4b5563);font-family:var(--src-font-family-header, Inter);font-size:var(--src-font-size-sm, 14px);font-style:normal;font-weight:400;line-height:var(--src-font-line-sm, 20px)}.unreal-modal .unreal-modal__header{position:relative;display:flex;padding:0;width:100%;border-bottom:var(--unrealModalTitleBorder)}.unreal-modal .unreal-modal__body{display:grid;grid-template-columns:minmax(0,1fr);grid-template-rows:auto minmax(0,1fr);gap:var(--src-layout-gap-const-md, 12px);padding:var(--unrealModalBodyPadding);overflow:auto;scrollbar-width:thin;scrollbar-color:var(--src-border-container-basic, rgba(148, 163, 184, .24)) transparent}.unreal-modal .unreal-modal__body .unreal-modal__title{grid-column:1/-1}.unreal-modal .unreal-modal__body::-webkit-scrollbar{width:4px}.unreal-modal .unreal-modal__body::-webkit-scrollbar-track{background:transparent}.unreal-modal .unreal-modal__body::-webkit-scrollbar-thumb{background-color:var(--src-border-container-basic, rgba(148, 163, 184, .24));border-radius:10px}.unreal-modal .unreal-modal__footer{display:flex;justify-content:flex-end;align-items:center;padding:var(--src-layout-padding-const-md, 12px) var(--src-layout-padding-const-xl, 24px) var(--src-layout-padding-const-lg, 16px) var(--src-layout-padding-const-xl, 24px);width:100%;border-top:var(--unrealModalFooterBorder);gap:var(--src-layout-gap-const-sm, 8px)}.unreal-modal .unreal-modal__title{font-size:var(--unrealModalTitleSize);font-weight:600;line-height:var(--unrealModalTitleLineHeight);color:var(--unrealModalTitleColor);word-break:break-word}.unreal-modal .unreal-modal__header{padding:16px 24px 0}\n"], dependencies: [{ kind: "component", type: UnrealButtonComponent, selector: "app-unreal-button", inputs: ["type", "weight", "size", "fullWidth", "isDisabled", "data-testid", "ariaLabel"], outputs: ["clicked"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
4417
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: UnrealErrorModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4418
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.3", type: UnrealErrorModalComponent, isStandalone: true, selector: "app-unreal-error-modal", ngImport: i0, template: "<div class=\"unreal-modal\">\n <header class=\"unreal-modal__header\">\n <div\n [attr.data-testid]=\"'unreal-error-header-title'\"\n class=\"unreal-modal__title\"\n >\n Warning\n </div>\n </header>\n <div class=\"unreal-modal__body\">\n <div>{{ dialogData.content }}</div>\n </div>\n <div class=\"unreal-modal__footer\">\n <app-unreal-button (clicked)=\"close()\">Ok</app-unreal-button>\n </div>\n</div>\n", styles: [".unreal-modal{--unrealModalWidth: 422px;--unrealModalMaxWidth: calc(100% - 16px) ;--unrealModalMaxHeight: 80vh;--unrealModalBg: var(--src-surface-container-main, #fff);--unrealModalBoxShadow: 0 16px 36px 4px var(--src-shadow-hard, rgba(71, 85, 105, .24)), 0 1px 4px 0 var(--src-shadow-light, rgba(148, 163, 184, .16));--unrealModalBorderRadius: var(--src-layout-radius-const-rounded-md, 12px);--unrealModalTitleSize: var(--src-font-size-base, 16px);--unrealModalTitleLineHeight: var(--src-font-line-base, 24px);--unrealModalTitleColor: var(--src-text-body-main, #111827);--unrealModalBodyPadding: var(--src-layout-padding-const-lg, 16px) var(--src-layout-padding-const-xl, 24px) var(--src-layout-padding-const-2xl, 32px) var(--src-layout-padding-const-xl, 24px);--unrealModalTitleBorder: none;--unrealModalFooterBorder: 1px solid var(--src-border-container-light, rgba(148, 163, 184, .16));display:grid;grid-template-columns:minmax(0,1fr);grid-template-rows:auto minmax(0,1fr) auto;width:var(--unrealModalWidth);max-width:var(--unrealModalMaxWidth);max-height:var(--unrealModalMaxHeight);margin:auto;background-color:var(--unrealModalBg);border-radius:var(--unrealModalBorderRadius);box-shadow:var(--unrealModalBoxShadow);color:var(--src-text-body-secondary, #4b5563);font-family:var(--src-font-family-header, Inter);font-size:var(--src-font-size-sm, 14px);font-style:normal;font-weight:400;line-height:var(--src-font-line-sm, 20px)}.unreal-modal .unreal-modal__header{position:relative;display:flex;padding:0;width:100%;border-bottom:var(--unrealModalTitleBorder)}.unreal-modal .unreal-modal__body{display:grid;grid-template-columns:minmax(0,1fr);grid-template-rows:auto minmax(0,1fr);gap:var(--src-layout-gap-const-md, 12px);padding:var(--unrealModalBodyPadding);overflow:auto;scrollbar-width:thin;scrollbar-color:var(--src-border-container-basic, rgba(148, 163, 184, .24)) transparent}.unreal-modal .unreal-modal__body .unreal-modal__title{grid-column:1/-1}.unreal-modal .unreal-modal__body::-webkit-scrollbar{width:4px}.unreal-modal .unreal-modal__body::-webkit-scrollbar-track{background:transparent}.unreal-modal .unreal-modal__body::-webkit-scrollbar-thumb{background-color:var(--src-border-container-basic, rgba(148, 163, 184, .24));border-radius:10px}.unreal-modal .unreal-modal__footer{display:flex;justify-content:flex-end;align-items:center;padding:var(--src-layout-padding-const-md, 12px) var(--src-layout-padding-const-xl, 24px) var(--src-layout-padding-const-lg, 16px) var(--src-layout-padding-const-xl, 24px);width:100%;border-top:var(--unrealModalFooterBorder);gap:var(--src-layout-gap-const-sm, 8px)}.unreal-modal .unreal-modal__title{font-size:var(--unrealModalTitleSize);font-weight:600;line-height:var(--unrealModalTitleLineHeight);color:var(--unrealModalTitleColor);word-break:break-word}.unreal-modal .unreal-modal__header{padding:16px 24px 0}\n"], dependencies: [{ kind: "component", type: UnrealButtonComponent, selector: "app-unreal-button", inputs: ["type", "weight", "size", "fullWidth", "isDisabled", "data-testid", "ariaLabel"], outputs: ["clicked"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3839
4419
  }
3840
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: UnrealErrorModalComponent, decorators: [{
4420
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: UnrealErrorModalComponent, decorators: [{
3841
4421
  type: Component,
3842
4422
  args: [{ selector: 'app-unreal-error-modal', changeDetection: ChangeDetectionStrategy.OnPush, imports: [UnrealButtonComponent], template: "<div class=\"unreal-modal\">\n <header class=\"unreal-modal__header\">\n <div\n [attr.data-testid]=\"'unreal-error-header-title'\"\n class=\"unreal-modal__title\"\n >\n Warning\n </div>\n </header>\n <div class=\"unreal-modal__body\">\n <div>{{ dialogData.content }}</div>\n </div>\n <div class=\"unreal-modal__footer\">\n <app-unreal-button (clicked)=\"close()\">Ok</app-unreal-button>\n </div>\n</div>\n", styles: [".unreal-modal{--unrealModalWidth: 422px;--unrealModalMaxWidth: calc(100% - 16px) ;--unrealModalMaxHeight: 80vh;--unrealModalBg: var(--src-surface-container-main, #fff);--unrealModalBoxShadow: 0 16px 36px 4px var(--src-shadow-hard, rgba(71, 85, 105, .24)), 0 1px 4px 0 var(--src-shadow-light, rgba(148, 163, 184, .16));--unrealModalBorderRadius: var(--src-layout-radius-const-rounded-md, 12px);--unrealModalTitleSize: var(--src-font-size-base, 16px);--unrealModalTitleLineHeight: var(--src-font-line-base, 24px);--unrealModalTitleColor: var(--src-text-body-main, #111827);--unrealModalBodyPadding: var(--src-layout-padding-const-lg, 16px) var(--src-layout-padding-const-xl, 24px) var(--src-layout-padding-const-2xl, 32px) var(--src-layout-padding-const-xl, 24px);--unrealModalTitleBorder: none;--unrealModalFooterBorder: 1px solid var(--src-border-container-light, rgba(148, 163, 184, .16));display:grid;grid-template-columns:minmax(0,1fr);grid-template-rows:auto minmax(0,1fr) auto;width:var(--unrealModalWidth);max-width:var(--unrealModalMaxWidth);max-height:var(--unrealModalMaxHeight);margin:auto;background-color:var(--unrealModalBg);border-radius:var(--unrealModalBorderRadius);box-shadow:var(--unrealModalBoxShadow);color:var(--src-text-body-secondary, #4b5563);font-family:var(--src-font-family-header, Inter);font-size:var(--src-font-size-sm, 14px);font-style:normal;font-weight:400;line-height:var(--src-font-line-sm, 20px)}.unreal-modal .unreal-modal__header{position:relative;display:flex;padding:0;width:100%;border-bottom:var(--unrealModalTitleBorder)}.unreal-modal .unreal-modal__body{display:grid;grid-template-columns:minmax(0,1fr);grid-template-rows:auto minmax(0,1fr);gap:var(--src-layout-gap-const-md, 12px);padding:var(--unrealModalBodyPadding);overflow:auto;scrollbar-width:thin;scrollbar-color:var(--src-border-container-basic, rgba(148, 163, 184, .24)) transparent}.unreal-modal .unreal-modal__body .unreal-modal__title{grid-column:1/-1}.unreal-modal .unreal-modal__body::-webkit-scrollbar{width:4px}.unreal-modal .unreal-modal__body::-webkit-scrollbar-track{background:transparent}.unreal-modal .unreal-modal__body::-webkit-scrollbar-thumb{background-color:var(--src-border-container-basic, rgba(148, 163, 184, .24));border-radius:10px}.unreal-modal .unreal-modal__footer{display:flex;justify-content:flex-end;align-items:center;padding:var(--src-layout-padding-const-md, 12px) var(--src-layout-padding-const-xl, 24px) var(--src-layout-padding-const-lg, 16px) var(--src-layout-padding-const-xl, 24px);width:100%;border-top:var(--unrealModalFooterBorder);gap:var(--src-layout-gap-const-sm, 8px)}.unreal-modal .unreal-modal__title{font-size:var(--unrealModalTitleSize);font-weight:600;line-height:var(--unrealModalTitleLineHeight);color:var(--unrealModalTitleColor);word-break:break-word}.unreal-modal .unreal-modal__header{padding:16px 24px 0}\n"] }]
3843
4423
  }] });
@@ -4233,10 +4813,10 @@ class UnrealEffects {
4233
4813
  // Fails silently if cross-origin
4234
4814
  }
4235
4815
  }
4236
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: UnrealEffects, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
4237
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: UnrealEffects }); }
4816
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: UnrealEffects, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
4817
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: UnrealEffects }); }
4238
4818
  }
4239
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: UnrealEffects, decorators: [{
4819
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: UnrealEffects, decorators: [{
4240
4820
  type: Injectable
4241
4821
  }] });
4242
4822
 
@@ -4388,10 +4968,9 @@ const floatToSmoothPercents = () => (source) => source.pipe(timestamp(), startWi
4388
4968
  * Smoothly interpolates from `start` to `end` over `duration`ms using the
4389
4969
  * animation frame scheduler. Completes when the transition finishes.
4390
4970
  *
4391
- * NOTE: duplicates `smoothTransition` from `@3dsource/utils`. The utils version
4392
- * is missing `takeWhile(... < 1, true)` and never completes — which would
4393
- * deadlock the `concatMap` above. Keep this local copy until utils is fixed.
4394
- * Not exported: internal to `floatToSmoothPercents`.
4971
+ * Deliberately local. The version this was forked from never completed — it
4972
+ * lacked `takeWhile(... < 1, true)` — which would deadlock the `concatMap`
4973
+ * above. Not exported: internal to `floatToSmoothPercents`.
4395
4974
  */
4396
4975
  const smoothTransition = (start, end, duration) => {
4397
4976
  const startTime = animationFrameScheduler.now();
@@ -4753,10 +5332,10 @@ class AFKService extends SubService {
4753
5332
  }
4754
5333
  }, 1000);
4755
5334
  }
4756
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: AFKService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
4757
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: AFKService, providedIn: 'root' }); }
5335
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: AFKService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
5336
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: AFKService, providedIn: 'root' }); }
4758
5337
  }
4759
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: AFKService, decorators: [{
5338
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: AFKService, decorators: [{
4760
5339
  type: Injectable,
4761
5340
  args: [{ providedIn: 'root' }]
4762
5341
  }], ctorParameters: () => [] });
@@ -4806,10 +5385,10 @@ class LoadingProgressService {
4806
5385
  // `switchMap` cancels the inner subscription on every visibility change
4807
5386
  // so stale emissions can't leak through.
4808
5387
  #boundedProgress$;
4809
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: LoadingProgressService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
4810
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: LoadingProgressService, providedIn: 'root' }); }
5388
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: LoadingProgressService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
5389
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: LoadingProgressService, providedIn: 'root' }); }
4811
5390
  }
4812
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: LoadingProgressService, decorators: [{
5391
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: LoadingProgressService, decorators: [{
4813
5392
  type: Injectable,
4814
5393
  args: [{ providedIn: 'root' }]
4815
5394
  }] });
@@ -4895,10 +5474,10 @@ class FreezeFrameService extends SubService {
4895
5474
  progress: 1,
4896
5475
  }));
4897
5476
  }
4898
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: FreezeFrameService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
4899
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: FreezeFrameService, providedIn: 'root' }); }
5477
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: FreezeFrameService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
5478
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: FreezeFrameService, providedIn: 'root' }); }
4900
5479
  }
4901
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: FreezeFrameService, decorators: [{
5480
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: FreezeFrameService, decorators: [{
4902
5481
  type: Injectable,
4903
5482
  args: [{ providedIn: 'root' }]
4904
5483
  }], ctorParameters: () => [] });
@@ -5050,19 +5629,21 @@ class FileReceiverService extends SubService {
5050
5629
  this.reset();
5051
5630
  }
5052
5631
  }
5053
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: FileReceiverService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
5054
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: FileReceiverService, providedIn: 'root' }); }
5632
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: FileReceiverService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
5633
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: FileReceiverService, providedIn: 'root' }); }
5055
5634
  }
5056
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: FileReceiverService, decorators: [{
5635
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: FileReceiverService, decorators: [{
5057
5636
  type: Injectable,
5058
5637
  args: [{ providedIn: 'root' }]
5059
5638
  }], ctorParameters: () => [] });
5060
5639
 
5061
- const filteredLogs = [
5062
- {
5063
- command: 'onChangeSequence',
5064
- },
5065
- ];
5640
+ /**
5641
+ * Matches the normalisation the old `where()` predicate applied to both sides:
5642
+ * trim, collapse space runs to `_`, lowercase.
5643
+ */
5644
+ const normalizeCommand = (value) => String(value).trim().replace(/ +/gi, '_').toLowerCase();
5645
+ /** Commands whose arrival is not worth a console line. */
5646
+ const FILTERED_LOG_COMMANDS = new Set(['onChangeSequence'].map(normalizeCommand));
5066
5647
  class AggregatorService extends SubService {
5067
5648
  constructor() {
5068
5649
  super();
@@ -5149,7 +5730,11 @@ class AggregatorService extends SubService {
5149
5730
  });
5150
5731
  }
5151
5732
  showMessageFromUnreal(json) {
5152
- if (filteredLogs.some(where({ command: json?.command }))) {
5733
+ // The `undefined` check is redundant while the set holds only real command
5734
+ // names, but `where()` ignored undefined criteria outright — keep it so an
5735
+ // entry literally named 'undefined' could never start matching.
5736
+ if (json?.command !== undefined &&
5737
+ FILTERED_LOG_COMMANDS.has(normalizeCommand(json.command))) {
5153
5738
  return;
5154
5739
  }
5155
5740
  Logger.colored(...COLOR_CODES.FROM_UNREAL, json);
@@ -5232,10 +5817,10 @@ class AggregatorService extends SubService {
5232
5817
  Logger.warn(`unrecognized data received, packet ID ${view[0]}`);
5233
5818
  }
5234
5819
  }
5235
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: AggregatorService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
5236
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: AggregatorService, providedIn: 'root' }); }
5820
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: AggregatorService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
5821
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: AggregatorService, providedIn: 'root' }); }
5237
5822
  }
5238
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: AggregatorService, decorators: [{
5823
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: AggregatorService, decorators: [{
5239
5824
  type: Injectable,
5240
5825
  args: [{ providedIn: 'root' }]
5241
5826
  }], ctorParameters: () => [] });
@@ -5396,10 +5981,10 @@ class ConsoleExtensionsService extends SubService {
5396
5981
  Logger.info('emitCommand(command) => send command to Unreal Engine.');
5397
5982
  Logger.info('stressTest({iterations}) => hammer reconnect: repeatedly drop DC/PC and assert recovery.');
5398
5983
  }
5399
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: ConsoleExtensionsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
5400
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: ConsoleExtensionsService, providedIn: 'root' }); }
5984
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ConsoleExtensionsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
5985
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ConsoleExtensionsService, providedIn: 'root' }); }
5401
5986
  }
5402
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: ConsoleExtensionsService, decorators: [{
5987
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ConsoleExtensionsService, decorators: [{
5403
5988
  type: Injectable,
5404
5989
  args: [{ providedIn: 'root' }]
5405
5990
  }], ctorParameters: () => [] });
@@ -5413,30 +5998,48 @@ const RESIZE_DEBOUNCE_MS = 300;
5413
5998
  const FUNCTION_KEY_MIN = 112;
5414
5999
  const FUNCTION_KEY_MAX = 123;
5415
6000
  const TAB_KEY_CODE = 9;
6001
+ // VK_LEFT..VK_DOWN. Held down, these scroll the page while they steer the
6002
+ // camera, so they belong with the keys `suppressBrowserKeys` neutralises.
6003
+ const ARROW_KEY_MIN = 37;
6004
+ const ARROW_KEY_MAX = 40;
6005
+ /** Elements that own the keyboard while focused — never forward to the stream. */
6006
+ const TEXT_ENTRY_SELECTOR = 'input, textarea, select, [contenteditable]:not([contenteditable="false"])';
5416
6007
  class InputService extends SubService {
5417
6008
  constructor() {
5418
6009
  super();
5419
6010
  this.commandsSender = inject(UnrealCommunicatorService);
5420
- this.isDevMode = inject(DevModeService).isDevMode;
6011
+ // The service, not its value: `isDevMode` is a getter over localStorage, and
6012
+ // `setDevMode()` is documented as callable from the console at runtime.
6013
+ this.devModeService = inject(DevModeService);
5421
6014
  this.videoService = inject(VideoService);
5422
6015
  this.unrealConfig = inject(UNREAL_CONFIG, { optional: true });
6016
+ // Physical key names (`KeyboardEvent.code`), which is what the allow-list is
6017
+ // matched against. The numpad digits are deliberately absent: with NumLock
6018
+ // off they *are* the arrows, but the browser reports them under their own
6019
+ // `code`, so enabling them here would forward a second set of arrow keys.
6020
+ // A consumer that wants them passes them to `useKeyboardKeys`.
5423
6021
  this.defaultKeys = [
5424
- KeyboardNumericCode.KeyW,
5425
- KeyboardNumericCode.KeyS,
5426
- KeyboardNumericCode.KeyA,
5427
- KeyboardNumericCode.KeyD,
5428
- KeyboardNumericCode.ArrowUp,
5429
- KeyboardNumericCode.ArrowDown,
5430
- KeyboardNumericCode.ArrowLeft,
5431
- KeyboardNumericCode.ArrowRight,
5432
- KeyboardNumericCode.NumpadAdd,
5433
- KeyboardNumericCode.NumpadSubtract,
5434
- KeyboardNumericCode.ControlRight,
5435
- KeyboardNumericCode.Equal,
5436
- KeyboardNumericCode.Minus,
5437
- KeyboardNumericCode.NumpadDecimal,
6022
+ 'KeyW',
6023
+ 'KeyS',
6024
+ 'KeyA',
6025
+ 'KeyD',
6026
+ 'ArrowUp',
6027
+ 'ArrowDown',
6028
+ 'ArrowLeft',
6029
+ 'ArrowRight',
6030
+ 'NumpadAdd',
6031
+ 'NumpadSubtract',
6032
+ // Both Control keys. Only `ControlRight` was ever declared here, but the
6033
+ // numeric allow-list resolved 17 to `'ControlLeft'`, so the left one is what
6034
+ // actually reached the engine — dropping it now would be a regression for
6035
+ // the more commonly pressed of the two.
6036
+ 'ControlLeft',
6037
+ 'ControlRight',
6038
+ 'Equal',
6039
+ 'Minus',
6040
+ 'NumpadDecimal',
5438
6041
  ];
5439
- this.availableKeys = this.defaultKeys;
6042
+ this.availableKeys = new Set(this.defaultKeys);
5440
6043
  this.options = InputOptions;
5441
6044
  this.reInit$ = new Subject();
5442
6045
  this.deactivatedVideoTrigger$ = NEVER;
@@ -5453,6 +6056,12 @@ class InputService extends SubService {
5453
6056
  this.fingerIds = new Map();
5454
6057
  this.maxByteValue = 255;
5455
6058
  this.fingers = [...INITIAL_FINGER_IDS];
6059
+ // Virtual-key codes for which a KeyDown has been sent and no KeyUp yet. This
6060
+ // is the sole gate on KeyUp, which makes the pairing an invariant instead of
6061
+ // something the filters have to agree on twice: a key the engine was never
6062
+ // told about cannot be released, and a key it was told about is always
6063
+ // released — including when the browser never delivers the `keyup` at all.
6064
+ this.heldKeys = new Set();
5456
6065
  // Reusable wire buffers. Input events fire at up to ~60 Hz; the data channel
5457
6066
  // copies the bytes synchronously on sending (RTCDataChannel.send), so a single
5458
6067
  // buffer per message shape is safe and avoids per-event allocation/GC churn.
@@ -5485,8 +6094,17 @@ class InputService extends SubService {
5485
6094
  .pipe(filter(() => !!this.video), takeUntil$1(this.destroy$))
5486
6095
  .subscribe(() => this.setup());
5487
6096
  }
6097
+ /**
6098
+ * Replaces the forwarded-key allow-list. Takes physical key names
6099
+ * (`KeyboardEvent.code`) such as `'KeyW'` or `'BracketLeft'`.
6100
+ *
6101
+ * Legacy numeric `keyCode` values are still accepted and converted, but they
6102
+ * cannot express every key: `InvertedKeyMap` is many-to-one, so 37 resolves
6103
+ * to `'ArrowLeft'` and 17 to `'ControlLeft'`, and `'Numpad4'`/`'ControlRight'`
6104
+ * are unreachable through the numeric form. Pass names.
6105
+ */
5488
6106
  useKeyboardKeys(keys) {
5489
- this.availableKeys = keys;
6107
+ this.availableKeys = new Set(keys.map((key) => (typeof key === 'number' ? InvertedKeyMap[key] : key)));
5490
6108
  }
5491
6109
  get video() {
5492
6110
  return this.videoService.video;
@@ -5501,6 +6119,10 @@ class InputService extends SubService {
5501
6119
  // Reset touch state to prevent stale identifiers across sessions
5502
6120
  this.fingerIds.clear();
5503
6121
  this.fingers = [...INITIAL_FINGER_IDS];
6122
+ // Same for the key registry: a key held across a reconnect belongs to the
6123
+ // previous engine session, and releasing it into the new one is worse than
6124
+ // dropping it.
6125
+ this.heldKeys.clear();
5504
6126
  merge(fromResizeObserver(video), this.actions$.pipe(ofType(videoAdaptedToContainer)))
5505
6127
  .pipe(debounceTime$1(RESIZE_DEBOUNCE_MS), takeUntil$1(this.deactivatedVideoTrigger$))
5506
6128
  .subscribe(() => this.setupNormalizeAndQuantize(video));
@@ -5588,9 +6210,17 @@ class InputService extends SubService {
5588
6210
  fromEvent(window, 'blur')
5589
6211
  .pipe(filter(() => !!video), takeUntil$1(this.deactivatedVideoTrigger$))
5590
6212
  .subscribe(() => this.releaseMouseButtons(255, 0, 0));
6213
+ // Deliberately *not* gated on `deactivatedVideoTrigger$`: that trigger
6214
+ // includes the visibility-hidden event, so a subscription behind it is torn
6215
+ // down by the very event this one has to react to. `reInit$` alone is
6216
+ // enough to avoid stacking duplicates across `setup()` calls, since
6217
+ // `setup()` fires it before it gets here.
6218
+ merge(fromEvent(window, 'blur'), fromEvent(document, 'visibilitychange').pipe(filter(() => document.visibilityState === 'hidden')))
6219
+ .pipe(takeUntil$1(merge(this.reInit$, this.destroy$)))
6220
+ .subscribe(() => this.releaseKeys());
5591
6221
  this.registerMouseEnterAndLeaveEvents(video);
5592
6222
  this.registerHoveringMouseEvents(video);
5593
- this.registerKeyboardEvents(this.availableKeys);
6223
+ this.registerKeyboardEvents();
5594
6224
  this.registerTouchEvents(video);
5595
6225
  }
5596
6226
  getTouchPositionWithOffset({ clientX, clientY }) {
@@ -5812,62 +6442,108 @@ class InputService extends SubService {
5812
6442
  this.releaseMouseButtons(e.buttons, e.offsetX, e.offsetY);
5813
6443
  });
5814
6444
  }
5815
- // Browser keys do not have a charCode so we only need to test keyCode.
5816
- isKeyCodeBrowserKey(keyCode) {
6445
+ /**
6446
+ * Keys whose default browser action fights the stream: the function keys
6447
+ * (F5 reloads mid-session), Tab (moves focus out of the player) and the
6448
+ * arrows (scroll the page while they steer the camera).
6449
+ *
6450
+ * Takes the *resolved* virtual-key code rather than `event.code`, so a numpad
6451
+ * key acting as an arrow with NumLock off is caught as well.
6452
+ */
6453
+ isBrowserKey(keyCode) {
5817
6454
  return ((keyCode >= FUNCTION_KEY_MIN && keyCode <= FUNCTION_KEY_MAX) ||
6455
+ (keyCode >= ARROW_KEY_MIN && keyCode <= ARROW_KEY_MAX) ||
5818
6456
  keyCode === TAB_KEY_CODE);
5819
6457
  }
5820
6458
  /**
5821
- * The key code Unreal expects, which is the Windows virtual-key code.
6459
+ * The key code Unreal expects — the Windows virtual-key code — or `null` when
6460
+ * the event carries no key worth forwarding.
5822
6461
  *
5823
- * `e.keyCode` is not that on Gecko: Firefox reports the punctuation keys by
5824
- * their ASCII value — `=` as 61 and `-` as 173, where Chrome sends 187 and 189
5825
- * — so zoom-on-plus/minus reached the engine as codes it does not know.
5826
- * `KeyboardNumericCode` is keyed by `e.code` and holds the virtual-key value,
5827
- * so it resolves the same number on every browser; a no-op on Chrome.
6462
+ * `KeyboardNumericCode` covers only the keys whose `event.keyCode` cannot be
6463
+ * trusted; everything else, the numpad included, falls through to the value
6464
+ * the browser resolved.
5828
6465
  *
5829
- * Checked after the right-hand modifiers, which need their own distinct codes
5830
- * that the map does not carry.
6466
+ * Returns `null` for an IME composition — `event.code` still names the
6467
+ * physical key there, so the keystrokes would otherwise reach the engine as
6468
+ * camera movement — and for a `keyCode` of 0, which no engine key uses.
5831
6469
  */
5832
6470
  getKeyCode(e) {
5833
- if (e.keyCode === SpecialKeyCodes.Shift && e.code === 'ShiftRight')
5834
- return SpecialKeyCodes.RightShift;
5835
- else if (e.keyCode === SpecialKeyCodes.Control && e.code === 'ControlRight')
5836
- return SpecialKeyCodes.RightControl;
5837
- else if (e.keyCode === SpecialKeyCodes.Alt && e.code === 'AltRight')
5838
- return SpecialKeyCodes.RightAlt;
5839
- return (KeyboardNumericCode[e.code] ??
5840
- e.keyCode);
5841
- }
5842
- registerKeyboardEvents(availableKeys) {
5843
- const availableKeyNames = availableKeys.map((value) => InvertedKeyMap[value]);
5844
- const keyBlock = (event) => {
5845
- const code = event.code;
5846
- const isInputFieldFocused = event.target?.localName === 'input';
5847
- const keyMatch = availableKeyNames.includes(code);
5848
- if (this.isDevMode && !keyMatch) {
5849
- Logger.warn('Code=>', code, 'Keyboard event is not filtered in DEV Mode. App may behave inappropriately!');
5850
- return isInputFieldFocused;
5851
- }
5852
- return isInputFieldFocused || !keyMatch;
5853
- };
5854
- this.startListenKeys(keyBlock);
6471
+ if (e.keyCode === IME_PROCESS_KEY) {
6472
+ return null;
6473
+ }
6474
+ const keyCode = KeyboardNumericCode[e.code] ??
6475
+ e.keyCode;
6476
+ return keyCode || null;
6477
+ }
6478
+ /**
6479
+ * True while a focused element owns the keyboard. Read off `composedPath()`
6480
+ * rather than `event.target`: retargeting reports the host element for an
6481
+ * `<input>` inside a shadow root, so a `target`-based check never fires in an
6482
+ * app built from web components.
6483
+ */
6484
+ isTextEntryTarget(e) {
6485
+ const target = e.composedPath()[0] ?? e.target;
6486
+ return target instanceof Element && !!target.closest(TEXT_ENTRY_SELECTOR);
6487
+ }
6488
+ /** True when `event.code` is on the allow-list, or dev mode waives it. */
6489
+ isKeyAllowed(e) {
6490
+ if (this.availableKeys.has(e.code)) {
6491
+ return true;
6492
+ }
6493
+ if (!this.devModeService.isDevMode) {
6494
+ return false;
6495
+ }
6496
+ // Once per press, not once per keydown/keyup/keypress and not on autorepeat.
6497
+ if (e.type === 'keydown' && !e.repeat) {
6498
+ Logger.warn('Code=>', e.code, 'Keyboard event is not filtered in DEV Mode. App may behave inappropriately!');
6499
+ }
6500
+ return true;
6501
+ }
6502
+ sendKeyUp(keyCode) {
6503
+ const keyUp = this.keyUpBytes;
6504
+ keyUp[0] = MessageType.KeyUp;
6505
+ keyUp[1] = keyCode;
6506
+ this.sendInputData(keyUp.buffer);
5855
6507
  }
5856
- startListenKeys(_filter) {
5857
- const filter = _filter ?? (() => false);
6508
+ /**
6509
+ * Releases everything the engine still believes is held. Needed because a
6510
+ * `keyup` is not guaranteed to arrive: the window can lose focus to a browser
6511
+ * dialog or another tab first, and then the engine keeps moving the camera
6512
+ * forever.
6513
+ */
6514
+ releaseKeys() {
6515
+ for (const keyCode of this.heldKeys) {
6516
+ this.sendKeyUp(keyCode);
6517
+ }
6518
+ this.heldKeys.clear();
6519
+ }
6520
+ registerKeyboardEvents() {
5858
6521
  fromEvent(document, 'keydown')
5859
6522
  .pipe(takeUntil$1(this.deactivatedVideoTrigger$))
5860
6523
  .subscribe((e) => {
5861
- if (filter(e)) {
6524
+ // A focused field owns the key outright — no forwarding and no
6525
+ // `preventDefault`, or Tab could not move between inputs.
6526
+ if (this.isTextEntryTarget(e)) {
5862
6527
  return;
5863
6528
  }
5864
- const isCtrlOrCommand = e.ctrlKey || e.metaKey;
5865
- // Normalised first: on Firefox the raw `e.keyCode` for these keys is 61
5866
- // and 173, so comparing it left browser zoom to reach the engine too.
5867
6529
  const keyCode = this.getKeyCode(e);
5868
- const isPlusOrMinus = keyCode === KeyboardNumericCode.Equal ||
5869
- keyCode === KeyboardNumericCode.Minus;
5870
- if (isCtrlOrCommand && isPlusOrMinus) {
6530
+ if (keyCode === null) {
6531
+ return;
6532
+ }
6533
+ // Ahead of the allow-list on purpose. None of the browser keys are
6534
+ // forwarded, so a check that sits behind the allow-list can never run,
6535
+ // which is what made `suppressBrowserKeys` a no-op.
6536
+ if (this.options.suppressBrowserKeys && this.isBrowserKey(keyCode)) {
6537
+ e.preventDefault();
6538
+ }
6539
+ if (!this.isKeyAllowed(e)) {
6540
+ return;
6541
+ }
6542
+ // A browser shortcut is in flight (Ctrl+W closes the tab, Ctrl+S opens
6543
+ // a dialog, Ctrl+= zooms). The browser acts on it regardless, so the
6544
+ // engine must not also treat it as gameplay input. Modifier keys are
6545
+ // exempt: their own keydown already reports themselves as pressed.
6546
+ if ((e.ctrlKey || e.metaKey) && !MODIFIER_KEY_CODES.has(e.code)) {
5871
6547
  return;
5872
6548
  }
5873
6549
  const keyDown = this.keyDownBytes;
@@ -5875,38 +6551,46 @@ class InputService extends SubService {
5875
6551
  keyDown[1] = keyCode;
5876
6552
  keyDown[2] = Number(e.repeat);
5877
6553
  this.sendInputData(keyDown.buffer);
6554
+ this.heldKeys.add(keyCode);
5878
6555
  // Backspace isn't a keypress in JS, but UE4 text-entry fields need it
5879
6556
  // to delete characters.
5880
- if (e.keyCode === SpecialKeyCodes.BackSpace) {
6557
+ if (keyCode === SpecialKeyCodes.BackSpace) {
5881
6558
  const data = this.keyPressView;
5882
6559
  data.setUint8(0, MessageType.KeyPress);
5883
6560
  data.setUint16(1, SpecialKeyCodes.BackSpace, true);
5884
6561
  this.sendInputData(data.buffer);
5885
6562
  }
5886
- if (this.options.suppressBrowserKeys &&
5887
- this.isKeyCodeBrowserKey(e.keyCode)) {
5888
- e.preventDefault();
5889
- }
5890
6563
  });
5891
6564
  fromEvent(document, 'keyup')
5892
6565
  .pipe(takeUntil$1(this.deactivatedVideoTrigger$))
5893
6566
  .subscribe((e) => {
5894
- if (filter(e)) {
6567
+ const keyCode = this.getKeyCode(e);
6568
+ if (keyCode === null) {
5895
6569
  return;
5896
6570
  }
5897
- const keyUp = this.keyUpBytes;
5898
- keyUp[0] = MessageType.KeyUp;
5899
- keyUp[1] = this.getKeyCode(e);
5900
- this.sendInputData(keyUp.buffer);
5901
6571
  if (this.options.suppressBrowserKeys &&
5902
- this.isKeyCodeBrowserKey(e.keyCode)) {
6572
+ this.isBrowserKey(keyCode) &&
6573
+ !this.isTextEntryTarget(e)) {
5903
6574
  e.preventDefault();
5904
6575
  }
6576
+ // The registry is the only gate here. Re-running the allow-list and the
6577
+ // focus check would let their state drift between press and release —
6578
+ // press over the video, release with a dialog focused, and the engine
6579
+ // never hears the key came up.
6580
+ if (!this.heldKeys.delete(keyCode)) {
6581
+ return;
6582
+ }
6583
+ this.sendKeyUp(keyCode);
5905
6584
  });
5906
6585
  fromEvent(document, 'keypress')
5907
6586
  .pipe(takeUntil$1(this.deactivatedVideoTrigger$))
5908
6587
  .subscribe((e) => {
5909
- if (filter(e)) {
6588
+ if (this.isTextEntryTarget(e) || !this.isKeyAllowed(e)) {
6589
+ return;
6590
+ }
6591
+ // Deprecated event: the payload is a character, so an IME composition
6592
+ // or a key with no character produces nothing to send.
6593
+ if (e.keyCode === IME_PROCESS_KEY || !e.charCode) {
5910
6594
  return;
5911
6595
  }
5912
6596
  const data = this.keyPressView;
@@ -5915,10 +6599,10 @@ class InputService extends SubService {
5915
6599
  this.sendInputData(data.buffer);
5916
6600
  });
5917
6601
  }
5918
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: InputService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
5919
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: InputService, providedIn: 'root' }); }
6602
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: InputService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
6603
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: InputService, providedIn: 'root' }); }
5920
6604
  }
5921
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: InputService, decorators: [{
6605
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: InputService, decorators: [{
5922
6606
  type: Injectable,
5923
6607
  args: [{ providedIn: 'root' }]
5924
6608
  }], ctorParameters: () => [] });
@@ -5987,46 +6671,36 @@ class StreamStatusTelemetryService extends SubService {
5987
6671
  VideoEncoderQP,
5988
6672
  };
5989
6673
  }
5990
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: StreamStatusTelemetryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
5991
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: StreamStatusTelemetryService, providedIn: 'root' }); }
6674
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: StreamStatusTelemetryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
6675
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: StreamStatusTelemetryService, providedIn: 'root' }); }
5992
6676
  }
5993
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: StreamStatusTelemetryService, decorators: [{
6677
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: StreamStatusTelemetryService, decorators: [{
5994
6678
  type: Injectable,
5995
6679
  args: [{ providedIn: 'root' }]
5996
6680
  }], ctorParameters: () => [] });
5997
6681
 
5998
6682
  /**
5999
- * Pro-grade telemetry for the WebRTC stream lifecycle.
6683
+ * Telemetry for the WebRTC stream lifecycle.
6000
6684
  *
6001
- * Wire-level event taxonomy (intentionally small — all phase-specific
6002
- * details ride inside `stream.phase_ended`'s payload):
6003
- * - `stream.session_started` — once per session, when a real connection
6004
- * attempt begins (`setEstablishingConnection(true)`)
6005
- * - `stream.phase_ended` — once per closer in {@link phaseCatalog};
6006
- * carries phaseNo / id / color /
6007
- * milestones / params / overlays
6008
- * - `stream.sample` — every 5s, window-aggregated (p. 50/p. 95/min/max)
6009
- * - `stream.quality_degraded` — on QP entering `red` (>35)
6010
- * - `stream.quality_recovered` — on QP leaving `red`
6011
- * - `stream.fps_low` — when the window FPS p50 < 70% of the target
6012
- * - `stream.session_ended` — once per session, on disconnect/pagehide/restart
6685
+ * The event taxonomy is deliberately small — phase-specific detail rides inside
6686
+ * `stream.phase_ended`'s payload rather than becoming its own event type:
6687
+ * `session_started`, `phase_ended` (once per closer in {@link phaseCatalog}),
6688
+ * `sample` (every 5s, window-aggregated), `quality_degraded` /
6689
+ * `quality_recovered` (QP entering/leaving `red`), `fps_low`, `session_ended`.
6690
+ * Every wire shape lives in `interfaces/stream-telemetry-events.interface.ts`,
6691
+ * and `track()` / `emitLifecycle()` are typed against it — a drifting payload is
6692
+ * a compile error.
6013
6693
  *
6014
- * Every event carries `sessionId` + `userId`. The `sessionId` IS the stream's
6015
- * connection id (`selectCirrusConnectionId`) — the same value the debug overlay
6016
- * shows as "Connection Id" — so a telemetry session can be matched to a Cirrus
6017
- * player session by id. Since that id only exists once the orchestration
6018
- * answered, the first events are queued and flushed with it (see
6694
+ * `sessionId` IS the stream's connection id (`selectCirrusConnectionId`), the
6695
+ * value the debug overlay labels "Connection Id", so a telemetry session can be
6696
+ * matched to a Cirrus player session. That id only exists once orchestration
6697
+ * answered, so early events are queued and flushed with it (see
6019
6698
  * {@link SessionState.wireId}); a session that never got one falls back to a
6020
- * locally generated uuid. Samples carry a monotonic `seq` for server-side
6021
- * ordering & dedup. Durations use `performance.now()` — immune to wall-clock
6022
- * jumps.
6699
+ * local uuid. Samples carry a monotonic `seq` for server-side ordering and
6700
+ * dedup, and durations use `performance.now()` to survive wall-clock jumps.
6023
6701
  *
6024
- * NOTE: For `stream.session_ended` on the tab close to survive teardown,
6025
- * the Mixpanel client must be configured with `api_transport: 'sendBeacon'`.
6026
- *
6027
- * Every wire shape lives in `interfaces/stream-telemetry-events.interface.ts`
6028
- * — `track()` / `emitLifecycle()` are typed against it, so a payload that
6029
- * drifts from the contract is a compile error.
6702
+ * NOTE: `stream.session_ended` only survives tab close if the Mixpanel client
6703
+ * is configured with `api_transport: 'sendBeacon'`.
6030
6704
  */
6031
6705
  /** Trim a resource URL to its path: drop origin and query string so the
6032
6706
  * emitted name is short, stable, and not a PII-bearing query. */
@@ -6351,45 +7025,31 @@ class StreamStatusTelemetryService2 {
6351
7025
  }
6352
7026
  }
6353
7027
  init() {
6354
- // Start a new session when a real connection attempt BEGINS, not on
6355
- // every `initSignalling` dispatch. `initSignalling` is fired multiple
6356
- // times per page load (the unreal-scene effect, `startStream`, the
6357
- // reconnect effect), but the connection layer collapses them into one
6358
- // attempt via `connectToSignaling$` (`exhaustMap` + `!cirrusConnected`
6359
- // filter). `setEstablishingConnection(true)` is dispatched exactly once
6360
- // per real attempt, by that effect's `startWith(...)` — the SINGLE
6361
- // dispatch site, downstream of the guard and ahead of both branches
6362
- // (prefetch adoption / fresh connect). So binding here yields one session
6363
- // per attempt instead of a burst of `restart` churn sessions. Genuine
6364
- // reconnects (e.g. DataChannelTimeout) re-enter the same path and
6365
- // correctly end the prior session as `restart` + start a new one.
7028
+ // Bound to `setEstablishingConnection(true)`, not `initSignalling`, which
7029
+ // fires several times per page load (unreal-scene effect, `startStream`,
7030
+ // reconnect). `connectToSignaling$` collapses those into one attempt
7031
+ // (`exhaustMap` + `!cirrusConnected`), and its `startWith(...)` is the
7032
+ // single dispatch site for `true` — downstream of that guard and ahead of
7033
+ // both branches (prefetch adoption / fresh connect). So this yields one
7034
+ // session per real attempt instead of a burst of `restart` churn, while
7035
+ // genuine reconnects still end the prior session as `restart`.
6366
7036
  //
6367
- // Do NOT add a second dispatch site: this binding turns every extra
6368
- // `true` into `endSession('restart')` + a new session, which throws away
6369
- // the first session's page-load measurement and its prefetch adoption.
7037
+ // Do NOT add a second dispatch site: every extra `true` becomes
7038
+ // `endSession('restart')` + a new session, discarding the first session's
7039
+ // page-load measurement and its prefetch adoption.
6370
7040
  this.actions$
6371
7041
  .pipe(ofType(setEstablishingConnection), filter$1(({ value }) => value === true), takeUntil$1(this.destroy$))
6372
7042
  .subscribe(() => this.startSession());
6373
- // Action-stream end triggers (NOT a state-observable subscription).
6374
- //
6375
- // We can't observe the cirrus connection flag via `pairwise`
6376
- // because the `destroyUnrealScene` reducer resets the entire
6377
- // unreal state to `initialState` in one step — `cirrusConnected`
6378
- // flips from `true` to `false` AND `disconnectReason` resets to
6379
- // `None` simultaneously. A state-observable subscriber would fire
6380
- // synchronously inside that reducer's emission, before the
6381
- // follow-up effect (`destroyUnrealScene → destroyRemoteConnections({Destroy})`)
6382
- // gets a chance to write `Destroy` back into state — so the
6383
- // subscriber would see `None` and end as `'disconnected'`.
6384
- //
6385
- // Subscribing to the actions instead lets us pick the right
6386
- // reason per dispatched action, and `endSession` is idempotent
6387
- // (`if (!this.session) return`) so any cascading second trigger
6388
- // is a no-op. Coverage:
7043
+ // End triggers read the action stream, not the state, on purpose. The
7044
+ // `destroyUnrealScene` reducer resets the whole unreal state in one step,
7045
+ // so `cirrusConnected` flips to `false` while `disconnectReason` resets to
7046
+ // `None`. A state subscriber fires synchronously inside that emission —
7047
+ // before the follow-up effect writes `Destroy` back — and would report
7048
+ // `'disconnected'`. Per-action subscriptions pick the right reason instead,
7049
+ // and `endSession` is idempotent, so a cascading second trigger is a no-op:
6389
7050
  // - `destroyUnrealScene` → `'destroy'` (route change, removed from DOM)
6390
- // - `setCirrusDisconnected` → `'disconnected'` (WS close, AFK,
6391
- // manual drop, WebRTCError, etc. — granular cause + message
6392
- // read from current state in `endSession`)
7051
+ // - `setCirrusDisconnected` → `'disconnected'` (WS close, AFK, manual
7052
+ // drop, WebRTCError — granular cause read from state in `endSession`)
6393
7053
  this.actions$
6394
7054
  .pipe(ofType(destroyUnrealScene), takeUntil$1(this.destroy$))
6395
7055
  .subscribe(() => this.endSession('destroy'));
@@ -6685,11 +7345,10 @@ class StreamStatusTelemetryService2 {
6685
7345
  /**
6686
7346
  * Emit the region / signaling / webrtc phases for an ADOPTED connection.
6687
7347
  *
6688
- * The prefetch script established the connection in PARALLEL with page load,
6689
- * so the phases are positioned at their real (anchor-relative) spans — shifted
6690
- * LEFT to overlap the HTML-loading / route-idle rows — rather than sequentially
6691
- * after them. That's the whole point of the prefetch: the handshake already
6692
- * happened while the page was loading. Each phase ships:
7348
+ * The prefetch script established the connection in parallel with page load,
7349
+ * so phases sit at their real anchor-relative spans — shifted left to overlap
7350
+ * the HTML-loading / route-idle rows instead of following them. Each phase
7351
+ * ships:
6693
7352
  * - `duration` — the real sub-step time the prefetch spent.
6694
7353
  * - `spanStartMs` — anchor-relative start, so the viewer left-shifts the bar.
6695
7354
  * - `adoptionAtMs` — where Angular took over (the next phase, Unreal
@@ -6955,22 +7614,15 @@ class StreamStatusTelemetryService2 {
6955
7614
  }
6956
7615
  }
6957
7616
  /**
6958
- * Aggregate all resources the browser has loaded so far
6959
- * (`performance.getEntriesByType('resource')`), grouped by `initiatorType`,
6960
- * **split by `window.load`**:
7617
+ * Aggregates `performance.getEntriesByType('resource')` by `initiatorType`,
7618
+ * split at `window.load`: `resources` for entries before `loadEventEnd` (the
7619
+ * actual page weight) and `lazyResources` for those after it (typically the
7620
+ * route's lazy chunks and manifests, downloaded when the user navigates to the
7621
+ * sub-route the streamer lives on). If `loadEventEnd` is 0 or unknown,
7622
+ * everything lands in `resources` and `lazyResources` is null.
6961
7623
  *
6962
- * - `resources` — fetched before `loadEventEnd` (= part of the actual
6963
- * page weight). What you'd traditionally call "page load."
6964
- * - `lazyResources` — fetched after `loadEventEnd` (= typically the
6965
- * route's lazy-loaded chunks, JSON manifests etc. that download
6966
- * when the user navigates to a sub-route the streamer lives on).
6967
- *
6968
- * When `loadEventEnd` is 0/unknown, everything goes into `resources`
6969
- * and `lazyResources` is null — old behavior.
6970
- *
6971
- * `transferSize === 0 && decodedBodySize > 0` means the resource came from
6972
- * the HTTP cache — counted separately so cached hits don't inflate
6973
- * the "bytes on the wire" metric.
7624
+ * `transferSize === 0 && decodedBodySize > 0` means an HTTP-cache hit, counted
7625
+ * separately so it does not inflate the bytes-on-the-wire metric.
6974
7626
  */
6975
7627
  collectResourceTotals(loadEventEnd) {
6976
7628
  try {
@@ -7092,10 +7744,10 @@ class StreamStatusTelemetryService2 {
7092
7744
  emit(event, data) {
7093
7745
  this.store.dispatch(trackTelemetryData({ event, data }));
7094
7746
  }
7095
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: StreamStatusTelemetryService2, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
7096
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: StreamStatusTelemetryService2, providedIn: 'root' }); }
7747
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: StreamStatusTelemetryService2, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
7748
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: StreamStatusTelemetryService2, providedIn: 'root' }); }
7097
7749
  }
7098
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: StreamStatusTelemetryService2, decorators: [{
7750
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: StreamStatusTelemetryService2, decorators: [{
7099
7751
  type: Injectable,
7100
7752
  args: [{ providedIn: 'root' }]
7101
7753
  }], ctorParameters: () => [] });
@@ -7181,10 +7833,10 @@ class FileHandlerService {
7181
7833
  return observable;
7182
7834
  });
7183
7835
  }
7184
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: FileHandlerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
7185
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: FileHandlerService, providedIn: 'root' }); }
7836
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: FileHandlerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
7837
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: FileHandlerService, providedIn: 'root' }); }
7186
7838
  }
7187
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: FileHandlerService, decorators: [{
7839
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: FileHandlerService, decorators: [{
7188
7840
  type: Injectable,
7189
7841
  args: [{ providedIn: 'root' }]
7190
7842
  }] });
@@ -7273,86 +7925,31 @@ class AnalyticsService extends SubService {
7273
7925
  Logger.log('Analytics Event:', name, params);
7274
7926
  this.store.dispatch(saveAnalyticsEvent({ event: { name, params } }));
7275
7927
  }
7276
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: AnalyticsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
7277
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: AnalyticsService, providedIn: 'root' }); }
7928
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: AnalyticsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
7929
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: AnalyticsService, providedIn: 'root' }); }
7278
7930
  }
7279
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: AnalyticsService, decorators: [{
7931
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: AnalyticsService, decorators: [{
7280
7932
  type: Injectable,
7281
7933
  args: [{ providedIn: 'root' }]
7282
7934
  }], ctorParameters: () => [] });
7283
7935
 
7284
7936
  /**
7285
- * Adaptive FPS controller that monitors the incoming WebRTC video stream
7286
- * quality and dynamically adjusts Unreal Engine's `t.MaxFPS` console variable.
7287
- *
7288
- * ## How it works
7289
- *
7290
- * The service subscribes to `VideoService.videoStats$` (emitted every 250 ms)
7291
- * and collects samples of FPS, bitrate, and QP (Quantization Parameter).
7292
- * Every {@link SAMPLE_WINDOW} ticks (~2 s) it averages the collected samples
7293
- * and feeds them into a set of **fuzzy-logic membership functions** that
7294
- * classify each metric into overlapping quality bands.
7295
- *
7296
- * ### Input signals
7297
- *
7298
- * **1. FPS ratio** = `actualFPS / currentTargetFPS` (0..1+)
7299
- *
7300
- * | Band | Range | Meaning |
7301
- * |--------|---------------------|-----------------------------------------------|
7302
- * | low | ratio < 0.7 → 1.0, linear ramp 0.7..0.9 → 0 | Stream cannot keep up with the target |
7303
- * | okays | triangle 0.8..1.0, peak at 0.95 | Stream roughly matches the target |
7304
- * | high | ratio 0.9..1.0 → ramp, >1.0 → 1.0 | Stream meets or exceeds the target |
7937
+ * Adaptive FPS controller: watches the incoming WebRTC stream and steers Unreal's
7938
+ * `t.MaxFPS` between the discrete {@link FPS_STEPS}.
7305
7939
  *
7306
- * **2. Bitrate load** = `currentBitrate / MAX_BITRATE` (0..1+)
7940
+ * Every {@link SAMPLE_WINDOW} ticks of `VideoService.videoStats$` (250 ms each)
7941
+ * it averages FPS, bitrate and QP, runs them through the fuzzy membership
7942
+ * functions below, and picks a decision by first-match rules. Fuzzy bands rather
7943
+ * than hard thresholds so a metric sitting on a boundary does not oscillate.
7307
7944
  *
7308
- * `MAX_BITRATE` = 20 Mbit/s — the reference ceiling for the encoder.
7945
+ * Cooldowns between steps are deliberately asymmetric: stepping *down* again is
7946
+ * allowed after {@link COOLDOWN_DECREASE_AFTER_DECREASE_MS}, but stepping back
7947
+ * *up* after a decrease waits {@link COOLDOWN_INCREASE_AFTER_DECREASE_MS} — a
7948
+ * stream that just failed its target is likely to fail it again.
7309
7949
  *
7310
- * | Band | Range | Meaning |
7311
- * |--------|-------------------------------------------------|-----------------------------------------------|
7312
- * | high | load 0.5..0.8 → ramp, >0.8 → 1.0 | Encoder is using most of the available bandwidth |
7313
- * | not high | load < 0.5 → 0.0 | Bandwidth headroom is available |
7314
- *
7315
- * **3. Quality** — mapped from QP via `mapQpToQuality()` (see {@link Quality}):
7316
- *
7317
- * | Quality | QP range | Meaning |
7318
- * |----------|-----------|-------------------------------------------------|
7319
- * | `lime` | QP <= 26 | Good — encoder has enough headroom |
7320
- * | `orange` | QP 27..35 | Fair — encoder is under moderate pressure |
7321
- * | `red` | QP > 35 | Poor — heavy compression, visible artifacts |
7322
- *
7323
- * ### Decision rules (evaluated in priority order, first match wins)
7324
- *
7325
- * | # | Condition | Decision |
7326
- * |---|------------------------------------------------------------|--------------|
7327
- * | 1 | FPS ratio is **low** (membership > 0.5) | **decrease** |
7328
- * | 2 | FPS ratio is **ok** AND quality is `red` | **decrease** |
7329
- * | 3 | FPS ratio is **ok** AND bitrate **high** AND quality is `orange` | **hold** |
7330
- * | 4 | FPS ratio is **high** AND quality is `lime` AND bitrate **not high** | **increase** |
7331
- * | — | Everything else | **hold** |
7332
- *
7333
- * ## Cooldown-based throttling
7334
- *
7335
- * The service continuously evaluates fuzzy rules in every sample window.
7336
- * After each FPS step change it enforces a cooldown before the next
7337
- * change can be applied. The cooldown duration depends on the
7338
- * (previous decision → next decision) pair:
7339
- *
7340
- * | Previous | Next | Cooldown |
7341
- * |-----------|-----------|-----------------------------------------------------|
7342
- * | decrease | decrease | {@link COOLDOWN_DECREASE_AFTER_DECREASE_MS} (2 s) |
7343
- * | decrease | increase | {@link COOLDOWN_INCREASE_AFTER_DECREASE_MS} (10 s) |
7344
- * | increase | decrease | {@link COOLDOWN_AFTER_INCREASE_MS} (2 s) |
7345
- * | increase | increase | {@link COOLDOWN_AFTER_INCREASE_MS} (2 s) |
7346
- *
7347
- * This means the system reacts quickly after an increase (2 s in any
7348
- * direction) but is cautious about stepping back up after a decrease
7349
- * (10 s), while still allowing consecutive decreases at 2 s intervals.
7350
- *
7351
- * ## FPS dispatch
7352
- *
7353
- * FPS changes are dispatched via the NgRx `setMaxFps` action, which triggers
7354
- * the existing `UnrealEffects.setMaxFps$` effect that sends the
7355
- * `t.MaxFPS <value>` console command to Unreal Engine.
7950
+ * Changes go out as the `setMaxFps` action, not a direct command: the existing
7951
+ * `UnrealEffects.setMaxFps$` effect turns it into the `t.MaxFPS <value>` console
7952
+ * command.
7356
7953
  */
7357
7954
  class FpsMonitorService extends SubService {
7358
7955
  constructor() {
@@ -7453,15 +8050,7 @@ class FpsMonitorService extends SubService {
7453
8050
  const decision = this.evaluateDecision(avg, currentTarget);
7454
8051
  this.upgradeFps(decision, avg.qp);
7455
8052
  }
7456
- /**
7457
- * Combines fuzzy membership values into a single discrete decision.
7458
- * Rules are evaluated in priority order — first match wins.
7459
- *
7460
- * Inputs:
7461
- * - `fpsRatio` = avgFPS / currentTargetFPS (e.g., 45/60 = 0.75)
7462
- * - `bitrateLoad` = avgBitrate / 20 Mbit/s (e.g., 12M/20M = 0.6)
7463
- * - `quality` = mapQpToQuality(avgQP) → 'lime' | 'orange' | 'red'
7464
- */
8053
+ /** Rules are evaluated in priority order — first match wins. */
7465
8054
  evaluateDecision(avg, currentTarget) {
7466
8055
  const referenceTarget = currentTarget > 0 ? currentTarget : this.UNLIMITED_REFERENCE_FPS;
7467
8056
  const fpsRatio = avg.fps / referenceTarget;
@@ -7501,21 +8090,10 @@ class FpsMonitorService extends SubService {
7501
8090
  }
7502
8091
  return 'hold';
7503
8092
  }
7504
- // ---------------------------------------------------------------------------
7505
- // Fuzzy membership functions
7506
- //
7507
- // Each function maps a normalised input (0..1+) to a membership degree (0..1).
7508
- // Overlapping trapezoid/triangle shapes let multiple categories be partially
7509
- // active at the same time, which makes the rules less brittle than hard thresholds.
7510
- // ---------------------------------------------------------------------------
7511
- /**
7512
- * FPS ratio membership "low".
7513
- * Input: a ratio = actualFPS / targetFPS (e.g., 42/60 = 0.70).
7514
- *
7515
- * ratio < 0.7 → 1.0 (clearly failing, e.g., 40/60)
7516
- * ratio 0.7..0.9 → linear ramp down (e.g., 0.8 → 0.5)
7517
- * a ratio > 0.9 → 0.0 (keeping up)
7518
- */
8093
+ // Fuzzy membership functions: normalised input (0..1+) → membership degree
8094
+ // (0..1). The shapes overlap on purpose, so several bands can be partially
8095
+ // active at once and the rules stay less brittle than hard thresholds.
8096
+ /** "low" — the stream cannot keep up with its target. */
7519
8097
  membershipFpsLow(ratio) {
7520
8098
  if (ratio < 0.7)
7521
8099
  return 1.0;
@@ -7523,15 +8101,7 @@ class FpsMonitorService extends SubService {
7523
8101
  return 0.0;
7524
8102
  return (0.9 - ratio) / 0.2;
7525
8103
  }
7526
- /**
7527
- * FPS ratio membership "ok" — triangle shape.
7528
- * Input: ratio = actualFPS / targetFPS.
7529
- *
7530
- * ratio < 0.8 → 0.0
7531
- * ratio 0.8..0.95 → ramp up, peak at 0.95 (e.g., 57/60)
7532
- * ratio 0.95..1.0 → ramp down
7533
- * a ratio > 1.0 → 0.0
7534
- */
8104
+ /** "ok" — triangle peaking just under the target. */
7535
8105
  membershipFpsOk(ratio) {
7536
8106
  if (ratio < 0.8)
7537
8107
  return 0.0;
@@ -7541,14 +8111,7 @@ class FpsMonitorService extends SubService {
7541
8111
  return (1.0 - ratio) / 0.05;
7542
8112
  return 0.0;
7543
8113
  }
7544
- /**
7545
- * FPS ratio membership "high".
7546
- * Input: ratio = actualFPS / targetFPS.
7547
- *
7548
- * ratio < 0.9 → 0.0
7549
- * ratio 0.9..1.0 → linear ramp up (e.g., 0.95 → 0.5)
7550
- * a ratio >= 1.0 → 1.0 (meeting or exceeding target, e.g., 60/60)
7551
- */
8114
+ /** "high" — the stream meets or exceeds its target. */
7552
8115
  membershipFpsHigh(ratio) {
7553
8116
  if (ratio < 0.9)
7554
8117
  return 0.0;
@@ -7556,14 +8119,7 @@ class FpsMonitorService extends SubService {
7556
8119
  return 1.0;
7557
8120
  return (ratio - 0.9) / 0.1;
7558
8121
  }
7559
- /**
7560
- * Bitrate load membership "high".
7561
- * Input: a load = currentBitrate / MAX_BITRATE (20 Mbit/s).
7562
- *
7563
- * load < 0.5 → 0.0 (below 10 Mbit/s — plenty of headroom)
7564
- * load 0.5..0.8 → linear ramp (e.g., 13M/20M = 0.65 → 0.5)
7565
- * load > 0.8 → 1.0 (above 16 Mbit/s — near capacity)
7566
- */
8122
+ /** "high" — the encoder is using most of the available bandwidth. */
7567
8123
  membershipBitrateHigh(load) {
7568
8124
  if (load < 0.5)
7569
8125
  return 0.0;
@@ -7643,10 +8199,10 @@ class FpsMonitorService extends SubService {
7643
8199
  }
7644
8200
  return { fps: fps / len, bitrate: bitrate / len, qp: qp / len };
7645
8201
  }
7646
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: FpsMonitorService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
7647
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: FpsMonitorService, providedIn: 'root' }); }
8202
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: FpsMonitorService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
8203
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: FpsMonitorService, providedIn: 'root' }); }
7648
8204
  }
7649
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: FpsMonitorService, decorators: [{
8205
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: FpsMonitorService, decorators: [{
7650
8206
  type: Injectable,
7651
8207
  args: [{ providedIn: 'root' }]
7652
8208
  }], ctorParameters: () => [] });
@@ -7681,10 +8237,10 @@ class AfkTimeoutModalComponent {
7681
8237
  resetAfk() {
7682
8238
  this.#afkService.reset();
7683
8239
  }
7684
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: AfkTimeoutModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
7685
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", type: AfkTimeoutModalComponent, isStandalone: true, selector: "app-afk-timeout-modal", inputs: { uiClasses: { classPropertyName: "uiClasses", publicName: "uiClasses", isSignal: true, isRequired: false, transformFunction: null }, contentTemplate: { classPropertyName: "contentTemplate", publicName: "contentTemplate", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@if (isShowAfkPopup()) {\n <div\n (click)=\"resetAfk()\"\n [class]=\"scrimSkin()\"\n class=\"afk-anchor clickableState\"\n id=\"videoPlayOverlay\"\n >\n <div [class]=\"cardSkin()\">\n @if (contentTemplate(); as tpl) {\n <ng-container\n [ngTemplateOutlet]=\"tpl\"\n [ngTemplateOutletContext]=\"{ $implicit: slotContext }\"\n />\n } @else {\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 <app-unreal-button\n [fullWidth]=\"true\"\n [data-testid]=\"'continue-session'\"\n [class]=\"buttonSkin()\"\n weight=\"primary\"\n size=\"lg\"\n >\n Continue\n </app-unreal-button>\n }\n </div>\n </div>\n}\n", styles: [".afk-anchor{position:absolute;z-index:30;top:0;width:100%;height:100%}.afk-scrim{font-size:1.8em;font-family:var(--unreal-font-family, var(--src-font-family-body, system-ui, sans-serif));background-color:var(--unreal-surface-scrim, rgba(100, 100, 100, .7))}.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(--unreal-radius-card, var(--src-border-rounded-parent, 8px));background:var(--unreal-surface-card, var(--src-surface-container-main, #fff));box-shadow:var(--unreal-shadow-card, 0 26px 80px 0 rgba(0, 0, 0, .2), 0 0 1px 0 rgba(0, 0, 0, .2))}.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}.text-number{color:var(--unreal-text-main, #1f2937);text-align:center;font-family:var(--unreal-font-family, var(--src-font-family-body, system-ui, sans-serif));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(--unreal-font-family, var(--src-font-family-body, system-ui, sans-serif));font-size:var(--src-font-size-sm, 14px);font-style:normal;font-weight:400;line-height:24px;color:var(--unreal-text-secondary, var(--src-text-body-secondary, #6b7280))}.resume-box__text .resume-box__heading{color:var(--unreal-text-main, var(--src-text-body-main, #1f2937));text-align:center;font-family:var(--unreal-font-family, var(--src-font-family-body, system-ui, sans-serif));font-size:18px;font-style:normal;font-weight:500;line-height:26px;margin-bottom:8px}\n"], dependencies: [{ kind: "component", type: UnrealButtonComponent, selector: "app-unreal-button", inputs: ["type", "weight", "size", "fullWidth", "isDisabled", "data-testid", "ariaLabel"], outputs: ["clicked"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
8240
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: AfkTimeoutModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
8241
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: AfkTimeoutModalComponent, isStandalone: true, selector: "app-afk-timeout-modal", inputs: { uiClasses: { classPropertyName: "uiClasses", publicName: "uiClasses", isSignal: true, isRequired: false, transformFunction: null }, contentTemplate: { classPropertyName: "contentTemplate", publicName: "contentTemplate", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@if (isShowAfkPopup()) {\n <div\n (click)=\"resetAfk()\"\n [class]=\"scrimSkin()\"\n class=\"afk-anchor clickableState\"\n id=\"videoPlayOverlay\"\n >\n <div [class]=\"cardSkin()\">\n @if (contentTemplate(); as tpl) {\n <ng-container\n [ngTemplateOutlet]=\"tpl\"\n [ngTemplateOutletContext]=\"{ $implicit: slotContext }\"\n />\n } @else {\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 <app-unreal-button\n [fullWidth]=\"true\"\n [data-testid]=\"'continue-session'\"\n [class]=\"buttonSkin()\"\n weight=\"primary\"\n size=\"lg\"\n >\n Continue\n </app-unreal-button>\n }\n </div>\n </div>\n}\n", styles: [".afk-anchor{position:absolute;z-index:30;top:0;width:100%;height:100%}.afk-scrim{font-size:1.8em;font-family:var(--unreal-font-family, var(--src-font-family-body, system-ui, sans-serif));background-color:var(--unreal-surface-scrim, rgba(100, 100, 100, .7))}.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(--unreal-radius-card, var(--src-border-rounded-parent, 8px));background:var(--unreal-surface-card, var(--src-surface-container-main, #fff));box-shadow:var(--unreal-shadow-card, 0 26px 80px 0 rgba(0, 0, 0, .2), 0 0 1px 0 rgba(0, 0, 0, .2))}.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}.text-number{color:var(--unreal-text-main, #1f2937);text-align:center;font-family:var(--unreal-font-family, var(--src-font-family-body, system-ui, sans-serif));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(--unreal-font-family, var(--src-font-family-body, system-ui, sans-serif));font-size:var(--src-font-size-sm, 14px);font-style:normal;font-weight:400;line-height:24px;color:var(--unreal-text-secondary, var(--src-text-body-secondary, #6b7280))}.resume-box__text .resume-box__heading{color:var(--unreal-text-main, var(--src-text-body-main, #1f2937));text-align:center;font-family:var(--unreal-font-family, var(--src-font-family-body, system-ui, sans-serif));font-size:18px;font-style:normal;font-weight:500;line-height:26px;margin-bottom:8px}\n"], dependencies: [{ kind: "component", type: UnrealButtonComponent, selector: "app-unreal-button", inputs: ["type", "weight", "size", "fullWidth", "isDisabled", "data-testid", "ariaLabel"], outputs: ["clicked"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
7686
8242
  }
7687
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: AfkTimeoutModalComponent, decorators: [{
8243
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: AfkTimeoutModalComponent, decorators: [{
7688
8244
  type: Component,
7689
8245
  args: [{ selector: 'app-afk-timeout-modal', changeDetection: ChangeDetectionStrategy.OnPush, imports: [UnrealButtonComponent, NgTemplateOutlet], template: "@if (isShowAfkPopup()) {\n <div\n (click)=\"resetAfk()\"\n [class]=\"scrimSkin()\"\n class=\"afk-anchor clickableState\"\n id=\"videoPlayOverlay\"\n >\n <div [class]=\"cardSkin()\">\n @if (contentTemplate(); as tpl) {\n <ng-container\n [ngTemplateOutlet]=\"tpl\"\n [ngTemplateOutletContext]=\"{ $implicit: slotContext }\"\n />\n } @else {\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 <app-unreal-button\n [fullWidth]=\"true\"\n [data-testid]=\"'continue-session'\"\n [class]=\"buttonSkin()\"\n weight=\"primary\"\n size=\"lg\"\n >\n Continue\n </app-unreal-button>\n }\n </div>\n </div>\n}\n", styles: [".afk-anchor{position:absolute;z-index:30;top:0;width:100%;height:100%}.afk-scrim{font-size:1.8em;font-family:var(--unreal-font-family, var(--src-font-family-body, system-ui, sans-serif));background-color:var(--unreal-surface-scrim, rgba(100, 100, 100, .7))}.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(--unreal-radius-card, var(--src-border-rounded-parent, 8px));background:var(--unreal-surface-card, var(--src-surface-container-main, #fff));box-shadow:var(--unreal-shadow-card, 0 26px 80px 0 rgba(0, 0, 0, .2), 0 0 1px 0 rgba(0, 0, 0, .2))}.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}.text-number{color:var(--unreal-text-main, #1f2937);text-align:center;font-family:var(--unreal-font-family, var(--src-font-family-body, system-ui, sans-serif));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(--unreal-font-family, var(--src-font-family-body, system-ui, sans-serif));font-size:var(--src-font-size-sm, 14px);font-style:normal;font-weight:400;line-height:24px;color:var(--unreal-text-secondary, var(--src-text-body-secondary, #6b7280))}.resume-box__text .resume-box__heading{color:var(--unreal-text-main, var(--src-text-body-main, #1f2937));text-align:center;font-family:var(--unreal-font-family, var(--src-font-family-body, system-ui, sans-serif));font-size:18px;font-style:normal;font-weight:500;line-height:26px;margin-bottom:8px}\n"] }]
7690
8246
  }], propDecorators: { uiClasses: [{ type: i0.Input, args: [{ isSignal: true, alias: "uiClasses", required: false }] }], contentTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "contentTemplate", required: false }] }] } });
@@ -7696,10 +8252,10 @@ class FreezeFrameComponent {
7696
8252
  this.combinedFreeze = this.#store.selectSignal(selectFreezeFrameCombinedDataUrl);
7697
8253
  }
7698
8254
  #store;
7699
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: FreezeFrameComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
7700
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", 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 }); }
8255
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: FreezeFrameComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
8256
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", 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 }); }
7701
8257
  }
7702
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: FreezeFrameComponent, decorators: [{
8258
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: FreezeFrameComponent, decorators: [{
7703
8259
  type: Component,
7704
8260
  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"] }]
7705
8261
  }] });
@@ -7714,10 +8270,10 @@ class ImageLoadingSrcComponent {
7714
8270
  loadingImageError() {
7715
8271
  this.#store.dispatch(setLoadingImageSrc({ src: '' }));
7716
8272
  }
7717
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: ImageLoadingSrcComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
7718
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", 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:var(--unreal-surface-screen, #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 }); }
8273
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ImageLoadingSrcComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
8274
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", 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:var(--unreal-surface-screen, #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 }); }
7719
8275
  }
7720
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: ImageLoadingSrcComponent, decorators: [{
8276
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ImageLoadingSrcComponent, decorators: [{
7721
8277
  type: Component,
7722
8278
  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:var(--unreal-surface-screen, #fff)}\n"] }]
7723
8279
  }] });
@@ -7800,10 +8356,10 @@ class StatGraphComponent {
7800
8356
  }
7801
8357
  this.draw();
7802
8358
  }
7803
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: StatGraphComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
7804
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.1.2", 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 }); }
8359
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: StatGraphComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
8360
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.1.3", 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 }); }
7805
8361
  }
7806
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: StatGraphComponent, decorators: [{
8362
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: StatGraphComponent, decorators: [{
7807
8363
  type: Component,
7808
8364
  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"] }]
7809
8365
  }], 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: [{
@@ -7818,10 +8374,10 @@ class UnrealResumeSlotDirective {
7818
8374
  static ngTemplateContextGuard(_dir, _ctx) {
7819
8375
  return true;
7820
8376
  }
7821
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: UnrealResumeSlotDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
7822
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.2", type: UnrealResumeSlotDirective, isStandalone: true, selector: "ng-template[unrealResumeSlot]", ngImport: i0 }); }
8377
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: UnrealResumeSlotDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
8378
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.3", type: UnrealResumeSlotDirective, isStandalone: true, selector: "ng-template[unrealResumeSlot]", ngImport: i0 }); }
7823
8379
  }
7824
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: UnrealResumeSlotDirective, decorators: [{
8380
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: UnrealResumeSlotDirective, decorators: [{
7825
8381
  type: Directive,
7826
8382
  args: [{ selector: 'ng-template[unrealResumeSlot]' }]
7827
8383
  }] });
@@ -7832,10 +8388,10 @@ class UnrealAfkSlotDirective {
7832
8388
  static ngTemplateContextGuard(_dir, _ctx) {
7833
8389
  return true;
7834
8390
  }
7835
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: UnrealAfkSlotDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
7836
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.2", type: UnrealAfkSlotDirective, isStandalone: true, selector: "ng-template[unrealAfkSlot]", ngImport: i0 }); }
8391
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: UnrealAfkSlotDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
8392
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.3", type: UnrealAfkSlotDirective, isStandalone: true, selector: "ng-template[unrealAfkSlot]", ngImport: i0 }); }
7837
8393
  }
7838
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: UnrealAfkSlotDirective, decorators: [{
8394
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: UnrealAfkSlotDirective, decorators: [{
7839
8395
  type: Directive,
7840
8396
  args: [{ selector: 'ng-template[unrealAfkSlot]' }]
7841
8397
  }] });
@@ -7846,10 +8402,10 @@ class UnrealStatusSlotDirective {
7846
8402
  static ngTemplateContextGuard(_dir, _ctx) {
7847
8403
  return true;
7848
8404
  }
7849
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: UnrealStatusSlotDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
7850
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.2", type: UnrealStatusSlotDirective, isStandalone: true, selector: "ng-template[unrealStatusSlot]", ngImport: i0 }); }
8405
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: UnrealStatusSlotDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
8406
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.3", type: UnrealStatusSlotDirective, isStandalone: true, selector: "ng-template[unrealStatusSlot]", ngImport: i0 }); }
7851
8407
  }
7852
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: UnrealStatusSlotDirective, decorators: [{
8408
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: UnrealStatusSlotDirective, decorators: [{
7853
8409
  type: Directive,
7854
8410
  args: [{ selector: 'ng-template[unrealStatusSlot]' }]
7855
8411
  }] });
@@ -7872,10 +8428,10 @@ class UnrealSpinnerComponent {
7872
8428
  this.viewBox = computed(() => `0 0 ${this.size()} ${this.size()}`, /* @ts-ignore */
7873
8429
  ...(ngDevMode ? [{ debugName: "viewBox" }] : /* istanbul ignore next */ []));
7874
8430
  }
7875
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: UnrealSpinnerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
7876
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.2", type: UnrealSpinnerComponent, isStandalone: true, selector: "app-unreal-spinner", inputs: { size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, strokeWidth: { classPropertyName: "strokeWidth", publicName: "strokeWidth", isSignal: true, isRequired: false, transformFunction: null }, strokeColor: { classPropertyName: "strokeColor", publicName: "strokeColor", isSignal: true, isRequired: false, transformFunction: null }, testID: { classPropertyName: "testID", publicName: "data-testid", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div\n [attr.data-testid]=\"testID() || null\"\n [attr.aria-label]=\"ariaLabel()\"\n class=\"unreal-spinner-container\"\n role=\"status\"\n>\n <svg\n [attr.width]=\"size()\"\n [attr.height]=\"size()\"\n [attr.viewBox]=\"viewBox()\"\n [style.--unrealSpinnerSize]=\"size()\"\n class=\"unreal-spinner unreal-spinner--animation\"\n version=\"1.1\"\n xmlns=\"http://www.w3.org/2000/svg\"\n style=\"transform: rotate(-90deg)\"\n aria-hidden=\"true\"\n >\n <circle\n [attr.r]=\"radius()\"\n [attr.cx]=\"center()\"\n [attr.cy]=\"center()\"\n [attr.stroke]=\"strokeColor()\"\n [attr.stroke-width]=\"strokeWidth()\"\n fill=\"transparent\"\n stroke-linecap=\"round\"\n ></circle>\n </svg>\n</div>\n", styles: [":host{display:block}.unreal-spinner-container{position:relative;display:flex;justify-content:center;align-items:center;width:100%;height:100%}.unreal-spinner{position:relative;display:inline-block}.unreal-spinner--animation{animation:1.4s linear 0s infinite normal none running unrealSpinnerRotate}.unreal-spinner--animation circle{transform-origin:50% 50%;stroke-dasharray:calc(var(--unrealSpinnerSize) * 2 * 1px),calc(var(--unrealSpinnerSize) * 5 * 1px);stroke-dashoffset:0;animation:1.6s ease-in-out 0s infinite normal none running unrealSpinnerDash}@keyframes unrealSpinnerRotate{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@keyframes unrealSpinnerDash{0%{stroke-dasharray:1px,calc(var(--unrealSpinnerSize) * 5 * 1px);stroke-dashoffset:0}50%{stroke-dasharray:calc(var(--unrealSpinnerSize) * 2.5 * 1px),calc(var(--unrealSpinnerSize) * 5 * 1px);stroke-dashoffset:calc(var(--unrealSpinnerSize) * .4 * -1px)}to{stroke-dasharray:calc(var(--unrealSpinnerSize) * 2.5 * 1px),calc(var(--unrealSpinnerSize) * 5 * 1px);stroke-dashoffset:calc(var(--unrealSpinnerSize) * 3 * -1px)}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
8431
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: UnrealSpinnerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
8432
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.3", type: UnrealSpinnerComponent, isStandalone: true, selector: "app-unreal-spinner", inputs: { size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, strokeWidth: { classPropertyName: "strokeWidth", publicName: "strokeWidth", isSignal: true, isRequired: false, transformFunction: null }, strokeColor: { classPropertyName: "strokeColor", publicName: "strokeColor", isSignal: true, isRequired: false, transformFunction: null }, testID: { classPropertyName: "testID", publicName: "data-testid", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div\n [attr.data-testid]=\"testID() || null\"\n [attr.aria-label]=\"ariaLabel()\"\n class=\"unreal-spinner-container\"\n role=\"status\"\n>\n <svg\n [attr.width]=\"size()\"\n [attr.height]=\"size()\"\n [attr.viewBox]=\"viewBox()\"\n [style.--unrealSpinnerSize]=\"size()\"\n class=\"unreal-spinner unreal-spinner--animation\"\n version=\"1.1\"\n xmlns=\"http://www.w3.org/2000/svg\"\n style=\"transform: rotate(-90deg)\"\n aria-hidden=\"true\"\n >\n <circle\n [attr.r]=\"radius()\"\n [attr.cx]=\"center()\"\n [attr.cy]=\"center()\"\n [attr.stroke]=\"strokeColor()\"\n [attr.stroke-width]=\"strokeWidth()\"\n fill=\"transparent\"\n stroke-linecap=\"round\"\n ></circle>\n </svg>\n</div>\n", styles: [":host{display:block}.unreal-spinner-container{position:relative;display:flex;justify-content:center;align-items:center;width:100%;height:100%}.unreal-spinner{position:relative;display:inline-block}.unreal-spinner--animation{animation:1.4s linear 0s infinite normal none running unrealSpinnerRotate}.unreal-spinner--animation circle{transform-origin:50% 50%;stroke-dasharray:calc(var(--unrealSpinnerSize) * 2 * 1px),calc(var(--unrealSpinnerSize) * 5 * 1px);stroke-dashoffset:0;animation:1.6s ease-in-out 0s infinite normal none running unrealSpinnerDash}@keyframes unrealSpinnerRotate{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@keyframes unrealSpinnerDash{0%{stroke-dasharray:1px,calc(var(--unrealSpinnerSize) * 5 * 1px);stroke-dashoffset:0}50%{stroke-dasharray:calc(var(--unrealSpinnerSize) * 2.5 * 1px),calc(var(--unrealSpinnerSize) * 5 * 1px);stroke-dashoffset:calc(var(--unrealSpinnerSize) * .4 * -1px)}to{stroke-dasharray:calc(var(--unrealSpinnerSize) * 2.5 * 1px),calc(var(--unrealSpinnerSize) * 5 * 1px);stroke-dashoffset:calc(var(--unrealSpinnerSize) * 3 * -1px)}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
7877
8433
  }
7878
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: UnrealSpinnerComponent, decorators: [{
8434
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: UnrealSpinnerComponent, decorators: [{
7879
8435
  type: Component,
7880
8436
  args: [{ selector: 'app-unreal-spinner', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div\n [attr.data-testid]=\"testID() || null\"\n [attr.aria-label]=\"ariaLabel()\"\n class=\"unreal-spinner-container\"\n role=\"status\"\n>\n <svg\n [attr.width]=\"size()\"\n [attr.height]=\"size()\"\n [attr.viewBox]=\"viewBox()\"\n [style.--unrealSpinnerSize]=\"size()\"\n class=\"unreal-spinner unreal-spinner--animation\"\n version=\"1.1\"\n xmlns=\"http://www.w3.org/2000/svg\"\n style=\"transform: rotate(-90deg)\"\n aria-hidden=\"true\"\n >\n <circle\n [attr.r]=\"radius()\"\n [attr.cx]=\"center()\"\n [attr.cy]=\"center()\"\n [attr.stroke]=\"strokeColor()\"\n [attr.stroke-width]=\"strokeWidth()\"\n fill=\"transparent\"\n stroke-linecap=\"round\"\n ></circle>\n </svg>\n</div>\n", styles: [":host{display:block}.unreal-spinner-container{position:relative;display:flex;justify-content:center;align-items:center;width:100%;height:100%}.unreal-spinner{position:relative;display:inline-block}.unreal-spinner--animation{animation:1.4s linear 0s infinite normal none running unrealSpinnerRotate}.unreal-spinner--animation circle{transform-origin:50% 50%;stroke-dasharray:calc(var(--unrealSpinnerSize) * 2 * 1px),calc(var(--unrealSpinnerSize) * 5 * 1px);stroke-dashoffset:0;animation:1.6s ease-in-out 0s infinite normal none running unrealSpinnerDash}@keyframes unrealSpinnerRotate{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@keyframes unrealSpinnerDash{0%{stroke-dasharray:1px,calc(var(--unrealSpinnerSize) * 5 * 1px);stroke-dashoffset:0}50%{stroke-dasharray:calc(var(--unrealSpinnerSize) * 2.5 * 1px),calc(var(--unrealSpinnerSize) * 5 * 1px);stroke-dashoffset:calc(var(--unrealSpinnerSize) * .4 * -1px)}to{stroke-dasharray:calc(var(--unrealSpinnerSize) * 2.5 * 1px),calc(var(--unrealSpinnerSize) * 5 * 1px);stroke-dashoffset:calc(var(--unrealSpinnerSize) * 3 * -1px)}}\n"] }]
7881
8437
  }], propDecorators: { size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], strokeWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "strokeWidth", required: false }] }], strokeColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "strokeColor", required: false }] }], testID: [{ type: i0.Input, args: [{ isSignal: true, alias: "data-testid", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }] } });
@@ -7906,10 +8462,10 @@ class UnrealStatusComponent {
7906
8462
  }
7907
8463
  #store;
7908
8464
  #loadingProgress;
7909
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: UnrealStatusComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
7910
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", type: UnrealStatusComponent, isStandalone: true, selector: "app-unreal-status", inputs: { uiClasses: { classPropertyName: "uiClasses", publicName: "uiClasses", isSignal: true, isRequired: false, transformFunction: null }, contentTemplate: { classPropertyName: "contentTemplate", publicName: "contentTemplate", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@if (isShowLoadingPercentsPopup()) {\n @let _awsInstance = awsInstance();\n <div [class]=\"cardSkin()\" class=\"status-anchor\">\n @if (contentTemplate(); as tpl) {\n <ng-container\n [ngTemplateOutlet]=\"tpl\"\n [ngTemplateOutletContext]=\"{ $implicit: slotContext }\"\n />\n } @else if (_awsInstance?.message) {\n <p>{{ _awsInstance?.message }}</p>\n } @else {\n <div [class]=\"messageSkin()\">\n <app-unreal-spinner [size]=\"30\" [strokeColor]=\"'#1F2937'\" />\n {{ smoothPercents() }}%\n </div>\n }\n </div>\n}\n", styles: ["app-stat-graph{height:30px;width:100%}.status-anchor{position:absolute;top:80%;left:50%;transform:translate(-50%,-50%);z-index:5;pointer-events:none}.status-box{display:flex;justify-content:center;align-items:center;width:auto;max-width:500px;margin:0;background:var(--unreal-surface-card, var(--src-surface-container-main, #fff));border:none;border-radius:var(--unreal-radius-pill, var(--src-border-rounded-full, 9999px));box-shadow:0 8px 20px #1718181f,0 3px 6px #17181814}.status-anchor p{padding:10px 15px;margin:0;text-align:center}.status-box__message{display:flex;align-items:center;padding:12px 20px 12px 16px;color:var(--unreal-text-main, 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 app-unreal-spinner{margin-right:12px}\n"], dependencies: [{ kind: "component", type: UnrealSpinnerComponent, selector: "app-unreal-spinner", inputs: ["size", "strokeWidth", "strokeColor", "data-testid", "ariaLabel"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
8465
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: UnrealStatusComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
8466
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: UnrealStatusComponent, isStandalone: true, selector: "app-unreal-status", inputs: { uiClasses: { classPropertyName: "uiClasses", publicName: "uiClasses", isSignal: true, isRequired: false, transformFunction: null }, contentTemplate: { classPropertyName: "contentTemplate", publicName: "contentTemplate", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@if (isShowLoadingPercentsPopup()) {\n @let _awsInstance = awsInstance();\n <div [class]=\"cardSkin()\" class=\"status-anchor\">\n @if (contentTemplate(); as tpl) {\n <ng-container\n [ngTemplateOutlet]=\"tpl\"\n [ngTemplateOutletContext]=\"{ $implicit: slotContext }\"\n />\n } @else if (_awsInstance?.message) {\n <p>{{ _awsInstance?.message }}</p>\n } @else {\n <div [class]=\"messageSkin()\">\n <app-unreal-spinner [size]=\"30\" [strokeColor]=\"'#1F2937'\" />\n {{ smoothPercents() }}%\n </div>\n }\n </div>\n}\n", styles: ["app-stat-graph{height:30px;width:100%}.status-anchor{position:absolute;top:80%;left:50%;transform:translate(-50%,-50%);z-index:5;pointer-events:none}.status-box{display:flex;justify-content:center;align-items:center;width:auto;max-width:500px;margin:0;background:var(--unreal-surface-card, var(--src-surface-container-main, #fff));border:none;border-radius:var(--unreal-radius-pill, var(--src-border-rounded-full, 9999px));box-shadow:0 8px 20px #1718181f,0 3px 6px #17181814}.status-anchor p{padding:10px 15px;margin:0;text-align:center}.status-box__message{display:flex;align-items:center;padding:12px 20px 12px 16px;color:var(--unreal-text-main, 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 app-unreal-spinner{margin-right:12px}\n"], dependencies: [{ kind: "component", type: UnrealSpinnerComponent, selector: "app-unreal-spinner", inputs: ["size", "strokeWidth", "strokeColor", "data-testid", "ariaLabel"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
7911
8467
  }
7912
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: UnrealStatusComponent, decorators: [{
8468
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: UnrealStatusComponent, decorators: [{
7913
8469
  type: Component,
7914
8470
  args: [{ selector: 'app-unreal-status', changeDetection: ChangeDetectionStrategy.OnPush, imports: [UnrealSpinnerComponent, NgTemplateOutlet], template: "@if (isShowLoadingPercentsPopup()) {\n @let _awsInstance = awsInstance();\n <div [class]=\"cardSkin()\" class=\"status-anchor\">\n @if (contentTemplate(); as tpl) {\n <ng-container\n [ngTemplateOutlet]=\"tpl\"\n [ngTemplateOutletContext]=\"{ $implicit: slotContext }\"\n />\n } @else if (_awsInstance?.message) {\n <p>{{ _awsInstance?.message }}</p>\n } @else {\n <div [class]=\"messageSkin()\">\n <app-unreal-spinner [size]=\"30\" [strokeColor]=\"'#1F2937'\" />\n {{ smoothPercents() }}%\n </div>\n }\n </div>\n}\n", styles: ["app-stat-graph{height:30px;width:100%}.status-anchor{position:absolute;top:80%;left:50%;transform:translate(-50%,-50%);z-index:5;pointer-events:none}.status-box{display:flex;justify-content:center;align-items:center;width:auto;max-width:500px;margin:0;background:var(--unreal-surface-card, var(--src-surface-container-main, #fff));border:none;border-radius:var(--unreal-radius-pill, var(--src-border-rounded-full, 9999px));box-shadow:0 8px 20px #1718181f,0 3px 6px #17181814}.status-anchor p{padding:10px 15px;margin:0;text-align:center}.status-box__message{display:flex;align-items:center;padding:12px 20px 12px 16px;color:var(--unreal-text-main, 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 app-unreal-spinner{margin-right:12px}\n"] }]
7915
8471
  }], propDecorators: { uiClasses: [{ type: i0.Input, args: [{ isSignal: true, alias: "uiClasses", required: false }] }], contentTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "contentTemplate", required: false }] }] } });
@@ -7942,10 +8498,10 @@ class SafePipe {
7942
8498
  // content instead of disabling framework security checks.
7943
8499
  return (this.sanitizer.sanitize(context, value) ?? '');
7944
8500
  }
7945
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: SafePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
7946
- static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "22.1.2", ngImport: i0, type: SafePipe, isStandalone: true, name: "safe" }); }
8501
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SafePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
8502
+ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "22.1.3", ngImport: i0, type: SafePipe, isStandalone: true, name: "safe" }); }
7947
8503
  }
7948
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: SafePipe, decorators: [{
8504
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SafePipe, decorators: [{
7949
8505
  type: Pipe,
7950
8506
  args: [{
7951
8507
  name: 'safe',
@@ -8058,10 +8614,10 @@ class VideoStatsComponent {
8058
8614
  elements: this.elementsToShow(),
8059
8615
  }));
8060
8616
  }
8061
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: VideoStatsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
8062
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", 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()\"\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]=\"ssData() | 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: JsonPipe, name: "json" }, { kind: "pipe", type: SafePipe, name: "safe" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
8617
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: VideoStatsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
8618
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", 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()\"\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]=\"ssData() | 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: JsonPipe, name: "json" }, { kind: "pipe", type: SafePipe, name: "safe" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
8063
8619
  }
8064
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: VideoStatsComponent, decorators: [{
8620
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: VideoStatsComponent, decorators: [{
8065
8621
  type: Component,
8066
8622
  args: [{ selector: 'app-video-stats', changeDetection: ChangeDetectionStrategy.OnPush, imports: [StatGraphComponent, 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(); 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()\"\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]=\"ssData() | 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"] }]
8067
8623
  }], ctorParameters: () => [] });
@@ -8123,10 +8679,10 @@ class ResumeStreamButtonComponent {
8123
8679
  disconnect() {
8124
8680
  this.isSecondStart.set(true);
8125
8681
  }
8126
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: ResumeStreamButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
8127
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", type: ResumeStreamButtonComponent, isStandalone: true, selector: "app-resume-stream-button", inputs: { uiClasses: { classPropertyName: "uiClasses", publicName: "uiClasses", isSignal: true, isRequired: false, transformFunction: null }, contentTemplate: { classPropertyName: "contentTemplate", publicName: "contentTemplate", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@if (isShowResumeButton()) {\n <div [class]=\"cardSkin()\" class=\"resume-anchor\">\n @if (contentTemplate(); as tpl) {\n <ng-container\n [ngTemplateOutlet]=\"tpl\"\n [ngTemplateOutletContext]=\"{ $implicit: slotContext }\"\n />\n } @else {\n @if (isSecondStart()) {\n <div [class]=\"textSkin()\">\n Your stream {{ isAfkDisconnect() ? 'has been' : 'is' }} paused due to\n inactivity\n </div>\n }\n\n <app-unreal-button\n (clicked)=\"connect()\"\n [fullWidth]=\"true\"\n [data-testid]=\"'connect-button'\"\n [class]=\"buttonSkin()\"\n size=\"lg\"\n weight=\"primary\"\n >\n {{ isSecondStart() ? 'Resume' : 'Start' }}\n </app-unreal-button>\n }\n </div>\n}\n", styles: [".resume-anchor{position:absolute;bottom:0;left:50%;transform:translate(-50%,-50%);z-index:6}.resume-box{padding:16px;gap:12px;display:flex;flex-direction:column;align-items:center;border-radius:var(--unreal-radius-card, var(--src-border-rounded-parent, 8px));background:var(--unreal-surface-card, var(--src-surface-container-main, #fff));box-shadow:var(--unreal-shadow-card, 0 26px 80px 0 rgba(0, 0, 0, .2), 0 0 1px 0 rgba(0, 0, 0, .2))}.connect-button{width:100%}.resume-box__text{color:var(--unreal-text-secondary, var(--src-color-grey-500, #6b7280));text-align:center;font-family:var(--unreal-font-family, var(--src-font-family-body, system-ui, sans-serif));font-size:14px;font-style:normal;font-weight:400;line-height:24px}\n"], dependencies: [{ kind: "component", type: UnrealButtonComponent, selector: "app-unreal-button", inputs: ["type", "weight", "size", "fullWidth", "isDisabled", "data-testid", "ariaLabel"], outputs: ["clicked"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
8682
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ResumeStreamButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
8683
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: ResumeStreamButtonComponent, isStandalone: true, selector: "app-resume-stream-button", inputs: { uiClasses: { classPropertyName: "uiClasses", publicName: "uiClasses", isSignal: true, isRequired: false, transformFunction: null }, contentTemplate: { classPropertyName: "contentTemplate", publicName: "contentTemplate", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@if (isShowResumeButton()) {\n <div [class]=\"cardSkin()\" class=\"resume-anchor\">\n @if (contentTemplate(); as tpl) {\n <ng-container\n [ngTemplateOutlet]=\"tpl\"\n [ngTemplateOutletContext]=\"{ $implicit: slotContext }\"\n />\n } @else {\n @if (isSecondStart()) {\n <div [class]=\"textSkin()\">\n Your stream {{ isAfkDisconnect() ? 'has been' : 'is' }} paused due to\n inactivity\n </div>\n }\n\n <app-unreal-button\n (clicked)=\"connect()\"\n [fullWidth]=\"true\"\n [data-testid]=\"'connect-button'\"\n [class]=\"buttonSkin()\"\n size=\"lg\"\n weight=\"primary\"\n >\n {{ isSecondStart() ? 'Resume' : 'Start' }}\n </app-unreal-button>\n }\n </div>\n}\n", styles: [".resume-anchor{position:absolute;bottom:0;left:50%;transform:translate(-50%,-50%);z-index:6}.resume-box{padding:16px;gap:12px;display:flex;flex-direction:column;align-items:center;border-radius:var(--unreal-radius-card, var(--src-border-rounded-parent, 8px));background:var(--unreal-surface-card, var(--src-surface-container-main, #fff));box-shadow:var(--unreal-shadow-card, 0 26px 80px 0 rgba(0, 0, 0, .2), 0 0 1px 0 rgba(0, 0, 0, .2))}.connect-button{width:100%}.resume-box__text{color:var(--unreal-text-secondary, var(--src-color-grey-500, #6b7280));text-align:center;font-family:var(--unreal-font-family, var(--src-font-family-body, system-ui, sans-serif));font-size:14px;font-style:normal;font-weight:400;line-height:24px}\n"], dependencies: [{ kind: "component", type: UnrealButtonComponent, selector: "app-unreal-button", inputs: ["type", "weight", "size", "fullWidth", "isDisabled", "data-testid", "ariaLabel"], outputs: ["clicked"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
8128
8684
  }
8129
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: ResumeStreamButtonComponent, decorators: [{
8685
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ResumeStreamButtonComponent, decorators: [{
8130
8686
  type: Component,
8131
8687
  args: [{ selector: 'app-resume-stream-button', changeDetection: ChangeDetectionStrategy.OnPush, imports: [UnrealButtonComponent, NgTemplateOutlet], template: "@if (isShowResumeButton()) {\n <div [class]=\"cardSkin()\" class=\"resume-anchor\">\n @if (contentTemplate(); as tpl) {\n <ng-container\n [ngTemplateOutlet]=\"tpl\"\n [ngTemplateOutletContext]=\"{ $implicit: slotContext }\"\n />\n } @else {\n @if (isSecondStart()) {\n <div [class]=\"textSkin()\">\n Your stream {{ isAfkDisconnect() ? 'has been' : 'is' }} paused due to\n inactivity\n </div>\n }\n\n <app-unreal-button\n (clicked)=\"connect()\"\n [fullWidth]=\"true\"\n [data-testid]=\"'connect-button'\"\n [class]=\"buttonSkin()\"\n size=\"lg\"\n weight=\"primary\"\n >\n {{ isSecondStart() ? 'Resume' : 'Start' }}\n </app-unreal-button>\n }\n </div>\n}\n", styles: [".resume-anchor{position:absolute;bottom:0;left:50%;transform:translate(-50%,-50%);z-index:6}.resume-box{padding:16px;gap:12px;display:flex;flex-direction:column;align-items:center;border-radius:var(--unreal-radius-card, var(--src-border-rounded-parent, 8px));background:var(--unreal-surface-card, var(--src-surface-container-main, #fff));box-shadow:var(--unreal-shadow-card, 0 26px 80px 0 rgba(0, 0, 0, .2), 0 0 1px 0 rgba(0, 0, 0, .2))}.connect-button{width:100%}.resume-box__text{color:var(--unreal-text-secondary, var(--src-color-grey-500, #6b7280));text-align:center;font-family:var(--unreal-font-family, var(--src-font-family-body, system-ui, sans-serif));font-size:14px;font-style:normal;font-weight:400;line-height:24px}\n"] }]
8132
8688
  }], ctorParameters: () => [], propDecorators: { uiClasses: [{ type: i0.Input, args: [{ isSignal: true, alias: "uiClasses", required: false }] }], contentTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "contentTemplate", required: false }] }] } });
@@ -8193,10 +8749,10 @@ class IntroSrcComponent {
8193
8749
  sourceElement.addEventListener('error', () => this.#store.dispatch(resetIntroSrc()));
8194
8750
  videoContainer.appendChild(videoElement);
8195
8751
  }
8196
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: IntroSrcComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
8197
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", 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 }); }
8752
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: IntroSrcComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
8753
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", 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 }); }
8198
8754
  }
8199
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: IntroSrcComponent, decorators: [{
8755
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: IntroSrcComponent, decorators: [{
8200
8756
  type: Component,
8201
8757
  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"] }]
8202
8758
  }], ctorParameters: () => [], propDecorators: { videoElement: [{ type: i0.ViewChild, args: ['videoPlayerCover', { isSignal: true }] }] } });
@@ -8427,10 +8983,10 @@ class UnrealSceneComponent {
8427
8983
  },
8428
8984
  });
8429
8985
  }
8430
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: UnrealSceneComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
8431
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", 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 }, uiClasses: { classPropertyName: "uiClasses", publicName: "uiClasses", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { changeMouseOverScene: "changeMouseOverScene" }, host: { listeners: { "mouseover": "onMouseOver()", "mouseout": "onMouseOut()", "contextmenu": "onContextMenu($event)" } }, queries: [{ propertyName: "resumeSlot", first: true, predicate: UnrealResumeSlotDirective, descendants: true, isSignal: true }, { propertyName: "afkSlot", first: true, predicate: UnrealAfkSlotDirective, descendants: true, isSignal: true }, { propertyName: "statusSlot", first: true, predicate: UnrealStatusSlotDirective, descendants: true, isSignal: true }], 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-freeze-frame />\n <app-resume-stream-button\n [uiClasses]=\"uiClasses()\"\n [contentTemplate]=\"resumeSlot()?.template ?? null\"\n />\n <app-unreal-status\n [uiClasses]=\"uiClasses()\"\n [contentTemplate]=\"statusSlot()?.template ?? null\"\n />\n <app-afk-timeout-modal\n [uiClasses]=\"uiClasses()\"\n [contentTemplate]=\"afkSlot()?.template ?? null\"\n />\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, system-ui, sans-serif)}#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, system-ui, sans-serif)}#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", inputs: ["uiClasses", "contentTemplate"] }, { kind: "component", type: UnrealStatusComponent, selector: "app-unreal-status", inputs: ["uiClasses", "contentTemplate"] }, { 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", inputs: ["uiClasses", "contentTemplate"] }, { kind: "component", type: ImageLoadingSrcComponent, selector: "app-image-loading-src" }, { kind: "component", type: IntroSrcComponent, selector: "app-intro-src" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
8986
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: UnrealSceneComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
8987
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", 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 }, uiClasses: { classPropertyName: "uiClasses", publicName: "uiClasses", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { changeMouseOverScene: "changeMouseOverScene" }, host: { listeners: { "mouseover": "onMouseOver()", "mouseout": "onMouseOut()", "contextmenu": "onContextMenu($event)" } }, queries: [{ propertyName: "resumeSlot", first: true, predicate: UnrealResumeSlotDirective, descendants: true, isSignal: true }, { propertyName: "afkSlot", first: true, predicate: UnrealAfkSlotDirective, descendants: true, isSignal: true }, { propertyName: "statusSlot", first: true, predicate: UnrealStatusSlotDirective, descendants: true, isSignal: true }], 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-freeze-frame />\n <app-resume-stream-button\n [uiClasses]=\"uiClasses()\"\n [contentTemplate]=\"resumeSlot()?.template ?? null\"\n />\n <app-unreal-status\n [uiClasses]=\"uiClasses()\"\n [contentTemplate]=\"statusSlot()?.template ?? null\"\n />\n <app-afk-timeout-modal\n [uiClasses]=\"uiClasses()\"\n [contentTemplate]=\"afkSlot()?.template ?? null\"\n />\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, system-ui, sans-serif)}#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, system-ui, sans-serif)}#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", inputs: ["uiClasses", "contentTemplate"] }, { kind: "component", type: UnrealStatusComponent, selector: "app-unreal-status", inputs: ["uiClasses", "contentTemplate"] }, { 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", inputs: ["uiClasses", "contentTemplate"] }, { kind: "component", type: ImageLoadingSrcComponent, selector: "app-image-loading-src" }, { kind: "component", type: IntroSrcComponent, selector: "app-intro-src" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
8432
8988
  }
8433
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: UnrealSceneComponent, decorators: [{
8989
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: UnrealSceneComponent, decorators: [{
8434
8990
  type: Component,
8435
8991
  args: [{ selector: 'app-unreal-scene', changeDetection: ChangeDetectionStrategy.OnPush, imports: [
8436
8992
  AfkTimeoutModalComponent,
@@ -8500,5 +9056,5 @@ function provideUnrealModule() {
8500
9056
  * Generated bundle index. Do not edit.
8501
9057
  */
8502
9058
 
8503
- export { AFKService, AUTO_HIGH_RESOLUTION_IDLE_MS, AfkTimeoutModalComponent, AggregatorService, AnalyticsService, AnswerHandler, CommandTelemetryService, ConfigHandler, ConsoleExtensionsService, ControlScheme, DATA_CHANNEL_CONNECTION_TIMEOUT, DEBOUNCE_TO_MANY_RESIZE_CALLS, DEFAULT_AFK_TIMEOUT, DEFAULT_AFK_TIMEOUT_PERIOD, DESTROY_DISCONNECT_REASONS, DevModeService, DisconnectReason, FULL_HD_HEIGHT, FULL_HD_WIDTH, FileHandlerService, FileReceiverService, FpsMonitorService, FreezeFrameComponent, FreezeFrameService, INNER_RETRY_LIMIT, INSTANCE_READY_TIMEOUT, IceCandidateHandler, ImageLoadingSrcComponent, InputOptions, InputService, InstanceReadyHandler, InstanceReservedHandler, IntroSrcComponent, LatencyTimings, LoadingProgressService, MINIMAL_FPS, MIN_CONTAINER_SIZE, MessageType, MouseButton, MouseButtonsMask, OUTER_RETRY_DELAY, OnCloseHandler, OnErrorHandler, OnMessageHandler, OnOpenHandler, OrchestrationMessageTypes, POLLING_TIME, PlayerCountHandler, RECONNECT_CONFIG, REGION_PING_ACCURATE_SAMPLES, REGION_PING_BACKGROUND_REFRESH_DELAY_MS, REGION_PING_CACHE_KEY, REGION_PING_CACHE_TTL_MS, REGION_PING_WARMUP_DEADLINE_MS, RegionsPingService, SAME_SIZE_THRESHOLD, SCREEN_LOCKER_CONTAINER_ID, SIGNALLING_PERCENT_VALUE, SSInfoHandler, STREAMING_AUDIO_ID, STREAMING_VIDEO_ID, SafePipe, SignallingService, SpecialKeyCodes, StatGraphComponent, StreamAdoptionService, StreamStatusTelemetryService, StreamStatusTelemetryService2, SubService, ToClientMessage, UnrealAfkSlotDirective, UnrealCallbackService, UnrealCommunicatorService, UnrealEffects, UnrealResumeSlotDirective, UnrealSceneComponent, UnrealStatusMessage, UnrealStatusSlotDirective, 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_RETRY_PER_INSTANCE, WS_TIMEOUT, WebRtcPlayerService, WebrtcErrorModalComponent, abortEstablishingConnection, answerSent, changeStatusMainVideoOnScene, changeStreamResolutionAction, commandCompleted, commandStarted, dataChannelConnected, dataChannelReady, decodeData, destroyRemoteConnections, destroyUnrealScene, disconnectStream, dispatchResize, dropConnection, dropDC, dropPC, floatToPercents, floatToSmoothPercents, forceResizeViewport, fromResizeObserver, getActiveUrl, getImageFromVideoStream, getResizeStudioValues, getResizeValuesUniversal, getRtcErrorMessage, iceConnectionFailed, initSignalling, instanceReady, isAppOwned, isMetaboxMode, keepMaxUntilReset, makeEven, pollingEnded, pollingStarted, provideUnrealModule, provideUnrealState, qpToQuality, reconnectPeer, reconnectPeerFailed, reconnectPeerSuccess, regionPingFetchEnd, regionPingFetchStart, regionResolved, resetAfk, resetConfig, resetDataChannelForReconnect, resetIntroSrc, resetUserInactivityTimeout, resetWarnTimeout, resolveSkin, saveAnalyticsEvent, selectCirrusConnectionId, selectClientAndViewIds, selectCommandProgress, selectFreezeFrameCombinedDataUrl, selectFreezeFrameDataUrl, selectFreezeFrameDataUrlFromVideo, selectFreezeFrameProgressMessageFromVideo, selectIsAutostart, selectIsExistMatchUrls, selectIsFreezeFrameLoading, selectIsFullyConnected, selectIsLoaderScreenVisible, selectIsVideoPlayingAndDataChannelConnected, selectShowLoader, selectShowReconnectPopup, selectSignalingParameters, selectStreamConfig, selectTotalProgress, selectUserInactivityDetected, 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, signalingRetryAttempted, startStream, streamAdopted, trackMixpanelEvent, trackTelemetryData, uint8ToBase64, unrealCommandCallback, unrealFeature, unrealReducer, updateCirrusInfo, videoAdaptedToContainer, videoStreamInitialized };
9059
+ export { AFKService, AUTO_HIGH_RESOLUTION_IDLE_MS, AfkTimeoutModalComponent, AggregatorService, AnalyticsService, AnswerHandler, CommandTelemetryService, ConfigHandler, ConsoleExtensionsService, ControlScheme, DATA_CHANNEL_CONNECTION_TIMEOUT, DEBOUNCE_TO_MANY_RESIZE_CALLS, DEFAULT_AFK_TIMEOUT, DEFAULT_AFK_TIMEOUT_PERIOD, DESTROY_DISCONNECT_REASONS, DevModeService, DisconnectReason, FULL_HD_HEIGHT, FULL_HD_WIDTH, FileHandlerService, FileReceiverService, FpsMonitorService, FreezeFrameComponent, FreezeFrameService, IME_PROCESS_KEY, INNER_RETRY_LIMIT, INSTANCE_READY_TIMEOUT, IceCandidateHandler, ImageLoadingSrcComponent, InputOptions, InputService, InstanceReadyHandler, InstanceReservedHandler, IntroSrcComponent, InvertedKeyMap, KeyboardNumericCode, LatencyTimings, LoadingProgressService, MINIMAL_FPS, MIN_CONTAINER_SIZE, MODIFIER_KEY_CODES, MessageType, MouseButton, MouseButtonsMask, OUTER_RETRY_DELAY, OnCloseHandler, OnErrorHandler, OnMessageHandler, OnOpenHandler, OrchestrationMessageTypes, POLLING_TIME, PlayerCountHandler, RECONNECT_CONFIG, REGION_PING_ACCURATE_SAMPLES, REGION_PING_BACKGROUND_REFRESH_DELAY_MS, REGION_PING_CACHE_KEY, REGION_PING_CACHE_TTL_MS, REGION_PING_WARMUP_DEADLINE_MS, RegionsPingService, SAME_SIZE_THRESHOLD, SCREEN_LOCKER_CONTAINER_ID, SIGNALLING_PERCENT_VALUE, SSInfoHandler, STREAMING_AUDIO_ID, STREAMING_VIDEO_ID, SafePipe, SignallingService, SpecialKeyCodes, StatGraphComponent, StreamAdoptionService, StreamStatusTelemetryService, StreamStatusTelemetryService2, SubService, ToClientMessage, UnrealAfkSlotDirective, UnrealCallbackService, UnrealCommunicatorService, UnrealEffects, UnrealResumeSlotDirective, UnrealSceneComponent, UnrealStatusMessage, UnrealStatusSlotDirective, 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_RETRY_PER_INSTANCE, WS_TIMEOUT, WebRtcPlayerService, WebrtcErrorModalComponent, abortEstablishingConnection, answerSent, changeStatusMainVideoOnScene, changeStreamResolutionAction, commandCompleted, commandStarted, dataChannelConnected, dataChannelReady, decodeData, destroyRemoteConnections, destroyUnrealScene, disconnectStream, dispatchResize, dropConnection, dropDC, dropPC, floatToPercents, floatToSmoothPercents, forceResizeViewport, fromResizeObserver, getActiveUrl, getImageFromVideoStream, getResizeStudioValues, getResizeValuesUniversal, getRtcErrorMessage, iceConnectionFailed, initSignalling, instanceReady, isAppOwned, isMetaboxMode, keepMaxUntilReset, makeEven, pollingEnded, pollingStarted, provideUnrealModule, provideUnrealState, qpToQuality, reconnectPeer, reconnectPeerFailed, reconnectPeerSuccess, regionPingFetchEnd, regionPingFetchStart, regionResolved, resetAfk, resetConfig, resetDataChannelForReconnect, resetIntroSrc, resetUserInactivityTimeout, resetWarnTimeout, resolveSkin, saveAnalyticsEvent, selectCirrusConnectionId, selectClientAndViewIds, selectCommandProgress, selectFreezeFrameCombinedDataUrl, selectFreezeFrameDataUrl, selectFreezeFrameDataUrlFromVideo, selectFreezeFrameProgressMessageFromVideo, selectIsAutostart, selectIsExistMatchUrls, selectIsFreezeFrameLoading, selectIsFullyConnected, selectIsLoaderScreenVisible, selectIsVideoPlayingAndDataChannelConnected, selectShowLoader, selectShowReconnectPopup, selectSignalingParameters, selectStreamConfig, selectTotalProgress, selectUserInactivityDetected, 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, signalingRetryAttempted, startStream, streamAdopted, trackMixpanelEvent, trackTelemetryData, uint8ToBase64, unrealCommandCallback, unrealFeature, unrealReducer, updateCirrusInfo, videoAdaptedToContainer, videoStreamInitialized };
8504
9060
  //# sourceMappingURL=3dsource-angular-unreal-module.mjs.map