@banou/media-player 0.8.17 → 0.8.18

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
@@ -67,17 +67,17 @@ var re = {
67
67
  1e3,
68
68
  3e3,
69
69
  1e4
70
- ], ue = 6e4, de = 500, z = (e) => e instanceof Error ? e.message : String(e), fe = (e) => {
70
+ ], ue = 6e4, de = 500, fe = (e) => e instanceof Error ? e.message : String(e), pe = (e) => {
71
71
  let t = [], n = e?.cause;
72
72
  for (let e = 0; n != null && e < 8; e++) {
73
73
  if (typeof MediaError < "u" && n instanceof MediaError) {
74
74
  t.push(`MediaError code ${n.code}${n.message ? `: ${n.message}` : ""}`);
75
75
  break;
76
76
  }
77
- t.push(z(n)), n = n?.cause;
77
+ t.push(fe(n)), n = n?.cause;
78
78
  }
79
79
  return t.length ? t.join("\n") : void 0;
80
- }, pe = (e, t, r) => {
80
+ }, me = (e, t, r) => {
81
81
  let a = R(), { read: o, size: s, publicPath: c = "", libavWorkerUrl: l = "", jassubWorkerUrl: f = "", jassubWasmUrl: h = "", jassubLegacyWasmUrl: g, defaultFontUrl: _, bufferSize: v, autoplay: y = !1, seekPrepareBudgetMs: b = de } = r ?? {}, [x, S] = m(void 0), [C, w] = m(0), T = p({
82
82
  count: 0,
83
83
  at: 0
@@ -137,7 +137,7 @@ var re = {
137
137
  ready: !1
138
138
  });
139
139
  let u = (t, n) => {
140
- let r = Date.now(), i = z(t), o = fe(t), s = a.playbackErrors, c = s[s.length - 1];
140
+ let r = Date.now(), i = fe(t), o = pe(t), s = a.playbackErrors, c = s[s.length - 1];
141
141
  if (c && c.message === i && c.detail === o && c.recovered === n) {
142
142
  let e = {
143
143
  ...c,
@@ -246,13 +246,13 @@ var re = {
246
246
  y,
247
247
  C
248
248
  ]);
249
- }, me = 5e3, he = 6e4, B = ({ publicPath: e, workerUrl: t, length: n, read: i, downloadedRanges: a }) => {
249
+ }, he = 5e3, ge = 6e4, _e = ({ publicPath: e, workerUrl: t, length: n, read: i, downloadedRanges: a }) => {
250
250
  let [o, s] = m([]), c = p(null), l = p(i);
251
251
  l.current = i;
252
252
  let u = f(() => a?.map(({ startByteOffset: e, endByteOffset: t }) => [e, t]), [a?.map(({ startByteOffset: e, endByteOffset: t }) => `${e}-${t}`).join(",")]), h = p(u);
253
253
  return h.current = u, d(() => {
254
254
  if (!n || !i || !e || !t) return;
255
- let a = !1, o = null, u, d = me, f = () => {
255
+ let a = !1, o = null, u, d = he, f = () => {
256
256
  r({
257
257
  publicPath: e,
258
258
  workerUrl: t,
@@ -268,7 +268,7 @@ var re = {
268
268
  }
269
269
  o = e, c.current = e, e.update(h.current);
270
270
  }, () => {
271
- a || (u = setTimeout(f, d), d = Math.min(d * 2, he));
271
+ a || (u = setTimeout(f, d), d = Math.min(d * 2, ge));
272
272
  });
273
273
  };
274
274
  return f(), () => {
@@ -281,7 +281,7 @@ var re = {
281
281
  ]), d(() => {
282
282
  c.current?.update(u);
283
283
  }, [u]), o;
284
- }, V = (e, n) => {
284
+ }, z = (e, n) => {
285
285
  let r = p(null), [i, o] = m(!1), [s] = m(() => t());
286
286
  d(() => {
287
287
  if (!e || !n || !s) return;
@@ -309,7 +309,7 @@ var re = {
309
309
  mode: s,
310
310
  burnedIn: i
311
311
  };
312
- }, H = {
312
+ }, B = {
313
313
  headings: {
314
314
  large: "\n font-weight: 600;\n font-size: calc(2.8 * var(--mp-unit));\n line-height: calc(3.4 * var(--mp-unit));\n @media (min-width: 960px) {\n font-size: calc(3.4 * var(--mp-unit));\n line-height: calc(4.1 * var(--mp-unit));\n }\n ",
315
315
  medium: "\n font-weight: 600;\n font-size: calc(2.4 * var(--mp-unit));\n line-height: calc(2.9 * var(--mp-unit));\n @media (min-width: 960px) {\n font-size: calc(2.8 * var(--mp-unit));\n line-height: calc(3.4 * var(--mp-unit));\n }\n ",
@@ -336,14 +336,14 @@ var re = {
336
336
  medium: "\n font-weight: 500;\n font-size: calc(0.8 * var(--mp-unit));\n line-height: calc(1.1 * var(--mp-unit));\n @media (min-width: 960px) {\n font-size: calc(1 * var(--mp-unit));\n line-height: calc(1.4 * var(--mp-unit));\n }\n ",
337
337
  regular: "\n font-weight: 400;\n font-size: calc(0.8 * var(--mp-unit));\n line-height: calc(1.1 * var(--mp-unit));\n @media (min-width: 960px) {\n font-size: calc(1 * var(--mp-unit));\n line-height: calc(1.4 * var(--mp-unit));\n }\n "
338
338
  }
339
- }, ge = h`
339
+ }, ve = h`
340
340
  top: unset !important;
341
341
  left: unset !important;
342
342
  width: 100%;
343
343
  height: 100%;
344
344
  margin: auto;
345
345
  pointer-events: none;
346
- `, _e = h`
346
+ `, ye = h`
347
347
  position: absolute;
348
348
  top: 0;
349
349
  left: 0;
@@ -351,7 +351,7 @@ var re = {
351
351
  padding: calc(1.2 * var(--mp-unit)) calc(1.6 * var(--mp-unit));
352
352
  /* clears a notch once the player is fullscreen; resolves to zero everywhere else */
353
353
  padding-top: calc(calc(1.2 * var(--mp-unit)) + env(safe-area-inset-top, 0px));
354
- ${H.headings.small}
354
+ ${B.headings.small}
355
355
  color: white;
356
356
  text-shadow: 0 0 4px rgba(0, 0, 0, 1);
357
357
  z-index: 2;
@@ -372,7 +372,7 @@ var re = {
372
372
 
373
373
  background: linear-gradient(180deg, rgba(0,0,0,0.4) 0%, rgba(0,0,0,0.3) 30%, rgba(0,0,0,0.2) 60%, rgba(0,0,0,0.1) 80%, transparent 100%);
374
374
  transition: opacity 0.1s cubic-bezier(.4,0,1,1);
375
- `, ve = h`
375
+ `, be = h`
376
376
  position: absolute;
377
377
  inset: 0;
378
378
  z-index: 2;
@@ -392,53 +392,75 @@ var re = {
392
392
  to { transform: rotate(360deg); }
393
393
  `} 0.8s linear infinite;
394
394
  }
395
- `, ye = h`
395
+ `, xe = h`
396
396
  position: absolute;
397
397
  inset: auto 0 20%;
398
398
  z-index: 3;
399
399
  padding: 0 calc(2 * var(--mp-unit));
400
400
  text-align: center;
401
401
  color: #fff;
402
- ${H.bLarge.regular}
402
+ ${B.bLarge.regular}
403
403
  text-shadow: 0 0 4px rgba(0, 0, 0, 1);
404
404
  pointer-events: none;
405
- `, be = (e) => e instanceof Error ? e.message : typeof e == "string" ? e : "Playback failed", xe = ({ onCanvasRef: e }) => {
405
+ `, Se = (e) => e instanceof Error ? e.message : typeof e == "string" ? e : "Playback failed", Ce = ({ onCanvasRef: e }) => {
406
406
  let t = R((e) => e.title), n = R((e) => e.hideUI), r = R((e) => e.playbackError), i = R((e) => e.ready), a = R((e) => e.size), o = R((e) => e.waiting);
407
407
  return /* @__PURE__ */ w(S, { children: [
408
408
  t ? /* @__PURE__ */ C("div", {
409
409
  className: "title",
410
- css: _e,
410
+ css: ye,
411
411
  style: { ...n ? {
412
412
  opacity: "0",
413
413
  pointerEvents: "none"
414
414
  } : {} },
415
415
  children: t
416
416
  }) : void 0,
417
- (a ? !i : o) && !r ? /* @__PURE__ */ C("div", { css: ve }) : void 0,
417
+ (a ? !i : o) && !r ? /* @__PURE__ */ C("div", { css: be }) : void 0,
418
418
  r ? /* @__PURE__ */ C("div", {
419
- css: ye,
420
- children: be(r)
419
+ css: xe,
420
+ children: Se(r)
421
421
  }) : void 0,
422
422
  /* @__PURE__ */ C("canvas", {
423
423
  ref: e,
424
- css: ge
424
+ css: ve
425
425
  })
426
426
  ] });
427
- }, U = 1e-4, W = 1, Se = (e) => Math.max(U, Math.min(W, e)) ** 2, G = (e) => Math.max(U, Math.min(W, e)) ** (W / 2), K = (e) => {
427
+ }, V = 1e-4, H = 1, U = (e) => Math.max(V, Math.min(H, e)) ** 2, W = (e) => Math.max(V, Math.min(H, e)) ** (H / 2), G = (e) => {
428
428
  if (e === void 0) return "0:00";
429
429
  let t = Math.floor(e / 3600), n = Math.floor(e % 3600 / 60), r = Math.floor(e % 60);
430
430
  return t > 0 ? `${t}:${n < 10 ? "0" : ""}${n}:${r < 10 ? "0" : ""}${r}` : `${n}:${r < 10 ? "0" : ""}${r}`;
431
- }, Ce = (e, t) => `${K(e)} / ${K(t)}`, we = (e) => h`
431
+ }, we = (e, t) => `${G(e)} / ${G(t)}`, Te = (e) => h`
432
432
  display: flex;
433
- justify-content: flex-end;
433
+ justify-content: flex-start;
434
434
 
435
- border-radius: calc(0.4 * var(--mp-unit));
435
+ /*
436
+ * Bounded here rather than at each call site.
437
+ *
438
+ * react-tooltip's own chip rule carries width: max-content, so with nothing opposing it a chip
439
+ * grows to the widest UNWRAPPED line of its content: a two sentence tooltip measured 1178px
440
+ * through this component. Both declarations are load bearing, since a bound with no wrapping only
441
+ * moves the overflow inside a narrow box. 26 units is the width control-bar.tsx was already
442
+ * imposing on its own two line tooltip by hand, so nothing in the bar changes shape.
443
+ */
444
+ max-width: min(calc(26 * var(--mp-unit)), calc(100vw - calc(2.4 * var(--mp-unit))));
445
+ white-space: normal;
446
+ overflow-wrap: anywhere;
447
+ text-align: left;
448
+
449
+ /*
450
+ * The radius carries !important for the same reason the paddings below do.
451
+ *
452
+ * react-tooltip injects its stylesheet from a passive effect while emotion inserts through
453
+ * useInsertionEffect, which runs earlier, so react-tooltip's sheet lands in the head LAST at
454
+ * exactly this specificity and wins every tie. Its own radius is 3px, which is what was drawn
455
+ * here until this was stated.
456
+ */
457
+ border-radius: calc(0.4 * var(--mp-unit))!important;
436
458
  user-select: none;
437
459
 
438
460
  z-index: 3;
439
461
 
440
462
  * {
441
- ${H.bMedium.regular}
463
+ ${B.bMedium.regular}
442
464
  }
443
465
 
444
466
  ${e === "sm" && h`
@@ -450,20 +472,48 @@ var re = {
450
472
  ${e === "lg" && h`
451
473
  padding: calc(1.2 * var(--mp-unit))!important;
452
474
  `}
453
- `, q = ({ id: e, toolTipText: t, text: n, delayShow: r = 0, closeDelay: i = 0, offset: a = 20, tooltipPlace: o = "top", disabled: s = !1, size: c = "md" }) => /* @__PURE__ */ w(S, { children: [/* @__PURE__ */ C("div", {
454
- "data-tooltip-id": e,
455
- "data-open": !0,
456
- "data-tooltip-offset": a,
457
- "data-tooltip-delay-show": r,
458
- "data-tooltip-delay-hide": i,
459
- "data-tooltip-place": o,
460
- children: n
461
- }), !s && /* @__PURE__ */ C(ne, {
462
- css: we(c),
463
- id: e,
464
- noArrow: !0,
465
- children: t
466
- })] }), J = ({ ref: e, onChange: t, orientation: n = "horizontal", disabled: r = !1 }) => {
475
+ `, K = {
476
+ x: -1,
477
+ y: -1,
478
+ anchors: 0
479
+ }, Ee = (e) => {
480
+ K.x = e.clientX, K.y = e.clientY;
481
+ }, De = () => (K.anchors++ === 0 && window.addEventListener("pointermove", Ee, {
482
+ capture: !0,
483
+ passive: !0
484
+ }), () => {
485
+ --K.anchors === 0 && window.removeEventListener("pointermove", Ee, { capture: !0 });
486
+ }), q = ({ id: e, toolTipText: t, text: n, delayShow: r = 0, closeDelay: i = 0, offset: a = 20, tooltipPlace: o = "top", disabled: s = !1, size: c = "md" }) => {
487
+ let l = p(null), u = p(null);
488
+ return d(() => {
489
+ let e = De(), t = () => {
490
+ let e = l.current;
491
+ if (!e) return;
492
+ let { left: t, right: n, top: r, bottom: i } = e.getBoundingClientRect();
493
+ K.x >= t && K.x < n && K.y >= r && K.y < i || u.current?.close();
494
+ }, n = ["fullscreenchange", "webkitfullscreenchange"];
495
+ for (let e of n) document.addEventListener(e, t);
496
+ return () => {
497
+ e();
498
+ for (let e of n) document.removeEventListener(e, t);
499
+ };
500
+ }, []), /* @__PURE__ */ w(S, { children: [/* @__PURE__ */ C("div", {
501
+ ref: l,
502
+ "data-tooltip-id": e,
503
+ "data-open": !0,
504
+ "data-tooltip-offset": a,
505
+ "data-tooltip-delay-show": r,
506
+ "data-tooltip-delay-hide": i,
507
+ "data-tooltip-place": o,
508
+ children: n
509
+ }), !s && /* @__PURE__ */ C(ne, {
510
+ ref: u,
511
+ css: Te(c),
512
+ id: e,
513
+ noArrow: !0,
514
+ children: t
515
+ })] });
516
+ }, J = ({ ref: e, onChange: t, orientation: n = "horizontal", disabled: r = !1 }) => {
467
517
  let [i, a] = m(!1), o = p(t);
468
518
  o.current = t;
469
519
  let s = p(null), c = u((t, r) => {
@@ -502,7 +552,7 @@ var re = {
502
552
  style: { touchAction: "none" }
503
553
  }
504
554
  };
505
- }, Te = h`
555
+ }, Oe = h`
506
556
  position: relative;
507
557
  height: calc(.4 * var(--mp-unit));
508
558
 
@@ -556,7 +606,7 @@ var re = {
556
606
  box-shadow: 0 0 calc(1 * var(--mp-unit)) rgba(0, 0, 0, .5);
557
607
 
558
608
  text-shadow: 0 0 4px rgba(0, 0, 0, 1);
559
- ${H.bMedium.bold}
609
+ ${B.bMedium.bold}
560
610
 
561
611
  position: absolute;
562
612
  /* Anchored on its bottom edge rather than its top: now that it has a background its height
@@ -673,14 +723,14 @@ var re = {
673
723
  --thumbnail-width: calc(18 * var(--mp-unit));
674
724
  --thumbnail-half: calc(9 * var(--mp-unit));
675
725
  }
676
- `, Ee = 4, De = () => {
726
+ `, ke = 4, Ae = () => {
677
727
  let e = R(), t = R((e) => e.currentTime), n = R((e) => e.requestSeek), r = R((e) => e.duration), i = R((e) => e.size), a = R((e) => e.downloadedRanges), o = R((e) => e.indexes), s = R((e) => e.thumbnails), c = R((e) => e.thumbnailAt), l = p(null), [u, h] = m(void 0), [g, _] = m(void 0), v = p(void 0), y = p(0), b = p(void 0), x = p(void 0), S = p(!1), { dragging: T, handlers: E } = J({
678
728
  ref: l,
679
729
  onChange: (e) => {
680
730
  if (y.current += 1, b.current = e, x.current === void 0) x.current = e;
681
731
  else if (!S.current) {
682
732
  let t = l.current?.getBoundingClientRect().width ?? 0;
683
- Math.abs(e - x.current) * t > Ee && (S.current = !0);
733
+ Math.abs(e - x.current) * t > ke && (S.current = !0);
684
734
  }
685
735
  h(e), v.current !== "mouse" && _(e * r);
686
736
  }
@@ -742,7 +792,7 @@ var re = {
742
792
  g
743
793
  ]);
744
794
  return /* @__PURE__ */ w("div", {
745
- css: Te,
795
+ css: Oe,
746
796
  ref: l,
747
797
  className: T ? "progress-bar dragging" : "progress-bar",
748
798
  onMouseMove: A,
@@ -783,7 +833,7 @@ var re = {
783
833
  })
784
834
  ]
785
835
  });
786
- }, Oe = "data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20width='24'%20height='24'%20viewBox='0%200%2024%2024'%20fill='none'%20stroke='%23fff'%20stroke-width='2'%20stroke-linecap='round'%20stroke-linejoin='round'%20class='icon%20icon-tabler%20icons-tabler-outline%20icon-tabler-picture-in-picture'%3e%3cpath%20stroke='none'%20d='M0%200h24v24H0z'%20fill='none'%20/%3e%3cpath%20d='M11%2019h-6a2%202%200%200%201%20-2%20-2v-10a2%202%200%200%201%202%20-2h14a2%202%200%200%201%202%202v4'%20/%3e%3cpath%20d='M14%2014m0%201a1%201%200%200%201%201%20-1h5a1%201%200%200%201%201%201v3a1%201%200%200%201%20-1%201h-5a1%201%200%200%201%20-1%20-1z'%20/%3e%3c/svg%3e", Y = {
836
+ }, je = "data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20width='24'%20height='24'%20viewBox='0%200%2024%2024'%20fill='none'%20stroke='%23fff'%20stroke-width='2'%20stroke-linecap='round'%20stroke-linejoin='round'%20class='icon%20icon-tabler%20icons-tabler-outline%20icon-tabler-picture-in-picture'%3e%3cpath%20stroke='none'%20d='M0%200h24v24H0z'%20fill='none'%20/%3e%3cpath%20d='M11%2019h-6a2%202%200%200%201%20-2%20-2v-10a2%202%200%200%201%202%20-2h14a2%202%200%200%201%202%202v4'%20/%3e%3cpath%20d='M14%2014m0%201a1%201%200%200%201%201%20-1h5a1%201%200%200%201%201%201v3a1%201%200%200%201%20-1%201h-5a1%201%200%200%201%20-1%20-1z'%20/%3e%3c/svg%3e", Y = {
787
837
  xmlns: "http://www.w3.org/2000/svg",
788
838
  width: 24,
789
839
  height: 24,
@@ -793,7 +843,7 @@ var re = {
793
843
  strokeWidth: 2,
794
844
  strokeLinecap: "round",
795
845
  strokeLinejoin: "round"
796
- }, ke = (e) => /* @__PURE__ */ w("svg", {
846
+ }, Me = (e) => /* @__PURE__ */ w("svg", {
797
847
  ...Y,
798
848
  ...e,
799
849
  children: [/* @__PURE__ */ C("rect", {
@@ -804,7 +854,7 @@ var re = {
804
854
  rx: "2",
805
855
  ry: "2"
806
856
  }), /* @__PURE__ */ C("path", { d: "M7 15h4m4 0h2M7 11h2m4 0h4" })]
807
- }), Ae = (e) => /* @__PURE__ */ w("svg", {
857
+ }), Ne = (e) => /* @__PURE__ */ w("svg", {
808
858
  ...Y,
809
859
  ...e,
810
860
  children: [
@@ -824,7 +874,7 @@ var re = {
824
874
  y2: "22"
825
875
  })
826
876
  ]
827
- }), je = (e) => /* @__PURE__ */ w("svg", {
877
+ }), Pe = (e) => /* @__PURE__ */ w("svg", {
828
878
  ...Y,
829
879
  ...e,
830
880
  children: [
@@ -839,6 +889,21 @@ var re = {
839
889
  }),
840
890
  /* @__PURE__ */ C("path", { d: "M14.5 18.5h2m2 0h1" })
841
891
  ]
892
+ }), Fe = (e) => /* @__PURE__ */ w("svg", {
893
+ ...Y,
894
+ ...e,
895
+ children: [
896
+ /* @__PURE__ */ C("path", { d: "M21 11V7a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h5" }),
897
+ /* @__PURE__ */ C("rect", {
898
+ x: "12",
899
+ y: "13",
900
+ width: "10",
901
+ height: "8",
902
+ rx: "1",
903
+ ry: "1"
904
+ }),
905
+ /* @__PURE__ */ C("path", { d: "M6 9h3m2 0h2" })
906
+ ]
842
907
  }), X = h`
843
908
  .popover {
844
909
  position: absolute;
@@ -878,7 +943,7 @@ var re = {
878
943
 
879
944
  border-radius: 8px;
880
945
  background-color: rgba(28,28,28,0.95);
881
- ${H.bMedium.regular}
946
+ ${B.bMedium.regular}
882
947
 
883
948
  z-index: 4;
884
949
 
@@ -921,7 +986,7 @@ var re = {
921
986
 
922
987
  .secondary {
923
988
  color: #eee;
924
- ${H.bSmall.regular}
989
+ ${B.bSmall.regular}
925
990
  }
926
991
  }
927
992
  }
@@ -963,10 +1028,10 @@ var re = {
963
1028
  .failed {
964
1029
  border-bottom: 1px solid #4D4D4E;
965
1030
  color: #f66;
966
- ${H.bSmall.regular}
1031
+ ${B.bSmall.regular}
967
1032
  }
968
1033
  }
969
- `, Me = ({ title: e, tracks: t, selected: n, onSelect: r, onBack: i, offLabel: a, pending: o, failed: s }) => {
1034
+ `, Ie = ({ title: e, tracks: t, selected: n, onSelect: r, onBack: i, offLabel: a, pending: o, failed: s }) => {
970
1035
  let c = o !== void 0, l = (e, i, a) => {
971
1036
  let s = c && o === e, l = c || t.find((t) => t.id === e)?.disabled;
972
1037
  return /* @__PURE__ */ w("div", {
@@ -1034,7 +1099,7 @@ var re = {
1034
1099
  });
1035
1100
  }
1036
1101
  };
1037
- }, Ne = h`
1102
+ }, Le = h`
1038
1103
  /* Not a containing block, for the reason given on popoverStyle: the menu anchors to the control bar,
1039
1104
  which is the width of the player box, so it can be clamped to it. */
1040
1105
  position: static;
@@ -1078,7 +1143,7 @@ ${X}
1078
1143
 
1079
1144
  color: #fff;
1080
1145
  cursor: pointer;
1081
- ${H.bSmall.regular}
1146
+ ${B.bSmall.regular}
1082
1147
 
1083
1148
  &:hover {
1084
1149
  background-color: rgba(255,255,255,.1);
@@ -1099,7 +1164,7 @@ ${X}
1099
1164
 
1100
1165
  .when {
1101
1166
  color: #bbb;
1102
- ${H.bSmall.regular}
1167
+ ${B.bSmall.regular}
1103
1168
 
1104
1169
  /* the one part of the line worth scanning for, so it is the one part at full brightness */
1105
1170
  .count {
@@ -1118,27 +1183,27 @@ ${X}
1118
1183
  color: #ddd;
1119
1184
  overflow-wrap: anywhere;
1120
1185
  white-space: pre-wrap;
1121
- ${H.bSmall.regular}
1186
+ ${B.bSmall.regular}
1122
1187
  }
1123
1188
  }
1124
1189
  }
1125
- `, Pe = (e) => new Date(e).toLocaleTimeString(), Q = (e) => e === void 0 || !Number.isFinite(e) ? void 0 : K(Math.max(0, e)), Fe = (e) => e.reduce((e, t) => e + t.count, 0), Ie = (e) => e.map((e, t) => {
1190
+ `, Re = (e) => new Date(e).toLocaleTimeString(), Q = (e) => e === void 0 || !Number.isFinite(e) ? void 0 : G(Math.max(0, e)), ze = (e) => e.reduce((e, t) => e + t.count, 0), Be = (e) => e.map((e, t) => {
1126
1191
  let n = Q(e.atMediaTime), r = e.count > 1 ? `${new Date(e.at).toISOString()} to ${new Date(e.lastAt).toISOString()} ×${e.count}` : new Date(e.at).toISOString();
1127
1192
  return [
1128
1193
  `${t + 1}. ${r}${n ? ` (at ${n})` : ""}${e.recovered ? " [recovered]" : ""}`,
1129
1194
  e.message,
1130
1195
  e.detail
1131
1196
  ].filter(Boolean).join("\n");
1132
- }).join("\n\n"), Le = () => {
1197
+ }).join("\n\n"), Ve = () => {
1133
1198
  let e = R((e) => e.playbackErrors), { open: t, toggle: n, containerRef: r } = Z(), [i, a] = m(!1);
1134
1199
  if (e.length === 0) return null;
1135
1200
  let o = () => {
1136
- navigator.clipboard?.writeText(Ie(e)).then(() => {
1201
+ navigator.clipboard?.writeText(Be(e)).then(() => {
1137
1202
  a(!0), setTimeout(() => a(!1), 2e3);
1138
1203
  }, (e) => console.warn("[media-player] could not copy the errors:", e));
1139
- }, s = `Playback errors (${Fe(e)})`;
1204
+ }, s = `Playback errors (${ze(e)})`;
1140
1205
  return /* @__PURE__ */ w("div", {
1141
- css: Ne,
1206
+ css: Le,
1142
1207
  ref: r,
1143
1208
  children: [/* @__PURE__ */ C(q, {
1144
1209
  id: "errors",
@@ -1169,8 +1234,8 @@ ${X}
1169
1234
  /* @__PURE__ */ w("span", {
1170
1235
  className: "when",
1171
1236
  children: [
1172
- Pe(e.at),
1173
- e.count > 1 ? ` to ${Pe(e.lastAt)}` : "",
1237
+ Re(e.at),
1238
+ e.count > 1 ? ` to ${Re(e.lastAt)}` : "",
1174
1239
  Q(e.atMediaTime) ? ` at ${Q(e.atMediaTime)}` : "",
1175
1240
  e.recovered ? " recovered" : "",
1176
1241
  e.count > 1 ? /* @__PURE__ */ C("span", {
@@ -1191,7 +1256,7 @@ ${X}
1191
1256
  }, `${e.at}-${t}`))]
1192
1257
  })]
1193
1258
  });
1194
- }, Re = h`
1259
+ }, He = h`
1195
1260
  display: flex;
1196
1261
  align-items: center;
1197
1262
  justify-content: center;
@@ -1244,7 +1309,7 @@ width: 100%;
1244
1309
  }
1245
1310
  }
1246
1311
  }
1247
- `, ze = () => {
1312
+ `, Ue = () => {
1248
1313
  let e = R(), t = R((e) => e.playbackRate), n = p(null), r = p(null), i = .25, a = 3 - i, o = (e, t = .05) => Math.round(e / t) * t, s = o(t || 1), c = (e) => (e - i) / a * 100, l = (e) => o(i + e * a), { handlers: u } = J({
1249
1314
  ref: r,
1250
1315
  onChange: (t) => e.setPlaybackRate(l(t))
@@ -1261,7 +1326,7 @@ width: 100%;
1261
1326
  }, [t]);
1262
1327
  let m = c(s);
1263
1328
  return /* @__PURE__ */ w("div", {
1264
- css: Re,
1329
+ css: He,
1265
1330
  children: [/* @__PURE__ */ w("div", { children: [s.toFixed(2), "x"] }), /* @__PURE__ */ C("div", {
1266
1331
  className: "playback-slider",
1267
1332
  ref: n,
@@ -1279,7 +1344,7 @@ width: 100%;
1279
1344
  })
1280
1345
  })]
1281
1346
  });
1282
- }, Be = h`
1347
+ }, We = h`
1283
1348
  /* Deliberately NOT a containing block. The menu anchors to the control bar instead, which is exactly
1284
1349
  as wide as the player box and can therefore be clamped to it. The outside-click check uses
1285
1350
  \`contains\` on the ref, which is DOM containment and needs no position, and the bar's other
@@ -1338,7 +1403,7 @@ ${X}
1338
1403
  }
1339
1404
  }
1340
1405
 
1341
- `, Ve = () => {
1406
+ `, Ge = () => {
1342
1407
  let e = R(), t = R((e) => e.playbackRate), n = R((e) => e.audioTracks), r = R((e) => e.selectedAudioTrack), i = R((e) => e.selectAudioTrack), [a, o] = m(0), { open: s, toggle: c, containerRef: l, pending: u, failed: d, runSelect: f } = Z({ onReset: () => o(0) }), p = (t) => {
1343
1408
  e.setPlaybackRate(t), o(0);
1344
1409
  }, h = (e) => {
@@ -1347,7 +1412,7 @@ ${X}
1347
1412
  o(e);
1348
1413
  };
1349
1414
  return /* @__PURE__ */ w("div", {
1350
- css: Be,
1415
+ css: We,
1351
1416
  ref: l,
1352
1417
  children: [
1353
1418
  /* @__PURE__ */ C(q, {
@@ -1386,7 +1451,7 @@ ${X}
1386
1451
  }),
1387
1452
  /* @__PURE__ */ C("div", {
1388
1453
  className: "slider no-hover",
1389
- children: /* @__PURE__ */ C(ze, {})
1454
+ children: /* @__PURE__ */ C(Ue, {})
1390
1455
  }),
1391
1456
  /* @__PURE__ */ w("div", {
1392
1457
  className: "options no-hover",
@@ -1411,7 +1476,7 @@ ${X}
1411
1476
  })
1412
1477
  ]
1413
1478
  }),
1414
- s && a === 2 && /* @__PURE__ */ C(Me, {
1479
+ s && a === 2 && /* @__PURE__ */ C(Ie, {
1415
1480
  title: "Audio",
1416
1481
  tracks: n,
1417
1482
  selected: r,
@@ -1422,7 +1487,7 @@ ${X}
1422
1487
  })
1423
1488
  ]
1424
1489
  });
1425
- }, He = h`
1490
+ }, Ke = h`
1426
1491
  /* Not a containing block, for the reason given on popoverStyle: the menu anchors to the control bar,
1427
1492
  which is the width of the player box, so it can be clamped to it. */
1428
1493
  position: static;
@@ -1439,12 +1504,12 @@ position: static;
1439
1504
  }
1440
1505
 
1441
1506
  ${X}
1442
- `, Ue = () => {
1507
+ `, qe = () => {
1443
1508
  let e = R((e) => e.subtitleTracks), t = R((e) => e.selectedSubtitleTrack), n = R((e) => e.selectSubtitleTrack), r = R((e) => e.subtitleOffLabel), { open: i, toggle: a, containerRef: o, pending: s, failed: c, runSelect: l } = Z();
1444
1509
  if (e.length === 0) return null;
1445
1510
  let u = t !== void 0;
1446
1511
  return /* @__PURE__ */ w("div", {
1447
- css: He,
1512
+ css: Ke,
1448
1513
  ref: o,
1449
1514
  children: [/* @__PURE__ */ C(q, {
1450
1515
  id: "subtitles",
@@ -1456,10 +1521,10 @@ ${X}
1456
1521
  "aria-label": "Subtitles",
1457
1522
  "aria-expanded": i,
1458
1523
  "aria-pressed": u,
1459
- children: u ? /* @__PURE__ */ C(ke, { className: "captions" }) : /* @__PURE__ */ C(Ae, { className: "captions-off" })
1524
+ children: u ? /* @__PURE__ */ C(Me, { className: "captions" }) : /* @__PURE__ */ C(Ne, { className: "captions-off" })
1460
1525
  }),
1461
1526
  toolTipText: /* @__PURE__ */ C("span", { children: "Subtitles" })
1462
- }), i && /* @__PURE__ */ C(Me, {
1527
+ }), i && /* @__PURE__ */ C(Ie, {
1463
1528
  title: "Subtitles",
1464
1529
  tracks: e,
1465
1530
  selected: t,
@@ -1469,14 +1534,13 @@ ${X}
1469
1534
  failed: c
1470
1535
  })]
1471
1536
  });
1472
- }, We = {
1537
+ }, Je = {
1473
1538
  primary: "#EAEBEE",
1474
1539
  secondary: "#D0D0D9",
1475
1540
  hover: "rgba(255, 255, 255, 0.13)",
1476
- accent: "#6EA8FE",
1477
1541
  borderPrimary: "#384F70",
1478
1542
  backgroundTooltip: "#222222"
1479
- }, Ge = h`
1543
+ }, Ye = h`
1480
1544
  display: flex;
1481
1545
  align-items: center;
1482
1546
 
@@ -1519,7 +1583,7 @@ ${X}
1519
1583
  width: 10px;
1520
1584
  height: 10px;
1521
1585
  }
1522
- `, Ke = ({ value: e, onChange: t }) => {
1586
+ `, Xe = ({ value: e, onChange: t }) => {
1523
1587
  let n = R((e) => e.volume), r = R((e) => e.muted), i = p(null), a = p(null), { handlers: o } = J({
1524
1588
  ref: a,
1525
1589
  onChange: t
@@ -1534,12 +1598,12 @@ ${X}
1534
1598
  e && e.removeEventListener("wheel", s);
1535
1599
  };
1536
1600
  }, [e]);
1537
- let c = e * 100, l = f(() => Math.round(r ? 0 : G(n) * 100), [n, r]);
1601
+ let c = e * 100, l = f(() => Math.round(r ? 0 : W(n) * 100), [n, r]);
1538
1602
  return /* @__PURE__ */ C(q, {
1539
1603
  id: "volume-slider",
1540
1604
  text: /* @__PURE__ */ C("div", {
1541
1605
  ref: i,
1542
- css: Ge,
1606
+ css: Ye,
1543
1607
  ...o,
1544
1608
  children: /* @__PURE__ */ C("div", {
1545
1609
  ref: a,
@@ -1558,7 +1622,7 @@ ${X}
1558
1622
  children: [l, "%"]
1559
1623
  })
1560
1624
  });
1561
- }, qe = h`
1625
+ }, Ze = h`
1562
1626
  position: relative;
1563
1627
  display: flex;
1564
1628
  align-items: center;
@@ -1583,7 +1647,7 @@ ${X}
1583
1647
  border-radius: 4px;
1584
1648
 
1585
1649
  .volume-value {
1586
- ${H.bMedium.regular};
1650
+ ${B.bMedium.regular};
1587
1651
  color: #ffffff;
1588
1652
  }
1589
1653
  }
@@ -1621,10 +1685,10 @@ ${X}
1621
1685
  }
1622
1686
  }
1623
1687
 
1624
- `, Je = ({ ref: e }) => {
1625
- let t = R(), n = R((e) => e.volume), r = R((e) => e.muted), i = f(() => G(n), [n]);
1688
+ `, Qe = ({ ref: e }) => {
1689
+ let t = R(), n = R((e) => e.volume), r = R((e) => e.muted), i = f(() => W(n), [n]);
1626
1690
  return /* @__PURE__ */ w("div", {
1627
- css: qe,
1691
+ css: Ze,
1628
1692
  children: [/* @__PURE__ */ C(q, {
1629
1693
  id: "sound",
1630
1694
  text: /* @__PURE__ */ C("button", {
@@ -1644,27 +1708,25 @@ ${X}
1644
1708
  className: "volume-slider-container",
1645
1709
  children: /* @__PURE__ */ C("div", {
1646
1710
  className: "volume-slider-background",
1647
- children: /* @__PURE__ */ C(Ke, {
1711
+ children: /* @__PURE__ */ C(Xe, {
1648
1712
  value: r ? 0 : i,
1649
1713
  onChange: (e) => {
1650
- t.setVolume(Se(e));
1714
+ t.setVolume(U(e));
1651
1715
  }
1652
1716
  })
1653
1717
  })
1654
1718
  })]
1655
1719
  });
1656
- }, $ = .05, Ye = 5, Xe = h`
1720
+ }, $ = .05, $e = 5, et = h`
1657
1721
  display: flex;
1658
1722
  flex-direction: column;
1659
1723
  gap: calc(0.4 * var(--mp-unit));
1660
- max-width: calc(26 * var(--mp-unit));
1661
- white-space: normal;
1662
1724
 
1663
1725
  .hint {
1664
1726
  opacity: 0.72;
1665
1727
  font-size: 0.9em;
1666
1728
  }
1667
- `, Ze = h`
1729
+ `, tt = h`
1668
1730
  position: absolute;
1669
1731
  bottom: 0;
1670
1732
  width: 100%;
@@ -1722,16 +1784,6 @@ ${X}
1722
1784
  }
1723
1785
  }
1724
1786
 
1725
- /**
1726
- * The burn-in control's own on state.
1727
- *
1728
- * Not \`colors.hover\`: the pointer is on the button at the moment of the click, so an on state
1729
- * drawn in the hover colour is invisible exactly when it is being looked for.
1730
- */
1731
- button[aria-pressed='true'] svg {
1732
- stroke: ${We.accent};
1733
- }
1734
-
1735
1787
  .play, .sound, .time, .errors, .subtitles, .settings, .picture-in-picture, .full-screen {
1736
1788
  display: flex;
1737
1789
  align-items: center;
@@ -1764,7 +1816,7 @@ ${X}
1764
1816
  cursor: pointer;
1765
1817
 
1766
1818
  :hover {
1767
- background-color: ${We.hover};
1819
+ background-color: ${Je.hover};
1768
1820
  }
1769
1821
 
1770
1822
  /* the hit area grows, the icon does not, keyed on the pointer because a narrow desktop
@@ -1781,7 +1833,7 @@ ${X}
1781
1833
 
1782
1834
  .left {
1783
1835
  .time {
1784
- ${H.bMedium.regular}
1836
+ ${B.bMedium.regular}
1785
1837
  text-shadow: 0 0 4px rgba(0, 0, 0, 1);
1786
1838
  }
1787
1839
  }
@@ -1802,10 +1854,10 @@ ${X}
1802
1854
  }
1803
1855
  }
1804
1856
  }
1805
- `, Qe = () => {
1857
+ `, nt = () => {
1806
1858
  let e = R(), t = R((e) => e.paused), n = R((e) => e.currentTime), r = R((e) => e.duration), i = R((e) => e.fullscreen), a = R((e) => e.hideUI), o = R((e) => e.togglePictureInPicture), s = R((e) => e.pictureInPictureMode), c = R((e) => e.burnedInSubtitles), l = R((e) => e.subtitleTracks), f = R((e) => e.requestSeek), [p, h] = m(null), g = s === "burn-in", _ = l.length > 0, v = r > 0 && n === r, y = u(({ direction: t, stepSize: n }) => {
1807
- let r = G(e.volume), i = t === "up" ? n : -n, a = Math.max(0, Math.min(1, r + i)), o = e.muted;
1808
- e.setVolume(Se(a)), o && !e.muted && e.toggleMuted();
1859
+ let r = W(e.volume), i = t === "up" ? n : -n, a = Math.max(0, Math.min(1, r + i)), o = e.muted;
1860
+ e.setVolume(U(a)), o && !e.muted && e.toggleMuted();
1809
1861
  }, [e]);
1810
1862
  d(() => {
1811
1863
  let t = (t) => f ? f(t) : e.seek(t), n = (n) => {
@@ -1826,8 +1878,8 @@ ${X}
1826
1878
  });
1827
1879
  else if (n.key === "ArrowRight") {
1828
1880
  if (!e.duration) return;
1829
- t(Math.min(e.currentTime + Ye, e.duration));
1830
- } else n.key === "ArrowLeft" ? t(Math.max(e.currentTime - Ye, 0)) : i = !1;
1881
+ t(Math.min(e.currentTime + $e, e.duration));
1882
+ } else n.key === "ArrowLeft" ? t(Math.max(e.currentTime - $e, 0)) : i = !1;
1831
1883
  i && n.preventDefault();
1832
1884
  };
1833
1885
  return window.addEventListener("keydown", n), () => window.removeEventListener("keydown", n);
@@ -1848,12 +1900,12 @@ ${X}
1848
1900
  return d(() => {
1849
1901
  if (p) return p.addEventListener("wheel", b, { passive: !1 }), () => p.removeEventListener("wheel", b);
1850
1902
  }, [p, b]), /* @__PURE__ */ w("div", {
1851
- css: Ze,
1903
+ css: tt,
1852
1904
  style: { ...a ? {
1853
1905
  opacity: "0",
1854
1906
  pointerEvents: "none"
1855
1907
  } : {} },
1856
- children: [/* @__PURE__ */ C(De, {}), /* @__PURE__ */ w("div", {
1908
+ children: [/* @__PURE__ */ C(Ae, {}), /* @__PURE__ */ w("div", {
1857
1909
  className: "actions",
1858
1910
  children: [/* @__PURE__ */ w("div", {
1859
1911
  className: "left",
@@ -1870,18 +1922,18 @@ ${X}
1870
1922
  }),
1871
1923
  toolTipText: /* @__PURE__ */ C("span", { children: v ? "Replay (k)" : t ? "Play (k)" : "Pause (k)" })
1872
1924
  }),
1873
- /* @__PURE__ */ C(Je, { ref: h }),
1925
+ /* @__PURE__ */ C(Qe, { ref: h }),
1874
1926
  /* @__PURE__ */ C("div", {
1875
1927
  className: "time",
1876
- children: Ce(n, r)
1928
+ children: we(n, r)
1877
1929
  })
1878
1930
  ]
1879
1931
  }), /* @__PURE__ */ w("div", {
1880
1932
  className: "right",
1881
1933
  children: [
1882
- /* @__PURE__ */ C(Le, {}),
1883
- /* @__PURE__ */ C(Ue, {}),
1884
1934
  /* @__PURE__ */ C(Ve, {}),
1935
+ /* @__PURE__ */ C(qe, {}),
1936
+ /* @__PURE__ */ C(Ge, {}),
1885
1937
  o ? /* @__PURE__ */ C(q, {
1886
1938
  id: "picture-in-picture",
1887
1939
  tooltipPlace: "top-end",
@@ -1892,13 +1944,13 @@ ${X}
1892
1944
  disabled: g && !_,
1893
1945
  "aria-label": g ? "Put the subtitles in the video" : "Picture in picture",
1894
1946
  "aria-pressed": g ? c : void 0,
1895
- children: g ? /* @__PURE__ */ C(je, {}) : /* @__PURE__ */ C("img", {
1896
- src: Oe,
1947
+ children: g ? C(c ? Pe : Fe, {}) : /* @__PURE__ */ C("img", {
1948
+ src: je,
1897
1949
  alt: ""
1898
1950
  })
1899
1951
  }),
1900
1952
  toolTipText: /* @__PURE__ */ w("span", {
1901
- css: Xe,
1953
+ css: et,
1902
1954
  children: [/* @__PURE__ */ C("span", {
1903
1955
  className: "lead",
1904
1956
  children: g ? _ ? c ? "Subtitles are in the picture" : "Put the subtitles in the picture" : "This file has no subtitles" : "Picture in picture"
@@ -1925,7 +1977,7 @@ ${X}
1925
1977
  })]
1926
1978
  })]
1927
1979
  });
1928
- }, $e = 9e3, et = h`
1980
+ }, rt = 9e3, it = h`
1929
1981
  position: absolute;
1930
1982
  inset: 0;
1931
1983
  z-index: 2;
@@ -1963,12 +2015,12 @@ ${X}
1963
2015
  }
1964
2016
 
1965
2017
  .title {
1966
- ${H.bMedium.bold}
2018
+ ${B.bMedium.bold}
1967
2019
  color: #fff;
1968
2020
  }
1969
2021
 
1970
2022
  .body {
1971
- ${H.bSmall.regular}
2023
+ ${B.bSmall.regular}
1972
2024
  color: rgba(255, 255, 255, 0.75);
1973
2025
  }
1974
2026
 
@@ -1977,9 +2029,9 @@ ${X}
1977
2029
  flex: none;
1978
2030
  font-size: calc(2.6 * var(--mp-unit));
1979
2031
  line-height: 1;
1980
- color: #6EA8FE;
2032
+ color: #fff;
1981
2033
  }
1982
- `, tt = () => {
2034
+ `, at = () => {
1983
2035
  let e = R((e) => e.burnedInSubtitles), t = R((e) => e.pictureInPictureMode), [n, r] = m(!1);
1984
2036
  return d(() => {
1985
2037
  if (t !== "burn-in" || !e) {
@@ -1987,10 +2039,10 @@ ${X}
1987
2039
  return;
1988
2040
  }
1989
2041
  r(!0);
1990
- let n = setTimeout(() => r(!1), $e);
2042
+ let n = setTimeout(() => r(!1), rt);
1991
2043
  return () => clearTimeout(n);
1992
2044
  }, [t, e]), t !== "burn-in" || !e ? null : /* @__PURE__ */ C("div", {
1993
- css: et,
2045
+ css: it,
1994
2046
  className: n ? "show" : "",
1995
2047
  role: "status",
1996
2048
  children: /* @__PURE__ */ w("div", {
@@ -2011,7 +2063,7 @@ ${X}
2011
2063
  })]
2012
2064
  })
2013
2065
  });
2014
- }, nt = 3e3, rt = h`
2066
+ }, ot = 3e3, st = h`
2015
2067
  position: absolute;
2016
2068
  inset: 0;
2017
2069
  z-index: 2;
@@ -2022,7 +2074,7 @@ ${X}
2022
2074
  hoverable while faded out, popping a tooltip over nothing. It cascades where pointer-events does
2023
2075
  not, and transitioning it holds the item on screen for the length of the fade. */
2024
2076
  transition: opacity 0.1s cubic-bezier(.4,0,1,1), visibility 0.1s;
2025
- `, it = h`
2077
+ `, ct = h`
2026
2078
  position: relative;
2027
2079
  background-color: black;
2028
2080
  display: flex;
@@ -2054,20 +2106,20 @@ ${X}
2054
2106
  &.hide {
2055
2107
  cursor: none;
2056
2108
  }
2057
- `, at = (e, t = "") => s.toArray(e).flatMap((e, n) => {
2109
+ `, lt = (e, t = "") => s.toArray(e).flatMap((e, n) => {
2058
2110
  let r = `${t}${l(e) && e.key != null ? e.key : n}`;
2059
- return l(e) && e.type === c ? at(e.props.children, `${r}/`) : [{
2111
+ return l(e) && e.type === c ? lt(e.props.children, `${r}/`) : [{
2060
2112
  key: r,
2061
2113
  item: e
2062
2114
  }];
2063
- }), ot = ({ ref: e, onVideoRef: t, onCanvasRef: n, overlay: r, controls: i, children: a }) => {
2115
+ }), ut = ({ ref: e, onVideoRef: t, onCanvasRef: n, overlay: r, controls: i, children: a }) => {
2064
2116
  let o = R(), s = R((e) => e.hideUI), c = R((e) => e.setHideUI), l = p(void 0), u = p("mouse");
2065
2117
  d(() => () => clearTimeout(l.current), []);
2066
2118
  let f = () => {
2067
- c(!1), clearTimeout(l.current), l.current = setTimeout(() => c(!0), nt);
2119
+ c(!1), clearTimeout(l.current), l.current = setTimeout(() => c(!0), ot);
2068
2120
  };
2069
2121
  return /* @__PURE__ */ w("div", {
2070
- css: it,
2122
+ css: ct,
2071
2123
  ref: e,
2072
2124
  onPointerMove: (e) => {
2073
2125
  u.current = e.pointerType, e.pointerType === "mouse" && f();
@@ -2083,9 +2135,9 @@ ${X}
2083
2135
  },
2084
2136
  className: s ? "hide" : "",
2085
2137
  children: [
2086
- /* @__PURE__ */ C(xe, { onCanvasRef: n }),
2087
- at(r).map(({ key: e, item: t }) => /* @__PURE__ */ C("div", {
2088
- css: rt,
2138
+ /* @__PURE__ */ C(Ce, { onCanvasRef: n }),
2139
+ lt(r).map(({ key: e, item: t }) => /* @__PURE__ */ C("div", {
2140
+ css: st,
2089
2141
  style: { ...s ? {
2090
2142
  opacity: "0",
2091
2143
  visibility: "hidden",
@@ -2093,8 +2145,8 @@ ${X}
2093
2145
  } : {} },
2094
2146
  children: t
2095
2147
  }, e)),
2096
- i === !1 ? null : /* @__PURE__ */ C(Qe, {}),
2097
- /* @__PURE__ */ C(tt, {}),
2148
+ i === !1 ? null : /* @__PURE__ */ C(nt, {}),
2149
+ /* @__PURE__ */ C(at, {}),
2098
2150
  /* @__PURE__ */ w("div", {
2099
2151
  className: "video",
2100
2152
  onClick: () => {
@@ -2111,16 +2163,16 @@ ${X}
2111
2163
  })
2112
2164
  ]
2113
2165
  });
2114
- }, st = ({ options: e, children: t }) => {
2166
+ }, dt = ({ options: e, children: t }) => {
2115
2167
  let n = "media" in e ? e : null, r = n ? null : e, { title: i } = e, { size: a, downloadedRanges: o, publicPath: s, libavWorkerUrl: c, read: l, thumbnailRead: u, thumbnailsEnabled: f } = r ?? {}, p = y(), h = v(), [g, _] = m(null), [b, x] = m(null), S = n?.media ?? g;
2116
- d(() => (p?.(S), () => p?.(null)), [S, p]), pe(g, b, r);
2117
- let w = B({
2168
+ d(() => (p?.(S), () => p?.(null)), [S, p]), me(g, b, r);
2169
+ let w = _e({
2118
2170
  publicPath: s,
2119
2171
  workerUrl: c,
2120
2172
  length: f === !1 ? void 0 : a,
2121
2173
  read: u ?? l,
2122
2174
  downloadedRanges: o
2123
- }), T = n?.thumbnails?.all ?? w, { toggle: E, mode: D, burnedIn: O } = V(g, b), k = R((e) => e.setSourceState);
2175
+ }), T = n?.thumbnails?.all ?? w, { toggle: E, mode: D, burnedIn: O } = z(g, b), k = R((e) => e.setSourceState);
2124
2176
  d(() => {
2125
2177
  k({
2126
2178
  title: i,
@@ -2172,7 +2224,7 @@ ${X}
2172
2224
  selectedAudioTrack: M.selectedId ?? void 0,
2173
2225
  selectAudioTrack: (e) => M.select(String(e))
2174
2226
  });
2175
- }, [k, M]), /* @__PURE__ */ C(ot, {
2227
+ }, [k, M]), /* @__PURE__ */ C(ut, {
2176
2228
  ref: h,
2177
2229
  onVideoRef: n ? void 0 : _,
2178
2230
  onCanvasRef: x,
@@ -2180,7 +2232,7 @@ ${X}
2180
2232
  controls: e.controls,
2181
2233
  children: t
2182
2234
  });
2183
- }, ct = h`
2235
+ }, ft = h`
2184
2236
  /**
2185
2237
  * The chrome's whole scale, in one place.
2186
2238
  *
@@ -2250,17 +2302,17 @@ ${X}
2250
2302
  height: 100%;
2251
2303
  width: 100%;
2252
2304
  overflow: hidden;
2253
- `, lt = (e) => /* @__PURE__ */ C("div", {
2254
- css: ct,
2255
- children: /* @__PURE__ */ C(L.Provider, { children: /* @__PURE__ */ C(st, {
2305
+ `, pt = (e) => /* @__PURE__ */ C("div", {
2306
+ css: ft,
2307
+ children: /* @__PURE__ */ C(L.Provider, { children: /* @__PURE__ */ C(dt, {
2256
2308
  options: e,
2257
2309
  children: e.children
2258
2310
  }) })
2259
- }), ut = (e) => !!e && typeof e == "object" && "at" in e, dt = (e) => !!e && typeof e == "object" && "selection" in e, ft = ({ blob: e, name: t }) => ({
2311
+ }), mt = (e) => !!e && typeof e == "object" && "at" in e, ht = (e) => !!e && typeof e == "object" && "selection" in e, gt = ({ blob: e, name: t }) => ({
2260
2312
  length: e.size,
2261
2313
  name: t ?? (e instanceof File ? e.name : void 0),
2262
2314
  read: (t, n) => e.slice(t, t + n).arrayBuffer()
2263
- }), pt = async (e, t) => {
2315
+ }), _t = async (e, t) => {
2264
2316
  let n = await fetch(e, {
2265
2317
  headers: { Range: "bytes=0-1" },
2266
2318
  credentials: t
@@ -2270,8 +2322,8 @@ ${X}
2270
2322
  if (!Number.isFinite(i) || i <= 0) throw Error("The source did not report its length");
2271
2323
  if (!r && n.status !== 206) throw Error("The source does not support range requests");
2272
2324
  return i;
2273
- }, mt = async ({ url: e, length: t, name: n, credentials: r }) => {
2274
- let i = t ?? await pt(e, r);
2325
+ }, vt = async ({ url: e, length: t, name: n, credentials: r }) => {
2326
+ let i = t ?? await _t(e, r);
2275
2327
  return {
2276
2328
  length: i,
2277
2329
  name: n,
@@ -2284,9 +2336,9 @@ ${X}
2284
2336
  return a.arrayBuffer();
2285
2337
  }
2286
2338
  };
2287
- }, ht = async (e) => {
2288
- if ("blob" in e) return ft(e);
2289
- if ("url" in e) return mt(e);
2339
+ }, yt = async (e) => {
2340
+ if ("blob" in e) return gt(e);
2341
+ if ("url" in e) return vt(e);
2290
2342
  if ("read" in e) return {
2291
2343
  length: e.length,
2292
2344
  name: e.name,
@@ -2295,4 +2347,4 @@ ${X}
2295
2347
  throw Error(`Unknown source type: ${e}`);
2296
2348
  };
2297
2349
  //#endregion
2298
- export { lt as MediaPlayer, lt as default, L as Player, e as SUBTITLES_OFF, a as createPictureInPicture, o as createSubtitleRenderer, r as createThumbnailGenerator, ht as inputToRemuxerInput, dt as isDelegatedTracks, ut as isExternalThumbnails, ie as sourceFeature, i as startPlayback, V as usePictureInPicture, R as usePlayer, B as useSeekThumbnails };
2350
+ export { pt as MediaPlayer, pt as default, L as Player, e as SUBTITLES_OFF, a as createPictureInPicture, o as createSubtitleRenderer, r as createThumbnailGenerator, yt as inputToRemuxerInput, ht as isDelegatedTracks, mt as isExternalThumbnails, ie as sourceFeature, i as startPlayback, z as usePictureInPicture, R as usePlayer, _e as useSeekThumbnails };
@@ -10,3 +10,12 @@ export declare const CaptionsOff: (props: SVGProps<SVGSVGElement>) => import("@e
10
10
  * captions glyph either, because the button beside it already is one.
11
11
  */
12
12
  export declare const SubtitlesInPicture: (props: SVGProps<SVGSVGElement>) => import("@emotion/react/jsx-runtime").JSX.Element;
13
+ /**
14
+ * The same frame with the captions still on the MAIN picture: burn-in is available but off.
15
+ *
16
+ * The pair exists because that control is the one button in the bar whose glyph did not move with its
17
+ * state, so the state was carried by an accent colour and by nothing else. A pair, not a slash: a
18
+ * slashed resting state reads as unavailable, and the subtitles button beside it already draws
19
+ * Feather's slash for its own off state.
20
+ */
21
+ export declare const SubtitlesOutsidePicture: (props: SVGProps<SVGSVGElement>) => import("@emotion/react/jsx-runtime").JSX.Element;
@@ -2,8 +2,6 @@ declare const colors: {
2
2
  primary: string;
3
3
  secondary: string;
4
4
  hover: string;
5
- /** A control that is ON. Distinct from `hover`, which the pointer is already painting. */
6
- accent: string;
7
5
  borderPrimary: string;
8
6
  backgroundTooltip: string;
9
7
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@banou/media-player",
3
- "version": "0.8.17",
3
+ "version": "0.8.18",
4
4
  "description": "A video player for containers and codecs the browser cannot play natively, remuxed on the fly",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -59,7 +59,7 @@ const style = css`
59
59
  flex: none;
60
60
  font-size: calc(2.6 * var(--mp-unit));
61
61
  line-height: 1;
62
- color: #6EA8FE;
62
+ color: #fff;
63
63
  }
64
64
  `
65
65
 
@@ -10,7 +10,7 @@ import { usePlayer } from '../player'
10
10
  import { TooltipDisplay } from './tooltip-display'
11
11
  import { ProgressBar } from './progress-bar'
12
12
  import pictureInPicture from '../../assets/picture-in-picture.svg'
13
- import { SubtitlesInPicture } from './icons'
13
+ import { SubtitlesInPicture, SubtitlesOutsidePicture } from './icons'
14
14
  import ErrorsAction from './errors'
15
15
  import SettingsAction from './settings'
16
16
  import SubtitlesAction from './subtitles'
@@ -21,17 +21,15 @@ const VOLUME_STEP = 0.05
21
21
  const SEEK_STEP = 5
22
22
 
23
23
  /**
24
- * Two stacked lines inside a tooltip, with a width to wrap against.
24
+ * Two stacked lines inside a tooltip.
25
25
  *
26
- * Applied to the content rather than to the tooltip, because react-tooltip renders into a portal and
27
- * an unconstrained tooltip grows to one long line that runs off the side of the player.
26
+ * The width to wrap against is no longer here: TooltipDisplay bounds every chip it draws, at the
27
+ * same 26 units this had imposed by hand, so a call site only says what its content is.
28
28
  */
29
29
  const tooltipLinesStyle = css`
30
30
  display: flex;
31
31
  flex-direction: column;
32
32
  gap: calc(0.4 * var(--mp-unit));
33
- max-width: calc(26 * var(--mp-unit));
34
- white-space: normal;
35
33
 
36
34
  .hint {
37
35
  opacity: 0.72;
@@ -97,16 +95,6 @@ const style = css`
97
95
  }
98
96
  }
99
97
 
100
- /**
101
- * The burn-in control's own on state.
102
- *
103
- * Not \`colors.hover\`: the pointer is on the button at the moment of the click, so an on state
104
- * drawn in the hover colour is invisible exactly when it is being looked for.
105
- */
106
- button[aria-pressed='true'] svg {
107
- stroke: ${colors.accent};
108
- }
109
-
110
98
  .play, .sound, .time, .errors, .subtitles, .settings, .picture-in-picture, .full-screen {
111
99
  display: flex;
112
100
  align-items: center;
@@ -333,15 +321,20 @@ export const ControlBar = () => {
333
321
  aria-label={burnIn ? 'Put the subtitles in the video' : 'Picture in picture'}
334
322
  aria-pressed={burnIn ? burnedInSubtitles : undefined}
335
323
  >
324
+ {/* The glyph carries the on state, which is what every other toggle in this bar
325
+ does. It used to be carried by an accent stroke instead, and that was the only
326
+ blue in the chrome. */}
336
327
  {burnIn
337
- ? <SubtitlesInPicture />
328
+ ? burnedInSubtitles
329
+ ? <SubtitlesInPicture />
330
+ : <SubtitlesOutsidePicture />
338
331
  : <img src={pictureInPicture} alt='' />}
339
332
  </button>
340
333
  }
341
334
  toolTipText={
342
- // The tooltip is portaled out of this subtree, so the control bar's own rules
343
- // never reach it: a bare `small` stays inline and runs straight on from the line
344
- // above it. Both lines carry their layout themselves.
335
+ // The chip is drawn in its own subtree, where the control bar's rules never
336
+ // reach it: a bare `small` stays inline and runs straight on from the line above
337
+ // it. Both lines carry their layout themselves.
345
338
  <span css={tooltipLinesStyle}>
346
339
  <span className='lead'>
347
340
  {!burnIn
@@ -51,3 +51,19 @@ export const SubtitlesInPicture = (props: SVGProps<SVGSVGElement>) => (
51
51
  <path d='M14.5 18.5h2m2 0h1' />
52
52
  </svg>
53
53
  )
54
+
55
+ /**
56
+ * The same frame with the captions still on the MAIN picture: burn-in is available but off.
57
+ *
58
+ * The pair exists because that control is the one button in the bar whose glyph did not move with its
59
+ * state, so the state was carried by an accent colour and by nothing else. A pair, not a slash: a
60
+ * slashed resting state reads as unavailable, and the subtitles button beside it already draws
61
+ * Feather's slash for its own off state.
62
+ */
63
+ export const SubtitlesOutsidePicture = (props: SVGProps<SVGSVGElement>) => (
64
+ <svg {...iconProps} {...props}>
65
+ <path d='M21 11V7a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h5' />
66
+ <rect x='12' y='13' width='10' height='8' rx='1' ry='1' />
67
+ <path d='M6 9h3m2 0h2' />
68
+ </svg>
69
+ )
@@ -1,5 +1,7 @@
1
1
  import type { ReactNode } from 'react'
2
+ import type { TooltipRefProps } from 'react-tooltip'
2
3
 
4
+ import { useEffect, useRef } from 'react'
3
5
  import { css } from '@emotion/react'
4
6
  import { PlacesType, Tooltip } from 'react-tooltip'
5
7
 
@@ -13,9 +15,31 @@ export enum buttonSize {
13
15
 
14
16
  const style = (size: buttonSize) => css`
15
17
  display: flex;
16
- justify-content: flex-end;
18
+ justify-content: flex-start;
17
19
 
18
- border-radius: calc(0.4 * var(--mp-unit));
20
+ /*
21
+ * Bounded here rather than at each call site.
22
+ *
23
+ * react-tooltip's own chip rule carries width: max-content, so with nothing opposing it a chip
24
+ * grows to the widest UNWRAPPED line of its content: a two sentence tooltip measured 1178px
25
+ * through this component. Both declarations are load bearing, since a bound with no wrapping only
26
+ * moves the overflow inside a narrow box. 26 units is the width control-bar.tsx was already
27
+ * imposing on its own two line tooltip by hand, so nothing in the bar changes shape.
28
+ */
29
+ max-width: min(calc(26 * var(--mp-unit)), calc(100vw - calc(2.4 * var(--mp-unit))));
30
+ white-space: normal;
31
+ overflow-wrap: anywhere;
32
+ text-align: left;
33
+
34
+ /*
35
+ * The radius carries !important for the same reason the paddings below do.
36
+ *
37
+ * react-tooltip injects its stylesheet from a passive effect while emotion inserts through
38
+ * useInsertionEffect, which runs earlier, so react-tooltip's sheet lands in the head LAST at
39
+ * exactly this specificity and wins every tie. Its own radius is 3px, which is what was drawn
40
+ * here until this was stated.
41
+ */
42
+ border-radius: calc(0.4 * var(--mp-unit))!important;
19
43
  user-select: none;
20
44
 
21
45
  z-index: 3;
@@ -35,6 +59,25 @@ const style = (size: buttonSize) => css`
35
59
  `}
36
60
  `
37
61
 
62
+ /**
63
+ * Where the pointer is, shared by every anchor rather than tracked once per tooltip.
64
+ *
65
+ * The chrome mounts eight of these, and each only needs to answer one question at one moment, so a
66
+ * single refcounted listener answers it for all of them. Installed on the first mount rather than at
67
+ * module scope, so importing this library still does nothing to the document.
68
+ */
69
+ const pointer = { x: -1, y: -1, anchors: 0 }
70
+ const trackPointer = (event: PointerEvent) => { pointer.x = event.clientX; pointer.y = event.clientY }
71
+
72
+ const watchPointer = () => {
73
+ if (pointer.anchors++ === 0) {
74
+ window.addEventListener('pointermove', trackPointer, { capture: true, passive: true })
75
+ }
76
+ return () => {
77
+ if (--pointer.anchors === 0) window.removeEventListener('pointermove', trackPointer, { capture: true })
78
+ }
79
+ }
80
+
38
81
  interface TooltipDisplayProps {
39
82
  id: string
40
83
  toolTipText: ReactNode
@@ -57,30 +100,77 @@ export const TooltipDisplay = ({
57
100
  tooltipPlace = 'top',
58
101
  disabled = false,
59
102
  size = buttonSize.md
60
- }: TooltipDisplayProps) => (
61
- <>
62
- <div
63
- data-tooltip-id={id}
64
- data-open={true}
65
- data-tooltip-offset={offset}
66
- data-tooltip-delay-show={delayShow}
67
- data-tooltip-delay-hide={closeDelay}
68
- data-tooltip-place={tooltipPlace}
69
- >
70
- {text}
71
- </div>
72
- {
73
- !disabled && (
74
- <Tooltip
75
- css={style(size)}
76
- id={id}
77
- noArrow={true}
78
- >
79
- {toolTipText}
80
- </Tooltip>
81
- )
103
+ }: TooltipDisplayProps) => {
104
+ const anchor = useRef<HTMLDivElement>(null)
105
+ const tooltip = useRef<TooltipRefProps>(null)
106
+
107
+ /**
108
+ * Close on a fullscreen transition, unless the pointer really is still on the anchor.
109
+ *
110
+ * Going fullscreen relays the whole chrome out from under a pointer that never moved, and the
111
+ * browser recomputes the hover chain for that SILENTLY: the :hover flag flips a frame later, so
112
+ * the grey pill corrects itself in about 14ms, but no boundary event is dispatched at all.
113
+ * react-tooltip closes on mouseout and on nothing else, so it never learns the pointer left and
114
+ * the chip stays painted for the rest of the session. It survives arbitrary pointer movement,
115
+ * because the browser has already updated its own element-under-pointer, and it survives the
116
+ * chrome's auto-hide, coming back the moment the controls wake.
117
+ *
118
+ * Scoped to the fullscreen transition on purpose: the same layout move made by ordinary CSS does
119
+ * dispatch mouseout and closes the chip by itself, so no other reflow needs this and widening it
120
+ * would only add ways to close a tooltip that should be open.
121
+ *
122
+ * Tested against the anchor's rect rather than closed outright, because a player that already
123
+ * fills the window moves nothing, and the tooltip under the pointer is then legitimately open.
124
+ * That is the same test chrome.tsx's onMouseOut makes when relatedTarget comes back null. The rect
125
+ * is already the post-transition one when fullscreenchange fires, where matches(':hover') is not,
126
+ * which is why the pointer is tracked rather than asked for.
127
+ */
128
+ useEffect(() => {
129
+ const untrack = watchPointer()
130
+ const closeIfPointerLeft = () => {
131
+ const element = anchor.current
132
+ if (!element) return
133
+ const { left, right, top, bottom } = element.getBoundingClientRect()
134
+ if (pointer.x >= left && pointer.x < right && pointer.y >= top && pointer.y < bottom) return
135
+ tooltip.current?.close()
136
+ }
137
+ // webkit's own name as well, the way @videojs/core's fullscreen feature listens for both. Typed
138
+ // as string so the prefixed name checks against the DOM lib's event map.
139
+ const changeEvents: string[] = ['fullscreenchange', 'webkitfullscreenchange']
140
+ for (const type of changeEvents) document.addEventListener(type, closeIfPointerLeft)
141
+ return () => {
142
+ untrack()
143
+ for (const type of changeEvents) document.removeEventListener(type, closeIfPointerLeft)
82
144
  }
83
- </>
84
- )
145
+ }, [])
146
+
147
+ return (
148
+ <>
149
+ <div
150
+ ref={anchor}
151
+ data-tooltip-id={id}
152
+ data-open={true}
153
+ data-tooltip-offset={offset}
154
+ data-tooltip-delay-show={delayShow}
155
+ data-tooltip-delay-hide={closeDelay}
156
+ data-tooltip-place={tooltipPlace}
157
+ >
158
+ {text}
159
+ </div>
160
+ {
161
+ !disabled && (
162
+ <Tooltip
163
+ ref={tooltip}
164
+ css={style(size)}
165
+ id={id}
166
+ noArrow={true}
167
+ >
168
+ {toolTipText}
169
+ </Tooltip>
170
+ )
171
+ }
172
+ </>
173
+ )
174
+ }
85
175
 
86
176
  export default TooltipDisplay
@@ -2,8 +2,6 @@ const colors = {
2
2
  primary: '#EAEBEE',
3
3
  secondary: '#D0D0D9',
4
4
  hover: 'rgba(255, 255, 255, 0.13)',
5
- /** A control that is ON. Distinct from `hover`, which the pointer is already painting. */
6
- accent: '#6EA8FE',
7
5
  borderPrimary: '#384F70',
8
6
  backgroundTooltip: '#222222',
9
7
  }