@waveform-playlist/ui-components 7.1.2 → 7.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -8,10 +8,7 @@ var PositionDisplay = styled.span`
8
8
  color: ${(props) => props.theme?.textColor || "#333"};
9
9
  user-select: none;
10
10
  `;
11
- var AudioPosition = ({
12
- formattedTime,
13
- className
14
- }) => {
11
+ var AudioPosition = ({ formattedTime, className }) => {
15
12
  return /* @__PURE__ */ jsx(PositionDisplay, { className, "aria-label": "Audio position", children: formattedTime });
16
13
  };
17
14
 
@@ -31,7 +28,9 @@ var BaseButton = styled2.button`
31
28
  border-radius: ${(props) => props.theme.borderRadius};
32
29
  cursor: pointer;
33
30
  outline: none;
34
- transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out,
31
+ transition:
32
+ background-color 0.15s ease-in-out,
33
+ border-color 0.15s ease-in-out,
35
34
  box-shadow 0.15s ease-in-out;
36
35
 
37
36
  &:hover:not(:disabled) {
@@ -128,7 +127,9 @@ var BaseInput = styled5.input`
128
127
  border: 1px solid ${(props) => props.theme.inputBorder};
129
128
  border-radius: ${(props) => props.theme.borderRadius};
130
129
  outline: none;
131
- transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
130
+ transition:
131
+ border-color 0.15s ease-in-out,
132
+ box-shadow 0.15s ease-in-out;
132
133
 
133
134
  &::placeholder {
134
135
  color: ${(props) => props.theme.inputPlaceholder};
@@ -196,7 +197,9 @@ var BaseSelect = styled7.select`
196
197
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23666' d='M6 8L1 3h10z'/%3E%3C/svg%3E");
197
198
  background-repeat: no-repeat;
198
199
  background-position: right 0.75rem center;
199
- transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
200
+ transition:
201
+ border-color 0.15s ease-in-out,
202
+ box-shadow 0.15s ease-in-out;
200
203
 
201
204
  &:focus {
202
205
  border-color: ${(props) => props.theme.inputFocusBorder};
@@ -242,7 +245,9 @@ var BaseSlider = styled8.input.attrs({ type: "range" })`
242
245
  border-radius: 50%;
243
246
  cursor: pointer;
244
247
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
245
- transition: transform 0.15s ease, box-shadow 0.15s ease;
248
+ transition:
249
+ transform 0.15s ease,
250
+ box-shadow 0.15s ease;
246
251
  }
247
252
 
248
253
  &::-webkit-slider-thumb:hover {
@@ -259,7 +264,9 @@ var BaseSlider = styled8.input.attrs({ type: "range" })`
259
264
  border-radius: 50%;
260
265
  cursor: pointer;
261
266
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
262
- transition: transform 0.15s ease, box-shadow 0.15s ease;
267
+ transition:
268
+ transform 0.15s ease,
269
+ box-shadow 0.15s ease;
263
270
  }
264
271
 
265
272
  &::-moz-range-thumb:hover {
@@ -530,10 +537,7 @@ var ViewportStoreContext = createContext(null);
530
537
  var EMPTY_SUBSCRIBE = () => () => {
531
538
  };
532
539
  var NULL_SNAPSHOT = () => null;
533
- var ScrollViewportProvider = ({
534
- containerRef,
535
- children
536
- }) => {
540
+ var ScrollViewportProvider = ({ containerRef, children }) => {
537
541
  const storeRef = useRef(null);
538
542
  if (storeRef.current === null) {
539
543
  storeRef.current = new ViewportStore();
@@ -588,7 +592,7 @@ function useScrollViewportSelector(selector) {
588
592
  () => selector(null)
589
593
  );
590
594
  }
591
- function useVisibleChunkIndices(totalWidth, chunkWidth) {
595
+ function useVisibleChunkIndices(totalWidth, chunkWidth, originX = 0) {
592
596
  const visibleChunkKey = useScrollViewportSelector((viewport) => {
593
597
  const totalChunks = Math.ceil(totalWidth / chunkWidth);
594
598
  const indices = [];
@@ -596,8 +600,9 @@ function useVisibleChunkIndices(totalWidth, chunkWidth) {
596
600
  const chunkLeft = i * chunkWidth;
597
601
  const thisChunkWidth = Math.min(totalWidth - chunkLeft, chunkWidth);
598
602
  if (viewport) {
599
- const chunkEnd = chunkLeft + thisChunkWidth;
600
- if (chunkEnd <= viewport.visibleStart || chunkLeft >= viewport.visibleEnd) {
603
+ const chunkLeftGlobal = originX + chunkLeft;
604
+ const chunkEndGlobal = chunkLeftGlobal + thisChunkWidth;
605
+ if (chunkEndGlobal <= viewport.visibleStart || chunkLeftGlobal >= viewport.visibleEnd) {
601
606
  continue;
602
607
  }
603
608
  }
@@ -611,6 +616,16 @@ function useVisibleChunkIndices(totalWidth, chunkWidth) {
611
616
  );
612
617
  }
613
618
 
619
+ // src/contexts/ClipViewportOrigin.tsx
620
+ import { createContext as createContext2, useContext as useContext2 } from "react";
621
+ import { jsx as jsx4 } from "react/jsx-runtime";
622
+ var ClipViewportOriginContext = createContext2(0);
623
+ var ClipViewportOriginProvider = ({
624
+ originX,
625
+ children
626
+ }) => /* @__PURE__ */ jsx4(ClipViewportOriginContext.Provider, { value: originX, children });
627
+ var useClipViewportOrigin = () => useContext2(ClipViewportOriginContext);
628
+
614
629
  // src/hooks/useChunkedCanvasRefs.ts
615
630
  import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2 } from "react";
616
631
  function useChunkedCanvasRefs() {
@@ -634,7 +649,7 @@ function useChunkedCanvasRefs() {
634
649
 
635
650
  // src/components/Channel.tsx
636
651
  import { MAX_CANVAS_WIDTH } from "@waveform-playlist/core";
637
- import { jsx as jsx4 } from "react/jsx-runtime";
652
+ import { jsx as jsx5 } from "react/jsx-runtime";
638
653
  function createCanvasFillStyle(ctx, color, width, height) {
639
654
  if (!isWaveformGradient(color)) {
640
655
  return color;
@@ -695,7 +710,8 @@ var Channel = (props) => {
695
710
  drawMode = "inverted"
696
711
  } = props;
697
712
  const { canvasRef, canvasMapRef } = useChunkedCanvasRefs();
698
- const visibleChunkIndices = useVisibleChunkIndices(length, MAX_CANVAS_WIDTH);
713
+ const clipOriginX = useClipViewportOrigin();
714
+ const visibleChunkIndices = useVisibleChunkIndices(length, MAX_CANVAS_WIDTH, clipOriginX);
699
715
  useLayoutEffect(() => {
700
716
  const step = barWidth + barGap;
701
717
  for (const [canvasIdx, canvas] of canvasMapRef.current.entries()) {
@@ -715,12 +731,7 @@ var Channel = (props) => {
715
731
  } else {
716
732
  fillColor = waveOutlineColor;
717
733
  }
718
- ctx.fillStyle = createCanvasFillStyle(
719
- ctx,
720
- fillColor,
721
- canvasWidth,
722
- waveHeight
723
- );
734
+ ctx.fillStyle = createCanvasFillStyle(ctx, fillColor, canvasWidth, waveHeight);
724
735
  const canvasStartGlobal = globalPixelOffset;
725
736
  const canvasEndGlobal = globalPixelOffset + canvasWidth;
726
737
  const firstBarGlobal = Math.floor((canvasStartGlobal - barWidth + step) / step) * step;
@@ -760,7 +771,7 @@ var Channel = (props) => {
760
771
  const waveforms = visibleChunkIndices.map((i) => {
761
772
  const chunkLeft = i * MAX_CANVAS_WIDTH;
762
773
  const currentWidth = Math.min(length - chunkLeft, MAX_CANVAS_WIDTH);
763
- return /* @__PURE__ */ jsx4(
774
+ return /* @__PURE__ */ jsx5(
764
775
  Waveform,
765
776
  {
766
777
  $cssWidth: currentWidth,
@@ -776,7 +787,7 @@ var Channel = (props) => {
776
787
  });
777
788
  const bgColor = waveFillColor;
778
789
  const backgroundCss = transparentBackground ? "transparent" : waveformColorToCss(bgColor);
779
- return /* @__PURE__ */ jsx4(
790
+ return /* @__PURE__ */ jsx5(
780
791
  Wrapper,
781
792
  {
782
793
  $index: index,
@@ -790,8 +801,8 @@ var Channel = (props) => {
790
801
  };
791
802
 
792
803
  // src/components/ErrorBoundary.tsx
793
- import React3 from "react";
794
- import { jsx as jsx5 } from "react/jsx-runtime";
804
+ import React4 from "react";
805
+ import { jsx as jsx6 } from "react/jsx-runtime";
795
806
  var errorContainerStyle = {
796
807
  padding: "16px",
797
808
  background: "#1a1a2e",
@@ -805,7 +816,7 @@ var errorContainerStyle = {
805
816
  alignItems: "center",
806
817
  justifyContent: "center"
807
818
  };
808
- var PlaylistErrorBoundary = class extends React3.Component {
819
+ var PlaylistErrorBoundary = class extends React4.Component {
809
820
  constructor(props) {
810
821
  super(props);
811
822
  this.state = { hasError: false, error: null };
@@ -821,7 +832,7 @@ var PlaylistErrorBoundary = class extends React3.Component {
821
832
  if (this.props.fallback) {
822
833
  return this.props.fallback;
823
834
  }
824
- return /* @__PURE__ */ jsx5("div", { style: errorContainerStyle, children: "Waveform playlist encountered an error. Check console for details." });
835
+ return /* @__PURE__ */ jsx6("div", { style: errorContainerStyle, children: "Waveform playlist encountered an error. Check console for details." });
825
836
  }
826
837
  return this.props.children;
827
838
  }
@@ -834,7 +845,7 @@ import { CSS } from "@dnd-kit/utilities";
834
845
 
835
846
  // src/components/ClipHeader.tsx
836
847
  import styled10 from "styled-components";
837
- import { jsx as jsx6 } from "react/jsx-runtime";
848
+ import { jsx as jsx7 } from "react/jsx-runtime";
838
849
  var CLIP_HEADER_HEIGHT = 22;
839
850
  var HeaderContainer = styled10.div`
840
851
  position: relative;
@@ -874,15 +885,7 @@ var ClipHeaderPresentational = ({
874
885
  trackName,
875
886
  isSelected = false
876
887
  }) => {
877
- return /* @__PURE__ */ jsx6(
878
- HeaderContainer,
879
- {
880
- $isDragging: false,
881
- $interactive: false,
882
- $isSelected: isSelected,
883
- children: /* @__PURE__ */ jsx6(TrackName, { children: trackName })
884
- }
885
- );
888
+ return /* @__PURE__ */ jsx7(HeaderContainer, { $isDragging: false, $interactive: false, $isSelected: isSelected, children: /* @__PURE__ */ jsx7(TrackName, { children: trackName }) });
886
889
  };
887
890
  var ClipHeader = ({
888
891
  clipId,
@@ -894,16 +897,10 @@ var ClipHeader = ({
894
897
  dragHandleProps
895
898
  }) => {
896
899
  if (disableDrag || !dragHandleProps) {
897
- return /* @__PURE__ */ jsx6(
898
- ClipHeaderPresentational,
899
- {
900
- trackName,
901
- isSelected
902
- }
903
- );
900
+ return /* @__PURE__ */ jsx7(ClipHeaderPresentational, { trackName, isSelected });
904
901
  }
905
902
  const { attributes, listeners, setActivatorNodeRef } = dragHandleProps;
906
- return /* @__PURE__ */ jsx6(
903
+ return /* @__PURE__ */ jsx7(
907
904
  HeaderContainer,
908
905
  {
909
906
  ref: setActivatorNodeRef,
@@ -912,15 +909,15 @@ var ClipHeader = ({
912
909
  $isSelected: isSelected,
913
910
  ...listeners,
914
911
  ...attributes,
915
- children: /* @__PURE__ */ jsx6(TrackName, { children: trackName })
912
+ children: /* @__PURE__ */ jsx7(TrackName, { children: trackName })
916
913
  }
917
914
  );
918
915
  };
919
916
 
920
917
  // src/components/ClipBoundary.tsx
921
- import React4 from "react";
918
+ import React5 from "react";
922
919
  import styled11 from "styled-components";
923
- import { jsx as jsx7 } from "react/jsx-runtime";
920
+ import { jsx as jsx8 } from "react/jsx-runtime";
924
921
  var CLIP_BOUNDARY_WIDTH = 8;
925
922
  var CLIP_BOUNDARY_WIDTH_TOUCH = 24;
926
923
  var BoundaryContainer = styled11.div`
@@ -960,12 +957,12 @@ var ClipBoundary = ({
960
957
  dragHandleProps,
961
958
  touchOptimized = false
962
959
  }) => {
963
- const [isHovered, setIsHovered] = React4.useState(false);
960
+ const [isHovered, setIsHovered] = React5.useState(false);
964
961
  if (!dragHandleProps) {
965
962
  return null;
966
963
  }
967
964
  const { attributes, listeners, setActivatorNodeRef, isDragging } = dragHandleProps;
968
- return /* @__PURE__ */ jsx7(
965
+ return /* @__PURE__ */ jsx8(
969
966
  BoundaryContainer,
970
967
  {
971
968
  ref: setActivatorNodeRef,
@@ -985,7 +982,7 @@ var ClipBoundary = ({
985
982
 
986
983
  // src/components/FadeOverlay.tsx
987
984
  import styled12, { useTheme } from "styled-components";
988
- import { jsx as jsx8 } from "react/jsx-runtime";
985
+ import { jsx as jsx9 } from "react/jsx-runtime";
989
986
  var FadeContainer = styled12.div.attrs((props) => ({
990
987
  style: {
991
988
  left: `${props.$left}px`,
@@ -1042,17 +1039,11 @@ var FadeOverlay = ({
1042
1039
  const theme = useTheme();
1043
1040
  if (width < 1) return null;
1044
1041
  const fillColor = color || theme?.fadeOverlayColor || "rgba(0, 0, 0, 0.4)";
1045
- return /* @__PURE__ */ jsx8(FadeContainer, { $left: left, $width: width, $type: type, children: /* @__PURE__ */ jsx8(FadeSvg, { $type: type, viewBox: `0 0 ${width} 100`, preserveAspectRatio: "none", children: /* @__PURE__ */ jsx8(
1046
- "path",
1047
- {
1048
- d: generateFadePath(width, 100, curveType),
1049
- fill: fillColor
1050
- }
1051
- ) }) });
1042
+ return /* @__PURE__ */ jsx9(FadeContainer, { $left: left, $width: width, $type: type, children: /* @__PURE__ */ jsx9(FadeSvg, { $type: type, viewBox: `0 0 ${width} 100`, preserveAspectRatio: "none", children: /* @__PURE__ */ jsx9("path", { d: generateFadePath(width, 100, curveType), fill: fillColor }) }) });
1052
1043
  };
1053
1044
 
1054
1045
  // src/components/Clip.tsx
1055
- import { Fragment, jsx as jsx9, jsxs as jsxs2 } from "react/jsx-runtime";
1046
+ import { Fragment, jsx as jsx10, jsxs as jsxs2 } from "react/jsx-runtime";
1056
1047
  var ClipContainer = styled13.div.attrs((props) => ({
1057
1048
  style: props.$isOverlay ? {} : {
1058
1049
  left: `${props.$left}px`,
@@ -1150,7 +1141,7 @@ var Clip = ({
1150
1141
  "data-track-id": trackId,
1151
1142
  onMouseDown,
1152
1143
  children: [
1153
- showHeader && /* @__PURE__ */ jsx9(
1144
+ showHeader && /* @__PURE__ */ jsx10(
1154
1145
  ClipHeader,
1155
1146
  {
1156
1147
  clipId,
@@ -1162,9 +1153,9 @@ var Clip = ({
1162
1153
  dragHandleProps: enableDrag ? { attributes, listeners, setActivatorNodeRef } : void 0
1163
1154
  }
1164
1155
  ),
1165
- /* @__PURE__ */ jsxs2(ChannelsWrapper, { $isOverlay: isOverlay, children: [
1156
+ /* @__PURE__ */ jsx10(ClipViewportOriginProvider, { originX: left, children: /* @__PURE__ */ jsxs2(ChannelsWrapper, { $isOverlay: isOverlay, children: [
1166
1157
  children,
1167
- showFades && fadeIn && fadeIn.duration > 0 && /* @__PURE__ */ jsx9(
1158
+ showFades && fadeIn && fadeIn.duration > 0 && /* @__PURE__ */ jsx10(
1168
1159
  FadeOverlay,
1169
1160
  {
1170
1161
  left: 0,
@@ -1173,7 +1164,7 @@ var Clip = ({
1173
1164
  curveType: fadeIn.type
1174
1165
  }
1175
1166
  ),
1176
- showFades && fadeOut && fadeOut.duration > 0 && /* @__PURE__ */ jsx9(
1167
+ showFades && fadeOut && fadeOut.duration > 0 && /* @__PURE__ */ jsx10(
1177
1168
  FadeOverlay,
1178
1169
  {
1179
1170
  left: width - Math.floor(fadeOut.duration * sampleRate / samplesPerPixel),
@@ -1182,9 +1173,9 @@ var Clip = ({
1182
1173
  curveType: fadeOut.type
1183
1174
  }
1184
1175
  )
1185
- ] }),
1176
+ ] }) }),
1186
1177
  showHeader && !disableHeaderDrag && !isOverlay && /* @__PURE__ */ jsxs2(Fragment, { children: [
1187
- /* @__PURE__ */ jsx9(
1178
+ /* @__PURE__ */ jsx10(
1188
1179
  ClipBoundary,
1189
1180
  {
1190
1181
  clipId,
@@ -1200,7 +1191,7 @@ var Clip = ({
1200
1191
  }
1201
1192
  }
1202
1193
  ),
1203
- /* @__PURE__ */ jsx9(
1194
+ /* @__PURE__ */ jsx10(
1204
1195
  ClipBoundary,
1205
1196
  {
1206
1197
  clipId,
@@ -1224,7 +1215,7 @@ var Clip = ({
1224
1215
 
1225
1216
  // src/components/MasterVolumeControl.tsx
1226
1217
  import styled14 from "styled-components";
1227
- import { jsx as jsx10, jsxs as jsxs3 } from "react/jsx-runtime";
1218
+ import { jsx as jsx11, jsxs as jsxs3 } from "react/jsx-runtime";
1228
1219
  var VolumeContainer = styled14.div`
1229
1220
  display: inline-flex;
1230
1221
  align-items: center;
@@ -1247,8 +1238,8 @@ var MasterVolumeControl = ({
1247
1238
  onChange(parseFloat(e.target.value) / 100);
1248
1239
  };
1249
1240
  return /* @__PURE__ */ jsxs3(VolumeContainer, { className, children: [
1250
- /* @__PURE__ */ jsx10(VolumeLabel, { htmlFor: "master-gain", children: "Master Volume" }),
1251
- /* @__PURE__ */ jsx10(
1241
+ /* @__PURE__ */ jsx11(VolumeLabel, { htmlFor: "master-gain", children: "Master Volume" }),
1242
+ /* @__PURE__ */ jsx11(
1252
1243
  VolumeSlider,
1253
1244
  {
1254
1245
  min: "0",
@@ -1265,7 +1256,7 @@ var MasterVolumeControl = ({
1265
1256
  // src/components/Playhead.tsx
1266
1257
  import { useRef as useRef3, useEffect as useEffect3 } from "react";
1267
1258
  import styled15 from "styled-components";
1268
- import { jsx as jsx11, jsxs as jsxs4 } from "react/jsx-runtime";
1259
+ import { jsx as jsx12, jsxs as jsxs4 } from "react/jsx-runtime";
1269
1260
  var PlayheadLine = styled15.div.attrs((props) => ({
1270
1261
  style: {
1271
1262
  transform: `translate3d(${props.$position}px, 0, 0)`
@@ -1282,7 +1273,7 @@ var PlayheadLine = styled15.div.attrs((props) => ({
1282
1273
  will-change: transform;
1283
1274
  `;
1284
1275
  var Playhead = ({ position, color = "#ff0000" }) => {
1285
- return /* @__PURE__ */ jsx11(PlayheadLine, { $position: position, $color: color });
1276
+ return /* @__PURE__ */ jsx12(PlayheadLine, { $position: position, $color: color });
1286
1277
  };
1287
1278
  var PlayheadWithMarkerContainer = styled15.div`
1288
1279
  position: absolute;
@@ -1352,7 +1343,16 @@ var PlayheadWithMarker = ({
1352
1343
  animationFrameRef.current = null;
1353
1344
  }
1354
1345
  };
1355
- }, [isPlaying, sampleRate, samplesPerPixel, controlsOffset, currentTimeRef, playbackStartTimeRef, audioStartPositionRef, getAudioContextTime]);
1346
+ }, [
1347
+ isPlaying,
1348
+ sampleRate,
1349
+ samplesPerPixel,
1350
+ controlsOffset,
1351
+ currentTimeRef,
1352
+ playbackStartTimeRef,
1353
+ audioStartPositionRef,
1354
+ getAudioContextTime
1355
+ ]);
1356
1356
  useEffect3(() => {
1357
1357
  if (!isPlaying && containerRef.current) {
1358
1358
  const time = currentTimeRef.current ?? 0;
@@ -1361,15 +1361,15 @@ var PlayheadWithMarker = ({
1361
1361
  }
1362
1362
  });
1363
1363
  return /* @__PURE__ */ jsxs4(PlayheadWithMarkerContainer, { ref: containerRef, $color: color, children: [
1364
- /* @__PURE__ */ jsx11(MarkerTriangle, { $color: color }),
1365
- /* @__PURE__ */ jsx11(MarkerLine, { $color: color })
1364
+ /* @__PURE__ */ jsx12(MarkerTriangle, { $color: color }),
1365
+ /* @__PURE__ */ jsx12(MarkerLine, { $color: color })
1366
1366
  ] });
1367
1367
  };
1368
1368
 
1369
1369
  // src/components/Playlist.tsx
1370
1370
  import styled16, { withTheme } from "styled-components";
1371
1371
  import { useRef as useRef4, useCallback as useCallback3 } from "react";
1372
- import { jsx as jsx12, jsxs as jsxs5 } from "react/jsx-runtime";
1372
+ import { jsx as jsx13, jsxs as jsxs5 } from "react/jsx-runtime";
1373
1373
  var Wrapper2 = styled16.div`
1374
1374
  overflow-y: hidden;
1375
1375
  overflow-x: auto;
@@ -1424,40 +1424,36 @@ var Playlist = ({
1424
1424
  "data-playlist-state": playlistState
1425
1425
  }) => {
1426
1426
  const wrapperRef = useRef4(null);
1427
- const handleRef = useCallback3((el) => {
1428
- wrapperRef.current = el;
1429
- scrollContainerRef?.(el);
1430
- }, [scrollContainerRef]);
1431
- return /* @__PURE__ */ jsx12(Wrapper2, { "data-scroll-container": "true", "data-playlist-state": playlistState, ref: handleRef, children: /* @__PURE__ */ jsx12(ScrollViewportProvider, { containerRef: wrapperRef, children: /* @__PURE__ */ jsxs5(
1432
- ScrollContainer,
1433
- {
1434
- $backgroundColor: backgroundColor,
1435
- $width: scrollContainerWidth,
1436
- children: [
1437
- timescale && /* @__PURE__ */ jsx12(TimescaleWrapper, { $width: timescaleWidth, $backgroundColor: timescaleBackgroundColor, children: timescale }),
1438
- /* @__PURE__ */ jsxs5(TracksContainer, { $width: tracksWidth, $backgroundColor: backgroundColor, children: [
1439
- children,
1440
- (onTracksClick || onTracksMouseDown) && /* @__PURE__ */ jsx12(
1441
- ClickOverlay,
1442
- {
1443
- $controlsWidth: controlsWidth,
1444
- $isSelecting: isSelecting,
1445
- onClick: onTracksClick,
1446
- onMouseDown: onTracksMouseDown,
1447
- onMouseMove: onTracksMouseMove,
1448
- onMouseUp: onTracksMouseUp
1449
- }
1450
- )
1451
- ] })
1452
- ]
1453
- }
1454
- ) }) });
1427
+ const handleRef = useCallback3(
1428
+ (el) => {
1429
+ wrapperRef.current = el;
1430
+ scrollContainerRef?.(el);
1431
+ },
1432
+ [scrollContainerRef]
1433
+ );
1434
+ return /* @__PURE__ */ jsx13(Wrapper2, { "data-scroll-container": "true", "data-playlist-state": playlistState, ref: handleRef, children: /* @__PURE__ */ jsx13(ScrollViewportProvider, { containerRef: wrapperRef, children: /* @__PURE__ */ jsxs5(ScrollContainer, { $backgroundColor: backgroundColor, $width: scrollContainerWidth, children: [
1435
+ timescale && /* @__PURE__ */ jsx13(TimescaleWrapper, { $width: timescaleWidth, $backgroundColor: timescaleBackgroundColor, children: timescale }),
1436
+ /* @__PURE__ */ jsxs5(TracksContainer, { $width: tracksWidth, $backgroundColor: backgroundColor, children: [
1437
+ children,
1438
+ (onTracksClick || onTracksMouseDown) && /* @__PURE__ */ jsx13(
1439
+ ClickOverlay,
1440
+ {
1441
+ $controlsWidth: controlsWidth,
1442
+ $isSelecting: isSelecting,
1443
+ onClick: onTracksClick,
1444
+ onMouseDown: onTracksMouseDown,
1445
+ onMouseMove: onTracksMouseMove,
1446
+ onMouseUp: onTracksMouseUp
1447
+ }
1448
+ )
1449
+ ] })
1450
+ ] }) }) });
1455
1451
  };
1456
1452
  var StyledPlaylist = withTheme(Playlist);
1457
1453
 
1458
1454
  // src/components/Selection.tsx
1459
1455
  import styled17 from "styled-components";
1460
- import { jsx as jsx13 } from "react/jsx-runtime";
1456
+ import { jsx as jsx14 } from "react/jsx-runtime";
1461
1457
  var SelectionOverlay = styled17.div.attrs((props) => ({
1462
1458
  style: {
1463
1459
  left: `${props.$left}px`,
@@ -1481,13 +1477,13 @@ var Selection = ({
1481
1477
  if (width <= 0) {
1482
1478
  return null;
1483
1479
  }
1484
- return /* @__PURE__ */ jsx13(SelectionOverlay, { $left: startPosition, $width: width, $color: color, "data-selection": true });
1480
+ return /* @__PURE__ */ jsx14(SelectionOverlay, { $left: startPosition, $width: width, $color: color, "data-selection": true });
1485
1481
  };
1486
1482
 
1487
1483
  // src/components/LoopRegion.tsx
1488
1484
  import { useCallback as useCallback4, useRef as useRef5, useState } from "react";
1489
1485
  import styled18 from "styled-components";
1490
- import { Fragment as Fragment2, jsx as jsx14, jsxs as jsxs6 } from "react/jsx-runtime";
1486
+ import { Fragment as Fragment2, jsx as jsx15, jsxs as jsxs6 } from "react/jsx-runtime";
1491
1487
  var LoopRegionOverlayDiv = styled18.div.attrs((props) => ({
1492
1488
  style: {
1493
1489
  left: `${props.$left}px`,
@@ -1537,7 +1533,7 @@ var LoopRegion = ({
1537
1533
  return null;
1538
1534
  }
1539
1535
  return /* @__PURE__ */ jsxs6(Fragment2, { children: [
1540
- /* @__PURE__ */ jsx14(
1536
+ /* @__PURE__ */ jsx15(
1541
1537
  LoopRegionOverlayDiv,
1542
1538
  {
1543
1539
  $left: startPosition,
@@ -1546,7 +1542,7 @@ var LoopRegion = ({
1546
1542
  "data-loop-region": true
1547
1543
  }
1548
1544
  ),
1549
- /* @__PURE__ */ jsx14(
1545
+ /* @__PURE__ */ jsx15(
1550
1546
  LoopMarker,
1551
1547
  {
1552
1548
  $left: startPosition,
@@ -1555,7 +1551,7 @@ var LoopRegion = ({
1555
1551
  "data-loop-marker": "start"
1556
1552
  }
1557
1553
  ),
1558
- /* @__PURE__ */ jsx14(
1554
+ /* @__PURE__ */ jsx15(
1559
1555
  LoopMarker,
1560
1556
  {
1561
1557
  $left: endPosition - 2,
@@ -1641,66 +1637,72 @@ var LoopRegionMarkers = ({
1641
1637
  const dragStartPosition = useRef5(0);
1642
1638
  const dragStartEnd = useRef5(0);
1643
1639
  const width = Math.max(0, endPosition - startPosition);
1644
- const handleMarkerMouseDown = useCallback4((e, marker) => {
1645
- e.preventDefault();
1646
- e.stopPropagation();
1647
- setDraggingMarker(marker);
1648
- dragStartX.current = e.clientX;
1649
- dragStartPosition.current = marker === "start" ? startPosition : endPosition;
1650
- const handleMouseMove = (moveEvent) => {
1651
- const delta = moveEvent.clientX - dragStartX.current;
1652
- const newPosition = dragStartPosition.current + delta;
1653
- if (marker === "start") {
1654
- const clampedPosition = Math.max(minPosition, Math.min(endPosition - 10, newPosition));
1655
- onLoopStartChange?.(clampedPosition);
1656
- } else {
1657
- const clampedPosition = Math.max(startPosition + 10, Math.min(maxPosition, newPosition));
1658
- onLoopEndChange?.(clampedPosition);
1659
- }
1660
- };
1661
- const handleMouseUp = () => {
1662
- setDraggingMarker(null);
1663
- document.removeEventListener("mousemove", handleMouseMove);
1664
- document.removeEventListener("mouseup", handleMouseUp);
1665
- };
1666
- document.addEventListener("mousemove", handleMouseMove);
1667
- document.addEventListener("mouseup", handleMouseUp);
1668
- }, [startPosition, endPosition, minPosition, maxPosition, onLoopStartChange, onLoopEndChange]);
1669
- const handleRegionMouseDown = useCallback4((e) => {
1670
- e.preventDefault();
1671
- e.stopPropagation();
1672
- setDraggingMarker("region");
1673
- dragStartX.current = e.clientX;
1674
- dragStartPosition.current = startPosition;
1675
- dragStartEnd.current = endPosition;
1676
- const regionWidth = endPosition - startPosition;
1677
- const handleMouseMove = (moveEvent) => {
1678
- const delta = moveEvent.clientX - dragStartX.current;
1679
- let newStart = dragStartPosition.current + delta;
1680
- let newEnd = dragStartEnd.current + delta;
1681
- if (newStart < minPosition) {
1682
- newStart = minPosition;
1683
- newEnd = minPosition + regionWidth;
1684
- }
1685
- if (newEnd > maxPosition) {
1686
- newEnd = maxPosition;
1687
- newStart = maxPosition - regionWidth;
1688
- }
1689
- onLoopRegionMove?.(newStart, newEnd);
1690
- };
1691
- const handleMouseUp = () => {
1692
- setDraggingMarker(null);
1693
- document.removeEventListener("mousemove", handleMouseMove);
1694
- document.removeEventListener("mouseup", handleMouseUp);
1695
- };
1696
- document.addEventListener("mousemove", handleMouseMove);
1697
- document.addEventListener("mouseup", handleMouseUp);
1698
- }, [startPosition, endPosition, minPosition, maxPosition, onLoopRegionMove]);
1640
+ const handleMarkerMouseDown = useCallback4(
1641
+ (e, marker) => {
1642
+ e.preventDefault();
1643
+ e.stopPropagation();
1644
+ setDraggingMarker(marker);
1645
+ dragStartX.current = e.clientX;
1646
+ dragStartPosition.current = marker === "start" ? startPosition : endPosition;
1647
+ const handleMouseMove = (moveEvent) => {
1648
+ const delta = moveEvent.clientX - dragStartX.current;
1649
+ const newPosition = dragStartPosition.current + delta;
1650
+ if (marker === "start") {
1651
+ const clampedPosition = Math.max(minPosition, Math.min(endPosition - 10, newPosition));
1652
+ onLoopStartChange?.(clampedPosition);
1653
+ } else {
1654
+ const clampedPosition = Math.max(startPosition + 10, Math.min(maxPosition, newPosition));
1655
+ onLoopEndChange?.(clampedPosition);
1656
+ }
1657
+ };
1658
+ const handleMouseUp = () => {
1659
+ setDraggingMarker(null);
1660
+ document.removeEventListener("mousemove", handleMouseMove);
1661
+ document.removeEventListener("mouseup", handleMouseUp);
1662
+ };
1663
+ document.addEventListener("mousemove", handleMouseMove);
1664
+ document.addEventListener("mouseup", handleMouseUp);
1665
+ },
1666
+ [startPosition, endPosition, minPosition, maxPosition, onLoopStartChange, onLoopEndChange]
1667
+ );
1668
+ const handleRegionMouseDown = useCallback4(
1669
+ (e) => {
1670
+ e.preventDefault();
1671
+ e.stopPropagation();
1672
+ setDraggingMarker("region");
1673
+ dragStartX.current = e.clientX;
1674
+ dragStartPosition.current = startPosition;
1675
+ dragStartEnd.current = endPosition;
1676
+ const regionWidth = endPosition - startPosition;
1677
+ const handleMouseMove = (moveEvent) => {
1678
+ const delta = moveEvent.clientX - dragStartX.current;
1679
+ let newStart = dragStartPosition.current + delta;
1680
+ let newEnd = dragStartEnd.current + delta;
1681
+ if (newStart < minPosition) {
1682
+ newStart = minPosition;
1683
+ newEnd = minPosition + regionWidth;
1684
+ }
1685
+ if (newEnd > maxPosition) {
1686
+ newEnd = maxPosition;
1687
+ newStart = maxPosition - regionWidth;
1688
+ }
1689
+ onLoopRegionMove?.(newStart, newEnd);
1690
+ };
1691
+ const handleMouseUp = () => {
1692
+ setDraggingMarker(null);
1693
+ document.removeEventListener("mousemove", handleMouseMove);
1694
+ document.removeEventListener("mouseup", handleMouseUp);
1695
+ };
1696
+ document.addEventListener("mousemove", handleMouseMove);
1697
+ document.addEventListener("mouseup", handleMouseUp);
1698
+ },
1699
+ [startPosition, endPosition, minPosition, maxPosition, onLoopRegionMove]
1700
+ );
1699
1701
  if (width <= 0) {
1700
1702
  return null;
1701
1703
  }
1702
1704
  return /* @__PURE__ */ jsxs6(Fragment2, { children: [
1703
- /* @__PURE__ */ jsx14(
1705
+ /* @__PURE__ */ jsx15(
1704
1706
  TimescaleLoopShade,
1705
1707
  {
1706
1708
  $left: startPosition,
@@ -1711,7 +1713,7 @@ var LoopRegionMarkers = ({
1711
1713
  "data-loop-region-timescale": true
1712
1714
  }
1713
1715
  ),
1714
- /* @__PURE__ */ jsx14(
1716
+ /* @__PURE__ */ jsx15(
1715
1717
  DraggableMarkerHandle,
1716
1718
  {
1717
1719
  $left: startPosition,
@@ -1722,7 +1724,7 @@ var LoopRegionMarkers = ({
1722
1724
  "data-loop-marker-handle": "start"
1723
1725
  }
1724
1726
  ),
1725
- /* @__PURE__ */ jsx14(
1727
+ /* @__PURE__ */ jsx15(
1726
1728
  DraggableMarkerHandle,
1727
1729
  {
1728
1730
  $left: endPosition,
@@ -1761,42 +1763,45 @@ var TimescaleLoopRegion = ({
1761
1763
  const createStartX = useRef5(0);
1762
1764
  const containerRef = useRef5(null);
1763
1765
  const hasLoopRegion = endPosition > startPosition;
1764
- const handleBackgroundMouseDown = useCallback4((e) => {
1765
- const target = e.target;
1766
- if (target.closest("[data-loop-marker-handle]") || target.closest("[data-loop-region-timescale]")) {
1767
- return;
1768
- }
1769
- e.preventDefault();
1770
- setIsCreating(true);
1771
- const rect = containerRef.current?.getBoundingClientRect();
1772
- if (!rect) return;
1773
- const clickX = e.clientX - rect.left;
1774
- const clampedX = Math.max(minPosition, Math.min(maxPosition, clickX));
1775
- createStartX.current = clampedX;
1776
- onLoopRegionChange?.(clampedX, clampedX);
1777
- const handleMouseMove = (moveEvent) => {
1778
- const currentX = moveEvent.clientX - rect.left;
1779
- const clampedCurrentX = Math.max(minPosition, Math.min(maxPosition, currentX));
1780
- const newStart = Math.min(createStartX.current, clampedCurrentX);
1781
- const newEnd = Math.max(createStartX.current, clampedCurrentX);
1782
- onLoopRegionChange?.(newStart, newEnd);
1783
- };
1784
- const handleMouseUp = () => {
1785
- setIsCreating(false);
1786
- document.removeEventListener("mousemove", handleMouseMove);
1787
- document.removeEventListener("mouseup", handleMouseUp);
1788
- };
1789
- document.addEventListener("mousemove", handleMouseMove);
1790
- document.addEventListener("mouseup", handleMouseUp);
1791
- }, [minPosition, maxPosition, onLoopRegionChange]);
1792
- return /* @__PURE__ */ jsx14(
1766
+ const handleBackgroundMouseDown = useCallback4(
1767
+ (e) => {
1768
+ const target = e.target;
1769
+ if (target.closest("[data-loop-marker-handle]") || target.closest("[data-loop-region-timescale]")) {
1770
+ return;
1771
+ }
1772
+ e.preventDefault();
1773
+ setIsCreating(true);
1774
+ const rect = containerRef.current?.getBoundingClientRect();
1775
+ if (!rect) return;
1776
+ const clickX = e.clientX - rect.left;
1777
+ const clampedX = Math.max(minPosition, Math.min(maxPosition, clickX));
1778
+ createStartX.current = clampedX;
1779
+ onLoopRegionChange?.(clampedX, clampedX);
1780
+ const handleMouseMove = (moveEvent) => {
1781
+ const currentX = moveEvent.clientX - rect.left;
1782
+ const clampedCurrentX = Math.max(minPosition, Math.min(maxPosition, currentX));
1783
+ const newStart = Math.min(createStartX.current, clampedCurrentX);
1784
+ const newEnd = Math.max(createStartX.current, clampedCurrentX);
1785
+ onLoopRegionChange?.(newStart, newEnd);
1786
+ };
1787
+ const handleMouseUp = () => {
1788
+ setIsCreating(false);
1789
+ document.removeEventListener("mousemove", handleMouseMove);
1790
+ document.removeEventListener("mouseup", handleMouseUp);
1791
+ };
1792
+ document.addEventListener("mousemove", handleMouseMove);
1793
+ document.addEventListener("mouseup", handleMouseUp);
1794
+ },
1795
+ [minPosition, maxPosition, onLoopRegionChange]
1796
+ );
1797
+ return /* @__PURE__ */ jsx15(
1793
1798
  TimescaleLoopCreator,
1794
1799
  {
1795
1800
  ref: containerRef,
1796
1801
  $leftOffset: controlsOffset,
1797
1802
  onMouseDown: handleBackgroundMouseDown,
1798
1803
  "data-timescale-loop-creator": true,
1799
- children: hasLoopRegion && /* @__PURE__ */ jsx14(
1804
+ children: hasLoopRegion && /* @__PURE__ */ jsx15(
1800
1805
  LoopRegionMarkers,
1801
1806
  {
1802
1807
  startPosition,
@@ -1868,7 +1873,7 @@ function parseTime(timeStr, format) {
1868
1873
  }
1869
1874
 
1870
1875
  // src/components/TimeInput.tsx
1871
- import { Fragment as Fragment3, jsx as jsx15, jsxs as jsxs7 } from "react/jsx-runtime";
1876
+ import { Fragment as Fragment3, jsx as jsx16, jsxs as jsxs7 } from "react/jsx-runtime";
1872
1877
  var TimeInput = ({
1873
1878
  id,
1874
1879
  label,
@@ -1900,8 +1905,8 @@ var TimeInput = ({
1900
1905
  }
1901
1906
  };
1902
1907
  return /* @__PURE__ */ jsxs7(Fragment3, { children: [
1903
- /* @__PURE__ */ jsx15(ScreenReaderOnly, { as: "label", htmlFor: id, children: label }),
1904
- /* @__PURE__ */ jsx15(
1908
+ /* @__PURE__ */ jsx16(ScreenReaderOnly, { as: "label", htmlFor: id, children: label }),
1909
+ /* @__PURE__ */ jsx16(
1905
1910
  BaseInput,
1906
1911
  {
1907
1912
  type: "text",
@@ -1918,7 +1923,7 @@ var TimeInput = ({
1918
1923
  };
1919
1924
 
1920
1925
  // src/components/SelectionTimeInputs.tsx
1921
- import { jsx as jsx16, jsxs as jsxs8 } from "react/jsx-runtime";
1926
+ import { jsx as jsx17, jsxs as jsxs8 } from "react/jsx-runtime";
1922
1927
  var SelectionTimeInputs = ({
1923
1928
  selectionStart,
1924
1929
  selectionEnd,
@@ -1952,7 +1957,7 @@ var SelectionTimeInputs = ({
1952
1957
  }
1953
1958
  };
1954
1959
  return /* @__PURE__ */ jsxs8("div", { className, children: [
1955
- /* @__PURE__ */ jsx16(
1960
+ /* @__PURE__ */ jsx17(
1956
1961
  TimeInput,
1957
1962
  {
1958
1963
  id: "audio_start",
@@ -1963,7 +1968,7 @@ var SelectionTimeInputs = ({
1963
1968
  onChange: handleStartChange
1964
1969
  }
1965
1970
  ),
1966
- /* @__PURE__ */ jsx16(
1971
+ /* @__PURE__ */ jsx17(
1967
1972
  TimeInput,
1968
1973
  {
1969
1974
  id: "audio_end",
@@ -1978,12 +1983,12 @@ var SelectionTimeInputs = ({
1978
1983
  };
1979
1984
 
1980
1985
  // src/contexts/DevicePixelRatio.tsx
1981
- import { useState as useState4, createContext as createContext2, useContext as useContext2 } from "react";
1982
- import { jsx as jsx17 } from "react/jsx-runtime";
1986
+ import { useState as useState4, createContext as createContext3, useContext as useContext3 } from "react";
1987
+ import { jsx as jsx18 } from "react/jsx-runtime";
1983
1988
  function getScale() {
1984
1989
  return window.devicePixelRatio;
1985
1990
  }
1986
- var DevicePixelRatioContext = createContext2(getScale());
1991
+ var DevicePixelRatioContext = createContext3(getScale());
1987
1992
  var DevicePixelRatioProvider = ({ children }) => {
1988
1993
  const [scale, setScale] = useState4(getScale());
1989
1994
  matchMedia(`(resolution: ${getScale()}dppx)`).addEventListener(
@@ -1993,13 +1998,13 @@ var DevicePixelRatioProvider = ({ children }) => {
1993
1998
  },
1994
1999
  { once: true }
1995
2000
  );
1996
- return /* @__PURE__ */ jsx17(DevicePixelRatioContext.Provider, { value: Math.ceil(scale), children });
2001
+ return /* @__PURE__ */ jsx18(DevicePixelRatioContext.Provider, { value: Math.ceil(scale), children });
1997
2002
  };
1998
- var useDevicePixelRatio = () => useContext2(DevicePixelRatioContext);
2003
+ var useDevicePixelRatio = () => useContext3(DevicePixelRatioContext);
1999
2004
 
2000
2005
  // src/contexts/PlaylistInfo.tsx
2001
- import { createContext as createContext3, useContext as useContext3 } from "react";
2002
- var PlaylistInfoContext = createContext3({
2006
+ import { createContext as createContext4, useContext as useContext4 } from "react";
2007
+ var PlaylistInfoContext = createContext4({
2003
2008
  sampleRate: 48e3,
2004
2009
  samplesPerPixel: 1e3,
2005
2010
  zoomLevels: [1e3, 1500, 2e3, 2500],
@@ -2013,26 +2018,26 @@ var PlaylistInfoContext = createContext3({
2013
2018
  barWidth: 1,
2014
2019
  barGap: 0
2015
2020
  });
2016
- var usePlaylistInfo = () => useContext3(PlaylistInfoContext);
2021
+ var usePlaylistInfo = () => useContext4(PlaylistInfoContext);
2017
2022
 
2018
2023
  // src/contexts/Theme.tsx
2019
- import { useContext as useContext4 } from "react";
2024
+ import { useContext as useContext5 } from "react";
2020
2025
  import { ThemeContext } from "styled-components";
2021
- var useTheme2 = () => useContext4(ThemeContext);
2026
+ var useTheme2 = () => useContext5(ThemeContext);
2022
2027
 
2023
2028
  // src/contexts/TrackControls.tsx
2024
- import { createContext as createContext4, useContext as useContext5, Fragment as Fragment4 } from "react";
2025
- import { jsx as jsx18 } from "react/jsx-runtime";
2026
- var TrackControlsContext = createContext4(/* @__PURE__ */ jsx18(Fragment4, {}));
2027
- var useTrackControls = () => useContext5(TrackControlsContext);
2029
+ import { createContext as createContext5, useContext as useContext6, Fragment as Fragment4 } from "react";
2030
+ import { jsx as jsx19 } from "react/jsx-runtime";
2031
+ var TrackControlsContext = createContext5(/* @__PURE__ */ jsx19(Fragment4, {}));
2032
+ var useTrackControls = () => useContext6(TrackControlsContext);
2028
2033
 
2029
2034
  // src/contexts/Playout.tsx
2030
2035
  import {
2031
2036
  useState as useState5,
2032
- createContext as createContext5,
2033
- useContext as useContext6
2037
+ createContext as createContext6,
2038
+ useContext as useContext7
2034
2039
  } from "react";
2035
- import { jsx as jsx19 } from "react/jsx-runtime";
2040
+ import { jsx as jsx20 } from "react/jsx-runtime";
2036
2041
  var defaultProgress = 0;
2037
2042
  var defaultIsPlaying = false;
2038
2043
  var defaultSelectionStart = 0;
@@ -2043,8 +2048,8 @@ var defaultPlayout = {
2043
2048
  selectionStart: defaultSelectionStart,
2044
2049
  selectionEnd: defaultSelectionEnd
2045
2050
  };
2046
- var PlayoutStatusContext = createContext5(defaultPlayout);
2047
- var PlayoutStatusUpdateContext = createContext5({
2051
+ var PlayoutStatusContext = createContext6(defaultPlayout);
2052
+ var PlayoutStatusUpdateContext = createContext6({
2048
2053
  setIsPlaying: () => {
2049
2054
  },
2050
2055
  setProgress: () => {
@@ -2061,16 +2066,16 @@ var PlayoutProvider = ({ children }) => {
2061
2066
  setSelectionStart(start);
2062
2067
  setSelectionEnd(end);
2063
2068
  };
2064
- return /* @__PURE__ */ jsx19(PlayoutStatusUpdateContext.Provider, { value: { setIsPlaying, setProgress, setSelection }, children: /* @__PURE__ */ jsx19(PlayoutStatusContext.Provider, { value: { isPlaying, progress, selectionStart, selectionEnd }, children }) });
2069
+ return /* @__PURE__ */ jsx20(PlayoutStatusUpdateContext.Provider, { value: { setIsPlaying, setProgress, setSelection }, children: /* @__PURE__ */ jsx20(PlayoutStatusContext.Provider, { value: { isPlaying, progress, selectionStart, selectionEnd }, children }) });
2065
2070
  };
2066
- var usePlayoutStatus = () => useContext6(PlayoutStatusContext);
2067
- var usePlayoutStatusUpdate = () => useContext6(PlayoutStatusUpdateContext);
2071
+ var usePlayoutStatus = () => useContext7(PlayoutStatusContext);
2072
+ var usePlayoutStatusUpdate = () => useContext7(PlayoutStatusUpdateContext);
2068
2073
 
2069
2074
  // src/components/SpectrogramChannel.tsx
2070
2075
  import { useLayoutEffect as useLayoutEffect2, useRef as useRef6, useEffect as useEffect6 } from "react";
2071
2076
  import styled19 from "styled-components";
2072
2077
  import { MAX_CANVAS_WIDTH as MAX_CANVAS_WIDTH2 } from "@waveform-playlist/core";
2073
- import { jsx as jsx20 } from "react/jsx-runtime";
2078
+ import { jsx as jsx21 } from "react/jsx-runtime";
2074
2079
  var LINEAR_FREQUENCY_SCALE = (f, minF, maxF) => (f - minF) / (maxF - minF);
2075
2080
  var Wrapper3 = styled19.div.attrs((props) => ({
2076
2081
  style: {
@@ -2129,7 +2134,8 @@ var SpectrogramChannel = ({
2129
2134
  const workerApiRef = useRef6(workerApi);
2130
2135
  const onCanvasesReadyRef = useRef6(onCanvasesReady);
2131
2136
  const isWorkerMode = !!(workerApi && clipId);
2132
- const visibleChunkIndices = useVisibleChunkIndices(length, MAX_CANVAS_WIDTH2);
2137
+ const clipOriginX = useClipViewportOrigin();
2138
+ const visibleChunkIndices = useVisibleChunkIndices(length, MAX_CANVAS_WIDTH2, clipOriginX);
2133
2139
  const lut = colorLUT ?? DEFAULT_COLOR_LUT;
2134
2140
  const maxF = maxFrequency ?? (data ? data.sampleRate / 2 : 22050);
2135
2141
  const scaleFn = frequencyScaleFn ?? LINEAR_FREQUENCY_SCALE;
@@ -2219,7 +2225,14 @@ var SpectrogramChannel = ({
2219
2225
  }, []);
2220
2226
  useLayoutEffect2(() => {
2221
2227
  if (isWorkerMode || !data) return;
2222
- const { frequencyBinCount, frameCount, hopSize, sampleRate, gainDb, rangeDb: rawRangeDb } = data;
2228
+ const {
2229
+ frequencyBinCount,
2230
+ frameCount,
2231
+ hopSize,
2232
+ sampleRate,
2233
+ gainDb,
2234
+ rangeDb: rawRangeDb
2235
+ } = data;
2223
2236
  const rangeDb = rawRangeDb === 0 ? 1 : rawRangeDb;
2224
2237
  const binToFreq = (bin) => bin / frequencyBinCount * (sampleRate / 2);
2225
2238
  for (const [canvasIdx, canvas] of canvasMapRef.current.entries()) {
@@ -2283,11 +2296,25 @@ var SpectrogramChannel = ({
2283
2296
  ctx.drawImage(tmpCanvas, 0, 0, canvas.width, canvas.height);
2284
2297
  }
2285
2298
  }
2286
- }, [canvasMapRef, isWorkerMode, data, length, waveHeight, devicePixelRatio, samplesPerPixel, lut, minFrequency, maxF, scaleFn, hasCustomFrequencyScale, visibleChunkIndices]);
2299
+ }, [
2300
+ canvasMapRef,
2301
+ isWorkerMode,
2302
+ data,
2303
+ length,
2304
+ waveHeight,
2305
+ devicePixelRatio,
2306
+ samplesPerPixel,
2307
+ lut,
2308
+ minFrequency,
2309
+ maxF,
2310
+ scaleFn,
2311
+ hasCustomFrequencyScale,
2312
+ visibleChunkIndices
2313
+ ]);
2287
2314
  const canvases = visibleChunkIndices.map((i) => {
2288
2315
  const chunkLeft = i * MAX_CANVAS_WIDTH2;
2289
2316
  const currentWidth = Math.min(length - chunkLeft, MAX_CANVAS_WIDTH2);
2290
- return /* @__PURE__ */ jsx20(
2317
+ return /* @__PURE__ */ jsx21(
2291
2318
  SpectrogramCanvas,
2292
2319
  {
2293
2320
  $cssWidth: currentWidth,
@@ -2301,11 +2328,11 @@ var SpectrogramChannel = ({
2301
2328
  `${length}-${i}`
2302
2329
  );
2303
2330
  });
2304
- return /* @__PURE__ */ jsx20(Wrapper3, { $index: index, $cssWidth: length, $waveHeight: waveHeight, children: canvases });
2331
+ return /* @__PURE__ */ jsx21(Wrapper3, { $index: index, $cssWidth: length, $waveHeight: waveHeight, children: canvases });
2305
2332
  };
2306
2333
 
2307
2334
  // src/components/SmartChannel.tsx
2308
- import { Fragment as Fragment5, jsx as jsx21, jsxs as jsxs9 } from "react/jsx-runtime";
2335
+ import { Fragment as Fragment5, jsx as jsx22, jsxs as jsxs9 } from "react/jsx-runtime";
2309
2336
  var SmartChannel = ({
2310
2337
  isSelected,
2311
2338
  transparentBackground,
@@ -2330,7 +2357,7 @@ var SmartChannel = ({
2330
2357
  const drawMode = theme?.waveformDrawMode || "inverted";
2331
2358
  const hasSpectrogram = spectrogramData || spectrogramWorkerApi;
2332
2359
  if (renderMode === "spectrogram" && hasSpectrogram) {
2333
- return /* @__PURE__ */ jsx21(
2360
+ return /* @__PURE__ */ jsx22(
2334
2361
  SpectrogramChannel,
2335
2362
  {
2336
2363
  index: props.index,
@@ -2352,7 +2379,7 @@ var SmartChannel = ({
2352
2379
  if (renderMode === "both" && hasSpectrogram) {
2353
2380
  const halfHeight = Math.floor(waveHeight / 2);
2354
2381
  return /* @__PURE__ */ jsxs9(Fragment5, { children: [
2355
- /* @__PURE__ */ jsx21(
2382
+ /* @__PURE__ */ jsx22(
2356
2383
  SpectrogramChannel,
2357
2384
  {
2358
2385
  index: props.index * 2,
@@ -2371,24 +2398,35 @@ var SmartChannel = ({
2371
2398
  onCanvasesReady: spectrogramOnCanvasesReady
2372
2399
  }
2373
2400
  ),
2374
- /* @__PURE__ */ jsx21("div", { style: { position: "absolute", top: (props.index * 2 + 1) * halfHeight, width: props.length, height: halfHeight }, children: /* @__PURE__ */ jsx21(
2375
- Channel,
2401
+ /* @__PURE__ */ jsx22(
2402
+ "div",
2376
2403
  {
2377
- ...props,
2378
- index: 0,
2379
- waveOutlineColor,
2380
- waveFillColor,
2381
- waveHeight: halfHeight,
2382
- devicePixelRatio,
2383
- barWidth,
2384
- barGap,
2385
- transparentBackground,
2386
- drawMode
2404
+ style: {
2405
+ position: "absolute",
2406
+ top: (props.index * 2 + 1) * halfHeight,
2407
+ width: props.length,
2408
+ height: halfHeight
2409
+ },
2410
+ children: /* @__PURE__ */ jsx22(
2411
+ Channel,
2412
+ {
2413
+ ...props,
2414
+ index: 0,
2415
+ waveOutlineColor,
2416
+ waveFillColor,
2417
+ waveHeight: halfHeight,
2418
+ devicePixelRatio,
2419
+ barWidth,
2420
+ barGap,
2421
+ transparentBackground,
2422
+ drawMode
2423
+ }
2424
+ )
2387
2425
  }
2388
- ) })
2426
+ )
2389
2427
  ] });
2390
2428
  }
2391
- return /* @__PURE__ */ jsx21(
2429
+ return /* @__PURE__ */ jsx22(
2392
2430
  Channel,
2393
2431
  {
2394
2432
  ...props,
@@ -2407,7 +2445,7 @@ var SmartChannel = ({
2407
2445
  // src/components/SpectrogramLabels.tsx
2408
2446
  import { useRef as useRef7, useLayoutEffect as useLayoutEffect3 } from "react";
2409
2447
  import styled20 from "styled-components";
2410
- import { jsx as jsx22 } from "react/jsx-runtime";
2448
+ import { jsx as jsx23 } from "react/jsx-runtime";
2411
2449
  var LABELS_WIDTH = 72;
2412
2450
  var LabelsStickyWrapper = styled20.div`
2413
2451
  position: sticky;
@@ -2488,8 +2526,19 @@ var SpectrogramLabels = ({
2488
2526
  ctx.fillText(text, padding, y);
2489
2527
  }
2490
2528
  }
2491
- }, [waveHeight, numChannels, frequencyScaleFn, minFrequency, maxFrequency, labelsColor, labelsBackground, devicePixelRatio, spectrogramHeight, clipHeaderOffset]);
2492
- return /* @__PURE__ */ jsx22(LabelsStickyWrapper, { $height: totalHeight + clipHeaderOffset, children: /* @__PURE__ */ jsx22(
2529
+ }, [
2530
+ waveHeight,
2531
+ numChannels,
2532
+ frequencyScaleFn,
2533
+ minFrequency,
2534
+ maxFrequency,
2535
+ labelsColor,
2536
+ labelsBackground,
2537
+ devicePixelRatio,
2538
+ spectrogramHeight,
2539
+ clipHeaderOffset
2540
+ ]);
2541
+ return /* @__PURE__ */ jsx23(LabelsStickyWrapper, { $height: totalHeight + clipHeaderOffset, children: /* @__PURE__ */ jsx23(
2493
2542
  "canvas",
2494
2543
  {
2495
2544
  ref: canvasRef,
@@ -2505,10 +2554,10 @@ var SpectrogramLabels = ({
2505
2554
  };
2506
2555
 
2507
2556
  // src/components/SmartScale.tsx
2508
- import { useContext as useContext8 } from "react";
2557
+ import { useContext as useContext9 } from "react";
2509
2558
 
2510
2559
  // src/components/TimeScale.tsx
2511
- import React15, { useLayoutEffect as useLayoutEffect4, useContext as useContext7, useMemo as useMemo2 } from "react";
2560
+ import React16, { useLayoutEffect as useLayoutEffect4, useContext as useContext8, useMemo as useMemo2 } from "react";
2512
2561
  import styled21, { withTheme as withTheme2 } from "styled-components";
2513
2562
 
2514
2563
  // src/utils/conversions.ts
@@ -2533,7 +2582,7 @@ function secondsToPixels(seconds, samplesPerPixel, sampleRate) {
2533
2582
 
2534
2583
  // src/components/TimeScale.tsx
2535
2584
  import { MAX_CANVAS_WIDTH as MAX_CANVAS_WIDTH3 } from "@waveform-playlist/core";
2536
- import { jsx as jsx23, jsxs as jsxs10 } from "react/jsx-runtime";
2585
+ import { jsx as jsx24, jsxs as jsxs10 } from "react/jsx-runtime";
2537
2586
  function formatTime2(milliseconds) {
2538
2587
  const seconds = Math.floor(milliseconds / 1e3);
2539
2588
  const s = seconds % 60;
@@ -2590,7 +2639,7 @@ var TimeScale = (props) => {
2590
2639
  samplesPerPixel,
2591
2640
  timeScaleHeight,
2592
2641
  controls: { show: showControls, width: controlWidth }
2593
- } = useContext7(PlaylistInfoContext);
2642
+ } = useContext8(PlaylistInfoContext);
2594
2643
  const devicePixelRatio = useDevicePixelRatio();
2595
2644
  const { widthX, canvasInfo, timeMarkersWithPositions } = useMemo2(() => {
2596
2645
  const nextCanvasInfo = /* @__PURE__ */ new Map();
@@ -2603,7 +2652,7 @@ var TimeScale = (props) => {
2603
2652
  if (counter % marker === 0) {
2604
2653
  const timeMs = counter;
2605
2654
  const timestamp = formatTime2(timeMs);
2606
- const element = renderTimestamp ? /* @__PURE__ */ jsx23(React15.Fragment, { children: renderTimestamp(timeMs, pix) }, `timestamp-${counter}`) : /* @__PURE__ */ jsx23(TimeStamp, { $left: pix, children: timestamp }, timestamp);
2655
+ const element = renderTimestamp ? /* @__PURE__ */ jsx24(React16.Fragment, { children: renderTimestamp(timeMs, pix) }, `timestamp-${counter}`) : /* @__PURE__ */ jsx24(TimeStamp, { $left: pix, children: timestamp }, timestamp);
2607
2656
  nextMarkers.push({ pix, element });
2608
2657
  nextCanvasInfo.set(pix, timeScaleHeight);
2609
2658
  } else if (counter % bigStep === 0) {
@@ -2618,12 +2667,21 @@ var TimeScale = (props) => {
2618
2667
  canvasInfo: nextCanvasInfo,
2619
2668
  timeMarkersWithPositions: nextMarkers
2620
2669
  };
2621
- }, [duration, samplesPerPixel, sampleRate, marker, bigStep, secondStep, renderTimestamp, timeScaleHeight]);
2670
+ }, [
2671
+ duration,
2672
+ samplesPerPixel,
2673
+ sampleRate,
2674
+ marker,
2675
+ bigStep,
2676
+ secondStep,
2677
+ renderTimestamp,
2678
+ timeScaleHeight
2679
+ ]);
2622
2680
  const visibleChunkIndices = useVisibleChunkIndices(widthX, MAX_CANVAS_WIDTH3);
2623
2681
  const visibleChunks = visibleChunkIndices.map((i) => {
2624
2682
  const chunkLeft = i * MAX_CANVAS_WIDTH3;
2625
2683
  const chunkWidth = Math.min(widthX - chunkLeft, MAX_CANVAS_WIDTH3);
2626
- return /* @__PURE__ */ jsx23(
2684
+ return /* @__PURE__ */ jsx24(
2627
2685
  TimeTickChunk,
2628
2686
  {
2629
2687
  $cssWidth: chunkWidth,
@@ -2658,7 +2716,15 @@ var TimeScale = (props) => {
2658
2716
  ctx.fillRect(localX, scaleY, 1, scaleHeight);
2659
2717
  }
2660
2718
  }
2661
- }, [canvasMapRef, duration, devicePixelRatio, timeColor, timeScaleHeight, canvasInfo, visibleChunkIndices]);
2719
+ }, [
2720
+ canvasMapRef,
2721
+ duration,
2722
+ devicePixelRatio,
2723
+ timeColor,
2724
+ timeScaleHeight,
2725
+ canvasInfo,
2726
+ visibleChunkIndices
2727
+ ]);
2662
2728
  return /* @__PURE__ */ jsxs10(
2663
2729
  PlaylistTimeScaleScroll,
2664
2730
  {
@@ -2675,7 +2741,7 @@ var TimeScale = (props) => {
2675
2741
  var StyledTimeScale = withTheme2(TimeScale);
2676
2742
 
2677
2743
  // src/components/SmartScale.tsx
2678
- import { jsx as jsx24 } from "react/jsx-runtime";
2744
+ import { jsx as jsx25 } from "react/jsx-runtime";
2679
2745
  var timeinfo = /* @__PURE__ */ new Map([
2680
2746
  [
2681
2747
  700,
@@ -2749,9 +2815,9 @@ function getScaleInfo(samplesPerPixel) {
2749
2815
  return config;
2750
2816
  }
2751
2817
  var SmartScale = ({ renderTimestamp }) => {
2752
- const { samplesPerPixel, duration } = useContext8(PlaylistInfoContext);
2818
+ const { samplesPerPixel, duration } = useContext9(PlaylistInfoContext);
2753
2819
  let config = getScaleInfo(samplesPerPixel);
2754
- return /* @__PURE__ */ jsx24(
2820
+ return /* @__PURE__ */ jsx25(
2755
2821
  StyledTimeScale,
2756
2822
  {
2757
2823
  marker: config.marker,
@@ -2765,7 +2831,7 @@ var SmartScale = ({ renderTimestamp }) => {
2765
2831
 
2766
2832
  // src/components/TimeFormatSelect.tsx
2767
2833
  import styled22 from "styled-components";
2768
- import { jsx as jsx25 } from "react/jsx-runtime";
2834
+ import { jsx as jsx26 } from "react/jsx-runtime";
2769
2835
  var SelectWrapper = styled22.div`
2770
2836
  display: inline-flex;
2771
2837
  align-items: center;
@@ -2788,7 +2854,7 @@ var TimeFormatSelect = ({
2788
2854
  const handleChange = (e) => {
2789
2855
  onChange(e.target.value);
2790
2856
  };
2791
- return /* @__PURE__ */ jsx25(SelectWrapper, { className, children: /* @__PURE__ */ jsx25(
2857
+ return /* @__PURE__ */ jsx26(SelectWrapper, { className, children: /* @__PURE__ */ jsx26(
2792
2858
  BaseSelect,
2793
2859
  {
2794
2860
  className: "time-format",
@@ -2796,14 +2862,14 @@ var TimeFormatSelect = ({
2796
2862
  onChange: handleChange,
2797
2863
  disabled,
2798
2864
  "aria-label": "Time format selection",
2799
- children: TIME_FORMAT_OPTIONS.map((option) => /* @__PURE__ */ jsx25("option", { value: option.value, children: option.label }, option.value))
2865
+ children: TIME_FORMAT_OPTIONS.map((option) => /* @__PURE__ */ jsx26("option", { value: option.value, children: option.label }, option.value))
2800
2866
  }
2801
2867
  ) });
2802
2868
  };
2803
2869
 
2804
2870
  // src/components/Track.tsx
2805
2871
  import styled23 from "styled-components";
2806
- import { jsx as jsx26, jsxs as jsxs11 } from "react/jsx-runtime";
2872
+ import { jsx as jsx27, jsxs as jsxs11 } from "react/jsx-runtime";
2807
2873
  var Container = styled23.div.attrs((props) => ({
2808
2874
  style: {
2809
2875
  height: `${props.$waveHeight * props.$numChannels + (props.$hasClipHeaders ? CLIP_HEADER_HEIGHT : 0)}px`
@@ -2869,15 +2935,8 @@ var Track = ({
2869
2935
  $hasClipHeaders: hasClipHeaders,
2870
2936
  $isSelected: isSelected,
2871
2937
  children: [
2872
- /* @__PURE__ */ jsx26(
2873
- ControlsWrapper,
2874
- {
2875
- $controlWidth: show ? controlWidth : 0,
2876
- $isSelected: isSelected,
2877
- children: controls
2878
- }
2879
- ),
2880
- /* @__PURE__ */ jsx26(
2938
+ /* @__PURE__ */ jsx27(ControlsWrapper, { $controlWidth: show ? controlWidth : 0, $isSelected: isSelected, children: controls }),
2939
+ /* @__PURE__ */ jsx27(
2881
2940
  ChannelContainer,
2882
2941
  {
2883
2942
  $controlWidth: show ? controlWidth : 0,
@@ -2908,8 +2967,11 @@ var Button = styled24.button.attrs({
2908
2967
  font-size: ${(props) => props.theme.fontSizeSmall};
2909
2968
  line-height: 1;
2910
2969
  border-radius: ${(props) => props.theme.borderRadius};
2911
- transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out,
2912
- border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
2970
+ transition:
2971
+ color 0.15s ease-in-out,
2972
+ background-color 0.15s ease-in-out,
2973
+ border-color 0.15s ease-in-out,
2974
+ box-shadow 0.15s ease-in-out;
2913
2975
  cursor: pointer;
2914
2976
 
2915
2977
  ${(props) => {
@@ -2985,7 +3047,7 @@ var ButtonGroup = styled25.div`
2985
3047
  // src/components/TrackControls/CloseButton.tsx
2986
3048
  import styled26 from "styled-components";
2987
3049
  import { X as XIcon } from "@phosphor-icons/react";
2988
- import { jsx as jsx27 } from "react/jsx-runtime";
3050
+ import { jsx as jsx28 } from "react/jsx-runtime";
2989
3051
  var StyledCloseButton = styled26.button`
2990
3052
  position: absolute;
2991
3053
  left: 0;
@@ -3000,17 +3062,16 @@ var StyledCloseButton = styled26.button`
3000
3062
  align-items: center;
3001
3063
  justify-content: center;
3002
3064
  opacity: 0.7;
3003
- transition: opacity 0.15s, color 0.15s;
3065
+ transition:
3066
+ opacity 0.15s,
3067
+ color 0.15s;
3004
3068
 
3005
3069
  &:hover {
3006
3070
  opacity: 1;
3007
3071
  color: #dc3545;
3008
3072
  }
3009
3073
  `;
3010
- var CloseButton = ({
3011
- onClick,
3012
- title = "Remove track"
3013
- }) => /* @__PURE__ */ jsx27(StyledCloseButton, { onClick, title, children: /* @__PURE__ */ jsx27(XIcon, { size: 12, weight: "bold" }) });
3074
+ var CloseButton = ({ onClick, title = "Remove track" }) => /* @__PURE__ */ jsx28(StyledCloseButton, { onClick, title, children: /* @__PURE__ */ jsx28(XIcon, { size: 12, weight: "bold" }) });
3014
3075
 
3015
3076
  // src/components/TrackControls/Controls.tsx
3016
3077
  import styled27 from "styled-components";
@@ -3046,23 +3107,23 @@ var Header = styled28.header`
3046
3107
 
3047
3108
  // src/components/TrackControls/VolumeDownIcon.tsx
3048
3109
  import { SpeakerLowIcon } from "@phosphor-icons/react";
3049
- import { jsx as jsx28 } from "react/jsx-runtime";
3050
- var VolumeDownIcon = (props) => /* @__PURE__ */ jsx28(SpeakerLowIcon, { weight: "light", ...props });
3110
+ import { jsx as jsx29 } from "react/jsx-runtime";
3111
+ var VolumeDownIcon = (props) => /* @__PURE__ */ jsx29(SpeakerLowIcon, { weight: "light", ...props });
3051
3112
 
3052
3113
  // src/components/TrackControls/VolumeUpIcon.tsx
3053
3114
  import { SpeakerHighIcon } from "@phosphor-icons/react";
3054
- import { jsx as jsx29 } from "react/jsx-runtime";
3055
- var VolumeUpIcon = (props) => /* @__PURE__ */ jsx29(SpeakerHighIcon, { weight: "light", ...props });
3115
+ import { jsx as jsx30 } from "react/jsx-runtime";
3116
+ var VolumeUpIcon = (props) => /* @__PURE__ */ jsx30(SpeakerHighIcon, { weight: "light", ...props });
3056
3117
 
3057
3118
  // src/components/TrackControls/TrashIcon.tsx
3058
3119
  import { TrashIcon as PhosphorTrashIcon } from "@phosphor-icons/react";
3059
- import { jsx as jsx30 } from "react/jsx-runtime";
3060
- var TrashIcon = (props) => /* @__PURE__ */ jsx30(PhosphorTrashIcon, { weight: "light", ...props });
3120
+ import { jsx as jsx31 } from "react/jsx-runtime";
3121
+ var TrashIcon = (props) => /* @__PURE__ */ jsx31(PhosphorTrashIcon, { weight: "light", ...props });
3061
3122
 
3062
3123
  // src/components/TrackControls/DotsIcon.tsx
3063
3124
  import { DotsThreeIcon } from "@phosphor-icons/react";
3064
- import { jsx as jsx31 } from "react/jsx-runtime";
3065
- var DotsIcon = (props) => /* @__PURE__ */ jsx31(DotsThreeIcon, { weight: "bold", ...props });
3125
+ import { jsx as jsx32 } from "react/jsx-runtime";
3126
+ var DotsIcon = (props) => /* @__PURE__ */ jsx32(DotsThreeIcon, { weight: "bold", ...props });
3066
3127
 
3067
3128
  // src/components/TrackControls/Slider.tsx
3068
3129
  import styled29 from "styled-components";
@@ -3130,10 +3191,10 @@ var SliderWrapper = styled30.label`
3130
3191
  `;
3131
3192
 
3132
3193
  // src/components/TrackMenu.tsx
3133
- import React17, { useState as useState6, useEffect as useEffect7, useRef as useRef8 } from "react";
3194
+ import React18, { useState as useState6, useEffect as useEffect7, useRef as useRef8 } from "react";
3134
3195
  import { createPortal } from "react-dom";
3135
3196
  import styled31 from "styled-components";
3136
- import { jsx as jsx32, jsxs as jsxs12 } from "react/jsx-runtime";
3197
+ import { jsx as jsx33, jsxs as jsxs12 } from "react/jsx-runtime";
3137
3198
  var MenuContainer = styled31.div`
3138
3199
  position: relative;
3139
3200
  display: inline-block;
@@ -3171,9 +3232,7 @@ var Divider = styled31.hr`
3171
3232
  border-top: 1px solid rgba(128, 128, 128, 0.3);
3172
3233
  margin: 0.35rem 0;
3173
3234
  `;
3174
- var TrackMenu = ({
3175
- items: itemsProp
3176
- }) => {
3235
+ var TrackMenu = ({ items: itemsProp }) => {
3177
3236
  const [open, setOpen] = useState6(false);
3178
3237
  const close = () => setOpen(false);
3179
3238
  const items = typeof itemsProp === "function" ? itemsProp(close) : itemsProp;
@@ -3201,7 +3260,7 @@ var TrackMenu = ({
3201
3260
  return () => document.removeEventListener("mousedown", handleClick);
3202
3261
  }, [open]);
3203
3262
  return /* @__PURE__ */ jsxs12(MenuContainer, { children: [
3204
- /* @__PURE__ */ jsx32(
3263
+ /* @__PURE__ */ jsx33(
3205
3264
  MenuButton,
3206
3265
  {
3207
3266
  ref: buttonRef,
@@ -3212,19 +3271,19 @@ var TrackMenu = ({
3212
3271
  onMouseDown: (e) => e.stopPropagation(),
3213
3272
  title: "Track menu",
3214
3273
  "aria-label": "Track menu",
3215
- children: /* @__PURE__ */ jsx32(DotsIcon, { size: 16 })
3274
+ children: /* @__PURE__ */ jsx33(DotsIcon, { size: 16 })
3216
3275
  }
3217
3276
  ),
3218
3277
  open && typeof document !== "undefined" && createPortal(
3219
- /* @__PURE__ */ jsx32(
3278
+ /* @__PURE__ */ jsx33(
3220
3279
  Dropdown,
3221
3280
  {
3222
3281
  ref: dropdownRef,
3223
3282
  $top: dropdownPos.top,
3224
3283
  $left: dropdownPos.left,
3225
3284
  onMouseDown: (e) => e.stopPropagation(),
3226
- children: items.map((item, index) => /* @__PURE__ */ jsxs12(React17.Fragment, { children: [
3227
- index > 0 && /* @__PURE__ */ jsx32(Divider, {}),
3285
+ children: items.map((item, index) => /* @__PURE__ */ jsxs12(React18.Fragment, { children: [
3286
+ index > 0 && /* @__PURE__ */ jsx33(Divider, {}),
3228
3287
  item.content
3229
3288
  ] }, item.id))
3230
3289
  }
@@ -3255,6 +3314,7 @@ export {
3255
3314
  ClipBoundary,
3256
3315
  ClipHeader,
3257
3316
  ClipHeaderPresentational,
3317
+ ClipViewportOriginProvider,
3258
3318
  CloseButton,
3259
3319
  Controls,
3260
3320
  DevicePixelRatioProvider,
@@ -3304,6 +3364,7 @@ export {
3304
3364
  samplesToSeconds,
3305
3365
  secondsToPixels,
3306
3366
  secondsToSamples,
3367
+ useClipViewportOrigin,
3307
3368
  useDevicePixelRatio,
3308
3369
  usePlaylistInfo,
3309
3370
  usePlayoutStatus,