@scarlett-player/ui 1.15.1 → 1.15.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -21,6 +21,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
23
  DEFAULT_PRIORITY: () => DEFAULT_PRIORITY,
24
+ UNRESOLVED_SLOT_GRACE_MS: () => UNRESOLVED_SLOT_GRACE_MS,
24
25
  assertFitLayout: () => assertFitLayout,
25
26
  default: () => index_default,
26
27
  formatLiveTime: () => import_core2.formatLiveTime,
@@ -3642,7 +3643,7 @@ function resetControlRegistry() {
3642
3643
  }
3643
3644
 
3644
3645
  // src/version.ts
3645
- var PKG_VERSION = true ? "1.15.0" : "0.0.0-dev";
3646
+ var PKG_VERSION = true ? "1.15.2" : "0.0.0-dev";
3646
3647
 
3647
3648
  // src/index.ts
3648
3649
  var DEFAULT_LAYOUT = [
@@ -3683,6 +3684,7 @@ var RESIZE_FALLBACK_DEBOUNCE_MS = 100;
3683
3684
  var MENU_HEIGHT_RESERVE = 16;
3684
3685
  var FALLBACK_BAR_HEIGHT = 56;
3685
3686
  var MIN_MENU_HEIGHT = 120;
3687
+ var UNRESOLVED_SLOT_GRACE_MS = 1500;
3686
3688
  function uiPlugin(config = {}) {
3687
3689
  let api;
3688
3690
  let controlBar = null;
@@ -3714,6 +3716,9 @@ function uiPlugin(config = {}) {
3714
3716
  let lastFitSignature = null;
3715
3717
  let fitPending = true;
3716
3718
  let addedContainerClass = false;
3719
+ let rebuildQueued = false;
3720
+ const unresolvedSlots = /* @__PURE__ */ new Set();
3721
+ let unresolvedSlotTimer = null;
3717
3722
  const layout = config.controls || DEFAULT_LAYOUT;
3718
3723
  const hideDelay = config.hideDelay ?? DEFAULT_HIDE_DELAY;
3719
3724
  const showBigPlayButton = config.bigPlayButton !== false;
@@ -3765,15 +3770,26 @@ function uiPlugin(config = {}) {
3765
3770
  return null;
3766
3771
  }
3767
3772
  }
3768
- api.logger.warn(`Unknown control slot: ${slot}`);
3773
+ unresolvedSlots.add(slot);
3774
+ api.logger.debug(`No control registered for slot "${slot}" yet`);
3769
3775
  return null;
3770
3776
  }
3771
3777
  }
3772
3778
  };
3779
+ const reportUnresolvedSlots = () => {
3780
+ unresolvedSlotTimer = null;
3781
+ for (const slot of unresolvedSlots) {
3782
+ api.logger.warn(
3783
+ `Control slot "${slot}" has no registered control after ${UNRESOLVED_SLOT_GRACE_MS}ms - is the plugin installed?`
3784
+ );
3785
+ }
3786
+ unresolvedSlots.clear();
3787
+ };
3773
3788
  const populateControlBar = () => {
3774
3789
  if (!controlBar) {
3775
3790
  return;
3776
3791
  }
3792
+ unresolvedSlots.clear();
3777
3793
  const rules = new Map(
3778
3794
  resolveFitItems(layout, config.priority).map((template) => [template.id, template])
3779
3795
  );
@@ -3963,6 +3979,20 @@ function uiPlugin(config = {}) {
3963
3979
  populateControlBar();
3964
3980
  updateControls();
3965
3981
  };
3982
+ const flushRebuild = () => {
3983
+ if (!rebuildQueued) {
3984
+ return;
3985
+ }
3986
+ rebuildQueued = false;
3987
+ rebuildControlBar();
3988
+ };
3989
+ const queueRebuild = () => {
3990
+ if (rebuildQueued) {
3991
+ return;
3992
+ }
3993
+ rebuildQueued = true;
3994
+ queueMicrotask(flushRebuild);
3995
+ };
3966
3996
  const updateControls = () => {
3967
3997
  controls.forEach((c) => c.update());
3968
3998
  progressBar?.update();
@@ -4191,6 +4221,9 @@ function uiPlugin(config = {}) {
4191
4221
  controlBar.setAttribute("role", "toolbar");
4192
4222
  controlBar.setAttribute("aria-label", "Video controls");
4193
4223
  populateControlBar();
4224
+ if (unresolvedSlots.size > 0) {
4225
+ unresolvedSlotTimer = setTimeout(reportUnresolvedSlots, UNRESOLVED_SLOT_GRACE_MS);
4226
+ }
4194
4227
  container.appendChild(controlBar);
4195
4228
  if (responsive && typeof ResizeObserver === "function") {
4196
4229
  resizeObserver = new ResizeObserver((observed) => {
@@ -4218,8 +4251,8 @@ function uiPlugin(config = {}) {
4218
4251
  if (owner && owner !== api.container) {
4219
4252
  return;
4220
4253
  }
4221
- api.logger.debug(`Control "${id}" registered after init, rebuilding control bar`);
4222
- rebuildControlBar();
4254
+ api.logger.debug(`Control "${id}" registered after init, queuing a control bar rebuild`);
4255
+ queueRebuild();
4223
4256
  });
4224
4257
  container.addEventListener("pointerdown", handlePointerActivity, { passive: true });
4225
4258
  container.addEventListener("pointermove", handlePointerActivity, { passive: true });
@@ -4283,6 +4316,12 @@ function uiPlugin(config = {}) {
4283
4316
  document.removeEventListener("keydown", handleKeyDown);
4284
4317
  controlRegistryUnsubscribe?.();
4285
4318
  controlRegistryUnsubscribe = null;
4319
+ rebuildQueued = false;
4320
+ if (unresolvedSlotTimer) {
4321
+ clearTimeout(unresolvedSlotTimer);
4322
+ unresolvedSlotTimer = null;
4323
+ }
4324
+ unresolvedSlots.clear();
4286
4325
  controls.forEach((c) => c.destroy());
4287
4326
  controls = [];
4288
4327
  entries = [];
@@ -4347,6 +4386,7 @@ var index_default = uiPlugin;
4347
4386
  // Annotate the CommonJS export names for ESM import in node:
4348
4387
  0 && (module.exports = {
4349
4388
  DEFAULT_PRIORITY,
4389
+ UNRESOLVED_SLOT_GRACE_MS,
4350
4390
  assertFitLayout,
4351
4391
  formatLiveTime,
4352
4392
  formatTime,
package/dist/index.d.cts CHANGED
@@ -605,6 +605,23 @@ declare const styles = "\n/* ============================================\n Co
605
605
  * @packageDocumentation
606
606
  */
607
607
 
608
+ /**
609
+ * How long a layout slot may stay without a registered control before the
610
+ * plugin warns about it.
611
+ *
612
+ * Plugin init order is not guaranteed, and the plugins that contribute
613
+ * controls (playlist, chapters) register them from inside
614
+ * `import('@scarlett-player/ui').then(...)`, which lands at least a microtask
615
+ * after this plugin has built its bar. Warning inline at build time therefore
616
+ * flagged every one of those controls on every mount, and again on each
617
+ * rebuild that followed. The bar is built first and the layout judged after
618
+ * this grace instead: the ui chunk is normally already loaded so the import
619
+ * resolves at once, but a host that split it lazily may still be fetching it.
620
+ * A typo in the layout still warns, just this much later.
621
+ *
622
+ * @internal Exported so the tests do not hard-code the number.
623
+ */
624
+ declare const UNRESOLVED_SLOT_GRACE_MS = 1500;
608
625
  /**
609
626
  * Create a UI controls plugin instance.
610
627
  *
@@ -631,4 +648,4 @@ declare const styles = "\n/* ============================================\n Co
631
648
  */
632
649
  declare function uiPlugin(config?: UIPluginConfig): IUIPlugin;
633
650
 
634
- export { type BuiltinControlSlot, type Control, type ControlFactory, type ControlSlot, DEFAULT_PRIORITY, type FitExit, type FitItem, type FitPlan, type FitRank, type FitRule, type FitTemplate, type IUIPlugin, type LayoutConfig, type RegisterControlOptions, type ThemeConfig, type TimelineExtension, type TimelineExtensionFactory, type TimelineSurface, type UIPluginConfig, assertFitLayout, uiPlugin as default, getControlFactory, hasTimelineExtension, icons, planFit, registerControl, registerTimelineExtension, resetControlRegistry, resolveFitItems, styles, uiPlugin, unregisterControl, unregisterControlsFor, unregisterTimelineExtension };
651
+ export { type BuiltinControlSlot, type Control, type ControlFactory, type ControlSlot, DEFAULT_PRIORITY, type FitExit, type FitItem, type FitPlan, type FitRank, type FitRule, type FitTemplate, type IUIPlugin, type LayoutConfig, type RegisterControlOptions, type ThemeConfig, type TimelineExtension, type TimelineExtensionFactory, type TimelineSurface, type UIPluginConfig, UNRESOLVED_SLOT_GRACE_MS, assertFitLayout, uiPlugin as default, getControlFactory, hasTimelineExtension, icons, planFit, registerControl, registerTimelineExtension, resetControlRegistry, resolveFitItems, styles, uiPlugin, unregisterControl, unregisterControlsFor, unregisterTimelineExtension };
package/dist/index.d.ts CHANGED
@@ -605,6 +605,23 @@ declare const styles = "\n/* ============================================\n Co
605
605
  * @packageDocumentation
606
606
  */
607
607
 
608
+ /**
609
+ * How long a layout slot may stay without a registered control before the
610
+ * plugin warns about it.
611
+ *
612
+ * Plugin init order is not guaranteed, and the plugins that contribute
613
+ * controls (playlist, chapters) register them from inside
614
+ * `import('@scarlett-player/ui').then(...)`, which lands at least a microtask
615
+ * after this plugin has built its bar. Warning inline at build time therefore
616
+ * flagged every one of those controls on every mount, and again on each
617
+ * rebuild that followed. The bar is built first and the layout judged after
618
+ * this grace instead: the ui chunk is normally already loaded so the import
619
+ * resolves at once, but a host that split it lazily may still be fetching it.
620
+ * A typo in the layout still warns, just this much later.
621
+ *
622
+ * @internal Exported so the tests do not hard-code the number.
623
+ */
624
+ declare const UNRESOLVED_SLOT_GRACE_MS = 1500;
608
625
  /**
609
626
  * Create a UI controls plugin instance.
610
627
  *
@@ -631,4 +648,4 @@ declare const styles = "\n/* ============================================\n Co
631
648
  */
632
649
  declare function uiPlugin(config?: UIPluginConfig): IUIPlugin;
633
650
 
634
- export { type BuiltinControlSlot, type Control, type ControlFactory, type ControlSlot, DEFAULT_PRIORITY, type FitExit, type FitItem, type FitPlan, type FitRank, type FitRule, type FitTemplate, type IUIPlugin, type LayoutConfig, type RegisterControlOptions, type ThemeConfig, type TimelineExtension, type TimelineExtensionFactory, type TimelineSurface, type UIPluginConfig, assertFitLayout, uiPlugin as default, getControlFactory, hasTimelineExtension, icons, planFit, registerControl, registerTimelineExtension, resetControlRegistry, resolveFitItems, styles, uiPlugin, unregisterControl, unregisterControlsFor, unregisterTimelineExtension };
651
+ export { type BuiltinControlSlot, type Control, type ControlFactory, type ControlSlot, DEFAULT_PRIORITY, type FitExit, type FitItem, type FitPlan, type FitRank, type FitRule, type FitTemplate, type IUIPlugin, type LayoutConfig, type RegisterControlOptions, type ThemeConfig, type TimelineExtension, type TimelineExtensionFactory, type TimelineSurface, type UIPluginConfig, UNRESOLVED_SLOT_GRACE_MS, assertFitLayout, uiPlugin as default, getControlFactory, hasTimelineExtension, icons, planFit, registerControl, registerTimelineExtension, resetControlRegistry, resolveFitItems, styles, uiPlugin, unregisterControl, unregisterControlsFor, unregisterTimelineExtension };
package/dist/index.js CHANGED
@@ -3606,7 +3606,7 @@ function resetControlRegistry() {
3606
3606
  }
3607
3607
 
3608
3608
  // src/version.ts
3609
- var PKG_VERSION = true ? "1.15.0" : "0.0.0-dev";
3609
+ var PKG_VERSION = true ? "1.15.2" : "0.0.0-dev";
3610
3610
 
3611
3611
  // src/index.ts
3612
3612
  var DEFAULT_LAYOUT = [
@@ -3647,6 +3647,7 @@ var RESIZE_FALLBACK_DEBOUNCE_MS = 100;
3647
3647
  var MENU_HEIGHT_RESERVE = 16;
3648
3648
  var FALLBACK_BAR_HEIGHT = 56;
3649
3649
  var MIN_MENU_HEIGHT = 120;
3650
+ var UNRESOLVED_SLOT_GRACE_MS = 1500;
3650
3651
  function uiPlugin(config = {}) {
3651
3652
  let api;
3652
3653
  let controlBar = null;
@@ -3678,6 +3679,9 @@ function uiPlugin(config = {}) {
3678
3679
  let lastFitSignature = null;
3679
3680
  let fitPending = true;
3680
3681
  let addedContainerClass = false;
3682
+ let rebuildQueued = false;
3683
+ const unresolvedSlots = /* @__PURE__ */ new Set();
3684
+ let unresolvedSlotTimer = null;
3681
3685
  const layout = config.controls || DEFAULT_LAYOUT;
3682
3686
  const hideDelay = config.hideDelay ?? DEFAULT_HIDE_DELAY;
3683
3687
  const showBigPlayButton = config.bigPlayButton !== false;
@@ -3729,15 +3733,26 @@ function uiPlugin(config = {}) {
3729
3733
  return null;
3730
3734
  }
3731
3735
  }
3732
- api.logger.warn(`Unknown control slot: ${slot}`);
3736
+ unresolvedSlots.add(slot);
3737
+ api.logger.debug(`No control registered for slot "${slot}" yet`);
3733
3738
  return null;
3734
3739
  }
3735
3740
  }
3736
3741
  };
3742
+ const reportUnresolvedSlots = () => {
3743
+ unresolvedSlotTimer = null;
3744
+ for (const slot of unresolvedSlots) {
3745
+ api.logger.warn(
3746
+ `Control slot "${slot}" has no registered control after ${UNRESOLVED_SLOT_GRACE_MS}ms - is the plugin installed?`
3747
+ );
3748
+ }
3749
+ unresolvedSlots.clear();
3750
+ };
3737
3751
  const populateControlBar = () => {
3738
3752
  if (!controlBar) {
3739
3753
  return;
3740
3754
  }
3755
+ unresolvedSlots.clear();
3741
3756
  const rules = new Map(
3742
3757
  resolveFitItems(layout, config.priority).map((template) => [template.id, template])
3743
3758
  );
@@ -3927,6 +3942,20 @@ function uiPlugin(config = {}) {
3927
3942
  populateControlBar();
3928
3943
  updateControls();
3929
3944
  };
3945
+ const flushRebuild = () => {
3946
+ if (!rebuildQueued) {
3947
+ return;
3948
+ }
3949
+ rebuildQueued = false;
3950
+ rebuildControlBar();
3951
+ };
3952
+ const queueRebuild = () => {
3953
+ if (rebuildQueued) {
3954
+ return;
3955
+ }
3956
+ rebuildQueued = true;
3957
+ queueMicrotask(flushRebuild);
3958
+ };
3930
3959
  const updateControls = () => {
3931
3960
  controls.forEach((c) => c.update());
3932
3961
  progressBar?.update();
@@ -4155,6 +4184,9 @@ function uiPlugin(config = {}) {
4155
4184
  controlBar.setAttribute("role", "toolbar");
4156
4185
  controlBar.setAttribute("aria-label", "Video controls");
4157
4186
  populateControlBar();
4187
+ if (unresolvedSlots.size > 0) {
4188
+ unresolvedSlotTimer = setTimeout(reportUnresolvedSlots, UNRESOLVED_SLOT_GRACE_MS);
4189
+ }
4158
4190
  container.appendChild(controlBar);
4159
4191
  if (responsive && typeof ResizeObserver === "function") {
4160
4192
  resizeObserver = new ResizeObserver((observed) => {
@@ -4182,8 +4214,8 @@ function uiPlugin(config = {}) {
4182
4214
  if (owner && owner !== api.container) {
4183
4215
  return;
4184
4216
  }
4185
- api.logger.debug(`Control "${id}" registered after init, rebuilding control bar`);
4186
- rebuildControlBar();
4217
+ api.logger.debug(`Control "${id}" registered after init, queuing a control bar rebuild`);
4218
+ queueRebuild();
4187
4219
  });
4188
4220
  container.addEventListener("pointerdown", handlePointerActivity, { passive: true });
4189
4221
  container.addEventListener("pointermove", handlePointerActivity, { passive: true });
@@ -4247,6 +4279,12 @@ function uiPlugin(config = {}) {
4247
4279
  document.removeEventListener("keydown", handleKeyDown);
4248
4280
  controlRegistryUnsubscribe?.();
4249
4281
  controlRegistryUnsubscribe = null;
4282
+ rebuildQueued = false;
4283
+ if (unresolvedSlotTimer) {
4284
+ clearTimeout(unresolvedSlotTimer);
4285
+ unresolvedSlotTimer = null;
4286
+ }
4287
+ unresolvedSlots.clear();
4250
4288
  controls.forEach((c) => c.destroy());
4251
4289
  controls = [];
4252
4290
  entries = [];
@@ -4310,6 +4348,7 @@ function uiPlugin(config = {}) {
4310
4348
  var index_default = uiPlugin;
4311
4349
  export {
4312
4350
  DEFAULT_PRIORITY,
4351
+ UNRESOLVED_SLOT_GRACE_MS,
4313
4352
  assertFitLayout,
4314
4353
  index_default as default,
4315
4354
  formatLiveTime,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scarlett-player/ui",
3
- "version": "1.15.1",
3
+ "version": "1.15.2",
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.15.1"
32
+ "@scarlett-player/core": "1.15.2"
33
33
  },
34
34
  "keywords": [
35
35
  "video",