lyrics-transcriber 0.46.0__py3-none-any.whl → 0.47.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.
@@ -33018,7 +33018,7 @@ function useWordClick({
33018
33018
  gap2 = matchingGap2;
33019
33019
  }
33020
33020
  }
33021
- if (mode === "highlight" || mode === "edit") {
33021
+ if (mode === "highlight" || mode === "edit" || mode === "delete_word") {
33022
33022
  if (belongsToAnchor && anchor) {
33023
33023
  onWordClick == null ? void 0 : onWordClick({
33024
33024
  word_id: wordId,
@@ -33050,7 +33050,7 @@ function useWordClick({
33050
33050
  gap: void 0
33051
33051
  });
33052
33052
  }
33053
- } else if (mode === "details") {
33053
+ } else {
33054
33054
  if (belongsToAnchor && anchor) {
33055
33055
  onElementClick({
33056
33056
  type: "anchor",
@@ -34766,9 +34766,16 @@ const WordRow = reactExports.memo(function WordRow2({
34766
34766
  onWordUpdate,
34767
34767
  onSplitWord,
34768
34768
  onRemoveWord,
34769
- wordsLength
34769
+ wordsLength,
34770
+ onTabNavigation
34770
34771
  }) {
34771
34772
  var _a, _b;
34773
+ const handleKeyDown = (e) => {
34774
+ if (e.key === "Tab" && !e.shiftKey) {
34775
+ e.preventDefault();
34776
+ onTabNavigation(index);
34777
+ }
34778
+ };
34772
34779
  return /* @__PURE__ */ jsxRuntimeExports.jsxs(Box, { sx: {
34773
34780
  display: "flex",
34774
34781
  gap: 2,
@@ -34781,8 +34788,10 @@ const WordRow = reactExports.memo(function WordRow2({
34781
34788
  label: `Word ${index}`,
34782
34789
  value: word.text,
34783
34790
  onChange: (e) => onWordUpdate(index, { text: e.target.value }),
34791
+ onKeyDown: handleKeyDown,
34784
34792
  fullWidth: true,
34785
- size: "small"
34793
+ size: "small",
34794
+ id: `word-text-${index}`
34786
34795
  }
34787
34796
  ),
34788
34797
  /* @__PURE__ */ jsxRuntimeExports.jsx(
@@ -34844,7 +34853,8 @@ const WordItem = reactExports.memo(function WordItem2({
34844
34853
  onAddSegment,
34845
34854
  onMergeSegment,
34846
34855
  wordsLength,
34847
- isGlobal
34856
+ isGlobal,
34857
+ onTabNavigation
34848
34858
  }) {
34849
34859
  return /* @__PURE__ */ jsxRuntimeExports.jsxs(Box, { children: [
34850
34860
  /* @__PURE__ */ jsxRuntimeExports.jsx(
@@ -34855,7 +34865,8 @@ const WordItem = reactExports.memo(function WordItem2({
34855
34865
  onWordUpdate,
34856
34866
  onSplitWord,
34857
34867
  onRemoveWord,
34858
- wordsLength
34868
+ wordsLength,
34869
+ onTabNavigation
34859
34870
  }
34860
34871
  ),
34861
34872
  !isGlobal && /* @__PURE__ */ jsxRuntimeExports.jsx(
@@ -34915,6 +34926,33 @@ function EditWordList({
34915
34926
  const handlePageChange = (_event, value) => {
34916
34927
  setPage(value);
34917
34928
  };
34929
+ const handleTabNavigation = (currentIndex) => {
34930
+ const nextIndex = (currentIndex + 1) % words.length;
34931
+ if (isGlobal && (nextIndex < startIndex || nextIndex >= endIndex)) {
34932
+ const nextPage = Math.floor(nextIndex / pageSize) + 1;
34933
+ setPage(nextPage);
34934
+ setTimeout(() => {
34935
+ focusWordTextField(nextIndex);
34936
+ }, 50);
34937
+ } else {
34938
+ focusWordTextField(nextIndex);
34939
+ }
34940
+ };
34941
+ const focusWordTextField = (index) => {
34942
+ const element = document.getElementById(`word-text-${index}`);
34943
+ if (element) {
34944
+ let input = element.querySelector("input");
34945
+ if (!input) {
34946
+ input = element.querySelector(".MuiInputBase-input");
34947
+ }
34948
+ if (input) {
34949
+ input.focus();
34950
+ input.select();
34951
+ } else {
34952
+ element.focus();
34953
+ }
34954
+ }
34955
+ };
34918
34956
  return /* @__PURE__ */ jsxRuntimeExports.jsxs(Box, { sx: { display: "flex", flexDirection: "column", gap: 1, flexGrow: 1, minHeight: 0 }, children: [
34919
34957
  !isGlobal && /* @__PURE__ */ jsxRuntimeExports.jsx(
34920
34958
  WordDivider,
@@ -34966,7 +35004,8 @@ function EditWordList({
34966
35004
  onAddSegment,
34967
35005
  onMergeSegment,
34968
35006
  wordsLength: words.length,
34969
- isGlobal
35007
+ isGlobal,
35008
+ onTabNavigation: handleTabNavigation
34970
35009
  },
34971
35010
  word.id
34972
35011
  );
@@ -35179,15 +35218,6 @@ function EditModal({
35179
35218
  isGlobal = false,
35180
35219
  isLoading = false
35181
35220
  }) {
35182
- console.log("EditModal - Render", {
35183
- open,
35184
- isGlobal,
35185
- isLoading,
35186
- hasSegment: !!segment,
35187
- segmentIndex,
35188
- hasOriginalSegment: !!originalSegment,
35189
- hasOriginalTranscribedSegment: !!originalTranscribedSegment
35190
- });
35191
35221
  const [editedSegment, setEditedSegment] = reactExports.useState(segment);
35192
35222
  const [isPlaying, setIsPlaying] = reactExports.useState(false);
35193
35223
  const updateSegment2 = reactExports.useCallback((newWords) => {
@@ -35218,19 +35248,12 @@ function EditModal({
35218
35248
  updateSegment: updateSegment2
35219
35249
  });
35220
35250
  const handleClose = reactExports.useCallback(() => {
35221
- console.log("EditModal - handleClose called");
35222
35251
  cleanupManualSync();
35223
35252
  onClose();
35224
35253
  }, [onClose, cleanupManualSync]);
35225
35254
  reactExports.useEffect(() => {
35226
35255
  const spacebarHandler = handleSpacebar;
35227
35256
  if (open) {
35228
- console.log("EditModal - Setting up modal spacebar handler", {
35229
- hasPlaySegment: !!onPlaySegment,
35230
- editedSegmentId: editedSegment == null ? void 0 : editedSegment.id,
35231
- handlerFunction: spacebarHandler.toString().slice(0, 100),
35232
- isLoading
35233
- });
35234
35257
  const handleKeyEvent = (e) => {
35235
35258
  if (e.code === "Space") {
35236
35259
  spacebarHandler(e);
@@ -35239,7 +35262,6 @@ function EditModal({
35239
35262
  setModalSpacebarHandler(() => handleKeyEvent);
35240
35263
  return () => {
35241
35264
  if (!open) {
35242
- console.log("EditModal - Cleanup: clearing modal spacebar handler");
35243
35265
  setModalSpacebarHandler(void 0);
35244
35266
  }
35245
35267
  };
@@ -35261,11 +35283,6 @@ function EditModal({
35261
35283
  }
35262
35284
  }, [currentTime, editedSegment]);
35263
35285
  reactExports.useEffect(() => {
35264
- console.log("EditModal - segment changed", {
35265
- hasSegment: !!segment,
35266
- segmentId: segment == null ? void 0 : segment.id,
35267
- wordCount: segment == null ? void 0 : segment.words.length
35268
- });
35269
35286
  setEditedSegment(segment);
35270
35287
  }, [segment]);
35271
35288
  reactExports.useEffect(() => {
@@ -35273,7 +35290,6 @@ function EditModal({
35273
35290
  if (!editedSegment) return;
35274
35291
  const endTime = editedSegment.end_time ?? 0;
35275
35292
  if (window.isAudioPlaying && currentTime > endTime) {
35276
- console.log("Stopping playback: current time exceeded end time");
35277
35293
  (_a = window.toggleAudioPlayback) == null ? void 0 : _a.call(window);
35278
35294
  cleanupManualSync();
35279
35295
  }
@@ -35436,7 +35452,6 @@ function EditModal({
35436
35452
  return getSafeTimeRange(editedSegment);
35437
35453
  }, [getSafeTimeRange, editedSegment]);
35438
35454
  const dialogTitle = reactExports.useMemo(() => {
35439
- console.log("EditModal - Rendering dialog title", { isLoading, isGlobal });
35440
35455
  if (isLoading) {
35441
35456
  return /* @__PURE__ */ jsxRuntimeExports.jsxs(DialogTitle, { sx: { display: "flex", alignItems: "center", gap: 1 }, children: [
35442
35457
  /* @__PURE__ */ jsxRuntimeExports.jsxs(Box, { sx: { flex: 1, display: "flex", alignItems: "center", gap: 1 }, children: [
@@ -35466,23 +35481,11 @@ function EditModal({
35466
35481
  ] });
35467
35482
  }, [isGlobal, segmentIndex, segment, onPlaySegment, handlePlayButtonClick, isPlaying, onClose, isLoading]);
35468
35483
  if (!isLoading && (!segment || !editedSegment || !originalSegment)) {
35469
- console.log("EditModal - Early return: missing required data", {
35470
- hasSegment: !!segment,
35471
- hasEditedSegment: !!editedSegment,
35472
- hasOriginalSegment: !!originalSegment,
35473
- isLoading
35474
- });
35475
35484
  return null;
35476
35485
  }
35477
35486
  if (!isLoading && !isGlobal && segmentIndex === null) {
35478
- console.log("EditModal - Early return: non-global mode with null segmentIndex");
35479
35487
  return null;
35480
35488
  }
35481
- console.log("EditModal - Rendering dialog content", {
35482
- isLoading,
35483
- hasEditedSegment: !!editedSegment,
35484
- hasOriginalSegment: !!originalSegment
35485
- });
35486
35489
  return /* @__PURE__ */ jsxRuntimeExports.jsxs(
35487
35490
  Dialog,
35488
35491
  {
@@ -36165,6 +36168,30 @@ function findAndReplace(data, findText, replaceText, options = {
36165
36168
  newData.corrected_segments = newData.corrected_segments.filter((segment) => segment.words.length > 0);
36166
36169
  return newData;
36167
36170
  }
36171
+ function deleteWord(data, wordId) {
36172
+ const segmentIndex = data.corrected_segments.findIndex(
36173
+ (segment2) => segment2.words.some((word) => word.id === wordId)
36174
+ );
36175
+ if (segmentIndex === -1) {
36176
+ return data;
36177
+ }
36178
+ const segment = data.corrected_segments[segmentIndex];
36179
+ const wordIndex = segment.words.findIndex((word) => word.id === wordId);
36180
+ if (wordIndex === -1) {
36181
+ return data;
36182
+ }
36183
+ const updatedWords = segment.words.filter((_, index) => index !== wordIndex);
36184
+ if (updatedWords.length > 0) {
36185
+ const updatedSegment = {
36186
+ ...segment,
36187
+ words: updatedWords,
36188
+ text: updatedWords.map((w) => w.text).join(" ")
36189
+ };
36190
+ return updateSegment(data, segmentIndex, updatedSegment);
36191
+ } else {
36192
+ return deleteSegment(data, segmentIndex);
36193
+ }
36194
+ }
36168
36195
  const generateStorageKey = (data) => {
36169
36196
  var _a;
36170
36197
  const text = ((_a = data.original_segments[0]) == null ? void 0 : _a.text) || "";
@@ -36238,6 +36265,12 @@ const setModalHandler = (handler, open) => {
36238
36265
  };
36239
36266
  const setupKeyboardHandlers = (state) => {
36240
36267
  Math.random().toString(36).substr(2, 9);
36268
+ const resetModifierStates = () => {
36269
+ var _a;
36270
+ state.setIsShiftPressed(false);
36271
+ (_a = state.setIsCtrlPressed) == null ? void 0 : _a.call(state, false);
36272
+ document.body.style.userSelect = "";
36273
+ };
36241
36274
  const handleKeyDown = (e) => {
36242
36275
  var _a;
36243
36276
  if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement) {
@@ -36246,7 +36279,7 @@ const setupKeyboardHandlers = (state) => {
36246
36279
  if (e.key === "Shift") {
36247
36280
  state.setIsShiftPressed(true);
36248
36281
  document.body.style.userSelect = "none";
36249
- } else if (e.key === "Meta") {
36282
+ } else if (e.key === "Control" || e.key === "Ctrl" || e.key === "Meta") {
36250
36283
  (_a = state.setIsCtrlPressed) == null ? void 0 : _a.call(state, true);
36251
36284
  } else if (e.key === " " || e.code === "Space") {
36252
36285
  e.preventDefault();
@@ -36258,20 +36291,30 @@ const setupKeyboardHandlers = (state) => {
36258
36291
  }
36259
36292
  };
36260
36293
  const handleKeyUp = (e) => {
36261
- var _a;
36262
- if (e.key === "Shift") {
36263
- state.setIsShiftPressed(false);
36264
- document.body.style.userSelect = "";
36265
- } else if (e.key === "Meta") {
36266
- (_a = state.setIsCtrlPressed) == null ? void 0 : _a.call(state, false);
36267
- } else if (e.key === " " || e.code === "Space") {
36294
+ resetModifierStates();
36295
+ if (e.key === " " || e.code === "Space") {
36268
36296
  e.preventDefault();
36269
36297
  if (isModalOpen && currentModalHandler) {
36270
36298
  currentModalHandler(e);
36271
36299
  }
36272
36300
  }
36273
36301
  };
36274
- return { handleKeyDown, handleKeyUp };
36302
+ const handleWindowBlur = () => {
36303
+ resetModifierStates();
36304
+ };
36305
+ const handleWindowFocus = () => {
36306
+ resetModifierStates();
36307
+ };
36308
+ window.addEventListener("blur", handleWindowBlur);
36309
+ window.addEventListener("focus", handleWindowFocus);
36310
+ return {
36311
+ handleKeyDown,
36312
+ handleKeyUp,
36313
+ cleanup: () => {
36314
+ window.removeEventListener("blur", handleWindowBlur);
36315
+ window.removeEventListener("focus", handleWindowFocus);
36316
+ }
36317
+ };
36275
36318
  };
36276
36319
  function ModeSelector({ effectiveMode, onChange }) {
36277
36320
  return /* @__PURE__ */ jsxRuntimeExports.jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: 1.2, height: "32px" }, children: [
@@ -36281,7 +36324,7 @@ function ModeSelector({ effectiveMode, onChange }) {
36281
36324
  {
36282
36325
  value: effectiveMode,
36283
36326
  exclusive: true,
36284
- onChange: (_, newMode) => newMode && onChange(newMode),
36327
+ onChange: (_, newMode) => newMode === "edit" && onChange(newMode),
36285
36328
  size: "small",
36286
36329
  sx: {
36287
36330
  height: "32px",
@@ -36292,28 +36335,38 @@ function ModeSelector({ effectiveMode, onChange }) {
36292
36335
  }
36293
36336
  },
36294
36337
  children: [
36295
- /* @__PURE__ */ jsxRuntimeExports.jsxs(
36338
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Tooltip, { title: "Default mode; click words to edit that lyrics segment", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
36296
36339
  ToggleButton,
36297
36340
  {
36298
36341
  value: "edit",
36299
- title: "Click to edit segments and make corrections in the transcription view",
36300
36342
  children: [
36301
36343
  /* @__PURE__ */ jsxRuntimeExports.jsx(EditIcon, { sx: { mr: 0.5, fontSize: "1rem" } }),
36302
36344
  "Edit"
36303
36345
  ]
36304
36346
  }
36305
- ),
36306
- /* @__PURE__ */ jsxRuntimeExports.jsxs(
36347
+ ) }),
36348
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Tooltip, { title: "Hold SHIFT and click words to highlight the matching anchor sequence in the reference lyrics", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
36307
36349
  ToggleButton,
36308
36350
  {
36309
36351
  value: "highlight",
36310
- title: "Click words in the transcription view to highlight the matching anchor sequence in the reference lyrics. You can also hold SHIFT to temporarily activate this mode.",
36352
+ disabled: true,
36311
36353
  children: [
36312
36354
  /* @__PURE__ */ jsxRuntimeExports.jsx(HighlightIcon, { sx: { mr: 0.5, fontSize: "1rem" } }),
36313
36355
  "Highlight"
36314
36356
  ]
36315
36357
  }
36316
- )
36358
+ ) }) }),
36359
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Tooltip, { title: "Hold CTRL and click words to delete them", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
36360
+ ToggleButton,
36361
+ {
36362
+ value: "delete_word",
36363
+ disabled: true,
36364
+ children: [
36365
+ /* @__PURE__ */ jsxRuntimeExports.jsx(DeleteIcon, { sx: { mr: 0.5, fontSize: "1rem" } }),
36366
+ "Delete"
36367
+ ]
36368
+ }
36369
+ ) }) })
36317
36370
  ]
36318
36371
  }
36319
36372
  )
@@ -37209,6 +37262,7 @@ function LyricsAnalyzer({ data: initialData, onFileLoad, apiClient, isReadOnly,
37209
37262
  const [originalData] = reactExports.useState(() => JSON.parse(JSON.stringify(initialData)));
37210
37263
  const [interactionMode, setInteractionMode] = reactExports.useState("edit");
37211
37264
  const [isShiftPressed, setIsShiftPressed] = reactExports.useState(false);
37265
+ const [isCtrlPressed, setIsCtrlPressed] = reactExports.useState(false);
37212
37266
  const [editModalSegment, setEditModalSegment] = reactExports.useState(null);
37213
37267
  const [isEditAllModalOpen, setIsEditAllModalOpen] = reactExports.useState(false);
37214
37268
  const [globalEditSegment, setGlobalEditSegment] = reactExports.useState(null);
@@ -37239,27 +37293,30 @@ function LyricsAnalyzer({ data: initialData, onFileLoad, apiClient, isReadOnly,
37239
37293
  }
37240
37294
  }, [data, isReadOnly, initialData]);
37241
37295
  reactExports.useEffect(() => {
37242
- const { handleKeyDown, handleKeyUp } = setupKeyboardHandlers({
37243
- setIsShiftPressed
37296
+ const { handleKeyDown, handleKeyUp, cleanup } = setupKeyboardHandlers({
37297
+ setIsShiftPressed,
37298
+ setIsCtrlPressed
37244
37299
  });
37245
37300
  window.addEventListener("keydown", handleKeyDown);
37246
37301
  window.addEventListener("keyup", handleKeyUp);
37247
37302
  if (isAnyModalOpen) {
37248
37303
  setIsShiftPressed(false);
37304
+ setIsCtrlPressed(false);
37249
37305
  }
37250
37306
  return () => {
37251
37307
  window.removeEventListener("keydown", handleKeyDown);
37252
37308
  window.removeEventListener("keyup", handleKeyUp);
37253
37309
  document.body.style.userSelect = "";
37310
+ cleanup();
37254
37311
  };
37255
- }, [setIsShiftPressed, isAnyModalOpen]);
37312
+ }, [setIsShiftPressed, setIsCtrlPressed, isAnyModalOpen]);
37256
37313
  reactExports.useEffect(() => {
37257
37314
  const modalOpen = Boolean(
37258
37315
  modalContent || editModalSegment || isReviewModalOpen || isAddLyricsModalOpen || isFindReplaceModalOpen || isEditAllModalOpen
37259
37316
  );
37260
37317
  setIsAnyModalOpen(modalOpen);
37261
37318
  }, [modalContent, editModalSegment, isReviewModalOpen, isAddLyricsModalOpen, isFindReplaceModalOpen, isEditAllModalOpen]);
37262
- const effectiveMode = isShiftPressed ? "highlight" : interactionMode;
37319
+ const effectiveMode = isCtrlPressed ? "delete_word" : isShiftPressed ? "highlight" : interactionMode;
37263
37320
  const handleFlash = reactExports.useCallback((type, info) => {
37264
37321
  setFlashingType(null);
37265
37322
  setHighlightInfo(null);
@@ -37278,6 +37335,12 @@ function LyricsAnalyzer({ data: initialData, onFileLoad, apiClient, isReadOnly,
37278
37335
  }, []);
37279
37336
  const handleWordClick = reactExports.useCallback((info) => {
37280
37337
  var _a, _b, _c, _d, _e, _f, _g;
37338
+ if (effectiveMode === "delete_word") {
37339
+ const newData = deleteWord(data, info.word_id);
37340
+ setData(newData);
37341
+ handleFlash("word");
37342
+ return;
37343
+ }
37281
37344
  if (effectiveMode === "highlight") {
37282
37345
  const correction = (_a = data.corrections) == null ? void 0 : _a.find(
37283
37346
  (c) => c.corrected_word_id === info.word_id || c.word_id === info.word_id
@@ -37385,7 +37448,7 @@ function LyricsAnalyzer({ data: initialData, onFileLoad, apiClient, isReadOnly,
37385
37448
  });
37386
37449
  }
37387
37450
  }
37388
- }, [data, effectiveMode, setModalContent]);
37451
+ }, [data, effectiveMode, setModalContent, handleFlash, deleteWord]);
37389
37452
  const handleUpdateSegment = reactExports.useCallback((updatedSegment) => {
37390
37453
  if (!editModalSegment) return;
37391
37454
  const newData = updateSegment(data, editModalSegment.index, updatedSegment);
@@ -38158,4 +38221,4 @@ ReactDOM$1.createRoot(document.getElementById("root")).render(
38158
38221
  /* @__PURE__ */ jsxRuntimeExports.jsx(App, {})
38159
38222
  ] })
38160
38223
  );
38161
- //# sourceMappingURL=index-BXOpmKq-.js.map
38224
+ //# sourceMappingURL=index-2vK-qVJS.js.map