@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.mjs CHANGED
@@ -57,10 +57,10 @@ import * as Tone2 from "tone";
57
57
  import {
58
58
  createContext,
59
59
  useContext,
60
- useState as useState14,
60
+ useState as useState15,
61
61
  useEffect as useEffect10,
62
- useRef as useRef14,
63
- useCallback as useCallback18,
62
+ useRef as useRef15,
63
+ useCallback as useCallback19,
64
64
  useMemo as useMemo4
65
65
  } from "react";
66
66
  import { ThemeProvider } from "styled-components";
@@ -451,12 +451,52 @@ function useSelectedTrack({ engineRef }) {
451
451
  };
452
452
  }
453
453
 
454
+ // src/hooks/useUndoState.ts
455
+ import { useState as useState7, useCallback as useCallback6, useRef as useRef6 } from "react";
456
+ function useUndoState({ engineRef }) {
457
+ const [canUndo, setCanUndo] = useState7(false);
458
+ const [canRedo, setCanRedo] = useState7(false);
459
+ const canUndoRef = useRef6(false);
460
+ const canRedoRef = useRef6(false);
461
+ const undo = useCallback6(() => {
462
+ if (!engineRef.current) {
463
+ console.warn("[waveform-playlist] undo: engine not ready, call ignored");
464
+ return;
465
+ }
466
+ engineRef.current.undo();
467
+ }, [engineRef]);
468
+ const redo = useCallback6(() => {
469
+ if (!engineRef.current) {
470
+ console.warn("[waveform-playlist] redo: engine not ready, call ignored");
471
+ return;
472
+ }
473
+ engineRef.current.redo();
474
+ }, [engineRef]);
475
+ const onEngineState = useCallback6((state) => {
476
+ if (state.canUndo !== canUndoRef.current) {
477
+ canUndoRef.current = state.canUndo;
478
+ setCanUndo(state.canUndo);
479
+ }
480
+ if (state.canRedo !== canRedoRef.current) {
481
+ canRedoRef.current = state.canRedo;
482
+ setCanRedo(state.canRedo);
483
+ }
484
+ }, []);
485
+ return {
486
+ canUndo,
487
+ canRedo,
488
+ undo,
489
+ redo,
490
+ onEngineState
491
+ };
492
+ }
493
+
454
494
  // src/hooks/useAudioEffects.ts
455
- import { useRef as useRef6, useCallback as useCallback6 } from "react";
495
+ import { useRef as useRef7, useCallback as useCallback7 } from "react";
456
496
  import { Analyser } from "tone";
457
497
  var useMasterAnalyser = (fftSize = 256) => {
458
- const analyserRef = useRef6(null);
459
- const masterEffects = useCallback6(
498
+ const analyserRef = useRef7(null);
499
+ const masterEffects = useCallback7(
460
500
  (masterGainNode, destination, _isOffline) => {
461
501
  const analyserNode = new Analyser("fft", fftSize);
462
502
  masterGainNode.connect(analyserNode);
@@ -473,7 +513,7 @@ var useMasterAnalyser = (fftSize = 256) => {
473
513
  };
474
514
 
475
515
  // src/hooks/useAudioTracks.ts
476
- import { useState as useState7, useEffect, useRef as useRef7, useMemo } from "react";
516
+ import { useState as useState8, useEffect, useRef as useRef8, useMemo } from "react";
477
517
  import {
478
518
  createTrack,
479
519
  createClipFromSeconds
@@ -534,13 +574,13 @@ function buildTrackFromConfig(config, index, audioBuffer, stableIds, contextSamp
534
574
  function useAudioTracks(configs, options = {}) {
535
575
  const { immediate = false, progressive = false } = options;
536
576
  const isImmediate = immediate || progressive;
537
- const [loading, setLoading] = useState7(true);
538
- const [error, setError] = useState7(null);
539
- const [loadedCount, setLoadedCount] = useState7(0);
577
+ const [loading, setLoading] = useState8(true);
578
+ const [error, setError] = useState8(null);
579
+ const [loadedCount, setLoadedCount] = useState8(0);
540
580
  const totalCount = configs.length;
541
- const [loadedBuffers, setLoadedBuffers] = useState7(/* @__PURE__ */ new Map());
542
- const stableIdsRef = useRef7(/* @__PURE__ */ new Map());
543
- const contextSampleRateRef = useRef7(48e3);
581
+ const [loadedBuffers, setLoadedBuffers] = useState8(/* @__PURE__ */ new Map());
582
+ const stableIdsRef = useRef8(/* @__PURE__ */ new Map());
583
+ const contextSampleRateRef = useRef8(48e3);
544
584
  const derivedTracks = useMemo(() => {
545
585
  if (!isImmediate) return null;
546
586
  const result = [];
@@ -556,8 +596,8 @@ function useAudioTracks(configs, options = {}) {
556
596
  }
557
597
  return result;
558
598
  }, [isImmediate, configs, loadedBuffers]);
559
- const [tracks, setTracks] = useState7(derivedTracks != null ? derivedTracks : []);
560
- const prevDerivedRef = useRef7(derivedTracks);
599
+ const [tracks, setTracks] = useState8(derivedTracks != null ? derivedTracks : []);
600
+ const prevDerivedRef = useRef8(derivedTracks);
561
601
  if (derivedTracks !== prevDerivedRef.current) {
562
602
  prevDerivedRef.current = derivedTracks;
563
603
  if (derivedTracks) setTracks(derivedTracks);
@@ -735,6 +775,13 @@ function useClipDragHandlers({
735
775
  if (!data) return;
736
776
  if (!data.boundary) {
737
777
  originalClipStateRef.current = null;
778
+ if (engineRef.current) {
779
+ engineRef.current.beginTransaction();
780
+ } else {
781
+ console.warn(
782
+ "[waveform-playlist] onDragStart: engine not ready, move will not be grouped for undo"
783
+ );
784
+ }
738
785
  return;
739
786
  }
740
787
  const track = tracks[data.trackIndex];
@@ -746,9 +793,16 @@ function useClipDragHandlers({
746
793
  startSample: clip.startSample
747
794
  };
748
795
  isDraggingRef.current = true;
796
+ if (engineRef.current) {
797
+ engineRef.current.beginTransaction();
798
+ } else {
799
+ console.warn(
800
+ "[waveform-playlist] onDragStart: engine not ready, trim will not be grouped for undo"
801
+ );
802
+ }
749
803
  }
750
804
  },
751
- [tracks, isDraggingRef]
805
+ [tracks, isDraggingRef, engineRef]
752
806
  );
753
807
  const onDragMove = React.useCallback(
754
808
  (event) => {
@@ -814,7 +868,7 @@ function useClipDragHandlers({
814
868
  );
815
869
  const onDragEnd = React.useCallback(
816
870
  (event) => {
817
- var _a, _b, _c;
871
+ var _a, _b, _c, _d, _e, _f, _g;
818
872
  if (event.canceled) {
819
873
  if (originalClipStateRef.current) {
820
874
  const cancelData = (_a = event.operation.source) == null ? void 0 : _a.data;
@@ -839,23 +893,30 @@ function useClipDragHandlers({
839
893
  isDraggingRef.current = false;
840
894
  originalClipStateRef.current = null;
841
895
  lastBoundaryDeltaRef.current = 0;
896
+ (_b = engineRef.current) == null ? void 0 : _b.abortTransaction();
897
+ return;
898
+ }
899
+ const data = (_c = event.operation.source) == null ? void 0 : _c.data;
900
+ if (!data) {
901
+ isDraggingRef.current = false;
902
+ (_d = engineRef.current) == null ? void 0 : _d.abortTransaction();
842
903
  return;
843
904
  }
844
- const data = (_b = event.operation.source) == null ? void 0 : _b.data;
845
- if (!data) return;
846
905
  const { trackIndex, clipId, boundary } = data;
847
906
  const sampleDelta = boundary ? lastBoundaryDeltaRef.current : event.operation.transform.x * samplesPerPixel;
848
- const trackId = (_c = tracks[trackIndex]) == null ? void 0 : _c.id;
907
+ const trackId = (_e = tracks[trackIndex]) == null ? void 0 : _e.id;
849
908
  if (boundary) {
850
909
  isDraggingRef.current = false;
851
910
  if (!trackId) {
852
911
  console.warn(
853
912
  `[waveform-playlist] onDragEnd: track at index ${trackIndex} not found \u2014 trim not synced to adapter`
854
913
  );
914
+ (_f = engineRef.current) == null ? void 0 : _f.abortTransaction();
855
915
  } else if (!engineRef.current) {
856
916
  console.warn("[waveform-playlist] engineRef is null \u2014 trim not synced to adapter");
857
917
  } else {
858
918
  engineRef.current.trimClip(trackId, clipId, boundary, Math.floor(sampleDelta));
919
+ engineRef.current.commitTransaction();
859
920
  }
860
921
  originalClipStateRef.current = null;
861
922
  lastBoundaryDeltaRef.current = 0;
@@ -865,10 +926,12 @@ function useClipDragHandlers({
865
926
  console.warn(
866
927
  `[waveform-playlist] onDragEnd: track at index ${trackIndex} not found \u2014 move not synced to adapter`
867
928
  );
929
+ (_g = engineRef.current) == null ? void 0 : _g.abortTransaction();
868
930
  } else if (!engineRef.current) {
869
931
  console.warn("[waveform-playlist] engineRef is null \u2014 move not synced to adapter");
870
932
  } else {
871
933
  engineRef.current.moveClip(trackId, clipId, Math.floor(sampleDelta));
934
+ engineRef.current.commitTransaction();
872
935
  }
873
936
  },
874
937
  [tracks, onTracksChange, samplesPerPixel, engineRef, isDraggingRef]
@@ -1084,14 +1147,14 @@ function useDragSensors(options = {}) {
1084
1147
  }
1085
1148
 
1086
1149
  // src/hooks/useClipSplitting.ts
1087
- import { useCallback as useCallback7 } from "react";
1150
+ import { useCallback as useCallback8 } from "react";
1088
1151
  import { calculateSplitPoint, canSplitAt } from "@waveform-playlist/engine";
1089
1152
  var useClipSplitting = (options) => {
1090
1153
  const { tracks, engineRef } = options;
1091
1154
  const { sampleRate } = usePlaylistData();
1092
1155
  const { currentTimeRef } = usePlaybackAnimation();
1093
1156
  const { selectedTrackId } = usePlaylistState();
1094
- const splitClipAt = useCallback7(
1157
+ const splitClipAt = useCallback8(
1095
1158
  (trackIndex, clipIndex, splitTime) => {
1096
1159
  const { samplesPerPixel } = options;
1097
1160
  const track = tracks[trackIndex];
@@ -1115,7 +1178,7 @@ var useClipSplitting = (options) => {
1115
1178
  },
1116
1179
  [tracks, options, engineRef, sampleRate]
1117
1180
  );
1118
- const splitClipAtPlayhead = useCallback7(() => {
1181
+ const splitClipAtPlayhead = useCallback8(() => {
1119
1182
  var _a;
1120
1183
  if (!selectedTrackId) {
1121
1184
  console.warn("[waveform-playlist] No track selected \u2014 click a clip to select a track first");
@@ -1146,32 +1209,12 @@ var useClipSplitting = (options) => {
1146
1209
  };
1147
1210
 
1148
1211
  // src/hooks/useKeyboardShortcuts.ts
1149
- import { useEffect as useEffect2, useCallback as useCallback8 } from "react";
1150
- function handleKeyboardEvent(event, shortcuts, enabled) {
1151
- if (!enabled) return;
1152
- if (event.repeat) return;
1153
- const target = event.target;
1154
- if (target.tagName === "INPUT" || target.tagName === "TEXTAREA" || target.isContentEditable) {
1155
- return;
1156
- }
1157
- const matchingShortcut = shortcuts.find((shortcut) => {
1158
- const keyMatch = event.key.toLowerCase() === shortcut.key.toLowerCase() || event.key === shortcut.key;
1159
- const ctrlMatch = shortcut.ctrlKey === void 0 || event.ctrlKey === shortcut.ctrlKey;
1160
- const shiftMatch = shortcut.shiftKey === void 0 || event.shiftKey === shortcut.shiftKey;
1161
- const metaMatch = shortcut.metaKey === void 0 || event.metaKey === shortcut.metaKey;
1162
- const altMatch = shortcut.altKey === void 0 || event.altKey === shortcut.altKey;
1163
- return keyMatch && ctrlMatch && shiftMatch && metaMatch && altMatch;
1164
- });
1165
- if (matchingShortcut) {
1166
- if (matchingShortcut.preventDefault !== false) {
1167
- event.preventDefault();
1168
- }
1169
- matchingShortcut.action();
1170
- }
1171
- }
1212
+ import { useEffect as useEffect2, useCallback as useCallback9 } from "react";
1213
+ import { handleKeyboardEvent } from "@waveform-playlist/core";
1214
+ import { handleKeyboardEvent as handleKeyboardEvent2, getShortcutLabel } from "@waveform-playlist/core";
1172
1215
  var useKeyboardShortcuts = (options) => {
1173
1216
  const { shortcuts, enabled = true } = options;
1174
- const handleKeyDown = useCallback8(
1217
+ const handleKeyDown = useCallback9(
1175
1218
  (event) => handleKeyboardEvent(event, shortcuts, enabled),
1176
1219
  [shortcuts, enabled]
1177
1220
  );
@@ -1183,42 +1226,24 @@ var useKeyboardShortcuts = (options) => {
1183
1226
  };
1184
1227
  }, [handleKeyDown, enabled]);
1185
1228
  };
1186
- var getShortcutLabel = (shortcut) => {
1187
- const parts = [];
1188
- const isMac = typeof navigator !== "undefined" && navigator.platform.includes("Mac");
1189
- if (shortcut.metaKey) {
1190
- parts.push(isMac ? "Cmd" : "Ctrl");
1191
- }
1192
- if (shortcut.ctrlKey && !shortcut.metaKey) {
1193
- parts.push("Ctrl");
1194
- }
1195
- if (shortcut.altKey) {
1196
- parts.push(isMac ? "Option" : "Alt");
1197
- }
1198
- if (shortcut.shiftKey) {
1199
- parts.push("Shift");
1200
- }
1201
- parts.push(shortcut.key.toUpperCase());
1202
- return parts.join("+");
1203
- };
1204
1229
 
1205
1230
  // src/hooks/usePlaybackShortcuts.ts
1206
- import { useCallback as useCallback9 } from "react";
1231
+ import { useCallback as useCallback10 } from "react";
1207
1232
  var usePlaybackShortcuts = (options = {}) => {
1208
1233
  const { enabled = true, additionalShortcuts = [], shortcuts: overrideShortcuts } = options;
1209
1234
  const { isPlaying } = usePlaybackAnimation();
1210
1235
  const { setCurrentTime, play, pause, stop } = usePlaylistControls();
1211
- const togglePlayPause = useCallback9(() => {
1236
+ const togglePlayPause = useCallback10(() => {
1212
1237
  if (isPlaying) {
1213
1238
  pause();
1214
1239
  } else {
1215
1240
  play();
1216
1241
  }
1217
1242
  }, [isPlaying, play, pause]);
1218
- const stopPlayback = useCallback9(() => {
1243
+ const stopPlayback = useCallback10(() => {
1219
1244
  stop();
1220
1245
  }, [stop]);
1221
- const rewindToStart = useCallback9(() => {
1246
+ const rewindToStart = useCallback10(() => {
1222
1247
  setCurrentTime(0);
1223
1248
  if (isPlaying) {
1224
1249
  play(0);
@@ -1258,7 +1283,7 @@ var usePlaybackShortcuts = (options = {}) => {
1258
1283
  };
1259
1284
 
1260
1285
  // src/hooks/useAnnotationKeyboardControls.ts
1261
- import { useCallback as useCallback10, useMemo as useMemo3, useEffect as useEffect3 } from "react";
1286
+ import { useCallback as useCallback11, useMemo as useMemo3, useEffect as useEffect3 } from "react";
1262
1287
  var LINK_THRESHOLD2 = 0.01;
1263
1288
  var TIME_DELTA = 0.01;
1264
1289
  function useAnnotationKeyboardControls({
@@ -1278,7 +1303,7 @@ function useAnnotationKeyboardControls({
1278
1303
  if (!activeAnnotationId) return -1;
1279
1304
  return annotations.findIndex((a) => a.id === activeAnnotationId);
1280
1305
  }, [annotations, activeAnnotationId]);
1281
- const scrollToAnnotation = useCallback10(
1306
+ const scrollToAnnotation = useCallback11(
1282
1307
  (annotationId) => {
1283
1308
  if (!(scrollContainerRef == null ? void 0 : scrollContainerRef.current) || !samplesPerPixel || !sampleRate) return;
1284
1309
  const annotation = annotations.find((a) => a.id === annotationId);
@@ -1306,7 +1331,7 @@ function useAnnotationKeyboardControls({
1306
1331
  scrollToAnnotation(activeAnnotationId);
1307
1332
  }
1308
1333
  }, [activeAnnotationId, scrollToAnnotation, scrollContainerRef, samplesPerPixel, sampleRate]);
1309
- const moveStartBoundary = useCallback10(
1334
+ const moveStartBoundary = useCallback11(
1310
1335
  (delta) => {
1311
1336
  if (activeIndex < 0) return;
1312
1337
  const annotation = annotations[activeIndex];
@@ -1335,7 +1360,7 @@ function useAnnotationKeyboardControls({
1335
1360
  },
1336
1361
  [annotations, activeIndex, linkEndpoints, onAnnotationsChange]
1337
1362
  );
1338
- const moveEndBoundary = useCallback10(
1363
+ const moveEndBoundary = useCallback11(
1339
1364
  (delta) => {
1340
1365
  if (activeIndex < 0) return;
1341
1366
  const annotation = annotations[activeIndex];
@@ -1395,7 +1420,7 @@ function useAnnotationKeyboardControls({
1395
1420
  },
1396
1421
  [annotations, activeIndex, duration, linkEndpoints, onAnnotationsChange]
1397
1422
  );
1398
- const selectPrevious = useCallback10(() => {
1423
+ const selectPrevious = useCallback11(() => {
1399
1424
  if (!onActiveAnnotationChange || annotations.length === 0) return;
1400
1425
  if (activeIndex <= 0) {
1401
1426
  onActiveAnnotationChange(annotations[annotations.length - 1].id);
@@ -1403,7 +1428,7 @@ function useAnnotationKeyboardControls({
1403
1428
  onActiveAnnotationChange(annotations[activeIndex - 1].id);
1404
1429
  }
1405
1430
  }, [annotations, activeIndex, onActiveAnnotationChange]);
1406
- const selectNext = useCallback10(() => {
1431
+ const selectNext = useCallback11(() => {
1407
1432
  if (!onActiveAnnotationChange || annotations.length === 0) return;
1408
1433
  if (activeIndex < 0 || activeIndex >= annotations.length - 1) {
1409
1434
  onActiveAnnotationChange(annotations[0].id);
@@ -1411,19 +1436,19 @@ function useAnnotationKeyboardControls({
1411
1436
  onActiveAnnotationChange(annotations[activeIndex + 1].id);
1412
1437
  }
1413
1438
  }, [annotations, activeIndex, onActiveAnnotationChange]);
1414
- const selectFirst = useCallback10(() => {
1439
+ const selectFirst = useCallback11(() => {
1415
1440
  if (!onActiveAnnotationChange || annotations.length === 0) return;
1416
1441
  onActiveAnnotationChange(annotations[0].id);
1417
1442
  }, [annotations, onActiveAnnotationChange]);
1418
- const selectLast = useCallback10(() => {
1443
+ const selectLast = useCallback11(() => {
1419
1444
  if (!onActiveAnnotationChange || annotations.length === 0) return;
1420
1445
  onActiveAnnotationChange(annotations[annotations.length - 1].id);
1421
1446
  }, [annotations, onActiveAnnotationChange]);
1422
- const clearSelection = useCallback10(() => {
1447
+ const clearSelection = useCallback11(() => {
1423
1448
  if (!onActiveAnnotationChange) return;
1424
1449
  onActiveAnnotationChange(null);
1425
1450
  }, [onActiveAnnotationChange]);
1426
- const playActiveAnnotation = useCallback10(() => {
1451
+ const playActiveAnnotation = useCallback11(() => {
1427
1452
  if (activeIndex < 0 || !onPlay) return;
1428
1453
  const annotation = annotations[activeIndex];
1429
1454
  const playDuration = !continuousPlay ? annotation.end - annotation.start : void 0;
@@ -1535,7 +1560,7 @@ function useAnnotationKeyboardControls({
1535
1560
  }
1536
1561
 
1537
1562
  // src/hooks/useDynamicEffects.ts
1538
- import { useState as useState8, useCallback as useCallback11, useRef as useRef8, useEffect as useEffect4 } from "react";
1563
+ import { useState as useState9, useCallback as useCallback12, useRef as useRef9, useEffect as useEffect4 } from "react";
1539
1564
 
1540
1565
  // src/effects/effectDefinitions.ts
1541
1566
  var effectDefinitions = [
@@ -2231,13 +2256,13 @@ function createEffectChain(effects) {
2231
2256
  // src/hooks/useDynamicEffects.ts
2232
2257
  import { Analyser as Analyser2 } from "tone";
2233
2258
  function useDynamicEffects(fftSize = 256) {
2234
- const [activeEffects, setActiveEffects] = useState8([]);
2235
- const activeEffectsRef = useRef8(activeEffects);
2259
+ const [activeEffects, setActiveEffects] = useState9([]);
2260
+ const activeEffectsRef = useRef9(activeEffects);
2236
2261
  activeEffectsRef.current = activeEffects;
2237
- const effectInstancesRef = useRef8(/* @__PURE__ */ new Map());
2238
- const analyserRef = useRef8(null);
2239
- const graphNodesRef = useRef8(null);
2240
- const rebuildChain = useCallback11((effects) => {
2262
+ const effectInstancesRef = useRef9(/* @__PURE__ */ new Map());
2263
+ const analyserRef = useRef9(null);
2264
+ const graphNodesRef = useRef9(null);
2265
+ const rebuildChain = useCallback12((effects) => {
2241
2266
  const nodes = graphNodesRef.current;
2242
2267
  if (!nodes) return;
2243
2268
  const { masterGainNode, destination, analyserNode } = nodes;
@@ -2265,7 +2290,7 @@ function useDynamicEffects(fftSize = 256) {
2265
2290
  analyserNode.connect(destination);
2266
2291
  }
2267
2292
  }, []);
2268
- const addEffect = useCallback11((effectId) => {
2293
+ const addEffect = useCallback12((effectId) => {
2269
2294
  const definition = getEffectDefinition(effectId);
2270
2295
  if (!definition) {
2271
2296
  console.error(`Unknown effect: ${effectId}`);
@@ -2286,7 +2311,7 @@ function useDynamicEffects(fftSize = 256) {
2286
2311
  };
2287
2312
  setActiveEffects((prev) => [...prev, newActiveEffect]);
2288
2313
  }, []);
2289
- const removeEffect = useCallback11((instanceId) => {
2314
+ const removeEffect = useCallback12((instanceId) => {
2290
2315
  const instance = effectInstancesRef.current.get(instanceId);
2291
2316
  if (instance) {
2292
2317
  instance.dispose();
@@ -2294,7 +2319,7 @@ function useDynamicEffects(fftSize = 256) {
2294
2319
  }
2295
2320
  setActiveEffects((prev) => prev.filter((e) => e.instanceId !== instanceId));
2296
2321
  }, []);
2297
- const updateParameter = useCallback11(
2322
+ const updateParameter = useCallback12(
2298
2323
  (instanceId, paramName, value) => {
2299
2324
  const instance = effectInstancesRef.current.get(instanceId);
2300
2325
  if (instance) {
@@ -2308,7 +2333,7 @@ function useDynamicEffects(fftSize = 256) {
2308
2333
  },
2309
2334
  []
2310
2335
  );
2311
- const toggleBypass = useCallback11((instanceId) => {
2336
+ const toggleBypass = useCallback12((instanceId) => {
2312
2337
  var _a;
2313
2338
  const effect = activeEffectsRef.current.find((e) => e.instanceId === instanceId);
2314
2339
  if (!effect) return;
@@ -2322,7 +2347,7 @@ function useDynamicEffects(fftSize = 256) {
2322
2347
  (prev) => prev.map((e) => e.instanceId === instanceId ? __spreadProps(__spreadValues({}, e), { bypassed: newBypassed }) : e)
2323
2348
  );
2324
2349
  }, []);
2325
- const reorderEffects = useCallback11((fromIndex, toIndex) => {
2350
+ const reorderEffects = useCallback12((fromIndex, toIndex) => {
2326
2351
  setActiveEffects((prev) => {
2327
2352
  const newEffects = [...prev];
2328
2353
  const [removed] = newEffects.splice(fromIndex, 1);
@@ -2330,7 +2355,7 @@ function useDynamicEffects(fftSize = 256) {
2330
2355
  return newEffects;
2331
2356
  });
2332
2357
  }, []);
2333
- const clearAllEffects = useCallback11(() => {
2358
+ const clearAllEffects = useCallback12(() => {
2334
2359
  effectInstancesRef.current.forEach((inst) => inst.dispose());
2335
2360
  effectInstancesRef.current.clear();
2336
2361
  setActiveEffects([]);
@@ -2338,7 +2363,7 @@ function useDynamicEffects(fftSize = 256) {
2338
2363
  useEffect4(() => {
2339
2364
  rebuildChain(activeEffects);
2340
2365
  }, [activeEffects, rebuildChain]);
2341
- const masterEffects = useCallback11(
2366
+ const masterEffects = useCallback12(
2342
2367
  (masterGainNode, destination, _isOffline) => {
2343
2368
  const analyserNode = new Analyser2("fft", fftSize);
2344
2369
  analyserRef.current = analyserNode;
@@ -2377,7 +2402,7 @@ function useDynamicEffects(fftSize = 256) {
2377
2402
  effectInstances.clear();
2378
2403
  };
2379
2404
  }, []);
2380
- const createOfflineEffectsFunction = useCallback11(() => {
2405
+ const createOfflineEffectsFunction = useCallback12(() => {
2381
2406
  const nonBypassedEffects = activeEffects.filter((e) => !e.bypassed);
2382
2407
  if (nonBypassedEffects.length === 0) {
2383
2408
  return void 0;
@@ -2419,14 +2444,14 @@ function useDynamicEffects(fftSize = 256) {
2419
2444
  }
2420
2445
 
2421
2446
  // src/hooks/useTrackDynamicEffects.ts
2422
- import { useState as useState9, useCallback as useCallback12, useRef as useRef9, useEffect as useEffect5 } from "react";
2447
+ import { useState as useState10, useCallback as useCallback13, useRef as useRef10, useEffect as useEffect5 } from "react";
2423
2448
  function useTrackDynamicEffects() {
2424
- const [trackEffectsState, setTrackEffectsState] = useState9(
2449
+ const [trackEffectsState, setTrackEffectsState] = useState10(
2425
2450
  /* @__PURE__ */ new Map()
2426
2451
  );
2427
- const trackEffectInstancesRef = useRef9(/* @__PURE__ */ new Map());
2428
- const trackGraphNodesRef = useRef9(/* @__PURE__ */ new Map());
2429
- const rebuildTrackChain = useCallback12((trackId, trackEffects) => {
2452
+ const trackEffectInstancesRef = useRef10(/* @__PURE__ */ new Map());
2453
+ const trackGraphNodesRef = useRef10(/* @__PURE__ */ new Map());
2454
+ const rebuildTrackChain = useCallback13((trackId, trackEffects) => {
2430
2455
  const nodes = trackGraphNodesRef.current.get(trackId);
2431
2456
  if (!nodes) return;
2432
2457
  const { graphEnd, masterGainNode } = nodes;
@@ -2456,7 +2481,7 @@ function useTrackDynamicEffects() {
2456
2481
  currentNode.connect(masterGainNode);
2457
2482
  }
2458
2483
  }, []);
2459
- const addEffectToTrack = useCallback12((trackId, effectId) => {
2484
+ const addEffectToTrack = useCallback13((trackId, effectId) => {
2460
2485
  const definition = getEffectDefinition(effectId);
2461
2486
  if (!definition) {
2462
2487
  console.error(`Unknown effect: ${effectId}`);
@@ -2485,7 +2510,7 @@ function useTrackDynamicEffects() {
2485
2510
  return newState;
2486
2511
  });
2487
2512
  }, []);
2488
- const removeEffectFromTrack = useCallback12((trackId, instanceId) => {
2513
+ const removeEffectFromTrack = useCallback13((trackId, instanceId) => {
2489
2514
  const instancesMap = trackEffectInstancesRef.current.get(trackId);
2490
2515
  const instance = instancesMap == null ? void 0 : instancesMap.get(instanceId);
2491
2516
  if (instance) {
@@ -2502,7 +2527,7 @@ function useTrackDynamicEffects() {
2502
2527
  return newState;
2503
2528
  });
2504
2529
  }, []);
2505
- const updateTrackEffectParameter = useCallback12(
2530
+ const updateTrackEffectParameter = useCallback13(
2506
2531
  (trackId, instanceId, paramName, value) => {
2507
2532
  const instancesMap = trackEffectInstancesRef.current.get(trackId);
2508
2533
  const instance = instancesMap == null ? void 0 : instancesMap.get(instanceId);
@@ -2523,7 +2548,7 @@ function useTrackDynamicEffects() {
2523
2548
  },
2524
2549
  []
2525
2550
  );
2526
- const toggleBypass = useCallback12((trackId, instanceId) => {
2551
+ const toggleBypass = useCallback13((trackId, instanceId) => {
2527
2552
  var _a;
2528
2553
  const trackEffects = trackEffectsStateRef.current.get(trackId) || [];
2529
2554
  const effect = trackEffects.find((e) => e.instanceId === instanceId);
@@ -2545,7 +2570,7 @@ function useTrackDynamicEffects() {
2545
2570
  return newState;
2546
2571
  });
2547
2572
  }, []);
2548
- const clearTrackEffects = useCallback12((trackId) => {
2573
+ const clearTrackEffects = useCallback13((trackId) => {
2549
2574
  const instancesMap = trackEffectInstancesRef.current.get(trackId);
2550
2575
  if (instancesMap) {
2551
2576
  instancesMap.forEach((inst) => inst.dispose());
@@ -2557,9 +2582,9 @@ function useTrackDynamicEffects() {
2557
2582
  return newState;
2558
2583
  });
2559
2584
  }, []);
2560
- const trackEffectsStateRef = useRef9(trackEffectsState);
2585
+ const trackEffectsStateRef = useRef10(trackEffectsState);
2561
2586
  trackEffectsStateRef.current = trackEffectsState;
2562
- const getTrackEffectsFunction = useCallback12(
2587
+ const getTrackEffectsFunction = useCallback13(
2563
2588
  (trackId) => {
2564
2589
  return (graphEnd, masterGainNode, _isOffline) => {
2565
2590
  trackGraphNodesRef.current.set(trackId, {
@@ -2602,7 +2627,7 @@ function useTrackDynamicEffects() {
2602
2627
  trackEffectInstances.clear();
2603
2628
  };
2604
2629
  }, []);
2605
- const createOfflineTrackEffectsFunction = useCallback12(
2630
+ const createOfflineTrackEffectsFunction = useCallback13(
2606
2631
  (trackId) => {
2607
2632
  const trackEffects = trackEffectsState.get(trackId) || [];
2608
2633
  const nonBypassedEffects = trackEffects.filter((e) => !e.bypassed);
@@ -2646,7 +2671,7 @@ function useTrackDynamicEffects() {
2646
2671
  }
2647
2672
 
2648
2673
  // src/hooks/useExportWav.ts
2649
- import { useState as useState10, useCallback as useCallback13 } from "react";
2674
+ import { useState as useState11, useCallback as useCallback14 } from "react";
2650
2675
  import {
2651
2676
  getUnderlyingAudioParam,
2652
2677
  getGlobalAudioContext as getGlobalAudioContext2
@@ -2723,10 +2748,10 @@ function downloadBlob(blob, filename) {
2723
2748
 
2724
2749
  // src/hooks/useExportWav.ts
2725
2750
  function useExportWav() {
2726
- const [isExporting, setIsExporting] = useState10(false);
2727
- const [progress, setProgress] = useState10(0);
2728
- const [error, setError] = useState10(null);
2729
- const exportWav = useCallback13(
2751
+ const [isExporting, setIsExporting] = useState11(false);
2752
+ const [progress, setProgress] = useState11(0);
2753
+ const [error, setError] = useState11(null);
2754
+ const exportWav = useCallback14(
2730
2755
  (_0, _1, ..._2) => __async(null, [_0, _1, ..._2], function* (tracks, trackStates, options = {}) {
2731
2756
  const {
2732
2757
  filename = "export",
@@ -3038,16 +3063,16 @@ function generateFadeCurve(startValue, endValue, numPoints, curveType) {
3038
3063
  }
3039
3064
 
3040
3065
  // src/hooks/useAnimationFrameLoop.ts
3041
- import { useCallback as useCallback14, useEffect as useEffect6, useRef as useRef10 } from "react";
3066
+ import { useCallback as useCallback15, useEffect as useEffect6, useRef as useRef11 } from "react";
3042
3067
  var useAnimationFrameLoop = () => {
3043
- const animationFrameRef = useRef10(null);
3044
- const stopAnimationFrameLoop = useCallback14(() => {
3068
+ const animationFrameRef = useRef11(null);
3069
+ const stopAnimationFrameLoop = useCallback15(() => {
3045
3070
  if (animationFrameRef.current !== null) {
3046
3071
  cancelAnimationFrame(animationFrameRef.current);
3047
3072
  animationFrameRef.current = null;
3048
3073
  }
3049
3074
  }, []);
3050
- const startAnimationFrameLoop = useCallback14(
3075
+ const startAnimationFrameLoop = useCallback15(
3051
3076
  (callback) => {
3052
3077
  stopAnimationFrameLoop();
3053
3078
  animationFrameRef.current = requestAnimationFrame(callback);
@@ -3067,7 +3092,7 @@ var useAnimationFrameLoop = () => {
3067
3092
  };
3068
3093
 
3069
3094
  // src/hooks/useWaveformDataCache.ts
3070
- import { useState as useState11, useEffect as useEffect7, useRef as useRef11, useCallback as useCallback15 } from "react";
3095
+ import { useState as useState12, useEffect as useEffect7, useRef as useRef12, useCallback as useCallback16 } from "react";
3071
3096
 
3072
3097
  // src/workers/peaksWorker.ts
3073
3098
  import WaveformData2 from "waveform-data";
@@ -3299,14 +3324,14 @@ function createPeaksWorker() {
3299
3324
 
3300
3325
  // src/hooks/useWaveformDataCache.ts
3301
3326
  function useWaveformDataCache(tracks, baseScale) {
3302
- const [cache, setCache] = useState11(() => /* @__PURE__ */ new Map());
3303
- const [isGenerating, setIsGenerating] = useState11(false);
3304
- const workerRef = useRef11(null);
3305
- const generatedByBufferRef = useRef11(/* @__PURE__ */ new WeakMap());
3306
- const inflightByBufferRef = useRef11(/* @__PURE__ */ new WeakMap());
3307
- const subscribersByBufferRef = useRef11(/* @__PURE__ */ new WeakMap());
3308
- const pendingCountRef = useRef11(0);
3309
- const getWorker = useCallback15(() => {
3327
+ const [cache, setCache] = useState12(() => /* @__PURE__ */ new Map());
3328
+ const [isGenerating, setIsGenerating] = useState12(false);
3329
+ const workerRef = useRef12(null);
3330
+ const generatedByBufferRef = useRef12(/* @__PURE__ */ new WeakMap());
3331
+ const inflightByBufferRef = useRef12(/* @__PURE__ */ new WeakMap());
3332
+ const subscribersByBufferRef = useRef12(/* @__PURE__ */ new WeakMap());
3333
+ const pendingCountRef = useRef12(0);
3334
+ const getWorker = useCallback16(() => {
3310
3335
  if (!workerRef.current) {
3311
3336
  workerRef.current = createPeaksWorker();
3312
3337
  }
@@ -3428,7 +3453,7 @@ function useWaveformDataCache(tracks, baseScale) {
3428
3453
  }
3429
3454
 
3430
3455
  // src/hooks/useDynamicTracks.ts
3431
- import { useState as useState12, useCallback as useCallback16, useRef as useRef12, useEffect as useEffect8 } from "react";
3456
+ import { useState as useState13, useCallback as useCallback17, useRef as useRef13, useEffect as useEffect8 } from "react";
3432
3457
  import { createTrack as createTrack2, createClipFromSeconds as createClipFromSeconds2 } from "@waveform-playlist/core";
3433
3458
  import { getGlobalAudioContext as getGlobalAudioContext3 } from "@waveform-playlist/playout";
3434
3459
  function getSourceName(source) {
@@ -3463,12 +3488,12 @@ function decodeSource(source, audioContext, signal) {
3463
3488
  });
3464
3489
  }
3465
3490
  function useDynamicTracks() {
3466
- const [tracks, setTracks] = useState12([]);
3467
- const [loadingCount, setLoadingCount] = useState12(0);
3468
- const [errors, setErrors] = useState12([]);
3469
- const cancelledRef = useRef12(false);
3470
- const loadingIdsRef = useRef12(/* @__PURE__ */ new Set());
3471
- const abortControllersRef = useRef12(/* @__PURE__ */ new Map());
3491
+ const [tracks, setTracks] = useState13([]);
3492
+ const [loadingCount, setLoadingCount] = useState13(0);
3493
+ const [errors, setErrors] = useState13([]);
3494
+ const cancelledRef = useRef13(false);
3495
+ const loadingIdsRef = useRef13(/* @__PURE__ */ new Set());
3496
+ const abortControllersRef = useRef13(/* @__PURE__ */ new Map());
3472
3497
  useEffect8(() => {
3473
3498
  const controllers = abortControllersRef.current;
3474
3499
  return () => {
@@ -3479,7 +3504,7 @@ function useDynamicTracks() {
3479
3504
  controllers.clear();
3480
3505
  };
3481
3506
  }, []);
3482
- const addTracks = useCallback16((sources) => {
3507
+ const addTracks = useCallback17((sources) => {
3483
3508
  if (sources.length === 0) return;
3484
3509
  const audioContext = getGlobalAudioContext3();
3485
3510
  const placeholders = sources.map((source) => ({
@@ -3529,7 +3554,7 @@ function useDynamicTracks() {
3529
3554
  }))();
3530
3555
  }
3531
3556
  }, []);
3532
- const removeTrack = useCallback16((trackId) => {
3557
+ const removeTrack = useCallback17((trackId) => {
3533
3558
  setTracks((prev) => prev.filter((t) => t.id !== trackId));
3534
3559
  const controller = abortControllersRef.current.get(trackId);
3535
3560
  if (controller) {
@@ -3551,20 +3576,20 @@ function useDynamicTracks() {
3551
3576
  }
3552
3577
 
3553
3578
  // src/hooks/useOutputMeter.ts
3554
- import { useEffect as useEffect9, useState as useState13, useRef as useRef13, useCallback as useCallback17 } from "react";
3579
+ import { useEffect as useEffect9, useState as useState14, useRef as useRef14, useCallback as useCallback18 } from "react";
3555
3580
  import { getGlobalContext } from "@waveform-playlist/playout";
3556
3581
  import { gainToNormalized } from "@waveform-playlist/core";
3557
3582
  import { meterProcessorUrl } from "@waveform-playlist/worklets";
3558
3583
  var PEAK_DECAY = 0.98;
3559
3584
  function useOutputMeter(options = {}) {
3560
3585
  const { channelCount = 2, updateRate = 60, isPlaying = false } = options;
3561
- const [levels, setLevels] = useState13(() => new Array(channelCount).fill(0));
3562
- const [peakLevels, setPeakLevels] = useState13(() => new Array(channelCount).fill(0));
3563
- const [rmsLevels, setRmsLevels] = useState13(() => new Array(channelCount).fill(0));
3564
- const workletNodeRef = useRef13(null);
3565
- const smoothedPeakRef = useRef13(new Array(channelCount).fill(0));
3566
- const [meterError, setMeterError] = useState13(null);
3567
- const resetPeak = useCallback17(
3586
+ const [levels, setLevels] = useState14(() => new Array(channelCount).fill(0));
3587
+ const [peakLevels, setPeakLevels] = useState14(() => new Array(channelCount).fill(0));
3588
+ const [rmsLevels, setRmsLevels] = useState14(() => new Array(channelCount).fill(0));
3589
+ const workletNodeRef = useRef14(null);
3590
+ const smoothedPeakRef = useRef14(new Array(channelCount).fill(0));
3591
+ const [meterError, setMeterError] = useState14(null);
3592
+ const resetPeak = useCallback18(
3568
3593
  () => setPeakLevels(new Array(channelCount).fill(0)),
3569
3594
  [channelCount]
3570
3595
  );
@@ -3679,7 +3704,7 @@ var WaveformPlaylistProvider = ({
3679
3704
  }) => {
3680
3705
  var _a, _b, _c, _d;
3681
3706
  const progressBarWidth = progressBarWidthProp != null ? progressBarWidthProp : barWidth + barGap;
3682
- const indefinitePlaybackRef = useRef14(indefinitePlayback);
3707
+ const indefinitePlaybackRef = useRef15(indefinitePlayback);
3683
3708
  indefinitePlaybackRef.current = indefinitePlayback;
3684
3709
  const stableZoomLevels = useMemo4(
3685
3710
  () => zoomLevels,
@@ -3699,46 +3724,46 @@ var WaveformPlaylistProvider = ({
3699
3724
  }
3700
3725
  return annotationList.annotations;
3701
3726
  }, [annotationList == null ? void 0 : annotationList.annotations]);
3702
- const annotationsRef = useRef14(annotations);
3727
+ const annotationsRef = useRef15(annotations);
3703
3728
  annotationsRef.current = annotations;
3704
- const [activeAnnotationId, setActiveAnnotationIdState] = useState14(null);
3705
- const [isPlaying, setIsPlaying] = useState14(false);
3706
- const [currentTime, setCurrentTime] = useState14(0);
3707
- const [duration, setDuration] = useState14(0);
3708
- const [audioBuffers, setAudioBuffers] = useState14([]);
3709
- const [peaksDataArray, setPeaksDataArray] = useState14([]);
3710
- const [trackStates, setTrackStates] = useState14([]);
3711
- const [isAutomaticScroll, setIsAutomaticScroll] = useState14(automaticScroll);
3712
- const [continuousPlay, setContinuousPlayState] = useState14(
3729
+ const [activeAnnotationId, setActiveAnnotationIdState] = useState15(null);
3730
+ const [isPlaying, setIsPlaying] = useState15(false);
3731
+ const [currentTime, setCurrentTime] = useState15(0);
3732
+ const [duration, setDuration] = useState15(0);
3733
+ const [audioBuffers, setAudioBuffers] = useState15([]);
3734
+ const [peaksDataArray, setPeaksDataArray] = useState15([]);
3735
+ const [trackStates, setTrackStates] = useState15([]);
3736
+ const [isAutomaticScroll, setIsAutomaticScroll] = useState15(automaticScroll);
3737
+ const [continuousPlay, setContinuousPlayState] = useState15(
3713
3738
  (_a = annotationList == null ? void 0 : annotationList.isContinuousPlay) != null ? _a : false
3714
3739
  );
3715
- const [linkEndpoints, setLinkEndpoints] = useState14((_b = annotationList == null ? void 0 : annotationList.linkEndpoints) != null ? _b : false);
3716
- const [annotationsEditable, setAnnotationsEditable] = useState14((_c = annotationList == null ? void 0 : annotationList.editable) != null ? _c : false);
3717
- const [isReady, setIsReady] = useState14(false);
3718
- const engineRef = useRef14(null);
3719
- const audioInitializedRef = useRef14(false);
3720
- const isPlayingRef = useRef14(false);
3740
+ const [linkEndpoints, setLinkEndpoints] = useState15((_b = annotationList == null ? void 0 : annotationList.linkEndpoints) != null ? _b : false);
3741
+ const [annotationsEditable, setAnnotationsEditable] = useState15((_c = annotationList == null ? void 0 : annotationList.editable) != null ? _c : false);
3742
+ const [isReady, setIsReady] = useState15(false);
3743
+ const engineRef = useRef15(null);
3744
+ const audioInitializedRef = useRef15(false);
3745
+ const isPlayingRef = useRef15(false);
3721
3746
  isPlayingRef.current = isPlaying;
3722
- const playStartPositionRef = useRef14(0);
3723
- const currentTimeRef = useRef14(0);
3724
- const tracksRef = useRef14(tracks);
3725
- const soundFontCacheRef = useRef14(soundFontCache);
3747
+ const playStartPositionRef = useRef15(0);
3748
+ const currentTimeRef = useRef15(0);
3749
+ const tracksRef = useRef15(tracks);
3750
+ const soundFontCacheRef = useRef15(soundFontCache);
3726
3751
  soundFontCacheRef.current = soundFontCache;
3727
- const trackStatesRef = useRef14(trackStates);
3728
- const playbackStartTimeRef = useRef14(0);
3729
- const audioStartPositionRef = useRef14(0);
3730
- const playbackEndTimeRef = useRef14(null);
3731
- const scrollContainerRef = useRef14(null);
3732
- const isAutomaticScrollRef = useRef14(false);
3733
- const continuousPlayRef = useRef14((_d = annotationList == null ? void 0 : annotationList.isContinuousPlay) != null ? _d : false);
3734
- const activeAnnotationIdRef = useRef14(null);
3735
- const engineTracksRef = useRef14(null);
3736
- const lastTracksVersionRef = useRef14(0);
3737
- const skipEngineDisposeRef = useRef14(false);
3738
- const isDraggingRef = useRef14(false);
3739
- const prevTracksRef = useRef14([]);
3740
- const samplesPerPixelRef = useRef14(initialSamplesPerPixel);
3741
- const sampleRateRef = useRef14(
3752
+ const trackStatesRef = useRef15(trackStates);
3753
+ const playbackStartTimeRef = useRef15(0);
3754
+ const audioStartPositionRef = useRef15(0);
3755
+ const playbackEndTimeRef = useRef15(null);
3756
+ const scrollContainerRef = useRef15(null);
3757
+ const isAutomaticScrollRef = useRef15(false);
3758
+ const continuousPlayRef = useRef15((_d = annotationList == null ? void 0 : annotationList.isContinuousPlay) != null ? _d : false);
3759
+ const activeAnnotationIdRef = useRef15(null);
3760
+ const engineTracksRef = useRef15(null);
3761
+ const lastTracksVersionRef = useRef15(0);
3762
+ const skipEngineDisposeRef = useRef15(false);
3763
+ const isDraggingRef = useRef15(false);
3764
+ const prevTracksRef = useRef15([]);
3765
+ const samplesPerPixelRef = useRef15(initialSamplesPerPixel);
3766
+ const sampleRateRef = useRef15(
3742
3767
  typeof AudioContext !== "undefined" ? getGlobalAudioContext4().sampleRate : 48e3
3743
3768
  );
3744
3769
  const { timeFormat, setTimeFormat, formatTime: formatTime2 } = useTimeFormat();
@@ -3777,21 +3802,28 @@ var WaveformPlaylistProvider = ({
3777
3802
  onEngineState: onSelectedTrackEngineState,
3778
3803
  selectedTrackIdRef
3779
3804
  } = useSelectedTrack({ engineRef });
3805
+ const {
3806
+ canUndo,
3807
+ canRedo,
3808
+ undo,
3809
+ redo,
3810
+ onEngineState: onUndoEngineState
3811
+ } = useUndoState({ engineRef });
3780
3812
  const { animationFrameRef, startAnimationFrameLoop, stopAnimationFrameLoop } = useAnimationFrameLoop();
3781
3813
  const baseScale = useMemo4(
3782
3814
  () => Math.min(...stableZoomLevels != null ? stableZoomLevels : [256, 512, 1024, 2048, 4096, 8192]),
3783
3815
  [stableZoomLevels]
3784
3816
  );
3785
3817
  const { cache: waveformDataCache } = useWaveformDataCache(tracks, baseScale);
3786
- const setContinuousPlay = useCallback18((value) => {
3818
+ const setContinuousPlay = useCallback19((value) => {
3787
3819
  continuousPlayRef.current = value;
3788
3820
  setContinuousPlayState(value);
3789
3821
  }, []);
3790
- const setActiveAnnotationId = useCallback18((value) => {
3822
+ const setActiveAnnotationId = useCallback19((value) => {
3791
3823
  activeAnnotationIdRef.current = value;
3792
3824
  setActiveAnnotationIdState(value);
3793
3825
  }, []);
3794
- const setLoopRegionFromSelection = useCallback18(() => {
3826
+ const setLoopRegionFromSelection = useCallback19(() => {
3795
3827
  var _a2, _b2;
3796
3828
  const start = (_a2 = selectionStartRef.current) != null ? _a2 : 0;
3797
3829
  const end = (_b2 = selectionEndRef.current) != null ? _b2 : 0;
@@ -3829,7 +3861,7 @@ var WaveformPlaylistProvider = ({
3829
3861
  container.scrollLeft = newScrollLeft;
3830
3862
  samplesPerPixelRef.current = newSamplesPerPixel;
3831
3863
  }, [samplesPerPixel, duration]);
3832
- const pendingResumeRef = useRef14(null);
3864
+ const pendingResumeRef = useRef15(null);
3833
3865
  useEffect10(() => {
3834
3866
  var _a2, _b2, _c2, _d2;
3835
3867
  if (isEngineTracks || isDraggingRef.current) {
@@ -4005,6 +4037,7 @@ var WaveformPlaylistProvider = ({
4005
4037
  onSelectedTrackEngineState(state);
4006
4038
  onZoomEngineState(state);
4007
4039
  onVolumeEngineState(state);
4040
+ onUndoEngineState(state);
4008
4041
  if (!suppressTracksMirroring && state.tracksVersion !== lastTracksVersionRef.current) {
4009
4042
  lastTracksVersionRef.current = state.tracksVersion;
4010
4043
  engineTracksRef.current = state.tracks;
@@ -4062,6 +4095,7 @@ var WaveformPlaylistProvider = ({
4062
4095
  onSelectedTrackEngineState,
4063
4096
  onZoomEngineState,
4064
4097
  onVolumeEngineState,
4098
+ onUndoEngineState,
4065
4099
  onTracksChange,
4066
4100
  masterVolumeRef,
4067
4101
  selectionStartRef,
@@ -4139,8 +4173,8 @@ var WaveformPlaylistProvider = ({
4139
4173
  });
4140
4174
  setPeaksDataArray(allTrackPeaks);
4141
4175
  }, [tracks, samplesPerPixel, mono, waveformDataCache, deferEngineRebuild]);
4142
- const getPlaybackTimeFallbackWarnedRef = useRef14(false);
4143
- const getPlaybackTime = useCallback18(() => {
4176
+ const getPlaybackTimeFallbackWarnedRef = useRef15(false);
4177
+ const getPlaybackTime = useCallback19(() => {
4144
4178
  var _a2, _b2;
4145
4179
  if (engineRef.current) {
4146
4180
  return engineRef.current.getCurrentTime();
@@ -4154,7 +4188,7 @@ var WaveformPlaylistProvider = ({
4154
4188
  const elapsed = getContext2().currentTime - ((_a2 = playbackStartTimeRef.current) != null ? _a2 : 0);
4155
4189
  return ((_b2 = audioStartPositionRef.current) != null ? _b2 : 0) + elapsed;
4156
4190
  }, []);
4157
- const startAnimationLoop = useCallback18(() => {
4191
+ const startAnimationLoop = useCallback19(() => {
4158
4192
  const updateTime = () => {
4159
4193
  const time = getPlaybackTime();
4160
4194
  currentTimeRef.current = time;
@@ -4272,7 +4306,7 @@ var WaveformPlaylistProvider = ({
4272
4306
  stopAnimationLoop();
4273
4307
  });
4274
4308
  }, [tracks, startAnimationLoop, stopAnimationLoop]);
4275
- const play = useCallback18(
4309
+ const play = useCallback19(
4276
4310
  (startTime, playDuration) => __async(null, null, function* () {
4277
4311
  if (!engineRef.current) return;
4278
4312
  const actualStartTime = startTime != null ? startTime : currentTimeRef.current;
@@ -4303,7 +4337,7 @@ var WaveformPlaylistProvider = ({
4303
4337
  }),
4304
4338
  [startAnimationLoop, stopAnimationLoop]
4305
4339
  );
4306
- const pause = useCallback18(() => {
4340
+ const pause = useCallback19(() => {
4307
4341
  if (!engineRef.current) return;
4308
4342
  const pauseTime = getPlaybackTime();
4309
4343
  engineRef.current.pause();
@@ -4312,7 +4346,7 @@ var WaveformPlaylistProvider = ({
4312
4346
  currentTimeRef.current = pauseTime;
4313
4347
  setCurrentTime(pauseTime);
4314
4348
  }, [stopAnimationLoop, getPlaybackTime]);
4315
- const stop = useCallback18(() => {
4349
+ const stop = useCallback19(() => {
4316
4350
  if (!engineRef.current) return;
4317
4351
  engineRef.current.stop();
4318
4352
  setIsPlaying(false);
@@ -4321,7 +4355,7 @@ var WaveformPlaylistProvider = ({
4321
4355
  setCurrentTime(playStartPositionRef.current);
4322
4356
  setActiveAnnotationId(null);
4323
4357
  }, [stopAnimationLoop, setActiveAnnotationId]);
4324
- const seekTo = useCallback18(
4358
+ const seekTo = useCallback19(
4325
4359
  (time) => {
4326
4360
  const clampedTime = Math.max(0, Math.min(time, duration));
4327
4361
  currentTimeRef.current = clampedTime;
@@ -4332,7 +4366,7 @@ var WaveformPlaylistProvider = ({
4332
4366
  },
4333
4367
  [duration, isPlaying, play]
4334
4368
  );
4335
- const setTrackMute = useCallback18(
4369
+ const setTrackMute = useCallback19(
4336
4370
  (trackIndex, muted) => {
4337
4371
  var _a2;
4338
4372
  const trackId = (_a2 = tracksRef.current[trackIndex]) == null ? void 0 : _a2.id;
@@ -4346,7 +4380,7 @@ var WaveformPlaylistProvider = ({
4346
4380
  },
4347
4381
  [trackStates]
4348
4382
  );
4349
- const setTrackSolo = useCallback18(
4383
+ const setTrackSolo = useCallback19(
4350
4384
  (trackIndex, soloed) => {
4351
4385
  var _a2;
4352
4386
  const trackId = (_a2 = tracksRef.current[trackIndex]) == null ? void 0 : _a2.id;
@@ -4360,7 +4394,7 @@ var WaveformPlaylistProvider = ({
4360
4394
  },
4361
4395
  [trackStates]
4362
4396
  );
4363
- const setTrackVolume = useCallback18(
4397
+ const setTrackVolume = useCallback19(
4364
4398
  (trackIndex, volume2) => {
4365
4399
  var _a2;
4366
4400
  const trackId = (_a2 = tracksRef.current[trackIndex]) == null ? void 0 : _a2.id;
@@ -4374,7 +4408,7 @@ var WaveformPlaylistProvider = ({
4374
4408
  },
4375
4409
  [trackStates]
4376
4410
  );
4377
- const setTrackPan = useCallback18(
4411
+ const setTrackPan = useCallback19(
4378
4412
  (trackIndex, pan) => {
4379
4413
  var _a2;
4380
4414
  const trackId = (_a2 = tracksRef.current[trackIndex]) == null ? void 0 : _a2.id;
@@ -4388,7 +4422,7 @@ var WaveformPlaylistProvider = ({
4388
4422
  },
4389
4423
  [trackStates]
4390
4424
  );
4391
- const setSelection = useCallback18(
4425
+ const setSelection = useCallback19(
4392
4426
  (start, end) => {
4393
4427
  setSelectionEngine(start, end);
4394
4428
  currentTimeRef.current = start;
@@ -4401,12 +4435,12 @@ var WaveformPlaylistProvider = ({
4401
4435
  },
4402
4436
  [isPlaying, setSelectionEngine]
4403
4437
  );
4404
- const setScrollContainer = useCallback18((element) => {
4438
+ const setScrollContainer = useCallback19((element) => {
4405
4439
  scrollContainerRef.current = element;
4406
4440
  }, []);
4407
- const onAnnotationsChangeRef = useRef14(onAnnotationsChange);
4441
+ const onAnnotationsChangeRef = useRef15(onAnnotationsChange);
4408
4442
  onAnnotationsChangeRef.current = onAnnotationsChange;
4409
- const setAnnotations = useCallback18(
4443
+ const setAnnotations = useCallback19(
4410
4444
  (action) => {
4411
4445
  const updated = typeof action === "function" ? action(annotationsRef.current) : action;
4412
4446
  if (!onAnnotationsChangeRef.current) {
@@ -4456,7 +4490,9 @@ var WaveformPlaylistProvider = ({
4456
4490
  selectedTrackId,
4457
4491
  loopStart,
4458
4492
  loopEnd,
4459
- indefinitePlayback
4493
+ indefinitePlayback,
4494
+ canUndo,
4495
+ canRedo
4460
4496
  }),
4461
4497
  [
4462
4498
  continuousPlay,
@@ -4471,17 +4507,19 @@ var WaveformPlaylistProvider = ({
4471
4507
  selectedTrackId,
4472
4508
  loopStart,
4473
4509
  loopEnd,
4474
- indefinitePlayback
4510
+ indefinitePlayback,
4511
+ canUndo,
4512
+ canRedo
4475
4513
  ]
4476
4514
  );
4477
- const setCurrentTimeControl = useCallback18(
4515
+ const setCurrentTimeControl = useCallback19(
4478
4516
  (time) => {
4479
4517
  currentTimeRef.current = time;
4480
4518
  setCurrentTime(time);
4481
4519
  },
4482
4520
  [currentTimeRef]
4483
4521
  );
4484
- const setAutomaticScrollControl = useCallback18((enabled) => {
4522
+ const setAutomaticScrollControl = useCallback19((enabled) => {
4485
4523
  setIsAutomaticScroll(enabled);
4486
4524
  }, []);
4487
4525
  const controlsValue = useMemo4(
@@ -4522,7 +4560,10 @@ var WaveformPlaylistProvider = ({
4522
4560
  setLoopEnabled,
4523
4561
  setLoopRegion,
4524
4562
  setLoopRegionFromSelection,
4525
- clearLoopRegion
4563
+ clearLoopRegion,
4564
+ // Undo/redo
4565
+ undo,
4566
+ redo
4526
4567
  }),
4527
4568
  [
4528
4569
  play,
@@ -4552,7 +4593,9 @@ var WaveformPlaylistProvider = ({
4552
4593
  setLoopEnabled,
4553
4594
  setLoopRegion,
4554
4595
  setLoopRegionFromSelection,
4555
- clearLoopRegion
4596
+ clearLoopRegion,
4597
+ undo,
4598
+ redo
4556
4599
  ]
4557
4600
  );
4558
4601
  const dataValue = useMemo4(
@@ -4643,10 +4686,10 @@ var usePlaylistData = () => {
4643
4686
  import {
4644
4687
  createContext as createContext2,
4645
4688
  useContext as useContext2,
4646
- useState as useState15,
4689
+ useState as useState16,
4647
4690
  useEffect as useEffect11,
4648
- useRef as useRef15,
4649
- useCallback as useCallback19,
4691
+ useRef as useRef16,
4692
+ useCallback as useCallback20,
4650
4693
  useMemo as useMemo5
4651
4694
  } from "react";
4652
4695
  import { ThemeProvider as ThemeProvider2 } from "styled-components";
@@ -4678,11 +4721,11 @@ var MediaElementPlaylistProvider = ({
4678
4721
  }) => {
4679
4722
  var _a;
4680
4723
  const progressBarWidth = progressBarWidthProp != null ? progressBarWidthProp : barWidth + barGap;
4681
- const [isPlaying, setIsPlaying] = useState15(false);
4682
- const [currentTime, setCurrentTime] = useState15(0);
4683
- const [duration, setDuration] = useState15(0);
4684
- const [peaksDataArray, setPeaksDataArray] = useState15([]);
4685
- const [playbackRate, setPlaybackRateState] = useState15(initialPlaybackRate);
4724
+ const [isPlaying, setIsPlaying] = useState16(false);
4725
+ const [currentTime, setCurrentTime] = useState16(0);
4726
+ const [duration, setDuration] = useState16(0);
4727
+ const [peaksDataArray, setPeaksDataArray] = useState16([]);
4728
+ const [playbackRate, setPlaybackRateState] = useState16(initialPlaybackRate);
4686
4729
  const annotations = useMemo5(() => {
4687
4730
  if (!(annotationList == null ? void 0 : annotationList.annotations)) return [];
4688
4731
  if (process.env.NODE_ENV !== "production" && annotationList.annotations.length > 0) {
@@ -4696,21 +4739,21 @@ var MediaElementPlaylistProvider = ({
4696
4739
  }
4697
4740
  return annotationList.annotations;
4698
4741
  }, [annotationList == null ? void 0 : annotationList.annotations]);
4699
- const annotationsRef = useRef15(annotations);
4742
+ const annotationsRef = useRef16(annotations);
4700
4743
  annotationsRef.current = annotations;
4701
- const [activeAnnotationId, setActiveAnnotationIdState] = useState15(null);
4702
- const [continuousPlay, setContinuousPlayState] = useState15(
4744
+ const [activeAnnotationId, setActiveAnnotationIdState] = useState16(null);
4745
+ const [continuousPlay, setContinuousPlayState] = useState16(
4703
4746
  (_a = annotationList == null ? void 0 : annotationList.isContinuousPlay) != null ? _a : false
4704
4747
  );
4705
- const [samplesPerPixel] = useState15(initialSamplesPerPixel);
4706
- const [isAutomaticScroll, setIsAutomaticScroll] = useState15(automaticScroll);
4707
- const playoutRef = useRef15(null);
4708
- const currentTimeRef = useRef15(0);
4709
- const continuousPlayRef = useRef15(continuousPlay);
4710
- const activeAnnotationIdRef = useRef15(null);
4711
- const scrollContainerRef = useRef15(null);
4712
- const isAutomaticScrollRef = useRef15(automaticScroll);
4713
- const samplesPerPixelRef = useRef15(initialSamplesPerPixel);
4748
+ const [samplesPerPixel] = useState16(initialSamplesPerPixel);
4749
+ const [isAutomaticScroll, setIsAutomaticScroll] = useState16(automaticScroll);
4750
+ const playoutRef = useRef16(null);
4751
+ const currentTimeRef = useRef16(0);
4752
+ const continuousPlayRef = useRef16(continuousPlay);
4753
+ const activeAnnotationIdRef = useRef16(null);
4754
+ const scrollContainerRef = useRef16(null);
4755
+ const isAutomaticScrollRef = useRef16(automaticScroll);
4756
+ const samplesPerPixelRef = useRef16(initialSamplesPerPixel);
4714
4757
  const { startAnimationFrameLoop, stopAnimationFrameLoop } = useAnimationFrameLoop();
4715
4758
  useEffect11(() => {
4716
4759
  continuousPlayRef.current = continuousPlay;
@@ -4718,15 +4761,15 @@ var MediaElementPlaylistProvider = ({
4718
4761
  useEffect11(() => {
4719
4762
  isAutomaticScrollRef.current = isAutomaticScroll;
4720
4763
  }, [isAutomaticScroll]);
4721
- const setActiveAnnotationId = useCallback19((value) => {
4764
+ const setActiveAnnotationId = useCallback20((value) => {
4722
4765
  activeAnnotationIdRef.current = value;
4723
4766
  setActiveAnnotationIdState(value);
4724
4767
  }, []);
4725
- const setContinuousPlay = useCallback19((value) => {
4768
+ const setContinuousPlay = useCallback20((value) => {
4726
4769
  continuousPlayRef.current = value;
4727
4770
  setContinuousPlayState(value);
4728
4771
  }, []);
4729
- const setScrollContainer = useCallback19((element) => {
4772
+ const setScrollContainer = useCallback20((element) => {
4730
4773
  scrollContainerRef.current = element;
4731
4774
  }, []);
4732
4775
  const sampleRate = track.waveformData.sample_rate;
@@ -4806,7 +4849,7 @@ var MediaElementPlaylistProvider = ({
4806
4849
  console.warn("[waveform-playlist] Failed to extract peaks from waveform data:", err);
4807
4850
  }
4808
4851
  }, [track.waveformData, track.name, samplesPerPixel, sampleRate]);
4809
- const startAnimationLoop = useCallback19(() => {
4852
+ const startAnimationLoop = useCallback20(() => {
4810
4853
  const updateTime = () => {
4811
4854
  var _a2, _b, _c;
4812
4855
  const time = (_b = (_a2 = playoutRef.current) == null ? void 0 : _a2.getCurrentTime()) != null ? _b : 0;
@@ -4849,7 +4892,7 @@ var MediaElementPlaylistProvider = ({
4849
4892
  startAnimationFrameLoop(updateTime);
4850
4893
  }, [setActiveAnnotationId, sampleRate, startAnimationFrameLoop]);
4851
4894
  const stopAnimationLoop = stopAnimationFrameLoop;
4852
- const play = useCallback19(
4895
+ const play = useCallback20(
4853
4896
  (startTime) => {
4854
4897
  if (!playoutRef.current) return;
4855
4898
  const actualStartTime = startTime != null ? startTime : currentTimeRef.current;
@@ -4859,14 +4902,14 @@ var MediaElementPlaylistProvider = ({
4859
4902
  },
4860
4903
  [startAnimationLoop]
4861
4904
  );
4862
- const pause = useCallback19(() => {
4905
+ const pause = useCallback20(() => {
4863
4906
  if (!playoutRef.current) return;
4864
4907
  playoutRef.current.pause();
4865
4908
  setIsPlaying(false);
4866
4909
  stopAnimationLoop();
4867
4910
  setCurrentTime(playoutRef.current.getCurrentTime());
4868
4911
  }, [stopAnimationLoop]);
4869
- const stop = useCallback19(() => {
4912
+ const stop = useCallback20(() => {
4870
4913
  if (!playoutRef.current) return;
4871
4914
  playoutRef.current.stop();
4872
4915
  setIsPlaying(false);
@@ -4875,7 +4918,7 @@ var MediaElementPlaylistProvider = ({
4875
4918
  setCurrentTime(0);
4876
4919
  setActiveAnnotationId(null);
4877
4920
  }, [stopAnimationLoop, setActiveAnnotationId]);
4878
- const seekTo = useCallback19(
4921
+ const seekTo = useCallback20(
4879
4922
  (time) => {
4880
4923
  const clampedTime = Math.max(0, Math.min(time, duration));
4881
4924
  currentTimeRef.current = clampedTime;
@@ -4886,7 +4929,7 @@ var MediaElementPlaylistProvider = ({
4886
4929
  },
4887
4930
  [duration]
4888
4931
  );
4889
- const setPlaybackRate = useCallback19((rate) => {
4932
+ const setPlaybackRate = useCallback20((rate) => {
4890
4933
  const clampedRate = Math.max(0.5, Math.min(2, rate));
4891
4934
  setPlaybackRateState(clampedRate);
4892
4935
  if (playoutRef.current) {
@@ -4912,9 +4955,9 @@ var MediaElementPlaylistProvider = ({
4912
4955
  }),
4913
4956
  [continuousPlay, annotations, activeAnnotationId, playbackRate, isAutomaticScroll]
4914
4957
  );
4915
- const onAnnotationsChangeRef = useRef15(onAnnotationsChange);
4958
+ const onAnnotationsChangeRef = useRef16(onAnnotationsChange);
4916
4959
  onAnnotationsChangeRef.current = onAnnotationsChange;
4917
- const setAnnotations = useCallback19(
4960
+ const setAnnotations = useCallback20(
4918
4961
  (action) => {
4919
4962
  const updated = typeof action === "function" ? action(annotationsRef.current) : action;
4920
4963
  if (!onAnnotationsChangeRef.current) {
@@ -5021,7 +5064,7 @@ var useMediaElementData = () => {
5021
5064
  };
5022
5065
 
5023
5066
  // src/components/PlaybackControls.tsx
5024
- import { useCallback as useCallback20 } from "react";
5067
+ import { useCallback as useCallback21 } from "react";
5025
5068
  import { BaseControlButton } from "@waveform-playlist/ui-components";
5026
5069
  import { jsx as jsx3 } from "react/jsx-runtime";
5027
5070
  var PlayButton = ({ className }) => {
@@ -5157,7 +5200,7 @@ var ClearAllButton = ({
5157
5200
  className
5158
5201
  }) => {
5159
5202
  const { stop } = usePlaylistControls();
5160
- const handleClick = useCallback20(() => {
5203
+ const handleClick = useCallback21(() => {
5161
5204
  stop();
5162
5205
  onClearAll();
5163
5206
  }, [stop, onClearAll]);
@@ -5185,7 +5228,7 @@ var ZoomOutButton = ({
5185
5228
  };
5186
5229
 
5187
5230
  // src/components/ContextualControls.tsx
5188
- import { useRef as useRef16, useEffect as useEffect12 } from "react";
5231
+ import { useRef as useRef17, useEffect as useEffect12 } from "react";
5189
5232
  import {
5190
5233
  MasterVolumeControl as BaseMasterVolumeControl,
5191
5234
  TimeFormatSelect as BaseTimeFormatSelect,
@@ -5224,8 +5267,8 @@ var PositionDisplay = styled.span`
5224
5267
  `;
5225
5268
  var AudioPosition = ({ className }) => {
5226
5269
  var _a;
5227
- const timeRef = useRef16(null);
5228
- const animationFrameRef = useRef16(null);
5270
+ const timeRef = useRef17(null);
5271
+ const animationFrameRef = useRef17(null);
5229
5272
  const { isPlaying, currentTimeRef, getPlaybackTime } = usePlaybackAnimation();
5230
5273
  const { timeFormat: format } = usePlaylistData();
5231
5274
  useEffect12(() => {
@@ -5384,7 +5427,7 @@ function useClipInteractionEnabled() {
5384
5427
  }
5385
5428
 
5386
5429
  // src/components/PlaylistVisualization.tsx
5387
- import { useContext as useContext6, useRef as useRef19, useState as useState16, useMemo as useMemo6, useCallback as useCallback21 } from "react";
5430
+ import { useContext as useContext6, useRef as useRef20, useState as useState17, useMemo as useMemo6, useCallback as useCallback22 } from "react";
5388
5431
  import { createPortal } from "react-dom";
5389
5432
  import styled4 from "styled-components";
5390
5433
  import { getGlobalContext as getGlobalContext2 } from "@waveform-playlist/playout";
@@ -5414,7 +5457,7 @@ import {
5414
5457
  } from "@waveform-playlist/ui-components";
5415
5458
 
5416
5459
  // src/components/AnimatedPlayhead.tsx
5417
- import { useRef as useRef17, useEffect as useEffect13 } from "react";
5460
+ import { useRef as useRef18, useEffect as useEffect13 } from "react";
5418
5461
  import styled2 from "styled-components";
5419
5462
  import { jsx as jsx8 } from "react/jsx-runtime";
5420
5463
  var PlayheadLine = styled2.div.attrs((props) => ({
@@ -5432,8 +5475,8 @@ var PlayheadLine = styled2.div.attrs((props) => ({
5432
5475
  will-change: transform;
5433
5476
  `;
5434
5477
  var AnimatedPlayhead = ({ color = "#ff0000" }) => {
5435
- const playheadRef = useRef17(null);
5436
- const animationFrameRef = useRef17(null);
5478
+ const playheadRef = useRef18(null);
5479
+ const animationFrameRef = useRef18(null);
5437
5480
  const { isPlaying, currentTimeRef, getPlaybackTime } = usePlaybackAnimation();
5438
5481
  const { samplesPerPixel, sampleRate, progressBarWidth } = usePlaylistData();
5439
5482
  useEffect13(() => {
@@ -5472,7 +5515,7 @@ var AnimatedPlayhead = ({ color = "#ff0000" }) => {
5472
5515
  };
5473
5516
 
5474
5517
  // src/components/ChannelWithProgress.tsx
5475
- import { useRef as useRef18, useEffect as useEffect14 } from "react";
5518
+ import { useRef as useRef19, useEffect as useEffect14 } from "react";
5476
5519
  import styled3 from "styled-components";
5477
5520
  import {
5478
5521
  clipPixelWidth as computeClipPixelWidth
@@ -5537,8 +5580,8 @@ var ChannelWithProgress = (_a) => {
5537
5580
  "clipSampleRate",
5538
5581
  "clipOffsetSeconds"
5539
5582
  ]);
5540
- const progressRef = useRef18(null);
5541
- const animationFrameRef = useRef18(null);
5583
+ const progressRef = useRef19(null);
5584
+ const animationFrameRef = useRef19(null);
5542
5585
  const theme = useTheme();
5543
5586
  const { waveHeight } = usePlaylistInfo();
5544
5587
  const { isPlaying, currentTimeRef, getPlaybackTime } = usePlaybackAnimation();
@@ -5821,11 +5864,11 @@ var PlaylistVisualization = ({
5821
5864
  )
5822
5865
  };
5823
5866
  }, [spectrogram == null ? void 0 : spectrogram.spectrogramWorkerApi]);
5824
- const [settingsModalTrackId, setSettingsModalTrackId] = useState16(null);
5825
- const [isSelecting, setIsSelecting] = useState16(false);
5826
- const mouseDownTimeRef = useRef19(0);
5827
- const scrollContainerRef = useRef19(null);
5828
- const handleScrollContainerRef = useCallback21(
5867
+ const [settingsModalTrackId, setSettingsModalTrackId] = useState17(null);
5868
+ const [isSelecting, setIsSelecting] = useState17(false);
5869
+ const mouseDownTimeRef = useRef20(0);
5870
+ const scrollContainerRef = useRef20(null);
5871
+ const handleScrollContainerRef = useCallback22(
5829
5872
  (element) => {
5830
5873
  scrollContainerRef.current = element;
5831
5874
  setScrollContainer(element);
@@ -5863,7 +5906,7 @@ var PlaylistVisualization = ({
5863
5906
  );
5864
5907
  }
5865
5908
  });
5866
- const selectTrack = useCallback21(
5909
+ const selectTrack = useCallback22(
5867
5910
  (trackIndex) => {
5868
5911
  if (trackIndex >= 0 && trackIndex < tracks.length) {
5869
5912
  const track = tracks[trackIndex];
@@ -6304,7 +6347,7 @@ var PlaylistVisualization = ({
6304
6347
  };
6305
6348
 
6306
6349
  // src/components/PlaylistAnnotationList.tsx
6307
- import { useCallback as useCallback22 } from "react";
6350
+ import { useCallback as useCallback23 } from "react";
6308
6351
  import { jsx as jsx11 } from "react/jsx-runtime";
6309
6352
  var PlaylistAnnotationList = ({
6310
6353
  height,
@@ -6319,7 +6362,7 @@ var PlaylistAnnotationList = ({
6319
6362
  const integration = useAnnotationIntegration();
6320
6363
  const { setAnnotations } = usePlaylistControls();
6321
6364
  const resolvedConfig = annotationListConfig != null ? annotationListConfig : { linkEndpoints, continuousPlay };
6322
- const handleAnnotationUpdate = useCallback22(
6365
+ const handleAnnotationUpdate = useCallback23(
6323
6366
  (updatedAnnotations) => {
6324
6367
  setAnnotations(updatedAnnotations);
6325
6368
  onAnnotationUpdate == null ? void 0 : onAnnotationUpdate(updatedAnnotations);
@@ -6403,7 +6446,7 @@ var Waveform = ({
6403
6446
  };
6404
6447
 
6405
6448
  // src/components/MediaElementPlaylist.tsx
6406
- import { useContext as useContext7, useRef as useRef22, useState as useState17, useCallback as useCallback23 } from "react";
6449
+ import { useContext as useContext7, useRef as useRef23, useState as useState18, useCallback as useCallback24 } from "react";
6407
6450
  import { DragDropProvider } from "@dnd-kit/react";
6408
6451
  import { RestrictToHorizontalAxis } from "@dnd-kit/abstract/modifiers";
6409
6452
  import {
@@ -6439,7 +6482,7 @@ var noDropAnimationPlugins = (defaults) => {
6439
6482
  };
6440
6483
 
6441
6484
  // src/components/AnimatedMediaElementPlayhead.tsx
6442
- import { useRef as useRef20, useEffect as useEffect15 } from "react";
6485
+ import { useRef as useRef21, useEffect as useEffect15 } from "react";
6443
6486
  import styled5 from "styled-components";
6444
6487
  import { jsx as jsx13 } from "react/jsx-runtime";
6445
6488
  var PlayheadLine2 = styled5.div`
@@ -6456,8 +6499,8 @@ var PlayheadLine2 = styled5.div`
6456
6499
  var AnimatedMediaElementPlayhead = ({
6457
6500
  color = "#ff0000"
6458
6501
  }) => {
6459
- const playheadRef = useRef20(null);
6460
- const animationFrameRef = useRef20(null);
6502
+ const playheadRef = useRef21(null);
6503
+ const animationFrameRef = useRef21(null);
6461
6504
  const { isPlaying, currentTimeRef } = useMediaElementAnimation();
6462
6505
  const { samplesPerPixel, sampleRate, progressBarWidth } = useMediaElementData();
6463
6506
  useEffect15(() => {
@@ -6496,7 +6539,7 @@ var AnimatedMediaElementPlayhead = ({
6496
6539
  };
6497
6540
 
6498
6541
  // src/components/ChannelWithMediaElementProgress.tsx
6499
- import { useRef as useRef21, useEffect as useEffect16 } from "react";
6542
+ import { useRef as useRef22, useEffect as useEffect16 } from "react";
6500
6543
  import styled6 from "styled-components";
6501
6544
  import {
6502
6545
  SmartChannel as SmartChannel2,
@@ -6541,8 +6584,8 @@ var ChannelWithMediaElementProgress = (_a) => {
6541
6584
  "clipStartSample",
6542
6585
  "clipDurationSamples"
6543
6586
  ]);
6544
- const progressRef = useRef21(null);
6545
- const animationFrameRef = useRef21(null);
6587
+ const progressRef = useRef22(null);
6588
+ const animationFrameRef = useRef22(null);
6546
6589
  const theme = useTheme3();
6547
6590
  const { waveHeight } = usePlaylistInfo2();
6548
6591
  const { isPlaying, currentTimeRef } = useMediaElementAnimation();
@@ -6685,11 +6728,11 @@ var MediaElementPlaylist = ({
6685
6728
  fadeIn,
6686
6729
  fadeOut
6687
6730
  } = useMediaElementData();
6688
- const [selectionStart, setSelectionStart] = useState17(0);
6689
- const [selectionEnd, setSelectionEnd] = useState17(0);
6690
- const [isSelecting, setIsSelecting] = useState17(false);
6691
- const scrollContainerRef = useRef22(null);
6692
- const handleScrollContainerRef = useCallback23(
6731
+ const [selectionStart, setSelectionStart] = useState18(0);
6732
+ const [selectionEnd, setSelectionEnd] = useState18(0);
6733
+ const [isSelecting, setIsSelecting] = useState18(false);
6734
+ const scrollContainerRef = useRef23(null);
6735
+ const handleScrollContainerRef = useCallback24(
6693
6736
  (el) => {
6694
6737
  scrollContainerRef.current = el;
6695
6738
  setScrollContainer(el);
@@ -6697,7 +6740,7 @@ var MediaElementPlaylist = ({
6697
6740
  [setScrollContainer]
6698
6741
  );
6699
6742
  const tracksFullWidth = Math.floor(duration * sampleRate / samplesPerPixel);
6700
- const handleAnnotationClick = useCallback23(
6743
+ const handleAnnotationClick = useCallback24(
6701
6744
  (annotation) => __async(null, null, function* () {
6702
6745
  setActiveAnnotationId(annotation.id);
6703
6746
  try {
@@ -6712,7 +6755,7 @@ var MediaElementPlaylist = ({
6712
6755
  }),
6713
6756
  [setActiveAnnotationId, play]
6714
6757
  );
6715
- const handleAnnotationUpdate = useCallback23(
6758
+ const handleAnnotationUpdate = useCallback24(
6716
6759
  (updatedAnnotations) => {
6717
6760
  setAnnotations(updatedAnnotations);
6718
6761
  onAnnotationUpdate == null ? void 0 : onAnnotationUpdate(updatedAnnotations);
@@ -6726,8 +6769,8 @@ var MediaElementPlaylist = ({
6726
6769
  duration,
6727
6770
  linkEndpoints: linkEndpointsProp
6728
6771
  });
6729
- const mouseDownTimeRef = useRef22(0);
6730
- const handleMouseDown = useCallback23(
6772
+ const mouseDownTimeRef = useRef23(0);
6773
+ const handleMouseDown = useCallback24(
6731
6774
  (e) => {
6732
6775
  const rect = e.currentTarget.getBoundingClientRect();
6733
6776
  const x = e.clientX - rect.left;
@@ -6739,7 +6782,7 @@ var MediaElementPlaylist = ({
6739
6782
  },
6740
6783
  [samplesPerPixel, sampleRate]
6741
6784
  );
6742
- const handleMouseMove = useCallback23(
6785
+ const handleMouseMove = useCallback24(
6743
6786
  (e) => {
6744
6787
  if (!isSelecting) return;
6745
6788
  const rect = e.currentTarget.getBoundingClientRect();
@@ -6749,7 +6792,7 @@ var MediaElementPlaylist = ({
6749
6792
  },
6750
6793
  [isSelecting, samplesPerPixel, sampleRate]
6751
6794
  );
6752
- const handleMouseUp = useCallback23(
6795
+ const handleMouseUp = useCallback24(
6753
6796
  (e) => {
6754
6797
  if (!isSelecting) return;
6755
6798
  setIsSelecting(false);
@@ -6935,7 +6978,7 @@ var MediaElementPlaylist = ({
6935
6978
  };
6936
6979
 
6937
6980
  // src/components/MediaElementAnnotationList.tsx
6938
- import { useCallback as useCallback24 } from "react";
6981
+ import { useCallback as useCallback25 } from "react";
6939
6982
  import { jsx as jsx16 } from "react/jsx-runtime";
6940
6983
  var MediaElementAnnotationList = ({
6941
6984
  height,
@@ -6951,7 +6994,7 @@ var MediaElementAnnotationList = ({
6951
6994
  const integration = useAnnotationIntegration();
6952
6995
  const { setAnnotations } = useMediaElementControls();
6953
6996
  const resolvedConfig = annotationListConfig != null ? annotationListConfig : { linkEndpoints: false, continuousPlay };
6954
- const handleAnnotationUpdate = useCallback24(
6997
+ const handleAnnotationUpdate = useCallback25(
6955
6998
  (updatedAnnotations) => {
6956
6999
  setAnnotations(updatedAnnotations);
6957
7000
  onAnnotationUpdate == null ? void 0 : onAnnotationUpdate(updatedAnnotations);
@@ -7026,6 +7069,7 @@ var KeyboardShortcuts = ({
7026
7069
  playback = false,
7027
7070
  clipSplitting = false,
7028
7071
  annotations = false,
7072
+ undo: undoEnabled = false,
7029
7073
  additionalShortcuts = []
7030
7074
  }) => {
7031
7075
  const { tracks, samplesPerPixel, playoutRef, duration } = usePlaylistData();
@@ -7035,7 +7079,7 @@ var KeyboardShortcuts = ({
7035
7079
  activeAnnotationId,
7036
7080
  continuousPlay
7037
7081
  } = usePlaylistState();
7038
- const { setAnnotations, setActiveAnnotationId, scrollContainerRef, play } = usePlaylistControls();
7082
+ const { setAnnotations, setActiveAnnotationId, scrollContainerRef, play, undo, redo } = usePlaylistControls();
7039
7083
  const { splitClipAtPlayhead } = useClipSplitting({
7040
7084
  tracks,
7041
7085
  samplesPerPixel,
@@ -7050,6 +7094,14 @@ var KeyboardShortcuts = ({
7050
7094
  preventDefault: true
7051
7095
  });
7052
7096
  }
7097
+ if (undoEnabled) {
7098
+ allAdditional.push(
7099
+ { key: "z", ctrlKey: true, shiftKey: false, action: undo, description: "Undo" },
7100
+ { key: "z", metaKey: true, shiftKey: false, action: undo, description: "Undo" },
7101
+ { key: "z", ctrlKey: true, shiftKey: true, action: redo, description: "Redo" },
7102
+ { key: "z", metaKey: true, shiftKey: true, action: redo, description: "Redo" }
7103
+ );
7104
+ }
7053
7105
  if (additionalShortcuts.length > 0) {
7054
7106
  allAdditional.push(...additionalShortcuts);
7055
7107
  }