@scarlett-player/ui 1.8.0 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,5 +1,10 @@
1
1
  // src/index.ts
2
- import { enterFullscreen as enterFullscreen2, exitFullscreen as exitFullscreen2, isFullscreen as isFullscreen2 } from "@scarlett-player/core";
2
+ import {
3
+ enterFullscreen as enterFullscreen2,
4
+ exitFullscreen as exitFullscreen2,
5
+ injectSharedStyles,
6
+ isFullscreen as isFullscreen2
7
+ } from "@scarlett-player/core";
3
8
 
4
9
  // src/fit.ts
5
10
  var UNKNOWN_RANK = 3;
@@ -86,6 +91,10 @@ var styles = `
86
91
  ============================================ */
87
92
  .sp-container {
88
93
  position: relative;
94
+ /* Own stacking context: the control bar, menus and overlays all carry
95
+ z-index, and without this they compete with the host page's own layers
96
+ instead of staying inside the player. */
97
+ isolation: isolate;
89
98
  width: 100%;
90
99
  height: 100%;
91
100
  background: #000;
@@ -207,11 +216,13 @@ var styles = `
207
216
  cursor: pointer;
208
217
  z-index: 10;
209
218
  opacity: 0;
219
+ pointer-events: none;
210
220
  transition: opacity 0.25s ease;
211
221
  }
212
222
 
213
223
  .sp-progress-wrapper--visible {
214
224
  opacity: 1;
225
+ pointer-events: auto;
215
226
  }
216
227
 
217
228
  /* Touch: a 20px wrapper is not a 20px target. The control bar is a later
@@ -1334,7 +1345,7 @@ var BigPlayButton = class {
1334
1345
  * must not sit on top of it, and `error` state alone does not say (a
1335
1346
  * dismissed overlay leaves the error behind)
1336
1347
  */
1337
- constructor(api, isOverlayVisible = () => false) {
1348
+ constructor(api, isOverlayVisible) {
1338
1349
  /**
1339
1350
  * Latched on the first `playing`.
1340
1351
  *
@@ -1348,7 +1359,8 @@ var BigPlayButton = class {
1348
1359
  this.start();
1349
1360
  };
1350
1361
  this.api = api;
1351
- this.isOverlayVisible = isOverlayVisible;
1362
+ this.hasOverlayProbe = typeof isOverlayVisible === "function";
1363
+ this.isOverlayVisible = isOverlayVisible ?? (() => false);
1352
1364
  const btn = document.createElement("button");
1353
1365
  btn.className = "sp-big-play";
1354
1366
  btn.setAttribute("type", "button");
@@ -1377,7 +1389,7 @@ var BigPlayButton = class {
1377
1389
  this.hasStarted = true;
1378
1390
  }
1379
1391
  let visible;
1380
- if (error || this.isOverlayVisible()) {
1392
+ if (this.hasOverlayProbe ? this.isOverlayVisible() : Boolean(error)) {
1381
1393
  visible = false;
1382
1394
  } else if (playbackState === "loading") {
1383
1395
  visible = false;
@@ -1803,15 +1815,17 @@ var ProgressBar = class {
1803
1815
  const seekableRange = this.api.getState("seekableRange");
1804
1816
  if (live && seekableRange) {
1805
1817
  const rangeLength = seekableRange.end - seekableRange.start;
1806
- return seekableRange.start + percent * rangeLength;
1818
+ const time2 = seekableRange.start + percent * rangeLength;
1819
+ return Number.isFinite(time2) ? time2 : null;
1807
1820
  }
1808
1821
  const duration = this.api.getState("duration") || 0;
1809
- return percent * duration;
1822
+ const time = percent * duration;
1823
+ return Number.isFinite(time) ? time : null;
1810
1824
  }
1811
1825
  updateTooltip(clientX) {
1812
1826
  const rect = this.el.getBoundingClientRect();
1813
1827
  const percent = Math.max(0, Math.min(1, (clientX - rect.left) / rect.width));
1814
- const time = this.getTimeFromPosition(clientX);
1828
+ const time = this.getTimeFromPosition(clientX) ?? 0;
1815
1829
  const live = this.api.getState("live");
1816
1830
  const seekableRange = this.api.getState("seekableRange");
1817
1831
  if (live && seekableRange) {
@@ -1846,7 +1860,9 @@ var ProgressBar = class {
1846
1860
  }
1847
1861
  this.lastSeekTime = now;
1848
1862
  const time = this.getTimeFromPosition(clientX);
1849
- video.currentTime = time;
1863
+ if (time !== null && Number.isFinite(time)) {
1864
+ video.currentTime = time;
1865
+ }
1850
1866
  }
1851
1867
  destroy() {
1852
1868
  this.wrapper.removeEventListener("mousedown", this.onMouseDown);
@@ -2077,11 +2093,9 @@ var LiveIndicator = class {
2077
2093
  }
2078
2094
  }
2079
2095
  seekToLive() {
2080
- const video = getVideo(this.api.container);
2081
- if (!video) return;
2082
2096
  const seekableRange = this.api.getState("seekableRange");
2083
2097
  if (seekableRange) {
2084
- video.currentTime = seekableRange.end;
2098
+ this.api.emit("playback:seeking", { time: seekableRange.end });
2085
2099
  }
2086
2100
  }
2087
2101
  destroy() {
@@ -2127,6 +2141,9 @@ var QualityMenu = class {
2127
2141
  const qualities = this.api.getState("qualities") || [];
2128
2142
  const currentQuality = this.api.getState("currentQuality");
2129
2143
  this.el.style.display = qualities.length > 0 ? "" : "none";
2144
+ if (qualities.length === 0 && this.isOpen) {
2145
+ this.close();
2146
+ }
2130
2147
  this.btnLabel.textContent = currentQuality?.label || "Auto";
2131
2148
  const qualitiesJson = JSON.stringify(qualities.map((q) => q.id));
2132
2149
  const currentId = currentQuality?.id || "auto";
@@ -2191,6 +2208,16 @@ var QualityMenu = class {
2191
2208
  this.menu.classList.remove("sp-quality-menu--open");
2192
2209
  this.btn.setAttribute("aria-expanded", "false");
2193
2210
  }
2211
+ /**
2212
+ * Whether the dropdown is open.
2213
+ *
2214
+ * Read by the UI plugin so the control bar's auto-hide waits for it.
2215
+ *
2216
+ * @returns True while the menu is showing
2217
+ */
2218
+ isMenuOpen() {
2219
+ return this.isOpen;
2220
+ }
2194
2221
  destroy() {
2195
2222
  document.removeEventListener("click", this.closeHandler);
2196
2223
  this.el.remove();
@@ -2615,7 +2642,7 @@ var SettingsMenu = class {
2615
2642
  this.el.appendChild(this.panel);
2616
2643
  this.closeHandler = (e) => {
2617
2644
  if (!this.el.contains(e.target)) {
2618
- this.close();
2645
+ this.close(false);
2619
2646
  }
2620
2647
  };
2621
2648
  document.addEventListener("click", this.closeHandler);
@@ -2628,7 +2655,6 @@ var SettingsMenu = class {
2628
2655
  this.showPanel("main");
2629
2656
  } else {
2630
2657
  this.close();
2631
- this.btn.focus();
2632
2658
  }
2633
2659
  return;
2634
2660
  }
@@ -2665,6 +2691,8 @@ var SettingsMenu = class {
2665
2691
  this.updateSpeedActiveStates();
2666
2692
  } else if (this.currentPanel === "captions") {
2667
2693
  this.updateCaptionsActiveStates();
2694
+ } else if (this.currentPanel === "audio") {
2695
+ this.updateAudioActiveStates();
2668
2696
  }
2669
2697
  }
2670
2698
  }
@@ -2679,11 +2707,25 @@ var SettingsMenu = class {
2679
2707
  this.btn.setAttribute("aria-expanded", "true");
2680
2708
  this.focusFirstItem();
2681
2709
  }
2682
- close() {
2710
+ /**
2711
+ * Close the panel.
2712
+ *
2713
+ * Choosing an item tears down the row that had focus, which drops focus to
2714
+ * `<body>` and strands keyboard viewers outside the player entirely. Hand it
2715
+ * back to the button that opened the menu instead.
2716
+ *
2717
+ * @param restoreFocus - Return focus to the gear button. The outside-click
2718
+ * handler passes false, so a click elsewhere is not pulled back into the bar.
2719
+ */
2720
+ close(restoreFocus = true) {
2721
+ const wasOpen = this.isOpen;
2683
2722
  this.isOpen = false;
2684
2723
  this.currentPanel = "main";
2685
2724
  this.panel.classList.remove("sp-settings-panel--open");
2686
2725
  this.btn.setAttribute("aria-expanded", "false");
2726
+ if (wasOpen && restoreFocus) {
2727
+ this.btn.focus();
2728
+ }
2687
2729
  }
2688
2730
  showPanel(panel) {
2689
2731
  this.currentPanel = panel;
@@ -2700,6 +2742,9 @@ var SettingsMenu = class {
2700
2742
  case "captions":
2701
2743
  this.renderCaptionsPanel();
2702
2744
  break;
2745
+ case "audio":
2746
+ this.renderAudioPanel();
2747
+ break;
2703
2748
  }
2704
2749
  this.focusFirstItem();
2705
2750
  }
@@ -2728,6 +2773,16 @@ var SettingsMenu = class {
2728
2773
  );
2729
2774
  this.panel.appendChild(captionsRow);
2730
2775
  }
2776
+ const audioTracks = this.api.getState("audioTracks") || [];
2777
+ if (audioTracks.length > 1) {
2778
+ const currentAudioTrack = this.api.getState("currentAudioTrack");
2779
+ const audioRow = this.createMainRow(
2780
+ "Audio",
2781
+ currentAudioTrack?.label || "Default",
2782
+ () => this.showPanel("audio")
2783
+ );
2784
+ this.panel.appendChild(audioRow);
2785
+ }
2731
2786
  const speedLabel = playbackRate === 1 ? "Normal" : `${playbackRate}x`;
2732
2787
  const speedRow = this.createMainRow(
2733
2788
  "Speed",
@@ -2828,6 +2883,36 @@ var SettingsMenu = class {
2828
2883
  this.panel.appendChild(item);
2829
2884
  }
2830
2885
  }
2886
+ renderAudioPanel() {
2887
+ this.panel.innerHTML = "";
2888
+ this.panel.className = "sp-settings-panel sp-settings-panel--open sp-settings-panel--sub";
2889
+ const header = this.createSubHeader("Audio");
2890
+ this.panel.appendChild(header);
2891
+ const audioTracks = this.api.getState("audioTracks") || [];
2892
+ const currentAudioTrack = this.api.getState("currentAudioTrack");
2893
+ const activeId = currentAudioTrack?.id ?? null;
2894
+ for (const track of audioTracks) {
2895
+ const item = this.createMenuItem(track.label, track.id, track.id === activeId);
2896
+ item.addEventListener("click", (e) => {
2897
+ e.preventDefault();
2898
+ this.selectAudioTrack(track.id);
2899
+ });
2900
+ this.panel.appendChild(item);
2901
+ }
2902
+ }
2903
+ selectAudioTrack(trackId) {
2904
+ this.api.emit("track:audio", { trackId });
2905
+ this.close();
2906
+ }
2907
+ updateAudioActiveStates() {
2908
+ const currentAudioTrack = this.api.getState("currentAudioTrack");
2909
+ const activeId = currentAudioTrack?.id ?? null;
2910
+ const items = this.panel.querySelectorAll(".sp-settings-panel__item");
2911
+ items.forEach((item) => {
2912
+ const id = item.getAttribute("data-id");
2913
+ item.classList.toggle("sp-settings-panel__item--active", id === activeId);
2914
+ });
2915
+ }
2831
2916
  selectCaption(trackId) {
2832
2917
  this.api.emit("track:text", { trackId });
2833
2918
  this.close();
@@ -3217,6 +3302,17 @@ var OverflowTray = class {
3217
3302
  this.panel.classList.remove("sp-overflow-tray--open");
3218
3303
  this.btn.setAttribute("aria-expanded", "false");
3219
3304
  }
3305
+ /**
3306
+ * Whether the strip is open.
3307
+ *
3308
+ * Read by the UI plugin so the control bar's auto-hide waits for it: hiding
3309
+ * the bar would take the open tray with it, mid-use.
3310
+ *
3311
+ * @returns True while the strip is showing
3312
+ */
3313
+ isMenuOpen() {
3314
+ return this.isOpen;
3315
+ }
3220
3316
  /**
3221
3317
  * Show the button only while the tray holds something the viewer can see.
3222
3318
  *
@@ -3256,19 +3352,53 @@ var OverflowTray = class {
3256
3352
  };
3257
3353
 
3258
3354
  // src/control-registry.ts
3259
- var registry = /* @__PURE__ */ new Map();
3355
+ var globalRegistry = /* @__PURE__ */ new Map();
3356
+ var scopedRegistries = /* @__PURE__ */ new Map();
3260
3357
  var listeners = /* @__PURE__ */ new Set();
3261
- function registerControl(id, factory) {
3262
- registry.set(id, factory);
3358
+ function registerControl(id, factory, options = {}) {
3359
+ const { owner } = options;
3360
+ if (owner) {
3361
+ let scoped = scopedRegistries.get(owner);
3362
+ if (!scoped) {
3363
+ scoped = /* @__PURE__ */ new Map();
3364
+ scopedRegistries.set(owner, scoped);
3365
+ }
3366
+ scoped.set(id, factory);
3367
+ } else {
3368
+ globalRegistry.set(id, factory);
3369
+ }
3263
3370
  for (const listener of listeners) {
3264
- listener(id);
3371
+ listener(id, owner);
3265
3372
  }
3266
3373
  }
3267
- function unregisterControl(id) {
3268
- return registry.delete(id);
3374
+ function unregisterControl(id, options = {}) {
3375
+ const { owner } = options;
3376
+ if (!owner) {
3377
+ return globalRegistry.delete(id);
3378
+ }
3379
+ const scoped = scopedRegistries.get(owner);
3380
+ if (!scoped) {
3381
+ return false;
3382
+ }
3383
+ const removed = scoped.delete(id);
3384
+ if (scoped.size === 0) {
3385
+ scopedRegistries.delete(owner);
3386
+ }
3387
+ return removed;
3388
+ }
3389
+ function unregisterControlsFor(owner) {
3390
+ const removed = scopedRegistries.get(owner)?.size ?? 0;
3391
+ scopedRegistries.delete(owner);
3392
+ return removed;
3269
3393
  }
3270
- function getControlFactory(id) {
3271
- return registry.get(id) ?? null;
3394
+ function getControlFactory(id, owner) {
3395
+ if (owner) {
3396
+ const scoped = scopedRegistries.get(owner)?.get(id);
3397
+ if (scoped) {
3398
+ return scoped;
3399
+ }
3400
+ }
3401
+ return globalRegistry.get(id) ?? null;
3272
3402
  }
3273
3403
  function onControlRegistered(listener) {
3274
3404
  listeners.add(listener);
@@ -3277,12 +3407,13 @@ function onControlRegistered(listener) {
3277
3407
  };
3278
3408
  }
3279
3409
  function resetControlRegistry() {
3280
- registry.clear();
3410
+ globalRegistry.clear();
3411
+ scopedRegistries.clear();
3281
3412
  listeners.clear();
3282
3413
  }
3283
3414
 
3284
3415
  // src/version.ts
3285
- var PKG_VERSION = true ? "1.7.1" : "0.0.0-dev";
3416
+ var PKG_VERSION = true ? "1.9.0" : "0.0.0-dev";
3286
3417
 
3287
3418
  // src/index.ts
3288
3419
  var DEFAULT_LAYOUT = [
@@ -3301,7 +3432,19 @@ var DEFAULT_LAYOUT = [
3301
3432
  "pip",
3302
3433
  "fullscreen"
3303
3434
  ];
3435
+ var STYLE_ID = "sp-ui-styles";
3304
3436
  var DEFAULT_HIDE_DELAY = 3e3;
3437
+ var ACTIVATION_KEYS = /* @__PURE__ */ new Set([" ", "Enter"]);
3438
+ var SLIDER_KEYS = /* @__PURE__ */ new Set([
3439
+ "ArrowLeft",
3440
+ "ArrowRight",
3441
+ "ArrowUp",
3442
+ "ArrowDown",
3443
+ "Home",
3444
+ "End",
3445
+ "PageUp",
3446
+ "PageDown"
3447
+ ]);
3305
3448
  var UNMEASURED_CONTROL_WIDTH = 48;
3306
3449
  var FALLBACK_VOLUME_SLIDER_WIDTH = 64;
3307
3450
  var OVERFLOW_BUTTON_WIDTH = 44;
@@ -3318,7 +3461,7 @@ function uiPlugin(config = {}) {
3318
3461
  let bufferingIndicator = null;
3319
3462
  let errorOverlay = null;
3320
3463
  let bigPlayButton = null;
3321
- let styleEl = null;
3464
+ let releaseStyles = null;
3322
3465
  let controls = [];
3323
3466
  let hideTimeout = null;
3324
3467
  let stateUnsubscribe = null;
@@ -3326,6 +3469,7 @@ function uiPlugin(config = {}) {
3326
3469
  let errorUnsubscribe = null;
3327
3470
  let reconnectingUnsubscribe = null;
3328
3471
  let recoveredUnsubscribe = null;
3472
+ let loadedUnsubscribe = null;
3329
3473
  let controlsVisible = true;
3330
3474
  let rafHandle = null;
3331
3475
  let tray = null;
@@ -3337,6 +3481,7 @@ function uiPlugin(config = {}) {
3337
3481
  let volumeSliderWidth = FALLBACK_VOLUME_SLIDER_WIDTH;
3338
3482
  let lastFitSignature = null;
3339
3483
  let fitPending = true;
3484
+ let addedContainerClass = false;
3340
3485
  const layout = config.controls || DEFAULT_LAYOUT;
3341
3486
  const hideDelay = config.hideDelay ?? DEFAULT_HIDE_DELAY;
3342
3487
  const showBigPlayButton = config.bigPlayButton !== false;
@@ -3379,7 +3524,7 @@ function uiPlugin(config = {}) {
3379
3524
  case "spacer":
3380
3525
  return new Spacer();
3381
3526
  default: {
3382
- const factory = getControlFactory(slot);
3527
+ const factory = getControlFactory(slot, api.container);
3383
3528
  if (factory) {
3384
3529
  try {
3385
3530
  return factory(api);
@@ -3606,6 +3751,10 @@ function uiPlugin(config = {}) {
3606
3751
  updateControls();
3607
3752
  });
3608
3753
  };
3754
+ const hasOpenMenu = () => controls.some((control) => {
3755
+ const menu = control;
3756
+ return typeof menu.isMenuOpen === "function" && menu.isMenuOpen();
3757
+ });
3609
3758
  const showControls = () => {
3610
3759
  if (controlsVisible) {
3611
3760
  resetHideTimer();
@@ -3622,6 +3771,10 @@ function uiPlugin(config = {}) {
3622
3771
  const hideControls = () => {
3623
3772
  const paused = api?.getState("paused");
3624
3773
  if (paused) return;
3774
+ if (hasOpenMenu()) {
3775
+ resetHideTimer();
3776
+ return;
3777
+ }
3625
3778
  controlsVisible = false;
3626
3779
  controlBar?.classList.remove("sp-controls--visible");
3627
3780
  controlBar?.classList.add("sp-controls--hidden");
@@ -3639,22 +3792,33 @@ function uiPlugin(config = {}) {
3639
3792
  const handlePointerActivity = (event) => {
3640
3793
  last_pointer_type = event.pointerType;
3641
3794
  };
3642
- const handleInteraction = () => {
3643
- if (last_pointer_type === "touch") {
3795
+ const handleInteraction = (event) => {
3796
+ if (last_pointer_type === "touch" && !isOnControls(event)) {
3644
3797
  const gestures = api?.getPlugin("gestures");
3645
3798
  if (gestures?.ownsTapInteraction()) return;
3646
3799
  }
3647
3800
  showControls();
3648
3801
  };
3802
+ const isOnControls = (event) => {
3803
+ const target = event?.target;
3804
+ if (!(target instanceof Node)) return false;
3805
+ return Boolean(controlBar?.contains(target)) || Boolean(progressBar?.render().contains(target));
3806
+ };
3649
3807
  const handleMouseLeave = () => {
3650
3808
  hideControls();
3651
3809
  };
3652
3810
  const handleKeyDown = (e) => {
3653
3811
  if (!api.container.contains(document.activeElement)) return;
3812
+ if (e.defaultPrevented) return;
3813
+ if (e.metaKey || e.ctrlKey || e.altKey) return;
3654
3814
  const activeEl = document.activeElement;
3655
3815
  if (activeEl instanceof HTMLInputElement || activeEl instanceof HTMLTextAreaElement || activeEl instanceof HTMLSelectElement || activeEl?.isContentEditable) {
3656
3816
  return;
3657
3817
  }
3818
+ const role = activeEl?.getAttribute("role");
3819
+ const isActivatable = activeEl instanceof HTMLButtonElement || role === "button" || role === "menuitem";
3820
+ if (isActivatable && ACTIVATION_KEYS.has(e.key)) return;
3821
+ if (role === "slider" && SLIDER_KEYS.has(e.key)) return;
3658
3822
  const video = api.container.querySelector("video");
3659
3823
  if (!video) return;
3660
3824
  const live = api.getState("live");
@@ -3721,9 +3885,7 @@ function uiPlugin(config = {}) {
3721
3885
  version: PKG_VERSION,
3722
3886
  async init(pluginApi) {
3723
3887
  api = pluginApi;
3724
- styleEl = document.createElement("style");
3725
- styleEl.textContent = styles;
3726
- document.head.appendChild(styleEl);
3888
+ releaseStyles = injectSharedStyles(STYLE_ID, styles);
3727
3889
  if (config.theme) {
3728
3890
  this.setTheme(config.theme);
3729
3891
  }
@@ -3736,6 +3898,10 @@ function uiPlugin(config = {}) {
3736
3898
  if (containerStyle.position === "static") {
3737
3899
  container.style.position = "relative";
3738
3900
  }
3901
+ if (!container.classList.contains("sp-container")) {
3902
+ container.classList.add("sp-container");
3903
+ addedContainerClass = true;
3904
+ }
3739
3905
  const isPlaying = api.getState("playing");
3740
3906
  gradient = document.createElement("div");
3741
3907
  gradient.className = isPlaying ? "sp-gradient" : "sp-gradient sp-gradient--visible";
@@ -3751,13 +3917,20 @@ function uiPlugin(config = {}) {
3751
3917
  if (payload?.fatal) {
3752
3918
  const error = api.getState("error") || payload;
3753
3919
  errorOverlay?.show(error);
3920
+ scheduleUpdate();
3754
3921
  }
3755
3922
  });
3756
3923
  reconnectingUnsubscribe = api.on("error:reconnecting", () => {
3757
3924
  errorOverlay?.showReconnecting();
3925
+ scheduleUpdate();
3758
3926
  });
3759
3927
  recoveredUnsubscribe = api.on("error:recovered", () => {
3760
3928
  errorOverlay?.hide();
3929
+ scheduleUpdate();
3930
+ });
3931
+ loadedUnsubscribe = api.on("media:loaded", () => {
3932
+ errorOverlay?.hide();
3933
+ scheduleUpdate();
3761
3934
  });
3762
3935
  if (showBigPlayButton) {
3763
3936
  bigPlayButton = new BigPlayButton(api, () => errorOverlay?.isVisible() ?? false);
@@ -3782,10 +3955,13 @@ function uiPlugin(config = {}) {
3782
3955
  });
3783
3956
  resizeObserver.observe(container);
3784
3957
  }
3785
- controlRegistryUnsubscribe = onControlRegistered((id) => {
3958
+ controlRegistryUnsubscribe = onControlRegistered((id, owner) => {
3786
3959
  if (!layout.includes(id)) {
3787
3960
  return;
3788
3961
  }
3962
+ if (owner && owner !== api.container) {
3963
+ return;
3964
+ }
3789
3965
  api.logger.debug(`Control "${id}" registered after init, rebuilding control bar`);
3790
3966
  rebuildControlBar();
3791
3967
  });
@@ -3829,6 +4005,8 @@ function uiPlugin(config = {}) {
3829
4005
  reconnectingUnsubscribe = null;
3830
4006
  recoveredUnsubscribe?.();
3831
4007
  recoveredUnsubscribe = null;
4008
+ loadedUnsubscribe?.();
4009
+ loadedUnsubscribe = null;
3832
4010
  if (api?.container) {
3833
4011
  api.container.removeEventListener("pointerdown", handlePointerActivity);
3834
4012
  api.container.removeEventListener("pointermove", handlePointerActivity);
@@ -3858,8 +4036,12 @@ function uiPlugin(config = {}) {
3858
4036
  gradient = null;
3859
4037
  bufferingIndicator?.remove();
3860
4038
  bufferingIndicator = null;
3861
- styleEl?.remove();
3862
- styleEl = null;
4039
+ releaseStyles?.();
4040
+ releaseStyles = null;
4041
+ if (addedContainerClass) {
4042
+ api?.container?.classList.remove("sp-container");
4043
+ addedContainerClass = false;
4044
+ }
3863
4045
  api?.logger.debug("UI controls plugin destroyed");
3864
4046
  },
3865
4047
  // Public API
@@ -3912,5 +4094,6 @@ export {
3912
4094
  resolveFitItems,
3913
4095
  styles,
3914
4096
  uiPlugin,
3915
- unregisterControl
4097
+ unregisterControl,
4098
+ unregisterControlsFor
3916
4099
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scarlett-player/ui",
3
- "version": "1.8.0",
3
+ "version": "1.9.0",
4
4
  "description": "UI Controls Plugin for Scarlett Player",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -29,7 +29,7 @@
29
29
  "typescript": "^5.3.0",
30
30
  "vitest": "^1.6.0",
31
31
  "jsdom": "^24.0.0",
32
- "@scarlett-player/core": "1.8.0"
32
+ "@scarlett-player/core": "1.9.0"
33
33
  },
34
34
  "keywords": [
35
35
  "video",
@@ -54,6 +54,6 @@
54
54
  "dev": "tsup --watch",
55
55
  "test": "vitest --run",
56
56
  "test:watch": "vitest",
57
- "typecheck": "tsc --noEmit"
57
+ "typecheck": "tsc --noEmit -p tsconfig.typecheck.json"
58
58
  }
59
59
  }