@waveform-playlist/browser 11.0.1 → 11.1.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");
@@ -542,12 +542,52 @@ function useSelectedTrack({ engineRef }) {
542
542
  };
543
543
  }
544
544
 
545
- // src/hooks/useAudioEffects.ts
545
+ // src/hooks/useUndoState.ts
546
546
  var import_react7 = require("react");
547
+ function useUndoState({ engineRef }) {
548
+ const [canUndo, setCanUndo] = (0, import_react7.useState)(false);
549
+ const [canRedo, setCanRedo] = (0, import_react7.useState)(false);
550
+ const canUndoRef = (0, import_react7.useRef)(false);
551
+ const canRedoRef = (0, import_react7.useRef)(false);
552
+ const undo = (0, import_react7.useCallback)(() => {
553
+ if (!engineRef.current) {
554
+ console.warn("[waveform-playlist] undo: engine not ready, call ignored");
555
+ return;
556
+ }
557
+ engineRef.current.undo();
558
+ }, [engineRef]);
559
+ const redo = (0, import_react7.useCallback)(() => {
560
+ if (!engineRef.current) {
561
+ console.warn("[waveform-playlist] redo: engine not ready, call ignored");
562
+ return;
563
+ }
564
+ engineRef.current.redo();
565
+ }, [engineRef]);
566
+ const onEngineState = (0, import_react7.useCallback)((state) => {
567
+ if (state.canUndo !== canUndoRef.current) {
568
+ canUndoRef.current = state.canUndo;
569
+ setCanUndo(state.canUndo);
570
+ }
571
+ if (state.canRedo !== canRedoRef.current) {
572
+ canRedoRef.current = state.canRedo;
573
+ setCanRedo(state.canRedo);
574
+ }
575
+ }, []);
576
+ return {
577
+ canUndo,
578
+ canRedo,
579
+ undo,
580
+ redo,
581
+ onEngineState
582
+ };
583
+ }
584
+
585
+ // src/hooks/useAudioEffects.ts
586
+ var import_react8 = require("react");
547
587
  var import_tone = require("tone");
548
588
  var useMasterAnalyser = (fftSize = 256) => {
549
- const analyserRef = (0, import_react7.useRef)(null);
550
- const masterEffects = (0, import_react7.useCallback)(
589
+ const analyserRef = (0, import_react8.useRef)(null);
590
+ const masterEffects = (0, import_react8.useCallback)(
551
591
  (masterGainNode, destination, _isOffline) => {
552
592
  const analyserNode = new import_tone.Analyser("fft", fftSize);
553
593
  masterGainNode.connect(analyserNode);
@@ -564,7 +604,7 @@ var useMasterAnalyser = (fftSize = 256) => {
564
604
  };
565
605
 
566
606
  // src/hooks/useAudioTracks.ts
567
- var import_react8 = require("react");
607
+ var import_react9 = require("react");
568
608
  var import_core = require("@waveform-playlist/core");
569
609
  var Tone = __toESM(require("tone"));
570
610
  function buildTrackFromConfig(config, index, audioBuffer, stableIds, contextSampleRate = 48e3) {
@@ -622,14 +662,14 @@ function buildTrackFromConfig(config, index, audioBuffer, stableIds, contextSamp
622
662
  function useAudioTracks(configs, options = {}) {
623
663
  const { immediate = false, progressive = false } = options;
624
664
  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);
665
+ const [loading, setLoading] = (0, import_react9.useState)(true);
666
+ const [error, setError] = (0, import_react9.useState)(null);
667
+ const [loadedCount, setLoadedCount] = (0, import_react9.useState)(0);
628
668
  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)(() => {
669
+ const [loadedBuffers, setLoadedBuffers] = (0, import_react9.useState)(/* @__PURE__ */ new Map());
670
+ const stableIdsRef = (0, import_react9.useRef)(/* @__PURE__ */ new Map());
671
+ const contextSampleRateRef = (0, import_react9.useRef)(48e3);
672
+ const derivedTracks = (0, import_react9.useMemo)(() => {
633
673
  if (!isImmediate) return null;
634
674
  const result = [];
635
675
  for (let i = 0; i < configs.length; i++) {
@@ -644,13 +684,13 @@ function useAudioTracks(configs, options = {}) {
644
684
  }
645
685
  return result;
646
686
  }, [isImmediate, configs, loadedBuffers]);
647
- const [tracks, setTracks] = (0, import_react8.useState)(derivedTracks != null ? derivedTracks : []);
648
- const prevDerivedRef = (0, import_react8.useRef)(derivedTracks);
687
+ const [tracks, setTracks] = (0, import_react9.useState)(derivedTracks != null ? derivedTracks : []);
688
+ const prevDerivedRef = (0, import_react9.useRef)(derivedTracks);
649
689
  if (derivedTracks !== prevDerivedRef.current) {
650
690
  prevDerivedRef.current = derivedTracks;
651
691
  if (derivedTracks) setTracks(derivedTracks);
652
692
  }
653
- (0, import_react8.useEffect)(() => {
693
+ (0, import_react9.useEffect)(() => {
654
694
  if (configs.length === 0) {
655
695
  setTracks([]);
656
696
  setLoading(false);
@@ -758,7 +798,7 @@ function useAudioTracks(configs, options = {}) {
758
798
  }
759
799
 
760
800
  // src/hooks/useClipDragHandlers.ts
761
- var import_react9 = __toESM(require("react"));
801
+ var import_react10 = __toESM(require("react"));
762
802
 
763
803
  // src/utils/boundaryTrim.ts
764
804
  var import_engine = require("@waveform-playlist/engine");
@@ -812,17 +852,24 @@ function useClipDragHandlers({
812
852
  snapSamplePosition
813
853
  }) {
814
854
  const { sampleRate } = usePlaylistData();
815
- const snapSamplePositionRef = import_react9.default.useRef(snapSamplePosition);
855
+ const snapSamplePositionRef = import_react10.default.useRef(snapSamplePosition);
816
856
  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(
857
+ const originalClipStateRef = import_react10.default.useRef(null);
858
+ const lastBoundaryDeltaRef = import_react10.default.useRef(0);
859
+ const onDragStart = import_react10.default.useCallback(
820
860
  (event) => {
821
861
  var _a;
822
862
  const data = (_a = event.operation.source) == null ? void 0 : _a.data;
823
863
  if (!data) return;
824
864
  if (!data.boundary) {
825
865
  originalClipStateRef.current = null;
866
+ if (engineRef.current) {
867
+ engineRef.current.beginTransaction();
868
+ } else {
869
+ console.warn(
870
+ "[waveform-playlist] onDragStart: engine not ready, move will not be grouped for undo"
871
+ );
872
+ }
826
873
  return;
827
874
  }
828
875
  const track = tracks[data.trackIndex];
@@ -834,11 +881,18 @@ function useClipDragHandlers({
834
881
  startSample: clip.startSample
835
882
  };
836
883
  isDraggingRef.current = true;
884
+ if (engineRef.current) {
885
+ engineRef.current.beginTransaction();
886
+ } else {
887
+ console.warn(
888
+ "[waveform-playlist] onDragStart: engine not ready, trim will not be grouped for undo"
889
+ );
890
+ }
837
891
  }
838
892
  },
839
- [tracks, isDraggingRef]
893
+ [tracks, isDraggingRef, engineRef]
840
894
  );
841
- const onDragMove = import_react9.default.useCallback(
895
+ const onDragMove = import_react10.default.useCallback(
842
896
  (event) => {
843
897
  var _a, _b, _c;
844
898
  const data = (_a = event.operation.source) == null ? void 0 : _a.data;
@@ -900,9 +954,9 @@ function useClipDragHandlers({
900
954
  },
901
955
  [tracks, onTracksChange, samplesPerPixel, sampleRate]
902
956
  );
903
- const onDragEnd = import_react9.default.useCallback(
957
+ const onDragEnd = import_react10.default.useCallback(
904
958
  (event) => {
905
- var _a, _b, _c;
959
+ var _a, _b, _c, _d, _e, _f, _g;
906
960
  if (event.canceled) {
907
961
  if (originalClipStateRef.current) {
908
962
  const cancelData = (_a = event.operation.source) == null ? void 0 : _a.data;
@@ -927,23 +981,30 @@ function useClipDragHandlers({
927
981
  isDraggingRef.current = false;
928
982
  originalClipStateRef.current = null;
929
983
  lastBoundaryDeltaRef.current = 0;
984
+ (_b = engineRef.current) == null ? void 0 : _b.abortTransaction();
985
+ return;
986
+ }
987
+ const data = (_c = event.operation.source) == null ? void 0 : _c.data;
988
+ if (!data) {
989
+ isDraggingRef.current = false;
990
+ (_d = engineRef.current) == null ? void 0 : _d.abortTransaction();
930
991
  return;
931
992
  }
932
- const data = (_b = event.operation.source) == null ? void 0 : _b.data;
933
- if (!data) return;
934
993
  const { trackIndex, clipId, boundary } = data;
935
994
  const sampleDelta = boundary ? lastBoundaryDeltaRef.current : event.operation.transform.x * samplesPerPixel;
936
- const trackId = (_c = tracks[trackIndex]) == null ? void 0 : _c.id;
995
+ const trackId = (_e = tracks[trackIndex]) == null ? void 0 : _e.id;
937
996
  if (boundary) {
938
997
  isDraggingRef.current = false;
939
998
  if (!trackId) {
940
999
  console.warn(
941
1000
  `[waveform-playlist] onDragEnd: track at index ${trackIndex} not found \u2014 trim not synced to adapter`
942
1001
  );
1002
+ (_f = engineRef.current) == null ? void 0 : _f.abortTransaction();
943
1003
  } else if (!engineRef.current) {
944
1004
  console.warn("[waveform-playlist] engineRef is null \u2014 trim not synced to adapter");
945
1005
  } else {
946
1006
  engineRef.current.trimClip(trackId, clipId, boundary, Math.floor(sampleDelta));
1007
+ engineRef.current.commitTransaction();
947
1008
  }
948
1009
  originalClipStateRef.current = null;
949
1010
  lastBoundaryDeltaRef.current = 0;
@@ -953,10 +1014,12 @@ function useClipDragHandlers({
953
1014
  console.warn(
954
1015
  `[waveform-playlist] onDragEnd: track at index ${trackIndex} not found \u2014 move not synced to adapter`
955
1016
  );
1017
+ (_g = engineRef.current) == null ? void 0 : _g.abortTransaction();
956
1018
  } else if (!engineRef.current) {
957
1019
  console.warn("[waveform-playlist] engineRef is null \u2014 move not synced to adapter");
958
1020
  } else {
959
1021
  engineRef.current.moveClip(trackId, clipId, Math.floor(sampleDelta));
1022
+ engineRef.current.commitTransaction();
960
1023
  }
961
1024
  },
962
1025
  [tracks, onTracksChange, samplesPerPixel, engineRef, isDraggingRef]
@@ -969,7 +1032,7 @@ function useClipDragHandlers({
969
1032
  }
970
1033
 
971
1034
  // src/hooks/useAnnotationDragHandlers.ts
972
- var import_react10 = __toESM(require("react"));
1035
+ var import_react11 = __toESM(require("react"));
973
1036
  var import_playout = require("@waveform-playlist/playout");
974
1037
  var LINK_THRESHOLD = 0.01;
975
1038
  function useAnnotationDragHandlers({
@@ -980,8 +1043,8 @@ function useAnnotationDragHandlers({
980
1043
  duration,
981
1044
  linkEndpoints
982
1045
  }) {
983
- const originalAnnotationStateRef = import_react10.default.useRef(null);
984
- const onDragStart = import_react10.default.useCallback(
1046
+ const originalAnnotationStateRef = import_react11.default.useRef(null);
1047
+ const onDragStart = import_react11.default.useCallback(
985
1048
  (event) => {
986
1049
  var _a;
987
1050
  const data = (_a = event.operation.source) == null ? void 0 : _a.data;
@@ -1000,7 +1063,7 @@ function useAnnotationDragHandlers({
1000
1063
  },
1001
1064
  [annotations]
1002
1065
  );
1003
- const onDragMove = import_react10.default.useCallback(
1066
+ const onDragMove = import_react11.default.useCallback(
1004
1067
  (event) => {
1005
1068
  var _a, _b, _c;
1006
1069
  if (!originalAnnotationStateRef.current) {
@@ -1026,7 +1089,7 @@ function useAnnotationDragHandlers({
1026
1089
  },
1027
1090
  [annotations, onAnnotationsChange, samplesPerPixel, sampleRate, duration, linkEndpoints]
1028
1091
  );
1029
- const onDragEnd = import_react10.default.useCallback(
1092
+ const onDragEnd = import_react11.default.useCallback(
1030
1093
  (event) => {
1031
1094
  if (event.canceled && originalAnnotationStateRef.current) {
1032
1095
  const { annotationIndex, start, end } = originalAnnotationStateRef.current;
@@ -1134,7 +1197,7 @@ function updateAnnotationBoundaries({
1134
1197
  }
1135
1198
 
1136
1199
  // src/hooks/useDragSensors.ts
1137
- var import_react11 = require("react");
1200
+ var import_react12 = require("react");
1138
1201
  var import_dom = require("@dnd-kit/dom");
1139
1202
  function useDragSensors(options = {}) {
1140
1203
  const {
@@ -1143,7 +1206,7 @@ function useDragSensors(options = {}) {
1143
1206
  touchTolerance = 5,
1144
1207
  mouseDistance = 1
1145
1208
  } = options;
1146
- return (0, import_react11.useMemo)(() => {
1209
+ return (0, import_react12.useMemo)(() => {
1147
1210
  if (touchOptimized) {
1148
1211
  return [
1149
1212
  import_dom.PointerSensor.configure({
@@ -1172,14 +1235,14 @@ function useDragSensors(options = {}) {
1172
1235
  }
1173
1236
 
1174
1237
  // src/hooks/useClipSplitting.ts
1175
- var import_react12 = require("react");
1238
+ var import_react13 = require("react");
1176
1239
  var import_engine2 = require("@waveform-playlist/engine");
1177
1240
  var useClipSplitting = (options) => {
1178
1241
  const { tracks, engineRef } = options;
1179
1242
  const { sampleRate } = usePlaylistData();
1180
1243
  const { currentTimeRef } = usePlaybackAnimation();
1181
1244
  const { selectedTrackId } = usePlaylistState();
1182
- const splitClipAt = (0, import_react12.useCallback)(
1245
+ const splitClipAt = (0, import_react13.useCallback)(
1183
1246
  (trackIndex, clipIndex, splitTime) => {
1184
1247
  const { samplesPerPixel } = options;
1185
1248
  const track = tracks[trackIndex];
@@ -1203,7 +1266,7 @@ var useClipSplitting = (options) => {
1203
1266
  },
1204
1267
  [tracks, options, engineRef, sampleRate]
1205
1268
  );
1206
- const splitClipAtPlayhead = (0, import_react12.useCallback)(() => {
1269
+ const splitClipAtPlayhead = (0, import_react13.useCallback)(() => {
1207
1270
  var _a;
1208
1271
  if (!selectedTrackId) {
1209
1272
  console.warn("[waveform-playlist] No track selected \u2014 click a clip to select a track first");
@@ -1234,36 +1297,16 @@ var useClipSplitting = (options) => {
1234
1297
  };
1235
1298
 
1236
1299
  // 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
- }
1300
+ var import_react14 = require("react");
1301
+ var import_core2 = require("@waveform-playlist/core");
1302
+ var import_core3 = require("@waveform-playlist/core");
1260
1303
  var useKeyboardShortcuts = (options) => {
1261
1304
  const { shortcuts, enabled = true } = options;
1262
- const handleKeyDown = (0, import_react13.useCallback)(
1263
- (event) => handleKeyboardEvent(event, shortcuts, enabled),
1305
+ const handleKeyDown = (0, import_react14.useCallback)(
1306
+ (event) => (0, import_core2.handleKeyboardEvent)(event, shortcuts, enabled),
1264
1307
  [shortcuts, enabled]
1265
1308
  );
1266
- (0, import_react13.useEffect)(() => {
1309
+ (0, import_react14.useEffect)(() => {
1267
1310
  if (!enabled) return;
1268
1311
  window.addEventListener("keydown", handleKeyDown);
1269
1312
  return () => {
@@ -1271,42 +1314,24 @@ var useKeyboardShortcuts = (options) => {
1271
1314
  };
1272
1315
  }, [handleKeyDown, enabled]);
1273
1316
  };
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
1317
 
1293
1318
  // src/hooks/usePlaybackShortcuts.ts
1294
- var import_react14 = require("react");
1319
+ var import_react15 = require("react");
1295
1320
  var usePlaybackShortcuts = (options = {}) => {
1296
1321
  const { enabled = true, additionalShortcuts = [], shortcuts: overrideShortcuts } = options;
1297
1322
  const { isPlaying } = usePlaybackAnimation();
1298
1323
  const { setCurrentTime, play, pause, stop } = usePlaylistControls();
1299
- const togglePlayPause = (0, import_react14.useCallback)(() => {
1324
+ const togglePlayPause = (0, import_react15.useCallback)(() => {
1300
1325
  if (isPlaying) {
1301
1326
  pause();
1302
1327
  } else {
1303
1328
  play();
1304
1329
  }
1305
1330
  }, [isPlaying, play, pause]);
1306
- const stopPlayback = (0, import_react14.useCallback)(() => {
1331
+ const stopPlayback = (0, import_react15.useCallback)(() => {
1307
1332
  stop();
1308
1333
  }, [stop]);
1309
- const rewindToStart = (0, import_react14.useCallback)(() => {
1334
+ const rewindToStart = (0, import_react15.useCallback)(() => {
1310
1335
  setCurrentTime(0);
1311
1336
  if (isPlaying) {
1312
1337
  play(0);
@@ -1346,7 +1371,7 @@ var usePlaybackShortcuts = (options = {}) => {
1346
1371
  };
1347
1372
 
1348
1373
  // src/hooks/useAnnotationKeyboardControls.ts
1349
- var import_react15 = require("react");
1374
+ var import_react16 = require("react");
1350
1375
  var LINK_THRESHOLD2 = 0.01;
1351
1376
  var TIME_DELTA = 0.01;
1352
1377
  function useAnnotationKeyboardControls({
@@ -1362,11 +1387,11 @@ function useAnnotationKeyboardControls({
1362
1387
  onPlay
1363
1388
  }) {
1364
1389
  const { samplesPerPixel, sampleRate } = usePlaylistData();
1365
- const activeIndex = (0, import_react15.useMemo)(() => {
1390
+ const activeIndex = (0, import_react16.useMemo)(() => {
1366
1391
  if (!activeAnnotationId) return -1;
1367
1392
  return annotations.findIndex((a) => a.id === activeAnnotationId);
1368
1393
  }, [annotations, activeAnnotationId]);
1369
- const scrollToAnnotation = (0, import_react15.useCallback)(
1394
+ const scrollToAnnotation = (0, import_react16.useCallback)(
1370
1395
  (annotationId) => {
1371
1396
  if (!(scrollContainerRef == null ? void 0 : scrollContainerRef.current) || !samplesPerPixel || !sampleRate) return;
1372
1397
  const annotation = annotations.find((a) => a.id === annotationId);
@@ -1389,12 +1414,12 @@ function useAnnotationKeyboardControls({
1389
1414
  },
1390
1415
  [annotations, scrollContainerRef, samplesPerPixel, sampleRate]
1391
1416
  );
1392
- (0, import_react15.useEffect)(() => {
1417
+ (0, import_react16.useEffect)(() => {
1393
1418
  if (activeAnnotationId && (scrollContainerRef == null ? void 0 : scrollContainerRef.current) && samplesPerPixel && sampleRate) {
1394
1419
  scrollToAnnotation(activeAnnotationId);
1395
1420
  }
1396
1421
  }, [activeAnnotationId, scrollToAnnotation, scrollContainerRef, samplesPerPixel, sampleRate]);
1397
- const moveStartBoundary = (0, import_react15.useCallback)(
1422
+ const moveStartBoundary = (0, import_react16.useCallback)(
1398
1423
  (delta) => {
1399
1424
  if (activeIndex < 0) return;
1400
1425
  const annotation = annotations[activeIndex];
@@ -1423,7 +1448,7 @@ function useAnnotationKeyboardControls({
1423
1448
  },
1424
1449
  [annotations, activeIndex, linkEndpoints, onAnnotationsChange]
1425
1450
  );
1426
- const moveEndBoundary = (0, import_react15.useCallback)(
1451
+ const moveEndBoundary = (0, import_react16.useCallback)(
1427
1452
  (delta) => {
1428
1453
  if (activeIndex < 0) return;
1429
1454
  const annotation = annotations[activeIndex];
@@ -1483,7 +1508,7 @@ function useAnnotationKeyboardControls({
1483
1508
  },
1484
1509
  [annotations, activeIndex, duration, linkEndpoints, onAnnotationsChange]
1485
1510
  );
1486
- const selectPrevious = (0, import_react15.useCallback)(() => {
1511
+ const selectPrevious = (0, import_react16.useCallback)(() => {
1487
1512
  if (!onActiveAnnotationChange || annotations.length === 0) return;
1488
1513
  if (activeIndex <= 0) {
1489
1514
  onActiveAnnotationChange(annotations[annotations.length - 1].id);
@@ -1491,7 +1516,7 @@ function useAnnotationKeyboardControls({
1491
1516
  onActiveAnnotationChange(annotations[activeIndex - 1].id);
1492
1517
  }
1493
1518
  }, [annotations, activeIndex, onActiveAnnotationChange]);
1494
- const selectNext = (0, import_react15.useCallback)(() => {
1519
+ const selectNext = (0, import_react16.useCallback)(() => {
1495
1520
  if (!onActiveAnnotationChange || annotations.length === 0) return;
1496
1521
  if (activeIndex < 0 || activeIndex >= annotations.length - 1) {
1497
1522
  onActiveAnnotationChange(annotations[0].id);
@@ -1499,25 +1524,25 @@ function useAnnotationKeyboardControls({
1499
1524
  onActiveAnnotationChange(annotations[activeIndex + 1].id);
1500
1525
  }
1501
1526
  }, [annotations, activeIndex, onActiveAnnotationChange]);
1502
- const selectFirst = (0, import_react15.useCallback)(() => {
1527
+ const selectFirst = (0, import_react16.useCallback)(() => {
1503
1528
  if (!onActiveAnnotationChange || annotations.length === 0) return;
1504
1529
  onActiveAnnotationChange(annotations[0].id);
1505
1530
  }, [annotations, onActiveAnnotationChange]);
1506
- const selectLast = (0, import_react15.useCallback)(() => {
1531
+ const selectLast = (0, import_react16.useCallback)(() => {
1507
1532
  if (!onActiveAnnotationChange || annotations.length === 0) return;
1508
1533
  onActiveAnnotationChange(annotations[annotations.length - 1].id);
1509
1534
  }, [annotations, onActiveAnnotationChange]);
1510
- const clearSelection = (0, import_react15.useCallback)(() => {
1535
+ const clearSelection = (0, import_react16.useCallback)(() => {
1511
1536
  if (!onActiveAnnotationChange) return;
1512
1537
  onActiveAnnotationChange(null);
1513
1538
  }, [onActiveAnnotationChange]);
1514
- const playActiveAnnotation = (0, import_react15.useCallback)(() => {
1539
+ const playActiveAnnotation = (0, import_react16.useCallback)(() => {
1515
1540
  if (activeIndex < 0 || !onPlay) return;
1516
1541
  const annotation = annotations[activeIndex];
1517
1542
  const playDuration = !continuousPlay ? annotation.end - annotation.start : void 0;
1518
1543
  onPlay(annotation.start, playDuration);
1519
1544
  }, [annotations, activeIndex, continuousPlay, onPlay]);
1520
- const activeAnnotationShortcuts = (0, import_react15.useMemo)(
1545
+ const activeAnnotationShortcuts = (0, import_react16.useMemo)(
1521
1546
  () => [
1522
1547
  {
1523
1548
  key: "[",
@@ -1554,7 +1579,7 @@ function useAnnotationKeyboardControls({
1554
1579
  ],
1555
1580
  [moveStartBoundary, moveEndBoundary, playActiveAnnotation]
1556
1581
  );
1557
- const navigationShortcuts = (0, import_react15.useMemo)(
1582
+ const navigationShortcuts = (0, import_react16.useMemo)(
1558
1583
  () => [
1559
1584
  {
1560
1585
  key: "ArrowUp",
@@ -1623,7 +1648,7 @@ function useAnnotationKeyboardControls({
1623
1648
  }
1624
1649
 
1625
1650
  // src/hooks/useDynamicEffects.ts
1626
- var import_react16 = require("react");
1651
+ var import_react17 = require("react");
1627
1652
 
1628
1653
  // src/effects/effectDefinitions.ts
1629
1654
  var effectDefinitions = [
@@ -2298,13 +2323,13 @@ function createEffectChain(effects) {
2298
2323
  // src/hooks/useDynamicEffects.ts
2299
2324
  var import_tone3 = require("tone");
2300
2325
  function useDynamicEffects(fftSize = 256) {
2301
- const [activeEffects, setActiveEffects] = (0, import_react16.useState)([]);
2302
- const activeEffectsRef = (0, import_react16.useRef)(activeEffects);
2326
+ const [activeEffects, setActiveEffects] = (0, import_react17.useState)([]);
2327
+ const activeEffectsRef = (0, import_react17.useRef)(activeEffects);
2303
2328
  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) => {
2329
+ const effectInstancesRef = (0, import_react17.useRef)(/* @__PURE__ */ new Map());
2330
+ const analyserRef = (0, import_react17.useRef)(null);
2331
+ const graphNodesRef = (0, import_react17.useRef)(null);
2332
+ const rebuildChain = (0, import_react17.useCallback)((effects) => {
2308
2333
  const nodes = graphNodesRef.current;
2309
2334
  if (!nodes) return;
2310
2335
  const { masterGainNode, destination, analyserNode } = nodes;
@@ -2332,7 +2357,7 @@ function useDynamicEffects(fftSize = 256) {
2332
2357
  analyserNode.connect(destination);
2333
2358
  }
2334
2359
  }, []);
2335
- const addEffect = (0, import_react16.useCallback)((effectId) => {
2360
+ const addEffect = (0, import_react17.useCallback)((effectId) => {
2336
2361
  const definition = getEffectDefinition(effectId);
2337
2362
  if (!definition) {
2338
2363
  console.error(`Unknown effect: ${effectId}`);
@@ -2353,7 +2378,7 @@ function useDynamicEffects(fftSize = 256) {
2353
2378
  };
2354
2379
  setActiveEffects((prev) => [...prev, newActiveEffect]);
2355
2380
  }, []);
2356
- const removeEffect = (0, import_react16.useCallback)((instanceId) => {
2381
+ const removeEffect = (0, import_react17.useCallback)((instanceId) => {
2357
2382
  const instance = effectInstancesRef.current.get(instanceId);
2358
2383
  if (instance) {
2359
2384
  instance.dispose();
@@ -2361,7 +2386,7 @@ function useDynamicEffects(fftSize = 256) {
2361
2386
  }
2362
2387
  setActiveEffects((prev) => prev.filter((e) => e.instanceId !== instanceId));
2363
2388
  }, []);
2364
- const updateParameter = (0, import_react16.useCallback)(
2389
+ const updateParameter = (0, import_react17.useCallback)(
2365
2390
  (instanceId, paramName, value) => {
2366
2391
  const instance = effectInstancesRef.current.get(instanceId);
2367
2392
  if (instance) {
@@ -2375,7 +2400,7 @@ function useDynamicEffects(fftSize = 256) {
2375
2400
  },
2376
2401
  []
2377
2402
  );
2378
- const toggleBypass = (0, import_react16.useCallback)((instanceId) => {
2403
+ const toggleBypass = (0, import_react17.useCallback)((instanceId) => {
2379
2404
  var _a;
2380
2405
  const effect = activeEffectsRef.current.find((e) => e.instanceId === instanceId);
2381
2406
  if (!effect) return;
@@ -2389,7 +2414,7 @@ function useDynamicEffects(fftSize = 256) {
2389
2414
  (prev) => prev.map((e) => e.instanceId === instanceId ? __spreadProps(__spreadValues({}, e), { bypassed: newBypassed }) : e)
2390
2415
  );
2391
2416
  }, []);
2392
- const reorderEffects = (0, import_react16.useCallback)((fromIndex, toIndex) => {
2417
+ const reorderEffects = (0, import_react17.useCallback)((fromIndex, toIndex) => {
2393
2418
  setActiveEffects((prev) => {
2394
2419
  const newEffects = [...prev];
2395
2420
  const [removed] = newEffects.splice(fromIndex, 1);
@@ -2397,15 +2422,15 @@ function useDynamicEffects(fftSize = 256) {
2397
2422
  return newEffects;
2398
2423
  });
2399
2424
  }, []);
2400
- const clearAllEffects = (0, import_react16.useCallback)(() => {
2425
+ const clearAllEffects = (0, import_react17.useCallback)(() => {
2401
2426
  effectInstancesRef.current.forEach((inst) => inst.dispose());
2402
2427
  effectInstancesRef.current.clear();
2403
2428
  setActiveEffects([]);
2404
2429
  }, []);
2405
- (0, import_react16.useEffect)(() => {
2430
+ (0, import_react17.useEffect)(() => {
2406
2431
  rebuildChain(activeEffects);
2407
2432
  }, [activeEffects, rebuildChain]);
2408
- const masterEffects = (0, import_react16.useCallback)(
2433
+ const masterEffects = (0, import_react17.useCallback)(
2409
2434
  (masterGainNode, destination, _isOffline) => {
2410
2435
  const analyserNode = new import_tone3.Analyser("fft", fftSize);
2411
2436
  analyserRef.current = analyserNode;
@@ -2437,14 +2462,14 @@ function useDynamicEffects(fftSize = 256) {
2437
2462
  [fftSize]
2438
2463
  // Only fftSize - reads effects from ref
2439
2464
  );
2440
- (0, import_react16.useEffect)(() => {
2465
+ (0, import_react17.useEffect)(() => {
2441
2466
  const effectInstances = effectInstancesRef.current;
2442
2467
  return () => {
2443
2468
  effectInstances.forEach((inst) => inst.dispose());
2444
2469
  effectInstances.clear();
2445
2470
  };
2446
2471
  }, []);
2447
- const createOfflineEffectsFunction = (0, import_react16.useCallback)(() => {
2472
+ const createOfflineEffectsFunction = (0, import_react17.useCallback)(() => {
2448
2473
  const nonBypassedEffects = activeEffects.filter((e) => !e.bypassed);
2449
2474
  if (nonBypassedEffects.length === 0) {
2450
2475
  return void 0;
@@ -2486,14 +2511,14 @@ function useDynamicEffects(fftSize = 256) {
2486
2511
  }
2487
2512
 
2488
2513
  // src/hooks/useTrackDynamicEffects.ts
2489
- var import_react17 = require("react");
2514
+ var import_react18 = require("react");
2490
2515
  function useTrackDynamicEffects() {
2491
- const [trackEffectsState, setTrackEffectsState] = (0, import_react17.useState)(
2516
+ const [trackEffectsState, setTrackEffectsState] = (0, import_react18.useState)(
2492
2517
  /* @__PURE__ */ new Map()
2493
2518
  );
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) => {
2519
+ const trackEffectInstancesRef = (0, import_react18.useRef)(/* @__PURE__ */ new Map());
2520
+ const trackGraphNodesRef = (0, import_react18.useRef)(/* @__PURE__ */ new Map());
2521
+ const rebuildTrackChain = (0, import_react18.useCallback)((trackId, trackEffects) => {
2497
2522
  const nodes = trackGraphNodesRef.current.get(trackId);
2498
2523
  if (!nodes) return;
2499
2524
  const { graphEnd, masterGainNode } = nodes;
@@ -2523,7 +2548,7 @@ function useTrackDynamicEffects() {
2523
2548
  currentNode.connect(masterGainNode);
2524
2549
  }
2525
2550
  }, []);
2526
- const addEffectToTrack = (0, import_react17.useCallback)((trackId, effectId) => {
2551
+ const addEffectToTrack = (0, import_react18.useCallback)((trackId, effectId) => {
2527
2552
  const definition = getEffectDefinition(effectId);
2528
2553
  if (!definition) {
2529
2554
  console.error(`Unknown effect: ${effectId}`);
@@ -2552,7 +2577,7 @@ function useTrackDynamicEffects() {
2552
2577
  return newState;
2553
2578
  });
2554
2579
  }, []);
2555
- const removeEffectFromTrack = (0, import_react17.useCallback)((trackId, instanceId) => {
2580
+ const removeEffectFromTrack = (0, import_react18.useCallback)((trackId, instanceId) => {
2556
2581
  const instancesMap = trackEffectInstancesRef.current.get(trackId);
2557
2582
  const instance = instancesMap == null ? void 0 : instancesMap.get(instanceId);
2558
2583
  if (instance) {
@@ -2569,7 +2594,7 @@ function useTrackDynamicEffects() {
2569
2594
  return newState;
2570
2595
  });
2571
2596
  }, []);
2572
- const updateTrackEffectParameter = (0, import_react17.useCallback)(
2597
+ const updateTrackEffectParameter = (0, import_react18.useCallback)(
2573
2598
  (trackId, instanceId, paramName, value) => {
2574
2599
  const instancesMap = trackEffectInstancesRef.current.get(trackId);
2575
2600
  const instance = instancesMap == null ? void 0 : instancesMap.get(instanceId);
@@ -2590,7 +2615,7 @@ function useTrackDynamicEffects() {
2590
2615
  },
2591
2616
  []
2592
2617
  );
2593
- const toggleBypass = (0, import_react17.useCallback)((trackId, instanceId) => {
2618
+ const toggleBypass = (0, import_react18.useCallback)((trackId, instanceId) => {
2594
2619
  var _a;
2595
2620
  const trackEffects = trackEffectsStateRef.current.get(trackId) || [];
2596
2621
  const effect = trackEffects.find((e) => e.instanceId === instanceId);
@@ -2612,7 +2637,7 @@ function useTrackDynamicEffects() {
2612
2637
  return newState;
2613
2638
  });
2614
2639
  }, []);
2615
- const clearTrackEffects = (0, import_react17.useCallback)((trackId) => {
2640
+ const clearTrackEffects = (0, import_react18.useCallback)((trackId) => {
2616
2641
  const instancesMap = trackEffectInstancesRef.current.get(trackId);
2617
2642
  if (instancesMap) {
2618
2643
  instancesMap.forEach((inst) => inst.dispose());
@@ -2624,9 +2649,9 @@ function useTrackDynamicEffects() {
2624
2649
  return newState;
2625
2650
  });
2626
2651
  }, []);
2627
- const trackEffectsStateRef = (0, import_react17.useRef)(trackEffectsState);
2652
+ const trackEffectsStateRef = (0, import_react18.useRef)(trackEffectsState);
2628
2653
  trackEffectsStateRef.current = trackEffectsState;
2629
- const getTrackEffectsFunction = (0, import_react17.useCallback)(
2654
+ const getTrackEffectsFunction = (0, import_react18.useCallback)(
2630
2655
  (trackId) => {
2631
2656
  return (graphEnd, masterGainNode, _isOffline) => {
2632
2657
  trackGraphNodesRef.current.set(trackId, {
@@ -2654,12 +2679,12 @@ function useTrackDynamicEffects() {
2654
2679
  []
2655
2680
  // No dependencies - stable function that reads from refs
2656
2681
  );
2657
- (0, import_react17.useEffect)(() => {
2682
+ (0, import_react18.useEffect)(() => {
2658
2683
  trackEffectsState.forEach((effects, trackId) => {
2659
2684
  rebuildTrackChain(trackId, effects);
2660
2685
  });
2661
2686
  }, [trackEffectsState, rebuildTrackChain]);
2662
- (0, import_react17.useEffect)(() => {
2687
+ (0, import_react18.useEffect)(() => {
2663
2688
  const trackEffectInstances = trackEffectInstancesRef.current;
2664
2689
  return () => {
2665
2690
  trackEffectInstances.forEach((instancesMap) => {
@@ -2669,7 +2694,7 @@ function useTrackDynamicEffects() {
2669
2694
  trackEffectInstances.clear();
2670
2695
  };
2671
2696
  }, []);
2672
- const createOfflineTrackEffectsFunction = (0, import_react17.useCallback)(
2697
+ const createOfflineTrackEffectsFunction = (0, import_react18.useCallback)(
2673
2698
  (trackId) => {
2674
2699
  const trackEffects = trackEffectsState.get(trackId) || [];
2675
2700
  const nonBypassedEffects = trackEffects.filter((e) => !e.bypassed);
@@ -2713,7 +2738,7 @@ function useTrackDynamicEffects() {
2713
2738
  }
2714
2739
 
2715
2740
  // src/hooks/useExportWav.ts
2716
- var import_react18 = require("react");
2741
+ var import_react19 = require("react");
2717
2742
  var import_playout2 = require("@waveform-playlist/playout");
2718
2743
 
2719
2744
  // src/utils/wavEncoder.ts
@@ -2787,10 +2812,10 @@ function downloadBlob(blob, filename) {
2787
2812
 
2788
2813
  // src/hooks/useExportWav.ts
2789
2814
  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)(
2815
+ const [isExporting, setIsExporting] = (0, import_react19.useState)(false);
2816
+ const [progress, setProgress] = (0, import_react19.useState)(0);
2817
+ const [error, setError] = (0, import_react19.useState)(null);
2818
+ const exportWav = (0, import_react19.useCallback)(
2794
2819
  (_0, _1, ..._2) => __async(null, [_0, _1, ..._2], function* (tracks, trackStates, options = {}) {
2795
2820
  const {
2796
2821
  filename = "export",
@@ -3102,23 +3127,23 @@ function generateFadeCurve(startValue, endValue, numPoints, curveType) {
3102
3127
  }
3103
3128
 
3104
3129
  // src/hooks/useAnimationFrameLoop.ts
3105
- var import_react19 = require("react");
3130
+ var import_react20 = require("react");
3106
3131
  var useAnimationFrameLoop = () => {
3107
- const animationFrameRef = (0, import_react19.useRef)(null);
3108
- const stopAnimationFrameLoop = (0, import_react19.useCallback)(() => {
3132
+ const animationFrameRef = (0, import_react20.useRef)(null);
3133
+ const stopAnimationFrameLoop = (0, import_react20.useCallback)(() => {
3109
3134
  if (animationFrameRef.current !== null) {
3110
3135
  cancelAnimationFrame(animationFrameRef.current);
3111
3136
  animationFrameRef.current = null;
3112
3137
  }
3113
3138
  }, []);
3114
- const startAnimationFrameLoop = (0, import_react19.useCallback)(
3139
+ const startAnimationFrameLoop = (0, import_react20.useCallback)(
3115
3140
  (callback) => {
3116
3141
  stopAnimationFrameLoop();
3117
3142
  animationFrameRef.current = requestAnimationFrame(callback);
3118
3143
  },
3119
3144
  [stopAnimationFrameLoop]
3120
3145
  );
3121
- (0, import_react19.useEffect)(() => {
3146
+ (0, import_react20.useEffect)(() => {
3122
3147
  return () => {
3123
3148
  stopAnimationFrameLoop();
3124
3149
  };
@@ -3131,7 +3156,7 @@ var useAnimationFrameLoop = () => {
3131
3156
  };
3132
3157
 
3133
3158
  // src/hooks/useWaveformDataCache.ts
3134
- var import_react20 = require("react");
3159
+ var import_react21 = require("react");
3135
3160
 
3136
3161
  // src/workers/peaksWorker.ts
3137
3162
  var import_waveform_data2 = __toESM(require("waveform-data"));
@@ -3363,20 +3388,20 @@ function createPeaksWorker() {
3363
3388
 
3364
3389
  // src/hooks/useWaveformDataCache.ts
3365
3390
  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)(() => {
3391
+ const [cache, setCache] = (0, import_react21.useState)(() => /* @__PURE__ */ new Map());
3392
+ const [isGenerating, setIsGenerating] = (0, import_react21.useState)(false);
3393
+ const workerRef = (0, import_react21.useRef)(null);
3394
+ const generatedByBufferRef = (0, import_react21.useRef)(/* @__PURE__ */ new WeakMap());
3395
+ const inflightByBufferRef = (0, import_react21.useRef)(/* @__PURE__ */ new WeakMap());
3396
+ const subscribersByBufferRef = (0, import_react21.useRef)(/* @__PURE__ */ new WeakMap());
3397
+ const pendingCountRef = (0, import_react21.useRef)(0);
3398
+ const getWorker = (0, import_react21.useCallback)(() => {
3374
3399
  if (!workerRef.current) {
3375
3400
  workerRef.current = createPeaksWorker();
3376
3401
  }
3377
3402
  return workerRef.current;
3378
3403
  }, []);
3379
- (0, import_react20.useEffect)(() => {
3404
+ (0, import_react21.useEffect)(() => {
3380
3405
  let cancelled = false;
3381
3406
  const generatedByBuffer = generatedByBufferRef.current;
3382
3407
  const inflightByBuffer = inflightByBufferRef.current;
@@ -3481,7 +3506,7 @@ function useWaveformDataCache(tracks, baseScale) {
3481
3506
  setIsGenerating(false);
3482
3507
  };
3483
3508
  }, [tracks, baseScale, getWorker]);
3484
- (0, import_react20.useEffect)(() => {
3509
+ (0, import_react21.useEffect)(() => {
3485
3510
  return () => {
3486
3511
  var _a;
3487
3512
  (_a = workerRef.current) == null ? void 0 : _a.terminate();
@@ -3492,8 +3517,8 @@ function useWaveformDataCache(tracks, baseScale) {
3492
3517
  }
3493
3518
 
3494
3519
  // src/hooks/useDynamicTracks.ts
3495
- var import_react21 = require("react");
3496
- var import_core2 = require("@waveform-playlist/core");
3520
+ var import_react22 = require("react");
3521
+ var import_core4 = require("@waveform-playlist/core");
3497
3522
  var import_playout3 = require("@waveform-playlist/playout");
3498
3523
  function getSourceName(source) {
3499
3524
  var _a, _b, _c, _d, _e;
@@ -3527,13 +3552,13 @@ function decodeSource(source, audioContext, signal) {
3527
3552
  });
3528
3553
  }
3529
3554
  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)(() => {
3555
+ const [tracks, setTracks] = (0, import_react22.useState)([]);
3556
+ const [loadingCount, setLoadingCount] = (0, import_react22.useState)(0);
3557
+ const [errors, setErrors] = (0, import_react22.useState)([]);
3558
+ const cancelledRef = (0, import_react22.useRef)(false);
3559
+ const loadingIdsRef = (0, import_react22.useRef)(/* @__PURE__ */ new Set());
3560
+ const abortControllersRef = (0, import_react22.useRef)(/* @__PURE__ */ new Map());
3561
+ (0, import_react22.useEffect)(() => {
3537
3562
  const controllers = abortControllersRef.current;
3538
3563
  return () => {
3539
3564
  cancelledRef.current = true;
@@ -3543,11 +3568,11 @@ function useDynamicTracks() {
3543
3568
  controllers.clear();
3544
3569
  };
3545
3570
  }, []);
3546
- const addTracks = (0, import_react21.useCallback)((sources) => {
3571
+ const addTracks = (0, import_react22.useCallback)((sources) => {
3547
3572
  if (sources.length === 0) return;
3548
3573
  const audioContext = (0, import_playout3.getGlobalAudioContext)();
3549
3574
  const placeholders = sources.map((source) => ({
3550
- track: (0, import_core2.createTrack)({ name: `${getSourceName(source)} (loading...)`, clips: [] }),
3575
+ track: (0, import_core4.createTrack)({ name: `${getSourceName(source)} (loading...)`, clips: [] }),
3551
3576
  source
3552
3577
  }));
3553
3578
  setTracks((prev) => [...prev, ...placeholders.map((p) => p.track)]);
@@ -3559,7 +3584,7 @@ function useDynamicTracks() {
3559
3584
  (() => __async(null, null, function* () {
3560
3585
  try {
3561
3586
  const { audioBuffer, name } = yield decodeSource(source, audioContext, controller.signal);
3562
- const clip = (0, import_core2.createClipFromSeconds)({
3587
+ const clip = (0, import_core4.createClipFromSeconds)({
3563
3588
  audioBuffer,
3564
3589
  startTime: 0,
3565
3590
  duration: audioBuffer.duration,
@@ -3593,7 +3618,7 @@ function useDynamicTracks() {
3593
3618
  }))();
3594
3619
  }
3595
3620
  }, []);
3596
- const removeTrack = (0, import_react21.useCallback)((trackId) => {
3621
+ const removeTrack = (0, import_react22.useCallback)((trackId) => {
3597
3622
  setTracks((prev) => prev.filter((t) => t.id !== trackId));
3598
3623
  const controller = abortControllersRef.current.get(trackId);
3599
3624
  if (controller) {
@@ -3615,24 +3640,24 @@ function useDynamicTracks() {
3615
3640
  }
3616
3641
 
3617
3642
  // src/hooks/useOutputMeter.ts
3618
- var import_react22 = require("react");
3643
+ var import_react23 = require("react");
3619
3644
  var import_playout4 = require("@waveform-playlist/playout");
3620
- var import_core3 = require("@waveform-playlist/core");
3645
+ var import_core5 = require("@waveform-playlist/core");
3621
3646
  var import_worklets = require("@waveform-playlist/worklets");
3622
3647
  var PEAK_DECAY = 0.98;
3623
3648
  function useOutputMeter(options = {}) {
3624
3649
  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)(
3650
+ const [levels, setLevels] = (0, import_react23.useState)(() => new Array(channelCount).fill(0));
3651
+ const [peakLevels, setPeakLevels] = (0, import_react23.useState)(() => new Array(channelCount).fill(0));
3652
+ const [rmsLevels, setRmsLevels] = (0, import_react23.useState)(() => new Array(channelCount).fill(0));
3653
+ const workletNodeRef = (0, import_react23.useRef)(null);
3654
+ const smoothedPeakRef = (0, import_react23.useRef)(new Array(channelCount).fill(0));
3655
+ const [meterError, setMeterError] = (0, import_react23.useState)(null);
3656
+ const resetPeak = (0, import_react23.useCallback)(
3632
3657
  () => setPeakLevels(new Array(channelCount).fill(0)),
3633
3658
  [channelCount]
3634
3659
  );
3635
- (0, import_react22.useEffect)(() => {
3660
+ (0, import_react23.useEffect)(() => {
3636
3661
  if (!isPlaying) {
3637
3662
  const zeros = new Array(channelCount).fill(0);
3638
3663
  smoothedPeakRef.current = new Array(channelCount).fill(0);
@@ -3641,7 +3666,7 @@ function useOutputMeter(options = {}) {
3641
3666
  setPeakLevels(zeros);
3642
3667
  }
3643
3668
  }, [isPlaying, channelCount]);
3644
- (0, import_react22.useEffect)(() => {
3669
+ (0, import_react23.useEffect)(() => {
3645
3670
  let isMounted = true;
3646
3671
  const setup = () => __async(null, null, function* () {
3647
3672
  const context = (0, import_playout4.getGlobalContext)();
@@ -3672,8 +3697,8 @@ function useOutputMeter(options = {}) {
3672
3697
  const rmsValues = [];
3673
3698
  for (let ch = 0; ch < peak.length; ch++) {
3674
3699
  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]));
3700
+ peakValues.push((0, import_core5.gainToNormalized)(smoothed[ch]));
3701
+ rmsValues.push((0, import_core5.gainToNormalized)(rms[ch]));
3677
3702
  }
3678
3703
  setLevels(peakValues);
3679
3704
  setRmsLevels(rmsValues);
@@ -3713,10 +3738,10 @@ function useOutputMeter(options = {}) {
3713
3738
 
3714
3739
  // src/WaveformPlaylistContext.tsx
3715
3740
  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);
3741
+ var PlaybackAnimationContext = (0, import_react24.createContext)(null);
3742
+ var PlaylistStateContext = (0, import_react24.createContext)(null);
3743
+ var PlaylistControlsContext = (0, import_react24.createContext)(null);
3744
+ var PlaylistDataContext = (0, import_react24.createContext)(null);
3720
3745
  var WaveformPlaylistProvider = ({
3721
3746
  tracks,
3722
3747
  timescale = false,
@@ -3743,14 +3768,14 @@ var WaveformPlaylistProvider = ({
3743
3768
  }) => {
3744
3769
  var _a, _b, _c, _d;
3745
3770
  const progressBarWidth = progressBarWidthProp != null ? progressBarWidthProp : barWidth + barGap;
3746
- const indefinitePlaybackRef = (0, import_react23.useRef)(indefinitePlayback);
3771
+ const indefinitePlaybackRef = (0, import_react24.useRef)(indefinitePlayback);
3747
3772
  indefinitePlaybackRef.current = indefinitePlayback;
3748
- const stableZoomLevels = (0, import_react23.useMemo)(
3773
+ const stableZoomLevels = (0, import_react24.useMemo)(
3749
3774
  () => zoomLevels,
3750
3775
  // eslint-disable-next-line react-hooks/exhaustive-deps
3751
3776
  [zoomLevels == null ? void 0 : zoomLevels.join(",")]
3752
3777
  );
3753
- const annotations = (0, import_react23.useMemo)(() => {
3778
+ const annotations = (0, import_react24.useMemo)(() => {
3754
3779
  if (!(annotationList == null ? void 0 : annotationList.annotations)) return [];
3755
3780
  if (process.env.NODE_ENV !== "production" && annotationList.annotations.length > 0) {
3756
3781
  const first = annotationList.annotations[0];
@@ -3763,46 +3788,46 @@ var WaveformPlaylistProvider = ({
3763
3788
  }
3764
3789
  return annotationList.annotations;
3765
3790
  }, [annotationList == null ? void 0 : annotationList.annotations]);
3766
- const annotationsRef = (0, import_react23.useRef)(annotations);
3791
+ const annotationsRef = (0, import_react24.useRef)(annotations);
3767
3792
  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)(
3793
+ const [activeAnnotationId, setActiveAnnotationIdState] = (0, import_react24.useState)(null);
3794
+ const [isPlaying, setIsPlaying] = (0, import_react24.useState)(false);
3795
+ const [currentTime, setCurrentTime] = (0, import_react24.useState)(0);
3796
+ const [duration, setDuration] = (0, import_react24.useState)(0);
3797
+ const [audioBuffers, setAudioBuffers] = (0, import_react24.useState)([]);
3798
+ const [peaksDataArray, setPeaksDataArray] = (0, import_react24.useState)([]);
3799
+ const [trackStates, setTrackStates] = (0, import_react24.useState)([]);
3800
+ const [isAutomaticScroll, setIsAutomaticScroll] = (0, import_react24.useState)(automaticScroll);
3801
+ const [continuousPlay, setContinuousPlayState] = (0, import_react24.useState)(
3777
3802
  (_a = annotationList == null ? void 0 : annotationList.isContinuousPlay) != null ? _a : false
3778
3803
  );
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);
3804
+ const [linkEndpoints, setLinkEndpoints] = (0, import_react24.useState)((_b = annotationList == null ? void 0 : annotationList.linkEndpoints) != null ? _b : false);
3805
+ const [annotationsEditable, setAnnotationsEditable] = (0, import_react24.useState)((_c = annotationList == null ? void 0 : annotationList.editable) != null ? _c : false);
3806
+ const [isReady, setIsReady] = (0, import_react24.useState)(false);
3807
+ const engineRef = (0, import_react24.useRef)(null);
3808
+ const audioInitializedRef = (0, import_react24.useRef)(false);
3809
+ const isPlayingRef = (0, import_react24.useRef)(false);
3785
3810
  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);
3811
+ const playStartPositionRef = (0, import_react24.useRef)(0);
3812
+ const currentTimeRef = (0, import_react24.useRef)(0);
3813
+ const tracksRef = (0, import_react24.useRef)(tracks);
3814
+ const soundFontCacheRef = (0, import_react24.useRef)(soundFontCache);
3790
3815
  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)(
3816
+ const trackStatesRef = (0, import_react24.useRef)(trackStates);
3817
+ const playbackStartTimeRef = (0, import_react24.useRef)(0);
3818
+ const audioStartPositionRef = (0, import_react24.useRef)(0);
3819
+ const playbackEndTimeRef = (0, import_react24.useRef)(null);
3820
+ const scrollContainerRef = (0, import_react24.useRef)(null);
3821
+ const isAutomaticScrollRef = (0, import_react24.useRef)(false);
3822
+ const continuousPlayRef = (0, import_react24.useRef)((_d = annotationList == null ? void 0 : annotationList.isContinuousPlay) != null ? _d : false);
3823
+ const activeAnnotationIdRef = (0, import_react24.useRef)(null);
3824
+ const engineTracksRef = (0, import_react24.useRef)(null);
3825
+ const lastTracksVersionRef = (0, import_react24.useRef)(0);
3826
+ const skipEngineDisposeRef = (0, import_react24.useRef)(false);
3827
+ const isDraggingRef = (0, import_react24.useRef)(false);
3828
+ const prevTracksRef = (0, import_react24.useRef)([]);
3829
+ const samplesPerPixelRef = (0, import_react24.useRef)(initialSamplesPerPixel);
3830
+ const sampleRateRef = (0, import_react24.useRef)(
3806
3831
  typeof AudioContext !== "undefined" ? (0, import_playout5.getGlobalAudioContext)().sampleRate : 48e3
3807
3832
  );
3808
3833
  const { timeFormat, setTimeFormat, formatTime: formatTime2 } = useTimeFormat();
@@ -3841,21 +3866,28 @@ var WaveformPlaylistProvider = ({
3841
3866
  onEngineState: onSelectedTrackEngineState,
3842
3867
  selectedTrackIdRef
3843
3868
  } = useSelectedTrack({ engineRef });
3869
+ const {
3870
+ canUndo,
3871
+ canRedo,
3872
+ undo,
3873
+ redo,
3874
+ onEngineState: onUndoEngineState
3875
+ } = useUndoState({ engineRef });
3844
3876
  const { animationFrameRef, startAnimationFrameLoop, stopAnimationFrameLoop } = useAnimationFrameLoop();
3845
- const baseScale = (0, import_react23.useMemo)(
3877
+ const baseScale = (0, import_react24.useMemo)(
3846
3878
  () => Math.min(...stableZoomLevels != null ? stableZoomLevels : [256, 512, 1024, 2048, 4096, 8192]),
3847
3879
  [stableZoomLevels]
3848
3880
  );
3849
3881
  const { cache: waveformDataCache } = useWaveformDataCache(tracks, baseScale);
3850
- const setContinuousPlay = (0, import_react23.useCallback)((value) => {
3882
+ const setContinuousPlay = (0, import_react24.useCallback)((value) => {
3851
3883
  continuousPlayRef.current = value;
3852
3884
  setContinuousPlayState(value);
3853
3885
  }, []);
3854
- const setActiveAnnotationId = (0, import_react23.useCallback)((value) => {
3886
+ const setActiveAnnotationId = (0, import_react24.useCallback)((value) => {
3855
3887
  activeAnnotationIdRef.current = value;
3856
3888
  setActiveAnnotationIdState(value);
3857
3889
  }, []);
3858
- const setLoopRegionFromSelection = (0, import_react23.useCallback)(() => {
3890
+ const setLoopRegionFromSelection = (0, import_react24.useCallback)(() => {
3859
3891
  var _a2, _b2;
3860
3892
  const start = (_a2 = selectionStartRef.current) != null ? _a2 : 0;
3861
3893
  const end = (_b2 = selectionEndRef.current) != null ? _b2 : 0;
@@ -3863,10 +3895,10 @@ var WaveformPlaylistProvider = ({
3863
3895
  setLoopRegion(start, end);
3864
3896
  }
3865
3897
  }, [setLoopRegion, selectionStartRef, selectionEndRef]);
3866
- (0, import_react23.useEffect)(() => {
3898
+ (0, import_react24.useEffect)(() => {
3867
3899
  isAutomaticScrollRef.current = isAutomaticScroll;
3868
3900
  }, [isAutomaticScroll]);
3869
- (0, import_react23.useEffect)(() => {
3901
+ (0, import_react24.useEffect)(() => {
3870
3902
  trackStatesRef.current = trackStates;
3871
3903
  }, [trackStates]);
3872
3904
  tracksRef.current = tracks;
@@ -3877,7 +3909,7 @@ var WaveformPlaylistProvider = ({
3877
3909
  return current === pt;
3878
3910
  });
3879
3911
  skipEngineDisposeRef.current = isEngineTracks || isDraggingRef.current || isIncrementalAdd;
3880
- (0, import_react23.useEffect)(() => {
3912
+ (0, import_react24.useEffect)(() => {
3881
3913
  if (!scrollContainerRef.current || duration === 0) return;
3882
3914
  const container = scrollContainerRef.current;
3883
3915
  const oldSamplesPerPixel = samplesPerPixelRef.current;
@@ -3893,8 +3925,8 @@ var WaveformPlaylistProvider = ({
3893
3925
  container.scrollLeft = newScrollLeft;
3894
3926
  samplesPerPixelRef.current = newSamplesPerPixel;
3895
3927
  }, [samplesPerPixel, duration]);
3896
- const pendingResumeRef = (0, import_react23.useRef)(null);
3897
- (0, import_react23.useEffect)(() => {
3928
+ const pendingResumeRef = (0, import_react24.useRef)(null);
3929
+ (0, import_react24.useEffect)(() => {
3898
3930
  var _a2, _b2, _c2, _d2;
3899
3931
  if (isEngineTracks || isDraggingRef.current) {
3900
3932
  if (isEngineTracks) {
@@ -4069,6 +4101,7 @@ var WaveformPlaylistProvider = ({
4069
4101
  onSelectedTrackEngineState(state);
4070
4102
  onZoomEngineState(state);
4071
4103
  onVolumeEngineState(state);
4104
+ onUndoEngineState(state);
4072
4105
  if (!suppressTracksMirroring && state.tracksVersion !== lastTracksVersionRef.current) {
4073
4106
  lastTracksVersionRef.current = state.tracksVersion;
4074
4107
  engineTracksRef.current = state.tracks;
@@ -4126,6 +4159,7 @@ var WaveformPlaylistProvider = ({
4126
4159
  onSelectedTrackEngineState,
4127
4160
  onZoomEngineState,
4128
4161
  onVolumeEngineState,
4162
+ onUndoEngineState,
4129
4163
  onTracksChange,
4130
4164
  masterVolumeRef,
4131
4165
  selectionStartRef,
@@ -4137,7 +4171,7 @@ var WaveformPlaylistProvider = ({
4137
4171
  soundFontCache,
4138
4172
  deferEngineRebuild
4139
4173
  ]);
4140
- (0, import_react23.useEffect)(() => {
4174
+ (0, import_react24.useEffect)(() => {
4141
4175
  if (tracks.length === 0) return;
4142
4176
  const allTrackPeaks = tracks.map((track) => {
4143
4177
  const clipPeaks = track.clips.map((clip) => {
@@ -4203,8 +4237,8 @@ var WaveformPlaylistProvider = ({
4203
4237
  });
4204
4238
  setPeaksDataArray(allTrackPeaks);
4205
4239
  }, [tracks, samplesPerPixel, mono, waveformDataCache, deferEngineRebuild]);
4206
- const getPlaybackTimeFallbackWarnedRef = (0, import_react23.useRef)(false);
4207
- const getPlaybackTime = (0, import_react23.useCallback)(() => {
4240
+ const getPlaybackTimeFallbackWarnedRef = (0, import_react24.useRef)(false);
4241
+ const getPlaybackTime = (0, import_react24.useCallback)(() => {
4208
4242
  var _a2, _b2;
4209
4243
  if (engineRef.current) {
4210
4244
  return engineRef.current.getCurrentTime();
@@ -4218,7 +4252,7 @@ var WaveformPlaylistProvider = ({
4218
4252
  const elapsed = (0, import_tone4.getContext)().currentTime - ((_a2 = playbackStartTimeRef.current) != null ? _a2 : 0);
4219
4253
  return ((_b2 = audioStartPositionRef.current) != null ? _b2 : 0) + elapsed;
4220
4254
  }, []);
4221
- const startAnimationLoop = (0, import_react23.useCallback)(() => {
4255
+ const startAnimationLoop = (0, import_react24.useCallback)(() => {
4222
4256
  const updateTime = () => {
4223
4257
  const time = getPlaybackTime();
4224
4258
  currentTimeRef.current = time;
@@ -4287,7 +4321,7 @@ var WaveformPlaylistProvider = ({
4287
4321
  startAnimationFrameLoop(updateTime);
4288
4322
  }, [duration, setActiveAnnotationId, startAnimationFrameLoop, getPlaybackTime]);
4289
4323
  const stopAnimationLoop = stopAnimationFrameLoop;
4290
- (0, import_react23.useEffect)(() => {
4324
+ (0, import_react24.useEffect)(() => {
4291
4325
  const reschedulePlayback = () => __async(null, null, function* () {
4292
4326
  if (isPlaying && animationFrameRef.current && engineRef.current) {
4293
4327
  if (continuousPlay) {
@@ -4312,7 +4346,7 @@ var WaveformPlaylistProvider = ({
4312
4346
  stopAnimationLoop();
4313
4347
  });
4314
4348
  }, [continuousPlay, isPlaying, startAnimationLoop, stopAnimationLoop, animationFrameRef]);
4315
- (0, import_react23.useEffect)(() => {
4349
+ (0, import_react24.useEffect)(() => {
4316
4350
  const resumePlayback = () => __async(null, null, function* () {
4317
4351
  if (pendingResumeRef.current && engineRef.current) {
4318
4352
  const { position } = pendingResumeRef.current;
@@ -4336,7 +4370,7 @@ var WaveformPlaylistProvider = ({
4336
4370
  stopAnimationLoop();
4337
4371
  });
4338
4372
  }, [tracks, startAnimationLoop, stopAnimationLoop]);
4339
- const play = (0, import_react23.useCallback)(
4373
+ const play = (0, import_react24.useCallback)(
4340
4374
  (startTime, playDuration) => __async(null, null, function* () {
4341
4375
  if (!engineRef.current) return;
4342
4376
  const actualStartTime = startTime != null ? startTime : currentTimeRef.current;
@@ -4367,7 +4401,7 @@ var WaveformPlaylistProvider = ({
4367
4401
  }),
4368
4402
  [startAnimationLoop, stopAnimationLoop]
4369
4403
  );
4370
- const pause = (0, import_react23.useCallback)(() => {
4404
+ const pause = (0, import_react24.useCallback)(() => {
4371
4405
  if (!engineRef.current) return;
4372
4406
  const pauseTime = getPlaybackTime();
4373
4407
  engineRef.current.pause();
@@ -4376,7 +4410,7 @@ var WaveformPlaylistProvider = ({
4376
4410
  currentTimeRef.current = pauseTime;
4377
4411
  setCurrentTime(pauseTime);
4378
4412
  }, [stopAnimationLoop, getPlaybackTime]);
4379
- const stop = (0, import_react23.useCallback)(() => {
4413
+ const stop = (0, import_react24.useCallback)(() => {
4380
4414
  if (!engineRef.current) return;
4381
4415
  engineRef.current.stop();
4382
4416
  setIsPlaying(false);
@@ -4385,7 +4419,7 @@ var WaveformPlaylistProvider = ({
4385
4419
  setCurrentTime(playStartPositionRef.current);
4386
4420
  setActiveAnnotationId(null);
4387
4421
  }, [stopAnimationLoop, setActiveAnnotationId]);
4388
- const seekTo = (0, import_react23.useCallback)(
4422
+ const seekTo = (0, import_react24.useCallback)(
4389
4423
  (time) => {
4390
4424
  const clampedTime = Math.max(0, Math.min(time, duration));
4391
4425
  currentTimeRef.current = clampedTime;
@@ -4396,7 +4430,7 @@ var WaveformPlaylistProvider = ({
4396
4430
  },
4397
4431
  [duration, isPlaying, play]
4398
4432
  );
4399
- const setTrackMute = (0, import_react23.useCallback)(
4433
+ const setTrackMute = (0, import_react24.useCallback)(
4400
4434
  (trackIndex, muted) => {
4401
4435
  var _a2;
4402
4436
  const trackId = (_a2 = tracksRef.current[trackIndex]) == null ? void 0 : _a2.id;
@@ -4410,7 +4444,7 @@ var WaveformPlaylistProvider = ({
4410
4444
  },
4411
4445
  [trackStates]
4412
4446
  );
4413
- const setTrackSolo = (0, import_react23.useCallback)(
4447
+ const setTrackSolo = (0, import_react24.useCallback)(
4414
4448
  (trackIndex, soloed) => {
4415
4449
  var _a2;
4416
4450
  const trackId = (_a2 = tracksRef.current[trackIndex]) == null ? void 0 : _a2.id;
@@ -4424,7 +4458,7 @@ var WaveformPlaylistProvider = ({
4424
4458
  },
4425
4459
  [trackStates]
4426
4460
  );
4427
- const setTrackVolume = (0, import_react23.useCallback)(
4461
+ const setTrackVolume = (0, import_react24.useCallback)(
4428
4462
  (trackIndex, volume2) => {
4429
4463
  var _a2;
4430
4464
  const trackId = (_a2 = tracksRef.current[trackIndex]) == null ? void 0 : _a2.id;
@@ -4438,7 +4472,7 @@ var WaveformPlaylistProvider = ({
4438
4472
  },
4439
4473
  [trackStates]
4440
4474
  );
4441
- const setTrackPan = (0, import_react23.useCallback)(
4475
+ const setTrackPan = (0, import_react24.useCallback)(
4442
4476
  (trackIndex, pan) => {
4443
4477
  var _a2;
4444
4478
  const trackId = (_a2 = tracksRef.current[trackIndex]) == null ? void 0 : _a2.id;
@@ -4452,7 +4486,7 @@ var WaveformPlaylistProvider = ({
4452
4486
  },
4453
4487
  [trackStates]
4454
4488
  );
4455
- const setSelection = (0, import_react23.useCallback)(
4489
+ const setSelection = (0, import_react24.useCallback)(
4456
4490
  (start, end) => {
4457
4491
  setSelectionEngine(start, end);
4458
4492
  currentTimeRef.current = start;
@@ -4465,12 +4499,12 @@ var WaveformPlaylistProvider = ({
4465
4499
  },
4466
4500
  [isPlaying, setSelectionEngine]
4467
4501
  );
4468
- const setScrollContainer = (0, import_react23.useCallback)((element) => {
4502
+ const setScrollContainer = (0, import_react24.useCallback)((element) => {
4469
4503
  scrollContainerRef.current = element;
4470
4504
  }, []);
4471
- const onAnnotationsChangeRef = (0, import_react23.useRef)(onAnnotationsChange);
4505
+ const onAnnotationsChangeRef = (0, import_react24.useRef)(onAnnotationsChange);
4472
4506
  onAnnotationsChangeRef.current = onAnnotationsChange;
4473
- const setAnnotations = (0, import_react23.useCallback)(
4507
+ const setAnnotations = (0, import_react24.useCallback)(
4474
4508
  (action) => {
4475
4509
  const updated = typeof action === "function" ? action(annotationsRef.current) : action;
4476
4510
  if (!onAnnotationsChangeRef.current) {
@@ -4488,7 +4522,7 @@ var WaveformPlaylistProvider = ({
4488
4522
  const sampleRate = sampleRateRef.current;
4489
4523
  const timeScaleHeight = timescale ? 30 : 0;
4490
4524
  const minimumPlaylistHeight = tracks.length * waveHeight + timeScaleHeight;
4491
- const animationValue = (0, import_react23.useMemo)(
4525
+ const animationValue = (0, import_react24.useMemo)(
4492
4526
  () => ({
4493
4527
  isPlaying,
4494
4528
  currentTime,
@@ -4506,7 +4540,7 @@ var WaveformPlaylistProvider = ({
4506
4540
  getPlaybackTime
4507
4541
  ]
4508
4542
  );
4509
- const stateValue = (0, import_react23.useMemo)(
4543
+ const stateValue = (0, import_react24.useMemo)(
4510
4544
  () => ({
4511
4545
  continuousPlay,
4512
4546
  linkEndpoints,
@@ -4520,7 +4554,9 @@ var WaveformPlaylistProvider = ({
4520
4554
  selectedTrackId,
4521
4555
  loopStart,
4522
4556
  loopEnd,
4523
- indefinitePlayback
4557
+ indefinitePlayback,
4558
+ canUndo,
4559
+ canRedo
4524
4560
  }),
4525
4561
  [
4526
4562
  continuousPlay,
@@ -4535,20 +4571,22 @@ var WaveformPlaylistProvider = ({
4535
4571
  selectedTrackId,
4536
4572
  loopStart,
4537
4573
  loopEnd,
4538
- indefinitePlayback
4574
+ indefinitePlayback,
4575
+ canUndo,
4576
+ canRedo
4539
4577
  ]
4540
4578
  );
4541
- const setCurrentTimeControl = (0, import_react23.useCallback)(
4579
+ const setCurrentTimeControl = (0, import_react24.useCallback)(
4542
4580
  (time) => {
4543
4581
  currentTimeRef.current = time;
4544
4582
  setCurrentTime(time);
4545
4583
  },
4546
4584
  [currentTimeRef]
4547
4585
  );
4548
- const setAutomaticScrollControl = (0, import_react23.useCallback)((enabled) => {
4586
+ const setAutomaticScrollControl = (0, import_react24.useCallback)((enabled) => {
4549
4587
  setIsAutomaticScroll(enabled);
4550
4588
  }, []);
4551
- const controlsValue = (0, import_react23.useMemo)(
4589
+ const controlsValue = (0, import_react24.useMemo)(
4552
4590
  () => ({
4553
4591
  // Playback controls
4554
4592
  play,
@@ -4586,7 +4624,10 @@ var WaveformPlaylistProvider = ({
4586
4624
  setLoopEnabled,
4587
4625
  setLoopRegion,
4588
4626
  setLoopRegionFromSelection,
4589
- clearLoopRegion
4627
+ clearLoopRegion,
4628
+ // Undo/redo
4629
+ undo,
4630
+ redo
4590
4631
  }),
4591
4632
  [
4592
4633
  play,
@@ -4616,10 +4657,12 @@ var WaveformPlaylistProvider = ({
4616
4657
  setLoopEnabled,
4617
4658
  setLoopRegion,
4618
4659
  setLoopRegionFromSelection,
4619
- clearLoopRegion
4660
+ clearLoopRegion,
4661
+ undo,
4662
+ redo
4620
4663
  ]
4621
4664
  );
4622
- const dataValue = (0, import_react23.useMemo)(
4665
+ const dataValue = (0, import_react24.useMemo)(
4623
4666
  () => ({
4624
4667
  duration,
4625
4668
  audioBuffers,
@@ -4675,28 +4718,28 @@ var WaveformPlaylistProvider = ({
4675
4718
  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
4719
  };
4677
4720
  var usePlaybackAnimation = () => {
4678
- const context = (0, import_react23.useContext)(PlaybackAnimationContext);
4721
+ const context = (0, import_react24.useContext)(PlaybackAnimationContext);
4679
4722
  if (!context) {
4680
4723
  throw new Error("usePlaybackAnimation must be used within WaveformPlaylistProvider");
4681
4724
  }
4682
4725
  return context;
4683
4726
  };
4684
4727
  var usePlaylistState = () => {
4685
- const context = (0, import_react23.useContext)(PlaylistStateContext);
4728
+ const context = (0, import_react24.useContext)(PlaylistStateContext);
4686
4729
  if (!context) {
4687
4730
  throw new Error("usePlaylistState must be used within WaveformPlaylistProvider");
4688
4731
  }
4689
4732
  return context;
4690
4733
  };
4691
4734
  var usePlaylistControls = () => {
4692
- const context = (0, import_react23.useContext)(PlaylistControlsContext);
4735
+ const context = (0, import_react24.useContext)(PlaylistControlsContext);
4693
4736
  if (!context) {
4694
4737
  throw new Error("usePlaylistControls must be used within WaveformPlaylistProvider");
4695
4738
  }
4696
4739
  return context;
4697
4740
  };
4698
4741
  var usePlaylistData = () => {
4699
- const context = (0, import_react23.useContext)(PlaylistDataContext);
4742
+ const context = (0, import_react24.useContext)(PlaylistDataContext);
4700
4743
  if (!context) {
4701
4744
  throw new Error("usePlaylistData must be used within WaveformPlaylistProvider");
4702
4745
  }
@@ -4704,15 +4747,15 @@ var usePlaylistData = () => {
4704
4747
  };
4705
4748
 
4706
4749
  // src/MediaElementPlaylistContext.tsx
4707
- var import_react24 = require("react");
4750
+ var import_react25 = require("react");
4708
4751
  var import_styled_components2 = require("styled-components");
4709
4752
  var import_media_element_playout = require("@waveform-playlist/media-element-playout");
4710
4753
  var import_ui_components3 = require("@waveform-playlist/ui-components");
4711
4754
  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);
4755
+ var MediaElementAnimationContext = (0, import_react25.createContext)(null);
4756
+ var MediaElementStateContext = (0, import_react25.createContext)(null);
4757
+ var MediaElementControlsContext = (0, import_react25.createContext)(null);
4758
+ var MediaElementDataContext = (0, import_react25.createContext)(null);
4716
4759
  var MediaElementPlaylistProvider = ({
4717
4760
  track,
4718
4761
  samplesPerPixel: initialSamplesPerPixel = 1024,
@@ -4734,12 +4777,12 @@ var MediaElementPlaylistProvider = ({
4734
4777
  }) => {
4735
4778
  var _a;
4736
4779
  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)(() => {
4780
+ const [isPlaying, setIsPlaying] = (0, import_react25.useState)(false);
4781
+ const [currentTime, setCurrentTime] = (0, import_react25.useState)(0);
4782
+ const [duration, setDuration] = (0, import_react25.useState)(0);
4783
+ const [peaksDataArray, setPeaksDataArray] = (0, import_react25.useState)([]);
4784
+ const [playbackRate, setPlaybackRateState] = (0, import_react25.useState)(initialPlaybackRate);
4785
+ const annotations = (0, import_react25.useMemo)(() => {
4743
4786
  if (!(annotationList == null ? void 0 : annotationList.annotations)) return [];
4744
4787
  if (process.env.NODE_ENV !== "production" && annotationList.annotations.length > 0) {
4745
4788
  const first = annotationList.annotations[0];
@@ -4752,41 +4795,41 @@ var MediaElementPlaylistProvider = ({
4752
4795
  }
4753
4796
  return annotationList.annotations;
4754
4797
  }, [annotationList == null ? void 0 : annotationList.annotations]);
4755
- const annotationsRef = (0, import_react24.useRef)(annotations);
4798
+ const annotationsRef = (0, import_react25.useRef)(annotations);
4756
4799
  annotationsRef.current = annotations;
4757
- const [activeAnnotationId, setActiveAnnotationIdState] = (0, import_react24.useState)(null);
4758
- const [continuousPlay, setContinuousPlayState] = (0, import_react24.useState)(
4800
+ const [activeAnnotationId, setActiveAnnotationIdState] = (0, import_react25.useState)(null);
4801
+ const [continuousPlay, setContinuousPlayState] = (0, import_react25.useState)(
4759
4802
  (_a = annotationList == null ? void 0 : annotationList.isContinuousPlay) != null ? _a : false
4760
4803
  );
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);
4804
+ const [samplesPerPixel] = (0, import_react25.useState)(initialSamplesPerPixel);
4805
+ const [isAutomaticScroll, setIsAutomaticScroll] = (0, import_react25.useState)(automaticScroll);
4806
+ const playoutRef = (0, import_react25.useRef)(null);
4807
+ const currentTimeRef = (0, import_react25.useRef)(0);
4808
+ const continuousPlayRef = (0, import_react25.useRef)(continuousPlay);
4809
+ const activeAnnotationIdRef = (0, import_react25.useRef)(null);
4810
+ const scrollContainerRef = (0, import_react25.useRef)(null);
4811
+ const isAutomaticScrollRef = (0, import_react25.useRef)(automaticScroll);
4812
+ const samplesPerPixelRef = (0, import_react25.useRef)(initialSamplesPerPixel);
4770
4813
  const { startAnimationFrameLoop, stopAnimationFrameLoop } = useAnimationFrameLoop();
4771
- (0, import_react24.useEffect)(() => {
4814
+ (0, import_react25.useEffect)(() => {
4772
4815
  continuousPlayRef.current = continuousPlay;
4773
4816
  }, [continuousPlay]);
4774
- (0, import_react24.useEffect)(() => {
4817
+ (0, import_react25.useEffect)(() => {
4775
4818
  isAutomaticScrollRef.current = isAutomaticScroll;
4776
4819
  }, [isAutomaticScroll]);
4777
- const setActiveAnnotationId = (0, import_react24.useCallback)((value) => {
4820
+ const setActiveAnnotationId = (0, import_react25.useCallback)((value) => {
4778
4821
  activeAnnotationIdRef.current = value;
4779
4822
  setActiveAnnotationIdState(value);
4780
4823
  }, []);
4781
- const setContinuousPlay = (0, import_react24.useCallback)((value) => {
4824
+ const setContinuousPlay = (0, import_react25.useCallback)((value) => {
4782
4825
  continuousPlayRef.current = value;
4783
4826
  setContinuousPlayState(value);
4784
4827
  }, []);
4785
- const setScrollContainer = (0, import_react24.useCallback)((element) => {
4828
+ const setScrollContainer = (0, import_react25.useCallback)((element) => {
4786
4829
  scrollContainerRef.current = element;
4787
4830
  }, []);
4788
4831
  const sampleRate = track.waveformData.sample_rate;
4789
- (0, import_react24.useEffect)(() => {
4832
+ (0, import_react25.useEffect)(() => {
4790
4833
  var _a2, _b;
4791
4834
  const playout = new import_media_element_playout.MediaElementPlayout({
4792
4835
  playbackRate: initialPlaybackRate,
@@ -4833,7 +4876,7 @@ var MediaElementPlaylistProvider = ({
4833
4876
  stopAnimationFrameLoop,
4834
4877
  setActiveAnnotationId
4835
4878
  ]);
4836
- (0, import_react24.useEffect)(() => {
4879
+ (0, import_react25.useEffect)(() => {
4837
4880
  var _a2;
4838
4881
  try {
4839
4882
  const extractedPeaks = extractPeaksFromWaveformData(
@@ -4862,7 +4905,7 @@ var MediaElementPlaylistProvider = ({
4862
4905
  console.warn("[waveform-playlist] Failed to extract peaks from waveform data:", err);
4863
4906
  }
4864
4907
  }, [track.waveformData, track.name, samplesPerPixel, sampleRate]);
4865
- const startAnimationLoop = (0, import_react24.useCallback)(() => {
4908
+ const startAnimationLoop = (0, import_react25.useCallback)(() => {
4866
4909
  const updateTime = () => {
4867
4910
  var _a2, _b, _c;
4868
4911
  const time = (_b = (_a2 = playoutRef.current) == null ? void 0 : _a2.getCurrentTime()) != null ? _b : 0;
@@ -4905,7 +4948,7 @@ var MediaElementPlaylistProvider = ({
4905
4948
  startAnimationFrameLoop(updateTime);
4906
4949
  }, [setActiveAnnotationId, sampleRate, startAnimationFrameLoop]);
4907
4950
  const stopAnimationLoop = stopAnimationFrameLoop;
4908
- const play = (0, import_react24.useCallback)(
4951
+ const play = (0, import_react25.useCallback)(
4909
4952
  (startTime) => {
4910
4953
  if (!playoutRef.current) return;
4911
4954
  const actualStartTime = startTime != null ? startTime : currentTimeRef.current;
@@ -4915,14 +4958,14 @@ var MediaElementPlaylistProvider = ({
4915
4958
  },
4916
4959
  [startAnimationLoop]
4917
4960
  );
4918
- const pause = (0, import_react24.useCallback)(() => {
4961
+ const pause = (0, import_react25.useCallback)(() => {
4919
4962
  if (!playoutRef.current) return;
4920
4963
  playoutRef.current.pause();
4921
4964
  setIsPlaying(false);
4922
4965
  stopAnimationLoop();
4923
4966
  setCurrentTime(playoutRef.current.getCurrentTime());
4924
4967
  }, [stopAnimationLoop]);
4925
- const stop = (0, import_react24.useCallback)(() => {
4968
+ const stop = (0, import_react25.useCallback)(() => {
4926
4969
  if (!playoutRef.current) return;
4927
4970
  playoutRef.current.stop();
4928
4971
  setIsPlaying(false);
@@ -4931,7 +4974,7 @@ var MediaElementPlaylistProvider = ({
4931
4974
  setCurrentTime(0);
4932
4975
  setActiveAnnotationId(null);
4933
4976
  }, [stopAnimationLoop, setActiveAnnotationId]);
4934
- const seekTo = (0, import_react24.useCallback)(
4977
+ const seekTo = (0, import_react25.useCallback)(
4935
4978
  (time) => {
4936
4979
  const clampedTime = Math.max(0, Math.min(time, duration));
4937
4980
  currentTimeRef.current = clampedTime;
@@ -4942,7 +4985,7 @@ var MediaElementPlaylistProvider = ({
4942
4985
  },
4943
4986
  [duration]
4944
4987
  );
4945
- const setPlaybackRate = (0, import_react24.useCallback)((rate) => {
4988
+ const setPlaybackRate = (0, import_react25.useCallback)((rate) => {
4946
4989
  const clampedRate = Math.max(0.5, Math.min(2, rate));
4947
4990
  setPlaybackRateState(clampedRate);
4948
4991
  if (playoutRef.current) {
@@ -4950,7 +4993,7 @@ var MediaElementPlaylistProvider = ({
4950
4993
  }
4951
4994
  }, []);
4952
4995
  const timeScaleHeight = timescale ? 30 : 0;
4953
- const animationValue = (0, import_react24.useMemo)(
4996
+ const animationValue = (0, import_react25.useMemo)(
4954
4997
  () => ({
4955
4998
  isPlaying,
4956
4999
  currentTime,
@@ -4958,7 +5001,7 @@ var MediaElementPlaylistProvider = ({
4958
5001
  }),
4959
5002
  [isPlaying, currentTime]
4960
5003
  );
4961
- const stateValue = (0, import_react24.useMemo)(
5004
+ const stateValue = (0, import_react25.useMemo)(
4962
5005
  () => ({
4963
5006
  continuousPlay,
4964
5007
  annotations,
@@ -4968,9 +5011,9 @@ var MediaElementPlaylistProvider = ({
4968
5011
  }),
4969
5012
  [continuousPlay, annotations, activeAnnotationId, playbackRate, isAutomaticScroll]
4970
5013
  );
4971
- const onAnnotationsChangeRef = (0, import_react24.useRef)(onAnnotationsChange);
5014
+ const onAnnotationsChangeRef = (0, import_react25.useRef)(onAnnotationsChange);
4972
5015
  onAnnotationsChangeRef.current = onAnnotationsChange;
4973
- const setAnnotations = (0, import_react24.useCallback)(
5016
+ const setAnnotations = (0, import_react25.useCallback)(
4974
5017
  (action) => {
4975
5018
  const updated = typeof action === "function" ? action(annotationsRef.current) : action;
4976
5019
  if (!onAnnotationsChangeRef.current) {
@@ -4985,7 +5028,7 @@ var MediaElementPlaylistProvider = ({
4985
5028
  },
4986
5029
  []
4987
5030
  );
4988
- const controlsValue = (0, import_react24.useMemo)(
5031
+ const controlsValue = (0, import_react25.useMemo)(
4989
5032
  () => ({
4990
5033
  play,
4991
5034
  pause,
@@ -5013,7 +5056,7 @@ var MediaElementPlaylistProvider = ({
5013
5056
  setScrollContainer
5014
5057
  ]
5015
5058
  );
5016
- const dataValue = (0, import_react24.useMemo)(
5059
+ const dataValue = (0, import_react25.useMemo)(
5017
5060
  () => ({
5018
5061
  duration,
5019
5062
  peaksDataArray,
@@ -5048,28 +5091,28 @@ var MediaElementPlaylistProvider = ({
5048
5091
  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
5092
  };
5050
5093
  var useMediaElementAnimation = () => {
5051
- const context = (0, import_react24.useContext)(MediaElementAnimationContext);
5094
+ const context = (0, import_react25.useContext)(MediaElementAnimationContext);
5052
5095
  if (!context) {
5053
5096
  throw new Error("useMediaElementAnimation must be used within MediaElementPlaylistProvider");
5054
5097
  }
5055
5098
  return context;
5056
5099
  };
5057
5100
  var useMediaElementState = () => {
5058
- const context = (0, import_react24.useContext)(MediaElementStateContext);
5101
+ const context = (0, import_react25.useContext)(MediaElementStateContext);
5059
5102
  if (!context) {
5060
5103
  throw new Error("useMediaElementState must be used within MediaElementPlaylistProvider");
5061
5104
  }
5062
5105
  return context;
5063
5106
  };
5064
5107
  var useMediaElementControls = () => {
5065
- const context = (0, import_react24.useContext)(MediaElementControlsContext);
5108
+ const context = (0, import_react25.useContext)(MediaElementControlsContext);
5066
5109
  if (!context) {
5067
5110
  throw new Error("useMediaElementControls must be used within MediaElementPlaylistProvider");
5068
5111
  }
5069
5112
  return context;
5070
5113
  };
5071
5114
  var useMediaElementData = () => {
5072
- const context = (0, import_react24.useContext)(MediaElementDataContext);
5115
+ const context = (0, import_react25.useContext)(MediaElementDataContext);
5073
5116
  if (!context) {
5074
5117
  throw new Error("useMediaElementData must be used within MediaElementPlaylistProvider");
5075
5118
  }
@@ -5077,7 +5120,7 @@ var useMediaElementData = () => {
5077
5120
  };
5078
5121
 
5079
5122
  // src/components/PlaybackControls.tsx
5080
- var import_react25 = require("react");
5123
+ var import_react26 = require("react");
5081
5124
  var import_ui_components4 = require("@waveform-playlist/ui-components");
5082
5125
  var import_jsx_runtime3 = require("react/jsx-runtime");
5083
5126
  var PlayButton = ({ className }) => {
@@ -5213,7 +5256,7 @@ var ClearAllButton = ({
5213
5256
  className
5214
5257
  }) => {
5215
5258
  const { stop } = usePlaylistControls();
5216
- const handleClick = (0, import_react25.useCallback)(() => {
5259
+ const handleClick = (0, import_react26.useCallback)(() => {
5217
5260
  stop();
5218
5261
  onClearAll();
5219
5262
  }, [stop, onClearAll]);
@@ -5241,7 +5284,7 @@ var ZoomOutButton = ({
5241
5284
  };
5242
5285
 
5243
5286
  // src/components/ContextualControls.tsx
5244
- var import_react26 = require("react");
5287
+ var import_react27 = require("react");
5245
5288
  var import_ui_components6 = require("@waveform-playlist/ui-components");
5246
5289
  var import_styled_components3 = __toESM(require("styled-components"));
5247
5290
  var import_jsx_runtime5 = require("react/jsx-runtime");
@@ -5274,11 +5317,11 @@ var PositionDisplay = import_styled_components3.default.span`
5274
5317
  `;
5275
5318
  var AudioPosition = ({ className }) => {
5276
5319
  var _a;
5277
- const timeRef = (0, import_react26.useRef)(null);
5278
- const animationFrameRef = (0, import_react26.useRef)(null);
5320
+ const timeRef = (0, import_react27.useRef)(null);
5321
+ const animationFrameRef = (0, import_react27.useRef)(null);
5279
5322
  const { isPlaying, currentTimeRef, getPlaybackTime } = usePlaybackAnimation();
5280
5323
  const { timeFormat: format } = usePlaylistData();
5281
- (0, import_react26.useEffect)(() => {
5324
+ (0, import_react27.useEffect)(() => {
5282
5325
  const updateTime = () => {
5283
5326
  var _a2;
5284
5327
  if (timeRef.current) {
@@ -5301,7 +5344,7 @@ var AudioPosition = ({ className }) => {
5301
5344
  }
5302
5345
  };
5303
5346
  }, [isPlaying, format, currentTimeRef, getPlaybackTime]);
5304
- (0, import_react26.useEffect)(() => {
5347
+ (0, import_react27.useEffect)(() => {
5305
5348
  var _a2;
5306
5349
  if (!isPlaying && timeRef.current) {
5307
5350
  timeRef.current.textContent = (0, import_ui_components6.formatTime)((_a2 = currentTimeRef.current) != null ? _a2 : 0, format);
@@ -5336,11 +5379,11 @@ var AutomaticScrollCheckbox = ({ className }) => {
5336
5379
  };
5337
5380
 
5338
5381
  // src/AnnotationIntegrationContext.tsx
5339
- var import_react27 = require("react");
5340
- var AnnotationIntegrationContext = (0, import_react27.createContext)(null);
5382
+ var import_react28 = require("react");
5383
+ var AnnotationIntegrationContext = (0, import_react28.createContext)(null);
5341
5384
  var AnnotationIntegrationProvider = AnnotationIntegrationContext.Provider;
5342
5385
  function useAnnotationIntegration() {
5343
- const context = (0, import_react27.useContext)(AnnotationIntegrationContext);
5386
+ const context = (0, import_react28.useContext)(AnnotationIntegrationContext);
5344
5387
  if (!context) {
5345
5388
  throw new Error(
5346
5389
  "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 +5469,22 @@ var ExportWavButton = ({
5426
5469
  };
5427
5470
 
5428
5471
  // src/contexts/ClipInteractionContext.tsx
5429
- var import_react28 = require("react");
5430
- var ClipInteractionContext = (0, import_react28.createContext)(false);
5472
+ var import_react29 = require("react");
5473
+ var ClipInteractionContext = (0, import_react29.createContext)(false);
5431
5474
  var ClipInteractionContextProvider = ClipInteractionContext.Provider;
5432
5475
  function useClipInteractionEnabled() {
5433
- return (0, import_react28.useContext)(ClipInteractionContext);
5476
+ return (0, import_react29.useContext)(ClipInteractionContext);
5434
5477
  }
5435
5478
 
5436
5479
  // src/components/PlaylistVisualization.tsx
5437
- var import_react32 = require("react");
5480
+ var import_react33 = require("react");
5438
5481
  var import_react_dom = require("react-dom");
5439
5482
  var import_styled_components6 = __toESM(require("styled-components"));
5440
5483
  var import_playout6 = require("@waveform-playlist/playout");
5441
5484
  var import_ui_components9 = require("@waveform-playlist/ui-components");
5442
5485
 
5443
5486
  // src/components/AnimatedPlayhead.tsx
5444
- var import_react29 = require("react");
5487
+ var import_react30 = require("react");
5445
5488
  var import_styled_components4 = __toESM(require("styled-components"));
5446
5489
  var import_jsx_runtime8 = require("react/jsx-runtime");
5447
5490
  var PlayheadLine = import_styled_components4.default.div.attrs((props) => ({
@@ -5459,11 +5502,11 @@ var PlayheadLine = import_styled_components4.default.div.attrs((props) => ({
5459
5502
  will-change: transform;
5460
5503
  `;
5461
5504
  var AnimatedPlayhead = ({ color = "#ff0000" }) => {
5462
- const playheadRef = (0, import_react29.useRef)(null);
5463
- const animationFrameRef = (0, import_react29.useRef)(null);
5505
+ const playheadRef = (0, import_react30.useRef)(null);
5506
+ const animationFrameRef = (0, import_react30.useRef)(null);
5464
5507
  const { isPlaying, currentTimeRef, getPlaybackTime } = usePlaybackAnimation();
5465
5508
  const { samplesPerPixel, sampleRate, progressBarWidth } = usePlaylistData();
5466
- (0, import_react29.useEffect)(() => {
5509
+ (0, import_react30.useEffect)(() => {
5467
5510
  const updatePosition = () => {
5468
5511
  var _a;
5469
5512
  if (playheadRef.current) {
@@ -5487,7 +5530,7 @@ var AnimatedPlayhead = ({ color = "#ff0000" }) => {
5487
5530
  }
5488
5531
  };
5489
5532
  }, [isPlaying, sampleRate, samplesPerPixel, currentTimeRef, getPlaybackTime]);
5490
- (0, import_react29.useEffect)(() => {
5533
+ (0, import_react30.useEffect)(() => {
5491
5534
  var _a;
5492
5535
  if (!isPlaying && playheadRef.current) {
5493
5536
  const time = (_a = currentTimeRef.current) != null ? _a : 0;
@@ -5499,9 +5542,9 @@ var AnimatedPlayhead = ({ color = "#ff0000" }) => {
5499
5542
  };
5500
5543
 
5501
5544
  // src/components/ChannelWithProgress.tsx
5502
- var import_react30 = require("react");
5545
+ var import_react31 = require("react");
5503
5546
  var import_styled_components5 = __toESM(require("styled-components"));
5504
- var import_core4 = require("@waveform-playlist/core");
5547
+ var import_core6 = require("@waveform-playlist/core");
5505
5548
  var import_ui_components8 = require("@waveform-playlist/ui-components");
5506
5549
  var import_jsx_runtime9 = require("react/jsx-runtime");
5507
5550
  var ChannelWrapper = import_styled_components5.default.div`
@@ -5557,19 +5600,19 @@ var ChannelWithProgress = (_a) => {
5557
5600
  "clipSampleRate",
5558
5601
  "clipOffsetSeconds"
5559
5602
  ]);
5560
- const progressRef = (0, import_react30.useRef)(null);
5561
- const animationFrameRef = (0, import_react30.useRef)(null);
5603
+ const progressRef = (0, import_react31.useRef)(null);
5604
+ const animationFrameRef = (0, import_react31.useRef)(null);
5562
5605
  const theme = (0, import_ui_components8.useTheme)();
5563
5606
  const { waveHeight } = (0, import_ui_components8.usePlaylistInfo)();
5564
5607
  const { isPlaying, currentTimeRef, getPlaybackTime } = usePlaybackAnimation();
5565
5608
  const { samplesPerPixel, sampleRate } = usePlaylistData();
5566
5609
  const progressColor = (theme == null ? void 0 : theme.waveProgressColor) || "rgba(0, 0, 0, 0.1)";
5567
- const clipPixelWidth = (0, import_core4.clipPixelWidth)(
5610
+ const clipPixelWidth = (0, import_core6.clipPixelWidth)(
5568
5611
  clipStartSample,
5569
5612
  clipDurationSamples,
5570
5613
  samplesPerPixel
5571
5614
  );
5572
- (0, import_react30.useEffect)(() => {
5615
+ (0, import_react31.useEffect)(() => {
5573
5616
  const updateProgress = () => {
5574
5617
  var _a2;
5575
5618
  if (progressRef.current) {
@@ -5611,7 +5654,7 @@ var ChannelWithProgress = (_a) => {
5611
5654
  currentTimeRef,
5612
5655
  getPlaybackTime
5613
5656
  ]);
5614
- (0, import_react30.useEffect)(() => {
5657
+ (0, import_react31.useEffect)(() => {
5615
5658
  var _a2;
5616
5659
  if (!isPlaying && progressRef.current) {
5617
5660
  const currentTime = (_a2 = currentTimeRef.current) != null ? _a2 : 0;
@@ -5696,11 +5739,11 @@ var ChannelWithProgress = (_a) => {
5696
5739
  };
5697
5740
 
5698
5741
  // src/SpectrogramIntegrationContext.tsx
5699
- var import_react31 = require("react");
5700
- var SpectrogramIntegrationContext = (0, import_react31.createContext)(null);
5742
+ var import_react32 = require("react");
5743
+ var SpectrogramIntegrationContext = (0, import_react32.createContext)(null);
5701
5744
  var SpectrogramIntegrationProvider = SpectrogramIntegrationContext.Provider;
5702
5745
  function useSpectrogramIntegration() {
5703
- const context = (0, import_react31.useContext)(SpectrogramIntegrationContext);
5746
+ const context = (0, import_react32.useContext)(SpectrogramIntegrationContext);
5704
5747
  if (!context) {
5705
5748
  throw new Error(
5706
5749
  "useSpectrogramIntegration must be used within <SpectrogramProvider>. Install @waveform-playlist/spectrogram and wrap your app with <SpectrogramProvider>."
@@ -5780,7 +5823,7 @@ var PlaylistVisualization = ({
5780
5823
  isLoopEnabled,
5781
5824
  indefinitePlayback
5782
5825
  } = usePlaylistState();
5783
- const annotationIntegration = (0, import_react32.useContext)(AnnotationIntegrationContext);
5826
+ const annotationIntegration = (0, import_react33.useContext)(AnnotationIntegrationContext);
5784
5827
  const {
5785
5828
  setAnnotations: _setAnnotations,
5786
5829
  setActiveAnnotationId,
@@ -5810,8 +5853,8 @@ var PlaylistVisualization = ({
5810
5853
  isReady,
5811
5854
  mono
5812
5855
  } = usePlaylistData();
5813
- const spectrogram = (0, import_react32.useContext)(SpectrogramIntegrationContext);
5814
- const perTrackSpectrogramHelpers = (0, import_react32.useMemo)(() => {
5856
+ const spectrogram = (0, import_react33.useContext)(SpectrogramIntegrationContext);
5857
+ const perTrackSpectrogramHelpers = (0, import_react33.useMemo)(() => {
5815
5858
  if (!spectrogram)
5816
5859
  return /* @__PURE__ */ new Map();
5817
5860
  const helpers = /* @__PURE__ */ new Map();
@@ -5830,7 +5873,7 @@ var PlaylistVisualization = ({
5830
5873
  });
5831
5874
  return helpers;
5832
5875
  }, [tracks, spectrogram]);
5833
- const workerCanvasApi = (0, import_react32.useMemo)(() => {
5876
+ const workerCanvasApi = (0, import_react33.useMemo)(() => {
5834
5877
  if (!(spectrogram == null ? void 0 : spectrogram.spectrogramWorkerApi)) return void 0;
5835
5878
  return {
5836
5879
  registerCanvas: spectrogram.spectrogramWorkerApi.registerCanvas.bind(
@@ -5841,11 +5884,11 @@ var PlaylistVisualization = ({
5841
5884
  )
5842
5885
  };
5843
5886
  }, [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)(
5887
+ const [settingsModalTrackId, setSettingsModalTrackId] = (0, import_react33.useState)(null);
5888
+ const [isSelecting, setIsSelecting] = (0, import_react33.useState)(false);
5889
+ const mouseDownTimeRef = (0, import_react33.useRef)(0);
5890
+ const scrollContainerRef = (0, import_react33.useRef)(null);
5891
+ const handleScrollContainerRef = (0, import_react33.useCallback)(
5849
5892
  (element) => {
5850
5893
  scrollContainerRef.current = element;
5851
5894
  setScrollContainer(element);
@@ -5883,7 +5926,7 @@ var PlaylistVisualization = ({
5883
5926
  );
5884
5927
  }
5885
5928
  });
5886
- const selectTrack = (0, import_react32.useCallback)(
5929
+ const selectTrack = (0, import_react33.useCallback)(
5887
5930
  (trackIndex) => {
5888
5931
  if (trackIndex >= 0 && trackIndex < tracks.length) {
5889
5932
  const track = tracks[trackIndex];
@@ -6324,7 +6367,7 @@ var PlaylistVisualization = ({
6324
6367
  };
6325
6368
 
6326
6369
  // src/components/PlaylistAnnotationList.tsx
6327
- var import_react33 = require("react");
6370
+ var import_react34 = require("react");
6328
6371
  var import_jsx_runtime11 = require("react/jsx-runtime");
6329
6372
  var PlaylistAnnotationList = ({
6330
6373
  height,
@@ -6339,7 +6382,7 @@ var PlaylistAnnotationList = ({
6339
6382
  const integration = useAnnotationIntegration();
6340
6383
  const { setAnnotations } = usePlaylistControls();
6341
6384
  const resolvedConfig = annotationListConfig != null ? annotationListConfig : { linkEndpoints, continuousPlay };
6342
- const handleAnnotationUpdate = (0, import_react33.useCallback)(
6385
+ const handleAnnotationUpdate = (0, import_react34.useCallback)(
6343
6386
  (updatedAnnotations) => {
6344
6387
  setAnnotations(updatedAnnotations);
6345
6388
  onAnnotationUpdate == null ? void 0 : onAnnotationUpdate(updatedAnnotations);
@@ -6423,8 +6466,8 @@ var Waveform = ({
6423
6466
  };
6424
6467
 
6425
6468
  // src/components/MediaElementPlaylist.tsx
6426
- var import_react36 = require("react");
6427
- var import_react37 = require("@dnd-kit/react");
6469
+ var import_react37 = require("react");
6470
+ var import_react38 = require("@dnd-kit/react");
6428
6471
  var import_modifiers = require("@dnd-kit/abstract/modifiers");
6429
6472
  var import_ui_components11 = require("@waveform-playlist/ui-components");
6430
6473
 
@@ -6448,7 +6491,7 @@ var noDropAnimationPlugins = (defaults) => {
6448
6491
  };
6449
6492
 
6450
6493
  // src/components/AnimatedMediaElementPlayhead.tsx
6451
- var import_react34 = require("react");
6494
+ var import_react35 = require("react");
6452
6495
  var import_styled_components7 = __toESM(require("styled-components"));
6453
6496
  var import_jsx_runtime13 = require("react/jsx-runtime");
6454
6497
  var PlayheadLine2 = import_styled_components7.default.div`
@@ -6465,11 +6508,11 @@ var PlayheadLine2 = import_styled_components7.default.div`
6465
6508
  var AnimatedMediaElementPlayhead = ({
6466
6509
  color = "#ff0000"
6467
6510
  }) => {
6468
- const playheadRef = (0, import_react34.useRef)(null);
6469
- const animationFrameRef = (0, import_react34.useRef)(null);
6511
+ const playheadRef = (0, import_react35.useRef)(null);
6512
+ const animationFrameRef = (0, import_react35.useRef)(null);
6470
6513
  const { isPlaying, currentTimeRef } = useMediaElementAnimation();
6471
6514
  const { samplesPerPixel, sampleRate, progressBarWidth } = useMediaElementData();
6472
- (0, import_react34.useEffect)(() => {
6515
+ (0, import_react35.useEffect)(() => {
6473
6516
  const updatePosition = () => {
6474
6517
  var _a;
6475
6518
  if (playheadRef.current) {
@@ -6493,7 +6536,7 @@ var AnimatedMediaElementPlayhead = ({
6493
6536
  }
6494
6537
  };
6495
6538
  }, [isPlaying, sampleRate, samplesPerPixel, currentTimeRef]);
6496
- (0, import_react34.useEffect)(() => {
6539
+ (0, import_react35.useEffect)(() => {
6497
6540
  var _a;
6498
6541
  if (!isPlaying && playheadRef.current) {
6499
6542
  const time = (_a = currentTimeRef.current) != null ? _a : 0;
@@ -6505,7 +6548,7 @@ var AnimatedMediaElementPlayhead = ({
6505
6548
  };
6506
6549
 
6507
6550
  // src/components/ChannelWithMediaElementProgress.tsx
6508
- var import_react35 = require("react");
6551
+ var import_react36 = require("react");
6509
6552
  var import_styled_components8 = __toESM(require("styled-components"));
6510
6553
  var import_ui_components10 = require("@waveform-playlist/ui-components");
6511
6554
  var import_jsx_runtime14 = require("react/jsx-runtime");
@@ -6545,14 +6588,14 @@ var ChannelWithMediaElementProgress = (_a) => {
6545
6588
  "clipStartSample",
6546
6589
  "clipDurationSamples"
6547
6590
  ]);
6548
- const progressRef = (0, import_react35.useRef)(null);
6549
- const animationFrameRef = (0, import_react35.useRef)(null);
6591
+ const progressRef = (0, import_react36.useRef)(null);
6592
+ const animationFrameRef = (0, import_react36.useRef)(null);
6550
6593
  const theme = (0, import_ui_components10.useTheme)();
6551
6594
  const { waveHeight } = (0, import_ui_components10.usePlaylistInfo)();
6552
6595
  const { isPlaying, currentTimeRef } = useMediaElementAnimation();
6553
6596
  const { samplesPerPixel, sampleRate } = useMediaElementData();
6554
6597
  const progressColor = (theme == null ? void 0 : theme.waveProgressColor) || "rgba(0, 0, 0, 0.1)";
6555
- (0, import_react35.useEffect)(() => {
6598
+ (0, import_react36.useEffect)(() => {
6556
6599
  const updateProgress = () => {
6557
6600
  var _a2;
6558
6601
  if (progressRef.current) {
@@ -6594,7 +6637,7 @@ var ChannelWithMediaElementProgress = (_a) => {
6594
6637
  smartChannelProps.length,
6595
6638
  currentTimeRef
6596
6639
  ]);
6597
- (0, import_react35.useEffect)(() => {
6640
+ (0, import_react36.useEffect)(() => {
6598
6641
  var _a2;
6599
6642
  if (!isPlaying && progressRef.current) {
6600
6643
  const currentTime = (_a2 = currentTimeRef.current) != null ? _a2 : 0;
@@ -6673,7 +6716,7 @@ var MediaElementPlaylist = ({
6673
6716
  const theme = (0, import_ui_components11.useTheme)();
6674
6717
  const { isPlaying } = useMediaElementAnimation();
6675
6718
  const { annotations, activeAnnotationId } = useMediaElementState();
6676
- const annotationIntegration = (0, import_react36.useContext)(AnnotationIntegrationContext);
6719
+ const annotationIntegration = (0, import_react37.useContext)(AnnotationIntegrationContext);
6677
6720
  const { play, seekTo, setActiveAnnotationId, setAnnotations, setScrollContainer } = useMediaElementControls();
6678
6721
  const {
6679
6722
  duration,
@@ -6689,11 +6732,11 @@ var MediaElementPlaylist = ({
6689
6732
  fadeIn,
6690
6733
  fadeOut
6691
6734
  } = 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)(
6735
+ const [selectionStart, setSelectionStart] = (0, import_react37.useState)(0);
6736
+ const [selectionEnd, setSelectionEnd] = (0, import_react37.useState)(0);
6737
+ const [isSelecting, setIsSelecting] = (0, import_react37.useState)(false);
6738
+ const scrollContainerRef = (0, import_react37.useRef)(null);
6739
+ const handleScrollContainerRef = (0, import_react37.useCallback)(
6697
6740
  (el) => {
6698
6741
  scrollContainerRef.current = el;
6699
6742
  setScrollContainer(el);
@@ -6701,7 +6744,7 @@ var MediaElementPlaylist = ({
6701
6744
  [setScrollContainer]
6702
6745
  );
6703
6746
  const tracksFullWidth = Math.floor(duration * sampleRate / samplesPerPixel);
6704
- const handleAnnotationClick = (0, import_react36.useCallback)(
6747
+ const handleAnnotationClick = (0, import_react37.useCallback)(
6705
6748
  (annotation) => __async(null, null, function* () {
6706
6749
  setActiveAnnotationId(annotation.id);
6707
6750
  try {
@@ -6716,7 +6759,7 @@ var MediaElementPlaylist = ({
6716
6759
  }),
6717
6760
  [setActiveAnnotationId, play]
6718
6761
  );
6719
- const handleAnnotationUpdate = (0, import_react36.useCallback)(
6762
+ const handleAnnotationUpdate = (0, import_react37.useCallback)(
6720
6763
  (updatedAnnotations) => {
6721
6764
  setAnnotations(updatedAnnotations);
6722
6765
  onAnnotationUpdate == null ? void 0 : onAnnotationUpdate(updatedAnnotations);
@@ -6730,8 +6773,8 @@ var MediaElementPlaylist = ({
6730
6773
  duration,
6731
6774
  linkEndpoints: linkEndpointsProp
6732
6775
  });
6733
- const mouseDownTimeRef = (0, import_react36.useRef)(0);
6734
- const handleMouseDown = (0, import_react36.useCallback)(
6776
+ const mouseDownTimeRef = (0, import_react37.useRef)(0);
6777
+ const handleMouseDown = (0, import_react37.useCallback)(
6735
6778
  (e) => {
6736
6779
  const rect = e.currentTarget.getBoundingClientRect();
6737
6780
  const x = e.clientX - rect.left;
@@ -6743,7 +6786,7 @@ var MediaElementPlaylist = ({
6743
6786
  },
6744
6787
  [samplesPerPixel, sampleRate]
6745
6788
  );
6746
- const handleMouseMove = (0, import_react36.useCallback)(
6789
+ const handleMouseMove = (0, import_react37.useCallback)(
6747
6790
  (e) => {
6748
6791
  if (!isSelecting) return;
6749
6792
  const rect = e.currentTarget.getBoundingClientRect();
@@ -6753,7 +6796,7 @@ var MediaElementPlaylist = ({
6753
6796
  },
6754
6797
  [isSelecting, samplesPerPixel, sampleRate]
6755
6798
  );
6756
- const handleMouseUp = (0, import_react36.useCallback)(
6799
+ const handleMouseUp = (0, import_react37.useCallback)(
6757
6800
  (e) => {
6758
6801
  if (!isSelecting) return;
6759
6802
  setIsSelecting(false);
@@ -6885,7 +6928,7 @@ var MediaElementPlaylist = ({
6885
6928
  );
6886
6929
  }),
6887
6930
  annotations.length > 0 && annotationIntegration && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
6888
- import_react37.DragDropProvider,
6931
+ import_react38.DragDropProvider,
6889
6932
  {
6890
6933
  onDragStart,
6891
6934
  onDragMove,
@@ -6939,7 +6982,7 @@ var MediaElementPlaylist = ({
6939
6982
  };
6940
6983
 
6941
6984
  // src/components/MediaElementAnnotationList.tsx
6942
- var import_react38 = require("react");
6985
+ var import_react39 = require("react");
6943
6986
  var import_jsx_runtime16 = require("react/jsx-runtime");
6944
6987
  var MediaElementAnnotationList = ({
6945
6988
  height,
@@ -6955,7 +6998,7 @@ var MediaElementAnnotationList = ({
6955
6998
  const integration = useAnnotationIntegration();
6956
6999
  const { setAnnotations } = useMediaElementControls();
6957
7000
  const resolvedConfig = annotationListConfig != null ? annotationListConfig : { linkEndpoints: false, continuousPlay };
6958
- const handleAnnotationUpdate = (0, import_react38.useCallback)(
7001
+ const handleAnnotationUpdate = (0, import_react39.useCallback)(
6959
7002
  (updatedAnnotations) => {
6960
7003
  setAnnotations(updatedAnnotations);
6961
7004
  onAnnotationUpdate == null ? void 0 : onAnnotationUpdate(updatedAnnotations);
@@ -7030,6 +7073,7 @@ var KeyboardShortcuts = ({
7030
7073
  playback = false,
7031
7074
  clipSplitting = false,
7032
7075
  annotations = false,
7076
+ undo: undoEnabled = false,
7033
7077
  additionalShortcuts = []
7034
7078
  }) => {
7035
7079
  const { tracks, samplesPerPixel, playoutRef, duration } = usePlaylistData();
@@ -7039,7 +7083,7 @@ var KeyboardShortcuts = ({
7039
7083
  activeAnnotationId,
7040
7084
  continuousPlay
7041
7085
  } = usePlaylistState();
7042
- const { setAnnotations, setActiveAnnotationId, scrollContainerRef, play } = usePlaylistControls();
7086
+ const { setAnnotations, setActiveAnnotationId, scrollContainerRef, play, undo, redo } = usePlaylistControls();
7043
7087
  const { splitClipAtPlayhead } = useClipSplitting({
7044
7088
  tracks,
7045
7089
  samplesPerPixel,
@@ -7054,6 +7098,14 @@ var KeyboardShortcuts = ({
7054
7098
  preventDefault: true
7055
7099
  });
7056
7100
  }
7101
+ if (undoEnabled) {
7102
+ allAdditional.push(
7103
+ { key: "z", ctrlKey: true, shiftKey: false, action: undo, description: "Undo" },
7104
+ { key: "z", metaKey: true, shiftKey: false, action: undo, description: "Undo" },
7105
+ { key: "z", ctrlKey: true, shiftKey: true, action: redo, description: "Redo" },
7106
+ { key: "z", metaKey: true, shiftKey: true, action: redo, description: "Redo" }
7107
+ );
7108
+ }
7057
7109
  if (additionalShortcuts.length > 0) {
7058
7110
  allAdditional.push(...additionalShortcuts);
7059
7111
  }
@@ -7076,10 +7128,10 @@ var KeyboardShortcuts = ({
7076
7128
  };
7077
7129
 
7078
7130
  // src/components/ClipInteractionProvider.tsx
7079
- var import_react39 = __toESM(require("react"));
7080
- var import_react40 = require("@dnd-kit/react");
7131
+ var import_react40 = __toESM(require("react"));
7132
+ var import_react41 = require("@dnd-kit/react");
7081
7133
  var import_modifiers2 = require("@dnd-kit/abstract/modifiers");
7082
- var import_core6 = require("@waveform-playlist/core");
7134
+ var import_core8 = require("@waveform-playlist/core");
7083
7135
  var import_ui_components12 = require("@waveform-playlist/ui-components");
7084
7136
 
7085
7137
  // src/modifiers/ClipCollisionModifier.ts
@@ -7108,7 +7160,7 @@ var ClipCollisionModifier = _ClipCollisionModifier;
7108
7160
 
7109
7161
  // src/modifiers/SnapToGridModifier.ts
7110
7162
  var import_abstract2 = require("@dnd-kit/abstract");
7111
- var import_core5 = require("@waveform-playlist/core");
7163
+ var import_core7 = require("@waveform-playlist/core");
7112
7164
  var _SnapToGridModifier = class _SnapToGridModifier extends import_abstract2.Modifier {
7113
7165
  apply(operation) {
7114
7166
  const { transform, source } = operation;
@@ -7129,18 +7181,18 @@ var _SnapToGridModifier = class _SnapToGridModifier extends import_abstract2.Mod
7129
7181
  }
7130
7182
  const { snapTo, bpm, timeSignature, sampleRate } = this.options;
7131
7183
  if (snapTo === "off") return transform;
7132
- const gridTicks = snapTo === "bar" ? (0, import_core5.ticksPerBar)(timeSignature) : (0, import_core5.ticksPerBeat)(timeSignature);
7184
+ const gridTicks = snapTo === "bar" ? (0, import_core7.ticksPerBar)(timeSignature) : (0, import_core7.ticksPerBeat)(timeSignature);
7133
7185
  if (startSample !== void 0) {
7134
7186
  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);
7187
+ const proposedTicks = (0, import_core7.samplesToTicks)(proposedSamples, bpm, sampleRate);
7188
+ const snappedTicks2 = (0, import_core7.snapToGrid)(proposedTicks, gridTicks);
7189
+ const snappedSamples2 = (0, import_core7.ticksToSamples)(snappedTicks2, bpm, sampleRate);
7138
7190
  return { x: (snappedSamples2 - startSample) / samplesPerPixel, y: 0 };
7139
7191
  }
7140
7192
  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);
7193
+ const deltaTicks = (0, import_core7.samplesToTicks)(deltaSamples, bpm, sampleRate);
7194
+ const snappedTicks = (0, import_core7.snapToGrid)(deltaTicks, gridTicks);
7195
+ const snappedSamples = (0, import_core7.ticksToSamples)(snappedTicks, bpm, sampleRate);
7144
7196
  return { x: snappedSamples / samplesPerPixel, y: 0 };
7145
7197
  }
7146
7198
  };
@@ -7161,21 +7213,21 @@ var ClipInteractionProvider = ({
7161
7213
  const beatsAndBars = (0, import_ui_components12.useBeatsAndBars)();
7162
7214
  const useBeatsSnap = snap && beatsAndBars != null && beatsAndBars.scaleMode === "beats" && beatsAndBars.snapTo !== "off";
7163
7215
  const useTimescaleSnap = snap && !useBeatsSnap;
7164
- (0, import_react39.useEffect)(() => {
7216
+ (0, import_react40.useEffect)(() => {
7165
7217
  if (onTracksChange == null) {
7166
7218
  console.warn(
7167
7219
  "[waveform-playlist] ClipInteractionProvider: onTracksChange is not set on WaveformPlaylistProvider. Drag and trim edits will not be persisted."
7168
7220
  );
7169
7221
  }
7170
7222
  }, [onTracksChange]);
7171
- const snapSamplePosition = (0, import_react39.useMemo)(() => {
7223
+ const snapSamplePosition = (0, import_react40.useMemo)(() => {
7172
7224
  if (useBeatsSnap && beatsAndBars) {
7173
7225
  const { bpm, timeSignature, snapTo } = beatsAndBars;
7174
- const gridTicks = snapTo === "bar" ? (0, import_core6.ticksPerBar)(timeSignature) : (0, import_core6.ticksPerBeat)(timeSignature);
7226
+ const gridTicks = snapTo === "bar" ? (0, import_core8.ticksPerBar)(timeSignature) : (0, import_core8.ticksPerBeat)(timeSignature);
7175
7227
  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);
7228
+ const ticks = (0, import_core8.samplesToTicks)(samplePos, bpm, sampleRate);
7229
+ const snapped = (0, import_core8.snapToGrid)(ticks, gridTicks);
7230
+ return (0, import_core8.ticksToSamples)(snapped, bpm, sampleRate);
7179
7231
  };
7180
7232
  }
7181
7233
  if (useTimescaleSnap) {
@@ -7197,7 +7249,7 @@ var ClipInteractionProvider = ({
7197
7249
  isDraggingRef,
7198
7250
  snapSamplePosition
7199
7251
  });
7200
- const onDragStart = import_react39.default.useCallback(
7252
+ const onDragStart = import_react40.default.useCallback(
7201
7253
  (event) => {
7202
7254
  var _a, _b, _c;
7203
7255
  const trackIndex = (_c = (_b = (_a = event.operation) == null ? void 0 : _a.source) == null ? void 0 : _b.data) == null ? void 0 : _c.trackIndex;
@@ -7208,7 +7260,7 @@ var ClipInteractionProvider = ({
7208
7260
  },
7209
7261
  [handleDragStart, tracks, setSelectedTrackId]
7210
7262
  );
7211
- const modifiers = (0, import_react39.useMemo)(() => {
7263
+ const modifiers = (0, import_react40.useMemo)(() => {
7212
7264
  const mods = [import_modifiers2.RestrictToHorizontalAxis];
7213
7265
  if (useBeatsSnap && beatsAndBars) {
7214
7266
  mods.push(
@@ -7234,7 +7286,7 @@ var ClipInteractionProvider = ({
7234
7286
  return mods;
7235
7287
  }, [useBeatsSnap, useTimescaleSnap, beatsAndBars, tracks, samplesPerPixel, sampleRate]);
7236
7288
  return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ClipInteractionContextProvider, { value: true, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
7237
- import_react40.DragDropProvider,
7289
+ import_react41.DragDropProvider,
7238
7290
  {
7239
7291
  sensors,
7240
7292
  onDragStart,