@waveform-playlist/browser 11.0.1 → 11.2.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/index.js CHANGED
@@ -122,7 +122,7 @@ __export(index_exports, {
122
122
  effectDefinitions: () => effectDefinitions,
123
123
  getEffectDefinition: () => getEffectDefinition,
124
124
  getEffectsByCategory: () => getEffectsByCategory,
125
- getShortcutLabel: () => getShortcutLabel,
125
+ getShortcutLabel: () => import_core3.getShortcutLabel,
126
126
  getWaveformDataMetadata: () => getWaveformDataMetadata,
127
127
  loadPeaksFromWaveformData: () => loadPeaksFromWaveformData,
128
128
  loadWaveformData: () => loadWaveformData,
@@ -161,7 +161,7 @@ module.exports = __toCommonJS(index_exports);
161
161
  var Tone2 = __toESM(require("tone"));
162
162
 
163
163
  // src/WaveformPlaylistContext.tsx
164
- var import_react23 = require("react");
164
+ var import_react24 = require("react");
165
165
  var import_styled_components = require("styled-components");
166
166
  var import_playout5 = require("@waveform-playlist/playout");
167
167
  var import_engine3 = require("@waveform-playlist/engine");
@@ -172,11 +172,13 @@ var import_tone4 = require("tone");
172
172
  var import_waveform_data = __toESM(require("waveform-data"));
173
173
  function loadWaveformData(src) {
174
174
  return __async(this, null, function* () {
175
+ var _a, _b;
175
176
  const response = yield fetch(src);
176
177
  if (!response.ok) {
177
178
  throw new Error(`Failed to fetch waveform data: ${response.statusText}`);
178
179
  }
179
- const isBinary = src.endsWith(".dat");
180
+ const { pathname } = new URL(src, (_b = (_a = globalThis.location) == null ? void 0 : _a.href) != null ? _b : "http://localhost");
181
+ const isBinary = pathname.toLowerCase().endsWith(".dat");
180
182
  if (isBinary) {
181
183
  const arrayBuffer = yield response.arrayBuffer();
182
184
  return import_waveform_data.default.create(arrayBuffer);
@@ -542,12 +544,52 @@ function useSelectedTrack({ engineRef }) {
542
544
  };
543
545
  }
544
546
 
545
- // src/hooks/useAudioEffects.ts
547
+ // src/hooks/useUndoState.ts
546
548
  var import_react7 = require("react");
549
+ function useUndoState({ engineRef }) {
550
+ const [canUndo, setCanUndo] = (0, import_react7.useState)(false);
551
+ const [canRedo, setCanRedo] = (0, import_react7.useState)(false);
552
+ const canUndoRef = (0, import_react7.useRef)(false);
553
+ const canRedoRef = (0, import_react7.useRef)(false);
554
+ const undo = (0, import_react7.useCallback)(() => {
555
+ if (!engineRef.current) {
556
+ console.warn("[waveform-playlist] undo: engine not ready, call ignored");
557
+ return;
558
+ }
559
+ engineRef.current.undo();
560
+ }, [engineRef]);
561
+ const redo = (0, import_react7.useCallback)(() => {
562
+ if (!engineRef.current) {
563
+ console.warn("[waveform-playlist] redo: engine not ready, call ignored");
564
+ return;
565
+ }
566
+ engineRef.current.redo();
567
+ }, [engineRef]);
568
+ const onEngineState = (0, import_react7.useCallback)((state) => {
569
+ if (state.canUndo !== canUndoRef.current) {
570
+ canUndoRef.current = state.canUndo;
571
+ setCanUndo(state.canUndo);
572
+ }
573
+ if (state.canRedo !== canRedoRef.current) {
574
+ canRedoRef.current = state.canRedo;
575
+ setCanRedo(state.canRedo);
576
+ }
577
+ }, []);
578
+ return {
579
+ canUndo,
580
+ canRedo,
581
+ undo,
582
+ redo,
583
+ onEngineState
584
+ };
585
+ }
586
+
587
+ // src/hooks/useAudioEffects.ts
588
+ var import_react8 = require("react");
547
589
  var import_tone = require("tone");
548
590
  var useMasterAnalyser = (fftSize = 256) => {
549
- const analyserRef = (0, import_react7.useRef)(null);
550
- const masterEffects = (0, import_react7.useCallback)(
591
+ const analyserRef = (0, import_react8.useRef)(null);
592
+ const masterEffects = (0, import_react8.useCallback)(
551
593
  (masterGainNode, destination, _isOffline) => {
552
594
  const analyserNode = new import_tone.Analyser("fft", fftSize);
553
595
  masterGainNode.connect(analyserNode);
@@ -564,7 +606,7 @@ var useMasterAnalyser = (fftSize = 256) => {
564
606
  };
565
607
 
566
608
  // src/hooks/useAudioTracks.ts
567
- var import_react8 = require("react");
609
+ var import_react9 = require("react");
568
610
  var import_core = require("@waveform-playlist/core");
569
611
  var Tone = __toESM(require("tone"));
570
612
  function buildTrackFromConfig(config, index, audioBuffer, stableIds, contextSampleRate = 48e3) {
@@ -622,14 +664,14 @@ function buildTrackFromConfig(config, index, audioBuffer, stableIds, contextSamp
622
664
  function useAudioTracks(configs, options = {}) {
623
665
  const { immediate = false, progressive = false } = options;
624
666
  const isImmediate = immediate || progressive;
625
- const [loading, setLoading] = (0, import_react8.useState)(true);
626
- const [error, setError] = (0, import_react8.useState)(null);
627
- const [loadedCount, setLoadedCount] = (0, import_react8.useState)(0);
667
+ const [loading, setLoading] = (0, import_react9.useState)(true);
668
+ const [error, setError] = (0, import_react9.useState)(null);
669
+ const [loadedCount, setLoadedCount] = (0, import_react9.useState)(0);
628
670
  const totalCount = configs.length;
629
- const [loadedBuffers, setLoadedBuffers] = (0, import_react8.useState)(/* @__PURE__ */ new Map());
630
- const stableIdsRef = (0, import_react8.useRef)(/* @__PURE__ */ new Map());
631
- const contextSampleRateRef = (0, import_react8.useRef)(48e3);
632
- const derivedTracks = (0, import_react8.useMemo)(() => {
671
+ const [loadedBuffers, setLoadedBuffers] = (0, import_react9.useState)(/* @__PURE__ */ new Map());
672
+ const stableIdsRef = (0, import_react9.useRef)(/* @__PURE__ */ new Map());
673
+ const contextSampleRateRef = (0, import_react9.useRef)(48e3);
674
+ const derivedTracks = (0, import_react9.useMemo)(() => {
633
675
  if (!isImmediate) return null;
634
676
  const result = [];
635
677
  for (let i = 0; i < configs.length; i++) {
@@ -644,13 +686,13 @@ function useAudioTracks(configs, options = {}) {
644
686
  }
645
687
  return result;
646
688
  }, [isImmediate, configs, loadedBuffers]);
647
- const [tracks, setTracks] = (0, import_react8.useState)(derivedTracks != null ? derivedTracks : []);
648
- const prevDerivedRef = (0, import_react8.useRef)(derivedTracks);
689
+ const [tracks, setTracks] = (0, import_react9.useState)(derivedTracks != null ? derivedTracks : []);
690
+ const prevDerivedRef = (0, import_react9.useRef)(derivedTracks);
649
691
  if (derivedTracks !== prevDerivedRef.current) {
650
692
  prevDerivedRef.current = derivedTracks;
651
693
  if (derivedTracks) setTracks(derivedTracks);
652
694
  }
653
- (0, import_react8.useEffect)(() => {
695
+ (0, import_react9.useEffect)(() => {
654
696
  if (configs.length === 0) {
655
697
  setTracks([]);
656
698
  setLoading(false);
@@ -758,7 +800,7 @@ function useAudioTracks(configs, options = {}) {
758
800
  }
759
801
 
760
802
  // src/hooks/useClipDragHandlers.ts
761
- var import_react9 = __toESM(require("react"));
803
+ var import_react10 = __toESM(require("react"));
762
804
 
763
805
  // src/utils/boundaryTrim.ts
764
806
  var import_engine = require("@waveform-playlist/engine");
@@ -812,17 +854,24 @@ function useClipDragHandlers({
812
854
  snapSamplePosition
813
855
  }) {
814
856
  const { sampleRate } = usePlaylistData();
815
- const snapSamplePositionRef = import_react9.default.useRef(snapSamplePosition);
857
+ const snapSamplePositionRef = import_react10.default.useRef(snapSamplePosition);
816
858
  snapSamplePositionRef.current = snapSamplePosition;
817
- const originalClipStateRef = import_react9.default.useRef(null);
818
- const lastBoundaryDeltaRef = import_react9.default.useRef(0);
819
- const onDragStart = import_react9.default.useCallback(
859
+ const originalClipStateRef = import_react10.default.useRef(null);
860
+ const lastBoundaryDeltaRef = import_react10.default.useRef(0);
861
+ const onDragStart = import_react10.default.useCallback(
820
862
  (event) => {
821
863
  var _a;
822
864
  const data = (_a = event.operation.source) == null ? void 0 : _a.data;
823
865
  if (!data) return;
824
866
  if (!data.boundary) {
825
867
  originalClipStateRef.current = null;
868
+ if (engineRef.current) {
869
+ engineRef.current.beginTransaction();
870
+ } else {
871
+ console.warn(
872
+ "[waveform-playlist] onDragStart: engine not ready, move will not be grouped for undo"
873
+ );
874
+ }
826
875
  return;
827
876
  }
828
877
  const track = tracks[data.trackIndex];
@@ -834,11 +883,18 @@ function useClipDragHandlers({
834
883
  startSample: clip.startSample
835
884
  };
836
885
  isDraggingRef.current = true;
886
+ if (engineRef.current) {
887
+ engineRef.current.beginTransaction();
888
+ } else {
889
+ console.warn(
890
+ "[waveform-playlist] onDragStart: engine not ready, trim will not be grouped for undo"
891
+ );
892
+ }
837
893
  }
838
894
  },
839
- [tracks, isDraggingRef]
895
+ [tracks, isDraggingRef, engineRef]
840
896
  );
841
- const onDragMove = import_react9.default.useCallback(
897
+ const onDragMove = import_react10.default.useCallback(
842
898
  (event) => {
843
899
  var _a, _b, _c;
844
900
  const data = (_a = event.operation.source) == null ? void 0 : _a.data;
@@ -900,9 +956,9 @@ function useClipDragHandlers({
900
956
  },
901
957
  [tracks, onTracksChange, samplesPerPixel, sampleRate]
902
958
  );
903
- const onDragEnd = import_react9.default.useCallback(
959
+ const onDragEnd = import_react10.default.useCallback(
904
960
  (event) => {
905
- var _a, _b, _c;
961
+ var _a, _b, _c, _d, _e, _f, _g;
906
962
  if (event.canceled) {
907
963
  if (originalClipStateRef.current) {
908
964
  const cancelData = (_a = event.operation.source) == null ? void 0 : _a.data;
@@ -927,23 +983,30 @@ function useClipDragHandlers({
927
983
  isDraggingRef.current = false;
928
984
  originalClipStateRef.current = null;
929
985
  lastBoundaryDeltaRef.current = 0;
986
+ (_b = engineRef.current) == null ? void 0 : _b.abortTransaction();
987
+ return;
988
+ }
989
+ const data = (_c = event.operation.source) == null ? void 0 : _c.data;
990
+ if (!data) {
991
+ isDraggingRef.current = false;
992
+ (_d = engineRef.current) == null ? void 0 : _d.abortTransaction();
930
993
  return;
931
994
  }
932
- const data = (_b = event.operation.source) == null ? void 0 : _b.data;
933
- if (!data) return;
934
995
  const { trackIndex, clipId, boundary } = data;
935
996
  const sampleDelta = boundary ? lastBoundaryDeltaRef.current : event.operation.transform.x * samplesPerPixel;
936
- const trackId = (_c = tracks[trackIndex]) == null ? void 0 : _c.id;
997
+ const trackId = (_e = tracks[trackIndex]) == null ? void 0 : _e.id;
937
998
  if (boundary) {
938
999
  isDraggingRef.current = false;
939
1000
  if (!trackId) {
940
1001
  console.warn(
941
1002
  `[waveform-playlist] onDragEnd: track at index ${trackIndex} not found \u2014 trim not synced to adapter`
942
1003
  );
1004
+ (_f = engineRef.current) == null ? void 0 : _f.abortTransaction();
943
1005
  } else if (!engineRef.current) {
944
1006
  console.warn("[waveform-playlist] engineRef is null \u2014 trim not synced to adapter");
945
1007
  } else {
946
1008
  engineRef.current.trimClip(trackId, clipId, boundary, Math.floor(sampleDelta));
1009
+ engineRef.current.commitTransaction();
947
1010
  }
948
1011
  originalClipStateRef.current = null;
949
1012
  lastBoundaryDeltaRef.current = 0;
@@ -953,10 +1016,12 @@ function useClipDragHandlers({
953
1016
  console.warn(
954
1017
  `[waveform-playlist] onDragEnd: track at index ${trackIndex} not found \u2014 move not synced to adapter`
955
1018
  );
1019
+ (_g = engineRef.current) == null ? void 0 : _g.abortTransaction();
956
1020
  } else if (!engineRef.current) {
957
1021
  console.warn("[waveform-playlist] engineRef is null \u2014 move not synced to adapter");
958
1022
  } else {
959
1023
  engineRef.current.moveClip(trackId, clipId, Math.floor(sampleDelta));
1024
+ engineRef.current.commitTransaction();
960
1025
  }
961
1026
  },
962
1027
  [tracks, onTracksChange, samplesPerPixel, engineRef, isDraggingRef]
@@ -969,7 +1034,7 @@ function useClipDragHandlers({
969
1034
  }
970
1035
 
971
1036
  // src/hooks/useAnnotationDragHandlers.ts
972
- var import_react10 = __toESM(require("react"));
1037
+ var import_react11 = __toESM(require("react"));
973
1038
  var import_playout = require("@waveform-playlist/playout");
974
1039
  var LINK_THRESHOLD = 0.01;
975
1040
  function useAnnotationDragHandlers({
@@ -980,8 +1045,8 @@ function useAnnotationDragHandlers({
980
1045
  duration,
981
1046
  linkEndpoints
982
1047
  }) {
983
- const originalAnnotationStateRef = import_react10.default.useRef(null);
984
- const onDragStart = import_react10.default.useCallback(
1048
+ const originalAnnotationStateRef = import_react11.default.useRef(null);
1049
+ const onDragStart = import_react11.default.useCallback(
985
1050
  (event) => {
986
1051
  var _a;
987
1052
  const data = (_a = event.operation.source) == null ? void 0 : _a.data;
@@ -1000,7 +1065,7 @@ function useAnnotationDragHandlers({
1000
1065
  },
1001
1066
  [annotations]
1002
1067
  );
1003
- const onDragMove = import_react10.default.useCallback(
1068
+ const onDragMove = import_react11.default.useCallback(
1004
1069
  (event) => {
1005
1070
  var _a, _b, _c;
1006
1071
  if (!originalAnnotationStateRef.current) {
@@ -1026,7 +1091,7 @@ function useAnnotationDragHandlers({
1026
1091
  },
1027
1092
  [annotations, onAnnotationsChange, samplesPerPixel, sampleRate, duration, linkEndpoints]
1028
1093
  );
1029
- const onDragEnd = import_react10.default.useCallback(
1094
+ const onDragEnd = import_react11.default.useCallback(
1030
1095
  (event) => {
1031
1096
  if (event.canceled && originalAnnotationStateRef.current) {
1032
1097
  const { annotationIndex, start, end } = originalAnnotationStateRef.current;
@@ -1134,7 +1199,7 @@ function updateAnnotationBoundaries({
1134
1199
  }
1135
1200
 
1136
1201
  // src/hooks/useDragSensors.ts
1137
- var import_react11 = require("react");
1202
+ var import_react12 = require("react");
1138
1203
  var import_dom = require("@dnd-kit/dom");
1139
1204
  function useDragSensors(options = {}) {
1140
1205
  const {
@@ -1143,7 +1208,7 @@ function useDragSensors(options = {}) {
1143
1208
  touchTolerance = 5,
1144
1209
  mouseDistance = 1
1145
1210
  } = options;
1146
- return (0, import_react11.useMemo)(() => {
1211
+ return (0, import_react12.useMemo)(() => {
1147
1212
  if (touchOptimized) {
1148
1213
  return [
1149
1214
  import_dom.PointerSensor.configure({
@@ -1172,14 +1237,14 @@ function useDragSensors(options = {}) {
1172
1237
  }
1173
1238
 
1174
1239
  // src/hooks/useClipSplitting.ts
1175
- var import_react12 = require("react");
1240
+ var import_react13 = require("react");
1176
1241
  var import_engine2 = require("@waveform-playlist/engine");
1177
1242
  var useClipSplitting = (options) => {
1178
1243
  const { tracks, engineRef } = options;
1179
1244
  const { sampleRate } = usePlaylistData();
1180
1245
  const { currentTimeRef } = usePlaybackAnimation();
1181
1246
  const { selectedTrackId } = usePlaylistState();
1182
- const splitClipAt = (0, import_react12.useCallback)(
1247
+ const splitClipAt = (0, import_react13.useCallback)(
1183
1248
  (trackIndex, clipIndex, splitTime) => {
1184
1249
  const { samplesPerPixel } = options;
1185
1250
  const track = tracks[trackIndex];
@@ -1203,7 +1268,7 @@ var useClipSplitting = (options) => {
1203
1268
  },
1204
1269
  [tracks, options, engineRef, sampleRate]
1205
1270
  );
1206
- const splitClipAtPlayhead = (0, import_react12.useCallback)(() => {
1271
+ const splitClipAtPlayhead = (0, import_react13.useCallback)(() => {
1207
1272
  var _a;
1208
1273
  if (!selectedTrackId) {
1209
1274
  console.warn("[waveform-playlist] No track selected \u2014 click a clip to select a track first");
@@ -1234,36 +1299,16 @@ var useClipSplitting = (options) => {
1234
1299
  };
1235
1300
 
1236
1301
  // src/hooks/useKeyboardShortcuts.ts
1237
- var import_react13 = require("react");
1238
- function handleKeyboardEvent(event, shortcuts, enabled) {
1239
- if (!enabled) return;
1240
- if (event.repeat) return;
1241
- const target = event.target;
1242
- if (target.tagName === "INPUT" || target.tagName === "TEXTAREA" || target.isContentEditable) {
1243
- return;
1244
- }
1245
- const matchingShortcut = shortcuts.find((shortcut) => {
1246
- const keyMatch = event.key.toLowerCase() === shortcut.key.toLowerCase() || event.key === shortcut.key;
1247
- const ctrlMatch = shortcut.ctrlKey === void 0 || event.ctrlKey === shortcut.ctrlKey;
1248
- const shiftMatch = shortcut.shiftKey === void 0 || event.shiftKey === shortcut.shiftKey;
1249
- const metaMatch = shortcut.metaKey === void 0 || event.metaKey === shortcut.metaKey;
1250
- const altMatch = shortcut.altKey === void 0 || event.altKey === shortcut.altKey;
1251
- return keyMatch && ctrlMatch && shiftMatch && metaMatch && altMatch;
1252
- });
1253
- if (matchingShortcut) {
1254
- if (matchingShortcut.preventDefault !== false) {
1255
- event.preventDefault();
1256
- }
1257
- matchingShortcut.action();
1258
- }
1259
- }
1302
+ var import_react14 = require("react");
1303
+ var import_core2 = require("@waveform-playlist/core");
1304
+ var import_core3 = require("@waveform-playlist/core");
1260
1305
  var useKeyboardShortcuts = (options) => {
1261
1306
  const { shortcuts, enabled = true } = options;
1262
- const handleKeyDown = (0, import_react13.useCallback)(
1263
- (event) => handleKeyboardEvent(event, shortcuts, enabled),
1307
+ const handleKeyDown = (0, import_react14.useCallback)(
1308
+ (event) => (0, import_core2.handleKeyboardEvent)(event, shortcuts, enabled),
1264
1309
  [shortcuts, enabled]
1265
1310
  );
1266
- (0, import_react13.useEffect)(() => {
1311
+ (0, import_react14.useEffect)(() => {
1267
1312
  if (!enabled) return;
1268
1313
  window.addEventListener("keydown", handleKeyDown);
1269
1314
  return () => {
@@ -1271,42 +1316,24 @@ var useKeyboardShortcuts = (options) => {
1271
1316
  };
1272
1317
  }, [handleKeyDown, enabled]);
1273
1318
  };
1274
- var getShortcutLabel = (shortcut) => {
1275
- const parts = [];
1276
- const isMac = typeof navigator !== "undefined" && navigator.platform.includes("Mac");
1277
- if (shortcut.metaKey) {
1278
- parts.push(isMac ? "Cmd" : "Ctrl");
1279
- }
1280
- if (shortcut.ctrlKey && !shortcut.metaKey) {
1281
- parts.push("Ctrl");
1282
- }
1283
- if (shortcut.altKey) {
1284
- parts.push(isMac ? "Option" : "Alt");
1285
- }
1286
- if (shortcut.shiftKey) {
1287
- parts.push("Shift");
1288
- }
1289
- parts.push(shortcut.key.toUpperCase());
1290
- return parts.join("+");
1291
- };
1292
1319
 
1293
1320
  // src/hooks/usePlaybackShortcuts.ts
1294
- var import_react14 = require("react");
1321
+ var import_react15 = require("react");
1295
1322
  var usePlaybackShortcuts = (options = {}) => {
1296
1323
  const { enabled = true, additionalShortcuts = [], shortcuts: overrideShortcuts } = options;
1297
1324
  const { isPlaying } = usePlaybackAnimation();
1298
1325
  const { setCurrentTime, play, pause, stop } = usePlaylistControls();
1299
- const togglePlayPause = (0, import_react14.useCallback)(() => {
1326
+ const togglePlayPause = (0, import_react15.useCallback)(() => {
1300
1327
  if (isPlaying) {
1301
1328
  pause();
1302
1329
  } else {
1303
1330
  play();
1304
1331
  }
1305
1332
  }, [isPlaying, play, pause]);
1306
- const stopPlayback = (0, import_react14.useCallback)(() => {
1333
+ const stopPlayback = (0, import_react15.useCallback)(() => {
1307
1334
  stop();
1308
1335
  }, [stop]);
1309
- const rewindToStart = (0, import_react14.useCallback)(() => {
1336
+ const rewindToStart = (0, import_react15.useCallback)(() => {
1310
1337
  setCurrentTime(0);
1311
1338
  if (isPlaying) {
1312
1339
  play(0);
@@ -1346,7 +1373,7 @@ var usePlaybackShortcuts = (options = {}) => {
1346
1373
  };
1347
1374
 
1348
1375
  // src/hooks/useAnnotationKeyboardControls.ts
1349
- var import_react15 = require("react");
1376
+ var import_react16 = require("react");
1350
1377
  var LINK_THRESHOLD2 = 0.01;
1351
1378
  var TIME_DELTA = 0.01;
1352
1379
  function useAnnotationKeyboardControls({
@@ -1362,11 +1389,11 @@ function useAnnotationKeyboardControls({
1362
1389
  onPlay
1363
1390
  }) {
1364
1391
  const { samplesPerPixel, sampleRate } = usePlaylistData();
1365
- const activeIndex = (0, import_react15.useMemo)(() => {
1392
+ const activeIndex = (0, import_react16.useMemo)(() => {
1366
1393
  if (!activeAnnotationId) return -1;
1367
1394
  return annotations.findIndex((a) => a.id === activeAnnotationId);
1368
1395
  }, [annotations, activeAnnotationId]);
1369
- const scrollToAnnotation = (0, import_react15.useCallback)(
1396
+ const scrollToAnnotation = (0, import_react16.useCallback)(
1370
1397
  (annotationId) => {
1371
1398
  if (!(scrollContainerRef == null ? void 0 : scrollContainerRef.current) || !samplesPerPixel || !sampleRate) return;
1372
1399
  const annotation = annotations.find((a) => a.id === annotationId);
@@ -1389,12 +1416,12 @@ function useAnnotationKeyboardControls({
1389
1416
  },
1390
1417
  [annotations, scrollContainerRef, samplesPerPixel, sampleRate]
1391
1418
  );
1392
- (0, import_react15.useEffect)(() => {
1419
+ (0, import_react16.useEffect)(() => {
1393
1420
  if (activeAnnotationId && (scrollContainerRef == null ? void 0 : scrollContainerRef.current) && samplesPerPixel && sampleRate) {
1394
1421
  scrollToAnnotation(activeAnnotationId);
1395
1422
  }
1396
1423
  }, [activeAnnotationId, scrollToAnnotation, scrollContainerRef, samplesPerPixel, sampleRate]);
1397
- const moveStartBoundary = (0, import_react15.useCallback)(
1424
+ const moveStartBoundary = (0, import_react16.useCallback)(
1398
1425
  (delta) => {
1399
1426
  if (activeIndex < 0) return;
1400
1427
  const annotation = annotations[activeIndex];
@@ -1423,7 +1450,7 @@ function useAnnotationKeyboardControls({
1423
1450
  },
1424
1451
  [annotations, activeIndex, linkEndpoints, onAnnotationsChange]
1425
1452
  );
1426
- const moveEndBoundary = (0, import_react15.useCallback)(
1453
+ const moveEndBoundary = (0, import_react16.useCallback)(
1427
1454
  (delta) => {
1428
1455
  if (activeIndex < 0) return;
1429
1456
  const annotation = annotations[activeIndex];
@@ -1483,7 +1510,7 @@ function useAnnotationKeyboardControls({
1483
1510
  },
1484
1511
  [annotations, activeIndex, duration, linkEndpoints, onAnnotationsChange]
1485
1512
  );
1486
- const selectPrevious = (0, import_react15.useCallback)(() => {
1513
+ const selectPrevious = (0, import_react16.useCallback)(() => {
1487
1514
  if (!onActiveAnnotationChange || annotations.length === 0) return;
1488
1515
  if (activeIndex <= 0) {
1489
1516
  onActiveAnnotationChange(annotations[annotations.length - 1].id);
@@ -1491,7 +1518,7 @@ function useAnnotationKeyboardControls({
1491
1518
  onActiveAnnotationChange(annotations[activeIndex - 1].id);
1492
1519
  }
1493
1520
  }, [annotations, activeIndex, onActiveAnnotationChange]);
1494
- const selectNext = (0, import_react15.useCallback)(() => {
1521
+ const selectNext = (0, import_react16.useCallback)(() => {
1495
1522
  if (!onActiveAnnotationChange || annotations.length === 0) return;
1496
1523
  if (activeIndex < 0 || activeIndex >= annotations.length - 1) {
1497
1524
  onActiveAnnotationChange(annotations[0].id);
@@ -1499,25 +1526,25 @@ function useAnnotationKeyboardControls({
1499
1526
  onActiveAnnotationChange(annotations[activeIndex + 1].id);
1500
1527
  }
1501
1528
  }, [annotations, activeIndex, onActiveAnnotationChange]);
1502
- const selectFirst = (0, import_react15.useCallback)(() => {
1529
+ const selectFirst = (0, import_react16.useCallback)(() => {
1503
1530
  if (!onActiveAnnotationChange || annotations.length === 0) return;
1504
1531
  onActiveAnnotationChange(annotations[0].id);
1505
1532
  }, [annotations, onActiveAnnotationChange]);
1506
- const selectLast = (0, import_react15.useCallback)(() => {
1533
+ const selectLast = (0, import_react16.useCallback)(() => {
1507
1534
  if (!onActiveAnnotationChange || annotations.length === 0) return;
1508
1535
  onActiveAnnotationChange(annotations[annotations.length - 1].id);
1509
1536
  }, [annotations, onActiveAnnotationChange]);
1510
- const clearSelection = (0, import_react15.useCallback)(() => {
1537
+ const clearSelection = (0, import_react16.useCallback)(() => {
1511
1538
  if (!onActiveAnnotationChange) return;
1512
1539
  onActiveAnnotationChange(null);
1513
1540
  }, [onActiveAnnotationChange]);
1514
- const playActiveAnnotation = (0, import_react15.useCallback)(() => {
1541
+ const playActiveAnnotation = (0, import_react16.useCallback)(() => {
1515
1542
  if (activeIndex < 0 || !onPlay) return;
1516
1543
  const annotation = annotations[activeIndex];
1517
1544
  const playDuration = !continuousPlay ? annotation.end - annotation.start : void 0;
1518
1545
  onPlay(annotation.start, playDuration);
1519
1546
  }, [annotations, activeIndex, continuousPlay, onPlay]);
1520
- const activeAnnotationShortcuts = (0, import_react15.useMemo)(
1547
+ const activeAnnotationShortcuts = (0, import_react16.useMemo)(
1521
1548
  () => [
1522
1549
  {
1523
1550
  key: "[",
@@ -1554,7 +1581,7 @@ function useAnnotationKeyboardControls({
1554
1581
  ],
1555
1582
  [moveStartBoundary, moveEndBoundary, playActiveAnnotation]
1556
1583
  );
1557
- const navigationShortcuts = (0, import_react15.useMemo)(
1584
+ const navigationShortcuts = (0, import_react16.useMemo)(
1558
1585
  () => [
1559
1586
  {
1560
1587
  key: "ArrowUp",
@@ -1623,7 +1650,7 @@ function useAnnotationKeyboardControls({
1623
1650
  }
1624
1651
 
1625
1652
  // src/hooks/useDynamicEffects.ts
1626
- var import_react16 = require("react");
1653
+ var import_react17 = require("react");
1627
1654
 
1628
1655
  // src/effects/effectDefinitions.ts
1629
1656
  var effectDefinitions = [
@@ -2298,13 +2325,13 @@ function createEffectChain(effects) {
2298
2325
  // src/hooks/useDynamicEffects.ts
2299
2326
  var import_tone3 = require("tone");
2300
2327
  function useDynamicEffects(fftSize = 256) {
2301
- const [activeEffects, setActiveEffects] = (0, import_react16.useState)([]);
2302
- const activeEffectsRef = (0, import_react16.useRef)(activeEffects);
2328
+ const [activeEffects, setActiveEffects] = (0, import_react17.useState)([]);
2329
+ const activeEffectsRef = (0, import_react17.useRef)(activeEffects);
2303
2330
  activeEffectsRef.current = activeEffects;
2304
- const effectInstancesRef = (0, import_react16.useRef)(/* @__PURE__ */ new Map());
2305
- const analyserRef = (0, import_react16.useRef)(null);
2306
- const graphNodesRef = (0, import_react16.useRef)(null);
2307
- const rebuildChain = (0, import_react16.useCallback)((effects) => {
2331
+ const effectInstancesRef = (0, import_react17.useRef)(/* @__PURE__ */ new Map());
2332
+ const analyserRef = (0, import_react17.useRef)(null);
2333
+ const graphNodesRef = (0, import_react17.useRef)(null);
2334
+ const rebuildChain = (0, import_react17.useCallback)((effects) => {
2308
2335
  const nodes = graphNodesRef.current;
2309
2336
  if (!nodes) return;
2310
2337
  const { masterGainNode, destination, analyserNode } = nodes;
@@ -2332,7 +2359,7 @@ function useDynamicEffects(fftSize = 256) {
2332
2359
  analyserNode.connect(destination);
2333
2360
  }
2334
2361
  }, []);
2335
- const addEffect = (0, import_react16.useCallback)((effectId) => {
2362
+ const addEffect = (0, import_react17.useCallback)((effectId) => {
2336
2363
  const definition = getEffectDefinition(effectId);
2337
2364
  if (!definition) {
2338
2365
  console.error(`Unknown effect: ${effectId}`);
@@ -2353,7 +2380,7 @@ function useDynamicEffects(fftSize = 256) {
2353
2380
  };
2354
2381
  setActiveEffects((prev) => [...prev, newActiveEffect]);
2355
2382
  }, []);
2356
- const removeEffect = (0, import_react16.useCallback)((instanceId) => {
2383
+ const removeEffect = (0, import_react17.useCallback)((instanceId) => {
2357
2384
  const instance = effectInstancesRef.current.get(instanceId);
2358
2385
  if (instance) {
2359
2386
  instance.dispose();
@@ -2361,7 +2388,7 @@ function useDynamicEffects(fftSize = 256) {
2361
2388
  }
2362
2389
  setActiveEffects((prev) => prev.filter((e) => e.instanceId !== instanceId));
2363
2390
  }, []);
2364
- const updateParameter = (0, import_react16.useCallback)(
2391
+ const updateParameter = (0, import_react17.useCallback)(
2365
2392
  (instanceId, paramName, value) => {
2366
2393
  const instance = effectInstancesRef.current.get(instanceId);
2367
2394
  if (instance) {
@@ -2375,7 +2402,7 @@ function useDynamicEffects(fftSize = 256) {
2375
2402
  },
2376
2403
  []
2377
2404
  );
2378
- const toggleBypass = (0, import_react16.useCallback)((instanceId) => {
2405
+ const toggleBypass = (0, import_react17.useCallback)((instanceId) => {
2379
2406
  var _a;
2380
2407
  const effect = activeEffectsRef.current.find((e) => e.instanceId === instanceId);
2381
2408
  if (!effect) return;
@@ -2389,7 +2416,7 @@ function useDynamicEffects(fftSize = 256) {
2389
2416
  (prev) => prev.map((e) => e.instanceId === instanceId ? __spreadProps(__spreadValues({}, e), { bypassed: newBypassed }) : e)
2390
2417
  );
2391
2418
  }, []);
2392
- const reorderEffects = (0, import_react16.useCallback)((fromIndex, toIndex) => {
2419
+ const reorderEffects = (0, import_react17.useCallback)((fromIndex, toIndex) => {
2393
2420
  setActiveEffects((prev) => {
2394
2421
  const newEffects = [...prev];
2395
2422
  const [removed] = newEffects.splice(fromIndex, 1);
@@ -2397,15 +2424,15 @@ function useDynamicEffects(fftSize = 256) {
2397
2424
  return newEffects;
2398
2425
  });
2399
2426
  }, []);
2400
- const clearAllEffects = (0, import_react16.useCallback)(() => {
2427
+ const clearAllEffects = (0, import_react17.useCallback)(() => {
2401
2428
  effectInstancesRef.current.forEach((inst) => inst.dispose());
2402
2429
  effectInstancesRef.current.clear();
2403
2430
  setActiveEffects([]);
2404
2431
  }, []);
2405
- (0, import_react16.useEffect)(() => {
2432
+ (0, import_react17.useEffect)(() => {
2406
2433
  rebuildChain(activeEffects);
2407
2434
  }, [activeEffects, rebuildChain]);
2408
- const masterEffects = (0, import_react16.useCallback)(
2435
+ const masterEffects = (0, import_react17.useCallback)(
2409
2436
  (masterGainNode, destination, _isOffline) => {
2410
2437
  const analyserNode = new import_tone3.Analyser("fft", fftSize);
2411
2438
  analyserRef.current = analyserNode;
@@ -2437,14 +2464,14 @@ function useDynamicEffects(fftSize = 256) {
2437
2464
  [fftSize]
2438
2465
  // Only fftSize - reads effects from ref
2439
2466
  );
2440
- (0, import_react16.useEffect)(() => {
2467
+ (0, import_react17.useEffect)(() => {
2441
2468
  const effectInstances = effectInstancesRef.current;
2442
2469
  return () => {
2443
2470
  effectInstances.forEach((inst) => inst.dispose());
2444
2471
  effectInstances.clear();
2445
2472
  };
2446
2473
  }, []);
2447
- const createOfflineEffectsFunction = (0, import_react16.useCallback)(() => {
2474
+ const createOfflineEffectsFunction = (0, import_react17.useCallback)(() => {
2448
2475
  const nonBypassedEffects = activeEffects.filter((e) => !e.bypassed);
2449
2476
  if (nonBypassedEffects.length === 0) {
2450
2477
  return void 0;
@@ -2486,14 +2513,14 @@ function useDynamicEffects(fftSize = 256) {
2486
2513
  }
2487
2514
 
2488
2515
  // src/hooks/useTrackDynamicEffects.ts
2489
- var import_react17 = require("react");
2516
+ var import_react18 = require("react");
2490
2517
  function useTrackDynamicEffects() {
2491
- const [trackEffectsState, setTrackEffectsState] = (0, import_react17.useState)(
2518
+ const [trackEffectsState, setTrackEffectsState] = (0, import_react18.useState)(
2492
2519
  /* @__PURE__ */ new Map()
2493
2520
  );
2494
- const trackEffectInstancesRef = (0, import_react17.useRef)(/* @__PURE__ */ new Map());
2495
- const trackGraphNodesRef = (0, import_react17.useRef)(/* @__PURE__ */ new Map());
2496
- const rebuildTrackChain = (0, import_react17.useCallback)((trackId, trackEffects) => {
2521
+ const trackEffectInstancesRef = (0, import_react18.useRef)(/* @__PURE__ */ new Map());
2522
+ const trackGraphNodesRef = (0, import_react18.useRef)(/* @__PURE__ */ new Map());
2523
+ const rebuildTrackChain = (0, import_react18.useCallback)((trackId, trackEffects) => {
2497
2524
  const nodes = trackGraphNodesRef.current.get(trackId);
2498
2525
  if (!nodes) return;
2499
2526
  const { graphEnd, masterGainNode } = nodes;
@@ -2523,7 +2550,7 @@ function useTrackDynamicEffects() {
2523
2550
  currentNode.connect(masterGainNode);
2524
2551
  }
2525
2552
  }, []);
2526
- const addEffectToTrack = (0, import_react17.useCallback)((trackId, effectId) => {
2553
+ const addEffectToTrack = (0, import_react18.useCallback)((trackId, effectId) => {
2527
2554
  const definition = getEffectDefinition(effectId);
2528
2555
  if (!definition) {
2529
2556
  console.error(`Unknown effect: ${effectId}`);
@@ -2552,7 +2579,7 @@ function useTrackDynamicEffects() {
2552
2579
  return newState;
2553
2580
  });
2554
2581
  }, []);
2555
- const removeEffectFromTrack = (0, import_react17.useCallback)((trackId, instanceId) => {
2582
+ const removeEffectFromTrack = (0, import_react18.useCallback)((trackId, instanceId) => {
2556
2583
  const instancesMap = trackEffectInstancesRef.current.get(trackId);
2557
2584
  const instance = instancesMap == null ? void 0 : instancesMap.get(instanceId);
2558
2585
  if (instance) {
@@ -2569,7 +2596,7 @@ function useTrackDynamicEffects() {
2569
2596
  return newState;
2570
2597
  });
2571
2598
  }, []);
2572
- const updateTrackEffectParameter = (0, import_react17.useCallback)(
2599
+ const updateTrackEffectParameter = (0, import_react18.useCallback)(
2573
2600
  (trackId, instanceId, paramName, value) => {
2574
2601
  const instancesMap = trackEffectInstancesRef.current.get(trackId);
2575
2602
  const instance = instancesMap == null ? void 0 : instancesMap.get(instanceId);
@@ -2590,7 +2617,7 @@ function useTrackDynamicEffects() {
2590
2617
  },
2591
2618
  []
2592
2619
  );
2593
- const toggleBypass = (0, import_react17.useCallback)((trackId, instanceId) => {
2620
+ const toggleBypass = (0, import_react18.useCallback)((trackId, instanceId) => {
2594
2621
  var _a;
2595
2622
  const trackEffects = trackEffectsStateRef.current.get(trackId) || [];
2596
2623
  const effect = trackEffects.find((e) => e.instanceId === instanceId);
@@ -2612,7 +2639,7 @@ function useTrackDynamicEffects() {
2612
2639
  return newState;
2613
2640
  });
2614
2641
  }, []);
2615
- const clearTrackEffects = (0, import_react17.useCallback)((trackId) => {
2642
+ const clearTrackEffects = (0, import_react18.useCallback)((trackId) => {
2616
2643
  const instancesMap = trackEffectInstancesRef.current.get(trackId);
2617
2644
  if (instancesMap) {
2618
2645
  instancesMap.forEach((inst) => inst.dispose());
@@ -2624,9 +2651,9 @@ function useTrackDynamicEffects() {
2624
2651
  return newState;
2625
2652
  });
2626
2653
  }, []);
2627
- const trackEffectsStateRef = (0, import_react17.useRef)(trackEffectsState);
2654
+ const trackEffectsStateRef = (0, import_react18.useRef)(trackEffectsState);
2628
2655
  trackEffectsStateRef.current = trackEffectsState;
2629
- const getTrackEffectsFunction = (0, import_react17.useCallback)(
2656
+ const getTrackEffectsFunction = (0, import_react18.useCallback)(
2630
2657
  (trackId) => {
2631
2658
  return (graphEnd, masterGainNode, _isOffline) => {
2632
2659
  trackGraphNodesRef.current.set(trackId, {
@@ -2654,12 +2681,12 @@ function useTrackDynamicEffects() {
2654
2681
  []
2655
2682
  // No dependencies - stable function that reads from refs
2656
2683
  );
2657
- (0, import_react17.useEffect)(() => {
2684
+ (0, import_react18.useEffect)(() => {
2658
2685
  trackEffectsState.forEach((effects, trackId) => {
2659
2686
  rebuildTrackChain(trackId, effects);
2660
2687
  });
2661
2688
  }, [trackEffectsState, rebuildTrackChain]);
2662
- (0, import_react17.useEffect)(() => {
2689
+ (0, import_react18.useEffect)(() => {
2663
2690
  const trackEffectInstances = trackEffectInstancesRef.current;
2664
2691
  return () => {
2665
2692
  trackEffectInstances.forEach((instancesMap) => {
@@ -2669,7 +2696,7 @@ function useTrackDynamicEffects() {
2669
2696
  trackEffectInstances.clear();
2670
2697
  };
2671
2698
  }, []);
2672
- const createOfflineTrackEffectsFunction = (0, import_react17.useCallback)(
2699
+ const createOfflineTrackEffectsFunction = (0, import_react18.useCallback)(
2673
2700
  (trackId) => {
2674
2701
  const trackEffects = trackEffectsState.get(trackId) || [];
2675
2702
  const nonBypassedEffects = trackEffects.filter((e) => !e.bypassed);
@@ -2713,7 +2740,7 @@ function useTrackDynamicEffects() {
2713
2740
  }
2714
2741
 
2715
2742
  // src/hooks/useExportWav.ts
2716
- var import_react18 = require("react");
2743
+ var import_react19 = require("react");
2717
2744
  var import_playout2 = require("@waveform-playlist/playout");
2718
2745
 
2719
2746
  // src/utils/wavEncoder.ts
@@ -2787,10 +2814,10 @@ function downloadBlob(blob, filename) {
2787
2814
 
2788
2815
  // src/hooks/useExportWav.ts
2789
2816
  function useExportWav() {
2790
- const [isExporting, setIsExporting] = (0, import_react18.useState)(false);
2791
- const [progress, setProgress] = (0, import_react18.useState)(0);
2792
- const [error, setError] = (0, import_react18.useState)(null);
2793
- const exportWav = (0, import_react18.useCallback)(
2817
+ const [isExporting, setIsExporting] = (0, import_react19.useState)(false);
2818
+ const [progress, setProgress] = (0, import_react19.useState)(0);
2819
+ const [error, setError] = (0, import_react19.useState)(null);
2820
+ const exportWav = (0, import_react19.useCallback)(
2794
2821
  (_0, _1, ..._2) => __async(null, [_0, _1, ..._2], function* (tracks, trackStates, options = {}) {
2795
2822
  const {
2796
2823
  filename = "export",
@@ -3102,23 +3129,23 @@ function generateFadeCurve(startValue, endValue, numPoints, curveType) {
3102
3129
  }
3103
3130
 
3104
3131
  // src/hooks/useAnimationFrameLoop.ts
3105
- var import_react19 = require("react");
3132
+ var import_react20 = require("react");
3106
3133
  var useAnimationFrameLoop = () => {
3107
- const animationFrameRef = (0, import_react19.useRef)(null);
3108
- const stopAnimationFrameLoop = (0, import_react19.useCallback)(() => {
3134
+ const animationFrameRef = (0, import_react20.useRef)(null);
3135
+ const stopAnimationFrameLoop = (0, import_react20.useCallback)(() => {
3109
3136
  if (animationFrameRef.current !== null) {
3110
3137
  cancelAnimationFrame(animationFrameRef.current);
3111
3138
  animationFrameRef.current = null;
3112
3139
  }
3113
3140
  }, []);
3114
- const startAnimationFrameLoop = (0, import_react19.useCallback)(
3141
+ const startAnimationFrameLoop = (0, import_react20.useCallback)(
3115
3142
  (callback) => {
3116
3143
  stopAnimationFrameLoop();
3117
3144
  animationFrameRef.current = requestAnimationFrame(callback);
3118
3145
  },
3119
3146
  [stopAnimationFrameLoop]
3120
3147
  );
3121
- (0, import_react19.useEffect)(() => {
3148
+ (0, import_react20.useEffect)(() => {
3122
3149
  return () => {
3123
3150
  stopAnimationFrameLoop();
3124
3151
  };
@@ -3131,7 +3158,7 @@ var useAnimationFrameLoop = () => {
3131
3158
  };
3132
3159
 
3133
3160
  // src/hooks/useWaveformDataCache.ts
3134
- var import_react20 = require("react");
3161
+ var import_react21 = require("react");
3135
3162
 
3136
3163
  // src/workers/peaksWorker.ts
3137
3164
  var import_waveform_data2 = __toESM(require("waveform-data"));
@@ -3363,20 +3390,20 @@ function createPeaksWorker() {
3363
3390
 
3364
3391
  // src/hooks/useWaveformDataCache.ts
3365
3392
  function useWaveformDataCache(tracks, baseScale) {
3366
- const [cache, setCache] = (0, import_react20.useState)(() => /* @__PURE__ */ new Map());
3367
- const [isGenerating, setIsGenerating] = (0, import_react20.useState)(false);
3368
- const workerRef = (0, import_react20.useRef)(null);
3369
- const generatedByBufferRef = (0, import_react20.useRef)(/* @__PURE__ */ new WeakMap());
3370
- const inflightByBufferRef = (0, import_react20.useRef)(/* @__PURE__ */ new WeakMap());
3371
- const subscribersByBufferRef = (0, import_react20.useRef)(/* @__PURE__ */ new WeakMap());
3372
- const pendingCountRef = (0, import_react20.useRef)(0);
3373
- const getWorker = (0, import_react20.useCallback)(() => {
3393
+ const [cache, setCache] = (0, import_react21.useState)(() => /* @__PURE__ */ new Map());
3394
+ const [isGenerating, setIsGenerating] = (0, import_react21.useState)(false);
3395
+ const workerRef = (0, import_react21.useRef)(null);
3396
+ const generatedByBufferRef = (0, import_react21.useRef)(/* @__PURE__ */ new WeakMap());
3397
+ const inflightByBufferRef = (0, import_react21.useRef)(/* @__PURE__ */ new WeakMap());
3398
+ const subscribersByBufferRef = (0, import_react21.useRef)(/* @__PURE__ */ new WeakMap());
3399
+ const pendingCountRef = (0, import_react21.useRef)(0);
3400
+ const getWorker = (0, import_react21.useCallback)(() => {
3374
3401
  if (!workerRef.current) {
3375
3402
  workerRef.current = createPeaksWorker();
3376
3403
  }
3377
3404
  return workerRef.current;
3378
3405
  }, []);
3379
- (0, import_react20.useEffect)(() => {
3406
+ (0, import_react21.useEffect)(() => {
3380
3407
  let cancelled = false;
3381
3408
  const generatedByBuffer = generatedByBufferRef.current;
3382
3409
  const inflightByBuffer = inflightByBufferRef.current;
@@ -3481,7 +3508,7 @@ function useWaveformDataCache(tracks, baseScale) {
3481
3508
  setIsGenerating(false);
3482
3509
  };
3483
3510
  }, [tracks, baseScale, getWorker]);
3484
- (0, import_react20.useEffect)(() => {
3511
+ (0, import_react21.useEffect)(() => {
3485
3512
  return () => {
3486
3513
  var _a;
3487
3514
  (_a = workerRef.current) == null ? void 0 : _a.terminate();
@@ -3492,8 +3519,8 @@ function useWaveformDataCache(tracks, baseScale) {
3492
3519
  }
3493
3520
 
3494
3521
  // src/hooks/useDynamicTracks.ts
3495
- var import_react21 = require("react");
3496
- var import_core2 = require("@waveform-playlist/core");
3522
+ var import_react22 = require("react");
3523
+ var import_core4 = require("@waveform-playlist/core");
3497
3524
  var import_playout3 = require("@waveform-playlist/playout");
3498
3525
  function getSourceName(source) {
3499
3526
  var _a, _b, _c, _d, _e;
@@ -3527,13 +3554,13 @@ function decodeSource(source, audioContext, signal) {
3527
3554
  });
3528
3555
  }
3529
3556
  function useDynamicTracks() {
3530
- const [tracks, setTracks] = (0, import_react21.useState)([]);
3531
- const [loadingCount, setLoadingCount] = (0, import_react21.useState)(0);
3532
- const [errors, setErrors] = (0, import_react21.useState)([]);
3533
- const cancelledRef = (0, import_react21.useRef)(false);
3534
- const loadingIdsRef = (0, import_react21.useRef)(/* @__PURE__ */ new Set());
3535
- const abortControllersRef = (0, import_react21.useRef)(/* @__PURE__ */ new Map());
3536
- (0, import_react21.useEffect)(() => {
3557
+ const [tracks, setTracks] = (0, import_react22.useState)([]);
3558
+ const [loadingCount, setLoadingCount] = (0, import_react22.useState)(0);
3559
+ const [errors, setErrors] = (0, import_react22.useState)([]);
3560
+ const cancelledRef = (0, import_react22.useRef)(false);
3561
+ const loadingIdsRef = (0, import_react22.useRef)(/* @__PURE__ */ new Set());
3562
+ const abortControllersRef = (0, import_react22.useRef)(/* @__PURE__ */ new Map());
3563
+ (0, import_react22.useEffect)(() => {
3537
3564
  const controllers = abortControllersRef.current;
3538
3565
  return () => {
3539
3566
  cancelledRef.current = true;
@@ -3543,11 +3570,11 @@ function useDynamicTracks() {
3543
3570
  controllers.clear();
3544
3571
  };
3545
3572
  }, []);
3546
- const addTracks = (0, import_react21.useCallback)((sources) => {
3573
+ const addTracks = (0, import_react22.useCallback)((sources) => {
3547
3574
  if (sources.length === 0) return;
3548
3575
  const audioContext = (0, import_playout3.getGlobalAudioContext)();
3549
3576
  const placeholders = sources.map((source) => ({
3550
- track: (0, import_core2.createTrack)({ name: `${getSourceName(source)} (loading...)`, clips: [] }),
3577
+ track: (0, import_core4.createTrack)({ name: `${getSourceName(source)} (loading...)`, clips: [] }),
3551
3578
  source
3552
3579
  }));
3553
3580
  setTracks((prev) => [...prev, ...placeholders.map((p) => p.track)]);
@@ -3559,7 +3586,7 @@ function useDynamicTracks() {
3559
3586
  (() => __async(null, null, function* () {
3560
3587
  try {
3561
3588
  const { audioBuffer, name } = yield decodeSource(source, audioContext, controller.signal);
3562
- const clip = (0, import_core2.createClipFromSeconds)({
3589
+ const clip = (0, import_core4.createClipFromSeconds)({
3563
3590
  audioBuffer,
3564
3591
  startTime: 0,
3565
3592
  duration: audioBuffer.duration,
@@ -3593,7 +3620,7 @@ function useDynamicTracks() {
3593
3620
  }))();
3594
3621
  }
3595
3622
  }, []);
3596
- const removeTrack = (0, import_react21.useCallback)((trackId) => {
3623
+ const removeTrack = (0, import_react22.useCallback)((trackId) => {
3597
3624
  setTracks((prev) => prev.filter((t) => t.id !== trackId));
3598
3625
  const controller = abortControllersRef.current.get(trackId);
3599
3626
  if (controller) {
@@ -3615,24 +3642,24 @@ function useDynamicTracks() {
3615
3642
  }
3616
3643
 
3617
3644
  // src/hooks/useOutputMeter.ts
3618
- var import_react22 = require("react");
3645
+ var import_react23 = require("react");
3619
3646
  var import_playout4 = require("@waveform-playlist/playout");
3620
- var import_core3 = require("@waveform-playlist/core");
3647
+ var import_core5 = require("@waveform-playlist/core");
3621
3648
  var import_worklets = require("@waveform-playlist/worklets");
3622
3649
  var PEAK_DECAY = 0.98;
3623
3650
  function useOutputMeter(options = {}) {
3624
3651
  const { channelCount = 2, updateRate = 60, isPlaying = false } = options;
3625
- const [levels, setLevels] = (0, import_react22.useState)(() => new Array(channelCount).fill(0));
3626
- const [peakLevels, setPeakLevels] = (0, import_react22.useState)(() => new Array(channelCount).fill(0));
3627
- const [rmsLevels, setRmsLevels] = (0, import_react22.useState)(() => new Array(channelCount).fill(0));
3628
- const workletNodeRef = (0, import_react22.useRef)(null);
3629
- const smoothedPeakRef = (0, import_react22.useRef)(new Array(channelCount).fill(0));
3630
- const [meterError, setMeterError] = (0, import_react22.useState)(null);
3631
- const resetPeak = (0, import_react22.useCallback)(
3652
+ const [levels, setLevels] = (0, import_react23.useState)(() => new Array(channelCount).fill(0));
3653
+ const [peakLevels, setPeakLevels] = (0, import_react23.useState)(() => new Array(channelCount).fill(0));
3654
+ const [rmsLevels, setRmsLevels] = (0, import_react23.useState)(() => new Array(channelCount).fill(0));
3655
+ const workletNodeRef = (0, import_react23.useRef)(null);
3656
+ const smoothedPeakRef = (0, import_react23.useRef)(new Array(channelCount).fill(0));
3657
+ const [meterError, setMeterError] = (0, import_react23.useState)(null);
3658
+ const resetPeak = (0, import_react23.useCallback)(
3632
3659
  () => setPeakLevels(new Array(channelCount).fill(0)),
3633
3660
  [channelCount]
3634
3661
  );
3635
- (0, import_react22.useEffect)(() => {
3662
+ (0, import_react23.useEffect)(() => {
3636
3663
  if (!isPlaying) {
3637
3664
  const zeros = new Array(channelCount).fill(0);
3638
3665
  smoothedPeakRef.current = new Array(channelCount).fill(0);
@@ -3641,7 +3668,7 @@ function useOutputMeter(options = {}) {
3641
3668
  setPeakLevels(zeros);
3642
3669
  }
3643
3670
  }, [isPlaying, channelCount]);
3644
- (0, import_react22.useEffect)(() => {
3671
+ (0, import_react23.useEffect)(() => {
3645
3672
  let isMounted = true;
3646
3673
  const setup = () => __async(null, null, function* () {
3647
3674
  const context = (0, import_playout4.getGlobalContext)();
@@ -3672,8 +3699,8 @@ function useOutputMeter(options = {}) {
3672
3699
  const rmsValues = [];
3673
3700
  for (let ch = 0; ch < peak.length; ch++) {
3674
3701
  smoothed[ch] = Math.max(peak[ch], ((_a = smoothed[ch]) != null ? _a : 0) * PEAK_DECAY);
3675
- peakValues.push((0, import_core3.gainToNormalized)(smoothed[ch]));
3676
- rmsValues.push((0, import_core3.gainToNormalized)(rms[ch]));
3702
+ peakValues.push((0, import_core5.gainToNormalized)(smoothed[ch]));
3703
+ rmsValues.push((0, import_core5.gainToNormalized)(rms[ch]));
3677
3704
  }
3678
3705
  setLevels(peakValues);
3679
3706
  setRmsLevels(rmsValues);
@@ -3713,10 +3740,10 @@ function useOutputMeter(options = {}) {
3713
3740
 
3714
3741
  // src/WaveformPlaylistContext.tsx
3715
3742
  var import_jsx_runtime = require("react/jsx-runtime");
3716
- var PlaybackAnimationContext = (0, import_react23.createContext)(null);
3717
- var PlaylistStateContext = (0, import_react23.createContext)(null);
3718
- var PlaylistControlsContext = (0, import_react23.createContext)(null);
3719
- var PlaylistDataContext = (0, import_react23.createContext)(null);
3743
+ var PlaybackAnimationContext = (0, import_react24.createContext)(null);
3744
+ var PlaylistStateContext = (0, import_react24.createContext)(null);
3745
+ var PlaylistControlsContext = (0, import_react24.createContext)(null);
3746
+ var PlaylistDataContext = (0, import_react24.createContext)(null);
3720
3747
  var WaveformPlaylistProvider = ({
3721
3748
  tracks,
3722
3749
  timescale = false,
@@ -3739,18 +3766,19 @@ var WaveformPlaylistProvider = ({
3739
3766
  soundFontCache,
3740
3767
  deferEngineRebuild = false,
3741
3768
  indefinitePlayback = false,
3769
+ sampleRate: sampleRateProp,
3742
3770
  children
3743
3771
  }) => {
3744
3772
  var _a, _b, _c, _d;
3745
3773
  const progressBarWidth = progressBarWidthProp != null ? progressBarWidthProp : barWidth + barGap;
3746
- const indefinitePlaybackRef = (0, import_react23.useRef)(indefinitePlayback);
3774
+ const indefinitePlaybackRef = (0, import_react24.useRef)(indefinitePlayback);
3747
3775
  indefinitePlaybackRef.current = indefinitePlayback;
3748
- const stableZoomLevels = (0, import_react23.useMemo)(
3776
+ const stableZoomLevels = (0, import_react24.useMemo)(
3749
3777
  () => zoomLevels,
3750
3778
  // eslint-disable-next-line react-hooks/exhaustive-deps
3751
3779
  [zoomLevels == null ? void 0 : zoomLevels.join(",")]
3752
3780
  );
3753
- const annotations = (0, import_react23.useMemo)(() => {
3781
+ const annotations = (0, import_react24.useMemo)(() => {
3754
3782
  if (!(annotationList == null ? void 0 : annotationList.annotations)) return [];
3755
3783
  if (process.env.NODE_ENV !== "production" && annotationList.annotations.length > 0) {
3756
3784
  const first = annotationList.annotations[0];
@@ -3763,48 +3791,60 @@ var WaveformPlaylistProvider = ({
3763
3791
  }
3764
3792
  return annotationList.annotations;
3765
3793
  }, [annotationList == null ? void 0 : annotationList.annotations]);
3766
- const annotationsRef = (0, import_react23.useRef)(annotations);
3794
+ const annotationsRef = (0, import_react24.useRef)(annotations);
3767
3795
  annotationsRef.current = annotations;
3768
- const [activeAnnotationId, setActiveAnnotationIdState] = (0, import_react23.useState)(null);
3769
- const [isPlaying, setIsPlaying] = (0, import_react23.useState)(false);
3770
- const [currentTime, setCurrentTime] = (0, import_react23.useState)(0);
3771
- const [duration, setDuration] = (0, import_react23.useState)(0);
3772
- const [audioBuffers, setAudioBuffers] = (0, import_react23.useState)([]);
3773
- const [peaksDataArray, setPeaksDataArray] = (0, import_react23.useState)([]);
3774
- const [trackStates, setTrackStates] = (0, import_react23.useState)([]);
3775
- const [isAutomaticScroll, setIsAutomaticScroll] = (0, import_react23.useState)(automaticScroll);
3776
- const [continuousPlay, setContinuousPlayState] = (0, import_react23.useState)(
3796
+ const [activeAnnotationId, setActiveAnnotationIdState] = (0, import_react24.useState)(null);
3797
+ const [isPlaying, setIsPlaying] = (0, import_react24.useState)(false);
3798
+ const [currentTime, setCurrentTime] = (0, import_react24.useState)(0);
3799
+ const [duration, setDuration] = (0, import_react24.useState)(0);
3800
+ const [audioBuffers, setAudioBuffers] = (0, import_react24.useState)([]);
3801
+ const [peaksDataArray, setPeaksDataArray] = (0, import_react24.useState)([]);
3802
+ const [trackStates, setTrackStates] = (0, import_react24.useState)([]);
3803
+ const [isAutomaticScroll, setIsAutomaticScroll] = (0, import_react24.useState)(automaticScroll);
3804
+ const [continuousPlay, setContinuousPlayState] = (0, import_react24.useState)(
3777
3805
  (_a = annotationList == null ? void 0 : annotationList.isContinuousPlay) != null ? _a : false
3778
3806
  );
3779
- const [linkEndpoints, setLinkEndpoints] = (0, import_react23.useState)((_b = annotationList == null ? void 0 : annotationList.linkEndpoints) != null ? _b : false);
3780
- const [annotationsEditable, setAnnotationsEditable] = (0, import_react23.useState)((_c = annotationList == null ? void 0 : annotationList.editable) != null ? _c : false);
3781
- const [isReady, setIsReady] = (0, import_react23.useState)(false);
3782
- const engineRef = (0, import_react23.useRef)(null);
3783
- const audioInitializedRef = (0, import_react23.useRef)(false);
3784
- const isPlayingRef = (0, import_react23.useRef)(false);
3807
+ const [linkEndpoints, setLinkEndpoints] = (0, import_react24.useState)((_b = annotationList == null ? void 0 : annotationList.linkEndpoints) != null ? _b : false);
3808
+ const [annotationsEditable, setAnnotationsEditable] = (0, import_react24.useState)((_c = annotationList == null ? void 0 : annotationList.editable) != null ? _c : false);
3809
+ const [isReady, setIsReady] = (0, import_react24.useState)(false);
3810
+ const engineRef = (0, import_react24.useRef)(null);
3811
+ const audioInitializedRef = (0, import_react24.useRef)(false);
3812
+ const isPlayingRef = (0, import_react24.useRef)(false);
3785
3813
  isPlayingRef.current = isPlaying;
3786
- const playStartPositionRef = (0, import_react23.useRef)(0);
3787
- const currentTimeRef = (0, import_react23.useRef)(0);
3788
- const tracksRef = (0, import_react23.useRef)(tracks);
3789
- const soundFontCacheRef = (0, import_react23.useRef)(soundFontCache);
3814
+ const playStartPositionRef = (0, import_react24.useRef)(0);
3815
+ const currentTimeRef = (0, import_react24.useRef)(0);
3816
+ const tracksRef = (0, import_react24.useRef)(tracks);
3817
+ const soundFontCacheRef = (0, import_react24.useRef)(soundFontCache);
3790
3818
  soundFontCacheRef.current = soundFontCache;
3791
- const trackStatesRef = (0, import_react23.useRef)(trackStates);
3792
- const playbackStartTimeRef = (0, import_react23.useRef)(0);
3793
- const audioStartPositionRef = (0, import_react23.useRef)(0);
3794
- const playbackEndTimeRef = (0, import_react23.useRef)(null);
3795
- const scrollContainerRef = (0, import_react23.useRef)(null);
3796
- const isAutomaticScrollRef = (0, import_react23.useRef)(false);
3797
- const continuousPlayRef = (0, import_react23.useRef)((_d = annotationList == null ? void 0 : annotationList.isContinuousPlay) != null ? _d : false);
3798
- const activeAnnotationIdRef = (0, import_react23.useRef)(null);
3799
- const engineTracksRef = (0, import_react23.useRef)(null);
3800
- const lastTracksVersionRef = (0, import_react23.useRef)(0);
3801
- const skipEngineDisposeRef = (0, import_react23.useRef)(false);
3802
- const isDraggingRef = (0, import_react23.useRef)(false);
3803
- const prevTracksRef = (0, import_react23.useRef)([]);
3804
- const samplesPerPixelRef = (0, import_react23.useRef)(initialSamplesPerPixel);
3805
- const sampleRateRef = (0, import_react23.useRef)(
3806
- typeof AudioContext !== "undefined" ? (0, import_playout5.getGlobalAudioContext)().sampleRate : 48e3
3807
- );
3819
+ const trackStatesRef = (0, import_react24.useRef)(trackStates);
3820
+ const playbackStartTimeRef = (0, import_react24.useRef)(0);
3821
+ const audioStartPositionRef = (0, import_react24.useRef)(0);
3822
+ const playbackEndTimeRef = (0, import_react24.useRef)(null);
3823
+ const scrollContainerRef = (0, import_react24.useRef)(null);
3824
+ const isAutomaticScrollRef = (0, import_react24.useRef)(false);
3825
+ const continuousPlayRef = (0, import_react24.useRef)((_d = annotationList == null ? void 0 : annotationList.isContinuousPlay) != null ? _d : false);
3826
+ const activeAnnotationIdRef = (0, import_react24.useRef)(null);
3827
+ const engineTracksRef = (0, import_react24.useRef)(null);
3828
+ const lastTracksVersionRef = (0, import_react24.useRef)(0);
3829
+ const skipEngineDisposeRef = (0, import_react24.useRef)(false);
3830
+ const isDraggingRef = (0, import_react24.useRef)(false);
3831
+ const prevTracksRef = (0, import_react24.useRef)([]);
3832
+ const samplesPerPixelRef = (0, import_react24.useRef)(initialSamplesPerPixel);
3833
+ const [initialSampleRate] = (0, import_react24.useState)(() => {
3834
+ if (typeof AudioContext === "undefined") return sampleRateProp != null ? sampleRateProp : 48e3;
3835
+ try {
3836
+ if (sampleRateProp !== void 0) {
3837
+ return (0, import_playout5.configureGlobalContext)({ sampleRate: sampleRateProp });
3838
+ }
3839
+ return (0, import_playout5.getGlobalAudioContext)().sampleRate;
3840
+ } catch (err) {
3841
+ console.warn(
3842
+ "[waveform-playlist] Failed to configure AudioContext: " + String(err) + " \u2014 falling back to " + (sampleRateProp != null ? sampleRateProp : 48e3) + " Hz"
3843
+ );
3844
+ return sampleRateProp != null ? sampleRateProp : 48e3;
3845
+ }
3846
+ });
3847
+ const sampleRateRef = (0, import_react24.useRef)(initialSampleRate);
3808
3848
  const { timeFormat, setTimeFormat, formatTime: formatTime2 } = useTimeFormat();
3809
3849
  const zoom = useZoomControls({ engineRef, initialSamplesPerPixel });
3810
3850
  const { samplesPerPixel, onEngineState: onZoomEngineState } = zoom;
@@ -3841,21 +3881,28 @@ var WaveformPlaylistProvider = ({
3841
3881
  onEngineState: onSelectedTrackEngineState,
3842
3882
  selectedTrackIdRef
3843
3883
  } = useSelectedTrack({ engineRef });
3884
+ const {
3885
+ canUndo,
3886
+ canRedo,
3887
+ undo,
3888
+ redo,
3889
+ onEngineState: onUndoEngineState
3890
+ } = useUndoState({ engineRef });
3844
3891
  const { animationFrameRef, startAnimationFrameLoop, stopAnimationFrameLoop } = useAnimationFrameLoop();
3845
- const baseScale = (0, import_react23.useMemo)(
3892
+ const baseScale = (0, import_react24.useMemo)(
3846
3893
  () => Math.min(...stableZoomLevels != null ? stableZoomLevels : [256, 512, 1024, 2048, 4096, 8192]),
3847
3894
  [stableZoomLevels]
3848
3895
  );
3849
3896
  const { cache: waveformDataCache } = useWaveformDataCache(tracks, baseScale);
3850
- const setContinuousPlay = (0, import_react23.useCallback)((value) => {
3897
+ const setContinuousPlay = (0, import_react24.useCallback)((value) => {
3851
3898
  continuousPlayRef.current = value;
3852
3899
  setContinuousPlayState(value);
3853
3900
  }, []);
3854
- const setActiveAnnotationId = (0, import_react23.useCallback)((value) => {
3901
+ const setActiveAnnotationId = (0, import_react24.useCallback)((value) => {
3855
3902
  activeAnnotationIdRef.current = value;
3856
3903
  setActiveAnnotationIdState(value);
3857
3904
  }, []);
3858
- const setLoopRegionFromSelection = (0, import_react23.useCallback)(() => {
3905
+ const setLoopRegionFromSelection = (0, import_react24.useCallback)(() => {
3859
3906
  var _a2, _b2;
3860
3907
  const start = (_a2 = selectionStartRef.current) != null ? _a2 : 0;
3861
3908
  const end = (_b2 = selectionEndRef.current) != null ? _b2 : 0;
@@ -3863,10 +3910,10 @@ var WaveformPlaylistProvider = ({
3863
3910
  setLoopRegion(start, end);
3864
3911
  }
3865
3912
  }, [setLoopRegion, selectionStartRef, selectionEndRef]);
3866
- (0, import_react23.useEffect)(() => {
3913
+ (0, import_react24.useEffect)(() => {
3867
3914
  isAutomaticScrollRef.current = isAutomaticScroll;
3868
3915
  }, [isAutomaticScroll]);
3869
- (0, import_react23.useEffect)(() => {
3916
+ (0, import_react24.useEffect)(() => {
3870
3917
  trackStatesRef.current = trackStates;
3871
3918
  }, [trackStates]);
3872
3919
  tracksRef.current = tracks;
@@ -3877,7 +3924,7 @@ var WaveformPlaylistProvider = ({
3877
3924
  return current === pt;
3878
3925
  });
3879
3926
  skipEngineDisposeRef.current = isEngineTracks || isDraggingRef.current || isIncrementalAdd;
3880
- (0, import_react23.useEffect)(() => {
3927
+ (0, import_react24.useEffect)(() => {
3881
3928
  if (!scrollContainerRef.current || duration === 0) return;
3882
3929
  const container = scrollContainerRef.current;
3883
3930
  const oldSamplesPerPixel = samplesPerPixelRef.current;
@@ -3893,8 +3940,8 @@ var WaveformPlaylistProvider = ({
3893
3940
  container.scrollLeft = newScrollLeft;
3894
3941
  samplesPerPixelRef.current = newSamplesPerPixel;
3895
3942
  }, [samplesPerPixel, duration]);
3896
- const pendingResumeRef = (0, import_react23.useRef)(null);
3897
- (0, import_react23.useEffect)(() => {
3943
+ const pendingResumeRef = (0, import_react24.useRef)(null);
3944
+ (0, import_react24.useEffect)(() => {
3898
3945
  var _a2, _b2, _c2, _d2;
3899
3946
  if (isEngineTracks || isDraggingRef.current) {
3900
3947
  if (isEngineTracks) {
@@ -4069,6 +4116,7 @@ var WaveformPlaylistProvider = ({
4069
4116
  onSelectedTrackEngineState(state);
4070
4117
  onZoomEngineState(state);
4071
4118
  onVolumeEngineState(state);
4119
+ onUndoEngineState(state);
4072
4120
  if (!suppressTracksMirroring && state.tracksVersion !== lastTracksVersionRef.current) {
4073
4121
  lastTracksVersionRef.current = state.tracksVersion;
4074
4122
  engineTracksRef.current = state.tracks;
@@ -4126,6 +4174,7 @@ var WaveformPlaylistProvider = ({
4126
4174
  onSelectedTrackEngineState,
4127
4175
  onZoomEngineState,
4128
4176
  onVolumeEngineState,
4177
+ onUndoEngineState,
4129
4178
  onTracksChange,
4130
4179
  masterVolumeRef,
4131
4180
  selectionStartRef,
@@ -4137,7 +4186,7 @@ var WaveformPlaylistProvider = ({
4137
4186
  soundFontCache,
4138
4187
  deferEngineRebuild
4139
4188
  ]);
4140
- (0, import_react23.useEffect)(() => {
4189
+ (0, import_react24.useEffect)(() => {
4141
4190
  if (tracks.length === 0) return;
4142
4191
  const allTrackPeaks = tracks.map((track) => {
4143
4192
  const clipPeaks = track.clips.map((clip) => {
@@ -4145,15 +4194,28 @@ var WaveformPlaylistProvider = ({
4145
4194
  let peaks;
4146
4195
  if (clip.waveformData) {
4147
4196
  try {
4197
+ const wdRate = clip.waveformData.sample_rate;
4198
+ const clipRate = clip.sampleRate;
4199
+ let peakOffset = clip.offsetSamples;
4200
+ let peakDuration = clip.durationSamples;
4201
+ let peakSpp = samplesPerPixel;
4202
+ if (wdRate !== clipRate && clipRate > 0 && wdRate > 0) {
4203
+ const ratio = wdRate / clipRate;
4204
+ peakOffset = Math.round(clip.offsetSamples * ratio);
4205
+ peakDuration = Math.round(clip.durationSamples * ratio);
4206
+ peakSpp = Math.max(1, Math.round(samplesPerPixel * ratio));
4207
+ }
4148
4208
  peaks = extractPeaksFromWaveformDataFull(
4149
4209
  clip.waveformData,
4150
- samplesPerPixel,
4210
+ peakSpp,
4151
4211
  mono,
4152
- clip.offsetSamples,
4153
- clip.durationSamples
4212
+ peakOffset,
4213
+ peakDuration
4154
4214
  );
4155
4215
  } catch (err) {
4156
- console.warn("[waveform-playlist] Failed to extract peaks from waveformData:", err);
4216
+ console.warn(
4217
+ "[waveform-playlist] Failed to extract peaks from waveformData: " + String(err)
4218
+ );
4157
4219
  }
4158
4220
  }
4159
4221
  if (!peaks) {
@@ -4168,7 +4230,9 @@ var WaveformPlaylistProvider = ({
4168
4230
  clip.durationSamples
4169
4231
  );
4170
4232
  } catch (err) {
4171
- console.warn("[waveform-playlist] Failed to extract peaks from cache:", err);
4233
+ console.warn(
4234
+ "[waveform-playlist] Failed to extract peaks from cache: " + String(err)
4235
+ );
4172
4236
  }
4173
4237
  }
4174
4238
  }
@@ -4203,8 +4267,8 @@ var WaveformPlaylistProvider = ({
4203
4267
  });
4204
4268
  setPeaksDataArray(allTrackPeaks);
4205
4269
  }, [tracks, samplesPerPixel, mono, waveformDataCache, deferEngineRebuild]);
4206
- const getPlaybackTimeFallbackWarnedRef = (0, import_react23.useRef)(false);
4207
- const getPlaybackTime = (0, import_react23.useCallback)(() => {
4270
+ const getPlaybackTimeFallbackWarnedRef = (0, import_react24.useRef)(false);
4271
+ const getPlaybackTime = (0, import_react24.useCallback)(() => {
4208
4272
  var _a2, _b2;
4209
4273
  if (engineRef.current) {
4210
4274
  return engineRef.current.getCurrentTime();
@@ -4218,7 +4282,7 @@ var WaveformPlaylistProvider = ({
4218
4282
  const elapsed = (0, import_tone4.getContext)().currentTime - ((_a2 = playbackStartTimeRef.current) != null ? _a2 : 0);
4219
4283
  return ((_b2 = audioStartPositionRef.current) != null ? _b2 : 0) + elapsed;
4220
4284
  }, []);
4221
- const startAnimationLoop = (0, import_react23.useCallback)(() => {
4285
+ const startAnimationLoop = (0, import_react24.useCallback)(() => {
4222
4286
  const updateTime = () => {
4223
4287
  const time = getPlaybackTime();
4224
4288
  currentTimeRef.current = time;
@@ -4287,7 +4351,7 @@ var WaveformPlaylistProvider = ({
4287
4351
  startAnimationFrameLoop(updateTime);
4288
4352
  }, [duration, setActiveAnnotationId, startAnimationFrameLoop, getPlaybackTime]);
4289
4353
  const stopAnimationLoop = stopAnimationFrameLoop;
4290
- (0, import_react23.useEffect)(() => {
4354
+ (0, import_react24.useEffect)(() => {
4291
4355
  const reschedulePlayback = () => __async(null, null, function* () {
4292
4356
  if (isPlaying && animationFrameRef.current && engineRef.current) {
4293
4357
  if (continuousPlay) {
@@ -4312,7 +4376,7 @@ var WaveformPlaylistProvider = ({
4312
4376
  stopAnimationLoop();
4313
4377
  });
4314
4378
  }, [continuousPlay, isPlaying, startAnimationLoop, stopAnimationLoop, animationFrameRef]);
4315
- (0, import_react23.useEffect)(() => {
4379
+ (0, import_react24.useEffect)(() => {
4316
4380
  const resumePlayback = () => __async(null, null, function* () {
4317
4381
  if (pendingResumeRef.current && engineRef.current) {
4318
4382
  const { position } = pendingResumeRef.current;
@@ -4336,7 +4400,7 @@ var WaveformPlaylistProvider = ({
4336
4400
  stopAnimationLoop();
4337
4401
  });
4338
4402
  }, [tracks, startAnimationLoop, stopAnimationLoop]);
4339
- const play = (0, import_react23.useCallback)(
4403
+ const play = (0, import_react24.useCallback)(
4340
4404
  (startTime, playDuration) => __async(null, null, function* () {
4341
4405
  if (!engineRef.current) return;
4342
4406
  const actualStartTime = startTime != null ? startTime : currentTimeRef.current;
@@ -4367,7 +4431,7 @@ var WaveformPlaylistProvider = ({
4367
4431
  }),
4368
4432
  [startAnimationLoop, stopAnimationLoop]
4369
4433
  );
4370
- const pause = (0, import_react23.useCallback)(() => {
4434
+ const pause = (0, import_react24.useCallback)(() => {
4371
4435
  if (!engineRef.current) return;
4372
4436
  const pauseTime = getPlaybackTime();
4373
4437
  engineRef.current.pause();
@@ -4376,7 +4440,7 @@ var WaveformPlaylistProvider = ({
4376
4440
  currentTimeRef.current = pauseTime;
4377
4441
  setCurrentTime(pauseTime);
4378
4442
  }, [stopAnimationLoop, getPlaybackTime]);
4379
- const stop = (0, import_react23.useCallback)(() => {
4443
+ const stop = (0, import_react24.useCallback)(() => {
4380
4444
  if (!engineRef.current) return;
4381
4445
  engineRef.current.stop();
4382
4446
  setIsPlaying(false);
@@ -4385,7 +4449,7 @@ var WaveformPlaylistProvider = ({
4385
4449
  setCurrentTime(playStartPositionRef.current);
4386
4450
  setActiveAnnotationId(null);
4387
4451
  }, [stopAnimationLoop, setActiveAnnotationId]);
4388
- const seekTo = (0, import_react23.useCallback)(
4452
+ const seekTo = (0, import_react24.useCallback)(
4389
4453
  (time) => {
4390
4454
  const clampedTime = Math.max(0, Math.min(time, duration));
4391
4455
  currentTimeRef.current = clampedTime;
@@ -4396,7 +4460,7 @@ var WaveformPlaylistProvider = ({
4396
4460
  },
4397
4461
  [duration, isPlaying, play]
4398
4462
  );
4399
- const setTrackMute = (0, import_react23.useCallback)(
4463
+ const setTrackMute = (0, import_react24.useCallback)(
4400
4464
  (trackIndex, muted) => {
4401
4465
  var _a2;
4402
4466
  const trackId = (_a2 = tracksRef.current[trackIndex]) == null ? void 0 : _a2.id;
@@ -4410,7 +4474,7 @@ var WaveformPlaylistProvider = ({
4410
4474
  },
4411
4475
  [trackStates]
4412
4476
  );
4413
- const setTrackSolo = (0, import_react23.useCallback)(
4477
+ const setTrackSolo = (0, import_react24.useCallback)(
4414
4478
  (trackIndex, soloed) => {
4415
4479
  var _a2;
4416
4480
  const trackId = (_a2 = tracksRef.current[trackIndex]) == null ? void 0 : _a2.id;
@@ -4424,7 +4488,7 @@ var WaveformPlaylistProvider = ({
4424
4488
  },
4425
4489
  [trackStates]
4426
4490
  );
4427
- const setTrackVolume = (0, import_react23.useCallback)(
4491
+ const setTrackVolume = (0, import_react24.useCallback)(
4428
4492
  (trackIndex, volume2) => {
4429
4493
  var _a2;
4430
4494
  const trackId = (_a2 = tracksRef.current[trackIndex]) == null ? void 0 : _a2.id;
@@ -4438,7 +4502,7 @@ var WaveformPlaylistProvider = ({
4438
4502
  },
4439
4503
  [trackStates]
4440
4504
  );
4441
- const setTrackPan = (0, import_react23.useCallback)(
4505
+ const setTrackPan = (0, import_react24.useCallback)(
4442
4506
  (trackIndex, pan) => {
4443
4507
  var _a2;
4444
4508
  const trackId = (_a2 = tracksRef.current[trackIndex]) == null ? void 0 : _a2.id;
@@ -4452,7 +4516,7 @@ var WaveformPlaylistProvider = ({
4452
4516
  },
4453
4517
  [trackStates]
4454
4518
  );
4455
- const setSelection = (0, import_react23.useCallback)(
4519
+ const setSelection = (0, import_react24.useCallback)(
4456
4520
  (start, end) => {
4457
4521
  setSelectionEngine(start, end);
4458
4522
  currentTimeRef.current = start;
@@ -4465,12 +4529,12 @@ var WaveformPlaylistProvider = ({
4465
4529
  },
4466
4530
  [isPlaying, setSelectionEngine]
4467
4531
  );
4468
- const setScrollContainer = (0, import_react23.useCallback)((element) => {
4532
+ const setScrollContainer = (0, import_react24.useCallback)((element) => {
4469
4533
  scrollContainerRef.current = element;
4470
4534
  }, []);
4471
- const onAnnotationsChangeRef = (0, import_react23.useRef)(onAnnotationsChange);
4535
+ const onAnnotationsChangeRef = (0, import_react24.useRef)(onAnnotationsChange);
4472
4536
  onAnnotationsChangeRef.current = onAnnotationsChange;
4473
- const setAnnotations = (0, import_react23.useCallback)(
4537
+ const setAnnotations = (0, import_react24.useCallback)(
4474
4538
  (action) => {
4475
4539
  const updated = typeof action === "function" ? action(annotationsRef.current) : action;
4476
4540
  if (!onAnnotationsChangeRef.current) {
@@ -4488,7 +4552,7 @@ var WaveformPlaylistProvider = ({
4488
4552
  const sampleRate = sampleRateRef.current;
4489
4553
  const timeScaleHeight = timescale ? 30 : 0;
4490
4554
  const minimumPlaylistHeight = tracks.length * waveHeight + timeScaleHeight;
4491
- const animationValue = (0, import_react23.useMemo)(
4555
+ const animationValue = (0, import_react24.useMemo)(
4492
4556
  () => ({
4493
4557
  isPlaying,
4494
4558
  currentTime,
@@ -4506,7 +4570,7 @@ var WaveformPlaylistProvider = ({
4506
4570
  getPlaybackTime
4507
4571
  ]
4508
4572
  );
4509
- const stateValue = (0, import_react23.useMemo)(
4573
+ const stateValue = (0, import_react24.useMemo)(
4510
4574
  () => ({
4511
4575
  continuousPlay,
4512
4576
  linkEndpoints,
@@ -4520,7 +4584,9 @@ var WaveformPlaylistProvider = ({
4520
4584
  selectedTrackId,
4521
4585
  loopStart,
4522
4586
  loopEnd,
4523
- indefinitePlayback
4587
+ indefinitePlayback,
4588
+ canUndo,
4589
+ canRedo
4524
4590
  }),
4525
4591
  [
4526
4592
  continuousPlay,
@@ -4535,20 +4601,22 @@ var WaveformPlaylistProvider = ({
4535
4601
  selectedTrackId,
4536
4602
  loopStart,
4537
4603
  loopEnd,
4538
- indefinitePlayback
4604
+ indefinitePlayback,
4605
+ canUndo,
4606
+ canRedo
4539
4607
  ]
4540
4608
  );
4541
- const setCurrentTimeControl = (0, import_react23.useCallback)(
4609
+ const setCurrentTimeControl = (0, import_react24.useCallback)(
4542
4610
  (time) => {
4543
4611
  currentTimeRef.current = time;
4544
4612
  setCurrentTime(time);
4545
4613
  },
4546
4614
  [currentTimeRef]
4547
4615
  );
4548
- const setAutomaticScrollControl = (0, import_react23.useCallback)((enabled) => {
4616
+ const setAutomaticScrollControl = (0, import_react24.useCallback)((enabled) => {
4549
4617
  setIsAutomaticScroll(enabled);
4550
4618
  }, []);
4551
- const controlsValue = (0, import_react23.useMemo)(
4619
+ const controlsValue = (0, import_react24.useMemo)(
4552
4620
  () => ({
4553
4621
  // Playback controls
4554
4622
  play,
@@ -4586,7 +4654,10 @@ var WaveformPlaylistProvider = ({
4586
4654
  setLoopEnabled,
4587
4655
  setLoopRegion,
4588
4656
  setLoopRegionFromSelection,
4589
- clearLoopRegion
4657
+ clearLoopRegion,
4658
+ // Undo/redo
4659
+ undo,
4660
+ redo
4590
4661
  }),
4591
4662
  [
4592
4663
  play,
@@ -4616,10 +4687,12 @@ var WaveformPlaylistProvider = ({
4616
4687
  setLoopEnabled,
4617
4688
  setLoopRegion,
4618
4689
  setLoopRegionFromSelection,
4619
- clearLoopRegion
4690
+ clearLoopRegion,
4691
+ undo,
4692
+ redo
4620
4693
  ]
4621
4694
  );
4622
- const dataValue = (0, import_react23.useMemo)(
4695
+ const dataValue = (0, import_react24.useMemo)(
4623
4696
  () => ({
4624
4697
  duration,
4625
4698
  audioBuffers,
@@ -4675,28 +4748,28 @@ var WaveformPlaylistProvider = ({
4675
4748
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_styled_components.ThemeProvider, { theme: mergedTheme, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlaybackAnimationContext.Provider, { value: animationValue, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlaylistStateContext.Provider, { value: stateValue, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlaylistControlsContext.Provider, { value: controlsValue, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlaylistDataContext.Provider, { value: dataValue, children }) }) }) }) });
4676
4749
  };
4677
4750
  var usePlaybackAnimation = () => {
4678
- const context = (0, import_react23.useContext)(PlaybackAnimationContext);
4751
+ const context = (0, import_react24.useContext)(PlaybackAnimationContext);
4679
4752
  if (!context) {
4680
4753
  throw new Error("usePlaybackAnimation must be used within WaveformPlaylistProvider");
4681
4754
  }
4682
4755
  return context;
4683
4756
  };
4684
4757
  var usePlaylistState = () => {
4685
- const context = (0, import_react23.useContext)(PlaylistStateContext);
4758
+ const context = (0, import_react24.useContext)(PlaylistStateContext);
4686
4759
  if (!context) {
4687
4760
  throw new Error("usePlaylistState must be used within WaveformPlaylistProvider");
4688
4761
  }
4689
4762
  return context;
4690
4763
  };
4691
4764
  var usePlaylistControls = () => {
4692
- const context = (0, import_react23.useContext)(PlaylistControlsContext);
4765
+ const context = (0, import_react24.useContext)(PlaylistControlsContext);
4693
4766
  if (!context) {
4694
4767
  throw new Error("usePlaylistControls must be used within WaveformPlaylistProvider");
4695
4768
  }
4696
4769
  return context;
4697
4770
  };
4698
4771
  var usePlaylistData = () => {
4699
- const context = (0, import_react23.useContext)(PlaylistDataContext);
4772
+ const context = (0, import_react24.useContext)(PlaylistDataContext);
4700
4773
  if (!context) {
4701
4774
  throw new Error("usePlaylistData must be used within WaveformPlaylistProvider");
4702
4775
  }
@@ -4704,15 +4777,15 @@ var usePlaylistData = () => {
4704
4777
  };
4705
4778
 
4706
4779
  // src/MediaElementPlaylistContext.tsx
4707
- var import_react24 = require("react");
4780
+ var import_react25 = require("react");
4708
4781
  var import_styled_components2 = require("styled-components");
4709
4782
  var import_media_element_playout = require("@waveform-playlist/media-element-playout");
4710
4783
  var import_ui_components3 = require("@waveform-playlist/ui-components");
4711
4784
  var import_jsx_runtime2 = require("react/jsx-runtime");
4712
- var MediaElementAnimationContext = (0, import_react24.createContext)(null);
4713
- var MediaElementStateContext = (0, import_react24.createContext)(null);
4714
- var MediaElementControlsContext = (0, import_react24.createContext)(null);
4715
- var MediaElementDataContext = (0, import_react24.createContext)(null);
4785
+ var MediaElementAnimationContext = (0, import_react25.createContext)(null);
4786
+ var MediaElementStateContext = (0, import_react25.createContext)(null);
4787
+ var MediaElementControlsContext = (0, import_react25.createContext)(null);
4788
+ var MediaElementDataContext = (0, import_react25.createContext)(null);
4716
4789
  var MediaElementPlaylistProvider = ({
4717
4790
  track,
4718
4791
  samplesPerPixel: initialSamplesPerPixel = 1024,
@@ -4734,12 +4807,12 @@ var MediaElementPlaylistProvider = ({
4734
4807
  }) => {
4735
4808
  var _a;
4736
4809
  const progressBarWidth = progressBarWidthProp != null ? progressBarWidthProp : barWidth + barGap;
4737
- const [isPlaying, setIsPlaying] = (0, import_react24.useState)(false);
4738
- const [currentTime, setCurrentTime] = (0, import_react24.useState)(0);
4739
- const [duration, setDuration] = (0, import_react24.useState)(0);
4740
- const [peaksDataArray, setPeaksDataArray] = (0, import_react24.useState)([]);
4741
- const [playbackRate, setPlaybackRateState] = (0, import_react24.useState)(initialPlaybackRate);
4742
- const annotations = (0, import_react24.useMemo)(() => {
4810
+ const [isPlaying, setIsPlaying] = (0, import_react25.useState)(false);
4811
+ const [currentTime, setCurrentTime] = (0, import_react25.useState)(0);
4812
+ const [duration, setDuration] = (0, import_react25.useState)(0);
4813
+ const [peaksDataArray, setPeaksDataArray] = (0, import_react25.useState)([]);
4814
+ const [playbackRate, setPlaybackRateState] = (0, import_react25.useState)(initialPlaybackRate);
4815
+ const annotations = (0, import_react25.useMemo)(() => {
4743
4816
  if (!(annotationList == null ? void 0 : annotationList.annotations)) return [];
4744
4817
  if (process.env.NODE_ENV !== "production" && annotationList.annotations.length > 0) {
4745
4818
  const first = annotationList.annotations[0];
@@ -4752,41 +4825,41 @@ var MediaElementPlaylistProvider = ({
4752
4825
  }
4753
4826
  return annotationList.annotations;
4754
4827
  }, [annotationList == null ? void 0 : annotationList.annotations]);
4755
- const annotationsRef = (0, import_react24.useRef)(annotations);
4828
+ const annotationsRef = (0, import_react25.useRef)(annotations);
4756
4829
  annotationsRef.current = annotations;
4757
- const [activeAnnotationId, setActiveAnnotationIdState] = (0, import_react24.useState)(null);
4758
- const [continuousPlay, setContinuousPlayState] = (0, import_react24.useState)(
4830
+ const [activeAnnotationId, setActiveAnnotationIdState] = (0, import_react25.useState)(null);
4831
+ const [continuousPlay, setContinuousPlayState] = (0, import_react25.useState)(
4759
4832
  (_a = annotationList == null ? void 0 : annotationList.isContinuousPlay) != null ? _a : false
4760
4833
  );
4761
- const [samplesPerPixel] = (0, import_react24.useState)(initialSamplesPerPixel);
4762
- const [isAutomaticScroll, setIsAutomaticScroll] = (0, import_react24.useState)(automaticScroll);
4763
- const playoutRef = (0, import_react24.useRef)(null);
4764
- const currentTimeRef = (0, import_react24.useRef)(0);
4765
- const continuousPlayRef = (0, import_react24.useRef)(continuousPlay);
4766
- const activeAnnotationIdRef = (0, import_react24.useRef)(null);
4767
- const scrollContainerRef = (0, import_react24.useRef)(null);
4768
- const isAutomaticScrollRef = (0, import_react24.useRef)(automaticScroll);
4769
- const samplesPerPixelRef = (0, import_react24.useRef)(initialSamplesPerPixel);
4834
+ const [samplesPerPixel] = (0, import_react25.useState)(initialSamplesPerPixel);
4835
+ const [isAutomaticScroll, setIsAutomaticScroll] = (0, import_react25.useState)(automaticScroll);
4836
+ const playoutRef = (0, import_react25.useRef)(null);
4837
+ const currentTimeRef = (0, import_react25.useRef)(0);
4838
+ const continuousPlayRef = (0, import_react25.useRef)(continuousPlay);
4839
+ const activeAnnotationIdRef = (0, import_react25.useRef)(null);
4840
+ const scrollContainerRef = (0, import_react25.useRef)(null);
4841
+ const isAutomaticScrollRef = (0, import_react25.useRef)(automaticScroll);
4842
+ const samplesPerPixelRef = (0, import_react25.useRef)(initialSamplesPerPixel);
4770
4843
  const { startAnimationFrameLoop, stopAnimationFrameLoop } = useAnimationFrameLoop();
4771
- (0, import_react24.useEffect)(() => {
4844
+ (0, import_react25.useEffect)(() => {
4772
4845
  continuousPlayRef.current = continuousPlay;
4773
4846
  }, [continuousPlay]);
4774
- (0, import_react24.useEffect)(() => {
4847
+ (0, import_react25.useEffect)(() => {
4775
4848
  isAutomaticScrollRef.current = isAutomaticScroll;
4776
4849
  }, [isAutomaticScroll]);
4777
- const setActiveAnnotationId = (0, import_react24.useCallback)((value) => {
4850
+ const setActiveAnnotationId = (0, import_react25.useCallback)((value) => {
4778
4851
  activeAnnotationIdRef.current = value;
4779
4852
  setActiveAnnotationIdState(value);
4780
4853
  }, []);
4781
- const setContinuousPlay = (0, import_react24.useCallback)((value) => {
4854
+ const setContinuousPlay = (0, import_react25.useCallback)((value) => {
4782
4855
  continuousPlayRef.current = value;
4783
4856
  setContinuousPlayState(value);
4784
4857
  }, []);
4785
- const setScrollContainer = (0, import_react24.useCallback)((element) => {
4858
+ const setScrollContainer = (0, import_react25.useCallback)((element) => {
4786
4859
  scrollContainerRef.current = element;
4787
4860
  }, []);
4788
4861
  const sampleRate = track.waveformData.sample_rate;
4789
- (0, import_react24.useEffect)(() => {
4862
+ (0, import_react25.useEffect)(() => {
4790
4863
  var _a2, _b;
4791
4864
  const playout = new import_media_element_playout.MediaElementPlayout({
4792
4865
  playbackRate: initialPlaybackRate,
@@ -4833,7 +4906,7 @@ var MediaElementPlaylistProvider = ({
4833
4906
  stopAnimationFrameLoop,
4834
4907
  setActiveAnnotationId
4835
4908
  ]);
4836
- (0, import_react24.useEffect)(() => {
4909
+ (0, import_react25.useEffect)(() => {
4837
4910
  var _a2;
4838
4911
  try {
4839
4912
  const extractedPeaks = extractPeaksFromWaveformData(
@@ -4862,7 +4935,7 @@ var MediaElementPlaylistProvider = ({
4862
4935
  console.warn("[waveform-playlist] Failed to extract peaks from waveform data:", err);
4863
4936
  }
4864
4937
  }, [track.waveformData, track.name, samplesPerPixel, sampleRate]);
4865
- const startAnimationLoop = (0, import_react24.useCallback)(() => {
4938
+ const startAnimationLoop = (0, import_react25.useCallback)(() => {
4866
4939
  const updateTime = () => {
4867
4940
  var _a2, _b, _c;
4868
4941
  const time = (_b = (_a2 = playoutRef.current) == null ? void 0 : _a2.getCurrentTime()) != null ? _b : 0;
@@ -4905,7 +4978,7 @@ var MediaElementPlaylistProvider = ({
4905
4978
  startAnimationFrameLoop(updateTime);
4906
4979
  }, [setActiveAnnotationId, sampleRate, startAnimationFrameLoop]);
4907
4980
  const stopAnimationLoop = stopAnimationFrameLoop;
4908
- const play = (0, import_react24.useCallback)(
4981
+ const play = (0, import_react25.useCallback)(
4909
4982
  (startTime) => {
4910
4983
  if (!playoutRef.current) return;
4911
4984
  const actualStartTime = startTime != null ? startTime : currentTimeRef.current;
@@ -4915,14 +4988,14 @@ var MediaElementPlaylistProvider = ({
4915
4988
  },
4916
4989
  [startAnimationLoop]
4917
4990
  );
4918
- const pause = (0, import_react24.useCallback)(() => {
4991
+ const pause = (0, import_react25.useCallback)(() => {
4919
4992
  if (!playoutRef.current) return;
4920
4993
  playoutRef.current.pause();
4921
4994
  setIsPlaying(false);
4922
4995
  stopAnimationLoop();
4923
4996
  setCurrentTime(playoutRef.current.getCurrentTime());
4924
4997
  }, [stopAnimationLoop]);
4925
- const stop = (0, import_react24.useCallback)(() => {
4998
+ const stop = (0, import_react25.useCallback)(() => {
4926
4999
  if (!playoutRef.current) return;
4927
5000
  playoutRef.current.stop();
4928
5001
  setIsPlaying(false);
@@ -4931,7 +5004,7 @@ var MediaElementPlaylistProvider = ({
4931
5004
  setCurrentTime(0);
4932
5005
  setActiveAnnotationId(null);
4933
5006
  }, [stopAnimationLoop, setActiveAnnotationId]);
4934
- const seekTo = (0, import_react24.useCallback)(
5007
+ const seekTo = (0, import_react25.useCallback)(
4935
5008
  (time) => {
4936
5009
  const clampedTime = Math.max(0, Math.min(time, duration));
4937
5010
  currentTimeRef.current = clampedTime;
@@ -4942,7 +5015,7 @@ var MediaElementPlaylistProvider = ({
4942
5015
  },
4943
5016
  [duration]
4944
5017
  );
4945
- const setPlaybackRate = (0, import_react24.useCallback)((rate) => {
5018
+ const setPlaybackRate = (0, import_react25.useCallback)((rate) => {
4946
5019
  const clampedRate = Math.max(0.5, Math.min(2, rate));
4947
5020
  setPlaybackRateState(clampedRate);
4948
5021
  if (playoutRef.current) {
@@ -4950,7 +5023,7 @@ var MediaElementPlaylistProvider = ({
4950
5023
  }
4951
5024
  }, []);
4952
5025
  const timeScaleHeight = timescale ? 30 : 0;
4953
- const animationValue = (0, import_react24.useMemo)(
5026
+ const animationValue = (0, import_react25.useMemo)(
4954
5027
  () => ({
4955
5028
  isPlaying,
4956
5029
  currentTime,
@@ -4958,7 +5031,7 @@ var MediaElementPlaylistProvider = ({
4958
5031
  }),
4959
5032
  [isPlaying, currentTime]
4960
5033
  );
4961
- const stateValue = (0, import_react24.useMemo)(
5034
+ const stateValue = (0, import_react25.useMemo)(
4962
5035
  () => ({
4963
5036
  continuousPlay,
4964
5037
  annotations,
@@ -4968,9 +5041,9 @@ var MediaElementPlaylistProvider = ({
4968
5041
  }),
4969
5042
  [continuousPlay, annotations, activeAnnotationId, playbackRate, isAutomaticScroll]
4970
5043
  );
4971
- const onAnnotationsChangeRef = (0, import_react24.useRef)(onAnnotationsChange);
5044
+ const onAnnotationsChangeRef = (0, import_react25.useRef)(onAnnotationsChange);
4972
5045
  onAnnotationsChangeRef.current = onAnnotationsChange;
4973
- const setAnnotations = (0, import_react24.useCallback)(
5046
+ const setAnnotations = (0, import_react25.useCallback)(
4974
5047
  (action) => {
4975
5048
  const updated = typeof action === "function" ? action(annotationsRef.current) : action;
4976
5049
  if (!onAnnotationsChangeRef.current) {
@@ -4985,7 +5058,7 @@ var MediaElementPlaylistProvider = ({
4985
5058
  },
4986
5059
  []
4987
5060
  );
4988
- const controlsValue = (0, import_react24.useMemo)(
5061
+ const controlsValue = (0, import_react25.useMemo)(
4989
5062
  () => ({
4990
5063
  play,
4991
5064
  pause,
@@ -5013,7 +5086,7 @@ var MediaElementPlaylistProvider = ({
5013
5086
  setScrollContainer
5014
5087
  ]
5015
5088
  );
5016
- const dataValue = (0, import_react24.useMemo)(
5089
+ const dataValue = (0, import_react25.useMemo)(
5017
5090
  () => ({
5018
5091
  duration,
5019
5092
  peaksDataArray,
@@ -5048,28 +5121,28 @@ var MediaElementPlaylistProvider = ({
5048
5121
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_styled_components2.ThemeProvider, { theme: mergedTheme, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(MediaElementAnimationContext.Provider, { value: animationValue, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(MediaElementStateContext.Provider, { value: stateValue, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(MediaElementControlsContext.Provider, { value: controlsValue, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(MediaElementDataContext.Provider, { value: dataValue, children }) }) }) }) });
5049
5122
  };
5050
5123
  var useMediaElementAnimation = () => {
5051
- const context = (0, import_react24.useContext)(MediaElementAnimationContext);
5124
+ const context = (0, import_react25.useContext)(MediaElementAnimationContext);
5052
5125
  if (!context) {
5053
5126
  throw new Error("useMediaElementAnimation must be used within MediaElementPlaylistProvider");
5054
5127
  }
5055
5128
  return context;
5056
5129
  };
5057
5130
  var useMediaElementState = () => {
5058
- const context = (0, import_react24.useContext)(MediaElementStateContext);
5131
+ const context = (0, import_react25.useContext)(MediaElementStateContext);
5059
5132
  if (!context) {
5060
5133
  throw new Error("useMediaElementState must be used within MediaElementPlaylistProvider");
5061
5134
  }
5062
5135
  return context;
5063
5136
  };
5064
5137
  var useMediaElementControls = () => {
5065
- const context = (0, import_react24.useContext)(MediaElementControlsContext);
5138
+ const context = (0, import_react25.useContext)(MediaElementControlsContext);
5066
5139
  if (!context) {
5067
5140
  throw new Error("useMediaElementControls must be used within MediaElementPlaylistProvider");
5068
5141
  }
5069
5142
  return context;
5070
5143
  };
5071
5144
  var useMediaElementData = () => {
5072
- const context = (0, import_react24.useContext)(MediaElementDataContext);
5145
+ const context = (0, import_react25.useContext)(MediaElementDataContext);
5073
5146
  if (!context) {
5074
5147
  throw new Error("useMediaElementData must be used within MediaElementPlaylistProvider");
5075
5148
  }
@@ -5077,7 +5150,7 @@ var useMediaElementData = () => {
5077
5150
  };
5078
5151
 
5079
5152
  // src/components/PlaybackControls.tsx
5080
- var import_react25 = require("react");
5153
+ var import_react26 = require("react");
5081
5154
  var import_ui_components4 = require("@waveform-playlist/ui-components");
5082
5155
  var import_jsx_runtime3 = require("react/jsx-runtime");
5083
5156
  var PlayButton = ({ className }) => {
@@ -5213,7 +5286,7 @@ var ClearAllButton = ({
5213
5286
  className
5214
5287
  }) => {
5215
5288
  const { stop } = usePlaylistControls();
5216
- const handleClick = (0, import_react25.useCallback)(() => {
5289
+ const handleClick = (0, import_react26.useCallback)(() => {
5217
5290
  stop();
5218
5291
  onClearAll();
5219
5292
  }, [stop, onClearAll]);
@@ -5241,7 +5314,7 @@ var ZoomOutButton = ({
5241
5314
  };
5242
5315
 
5243
5316
  // src/components/ContextualControls.tsx
5244
- var import_react26 = require("react");
5317
+ var import_react27 = require("react");
5245
5318
  var import_ui_components6 = require("@waveform-playlist/ui-components");
5246
5319
  var import_styled_components3 = __toESM(require("styled-components"));
5247
5320
  var import_jsx_runtime5 = require("react/jsx-runtime");
@@ -5274,11 +5347,11 @@ var PositionDisplay = import_styled_components3.default.span`
5274
5347
  `;
5275
5348
  var AudioPosition = ({ className }) => {
5276
5349
  var _a;
5277
- const timeRef = (0, import_react26.useRef)(null);
5278
- const animationFrameRef = (0, import_react26.useRef)(null);
5350
+ const timeRef = (0, import_react27.useRef)(null);
5351
+ const animationFrameRef = (0, import_react27.useRef)(null);
5279
5352
  const { isPlaying, currentTimeRef, getPlaybackTime } = usePlaybackAnimation();
5280
5353
  const { timeFormat: format } = usePlaylistData();
5281
- (0, import_react26.useEffect)(() => {
5354
+ (0, import_react27.useEffect)(() => {
5282
5355
  const updateTime = () => {
5283
5356
  var _a2;
5284
5357
  if (timeRef.current) {
@@ -5301,7 +5374,7 @@ var AudioPosition = ({ className }) => {
5301
5374
  }
5302
5375
  };
5303
5376
  }, [isPlaying, format, currentTimeRef, getPlaybackTime]);
5304
- (0, import_react26.useEffect)(() => {
5377
+ (0, import_react27.useEffect)(() => {
5305
5378
  var _a2;
5306
5379
  if (!isPlaying && timeRef.current) {
5307
5380
  timeRef.current.textContent = (0, import_ui_components6.formatTime)((_a2 = currentTimeRef.current) != null ? _a2 : 0, format);
@@ -5336,11 +5409,11 @@ var AutomaticScrollCheckbox = ({ className }) => {
5336
5409
  };
5337
5410
 
5338
5411
  // src/AnnotationIntegrationContext.tsx
5339
- var import_react27 = require("react");
5340
- var AnnotationIntegrationContext = (0, import_react27.createContext)(null);
5412
+ var import_react28 = require("react");
5413
+ var AnnotationIntegrationContext = (0, import_react28.createContext)(null);
5341
5414
  var AnnotationIntegrationProvider = AnnotationIntegrationContext.Provider;
5342
5415
  function useAnnotationIntegration() {
5343
- const context = (0, import_react27.useContext)(AnnotationIntegrationContext);
5416
+ const context = (0, import_react28.useContext)(AnnotationIntegrationContext);
5344
5417
  if (!context) {
5345
5418
  throw new Error(
5346
5419
  "useAnnotationIntegration must be used within <AnnotationProvider>. Install @waveform-playlist/annotations and wrap your app with <AnnotationProvider>. See: https://waveform-playlist.naomiaro.com/docs/guides/annotations"
@@ -5426,22 +5499,22 @@ var ExportWavButton = ({
5426
5499
  };
5427
5500
 
5428
5501
  // src/contexts/ClipInteractionContext.tsx
5429
- var import_react28 = require("react");
5430
- var ClipInteractionContext = (0, import_react28.createContext)(false);
5502
+ var import_react29 = require("react");
5503
+ var ClipInteractionContext = (0, import_react29.createContext)(false);
5431
5504
  var ClipInteractionContextProvider = ClipInteractionContext.Provider;
5432
5505
  function useClipInteractionEnabled() {
5433
- return (0, import_react28.useContext)(ClipInteractionContext);
5506
+ return (0, import_react29.useContext)(ClipInteractionContext);
5434
5507
  }
5435
5508
 
5436
5509
  // src/components/PlaylistVisualization.tsx
5437
- var import_react32 = require("react");
5510
+ var import_react33 = require("react");
5438
5511
  var import_react_dom = require("react-dom");
5439
5512
  var import_styled_components6 = __toESM(require("styled-components"));
5440
5513
  var import_playout6 = require("@waveform-playlist/playout");
5441
5514
  var import_ui_components9 = require("@waveform-playlist/ui-components");
5442
5515
 
5443
5516
  // src/components/AnimatedPlayhead.tsx
5444
- var import_react29 = require("react");
5517
+ var import_react30 = require("react");
5445
5518
  var import_styled_components4 = __toESM(require("styled-components"));
5446
5519
  var import_jsx_runtime8 = require("react/jsx-runtime");
5447
5520
  var PlayheadLine = import_styled_components4.default.div.attrs((props) => ({
@@ -5459,11 +5532,11 @@ var PlayheadLine = import_styled_components4.default.div.attrs((props) => ({
5459
5532
  will-change: transform;
5460
5533
  `;
5461
5534
  var AnimatedPlayhead = ({ color = "#ff0000" }) => {
5462
- const playheadRef = (0, import_react29.useRef)(null);
5463
- const animationFrameRef = (0, import_react29.useRef)(null);
5535
+ const playheadRef = (0, import_react30.useRef)(null);
5536
+ const animationFrameRef = (0, import_react30.useRef)(null);
5464
5537
  const { isPlaying, currentTimeRef, getPlaybackTime } = usePlaybackAnimation();
5465
5538
  const { samplesPerPixel, sampleRate, progressBarWidth } = usePlaylistData();
5466
- (0, import_react29.useEffect)(() => {
5539
+ (0, import_react30.useEffect)(() => {
5467
5540
  const updatePosition = () => {
5468
5541
  var _a;
5469
5542
  if (playheadRef.current) {
@@ -5487,7 +5560,7 @@ var AnimatedPlayhead = ({ color = "#ff0000" }) => {
5487
5560
  }
5488
5561
  };
5489
5562
  }, [isPlaying, sampleRate, samplesPerPixel, currentTimeRef, getPlaybackTime]);
5490
- (0, import_react29.useEffect)(() => {
5563
+ (0, import_react30.useEffect)(() => {
5491
5564
  var _a;
5492
5565
  if (!isPlaying && playheadRef.current) {
5493
5566
  const time = (_a = currentTimeRef.current) != null ? _a : 0;
@@ -5499,9 +5572,9 @@ var AnimatedPlayhead = ({ color = "#ff0000" }) => {
5499
5572
  };
5500
5573
 
5501
5574
  // src/components/ChannelWithProgress.tsx
5502
- var import_react30 = require("react");
5575
+ var import_react31 = require("react");
5503
5576
  var import_styled_components5 = __toESM(require("styled-components"));
5504
- var import_core4 = require("@waveform-playlist/core");
5577
+ var import_core6 = require("@waveform-playlist/core");
5505
5578
  var import_ui_components8 = require("@waveform-playlist/ui-components");
5506
5579
  var import_jsx_runtime9 = require("react/jsx-runtime");
5507
5580
  var ChannelWrapper = import_styled_components5.default.div`
@@ -5557,19 +5630,19 @@ var ChannelWithProgress = (_a) => {
5557
5630
  "clipSampleRate",
5558
5631
  "clipOffsetSeconds"
5559
5632
  ]);
5560
- const progressRef = (0, import_react30.useRef)(null);
5561
- const animationFrameRef = (0, import_react30.useRef)(null);
5633
+ const progressRef = (0, import_react31.useRef)(null);
5634
+ const animationFrameRef = (0, import_react31.useRef)(null);
5562
5635
  const theme = (0, import_ui_components8.useTheme)();
5563
5636
  const { waveHeight } = (0, import_ui_components8.usePlaylistInfo)();
5564
5637
  const { isPlaying, currentTimeRef, getPlaybackTime } = usePlaybackAnimation();
5565
5638
  const { samplesPerPixel, sampleRate } = usePlaylistData();
5566
5639
  const progressColor = (theme == null ? void 0 : theme.waveProgressColor) || "rgba(0, 0, 0, 0.1)";
5567
- const clipPixelWidth = (0, import_core4.clipPixelWidth)(
5640
+ const clipPixelWidth = (0, import_core6.clipPixelWidth)(
5568
5641
  clipStartSample,
5569
5642
  clipDurationSamples,
5570
5643
  samplesPerPixel
5571
5644
  );
5572
- (0, import_react30.useEffect)(() => {
5645
+ (0, import_react31.useEffect)(() => {
5573
5646
  const updateProgress = () => {
5574
5647
  var _a2;
5575
5648
  if (progressRef.current) {
@@ -5611,7 +5684,7 @@ var ChannelWithProgress = (_a) => {
5611
5684
  currentTimeRef,
5612
5685
  getPlaybackTime
5613
5686
  ]);
5614
- (0, import_react30.useEffect)(() => {
5687
+ (0, import_react31.useEffect)(() => {
5615
5688
  var _a2;
5616
5689
  if (!isPlaying && progressRef.current) {
5617
5690
  const currentTime = (_a2 = currentTimeRef.current) != null ? _a2 : 0;
@@ -5696,11 +5769,11 @@ var ChannelWithProgress = (_a) => {
5696
5769
  };
5697
5770
 
5698
5771
  // src/SpectrogramIntegrationContext.tsx
5699
- var import_react31 = require("react");
5700
- var SpectrogramIntegrationContext = (0, import_react31.createContext)(null);
5772
+ var import_react32 = require("react");
5773
+ var SpectrogramIntegrationContext = (0, import_react32.createContext)(null);
5701
5774
  var SpectrogramIntegrationProvider = SpectrogramIntegrationContext.Provider;
5702
5775
  function useSpectrogramIntegration() {
5703
- const context = (0, import_react31.useContext)(SpectrogramIntegrationContext);
5776
+ const context = (0, import_react32.useContext)(SpectrogramIntegrationContext);
5704
5777
  if (!context) {
5705
5778
  throw new Error(
5706
5779
  "useSpectrogramIntegration must be used within <SpectrogramProvider>. Install @waveform-playlist/spectrogram and wrap your app with <SpectrogramProvider>."
@@ -5780,7 +5853,7 @@ var PlaylistVisualization = ({
5780
5853
  isLoopEnabled,
5781
5854
  indefinitePlayback
5782
5855
  } = usePlaylistState();
5783
- const annotationIntegration = (0, import_react32.useContext)(AnnotationIntegrationContext);
5856
+ const annotationIntegration = (0, import_react33.useContext)(AnnotationIntegrationContext);
5784
5857
  const {
5785
5858
  setAnnotations: _setAnnotations,
5786
5859
  setActiveAnnotationId,
@@ -5810,8 +5883,8 @@ var PlaylistVisualization = ({
5810
5883
  isReady,
5811
5884
  mono
5812
5885
  } = usePlaylistData();
5813
- const spectrogram = (0, import_react32.useContext)(SpectrogramIntegrationContext);
5814
- const perTrackSpectrogramHelpers = (0, import_react32.useMemo)(() => {
5886
+ const spectrogram = (0, import_react33.useContext)(SpectrogramIntegrationContext);
5887
+ const perTrackSpectrogramHelpers = (0, import_react33.useMemo)(() => {
5815
5888
  if (!spectrogram)
5816
5889
  return /* @__PURE__ */ new Map();
5817
5890
  const helpers = /* @__PURE__ */ new Map();
@@ -5830,7 +5903,7 @@ var PlaylistVisualization = ({
5830
5903
  });
5831
5904
  return helpers;
5832
5905
  }, [tracks, spectrogram]);
5833
- const workerCanvasApi = (0, import_react32.useMemo)(() => {
5906
+ const workerCanvasApi = (0, import_react33.useMemo)(() => {
5834
5907
  if (!(spectrogram == null ? void 0 : spectrogram.spectrogramWorkerApi)) return void 0;
5835
5908
  return {
5836
5909
  registerCanvas: spectrogram.spectrogramWorkerApi.registerCanvas.bind(
@@ -5841,11 +5914,11 @@ var PlaylistVisualization = ({
5841
5914
  )
5842
5915
  };
5843
5916
  }, [spectrogram == null ? void 0 : spectrogram.spectrogramWorkerApi]);
5844
- const [settingsModalTrackId, setSettingsModalTrackId] = (0, import_react32.useState)(null);
5845
- const [isSelecting, setIsSelecting] = (0, import_react32.useState)(false);
5846
- const mouseDownTimeRef = (0, import_react32.useRef)(0);
5847
- const scrollContainerRef = (0, import_react32.useRef)(null);
5848
- const handleScrollContainerRef = (0, import_react32.useCallback)(
5917
+ const [settingsModalTrackId, setSettingsModalTrackId] = (0, import_react33.useState)(null);
5918
+ const [isSelecting, setIsSelecting] = (0, import_react33.useState)(false);
5919
+ const mouseDownTimeRef = (0, import_react33.useRef)(0);
5920
+ const scrollContainerRef = (0, import_react33.useRef)(null);
5921
+ const handleScrollContainerRef = (0, import_react33.useCallback)(
5849
5922
  (element) => {
5850
5923
  scrollContainerRef.current = element;
5851
5924
  setScrollContainer(element);
@@ -5883,7 +5956,7 @@ var PlaylistVisualization = ({
5883
5956
  );
5884
5957
  }
5885
5958
  });
5886
- const selectTrack = (0, import_react32.useCallback)(
5959
+ const selectTrack = (0, import_react33.useCallback)(
5887
5960
  (trackIndex) => {
5888
5961
  if (trackIndex >= 0 && trackIndex < tracks.length) {
5889
5962
  const track = tracks[trackIndex];
@@ -6324,7 +6397,7 @@ var PlaylistVisualization = ({
6324
6397
  };
6325
6398
 
6326
6399
  // src/components/PlaylistAnnotationList.tsx
6327
- var import_react33 = require("react");
6400
+ var import_react34 = require("react");
6328
6401
  var import_jsx_runtime11 = require("react/jsx-runtime");
6329
6402
  var PlaylistAnnotationList = ({
6330
6403
  height,
@@ -6339,7 +6412,7 @@ var PlaylistAnnotationList = ({
6339
6412
  const integration = useAnnotationIntegration();
6340
6413
  const { setAnnotations } = usePlaylistControls();
6341
6414
  const resolvedConfig = annotationListConfig != null ? annotationListConfig : { linkEndpoints, continuousPlay };
6342
- const handleAnnotationUpdate = (0, import_react33.useCallback)(
6415
+ const handleAnnotationUpdate = (0, import_react34.useCallback)(
6343
6416
  (updatedAnnotations) => {
6344
6417
  setAnnotations(updatedAnnotations);
6345
6418
  onAnnotationUpdate == null ? void 0 : onAnnotationUpdate(updatedAnnotations);
@@ -6423,8 +6496,8 @@ var Waveform = ({
6423
6496
  };
6424
6497
 
6425
6498
  // src/components/MediaElementPlaylist.tsx
6426
- var import_react36 = require("react");
6427
- var import_react37 = require("@dnd-kit/react");
6499
+ var import_react37 = require("react");
6500
+ var import_react38 = require("@dnd-kit/react");
6428
6501
  var import_modifiers = require("@dnd-kit/abstract/modifiers");
6429
6502
  var import_ui_components11 = require("@waveform-playlist/ui-components");
6430
6503
 
@@ -6448,7 +6521,7 @@ var noDropAnimationPlugins = (defaults) => {
6448
6521
  };
6449
6522
 
6450
6523
  // src/components/AnimatedMediaElementPlayhead.tsx
6451
- var import_react34 = require("react");
6524
+ var import_react35 = require("react");
6452
6525
  var import_styled_components7 = __toESM(require("styled-components"));
6453
6526
  var import_jsx_runtime13 = require("react/jsx-runtime");
6454
6527
  var PlayheadLine2 = import_styled_components7.default.div`
@@ -6465,11 +6538,11 @@ var PlayheadLine2 = import_styled_components7.default.div`
6465
6538
  var AnimatedMediaElementPlayhead = ({
6466
6539
  color = "#ff0000"
6467
6540
  }) => {
6468
- const playheadRef = (0, import_react34.useRef)(null);
6469
- const animationFrameRef = (0, import_react34.useRef)(null);
6541
+ const playheadRef = (0, import_react35.useRef)(null);
6542
+ const animationFrameRef = (0, import_react35.useRef)(null);
6470
6543
  const { isPlaying, currentTimeRef } = useMediaElementAnimation();
6471
6544
  const { samplesPerPixel, sampleRate, progressBarWidth } = useMediaElementData();
6472
- (0, import_react34.useEffect)(() => {
6545
+ (0, import_react35.useEffect)(() => {
6473
6546
  const updatePosition = () => {
6474
6547
  var _a;
6475
6548
  if (playheadRef.current) {
@@ -6493,7 +6566,7 @@ var AnimatedMediaElementPlayhead = ({
6493
6566
  }
6494
6567
  };
6495
6568
  }, [isPlaying, sampleRate, samplesPerPixel, currentTimeRef]);
6496
- (0, import_react34.useEffect)(() => {
6569
+ (0, import_react35.useEffect)(() => {
6497
6570
  var _a;
6498
6571
  if (!isPlaying && playheadRef.current) {
6499
6572
  const time = (_a = currentTimeRef.current) != null ? _a : 0;
@@ -6505,7 +6578,7 @@ var AnimatedMediaElementPlayhead = ({
6505
6578
  };
6506
6579
 
6507
6580
  // src/components/ChannelWithMediaElementProgress.tsx
6508
- var import_react35 = require("react");
6581
+ var import_react36 = require("react");
6509
6582
  var import_styled_components8 = __toESM(require("styled-components"));
6510
6583
  var import_ui_components10 = require("@waveform-playlist/ui-components");
6511
6584
  var import_jsx_runtime14 = require("react/jsx-runtime");
@@ -6545,14 +6618,14 @@ var ChannelWithMediaElementProgress = (_a) => {
6545
6618
  "clipStartSample",
6546
6619
  "clipDurationSamples"
6547
6620
  ]);
6548
- const progressRef = (0, import_react35.useRef)(null);
6549
- const animationFrameRef = (0, import_react35.useRef)(null);
6621
+ const progressRef = (0, import_react36.useRef)(null);
6622
+ const animationFrameRef = (0, import_react36.useRef)(null);
6550
6623
  const theme = (0, import_ui_components10.useTheme)();
6551
6624
  const { waveHeight } = (0, import_ui_components10.usePlaylistInfo)();
6552
6625
  const { isPlaying, currentTimeRef } = useMediaElementAnimation();
6553
6626
  const { samplesPerPixel, sampleRate } = useMediaElementData();
6554
6627
  const progressColor = (theme == null ? void 0 : theme.waveProgressColor) || "rgba(0, 0, 0, 0.1)";
6555
- (0, import_react35.useEffect)(() => {
6628
+ (0, import_react36.useEffect)(() => {
6556
6629
  const updateProgress = () => {
6557
6630
  var _a2;
6558
6631
  if (progressRef.current) {
@@ -6594,7 +6667,7 @@ var ChannelWithMediaElementProgress = (_a) => {
6594
6667
  smartChannelProps.length,
6595
6668
  currentTimeRef
6596
6669
  ]);
6597
- (0, import_react35.useEffect)(() => {
6670
+ (0, import_react36.useEffect)(() => {
6598
6671
  var _a2;
6599
6672
  if (!isPlaying && progressRef.current) {
6600
6673
  const currentTime = (_a2 = currentTimeRef.current) != null ? _a2 : 0;
@@ -6673,7 +6746,7 @@ var MediaElementPlaylist = ({
6673
6746
  const theme = (0, import_ui_components11.useTheme)();
6674
6747
  const { isPlaying } = useMediaElementAnimation();
6675
6748
  const { annotations, activeAnnotationId } = useMediaElementState();
6676
- const annotationIntegration = (0, import_react36.useContext)(AnnotationIntegrationContext);
6749
+ const annotationIntegration = (0, import_react37.useContext)(AnnotationIntegrationContext);
6677
6750
  const { play, seekTo, setActiveAnnotationId, setAnnotations, setScrollContainer } = useMediaElementControls();
6678
6751
  const {
6679
6752
  duration,
@@ -6689,11 +6762,11 @@ var MediaElementPlaylist = ({
6689
6762
  fadeIn,
6690
6763
  fadeOut
6691
6764
  } = useMediaElementData();
6692
- const [selectionStart, setSelectionStart] = (0, import_react36.useState)(0);
6693
- const [selectionEnd, setSelectionEnd] = (0, import_react36.useState)(0);
6694
- const [isSelecting, setIsSelecting] = (0, import_react36.useState)(false);
6695
- const scrollContainerRef = (0, import_react36.useRef)(null);
6696
- const handleScrollContainerRef = (0, import_react36.useCallback)(
6765
+ const [selectionStart, setSelectionStart] = (0, import_react37.useState)(0);
6766
+ const [selectionEnd, setSelectionEnd] = (0, import_react37.useState)(0);
6767
+ const [isSelecting, setIsSelecting] = (0, import_react37.useState)(false);
6768
+ const scrollContainerRef = (0, import_react37.useRef)(null);
6769
+ const handleScrollContainerRef = (0, import_react37.useCallback)(
6697
6770
  (el) => {
6698
6771
  scrollContainerRef.current = el;
6699
6772
  setScrollContainer(el);
@@ -6701,7 +6774,7 @@ var MediaElementPlaylist = ({
6701
6774
  [setScrollContainer]
6702
6775
  );
6703
6776
  const tracksFullWidth = Math.floor(duration * sampleRate / samplesPerPixel);
6704
- const handleAnnotationClick = (0, import_react36.useCallback)(
6777
+ const handleAnnotationClick = (0, import_react37.useCallback)(
6705
6778
  (annotation) => __async(null, null, function* () {
6706
6779
  setActiveAnnotationId(annotation.id);
6707
6780
  try {
@@ -6716,7 +6789,7 @@ var MediaElementPlaylist = ({
6716
6789
  }),
6717
6790
  [setActiveAnnotationId, play]
6718
6791
  );
6719
- const handleAnnotationUpdate = (0, import_react36.useCallback)(
6792
+ const handleAnnotationUpdate = (0, import_react37.useCallback)(
6720
6793
  (updatedAnnotations) => {
6721
6794
  setAnnotations(updatedAnnotations);
6722
6795
  onAnnotationUpdate == null ? void 0 : onAnnotationUpdate(updatedAnnotations);
@@ -6730,8 +6803,8 @@ var MediaElementPlaylist = ({
6730
6803
  duration,
6731
6804
  linkEndpoints: linkEndpointsProp
6732
6805
  });
6733
- const mouseDownTimeRef = (0, import_react36.useRef)(0);
6734
- const handleMouseDown = (0, import_react36.useCallback)(
6806
+ const mouseDownTimeRef = (0, import_react37.useRef)(0);
6807
+ const handleMouseDown = (0, import_react37.useCallback)(
6735
6808
  (e) => {
6736
6809
  const rect = e.currentTarget.getBoundingClientRect();
6737
6810
  const x = e.clientX - rect.left;
@@ -6743,7 +6816,7 @@ var MediaElementPlaylist = ({
6743
6816
  },
6744
6817
  [samplesPerPixel, sampleRate]
6745
6818
  );
6746
- const handleMouseMove = (0, import_react36.useCallback)(
6819
+ const handleMouseMove = (0, import_react37.useCallback)(
6747
6820
  (e) => {
6748
6821
  if (!isSelecting) return;
6749
6822
  const rect = e.currentTarget.getBoundingClientRect();
@@ -6753,7 +6826,7 @@ var MediaElementPlaylist = ({
6753
6826
  },
6754
6827
  [isSelecting, samplesPerPixel, sampleRate]
6755
6828
  );
6756
- const handleMouseUp = (0, import_react36.useCallback)(
6829
+ const handleMouseUp = (0, import_react37.useCallback)(
6757
6830
  (e) => {
6758
6831
  if (!isSelecting) return;
6759
6832
  setIsSelecting(false);
@@ -6885,7 +6958,7 @@ var MediaElementPlaylist = ({
6885
6958
  );
6886
6959
  }),
6887
6960
  annotations.length > 0 && annotationIntegration && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
6888
- import_react37.DragDropProvider,
6961
+ import_react38.DragDropProvider,
6889
6962
  {
6890
6963
  onDragStart,
6891
6964
  onDragMove,
@@ -6939,7 +7012,7 @@ var MediaElementPlaylist = ({
6939
7012
  };
6940
7013
 
6941
7014
  // src/components/MediaElementAnnotationList.tsx
6942
- var import_react38 = require("react");
7015
+ var import_react39 = require("react");
6943
7016
  var import_jsx_runtime16 = require("react/jsx-runtime");
6944
7017
  var MediaElementAnnotationList = ({
6945
7018
  height,
@@ -6955,7 +7028,7 @@ var MediaElementAnnotationList = ({
6955
7028
  const integration = useAnnotationIntegration();
6956
7029
  const { setAnnotations } = useMediaElementControls();
6957
7030
  const resolvedConfig = annotationListConfig != null ? annotationListConfig : { linkEndpoints: false, continuousPlay };
6958
- const handleAnnotationUpdate = (0, import_react38.useCallback)(
7031
+ const handleAnnotationUpdate = (0, import_react39.useCallback)(
6959
7032
  (updatedAnnotations) => {
6960
7033
  setAnnotations(updatedAnnotations);
6961
7034
  onAnnotationUpdate == null ? void 0 : onAnnotationUpdate(updatedAnnotations);
@@ -7030,6 +7103,7 @@ var KeyboardShortcuts = ({
7030
7103
  playback = false,
7031
7104
  clipSplitting = false,
7032
7105
  annotations = false,
7106
+ undo: undoEnabled = false,
7033
7107
  additionalShortcuts = []
7034
7108
  }) => {
7035
7109
  const { tracks, samplesPerPixel, playoutRef, duration } = usePlaylistData();
@@ -7039,7 +7113,7 @@ var KeyboardShortcuts = ({
7039
7113
  activeAnnotationId,
7040
7114
  continuousPlay
7041
7115
  } = usePlaylistState();
7042
- const { setAnnotations, setActiveAnnotationId, scrollContainerRef, play } = usePlaylistControls();
7116
+ const { setAnnotations, setActiveAnnotationId, scrollContainerRef, play, undo, redo } = usePlaylistControls();
7043
7117
  const { splitClipAtPlayhead } = useClipSplitting({
7044
7118
  tracks,
7045
7119
  samplesPerPixel,
@@ -7054,6 +7128,14 @@ var KeyboardShortcuts = ({
7054
7128
  preventDefault: true
7055
7129
  });
7056
7130
  }
7131
+ if (undoEnabled) {
7132
+ allAdditional.push(
7133
+ { key: "z", ctrlKey: true, shiftKey: false, action: undo, description: "Undo" },
7134
+ { key: "z", metaKey: true, shiftKey: false, action: undo, description: "Undo" },
7135
+ { key: "z", ctrlKey: true, shiftKey: true, action: redo, description: "Redo" },
7136
+ { key: "z", metaKey: true, shiftKey: true, action: redo, description: "Redo" }
7137
+ );
7138
+ }
7057
7139
  if (additionalShortcuts.length > 0) {
7058
7140
  allAdditional.push(...additionalShortcuts);
7059
7141
  }
@@ -7076,10 +7158,10 @@ var KeyboardShortcuts = ({
7076
7158
  };
7077
7159
 
7078
7160
  // src/components/ClipInteractionProvider.tsx
7079
- var import_react39 = __toESM(require("react"));
7080
- var import_react40 = require("@dnd-kit/react");
7161
+ var import_react40 = __toESM(require("react"));
7162
+ var import_react41 = require("@dnd-kit/react");
7081
7163
  var import_modifiers2 = require("@dnd-kit/abstract/modifiers");
7082
- var import_core6 = require("@waveform-playlist/core");
7164
+ var import_core8 = require("@waveform-playlist/core");
7083
7165
  var import_ui_components12 = require("@waveform-playlist/ui-components");
7084
7166
 
7085
7167
  // src/modifiers/ClipCollisionModifier.ts
@@ -7108,7 +7190,7 @@ var ClipCollisionModifier = _ClipCollisionModifier;
7108
7190
 
7109
7191
  // src/modifiers/SnapToGridModifier.ts
7110
7192
  var import_abstract2 = require("@dnd-kit/abstract");
7111
- var import_core5 = require("@waveform-playlist/core");
7193
+ var import_core7 = require("@waveform-playlist/core");
7112
7194
  var _SnapToGridModifier = class _SnapToGridModifier extends import_abstract2.Modifier {
7113
7195
  apply(operation) {
7114
7196
  const { transform, source } = operation;
@@ -7129,18 +7211,18 @@ var _SnapToGridModifier = class _SnapToGridModifier extends import_abstract2.Mod
7129
7211
  }
7130
7212
  const { snapTo, bpm, timeSignature, sampleRate } = this.options;
7131
7213
  if (snapTo === "off") return transform;
7132
- const gridTicks = snapTo === "bar" ? (0, import_core5.ticksPerBar)(timeSignature) : (0, import_core5.ticksPerBeat)(timeSignature);
7214
+ const gridTicks = snapTo === "bar" ? (0, import_core7.ticksPerBar)(timeSignature) : (0, import_core7.ticksPerBeat)(timeSignature);
7133
7215
  if (startSample !== void 0) {
7134
7216
  const proposedSamples = startSample + transform.x * samplesPerPixel;
7135
- const proposedTicks = (0, import_core5.samplesToTicks)(proposedSamples, bpm, sampleRate);
7136
- const snappedTicks2 = (0, import_core5.snapToGrid)(proposedTicks, gridTicks);
7137
- const snappedSamples2 = (0, import_core5.ticksToSamples)(snappedTicks2, bpm, sampleRate);
7217
+ const proposedTicks = (0, import_core7.samplesToTicks)(proposedSamples, bpm, sampleRate);
7218
+ const snappedTicks2 = (0, import_core7.snapToGrid)(proposedTicks, gridTicks);
7219
+ const snappedSamples2 = (0, import_core7.ticksToSamples)(snappedTicks2, bpm, sampleRate);
7138
7220
  return { x: (snappedSamples2 - startSample) / samplesPerPixel, y: 0 };
7139
7221
  }
7140
7222
  const deltaSamples = transform.x * samplesPerPixel;
7141
- const deltaTicks = (0, import_core5.samplesToTicks)(deltaSamples, bpm, sampleRate);
7142
- const snappedTicks = (0, import_core5.snapToGrid)(deltaTicks, gridTicks);
7143
- const snappedSamples = (0, import_core5.ticksToSamples)(snappedTicks, bpm, sampleRate);
7223
+ const deltaTicks = (0, import_core7.samplesToTicks)(deltaSamples, bpm, sampleRate);
7224
+ const snappedTicks = (0, import_core7.snapToGrid)(deltaTicks, gridTicks);
7225
+ const snappedSamples = (0, import_core7.ticksToSamples)(snappedTicks, bpm, sampleRate);
7144
7226
  return { x: snappedSamples / samplesPerPixel, y: 0 };
7145
7227
  }
7146
7228
  };
@@ -7161,21 +7243,21 @@ var ClipInteractionProvider = ({
7161
7243
  const beatsAndBars = (0, import_ui_components12.useBeatsAndBars)();
7162
7244
  const useBeatsSnap = snap && beatsAndBars != null && beatsAndBars.scaleMode === "beats" && beatsAndBars.snapTo !== "off";
7163
7245
  const useTimescaleSnap = snap && !useBeatsSnap;
7164
- (0, import_react39.useEffect)(() => {
7246
+ (0, import_react40.useEffect)(() => {
7165
7247
  if (onTracksChange == null) {
7166
7248
  console.warn(
7167
7249
  "[waveform-playlist] ClipInteractionProvider: onTracksChange is not set on WaveformPlaylistProvider. Drag and trim edits will not be persisted."
7168
7250
  );
7169
7251
  }
7170
7252
  }, [onTracksChange]);
7171
- const snapSamplePosition = (0, import_react39.useMemo)(() => {
7253
+ const snapSamplePosition = (0, import_react40.useMemo)(() => {
7172
7254
  if (useBeatsSnap && beatsAndBars) {
7173
7255
  const { bpm, timeSignature, snapTo } = beatsAndBars;
7174
- const gridTicks = snapTo === "bar" ? (0, import_core6.ticksPerBar)(timeSignature) : (0, import_core6.ticksPerBeat)(timeSignature);
7256
+ const gridTicks = snapTo === "bar" ? (0, import_core8.ticksPerBar)(timeSignature) : (0, import_core8.ticksPerBeat)(timeSignature);
7175
7257
  return (samplePos) => {
7176
- const ticks = (0, import_core6.samplesToTicks)(samplePos, bpm, sampleRate);
7177
- const snapped = (0, import_core6.snapToGrid)(ticks, gridTicks);
7178
- return (0, import_core6.ticksToSamples)(snapped, bpm, sampleRate);
7258
+ const ticks = (0, import_core8.samplesToTicks)(samplePos, bpm, sampleRate);
7259
+ const snapped = (0, import_core8.snapToGrid)(ticks, gridTicks);
7260
+ return (0, import_core8.ticksToSamples)(snapped, bpm, sampleRate);
7179
7261
  };
7180
7262
  }
7181
7263
  if (useTimescaleSnap) {
@@ -7197,7 +7279,7 @@ var ClipInteractionProvider = ({
7197
7279
  isDraggingRef,
7198
7280
  snapSamplePosition
7199
7281
  });
7200
- const onDragStart = import_react39.default.useCallback(
7282
+ const onDragStart = import_react40.default.useCallback(
7201
7283
  (event) => {
7202
7284
  var _a, _b, _c;
7203
7285
  const trackIndex = (_c = (_b = (_a = event.operation) == null ? void 0 : _a.source) == null ? void 0 : _b.data) == null ? void 0 : _c.trackIndex;
@@ -7208,7 +7290,7 @@ var ClipInteractionProvider = ({
7208
7290
  },
7209
7291
  [handleDragStart, tracks, setSelectedTrackId]
7210
7292
  );
7211
- const modifiers = (0, import_react39.useMemo)(() => {
7293
+ const modifiers = (0, import_react40.useMemo)(() => {
7212
7294
  const mods = [import_modifiers2.RestrictToHorizontalAxis];
7213
7295
  if (useBeatsSnap && beatsAndBars) {
7214
7296
  mods.push(
@@ -7234,7 +7316,7 @@ var ClipInteractionProvider = ({
7234
7316
  return mods;
7235
7317
  }, [useBeatsSnap, useTimescaleSnap, beatsAndBars, tracks, samplesPerPixel, sampleRate]);
7236
7318
  return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ClipInteractionContextProvider, { value: true, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
7237
- import_react40.DragDropProvider,
7319
+ import_react41.DragDropProvider,
7238
7320
  {
7239
7321
  sensors,
7240
7322
  onDragStart,