lyrics-transcriber 0.48.0__py3-none-any.whl → 0.49.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.
- lyrics_transcriber/frontend/dist/assets/{index-BvRLUQmZ.js → index-BpvPgWoc.js} +159 -30
- lyrics_transcriber/frontend/dist/assets/index-BpvPgWoc.js.map +1 -0
- lyrics_transcriber/frontend/dist/index.html +1 -1
- lyrics_transcriber/frontend/src/components/Header.tsx +55 -5
- lyrics_transcriber/frontend/src/components/LyricsAnalyzer.tsx +226 -47
- {lyrics_transcriber-0.48.0.dist-info → lyrics_transcriber-0.49.0.dist-info}/METADATA +1 -1
- {lyrics_transcriber-0.48.0.dist-info → lyrics_transcriber-0.49.0.dist-info}/RECORD +10 -10
- {lyrics_transcriber-0.48.0.dist-info → lyrics_transcriber-0.49.0.dist-info}/WHEEL +1 -1
- lyrics_transcriber/frontend/dist/assets/index-BvRLUQmZ.js.map +0 -1
- {lyrics_transcriber-0.48.0.dist-info → lyrics_transcriber-0.49.0.dist-info}/LICENSE +0 -0
- {lyrics_transcriber-0.48.0.dist-info → lyrics_transcriber-0.49.0.dist-info}/entry_points.txt +0 -0
@@ -35962,6 +35962,12 @@ const PauseIcon = createSvgIcon(/* @__PURE__ */ jsxRuntimeExports.jsx("path", {
|
|
35962
35962
|
const PlayArrowIcon = createSvgIcon(/* @__PURE__ */ jsxRuntimeExports.jsx("path", {
|
35963
35963
|
d: "M8 5v14l11-7z"
|
35964
35964
|
}), "PlayArrow");
|
35965
|
+
const RedoIcon = createSvgIcon(/* @__PURE__ */ jsxRuntimeExports.jsx("path", {
|
35966
|
+
d: "M18.4 10.6C16.55 8.99 14.15 8 11.5 8c-4.65 0-8.58 3.03-9.96 7.22L3.9 16c1.05-3.19 4.05-5.5 7.6-5.5 1.95 0 3.73.72 5.12 1.88L13 16h9V7z"
|
35967
|
+
}), "Redo");
|
35968
|
+
const UndoIcon = createSvgIcon(/* @__PURE__ */ jsxRuntimeExports.jsx("path", {
|
35969
|
+
d: "M12.5 8c-2.65 0-5.05.99-6.9 2.6L2 7v9h9l-3.62-3.62c1.39-1.16 3.16-1.88 5.12-1.88 3.54 0 6.55 2.31 7.6 5.5l2.37-.78C21.08 11.03 17.15 8 12.5 8"
|
35970
|
+
}), "Undo");
|
35965
35971
|
const normalizeWordForComparison = (word) => ({
|
35966
35972
|
text: word.text,
|
35967
35973
|
start_time: word.start_time ?? 0,
|
@@ -36546,7 +36552,11 @@ function Header({
|
|
36546
36552
|
isUpdatingHandlers,
|
36547
36553
|
onHandlerClick,
|
36548
36554
|
onFindReplace,
|
36549
|
-
onEditAll
|
36555
|
+
onEditAll,
|
36556
|
+
onUndo,
|
36557
|
+
onRedo,
|
36558
|
+
canUndo,
|
36559
|
+
canRedo
|
36550
36560
|
}) {
|
36551
36561
|
var _a, _b, _c;
|
36552
36562
|
const theme2 = useTheme();
|
@@ -36711,6 +36721,40 @@ function Header({
|
|
36711
36721
|
onChange: onModeChange
|
36712
36722
|
}
|
36713
36723
|
),
|
36724
|
+
!isReadOnly && /* @__PURE__ */ jsxRuntimeExports.jsxs(Box, { sx: { display: "flex", height: "32px" }, children: [
|
36725
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(Tooltip, { title: "Undo (Cmd/Ctrl+Z)", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
36726
|
+
IconButton,
|
36727
|
+
{
|
36728
|
+
size: "small",
|
36729
|
+
onClick: onUndo,
|
36730
|
+
disabled: !canUndo,
|
36731
|
+
sx: {
|
36732
|
+
border: `1px solid ${theme2.palette.divider}`,
|
36733
|
+
borderRadius: "4px",
|
36734
|
+
mx: 0.25,
|
36735
|
+
height: "32px",
|
36736
|
+
width: "32px"
|
36737
|
+
},
|
36738
|
+
children: /* @__PURE__ */ jsxRuntimeExports.jsx(UndoIcon, { fontSize: "small" })
|
36739
|
+
}
|
36740
|
+
) }) }),
|
36741
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(Tooltip, { title: "Redo (Cmd/Ctrl+Shift+Z)", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
36742
|
+
IconButton,
|
36743
|
+
{
|
36744
|
+
size: "small",
|
36745
|
+
onClick: onRedo,
|
36746
|
+
disabled: !canRedo,
|
36747
|
+
sx: {
|
36748
|
+
border: `1px solid ${theme2.palette.divider}`,
|
36749
|
+
borderRadius: "4px",
|
36750
|
+
mx: 0.25,
|
36751
|
+
height: "32px",
|
36752
|
+
width: "32px"
|
36753
|
+
},
|
36754
|
+
children: /* @__PURE__ */ jsxRuntimeExports.jsx(RedoIcon, { fontSize: "small" })
|
36755
|
+
}
|
36756
|
+
) }) })
|
36757
|
+
] }),
|
36714
36758
|
!isReadOnly && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
36715
36759
|
Button,
|
36716
36760
|
{
|
@@ -37253,7 +37297,11 @@ const MemoizedHeader = reactExports.memo(function MemoizedHeader2({
|
|
37253
37297
|
isUpdatingHandlers,
|
37254
37298
|
onHandlerClick,
|
37255
37299
|
onFindReplace,
|
37256
|
-
onEditAll
|
37300
|
+
onEditAll,
|
37301
|
+
onUndo,
|
37302
|
+
onRedo,
|
37303
|
+
canUndo,
|
37304
|
+
canRedo
|
37257
37305
|
}) {
|
37258
37306
|
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
37259
37307
|
Header,
|
@@ -37271,7 +37319,11 @@ const MemoizedHeader = reactExports.memo(function MemoizedHeader2({
|
|
37271
37319
|
isUpdatingHandlers,
|
37272
37320
|
onHandlerClick,
|
37273
37321
|
onFindReplace,
|
37274
|
-
onEditAll
|
37322
|
+
onEditAll,
|
37323
|
+
onUndo,
|
37324
|
+
onRedo,
|
37325
|
+
canUndo,
|
37326
|
+
canRedo
|
37275
37327
|
}
|
37276
37328
|
);
|
37277
37329
|
});
|
@@ -37287,7 +37339,6 @@ function LyricsAnalyzer({ data: initialData, onFileLoad, apiClient, isReadOnly,
|
|
37287
37339
|
return availableSources.length > 0 ? availableSources[0] : "";
|
37288
37340
|
});
|
37289
37341
|
const [isReviewComplete, setIsReviewComplete] = reactExports.useState(false);
|
37290
|
-
const [data, setData] = reactExports.useState(initialData);
|
37291
37342
|
const [originalData] = reactExports.useState(() => JSON.parse(JSON.stringify(initialData)));
|
37292
37343
|
const [interactionMode, setInteractionMode] = reactExports.useState("edit");
|
37293
37344
|
const [isShiftPressed, setIsShiftPressed] = reactExports.useState(false);
|
@@ -37308,12 +37359,27 @@ function LyricsAnalyzer({ data: initialData, onFileLoad, apiClient, isReadOnly,
|
|
37308
37359
|
const [isFindReplaceModalOpen, setIsFindReplaceModalOpen] = reactExports.useState(false);
|
37309
37360
|
const theme2 = useTheme();
|
37310
37361
|
const isMobile = useMediaQuery(theme2.breakpoints.down("md"));
|
37362
|
+
const [history, setHistory] = reactExports.useState([initialData]);
|
37363
|
+
const [historyIndex, setHistoryIndex] = reactExports.useState(0);
|
37364
|
+
const data = history[historyIndex];
|
37365
|
+
const updateDataWithHistory = reactExports.useCallback((newData, actionDescription) => {
|
37366
|
+
const newHistory = history.slice(0, historyIndex + 1);
|
37367
|
+
const deepCopiedNewData = JSON.parse(JSON.stringify(newData));
|
37368
|
+
newHistory.push(deepCopiedNewData);
|
37369
|
+
setHistory(newHistory);
|
37370
|
+
setHistoryIndex(newHistory.length - 1);
|
37371
|
+
}, [history, historyIndex]);
|
37372
|
+
reactExports.useEffect(() => {
|
37373
|
+
setHistory([initialData]);
|
37374
|
+
setHistoryIndex(0);
|
37375
|
+
}, [initialData]);
|
37311
37376
|
reactExports.useEffect(() => {
|
37312
37377
|
}, [initialData]);
|
37313
37378
|
reactExports.useEffect(() => {
|
37314
37379
|
const savedData = loadSavedData(initialData);
|
37315
37380
|
if (savedData && window.confirm("Found saved progress for this song. Would you like to restore it?")) {
|
37316
|
-
|
37381
|
+
setHistory([savedData]);
|
37382
|
+
setHistoryIndex(0);
|
37317
37383
|
}
|
37318
37384
|
}, [initialData]);
|
37319
37385
|
reactExports.useEffect(() => {
|
@@ -37366,7 +37432,7 @@ function LyricsAnalyzer({ data: initialData, onFileLoad, apiClient, isReadOnly,
|
|
37366
37432
|
var _a, _b, _c, _d, _e, _f, _g;
|
37367
37433
|
if (effectiveMode === "delete_word") {
|
37368
37434
|
const newData = deleteWord(data, info.word_id);
|
37369
|
-
|
37435
|
+
updateDataWithHistory(newData, "delete word");
|
37370
37436
|
handleFlash("word");
|
37371
37437
|
return;
|
37372
37438
|
}
|
@@ -37477,17 +37543,24 @@ function LyricsAnalyzer({ data: initialData, onFileLoad, apiClient, isReadOnly,
|
|
37477
37543
|
});
|
37478
37544
|
}
|
37479
37545
|
}
|
37480
|
-
}, [data, effectiveMode, setModalContent, handleFlash, deleteWord]);
|
37546
|
+
}, [data, effectiveMode, setModalContent, handleFlash, deleteWord, updateDataWithHistory]);
|
37481
37547
|
const handleUpdateSegment = reactExports.useCallback((updatedSegment) => {
|
37482
37548
|
if (!editModalSegment) return;
|
37483
|
-
const
|
37484
|
-
|
37549
|
+
const currentData = history[historyIndex];
|
37550
|
+
const newSegments = currentData.corrected_segments.map(
|
37551
|
+
(segment, i) => i === editModalSegment.index ? updatedSegment : segment
|
37552
|
+
);
|
37553
|
+
const newDataImmutable = {
|
37554
|
+
...currentData,
|
37555
|
+
corrected_segments: newSegments
|
37556
|
+
};
|
37557
|
+
updateDataWithHistory(newDataImmutable, "update segment");
|
37485
37558
|
setEditModalSegment(null);
|
37486
|
-
}, [
|
37559
|
+
}, [history, historyIndex, editModalSegment, updateDataWithHistory]);
|
37487
37560
|
const handleDeleteSegment = reactExports.useCallback((segmentIndex) => {
|
37488
37561
|
const newData = deleteSegment(data, segmentIndex);
|
37489
|
-
|
37490
|
-
}, [data]);
|
37562
|
+
updateDataWithHistory(newData, "delete segment");
|
37563
|
+
}, [data, updateDataWithHistory]);
|
37491
37564
|
const handleFinishReview = reactExports.useCallback(() => {
|
37492
37565
|
setIsReviewModalOpen(true);
|
37493
37566
|
}, []);
|
@@ -37512,7 +37585,8 @@ function LyricsAnalyzer({ data: initialData, onFileLoad, apiClient, isReadOnly,
|
|
37512
37585
|
const handleResetCorrections = reactExports.useCallback(() => {
|
37513
37586
|
if (window.confirm("Are you sure you want to reset all corrections? This cannot be undone.")) {
|
37514
37587
|
clearSavedData(initialData);
|
37515
|
-
|
37588
|
+
setHistory([JSON.parse(JSON.stringify(initialData))]);
|
37589
|
+
setHistoryIndex(0);
|
37516
37590
|
setModalContent(null);
|
37517
37591
|
setFlashingType(null);
|
37518
37592
|
setHighlightInfo(null);
|
@@ -37521,20 +37595,20 @@ function LyricsAnalyzer({ data: initialData, onFileLoad, apiClient, isReadOnly,
|
|
37521
37595
|
}, [initialData]);
|
37522
37596
|
const handleAddSegment = reactExports.useCallback((beforeIndex) => {
|
37523
37597
|
const newData = addSegmentBefore(data, beforeIndex);
|
37524
|
-
|
37525
|
-
}, [data]);
|
37598
|
+
updateDataWithHistory(newData, "add segment");
|
37599
|
+
}, [data, updateDataWithHistory]);
|
37526
37600
|
const handleSplitSegment = reactExports.useCallback((segmentIndex, afterWordIndex) => {
|
37527
37601
|
const newData = splitSegment(data, segmentIndex, afterWordIndex);
|
37528
37602
|
if (newData) {
|
37529
|
-
|
37603
|
+
updateDataWithHistory(newData, "split segment");
|
37530
37604
|
setEditModalSegment(null);
|
37531
37605
|
}
|
37532
|
-
}, [data]);
|
37606
|
+
}, [data, updateDataWithHistory]);
|
37533
37607
|
const handleMergeSegment = reactExports.useCallback((segmentIndex, mergeWithNext) => {
|
37534
37608
|
const newData = mergeSegment(data, segmentIndex, mergeWithNext);
|
37535
|
-
|
37609
|
+
updateDataWithHistory(newData, "merge segment");
|
37536
37610
|
setEditModalSegment(null);
|
37537
|
-
}, [data]);
|
37611
|
+
}, [data, updateDataWithHistory]);
|
37538
37612
|
const handleHandlerToggle = reactExports.useCallback(async (handler, enabled) => {
|
37539
37613
|
if (!apiClient) return;
|
37540
37614
|
try {
|
@@ -37546,7 +37620,7 @@ function LyricsAnalyzer({ data: initialData, onFileLoad, apiClient, isReadOnly,
|
|
37546
37620
|
currentEnabled.delete(handler);
|
37547
37621
|
}
|
37548
37622
|
const newData = await apiClient.updateHandlers(Array.from(currentEnabled));
|
37549
|
-
|
37623
|
+
updateDataWithHistory(newData, `toggle handler ${handler}`);
|
37550
37624
|
setModalContent(null);
|
37551
37625
|
setFlashingType(null);
|
37552
37626
|
setHighlightInfo(null);
|
@@ -37557,7 +37631,7 @@ function LyricsAnalyzer({ data: initialData, onFileLoad, apiClient, isReadOnly,
|
|
37557
37631
|
} finally {
|
37558
37632
|
setIsUpdatingHandlers(false);
|
37559
37633
|
}
|
37560
|
-
}, [apiClient, data.metadata.enabled_handlers, handleFlash]);
|
37634
|
+
}, [apiClient, data.metadata.enabled_handlers, handleFlash, updateDataWithHistory]);
|
37561
37635
|
const handleHandlerClick = reactExports.useCallback((handler) => {
|
37562
37636
|
setFlashingHandler(handler);
|
37563
37637
|
setFlashingType("handler");
|
@@ -37574,14 +37648,14 @@ function LyricsAnalyzer({ data: initialData, onFileLoad, apiClient, isReadOnly,
|
|
37574
37648
|
try {
|
37575
37649
|
setIsAddingLyrics(true);
|
37576
37650
|
const newData = await apiClient.addLyrics(source, lyrics);
|
37577
|
-
|
37651
|
+
updateDataWithHistory(newData, "add lyrics");
|
37578
37652
|
} finally {
|
37579
37653
|
setIsAddingLyrics(false);
|
37580
37654
|
}
|
37581
|
-
}, [apiClient]);
|
37655
|
+
}, [apiClient, updateDataWithHistory]);
|
37582
37656
|
const handleFindReplace = (findText, replaceText, options) => {
|
37583
37657
|
const newData = findAndReplace(data, findText, replaceText, options);
|
37584
|
-
|
37658
|
+
updateDataWithHistory(newData, "find/replace");
|
37585
37659
|
};
|
37586
37660
|
const handleEditAll = reactExports.useCallback(() => {
|
37587
37661
|
console.log("EditAll - Starting process");
|
@@ -37714,19 +37788,70 @@ function LyricsAnalyzer({ data: initialData, onFileLoad, apiClient, isReadOnly,
|
|
37714
37788
|
totalWordCount: updatedSegments.reduce((count, segment) => count + segment.words.length, 0),
|
37715
37789
|
originalTotalWordCount: data.corrected_segments.reduce((count, segment) => count + segment.words.length, 0)
|
37716
37790
|
});
|
37717
|
-
|
37791
|
+
const newData = {
|
37718
37792
|
...data,
|
37719
37793
|
corrected_segments: updatedSegments
|
37720
|
-
}
|
37794
|
+
};
|
37795
|
+
updateDataWithHistory(newData, "edit all");
|
37721
37796
|
setIsEditAllModalOpen(false);
|
37722
37797
|
setGlobalEditSegment(null);
|
37723
|
-
}, [data]);
|
37798
|
+
}, [data, updateDataWithHistory]);
|
37799
|
+
const handleUndo = reactExports.useCallback(() => {
|
37800
|
+
if (historyIndex > 0) {
|
37801
|
+
const newIndex = historyIndex - 1;
|
37802
|
+
setHistoryIndex(newIndex);
|
37803
|
+
}
|
37804
|
+
}, [historyIndex, history]);
|
37805
|
+
const handleRedo = reactExports.useCallback(() => {
|
37806
|
+
if (historyIndex < history.length - 1) {
|
37807
|
+
const newIndex = historyIndex + 1;
|
37808
|
+
setHistoryIndex(newIndex);
|
37809
|
+
}
|
37810
|
+
}, [historyIndex, history]);
|
37811
|
+
const canUndo = historyIndex > 0;
|
37812
|
+
const canRedo = historyIndex < history.length - 1;
|
37724
37813
|
const metricClickHandlers = reactExports.useMemo(() => ({
|
37725
37814
|
anchor: () => handleFlash("anchor"),
|
37726
37815
|
corrected: () => handleFlash("corrected"),
|
37727
37816
|
uncorrected: () => handleFlash("uncorrected")
|
37728
37817
|
}), [handleFlash]);
|
37729
37818
|
const isAnyModalOpenMemo = reactExports.useMemo(() => isAnyModalOpen, [isAnyModalOpen]);
|
37819
|
+
reactExports.useEffect(() => {
|
37820
|
+
const { handleKeyDown: baseHandleKeyDown, handleKeyUp, cleanup } = setupKeyboardHandlers({
|
37821
|
+
setIsShiftPressed,
|
37822
|
+
setIsCtrlPressed
|
37823
|
+
});
|
37824
|
+
const handleKeyDown = (e) => {
|
37825
|
+
const targetElement = e.target;
|
37826
|
+
const isInputFocused = targetElement.tagName === "INPUT" || targetElement.tagName === "TEXTAREA";
|
37827
|
+
if (!isAnyModalOpen && !isInputFocused) {
|
37828
|
+
const isMac = navigator.platform.toUpperCase().indexOf("MAC") >= 0;
|
37829
|
+
const modifierKey = isMac ? e.metaKey : e.ctrlKey;
|
37830
|
+
if (modifierKey && e.key.toLowerCase() === "z") {
|
37831
|
+
e.preventDefault();
|
37832
|
+
if (e.shiftKey) {
|
37833
|
+
if (canRedo) handleRedo();
|
37834
|
+
} else {
|
37835
|
+
if (canUndo) handleUndo();
|
37836
|
+
}
|
37837
|
+
return;
|
37838
|
+
}
|
37839
|
+
}
|
37840
|
+
baseHandleKeyDown(e);
|
37841
|
+
};
|
37842
|
+
window.addEventListener("keydown", handleKeyDown);
|
37843
|
+
window.addEventListener("keyup", handleKeyUp);
|
37844
|
+
if (isAnyModalOpen) {
|
37845
|
+
setIsShiftPressed(false);
|
37846
|
+
setIsCtrlPressed(false);
|
37847
|
+
}
|
37848
|
+
return () => {
|
37849
|
+
window.removeEventListener("keydown", handleKeyDown);
|
37850
|
+
window.removeEventListener("keyup", handleKeyUp);
|
37851
|
+
document.body.style.userSelect = "";
|
37852
|
+
cleanup();
|
37853
|
+
};
|
37854
|
+
}, [setIsShiftPressed, setIsCtrlPressed, isAnyModalOpen, handleUndo, handleRedo, canUndo, canRedo]);
|
37730
37855
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs(Box, { sx: {
|
37731
37856
|
p: 1,
|
37732
37857
|
pb: 3,
|
@@ -37750,7 +37875,11 @@ function LyricsAnalyzer({ data: initialData, onFileLoad, apiClient, isReadOnly,
|
|
37750
37875
|
onHandlerClick: handleHandlerClick,
|
37751
37876
|
onAddLyrics: () => setIsAddLyricsModalOpen(true),
|
37752
37877
|
onFindReplace: () => setIsFindReplaceModalOpen(true),
|
37753
|
-
onEditAll: handleEditAll
|
37878
|
+
onEditAll: handleEditAll,
|
37879
|
+
onUndo: handleUndo,
|
37880
|
+
onRedo: handleRedo,
|
37881
|
+
canUndo,
|
37882
|
+
canRedo
|
37754
37883
|
}
|
37755
37884
|
),
|
37756
37885
|
/* @__PURE__ */ jsxRuntimeExports.jsxs(Grid, { container: true, direction: isMobile ? "column" : "row", children: [
|
@@ -37770,7 +37899,7 @@ function LyricsAnalyzer({ data: initialData, onFileLoad, apiClient, isReadOnly,
|
|
37770
37899
|
anchors: data.anchor_sequences,
|
37771
37900
|
disableHighlighting: isAnyModalOpenMemo,
|
37772
37901
|
onDataChange: (updatedData) => {
|
37773
|
-
|
37902
|
+
updateDataWithHistory(updatedData, "direct data change");
|
37774
37903
|
}
|
37775
37904
|
}
|
37776
37905
|
),
|
@@ -38253,4 +38382,4 @@ ReactDOM$1.createRoot(document.getElementById("root")).render(
|
|
38253
38382
|
/* @__PURE__ */ jsxRuntimeExports.jsx(App, {})
|
38254
38383
|
] })
|
38255
38384
|
);
|
38256
|
-
//# sourceMappingURL=index-
|
38385
|
+
//# sourceMappingURL=index-BpvPgWoc.js.map
|