@mieweb/ui 0.7.1-dev.2 → 0.7.1-dev.4

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.
@@ -7,7 +7,7 @@ var chunkDAPZPT5N_cjs = require('./chunk-DAPZPT5N.cjs');
7
7
  var chunkKJPOFUF5_cjs = require('./chunk-KJPOFUF5.cjs');
8
8
  var chunkMSRU3XBH_cjs = require('./chunk-MSRU3XBH.cjs');
9
9
  var chunkA2QVQF54_cjs = require('./chunk-A2QVQF54.cjs');
10
- var chunkFCBQP2U4_cjs = require('./chunk-FCBQP2U4.cjs');
10
+ var chunkO5UJRGRV_cjs = require('./chunk-O5UJRGRV.cjs');
11
11
  var chunkNUFUV43P_cjs = require('./chunk-NUFUV43P.cjs');
12
12
  var React6 = require('react');
13
13
  var classVarianceAuthority = require('class-variance-authority');
@@ -54,12 +54,12 @@ var FillerWordsModal = ({
54
54
  }) => {
55
55
  const [searchTerm, setSearchTerm] = React6__namespace.useState("");
56
56
  const [selectedFillers, setSelectedFillers] = React6__namespace.useState(
57
- new Set(chunkFCBQP2U4_cjs.DEFAULT_FILLER_WORDS)
57
+ new Set(chunkO5UJRGRV_cjs.DEFAULT_FILLER_WORDS)
58
58
  );
59
59
  const [customWord, setCustomWord] = React6__namespace.useState("");
60
60
  const [removeSilence, setRemoveSilence] = React6__namespace.useState(false);
61
61
  const [silenceThreshold, setSilenceThreshold] = React6__namespace.useState(0.4);
62
- const filteredFillers = chunkFCBQP2U4_cjs.DEFAULT_FILLER_WORDS.filter(
62
+ const filteredFillers = chunkO5UJRGRV_cjs.DEFAULT_FILLER_WORDS.filter(
63
63
  (word) => word.toLowerCase().includes(searchTerm.toLowerCase())
64
64
  ).sort((a, b) => {
65
65
  const countA = matchingCounts.get(a) || 0;
@@ -67,7 +67,7 @@ var FillerWordsModal = ({
67
67
  if (countB !== countA) return countB - countA;
68
68
  return a.localeCompare(b);
69
69
  });
70
- const customFillers = Array.from(selectedFillers).filter((word) => !chunkFCBQP2U4_cjs.DEFAULT_FILLER_WORDS.includes(word)).filter((word) => word.toLowerCase().includes(searchTerm.toLowerCase())).sort((a, b) => {
70
+ const customFillers = Array.from(selectedFillers).filter((word) => !chunkO5UJRGRV_cjs.DEFAULT_FILLER_WORDS.includes(word)).filter((word) => word.toLowerCase().includes(searchTerm.toLowerCase())).sort((a, b) => {
71
71
  const countA = matchingCounts.get(a) || 0;
72
72
  const countB = matchingCounts.get(b) || 0;
73
73
  if (countB !== countA) return countB - countA;
@@ -105,7 +105,7 @@ var FillerWordsModal = ({
105
105
  onClose();
106
106
  };
107
107
  const selectAll = () => {
108
- setSelectedFillers(/* @__PURE__ */ new Set([...chunkFCBQP2U4_cjs.DEFAULT_FILLER_WORDS, ...customFillers]));
108
+ setSelectedFillers(/* @__PURE__ */ new Set([...chunkO5UJRGRV_cjs.DEFAULT_FILLER_WORDS, ...customFillers]));
109
109
  };
110
110
  const selectNone = () => {
111
111
  setSelectedFillers(/* @__PURE__ */ new Set());
@@ -882,6 +882,11 @@ function wordClassName(s) {
882
882
  if (s.hasSpeedMarker) {
883
883
  parts.push("ml-0.5 border-l-[3px] border-l-warning pl-1.5");
884
884
  }
885
+ if (s.isSped) {
886
+ parts.push(
887
+ "underline decoration-warning decoration-dotted decoration-2 underline-offset-4"
888
+ );
889
+ }
885
890
  return parts.join(" ");
886
891
  }
887
892
  var Caret = ({ side }) => /* @__PURE__ */ jsxRuntime.jsx(
@@ -898,18 +903,23 @@ var MediaEditor = React6__namespace.forwardRef(
898
903
  transcript,
899
904
  initialEditedWords,
900
905
  initialUndoStack,
906
+ initialSpeedMarkers,
907
+ initialDefaultSpeed,
901
908
  onEditorStateChange,
902
909
  onHasEditsChange,
903
910
  onCursorTimestampChange,
904
911
  onEditedWordsRender,
912
+ onSpeedStateChange,
905
913
  playerRef: externalPlayerRef,
906
914
  className,
907
915
  splitLayout
908
916
  }, ref) => {
909
- const edits = chunkFCBQP2U4_cjs.useTranscriptEdits({
917
+ const edits = chunkO5UJRGRV_cjs.useTranscriptEdits({
910
918
  transcript,
911
919
  initialEditedWords,
912
920
  initialUndoStack,
921
+ initialSpeedMarkers,
922
+ initialDefaultSpeed,
913
923
  onChange: onEditorStateChange
914
924
  });
915
925
  const {
@@ -934,6 +944,7 @@ var MediaEditor = React6__namespace.forwardRef(
934
944
  defaultSpeed,
935
945
  setDefaultSpeed,
936
946
  toggleSpeedMarker,
947
+ setSpeedForRange,
937
948
  removeSpeedMarker,
938
949
  getSpeedAtIndex,
939
950
  getSpeedMarkerAtIndex,
@@ -987,6 +998,10 @@ var MediaEditor = React6__namespace.forwardRef(
987
998
  null
988
999
  );
989
1000
  const longPressTriggered = React6__namespace.useRef(false);
1001
+ const selectionAnchorRef = React6__namespace.useRef(null);
1002
+ const dragPointer = React6__namespace.useRef(null);
1003
+ const dragScrollFrame = React6__namespace.useRef(null);
1004
+ const dragListeners = React6__namespace.useRef(null);
990
1005
  const wordPlaybackStartMs = React6__namespace.useRef(null);
991
1006
  const wordPlaybackEndMs = React6__namespace.useRef(null);
992
1007
  const sequenceSegments = React6__namespace.useRef([]);
@@ -997,6 +1012,13 @@ var MediaEditor = React6__namespace.forwardRef(
997
1012
  React6__namespace.useEffect(() => {
998
1013
  onEditedWordsRender?.(editedWords);
999
1014
  }, [editedWords, onEditedWordsRender]);
1015
+ const onSpeedStateChangeRef = React6__namespace.useRef(onSpeedStateChange);
1016
+ React6__namespace.useEffect(() => {
1017
+ onSpeedStateChangeRef.current = onSpeedStateChange;
1018
+ });
1019
+ React6__namespace.useEffect(() => {
1020
+ onSpeedStateChangeRef.current?.(speedMarkers, defaultSpeed);
1021
+ }, [speedMarkers, defaultSpeed]);
1000
1022
  React6__namespace.useEffect(() => {
1001
1023
  if (onCursorTimestampChange) {
1002
1024
  const word = editedWords[cursorIndex];
@@ -1236,6 +1258,157 @@ var MediaEditor = React6__namespace.forwardRef(
1236
1258
  setSelection(null);
1237
1259
  }
1238
1260
  };
1261
+ const AUTOSCROLL_EDGE_PX = 24;
1262
+ const AUTOSCROLL_MAX_STEP_PX = 24;
1263
+ const extendDragSelection = (index) => {
1264
+ const anchor = selectionAnchorRef.current;
1265
+ if (!isDragging.current || anchor === null) return;
1266
+ setSelection({
1267
+ start: Math.min(anchor, index),
1268
+ end: Math.max(anchor, index)
1269
+ });
1270
+ setCursorIndex(index);
1271
+ };
1272
+ const wordIndexAtPoint = (x, y) => {
1273
+ const span = document.elementFromPoint?.(x, y)?.closest?.("[data-word-index]");
1274
+ if (!span) return null;
1275
+ const parsed = Number(span.dataset.wordIndex);
1276
+ return Number.isInteger(parsed) ? parsed : null;
1277
+ };
1278
+ const extendSelectionToPointer = () => {
1279
+ const container = contentRef.current;
1280
+ const pointer = dragPointer.current;
1281
+ if (!container || !pointer) return;
1282
+ const rect = container.getBoundingClientRect();
1283
+ const top = Math.max(rect.top, 0);
1284
+ const bottom = Math.min(rect.bottom, window.innerHeight);
1285
+ const left = Math.max(rect.left, 0);
1286
+ const right = Math.min(rect.right, window.innerWidth);
1287
+ if (bottom <= top || right <= left) return;
1288
+ const y = Math.min(Math.max(pointer.y, top + 2), bottom - 2);
1289
+ const x = Math.min(Math.max(pointer.x, left + 2), right - 2);
1290
+ for (const rawX of [x, left + (right - left) / 2, left + 16]) {
1291
+ const probeX = Math.min(Math.max(rawX, left + 2), right - 2);
1292
+ const index = wordIndexAtPoint(probeX, y);
1293
+ if (index !== null) {
1294
+ extendDragSelection(index);
1295
+ return;
1296
+ }
1297
+ }
1298
+ const spans = container.querySelectorAll("[data-word-index]");
1299
+ if (spans.length === 0) return;
1300
+ if (spans[0].getBoundingClientRect().top > y) {
1301
+ const parsed = Number(spans[0].dataset.wordIndex);
1302
+ if (Number.isInteger(parsed)) extendDragSelection(parsed);
1303
+ return;
1304
+ }
1305
+ for (let i = spans.length - 1; i >= 0; i--) {
1306
+ const spanRect = spans[i].getBoundingClientRect();
1307
+ if (spanRect.top <= y) {
1308
+ const parsed = Number(spans[i].dataset.wordIndex);
1309
+ if (Number.isInteger(parsed)) extendDragSelection(parsed);
1310
+ return;
1311
+ }
1312
+ }
1313
+ };
1314
+ const stepDragAutoScroll = () => {
1315
+ dragScrollFrame.current = null;
1316
+ const container = contentRef.current;
1317
+ const pointer = dragPointer.current;
1318
+ if (!isDragging.current || !container || !pointer) return;
1319
+ const rect = container.getBoundingClientRect();
1320
+ const top = Math.max(rect.top, 0);
1321
+ const bottom = Math.min(rect.bottom, window.innerHeight);
1322
+ if (bottom <= top) {
1323
+ dragScrollFrame.current = requestAnimationFrame(stepDragAutoScroll);
1324
+ return;
1325
+ }
1326
+ let step = 0;
1327
+ if (pointer.y < top + AUTOSCROLL_EDGE_PX) {
1328
+ step = Math.max(
1329
+ (pointer.y - (top + AUTOSCROLL_EDGE_PX)) / 3,
1330
+ -AUTOSCROLL_MAX_STEP_PX
1331
+ );
1332
+ } else if (pointer.y > bottom - AUTOSCROLL_EDGE_PX) {
1333
+ step = Math.min(
1334
+ (pointer.y - (bottom - AUTOSCROLL_EDGE_PX)) / 3,
1335
+ AUTOSCROLL_MAX_STEP_PX
1336
+ );
1337
+ }
1338
+ if (step !== 0) {
1339
+ const before = container.scrollTop;
1340
+ container.scrollTop += step;
1341
+ if (container.scrollTop !== before) {
1342
+ extendSelectionToPointer();
1343
+ }
1344
+ }
1345
+ dragScrollFrame.current = requestAnimationFrame(stepDragAutoScroll);
1346
+ };
1347
+ const detachDragListeners = () => {
1348
+ if (dragListeners.current) {
1349
+ document.removeEventListener("mousemove", dragListeners.current.move);
1350
+ document.removeEventListener("mouseup", dragListeners.current.up);
1351
+ dragListeners.current = null;
1352
+ }
1353
+ if (dragScrollFrame.current !== null) {
1354
+ cancelAnimationFrame(dragScrollFrame.current);
1355
+ dragScrollFrame.current = null;
1356
+ }
1357
+ };
1358
+ const handleMouseUp = () => {
1359
+ if (longPressTimer.current) {
1360
+ clearTimeout(longPressTimer.current);
1361
+ longPressTimer.current = null;
1362
+ }
1363
+ if (isDragging.current) {
1364
+ isDragging.current = false;
1365
+ }
1366
+ detachDragListeners();
1367
+ };
1368
+ const beginDragTracking = () => {
1369
+ detachDragListeners();
1370
+ const up = () => handleMouseUp();
1371
+ const move = (e) => {
1372
+ if (!isDragging.current) return;
1373
+ if (e.buttons === 0) {
1374
+ up();
1375
+ return;
1376
+ }
1377
+ dragPointer.current = { x: e.clientX, y: e.clientY };
1378
+ const container = contentRef.current;
1379
+ if (container && longPressTimer.current) {
1380
+ const rect = container.getBoundingClientRect();
1381
+ const outside = e.clientX < rect.left || e.clientX > rect.right || e.clientY < rect.top || e.clientY > rect.bottom;
1382
+ if (outside) {
1383
+ clearTimeout(longPressTimer.current);
1384
+ longPressTimer.current = null;
1385
+ }
1386
+ }
1387
+ extendSelectionToPointer();
1388
+ };
1389
+ dragListeners.current = { move, up };
1390
+ document.addEventListener("mousemove", move);
1391
+ document.addEventListener("mouseup", up);
1392
+ dragScrollFrame.current = requestAnimationFrame(stepDragAutoScroll);
1393
+ };
1394
+ React6__namespace.useEffect(
1395
+ () => () => {
1396
+ if (dragListeners.current) {
1397
+ document.removeEventListener("mousemove", dragListeners.current.move);
1398
+ document.removeEventListener("mouseup", dragListeners.current.up);
1399
+ dragListeners.current = null;
1400
+ }
1401
+ if (dragScrollFrame.current !== null) {
1402
+ cancelAnimationFrame(dragScrollFrame.current);
1403
+ dragScrollFrame.current = null;
1404
+ }
1405
+ if (longPressTimer.current) {
1406
+ clearTimeout(longPressTimer.current);
1407
+ longPressTimer.current = null;
1408
+ }
1409
+ },
1410
+ []
1411
+ );
1239
1412
  const handleWordMouseDown = (index, e) => {
1240
1413
  if (e.button !== 0) return;
1241
1414
  longPressTriggered.current = false;
@@ -1244,31 +1417,20 @@ var MediaEditor = React6__namespace.forwardRef(
1244
1417
  openWordEditor(index);
1245
1418
  }, 500);
1246
1419
  isDragging.current = true;
1420
+ selectionAnchorRef.current = index;
1421
+ dragPointer.current = { x: e.clientX, y: e.clientY };
1247
1422
  setSelectionAnchor(index);
1248
1423
  setCursorIndex(index);
1249
1424
  setCursorPosition("before");
1250
1425
  setSelection(null);
1426
+ beginDragTracking();
1251
1427
  };
1252
1428
  const handleWordMouseEnter = (index) => {
1253
1429
  if (longPressTimer.current) {
1254
1430
  clearTimeout(longPressTimer.current);
1255
1431
  longPressTimer.current = null;
1256
1432
  }
1257
- if (!isDragging.current || selectionAnchor === null) return;
1258
- setSelection({
1259
- start: Math.min(selectionAnchor, index),
1260
- end: Math.max(selectionAnchor, index)
1261
- });
1262
- setCursorIndex(index);
1263
- };
1264
- const handleMouseUp = () => {
1265
- if (longPressTimer.current) {
1266
- clearTimeout(longPressTimer.current);
1267
- longPressTimer.current = null;
1268
- }
1269
- if (isDragging.current) {
1270
- isDragging.current = false;
1271
- }
1433
+ extendDragSelection(index);
1272
1434
  };
1273
1435
  const handleWordContextMenu = (index, e) => {
1274
1436
  e.preventDefault();
@@ -1485,7 +1647,8 @@ var MediaEditor = React6__namespace.forwardRef(
1485
1647
  isSilenceNewline,
1486
1648
  isAnchor,
1487
1649
  isFocused,
1488
- hasSpeedMarker
1650
+ hasSpeedMarker,
1651
+ isSped: effectiveSpeed !== defaultSpeed
1489
1652
  }),
1490
1653
  onClick: (e) => handleWordClick(ew.word, index, e),
1491
1654
  onDoubleClick: (e) => handleWordDoubleClick(index, e),
@@ -1723,7 +1886,6 @@ var MediaEditor = React6__namespace.forwardRef(
1723
1886
  onFocus: () => setIsFocused(true),
1724
1887
  onBlur: () => setIsFocused(false),
1725
1888
  onMouseUp: handleMouseUp,
1726
- onMouseLeave: handleMouseUp,
1727
1889
  role: "listbox",
1728
1890
  "aria-label": "Transcript words",
1729
1891
  "aria-activedescendant": `word-${cursorIndex}`,
@@ -1787,7 +1949,10 @@ var MediaEditor = React6__namespace.forwardRef(
1787
1949
  currentMarker: speedMenuWordIndex !== null ? getSpeedMarkerAtIndex(speedMenuWordIndex) : void 0,
1788
1950
  defaultSpeed,
1789
1951
  onSetSpeed: (speed) => {
1790
- if (speedMenuWordIndex !== null) {
1952
+ if (speedMenuWordIndex === null) return;
1953
+ if (selection) {
1954
+ setSpeedForRange(selection.start, selection.end, speed);
1955
+ } else {
1791
1956
  toggleSpeedMarker(speedMenuWordIndex, speed);
1792
1957
  }
1793
1958
  },
@@ -1814,5 +1979,5 @@ exports.MediaEditor = MediaEditor;
1814
1979
  exports.SilenceSettingsModal = SilenceSettingsModal;
1815
1980
  exports.SpeedMarkerMenu = SpeedMarkerMenu;
1816
1981
  exports.WordEditorModal = WordEditorModal;
1817
- //# sourceMappingURL=chunk-7VF6WO7H.cjs.map
1818
- //# sourceMappingURL=chunk-7VF6WO7H.cjs.map
1982
+ //# sourceMappingURL=chunk-MBESCEWS.cjs.map
1983
+ //# sourceMappingURL=chunk-MBESCEWS.cjs.map