box-content-preview 3.86.0 → 3.87.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
@@ -1010,7 +1010,7 @@ function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e =
1010
1010
  const CLIENT_NAME = "box-content-preview"; // eslint-disable-line no-undef
1011
1011
  const CLIENT_NAME_KEY = 'box_client_name';
1012
1012
  const CLIENT_VERSION_KEY = 'box_client_version';
1013
- const CLIENT_VERSION = "3.86.0"; // eslint-disable-line no-undef
1013
+ const CLIENT_VERSION = "3.87.0"; // eslint-disable-line no-undef
1014
1014
  const HEADER_CLIENT_NAME = 'X-Box-Client-Name';
1015
1015
  const HEADER_CLIENT_VERSION = 'X-Box-Client-Version';
1016
1016
  const PROMISE_MAP = {};
@@ -2135,6 +2135,204 @@ function DurationLabels({
2135
2135
 
2136
2136
  /***/ },
2137
2137
 
2138
+ /***/ 3524
2139
+ (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
2140
+
2141
+
2142
+ // EXPORTS
2143
+ __webpack_require__.d(__webpack_exports__, {
2144
+ A: () => (/* binding */ MarkerAvatar)
2145
+ });
2146
+
2147
+ // EXTERNAL MODULE: external "react"
2148
+ var external_react_ = __webpack_require__(1649);
2149
+ ;// ./src/lib/viewers/controls/media/MarkerAvatar.scss
2150
+ // extracted by mini-css-extract-plugin
2151
+
2152
+ ;// ./src/lib/viewers/controls/media/MarkerAvatar.tsx
2153
+
2154
+
2155
+ const AVATAR_PALETTE = [{
2156
+ bg: '#7fb0ea',
2157
+ fg: '#222'
2158
+ }, {
2159
+ bg: '#003c84',
2160
+ fg: '#fff'
2161
+ }, {
2162
+ bg: '#ffeb7f',
2163
+ fg: '#222'
2164
+ }, {
2165
+ bg: '#92e0c0',
2166
+ fg: '#222'
2167
+ }, {
2168
+ bg: '#fad98d',
2169
+ fg: '#222'
2170
+ }, {
2171
+ bg: '#91c2fd',
2172
+ fg: '#222'
2173
+ }, {
2174
+ bg: '#f69bab',
2175
+ fg: '#222'
2176
+ }, {
2177
+ bg: '#cf9ff6',
2178
+ fg: '#222'
2179
+ }, {
2180
+ bg: '#f8c08c',
2181
+ fg: '#222'
2182
+ }, {
2183
+ bg: '#a392e0',
2184
+ fg: '#222'
2185
+ }];
2186
+ function AnonymousAvatarIcon() {
2187
+ return /*#__PURE__*/external_react_["default"].createElement("svg", {
2188
+ "aria-hidden": "true",
2189
+ className: "bp-MarkerAvatar-anonymousIcon",
2190
+ focusable: "false",
2191
+ viewBox: "0 0 16 16"
2192
+ }, /*#__PURE__*/external_react_["default"].createElement("path", {
2193
+ d: "M8 8a3 3 0 1 0 0-6 3 3 0 0 0 0 6Zm0 1.5c-2.5 0-5 1.25-5 3.75V14h10v-.75c0-2.5-2.5-3.75-5-3.75Z",
2194
+ fill: "currentColor"
2195
+ }));
2196
+ }
2197
+ function MarkerAvatar({
2198
+ avatarUrl,
2199
+ colorIndex = 0,
2200
+ initial,
2201
+ size
2202
+ }) {
2203
+ const safeIndex = Number.isFinite(colorIndex) ? Math.abs(colorIndex) % AVATAR_PALETTE.length : 0;
2204
+ const {
2205
+ bg: bgColor,
2206
+ fg: textColor
2207
+ } = AVATAR_PALETTE[safeIndex];
2208
+ const [imgFailed, setImgFailed] = external_react_["default"].useState(false);
2209
+ const [loadedUrl, setLoadedUrl] = external_react_["default"].useState(null);
2210
+ const imgRef = external_react_["default"].useRef(null);
2211
+ // Keep initial + palette until onLoad (and for cached complete). Shared with video ticks.
2212
+ const showImage = Boolean(avatarUrl) && !imgFailed && loadedUrl === avatarUrl;
2213
+ external_react_["default"].useEffect(() => {
2214
+ setImgFailed(false);
2215
+ }, [avatarUrl]);
2216
+ external_react_["default"].useLayoutEffect(() => {
2217
+ const img = imgRef.current;
2218
+ if (avatarUrl && img?.complete && img.naturalWidth > 0) {
2219
+ setLoadedUrl(avatarUrl);
2220
+ }
2221
+ }, [avatarUrl]);
2222
+ let fallback = /*#__PURE__*/external_react_["default"].createElement(AnonymousAvatarIcon, null);
2223
+ if (initial) {
2224
+ fallback = /*#__PURE__*/external_react_["default"].createElement("span", {
2225
+ className: "bp-MarkerAvatar-initial",
2226
+ style: {
2227
+ color: textColor
2228
+ }
2229
+ }, initial);
2230
+ }
2231
+ const style = {};
2232
+ if (!showImage) {
2233
+ style.backgroundColor = bgColor;
2234
+ }
2235
+ if (size) {
2236
+ style.width = size;
2237
+ style.height = size;
2238
+ }
2239
+ return /*#__PURE__*/external_react_["default"].createElement("span", {
2240
+ className: "bp-MarkerAvatar",
2241
+ style: Object.keys(style).length > 0 ? style : undefined
2242
+ }, avatarUrl && !imgFailed && /*#__PURE__*/external_react_["default"].createElement("img", {
2243
+ ref: imgRef,
2244
+ alt: "",
2245
+ onError: () => {
2246
+ setImgFailed(true);
2247
+ },
2248
+ onLoad: () => {
2249
+ if (avatarUrl) {
2250
+ setLoadedUrl(avatarUrl);
2251
+ }
2252
+ },
2253
+ src: avatarUrl
2254
+ }), !showImage && fallback);
2255
+ }
2256
+
2257
+ /***/ },
2258
+
2259
+ /***/ 9232
2260
+ (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
2261
+
2262
+
2263
+ // EXPORTS
2264
+ __webpack_require__.d(__webpack_exports__, {
2265
+ A: () => (/* binding */ MarkerAvatarStack)
2266
+ });
2267
+
2268
+ // EXTERNAL MODULE: external "react"
2269
+ var external_react_ = __webpack_require__(1649);
2270
+ // EXTERNAL MODULE: ./src/lib/viewers/controls/media/MarkerAvatar.tsx + 1 modules
2271
+ var MarkerAvatar = __webpack_require__(3524);
2272
+ ;// ./src/lib/viewers/controls/media/MarkerAvatarStack.scss
2273
+ // extracted by mini-css-extract-plugin
2274
+
2275
+ ;// ./src/lib/viewers/controls/media/MarkerAvatarStack.tsx
2276
+
2277
+
2278
+
2279
+ const MAX_VISIBLE_AVATARS = 4;
2280
+ function MarkerAvatarStack({
2281
+ markers,
2282
+ onMarkerClick,
2283
+ overlapPx,
2284
+ selectedId,
2285
+ size
2286
+ }) {
2287
+ const hasOverflow = markers.length > MAX_VISIBLE_AVATARS;
2288
+ const visibleMarkers = hasOverflow ? markers.slice(0, MAX_VISIBLE_AVATARS - 1) : markers;
2289
+ const overflowMarkers = hasOverflow ? markers.slice(MAX_VISIBLE_AVATARS - 1) : [];
2290
+ const isOverflowSelected = overflowMarkers.some(marker => marker.id === selectedId);
2291
+ const style = overlapPx != null ? {
2292
+ '--bp-marker-stack-overlap': `-${overlapPx}px`
2293
+ } : undefined;
2294
+ return /*#__PURE__*/external_react_["default"].createElement("span", {
2295
+ className: "bp-MarkerAvatarStack",
2296
+ style: style
2297
+ }, visibleMarkers.map(marker => /*#__PURE__*/external_react_["default"].createElement("button", {
2298
+ key: marker.id,
2299
+ "aria-label": "Comment marker",
2300
+ "aria-pressed": marker.id === selectedId,
2301
+ className: `bp-MarkerAvatarStack-item${marker.id === selectedId ? ' bp-MarkerAvatarStack-item--selected' : ''}`,
2302
+ "data-resin-target": "commentMarkerStackAvatar",
2303
+ onClick: e => {
2304
+ e.stopPropagation();
2305
+ onMarkerClick?.(marker);
2306
+ },
2307
+ type: "button"
2308
+ }, /*#__PURE__*/external_react_["default"].createElement(MarkerAvatar/* default */.A, {
2309
+ avatarUrl: marker.avatarUrl,
2310
+ colorIndex: marker.colorIndex,
2311
+ initial: marker.initial,
2312
+ size: size
2313
+ }))), hasOverflow && /*#__PURE__*/external_react_["default"].createElement("button", {
2314
+ "aria-label": "Comment marker",
2315
+ "aria-pressed": isOverflowSelected,
2316
+ className: `bp-MarkerAvatarStack-item bp-MarkerAvatarStack-overflow${isOverflowSelected ? ' bp-MarkerAvatarStack-item--selected' : ''}`,
2317
+ "data-resin-target": "commentMarkerStackAvatarOverflow",
2318
+ onClick: e => {
2319
+ e.stopPropagation();
2320
+ onMarkerClick?.(markers[MAX_VISIBLE_AVATARS - 1]);
2321
+ },
2322
+ type: "button"
2323
+ }, /*#__PURE__*/external_react_["default"].createElement("span", {
2324
+ className: "bp-MarkerAvatar bp-MarkerAvatarStack-overflowBadge",
2325
+ style: size ? {
2326
+ width: size,
2327
+ height: size
2328
+ } : undefined
2329
+ }, /*#__PURE__*/external_react_["default"].createElement("span", {
2330
+ className: "bp-MarkerAvatar-initial"
2331
+ }, "+", markers.length - (MAX_VISIBLE_AVATARS - 1)))));
2332
+ }
2333
+
2334
+ /***/ },
2335
+
2138
2336
  /***/ 6929
2139
2337
  (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
2140
2338
 
@@ -3245,6 +3443,75 @@ function VolumeControls({
3245
3443
 
3246
3444
  /***/ },
3247
3445
 
3446
+ /***/ 2400
3447
+ (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
3448
+
3449
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
3450
+ /* harmony export */ A: () => (/* binding */ buildClusters)
3451
+ /* harmony export */ });
3452
+ /* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(5346);
3453
+
3454
+
3455
+ /** Max pixel distance between adjacent markers (sorted by time) for them to be grouped into a single cluster. */
3456
+ const CLUSTER_THRESHOLD_PX = 2;
3457
+
3458
+ /** Converts a group of markers into a ClusterData object with computed positions and metadata. */
3459
+ function finalizeCluster(group, durationValue) {
3460
+ const leftPercent = (0,_utils__WEBPACK_IMPORTED_MODULE_0__/* .percent */ .K)(group[0].time, durationValue);
3461
+ const rightPercent = (0,_utils__WEBPACK_IMPORTED_MODULE_0__/* .percent */ .K)(group[group.length - 1].time, durationValue);
3462
+ const isSinglePoint = leftPercent === rightPercent;
3463
+ return {
3464
+ id: group.map(m => m.id).join('|'),
3465
+ isSinglePoint,
3466
+ leftPercent,
3467
+ markers: group,
3468
+ rightPercent
3469
+ };
3470
+ }
3471
+
3472
+ /**
3473
+ * Groups comment markers into clusters based on their pixel proximity on the scrubber track.
3474
+ * Markers are sorted by time, then chained: each marker that is within CLUSTER_THRESHOLD_PX
3475
+ * of its neighbor joins the same cluster. This means distant markers can end up in one cluster
3476
+ * if intermediate markers bridge the gap.
3477
+ */
3478
+ function buildClusters(markers, durationValue, trackWidth, thresholdPx = CLUSTER_THRESHOLD_PX) {
3479
+ if (durationValue <= 0 || markers.length === 0 || trackWidth <= 0) return [];
3480
+ const sorted = [...markers].sort((a, b) => a.time - b.time);
3481
+ const clusters = [];
3482
+ let currentGroup = [sorted[0]];
3483
+ for (let i = 1; i < sorted.length; i += 1) {
3484
+ const prevPx = sorted[i - 1].time / durationValue * trackWidth;
3485
+ const currPx = sorted[i].time / durationValue * trackWidth;
3486
+ if (currPx - prevPx <= thresholdPx) {
3487
+ currentGroup.push(sorted[i]);
3488
+ } else {
3489
+ clusters.push(finalizeCluster(currentGroup, durationValue));
3490
+ currentGroup = [sorted[i]];
3491
+ }
3492
+ }
3493
+ clusters.push(finalizeCluster(currentGroup, durationValue));
3494
+ return clusters;
3495
+ }
3496
+
3497
+ /***/ },
3498
+
3499
+ /***/ 5346
3500
+ (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
3501
+
3502
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
3503
+ /* harmony export */ K: () => (/* binding */ percent)
3504
+ /* harmony export */ });
3505
+ /* unused harmony export round */
3506
+ const round = value => {
3507
+ return +value.toFixed(4);
3508
+ };
3509
+ const percent = (value1, value2) => {
3510
+ return round(value1 / value2 * 100);
3511
+ };
3512
+
3513
+ /***/ },
3514
+
3248
3515
  /***/ 709
3249
3516
  (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
3250
3517
 
@@ -4118,7 +4385,7 @@ function getPdfjsWorkerSrc() {
4118
4385
 
4119
4386
  /***/ },
4120
4387
 
4121
- /***/ 3678
4388
+ /***/ 9286
4122
4389
  (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
4123
4390
 
4124
4391
 
@@ -4283,6 +4550,11 @@ function getViewportAtScroll(viewport, scrollLeftPx) {
4283
4550
  }));
4284
4551
  }
4285
4552
 
4553
+ /** True when the visible window has not moved — skip a React state update. */
4554
+ function viewportEquals(prev, next) {
4555
+ return !!prev && prev.durationSec === next.durationSec && prev.endSec === next.endSec && prev.maxZoom === next.maxZoom && prev.scrollLeftPx === next.scrollLeftPx && prev.startSec === next.startSec && prev.widthPx === next.widthPx && prev.zoomLevel === next.zoomLevel;
4556
+ }
4557
+
4286
4558
  /** How many CSS pixels from the left of the visible window this time sits. */
4287
4559
  function positionPxFromTime(timeSec, viewport) {
4288
4560
  return (timeSec - viewport.startSec) * viewport.pixelsPerSecond;
@@ -4371,6 +4643,35 @@ function clampScrollLeft(scrollLeftPx, viewport) {
4371
4643
  return Math.min(maxScrollLeft(viewport), Math.max(0, scrollLeftPx));
4372
4644
  }
4373
4645
 
4646
+ /** Center this time in the view, clamped so the window still fills the canvas. */
4647
+ function getCenteredScrollLeft(timeSec, viewport) {
4648
+ return clampScrollLeft(timeSec * viewport.pixelsPerSecond - viewport.widthPx / 2, viewport);
4649
+ }
4650
+
4651
+ /**
4652
+ * Host/media seek while zoomed: jump so the time (and its comment avatar) is on screen.
4653
+ * No-op at fit-to-width or when that time is already visible.
4654
+ */
4655
+ function getSeekCameraAction({
4656
+ timeSec,
4657
+ viewport
4658
+ }) {
4659
+ if (viewport.zoomLevel <= constants/* WAVEFORM_ZOOM_MIN */.LW || !(viewport.widthPx > 0) || !(viewport.pixelsPerSecond > 0)) {
4660
+ return {
4661
+ type: 'none'
4662
+ };
4663
+ }
4664
+ if (isTimeInView(timeSec, viewport)) {
4665
+ return {
4666
+ type: 'none'
4667
+ };
4668
+ }
4669
+ return {
4670
+ type: 'jump',
4671
+ scrollLeftPx: getCenteredScrollLeft(timeSec, viewport)
4672
+ };
4673
+ }
4674
+
4374
4675
  /** Follow inset in CSS px, capped at one third of the view so a narrow player still has room. */
4375
4676
  function getFollowInsetPx(widthPx, insetPx = constants/* WAVEFORM_FOLLOW_INSET_PX */.NM) {
4376
4677
  if (!(widthPx > 0)) {
@@ -4447,6 +4748,198 @@ function getPlayheadCameraAction({
4447
4748
  type: 'none'
4448
4749
  };
4449
4750
  }
4751
+ // EXTERNAL MODULE: ./src/lib/viewers/controls/media/buildClusters.ts
4752
+ var buildClusters = __webpack_require__(2400);
4753
+ // EXTERNAL MODULE: ./src/lib/viewers/controls/media/MarkerAvatar.tsx + 1 modules
4754
+ var MarkerAvatar = __webpack_require__(3524);
4755
+ // EXTERNAL MODULE: ./src/lib/viewers/controls/media/MarkerAvatarStack.tsx + 1 modules
4756
+ var MarkerAvatarStack = __webpack_require__(9232);
4757
+ // EXTERNAL MODULE: ./src/lib/viewers/controls/media/utils.ts
4758
+ var utils = __webpack_require__(5346);
4759
+ ;// ./src/lib/viewers/media/waveform/WaveformCommentMarkers.scss
4760
+ // extracted by mini-css-extract-plugin
4761
+
4762
+ ;// ./src/lib/viewers/media/waveform/WaveformCommentMarkers.tsx
4763
+
4764
+
4765
+
4766
+
4767
+
4768
+
4769
+
4770
+ const WAVEFORM_MARKER_SIZE_PX = 20;
4771
+ function hasMappedWindow(viewport) {
4772
+ return !!viewport && Number.isFinite(viewport.startSec) && viewport.endSec > viewport.startSec;
4773
+ }
4774
+ function markerLeftPercent(time, durationSec, viewport) {
4775
+ const mapped = hasMappedWindow(viewport);
4776
+ const origin = mapped ? viewport.startSec : 0;
4777
+ const span = mapped ? viewport.endSec - viewport.startSec : durationSec;
4778
+ return (0,utils/* percent */.K)(time - origin, span);
4779
+ }
4780
+
4781
+ /** Width is unknown until layout. Still place badges, and stack exact same timestamps. */
4782
+ function clustersByExactTime(markers, durationSec) {
4783
+ const sorted = [...markers].sort((a, b) => a.time - b.time);
4784
+ const groups = [];
4785
+ sorted.forEach(marker => {
4786
+ const last = groups[groups.length - 1];
4787
+ if (last && last[last.length - 1].time === marker.time) {
4788
+ last.push(marker);
4789
+ } else {
4790
+ groups.push([marker]);
4791
+ }
4792
+ });
4793
+ return groups.map(group => {
4794
+ const leftPercent = (0,utils/* percent */.K)(group[0].time, durationSec);
4795
+ const rightPercent = (0,utils/* percent */.K)(group[group.length - 1].time, durationSec);
4796
+ return {
4797
+ id: group.map(m => m.id).join('|'),
4798
+ isSinglePoint: leftPercent === rightPercent,
4799
+ leftPercent,
4800
+ markers: group,
4801
+ rightPercent
4802
+ };
4803
+ });
4804
+ }
4805
+ function WaveformCommentMarkers({
4806
+ commentMarkers,
4807
+ durationSec,
4808
+ onCommentMarkerClick,
4809
+ selectedId: hostSelectedId = null,
4810
+ viewport = null
4811
+ }) {
4812
+ const overlayRef = (0,external_react_.useRef)(null);
4813
+ const trackRef = (0,external_react_.useRef)(null);
4814
+ const dismissedIdRef = (0,external_react_.useRef)(null);
4815
+ const [trackWidth, setTrackWidth] = (0,external_react_.useState)(0);
4816
+ const [optimisticSelectedId, setOptimisticSelectedId] = (0,external_react_.useState)(null);
4817
+ const [isSelectionDismissed, setIsSelectionDismissed] = (0,external_react_.useState)(false);
4818
+ const selectedId = optimisticSelectedId ?? (isSelectionDismissed ? null : hostSelectedId);
4819
+ const canShowTrack = durationSec > 0 && commentMarkers.length > 0;
4820
+ const zoomLevel = viewport?.zoomLevel ?? constants/* WAVEFORM_ZOOM_MIN */.LW;
4821
+ const isZoomed = hasMappedWindow(viewport) && viewport.zoomLevel > constants/* WAVEFORM_ZOOM_MIN */.LW;
4822
+ (0,external_react_.useEffect)(() => {
4823
+ setOptimisticSelectedId(null);
4824
+ // Host ack of the dismissed id must not bring the ring back.
4825
+ if (dismissedIdRef.current && hostSelectedId === dismissedIdRef.current) {
4826
+ return;
4827
+ }
4828
+ dismissedIdRef.current = null;
4829
+ setIsSelectionDismissed(false);
4830
+ }, [hostSelectedId]);
4831
+ (0,external_react_.useLayoutEffect)(() => {
4832
+ if (!canShowTrack) {
4833
+ setTrackWidth(0);
4834
+ return undefined;
4835
+ }
4836
+ const el = trackRef.current;
4837
+ if (!el) {
4838
+ return undefined;
4839
+ }
4840
+ setTrackWidth(el.clientWidth);
4841
+ const observer = new ResizeObserver(entries => {
4842
+ entries.forEach(entry => {
4843
+ setTrackWidth(entry.contentRect.width);
4844
+ });
4845
+ });
4846
+ observer.observe(el);
4847
+ return () => observer.disconnect();
4848
+ }, [canShowTrack]);
4849
+ const clusters = (0,external_react_.useMemo)(() => {
4850
+ const clusterWidth = trackWidth * Math.max(constants/* WAVEFORM_ZOOM_MIN */.LW, zoomLevel);
4851
+ if (clusterWidth <= 0) {
4852
+ return clustersByExactTime(commentMarkers, durationSec);
4853
+ }
4854
+ return (0,buildClusters/* default */.A)(commentMarkers, durationSec, clusterWidth, WAVEFORM_MARKER_SIZE_PX);
4855
+ }, [commentMarkers, durationSec, trackWidth, zoomLevel]);
4856
+ const handleMarkerClick = (0,external_react_.useCallback)((marker, event) => {
4857
+ event?.stopPropagation();
4858
+ dismissedIdRef.current = null;
4859
+ setIsSelectionDismissed(false);
4860
+ setOptimisticSelectedId(marker.id);
4861
+ onCommentMarkerClick?.(marker);
4862
+ }, [onCommentMarkerClick]);
4863
+ (0,external_react_.useEffect)(() => {
4864
+ if (!selectedId) {
4865
+ return undefined;
4866
+ }
4867
+ const isEventInsideSelectedBadge = target => {
4868
+ if (!(target instanceof Element) || !overlayRef.current) {
4869
+ return false;
4870
+ }
4871
+ const selected = overlayRef.current.querySelector('.bp-WaveformCommentMarkers-marker--selected, .bp-MarkerAvatarStack-item--selected');
4872
+ return Boolean(selected && selected.contains(target));
4873
+ };
4874
+ const onDocumentPointerDown = event => {
4875
+ if (isEventInsideSelectedBadge(event.target)) {
4876
+ return;
4877
+ }
4878
+ dismissedIdRef.current = selectedId;
4879
+ setOptimisticSelectedId(null);
4880
+ setIsSelectionDismissed(true);
4881
+ const active = document.activeElement;
4882
+ if (active instanceof HTMLElement && overlayRef.current?.contains(active)) {
4883
+ active.blur();
4884
+ }
4885
+ };
4886
+ document.addEventListener('pointerdown', onDocumentPointerDown, true);
4887
+ return () => {
4888
+ document.removeEventListener('pointerdown', onDocumentPointerDown, true);
4889
+ };
4890
+ }, [selectedId]);
4891
+ if (!(durationSec > 0) || commentMarkers.length === 0) {
4892
+ return null;
4893
+ }
4894
+ return /*#__PURE__*/external_react_["default"].createElement("div", {
4895
+ ref: overlayRef,
4896
+ className: `bp-WaveformCommentMarkers${isZoomed ? ' bp-WaveformCommentMarkers--zoomed' : ''}`,
4897
+ "data-testid": "bp-waveform-comment-markers"
4898
+ }, /*#__PURE__*/external_react_["default"].createElement("div", {
4899
+ ref: trackRef,
4900
+ className: "bp-WaveformCommentMarkers-track"
4901
+ }, clusters.map(cluster => {
4902
+ const marker = cluster.markers[0];
4903
+ const isGroup = cluster.markers.length > 1;
4904
+ const isSelected = cluster.markers.some(entry => entry.id === selectedId);
4905
+ const className = `bp-WaveformCommentMarkers-marker${isSelected ? ' bp-WaveformCommentMarkers-marker--selected' : ''}${isGroup ? ' bp-WaveformCommentMarkers-marker--group' : ''}`;
4906
+ const left = `${markerLeftPercent(marker.time, durationSec, viewport)}%`;
4907
+ if (isGroup) {
4908
+ return /*#__PURE__*/external_react_["default"].createElement("div", {
4909
+ key: cluster.id,
4910
+ className: className,
4911
+ "data-testid": "bp-waveform-comment-marker",
4912
+ style: {
4913
+ left
4914
+ }
4915
+ }, /*#__PURE__*/external_react_["default"].createElement(MarkerAvatarStack/* default */.A, {
4916
+ markers: cluster.markers,
4917
+ onMarkerClick: handleMarkerClick,
4918
+ overlapPx: WAVEFORM_MARKER_SIZE_PX / 2,
4919
+ selectedId: selectedId,
4920
+ size: WAVEFORM_MARKER_SIZE_PX
4921
+ }));
4922
+ }
4923
+ return /*#__PURE__*/external_react_["default"].createElement("button", {
4924
+ key: cluster.id,
4925
+ "aria-label": "Comment marker",
4926
+ "aria-pressed": isSelected,
4927
+ className: className,
4928
+ "data-resin-target": "commentMarker",
4929
+ "data-testid": "bp-waveform-comment-marker",
4930
+ onClick: event => handleMarkerClick(marker, event),
4931
+ style: {
4932
+ left
4933
+ },
4934
+ type: "button"
4935
+ }, /*#__PURE__*/external_react_["default"].createElement(MarkerAvatar/* default */.A, {
4936
+ avatarUrl: marker.avatarUrl,
4937
+ colorIndex: marker.colorIndex,
4938
+ initial: marker.initial,
4939
+ size: WAVEFORM_MARKER_SIZE_PX
4940
+ }));
4941
+ })));
4942
+ }
4450
4943
  ;// ./node_modules/wavesurfer.js/dist/wavesurfer.esm.js
4451
4944
  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
4452
4945
  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;
@@ -4792,9 +5285,9 @@ function usePlayheadCamera({
4792
5285
  }
4793
5286
  programmaticScrollRef.current = false;
4794
5287
  });
4795
- if (shouldCommitState) {
4796
- onViewportCommitRef.current(appliedScrollLeft, viewportRef.current);
4797
- }
5288
+ // Overlay always gets the live window. React scroll state only when not pinned,
5289
+ // so WaveformView does not re-render every follow frame.
5290
+ onViewportCommitRef.current(appliedScrollLeft, viewportRef.current, shouldCommitState);
4798
5291
  }, [viewportRef, wavesurferRef]);
4799
5292
  const pinFollowPlayhead = (0,external_react_.useCallback)((playhead, viewport, isPinned) => {
4800
5293
  isFollowPinnedRef.current = isPinned;
@@ -4890,6 +5383,47 @@ function usePlayheadCamera({
4890
5383
  const isFollowCameraScroll = isFollowPinnedRef.current && lastCameraScroll != null && Math.abs(scrollLeftPx - lastCameraScroll) <= 1;
4891
5384
  return !(isCameraOwnedScroll && (jumpAnimationRef.current !== 0 || isFollowCameraScroll || isClampedToMax || !isFollowPinnedRef.current));
4892
5385
  }, [viewportRef]);
5386
+ const seekTo = (0,external_react_.useCallback)(timeSec => {
5387
+ mediaTimeRef.current = timeSec;
5388
+ const wavesurfer = wavesurferRef.current;
5389
+ if (!wavesurfer) {
5390
+ return;
5391
+ }
5392
+ releaseUserPanHold();
5393
+ cancelJump();
5394
+ isFollowPinnedRef.current = false;
5395
+ const liveViewport = getViewportAtScroll(viewportRef.current, getScrollLeft(wavesurfer, viewportRef.current.scrollLeftPx));
5396
+ const action = getSeekCameraAction({
5397
+ timeSec,
5398
+ viewport: liveViewport
5399
+ });
5400
+ if (action.type !== 'jump') {
5401
+ return;
5402
+ }
5403
+ const from = liveViewport.scrollLeftPx;
5404
+ const to = action.scrollLeftPx;
5405
+ if (prefersReducedMotion() || typeof window.requestAnimationFrame !== 'function' || from === to) {
5406
+ applyScrollLeft(to, true);
5407
+ return;
5408
+ }
5409
+ programmaticScrollRef.current = true;
5410
+ const start = (0,util/* getCurrentTimeMs */.RU)();
5411
+ const tick = now => {
5412
+ const t = Math.min(1, (now - start) / constants/* WAVEFORM_PLAYHEAD_JUMP_MS */.Bq);
5413
+ applyScrollLeft(from + (to - from) * (1 - (1 - t) * (1 - t)), false);
5414
+ const playhead = playheadRef.current;
5415
+ if (playhead) {
5416
+ playhead.style.left = timeLeftPercent(mediaTimeRef.current, viewportRef.current.durationSec, viewportRef.current);
5417
+ }
5418
+ if (t < 1) {
5419
+ jumpAnimationRef.current = window.requestAnimationFrame(tick);
5420
+ return;
5421
+ }
5422
+ jumpAnimationRef.current = 0;
5423
+ applyScrollLeft(to, true);
5424
+ };
5425
+ jumpAnimationRef.current = window.requestAnimationFrame(tick);
5426
+ }, [applyScrollLeft, cancelJump, playheadRef, releaseUserPanHold, viewportRef, wavesurferRef]);
4893
5427
  const handleScroll = (0,external_react_.useCallback)(onUserPan => {
4894
5428
  const wavesurfer = wavesurferRef.current;
4895
5429
  if (!wavesurfer) {
@@ -4935,7 +5469,8 @@ function usePlayheadCamera({
4935
5469
  isFollowPinned,
4936
5470
  onSeek,
4937
5471
  onZoom,
4938
- releaseUserPanHold
5472
+ releaseUserPanHold,
5473
+ seekTo
4939
5474
  };
4940
5475
  }
4941
5476
  ;// ./src/lib/viewers/media/waveform/WaveformView.scss
@@ -4977,6 +5512,15 @@ function applyPeaks(wavesurfer, peaks, durationSec) {
4977
5512
  wavesurfer.load('', toChannels(peaks), durationSec);
4978
5513
  }
4979
5514
 
5515
+ /** Stop the zoomed waveform from bouncing on trackpad overscroll. WaveSurfer's scroller is inside a shadow root, so SCSS uses `::part(scroll)` while zoomed and this sets the same property as soon as WaveSurfer exists. */
5516
+ function disableScrollOverscroll(container) {
5517
+ const host = container.firstElementChild;
5518
+ const scroll = host instanceof HTMLElement ? host.shadowRoot?.querySelector('.scroll') : null;
5519
+ if (scroll instanceof HTMLElement) {
5520
+ scroll.style.overscrollBehaviorX = 'none';
5521
+ }
5522
+ }
5523
+
4980
5524
  /** Tint WaveSurfer's zoomed tiles with played/unplayed/hover/buffer colors. */
4981
5525
  function tintZoomedWaveform(wavesurfer, fills, replaceSnapshot = false) {
4982
5526
  const wrapper = wavesurfer.getWrapper ? wavesurfer.getWrapper() : null;
@@ -5054,6 +5598,9 @@ function WaveformView({
5054
5598
  const pinchStartRef = (0,external_react_.useRef)(null);
5055
5599
  const pointerZoomRef = (0,external_react_.useRef)(false);
5056
5600
  const pointerZoomClearTimerRef = (0,external_react_.useRef)(0);
5601
+ const applyZoomWindowRef = (0,external_react_.useRef)(null);
5602
+ // WaveSurfer `setOptions` fires zoom/scroll before we apply the intended scroll.
5603
+ const suppressViewportSyncRef = (0,external_react_.useRef)(false);
5057
5604
  const bufferProgressRef = (0,external_react_.useRef)(0);
5058
5605
  const hoverProgressRef = (0,external_react_.useRef)(null);
5059
5606
  onSeekRef.current = onSeek;
@@ -5091,9 +5638,11 @@ function WaveformView({
5091
5638
  zoomLevel
5092
5639
  }), [canvasWidthPx, durationSec, height, maxZoom, scrollLeft, zoomLevel]);
5093
5640
  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) => {
5641
+ const onViewportCommit = (0,external_react_.useCallback)((scrollLeftPx, nextViewport, commitReactState = true) => {
5095
5642
  onViewportChangeRef.current?.(nextViewport);
5096
- setScrollLeft(scrollLeftPx);
5643
+ if (commitReactState) {
5644
+ setScrollLeft(scrollLeftPx);
5645
+ }
5097
5646
  }, []);
5098
5647
  const {
5099
5648
  apply: applyPlayheadCamera,
@@ -5104,7 +5653,8 @@ function WaveformView({
5104
5653
  isFollowPinned,
5105
5654
  onSeek: onPlayheadSeek,
5106
5655
  onZoom,
5107
- releaseUserPanHold
5656
+ releaseUserPanHold,
5657
+ seekTo
5108
5658
  } = usePlayheadCamera({
5109
5659
  mediaElRef,
5110
5660
  onViewportCommit,
@@ -5160,6 +5710,60 @@ function WaveformView({
5160
5710
  wavesurfer.setTime(timeSec);
5161
5711
  }
5162
5712
  }, [isFollowPinned]);
5713
+
5714
+ // Same zoom-window body as the zoom effect; extracted so resize can call it too.
5715
+ const applyZoomWindow = (0,external_react_.useCallback)(() => {
5716
+ const wavesurfer = wavesurferRef.current;
5717
+ const container = containerRef.current;
5718
+ if (!wavesurfer || !wavesurfer.setOptions || !container) {
5719
+ return;
5720
+ }
5721
+ const viewWidthPx = wavesurfer.getWidth ? wavesurfer.getWidth() : container.clientWidth;
5722
+ const minPxPerSec = getZoomedPixelsPerSecond({
5723
+ durationSec,
5724
+ maxZoom,
5725
+ viewWidthPx,
5726
+ zoomLevel
5727
+ });
5728
+ const origin = zoomOriginRef.current;
5729
+ zoomOriginRef.current = null;
5730
+ const didZoomChange = prevZoomRef.current !== zoomLevel;
5731
+ if (origin || didZoomChange) {
5732
+ onZoom();
5733
+ }
5734
+ suppressViewportSyncRef.current = true;
5735
+ try {
5736
+ wavesurfer.setOptions(WaveformView_objectSpread({
5737
+ autoScroll: false,
5738
+ minPxPerSec
5739
+ }, zoomLevel > constants/* WAVEFORM_ZOOM_MIN */.LW ? {
5740
+ progressColor: WAVEFORM_COLOR_PLAYED,
5741
+ waveColor: WAVEFORM_COLOR_UNPLAYED
5742
+ } : {}));
5743
+ if (minPxPerSec > 0) {
5744
+ if (origin) {
5745
+ applyScrollLeft(origin.timeSec * minPxPerSec - origin.pointerX, true);
5746
+ } else if (didZoomChange && !pointerZoomRef.current) {
5747
+ const zoomedViewport = createWaveformViewport({
5748
+ durationSec,
5749
+ heightPx: height,
5750
+ maxZoom,
5751
+ scrollLeftPx: 0,
5752
+ widthPx: viewWidthPx,
5753
+ zoomLevel
5754
+ });
5755
+ applyScrollLeft(Math.min(maxScrollLeft(zoomedViewport), Math.max(0, currentTimeRef.current * minPxPerSec - viewWidthPx / 2)), true);
5756
+ }
5757
+ }
5758
+ wavesurfer.setTime(currentTimeRef.current);
5759
+ } finally {
5760
+ suppressViewportSyncRef.current = false;
5761
+ prevZoomRef.current = zoomLevel;
5762
+ }
5763
+ syncViewport();
5764
+ updatePlayheadPosition(currentTimeRef.current);
5765
+ }, [applyScrollLeft, durationSec, height, maxZoom, onZoom, syncViewport, updatePlayheadPosition, zoomLevel]);
5766
+ applyZoomWindowRef.current = applyZoomWindow;
5163
5767
  (0,external_react_.useEffect)(() => {
5164
5768
  const container = containerRef.current;
5165
5769
  if (!container) {
@@ -5190,11 +5794,17 @@ function WaveformView({
5190
5794
  onSeekRef.current?.(relativeX * durationSecRef.current);
5191
5795
  });
5192
5796
  const unsubscribeScroll = wavesurfer.on('scroll', () => {
5797
+ if (suppressViewportSyncRef.current) {
5798
+ return;
5799
+ }
5193
5800
  handleCameraScroll(() => {
5194
5801
  syncViewport();
5195
5802
  });
5196
5803
  });
5197
5804
  const unsubscribeZoom = wavesurfer.on('zoom', () => {
5805
+ if (suppressViewportSyncRef.current) {
5806
+ return;
5807
+ }
5198
5808
  syncViewport();
5199
5809
  });
5200
5810
  const unsubscribeRedraw = wavesurfer.on('redrawcomplete', () => {
@@ -5208,7 +5818,9 @@ function WaveformView({
5208
5818
  });
5209
5819
  wavesurferRef.current = wavesurfer;
5210
5820
  displayedPeaksRef.current = peaksRef.current;
5821
+ disableScrollOverscroll(container);
5211
5822
  syncViewport();
5823
+ applyZoomWindowRef.current?.();
5212
5824
  return () => {
5213
5825
  releaseUserPanHold();
5214
5826
  cancelJump();
@@ -5248,7 +5860,8 @@ function WaveformView({
5248
5860
  }
5249
5861
  }, [internalZoom, isControlled, maxZoom, zoomLevelProp]);
5250
5862
  (0,external_react_.useEffect)(() => {
5251
- onViewportChange?.(viewport);
5863
+ // Follow skips React scrollLeft; emit the layout-refreshed live window instead.
5864
+ onViewportChange?.(viewportRef.current);
5252
5865
  }, [onViewportChange, viewport]);
5253
5866
  (0,external_react_.useEffect)(() => {
5254
5867
  const wavesurfer = wavesurferRef.current;
@@ -5259,52 +5872,9 @@ function WaveformView({
5259
5872
  interact: interactive
5260
5873
  });
5261
5874
  }, [interactive]);
5262
- (0,external_react_.useEffect)(() => {
5263
- const wavesurfer = wavesurferRef.current;
5264
- const container = containerRef.current;
5265
- if (!wavesurfer || !wavesurfer.setOptions || !container) {
5266
- return;
5267
- }
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]);
5875
+ (0,external_react_.useLayoutEffect)(() => {
5876
+ applyZoomWindow();
5877
+ }, [applyZoomWindow]);
5308
5878
  (0,external_react_.useLayoutEffect)(() => {
5309
5879
  updatePlayheadPosition(mediaEl ? mediaEl.currentTime : currentTime);
5310
5880
  }, [currentTime, durationSec, mediaEl, updatePlayheadPosition, viewport]);
@@ -5336,6 +5906,7 @@ function WaveformView({
5336
5906
  };
5337
5907
  const handleSeeked = () => {
5338
5908
  onPlayheadSeek(media.currentTime);
5909
+ seekTo(media.currentTime);
5339
5910
  updatePlayheadPosition(media.currentTime);
5340
5911
  };
5341
5912
  if (!media.paused) {
@@ -5354,7 +5925,7 @@ function WaveformView({
5354
5925
  media.removeEventListener('pause', stopLoop);
5355
5926
  media.removeEventListener('seeked', handleSeeked);
5356
5927
  };
5357
- }, [applyPlayheadCamera, cancelJump, clearFollowPin, mediaEl, onPlayheadSeek, releaseUserPanHold, syncViewport, updatePlayheadPosition]);
5928
+ }, [applyPlayheadCamera, cancelJump, clearFollowPin, mediaEl, onPlayheadSeek, releaseUserPanHold, seekTo, syncViewport, updatePlayheadPosition]);
5358
5929
  (0,external_react_.useEffect)(() => {
5359
5930
  const wavesurfer = wavesurferRef.current;
5360
5931
  if (!wavesurfer || !wavesurfer.setOptions || !(durationSec > 0)) {
@@ -5530,6 +6101,7 @@ function WaveformView({
5530
6101
  "data-testid": "bp-waveform-hover-time"
5531
6102
  }, formatTime(hoverProgress * durationSec)))));
5532
6103
  }
6104
+ /* harmony default export */ const waveform_WaveformView = (/*#__PURE__*/external_react_["default"].memo(WaveformView));
5533
6105
  // EXTERNAL MODULE: ./node_modules/classnames/index.js
5534
6106
  var classnames = __webpack_require__(2485);
5535
6107
  var classnames_default = /*#__PURE__*/__webpack_require__.n(classnames);
@@ -5680,15 +6252,18 @@ function WaveformZoomControl({
5680
6252
 
5681
6253
 
5682
6254
 
6255
+
5683
6256
  const PLACEHOLDER_PEAKS = placeholderPeaks();
5684
6257
  function MP3ControlsV2({
5685
6258
  autoplay,
5686
6259
  bufferedRange,
6260
+ commentMarkers,
5687
6261
  currentTime,
5688
6262
  durationTime,
5689
6263
  isPlaying,
5690
6264
  mediaEl,
5691
6265
  onAutoplayChange,
6266
+ onCommentMarkerClick,
5692
6267
  onMuteChange,
5693
6268
  onPlayPause,
5694
6269
  onRateChange,
@@ -5708,8 +6283,10 @@ function MP3ControlsV2({
5708
6283
  const hasMetadata = durationValue > 0;
5709
6284
  const waveformDurationSec = hasMetadata ? durationValue : PLACEHOLDER_DURATION_SEC;
5710
6285
  const [playRequested, setPlayRequested] = (0,external_react_.useState)(false);
5711
- const handleViewportChange = (0,external_react_.useCallback)(viewport => {
5712
- setMaxZoom(viewport.maxZoom);
6286
+ const [viewport, setViewport] = (0,external_react_.useState)(null);
6287
+ const handleViewportChange = (0,external_react_.useCallback)(next => {
6288
+ setMaxZoom(prev => prev === next.maxZoom ? prev : next.maxZoom);
6289
+ setViewport(prev => viewportEquals(prev, next) ? prev : next);
5713
6290
  }, []);
5714
6291
  const revealZoomControl = (0,external_react_.useCallback)(() => {
5715
6292
  setIsZoomRevealed(true);
@@ -5735,7 +6312,20 @@ function MP3ControlsV2({
5735
6312
  const handlePlayOverlayClick = (0,external_react_.useCallback)(() => {
5736
6313
  setPlayRequested(true);
5737
6314
  onPlayPause(true);
5738
- }, [onPlayPause]);
6315
+ // Overlay unmounts on click; without this, focus lands on body and Space is lost.
6316
+ mediaEl?.closest('.bp-media-container')?.focus();
6317
+ }, [mediaEl, onPlayPause]);
6318
+ const waveformMarkers = (0,external_react_.useMemo)(() => commentMarkers || [], [commentMarkers]);
6319
+ const selectedMarkerId = (0,external_react_.useMemo)(() => waveformMarkers.find(marker => marker.isSelected)?.id ?? null, [waveformMarkers]);
6320
+ const handleCommentMarkerClick = (0,external_react_.useCallback)(marker => {
6321
+ setPlayRequested(true);
6322
+ onPlayPause(false);
6323
+ if (onCommentMarkerClick) {
6324
+ onCommentMarkerClick(marker);
6325
+ return;
6326
+ }
6327
+ onTimeChange(marker.time);
6328
+ }, [onCommentMarkerClick, onPlayPause, onTimeChange]);
5739
6329
  const isWaveformInteractive = playRequested && hasMetadata;
5740
6330
  const isWaitingToPlay = playRequested && !hasMetadata;
5741
6331
  const showPlayOverlay = !playRequested && !isPlaying;
@@ -5746,7 +6336,9 @@ function MP3ControlsV2({
5746
6336
  "data-testid": "media-controls-wrapper-v2"
5747
6337
  }, /*#__PURE__*/external_react_["default"].createElement("div", {
5748
6338
  className: "bp-MP3ControlsV2-stage"
5749
- }, /*#__PURE__*/external_react_["default"].createElement(WaveformView, {
6339
+ }, /*#__PURE__*/external_react_["default"].createElement("div", {
6340
+ className: "bp-MP3ControlsV2-waveform"
6341
+ }, /*#__PURE__*/external_react_["default"].createElement(waveform_WaveformView, {
5750
6342
  bufferedRange: bufferedRange,
5751
6343
  currentTime: currentTime,
5752
6344
  durationSec: waveformDurationSec,
@@ -5757,7 +6349,13 @@ function MP3ControlsV2({
5757
6349
  onZoomChange: hasZoomHandlers ? handleWaveformZoom : undefined,
5758
6350
  peaks: waveformPeaks,
5759
6351
  zoomLevel: hasZoomHandlers ? zoomLevel : constants/* WAVEFORM_ZOOM_MIN */.LW
5760
- }), hasZoomControl && /*#__PURE__*/external_react_["default"].createElement("div", {
6352
+ }), /*#__PURE__*/external_react_["default"].createElement(WaveformCommentMarkers, {
6353
+ commentMarkers: waveformMarkers,
6354
+ durationSec: hasMetadata ? durationValue : 0,
6355
+ onCommentMarkerClick: handleCommentMarkerClick,
6356
+ selectedId: selectedMarkerId,
6357
+ viewport: viewport
6358
+ })), hasZoomControl && /*#__PURE__*/external_react_["default"].createElement("div", {
5761
6359
  className: "bp-MP3ControlsV2-waveformZoom"
5762
6360
  }, /*#__PURE__*/external_react_["default"].createElement(WaveformZoomControl, {
5763
6361
  isRevealed: isZoomRevealed,
@@ -5774,6 +6372,7 @@ function MP3ControlsV2({
5774
6372
  },
5775
6373
  "data-testid": "bp-MP3ControlsV2-play-overlay",
5776
6374
  onClick: handlePlayOverlayClick,
6375
+ onMouseDown: event => event.preventDefault(),
5777
6376
  title: "Play",
5778
6377
  type: "button"
5779
6378
  }), isWaitingToPlay && /*#__PURE__*/external_react_["default"].createElement("div", {
@@ -22321,7 +22920,7 @@ class Browser {
22321
22920
  ;// ./src/lib/Logger.js
22322
22921
  /* eslint-disable no-undef */
22323
22922
  const CLIENT_NAME = "box-content-preview";
22324
- const CLIENT_VERSION = "3.86.0";
22923
+ const CLIENT_VERSION = "3.87.0";
22325
22924
  /* eslint-enable no-undef */
22326
22925
 
22327
22926
  class Logger {
@@ -35041,6 +35640,49 @@ class MP3Viewer extends media_MediaBaseViewer {
35041
35640
  this.startClientWaveformDecode();
35042
35641
  }
35043
35642
  });
35643
+ /**
35644
+ * Honor the requested play/pause state. The play button passes the next state;
35645
+ * comment markers pass false so a click always pauses instead of toggling.
35646
+ *
35647
+ * @param {boolean} shouldPlay
35648
+ * @return {void}
35649
+ */
35650
+ MP3Viewer_defineProperty(this, "handlePlayPause", shouldPlay => {
35651
+ if (shouldPlay) {
35652
+ this.handlePlayRequest();
35653
+ return;
35654
+ }
35655
+ this.pause(undefined, true);
35656
+ });
35657
+ MP3Viewer_defineProperty(this, "handleCommentMarkersUpdated", (markers = []) => {
35658
+ this.commentMarkers = markers;
35659
+ const selected = markers.find(marker => marker.isSelected);
35660
+ const selectedId = selected ? selected.id : null;
35661
+ if (selectedId !== this.hostSelectedMarkerId) {
35662
+ this.hostSelectedMarkerId = selectedId;
35663
+ if (selected && Number.isFinite(selected.time)) {
35664
+ this.pendingHostSelectedSeek = selected;
35665
+ this.applyPendingHostSelectedSeek();
35666
+ } else {
35667
+ this.pendingHostSelectedSeek = null;
35668
+ }
35669
+ }
35670
+ this.renderUI();
35671
+ });
35672
+ MP3Viewer_defineProperty(this, "handleCommentMarkerClick", marker => {
35673
+ this.hostSelectedMarkerId = marker.id;
35674
+ this.pendingHostSelectedSeek = null;
35675
+ if (this.mediaEl) {
35676
+ this.mediaEl.pause();
35677
+ this.mediaEl.currentTime = marker.time;
35678
+ }
35679
+ // Overlay paints the ring from click optimism until the host re-emits selected.
35680
+ this.emit('comment_marker_select', {
35681
+ id: marker.id,
35682
+ time: marker.time
35683
+ });
35684
+ this.renderUI();
35685
+ });
35044
35686
  /**
35045
35687
  * Auto-play was prevented, pause the audio
35046
35688
  *
@@ -35075,6 +35717,7 @@ class MP3Viewer extends media_MediaBaseViewer {
35075
35717
  // Audio element
35076
35718
  this.mediaEl = this.mediaContainerEl.appendChild(document.createElement('audio'));
35077
35719
  this.mediaEl.setAttribute('preload', 'auto');
35720
+ this.commentMarkers = [];
35078
35721
  }
35079
35722
 
35080
35723
  /**
@@ -35128,7 +35771,7 @@ class MP3Viewer extends media_MediaBaseViewer {
35128
35771
  * @return {Promise<{ default: Function }>} MP3ControlsV2 module
35129
35772
  */
35130
35773
  importV2Controls() {
35131
- return Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, 3678));
35774
+ return Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, 9286));
35132
35775
  }
35133
35776
 
35134
35777
  /**
@@ -35150,6 +35793,7 @@ class MP3Viewer extends media_MediaBaseViewer {
35150
35793
  * @inheritdoc
35151
35794
  */
35152
35795
  destroy() {
35796
+ this.removeListener('comment_markers', this.handleCommentMarkersUpdated);
35153
35797
  this.abortClientWaveformDecode();
35154
35798
  super.destroy();
35155
35799
  }
@@ -35201,6 +35845,7 @@ class MP3Viewer extends media_MediaBaseViewer {
35201
35845
  if (!this.isAudioPlayerV2) {
35202
35846
  return;
35203
35847
  }
35848
+ this.applyPendingHostSelectedSeek();
35204
35849
 
35205
35850
  // Play first so Safari has a user gesture before AudioContext is created.
35206
35851
  if (this.userRequestedPlay) {
@@ -35337,9 +35982,30 @@ class MP3Viewer extends media_MediaBaseViewer {
35337
35982
  containerEl: this.mediaContainerEl
35338
35983
  });
35339
35984
  }
35985
+ if (this.isAudioPlayerV2) {
35986
+ this.removeListener('comment_markers', this.handleCommentMarkersUpdated);
35987
+ this.addListener('comment_markers', this.handleCommentMarkersUpdated);
35988
+ }
35340
35989
  this.renderUI();
35341
35990
  }
35342
-
35991
+ /**
35992
+ * Seek+pause to a host-selected comment (feed / deeplink). Audio-only; video does not
35993
+ * seek on comment_markers. No-ops until duration is known. Skips currentTime if the
35994
+ * element is already there so a feed seek does not fire a second `seeked`.
35995
+ *
35996
+ * @return {void}
35997
+ */
35998
+ applyPendingHostSelectedSeek() {
35999
+ const marker = this.pendingHostSelectedSeek;
36000
+ if (!marker || !this.mediaEl || !(this.mediaEl.duration > 0) || !Number.isFinite(marker.time)) {
36001
+ return;
36002
+ }
36003
+ this.pendingHostSelectedSeek = null;
36004
+ this.mediaEl.pause();
36005
+ if (this.mediaEl.currentTime !== marker.time) {
36006
+ this.mediaEl.currentTime = marker.time;
36007
+ }
36008
+ }
35343
36009
  /**
35344
36010
  * @inheritdoc
35345
36011
  */
@@ -35356,7 +36022,7 @@ class MP3Viewer extends media_MediaBaseViewer {
35356
36022
  movePlayback: this.movePlayback,
35357
36023
  onAutoplayChange: this.setAutoplay,
35358
36024
  onMuteChange: this.toggleMute,
35359
- onPlayPause: this.isAudioPlayerV2 ? this.handlePlayRequest : this.togglePlay,
36025
+ onPlayPause: this.isAudioPlayerV2 ? this.handlePlayPause : this.togglePlay,
35360
36026
  onRateChange: this.setRate,
35361
36027
  onTimeChange: this.handleTimeupdateFromMediaControls,
35362
36028
  onVolumeChange: this.setVolume,
@@ -35374,7 +36040,9 @@ class MP3Viewer extends media_MediaBaseViewer {
35374
36040
  }
35375
36041
  const Mp3ControlsV2 = this.MP3ControlsV2;
35376
36042
  this.controls.render(/*#__PURE__*/external_react_["default"].createElement(Mp3ControlsV2, MP3Viewer_extends({}, sharedProps, {
36043
+ commentMarkers: this.commentMarkers || [],
35377
36044
  mediaEl: this.mediaEl,
36045
+ onCommentMarkerClick: this.handleCommentMarkerClick,
35378
36046
  peaks: this.waveformPeaks
35379
36047
  })));
35380
36048
  return;
@@ -37002,57 +37670,8 @@ function VideoControls({
37002
37670
  }
37003
37671
  // EXTERNAL MODULE: ./src/lib/viewers/controls/media/TimestampControl.tsx + 1 modules
37004
37672
  var TimestampControl = __webpack_require__(3904);
37005
- ;// ./src/lib/viewers/controls/media/utils.ts
37006
- const utils_round = value => {
37007
- return +value.toFixed(4);
37008
- };
37009
- const utils_percent = (value1, value2) => {
37010
- return utils_round(value1 / value2 * 100);
37011
- };
37012
- ;// ./src/lib/viewers/controls/media/buildClusters.ts
37013
-
37014
-
37015
- /** Max pixel distance between adjacent markers (sorted by time) for them to be grouped into a single cluster. */
37016
- const CLUSTER_THRESHOLD_PX = 2;
37017
-
37018
- /** Converts a group of markers into a ClusterData object with computed positions and metadata. */
37019
- function finalizeCluster(group, durationValue) {
37020
- const leftPercent = utils_percent(group[0].time, durationValue);
37021
- const rightPercent = utils_percent(group[group.length - 1].time, durationValue);
37022
- const isSinglePoint = leftPercent === rightPercent;
37023
- return {
37024
- id: group.map(m => m.id).join('|'),
37025
- isSinglePoint,
37026
- leftPercent,
37027
- markers: group,
37028
- rightPercent
37029
- };
37030
- }
37031
-
37032
- /**
37033
- * Groups comment markers into clusters based on their pixel proximity on the scrubber track.
37034
- * Markers are sorted by time, then chained: each marker that is within CLUSTER_THRESHOLD_PX
37035
- * of its neighbor joins the same cluster. This means distant markers can end up in one cluster
37036
- * if intermediate markers bridge the gap.
37037
- */
37038
- function buildClusters(markers, durationValue, trackWidth, thresholdPx = CLUSTER_THRESHOLD_PX) {
37039
- if (durationValue <= 0 || markers.length === 0 || trackWidth <= 0) return [];
37040
- const sorted = [...markers].sort((a, b) => a.time - b.time);
37041
- const clusters = [];
37042
- let currentGroup = [sorted[0]];
37043
- for (let i = 1; i < sorted.length; i += 1) {
37044
- const prevPx = sorted[i - 1].time / durationValue * trackWidth;
37045
- const currPx = sorted[i].time / durationValue * trackWidth;
37046
- if (currPx - prevPx <= thresholdPx) {
37047
- currentGroup.push(sorted[i]);
37048
- } else {
37049
- clusters.push(finalizeCluster(currentGroup, durationValue));
37050
- currentGroup = [sorted[i]];
37051
- }
37052
- }
37053
- clusters.push(finalizeCluster(currentGroup, durationValue));
37054
- return clusters;
37055
- }
37673
+ // EXTERNAL MODULE: ./src/lib/viewers/controls/media/buildClusters.ts
37674
+ var buildClusters = __webpack_require__(2400);
37056
37675
  // EXTERNAL MODULE: ./src/lib/viewers/media/formatTimecode.ts
37057
37676
  var formatTimecode = __webpack_require__(4179);
37058
37677
  ;// ./src/lib/viewers/controls/media/FilmstripV2.scss
@@ -37133,156 +37752,8 @@ function FilmstripV2({
37133
37752
  "data-testid": "bp-FilmstripV2-time"
37134
37753
  }, fps ? (0,formatTimecode/* default */.A)(time, fps) : (0,DurationLabels/* formatTime */.f)(time)));
37135
37754
  }
37136
- ;// ./src/lib/viewers/controls/media/MarkerAvatar.scss
37137
- // extracted by mini-css-extract-plugin
37138
-
37139
- ;// ./src/lib/viewers/controls/media/MarkerAvatar.tsx
37140
-
37141
-
37142
- const AVATAR_PALETTE = [{
37143
- bg: '#7fb0ea',
37144
- fg: '#222'
37145
- }, {
37146
- bg: '#003c84',
37147
- fg: '#fff'
37148
- }, {
37149
- bg: '#ffeb7f',
37150
- fg: '#222'
37151
- }, {
37152
- bg: '#92e0c0',
37153
- fg: '#222'
37154
- }, {
37155
- bg: '#fad98d',
37156
- fg: '#222'
37157
- }, {
37158
- bg: '#91c2fd',
37159
- fg: '#222'
37160
- }, {
37161
- bg: '#f69bab',
37162
- fg: '#222'
37163
- }, {
37164
- bg: '#cf9ff6',
37165
- fg: '#222'
37166
- }, {
37167
- bg: '#f8c08c',
37168
- fg: '#222'
37169
- }, {
37170
- bg: '#a392e0',
37171
- fg: '#222'
37172
- }];
37173
- function AnonymousAvatarIcon() {
37174
- return /*#__PURE__*/external_react_["default"].createElement("svg", {
37175
- "aria-hidden": "true",
37176
- className: "bp-MarkerAvatar-anonymousIcon",
37177
- focusable: "false",
37178
- viewBox: "0 0 16 16"
37179
- }, /*#__PURE__*/external_react_["default"].createElement("path", {
37180
- d: "M8 8a3 3 0 1 0 0-6 3 3 0 0 0 0 6Zm0 1.5c-2.5 0-5 1.25-5 3.75V14h10v-.75c0-2.5-2.5-3.75-5-3.75Z",
37181
- fill: "currentColor"
37182
- }));
37183
- }
37184
- function MarkerAvatar({
37185
- avatarUrl,
37186
- colorIndex = 0,
37187
- initial,
37188
- size
37189
- }) {
37190
- const safeIndex = Number.isFinite(colorIndex) ? Math.abs(colorIndex) % AVATAR_PALETTE.length : 0;
37191
- const {
37192
- bg: bgColor,
37193
- fg: textColor
37194
- } = AVATAR_PALETTE[safeIndex];
37195
- const [imgFailed, setImgFailed] = external_react_["default"].useState(false);
37196
- const showImage = Boolean(avatarUrl) && !imgFailed;
37197
- let avatar = /*#__PURE__*/external_react_["default"].createElement(AnonymousAvatarIcon, null);
37198
- if (showImage) {
37199
- avatar = /*#__PURE__*/external_react_["default"].createElement("img", {
37200
- alt: "",
37201
- onError: () => setImgFailed(true),
37202
- src: avatarUrl
37203
- });
37204
- } else if (initial) {
37205
- avatar = /*#__PURE__*/external_react_["default"].createElement("span", {
37206
- className: "bp-MarkerAvatar-initial",
37207
- style: {
37208
- color: textColor
37209
- }
37210
- }, initial);
37211
- }
37212
- const style = {};
37213
- if (!showImage) {
37214
- style.backgroundColor = bgColor;
37215
- }
37216
- if (size) {
37217
- style.width = size;
37218
- style.height = size;
37219
- }
37220
- return /*#__PURE__*/external_react_["default"].createElement("span", {
37221
- className: "bp-MarkerAvatar",
37222
- style: Object.keys(style).length > 0 ? style : undefined
37223
- }, avatar);
37224
- }
37225
- ;// ./src/lib/viewers/controls/media/MarkerAvatarStack.scss
37226
- // extracted by mini-css-extract-plugin
37227
-
37228
- ;// ./src/lib/viewers/controls/media/MarkerAvatarStack.tsx
37229
-
37230
-
37231
-
37232
- const MAX_VISIBLE_AVATARS = 4;
37233
- function MarkerAvatarStack({
37234
- markers,
37235
- onMarkerClick,
37236
- overlapPx,
37237
- selectedId,
37238
- size
37239
- }) {
37240
- const hasOverflow = markers.length > MAX_VISIBLE_AVATARS;
37241
- const visibleMarkers = hasOverflow ? markers.slice(0, MAX_VISIBLE_AVATARS - 1) : markers;
37242
- const overflowMarkers = hasOverflow ? markers.slice(MAX_VISIBLE_AVATARS - 1) : [];
37243
- const isOverflowSelected = overflowMarkers.some(marker => marker.id === selectedId);
37244
- const style = overlapPx != null ? {
37245
- '--bp-marker-stack-overlap': `-${overlapPx}px`
37246
- } : undefined;
37247
- return /*#__PURE__*/external_react_["default"].createElement("span", {
37248
- className: "bp-MarkerAvatarStack",
37249
- style: style
37250
- }, visibleMarkers.map(marker => /*#__PURE__*/external_react_["default"].createElement("button", {
37251
- key: marker.id,
37252
- "aria-label": "Comment marker",
37253
- "aria-pressed": marker.id === selectedId,
37254
- className: `bp-MarkerAvatarStack-item${marker.id === selectedId ? ' bp-MarkerAvatarStack-item--selected' : ''}`,
37255
- "data-resin-target": "commentMarkerStackAvatar",
37256
- onClick: e => {
37257
- e.stopPropagation();
37258
- onMarkerClick?.(marker);
37259
- },
37260
- type: "button"
37261
- }, /*#__PURE__*/external_react_["default"].createElement(MarkerAvatar, {
37262
- avatarUrl: marker.avatarUrl,
37263
- colorIndex: marker.colorIndex,
37264
- initial: marker.initial,
37265
- size: size
37266
- }))), hasOverflow && /*#__PURE__*/external_react_["default"].createElement("button", {
37267
- "aria-label": "Comment marker",
37268
- "aria-pressed": isOverflowSelected,
37269
- className: `bp-MarkerAvatarStack-item bp-MarkerAvatarStack-overflow${isOverflowSelected ? ' bp-MarkerAvatarStack-item--selected' : ''}`,
37270
- "data-resin-target": "commentMarkerStackAvatarOverflow",
37271
- onClick: e => {
37272
- e.stopPropagation();
37273
- onMarkerClick?.(markers[MAX_VISIBLE_AVATARS - 1]);
37274
- },
37275
- type: "button"
37276
- }, /*#__PURE__*/external_react_["default"].createElement("span", {
37277
- className: "bp-MarkerAvatar bp-MarkerAvatarStack-overflowBadge",
37278
- style: size ? {
37279
- width: size,
37280
- height: size
37281
- } : undefined
37282
- }, /*#__PURE__*/external_react_["default"].createElement("span", {
37283
- className: "bp-MarkerAvatar-initial"
37284
- }, "+", markers.length - (MAX_VISIBLE_AVATARS - 1)))));
37285
- }
37755
+ // EXTERNAL MODULE: ./src/lib/viewers/controls/media/MarkerAvatarStack.tsx + 1 modules
37756
+ var MarkerAvatarStack = __webpack_require__(9232);
37286
37757
  ;// ./src/lib/viewers/controls/media/MarkerCluster.scss
37287
37758
  // extracted by mini-css-extract-plugin
37288
37759
 
@@ -37316,11 +37787,13 @@ function MarkerCluster({
37316
37787
  onMarkerClick?.(markers[0]);
37317
37788
  },
37318
37789
  type: "button"
37319
- }), /*#__PURE__*/external_react_["default"].createElement(MarkerAvatarStack, {
37790
+ }), /*#__PURE__*/external_react_["default"].createElement(MarkerAvatarStack/* default */.A, {
37320
37791
  markers: markers,
37321
37792
  onMarkerClick: onMarkerClick
37322
37793
  }));
37323
37794
  }
37795
+ // EXTERNAL MODULE: ./src/lib/viewers/controls/media/MarkerAvatar.tsx + 1 modules
37796
+ var MarkerAvatar = __webpack_require__(3524);
37324
37797
  ;// ./src/lib/viewers/controls/media/MarkerTick.tsx
37325
37798
 
37326
37799
 
@@ -37344,15 +37817,17 @@ function MarkerTick({
37344
37817
  left: `${position}%`
37345
37818
  },
37346
37819
  type: "button"
37347
- }, isGroup ? /*#__PURE__*/external_react_["default"].createElement(MarkerAvatarStack, {
37820
+ }, isGroup ? /*#__PURE__*/external_react_["default"].createElement(MarkerAvatarStack/* default */.A, {
37348
37821
  markers: markers,
37349
37822
  onMarkerClick: onMarkerClick
37350
- }) : /*#__PURE__*/external_react_["default"].createElement(MarkerAvatar, {
37823
+ }) : /*#__PURE__*/external_react_["default"].createElement(MarkerAvatar/* default */.A, {
37351
37824
  avatarUrl: markers[0].avatarUrl,
37352
37825
  colorIndex: markers[0].colorIndex,
37353
37826
  initial: markers[0].initial
37354
37827
  }));
37355
37828
  }
37829
+ // EXTERNAL MODULE: ./src/lib/viewers/controls/media/utils.ts
37830
+ var utils = __webpack_require__(5346);
37356
37831
  ;// ./src/lib/viewers/controls/media/TimeControlsV2.scss
37357
37832
  // extracted by mini-css-extract-plugin
37358
37833
 
@@ -37390,9 +37865,9 @@ function TimeControlsV2({
37390
37865
  const scrubberRef = external_react_["default"].useRef(null);
37391
37866
  const currentValue = isFinite_default()(currentTime) ? currentTime : 0;
37392
37867
  const durationValue = isFinite_default()(durationTime) ? durationTime : 0;
37393
- const currentPercentage = utils_percent(currentValue, durationValue);
37868
+ const currentPercentage = (0,utils/* percent */.K)(currentValue, durationValue);
37394
37869
  const bufferedAmount = bufferedRange && bufferedRange.length ? bufferedRange.end(bufferedRange.length - 1) : 0;
37395
- const bufferedPercentage = utils_percent(bufferedAmount, durationValue);
37870
+ const bufferedPercentage = (0,utils/* percent */.K)(bufferedAmount, durationValue);
37396
37871
  external_react_["default"].useLayoutEffect(() => {
37397
37872
  const el = scrubberRef.current;
37398
37873
  if (!el) return undefined;
@@ -37405,7 +37880,7 @@ function TimeControlsV2({
37405
37880
  observer.observe(el);
37406
37881
  return () => observer.disconnect();
37407
37882
  }, []);
37408
- const clusters = external_react_["default"].useMemo(() => buildClusters(commentMarkers, durationValue, trackWidth), [commentMarkers, durationValue, trackWidth]);
37883
+ const clusters = external_react_["default"].useMemo(() => (0,buildClusters/* default */.A)(commentMarkers, durationValue, trackWidth), [commentMarkers, durationValue, trackWidth]);
37409
37884
  const trackMask = external_react_["default"].useMemo(() => {
37410
37885
  if (durationValue <= 0 || clusters.length === 0) return undefined;
37411
37886
  const HALF_GAP = 2;