box-content-preview 3.88.0 → 3.90.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
@@ -327,6 +327,7 @@ class PreviewError extends Error {
327
327
  /* harmony export */ Jm: () => (/* binding */ SELECTOR_BOX_PREVIEW_LOGO_CUSTOM),
328
328
  /* harmony export */ Kb: () => (/* binding */ CLASS_BOX_PREVIEW_THUMBNAILS_CONTAINER),
329
329
  /* harmony export */ Kh: () => (/* binding */ SELECTOR_NAVIGATION_RIGHT),
330
+ /* harmony export */ Ku: () => (/* binding */ AUDIO_PLAYER_V2),
330
331
  /* harmony export */ Lq: () => (/* binding */ PREVIEW_SCRIPT_NAME),
331
332
  /* harmony export */ Lz: () => (/* binding */ CLASS_BOX_PREVIEW_THUMBNAILS_CLOSE),
332
333
  /* harmony export */ M$: () => (/* binding */ CLASS_BOX_PREVIEW_PRELOAD_WRAPPER_DOCUMENT),
@@ -379,6 +380,7 @@ class PreviewError extends Error {
379
380
  /* harmony export */ jF: () => (/* binding */ CLASS_IS_VISIBLE),
380
381
  /* harmony export */ jP: () => (/* binding */ CLASS_PREVIEW_LOADED),
381
382
  /* harmony export */ jf: () => (/* binding */ CLASS_FULLSCREEN_UNSUPPORTED),
383
+ /* harmony export */ jr: () => (/* binding */ X_REP_HINT_WAVEFORM),
382
384
  /* harmony export */ lM: () => (/* binding */ CLASS_BOX_PREVIEW_MOBILE),
383
385
  /* harmony export */ lR: () => (/* binding */ SELECTOR_BOX_PREVIEW_HEADER_CONTAINER),
384
386
  /* harmony export */ lX: () => (/* binding */ CLASS_BOX_PREVIEW_PRELOAD_OVERLAY),
@@ -396,6 +398,8 @@ class PreviewError extends Error {
396
398
  /* harmony export */ rR: () => (/* binding */ CLASS_BOX_PREVIEW_THEME_DARK),
397
399
  /* harmony export */ rZ: () => (/* binding */ CLASS_BOX_PREVIEW_OVERLAY),
398
400
  /* harmony export */ rr: () => (/* binding */ CLASS_IS_BUFFERING),
401
+ /* harmony export */ u4: () => (/* binding */ WAVEFORM_REP_NAME),
402
+ /* harmony export */ uD: () => (/* binding */ STATUS_ERROR),
399
403
  /* harmony export */ uN: () => (/* binding */ MEDIA_PLAYBACK_SKIP_DURATION),
400
404
  /* harmony export */ vV: () => (/* binding */ CLASS_BOX_PREVIEW_PRELOAD_WRAPPER_PRESENTATION),
401
405
  /* harmony export */ v_: () => (/* binding */ SELECTOR_BOX_PREVIEW_BTN_DOWNLOAD),
@@ -415,7 +419,7 @@ class PreviewError extends Error {
415
419
  /* harmony export */ zb: () => (/* binding */ CLASS_DOC_FIRST_IMAGE),
416
420
  /* harmony export */ ze: () => (/* binding */ STATUS_PENDING)
417
421
  /* harmony export */ });
418
- /* unused harmony exports CLASS_ACTIVE, CLASS_BOX_PREVIEW, CLASS_BOX_PREVIEW_CONTAINER, CLASS_BOX_PREVIEW_HEADER_CONTAINER, CLASS_BOX_PREVIEW_ICON, CLASS_BOX_PREVIEW_LOGO_CUSTOM, CLASS_BOX_PREVIEW_LOGO_DEFAULT, CLASS_PREFETCHED_IMAGE, SELECTOR_BOX_PREVIEW_HEADER, SELECTOR_BOX_PREVIEW_NOTIFICATION, STATUS_ERROR */
422
+ /* unused harmony exports CLASS_ACTIVE, CLASS_BOX_PREVIEW, CLASS_BOX_PREVIEW_CONTAINER, CLASS_BOX_PREVIEW_HEADER_CONTAINER, CLASS_BOX_PREVIEW_ICON, CLASS_BOX_PREVIEW_LOGO_CUSTOM, CLASS_BOX_PREVIEW_LOGO_DEFAULT, CLASS_PREFETCHED_IMAGE, SELECTOR_BOX_PREVIEW_HEADER, SELECTOR_BOX_PREVIEW_NOTIFICATION */
419
423
  const CLASS_ACTIVE = 'bp-is-active';
420
424
  const CLASS_ANNOTATIONS_CREATE_DRAWING = 'bp-annotations-create--drawing';
421
425
  const CLASS_ANNOTATIONS_CREATE_HIGHLIGHT = 'bp-annotations-create--highlight';
@@ -502,6 +506,7 @@ const APP_HOST = 'https://app.box.com';
502
506
  const ORIGINAL_REP_NAME = 'ORIGINAL';
503
507
  const PRELOAD_REP_NAME = 'jpg';
504
508
  const PRELOAD_PAGED_REP_NAME = 'webp';
509
+ const WAVEFORM_REP_NAME = 'waveform';
505
510
  const VIDEO_VIEWER_NAMES = ['Dash', 'MP4'];
506
511
  const STATUS_ERROR = 'error';
507
512
  const STATUS_NONE = 'none';
@@ -516,7 +521,9 @@ const X_REP_HINT_IMAGE = '[jpg?dimensions=2048x2048,png?dimensions=2048x2048]';
516
521
  const X_REP_HINT_VIDEO_DASH = '[dash,mp4][filmstrip]';
517
522
  const X_REP_HINT_VIDEO_DASH_EXTRACTED_TEXT = '[extracted_text]';
518
523
  const X_REP_HINT_VIDEO_MP4 = '[mp4]';
524
+ const X_REP_HINT_WAVEFORM = '[waveform]';
519
525
  const AI_TRANSCRIPTION_FOR_VIDEO_SUBTITLES = 'aiTranscriptionForVideoSubtitles';
526
+ const AUDIO_PLAYER_V2 = 'audioPlayerV2.enabled';
520
527
  const PDFJS_CSS_UNITS = 96.0 / 72.0; // Should match CSS_UNITS in pdf_viewer.js
521
528
  const PDFJS_MAX_AUTO_SCALE = 1.25; // Should match MAX_AUTO_SCALE in pdf_viewer.js
522
529
  const PDFJS_WIDTH_PADDING_PX = 40; // Should match SCROLLBAR_PADDING in pdf_viewer.js
@@ -1010,7 +1017,7 @@ function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e =
1010
1017
  const CLIENT_NAME = "box-content-preview"; // eslint-disable-line no-undef
1011
1018
  const CLIENT_NAME_KEY = 'box_client_name';
1012
1019
  const CLIENT_VERSION_KEY = 'box_client_version';
1013
- const CLIENT_VERSION = "3.88.0"; // eslint-disable-line no-undef
1020
+ const CLIENT_VERSION = "3.90.0"; // eslint-disable-line no-undef
1014
1021
  const HEADER_CLIENT_NAME = 'X-Box-Client-Name';
1015
1022
  const HEADER_CLIENT_VERSION = 'X-Box-Client-Version';
1016
1023
  const PROMISE_MAP = {};
@@ -2135,204 +2142,6 @@ function DurationLabels({
2135
2142
 
2136
2143
  /***/ },
2137
2144
 
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
-
2336
2145
  /***/ 6929
2337
2146
  (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
2338
2147
 
@@ -3443,7 +3252,209 @@ function VolumeControls({
3443
3252
 
3444
3253
  /***/ },
3445
3254
 
3446
- /***/ 2400
3255
+ /***/ 2040
3256
+ (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
3257
+
3258
+
3259
+ // EXPORTS
3260
+ __webpack_require__.d(__webpack_exports__, {
3261
+ A: () => (/* binding */ MarkerAvatar)
3262
+ });
3263
+
3264
+ // EXTERNAL MODULE: external "react"
3265
+ var external_react_ = __webpack_require__(1649);
3266
+ ;// ./src/lib/viewers/controls/media/markers/MarkerAvatar.scss
3267
+ // extracted by mini-css-extract-plugin
3268
+
3269
+ ;// ./src/lib/viewers/controls/media/markers/MarkerAvatar.tsx
3270
+ function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
3271
+
3272
+
3273
+ const AVATAR_PALETTE = [{
3274
+ bg: '#7fb0ea',
3275
+ fg: '#222'
3276
+ }, {
3277
+ bg: '#003c84',
3278
+ fg: '#fff'
3279
+ }, {
3280
+ bg: '#ffeb7f',
3281
+ fg: '#222'
3282
+ }, {
3283
+ bg: '#92e0c0',
3284
+ fg: '#222'
3285
+ }, {
3286
+ bg: '#fad98d',
3287
+ fg: '#222'
3288
+ }, {
3289
+ bg: '#91c2fd',
3290
+ fg: '#222'
3291
+ }, {
3292
+ bg: '#f69bab',
3293
+ fg: '#222'
3294
+ }, {
3295
+ bg: '#cf9ff6',
3296
+ fg: '#222'
3297
+ }, {
3298
+ bg: '#f8c08c',
3299
+ fg: '#222'
3300
+ }, {
3301
+ bg: '#a392e0',
3302
+ fg: '#222'
3303
+ }];
3304
+ function AnonymousAvatarIcon() {
3305
+ return /*#__PURE__*/external_react_["default"].createElement("svg", {
3306
+ "aria-hidden": "true",
3307
+ className: "bp-MarkerAvatar-anonymousIcon",
3308
+ focusable: "false",
3309
+ viewBox: "0 0 16 16"
3310
+ }, /*#__PURE__*/external_react_["default"].createElement("path", {
3311
+ 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",
3312
+ fill: "currentColor"
3313
+ }));
3314
+ }
3315
+ function MarkerAvatar({
3316
+ avatarUrl,
3317
+ colorIndex = 0,
3318
+ initial,
3319
+ size
3320
+ }) {
3321
+ const safeIndex = Number.isFinite(colorIndex) ? Math.abs(colorIndex) % AVATAR_PALETTE.length : 0;
3322
+ const {
3323
+ bg: bgColor,
3324
+ fg: textColor
3325
+ } = AVATAR_PALETTE[safeIndex];
3326
+ const [imgFailed, setImgFailed] = external_react_["default"].useState(false);
3327
+ const [loadedUrl, setLoadedUrl] = external_react_["default"].useState(null);
3328
+ const imgRef = external_react_["default"].useRef(null);
3329
+ // Keep initial + palette until onLoad (and for cached complete). Shared with video ticks.
3330
+ const showImage = Boolean(avatarUrl) && !imgFailed && loadedUrl === avatarUrl;
3331
+ external_react_["default"].useEffect(() => {
3332
+ setImgFailed(false);
3333
+ }, [avatarUrl]);
3334
+ external_react_["default"].useLayoutEffect(() => {
3335
+ const img = imgRef.current;
3336
+ if (avatarUrl && img?.complete && img.naturalWidth > 0) {
3337
+ setLoadedUrl(avatarUrl);
3338
+ }
3339
+ }, [avatarUrl]);
3340
+ let fallback = /*#__PURE__*/external_react_["default"].createElement(AnonymousAvatarIcon, null);
3341
+ if (initial) {
3342
+ fallback = /*#__PURE__*/external_react_["default"].createElement("span", {
3343
+ className: "bp-MarkerAvatar-initial",
3344
+ style: {
3345
+ color: textColor
3346
+ }
3347
+ }, initial);
3348
+ }
3349
+ const style = {};
3350
+ if (!showImage) {
3351
+ style.backgroundColor = bgColor;
3352
+ }
3353
+ if (size) {
3354
+ _extends(style, {
3355
+ '--bp-marker-avatar-size': `${size}px`
3356
+ });
3357
+ }
3358
+ return /*#__PURE__*/external_react_["default"].createElement("span", {
3359
+ className: "bp-MarkerAvatar",
3360
+ style: Object.keys(style).length > 0 ? style : undefined
3361
+ }, avatarUrl && !imgFailed && /*#__PURE__*/external_react_["default"].createElement("img", {
3362
+ ref: imgRef,
3363
+ alt: "",
3364
+ onError: () => {
3365
+ setImgFailed(true);
3366
+ },
3367
+ onLoad: () => {
3368
+ if (avatarUrl) {
3369
+ setLoadedUrl(avatarUrl);
3370
+ }
3371
+ },
3372
+ src: avatarUrl
3373
+ }), !showImage && fallback);
3374
+ }
3375
+
3376
+ /***/ },
3377
+
3378
+ /***/ 308
3379
+ (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
3380
+
3381
+
3382
+ // EXPORTS
3383
+ __webpack_require__.d(__webpack_exports__, {
3384
+ A: () => (/* binding */ MarkerAvatarStack)
3385
+ });
3386
+
3387
+ // EXTERNAL MODULE: external "react"
3388
+ var external_react_ = __webpack_require__(1649);
3389
+ // EXTERNAL MODULE: ./src/lib/viewers/controls/media/markers/MarkerAvatar.tsx + 1 modules
3390
+ var MarkerAvatar = __webpack_require__(2040);
3391
+ ;// ./src/lib/viewers/controls/media/markers/MarkerAvatarStack.scss
3392
+ // extracted by mini-css-extract-plugin
3393
+
3394
+ ;// ./src/lib/viewers/controls/media/markers/MarkerAvatarStack.tsx
3395
+
3396
+
3397
+
3398
+ const MAX_VISIBLE_AVATARS = 4;
3399
+ function MarkerAvatarStack({
3400
+ markers,
3401
+ onMarkerClick,
3402
+ overlapPx,
3403
+ selectedId,
3404
+ size
3405
+ }) {
3406
+ const hasOverflow = markers.length > MAX_VISIBLE_AVATARS;
3407
+ const visibleMarkers = hasOverflow ? markers.slice(0, MAX_VISIBLE_AVATARS - 1) : markers;
3408
+ const overflowMarkers = hasOverflow ? markers.slice(MAX_VISIBLE_AVATARS - 1) : [];
3409
+ const isOverflowSelected = overflowMarkers.some(marker => marker.id === selectedId);
3410
+ const style = overlapPx != null ? {
3411
+ '--bp-marker-stack-overlap': `-${overlapPx}px`
3412
+ } : undefined;
3413
+ return /*#__PURE__*/external_react_["default"].createElement("span", {
3414
+ className: "bp-MarkerAvatarStack",
3415
+ style: style
3416
+ }, visibleMarkers.map(marker => /*#__PURE__*/external_react_["default"].createElement("button", {
3417
+ key: marker.id,
3418
+ "aria-label": "Comment marker",
3419
+ "aria-pressed": marker.id === selectedId,
3420
+ className: `bp-MarkerAvatarStack-item${marker.id === selectedId ? ' bp-MarkerAvatarStack-item--selected' : ''}`,
3421
+ "data-bp-marker-selected": marker.id === selectedId ? '' : undefined,
3422
+ "data-resin-target": "commentMarkerStackAvatar",
3423
+ onClick: e => {
3424
+ e.stopPropagation();
3425
+ onMarkerClick?.(marker);
3426
+ },
3427
+ type: "button"
3428
+ }, /*#__PURE__*/external_react_["default"].createElement(MarkerAvatar/* default */.A, {
3429
+ avatarUrl: marker.avatarUrl,
3430
+ colorIndex: marker.colorIndex,
3431
+ initial: marker.initial,
3432
+ size: size
3433
+ }))), hasOverflow && /*#__PURE__*/external_react_["default"].createElement("button", {
3434
+ "aria-label": "Comment marker",
3435
+ "aria-pressed": isOverflowSelected,
3436
+ className: `bp-MarkerAvatarStack-item bp-MarkerAvatarStack-overflow${isOverflowSelected ? ' bp-MarkerAvatarStack-item--selected' : ''}`,
3437
+ "data-bp-marker-selected": isOverflowSelected ? '' : undefined,
3438
+ "data-resin-target": "commentMarkerStackAvatarOverflow",
3439
+ onClick: e => {
3440
+ e.stopPropagation();
3441
+ const overflowTarget = overflowMarkers.find(marker => marker.id === selectedId) ?? overflowMarkers[0];
3442
+ onMarkerClick?.(overflowTarget);
3443
+ },
3444
+ type: "button"
3445
+ }, /*#__PURE__*/external_react_["default"].createElement("span", {
3446
+ className: "bp-MarkerAvatar bp-MarkerAvatarStack-overflowBadge",
3447
+ style: size ? {
3448
+ '--bp-marker-avatar-size': `${size}px`
3449
+ } : undefined
3450
+ }, /*#__PURE__*/external_react_["default"].createElement("span", {
3451
+ className: "bp-MarkerAvatar-initial"
3452
+ }, "+", markers.length - (MAX_VISIBLE_AVATARS - 1)))));
3453
+ }
3454
+
3455
+ /***/ },
3456
+
3457
+ /***/ 5510
3447
3458
  (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
3448
3459
 
3449
3460
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
@@ -3496,6 +3507,97 @@ function buildClusters(markers, durationValue, trackWidth, thresholdPx = CLUSTER
3496
3507
 
3497
3508
  /***/ },
3498
3509
 
3510
+ /***/ 2378
3511
+ (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
3512
+
3513
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
3514
+ /* harmony export */ A: () => (/* binding */ useDismissableMarkerSelection)
3515
+ /* harmony export */ });
3516
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1649);
3517
+
3518
+ function eventTargetElement(target) {
3519
+ if (target instanceof Element) {
3520
+ return target;
3521
+ }
3522
+ if (target instanceof Text) {
3523
+ return target.parentElement;
3524
+ }
3525
+ return null;
3526
+ }
3527
+
3528
+ /**
3529
+ * Optimistic comment-badge selection that clears on pointerdown outside the
3530
+ * selected avatar or its cluster. Host `hostSelectedId` can restore a different
3531
+ * id; acking the dismissed id must not bring the ring back.
3532
+ */
3533
+ function useDismissableMarkerSelection(hostSelectedId = null) {
3534
+ const containerRef = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(null);
3535
+ const dismissedIdRef = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(null);
3536
+ const [optimisticSelectedId, setOptimisticSelectedId] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(null);
3537
+ const [isSelectionDismissed, setIsSelectionDismissed] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false);
3538
+ const selectedId = optimisticSelectedId ?? (isSelectionDismissed ? null : hostSelectedId);
3539
+ (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
3540
+ setOptimisticSelectedId(null);
3541
+ if (dismissedIdRef.current && hostSelectedId === dismissedIdRef.current) {
3542
+ return;
3543
+ }
3544
+ dismissedIdRef.current = null;
3545
+ setIsSelectionDismissed(false);
3546
+ }, [hostSelectedId]);
3547
+ const selectMarker = (0,react__WEBPACK_IMPORTED_MODULE_0__.useCallback)(id => {
3548
+ dismissedIdRef.current = null;
3549
+ setIsSelectionDismissed(false);
3550
+ setOptimisticSelectedId(id);
3551
+ }, []);
3552
+ (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
3553
+ if (!selectedId) {
3554
+ return undefined;
3555
+ }
3556
+ const isEventInsideSelectedBadge = target => {
3557
+ const container = containerRef.current;
3558
+ if (!container) {
3559
+ return true;
3560
+ }
3561
+ const selected = container.querySelector('[data-bp-marker-selected]');
3562
+ if (!selected) {
3563
+ return true;
3564
+ }
3565
+ const el = eventTargetElement(target);
3566
+ if (!el) {
3567
+ return true;
3568
+ }
3569
+ if (selected.contains(el)) {
3570
+ return true;
3571
+ }
3572
+ const group = el.closest('[data-bp-marker-group]');
3573
+ return Boolean(group && group.contains(selected));
3574
+ };
3575
+ const onDocumentPointerDown = event => {
3576
+ if (isEventInsideSelectedBadge(event.target)) {
3577
+ return;
3578
+ }
3579
+ dismissedIdRef.current = selectedId;
3580
+ setOptimisticSelectedId(null);
3581
+ setIsSelectionDismissed(true);
3582
+ const active = document.activeElement;
3583
+ if (active instanceof HTMLElement && containerRef.current?.contains(active)) {
3584
+ active.blur();
3585
+ }
3586
+ };
3587
+ document.addEventListener('pointerdown', onDocumentPointerDown, true);
3588
+ return () => {
3589
+ document.removeEventListener('pointerdown', onDocumentPointerDown, true);
3590
+ };
3591
+ }, [selectedId]);
3592
+ return {
3593
+ containerRef,
3594
+ selectMarker,
3595
+ selectedId
3596
+ };
3597
+ }
3598
+
3599
+ /***/ },
3600
+
3499
3601
  /***/ 5346
3500
3602
  (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
3501
3603
 
@@ -4748,12 +4850,14 @@ function getPlayheadCameraAction({
4748
4850
  type: 'none'
4749
4851
  };
4750
4852
  }
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);
4853
+ // EXTERNAL MODULE: ./src/lib/viewers/controls/media/markers/helpers/useDismissableMarkerSelection.ts
4854
+ var useDismissableMarkerSelection = __webpack_require__(2378);
4855
+ // EXTERNAL MODULE: ./src/lib/viewers/controls/media/markers/helpers/buildClusters.ts
4856
+ var buildClusters = __webpack_require__(5510);
4857
+ // EXTERNAL MODULE: ./src/lib/viewers/controls/media/markers/MarkerAvatarStack.tsx + 1 modules
4858
+ var MarkerAvatarStack = __webpack_require__(308);
4859
+ // EXTERNAL MODULE: ./src/lib/viewers/controls/media/markers/MarkerAvatar.tsx + 1 modules
4860
+ var MarkerAvatar = __webpack_require__(2040);
4757
4861
  // EXTERNAL MODULE: ./src/lib/viewers/controls/media/utils.ts
4758
4862
  var utils = __webpack_require__(5346);
4759
4863
  ;// ./src/lib/viewers/media/waveform/WaveformCommentMarkers.scss
@@ -4765,8 +4869,6 @@ var utils = __webpack_require__(5346);
4765
4869
 
4766
4870
 
4767
4871
 
4768
-
4769
-
4770
4872
  const WAVEFORM_MARKER_SIZE_PX = 20;
4771
4873
  function hasMappedWindow(viewport) {
4772
4874
  return !!viewport && Number.isFinite(viewport.startSec) && viewport.endSec > viewport.startSec;
@@ -4809,25 +4911,16 @@ function WaveformCommentMarkers({
4809
4911
  selectedId: hostSelectedId = null,
4810
4912
  viewport = null
4811
4913
  }) {
4812
- const overlayRef = (0,external_react_.useRef)(null);
4813
4914
  const trackRef = (0,external_react_.useRef)(null);
4814
- const dismissedIdRef = (0,external_react_.useRef)(null);
4915
+ const {
4916
+ containerRef,
4917
+ selectMarker,
4918
+ selectedId
4919
+ } = (0,useDismissableMarkerSelection/* default */.A)(hostSelectedId);
4815
4920
  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
4921
  const canShowTrack = durationSec > 0 && commentMarkers.length > 0;
4820
4922
  const zoomLevel = viewport?.zoomLevel ?? constants/* WAVEFORM_ZOOM_MIN */.LW;
4821
4923
  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
4924
  (0,external_react_.useLayoutEffect)(() => {
4832
4925
  if (!canShowTrack) {
4833
4926
  setTrackWidth(0);
@@ -4855,44 +4948,14 @@ function WaveformCommentMarkers({
4855
4948
  }, [commentMarkers, durationSec, trackWidth, zoomLevel]);
4856
4949
  const handleMarkerClick = (0,external_react_.useCallback)((marker, event) => {
4857
4950
  event?.stopPropagation();
4858
- dismissedIdRef.current = null;
4859
- setIsSelectionDismissed(false);
4860
- setOptimisticSelectedId(marker.id);
4951
+ selectMarker(marker.id);
4861
4952
  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]);
4953
+ }, [onCommentMarkerClick, selectMarker]);
4891
4954
  if (!(durationSec > 0) || commentMarkers.length === 0) {
4892
4955
  return null;
4893
4956
  }
4894
4957
  return /*#__PURE__*/external_react_["default"].createElement("div", {
4895
- ref: overlayRef,
4958
+ ref: containerRef,
4896
4959
  className: `bp-WaveformCommentMarkers${isZoomed ? ' bp-WaveformCommentMarkers--zoomed' : ''}`,
4897
4960
  "data-testid": "bp-waveform-comment-markers"
4898
4961
  }, /*#__PURE__*/external_react_["default"].createElement("div", {
@@ -4908,6 +4971,7 @@ function WaveformCommentMarkers({
4908
4971
  return /*#__PURE__*/external_react_["default"].createElement("div", {
4909
4972
  key: cluster.id,
4910
4973
  className: className,
4974
+ "data-bp-marker-group": "",
4911
4975
  "data-testid": "bp-waveform-comment-marker",
4912
4976
  style: {
4913
4977
  left
@@ -4916,8 +4980,7 @@ function WaveformCommentMarkers({
4916
4980
  markers: cluster.markers,
4917
4981
  onMarkerClick: handleMarkerClick,
4918
4982
  overlapPx: WAVEFORM_MARKER_SIZE_PX / 2,
4919
- selectedId: selectedId,
4920
- size: WAVEFORM_MARKER_SIZE_PX
4983
+ selectedId: selectedId
4921
4984
  }));
4922
4985
  }
4923
4986
  return /*#__PURE__*/external_react_["default"].createElement("button", {
@@ -4925,6 +4988,7 @@ function WaveformCommentMarkers({
4925
4988
  "aria-label": "Comment marker",
4926
4989
  "aria-pressed": isSelected,
4927
4990
  className: className,
4991
+ "data-bp-marker-selected": isSelected ? '' : undefined,
4928
4992
  "data-resin-target": "commentMarker",
4929
4993
  "data-testid": "bp-waveform-comment-marker",
4930
4994
  onClick: event => handleMarkerClick(marker, event),
@@ -4935,8 +4999,7 @@ function WaveformCommentMarkers({
4935
4999
  }, /*#__PURE__*/external_react_["default"].createElement(MarkerAvatar/* default */.A, {
4936
5000
  avatarUrl: marker.avatarUrl,
4937
5001
  colorIndex: marker.colorIndex,
4938
- initial: marker.initial,
4939
- size: WAVEFORM_MARKER_SIZE_PX
5002
+ initial: marker.initial
4940
5003
  }));
4941
5004
  })));
4942
5005
  }
@@ -6319,14 +6382,16 @@ function MP3ControlsV2({
6319
6382
  volume
6320
6383
  }) {
6321
6384
  const durationValue = typeof durationTime === 'number' && isFinite_default()(durationTime) ? durationTime : 0;
6385
+ const mediaDuration = mediaEl ? mediaEl.duration : NaN;
6386
+ const hasMediaMetadata = typeof mediaDuration === 'number' && Number.isFinite(mediaDuration) && mediaDuration > 0;
6387
+ const hasWaveformDuration = durationValue > 0;
6322
6388
  const [zoomLevel, setZoomLevel] = (0,external_react_.useState)(constants/* WAVEFORM_ZOOM_MIN */.LW);
6323
6389
  const [maxZoom, setMaxZoom] = (0,external_react_.useState)(constants/* WAVEFORM_ZOOM_MIN */.LW);
6324
6390
  const [isZoomRevealed, setIsZoomRevealed] = (0,external_react_.useState)(false);
6325
6391
  const zoomRevealTimerRef = (0,external_react_.useRef)(0);
6326
6392
  const hasRealPeaks = !!(peaks && peaks.length);
6327
6393
  const waveformPeaks = hasRealPeaks ? peaks : PLACEHOLDER_PEAKS;
6328
- const hasMetadata = durationValue > 0;
6329
- const waveformDurationSec = hasMetadata ? durationValue : PLACEHOLDER_DURATION_SEC;
6394
+ const waveformDurationSec = hasWaveformDuration ? durationValue : PLACEHOLDER_DURATION_SEC;
6330
6395
  const [playRequested, setPlayRequested] = (0,external_react_.useState)(false);
6331
6396
  const [viewport, setViewport] = (0,external_react_.useState)(null);
6332
6397
  const handleViewportChange = (0,external_react_.useCallback)(next => {
@@ -6371,11 +6436,11 @@ function MP3ControlsV2({
6371
6436
  }
6372
6437
  onTimeChange(marker.time);
6373
6438
  }, [onCommentMarkerClick, onPlayPause, onTimeChange]);
6374
- const isWaveformInteractive = playRequested && hasMetadata;
6375
- const isWaitingToPlay = playRequested && !hasMetadata;
6439
+ const isWaveformInteractive = playRequested && hasMediaMetadata;
6440
+ const isWaitingToPlay = playRequested && !hasMediaMetadata;
6376
6441
  const showPlayOverlay = !playRequested && !isPlaying;
6377
6442
  const hasZoomHandlers = hasRealPeaks && !showPlayOverlay;
6378
- const hasZoomControl = hasZoomHandlers && hasMetadata && maxZoom > constants/* WAVEFORM_ZOOM_MIN */.LW;
6443
+ const hasZoomControl = hasZoomHandlers && hasMediaMetadata && maxZoom > constants/* WAVEFORM_ZOOM_MIN */.LW;
6379
6444
  return /*#__PURE__*/external_react_["default"].createElement("div", {
6380
6445
  className: "bp-MP3ControlsV2",
6381
6446
  "data-testid": "media-controls-wrapper-v2"
@@ -6396,7 +6461,7 @@ function MP3ControlsV2({
6396
6461
  zoomLevel: hasZoomHandlers ? zoomLevel : constants/* WAVEFORM_ZOOM_MIN */.LW
6397
6462
  }), /*#__PURE__*/external_react_["default"].createElement(WaveformCommentMarkers, {
6398
6463
  commentMarkers: waveformMarkers,
6399
- durationSec: hasMetadata ? durationValue : 0,
6464
+ durationSec: hasWaveformDuration ? durationValue : 0,
6400
6465
  onCommentMarkerClick: handleCommentMarkerClick,
6401
6466
  selectedId: selectedMarkerId,
6402
6467
  viewport: viewport
@@ -6423,7 +6488,7 @@ function MP3ControlsV2({
6423
6488
  }), isWaitingToPlay && /*#__PURE__*/external_react_["default"].createElement("div", {
6424
6489
  className: "bp-media-buffering-spinner",
6425
6490
  "data-testid": "bp-MP3ControlsV2-loading"
6426
- })), hasMetadata && /*#__PURE__*/external_react_["default"].createElement("div", {
6491
+ })), hasMediaMetadata && /*#__PURE__*/external_react_["default"].createElement("div", {
6427
6492
  className: "bp-MP3ControlsV2-bar",
6428
6493
  "data-testid": "bp-MP3ControlsV2-bar"
6429
6494
  }, /*#__PURE__*/external_react_["default"].createElement("div", {
@@ -6594,25 +6659,19 @@ const WAVEFORM_HEIGHT = 140;
6594
6659
 
6595
6660
  /***/ },
6596
6661
 
6597
- /***/ 546
6662
+ /***/ 4971
6598
6663
  (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
6599
6664
 
6600
6665
 
6601
6666
  // EXPORTS
6602
6667
  __webpack_require__.d(__webpack_exports__, {
6603
- decodeToPeaks: () => (/* binding */ decodeToPeaks),
6604
- extractPeaks: () => (/* binding */ extractPeaks),
6605
- getDecodeDecision: () => (/* binding */ getDecodeDecision),
6606
- loadPeaks: () => (/* binding */ loadPeaks),
6607
- runClientDecode: () => (/* binding */ runClientDecode)
6668
+ oz: () => (/* binding */ WaveformLoadError),
6669
+ iY: () => (/* binding */ createCappedWaveformState),
6670
+ nR: () => (/* binding */ createWaveformLoader),
6671
+ Vq: () => (/* binding */ errorFromUnknown),
6672
+ zf: () => (/* binding */ isAbortError)
6608
6673
  });
6609
- // ESM COMPAT FLAG
6610
- __webpack_require__.r(__webpack_exports__);
6611
6674
 
6612
- // EXTERNAL MODULE: ./src/lib/util.js + 1 modules
6613
- var util = __webpack_require__(4410);
6614
- // EXTERNAL MODULE: ./src/lib/viewers/media/waveform/constants.ts
6615
- var constants = __webpack_require__(4929);
6616
6675
  ;// ./src/lib/viewers/media/waveform/types.ts
6617
6676
  /**
6618
6677
  * Box V1 is the in-viewer form: unsigned mono peaks in [0, 1] (peak envelope, mono_max).
@@ -6648,9 +6707,9 @@ function isWaveformErrorCode(value) {
6648
6707
  * Wavesurfer's two paints: left of the playhead (`progressColor`) and right of it (`waveColor`).
6649
6708
  * A string is a solid fill; a stop list is a left-to-right step gradient.
6650
6709
  */
6710
+ // EXTERNAL MODULE: ./src/lib/viewers/media/waveform/validateWaveformPayload.ts
6711
+ var validateWaveformPayload = __webpack_require__(1053);
6651
6712
  ;// ./src/lib/viewers/media/waveform/createWaveformLoader.ts
6652
- /* unused harmony import specifier */ var isRetryableWaveformError;
6653
- /* unused harmony import specifier */ var validateWaveformPayload;
6654
6713
  function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
6655
6714
  function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
6656
6715
  function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
@@ -6697,7 +6756,7 @@ function failedLoadState(error) {
6697
6756
  return {
6698
6757
  status: 'failed',
6699
6758
  error,
6700
- retryable: isRetryableWaveformError(error.code)
6759
+ retryable: (0,validateWaveformPayload/* isRetryableWaveformError */.Oe)(error.code)
6701
6760
  };
6702
6761
  }
6703
6762
  function readyLoadState(payload) {
@@ -6779,7 +6838,7 @@ function createWaveformLoader(fetchPayload, options = {}) {
6779
6838
  if (staleAfterFetch) {
6780
6839
  return staleAfterFetch;
6781
6840
  }
6782
- const validation = validateWaveformPayload(raw, options);
6841
+ const validation = (0,validateWaveformPayload/* validateWaveformPayload */.a9)(raw, options);
6783
6842
  const staleAfterValidate = cancelledIfStale(request);
6784
6843
  if (staleAfterValidate) {
6785
6844
  return staleAfterValidate;
@@ -6821,185 +6880,29 @@ function createCappedWaveformState(message) {
6821
6880
  }
6822
6881
  };
6823
6882
  }
6824
- ;// ./src/lib/viewers/media/waveform/validateWaveformPayload.ts
6825
6883
 
6826
- const DEFAULT_PEAK_SCALE = 'unit';
6827
- const DEFAULT_CHANNEL_POLICY = 'mono_max';
6828
- const DEFAULT_ENVELOPE = 'peak';
6829
- function fail(code, message, retryable = false) {
6830
- return {
6831
- ok: false,
6832
- error: {
6833
- code,
6834
- message
6835
- },
6836
- retryable
6837
- };
6838
- }
6839
- function isPlainObject(value) {
6840
- return typeof value === 'object' && value !== null && !Array.isArray(value);
6841
- }
6842
- function payloadByteLength(raw) {
6843
- try {
6844
- const serialized = JSON.stringify(raw);
6845
- if (typeof Buffer !== 'undefined') {
6846
- return Buffer.byteLength(serialized, 'utf8');
6847
- }
6848
- if (typeof TextEncoder !== 'undefined') {
6849
- return new TextEncoder().encode(serialized).length;
6850
- }
6851
- return serialized.length;
6852
- } catch {
6853
- return Number.MAX_SAFE_INTEGER;
6854
- }
6855
- }
6856
- function readOptionalPolicy(raw, key, allowed) {
6857
- if (!(key in raw) || raw[key] === undefined) {
6858
- return undefined;
6859
- }
6860
- return raw[key] === allowed ? allowed : null;
6861
- }
6862
- function asWaveformPayloadV1(raw) {
6863
- const {
6864
- version,
6865
- durationSec,
6866
- peaks
6867
- } = raw;
6868
- if (version !== constants/* WAVEFORM_PAYLOAD_VERSION */.sh || typeof durationSec !== 'number' || !Array.isArray(peaks)) {
6869
- return null;
6870
- }
6871
- const peakScale = readOptionalPolicy(raw, 'peakScale', DEFAULT_PEAK_SCALE);
6872
- const channelPolicy = readOptionalPolicy(raw, 'channelPolicy', DEFAULT_CHANNEL_POLICY);
6873
- const envelope = readOptionalPolicy(raw, 'envelope', DEFAULT_ENVELOPE);
6874
- if (peakScale === null || channelPolicy === null || envelope === null) {
6875
- return null;
6876
- }
6877
- if ('channels' in raw && raw.channels !== undefined && raw.channels !== 1) {
6878
- return null;
6879
- }
6880
- return {
6881
- version: constants/* WAVEFORM_PAYLOAD_VERSION */.sh,
6882
- durationSec,
6883
- peaks: peaks,
6884
- peakScale: peakScale ?? DEFAULT_PEAK_SCALE,
6885
- channelPolicy: channelPolicy ?? DEFAULT_CHANNEL_POLICY,
6886
- envelope: envelope ?? DEFAULT_ENVELOPE,
6887
- channels: 1
6888
- };
6889
- }
6890
- function readPayloadObject(raw, maxPayloadBytes, isPayloadByteCheckSkipped = false) {
6891
- if (raw === null || raw === undefined) {
6892
- return fail('INVALID_PAYLOAD', 'Payload is null or undefined');
6893
- }
6894
- if (!isPayloadByteCheckSkipped) {
6895
- const byteLength = payloadByteLength(raw);
6896
- if (byteLength > maxPayloadBytes) {
6897
- return fail('PAYLOAD_TOO_LARGE', `Payload size ${byteLength} exceeds limit ${maxPayloadBytes}`);
6898
- }
6899
- }
6900
- if (!isPlainObject(raw)) {
6901
- return fail('INVALID_PAYLOAD', 'Payload must be an object');
6902
- }
6903
- return {
6904
- ok: true,
6905
- value: raw
6906
- };
6907
- }
6908
- function checkVersion(raw) {
6909
- if (raw.version === constants/* WAVEFORM_PAYLOAD_VERSION */.sh) {
6910
- return null;
6911
- }
6912
- const {
6913
- version
6914
- } = raw;
6915
- if (typeof version === 'number' && version > constants/* WAVEFORM_PAYLOAD_VERSION */.sh) {
6916
- return fail('UNSUPPORTED_VERSION', `Unsupported waveform version ${version}; max supported is ${constants/* WAVEFORM_PAYLOAD_VERSION */.sh}`);
6917
- }
6918
- return fail('INVALID_PAYLOAD', 'Missing or invalid version field');
6919
- }
6920
- function checkDuration(payload, expectedDurationSec) {
6921
- if (!Number.isFinite(payload.durationSec) || payload.durationSec <= 0) {
6922
- return fail('INVALID_DURATION', 'durationSec must be a positive finite number');
6923
- }
6924
- if (expectedDurationSec !== undefined && Number.isFinite(expectedDurationSec) && Math.abs(payload.durationSec - expectedDurationSec) > constants/* DURATION_MISMATCH_TOLERANCE_SEC */.GQ) {
6925
- return fail('DURATION_MISMATCH', `Payload duration ${payload.durationSec}s differs from media duration ${expectedDurationSec}s`, true);
6926
- }
6927
- return null;
6928
- }
6929
- function normalizePeaks(peaks, maxPeakCount) {
6930
- if (peaks.length === 0) {
6931
- return fail('EMPTY_PEAKS', 'peaks array must not be empty');
6932
- }
6933
- if (peaks.length > maxPeakCount) {
6934
- return fail('PEAK_COUNT_EXCEEDED', `peaks length ${peaks.length} exceeds max ${maxPeakCount}`);
6935
- }
6936
- const normalized = new Float32Array(peaks.length);
6937
- for (let i = 0; i < peaks.length; i += 1) {
6938
- const value = peaks[i];
6939
- if (!Number.isFinite(value)) {
6940
- return fail('NON_FINITE_PEAK', `Peak at index ${i} is not finite`);
6941
- }
6942
- if (value < constants/* PEAK_UNIT_MIN */.mJ || value > constants/* PEAK_UNIT_MAX */.i8) {
6943
- return fail('PEAK_OUT_OF_RANGE', `Peak at index ${i} is outside [${constants/* PEAK_UNIT_MIN */.mJ}, ${constants/* PEAK_UNIT_MAX */.i8}]`);
6944
- }
6945
- normalized[i] = value;
6946
- }
6947
- return {
6948
- ok: true,
6949
- peaks: normalized
6950
- };
6951
- }
6884
+ /***/ },
6952
6885
 
6953
- /**
6954
- * Validates a version-1 waveform payload and returns normalized peaks for rendering.
6955
- * Wire JSON requires version, durationSec, and peaks. sampleCount and source are ignored.
6956
- */
6957
- function validateWaveformPayload_validateWaveformPayload(raw, options = {}) {
6958
- const maxPeakCount = options.maxPeakCount ?? constants/* MAX_PEAK_COUNT */.FY;
6959
- const maxPayloadBytes = options.maxPayloadBytes ?? constants/* MAX_PAYLOAD_BYTES */.qY;
6960
- const objectResult = readPayloadObject(raw, maxPayloadBytes, options.isPayloadByteCheckSkipped === true);
6961
- if (!objectResult.ok) {
6962
- return objectResult;
6963
- }
6964
- const versionFailure = checkVersion(objectResult.value);
6965
- if (versionFailure) {
6966
- return versionFailure;
6967
- }
6968
- const payload = asWaveformPayloadV1(objectResult.value);
6969
- if (!payload) {
6970
- return fail('INVALID_PAYLOAD', 'Payload failed structural validation');
6971
- }
6972
- const durationFailure = checkDuration(payload, options.expectedDurationSec);
6973
- if (durationFailure) {
6974
- return durationFailure;
6975
- }
6976
- const peaksResult = normalizePeaks(payload.peaks, maxPeakCount);
6977
- if (!peaksResult.ok) {
6978
- return peaksResult;
6979
- }
6980
- return {
6981
- ok: true,
6982
- payload: {
6983
- version: constants/* WAVEFORM_PAYLOAD_VERSION */.sh,
6984
- durationSec: payload.durationSec,
6985
- peaks: peaksResult.peaks,
6986
- peakScale: payload.peakScale ?? DEFAULT_PEAK_SCALE,
6987
- channelPolicy: payload.channelPolicy ?? DEFAULT_CHANNEL_POLICY,
6988
- envelope: payload.envelope ?? DEFAULT_ENVELOPE
6989
- }
6990
- };
6991
- }
6886
+ /***/ 8296
6887
+ (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
6992
6888
 
6993
- /** Maps validation / load failures to retry policy for loaders. */
6994
- function validateWaveformPayload_isRetryableWaveformError(code) {
6995
- return code === 'DURATION_MISMATCH' || code === 'DECODE_FAILED' || code === 'LOAD_FAILED';
6996
- }
6997
- ;// ./src/lib/viewers/media/waveform/decode.ts
6889
+ __webpack_require__.r(__webpack_exports__);
6890
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
6891
+ /* harmony export */ decodeToPeaks: () => (/* binding */ decodeToPeaks),
6892
+ /* harmony export */ extractPeaks: () => (/* binding */ extractPeaks),
6893
+ /* harmony export */ getDecodeDecision: () => (/* binding */ getDecodeDecision),
6894
+ /* harmony export */ loadPeaks: () => (/* binding */ loadPeaks),
6895
+ /* harmony export */ runClientDecode: () => (/* binding */ runClientDecode)
6896
+ /* harmony export */ });
6897
+ /* harmony import */ var _util__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4410);
6898
+ /* harmony import */ var _constants__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4929);
6899
+ /* harmony import */ var _createWaveformLoader__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(4971);
6900
+ /* harmony import */ var _validateWaveformPayload__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(1053);
6998
6901
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
6999
- function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { decode_defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
7000
- function decode_defineProperty(e, r, t) { return (r = decode_toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
7001
- function decode_toPropertyKey(t) { var i = decode_toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
7002
- function decode_toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
6902
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
6903
+ function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
6904
+ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
6905
+ function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
7003
6906
 
7004
6907
 
7005
6908
 
@@ -7011,9 +6914,6 @@ const EMPTY_TIMINGS = {
7011
6914
  function createAbortError() {
7012
6915
  return new DOMException('Aborted', 'AbortError');
7013
6916
  }
7014
- function isPositiveFinite(value) {
7015
- return typeof value === 'number' && Number.isFinite(value) && value > 0;
7016
- }
7017
6917
  function getAudioContextConstructor() {
7018
6918
  if (typeof window === 'undefined') {
7019
6919
  return undefined;
@@ -7046,7 +6946,7 @@ function createSkippedDecodeResult(reason) {
7046
6946
  reason
7047
6947
  };
7048
6948
  }
7049
- return _objectSpread(_objectSpread({}, createCappedWaveformState(getDecodeSkipMessage(reason))), {}, {
6949
+ return _objectSpread(_objectSpread({}, (0,_createWaveformLoader__WEBPACK_IMPORTED_MODULE_2__/* .createCappedWaveformState */ .iY)(getDecodeSkipMessage(reason))), {}, {
7050
6950
  isDecodeSkipped: true,
7051
6951
  timings: EMPTY_TIMINGS,
7052
6952
  reason
@@ -7070,11 +6970,11 @@ function decodeWithContext(context, buffer, signal) {
7070
6970
  }
7071
6971
  isSettled = true;
7072
6972
  signal?.removeEventListener('abort', onAbort);
7073
- if (isAbortError(error)) {
6973
+ if ((0,_createWaveformLoader__WEBPACK_IMPORTED_MODULE_2__/* .isAbortError */ .zf)(error)) {
7074
6974
  reject(error instanceof DOMException ? error : createAbortError());
7075
6975
  return;
7076
6976
  }
7077
- reject(error instanceof WaveformLoadError ? error : new WaveformLoadError('DECODE_FAILED', 'decodeAudioData failed'));
6977
+ reject(error instanceof _createWaveformLoader__WEBPACK_IMPORTED_MODULE_2__/* .WaveformLoadError */ .oz ? error : new _createWaveformLoader__WEBPACK_IMPORTED_MODULE_2__/* .WaveformLoadError */ .oz('DECODE_FAILED', 'decodeAudioData failed'));
7078
6978
  };
7079
6979
  onAbort = () => onFailure(createAbortError());
7080
6980
  if (signal?.aborted) {
@@ -7100,13 +7000,13 @@ function decodeWithContext(context, buffer, signal) {
7100
7000
  * skips decode so playback is never blocked by expanding the full audio buffer.
7101
7001
  */
7102
7002
  function getDecodeDecision(media, caps = {}) {
7103
- const maxCompressedBytes = caps.maxCompressedBytes ?? constants/* CLIENT_DECODE_MAX_COMPRESSED_BYTES */.EB;
7104
- const maxDurationSec = caps.maxDurationSec ?? constants/* CLIENT_DECODE_MAX_DURATION_SEC */.nG;
7003
+ const maxCompressedBytes = caps.maxCompressedBytes ?? _constants__WEBPACK_IMPORTED_MODULE_1__/* .CLIENT_DECODE_MAX_COMPRESSED_BYTES */ .EB;
7004
+ const maxDurationSec = caps.maxDurationSec ?? _constants__WEBPACK_IMPORTED_MODULE_1__/* .CLIENT_DECODE_MAX_DURATION_SEC */ .nG;
7105
7005
  const {
7106
7006
  compressedBytes,
7107
7007
  durationSec
7108
7008
  } = media;
7109
- if (!isPositiveFinite(compressedBytes) || !isPositiveFinite(durationSec)) {
7009
+ if (!(0,_validateWaveformPayload__WEBPACK_IMPORTED_MODULE_3__/* .isPositiveFinite */ .Ze)(compressedBytes) || !(0,_validateWaveformPayload__WEBPACK_IMPORTED_MODULE_3__/* .isPositiveFinite */ .Ze)(durationSec)) {
7110
7010
  return {
7111
7011
  isAllowed: false,
7112
7012
  reason: 'missing_metadata'
@@ -7133,7 +7033,7 @@ function getDecodeDecision(media, caps = {}) {
7133
7033
  * Collapse audio channels to one unsigned peak per time bucket (max abs, then clamp to unit).
7134
7034
  * Accepts an AudioBuffer so callers can extract before closing the AudioContext.
7135
7035
  */
7136
- function extractPeaks(audio, peakCount = constants/* CLIENT_DECODE_PEAK_COUNT */.f3) {
7036
+ function extractPeaks(audio, peakCount = _constants__WEBPACK_IMPORTED_MODULE_1__/* .CLIENT_DECODE_PEAK_COUNT */ .f3) {
7137
7037
  const channels = Array.isArray(audio) ? audio : Array.from({
7138
7038
  length: audio.numberOfChannels
7139
7039
  }, (_, channelIndex) => audio.getChannelData(channelIndex));
@@ -7158,7 +7058,7 @@ function extractPeaks(audio, peakCount = constants/* CLIENT_DECODE_PEAK_COUNT */
7158
7058
  }
7159
7059
  }
7160
7060
  }
7161
- peaks[i] = Math.min(constants/* PEAK_UNIT_MAX */.i8, Math.max(constants/* PEAK_UNIT_MIN */.mJ, maxAbs));
7061
+ peaks[i] = Math.min(_constants__WEBPACK_IMPORTED_MODULE_1__/* .PEAK_UNIT_MAX */ .i8, Math.max(_constants__WEBPACK_IMPORTED_MODULE_1__/* .PEAK_UNIT_MIN */ .mJ, maxAbs));
7162
7062
  }
7163
7063
  return peaks;
7164
7064
  }
@@ -7167,13 +7067,13 @@ function extractPeaks(audio, peakCount = constants/* CLIENT_DECODE_PEAK_COUNT */
7167
7067
  * Decode compressed audio and extract unit peaks while the AudioBuffer is live.
7168
7068
  * Does not attach a media element or fetch a URL. Does not copy channel data.
7169
7069
  */
7170
- async function decodeToPeaks(arrayBuffer, signal, peakCount = constants/* CLIENT_DECODE_PEAK_COUNT */.f3) {
7070
+ async function decodeToPeaks(arrayBuffer, signal, peakCount = _constants__WEBPACK_IMPORTED_MODULE_1__/* .CLIENT_DECODE_PEAK_COUNT */ .f3) {
7171
7071
  if (signal?.aborted) {
7172
7072
  throw createAbortError();
7173
7073
  }
7174
7074
  const AudioContextConstructor = getAudioContextConstructor();
7175
7075
  if (!AudioContextConstructor) {
7176
- throw new WaveformLoadError('DECODE_FAILED', 'AudioContext is not available');
7076
+ throw new _createWaveformLoader__WEBPACK_IMPORTED_MODULE_2__/* .WaveformLoadError */ .oz('DECODE_FAILED', 'AudioContext is not available');
7177
7077
  }
7178
7078
  const context = new AudioContextConstructor();
7179
7079
  try {
@@ -7181,15 +7081,15 @@ async function decodeToPeaks(arrayBuffer, signal, peakCount = constants/* CLIENT
7181
7081
  if (signal?.aborted) {
7182
7082
  throw createAbortError();
7183
7083
  }
7184
- const extractStarted = (0,util/* getCurrentTimeMs */.RU)();
7084
+ const extractStarted = (0,_util__WEBPACK_IMPORTED_MODULE_0__/* .getCurrentTimeMs */ .RU)();
7185
7085
  const peaks = extractPeaks(audioBuffer, peakCount);
7186
7086
  return {
7187
7087
  durationSec: audioBuffer.duration,
7188
7088
  peaks,
7189
- extractMs: (0,util/* getCurrentTimeMs */.RU)() - extractStarted
7089
+ extractMs: (0,_util__WEBPACK_IMPORTED_MODULE_0__/* .getCurrentTimeMs */ .RU)() - extractStarted
7190
7090
  };
7191
7091
  } catch (error) {
7192
- if (signal?.aborted || isAbortError(error)) {
7092
+ if (signal?.aborted || (0,_createWaveformLoader__WEBPACK_IMPORTED_MODULE_2__/* .isAbortError */ .zf)(error)) {
7193
7093
  throw createAbortError();
7194
7094
  }
7195
7095
  throw error;
@@ -7222,31 +7122,31 @@ async function runClientDecode(options) {
7222
7122
  };
7223
7123
  }
7224
7124
  const signal = options.signal ?? new AbortController().signal;
7225
- const decodeStarted = (0,util/* getCurrentTimeMs */.RU)();
7125
+ const decodeStarted = (0,_util__WEBPACK_IMPORTED_MODULE_0__/* .getCurrentTimeMs */ .RU)();
7226
7126
  let decodeOutput;
7227
7127
  try {
7228
7128
  decodeOutput = await options.decode(signal);
7229
7129
  } catch (error) {
7230
- if (signal.aborted || isAbortError(error)) {
7130
+ if (signal.aborted || (0,_createWaveformLoader__WEBPACK_IMPORTED_MODULE_2__/* .isAbortError */ .zf)(error)) {
7231
7131
  return {
7232
7132
  status: 'cancelled',
7233
7133
  isDecodeSkipped: false,
7234
7134
  timings: EMPTY_TIMINGS
7235
7135
  };
7236
7136
  }
7237
- const waveformError = errorFromUnknown(error, 'DECODE_FAILED');
7137
+ const waveformError = (0,_createWaveformLoader__WEBPACK_IMPORTED_MODULE_2__/* .errorFromUnknown */ .Vq)(error, 'DECODE_FAILED');
7238
7138
  return {
7239
7139
  status: 'failed',
7240
7140
  error: waveformError,
7241
- retryable: validateWaveformPayload_isRetryableWaveformError(waveformError.code),
7141
+ retryable: (0,_validateWaveformPayload__WEBPACK_IMPORTED_MODULE_3__/* .isRetryableWaveformError */ .Oe)(waveformError.code),
7242
7142
  isDecodeSkipped: false,
7243
7143
  timings: {
7244
- attemptMs: (0,util/* getCurrentTimeMs */.RU)() - decodeStarted,
7144
+ attemptMs: (0,_util__WEBPACK_IMPORTED_MODULE_0__/* .getCurrentTimeMs */ .RU)() - decodeStarted,
7245
7145
  extractMs: null
7246
7146
  }
7247
7147
  };
7248
7148
  }
7249
- const attemptMs = (0,util/* getCurrentTimeMs */.RU)() - decodeStarted;
7149
+ const attemptMs = (0,_util__WEBPACK_IMPORTED_MODULE_0__/* .getCurrentTimeMs */ .RU)() - decodeStarted;
7250
7150
  if (signal.aborted) {
7251
7151
  return {
7252
7152
  status: 'cancelled',
@@ -7257,8 +7157,8 @@ async function runClientDecode(options) {
7257
7157
  }
7258
7158
  };
7259
7159
  }
7260
- const validation = validateWaveformPayload_validateWaveformPayload({
7261
- version: constants/* WAVEFORM_PAYLOAD_VERSION */.sh,
7160
+ const validation = (0,_validateWaveformPayload__WEBPACK_IMPORTED_MODULE_3__/* .validateWaveformPayload */ .a9)({
7161
+ version: _constants__WEBPACK_IMPORTED_MODULE_1__/* .WAVEFORM_PAYLOAD_VERSION */ .sh,
7262
7162
  durationSec: options.durationSec ?? decodeOutput.durationSec,
7263
7163
  peaks: decodeOutput.peaks
7264
7164
  }, {
@@ -7268,7 +7168,7 @@ async function runClientDecode(options) {
7268
7168
  return {
7269
7169
  status: 'failed',
7270
7170
  error: validation.error,
7271
- retryable: validateWaveformPayload_isRetryableWaveformError(validation.error.code),
7171
+ retryable: (0,_validateWaveformPayload__WEBPACK_IMPORTED_MODULE_3__/* .isRetryableWaveformError */ .Oe)(validation.error.code),
7272
7172
  isDecodeSkipped: false,
7273
7173
  timings: {
7274
7174
  attemptMs,
@@ -7305,6 +7205,193 @@ function loadPeaks(request) {
7305
7205
 
7306
7206
  /***/ },
7307
7207
 
7208
+ /***/ 1053
7209
+ (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
7210
+
7211
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
7212
+ /* harmony export */ Oe: () => (/* binding */ isRetryableWaveformError),
7213
+ /* harmony export */ Ze: () => (/* binding */ isPositiveFinite),
7214
+ /* harmony export */ a9: () => (/* binding */ validateWaveformPayload)
7215
+ /* harmony export */ });
7216
+ /* harmony import */ var _constants__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4929);
7217
+
7218
+ const DEFAULT_PEAK_SCALE = 'unit';
7219
+ const DEFAULT_CHANNEL_POLICY = 'mono_max';
7220
+ const DEFAULT_ENVELOPE = 'peak';
7221
+ function fail(code, message, retryable = false) {
7222
+ return {
7223
+ ok: false,
7224
+ error: {
7225
+ code,
7226
+ message
7227
+ },
7228
+ retryable
7229
+ };
7230
+ }
7231
+ function isPositiveFinite(value) {
7232
+ return typeof value === 'number' && Number.isFinite(value) && value > 0;
7233
+ }
7234
+ function isPlainObject(value) {
7235
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
7236
+ }
7237
+ function payloadByteLength(raw) {
7238
+ try {
7239
+ const serialized = JSON.stringify(raw);
7240
+ if (typeof Buffer !== 'undefined') {
7241
+ return Buffer.byteLength(serialized, 'utf8');
7242
+ }
7243
+ if (typeof TextEncoder !== 'undefined') {
7244
+ return new TextEncoder().encode(serialized).length;
7245
+ }
7246
+ return serialized.length;
7247
+ } catch {
7248
+ return Number.MAX_SAFE_INTEGER;
7249
+ }
7250
+ }
7251
+ function readOptionalPolicy(raw, key, allowed) {
7252
+ if (!(key in raw) || raw[key] === undefined) {
7253
+ return undefined;
7254
+ }
7255
+ return raw[key] === allowed ? allowed : null;
7256
+ }
7257
+ function asWaveformPayloadV1(raw) {
7258
+ const {
7259
+ version,
7260
+ durationSec,
7261
+ peaks
7262
+ } = raw;
7263
+ if (version !== _constants__WEBPACK_IMPORTED_MODULE_0__/* .WAVEFORM_PAYLOAD_VERSION */ .sh || typeof durationSec !== 'number' || !Array.isArray(peaks)) {
7264
+ return null;
7265
+ }
7266
+ const peakScale = readOptionalPolicy(raw, 'peakScale', DEFAULT_PEAK_SCALE);
7267
+ const channelPolicy = readOptionalPolicy(raw, 'channelPolicy', DEFAULT_CHANNEL_POLICY);
7268
+ const envelope = readOptionalPolicy(raw, 'envelope', DEFAULT_ENVELOPE);
7269
+ if (peakScale === null || channelPolicy === null || envelope === null) {
7270
+ return null;
7271
+ }
7272
+ if ('channels' in raw && raw.channels !== undefined && raw.channels !== 1) {
7273
+ return null;
7274
+ }
7275
+ return {
7276
+ version: _constants__WEBPACK_IMPORTED_MODULE_0__/* .WAVEFORM_PAYLOAD_VERSION */ .sh,
7277
+ durationSec,
7278
+ peaks: peaks,
7279
+ peakScale: peakScale ?? DEFAULT_PEAK_SCALE,
7280
+ channelPolicy: channelPolicy ?? DEFAULT_CHANNEL_POLICY,
7281
+ envelope: envelope ?? DEFAULT_ENVELOPE,
7282
+ channels: 1
7283
+ };
7284
+ }
7285
+ function readPayloadObject(raw, maxPayloadBytes, isPayloadByteCheckSkipped = false) {
7286
+ if (raw === null || raw === undefined) {
7287
+ return fail('INVALID_PAYLOAD', 'Payload is null or undefined');
7288
+ }
7289
+ if (!isPayloadByteCheckSkipped) {
7290
+ const byteLength = payloadByteLength(raw);
7291
+ if (byteLength > maxPayloadBytes) {
7292
+ return fail('PAYLOAD_TOO_LARGE', `Payload size ${byteLength} exceeds limit ${maxPayloadBytes}`);
7293
+ }
7294
+ }
7295
+ if (!isPlainObject(raw)) {
7296
+ return fail('INVALID_PAYLOAD', 'Payload must be an object');
7297
+ }
7298
+ return {
7299
+ ok: true,
7300
+ value: raw
7301
+ };
7302
+ }
7303
+ function checkVersion(raw) {
7304
+ if (raw.version === _constants__WEBPACK_IMPORTED_MODULE_0__/* .WAVEFORM_PAYLOAD_VERSION */ .sh) {
7305
+ return null;
7306
+ }
7307
+ const {
7308
+ version
7309
+ } = raw;
7310
+ if (typeof version === 'number' && version > _constants__WEBPACK_IMPORTED_MODULE_0__/* .WAVEFORM_PAYLOAD_VERSION */ .sh) {
7311
+ return fail('UNSUPPORTED_VERSION', `Unsupported waveform version ${version}; max supported is ${_constants__WEBPACK_IMPORTED_MODULE_0__/* .WAVEFORM_PAYLOAD_VERSION */ .sh}`);
7312
+ }
7313
+ return fail('INVALID_PAYLOAD', 'Missing or invalid version field');
7314
+ }
7315
+ function checkDuration(payload, expectedDurationSec) {
7316
+ if (!isPositiveFinite(payload.durationSec)) {
7317
+ return fail('INVALID_DURATION', 'durationSec must be a positive finite number');
7318
+ }
7319
+ if (expectedDurationSec !== undefined && Number.isFinite(expectedDurationSec) && Math.abs(payload.durationSec - expectedDurationSec) > _constants__WEBPACK_IMPORTED_MODULE_0__/* .DURATION_MISMATCH_TOLERANCE_SEC */ .GQ) {
7320
+ return fail('DURATION_MISMATCH', `Payload duration ${payload.durationSec}s differs from media duration ${expectedDurationSec}s`, true);
7321
+ }
7322
+ return null;
7323
+ }
7324
+ function normalizePeaks(peaks, maxPeakCount) {
7325
+ if (peaks.length === 0) {
7326
+ return fail('EMPTY_PEAKS', 'peaks array must not be empty');
7327
+ }
7328
+ if (peaks.length > maxPeakCount) {
7329
+ return fail('PEAK_COUNT_EXCEEDED', `peaks length ${peaks.length} exceeds max ${maxPeakCount}`);
7330
+ }
7331
+ const normalized = new Float32Array(peaks.length);
7332
+ for (let i = 0; i < peaks.length; i += 1) {
7333
+ const value = peaks[i];
7334
+ if (!Number.isFinite(value)) {
7335
+ return fail('NON_FINITE_PEAK', `Peak at index ${i} is not finite`);
7336
+ }
7337
+ if (value < _constants__WEBPACK_IMPORTED_MODULE_0__/* .PEAK_UNIT_MIN */ .mJ || value > _constants__WEBPACK_IMPORTED_MODULE_0__/* .PEAK_UNIT_MAX */ .i8) {
7338
+ return fail('PEAK_OUT_OF_RANGE', `Peak at index ${i} is outside [${_constants__WEBPACK_IMPORTED_MODULE_0__/* .PEAK_UNIT_MIN */ .mJ}, ${_constants__WEBPACK_IMPORTED_MODULE_0__/* .PEAK_UNIT_MAX */ .i8}]`);
7339
+ }
7340
+ normalized[i] = value;
7341
+ }
7342
+ return {
7343
+ ok: true,
7344
+ peaks: normalized
7345
+ };
7346
+ }
7347
+
7348
+ /**
7349
+ * Validates a version-1 waveform payload and returns normalized peaks for rendering.
7350
+ * Wire JSON requires version, durationSec, and peaks. sampleCount and source are ignored.
7351
+ */
7352
+ function validateWaveformPayload(raw, options = {}) {
7353
+ const maxPeakCount = options.maxPeakCount ?? _constants__WEBPACK_IMPORTED_MODULE_0__/* .MAX_PEAK_COUNT */ .FY;
7354
+ const maxPayloadBytes = options.maxPayloadBytes ?? _constants__WEBPACK_IMPORTED_MODULE_0__/* .MAX_PAYLOAD_BYTES */ .qY;
7355
+ const objectResult = readPayloadObject(raw, maxPayloadBytes, options.isPayloadByteCheckSkipped === true);
7356
+ if (!objectResult.ok) {
7357
+ return objectResult;
7358
+ }
7359
+ const versionFailure = checkVersion(objectResult.value);
7360
+ if (versionFailure) {
7361
+ return versionFailure;
7362
+ }
7363
+ const payload = asWaveformPayloadV1(objectResult.value);
7364
+ if (!payload) {
7365
+ return fail('INVALID_PAYLOAD', 'Payload failed structural validation');
7366
+ }
7367
+ const durationFailure = checkDuration(payload, options.expectedDurationSec);
7368
+ if (durationFailure) {
7369
+ return durationFailure;
7370
+ }
7371
+ const peaksResult = normalizePeaks(payload.peaks, maxPeakCount);
7372
+ if (!peaksResult.ok) {
7373
+ return peaksResult;
7374
+ }
7375
+ return {
7376
+ ok: true,
7377
+ payload: {
7378
+ version: _constants__WEBPACK_IMPORTED_MODULE_0__/* .WAVEFORM_PAYLOAD_VERSION */ .sh,
7379
+ durationSec: payload.durationSec,
7380
+ peaks: peaksResult.peaks,
7381
+ peakScale: payload.peakScale ?? DEFAULT_PEAK_SCALE,
7382
+ channelPolicy: payload.channelPolicy ?? DEFAULT_CHANNEL_POLICY,
7383
+ envelope: payload.envelope ?? DEFAULT_ENVELOPE
7384
+ }
7385
+ };
7386
+ }
7387
+
7388
+ /** Maps validation / load failures to retry policy for loaders. */
7389
+ function isRetryableWaveformError(code) {
7390
+ return code === 'DURATION_MISMATCH' || code === 'DECODE_FAILED' || code === 'LOAD_FAILED';
7391
+ }
7392
+
7393
+ /***/ },
7394
+
7308
7395
  /***/ 2485
7309
7396
  (module, exports) {
7310
7397
 
@@ -22968,7 +23055,7 @@ class Browser {
22968
23055
  ;// ./src/lib/Logger.js
22969
23056
  /* eslint-disable no-undef */
22970
23057
  const CLIENT_NAME = "box-content-preview";
22971
- const CLIENT_VERSION = "3.88.0";
23058
+ const CLIENT_VERSION = "3.90.0";
22972
23059
  /* eslint-enable no-undef */
22973
23060
 
22974
23061
  class Logger {
@@ -35653,6 +35740,12 @@ class MP3ControlsRoot {
35653
35740
  this.root.render(controls);
35654
35741
  }
35655
35742
  }
35743
+ // EXTERNAL MODULE: ./src/lib/viewers/media/waveform/constants.ts
35744
+ var waveform_constants = __webpack_require__(4929);
35745
+ // EXTERNAL MODULE: ./src/lib/viewers/media/waveform/createWaveformLoader.ts + 1 modules
35746
+ var createWaveformLoader = __webpack_require__(4971);
35747
+ // EXTERNAL MODULE: ./src/lib/viewers/media/waveform/validateWaveformPayload.ts
35748
+ var validateWaveformPayload = __webpack_require__(1053);
35656
35749
  ;// ./src/lib/viewers/media/MP3.scss
35657
35750
  // extracted by mini-css-extract-plugin
35658
35751
 
@@ -35667,6 +35760,11 @@ function MP3Viewer_toPrimitive(t, r) { if ("object" != typeof t || !t) return t;
35667
35760
 
35668
35761
 
35669
35762
 
35763
+
35764
+
35765
+
35766
+
35767
+
35670
35768
  const CSS_CLASS_MP3 = 'bp-media-mp3';
35671
35769
  function createLoadFailedError(message) {
35672
35770
  const error = new Error(message);
@@ -35682,7 +35780,7 @@ class MP3Viewer extends media_MediaBaseViewer {
35682
35780
  MP3Viewer_defineProperty(this, "handlePlayRequest", () => {
35683
35781
  this.userRequestedPlay = true;
35684
35782
  this.togglePlay();
35685
- if (this.isWaveformDecodeRetryPending && !this.hasUsedWaveformDecodePlayRetry) {
35783
+ if (this.shouldRaceClientWaveformDecode() && this.isWaveformDecodeRetryPending && !this.hasUsedWaveformDecodePlayRetry) {
35686
35784
  this.hasUsedWaveformDecodePlayRetry = true;
35687
35785
  this.isWaveformDecodeRetryPending = false;
35688
35786
  this.startClientWaveformDecode();
@@ -35719,11 +35817,11 @@ class MP3Viewer extends media_MediaBaseViewer {
35719
35817
  });
35720
35818
  MP3Viewer_defineProperty(this, "handleCommentMarkerClick", marker => {
35721
35819
  this.hostSelectedMarkerId = marker.id;
35722
- this.pendingHostSelectedSeek = null;
35820
+ this.pendingHostSelectedSeek = marker;
35723
35821
  if (this.mediaEl) {
35724
35822
  this.mediaEl.pause();
35725
- this.mediaEl.currentTime = marker.time;
35726
35823
  }
35824
+ this.applyPendingHostSelectedSeek();
35727
35825
  // Overlay paints the ring from click optimism until the host re-emits selected.
35728
35826
  this.emit('comment_marker_select', {
35729
35827
  id: marker.id,
@@ -35755,11 +35853,16 @@ class MP3Viewer extends media_MediaBaseViewer {
35755
35853
  this.wrapperEl.classList.add(CSS_CLASS_MP3);
35756
35854
  this.isAudioPlayerV2 = this.getIsAudioPlayerV2();
35757
35855
  this.waveformPeaks = [];
35856
+ this.waveformPeaksSource = null;
35857
+ this.waveformDurationSec = 0;
35758
35858
  if (this.isAudioPlayerV2) {
35759
35859
  this.wrapperEl.classList.add('bp-media--v2');
35760
35860
  this.mediaContainerEl.classList.add('bp-media-container--v2');
35761
35861
  this.ensureV2Controls();
35762
35862
  this.importWaveformDecode();
35863
+ // Listen on the loading shell. The waveform is on
35864
+ // screen before the audio blob is playable; getViewer() is still null.
35865
+ this.bindCommentMarkersListener();
35763
35866
  }
35764
35867
 
35765
35868
  // Audio element
@@ -35767,6 +35870,13 @@ class MP3Viewer extends media_MediaBaseViewer {
35767
35870
  this.mediaEl.setAttribute('preload', 'auto');
35768
35871
  this.commentMarkers = [];
35769
35872
  }
35873
+ bindCommentMarkersListener() {
35874
+ if (!this.isAudioPlayerV2) {
35875
+ return;
35876
+ }
35877
+ this.removeListener('comment_markers', this.handleCommentMarkersUpdated);
35878
+ this.addListener('comment_markers', this.handleCommentMarkersUpdated);
35879
+ }
35770
35880
 
35771
35881
  /**
35772
35882
  * Audio player v2 (waveform). Off unless the host enables `audioPlayerV2`
@@ -35775,7 +35885,7 @@ class MP3Viewer extends media_MediaBaseViewer {
35775
35885
  * @return {boolean} whether v2 treatment is on
35776
35886
  */
35777
35887
  getIsAudioPlayerV2() {
35778
- return this.featureEnabled('audioPlayerV2.enabled') && this.useReactControls();
35888
+ return this.featureEnabled(constants/* AUDIO_PLAYER_V2 */.Ku) && this.useReactControls();
35779
35889
  }
35780
35890
 
35781
35891
  /**
@@ -35807,6 +35917,7 @@ class MP3Viewer extends media_MediaBaseViewer {
35807
35917
  */
35808
35918
  fallbackToV1Controls() {
35809
35919
  this.isAudioPlayerV2 = false;
35920
+ this.abortWaveformLoads();
35810
35921
  if (this.wrapperEl) {
35811
35922
  this.wrapperEl.classList.remove('bp-media--v2');
35812
35923
  }
@@ -35827,7 +35938,7 @@ class MP3Viewer extends media_MediaBaseViewer {
35827
35938
  */
35828
35939
  async importWaveformDecode() {
35829
35940
  if (!this.waveformDecodeImport) {
35830
- this.waveformDecodeImport = Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, 546));
35941
+ this.waveformDecodeImport = Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, 8296));
35831
35942
  }
35832
35943
  try {
35833
35944
  return await this.waveformDecodeImport;
@@ -35842,7 +35953,7 @@ class MP3Viewer extends media_MediaBaseViewer {
35842
35953
  */
35843
35954
  destroy() {
35844
35955
  this.removeListener('comment_markers', this.handleCommentMarkersUpdated);
35845
- this.abortClientWaveformDecode();
35956
+ this.abortWaveformLoads();
35846
35957
  super.destroy();
35847
35958
  }
35848
35959
 
@@ -35852,6 +35963,7 @@ class MP3Viewer extends media_MediaBaseViewer {
35852
35963
  load() {
35853
35964
  if (this.isAudioPlayerV2) {
35854
35965
  this.showAudioLoadingShell();
35966
+ this.startConversionWaveformLoad();
35855
35967
  }
35856
35968
  return super.load();
35857
35969
  }
@@ -35889,6 +36001,9 @@ class MP3Viewer extends media_MediaBaseViewer {
35889
36001
  * @inheritdoc
35890
36002
  */
35891
36003
  loadeddataHandler() {
36004
+ if (this.isAudioPlayerV2) {
36005
+ this.dropConversionPeaksOnDurationMismatch();
36006
+ }
35892
36007
  super.loadeddataHandler();
35893
36008
  if (!this.isAudioPlayerV2) {
35894
36009
  return;
@@ -35899,7 +36014,173 @@ class MP3Viewer extends media_MediaBaseViewer {
35899
36014
  if (this.userRequestedPlay) {
35900
36015
  this.play();
35901
36016
  }
35902
- this.startClientWaveformDecode();
36017
+ if (this.shouldRaceClientWaveformDecode()) {
36018
+ this.startClientWaveformDecode();
36019
+ }
36020
+ }
36021
+
36022
+ /**
36023
+ * Client decode races conversion until some source applies peaks. Size and
36024
+ * duration caps still apply. First source to apply peaks wins; later
36025
+ * results are ignored.
36026
+ *
36027
+ * @return {boolean}
36028
+ */
36029
+ shouldRaceClientWaveformDecode() {
36030
+ if (!this.isAudioPlayerV2 || this.destroyed || this.waveformPeaksSource) {
36031
+ return false;
36032
+ }
36033
+ const compressedBytes = this.options.file?.size;
36034
+ const durationSec = this.mediaEl?.duration;
36035
+ return (0,validateWaveformPayload/* isPositiveFinite */.Ze)(compressedBytes) && compressedBytes <= waveform_constants/* CLIENT_DECODE_MAX_COMPRESSED_BYTES */.EB && (0,validateWaveformPayload/* isPositiveFinite */.Ze)(durationSec) && durationSec <= waveform_constants/* CLIENT_DECODE_MAX_DURATION_SEC */.nG;
36036
+ }
36037
+
36038
+ /**
36039
+ * @param {number} durationSec
36040
+ * @return {boolean}
36041
+ */
36042
+ isConversionDurationMismatch(durationSec) {
36043
+ const mediaDuration = this.mediaEl?.duration;
36044
+ return (0,validateWaveformPayload/* isPositiveFinite */.Ze)(mediaDuration) && (0,validateWaveformPayload/* isPositiveFinite */.Ze)(durationSec) && Math.abs(durationSec - mediaDuration) > waveform_constants/* DURATION_MISMATCH_TOLERANCE_SEC */.GQ;
36045
+ }
36046
+
36047
+ /**
36048
+ * Conversion JSON fetched before metadata skipped duration matching. Drop
36049
+ * those peaks once media duration is known if they disagree.
36050
+ *
36051
+ * @return {void}
36052
+ */
36053
+ dropConversionPeaksOnDurationMismatch() {
36054
+ if (this.waveformPeaksSource !== 'conversion' || !this.isConversionDurationMismatch(this.waveformDurationSec)) {
36055
+ return;
36056
+ }
36057
+ this.waveformPeaks = [];
36058
+ this.waveformPeaksSource = null;
36059
+ this.waveformDurationSec = 0;
36060
+ this.renderUI();
36061
+ }
36062
+
36063
+ /**
36064
+ * Poll the conversion waveform representation and fetch its JSON.
36065
+ * Missing, failed, or unloadable conversion falls back to client decode
36066
+ * when the file is under size and duration caps.
36067
+ *
36068
+ * @return {Promise<void>}
36069
+ */
36070
+ async startConversionWaveformLoad() {
36071
+ if (!this.isAudioPlayerV2 || this.destroyed) {
36072
+ return;
36073
+ }
36074
+ const {
36075
+ file
36076
+ } = this.options;
36077
+ const waveform = file?.representations?.entries ? getRepresentation(file, constants/* WAVEFORM_REP_NAME */.u4) : null;
36078
+ const template = waveform?.content?.url_template;
36079
+ const conversionStatus = waveform?.status;
36080
+ const statusState = conversionStatus && typeof conversionStatus === 'object' ? conversionStatus.state : null;
36081
+ if (!waveform || !template || !statusState || statusState === constants/* STATUS_ERROR */.uD) {
36082
+ this.startClientWaveformDecode();
36083
+ return;
36084
+ }
36085
+ this.abortConversionWaveformLoad();
36086
+ this.waveformStatus = this.getRepStatus(waveform);
36087
+ this.waveformStatus.removeListener('conversionpending', this.resetLoadTimeout);
36088
+ const status = this.waveformStatus;
36089
+ try {
36090
+ await status.getPromise();
36091
+ if (this.destroyed || this.waveformStatus !== status || this.waveformPeaksSource) {
36092
+ return;
36093
+ }
36094
+ const result = await this.loadConversionWaveformPayload(template);
36095
+ this.handleConversionWaveformResult(result, status);
36096
+ } catch {
36097
+ this.startClientWaveformDecode();
36098
+ }
36099
+ }
36100
+
36101
+ /**
36102
+ * Fetch and validate conversion waveform JSON after the rep is ready.
36103
+ * `{+asset_path}` is empty. Auth is a header, not a query token.
36104
+ *
36105
+ * @param {string} template - content URL template
36106
+ * @return {Promise<import('./waveform/types').WaveformLoadState>}
36107
+ */
36108
+ loadConversionWaveformPayload(template) {
36109
+ const url = this.createContentUrlV2(template);
36110
+ const durationSec = this.mediaEl?.duration;
36111
+ const source = (0,createWaveformLoader/* createWaveformLoader */.nR)(signal => this.api.get(url, {
36112
+ headers: this.appendAuthHeader(),
36113
+ signal
36114
+ }), Number.isFinite(durationSec) ? {
36115
+ expectedDurationSec: durationSec
36116
+ } : {});
36117
+ this.waveformSource = source;
36118
+ return source.load();
36119
+ }
36120
+ abortWaveformLoads() {
36121
+ this.abortConversionWaveformLoad();
36122
+ this.abortClientWaveformDecode();
36123
+ }
36124
+ abortConversionWaveformLoad() {
36125
+ if (this.waveformSource) {
36126
+ this.waveformSource.abort();
36127
+ this.waveformSource = null;
36128
+ }
36129
+ if (this.waveformStatus) {
36130
+ this.waveformStatus.destroy();
36131
+ this.waveformStatus = null;
36132
+ }
36133
+ }
36134
+
36135
+ /**
36136
+ * First source to apply peaks wins. Later results are ignored.
36137
+ *
36138
+ * @param {'conversion'|'client'} source
36139
+ * @param {{ peaks: ArrayLike<number>, durationSec?: number }} payload
36140
+ * @return {boolean} true when this source claimed peaks
36141
+ */
36142
+ applyWaveformPeaks(source, payload) {
36143
+ if (this.waveformPeaksSource) {
36144
+ return false;
36145
+ }
36146
+ this.waveformPeaks = payload.peaks;
36147
+ this.waveformPeaksSource = source;
36148
+ this.isWaveformDecodeRetryPending = false;
36149
+ if (source === 'conversion') {
36150
+ this.waveformDurationSec = payload.durationSec;
36151
+ this.abortClientWaveformDecode();
36152
+ } else if (this.waveformSource) {
36153
+ // RepStatus.destroy() does not reject getPromise().
36154
+ this.waveformSource.abort();
36155
+ this.waveformSource = null;
36156
+ }
36157
+ this.renderUI();
36158
+ return true;
36159
+ }
36160
+
36161
+ /**
36162
+ * Apply conversion peaks when the load is still current. Ignored if client
36163
+ * decode already applied peaks.
36164
+ *
36165
+ * @param {import('./waveform/types').WaveformLoadState} result
36166
+ * @param {Object} status - RepStatus instance that started this load
36167
+ * @return {void}
36168
+ */
36169
+ handleConversionWaveformResult(result, status) {
36170
+ if (!result || this.destroyed || !this.isAudioPlayerV2 || this.waveformStatus !== status) {
36171
+ return;
36172
+ }
36173
+ if (result.status !== 'ready') {
36174
+ if (result.status !== 'cancelled') {
36175
+ this.startClientWaveformDecode();
36176
+ }
36177
+ return;
36178
+ }
36179
+ if (this.isConversionDurationMismatch(result.payload.durationSec)) {
36180
+ this.startClientWaveformDecode();
36181
+ return;
36182
+ }
36183
+ this.applyWaveformPeaks('conversion', result.payload);
35903
36184
  }
35904
36185
  /**
35905
36186
  * Fetch compressed audio bytes for client decode. Prefers an already-fetched blob URL.
@@ -35940,16 +36221,19 @@ class MP3Viewer extends media_MediaBaseViewer {
35940
36221
  }
35941
36222
 
35942
36223
  /**
35943
- * Decode peaks in the background when the file is under size and duration caps.
35944
- * Does not block playback. Capped or failed decode keeps the placeholder waveform.
36224
+ * Decode peaks in the background until conversion or client decode applies
36225
+ * them, when the file is under size and duration caps. Does not block
36226
+ * playback. First source to apply peaks wins.
35945
36227
  *
35946
36228
  * @return {Promise<void>}
35947
36229
  */
35948
36230
  async startClientWaveformDecode() {
35949
- if (!this.isAudioPlayerV2 || this.destroyed) {
36231
+ if (!this.shouldRaceClientWaveformDecode()) {
36232
+ return;
36233
+ }
36234
+ if (this.waveformDecodeController && !this.waveformDecodeController.signal.aborted) {
35950
36235
  return;
35951
36236
  }
35952
- this.abortClientWaveformDecode();
35953
36237
  const controller = new AbortController();
35954
36238
  this.waveformDecodeController = controller;
35955
36239
  const {
@@ -35998,9 +36282,7 @@ class MP3Viewer extends media_MediaBaseViewer {
35998
36282
  return;
35999
36283
  }
36000
36284
  if (result.status === 'ready') {
36001
- this.waveformPeaks = result.payload.peaks;
36002
- this.isWaveformDecodeRetryPending = false;
36003
- this.renderUI();
36285
+ this.applyWaveformPeaks('client', result.payload);
36004
36286
  return;
36005
36287
  }
36006
36288
  if (result.status === 'cancelled') {
@@ -36030,10 +36312,7 @@ class MP3Viewer extends media_MediaBaseViewer {
36030
36312
  containerEl: this.mediaContainerEl
36031
36313
  });
36032
36314
  }
36033
- if (this.isAudioPlayerV2) {
36034
- this.removeListener('comment_markers', this.handleCommentMarkersUpdated);
36035
- this.addListener('comment_markers', this.handleCommentMarkersUpdated);
36036
- }
36315
+ this.bindCommentMarkersListener();
36037
36316
  this.renderUI();
36038
36317
  }
36039
36318
  /**
@@ -36061,11 +36340,13 @@ class MP3Viewer extends media_MediaBaseViewer {
36061
36340
  if (!this.controls) {
36062
36341
  return;
36063
36342
  }
36343
+ const mediaDuration = this.mediaEl.duration;
36344
+ const durationTime = typeof mediaDuration === 'number' && mediaDuration > 0 ? mediaDuration : this.waveformDurationSec;
36064
36345
  const sharedProps = {
36065
36346
  autoplay: this.isAutoplayEnabled(),
36066
36347
  bufferedRange: this.mediaEl.buffered,
36067
36348
  currentTime: this.mediaEl.currentTime,
36068
- durationTime: this.mediaEl.duration,
36349
+ durationTime,
36069
36350
  isPlaying: !this.mediaEl.paused,
36070
36351
  movePlayback: this.movePlayback,
36071
36352
  onAutoplayChange: this.setAutoplay,
@@ -37718,8 +37999,6 @@ function VideoControls({
37718
37999
  }
37719
38000
  // EXTERNAL MODULE: ./src/lib/viewers/controls/media/TimestampControl.tsx + 1 modules
37720
38001
  var TimestampControl = __webpack_require__(3904);
37721
- // EXTERNAL MODULE: ./src/lib/viewers/controls/media/buildClusters.ts
37722
- var buildClusters = __webpack_require__(2400);
37723
38002
  // EXTERNAL MODULE: ./src/lib/viewers/media/formatTimecode.ts
37724
38003
  var formatTimecode = __webpack_require__(4179);
37725
38004
  ;// ./src/lib/viewers/controls/media/FilmstripV2.scss
@@ -37800,18 +38079,89 @@ function FilmstripV2({
37800
38079
  "data-testid": "bp-FilmstripV2-time"
37801
38080
  }, fps ? (0,formatTimecode/* default */.A)(time, fps) : (0,DurationLabels/* formatTime */.f)(time)));
37802
38081
  }
37803
- // EXTERNAL MODULE: ./src/lib/viewers/controls/media/MarkerAvatarStack.tsx + 1 modules
37804
- var MarkerAvatarStack = __webpack_require__(9232);
37805
- ;// ./src/lib/viewers/controls/media/MarkerCluster.scss
38082
+ // EXTERNAL MODULE: ./src/lib/viewers/controls/media/markers/helpers/useDismissableMarkerSelection.ts
38083
+ var useDismissableMarkerSelection = __webpack_require__(2378);
38084
+ // EXTERNAL MODULE: ./src/lib/viewers/controls/media/markers/helpers/buildClusters.ts
38085
+ var buildClusters = __webpack_require__(5510);
38086
+ // EXTERNAL MODULE: ./src/lib/viewers/controls/media/markers/MarkerAvatar.tsx + 1 modules
38087
+ var MarkerAvatar = __webpack_require__(2040);
38088
+ // EXTERNAL MODULE: ./src/lib/viewers/controls/media/markers/MarkerAvatarStack.tsx + 1 modules
38089
+ var MarkerAvatarStack = __webpack_require__(308);
38090
+ ;// ./src/lib/viewers/controls/media/markers/helpers/onMarkerGroupTickClick.ts
38091
+ /** Click the range/group tick: first member, unless one is already selected. */
38092
+ function onMarkerGroupTickClick(event, markers, selectedId, onMarkerClick) {
38093
+ event.stopPropagation();
38094
+ if (selectedId && markers.some(marker => marker.id === selectedId)) {
38095
+ return;
38096
+ }
38097
+ onMarkerClick?.(markers[0]);
38098
+ }
38099
+ ;// ./src/lib/viewers/controls/media/markers/MarkerTick.tsx
38100
+
38101
+
38102
+
38103
+
38104
+ function MarkerTick({
38105
+ markers,
38106
+ onMarkerClick,
38107
+ position,
38108
+ selectedId
38109
+ }) {
38110
+ const isGroup = markers.length > 1;
38111
+ const isSelected = !isGroup && markers[0].id === selectedId;
38112
+ const className = `bp-TimeControlsV2-marker${isGroup ? ' bp-TimeControlsV2-marker--group' : ''}${isSelected ? ' bp-TimeControlsV2-marker--selected' : ''}`;
38113
+ const style = {
38114
+ left: `${position}%`
38115
+ };
38116
+ if (isGroup) {
38117
+ return /*#__PURE__*/external_react_["default"].createElement("div", {
38118
+ className: className,
38119
+ "data-bp-marker-group": "",
38120
+ "data-testid": "bp-time-controls-marker",
38121
+ style: style
38122
+ }, /*#__PURE__*/external_react_["default"].createElement("button", {
38123
+ "aria-label": "Comment marker",
38124
+ className: "bp-TimeControlsV2-marker-tick",
38125
+ "data-resin-target": "commentMarkerGroup",
38126
+ onClick: e => onMarkerGroupTickClick(e, markers, selectedId, onMarkerClick),
38127
+ type: "button"
38128
+ }), /*#__PURE__*/external_react_["default"].createElement(MarkerAvatarStack/* default */.A, {
38129
+ markers: markers,
38130
+ onMarkerClick: onMarkerClick,
38131
+ selectedId: selectedId
38132
+ }));
38133
+ }
38134
+ return /*#__PURE__*/external_react_["default"].createElement("button", {
38135
+ "aria-label": "Comment marker",
38136
+ "aria-pressed": isSelected,
38137
+ className: className,
38138
+ "data-bp-marker-selected": isSelected ? '' : undefined,
38139
+ "data-resin-target": "commentMarker",
38140
+ "data-testid": "bp-time-controls-marker",
38141
+ onClick: e => {
38142
+ e.stopPropagation();
38143
+ onMarkerClick?.(markers[0]);
38144
+ },
38145
+ style: style,
38146
+ type: "button"
38147
+ }, /*#__PURE__*/external_react_["default"].createElement(MarkerAvatar/* default */.A, {
38148
+ avatarUrl: markers[0].avatarUrl,
38149
+ colorIndex: markers[0].colorIndex,
38150
+ initial: markers[0].initial
38151
+ }));
38152
+ }
38153
+ ;// ./src/lib/viewers/controls/media/markers/MarkerCluster.scss
37806
38154
  // extracted by mini-css-extract-plugin
37807
38155
 
37808
- ;// ./src/lib/viewers/controls/media/MarkerCluster.tsx
38156
+ ;// ./src/lib/viewers/controls/media/markers/MarkerCluster.tsx
38157
+
37809
38158
 
37810
38159
 
37811
38160
 
37812
38161
  function MarkerCluster({
37813
38162
  cluster,
37814
- onMarkerClick
38163
+ onMarkerClick,
38164
+ selectedId
37815
38165
  }) {
37816
38166
  const {
37817
38167
  markers,
@@ -37824,54 +38174,19 @@ function MarkerCluster({
37824
38174
  };
37825
38175
  return /*#__PURE__*/external_react_["default"].createElement("div", {
37826
38176
  className: "bp-MarkerCluster",
38177
+ "data-bp-marker-group": "",
37827
38178
  "data-testid": "bp-marker-cluster",
37828
38179
  style: style
37829
38180
  }, /*#__PURE__*/external_react_["default"].createElement("button", {
37830
38181
  "aria-label": "Comment marker",
37831
38182
  className: "bp-MarkerCluster-tick",
37832
38183
  "data-resin-target": "commentMarkerCluster",
37833
- onClick: e => {
37834
- e.stopPropagation();
37835
- onMarkerClick?.(markers[0]);
37836
- },
38184
+ onClick: e => onMarkerGroupTickClick(e, markers, selectedId, onMarkerClick),
37837
38185
  type: "button"
37838
38186
  }), /*#__PURE__*/external_react_["default"].createElement(MarkerAvatarStack/* default */.A, {
37839
38187
  markers: markers,
37840
- onMarkerClick: onMarkerClick
37841
- }));
37842
- }
37843
- // EXTERNAL MODULE: ./src/lib/viewers/controls/media/MarkerAvatar.tsx + 1 modules
37844
- var MarkerAvatar = __webpack_require__(3524);
37845
- ;// ./src/lib/viewers/controls/media/MarkerTick.tsx
37846
-
37847
-
37848
-
37849
- function MarkerTick({
37850
- markers,
37851
- onMarkerClick,
37852
- position
37853
- }) {
37854
- const isGroup = markers.length > 1;
37855
- return /*#__PURE__*/external_react_["default"].createElement("button", {
37856
- "aria-label": "Comment marker",
37857
- className: `bp-TimeControlsV2-marker${isGroup ? ' bp-TimeControlsV2-marker--group' : ''}`,
37858
- "data-resin-target": "commentMarker",
37859
- "data-testid": "bp-time-controls-marker",
37860
- onClick: e => {
37861
- e.stopPropagation();
37862
- onMarkerClick?.(markers[0]);
37863
- },
37864
- style: {
37865
- left: `${position}%`
37866
- },
37867
- type: "button"
37868
- }, isGroup ? /*#__PURE__*/external_react_["default"].createElement(MarkerAvatarStack/* default */.A, {
37869
- markers: markers,
37870
- onMarkerClick: onMarkerClick
37871
- }) : /*#__PURE__*/external_react_["default"].createElement(MarkerAvatar/* default */.A, {
37872
- avatarUrl: markers[0].avatarUrl,
37873
- colorIndex: markers[0].colorIndex,
37874
- initial: markers[0].initial
38188
+ onMarkerClick: onMarkerClick,
38189
+ selectedId: selectedId
37875
38190
  }));
37876
38191
  }
37877
38192
  // EXTERNAL MODULE: ./src/lib/viewers/controls/media/utils.ts
@@ -37889,8 +38204,6 @@ var utils = __webpack_require__(5346);
37889
38204
 
37890
38205
 
37891
38206
 
37892
-
37893
-
37894
38207
  const TRACK_COLOR_BUFFERED = 'rgb(127, 127, 127)';
37895
38208
  const TRACK_COLOR_UNPLAYED = 'rgb(85, 85, 85)';
37896
38209
  function TimeControlsV2({
@@ -37911,6 +38224,12 @@ function TimeControlsV2({
37911
38224
  const [hoverTime, setHoverTime] = external_react_["default"].useState(0);
37912
38225
  const [trackWidth, setTrackWidth] = external_react_["default"].useState(0);
37913
38226
  const scrubberRef = external_react_["default"].useRef(null);
38227
+ const hostSelectedId = external_react_["default"].useMemo(() => commentMarkers.find(marker => marker.isSelected)?.id ?? null, [commentMarkers]);
38228
+ const {
38229
+ containerRef,
38230
+ selectMarker,
38231
+ selectedId
38232
+ } = (0,useDismissableMarkerSelection/* default */.A)(hostSelectedId);
37914
38233
  const currentValue = isFinite_default()(currentTime) ? currentTime : 0;
37915
38234
  const durationValue = isFinite_default()(durationTime) ? durationTime : 0;
37916
38235
  const currentPercentage = (0,utils/* percent */.K)(currentValue, durationValue);
@@ -37928,6 +38247,10 @@ function TimeControlsV2({
37928
38247
  observer.observe(el);
37929
38248
  return () => observer.disconnect();
37930
38249
  }, []);
38250
+ const handleMarkerClick = external_react_["default"].useCallback(marker => {
38251
+ selectMarker(marker.id);
38252
+ onCommentMarkerClick?.(marker);
38253
+ }, [onCommentMarkerClick, selectMarker]);
37931
38254
  const clusters = external_react_["default"].useMemo(() => (0,buildClusters/* default */.A)(commentMarkers, durationValue, trackWidth), [commentMarkers, durationValue, trackWidth]);
37932
38255
  const trackMask = external_react_["default"].useMemo(() => {
37933
38256
  if (durationValue <= 0 || clusters.length === 0) return undefined;
@@ -37955,6 +38278,7 @@ function TimeControlsV2({
37955
38278
  setHoverTime(newTime);
37956
38279
  };
37957
38280
  return /*#__PURE__*/external_react_["default"].createElement("div", {
38281
+ ref: containerRef,
37958
38282
  className: "bp-TimeControlsV2",
37959
38283
  "data-testid": "bp-time-controls-v2"
37960
38284
  }, !!filmstripInterval && /*#__PURE__*/external_react_["default"].createElement(FilmstripV2, {
@@ -37990,12 +38314,14 @@ function TimeControlsV2({
37990
38314
  }), durationValue > 0 && clusters.map(cluster => cluster.isSinglePoint ? /*#__PURE__*/external_react_["default"].createElement(MarkerTick, {
37991
38315
  key: cluster.id,
37992
38316
  markers: cluster.markers,
37993
- onMarkerClick: onCommentMarkerClick,
37994
- position: cluster.leftPercent
38317
+ onMarkerClick: handleMarkerClick,
38318
+ position: cluster.leftPercent,
38319
+ selectedId: selectedId
37995
38320
  }) : /*#__PURE__*/external_react_["default"].createElement(MarkerCluster, {
37996
38321
  key: cluster.id,
37997
38322
  cluster: cluster,
37998
- onMarkerClick: onCommentMarkerClick
38323
+ onMarkerClick: handleMarkerClick,
38324
+ selectedId: selectedId
37999
38325
  }))));
38000
38326
  }
38001
38327
  ;// ./src/lib/viewers/controls/media/VideoFullscreenButton.scss
@@ -54252,8 +54578,12 @@ class Preview extends (events_default()) {
54252
54578
  if (isDash && isFeatureEnabled(this.options.features, constants/* AI_TRANSCRIPTION_FOR_VIDEO_SUBTITLES */.Iu)) {
54253
54579
  videoHint += constants/* X_REP_HINT_VIDEO_DASH_EXTRACTED_TEXT */.xU;
54254
54580
  }
54581
+ let hints = `${constants/* X_REP_HINT_BASE */.pl}${constants/* X_REP_HINT_DOC_THUMBNAIL */.nC}${constants/* X_REP_HINT_IMAGE */.h3}${videoHint}`;
54582
+ if (isFeatureEnabled(this.options.features, constants/* AUDIO_PLAYER_V2 */.Ku)) {
54583
+ hints += constants/* X_REP_HINT_WAVEFORM */.jr;
54584
+ }
54255
54585
  const headers = {
54256
- 'X-Rep-Hints': `${constants/* X_REP_HINT_BASE */.pl}${constants/* X_REP_HINT_DOC_THUMBNAIL */.nC}${constants/* X_REP_HINT_IMAGE */.h3}${videoHint}`
54586
+ 'X-Rep-Hints': hints
54257
54587
  };
54258
54588
  return (0,util/* getHeaders */.dJ)(headers, token || this.options.token, this.options.sharedLink, this.options.sharedLinkPassword);
54259
54589
  }