box-content-preview 3.83.0 → 3.85.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/lib/index.js CHANGED
@@ -955,6 +955,7 @@ __webpack_require__.d(__webpack_exports__, {
955
955
  wU: () => (/* binding */ decodeKeydown),
956
956
  Is: () => (/* binding */ findScriptLocation),
957
957
  kd: () => (/* binding */ getClosestPageToPinch),
958
+ RU: () => (/* binding */ getCurrentTimeMs),
958
959
  Yf: () => (/* binding */ getDistance),
959
960
  dJ: () => (/* binding */ getHeaders),
960
961
  t9: () => (/* binding */ getMidpoint),
@@ -1009,7 +1010,7 @@ function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e =
1009
1010
  const CLIENT_NAME = "box-content-preview"; // eslint-disable-line no-undef
1010
1011
  const CLIENT_NAME_KEY = 'box_client_name';
1011
1012
  const CLIENT_VERSION_KEY = 'box_client_version';
1012
- const CLIENT_VERSION = "3.83.0"; // eslint-disable-line no-undef
1013
+ const CLIENT_VERSION = "3.85.0"; // eslint-disable-line no-undef
1013
1014
  const HEADER_CLIENT_NAME = 'X-Box-Client-Name';
1014
1015
  const HEADER_CLIENT_VERSION = 'X-Box-Client-Version';
1015
1016
  const PROMISE_MAP = {};
@@ -1617,6 +1618,16 @@ function getDistance(x1, y1, x2, y2) {
1617
1618
  return Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2);
1618
1619
  }
1619
1620
 
1621
+ /**
1622
+ * Monotonic clock in milliseconds. Falls back to wall time when performance is missing.
1623
+ *
1624
+ * @public
1625
+ * @return {number} Current time in milliseconds
1626
+ */
1627
+ function getCurrentTimeMs() {
1628
+ return typeof performance !== 'undefined' ? performance.now() : Date.now();
1629
+ }
1630
+
1620
1631
  /**
1621
1632
  * Returns the closest visible page to a pinch event
1622
1633
  *
@@ -3929,6 +3940,172 @@ let Menu = /*#__PURE__*/function (Menu) {
3929
3940
 
3930
3941
  /***/ },
3931
3942
 
3943
+ /***/ 4937
3944
+ (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
3945
+
3946
+
3947
+ // EXPORTS
3948
+ __webpack_require__.d(__webpack_exports__, {
3949
+ A: () => (/* binding */ SliderControl)
3950
+ });
3951
+
3952
+ // EXTERNAL MODULE: external "react"
3953
+ var external_react_ = __webpack_require__(1649);
3954
+ // EXTERNAL MODULE: ./node_modules/classnames/index.js
3955
+ var classnames = __webpack_require__(2485);
3956
+ var classnames_default = /*#__PURE__*/__webpack_require__.n(classnames);
3957
+ // EXTERNAL MODULE: ./node_modules/lodash/noop.js
3958
+ var noop = __webpack_require__(3950);
3959
+ var noop_default = /*#__PURE__*/__webpack_require__.n(noop);
3960
+ // EXTERNAL MODULE: ./src/lib/util.js + 1 modules
3961
+ var util = __webpack_require__(4410);
3962
+ ;// ./src/lib/viewers/controls/slider/SliderControl.scss
3963
+ // extracted by mini-css-extract-plugin
3964
+
3965
+ ;// ./src/lib/viewers/controls/slider/SliderControl.tsx
3966
+ const _excluded = ["className", "max", "min", "onMove", "onUpdate", "step", "title", "track", "value"];
3967
+ function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
3968
+ function _objectWithoutProperties(e, t) { if (null == e) return {}; var o, r, i = _objectWithoutPropertiesLoose(e, t); if (Object.getOwnPropertySymbols) { var n = Object.getOwnPropertySymbols(e); for (r = 0; r < n.length; r++) o = n[r], -1 === t.indexOf(o) && {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]); } return i; }
3969
+ function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
3970
+
3971
+
3972
+
3973
+
3974
+
3975
+ function SliderControl(_ref) {
3976
+ let {
3977
+ className,
3978
+ max = 100,
3979
+ min = 0,
3980
+ onMove = (noop_default()),
3981
+ onUpdate = (noop_default()),
3982
+ step = 1,
3983
+ title,
3984
+ track,
3985
+ value
3986
+ } = _ref,
3987
+ rest = _objectWithoutProperties(_ref, _excluded);
3988
+ const [isScrubbing, setIsScrubbing] = external_react_["default"].useState(false);
3989
+ const sliderElRef = external_react_["default"].useRef(null);
3990
+ const getPosition = external_react_["default"].useCallback(pageX => {
3991
+ const {
3992
+ current: sliderEl
3993
+ } = sliderElRef;
3994
+ if (!sliderEl) return 0;
3995
+ const {
3996
+ left: sliderLeft,
3997
+ width: sliderWidth
3998
+ } = sliderEl.getBoundingClientRect();
3999
+ return Math.max(0, Math.min(pageX - sliderLeft, sliderWidth));
4000
+ }, []);
4001
+ const getPositionValue = external_react_["default"].useCallback(pageX => {
4002
+ const {
4003
+ current: sliderEl
4004
+ } = sliderElRef;
4005
+ if (!sliderEl) return 0;
4006
+ const {
4007
+ width: sliderWidth
4008
+ } = sliderEl.getBoundingClientRect();
4009
+ const newValue = getPosition(pageX) / sliderWidth * max;
4010
+ return Math.max(min, Math.min(newValue, max));
4011
+ }, [getPosition, max, min]);
4012
+ const handleKeydown = event => {
4013
+ const key = (0,util/* decodeKeydown */.wU)(event);
4014
+ if (key === 'ArrowLeft') {
4015
+ event.stopPropagation(); // Prevents global key handling
4016
+ onUpdate(Math.max(min, Math.min(value - step, max)));
4017
+ }
4018
+ if (key === 'ArrowRight') {
4019
+ event.stopPropagation(); // Prevents global key handling
4020
+ onUpdate(Math.max(min, Math.min(value + step, max)));
4021
+ }
4022
+ };
4023
+ const handleMouseDown = ({
4024
+ button,
4025
+ ctrlKey,
4026
+ metaKey,
4027
+ pageX
4028
+ }) => {
4029
+ if (button > 1 || ctrlKey || metaKey) return;
4030
+ onUpdate(getPositionValue(pageX));
4031
+ setIsScrubbing(true);
4032
+ };
4033
+ const handleMouseMove = ({
4034
+ pageX
4035
+ }) => {
4036
+ const {
4037
+ current: sliderEl
4038
+ } = sliderElRef;
4039
+ const {
4040
+ width: sliderWidth
4041
+ } = sliderEl ? sliderEl.getBoundingClientRect() : {
4042
+ width: 0
4043
+ };
4044
+ onMove(getPositionValue(pageX), getPosition(pageX), sliderWidth);
4045
+ };
4046
+ const handleTouchStart = ({
4047
+ touches
4048
+ }) => {
4049
+ onUpdate(getPositionValue(touches[0].pageX));
4050
+ setIsScrubbing(true);
4051
+ };
4052
+ external_react_["default"].useEffect(() => {
4053
+ const handleDocumentMoveStop = () => setIsScrubbing(false);
4054
+ const handleDocumentMouseMove = event => {
4055
+ if (!isScrubbing || event.button > 1 || event.ctrlKey || event.metaKey) return;
4056
+ event.preventDefault();
4057
+ onUpdate(getPositionValue(event.pageX));
4058
+ };
4059
+ const handleDocumentTouchMove = event => {
4060
+ if (!isScrubbing || !event.touches || !event.touches[0]) return;
4061
+ event.preventDefault();
4062
+ onUpdate(getPositionValue(event.touches[0].pageX));
4063
+ };
4064
+ if (isScrubbing) {
4065
+ document.addEventListener('mousemove', handleDocumentMouseMove);
4066
+ document.addEventListener('mouseup', handleDocumentMoveStop);
4067
+ document.addEventListener('touchend', handleDocumentMoveStop);
4068
+ document.addEventListener('touchmove', handleDocumentTouchMove);
4069
+ }
4070
+ return () => {
4071
+ document.removeEventListener('mousemove', handleDocumentMouseMove);
4072
+ document.removeEventListener('mouseup', handleDocumentMoveStop);
4073
+ document.removeEventListener('touchend', handleDocumentMoveStop);
4074
+ document.removeEventListener('touchmove', handleDocumentTouchMove);
4075
+ };
4076
+ }, [isScrubbing, getPositionValue, onUpdate]);
4077
+ return /*#__PURE__*/external_react_["default"].createElement("div", _extends({
4078
+ ref: sliderElRef,
4079
+ "aria-label": title,
4080
+ "aria-valuemax": max,
4081
+ "aria-valuemin": min,
4082
+ "aria-valuenow": value,
4083
+ className: classnames_default()('bp-SliderControl', className, {
4084
+ 'bp-is-scrubbing': isScrubbing
4085
+ }),
4086
+ onKeyDown: handleKeydown,
4087
+ onMouseDown: handleMouseDown,
4088
+ onMouseMove: handleMouseMove,
4089
+ onTouchStart: handleTouchStart,
4090
+ role: "slider",
4091
+ tabIndex: 0
4092
+ }, rest), /*#__PURE__*/external_react_["default"].createElement("div", {
4093
+ className: "bp-SliderControl-track",
4094
+ "data-testid": "bp-slider-control-track",
4095
+ style: {
4096
+ backgroundImage: track
4097
+ }
4098
+ }), /*#__PURE__*/external_react_["default"].createElement("div", {
4099
+ className: "bp-SliderControl-thumb",
4100
+ "data-testid": "bp-slider-control-thumb",
4101
+ style: {
4102
+ left: `${value / max * 100}%`
4103
+ }
4104
+ }));
4105
+ }
4106
+
4107
+ /***/ },
4108
+
3932
4109
  /***/ 9974
3933
4110
  (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
3934
4111
 
@@ -3941,7 +4118,7 @@ function getPdfjsWorkerSrc() {
3941
4118
 
3942
4119
  /***/ },
3943
4120
 
3944
- /***/ 7893
4121
+ /***/ 3678
3945
4122
  (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
3946
4123
 
3947
4124
 
@@ -3967,6 +4144,8 @@ var TimestampControl = __webpack_require__(3904);
3967
4144
  var VolumeControls = __webpack_require__(3945);
3968
4145
  // EXTERNAL MODULE: ./src/lib/icons/play_48px.svg
3969
4146
  var play_48px = __webpack_require__(3493);
4147
+ // EXTERNAL MODULE: ./src/lib/viewers/media/waveform/constants.ts
4148
+ var constants = __webpack_require__(4929);
3970
4149
  ;// ./src/lib/viewers/media/waveform/peaks.ts
3971
4150
  const PLACEHOLDER_PEAK_AMPLITUDE = 0;
3972
4151
  const PLACEHOLDER_PEAK_COUNT = 2000;
@@ -4063,10 +4242,217 @@ function morphPeaks(from, to, elapsedMs) {
4063
4242
  }
4064
4243
  return out;
4065
4244
  }
4245
+ ;// ./src/lib/viewers/media/waveform/viewport.ts
4246
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
4247
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
4248
+ function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
4249
+ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
4250
+ function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
4251
+
4252
+ /** Visible window: start/end times, pixels-per-second, and the current zoom. */
4253
+ function createWaveformViewport({
4254
+ durationSec,
4255
+ heightPx,
4256
+ maxZoom,
4257
+ scrollLeftPx,
4258
+ widthPx,
4259
+ zoomLevel
4260
+ }) {
4261
+ const zoom = Number.isFinite(zoomLevel) && zoomLevel > 0 ? zoomLevel : constants/* WAVEFORM_ZOOM_MIN */.LW;
4262
+ const viewDurationSec = durationSec > 0 ? durationSec / zoom : 0; // seconds visible at this zoom
4263
+ const pixelsPerSecond = viewDurationSec > 0 && widthPx > 0 ? widthPx / viewDurationSec : 0;
4264
+ const maxStartSec = Math.max(0, durationSec - viewDurationSec); // last start that still fills the window
4265
+ const startSec = pixelsPerSecond > 0 ? Math.min(maxStartSec, Math.max(0, scrollLeftPx / pixelsPerSecond)) : 0;
4266
+ return {
4267
+ durationSec,
4268
+ endSec: startSec + viewDurationSec,
4269
+ heightPx,
4270
+ maxZoom,
4271
+ pixelsPerSecond,
4272
+ scrollLeftPx,
4273
+ startSec,
4274
+ widthPx,
4275
+ zoomLevel: zoom
4276
+ };
4277
+ }
4278
+
4279
+ /** Same viewport with a different scroll, so start/end times update. */
4280
+ function getViewportAtScroll(viewport, scrollLeftPx) {
4281
+ return createWaveformViewport(_objectSpread(_objectSpread({}, viewport), {}, {
4282
+ scrollLeftPx
4283
+ }));
4284
+ }
4285
+
4286
+ /** How many CSS pixels from the left of the visible window this time sits. */
4287
+ function positionPxFromTime(timeSec, viewport) {
4288
+ return (timeSec - viewport.startSec) * viewport.pixelsPerSecond;
4289
+ }
4290
+
4291
+ /** Media time under a point this many CSS pixels from the left of the visible window. */
4292
+ function timeFromPositionPx(positionPx, viewport) {
4293
+ if (!(viewport.pixelsPerSecond > 0)) {
4294
+ return viewport.startSec;
4295
+ }
4296
+ return viewport.startSec + positionPx / viewport.pixelsPerSecond;
4297
+ }
4298
+
4299
+ /** True when this time sits inside the visible window. */
4300
+ function isTimeInView(timeSec, viewport) {
4301
+ return timeSec >= viewport.startSec && timeSec <= viewport.endSec;
4302
+ }
4303
+
4304
+ /** UI max zoom: 24×, ~1 peak per CSS pixel, and a minimum visible window. */
4305
+ function getWaveformZoomMax({
4306
+ durationSec,
4307
+ peakCount,
4308
+ viewWidthPx
4309
+ }) {
4310
+ if (!(peakCount > 0) || !(viewWidthPx > 0)) {
4311
+ return constants/* WAVEFORM_ZOOM_MIN */.LW;
4312
+ }
4313
+ const peakLimitedMax = Math.floor(peakCount / viewWidthPx);
4314
+ const durationLimitedMax = durationSec > 0 ? durationSec / constants/* WAVEFORM_MIN_VIEW_WINDOW_SEC */.Kl : constants/* WAVEFORM_ZOOM_MIN */.LW;
4315
+ return Math.max(constants/* WAVEFORM_ZOOM_MIN */.LW, Math.min(constants/* WAVEFORM_ZOOM_MAX */.tK, peakLimitedMax, durationLimitedMax));
4316
+ }
4317
+
4318
+ /** Keep zoom between 1× and this file's max. */
4319
+ function clampWaveformZoom(zoomLevel, maxZoom = constants/* WAVEFORM_ZOOM_MIN */.LW) {
4320
+ const max = Math.max(constants/* WAVEFORM_ZOOM_MIN */.LW, maxZoom);
4321
+ if (!Number.isFinite(zoomLevel)) {
4322
+ return constants/* WAVEFORM_ZOOM_MIN */.LW;
4323
+ }
4324
+ return Math.min(max, Math.max(constants/* WAVEFORM_ZOOM_MIN */.LW, zoomLevel));
4325
+ }
4326
+
4327
+ /** WaveSurfer zoom density. 0 = fit the whole file in the view. */
4328
+ function getZoomedPixelsPerSecond({
4329
+ durationSec,
4330
+ maxZoom = constants/* WAVEFORM_ZOOM_MIN */.LW,
4331
+ viewWidthPx,
4332
+ zoomLevel
4333
+ }) {
4334
+ const zoom = clampWaveformZoom(zoomLevel, maxZoom);
4335
+ if (zoom <= constants/* WAVEFORM_ZOOM_MIN */.LW || !(durationSec > 0) || !(viewWidthPx > 0)) {
4336
+ return 0;
4337
+ }
4338
+ return viewWidthPx / durationSec * zoom;
4339
+ }
4340
+
4341
+ /** Map zoom (1…max) onto the 0–100 slider. */
4342
+ function sliderValueFromZoom(zoomLevel, maxZoom = constants/* WAVEFORM_ZOOM_MIN */.LW) {
4343
+ const max = Math.max(constants/* WAVEFORM_ZOOM_MIN */.LW, maxZoom);
4344
+ const zoom = clampWaveformZoom(zoomLevel, max);
4345
+ if (max <= constants/* WAVEFORM_ZOOM_MIN */.LW) {
4346
+ return 0;
4347
+ }
4348
+ return (zoom - constants/* WAVEFORM_ZOOM_MIN */.LW) / (max - constants/* WAVEFORM_ZOOM_MIN */.LW) * constants/* WAVEFORM_ZOOM_SLIDER_MAX */.nh;
4349
+ }
4350
+
4351
+ /** Inverse of sliderValueFromZoom. */
4352
+ function zoomFromSliderValue(value, maxZoom = constants/* WAVEFORM_ZOOM_MIN */.LW) {
4353
+ const max = Math.max(constants/* WAVEFORM_ZOOM_MIN */.LW, maxZoom);
4354
+ if (max <= constants/* WAVEFORM_ZOOM_MIN */.LW) {
4355
+ return constants/* WAVEFORM_ZOOM_MIN */.LW;
4356
+ }
4357
+ const t = Math.min(constants/* WAVEFORM_ZOOM_SLIDER_MAX */.nh, Math.max(0, value)) / constants/* WAVEFORM_ZOOM_SLIDER_MAX */.nh;
4358
+ return clampWaveformZoom(constants/* WAVEFORM_ZOOM_MIN */.LW + t * (max - constants/* WAVEFORM_ZOOM_MIN */.LW), max);
4359
+ }
4360
+
4361
+ /** Max scroll that still shows a full window (no overscroll). */
4362
+ function maxScrollLeft(viewport) {
4363
+ return Math.max(0, viewport.durationSec * viewport.pixelsPerSecond - viewport.widthPx);
4364
+ }
4365
+
4366
+ /** Scroll offset that still shows a full window (no overscroll). */
4367
+ function clampScrollLeft(scrollLeftPx, viewport) {
4368
+ if (!Number.isFinite(scrollLeftPx)) {
4369
+ return 0;
4370
+ }
4371
+ return Math.min(maxScrollLeft(viewport), Math.max(0, scrollLeftPx));
4372
+ }
4373
+
4374
+ /** Follow inset in CSS px, capped at one third of the view so a narrow player still has room. */
4375
+ function getFollowInsetPx(widthPx, insetPx = constants/* WAVEFORM_FOLLOW_INSET_PX */.NM) {
4376
+ if (!(widthPx > 0)) {
4377
+ return 0;
4378
+ }
4379
+ return Math.min(Math.max(0, insetPx), widthPx / 3);
4380
+ }
4381
+
4382
+ /** CSS left for a playhead pinned to the follow inset. */
4383
+ function getPinnedPlayheadLeft(widthPx, insetPx = constants/* WAVEFORM_FOLLOW_INSET_PX */.NM) {
4384
+ const inset = getFollowInsetPx(widthPx, insetPx);
4385
+ if (!(widthPx > 0)) {
4386
+ return '0%';
4387
+ }
4388
+ return `${(widthPx - inset) / widthPx * 100}%`;
4389
+ }
4390
+
4391
+ /** CSS left % of the playhead from the left of the visible window. */
4392
+ function timeLeftPercent(timeSec, durationSec, viewport) {
4393
+ if (viewport.widthPx > 0 && viewport.pixelsPerSecond > 0) {
4394
+ return `${positionPxFromTime(timeSec, viewport) / viewport.widthPx * 100}%`;
4395
+ }
4396
+ const progress = durationSec > 0 ? Math.min(1, Math.max(0, timeSec / durationSec)) : 0;
4397
+ return `${progress * 100}%`;
4398
+ }
4399
+
4400
+ /**
4401
+ * Walk across the view until the playhead nears the right edge, then follow.
4402
+ * Off-screen at play start jumps in; off-screen right while playing keeps following.
4403
+ */
4404
+ function getPlayheadCameraAction({
4405
+ insetPx = constants/* WAVEFORM_FOLLOW_INSET_PX */.NM,
4406
+ isPlaying,
4407
+ playJustStarted,
4408
+ timeSec,
4409
+ viewport
4410
+ }) {
4411
+ if (!isPlaying || viewport.zoomLevel <= constants/* WAVEFORM_ZOOM_MIN */.LW || !(viewport.widthPx > 0) || !(viewport.pixelsPerSecond > 0)) {
4412
+ return {
4413
+ type: 'none'
4414
+ };
4415
+ }
4416
+ const inset = getFollowInsetPx(viewport.widthPx, insetPx);
4417
+ const viewX = positionPxFromTime(timeSec, viewport);
4418
+ const playheadCanvasX = timeSec * viewport.pixelsPerSecond;
4419
+ const followX = viewport.widthPx - inset;
4420
+ if (viewX < 0) {
4421
+ if (!playJustStarted) {
4422
+ return {
4423
+ type: 'none'
4424
+ };
4425
+ }
4426
+ return {
4427
+ type: 'jump',
4428
+ scrollLeftPx: clampScrollLeft(playheadCanvasX - inset, viewport)
4429
+ };
4430
+ }
4431
+ if (viewX >= followX) {
4432
+ const unclampedScroll = playheadCanvasX - followX;
4433
+ const scrollLeftPx = clampScrollLeft(unclampedScroll, viewport);
4434
+ if (playJustStarted && viewX > viewport.widthPx) {
4435
+ return {
4436
+ type: 'jump',
4437
+ scrollLeftPx
4438
+ };
4439
+ }
4440
+ return {
4441
+ type: 'followRight',
4442
+ isPlayheadPinned: unclampedScroll <= maxScrollLeft(viewport),
4443
+ scrollLeftPx
4444
+ };
4445
+ }
4446
+ return {
4447
+ type: 'none'
4448
+ };
4449
+ }
4066
4450
  ;// ./node_modules/wavesurfer.js/dist/wavesurfer.esm.js
4067
4451
  function t(t,e,i,n){return new(i||(i=Promise))((function(s,r){function o(t){try{l(n.next(t))}catch(t){r(t)}}function a(t){try{l(n.throw(t))}catch(t){r(t)}}function l(t){var e;t.done?s(t.value):(e=t.value,e instanceof i?e:new i((function(t){t(e)}))).then(o,a)}l((n=n.apply(t,e||[])).next())}))}"function"==typeof SuppressedError&&SuppressedError;class e{constructor(){this.listeners={}}on(t,e,i){if(this.listeners[t]||(this.listeners[t]=new Set),null==i?void 0:i.once){const i=(...n)=>{this.un(t,i),e(...n)};return this.listeners[t].add(i),()=>this.un(t,i)}return this.listeners[t].add(e),()=>this.un(t,e)}un(t,e){var i;null===(i=this.listeners[t])||void 0===i||i.delete(e)}once(t,e){return this.on(t,e,{once:!0})}unAll(){this.listeners={}}emit(t,...e){this.listeners[t]&&this.listeners[t].forEach((t=>t(...e)))}}const i={decode:function(e,i){return t(this,void 0,void 0,(function*(){const t=new AudioContext({sampleRate:i});try{return yield t.decodeAudioData(e)}finally{"closed"!==t.state&&(yield t.close().catch((()=>{})))}}))},createBuffer:function(t,e){if(!t||0===t.length)throw new Error("channelData must be a non-empty array");if(e<=0)throw new Error("duration must be greater than 0");if("number"==typeof t[0]&&(t=[t]),!t[0]||0===t[0].length)throw new Error("channelData must contain non-empty channel arrays");!function(t){const e=t[0];if(e.some((t=>t>1||t<-1))){const i=e.length;let n=0;for(let t=0;t<i;t++){const i=Math.abs(e[t]);i>n&&(n=i)}for(const e of t)for(let t=0;t<i;t++)e[t]/=n}}(t);const i=t.map((t=>t instanceof Float32Array?t:Float32Array.from(t)));return{duration:e,length:i[0].length,sampleRate:i[0].length/e,numberOfChannels:i.length,getChannelData:t=>{const e=i[t];if(!e)throw new Error(`Channel ${t} not found`);return e},copyFromChannel:AudioBuffer.prototype.copyFromChannel,copyToChannel:AudioBuffer.prototype.copyToChannel}}};function n(t,e){const i=e.xmlns?document.createElementNS(e.xmlns,t):document.createElement(t);for(const[t,s]of Object.entries(e))if("children"===t&&s)for(const[t,e]of Object.entries(s))e instanceof Node?i.appendChild(e):"string"==typeof e?i.appendChild(document.createTextNode(e)):i.appendChild(n(t,e));else"style"===t?Object.assign(i.style,s):"textContent"===t?i.textContent=s:i.setAttribute(t,s.toString());return i}function s(t,e,i){const s=n(t,e||{});return null==i||i.appendChild(s),s}function r(t){return t instanceof HTMLElement||"object"==typeof t&&null!==t&&t.nodeType===Node.ELEMENT_NODE&&"object"==typeof t.style}var o=Object.freeze({__proto__:null,createElement:s,default:s,isHTMLElement:r});const a={fetchBlob:function(e,i,n){return t(this,void 0,void 0,(function*(){var s;const r=yield fetch(e,n);if(r.status>=400)throw new Error(`Failed to fetch ${e}: ${r.status} (${r.statusText})`);return function(e,i,n){t(this,void 0,void 0,(function*(){var t;if(!e.body||!e.headers)return;const s=e.body.getReader(),r=Number(e.headers.get("Content-Length"))||0;let o=0;const a=()=>{s.cancel()};if(n){if(n.aborted)return void s.cancel();n.addEventListener("abort",a,{once:!0})}try{for(;;){const e=yield s.read();if(e.done)break;if(o+=(null===(t=e.value)||void 0===t?void 0:t.length)||0,r>0){const t=Math.round(o/r*100);i(t)}}}catch(t){if(t instanceof DOMException&&"AbortError"===t.name)return;console.warn("Progress tracking error:",t)}finally{n&&n.removeEventListener("abort",a)}}))}(r.clone(),i,null!==(s=null==n?void 0:n.signal)&&void 0!==s?s:void 0),r.blob()}))}};function l(t){let e=t;const i=new Set;return{get value(){return e},set(t){Object.is(e,t)||(e=t,i.forEach((t=>t(e))))},update(t){this.set(t(e))},subscribe:t=>(i.add(t),()=>i.delete(t))}}function h(t,e){const i=l(t());return e.forEach((e=>e.subscribe((()=>{const e=t();Object.is(i.value,e)||i.set(e)})))),{get value(){return i.value},subscribe:t=>i.subscribe(t)}}function c(t,e){let i;const n=()=>{i&&(i(),i=void 0),i=t()},s=e.map((t=>t.subscribe(n)));return n(),()=>{i&&(i(),i=void 0),s.forEach((t=>t()))}}class u extends e{get isPlayingSignal(){return this._isPlaying}get currentTimeSignal(){return this._currentTime}get durationSignal(){return this._duration}get volumeSignal(){return this._volume}get mutedSignal(){return this._muted}get playbackRateSignal(){return this._playbackRate}get seekingSignal(){return this._seeking}constructor(t){super(),this.isExternalMedia=!1,this._ownBlobUrl=null,this.reactiveMediaEventCleanups=[],t.media?(this.media=t.media,this.isExternalMedia=!0):this.media=document.createElement("audio"),this._isPlaying=l(!1),this._currentTime=l(0),this._duration=l(0),this._volume=l(this.media.volume),this._muted=l(this.media.muted),this._playbackRate=l(this.media.playbackRate||1),this._seeking=l(!1),this.setupReactiveMediaEvents(),t.mediaControls&&(this.media.controls=!0),t.autoplay&&(this.media.autoplay=!0),null!=t.playbackRate&&this.onMediaEvent("canplay",(()=>{null!=t.playbackRate&&(this.media.playbackRate=t.playbackRate)}),{once:!0})}setupReactiveMediaEvents(){this.reactiveMediaEventCleanups.push(this.onMediaEvent("play",(()=>{this._isPlaying.set(!0)}))),this.reactiveMediaEventCleanups.push(this.onMediaEvent("pause",(()=>{this._isPlaying.set(!1)}))),this.reactiveMediaEventCleanups.push(this.onMediaEvent("ended",(()=>{this._isPlaying.set(!1)}))),this.reactiveMediaEventCleanups.push(this.onMediaEvent("timeupdate",(()=>{this._currentTime.set(this.media.currentTime)}))),this.reactiveMediaEventCleanups.push(this.onMediaEvent("durationchange",(()=>{this._duration.set(this.media.duration||0)}))),this.reactiveMediaEventCleanups.push(this.onMediaEvent("loadedmetadata",(()=>{this._duration.set(this.media.duration||0)}))),this.reactiveMediaEventCleanups.push(this.onMediaEvent("seeking",(()=>{this._seeking.set(!0)}))),this.reactiveMediaEventCleanups.push(this.onMediaEvent("seeked",(()=>{this._seeking.set(!1)}))),this.reactiveMediaEventCleanups.push(this.onMediaEvent("volumechange",(()=>{this._volume.set(this.media.volume),this._muted.set(this.media.muted)}))),this.reactiveMediaEventCleanups.push(this.onMediaEvent("ratechange",(()=>{this._playbackRate.set(this.media.playbackRate)})))}onMediaEvent(t,e,i){return this.media.addEventListener(t,e,i),()=>this.media.removeEventListener(t,e,i)}getSrc(){return this.media.currentSrc||this.media.src||""}revokeSrc(){this._ownBlobUrl&&(URL.revokeObjectURL(this._ownBlobUrl),this._ownBlobUrl=null)}canPlayType(t){return""!==this.media.canPlayType(t)}setSrc(t,e){const i=this.getSrc();if(t&&i===t)return;this.revokeSrc();const n=e instanceof Blob&&(this.canPlayType(e.type)||!t)?URL.createObjectURL(e):t;if(n!==t&&(this._ownBlobUrl=n),i&&this.media.removeAttribute("src"),n||t)try{this.media.src=n}catch(e){this.media.src=t}}destroy(){this.reactiveMediaEventCleanups.forEach((t=>t())),this.reactiveMediaEventCleanups=[],this.revokeSrc(),this.unAll(),this.isExternalMedia||(this.media.pause(),this.media.removeAttribute("src"),this.media.load(),this.media.remove())}setMediaElement(t){this.reactiveMediaEventCleanups.forEach((t=>t())),this.reactiveMediaEventCleanups=[],this.media=t,this.setupReactiveMediaEvents()}play(){return t(this,void 0,void 0,(function*(){try{return yield this.media.play()}catch(t){if(t instanceof DOMException&&"AbortError"===t.name)return;throw t}}))}pause(){this.media.pause()}isPlaying(){return!this.media.paused&&!this.media.ended}setTime(t){this.media.currentTime=Math.max(0,Math.min(t,this.getDuration()))}getDuration(){return this.media.duration}getCurrentTime(){return this.media.currentTime}getVolume(){return this.media.volume}setVolume(t){this.media.volume=t}getMuted(){return this.media.muted}setMuted(t){this.media.muted=t}getPlaybackRate(){return this.media.playbackRate}isSeeking(){return this.media.seeking}setPlaybackRate(t,e){null!=e&&(this.media.preservesPitch=e),this.media.playbackRate=t}getMediaElement(){return this.media}setSinkId(t){return this.media.setSinkId(t)}}function d({maxTop:t,maxBottom:e,halfHeight:i,vScale:n,barMinHeight:s=0,barAlign:r}){let o=Math.round(t*i*n);let a=o+Math.round(e*i*n)||1;return a<s&&(a=s,r||(o=a/2)),{topHeight:o,totalHeight:a}}function p({barAlign:t,halfHeight:e,topHeight:i,totalHeight:n,canvasHeight:s}){return"top"===t?0:"bottom"===t?s-n:e-i}function m(t,e,i){const n=e-t.left,s=i-t.top;return[n/t.width,s/t.height]}function g(t){return Boolean(t.barWidth||t.barGap||t.barAlign)}function f(t,e){if(!g(e))return t;const i=e.barWidth||.5,n=i+(e.barGap||i/2);return 0===n?t:Math.floor(t/n)*n}function v({scrollLeft:t,totalWidth:e,numCanvases:i}){if(0===e)return[0];const n=t/e,s=Math.floor(n*i);return[s-1,s,s+1]}function b(t){const e=t._cleanup;"function"==typeof e&&e()}function y(t){const e=l({scrollLeft:t.scrollLeft,scrollWidth:t.scrollWidth,clientWidth:t.clientWidth}),i=h((()=>function(t){const{scrollLeft:e,scrollWidth:i,clientWidth:n}=t;if(0===i)return{startX:0,endX:1};const s=e/i,r=(e+n)/i;return{startX:Math.max(0,Math.min(1,s)),endX:Math.max(0,Math.min(1,r))}}(e.value)),[e]),n=h((()=>function(t){return{left:t.scrollLeft,right:t.scrollLeft+t.clientWidth}}(e.value)),[e]),s=()=>{e.set({scrollLeft:t.scrollLeft,scrollWidth:t.scrollWidth,clientWidth:t.clientWidth})};t.addEventListener("scroll",s,{passive:!0});return{scrollData:e,percentages:i,bounds:n,cleanup:()=>{t.removeEventListener("scroll",s),b(e)}}}class C extends e{constructor(t,e){super(),this.timeouts=[],this.isScrollable=!1,this.audioData=null,this.resizeObserver=null,this.lastContainerWidth=0,this.isDragging=!1,this.subscriptions=[],this.unsubscribeOnScroll=[],this.dragStream=null,this.scrollStream=null,this.containerInlinePadding=0,this.onClickWrapper=t=>{const e=this.wrapper.getBoundingClientRect(),[i,n]=m(e,t.clientX,t.clientY);this.emit("click",i,n)},this.onDblClickWrapper=t=>{const e=this.wrapper.getBoundingClientRect(),[i,n]=m(e,t.clientX,t.clientY);this.emit("dblclick",i,n)},this.subscriptions=[],this.options=t;const i=this.parentFromOptionsContainer(t.container);this.parent=i;const[n,s]=this.initHtml();i.appendChild(n),this.container=n,this.scrollContainer=s.querySelector(".scroll"),this.wrapper=s.querySelector(".wrapper"),this.canvasWrapper=s.querySelector(".canvases"),this.progressWrapper=s.querySelector(".progress"),this.cursor=s.querySelector(".cursor"),this.calculateInlinePadding(),e&&s.appendChild(e),this.initEvents()}parentFromOptionsContainer(t){let e;if("string"==typeof t?e=document.querySelector(t):r(t)&&(e=t),!e)throw new Error("Container not found");return e}initEvents(){this.wrapper.addEventListener("click",this.onClickWrapper),this.wrapper.addEventListener("dblclick",this.onDblClickWrapper),!0!==this.options.dragToSeek&&"object"!=typeof this.options.dragToSeek||this.initDrag(),this.scrollStream=y(this.scrollContainer);const t=c((()=>{const{startX:t,endX:e}=this.scrollStream.percentages.value,{left:i,right:n}=this.scrollStream.bounds.value;this.emit("scroll",t,e,i,n)}),[this.scrollStream.percentages,this.scrollStream.bounds]);if(this.subscriptions.push(t),"function"==typeof ResizeObserver){const t=this.createDelay(100);this.resizeObserver=new ResizeObserver((()=>{t().then((()=>this.onContainerResize())).catch((()=>{}))})),this.resizeObserver.observe(this.scrollContainer)}}onContainerResize(){const t=this.parent.clientWidth;this.calculateInlinePadding(),t===this.lastContainerWidth&&"auto"!==this.options.height||(this.lastContainerWidth=t,this.reRender(),this.emit("resize"))}initDrag(){if(this.dragStream)return;this.dragStream=function(t,e={}){const{threshold:i=3,mouseButton:n=0,touchDelay:s=100}=e,r=l(null),o=new Map,a=matchMedia("(pointer: coarse)").matches;let h=()=>{};const c=e=>{if(e.button!==n)return;if(o.has(e.pointerId))return;if(o.set(e.pointerId,e),o.size>1)return;const l=e.pointerId;let c=e.clientX,u=e.clientY,d=!1;const p=Date.now(),m=t.getBoundingClientRect(),{left:g,top:f}=m,v=t=>{if(t.pointerId!==l)return;if(t.defaultPrevented||o.size>1)return;if(a&&Date.now()-p<s)return;const e=t.clientX,n=t.clientY,h=e-c,m=n-u;(d||Math.abs(h)>i||Math.abs(m)>i)&&(t.preventDefault(),t.stopPropagation(),d||(r.set({type:"start",x:c-g,y:u-f}),d=!0),r.set({type:"move",x:e-g,y:n-f,deltaX:h,deltaY:m}),c=e,u=n)},b=t=>{if(o.delete(t.pointerId)){if(t.pointerId===l&&d){const e=t.clientX,i=t.clientY;r.set({type:"end",x:e-g,y:i-f})}0===o.size&&h()}},y=t=>{t.relatedTarget&&t.relatedTarget!==document.documentElement||b(t)},C=t=>{d&&(t.stopPropagation(),t.preventDefault())},S=t=>{t.defaultPrevented||o.size>1||d&&t.preventDefault()};document.addEventListener("pointermove",v),document.addEventListener("pointerup",b),document.addEventListener("pointerout",y),document.addEventListener("pointercancel",y),document.addEventListener("touchmove",S,{passive:!1}),document.addEventListener("click",C,{capture:!0}),h=()=>{document.removeEventListener("pointermove",v),document.removeEventListener("pointerup",b),document.removeEventListener("pointerout",y),document.removeEventListener("pointercancel",y),document.removeEventListener("touchmove",S),setTimeout((()=>{document.removeEventListener("click",C,{capture:!0})}),10)}};return t.addEventListener("pointerdown",c),{signal:r,cleanup:()=>{h(),t.removeEventListener("pointerdown",c),o.clear(),b(r)}}}(this.wrapper);const t=c((()=>{const t=this.dragStream.signal.value;if(!t)return;const e=this.wrapper.getBoundingClientRect().width,i=(n=t.x/e)<0?0:n>1?1:n;var n;"start"===t.type?(this.isDragging=!0,this.emit("dragstart",i)):"move"===t.type?this.emit("drag",i):"end"===t.type&&(this.isDragging=!1,this.emit("dragend",i))}),[this.dragStream.signal]);this.subscriptions.push(t)}calculateInlinePadding(){const{paddingLeft:t,paddingRight:e}=getComputedStyle(this.scrollContainer),i=parseFloat(t)+parseFloat(e);this.containerInlinePadding=Number.isNaN(i)?0:i}initHtml(){const t=document.createElement("div"),e=t.attachShadow({mode:"open"}),i=this.options.cspNonce&&"string"==typeof this.options.cspNonce?this.options.cspNonce.replace(/"/g,""):"";return e.innerHTML=`\n <style${i?` nonce="${i}"`:""}>\n :host {\n user-select: none;\n min-width: 1px;\n }\n :host audio {\n display: block;\n width: 100%;\n }\n :host .scroll {\n overflow-x: auto;\n overflow-y: hidden;\n width: 100%;\n position: relative;\n }\n :host .noScrollbar {\n scrollbar-color: transparent;\n scrollbar-width: none;\n }\n :host .noScrollbar::-webkit-scrollbar {\n display: none;\n -webkit-appearance: none;\n }\n :host .wrapper {\n position: relative;\n overflow: visible;\n z-index: 2;\n }\n :host .canvases {\n min-height: ${this.getHeight(this.options.height,this.options.splitChannels)}px;\n pointer-events: none;\n }\n :host .canvases > div {\n position: relative;\n }\n :host canvas {\n display: block;\n position: absolute;\n top: 0;\n image-rendering: pixelated;\n }\n :host .progress {\n pointer-events: none;\n position: absolute;\n z-index: 2;\n top: 0;\n left: 0;\n width: 0;\n height: 100%;\n overflow: hidden;\n }\n :host .progress > div {\n position: relative;\n }\n :host .cursor {\n pointer-events: none;\n position: absolute;\n z-index: 5;\n top: 0;\n left: 0;\n height: 100%;\n border-radius: 2px;\n }\n </style>\n\n <div class="scroll" part="scroll">\n <div class="wrapper" part="wrapper">\n <div class="canvases" part="canvases"></div>\n <div class="progress" part="progress"></div>\n <div class="cursor" part="cursor"></div>\n </div>\n </div>\n `,[t,e]}setOptions(t){var e;if(this.options.container!==t.container){const e=this.parentFromOptionsContainer(t.container);e.appendChild(this.container),this.parent=e}!0===t.dragToSeek||"object"==typeof this.options.dragToSeek?this.initDrag():(null===(e=this.dragStream)||void 0===e||e.cleanup(),this.dragStream=null),this.options=t,this.reRender()}getWrapper(){return this.wrapper}getWidth(){return this.scrollContainer.clientWidth-this.containerInlinePadding}getScroll(){return this.scrollContainer.scrollLeft}setScroll(t){this.scrollContainer.scrollLeft=t}setScrollPercentage(t){const{scrollWidth:e}=this.scrollContainer,i=e*t;this.setScroll(i)}destroy(){var t;this.wrapper.removeEventListener("click",this.onClickWrapper),this.wrapper.removeEventListener("dblclick",this.onDblClickWrapper),this.timeouts.forEach((t=>t())),this.timeouts=[],this.subscriptions.forEach((t=>t())),this.container.remove(),this.resizeObserver&&(this.resizeObserver.disconnect(),this.resizeObserver=null),null===(t=this.unsubscribeOnScroll)||void 0===t||t.forEach((t=>t())),this.unsubscribeOnScroll=[],this.dragStream&&(this.dragStream.cleanup(),this.dragStream=null),this.scrollStream&&(this.scrollStream.cleanup(),this.scrollStream=null)}createDelay(t=10){let e,i;const n=()=>{e&&(clearTimeout(e),e=void 0),i&&(i(),i=void 0)};return this.timeouts.push(n),()=>new Promise(((s,r)=>{n(),i=r,e=setTimeout((()=>{e=void 0,i=void 0,s()}),t)}))}getHeight(t,e){var i;const n=(null===(i=this.audioData)||void 0===i?void 0:i.numberOfChannels)||1;return function({optionsHeight:t,optionsSplitChannels:e,parentHeight:i,numberOfChannels:n,defaultHeight:s=128}){if(null==t)return s;const r=Number(t);if(!isNaN(r))return r;if("auto"===t){const t=i||s;return(null==e?void 0:e.every((t=>!t.overlay)))?t/n:t}return s}({optionsHeight:t,optionsSplitChannels:e,parentHeight:this.parent.clientHeight,numberOfChannels:n,defaultHeight:128})}convertColorValues(t,e){return function(t,e,i){if(!Array.isArray(t))return t||"";if(0===t.length)return"#999";if(t.length<2)return t[0]||"";const n=document.createElement("canvas"),s=n.getContext("2d");if(!s)return t[0]||"";const r=i||n.height*e,o=s.createLinearGradient(0,0,0,r),a=1/(t.length-1);return t.forEach(((t,e)=>{o.addColorStop(e*a,t)})),o}(t,this.getPixelRatio(),null==e?void 0:e.canvas.height)}getPixelRatio(){return t=window.devicePixelRatio,Math.max(1,t||1);// removed by dead control flow
4068
4452
  var t; }renderBarWaveform(t,e,i,n){const{width:s,height:r}=i.canvas,{halfHeight:o,barWidth:a,barRadius:l,barIndexScale:h,barSpacing:c,barMinHeight:u}=function({width:t,height:e,length:i,options:n,pixelRatio:s}){const r=e/2,o=n.barWidth?n.barWidth*s:1,a=n.barGap?n.barGap*s:n.barWidth?o/2:0,l=o+a||1;return{halfHeight:r,barWidth:o,barGap:a,barRadius:n.barRadius||0,barMinHeight:n.barMinHeight?n.barMinHeight*s:0,barIndexScale:i>0?t/l/i:0,barSpacing:l}}({width:s,height:r,length:(t[0]||[]).length,options:e,pixelRatio:this.getPixelRatio()}),m=function({channelData:t,barIndexScale:e,barSpacing:i,barWidth:n,halfHeight:s,vScale:r,canvasHeight:o,barAlign:a,barMinHeight:l}){const h=t[0]||[],c=t[1]||h,u=h.length,m=[];let g=0,f=0,v=0;for(let t=0;t<=u;t++){const u=Math.round(t*e);if(u>g){const{topHeight:t,totalHeight:e}=d({maxTop:f,maxBottom:v,halfHeight:s,vScale:r,barMinHeight:l,barAlign:a}),h=p({barAlign:a,halfHeight:s,topHeight:t,totalHeight:e,canvasHeight:o});m.push({x:g*i,y:h,width:n,height:e}),g=u,f=0,v=0}const b=Math.abs(h[t]||0),y=Math.abs(c[t]||0);b>f&&(f=b),y>v&&(v=y)}return m}({channelData:t,barIndexScale:h,barSpacing:c,barWidth:a,halfHeight:o,vScale:n,canvasHeight:r,barAlign:e.barAlign,barMinHeight:u});i.beginPath();for(const t of m)l&&"roundRect"in i?i.roundRect(t.x,t.y,t.width,t.height,l):i.rect(t.x,t.y,t.width,t.height);i.fill(),i.closePath()}renderLineWaveform(t,e,i,n){const{width:s,height:r}=i.canvas,o=function({channelData:t,width:e,height:i,vScale:n}){const s=i/2,r=t[0]||[];return[r,t[1]||r].map(((t,i)=>{const r=t.length,o=r?e/r:0,a=s,l=0===i?-1:1,h=[{x:0,y:a}];let c=0,u=0;for(let e=0;e<=r;e++){const i=Math.round(e*o);if(i>c){const t=a+(Math.round(u*s*n)||1)*l;h.push({x:c,y:t}),c=i,u=0}const r=Math.abs(t[e]||0);r>u&&(u=r)}return h.push({x:c,y:a}),h}))}({channelData:t,width:s,height:r,vScale:n});i.beginPath();for(const t of o)if(t.length){i.moveTo(t[0].x,t[0].y);for(let e=1;e<t.length;e++){const n=t[e];i.lineTo(n.x,n.y)}}i.fill(),i.closePath()}renderWaveform(t,e,i){if(i.fillStyle=this.convertColorValues(e.waveColor,i),e.renderFunction)return void e.renderFunction(t,i);const n=function({channelData:t,barHeight:e,normalize:i,maxPeak:n}){var s;const r=e||1;if(!i)return r;const o=t[0];if(!o||0===o.length)return r;let a=null!=n?n:0;if(!n)for(let t=0;t<o.length;t++){const e=null!==(s=o[t])&&void 0!==s?s:0,i=Math.abs(e);i>a&&(a=i)}return a?r/a:r}({channelData:t,barHeight:e.barHeight,normalize:e.normalize,maxPeak:e.maxPeak});g(e)?this.renderBarWaveform(t,e,i,n):this.renderLineWaveform(t,e,i,n)}renderSingleCanvas(t,e,i,n,s,r,o){const a=this.getPixelRatio(),l=document.createElement("canvas");l.width=Math.round(i*a),l.height=Math.round(n*a),l.style.width=`${i}px`,l.style.height=`${n}px`,l.style.left=`${Math.round(s)}px`,r.appendChild(l);const h=l.getContext("2d");if(e.renderFunction?(h.fillStyle=this.convertColorValues(e.waveColor,h),e.renderFunction(t,h)):this.renderWaveform(t,e,h),l.width>0&&l.height>0){const t=l.cloneNode(),i=t.getContext("2d");i.drawImage(l,0,0),i.globalCompositeOperation="source-in",i.fillStyle=this.convertColorValues(e.progressColor,i),i.fillRect(0,0,l.width,l.height),o.appendChild(t)}}renderMultiCanvas(t,e,i,n,s,r){const o=this.getPixelRatio(),{clientWidth:a}=this.scrollContainer,l=i/o,h=function({clientWidth:t,totalWidth:e,options:i}){return f(Math.min(8e3,t,e),i)}({clientWidth:a,totalWidth:l,options:e});let c={};if(0===h)return;const u=i=>{if(i<0||i>=d)return;if(c[i])return;c[i]=!0;const o=i*h;let a=Math.min(l-o,h);if(a=f(a,e),a<=0)return;const u=function({channelData:t,offset:e,clampedWidth:i,totalWidth:n}){return t.map((t=>{const s=Math.floor(e/n*t.length),r=Math.floor((e+i)/n*t.length);return t.slice(s,r)}))}({channelData:t,offset:o,clampedWidth:a,totalWidth:l});this.renderSingleCanvas(u,e,a,n,o,s,r)},d=Math.ceil(l/h);if(!this.isScrollable){for(let t=0;t<d;t++)u(t);return}if(v({scrollLeft:this.scrollContainer.scrollLeft,totalWidth:l,numCanvases:d}).forEach((t=>u(t))),d>1){const t=this.on("scroll",(()=>{const{scrollLeft:t}=this.scrollContainer;Object.keys(c).length>10&&(s.innerHTML="",r.innerHTML="",c={}),v({scrollLeft:t,totalWidth:l,numCanvases:d}).forEach((t=>u(t)))}));this.unsubscribeOnScroll.push(t)}}renderChannel(t,e,i,n){var{overlay:s}=e,r=function(t,e){var i={};for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&e.indexOf(n)<0&&(i[n]=t[n]);if(null!=t&&"function"==typeof Object.getOwnPropertySymbols){var s=0;for(n=Object.getOwnPropertySymbols(t);s<n.length;s++)e.indexOf(n[s])<0&&Object.prototype.propertyIsEnumerable.call(t,n[s])&&(i[n[s]]=t[n[s]])}return i}(e,["overlay"]);const o=document.createElement("div"),a=this.getHeight(r.height,r.splitChannels);o.style.height=`${a}px`,s&&n>0&&(o.style.marginTop=`-${a}px`),this.canvasWrapper.style.minHeight=`${a}px`,this.canvasWrapper.appendChild(o);const l=o.cloneNode();this.progressWrapper.appendChild(l),this.renderMultiCanvas(t,r,i,a,o,l)}render(e){return t(this,void 0,void 0,(function*(){var t;this.timeouts.forEach((t=>t())),this.timeouts=[],this.unsubscribeOnScroll.forEach((t=>t())),this.unsubscribeOnScroll=[],this.canvasWrapper.innerHTML="",this.progressWrapper.innerHTML="",null!=this.options.width&&(this.scrollContainer.style.width="number"==typeof this.options.width?`${this.options.width}px`:this.options.width);const i=this.getPixelRatio(),n=this.scrollContainer.clientWidth-this.containerInlinePadding,{scrollWidth:s,isScrollable:r,useParentWidth:o,width:a}=function({duration:t,minPxPerSec:e=0,parentWidth:i,fillParent:n,pixelRatio:s}){const r=Math.ceil(t*e),o=r>i,a=Boolean(n&&!o);return{scrollWidth:r,isScrollable:o,useParentWidth:a,width:(a?i:r)*s}}({duration:e.duration,minPxPerSec:this.options.minPxPerSec||0,parentWidth:n,fillParent:this.options.fillParent,pixelRatio:i});if(this.isScrollable=r,this.wrapper.style.width=o?"100%":`${s}px`,this.scrollContainer.style.overflowX=this.isScrollable?"auto":"hidden",this.scrollContainer.classList.toggle("noScrollbar",!!this.options.hideScrollbar),this.cursor.style.backgroundColor=`${this.options.cursorColor||this.options.progressColor}`,this.cursor.style.width=`${this.options.cursorWidth}px`,this.audioData=e,this.emit("render"),this.options.splitChannels)for(let i=0;i<e.numberOfChannels;i++){const n=Object.assign(Object.assign({},this.options),null===(t=this.options.splitChannels)||void 0===t?void 0:t[i]);this.renderChannel([e.getChannelData(i)],n,a,i)}else{const t=[e.getChannelData(0)];e.numberOfChannels>1&&t.push(e.getChannelData(1)),this.renderChannel(t,this.options,a,0)}Promise.resolve().then((()=>this.emit("rendered")))}))}reRender(){if(this.unsubscribeOnScroll.forEach((t=>t())),this.unsubscribeOnScroll=[],!this.audioData)return;const{scrollWidth:t}=this.scrollContainer,{right:e}=this.progressWrapper.getBoundingClientRect();if(this.render(this.audioData),!this.isScrollable&&this.scrollContainer.scrollLeft)this.scrollContainer.scrollLeft=0;else if(this.isScrollable&&t!==this.scrollContainer.scrollWidth){const{right:t}=this.progressWrapper.getBoundingClientRect(),i=function(t){const e=2*t;return(e<0?Math.floor(e):Math.ceil(e))/2}(t-e);this.scrollContainer.scrollLeft+=i}}zoom(t){this.options.minPxPerSec=t,this.reRender()}scrollIntoView(t,e=!1){var i;const{scrollLeft:n,scrollWidth:s,clientWidth:r}=this.scrollContainer,o=t*s,a=n,l=n+r,h=r/2;if(this.isDragging){const t=30;o+t>l?this.scrollContainer.scrollLeft+=t:o-t<a&&(this.scrollContainer.scrollLeft-=t)}else{(o<a||o>l)&&(this.scrollContainer.scrollLeft=o-(this.options.autoCenter?h:0));const t=o-n-h;if(e&&this.options.autoCenter&&t>0){const e=null===(i=this.audioData)||void 0===i?void 0:i.duration;if(void 0===e||e<=0)return void(this.scrollContainer.scrollLeft+=t);const n=s/e;this.scrollContainer.scrollLeft+=n<=600?Math.min(t,10):t}}}renderProgress(t,e){if(isNaN(t))return;const i=100*t;this.canvasWrapper.style.clipPath=`polygon(${i}% 0%, 100% 0%, 100% 100%, ${i}% 100%)`,this.progressWrapper.style.width=`${i}%`,this.cursor.style.left=`${i}%`,this.cursor.style.transform=this.options.cursorWidth?`translateX(-${t*this.options.cursorWidth}px)`:"",this.isScrollable&&this.options.autoScroll&&this.audioData&&this.audioData.duration>0&&this.scrollIntoView(t,e)}exportImage(e,i,n){return t(this,void 0,void 0,(function*(){const t=this.canvasWrapper.querySelectorAll("canvas");if(!t.length)throw new Error("No waveform data");if("dataURL"===n){const n=Array.from(t).map((t=>t.toDataURL(e,i)));return Promise.resolve(n)}return Promise.all(Array.from(t).map((t=>new Promise(((n,s)=>{t.toBlob((t=>{t?n(t):s(new Error("Could not export image"))}),e,i)})))))}))}}class S extends e{constructor(){super(...arguments),this.animationFrameId=null,this.isRunning=!1}start(){if(this.isRunning)return;this.isRunning=!0;const t=()=>{this.isRunning&&(this.emit("tick"),this.animationFrameId=requestAnimationFrame(t))};t()}stop(){this.isRunning=!1,null!==this.animationFrameId&&(cancelAnimationFrame(this.animationFrameId),this.animationFrameId=null)}destroy(){this.stop(),this.unAll()}}class E extends e{constructor(t){super(),this.bufferNode=null,this.playStartTime=0,this.playbackPosition=0,this._muted=!1,this._playbackRate=1,this._duration=void 0,this.buffer=null,this.currentSrc="",this.paused=!0,this.crossOrigin=null,this.seeking=!1,this.autoplay=!1,this.addEventListener=this.on,this.removeEventListener=this.un,this._destroyed=!1,function(){const t=globalThis.navigator;if(null==t?void 0:t.audioSession)try{t.audioSession.type="playback"}catch(t){console.warn("Setting navigator.audioSession.type failed:",t)}}(),this.audioContext=t||new AudioContext,this.gainNode=this.audioContext.createGain(),this.gainNode.connect(this.audioContext.destination)}load(){return t(this,void 0,void 0,(function*(){}))}remove(){this.destroy()}destroy(){if(!this._destroyed){if(this._destroyed=!0,this.currentSrc="",this.bufferNode){this.bufferNode.onended=null;try{this.bufferNode.stop()}catch(t){}this.bufferNode.disconnect(),this.bufferNode=null}this.gainNode.disconnect(),"function"==typeof this.audioContext.close&&Promise.resolve(this.audioContext.close.call(this.audioContext)).catch((()=>{})),this.buffer=null,this.unAll()}}get src(){return this.currentSrc}set src(t){if(this.currentSrc=t,this._duration=void 0,!t)return this.buffer=null,void this.emit("emptied");fetch(t).then((e=>{if(e.status>=400)throw new Error(`Failed to fetch ${t}: ${e.status} (${e.statusText})`);return e.arrayBuffer()})).then((e=>this.currentSrc!==t?null:this.audioContext.decodeAudioData(e))).then((e=>{this.currentSrc===t&&(this.buffer=e,this.emit("loadedmetadata"),this.emit("canplay"),this.autoplay&&this.play())})).catch((t=>{console.error("WebAudioPlayer load error:",t)}))}_play(){if(!this.paused)return;this.paused=!1,this.bufferNode&&(this.bufferNode.onended=null,this.bufferNode.disconnect()),this.bufferNode=this.audioContext.createBufferSource(),this.buffer&&(this.bufferNode.buffer=this.buffer),this.bufferNode.playbackRate.value=this._playbackRate,this.bufferNode.connect(this.gainNode);let t=this.playbackPosition;(t>=this.duration||t<0)&&(t=0,this.playbackPosition=0),this.bufferNode.start(this.audioContext.currentTime,t),this.playStartTime=this.audioContext.currentTime,this.bufferNode.onended=()=>{!this.paused&&this.duration-this.currentTime<.01&&(this.pause(),this.emit("ended"))}}_pause(){if(this.paused=!0,this.bufferNode){this.bufferNode.onended=null;try{this.bufferNode.stop()}catch(t){}}this.playbackPosition+=(this.audioContext.currentTime-this.playStartTime)*this._playbackRate}play(){return t(this,void 0,void 0,(function*(){this.paused&&(this._play(),this.emit("play"))}))}pause(){this.paused||(this._pause(),this.emit("pause"))}stopAt(t){const e=(t-this.currentTime)/this._playbackRate,i=this.bufferNode;null==i||i.stop(this.audioContext.currentTime+e),null==i||i.addEventListener("ended",(()=>{i===this.bufferNode&&(this.bufferNode=null,this.pause(),this.playbackPosition=Math.min(t,this.duration),this.emit("timeupdate"))}),{once:!0})}setSinkId(e){return t(this,void 0,void 0,(function*(){return this.audioContext.setSinkId(e)}))}get playbackRate(){return this._playbackRate}set playbackRate(t){const e=!this.paused;e&&this._pause(),this._playbackRate=t,e&&this._play(),this.bufferNode&&(this.bufferNode.playbackRate.value=t)}get currentTime(){return this.paused?this.playbackPosition:this.playbackPosition+(this.audioContext.currentTime-this.playStartTime)*this._playbackRate}set currentTime(t){const e=!this.paused;e&&this._pause(),this.playbackPosition=t,e&&this._play(),this.emit("seeking"),this.emit("timeupdate")}get duration(){var t,e;return null!==(t=this._duration)&&void 0!==t?t:(null===(e=this.buffer)||void 0===e?void 0:e.duration)||0}set duration(t){this._duration=t}get volume(){return this.gainNode.gain.value}set volume(t){this.gainNode.gain.value=t,this.emit("volumechange")}get muted(){return this._muted}set muted(t){this._muted!==t&&(this._muted=t,this._muted?this.gainNode.disconnect():this.gainNode.connect(this.audioContext.destination))}canPlayType(t){return/^(audio|video)\//.test(t)}getGainNode(){return this.gainNode}getChannelData(){const t=[];if(!this.buffer)return t;const e=this.buffer.numberOfChannels;for(let i=0;i<e;i++)t.push(this.buffer.getChannelData(i));return t}removeAttribute(t){switch(t){case"src":this.src="";break;case"playbackRate":this.playbackRate=0;break;case"currentTime":this.currentTime=0;break;case"duration":this.duration=0;break;case"volume":this.volume=0;break;case"muted":this.muted=!1}}}const P={waveColor:"#999",progressColor:"#555",cursorWidth:1,minPxPerSec:0,fillParent:!0,interact:!0,dragToSeek:!1,autoScroll:!0,autoCenter:!0,sampleRate:8e3};class w extends u{static create(t){return new w(t)}getState(){return this.wavesurferState}getRenderer(){return this.renderer}constructor(t){const e=t.media||("WebAudio"===t.backend?new E:void 0);super({media:e,mediaControls:t.mediaControls,autoplay:t.autoplay,playbackRate:t.audioRate}),this.plugins=[],this.decodedData=null,this.stopAtPosition=null,this.subscriptions=[],this.mediaSubscriptions=[],this.abortController=null,this._isDestroyed=!1,this._loadVersion=0,this.reactiveCleanups=[],this.options=Object.assign({},P,t);const{state:i,actions:n}=function(t){var e,i,n,s,r,o;const a=null!==(e=null==t?void 0:t.currentTime)&&void 0!==e?e:l(0),c=null!==(i=null==t?void 0:t.duration)&&void 0!==i?i:l(0),u=null!==(n=null==t?void 0:t.isPlaying)&&void 0!==n?n:l(!1),d=null!==(s=null==t?void 0:t.isSeeking)&&void 0!==s?s:l(!1),p=null!==(r=null==t?void 0:t.volume)&&void 0!==r?r:l(1),m=null!==(o=null==t?void 0:t.playbackRate)&&void 0!==o?o:l(1),g=l(null),f=l(null),v=l(""),b=l(0),y=l(0),C=h((()=>!u.value),[u]),S=h((()=>null!==g.value),[g]),E=h((()=>S.value&&c.value>0),[S,c]),P=h((()=>a.value),[a]),w=h((()=>c.value>0?a.value/c.value:0),[a,c]);return{state:{currentTime:a,duration:c,isPlaying:u,isPaused:C,isSeeking:d,volume:p,playbackRate:m,audioBuffer:g,peaks:f,url:v,zoom:b,scrollPosition:y,canPlay:S,isReady:E,progress:P,progressPercent:w},actions:{setCurrentTime:t=>{const e=Math.max(0,Math.min(c.value||1/0,t));a.set(e)},setDuration:t=>{c.set(Math.max(0,t))},setPlaying:t=>{u.set(t)},setSeeking:t=>{d.set(t)},setVolume:t=>{const e=Math.max(0,Math.min(1,t));p.set(e)},setPlaybackRate:t=>{const e=Math.max(.1,Math.min(16,t));m.set(e)},setAudioBuffer:t=>{g.set(t),t&&c.set(t.duration)},setPeaks:t=>{f.set(t)},setUrl:t=>{v.set(t)},setZoom:t=>{b.set(Math.max(0,t))},setScrollPosition:t=>{y.set(Math.max(0,t))}}}}({isPlaying:this.isPlayingSignal,currentTime:this.currentTimeSignal,duration:this.durationSignal,volume:this.volumeSignal,playbackRate:this.playbackRateSignal,isSeeking:this.seekingSignal});this.wavesurferState=i,this.wavesurferActions=n,this.timer=new S;const s=e?void 0:this.getMediaElement();this.renderer=new C(this.options,s),this.initPlayerEvents(),this.initRendererEvents(),this.initTimerEvents(),this.initReactiveState(),this.initPlugins();const r=this.options.url||this.getSrc()||"";Promise.resolve().then((()=>{this.emit("init");const{peaks:t,duration:e}=this.options;(r||t&&e)&&this.load(r,t,e).catch((()=>{}))}))}updateProgress(t=this.getCurrentTime()){return this.renderer.renderProgress(t/this.getDuration(),this.isPlaying()),t}initTimerEvents(){this.subscriptions.push(this.timer.on("tick",(()=>{if(!this.isSeeking()){const t=this.updateProgress();if(this.emit("timeupdate",t),this.emit("audioprocess",t),null!=this.stopAtPosition&&this.isPlaying()&&t>=this.stopAtPosition){const t=this.stopAtPosition;this.pause(),this.setTime(t)}}})))}initReactiveState(){this.reactiveCleanups.push(function(t,e){const i=[];i.push(c((()=>{const i=t.isPlaying.value;e.emit(i?"play":"pause")}),[t.isPlaying])),i.push(c((()=>{const i=t.currentTime.value;e.emit("timeupdate",i),t.isPlaying.value&&e.emit("audioprocess",i)}),[t.currentTime,t.isPlaying])),i.push(c((()=>{t.isSeeking.value&&e.emit("seeking",t.currentTime.value)}),[t.isSeeking,t.currentTime]));let n=!1;i.push(c((()=>{t.isReady.value&&!n&&(n=!0,e.emit("ready",t.duration.value))}),[t.isReady,t.duration])),i.push(c((()=>{null===t.audioBuffer.value&&(n=!1)}),[t.audioBuffer]));let s=!1;return i.push(c((()=>{const i=t.isPlaying.value,n=t.currentTime.value,r=t.duration.value,o=r>0&&n>=r;s&&!i&&o&&e.emit("finish"),s=i&&o}),[t.isPlaying,t.currentTime,t.duration])),i.push(c((()=>{const i=t.zoom.value;i>0&&e.emit("zoom",i)}),[t.zoom])),()=>{i.forEach((t=>t()))}}(this.wavesurferState,{emit:this.emit.bind(this)}))}initPlayerEvents(){this.isPlaying()&&(this.emit("play"),this.timer.start()),this.mediaSubscriptions.push(this.onMediaEvent("timeupdate",(()=>{const t=this.updateProgress();this.emit("timeupdate",t)})),this.onMediaEvent("play",(()=>{this.emit("play"),this.timer.start()})),this.onMediaEvent("pause",(()=>{this.emit("pause"),this.timer.stop(),this.stopAtPosition=null})),this.onMediaEvent("emptied",(()=>{this.timer.stop(),this.stopAtPosition=null})),this.onMediaEvent("ended",(()=>{this.emit("timeupdate",this.getDuration()),this.emit("finish"),this.stopAtPosition=null})),this.onMediaEvent("seeking",(()=>{this.emit("seeking",this.getCurrentTime())})),this.onMediaEvent("error",(()=>{var t;this.emit("error",null!==(t=this.getMediaElement().error)&&void 0!==t?t:new Error("Media error")),this.stopAtPosition=null})))}initRendererEvents(){this.subscriptions.push(this.renderer.on("click",((t,e)=>{this.options.interact&&(this.seekTo(t),this.emit("interaction",t*this.getDuration()),this.emit("click",t,e))})),this.renderer.on("dblclick",((t,e)=>{this.emit("dblclick",t,e)})),this.renderer.on("scroll",((t,e,i,n)=>{const s=this.getDuration();this.emit("scroll",t*s,e*s,i,n)})),this.renderer.on("render",(()=>{this.emit("redraw")})),this.renderer.on("rendered",(()=>{this.emit("redrawcomplete")})),this.renderer.on("dragstart",(t=>{this.emit("dragstart",t)})),this.renderer.on("dragend",(t=>{this.emit("dragend",t)})),this.renderer.on("resize",(()=>{this.emit("resize")})));{let t;const e=this.renderer.on("drag",(e=>{var i;if(!this.options.interact)return;this.renderer.renderProgress(e),clearTimeout(t);let n=0;const s=this.options.dragToSeek;this.isPlaying()?n=0:!0===s?n=200:s&&"object"==typeof s&&(n=null!==(i=s.debounceTime)&&void 0!==i?i:200),t=setTimeout((()=>{this.seekTo(e)}),n),this.emit("interaction",e*this.getDuration()),this.emit("drag",e)}));this.subscriptions.push((()=>{clearTimeout(t),e()}))}}initPlugins(){var t;(null===(t=this.options.plugins)||void 0===t?void 0:t.length)&&this.options.plugins.forEach((t=>{this.registerPlugin(t)}))}unsubscribePlayerEvents(){this.mediaSubscriptions.forEach((t=>t())),this.mediaSubscriptions=[]}setOptions(t){this.options=Object.assign({},this.options,t),t.duration&&!t.peaks&&(this.decodedData=i.createBuffer(this.exportPeaks(),t.duration)),t.peaks&&t.duration&&(this.decodedData=i.createBuffer(t.peaks,t.duration)),this.renderer.setOptions(this.options),t.audioRate&&this.setPlaybackRate(t.audioRate),null!=t.mediaControls&&(this.getMediaElement().controls=t.mediaControls)}registerPlugin(t){if(this.plugins.includes(t))return t;t._init(this),this.plugins.push(t);const e=t.once("destroy",(()=>{this.plugins=this.plugins.filter((e=>e!==t)),this.subscriptions=this.subscriptions.filter((t=>t!==e))}));return this.subscriptions.push(e),t}unregisterPlugin(t){this.plugins=this.plugins.filter((e=>e!==t)),t.destroy()}getWrapper(){return this.renderer.getWrapper()}getWidth(){return this.renderer.getWidth()}getScroll(){return this.renderer.getScroll()}setScroll(t){return this.renderer.setScroll(t)}setScrollTime(t){const e=t/this.getDuration();this.renderer.setScrollPercentage(e)}getActivePlugins(){return this.plugins}loadAudio(e,n,s,r){return t(this,void 0,void 0,(function*(){var t;const o=++this._loadVersion;if(this._isDestroyed=!1,this.emit("load",e),!this.options.media&&this.isPlaying()&&this.pause(),this.decodedData=null,this.stopAtPosition=null,null===(t=this.abortController)||void 0===t||t.abort(),this.abortController=null,!n&&!s){const t=this.options.fetchParams||{};window.AbortController&&!t.signal&&(this.abortController=new AbortController,t.signal=this.abortController.signal);const i=t=>this.emit("loading",t);if(n=yield a.fetchBlob(e,i,t),this._isDestroyed||o!==this._loadVersion)return;const s=this.options.blobMimeType;s&&(n=new Blob([n],{type:s}))}if(this._isDestroyed||o!==this._loadVersion)return;this.setSrc(e,n);const l=yield new Promise((t=>{const e=r||this.getDuration();e?t(e):this.mediaSubscriptions.push(this.onMediaEvent("loadedmetadata",(()=>t(this.getDuration())),{once:!0}))}));if(!this._isDestroyed&&o===this._loadVersion){if(!e&&!n){const t=this.getMediaElement();t instanceof E&&(t.duration=l)}if(s)this.decodedData=i.createBuffer(s,l||0);else if(n){const t=yield n.arrayBuffer();if(this._isDestroyed||o!==this._loadVersion)return;this.decodedData=yield i.decode(t,this.options.sampleRate)}this._isDestroyed||o!==this._loadVersion||(this.decodedData&&(this.emit("decode",this.getDuration()),this.renderer.render(this.decodedData)),this.emit("ready",this.getDuration()))}}))}load(e,i,n){return t(this,void 0,void 0,(function*(){try{return yield this.loadAudio(e,void 0,i,n)}catch(t){throw this.emit("error",t),t}}))}loadBlob(e,i,n){return t(this,void 0,void 0,(function*(){try{return yield this.loadAudio("",e,i,n)}catch(t){throw this.emit("error",t),t}}))}zoom(t){if(!this.decodedData)throw new Error("No audio loaded");this.renderer.zoom(t),this.emit("zoom",t)}getDecodedData(){return this.decodedData}exportPeaks({channels:t=2,maxLength:e=8e3,precision:i=1e4}={}){if(!this.decodedData)throw new Error("The audio has not been decoded yet");const n=Math.min(t,this.decodedData.numberOfChannels),s=[];for(let t=0;t<n;t++){const n=this.decodedData.getChannelData(t),r=[],o=n.length/e;for(let t=0;t<e;t++){const e=n.slice(Math.floor(t*o),Math.ceil((t+1)*o));let s=0;for(let t=0;t<e.length;t++){const i=e[t];Math.abs(i)>Math.abs(s)&&(s=i)}r.push(Math.round(s*i)/i)}s.push(r)}return s}getDuration(){let t=super.getDuration()||0;return 0!==t&&t!==1/0||!this.decodedData||(t=this.decodedData.duration),t}toggleInteraction(t){this.options.interact=t}setTime(t){this.stopAtPosition=null,super.setTime(t),this.updateProgress(t),this.emit("timeupdate",t)}seekTo(t){const e=this.getDuration()*t;this.setTime(e)}play(e,i){const n=Object.create(null,{play:{get:()=>super.play}});return t(this,void 0,void 0,(function*(){null!=e&&this.setTime(e);const t=yield n.play.call(this);return null!=i&&(this.media instanceof E?this.media.stopAt(i):this.stopAtPosition=i),t}))}playPause(){return t(this,void 0,void 0,(function*(){return this.isPlaying()?this.pause():this.play()}))}stop(){this.pause(),this.setTime(0)}skip(t){this.setTime(this.getCurrentTime()+t)}empty(){this.load("",[[0]],.001)}setMediaElement(t){this.unsubscribePlayerEvents(),super.setMediaElement(t),this.initPlayerEvents()}exportImage(){return t(this,arguments,void 0,(function*(t="image/png",e=1,i="dataURL"){return this.renderer.exportImage(t,e,i)}))}destroy(){var t;this._isDestroyed=!0,this.emit("destroy"),null===(t=this.abortController)||void 0===t||t.abort(),this.plugins.forEach((t=>t.destroy())),this.subscriptions.forEach((t=>t())),this.unsubscribePlayerEvents(),this.reactiveCleanups.forEach((t=>t())),this.reactiveCleanups=[],this.timer.destroy(),this.renderer.destroy(),super.destroy()}}w.BasePlugin=class extends e{constructor(t){super(),this.subscriptions=[],this.isDestroyed=!1,this.options=t}onInit(){}_init(t){this.isDestroyed&&(this.subscriptions=[],this.isDestroyed=!1),this.wavesurfer=t,this.onInit()}destroy(){this.emit("destroy"),this.subscriptions.forEach((t=>t())),this.subscriptions=[],this.isDestroyed=!0,this.wavesurfer=void 0}},w.dom=o;
4069
4453
 
4454
+ // EXTERNAL MODULE: ./src/lib/util.js + 1 modules
4455
+ var util = __webpack_require__(4410);
4070
4456
  ;// ./src/lib/viewers/media/waveform/colors.ts
4071
4457
  /**
4072
4458
  * Figma Audio Player waveform tokens as opaque fills.
@@ -4085,14 +4471,6 @@ const WAVEFORM_COLOR_HOVER_PLAYED = whiteOnBlack(0.9);
4085
4471
  const WAVEFORM_COLOR_HOVER_AREA = whiteOnBlack(0.6);
4086
4472
  const WAVEFORM_COLOR_HOVER_UNPLAYED = whiteOnBlack(0.3);
4087
4473
  const WAVEFORM_COLOR_HOVER_BUFFER = whiteOnBlack(0.16);
4088
-
4089
- /** One canvas linear-gradient stop. `offset` is 0–1 along the bar. */
4090
-
4091
- /**
4092
- * Wavesurfer's two paints: left of the playhead (`progressColor`) and right of it (`waveColor`).
4093
- * A string is a solid fill; a stop list is a left-to-right step gradient.
4094
- */
4095
-
4096
4474
  /** Pin a value to the closed interval [0, 1]. */
4097
4475
  function clampTo0And1(value) {
4098
4476
  if (!Number.isFinite(value) || value < 0) {
@@ -4227,29 +4605,371 @@ function toCanvasFill(color, widthPx) {
4227
4605
  });
4228
4606
  return gradient;
4229
4607
  }
4608
+
4609
+ /** Untinted bar pixels for each WaveSurfer tile, so hover can re-tint without stacking. */
4610
+ const tileBarSnapshots = new WeakMap();
4611
+
4612
+ /** Restore the last bar snapshot, or take a new one after WaveSurfer redraws. */
4613
+ function restoreOrSnapshotTile(canvas, context, replaceSnapshot) {
4614
+ if (!(canvas.width > 0) || !(canvas.height > 0) || !context.getImageData) {
4615
+ return;
4616
+ }
4617
+ const stored = tileBarSnapshots.get(canvas);
4618
+ if (!replaceSnapshot && stored && context.putImageData) {
4619
+ context.putImageData(stored, 0, 0);
4620
+ return;
4621
+ }
4622
+ try {
4623
+ tileBarSnapshots.set(canvas, context.getImageData(0, 0, canvas.width, canvas.height));
4624
+ } catch {
4625
+ // Tainted or zero-size canvases cannot snapshot; tint in place.
4626
+ }
4627
+ }
4628
+
4629
+ /** CSS width of a tile canvas (style.width, then clientWidth, then bitmap width). */
4630
+ function tileWidthCss(canvas) {
4631
+ const styled = parseFloat(canvas.style.width);
4632
+ if (styled > 0) {
4633
+ return styled;
4634
+ }
4635
+ if (canvas.clientWidth > 0) {
4636
+ return canvas.clientWidth;
4637
+ }
4638
+ return canvas.width;
4639
+ }
4640
+
4641
+ /** Solid color, or a gradient aligned to the full waveform and shifted to this tile. */
4642
+ function fillForTile(context, color, totalWidthCss, offsetCss, canvas) {
4643
+ if (typeof color === 'string') {
4644
+ return color;
4645
+ }
4646
+ const cssWidth = tileWidthCss(canvas);
4647
+ const scale = cssWidth > 0 ? canvas.width / cssWidth : 1;
4648
+ if (!(totalWidthCss > 0) || !(scale > 0)) {
4649
+ return color[0] ? color[0].color : WAVEFORM_COLOR_UNPLAYED;
4650
+ }
4651
+ const x0 = -offsetCss * scale || 0;
4652
+ const x1 = (totalWidthCss - offsetCss) * scale;
4653
+ const gradient = context.createLinearGradient(x0, 0, x1, 0);
4654
+ let lastOffset = -1;
4655
+ color.forEach(stop => {
4656
+ let offset = clampTo0And1(stop.offset);
4657
+ if (offset <= lastOffset) {
4658
+ offset = Math.min(1, lastOffset + 1e-6);
4659
+ }
4660
+ lastOffset = offset;
4661
+ gradient.addColorStop(offset, stop.color);
4662
+ });
4663
+ return gradient;
4664
+ }
4665
+
4666
+ /**
4667
+ * Wavesurfer splits a zoomed waveform into viewport-sized tiles and paints each
4668
+ * from x=0. Re-tint with a gradient in global waveform space so hover/buffer
4669
+ * colors do not repeat on the last tile.
4670
+ */
4671
+ function tintWaveformTiles({
4672
+ fills,
4673
+ host,
4674
+ replaceSnapshot = false,
4675
+ totalWidthCss
4676
+ }) {
4677
+ if (!(totalWidthCss > 0) || !host.querySelectorAll) {
4678
+ return;
4679
+ }
4680
+ const groups = [{
4681
+ canvases: host.querySelectorAll('.canvases canvas'),
4682
+ color: fills.waveColor
4683
+ }, {
4684
+ canvases: host.querySelectorAll('.progress canvas'),
4685
+ color: fills.progressColor
4686
+ }];
4687
+ groups.forEach(({
4688
+ canvases,
4689
+ color
4690
+ }) => {
4691
+ canvases.forEach(canvas => {
4692
+ const context = canvas.getContext('2d');
4693
+ if (!context) {
4694
+ return;
4695
+ }
4696
+ restoreOrSnapshotTile(canvas, context, replaceSnapshot);
4697
+ const offsetCss = parseFloat(canvas.style.left) || 0;
4698
+ context.save();
4699
+ context.globalCompositeOperation = 'source-in';
4700
+ context.fillStyle = fillForTile(context, color, totalWidthCss, offsetCss, canvas);
4701
+ context.fillRect(0, 0, canvas.width, canvas.height);
4702
+ context.restore();
4703
+ });
4704
+ });
4705
+ }
4706
+ ;// ./src/lib/viewers/media/waveform/usePlayheadCamera.ts
4707
+
4708
+
4709
+
4710
+
4711
+ function getScrollLeft(wavesurfer, fallback = 0) {
4712
+ return wavesurfer && wavesurfer.getScroll ? wavesurfer.getScroll() : fallback;
4713
+ }
4714
+ function prefersReducedMotion() {
4715
+ return typeof window !== 'undefined' && typeof window.matchMedia === 'function' && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
4716
+ }
4717
+ /**
4718
+ * Pin/follow vs user pan. Refs, not state: rAF and WaveSurfer `scroll` read these
4719
+ * every frame. WaveformView owns WaveSurfer, zoom, and the media rAF loop.
4720
+ */
4721
+ function usePlayheadCamera({
4722
+ mediaElRef,
4723
+ onViewportCommit,
4724
+ playheadRef,
4725
+ viewportRef,
4726
+ wavesurferRef
4727
+ }) {
4728
+ const onViewportCommitRef = (0,external_react_.useRef)(onViewportCommit);
4729
+ onViewportCommitRef.current = onViewportCommit;
4730
+ const programmaticScrollRef = (0,external_react_.useRef)(false);
4731
+ const jumpAnimationRef = (0,external_react_.useRef)(0);
4732
+ const lastCameraScrollRef = (0,external_react_.useRef)(null);
4733
+ const mediaTimeRef = (0,external_react_.useRef)(0);
4734
+ const isFollowPinnedRef = (0,external_react_.useRef)(false);
4735
+ const userIsScrollingRef = (0,external_react_.useRef)(false);
4736
+ const holdFollowUntilInsetRef = (0,external_react_.useRef)(false);
4737
+ const scrollSettleTimerRef = (0,external_react_.useRef)(0);
4738
+ const applyRef = (0,external_react_.useRef)(null);
4739
+ const isFollowPinned = (0,external_react_.useCallback)(() => isFollowPinnedRef.current, []);
4740
+ const cancelJump = (0,external_react_.useCallback)(() => {
4741
+ if (!jumpAnimationRef.current) {
4742
+ return;
4743
+ }
4744
+ window.cancelAnimationFrame(jumpAnimationRef.current);
4745
+ jumpAnimationRef.current = 0;
4746
+ }, []);
4747
+ const clearFollowPin = (0,external_react_.useCallback)(() => {
4748
+ isFollowPinnedRef.current = false;
4749
+ programmaticScrollRef.current = false;
4750
+ }, []);
4751
+ const releaseUserPanHold = (0,external_react_.useCallback)(() => {
4752
+ window.clearTimeout(scrollSettleTimerRef.current);
4753
+ scrollSettleTimerRef.current = 0;
4754
+ userIsScrollingRef.current = false;
4755
+ holdFollowUntilInsetRef.current = false;
4756
+ }, []);
4757
+ const readMediaTime = (0,external_react_.useCallback)(() => {
4758
+ const live = mediaElRef.current?.currentTime;
4759
+ if (typeof live === 'number' && Number.isFinite(live)) {
4760
+ mediaTimeRef.current = live;
4761
+ return live;
4762
+ }
4763
+ return mediaTimeRef.current;
4764
+ }, [mediaElRef]);
4765
+ const onSeek = (0,external_react_.useCallback)(timeSec => {
4766
+ mediaTimeRef.current = timeSec;
4767
+ cancelJump();
4768
+ clearFollowPin();
4769
+ }, [cancelJump, clearFollowPin]);
4770
+
4771
+ /** Unpin and hold follow so zoom setScroll (slider center or pinch origin) is not stolen. */
4772
+ const onZoom = (0,external_react_.useCallback)(() => {
4773
+ cancelJump();
4774
+ isFollowPinnedRef.current = false;
4775
+ holdFollowUntilInsetRef.current = true;
4776
+ }, [cancelJump]);
4777
+ const applyScrollLeft = (0,external_react_.useCallback)((scrollLeftPx, shouldCommitState) => {
4778
+ const wavesurfer = wavesurferRef.current;
4779
+ if (!wavesurfer || !wavesurfer.setScroll) {
4780
+ return;
4781
+ }
4782
+ programmaticScrollRef.current = true;
4783
+ lastCameraScrollRef.current = scrollLeftPx;
4784
+ wavesurfer.setScroll(scrollLeftPx);
4785
+ const appliedScrollLeft = getScrollLeft(wavesurfer, scrollLeftPx);
4786
+ lastCameraScrollRef.current = appliedScrollLeft;
4787
+ viewportRef.current = getViewportAtScroll(viewportRef.current, appliedScrollLeft);
4788
+ window.requestAnimationFrame(() => {
4789
+ // Keep the flag through delayed `scroll` while the camera is driving.
4790
+ if (jumpAnimationRef.current || isFollowPinnedRef.current) {
4791
+ return;
4792
+ }
4793
+ programmaticScrollRef.current = false;
4794
+ });
4795
+ if (shouldCommitState) {
4796
+ onViewportCommitRef.current(appliedScrollLeft, viewportRef.current);
4797
+ }
4798
+ }, [viewportRef, wavesurferRef]);
4799
+ const pinFollowPlayhead = (0,external_react_.useCallback)((playhead, viewport, isPinned) => {
4800
+ isFollowPinnedRef.current = isPinned;
4801
+ if (!playhead || !isPinned || !(viewport.widthPx > 0)) {
4802
+ return;
4803
+ }
4804
+ playhead.style.left = getPinnedPlayheadLeft(viewport.widthPx);
4805
+ }, []);
4806
+ const apply = (0,external_react_.useCallback)((timeSec, playJustStarted = false) => {
4807
+ mediaTimeRef.current = timeSec;
4808
+ const wavesurfer = wavesurferRef.current;
4809
+ const viewport = viewportRef.current;
4810
+ if (!wavesurfer) {
4811
+ return;
4812
+ }
4813
+ if (viewport.zoomLevel <= constants/* WAVEFORM_ZOOM_MIN */.LW) {
4814
+ clearFollowPin();
4815
+ return;
4816
+ }
4817
+ if (userIsScrollingRef.current && !playJustStarted) {
4818
+ return;
4819
+ }
4820
+ if (jumpAnimationRef.current && !playJustStarted) {
4821
+ return;
4822
+ }
4823
+ const liveViewport = getViewportAtScroll(viewport, getScrollLeft(wavesurfer, viewport.scrollLeftPx));
4824
+ const action = getPlayheadCameraAction({
4825
+ isPlaying: true,
4826
+ playJustStarted,
4827
+ timeSec,
4828
+ viewport: liveViewport
4829
+ });
4830
+ if (holdFollowUntilInsetRef.current && !playJustStarted) {
4831
+ if (action.type !== 'none') {
4832
+ return;
4833
+ }
4834
+ holdFollowUntilInsetRef.current = false;
4835
+ }
4836
+ if (action.type === 'none') {
4837
+ if (isFollowPinnedRef.current) {
4838
+ isFollowPinnedRef.current = false;
4839
+ applyScrollLeft(liveViewport.scrollLeftPx, true);
4840
+ }
4841
+ programmaticScrollRef.current = false;
4842
+ return;
4843
+ }
4844
+ cancelJump();
4845
+ if (action.type === 'followRight') {
4846
+ pinFollowPlayhead(playheadRef.current, liveViewport, action.isPlayheadPinned);
4847
+ applyScrollLeft(action.scrollLeftPx, !action.isPlayheadPinned);
4848
+ return;
4849
+ }
4850
+ isFollowPinnedRef.current = false;
4851
+ const from = getScrollLeft(wavesurfer, liveViewport.scrollLeftPx);
4852
+ if (prefersReducedMotion() || typeof window.requestAnimationFrame !== 'function') {
4853
+ applyScrollLeft(action.scrollLeftPx, true);
4854
+ apply(timeSec, false);
4855
+ return;
4856
+ }
4857
+ programmaticScrollRef.current = true;
4858
+ const start = (0,util/* getCurrentTimeMs */.RU)();
4859
+ const tick = now => {
4860
+ const t = Math.min(1, (now - start) / constants/* WAVEFORM_PLAYHEAD_JUMP_MS */.Bq);
4861
+ const liveAction = getPlayheadCameraAction({
4862
+ isPlaying: true,
4863
+ playJustStarted: true,
4864
+ timeSec: mediaTimeRef.current,
4865
+ viewport: getViewportAtScroll(viewportRef.current, getScrollLeft(wavesurfer, from))
4866
+ });
4867
+ const to = liveAction.type === 'none' ? action.scrollLeftPx : liveAction.scrollLeftPx;
4868
+ applyScrollLeft(from + (to - from) * (1 - (1 - t) * (1 - t)), false);
4869
+ if (t < 1) {
4870
+ jumpAnimationRef.current = window.requestAnimationFrame(tick);
4871
+ return;
4872
+ }
4873
+ jumpAnimationRef.current = 0;
4874
+ applyScrollLeft(to, true);
4875
+ apply(mediaTimeRef.current, false);
4876
+ };
4877
+ jumpAnimationRef.current = window.requestAnimationFrame(tick);
4878
+ }, [applyScrollLeft, cancelJump, clearFollowPin, pinFollowPlayhead, playheadRef, viewportRef, wavesurferRef]);
4879
+ applyRef.current = apply;
4880
+
4881
+ /**
4882
+ * Camera-owned: jumping; matching follow scroll; unpinned programmatic; clamp-to-max.
4883
+ * User pan: pinned and |scroll - lastCamera| > 1, or any non-programmatic scroll while not jumping.
4884
+ */
4885
+ const isUserPan = (0,external_react_.useCallback)(scrollLeftPx => {
4886
+ const isCameraOwnedScroll = programmaticScrollRef.current || jumpAnimationRef.current !== 0;
4887
+ const lastCameraScroll = lastCameraScrollRef.current;
4888
+ const maxScroll = maxScrollLeft(viewportRef.current);
4889
+ const isClampedToMax = lastCameraScroll != null && scrollLeftPx >= maxScroll - 1 && lastCameraScroll >= maxScroll - 1;
4890
+ const isFollowCameraScroll = isFollowPinnedRef.current && lastCameraScroll != null && Math.abs(scrollLeftPx - lastCameraScroll) <= 1;
4891
+ return !(isCameraOwnedScroll && (jumpAnimationRef.current !== 0 || isFollowCameraScroll || isClampedToMax || !isFollowPinnedRef.current));
4892
+ }, [viewportRef]);
4893
+ const handleScroll = (0,external_react_.useCallback)(onUserPan => {
4894
+ const wavesurfer = wavesurferRef.current;
4895
+ if (!wavesurfer) {
4896
+ return;
4897
+ }
4898
+ const scrollLeftPx = getScrollLeft(wavesurfer);
4899
+ if (!isUserPan(scrollLeftPx)) {
4900
+ viewportRef.current = getViewportAtScroll(viewportRef.current, scrollLeftPx);
4901
+ return;
4902
+ }
4903
+ viewportRef.current = getViewportAtScroll(viewportRef.current, scrollLeftPx);
4904
+ const timeSec = readMediaTime();
4905
+ const playhead = playheadRef.current;
4906
+ if (playhead) {
4907
+ playhead.style.left = timeLeftPercent(timeSec, viewportRef.current.durationSec, viewportRef.current);
4908
+ }
4909
+ userIsScrollingRef.current = true;
4910
+ holdFollowUntilInsetRef.current = true;
4911
+ window.clearTimeout(scrollSettleTimerRef.current);
4912
+ scrollSettleTimerRef.current = window.setTimeout(() => {
4913
+ userIsScrollingRef.current = false;
4914
+ scrollSettleTimerRef.current = 0;
4915
+ if (!mediaElRef.current || mediaElRef.current.paused) {
4916
+ return;
4917
+ }
4918
+ applyRef.current?.(readMediaTime(), false);
4919
+ }, constants/* WAVEFORM_FOLLOW_SCROLL_SETTLE_MS */.Zs);
4920
+ cancelJump();
4921
+ isFollowPinnedRef.current = false;
4922
+ programmaticScrollRef.current = false;
4923
+ onUserPan(timeSec);
4924
+ }, [cancelJump, isUserPan, mediaElRef, playheadRef, readMediaTime, viewportRef, wavesurferRef]);
4925
+ (0,external_react_.useEffect)(() => () => {
4926
+ releaseUserPanHold();
4927
+ cancelJump();
4928
+ }, [cancelJump, releaseUserPanHold]);
4929
+ return {
4930
+ apply,
4931
+ applyScrollLeft,
4932
+ cancelJump,
4933
+ clearFollowPin,
4934
+ handleScroll,
4935
+ isFollowPinned,
4936
+ onSeek,
4937
+ onZoom,
4938
+ releaseUserPanHold
4939
+ };
4940
+ }
4230
4941
  ;// ./src/lib/viewers/media/waveform/WaveformView.scss
4231
4942
  // extracted by mini-css-extract-plugin
4232
4943
 
4233
4944
  ;// ./src/lib/viewers/media/waveform/WaveformView.tsx
4945
+ function WaveformView_ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
4946
+ function WaveformView_objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? WaveformView_ownKeys(Object(t), !0).forEach(function (r) { WaveformView_defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : WaveformView_ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
4947
+ function WaveformView_defineProperty(e, r, t) { return (r = WaveformView_toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
4948
+ function WaveformView_toPropertyKey(t) { var i = WaveformView_toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
4949
+ function WaveformView_toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
4234
4950
 
4235
4951
 
4236
4952
 
4237
4953
 
4238
4954
 
4239
- const WAVEFORM_BAR_GAP = 2;
4240
- const WAVEFORM_BAR_WIDTH = 2;
4241
- const WAVEFORM_BAR_RADIUS = WAVEFORM_BAR_WIDTH / 2;
4242
- /** Total bar height so the top and bottom radii meet as a circle on the mirror. */
4243
- const WAVEFORM_BAR_MIN_HEIGHT = WAVEFORM_BAR_WIDTH;
4244
- const WAVEFORM_HEIGHT = 140;
4245
- function leftPercent(timeSec, durationSec) {
4246
- const progress = durationSec > 0 ? Math.min(1, Math.max(0, timeSec / durationSec)) : 0;
4247
- return `${progress * 100}%`;
4248
- }
4249
- function fillWidth(widthCssPx) {
4955
+
4956
+
4957
+
4958
+
4959
+
4960
+ /** Pointer X in the view and the media time under it; zoom keeps this point fixed. */
4961
+
4962
+ /** CSS width × devicePixelRatio, for canvas gradient fills. */
4963
+ function devicePixelWidth(widthCssPx) {
4250
4964
  const pixelRatio = typeof window !== 'undefined' ? window.devicePixelRatio || 1 : 1;
4251
4965
  return widthCssPx * pixelRatio;
4252
4966
  }
4967
+ function WaveformView_prefersReducedMotion() {
4968
+ return typeof window !== 'undefined' && typeof window.matchMedia === 'function' && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
4969
+ }
4970
+ function WaveformView_getScrollLeft(wavesurfer, fallback = 0) {
4971
+ return wavesurfer && wavesurfer.getScroll ? wavesurfer.getScroll() : fallback;
4972
+ }
4253
4973
  function applyPeaks(wavesurfer, peaks, durationSec) {
4254
4974
  if (!wavesurfer.load) {
4255
4975
  return;
@@ -4257,6 +4977,44 @@ function applyPeaks(wavesurfer, peaks, durationSec) {
4257
4977
  wavesurfer.load('', toChannels(peaks), durationSec);
4258
4978
  }
4259
4979
 
4980
+ /** Tint WaveSurfer's zoomed tiles with played/unplayed/hover/buffer colors. */
4981
+ function tintZoomedWaveform(wavesurfer, fills, replaceSnapshot = false) {
4982
+ const wrapper = wavesurfer.getWrapper ? wavesurfer.getWrapper() : null;
4983
+ if (!wrapper || !(wrapper.clientWidth > 0)) {
4984
+ return;
4985
+ }
4986
+ tintWaveformTiles({
4987
+ fills,
4988
+ host: wrapper,
4989
+ replaceSnapshot,
4990
+ totalWidthCss: wrapper.clientWidth
4991
+ });
4992
+ }
4993
+ function touchDistance(touches) {
4994
+ if (touches.length < 2) {
4995
+ return 0;
4996
+ }
4997
+ const dx = touches[0].clientX - touches[1].clientX;
4998
+ const dy = touches[0].clientY - touches[1].clientY;
4999
+ return Math.hypot(dx, dy);
5000
+ }
5001
+
5002
+ /** Time under the pointer, plus its X in the view, so zoom can keep that point fixed. */
5003
+ function zoomOriginAtPointer(pointerX, wavesurfer, durationSec, fallbackWidth) {
5004
+ if (!(fallbackWidth > 0) || !(durationSec > 0) || !Number.isFinite(pointerX)) {
5005
+ return null;
5006
+ }
5007
+ const wrapper = wavesurfer && wavesurfer.getWrapper ? wavesurfer.getWrapper() : null;
5008
+ const fullWidth = wrapper && wrapper.clientWidth ? wrapper.clientWidth : fallbackWidth;
5009
+ if (!(fullWidth > 0)) {
5010
+ return null;
5011
+ }
5012
+ return {
5013
+ pointerX,
5014
+ timeSec: (WaveformView_getScrollLeft(wavesurfer) + pointerX) / fullWidth * durationSec
5015
+ };
5016
+ }
5017
+
4260
5018
  /**
4261
5019
  * Renders V1 peaks with wavesurfer. Does not fetch audio or attach a media element.
4262
5020
  */
@@ -4264,42 +5022,144 @@ function WaveformView({
4264
5022
  bufferedRange,
4265
5023
  currentTime = 0,
4266
5024
  durationSec,
4267
- height = WAVEFORM_HEIGHT,
5025
+ height = constants/* WAVEFORM_HEIGHT */.oN,
4268
5026
  interactive = true,
4269
5027
  mediaEl,
4270
5028
  onSeek,
4271
- peaks
5029
+ onViewportChange,
5030
+ onZoomChange,
5031
+ peaks,
5032
+ zoomLevel: zoomLevelProp
4272
5033
  }) {
5034
+ // DOM / WaveSurfer
4273
5035
  const containerRef = (0,external_react_.useRef)(null);
5036
+ const trackRef = (0,external_react_.useRef)(null);
4274
5037
  const playheadRef = (0,external_react_.useRef)(null);
4275
- const playheadRafRef = (0,external_react_.useRef)(0);
5038
+ const playheadAnimationRef = (0,external_react_.useRef)(0);
4276
5039
  const wavesurferRef = (0,external_react_.useRef)(null);
5040
+
5041
+ // Latest props for WaveSurfer + media listeners that must not re-subscribe each render.
4277
5042
  const onSeekRef = (0,external_react_.useRef)(onSeek);
4278
5043
  const interactiveRef = (0,external_react_.useRef)(interactive);
5044
+ const hasZoomHandlersRef = (0,external_react_.useRef)(false);
4279
5045
  const currentTimeRef = (0,external_react_.useRef)(currentTime);
4280
5046
  const peaksRef = (0,external_react_.useRef)(peaks);
4281
- const displayedPeaksRef = (0,external_react_.useRef)(null);
4282
- const peakTransitionRafRef = (0,external_react_.useRef)(0);
4283
5047
  const durationSecRef = (0,external_react_.useRef)(durationSec);
5048
+ const mediaElRef = (0,external_react_.useRef)(mediaEl);
5049
+ const onViewportChangeRef = (0,external_react_.useRef)(onViewportChange);
5050
+ const displayedPeaksRef = (0,external_react_.useRef)(null);
5051
+ const peakTransitionAnimationRef = (0,external_react_.useRef)(0);
5052
+ // Zoom / pinch / tile tint (read from pointer + WaveSurfer handlers)
5053
+ const zoomOriginRef = (0,external_react_.useRef)(null);
5054
+ const pinchStartRef = (0,external_react_.useRef)(null);
5055
+ const pointerZoomRef = (0,external_react_.useRef)(false);
5056
+ const pointerZoomClearTimerRef = (0,external_react_.useRef)(0);
5057
+ const bufferProgressRef = (0,external_react_.useRef)(0);
5058
+ const hoverProgressRef = (0,external_react_.useRef)(null);
4284
5059
  onSeekRef.current = onSeek;
4285
5060
  interactiveRef.current = interactive;
4286
5061
  currentTimeRef.current = currentTime;
4287
5062
  peaksRef.current = peaks;
4288
5063
  durationSecRef.current = durationSec;
5064
+ mediaElRef.current = mediaEl;
5065
+ onViewportChangeRef.current = onViewportChange;
5066
+ const [internalZoom, setInternalZoom] = (0,external_react_.useState)(constants/* WAVEFORM_ZOOM_MIN */.LW);
4289
5067
  const [hoverProgress, setHoverProgress] = (0,external_react_.useState)(null);
4290
5068
  const [canvasWidthPx, setCanvasWidthPx] = (0,external_react_.useState)(0);
5069
+ const [scrollLeft, setScrollLeft] = (0,external_react_.useState)(0);
5070
+ const isControlled = typeof zoomLevelProp === 'number';
5071
+ const maxZoom = getWaveformZoomMax({
5072
+ durationSec,
5073
+ peakCount: peaks.length,
5074
+ viewWidthPx: canvasWidthPx
5075
+ });
5076
+ hasZoomHandlersRef.current = maxZoom > constants/* WAVEFORM_ZOOM_MIN */.LW && (typeof onZoomChange === 'function' || typeof zoomLevelProp !== 'number');
5077
+ const zoomLevel = clampWaveformZoom(isControlled ? zoomLevelProp : internalZoom, maxZoom);
5078
+ const zoomRef = (0,external_react_.useRef)(zoomLevel); // latest zoom for WaveSurfer redraw/zoom handlers
5079
+ zoomRef.current = zoomLevel;
5080
+ const prevZoomRef = (0,external_react_.useRef)(null);
5081
+ const isZoomed = zoomLevel > constants/* WAVEFORM_ZOOM_MIN */.LW;
4291
5082
  const bufferProgress = getBufferedProgress(bufferedRange, durationSec);
5083
+ bufferProgressRef.current = bufferProgress;
5084
+ hoverProgressRef.current = hoverProgress;
5085
+ const viewport = (0,external_react_.useMemo)(() => createWaveformViewport({
5086
+ durationSec,
5087
+ heightPx: height,
5088
+ maxZoom,
5089
+ scrollLeftPx: scrollLeft,
5090
+ widthPx: canvasWidthPx,
5091
+ zoomLevel
5092
+ }), [canvasWidthPx, durationSec, height, maxZoom, scrollLeft, zoomLevel]);
5093
+ const viewportRef = (0,external_react_.useRef)(viewport); // live scroll window; prefer this over render-state while the camera is moving
5094
+ const onViewportCommit = (0,external_react_.useCallback)((scrollLeftPx, nextViewport) => {
5095
+ onViewportChangeRef.current?.(nextViewport);
5096
+ setScrollLeft(scrollLeftPx);
5097
+ }, []);
5098
+ const {
5099
+ apply: applyPlayheadCamera,
5100
+ applyScrollLeft,
5101
+ cancelJump,
5102
+ clearFollowPin,
5103
+ handleScroll: handleCameraScroll,
5104
+ isFollowPinned,
5105
+ onSeek: onPlayheadSeek,
5106
+ onZoom,
5107
+ releaseUserPanHold
5108
+ } = usePlayheadCamera({
5109
+ mediaElRef,
5110
+ onViewportCommit,
5111
+ playheadRef,
5112
+ viewportRef,
5113
+ wavesurferRef
5114
+ });
5115
+ (0,external_react_.useLayoutEffect)(() => {
5116
+ viewportRef.current = createWaveformViewport({
5117
+ durationSec: viewport.durationSec,
5118
+ heightPx: viewport.heightPx,
5119
+ maxZoom: viewport.maxZoom,
5120
+ scrollLeftPx: viewportRef.current.scrollLeftPx,
5121
+ widthPx: viewport.widthPx,
5122
+ zoomLevel: viewport.zoomLevel
5123
+ });
5124
+ }, [viewport]);
5125
+ const setZoomLevel = (0,external_react_.useCallback)(nextZoom => {
5126
+ const zoom = clampWaveformZoom(nextZoom, maxZoom);
5127
+ if (!isControlled) {
5128
+ setInternalZoom(zoom);
5129
+ }
5130
+ onZoomChange?.(zoom);
5131
+ }, [isControlled, maxZoom, onZoomChange]);
5132
+ const markPointerZoom = (0,external_react_.useCallback)(() => {
5133
+ pointerZoomRef.current = true;
5134
+ window.clearTimeout(pointerZoomClearTimerRef.current);
5135
+ pointerZoomClearTimerRef.current = window.setTimeout(() => {
5136
+ pointerZoomRef.current = false;
5137
+ pointerZoomClearTimerRef.current = 0;
5138
+ }, constants/* WAVEFORM_ZOOM_DISMISS_MS */.m6);
5139
+ }, []);
5140
+ const syncViewport = (0,external_react_.useCallback)(() => {
5141
+ const wavesurfer = wavesurferRef.current;
5142
+ if (!wavesurfer) {
5143
+ return;
5144
+ }
5145
+ const scrollLeftPx = WaveformView_getScrollLeft(wavesurfer);
5146
+ viewportRef.current = getViewportAtScroll(viewportRef.current, scrollLeftPx);
5147
+ onViewportChangeRef.current?.(viewportRef.current);
5148
+ setScrollLeft(scrollLeftPx);
5149
+ }, []);
4292
5150
  const updatePlayheadPosition = (0,external_react_.useCallback)(timeSec => {
4293
5151
  const playhead = playheadRef.current;
4294
5152
  if (!playhead) {
4295
5153
  return;
4296
5154
  }
4297
- playhead.style.left = leftPercent(timeSec, durationSecRef.current);
5155
+ if (!isFollowPinned()) {
5156
+ playhead.style.left = timeLeftPercent(timeSec, durationSecRef.current, viewportRef.current);
5157
+ }
4298
5158
  const wavesurfer = wavesurferRef.current;
4299
5159
  if (wavesurfer && wavesurfer.setTime) {
4300
5160
  wavesurfer.setTime(timeSec);
4301
5161
  }
4302
- }, []);
5162
+ }, [isFollowPinned]);
4303
5163
  (0,external_react_.useEffect)(() => {
4304
5164
  const container = containerRef.current;
4305
5165
  if (!container) {
@@ -4307,10 +5167,10 @@ function WaveformView({
4307
5167
  }
4308
5168
  const wavesurfer = w.create({
4309
5169
  autoScroll: false,
4310
- barGap: WAVEFORM_BAR_GAP,
4311
- barMinHeight: WAVEFORM_BAR_MIN_HEIGHT,
4312
- barRadius: WAVEFORM_BAR_RADIUS,
4313
- barWidth: WAVEFORM_BAR_WIDTH,
5170
+ barGap: constants/* WAVEFORM_BAR_GAP */.Lu,
5171
+ barMinHeight: constants/* WAVEFORM_BAR_MIN_HEIGHT */.XS,
5172
+ barRadius: constants/* WAVEFORM_BAR_RADIUS */.DY,
5173
+ barWidth: constants/* WAVEFORM_BAR_WIDTH */.zo,
4314
5174
  container,
4315
5175
  cursorWidth: 0,
4316
5176
  duration: durationSecRef.current,
@@ -4329,16 +5189,39 @@ function WaveformView({
4329
5189
  }
4330
5190
  onSeekRef.current?.(relativeX * durationSecRef.current);
4331
5191
  });
5192
+ const unsubscribeScroll = wavesurfer.on('scroll', () => {
5193
+ handleCameraScroll(() => {
5194
+ syncViewport();
5195
+ });
5196
+ });
5197
+ const unsubscribeZoom = wavesurfer.on('zoom', () => {
5198
+ syncViewport();
5199
+ });
5200
+ const unsubscribeRedraw = wavesurfer.on('redrawcomplete', () => {
5201
+ if (!(zoomRef.current > constants/* WAVEFORM_ZOOM_MIN */.LW)) {
5202
+ return;
5203
+ }
5204
+ tintZoomedWaveform(wavesurfer, getWaveformFills({
5205
+ bufferProgress: bufferProgressRef.current,
5206
+ hoverProgress: hoverProgressRef.current
5207
+ }), true);
5208
+ });
4332
5209
  wavesurferRef.current = wavesurfer;
4333
5210
  displayedPeaksRef.current = peaksRef.current;
5211
+ syncViewport();
4334
5212
  return () => {
4335
- window.cancelAnimationFrame(peakTransitionRafRef.current);
5213
+ releaseUserPanHold();
5214
+ cancelJump();
5215
+ window.cancelAnimationFrame(peakTransitionAnimationRef.current);
4336
5216
  unsubscribeClick();
5217
+ unsubscribeScroll();
5218
+ unsubscribeZoom();
5219
+ unsubscribeRedraw();
4337
5220
  wavesurfer.destroy();
4338
5221
  wavesurferRef.current = null;
4339
5222
  displayedPeaksRef.current = null;
4340
5223
  };
4341
- }, [height]);
5224
+ }, [cancelJump, handleCameraScroll, height, releaseUserPanHold, syncViewport]);
4342
5225
  (0,external_react_.useLayoutEffect)(() => {
4343
5226
  const el = containerRef.current;
4344
5227
  if (!el) {
@@ -4349,10 +5232,24 @@ function WaveformView({
4349
5232
  entries.forEach(entry => {
4350
5233
  setCanvasWidthPx(entry.contentRect.width);
4351
5234
  });
5235
+ syncViewport();
4352
5236
  });
4353
5237
  observer.observe(el);
4354
5238
  return () => observer.disconnect();
4355
- }, []);
5239
+ }, [syncViewport]);
5240
+ (0,external_react_.useEffect)(() => {
5241
+ const rawZoom = isControlled ? zoomLevelProp : internalZoom;
5242
+ const clamped = clampWaveformZoom(typeof rawZoom === 'number' ? rawZoom : constants/* WAVEFORM_ZOOM_MIN */.LW, maxZoom);
5243
+ if (clamped === rawZoom) {
5244
+ return;
5245
+ }
5246
+ if (!isControlled) {
5247
+ setInternalZoom(clamped);
5248
+ }
5249
+ }, [internalZoom, isControlled, maxZoom, zoomLevelProp]);
5250
+ (0,external_react_.useEffect)(() => {
5251
+ onViewportChange?.(viewport);
5252
+ }, [onViewportChange, viewport]);
4356
5253
  (0,external_react_.useEffect)(() => {
4357
5254
  const wavesurfer = wavesurferRef.current;
4358
5255
  if (!wavesurfer || !wavesurfer.setOptions) {
@@ -4362,13 +5259,55 @@ function WaveformView({
4362
5259
  interact: interactive
4363
5260
  });
4364
5261
  }, [interactive]);
4365
- (0,external_react_.useLayoutEffect)(() => {
4366
- if (mediaEl && !mediaEl.paused) {
4367
- updatePlayheadPosition(mediaEl.currentTime);
5262
+ (0,external_react_.useEffect)(() => {
5263
+ const wavesurfer = wavesurferRef.current;
5264
+ const container = containerRef.current;
5265
+ if (!wavesurfer || !wavesurfer.setOptions || !container) {
4368
5266
  return;
4369
5267
  }
4370
- updatePlayheadPosition(currentTime);
4371
- }, [currentTime, durationSec, mediaEl, updatePlayheadPosition]);
5268
+ const viewWidthPx = wavesurfer.getWidth ? wavesurfer.getWidth() : container.clientWidth;
5269
+ const minPxPerSec = getZoomedPixelsPerSecond({
5270
+ durationSec,
5271
+ maxZoom,
5272
+ viewWidthPx,
5273
+ zoomLevel
5274
+ });
5275
+ wavesurfer.setOptions(WaveformView_objectSpread({
5276
+ autoScroll: false,
5277
+ minPxPerSec
5278
+ }, zoomLevel > constants/* WAVEFORM_ZOOM_MIN */.LW ? {
5279
+ progressColor: WAVEFORM_COLOR_PLAYED,
5280
+ waveColor: WAVEFORM_COLOR_UNPLAYED
5281
+ } : {}));
5282
+ const origin = zoomOriginRef.current;
5283
+ zoomOriginRef.current = null;
5284
+ const didZoomChange = prevZoomRef.current !== zoomLevel;
5285
+ prevZoomRef.current = zoomLevel;
5286
+ if (origin || didZoomChange) {
5287
+ onZoom();
5288
+ }
5289
+ if (minPxPerSec > 0) {
5290
+ if (origin) {
5291
+ applyScrollLeft(origin.timeSec * minPxPerSec - origin.pointerX, true);
5292
+ } else if (didZoomChange && !pointerZoomRef.current) {
5293
+ const zoomedViewport = createWaveformViewport({
5294
+ durationSec,
5295
+ heightPx: height,
5296
+ maxZoom,
5297
+ scrollLeftPx: 0,
5298
+ widthPx: viewWidthPx,
5299
+ zoomLevel
5300
+ });
5301
+ applyScrollLeft(Math.min(maxScrollLeft(zoomedViewport), Math.max(0, currentTimeRef.current * minPxPerSec - viewWidthPx / 2)), true);
5302
+ }
5303
+ }
5304
+ wavesurfer.setTime(currentTimeRef.current);
5305
+ syncViewport();
5306
+ updatePlayheadPosition(currentTimeRef.current);
5307
+ }, [applyScrollLeft, durationSec, height, maxZoom, onZoom, syncViewport, updatePlayheadPosition, zoomLevel]);
5308
+ (0,external_react_.useLayoutEffect)(() => {
5309
+ updatePlayheadPosition(mediaEl ? mediaEl.currentTime : currentTime);
5310
+ }, [currentTime, durationSec, mediaEl, updatePlayheadPosition, viewport]);
4372
5311
  (0,external_react_.useEffect)(() => {
4373
5312
  const media = mediaEl;
4374
5313
  if (!media) {
@@ -4377,60 +5316,71 @@ function WaveformView({
4377
5316
  const tick = () => {
4378
5317
  if (!media.paused) {
4379
5318
  updatePlayheadPosition(media.currentTime);
5319
+ applyPlayheadCamera(media.currentTime, false);
4380
5320
  }
4381
- playheadRafRef.current = window.requestAnimationFrame(tick);
5321
+ playheadAnimationRef.current = window.requestAnimationFrame(tick);
4382
5322
  };
4383
5323
  const startLoop = () => {
4384
- window.cancelAnimationFrame(playheadRafRef.current);
4385
- playheadRafRef.current = window.requestAnimationFrame(tick);
5324
+ window.cancelAnimationFrame(playheadAnimationRef.current);
5325
+ releaseUserPanHold();
5326
+ applyPlayheadCamera(media.currentTime, true);
5327
+ playheadAnimationRef.current = window.requestAnimationFrame(tick);
4386
5328
  };
4387
5329
  const stopLoop = () => {
4388
- window.cancelAnimationFrame(playheadRafRef.current);
5330
+ window.cancelAnimationFrame(playheadAnimationRef.current);
5331
+ cancelJump();
5332
+ releaseUserPanHold();
5333
+ clearFollowPin();
5334
+ syncViewport();
4389
5335
  updatePlayheadPosition(media.currentTime);
4390
5336
  };
4391
5337
  const handleSeeked = () => {
5338
+ onPlayheadSeek(media.currentTime);
4392
5339
  updatePlayheadPosition(media.currentTime);
4393
5340
  };
4394
5341
  if (!media.paused) {
4395
5342
  startLoop();
4396
5343
  }
4397
5344
  media.addEventListener('play', startLoop);
5345
+ media.addEventListener('playing', startLoop);
4398
5346
  media.addEventListener('pause', stopLoop);
4399
5347
  media.addEventListener('seeked', handleSeeked);
4400
5348
  return () => {
4401
- window.cancelAnimationFrame(playheadRafRef.current);
5349
+ window.cancelAnimationFrame(playheadAnimationRef.current);
5350
+ cancelJump();
5351
+ releaseUserPanHold();
4402
5352
  media.removeEventListener('play', startLoop);
5353
+ media.removeEventListener('playing', startLoop);
4403
5354
  media.removeEventListener('pause', stopLoop);
4404
5355
  media.removeEventListener('seeked', handleSeeked);
4405
5356
  };
4406
- }, [mediaEl, updatePlayheadPosition]);
5357
+ }, [applyPlayheadCamera, cancelJump, clearFollowPin, mediaEl, onPlayheadSeek, releaseUserPanHold, syncViewport, updatePlayheadPosition]);
4407
5358
  (0,external_react_.useEffect)(() => {
4408
5359
  const wavesurfer = wavesurferRef.current;
4409
5360
  if (!wavesurfer || !wavesurfer.setOptions || !(durationSec > 0)) {
4410
5361
  return undefined;
4411
5362
  }
5363
+ window.cancelAnimationFrame(peakTransitionAnimationRef.current);
4412
5364
  const fromPeaks = displayedPeaksRef.current;
4413
- const reduceMotion = typeof window !== 'undefined' && typeof window.matchMedia === 'function' && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
4414
- window.cancelAnimationFrame(peakTransitionRafRef.current);
4415
- if (!fromPeaks || fromPeaks === peaks || reduceMotion) {
5365
+ if (!fromPeaks || fromPeaks === peaks || WaveformView_prefersReducedMotion()) {
4416
5366
  displayedPeaksRef.current = peaks;
4417
5367
  applyPeaks(wavesurfer, peaks, durationSec);
4418
5368
  return undefined;
4419
5369
  }
4420
- const start = typeof performance !== 'undefined' ? performance.now() : Date.now();
5370
+ const start = (0,util/* getCurrentTimeMs */.RU)();
4421
5371
  const tick = now => {
4422
5372
  const elapsedMs = now - start;
4423
5373
  const framePeaks = morphPeaks(fromPeaks, peaks, elapsedMs);
4424
5374
  displayedPeaksRef.current = framePeaks;
4425
5375
  applyPeaks(wavesurfer, framePeaks, durationSec);
4426
5376
  if (elapsedMs < WAVEFORM_PEAK_TRANSITION_MS) {
4427
- peakTransitionRafRef.current = window.requestAnimationFrame(tick);
5377
+ peakTransitionAnimationRef.current = window.requestAnimationFrame(tick);
4428
5378
  } else {
4429
5379
  displayedPeaksRef.current = peaks;
4430
5380
  }
4431
5381
  };
4432
- peakTransitionRafRef.current = window.requestAnimationFrame(tick);
4433
- return () => window.cancelAnimationFrame(peakTransitionRafRef.current);
5382
+ peakTransitionAnimationRef.current = window.requestAnimationFrame(tick);
5383
+ return () => window.cancelAnimationFrame(peakTransitionAnimationRef.current);
4434
5384
  }, [durationSec, peaks]);
4435
5385
  (0,external_react_.useEffect)(() => {
4436
5386
  const wavesurfer = wavesurferRef.current;
@@ -4441,12 +5391,95 @@ function WaveformView({
4441
5391
  bufferProgress,
4442
5392
  hoverProgress
4443
5393
  });
5394
+ if (isZoomed) {
5395
+ tintZoomedWaveform(wavesurfer, fills);
5396
+ return;
5397
+ }
4444
5398
  wavesurfer.setOptions({
4445
- progressColor: toCanvasFill(fills.progressColor, fillWidth(canvasWidthPx)),
4446
- waveColor: toCanvasFill(fills.waveColor, fillWidth(canvasWidthPx))
5399
+ progressColor: toCanvasFill(fills.progressColor, devicePixelWidth(canvasWidthPx)),
5400
+ waveColor: toCanvasFill(fills.waveColor, devicePixelWidth(canvasWidthPx))
4447
5401
  });
4448
5402
  wavesurfer.setTime(currentTimeRef.current);
4449
- }, [bufferProgress, canvasWidthPx, hoverProgress]);
5403
+ }, [bufferProgress, canvasWidthPx, hoverProgress, isZoomed]);
5404
+ (0,external_react_.useEffect)(() => {
5405
+ const track = trackRef.current;
5406
+ if (!track) {
5407
+ return undefined;
5408
+ }
5409
+
5410
+ /** Remember the time under this pointer so pinch/wheel zoom stays anchored. */
5411
+ const captureZoomOrigin = clientX => {
5412
+ const rect = track.getBoundingClientRect();
5413
+ zoomOriginRef.current = zoomOriginAtPointer(clientX - rect.left, wavesurferRef.current, durationSec, rect.width);
5414
+ markPointerZoom();
5415
+ };
5416
+
5417
+ /** Zoom origin at the midpoint of a two-finger pinch. */
5418
+ const zoomOriginFromPinch = touches => {
5419
+ if (touches.length < 2) {
5420
+ return null;
5421
+ }
5422
+ const rect = track.getBoundingClientRect();
5423
+ const pointerX = (touches[0].clientX + touches[1].clientX) / 2 - rect.left;
5424
+ return zoomOriginAtPointer(pointerX, wavesurferRef.current, durationSec, rect.width);
5425
+ };
5426
+
5427
+ /** Ctrl/meta + wheel zooms around the pointer. */
5428
+ const onZoomWheel = event => {
5429
+ if (!interactiveRef.current || !hasZoomHandlersRef.current || !event.ctrlKey && !event.metaKey) {
5430
+ return;
5431
+ }
5432
+ event.preventDefault();
5433
+ captureZoomOrigin(event.clientX);
5434
+ setZoomLevel(zoomRef.current * Math.exp(-event.deltaY * 0.01));
5435
+ };
5436
+ const onTouchStart = event => {
5437
+ if (!interactiveRef.current || !hasZoomHandlersRef.current || event.touches.length !== 2) {
5438
+ pinchStartRef.current = null;
5439
+ return;
5440
+ }
5441
+ zoomOriginRef.current = zoomOriginFromPinch(event.touches);
5442
+ pinchStartRef.current = {
5443
+ distance: touchDistance(event.touches),
5444
+ zoom: zoomRef.current
5445
+ };
5446
+ markPointerZoom();
5447
+ };
5448
+ const onTouchMove = event => {
5449
+ const pinch = pinchStartRef.current;
5450
+ if (!interactiveRef.current || !hasZoomHandlersRef.current || !pinch || event.touches.length !== 2 || !(pinch.distance > 0)) {
5451
+ return;
5452
+ }
5453
+ event.preventDefault();
5454
+ zoomOriginRef.current = zoomOriginFromPinch(event.touches);
5455
+ markPointerZoom();
5456
+ setZoomLevel(pinch.zoom * (touchDistance(event.touches) / pinch.distance));
5457
+ };
5458
+ const onTouchEnd = event => {
5459
+ if (event.touches.length < 2) {
5460
+ pinchStartRef.current = null;
5461
+ }
5462
+ };
5463
+ track.addEventListener('wheel', onZoomWheel, {
5464
+ passive: false
5465
+ });
5466
+ track.addEventListener('touchstart', onTouchStart, {
5467
+ passive: true
5468
+ });
5469
+ track.addEventListener('touchmove', onTouchMove, {
5470
+ passive: false
5471
+ });
5472
+ track.addEventListener('touchend', onTouchEnd);
5473
+ track.addEventListener('touchcancel', onTouchEnd);
5474
+ return () => {
5475
+ window.clearTimeout(pointerZoomClearTimerRef.current);
5476
+ track.removeEventListener('wheel', onZoomWheel);
5477
+ track.removeEventListener('touchstart', onTouchStart);
5478
+ track.removeEventListener('touchmove', onTouchMove);
5479
+ track.removeEventListener('touchend', onTouchEnd);
5480
+ track.removeEventListener('touchcancel', onTouchEnd);
5481
+ };
5482
+ }, [durationSec, markPointerZoom, setZoomLevel]);
4450
5483
  const onHoverMove = (0,external_react_.useCallback)(event => {
4451
5484
  if (!interactive) {
4452
5485
  return;
@@ -4455,20 +5488,26 @@ function WaveformView({
4455
5488
  if (!(rect.width > 0) || !(durationSec > 0)) {
4456
5489
  return;
4457
5490
  }
4458
- const x = event.clientX - rect.left;
4459
- if (!Number.isFinite(x)) {
5491
+ const pointerX = event.clientX - rect.left;
5492
+ if (!Number.isFinite(pointerX)) {
5493
+ return;
5494
+ }
5495
+ const vp = viewportRef.current;
5496
+ if (vp.pixelsPerSecond > 0) {
5497
+ setHoverProgress(Math.min(1, Math.max(0, timeFromPositionPx(pointerX, vp) / durationSec)));
4460
5498
  return;
4461
5499
  }
4462
- setHoverProgress(Math.min(1, Math.max(0, x / rect.width)));
5500
+ setHoverProgress(Math.min(1, Math.max(0, pointerX / rect.width)));
4463
5501
  }, [durationSec, interactive]);
4464
5502
  const onHoverLeave = (0,external_react_.useCallback)(() => {
4465
5503
  setHoverProgress(null);
4466
5504
  }, []);
4467
- const hoverLeft = hoverProgress == null ? null : `${hoverProgress * 100}%`;
5505
+ const hoverLeft = hoverProgress == null ? null : timeLeftPercent(hoverProgress * durationSec, durationSec, viewportRef.current);
4468
5506
  return /*#__PURE__*/external_react_["default"].createElement("div", {
4469
- className: `bp-WaveformView${interactive ? '' : ' bp-WaveformView--inert'}`,
5507
+ className: `bp-WaveformView${interactive ? '' : ' bp-WaveformView--inert'}${isZoomed ? ' bp-WaveformView--zoomed' : ''}`,
4470
5508
  "data-testid": "bp-waveform-view"
4471
5509
  }, /*#__PURE__*/external_react_["default"].createElement("div", {
5510
+ ref: trackRef,
4472
5511
  className: "bp-WaveformView-track",
4473
5512
  onMouseLeave: interactive ? onHoverLeave : undefined,
4474
5513
  onMouseMove: interactive ? onHoverMove : undefined
@@ -4491,6 +5530,139 @@ function WaveformView({
4491
5530
  "data-testid": "bp-waveform-hover-time"
4492
5531
  }, formatTime(hoverProgress * durationSec)))));
4493
5532
  }
5533
+ // EXTERNAL MODULE: ./node_modules/classnames/index.js
5534
+ var classnames = __webpack_require__(2485);
5535
+ var classnames_default = /*#__PURE__*/__webpack_require__.n(classnames);
5536
+ ;// ./src/lib/viewers/controls/icons/IconZoom24.tsx
5537
+
5538
+ function IconZoom24() {
5539
+ return /*#__PURE__*/external_react_.createElement("svg", {
5540
+ focusable: false,
5541
+ height: 24,
5542
+ viewBox: "0 0 24 24",
5543
+ width: 24
5544
+ }, /*#__PURE__*/external_react_.createElement("path", {
5545
+ d: "M2 12h20M6 8l-4 4 4 4M18 8l4 4-4 4",
5546
+ fill: "none",
5547
+ stroke: "currentColor",
5548
+ strokeLinecap: "round",
5549
+ strokeLinejoin: "round",
5550
+ strokeWidth: "2"
5551
+ }));
5552
+ }
5553
+ /* harmony default export */ const icons_IconZoom24 = (IconZoom24);
5554
+ // EXTERNAL MODULE: ./src/lib/viewers/controls/media/MediaToggle.tsx
5555
+ var MediaToggle = __webpack_require__(8997);
5556
+ // EXTERNAL MODULE: ./src/lib/viewers/controls/slider/SliderControl.tsx + 1 modules
5557
+ var SliderControl = __webpack_require__(4937);
5558
+ ;// ./src/lib/viewers/media/waveform/WaveformZoomControl.scss
5559
+ // extracted by mini-css-extract-plugin
5560
+
5561
+ ;// ./src/lib/viewers/media/waveform/WaveformZoomControl.tsx
5562
+
5563
+
5564
+
5565
+
5566
+
5567
+
5568
+
5569
+
5570
+ function WaveformZoomControl({
5571
+ isRevealed = false,
5572
+ maxZoom,
5573
+ onZoomChange,
5574
+ zoomLevel
5575
+ }) {
5576
+ const [isHovered, setHovered] = (0,external_react_.useState)(false);
5577
+ const [isFocused, setFocused] = (0,external_react_.useState)(false);
5578
+ const dismissTimerRef = (0,external_react_.useRef)(0);
5579
+ const flyoutRef = (0,external_react_.useRef)(null);
5580
+ const shouldFocusSliderRef = (0,external_react_.useRef)(false);
5581
+ const sliderId = `bp-waveform-zoom-slider${(0,external_react_.useId)()}`;
5582
+ const zoom = clampWaveformZoom(zoomLevel, maxZoom);
5583
+ const zoomValue = Math.round(sliderValueFromZoom(zoom, maxZoom));
5584
+ const isOpen = isHovered || isFocused || isRevealed;
5585
+ const clearDismiss = (0,external_react_.useCallback)(() => {
5586
+ window.clearTimeout(dismissTimerRef.current);
5587
+ dismissTimerRef.current = 0;
5588
+ }, []);
5589
+ (0,external_react_.useEffect)(() => () => window.clearTimeout(dismissTimerRef.current), []);
5590
+ (0,external_react_.useLayoutEffect)(() => {
5591
+ if (!isOpen || !shouldFocusSliderRef.current) {
5592
+ return;
5593
+ }
5594
+ shouldFocusSliderRef.current = false;
5595
+ flyoutRef.current?.querySelector('[role="slider"]')?.focus();
5596
+ }, [isOpen]);
5597
+ const handleSlider = (0,external_react_.useCallback)(newValue => {
5598
+ onZoomChange(zoomFromSliderValue(newValue, maxZoom));
5599
+ }, [maxZoom, onZoomChange]);
5600
+ const handleToggleClick = (0,external_react_.useCallback)(() => {
5601
+ clearDismiss();
5602
+ if (isOpen) {
5603
+ shouldFocusSliderRef.current = false;
5604
+ setFocused(false);
5605
+ setHovered(false);
5606
+ return;
5607
+ }
5608
+ shouldFocusSliderRef.current = true;
5609
+ setFocused(true);
5610
+ }, [clearDismiss, isOpen]);
5611
+ return /*#__PURE__*/external_react_["default"].createElement("div", {
5612
+ className: classnames_default()('bp-WaveformZoomControl', {
5613
+ 'bp-is-open': isOpen
5614
+ }),
5615
+ "data-testid": "bp-waveform-zoom",
5616
+ onBlur: event => {
5617
+ if (event.currentTarget.contains(event.relatedTarget)) {
5618
+ return;
5619
+ }
5620
+ setFocused(false);
5621
+ },
5622
+ onFocus: () => {
5623
+ clearDismiss();
5624
+ setFocused(true);
5625
+ },
5626
+ onMouseEnter: () => {
5627
+ clearDismiss();
5628
+ setHovered(true);
5629
+ },
5630
+ onMouseLeave: () => {
5631
+ clearDismiss();
5632
+ dismissTimerRef.current = window.setTimeout(() => {
5633
+ setHovered(false);
5634
+ }, constants/* WAVEFORM_ZOOM_DISMISS_MS */.m6);
5635
+ }
5636
+ }, /*#__PURE__*/external_react_["default"].createElement("div", {
5637
+ ref: flyoutRef,
5638
+ "aria-hidden": !isOpen,
5639
+ className: classnames_default()('bp-WaveformZoomControl-flyout', {
5640
+ 'bp-is-open': isOpen
5641
+ })
5642
+ }, /*#__PURE__*/external_react_["default"].createElement(SliderControl/* default */.A, {
5643
+ "aria-hidden": !isOpen,
5644
+ className: "bp-WaveformZoomControl-slider",
5645
+ "data-resin-target": "waveformZoomSlider",
5646
+ id: sliderId,
5647
+ max: constants/* WAVEFORM_ZOOM_SLIDER_MAX */.nh,
5648
+ min: 0,
5649
+ onUpdate: handleSlider,
5650
+ step: 1,
5651
+ style: {
5652
+ '--bp-zoom-t': zoomValue / constants/* WAVEFORM_ZOOM_SLIDER_MAX */.nh
5653
+ },
5654
+ tabIndex: isOpen ? 0 : -1,
5655
+ title: "Zoom Slider",
5656
+ value: zoomValue
5657
+ })), /*#__PURE__*/external_react_["default"].createElement(MediaToggle/* default */.A, {
5658
+ "aria-controls": sliderId,
5659
+ "aria-expanded": isOpen,
5660
+ className: "bp-WaveformZoomControl-toggle",
5661
+ "data-resin-target": "waveformZoom",
5662
+ onClick: handleToggleClick,
5663
+ title: "Zoom"
5664
+ }, /*#__PURE__*/external_react_["default"].createElement(icons_IconZoom24, null)));
5665
+ }
4494
5666
  ;// ./src/lib/viewers/media/MP3ControlsV2.scss
4495
5667
  // extracted by mini-css-extract-plugin
4496
5668
 
@@ -4505,6 +5677,9 @@ function WaveformView({
4505
5677
 
4506
5678
 
4507
5679
 
5680
+
5681
+
5682
+
4508
5683
  const PLACEHOLDER_PEAKS = placeholderPeaks();
4509
5684
  function MP3ControlsV2({
4510
5685
  autoplay,
@@ -4524,11 +5699,34 @@ function MP3ControlsV2({
4524
5699
  volume
4525
5700
  }) {
4526
5701
  const durationValue = typeof durationTime === 'number' && isFinite_default()(durationTime) ? durationTime : 0;
5702
+ const [zoomLevel, setZoomLevel] = (0,external_react_.useState)(constants/* WAVEFORM_ZOOM_MIN */.LW);
5703
+ const [maxZoom, setMaxZoom] = (0,external_react_.useState)(constants/* WAVEFORM_ZOOM_MIN */.LW);
5704
+ const [isZoomRevealed, setIsZoomRevealed] = (0,external_react_.useState)(false);
5705
+ const zoomRevealTimerRef = (0,external_react_.useRef)(0);
4527
5706
  const hasRealPeaks = !!(peaks && peaks.length);
4528
5707
  const waveformPeaks = hasRealPeaks ? peaks : PLACEHOLDER_PEAKS;
4529
5708
  const hasMetadata = durationValue > 0;
4530
5709
  const waveformDurationSec = hasMetadata ? durationValue : PLACEHOLDER_DURATION_SEC;
4531
5710
  const [playRequested, setPlayRequested] = (0,external_react_.useState)(false);
5711
+ const handleViewportChange = (0,external_react_.useCallback)(viewport => {
5712
+ setMaxZoom(viewport.maxZoom);
5713
+ }, []);
5714
+ const revealZoomControl = (0,external_react_.useCallback)(() => {
5715
+ setIsZoomRevealed(true);
5716
+ window.clearTimeout(zoomRevealTimerRef.current);
5717
+ zoomRevealTimerRef.current = window.setTimeout(() => {
5718
+ setIsZoomRevealed(false);
5719
+ zoomRevealTimerRef.current = 0;
5720
+ }, constants/* WAVEFORM_ZOOM_DISMISS_MS */.m6);
5721
+ }, []);
5722
+ const handleWaveformZoom = (0,external_react_.useCallback)(nextZoom => {
5723
+ setZoomLevel(nextZoom);
5724
+ revealZoomControl();
5725
+ }, [revealZoomControl]);
5726
+ (0,external_react_.useEffect)(() => {
5727
+ setZoomLevel(prev => clampWaveformZoom(prev, maxZoom));
5728
+ }, [maxZoom]);
5729
+ (0,external_react_.useEffect)(() => () => window.clearTimeout(zoomRevealTimerRef.current), []);
4532
5730
  (0,external_react_.useEffect)(() => {
4533
5731
  if (isPlaying) {
4534
5732
  setPlayRequested(true);
@@ -4541,6 +5739,8 @@ function MP3ControlsV2({
4541
5739
  const isWaveformInteractive = playRequested && hasMetadata;
4542
5740
  const isWaitingToPlay = playRequested && !hasMetadata;
4543
5741
  const showPlayOverlay = !playRequested && !isPlaying;
5742
+ const hasZoomHandlers = hasRealPeaks && !showPlayOverlay;
5743
+ const hasZoomControl = hasZoomHandlers && hasMetadata && maxZoom > constants/* WAVEFORM_ZOOM_MIN */.LW;
4544
5744
  return /*#__PURE__*/external_react_["default"].createElement("div", {
4545
5745
  className: "bp-MP3ControlsV2",
4546
5746
  "data-testid": "media-controls-wrapper-v2"
@@ -4553,8 +5753,18 @@ function MP3ControlsV2({
4553
5753
  interactive: isWaveformInteractive,
4554
5754
  mediaEl: mediaEl,
4555
5755
  onSeek: isWaveformInteractive ? onTimeChange : undefined,
4556
- peaks: waveformPeaks
4557
- }), showPlayOverlay && /*#__PURE__*/external_react_["default"].createElement("button", {
5756
+ onViewportChange: hasRealPeaks ? handleViewportChange : undefined,
5757
+ onZoomChange: hasZoomHandlers ? handleWaveformZoom : undefined,
5758
+ peaks: waveformPeaks,
5759
+ zoomLevel: hasZoomHandlers ? zoomLevel : constants/* WAVEFORM_ZOOM_MIN */.LW
5760
+ }), hasZoomControl && /*#__PURE__*/external_react_["default"].createElement("div", {
5761
+ className: "bp-MP3ControlsV2-waveformZoom"
5762
+ }, /*#__PURE__*/external_react_["default"].createElement(WaveformZoomControl, {
5763
+ isRevealed: isZoomRevealed,
5764
+ maxZoom: maxZoom,
5765
+ onZoomChange: setZoomLevel,
5766
+ zoomLevel: zoomLevel
5767
+ })), showPlayOverlay && /*#__PURE__*/external_react_["default"].createElement("button", {
4558
5768
  className: "bp-MP3ControlsV2-playOverlay"
4559
5769
  // Static SVG from the icons module, same asset video uses for the overlay.
4560
5770
  // eslint-disable-next-line react/no-danger
@@ -4667,22 +5877,33 @@ function isFpsAvailable(player) {
4667
5877
 
4668
5878
  /***/ },
4669
5879
 
4670
- /***/ 9514
5880
+ /***/ 4929
4671
5881
  (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
4672
5882
 
4673
-
4674
- // EXPORTS
4675
- __webpack_require__.d(__webpack_exports__, {
4676
- decodeToPeaks: () => (/* binding */ decodeToPeaks),
4677
- extractPeaks: () => (/* binding */ extractPeaks),
4678
- getDecodeDecision: () => (/* binding */ getDecodeDecision),
4679
- loadPeaks: () => (/* binding */ loadPeaks),
4680
- runClientDecode: () => (/* binding */ runClientDecode)
4681
- });
4682
- // ESM COMPAT FLAG
4683
- __webpack_require__.r(__webpack_exports__);
4684
-
4685
- ;// ./src/lib/viewers/media/waveform/constants.ts
5883
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
5884
+ /* harmony export */ Bq: () => (/* binding */ WAVEFORM_PLAYHEAD_JUMP_MS),
5885
+ /* harmony export */ DY: () => (/* binding */ WAVEFORM_BAR_RADIUS),
5886
+ /* harmony export */ EB: () => (/* binding */ CLIENT_DECODE_MAX_COMPRESSED_BYTES),
5887
+ /* harmony export */ FY: () => (/* binding */ MAX_PEAK_COUNT),
5888
+ /* harmony export */ GQ: () => (/* binding */ DURATION_MISMATCH_TOLERANCE_SEC),
5889
+ /* harmony export */ Kl: () => (/* binding */ WAVEFORM_MIN_VIEW_WINDOW_SEC),
5890
+ /* harmony export */ LW: () => (/* binding */ WAVEFORM_ZOOM_MIN),
5891
+ /* harmony export */ Lu: () => (/* binding */ WAVEFORM_BAR_GAP),
5892
+ /* harmony export */ NM: () => (/* binding */ WAVEFORM_FOLLOW_INSET_PX),
5893
+ /* harmony export */ XS: () => (/* binding */ WAVEFORM_BAR_MIN_HEIGHT),
5894
+ /* harmony export */ Zs: () => (/* binding */ WAVEFORM_FOLLOW_SCROLL_SETTLE_MS),
5895
+ /* harmony export */ f3: () => (/* binding */ CLIENT_DECODE_PEAK_COUNT),
5896
+ /* harmony export */ i8: () => (/* binding */ PEAK_UNIT_MAX),
5897
+ /* harmony export */ m6: () => (/* binding */ WAVEFORM_ZOOM_DISMISS_MS),
5898
+ /* harmony export */ mJ: () => (/* binding */ PEAK_UNIT_MIN),
5899
+ /* harmony export */ nG: () => (/* binding */ CLIENT_DECODE_MAX_DURATION_SEC),
5900
+ /* harmony export */ nh: () => (/* binding */ WAVEFORM_ZOOM_SLIDER_MAX),
5901
+ /* harmony export */ oN: () => (/* binding */ WAVEFORM_HEIGHT),
5902
+ /* harmony export */ qY: () => (/* binding */ MAX_PAYLOAD_BYTES),
5903
+ /* harmony export */ sh: () => (/* binding */ WAVEFORM_PAYLOAD_VERSION),
5904
+ /* harmony export */ tK: () => (/* binding */ WAVEFORM_ZOOM_MAX),
5905
+ /* harmony export */ zo: () => (/* binding */ WAVEFORM_BAR_WIDTH)
5906
+ /* harmony export */ });
4686
5907
  /** Current waveform payload schema version. Bump only with a migration path. */
4687
5908
  const WAVEFORM_PAYLOAD_VERSION = 1;
4688
5909
 
@@ -4707,6 +5928,44 @@ const CLIENT_DECODE_MAX_DURATION_SEC = 5 * 60;
4707
5928
 
4708
5929
  /** Default overview resolution for client-generated peaks. */
4709
5930
  const CLIENT_DECODE_PEAK_COUNT = 16384;
5931
+ const WAVEFORM_ZOOM_MIN = 1;
5932
+ const WAVEFORM_ZOOM_MAX = 24;
5933
+ /** Visible window never shorter than this. */
5934
+ const WAVEFORM_MIN_VIEW_WINDOW_SEC = 4;
5935
+ const WAVEFORM_ZOOM_SLIDER_MAX = 100;
5936
+ const WAVEFORM_ZOOM_DISMISS_MS = 250;
5937
+ const WAVEFORM_FOLLOW_INSET_PX = 200;
5938
+ const WAVEFORM_PLAYHEAD_JUMP_MS = 400;
5939
+ /** After the last user pan, wait this long before the camera may pin again. */
5940
+ const WAVEFORM_FOLLOW_SCROLL_SETTLE_MS = 150;
5941
+ const WAVEFORM_BAR_GAP = 2;
5942
+ const WAVEFORM_BAR_WIDTH = 2;
5943
+ const WAVEFORM_BAR_RADIUS = WAVEFORM_BAR_WIDTH / 2;
5944
+ /** Total bar height so the top and bottom radii meet as a circle on the mirror. */
5945
+ const WAVEFORM_BAR_MIN_HEIGHT = WAVEFORM_BAR_WIDTH;
5946
+ const WAVEFORM_HEIGHT = 140;
5947
+
5948
+ /***/ },
5949
+
5950
+ /***/ 546
5951
+ (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
5952
+
5953
+
5954
+ // EXPORTS
5955
+ __webpack_require__.d(__webpack_exports__, {
5956
+ decodeToPeaks: () => (/* binding */ decodeToPeaks),
5957
+ extractPeaks: () => (/* binding */ extractPeaks),
5958
+ getDecodeDecision: () => (/* binding */ getDecodeDecision),
5959
+ loadPeaks: () => (/* binding */ loadPeaks),
5960
+ runClientDecode: () => (/* binding */ runClientDecode)
5961
+ });
5962
+ // ESM COMPAT FLAG
5963
+ __webpack_require__.r(__webpack_exports__);
5964
+
5965
+ // EXTERNAL MODULE: ./src/lib/util.js + 1 modules
5966
+ var util = __webpack_require__(4410);
5967
+ // EXTERNAL MODULE: ./src/lib/viewers/media/waveform/constants.ts
5968
+ var constants = __webpack_require__(4929);
4710
5969
  ;// ./src/lib/viewers/media/waveform/types.ts
4711
5970
  /**
4712
5971
  * Box V1 is the in-viewer form: unsigned mono peaks in [0, 1] (peak envelope, mono_max).
@@ -4733,6 +5992,15 @@ function isWaveformErrorCode(value) {
4733
5992
  * Async boundary for waveform data. Implementations may fetch fixtures, decode client-side,
4734
5993
  * or load Conversion reps — callers only observe WaveformLoadState.
4735
5994
  */
5995
+
5996
+ /** Visible slice of the timeline. Emitted whenever zoom, scroll, or width changes. */
5997
+
5998
+ /** One canvas linear-gradient stop. `offset` is 0–1 along the bar. */
5999
+
6000
+ /**
6001
+ * Wavesurfer's two paints: left of the playhead (`progressColor`) and right of it (`waveColor`).
6002
+ * A string is a solid fill; a stop list is a left-to-right step gradient.
6003
+ */
4736
6004
  ;// ./src/lib/viewers/media/waveform/createWaveformLoader.ts
4737
6005
  /* unused harmony import specifier */ var isRetryableWaveformError;
4738
6006
  /* unused harmony import specifier */ var validateWaveformPayload;
@@ -4950,7 +6218,7 @@ function asWaveformPayloadV1(raw) {
4950
6218
  durationSec,
4951
6219
  peaks
4952
6220
  } = raw;
4953
- if (version !== WAVEFORM_PAYLOAD_VERSION || typeof durationSec !== 'number' || !Array.isArray(peaks)) {
6221
+ if (version !== constants/* WAVEFORM_PAYLOAD_VERSION */.sh || typeof durationSec !== 'number' || !Array.isArray(peaks)) {
4954
6222
  return null;
4955
6223
  }
4956
6224
  const peakScale = readOptionalPolicy(raw, 'peakScale', DEFAULT_PEAK_SCALE);
@@ -4963,7 +6231,7 @@ function asWaveformPayloadV1(raw) {
4963
6231
  return null;
4964
6232
  }
4965
6233
  return {
4966
- version: WAVEFORM_PAYLOAD_VERSION,
6234
+ version: constants/* WAVEFORM_PAYLOAD_VERSION */.sh,
4967
6235
  durationSec,
4968
6236
  peaks: peaks,
4969
6237
  peakScale: peakScale ?? DEFAULT_PEAK_SCALE,
@@ -4991,14 +6259,14 @@ function readPayloadObject(raw, maxPayloadBytes, isPayloadByteCheckSkipped = fal
4991
6259
  };
4992
6260
  }
4993
6261
  function checkVersion(raw) {
4994
- if (raw.version === WAVEFORM_PAYLOAD_VERSION) {
6262
+ if (raw.version === constants/* WAVEFORM_PAYLOAD_VERSION */.sh) {
4995
6263
  return null;
4996
6264
  }
4997
6265
  const {
4998
6266
  version
4999
6267
  } = raw;
5000
- if (typeof version === 'number' && version > WAVEFORM_PAYLOAD_VERSION) {
5001
- return fail('UNSUPPORTED_VERSION', `Unsupported waveform version ${version}; max supported is ${WAVEFORM_PAYLOAD_VERSION}`);
6268
+ if (typeof version === 'number' && version > constants/* WAVEFORM_PAYLOAD_VERSION */.sh) {
6269
+ return fail('UNSUPPORTED_VERSION', `Unsupported waveform version ${version}; max supported is ${constants/* WAVEFORM_PAYLOAD_VERSION */.sh}`);
5002
6270
  }
5003
6271
  return fail('INVALID_PAYLOAD', 'Missing or invalid version field');
5004
6272
  }
@@ -5006,7 +6274,7 @@ function checkDuration(payload, expectedDurationSec) {
5006
6274
  if (!Number.isFinite(payload.durationSec) || payload.durationSec <= 0) {
5007
6275
  return fail('INVALID_DURATION', 'durationSec must be a positive finite number');
5008
6276
  }
5009
- if (expectedDurationSec !== undefined && Number.isFinite(expectedDurationSec) && Math.abs(payload.durationSec - expectedDurationSec) > DURATION_MISMATCH_TOLERANCE_SEC) {
6277
+ if (expectedDurationSec !== undefined && Number.isFinite(expectedDurationSec) && Math.abs(payload.durationSec - expectedDurationSec) > constants/* DURATION_MISMATCH_TOLERANCE_SEC */.GQ) {
5010
6278
  return fail('DURATION_MISMATCH', `Payload duration ${payload.durationSec}s differs from media duration ${expectedDurationSec}s`, true);
5011
6279
  }
5012
6280
  return null;
@@ -5024,8 +6292,8 @@ function normalizePeaks(peaks, maxPeakCount) {
5024
6292
  if (!Number.isFinite(value)) {
5025
6293
  return fail('NON_FINITE_PEAK', `Peak at index ${i} is not finite`);
5026
6294
  }
5027
- if (value < PEAK_UNIT_MIN || value > PEAK_UNIT_MAX) {
5028
- return fail('PEAK_OUT_OF_RANGE', `Peak at index ${i} is outside [${PEAK_UNIT_MIN}, ${PEAK_UNIT_MAX}]`);
6295
+ if (value < constants/* PEAK_UNIT_MIN */.mJ || value > constants/* PEAK_UNIT_MAX */.i8) {
6296
+ return fail('PEAK_OUT_OF_RANGE', `Peak at index ${i} is outside [${constants/* PEAK_UNIT_MIN */.mJ}, ${constants/* PEAK_UNIT_MAX */.i8}]`);
5029
6297
  }
5030
6298
  normalized[i] = value;
5031
6299
  }
@@ -5040,8 +6308,8 @@ function normalizePeaks(peaks, maxPeakCount) {
5040
6308
  * Wire JSON requires version, durationSec, and peaks. sampleCount and source are ignored.
5041
6309
  */
5042
6310
  function validateWaveformPayload_validateWaveformPayload(raw, options = {}) {
5043
- const maxPeakCount = options.maxPeakCount ?? MAX_PEAK_COUNT;
5044
- const maxPayloadBytes = options.maxPayloadBytes ?? MAX_PAYLOAD_BYTES;
6311
+ const maxPeakCount = options.maxPeakCount ?? constants/* MAX_PEAK_COUNT */.FY;
6312
+ const maxPayloadBytes = options.maxPayloadBytes ?? constants/* MAX_PAYLOAD_BYTES */.qY;
5045
6313
  const objectResult = readPayloadObject(raw, maxPayloadBytes, options.isPayloadByteCheckSkipped === true);
5046
6314
  if (!objectResult.ok) {
5047
6315
  return objectResult;
@@ -5065,7 +6333,7 @@ function validateWaveformPayload_validateWaveformPayload(raw, options = {}) {
5065
6333
  return {
5066
6334
  ok: true,
5067
6335
  payload: {
5068
- version: WAVEFORM_PAYLOAD_VERSION,
6336
+ version: constants/* WAVEFORM_PAYLOAD_VERSION */.sh,
5069
6337
  durationSec: payload.durationSec,
5070
6338
  peaks: peaksResult.peaks,
5071
6339
  peakScale: payload.peakScale ?? DEFAULT_PEAK_SCALE,
@@ -5088,13 +6356,11 @@ function decode_toPrimitive(t, r) { if ("object" != typeof t || !t) return t; va
5088
6356
 
5089
6357
 
5090
6358
 
6359
+
5091
6360
  const EMPTY_TIMINGS = {
5092
6361
  attemptMs: null,
5093
6362
  extractMs: null
5094
6363
  };
5095
- function getCurrentTimeMs() {
5096
- return typeof performance !== 'undefined' ? performance.now() : Date.now();
5097
- }
5098
6364
  function createAbortError() {
5099
6365
  return new DOMException('Aborted', 'AbortError');
5100
6366
  }
@@ -5187,8 +6453,8 @@ function decodeWithContext(context, buffer, signal) {
5187
6453
  * skips decode so playback is never blocked by expanding the full audio buffer.
5188
6454
  */
5189
6455
  function getDecodeDecision(media, caps = {}) {
5190
- const maxCompressedBytes = caps.maxCompressedBytes ?? CLIENT_DECODE_MAX_COMPRESSED_BYTES;
5191
- const maxDurationSec = caps.maxDurationSec ?? CLIENT_DECODE_MAX_DURATION_SEC;
6456
+ const maxCompressedBytes = caps.maxCompressedBytes ?? constants/* CLIENT_DECODE_MAX_COMPRESSED_BYTES */.EB;
6457
+ const maxDurationSec = caps.maxDurationSec ?? constants/* CLIENT_DECODE_MAX_DURATION_SEC */.nG;
5192
6458
  const {
5193
6459
  compressedBytes,
5194
6460
  durationSec
@@ -5220,7 +6486,7 @@ function getDecodeDecision(media, caps = {}) {
5220
6486
  * Collapse audio channels to one unsigned peak per time bucket (max abs, then clamp to unit).
5221
6487
  * Accepts an AudioBuffer so callers can extract before closing the AudioContext.
5222
6488
  */
5223
- function extractPeaks(audio, peakCount = CLIENT_DECODE_PEAK_COUNT) {
6489
+ function extractPeaks(audio, peakCount = constants/* CLIENT_DECODE_PEAK_COUNT */.f3) {
5224
6490
  const channels = Array.isArray(audio) ? audio : Array.from({
5225
6491
  length: audio.numberOfChannels
5226
6492
  }, (_, channelIndex) => audio.getChannelData(channelIndex));
@@ -5245,7 +6511,7 @@ function extractPeaks(audio, peakCount = CLIENT_DECODE_PEAK_COUNT) {
5245
6511
  }
5246
6512
  }
5247
6513
  }
5248
- peaks[i] = Math.min(PEAK_UNIT_MAX, Math.max(PEAK_UNIT_MIN, maxAbs));
6514
+ peaks[i] = Math.min(constants/* PEAK_UNIT_MAX */.i8, Math.max(constants/* PEAK_UNIT_MIN */.mJ, maxAbs));
5249
6515
  }
5250
6516
  return peaks;
5251
6517
  }
@@ -5254,7 +6520,7 @@ function extractPeaks(audio, peakCount = CLIENT_DECODE_PEAK_COUNT) {
5254
6520
  * Decode compressed audio and extract unit peaks while the AudioBuffer is live.
5255
6521
  * Does not attach a media element or fetch a URL. Does not copy channel data.
5256
6522
  */
5257
- async function decodeToPeaks(arrayBuffer, signal, peakCount = CLIENT_DECODE_PEAK_COUNT) {
6523
+ async function decodeToPeaks(arrayBuffer, signal, peakCount = constants/* CLIENT_DECODE_PEAK_COUNT */.f3) {
5258
6524
  if (signal?.aborted) {
5259
6525
  throw createAbortError();
5260
6526
  }
@@ -5268,12 +6534,12 @@ async function decodeToPeaks(arrayBuffer, signal, peakCount = CLIENT_DECODE_PEAK
5268
6534
  if (signal?.aborted) {
5269
6535
  throw createAbortError();
5270
6536
  }
5271
- const extractStarted = getCurrentTimeMs();
6537
+ const extractStarted = (0,util/* getCurrentTimeMs */.RU)();
5272
6538
  const peaks = extractPeaks(audioBuffer, peakCount);
5273
6539
  return {
5274
6540
  durationSec: audioBuffer.duration,
5275
6541
  peaks,
5276
- extractMs: getCurrentTimeMs() - extractStarted
6542
+ extractMs: (0,util/* getCurrentTimeMs */.RU)() - extractStarted
5277
6543
  };
5278
6544
  } catch (error) {
5279
6545
  if (signal?.aborted || isAbortError(error)) {
@@ -5309,7 +6575,7 @@ async function runClientDecode(options) {
5309
6575
  };
5310
6576
  }
5311
6577
  const signal = options.signal ?? new AbortController().signal;
5312
- const decodeStarted = getCurrentTimeMs();
6578
+ const decodeStarted = (0,util/* getCurrentTimeMs */.RU)();
5313
6579
  let decodeOutput;
5314
6580
  try {
5315
6581
  decodeOutput = await options.decode(signal);
@@ -5328,12 +6594,12 @@ async function runClientDecode(options) {
5328
6594
  retryable: validateWaveformPayload_isRetryableWaveformError(waveformError.code),
5329
6595
  isDecodeSkipped: false,
5330
6596
  timings: {
5331
- attemptMs: getCurrentTimeMs() - decodeStarted,
6597
+ attemptMs: (0,util/* getCurrentTimeMs */.RU)() - decodeStarted,
5332
6598
  extractMs: null
5333
6599
  }
5334
6600
  };
5335
6601
  }
5336
- const attemptMs = getCurrentTimeMs() - decodeStarted;
6602
+ const attemptMs = (0,util/* getCurrentTimeMs */.RU)() - decodeStarted;
5337
6603
  if (signal.aborted) {
5338
6604
  return {
5339
6605
  status: 'cancelled',
@@ -5345,7 +6611,7 @@ async function runClientDecode(options) {
5345
6611
  };
5346
6612
  }
5347
6613
  const validation = validateWaveformPayload_validateWaveformPayload({
5348
- version: WAVEFORM_PAYLOAD_VERSION,
6614
+ version: constants/* WAVEFORM_PAYLOAD_VERSION */.sh,
5349
6615
  durationSec: options.durationSec ?? decodeOutput.durationSec,
5350
6616
  peaks: decodeOutput.peaks
5351
6617
  }, {
@@ -12161,7 +13427,7 @@ var x = (y) => {
12161
13427
  var x = {}; __webpack_require__.d(x, y); return x
12162
13428
  }
12163
13429
  var y = (x) => (() => (x))
12164
- module.exports = x({ ["createElement"]: () => (__WEBPACK_EXTERNAL_MODULE_react__.createElement), ["default"]: () => (__WEBPACK_EXTERNAL_MODULE_react__["default"]), ["useCallback"]: () => (__WEBPACK_EXTERNAL_MODULE_react__.useCallback), ["useEffect"]: () => (__WEBPACK_EXTERNAL_MODULE_react__.useEffect), ["useLayoutEffect"]: () => (__WEBPACK_EXTERNAL_MODULE_react__.useLayoutEffect), ["useRef"]: () => (__WEBPACK_EXTERNAL_MODULE_react__.useRef), ["useState"]: () => (__WEBPACK_EXTERNAL_MODULE_react__.useState) });
13430
+ module.exports = x({ ["createElement"]: () => (__WEBPACK_EXTERNAL_MODULE_react__.createElement), ["default"]: () => (__WEBPACK_EXTERNAL_MODULE_react__["default"]), ["useCallback"]: () => (__WEBPACK_EXTERNAL_MODULE_react__.useCallback), ["useEffect"]: () => (__WEBPACK_EXTERNAL_MODULE_react__.useEffect), ["useId"]: () => (__WEBPACK_EXTERNAL_MODULE_react__.useId), ["useLayoutEffect"]: () => (__WEBPACK_EXTERNAL_MODULE_react__.useLayoutEffect), ["useMemo"]: () => (__WEBPACK_EXTERNAL_MODULE_react__.useMemo), ["useRef"]: () => (__WEBPACK_EXTERNAL_MODULE_react__.useRef), ["useState"]: () => (__WEBPACK_EXTERNAL_MODULE_react__.useState) });
12165
13431
 
12166
13432
  /***/ },
12167
13433
 
@@ -21055,7 +22321,7 @@ class Browser {
21055
22321
  ;// ./src/lib/Logger.js
21056
22322
  /* eslint-disable no-undef */
21057
22323
  const CLIENT_NAME = "box-content-preview";
21058
- const CLIENT_VERSION = "3.83.0";
22324
+ const CLIENT_VERSION = "3.85.0";
21059
22325
  /* eslint-enable no-undef */
21060
22326
 
21061
22327
  class Logger {
@@ -33569,150 +34835,8 @@ function Filmstrip({
33569
34835
  "data-testid": "bp-Filmstrip-time"
33570
34836
  }, (0,DurationLabels/* formatTime */.f)(time)));
33571
34837
  }
33572
- ;// ./src/lib/viewers/controls/slider/SliderControl.scss
33573
- // extracted by mini-css-extract-plugin
33574
-
33575
- ;// ./src/lib/viewers/controls/slider/SliderControl.tsx
33576
- const SliderControl_excluded = ["className", "max", "min", "onMove", "onUpdate", "step", "title", "track", "value"];
33577
- function SliderControl_extends() { return SliderControl_extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, SliderControl_extends.apply(null, arguments); }
33578
- function SliderControl_objectWithoutProperties(e, t) { if (null == e) return {}; var o, r, i = SliderControl_objectWithoutPropertiesLoose(e, t); if (Object.getOwnPropertySymbols) { var n = Object.getOwnPropertySymbols(e); for (r = 0; r < n.length; r++) o = n[r], -1 === t.indexOf(o) && {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]); } return i; }
33579
- function SliderControl_objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
33580
-
33581
-
33582
-
33583
-
33584
-
33585
- function SliderControl(_ref) {
33586
- let {
33587
- className,
33588
- max = 100,
33589
- min = 0,
33590
- onMove = (noop_default()),
33591
- onUpdate = (noop_default()),
33592
- step = 1,
33593
- title,
33594
- track,
33595
- value
33596
- } = _ref,
33597
- rest = SliderControl_objectWithoutProperties(_ref, SliderControl_excluded);
33598
- const [isScrubbing, setIsScrubbing] = external_react_["default"].useState(false);
33599
- const sliderElRef = external_react_["default"].useRef(null);
33600
- const getPosition = external_react_["default"].useCallback(pageX => {
33601
- const {
33602
- current: sliderEl
33603
- } = sliderElRef;
33604
- if (!sliderEl) return 0;
33605
- const {
33606
- left: sliderLeft,
33607
- width: sliderWidth
33608
- } = sliderEl.getBoundingClientRect();
33609
- return Math.max(0, Math.min(pageX - sliderLeft, sliderWidth));
33610
- }, []);
33611
- const getPositionValue = external_react_["default"].useCallback(pageX => {
33612
- const {
33613
- current: sliderEl
33614
- } = sliderElRef;
33615
- if (!sliderEl) return 0;
33616
- const {
33617
- width: sliderWidth
33618
- } = sliderEl.getBoundingClientRect();
33619
- const newValue = getPosition(pageX) / sliderWidth * max;
33620
- return Math.max(min, Math.min(newValue, max));
33621
- }, [getPosition, max, min]);
33622
- const handleKeydown = event => {
33623
- const key = (0,util/* decodeKeydown */.wU)(event);
33624
- if (key === 'ArrowLeft') {
33625
- event.stopPropagation(); // Prevents global key handling
33626
- onUpdate(Math.max(min, Math.min(value - step, max)));
33627
- }
33628
- if (key === 'ArrowRight') {
33629
- event.stopPropagation(); // Prevents global key handling
33630
- onUpdate(Math.max(min, Math.min(value + step, max)));
33631
- }
33632
- };
33633
- const handleMouseDown = ({
33634
- button,
33635
- ctrlKey,
33636
- metaKey,
33637
- pageX
33638
- }) => {
33639
- if (button > 1 || ctrlKey || metaKey) return;
33640
- onUpdate(getPositionValue(pageX));
33641
- setIsScrubbing(true);
33642
- };
33643
- const handleMouseMove = ({
33644
- pageX
33645
- }) => {
33646
- const {
33647
- current: sliderEl
33648
- } = sliderElRef;
33649
- const {
33650
- width: sliderWidth
33651
- } = sliderEl ? sliderEl.getBoundingClientRect() : {
33652
- width: 0
33653
- };
33654
- onMove(getPositionValue(pageX), getPosition(pageX), sliderWidth);
33655
- };
33656
- const handleTouchStart = ({
33657
- touches
33658
- }) => {
33659
- onUpdate(getPositionValue(touches[0].pageX));
33660
- setIsScrubbing(true);
33661
- };
33662
- external_react_["default"].useEffect(() => {
33663
- const handleDocumentMoveStop = () => setIsScrubbing(false);
33664
- const handleDocumentMouseMove = event => {
33665
- if (!isScrubbing || event.button > 1 || event.ctrlKey || event.metaKey) return;
33666
- event.preventDefault();
33667
- onUpdate(getPositionValue(event.pageX));
33668
- };
33669
- const handleDocumentTouchMove = event => {
33670
- if (!isScrubbing || !event.touches || !event.touches[0]) return;
33671
- event.preventDefault();
33672
- onUpdate(getPositionValue(event.touches[0].pageX));
33673
- };
33674
- if (isScrubbing) {
33675
- document.addEventListener('mousemove', handleDocumentMouseMove);
33676
- document.addEventListener('mouseup', handleDocumentMoveStop);
33677
- document.addEventListener('touchend', handleDocumentMoveStop);
33678
- document.addEventListener('touchmove', handleDocumentTouchMove);
33679
- }
33680
- return () => {
33681
- document.removeEventListener('mousemove', handleDocumentMouseMove);
33682
- document.removeEventListener('mouseup', handleDocumentMoveStop);
33683
- document.removeEventListener('touchend', handleDocumentMoveStop);
33684
- document.removeEventListener('touchmove', handleDocumentTouchMove);
33685
- };
33686
- }, [isScrubbing, getPositionValue, onUpdate]);
33687
- return /*#__PURE__*/external_react_["default"].createElement("div", SliderControl_extends({
33688
- ref: sliderElRef,
33689
- "aria-label": title,
33690
- "aria-valuemax": max,
33691
- "aria-valuemin": min,
33692
- "aria-valuenow": value,
33693
- className: classnames_default()('bp-SliderControl', className, {
33694
- 'bp-is-scrubbing': isScrubbing
33695
- }),
33696
- onKeyDown: handleKeydown,
33697
- onMouseDown: handleMouseDown,
33698
- onMouseMove: handleMouseMove,
33699
- onTouchStart: handleTouchStart,
33700
- role: "slider",
33701
- tabIndex: 0
33702
- }, rest), /*#__PURE__*/external_react_["default"].createElement("div", {
33703
- className: "bp-SliderControl-track",
33704
- "data-testid": "bp-slider-control-track",
33705
- style: {
33706
- backgroundImage: track
33707
- }
33708
- }), /*#__PURE__*/external_react_["default"].createElement("div", {
33709
- className: "bp-SliderControl-thumb",
33710
- "data-testid": "bp-slider-control-thumb",
33711
- style: {
33712
- left: `${value / max * 100}%`
33713
- }
33714
- }));
33715
- }
34838
+ // EXTERNAL MODULE: ./src/lib/viewers/controls/slider/SliderControl.tsx + 1 modules
34839
+ var SliderControl = __webpack_require__(4937);
33716
34840
  ;// ./src/lib/viewers/controls/media/TimeControls.scss
33717
34841
  // extracted by mini-css-extract-plugin
33718
34842
 
@@ -33772,7 +34896,7 @@ function TimeControls({
33772
34896
  durationTime: durationTime,
33773
34897
  fps: fps,
33774
34898
  mediaEl: mediaEl
33775
- }), /*#__PURE__*/external_react_["default"].createElement(SliderControl, {
34899
+ }), /*#__PURE__*/external_react_["default"].createElement(SliderControl/* default */.A, {
33776
34900
  className: "bp-TimeControls-slider",
33777
34901
  "data-resin-target": "timeScrubber",
33778
34902
  max: durationValue,
@@ -34004,7 +35128,7 @@ class MP3Viewer extends media_MediaBaseViewer {
34004
35128
  * @return {Promise<{ default: Function }>} MP3ControlsV2 module
34005
35129
  */
34006
35130
  importV2Controls() {
34007
- return Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, 7893));
35131
+ return Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, 3678));
34008
35132
  }
34009
35133
 
34010
35134
  /**
@@ -34012,7 +35136,7 @@ class MP3Viewer extends media_MediaBaseViewer {
34012
35136
  */
34013
35137
  async importWaveformDecode() {
34014
35138
  if (!this.waveformDecodeImport) {
34015
- this.waveformDecodeImport = Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, 9514));
35139
+ this.waveformDecodeImport = Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, 546));
34016
35140
  }
34017
35141
  try {
34018
35142
  return await this.waveformDecodeImport;
@@ -36300,7 +37424,7 @@ function TimeControlsV2({
36300
37424
  style: trackMask ? {
36301
37425
  '--bp-track-mask': trackMask
36302
37426
  } : undefined
36303
- }, /*#__PURE__*/external_react_["default"].createElement(SliderControl, {
37427
+ }, /*#__PURE__*/external_react_["default"].createElement(SliderControl/* default */.A, {
36304
37428
  className: "bp-TimeControlsV2-slider",
36305
37429
  "data-resin-target": "timeScrubber",
36306
37430
  max: durationValue,