lyrics-transcriber 0.42.0__py3-none-any.whl → 0.43.0__py3-none-any.whl

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.
Files changed (21) hide show
  1. lyrics_transcriber/frontend/dist/assets/{index-coH8y7gV.js → index-D0Gr3Ep7.js} +283 -64
  2. lyrics_transcriber/frontend/dist/assets/index-D0Gr3Ep7.js.map +1 -0
  3. lyrics_transcriber/frontend/dist/index.html +1 -1
  4. lyrics_transcriber/frontend/src/components/AudioPlayer.tsx +7 -0
  5. lyrics_transcriber/frontend/src/components/EditModal.tsx +198 -30
  6. lyrics_transcriber/frontend/src/components/Header.tsx +0 -2
  7. lyrics_transcriber/frontend/src/components/LyricsAnalyzer.tsx +19 -3
  8. lyrics_transcriber/frontend/src/components/PreviewVideoSection.tsx +4 -1
  9. lyrics_transcriber/frontend/src/components/ReferenceView.tsx +54 -17
  10. lyrics_transcriber/frontend/src/components/ReviewChangesModal.tsx +32 -2
  11. lyrics_transcriber/frontend/src/components/TranscriptionView.tsx +0 -1
  12. lyrics_transcriber/frontend/src/components/shared/components/HighlightedText.tsx +0 -3
  13. lyrics_transcriber/frontend/src/components/shared/utils/keyboardHandlers.ts +33 -1
  14. lyrics_transcriber/frontend/src/types/global.d.ts +9 -0
  15. lyrics_transcriber/frontend/tsconfig.tsbuildinfo +1 -1
  16. {lyrics_transcriber-0.42.0.dist-info → lyrics_transcriber-0.43.0.dist-info}/METADATA +1 -1
  17. {lyrics_transcriber-0.42.0.dist-info → lyrics_transcriber-0.43.0.dist-info}/RECORD +20 -19
  18. lyrics_transcriber/frontend/dist/assets/index-coH8y7gV.js.map +0 -1
  19. {lyrics_transcriber-0.42.0.dist-info → lyrics_transcriber-0.43.0.dist-info}/LICENSE +0 -0
  20. {lyrics_transcriber-0.42.0.dist-info → lyrics_transcriber-0.43.0.dist-info}/WHEEL +0 -0
  21. {lyrics_transcriber-0.42.0.dist-info → lyrics_transcriber-0.43.0.dist-info}/entry_points.txt +0 -0
@@ -31235,7 +31235,6 @@ function HighlightedText({
31235
31235
  flashingHandler,
31236
31236
  corrections = []
31237
31237
  }) {
31238
- console.log("HighlightedText props:", { flashingType, flashingHandler });
31239
31238
  const { handleWordClick } = useWordClick({
31240
31239
  mode,
31241
31240
  onElementClick,
@@ -31249,7 +31248,6 @@ function HighlightedText({
31249
31248
  const shouldWordFlash = (wordPos) => {
31250
31249
  var _a, _b, _c;
31251
31250
  if (!flashingType) {
31252
- console.log("No flashingType");
31253
31251
  return false;
31254
31252
  }
31255
31253
  if ("type" in wordPos) {
@@ -31463,6 +31461,17 @@ function HighlightedText({
31463
31461
  }
31464
31462
  );
31465
31463
  }
31464
+ const SegmentControls$1 = styled(Box)({
31465
+ display: "flex",
31466
+ alignItems: "center",
31467
+ gap: "4px",
31468
+ paddingTop: "3px",
31469
+ paddingRight: "8px"
31470
+ });
31471
+ const TextContainer$1 = styled(Box)({
31472
+ flex: 1,
31473
+ minWidth: 0
31474
+ });
31466
31475
  function ReferenceView({
31467
31476
  referenceSources,
31468
31477
  anchors,
@@ -31574,6 +31583,9 @@ function ReferenceView({
31574
31583
  return correctionMap;
31575
31584
  }, [corrections, effectiveCurrentSource]);
31576
31585
  const currentSourceSegments = ((_a = referenceSources[effectiveCurrentSource]) == null ? void 0 : _a.segments) || [];
31586
+ const copyToClipboard = (text) => {
31587
+ navigator.clipboard.writeText(text);
31588
+ };
31577
31589
  return /* @__PURE__ */ jsxRuntimeExports.jsxs(Paper, { sx: { p: 2 }, children: [
31578
31590
  /* @__PURE__ */ jsxRuntimeExports.jsxs(Box, { sx: { display: "flex", justifyContent: "space-between", alignItems: "center", mb: 2 }, children: [
31579
31591
  /* @__PURE__ */ jsxRuntimeExports.jsx(Typography, { variant: "h6", children: "Reference Text" }),
@@ -31586,25 +31598,38 @@ function ReferenceView({
31586
31598
  }
31587
31599
  )
31588
31600
  ] }),
31589
- /* @__PURE__ */ jsxRuntimeExports.jsx(Box, { sx: { display: "flex", flexDirection: "column" }, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
31590
- HighlightedText,
31591
- {
31592
- wordPositions: referenceWordPositions,
31593
- segments: currentSourceSegments,
31594
- anchors,
31595
- onElementClick,
31596
- onWordClick,
31597
- flashingType,
31598
- highlightInfo,
31599
- mode,
31600
- isReference: true,
31601
- currentSource: effectiveCurrentSource,
31602
- linePositions,
31603
- referenceCorrections,
31604
- gaps,
31605
- preserveSegments: true
31606
- }
31607
- ) })
31601
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Box, { sx: { display: "flex", flexDirection: "column" }, children: currentSourceSegments.map((segment, index) => /* @__PURE__ */ jsxRuntimeExports.jsxs(Box, { sx: { display: "flex", alignItems: "flex-start", width: "100%" }, children: [
31602
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SegmentControls$1, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(
31603
+ IconButton,
31604
+ {
31605
+ size: "small",
31606
+ onClick: () => copyToClipboard(segment.words.map((w) => w.text).join(" ")),
31607
+ sx: { padding: "2px" },
31608
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(ContentCopyIcon, { fontSize: "small" })
31609
+ }
31610
+ ) }),
31611
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TextContainer$1, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(
31612
+ HighlightedText,
31613
+ {
31614
+ wordPositions: referenceWordPositions.filter(
31615
+ (wp) => segment.words.some((w) => w.id === wp.word.id)
31616
+ ),
31617
+ segments: [segment],
31618
+ anchors,
31619
+ onElementClick,
31620
+ onWordClick,
31621
+ flashingType,
31622
+ highlightInfo,
31623
+ mode,
31624
+ isReference: true,
31625
+ currentSource: effectiveCurrentSource,
31626
+ linePositions,
31627
+ referenceCorrections,
31628
+ gaps,
31629
+ preserveSegments: true
31630
+ }
31631
+ ) })
31632
+ ] }, index)) })
31608
31633
  ] });
31609
31634
  }
31610
31635
  function SegmentDetailsModal({
@@ -31701,7 +31726,6 @@ function TranscriptionView({
31701
31726
  currentTime = 0,
31702
31727
  anchors = []
31703
31728
  }) {
31704
- console.log("TranscriptionView props:", { flashingType, flashingHandler });
31705
31729
  const [selectedSegmentIndex, setSelectedSegmentIndex] = reactExports.useState(null);
31706
31730
  return /* @__PURE__ */ jsxRuntimeExports.jsxs(Paper, { sx: { p: 2 }, children: [
31707
31731
  /* @__PURE__ */ jsxRuntimeExports.jsx(Typography, { variant: "h6", gutterBottom: true, children: "Corrected Transcription" }),
@@ -31810,6 +31834,12 @@ const MoreVertIcon = createSvgIcon(/* @__PURE__ */ jsxRuntimeExports.jsx("path",
31810
31834
  const AutoFixHighIcon = createSvgIcon(/* @__PURE__ */ jsxRuntimeExports.jsx("path", {
31811
31835
  d: "M7.5 5.6 10 7 8.6 4.5 10 2 7.5 3.4 5 2l1.4 2.5L5 7zm12 9.8L17 14l1.4 2.5L17 19l2.5-1.4L22 19l-1.4-2.5L22 14zM22 2l-2.5 1.4L17 2l1.4 2.5L17 7l2.5-1.4L22 7l-1.4-2.5zm-7.63 5.29a.996.996 0 0 0-1.41 0L1.29 18.96c-.39.39-.39 1.02 0 1.41l2.34 2.34c.39.39 1.02.39 1.41 0L16.7 11.05c.39-.39.39-1.02 0-1.41zm-1.03 5.49-2.12-2.12 2.44-2.44 2.12 2.12z"
31812
31836
  }), "AutoFixHigh");
31837
+ const CancelIcon = createSvgIcon(/* @__PURE__ */ jsxRuntimeExports.jsx("path", {
31838
+ d: "M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2m5 13.59L15.59 17 12 13.41 8.41 17 7 15.59 10.59 12 7 8.41 8.41 7 12 10.59 15.59 7 17 8.41 13.41 12z"
31839
+ }), "Cancel");
31840
+ const StopIcon = createSvgIcon(/* @__PURE__ */ jsxRuntimeExports.jsx("path", {
31841
+ d: "M6 6h12v12H6z"
31842
+ }), "Stop");
31813
31843
  const TimelineContainer = styled(Box)(({ theme }) => ({
31814
31844
  position: "relative",
31815
31845
  height: "80px",
@@ -32124,16 +32154,113 @@ function EditModal({
32124
32154
  currentTime = 0,
32125
32155
  onDelete,
32126
32156
  onAddSegment,
32127
- onSplitSegment
32157
+ onSplitSegment,
32158
+ setModalSpacebarHandler
32128
32159
  }) {
32129
32160
  var _a, _b, _c, _d;
32130
32161
  const [editedSegment, setEditedSegment] = reactExports.useState(segment);
32131
32162
  const [menuAnchorEl, setMenuAnchorEl] = reactExports.useState(null);
32132
32163
  const [selectedWordIndex, setSelectedWordIndex] = reactExports.useState(null);
32133
32164
  const [replacementText, setReplacementText] = reactExports.useState("");
32165
+ const [isManualSyncing, setIsManualSyncing] = reactExports.useState(false);
32166
+ const [syncWordIndex, setSyncWordIndex] = reactExports.useState(-1);
32167
+ const [isPlaying, setIsPlaying] = reactExports.useState(false);
32168
+ const updateSegment2 = reactExports.useCallback((newWords) => {
32169
+ if (!editedSegment) return;
32170
+ const validStartTimes = newWords.map((w) => w.start_time).filter((t) => t !== null);
32171
+ const validEndTimes = newWords.map((w) => w.end_time).filter((t) => t !== null);
32172
+ const segmentStartTime = validStartTimes.length > 0 ? Math.min(...validStartTimes) : null;
32173
+ const segmentEndTime = validEndTimes.length > 0 ? Math.max(...validEndTimes) : null;
32174
+ setEditedSegment({
32175
+ ...editedSegment,
32176
+ words: newWords,
32177
+ text: newWords.map((w) => w.text).join(" "),
32178
+ start_time: segmentStartTime,
32179
+ end_time: segmentEndTime
32180
+ });
32181
+ }, [editedSegment]);
32182
+ const cleanupManualSync = reactExports.useCallback(() => {
32183
+ setIsManualSyncing(false);
32184
+ setSyncWordIndex(-1);
32185
+ }, []);
32186
+ const handleClose = reactExports.useCallback(() => {
32187
+ cleanupManualSync();
32188
+ onClose();
32189
+ }, [onClose, cleanupManualSync]);
32134
32190
  reactExports.useEffect(() => {
32135
32191
  setEditedSegment(segment);
32136
32192
  }, [segment]);
32193
+ reactExports.useEffect(() => {
32194
+ if (open) {
32195
+ setModalSpacebarHandler(() => (e) => {
32196
+ e.preventDefault();
32197
+ e.stopPropagation();
32198
+ if (isManualSyncing && editedSegment) {
32199
+ if (syncWordIndex < editedSegment.words.length) {
32200
+ const newWords = [...editedSegment.words];
32201
+ const currentWord = newWords[syncWordIndex];
32202
+ const prevWord = syncWordIndex > 0 ? newWords[syncWordIndex - 1] : null;
32203
+ currentWord.start_time = currentTime;
32204
+ if (prevWord) {
32205
+ prevWord.end_time = currentTime - 0.01;
32206
+ }
32207
+ if (syncWordIndex === editedSegment.words.length - 1) {
32208
+ currentWord.end_time = editedSegment.end_time;
32209
+ setIsManualSyncing(false);
32210
+ setSyncWordIndex(-1);
32211
+ updateSegment2(newWords);
32212
+ } else {
32213
+ setSyncWordIndex(syncWordIndex + 1);
32214
+ updateSegment2(newWords);
32215
+ }
32216
+ }
32217
+ } else if (editedSegment && onPlaySegment) {
32218
+ const startTime = editedSegment.start_time ?? 0;
32219
+ const endTime = editedSegment.end_time ?? 0;
32220
+ if (currentTime >= startTime && currentTime <= endTime) {
32221
+ if (window.toggleAudioPlayback) {
32222
+ window.toggleAudioPlayback();
32223
+ }
32224
+ } else {
32225
+ onPlaySegment(startTime);
32226
+ }
32227
+ }
32228
+ });
32229
+ } else {
32230
+ setModalSpacebarHandler(void 0);
32231
+ }
32232
+ return () => {
32233
+ setModalSpacebarHandler(void 0);
32234
+ };
32235
+ }, [
32236
+ open,
32237
+ isManualSyncing,
32238
+ editedSegment,
32239
+ syncWordIndex,
32240
+ currentTime,
32241
+ onPlaySegment,
32242
+ updateSegment2,
32243
+ setModalSpacebarHandler
32244
+ ]);
32245
+ reactExports.useEffect(() => {
32246
+ var _a2;
32247
+ if (!editedSegment) return;
32248
+ const endTime = editedSegment.end_time ?? 0;
32249
+ if (window.isAudioPlaying && currentTime > endTime) {
32250
+ console.log("Stopping playback: current time exceeded end time");
32251
+ (_a2 = window.toggleAudioPlayback) == null ? void 0 : _a2.call(window);
32252
+ setIsManualSyncing(false);
32253
+ setSyncWordIndex(-1);
32254
+ }
32255
+ }, [isManualSyncing, editedSegment, currentTime, setSyncWordIndex]);
32256
+ reactExports.useEffect(() => {
32257
+ if (editedSegment) {
32258
+ const startTime = editedSegment.start_time ?? 0;
32259
+ const endTime = editedSegment.end_time ?? 0;
32260
+ const isWithinSegment = currentTime >= startTime && currentTime <= endTime;
32261
+ setIsPlaying(isWithinSegment && window.isAudioPlaying === true);
32262
+ }
32263
+ }, [currentTime, editedSegment]);
32137
32264
  const getSafeTimeRange = (segment2) => {
32138
32265
  if (!segment2) return { start: 0, end: 1 };
32139
32266
  const start2 = segment2.start_time ?? 0;
@@ -32150,19 +32277,6 @@ function EditModal({
32150
32277
  };
32151
32278
  updateSegment2(newWords);
32152
32279
  };
32153
- const updateSegment2 = (newWords) => {
32154
- const validStartTimes = newWords.map((w) => w.start_time).filter((t) => t !== null);
32155
- const validEndTimes = newWords.map((w) => w.end_time).filter((t) => t !== null);
32156
- const segmentStartTime = validStartTimes.length > 0 ? Math.min(...validStartTimes) : null;
32157
- const segmentEndTime = validEndTimes.length > 0 ? Math.max(...validEndTimes) : null;
32158
- setEditedSegment({
32159
- ...editedSegment,
32160
- words: newWords,
32161
- text: newWords.map((w) => w.text).join(" "),
32162
- start_time: segmentStartTime,
32163
- end_time: segmentEndTime
32164
- });
32165
- };
32166
32280
  const handleAddWord = (index) => {
32167
32281
  const newWords = [...editedSegment.words];
32168
32282
  let newWord;
@@ -32316,11 +32430,33 @@ function EditModal({
32316
32430
  onSplitSegment == null ? void 0 : onSplitSegment(segmentIndex, wordIndex);
32317
32431
  }
32318
32432
  };
32433
+ const startManualSync = () => {
32434
+ if (isManualSyncing) {
32435
+ setIsManualSyncing(false);
32436
+ setSyncWordIndex(-1);
32437
+ return;
32438
+ }
32439
+ if (!editedSegment || !onPlaySegment) return;
32440
+ setIsManualSyncing(true);
32441
+ setSyncWordIndex(0);
32442
+ const startTime = (editedSegment.start_time ?? 0) - 3;
32443
+ onPlaySegment(startTime);
32444
+ };
32445
+ const handlePlayButtonClick = () => {
32446
+ if (!(segment == null ? void 0 : segment.start_time) || !onPlaySegment) return;
32447
+ if (isPlaying) {
32448
+ if (window.toggleAudioPlayback) {
32449
+ window.toggleAudioPlayback();
32450
+ }
32451
+ } else {
32452
+ onPlaySegment(segment.start_time);
32453
+ }
32454
+ };
32319
32455
  return /* @__PURE__ */ jsxRuntimeExports.jsxs(
32320
32456
  Dialog,
32321
32457
  {
32322
32458
  open,
32323
- onClose,
32459
+ onClose: handleClose,
32324
32460
  maxWidth: "md",
32325
32461
  fullWidth: true,
32326
32462
  onKeyDown: handleKeyDown,
@@ -32333,9 +32469,9 @@ function EditModal({
32333
32469
  IconButton,
32334
32470
  {
32335
32471
  size: "small",
32336
- onClick: () => onPlaySegment(segment.start_time),
32472
+ onClick: handlePlayButtonClick,
32337
32473
  sx: { padding: "4px" },
32338
- children: /* @__PURE__ */ jsxRuntimeExports.jsx(PlayCircleOutlineIcon, {})
32474
+ children: isPlaying ? /* @__PURE__ */ jsxRuntimeExports.jsx(StopIcon, {}) : /* @__PURE__ */ jsxRuntimeExports.jsx(PlayCircleOutlineIcon, {})
32339
32475
  }
32340
32476
  )
32341
32477
  ] }),
@@ -32353,16 +32489,37 @@ function EditModal({
32353
32489
  onPlaySegment
32354
32490
  }
32355
32491
  ) }),
32356
- /* @__PURE__ */ jsxRuntimeExports.jsxs(Typography, { variant: "body2", color: "text.secondary", sx: { mb: 2 }, children: [
32357
- "Original Time Range: ",
32358
- ((_a = originalSegment.start_time) == null ? void 0 : _a.toFixed(2)) ?? "N/A",
32359
- " - ",
32360
- ((_b = originalSegment.end_time) == null ? void 0 : _b.toFixed(2)) ?? "N/A",
32361
- /* @__PURE__ */ jsxRuntimeExports.jsx("br", {}),
32362
- "Current Time Range: ",
32363
- ((_c = editedSegment.start_time) == null ? void 0 : _c.toFixed(2)) ?? "N/A",
32364
- " - ",
32365
- ((_d = editedSegment.end_time) == null ? void 0 : _d.toFixed(2)) ?? "N/A"
32492
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(Box, { sx: { mb: 2, display: "flex", alignItems: "center", justifyContent: "space-between" }, children: [
32493
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(Typography, { variant: "body2", color: "text.secondary", children: [
32494
+ "Original Time Range: ",
32495
+ ((_a = originalSegment.start_time) == null ? void 0 : _a.toFixed(2)) ?? "N/A",
32496
+ " - ",
32497
+ ((_b = originalSegment.end_time) == null ? void 0 : _b.toFixed(2)) ?? "N/A",
32498
+ /* @__PURE__ */ jsxRuntimeExports.jsx("br", {}),
32499
+ "Current Time Range: ",
32500
+ ((_c = editedSegment.start_time) == null ? void 0 : _c.toFixed(2)) ?? "N/A",
32501
+ " - ",
32502
+ ((_d = editedSegment.end_time) == null ? void 0 : _d.toFixed(2)) ?? "N/A"
32503
+ ] }),
32504
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: 2 }, children: [
32505
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
32506
+ Button,
32507
+ {
32508
+ variant: isManualSyncing ? "outlined" : "contained",
32509
+ onClick: startManualSync,
32510
+ disabled: !onPlaySegment,
32511
+ startIcon: isManualSyncing ? /* @__PURE__ */ jsxRuntimeExports.jsx(CancelIcon, {}) : /* @__PURE__ */ jsxRuntimeExports.jsx(PlayCircleOutlineIcon, {}),
32512
+ color: isManualSyncing ? "error" : "primary",
32513
+ children: isManualSyncing ? "Cancel Sync" : "Manual Sync"
32514
+ }
32515
+ ),
32516
+ isManualSyncing && /* @__PURE__ */ jsxRuntimeExports.jsxs(Typography, { variant: "body2", children: [
32517
+ "Press spacebar for word ",
32518
+ syncWordIndex + 1,
32519
+ " of ",
32520
+ editedSegment == null ? void 0 : editedSegment.words.length
32521
+ ] })
32522
+ ] })
32366
32523
  ] }),
32367
32524
  /* @__PURE__ */ jsxRuntimeExports.jsx(Box, { sx: { display: "flex", flexDirection: "column", gap: 1, mb: 3 }, children: editedSegment.words.map((word, index) => {
32368
32525
  var _a2, _b2;
@@ -32467,8 +32624,11 @@ function EditModal({
32467
32624
  }
32468
32625
  )
32469
32626
  ] }),
32470
- /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { onClick: onClose, children: "Cancel" }),
32471
- /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { onClick: handleSave, variant: "contained", children: "Save Changes" })
32627
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { onClick: handleClose, children: "Cancel" }),
32628
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { onClick: () => {
32629
+ cleanupManualSync();
32630
+ onSave(editedSegment);
32631
+ }, children: "Save Changes" })
32472
32632
  ] }),
32473
32633
  /* @__PURE__ */ jsxRuntimeExports.jsxs(
32474
32634
  Menu,
@@ -32535,12 +32695,13 @@ function EditModal({
32535
32695
  }
32536
32696
  function PreviewVideoSection({
32537
32697
  apiClient,
32538
- isModalOpen,
32539
- updatedData
32698
+ isModalOpen: isModalOpen2,
32699
+ updatedData,
32700
+ videoRef
32540
32701
  }) {
32541
32702
  const [previewState, setPreviewState] = reactExports.useState({ status: "loading" });
32542
32703
  reactExports.useEffect(() => {
32543
- if (isModalOpen && apiClient) {
32704
+ if (isModalOpen2 && apiClient) {
32544
32705
  const generatePreview = async () => {
32545
32706
  setPreviewState({ status: "loading" });
32546
32707
  try {
@@ -32573,7 +32734,7 @@ function PreviewVideoSection({
32573
32734
  };
32574
32735
  generatePreview();
32575
32736
  }
32576
- }, [isModalOpen, apiClient, updatedData]);
32737
+ }, [isModalOpen2, apiClient, updatedData]);
32577
32738
  if (!apiClient) return null;
32578
32739
  return /* @__PURE__ */ jsxRuntimeExports.jsxs(Box, { sx: { mb: 2 }, children: [
32579
32740
  previewState.status === "loading" && /* @__PURE__ */ jsxRuntimeExports.jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: 2, p: 2 }, children: [
@@ -32604,6 +32765,7 @@ function PreviewVideoSection({
32604
32765
  }, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
32605
32766
  "video",
32606
32767
  {
32768
+ ref: videoRef,
32607
32769
  controls: true,
32608
32770
  src: previewState.videoUrl,
32609
32771
  style: {
@@ -32637,8 +32799,30 @@ function ReviewChangesModal({
32637
32799
  originalData,
32638
32800
  updatedData,
32639
32801
  onSubmit,
32640
- apiClient
32802
+ apiClient,
32803
+ setModalSpacebarHandler
32641
32804
  }) {
32805
+ const videoRef = reactExports.useRef(null);
32806
+ reactExports.useEffect(() => {
32807
+ if (open) {
32808
+ setModalSpacebarHandler(() => (e) => {
32809
+ e.preventDefault();
32810
+ e.stopPropagation();
32811
+ if (videoRef.current) {
32812
+ if (videoRef.current.paused) {
32813
+ videoRef.current.play();
32814
+ } else {
32815
+ videoRef.current.pause();
32816
+ }
32817
+ }
32818
+ });
32819
+ } else {
32820
+ setModalSpacebarHandler(void 0);
32821
+ }
32822
+ return () => {
32823
+ setModalSpacebarHandler(void 0);
32824
+ };
32825
+ }, [open, setModalSpacebarHandler]);
32642
32826
  const differences = reactExports.useMemo(() => {
32643
32827
  var _a, _b;
32644
32828
  const diffs = [];
@@ -32798,7 +32982,8 @@ function ReviewChangesModal({
32798
32982
  {
32799
32983
  apiClient,
32800
32984
  isModalOpen: open,
32801
- updatedData
32985
+ updatedData,
32986
+ videoRef
32802
32987
  }
32803
32988
  ),
32804
32989
  /* @__PURE__ */ jsxRuntimeExports.jsx(Box, { sx: { p: 2, mt: 0 }, children: differences.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsxs(Box, { children: [
@@ -32972,9 +33157,18 @@ const clearSavedData = (data) => {
32972
33157
  delete savedDataObj[storageKey];
32973
33158
  localStorage.setItem("lyrics_analyzer_data", JSON.stringify(savedDataObj));
32974
33159
  };
33160
+ let currentModalHandler;
33161
+ let isModalOpen = false;
33162
+ const setModalHandler = (handler, open) => {
33163
+ currentModalHandler = handler;
33164
+ isModalOpen = open;
33165
+ };
32975
33166
  const setupKeyboardHandlers = (state) => {
33167
+ const handlerId = Math.random().toString(36).substr(2, 9);
33168
+ console.log(`Setting up keyboard handlers [${handlerId}]`);
32976
33169
  const handleKeyDown = (e) => {
32977
33170
  if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement) {
33171
+ console.log(`[${handlerId}] Ignoring keydown in input/textarea`);
32978
33172
  return;
32979
33173
  }
32980
33174
  if (e.key === "Shift") {
@@ -32983,8 +33177,17 @@ const setupKeyboardHandlers = (state) => {
32983
33177
  } else if (e.key === "Meta") {
32984
33178
  state.setIsCtrlPressed(true);
32985
33179
  } else if (e.key === " " || e.code === "Space") {
33180
+ console.log(`[${handlerId}] Spacebar pressed:`, {
33181
+ modalOpen: isModalOpen,
33182
+ hasModalHandler: !!currentModalHandler,
33183
+ hasGlobalToggle: !!window.toggleAudioPlayback
33184
+ });
32986
33185
  e.preventDefault();
32987
- if (window.toggleAudioPlayback) {
33186
+ if (isModalOpen && currentModalHandler) {
33187
+ console.log(`[${handlerId}] Using modal spacebar handler`);
33188
+ currentModalHandler(e);
33189
+ } else if (window.toggleAudioPlayback && !isModalOpen) {
33190
+ console.log(`[${handlerId}] Using global audio toggle`);
32988
33191
  window.toggleAudioPlayback();
32989
33192
  }
32990
33193
  }
@@ -33062,14 +33265,19 @@ function AudioPlayer({ apiClient, onTimeUpdate, audioHash }) {
33062
33265
  animationFrameId = requestAnimationFrame(updateTime);
33063
33266
  };
33064
33267
  audio.addEventListener("play", () => {
33268
+ setIsPlaying(true);
33269
+ window.isAudioPlaying = true;
33065
33270
  updateTime();
33066
33271
  });
33067
33272
  audio.addEventListener("pause", () => {
33273
+ setIsPlaying(false);
33274
+ window.isAudioPlaying = false;
33068
33275
  cancelAnimationFrame(animationFrameId);
33069
33276
  });
33070
33277
  audio.addEventListener("ended", () => {
33071
33278
  cancelAnimationFrame(animationFrameId);
33072
33279
  setIsPlaying(false);
33280
+ window.isAudioPlaying = false;
33073
33281
  setCurrentTime(0);
33074
33282
  });
33075
33283
  audio.addEventListener("loadedmetadata", () => {
@@ -33080,6 +33288,7 @@ function AudioPlayer({ apiClient, onTimeUpdate, audioHash }) {
33080
33288
  audio.pause();
33081
33289
  audio.src = "";
33082
33290
  audioRef.current = null;
33291
+ window.isAudioPlaying = false;
33083
33292
  };
33084
33293
  }, [apiClient, onTimeUpdate, audioHash]);
33085
33294
  const handlePlayPause = () => {
@@ -33213,7 +33422,6 @@ function Header({
33213
33422
  const replacedCount = data.corrections.filter((c) => !c.is_deletion && !c.split_total).length;
33214
33423
  const addedCount = data.corrections.filter((c) => c.split_total).length;
33215
33424
  const deletedCount = data.corrections.filter((c) => c.is_deletion).length;
33216
- console.log("Header: Render with isUpdatingHandlers =", isUpdatingHandlers);
33217
33425
  return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
33218
33426
  isReadOnly && /* @__PURE__ */ jsxRuntimeExports.jsxs(Box, { sx: { display: "flex", alignItems: "center", mb: 2, color: "text.secondary" }, children: [
33219
33427
  /* @__PURE__ */ jsxRuntimeExports.jsx(LockIcon, { sx: { mr: 1 } }),
@@ -33414,18 +33622,21 @@ function LyricsAnalyzer({ data: initialData, onFileLoad, apiClient, isReadOnly,
33414
33622
  }
33415
33623
  }, [data, isReadOnly, initialData]);
33416
33624
  reactExports.useEffect(() => {
33625
+ console.log("Setting up keyboard handlers in LyricsAnalyzer");
33417
33626
  const { handleKeyDown, handleKeyUp } = setupKeyboardHandlers({
33418
33627
  setIsShiftPressed,
33419
33628
  setIsCtrlPressed
33420
33629
  });
33630
+ console.log("Adding keyboard event listeners");
33421
33631
  window.addEventListener("keydown", handleKeyDown);
33422
33632
  window.addEventListener("keyup", handleKeyUp);
33423
33633
  return () => {
33634
+ console.log("Removing keyboard event listeners");
33424
33635
  window.removeEventListener("keydown", handleKeyDown);
33425
33636
  window.removeEventListener("keyup", handleKeyUp);
33426
33637
  document.body.style.userSelect = "";
33427
33638
  };
33428
- }, []);
33639
+ }, [setIsShiftPressed, setIsCtrlPressed]);
33429
33640
  const effectiveMode = isShiftPressed ? "highlight" : isCtrlPressed ? "edit" : interactionMode;
33430
33641
  const handleFlash = reactExports.useCallback((type, info) => {
33431
33642
  setFlashingType(null);
@@ -33673,6 +33884,9 @@ function LyricsAnalyzer({ data: initialData, onFileLoad, apiClient, isReadOnly,
33673
33884
  setFlashingType(null);
33674
33885
  }, 1500);
33675
33886
  }, []);
33887
+ const handleSetModalSpacebarHandler = reactExports.useCallback((handler) => {
33888
+ setModalHandler(handler ? handler() : void 0, !!handler);
33889
+ }, []);
33676
33890
  return /* @__PURE__ */ jsxRuntimeExports.jsxs(Box, { sx: {
33677
33891
  p: 3,
33678
33892
  pb: 6,
@@ -33748,7 +33962,10 @@ function LyricsAnalyzer({ data: initialData, onFileLoad, apiClient, isReadOnly,
33748
33962
  EditModal,
33749
33963
  {
33750
33964
  open: Boolean(editModalSegment),
33751
- onClose: () => setEditModalSegment(null),
33965
+ onClose: () => {
33966
+ setEditModalSegment(null);
33967
+ handleSetModalSpacebarHandler(void 0);
33968
+ },
33752
33969
  segment: (editModalSegment == null ? void 0 : editModalSegment.segment) ?? null,
33753
33970
  segmentIndex: (editModalSegment == null ? void 0 : editModalSegment.index) ?? null,
33754
33971
  originalSegment: (editModalSegment == null ? void 0 : editModalSegment.originalSegment) ?? null,
@@ -33757,7 +33974,8 @@ function LyricsAnalyzer({ data: initialData, onFileLoad, apiClient, isReadOnly,
33757
33974
  onAddSegment: handleAddSegment,
33758
33975
  onSplitSegment: handleSplitSegment,
33759
33976
  onPlaySegment: handlePlaySegment,
33760
- currentTime: currentAudioTime
33977
+ currentTime: currentAudioTime,
33978
+ setModalSpacebarHandler: handleSetModalSpacebarHandler
33761
33979
  }
33762
33980
  ),
33763
33981
  /* @__PURE__ */ jsxRuntimeExports.jsx(
@@ -33768,7 +33986,8 @@ function LyricsAnalyzer({ data: initialData, onFileLoad, apiClient, isReadOnly,
33768
33986
  originalData,
33769
33987
  updatedData: data,
33770
33988
  onSubmit: handleSubmitToServer,
33771
- apiClient
33989
+ apiClient,
33990
+ setModalSpacebarHandler: handleSetModalSpacebarHandler
33772
33991
  }
33773
33992
  ),
33774
33993
  !isReadOnly && apiClient && /* @__PURE__ */ jsxRuntimeExports.jsxs(Box, { sx: { mt: 2, mb: 3, display: "flex", gap: 2 }, children: [
@@ -33945,4 +34164,4 @@ function App() {
33945
34164
  ReactDOM$1.createRoot(document.getElementById("root")).render(
33946
34165
  /* @__PURE__ */ jsxRuntimeExports.jsx(App, {})
33947
34166
  );
33948
- //# sourceMappingURL=index-coH8y7gV.js.map
34167
+ //# sourceMappingURL=index-D0Gr3Ep7.js.map