lyrics-transcriber 0.47.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-2vK-qVJS.js → index-BpvPgWoc.js} +603 -442
- 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 +232 -47
- lyrics_transcriber/frontend/src/components/TranscriptionView.tsx +26 -1
- lyrics_transcriber/frontend/src/components/shared/types.ts +1 -0
- {lyrics_transcriber-0.47.0.dist-info → lyrics_transcriber-0.49.0.dist-info}/METADATA +1 -1
- {lyrics_transcriber-0.47.0.dist-info → lyrics_transcriber-0.49.0.dist-info}/RECORD +12 -12
- {lyrics_transcriber-0.47.0.dist-info → lyrics_transcriber-0.49.0.dist-info}/WHEEL +1 -1
- lyrics_transcriber/frontend/dist/assets/index-2vK-qVJS.js.map +0 -1
- {lyrics_transcriber-0.47.0.dist-info → lyrics_transcriber-0.49.0.dist-info}/LICENSE +0 -0
- {lyrics_transcriber-0.47.0.dist-info → lyrics_transcriber-0.49.0.dist-info}/entry_points.txt +0 -0
@@ -33650,6 +33650,243 @@ function SegmentDetailsModal({
|
|
33650
33650
|
const PlayCircleOutlineIcon = createSvgIcon(/* @__PURE__ */ jsxRuntimeExports.jsx("path", {
|
33651
33651
|
d: "m10 16.5 6-4.5-6-4.5zM12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2m0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8"
|
33652
33652
|
}), "PlayCircleOutline");
|
33653
|
+
const DeleteOutlineIcon = createSvgIcon(/* @__PURE__ */ jsxRuntimeExports.jsx("path", {
|
33654
|
+
d: "M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6zM8 9h8v10H8zm7.5-5-1-1h-5l-1 1H5v2h14V4z"
|
33655
|
+
}), "DeleteOutline");
|
33656
|
+
const urlAlphabet = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";
|
33657
|
+
let nanoid = (size = 21) => {
|
33658
|
+
let id = "";
|
33659
|
+
let bytes = crypto.getRandomValues(new Uint8Array(size |= 0));
|
33660
|
+
while (size--) {
|
33661
|
+
id += urlAlphabet[bytes[size] & 63];
|
33662
|
+
}
|
33663
|
+
return id;
|
33664
|
+
};
|
33665
|
+
const addSegmentBefore = (data, beforeIndex) => {
|
33666
|
+
const newData = { ...data };
|
33667
|
+
const beforeSegment = newData.corrected_segments[beforeIndex];
|
33668
|
+
const newStartTime = Math.max(0, (beforeSegment.start_time ?? 1) - 1);
|
33669
|
+
const newEndTime = newStartTime + 1;
|
33670
|
+
const newSegment = {
|
33671
|
+
id: nanoid(),
|
33672
|
+
text: "REPLACE",
|
33673
|
+
start_time: newStartTime,
|
33674
|
+
end_time: newEndTime,
|
33675
|
+
words: [{
|
33676
|
+
id: nanoid(),
|
33677
|
+
text: "REPLACE",
|
33678
|
+
start_time: newStartTime,
|
33679
|
+
end_time: newEndTime,
|
33680
|
+
confidence: 1
|
33681
|
+
}]
|
33682
|
+
};
|
33683
|
+
newData.corrected_segments.splice(beforeIndex, 0, newSegment);
|
33684
|
+
return newData;
|
33685
|
+
};
|
33686
|
+
const splitSegment = (data, segmentIndex, afterWordIndex) => {
|
33687
|
+
const newData = { ...data };
|
33688
|
+
const segment = newData.corrected_segments[segmentIndex];
|
33689
|
+
const firstHalfWords = segment.words.slice(0, afterWordIndex + 1);
|
33690
|
+
const secondHalfWords = segment.words.slice(afterWordIndex + 1);
|
33691
|
+
if (secondHalfWords.length === 0) return null;
|
33692
|
+
const lastFirstWord = firstHalfWords[firstHalfWords.length - 1];
|
33693
|
+
const firstSecondWord = secondHalfWords[0];
|
33694
|
+
const lastSecondWord = secondHalfWords[secondHalfWords.length - 1];
|
33695
|
+
const firstSegment = {
|
33696
|
+
...segment,
|
33697
|
+
words: firstHalfWords,
|
33698
|
+
text: firstHalfWords.map((w) => w.text).join(" "),
|
33699
|
+
end_time: lastFirstWord.end_time ?? null
|
33700
|
+
};
|
33701
|
+
const secondSegment = {
|
33702
|
+
id: nanoid(),
|
33703
|
+
words: secondHalfWords,
|
33704
|
+
text: secondHalfWords.map((w) => w.text).join(" "),
|
33705
|
+
start_time: firstSecondWord.start_time ?? null,
|
33706
|
+
end_time: lastSecondWord.end_time ?? null
|
33707
|
+
};
|
33708
|
+
newData.corrected_segments.splice(segmentIndex, 1, firstSegment, secondSegment);
|
33709
|
+
return newData;
|
33710
|
+
};
|
33711
|
+
const deleteSegment = (data, segmentIndex) => {
|
33712
|
+
const newData = { ...data };
|
33713
|
+
const deletedSegment = newData.corrected_segments[segmentIndex];
|
33714
|
+
newData.corrected_segments = newData.corrected_segments.filter((_, index) => index !== segmentIndex);
|
33715
|
+
newData.anchor_sequences = newData.anchor_sequences.map((anchor) => ({
|
33716
|
+
...anchor,
|
33717
|
+
transcribed_word_ids: anchor.transcribed_word_ids.filter(
|
33718
|
+
(wordId) => !deletedSegment.words.some((deletedWord) => deletedWord.id === wordId)
|
33719
|
+
)
|
33720
|
+
}));
|
33721
|
+
newData.gap_sequences = newData.gap_sequences.map((gap2) => ({
|
33722
|
+
...gap2,
|
33723
|
+
transcribed_word_ids: gap2.transcribed_word_ids.filter(
|
33724
|
+
(wordId) => !deletedSegment.words.some((deletedWord) => deletedWord.id === wordId)
|
33725
|
+
)
|
33726
|
+
}));
|
33727
|
+
return newData;
|
33728
|
+
};
|
33729
|
+
const updateSegment = (data, segmentIndex, updatedSegment) => {
|
33730
|
+
const newData = { ...data };
|
33731
|
+
updatedSegment.words = updatedSegment.words.map((word) => ({
|
33732
|
+
...word,
|
33733
|
+
id: word.id || nanoid()
|
33734
|
+
}));
|
33735
|
+
newData.corrected_segments[segmentIndex] = updatedSegment;
|
33736
|
+
return newData;
|
33737
|
+
};
|
33738
|
+
function mergeSegment(data, segmentIndex, mergeWithNext) {
|
33739
|
+
const segments = [...data.corrected_segments];
|
33740
|
+
const targetIndex = mergeWithNext ? segmentIndex + 1 : segmentIndex - 1;
|
33741
|
+
if (targetIndex < 0 || targetIndex >= segments.length) {
|
33742
|
+
return data;
|
33743
|
+
}
|
33744
|
+
const baseSegment = segments[segmentIndex];
|
33745
|
+
const targetSegment = segments[targetIndex];
|
33746
|
+
const mergedSegment = {
|
33747
|
+
id: nanoid(),
|
33748
|
+
words: mergeWithNext ? [...baseSegment.words, ...targetSegment.words] : [...targetSegment.words, ...baseSegment.words],
|
33749
|
+
text: mergeWithNext ? `${baseSegment.text} ${targetSegment.text}` : `${targetSegment.text} ${baseSegment.text}`,
|
33750
|
+
start_time: Math.min(
|
33751
|
+
baseSegment.start_time ?? Infinity,
|
33752
|
+
targetSegment.start_time ?? Infinity
|
33753
|
+
),
|
33754
|
+
end_time: Math.max(
|
33755
|
+
baseSegment.end_time ?? -Infinity,
|
33756
|
+
targetSegment.end_time ?? -Infinity
|
33757
|
+
)
|
33758
|
+
};
|
33759
|
+
const minIndex = Math.min(segmentIndex, targetIndex);
|
33760
|
+
segments.splice(minIndex, 2, mergedSegment);
|
33761
|
+
return {
|
33762
|
+
...data,
|
33763
|
+
corrected_segments: segments
|
33764
|
+
};
|
33765
|
+
}
|
33766
|
+
function findAndReplace(data, findText, replaceText, options = {
|
33767
|
+
caseSensitive: false,
|
33768
|
+
useRegex: false,
|
33769
|
+
fullTextMode: false
|
33770
|
+
}) {
|
33771
|
+
const newData = { ...data };
|
33772
|
+
if (options.fullTextMode) {
|
33773
|
+
newData.corrected_segments = data.corrected_segments.map((segment) => {
|
33774
|
+
let pattern;
|
33775
|
+
if (options.useRegex) {
|
33776
|
+
pattern = new RegExp(findText, options.caseSensitive ? "g" : "gi");
|
33777
|
+
} else {
|
33778
|
+
const escapedFindText = findText.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
33779
|
+
pattern = new RegExp(escapedFindText, options.caseSensitive ? "g" : "gi");
|
33780
|
+
}
|
33781
|
+
const segmentText = segment.text;
|
33782
|
+
if (!pattern.test(segmentText)) {
|
33783
|
+
return segment;
|
33784
|
+
}
|
33785
|
+
pattern.lastIndex = 0;
|
33786
|
+
const newSegmentText = segmentText.replace(pattern, replaceText);
|
33787
|
+
const newWordTexts = newSegmentText.trim().split(/\s+/).filter((text) => text.length > 0);
|
33788
|
+
const newWords = [];
|
33789
|
+
if (newWordTexts.length === segment.words.length) {
|
33790
|
+
for (let i = 0; i < newWordTexts.length; i++) {
|
33791
|
+
newWords.push({
|
33792
|
+
...segment.words[i],
|
33793
|
+
text: newWordTexts[i]
|
33794
|
+
});
|
33795
|
+
}
|
33796
|
+
} else if (newWordTexts.length < segment.words.length) {
|
33797
|
+
let oldWordIndex = 0;
|
33798
|
+
for (let i = 0; i < newWordTexts.length; i++) {
|
33799
|
+
while (oldWordIndex < segment.words.length && segment.words[oldWordIndex].text.trim() === "") {
|
33800
|
+
oldWordIndex++;
|
33801
|
+
}
|
33802
|
+
if (oldWordIndex < segment.words.length) {
|
33803
|
+
newWords.push({
|
33804
|
+
...segment.words[oldWordIndex],
|
33805
|
+
text: newWordTexts[i]
|
33806
|
+
});
|
33807
|
+
oldWordIndex++;
|
33808
|
+
} else {
|
33809
|
+
newWords.push({
|
33810
|
+
id: nanoid(),
|
33811
|
+
text: newWordTexts[i],
|
33812
|
+
start_time: null,
|
33813
|
+
end_time: null
|
33814
|
+
});
|
33815
|
+
}
|
33816
|
+
}
|
33817
|
+
} else {
|
33818
|
+
for (let i = 0; i < newWordTexts.length; i++) {
|
33819
|
+
if (i < segment.words.length) {
|
33820
|
+
newWords.push({
|
33821
|
+
...segment.words[i],
|
33822
|
+
text: newWordTexts[i]
|
33823
|
+
});
|
33824
|
+
} else {
|
33825
|
+
newWords.push({
|
33826
|
+
id: nanoid(),
|
33827
|
+
text: newWordTexts[i],
|
33828
|
+
start_time: null,
|
33829
|
+
end_time: null
|
33830
|
+
});
|
33831
|
+
}
|
33832
|
+
}
|
33833
|
+
}
|
33834
|
+
return {
|
33835
|
+
...segment,
|
33836
|
+
words: newWords,
|
33837
|
+
text: newSegmentText
|
33838
|
+
};
|
33839
|
+
});
|
33840
|
+
} else {
|
33841
|
+
newData.corrected_segments = data.corrected_segments.map((segment) => {
|
33842
|
+
let newWords = segment.words.map((word) => {
|
33843
|
+
let pattern;
|
33844
|
+
if (options.useRegex) {
|
33845
|
+
pattern = new RegExp(findText, options.caseSensitive ? "g" : "gi");
|
33846
|
+
} else {
|
33847
|
+
const escapedFindText = findText.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
33848
|
+
pattern = new RegExp(escapedFindText, options.caseSensitive ? "g" : "gi");
|
33849
|
+
}
|
33850
|
+
return {
|
33851
|
+
...word,
|
33852
|
+
text: word.text.replace(pattern, replaceText)
|
33853
|
+
};
|
33854
|
+
});
|
33855
|
+
newWords = newWords.filter((word) => word.text.trim() !== "");
|
33856
|
+
return {
|
33857
|
+
...segment,
|
33858
|
+
words: newWords,
|
33859
|
+
text: newWords.map((w) => w.text).join(" ")
|
33860
|
+
};
|
33861
|
+
});
|
33862
|
+
}
|
33863
|
+
newData.corrected_segments = newData.corrected_segments.filter((segment) => segment.words.length > 0);
|
33864
|
+
return newData;
|
33865
|
+
}
|
33866
|
+
function deleteWord(data, wordId) {
|
33867
|
+
const segmentIndex = data.corrected_segments.findIndex(
|
33868
|
+
(segment2) => segment2.words.some((word) => word.id === wordId)
|
33869
|
+
);
|
33870
|
+
if (segmentIndex === -1) {
|
33871
|
+
return data;
|
33872
|
+
}
|
33873
|
+
const segment = data.corrected_segments[segmentIndex];
|
33874
|
+
const wordIndex = segment.words.findIndex((word) => word.id === wordId);
|
33875
|
+
if (wordIndex === -1) {
|
33876
|
+
return data;
|
33877
|
+
}
|
33878
|
+
const updatedWords = segment.words.filter((_, index) => index !== wordIndex);
|
33879
|
+
if (updatedWords.length > 0) {
|
33880
|
+
const updatedSegment = {
|
33881
|
+
...segment,
|
33882
|
+
words: updatedWords,
|
33883
|
+
text: updatedWords.map((w) => w.text).join(" ")
|
33884
|
+
};
|
33885
|
+
return updateSegment(data, segmentIndex, updatedSegment);
|
33886
|
+
} else {
|
33887
|
+
return deleteSegment(data, segmentIndex);
|
33888
|
+
}
|
33889
|
+
}
|
33653
33890
|
const SegmentIndex = styled(Typography)(({ theme: theme2 }) => ({
|
33654
33891
|
color: theme2.palette.text.secondary,
|
33655
33892
|
width: "1.8em",
|
@@ -33688,9 +33925,16 @@ function TranscriptionView({
|
|
33688
33925
|
mode,
|
33689
33926
|
onPlaySegment,
|
33690
33927
|
currentTime = 0,
|
33691
|
-
anchors = []
|
33928
|
+
anchors = [],
|
33929
|
+
onDataChange
|
33692
33930
|
}) {
|
33693
33931
|
const [selectedSegmentIndex, setSelectedSegmentIndex] = reactExports.useState(null);
|
33932
|
+
const handleDeleteSegment = (segmentIndex) => {
|
33933
|
+
if (onDataChange) {
|
33934
|
+
const updatedData = deleteSegment(data, segmentIndex);
|
33935
|
+
onDataChange(updatedData);
|
33936
|
+
}
|
33937
|
+
};
|
33694
33938
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs(Paper, { sx: { p: 0.8 }, children: [
|
33695
33939
|
/* @__PURE__ */ jsxRuntimeExports.jsx(Box, { sx: { display: "flex", justifyContent: "space-between", alignItems: "center", mb: 0.5 }, children: /* @__PURE__ */ jsxRuntimeExports.jsx(Typography, { variant: "h6", sx: { fontSize: "0.9rem", mb: 0 }, children: "Corrected Transcription" }) }),
|
33696
33940
|
/* @__PURE__ */ jsxRuntimeExports.jsx(Box, { sx: { display: "flex", flexDirection: "column", gap: 0.2 }, children: data.corrected_segments.map((segment, segmentIndex) => {
|
@@ -33745,6 +33989,22 @@ function TranscriptionView({
|
|
33745
33989
|
children: segmentIndex
|
33746
33990
|
}
|
33747
33991
|
),
|
33992
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
33993
|
+
IconButton,
|
33994
|
+
{
|
33995
|
+
size: "small",
|
33996
|
+
onClick: () => handleDeleteSegment(segmentIndex),
|
33997
|
+
sx: {
|
33998
|
+
padding: "1px",
|
33999
|
+
height: "18px",
|
34000
|
+
width: "18px",
|
34001
|
+
minHeight: "18px",
|
34002
|
+
minWidth: "18px"
|
34003
|
+
},
|
34004
|
+
title: "Delete segment",
|
34005
|
+
children: /* @__PURE__ */ jsxRuntimeExports.jsx(DeleteOutlineIcon, { sx: { fontSize: "0.9rem", color: "error.main" } })
|
34006
|
+
}
|
34007
|
+
),
|
33748
34008
|
segment.start_time !== null && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
33749
34009
|
IconButton,
|
33750
34010
|
{
|
@@ -33757,6 +34017,7 @@ function TranscriptionView({
|
|
33757
34017
|
minHeight: "18px",
|
33758
34018
|
minWidth: "18px"
|
33759
34019
|
},
|
34020
|
+
title: "Play segment",
|
33760
34021
|
children: /* @__PURE__ */ jsxRuntimeExports.jsx(PlayCircleOutlineIcon, { sx: { fontSize: "0.9rem" } })
|
33761
34022
|
}
|
33762
34023
|
)
|
@@ -33794,15 +34055,6 @@ function TranscriptionView({
|
|
33794
34055
|
const StopIcon = createSvgIcon(/* @__PURE__ */ jsxRuntimeExports.jsx("path", {
|
33795
34056
|
d: "M6 6h12v12H6z"
|
33796
34057
|
}), "Stop");
|
33797
|
-
const urlAlphabet = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";
|
33798
|
-
let nanoid = (size = 21) => {
|
33799
|
-
let id = "";
|
33800
|
-
let bytes = crypto.getRandomValues(new Uint8Array(size |= 0));
|
33801
|
-
while (size--) {
|
33802
|
-
id += urlAlphabet[bytes[size] & 63];
|
33803
|
-
}
|
33804
|
-
return id;
|
33805
|
-
};
|
33806
34058
|
const TAP_THRESHOLD_MS = 200;
|
33807
34059
|
const DEFAULT_WORD_DURATION = 1;
|
33808
34060
|
const OVERLAP_BUFFER = 0.01;
|
@@ -35710,6 +35962,12 @@ const PauseIcon = createSvgIcon(/* @__PURE__ */ jsxRuntimeExports.jsx("path", {
|
|
35710
35962
|
const PlayArrowIcon = createSvgIcon(/* @__PURE__ */ jsxRuntimeExports.jsx("path", {
|
35711
35963
|
d: "M8 5v14l11-7z"
|
35712
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");
|
35713
35971
|
const normalizeWordForComparison = (word) => ({
|
35714
35972
|
text: word.text,
|
35715
35973
|
start_time: word.start_time ?? 0,
|
@@ -35787,410 +36045,185 @@ function ReviewChangesModal({
|
|
35787
36045
|
path: `Word ${wordIndex}`,
|
35788
36046
|
oldValue: `"${word.text}" (${((_a3 = word.start_time) == null ? void 0 : _a3.toFixed(4)) ?? "N/A"} - ${((_b3 = word.end_time) == null ? void 0 : _b3.toFixed(4)) ?? "N/A"})`
|
35789
36047
|
});
|
35790
|
-
return;
|
35791
|
-
}
|
35792
|
-
if (word.text !== updatedWord.text || Math.abs((word.start_time ?? 0) - (updatedWord.start_time ?? 0)) > 1e-4 || Math.abs((word.end_time ?? 0) - (updatedWord.end_time ?? 0)) > 1e-4) {
|
35793
|
-
wordChanges.push({
|
35794
|
-
type: "modified",
|
35795
|
-
path: `Word ${wordIndex}`,
|
35796
|
-
oldValue: `"${word.text}" (${((_c2 = word.start_time) == null ? void 0 : _c2.toFixed(4)) ?? "N/A"} - ${((_d2 = word.end_time) == null ? void 0 : _d2.toFixed(4)) ?? "N/A"})`,
|
35797
|
-
newValue: `"${updatedWord.text}" (${((_e2 = updatedWord.start_time) == null ? void 0 : _e2.toFixed(4)) ?? "N/A"} - ${((_f2 = updatedWord.end_time) == null ? void 0 : _f2.toFixed(4)) ?? "N/A"})`
|
35798
|
-
});
|
35799
|
-
}
|
35800
|
-
});
|
35801
|
-
if (normalizedUpdated.words.length > normalizedOriginal.words.length) {
|
35802
|
-
for (let i = normalizedOriginal.words.length; i < normalizedUpdated.words.length; i++) {
|
35803
|
-
const word = normalizedUpdated.words[i];
|
35804
|
-
wordChanges.push({
|
35805
|
-
type: "added",
|
35806
|
-
path: `Word ${i}`,
|
35807
|
-
newValue: `"${word.text}" (${((_a2 = word.start_time) == null ? void 0 : _a2.toFixed(4)) ?? "N/A"} - ${((_b2 = word.end_time) == null ? void 0 : _b2.toFixed(4)) ?? "N/A"})`
|
35808
|
-
});
|
35809
|
-
}
|
35810
|
-
}
|
35811
|
-
if (normalizedOriginal.text !== normalizedUpdated.text || Math.abs((normalizedOriginal.start_time ?? 0) - (normalizedUpdated.start_time ?? 0)) > 1e-4 || Math.abs((normalizedOriginal.end_time ?? 0) - (normalizedUpdated.end_time ?? 0)) > 1e-4 || wordChanges.length > 0) {
|
35812
|
-
diffs.push({
|
35813
|
-
type: "modified",
|
35814
|
-
path: `Segment ${index}`,
|
35815
|
-
segmentIndex: index,
|
35816
|
-
oldValue: `"${normalizedOriginal.text}" (${((_c = normalizedOriginal.start_time) == null ? void 0 : _c.toFixed(4)) ?? "N/A"} - ${((_d = normalizedOriginal.end_time) == null ? void 0 : _d.toFixed(4)) ?? "N/A"})`,
|
35817
|
-
newValue: `"${normalizedUpdated.text}" (${((_e = normalizedUpdated.start_time) == null ? void 0 : _e.toFixed(4)) ?? "N/A"} - ${((_f = normalizedUpdated.end_time) == null ? void 0 : _f.toFixed(4)) ?? "N/A"})`,
|
35818
|
-
wordChanges: wordChanges.length > 0 ? wordChanges : void 0
|
35819
|
-
});
|
35820
|
-
}
|
35821
|
-
});
|
35822
|
-
if (updatedData.corrected_segments.length > originalData.corrected_segments.length) {
|
35823
|
-
for (let i = originalData.corrected_segments.length; i < updatedData.corrected_segments.length; i++) {
|
35824
|
-
const segment = updatedData.corrected_segments[i];
|
35825
|
-
diffs.push({
|
35826
|
-
type: "added",
|
35827
|
-
path: `Segment ${i}`,
|
35828
|
-
segmentIndex: i,
|
35829
|
-
newValue: `"${segment.text}" (${((_a = segment.start_time) == null ? void 0 : _a.toFixed(4)) ?? "N/A"} - ${((_b = segment.end_time) == null ? void 0 : _b.toFixed(4)) ?? "N/A"})`
|
35830
|
-
});
|
35831
|
-
}
|
35832
|
-
}
|
35833
|
-
return diffs;
|
35834
|
-
}, [originalData, updatedData]);
|
35835
|
-
const renderCompactDiff = (diff) => {
|
35836
|
-
var _a, _b, _c;
|
35837
|
-
if (diff.type !== "modified") {
|
35838
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
35839
|
-
Typography,
|
35840
|
-
{
|
35841
|
-
color: diff.type === "added" ? "success.main" : "error.main",
|
35842
|
-
sx: { mb: 0.5 },
|
35843
|
-
children: [
|
35844
|
-
diff.segmentIndex,
|
35845
|
-
": ",
|
35846
|
-
diff.type === "added" ? "+ " : "- ",
|
35847
|
-
diff.type === "added" ? diff.newValue : diff.oldValue
|
35848
|
-
]
|
35849
|
-
},
|
35850
|
-
diff.path
|
35851
|
-
);
|
35852
|
-
}
|
35853
|
-
const oldText = ((_a = diff.oldValue) == null ? void 0 : _a.split('"')[1]) || "";
|
35854
|
-
const newText = ((_b = diff.newValue) == null ? void 0 : _b.split('"')[1]) || "";
|
35855
|
-
const oldWords = oldText.split(" ");
|
35856
|
-
const newWords = newText.split(" ");
|
35857
|
-
const timingMatch = (_c = diff.newValue) == null ? void 0 : _c.match(/\(([\d.]+) - ([\d.]+)\)/);
|
35858
|
-
const timing = timingMatch ? `(${parseFloat(timingMatch[1]).toFixed(2)} - ${parseFloat(timingMatch[2]).toFixed(2)})` : "";
|
35859
|
-
const unifiedDiff = [];
|
35860
|
-
let i = 0, j = 0;
|
35861
|
-
while (i < oldWords.length || j < newWords.length) {
|
35862
|
-
if (i < oldWords.length && j < newWords.length && oldWords[i] === newWords[j]) {
|
35863
|
-
unifiedDiff.push({ type: "unchanged", text: oldWords[i] });
|
35864
|
-
i++;
|
35865
|
-
j++;
|
35866
|
-
} else if (i < oldWords.length && (!newWords[j] || oldWords[i] !== newWords[j])) {
|
35867
|
-
unifiedDiff.push({ type: "deleted", text: oldWords[i] });
|
35868
|
-
i++;
|
35869
|
-
} else if (j < newWords.length) {
|
35870
|
-
unifiedDiff.push({ type: "added", text: newWords[j] });
|
35871
|
-
j++;
|
35872
|
-
}
|
35873
|
-
}
|
35874
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsxs(Box, { sx: { mb: 0.5, display: "flex", alignItems: "center" }, children: [
|
35875
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs(Typography, { variant: "body2", color: "text.secondary", sx: { mr: 1, minWidth: "30px" }, children: [
|
35876
|
-
diff.segmentIndex,
|
35877
|
-
":"
|
35878
|
-
] }),
|
35879
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs(Box, { sx: { display: "flex", flexWrap: "wrap", flexGrow: 1, alignItems: "center" }, children: [
|
35880
|
-
unifiedDiff.map((word, idx) => /* @__PURE__ */ jsxRuntimeExports.jsx(
|
35881
|
-
Typography,
|
35882
|
-
{
|
35883
|
-
component: "span",
|
35884
|
-
color: word.type === "unchanged" ? "text.primary" : word.type === "deleted" ? "error.main" : "success.main",
|
35885
|
-
sx: {
|
35886
|
-
textDecoration: word.type === "deleted" ? "line-through" : "none",
|
35887
|
-
mr: 0.5
|
35888
|
-
},
|
35889
|
-
children: word.text
|
35890
|
-
},
|
35891
|
-
idx
|
35892
|
-
)),
|
35893
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(Typography, { variant: "body2", color: "text.secondary", sx: { ml: 1 }, children: timing })
|
35894
|
-
] })
|
35895
|
-
] }, diff.path);
|
35896
|
-
};
|
35897
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
35898
|
-
Dialog,
|
35899
|
-
{
|
35900
|
-
open,
|
35901
|
-
onClose,
|
35902
|
-
maxWidth: "md",
|
35903
|
-
fullWidth: true,
|
35904
|
-
children: [
|
35905
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(DialogTitle, { children: "Preview Video (With Vocals)" }),
|
35906
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
35907
|
-
DialogContent,
|
35908
|
-
{
|
35909
|
-
dividers: true,
|
35910
|
-
sx: {
|
35911
|
-
p: 0,
|
35912
|
-
// Remove default padding
|
35913
|
-
"&:first-of-type": { pt: 0 }
|
35914
|
-
// Remove default top padding
|
35915
|
-
},
|
35916
|
-
children: [
|
35917
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
35918
|
-
PreviewVideoSection,
|
35919
|
-
{
|
35920
|
-
apiClient,
|
35921
|
-
isModalOpen: open,
|
35922
|
-
updatedData,
|
35923
|
-
videoRef
|
35924
|
-
}
|
35925
|
-
),
|
35926
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(Box, { sx: { p: 2, mt: 0 }, children: differences.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsxs(Box, { children: [
|
35927
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(Typography, { color: "text.secondary", children: "No manual corrections detected. If everything looks good in the preview, click submit and the server will generate the final karaoke video." }),
|
35928
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs(Typography, { variant: "body2", color: "text.secondary", children: [
|
35929
|
-
"Total segments: ",
|
35930
|
-
updatedData.corrected_segments.length
|
35931
|
-
] })
|
35932
|
-
] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(Box, { children: [
|
35933
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs(Typography, { variant: "body2", color: "text.secondary", sx: { mb: 1 }, children: [
|
35934
|
-
differences.length,
|
35935
|
-
" segment",
|
35936
|
-
differences.length !== 1 ? "s" : "",
|
35937
|
-
" modified:"
|
35938
|
-
] }),
|
35939
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(Paper, { sx: { p: 2 }, children: differences.map(renderCompactDiff) })
|
35940
|
-
] }) })
|
35941
|
-
]
|
35942
|
-
}
|
35943
|
-
),
|
35944
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs(DialogActions, { children: [
|
35945
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
35946
|
-
Button,
|
35947
|
-
{
|
35948
|
-
onClick: onClose,
|
35949
|
-
color: "warning",
|
35950
|
-
startIcon: /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowBack, {}),
|
35951
|
-
sx: { mr: "auto" },
|
35952
|
-
children: "Cancel"
|
35953
|
-
}
|
35954
|
-
),
|
35955
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
35956
|
-
Button,
|
35957
|
-
{
|
35958
|
-
onClick: onSubmit,
|
35959
|
-
variant: "contained",
|
35960
|
-
color: "success",
|
35961
|
-
endIcon: /* @__PURE__ */ jsxRuntimeExports.jsx(CloudUpload, {}),
|
35962
|
-
children: "Complete Review"
|
35963
|
-
}
|
35964
|
-
)
|
35965
|
-
] })
|
35966
|
-
]
|
35967
|
-
}
|
35968
|
-
);
|
35969
|
-
}
|
35970
|
-
const addSegmentBefore = (data, beforeIndex) => {
|
35971
|
-
const newData = { ...data };
|
35972
|
-
const beforeSegment = newData.corrected_segments[beforeIndex];
|
35973
|
-
const newStartTime = Math.max(0, (beforeSegment.start_time ?? 1) - 1);
|
35974
|
-
const newEndTime = newStartTime + 1;
|
35975
|
-
const newSegment = {
|
35976
|
-
id: nanoid(),
|
35977
|
-
text: "REPLACE",
|
35978
|
-
start_time: newStartTime,
|
35979
|
-
end_time: newEndTime,
|
35980
|
-
words: [{
|
35981
|
-
id: nanoid(),
|
35982
|
-
text: "REPLACE",
|
35983
|
-
start_time: newStartTime,
|
35984
|
-
end_time: newEndTime,
|
35985
|
-
confidence: 1
|
35986
|
-
}]
|
35987
|
-
};
|
35988
|
-
newData.corrected_segments.splice(beforeIndex, 0, newSegment);
|
35989
|
-
return newData;
|
35990
|
-
};
|
35991
|
-
const splitSegment = (data, segmentIndex, afterWordIndex) => {
|
35992
|
-
const newData = { ...data };
|
35993
|
-
const segment = newData.corrected_segments[segmentIndex];
|
35994
|
-
const firstHalfWords = segment.words.slice(0, afterWordIndex + 1);
|
35995
|
-
const secondHalfWords = segment.words.slice(afterWordIndex + 1);
|
35996
|
-
if (secondHalfWords.length === 0) return null;
|
35997
|
-
const lastFirstWord = firstHalfWords[firstHalfWords.length - 1];
|
35998
|
-
const firstSecondWord = secondHalfWords[0];
|
35999
|
-
const lastSecondWord = secondHalfWords[secondHalfWords.length - 1];
|
36000
|
-
const firstSegment = {
|
36001
|
-
...segment,
|
36002
|
-
words: firstHalfWords,
|
36003
|
-
text: firstHalfWords.map((w) => w.text).join(" "),
|
36004
|
-
end_time: lastFirstWord.end_time ?? null
|
36005
|
-
};
|
36006
|
-
const secondSegment = {
|
36007
|
-
id: nanoid(),
|
36008
|
-
words: secondHalfWords,
|
36009
|
-
text: secondHalfWords.map((w) => w.text).join(" "),
|
36010
|
-
start_time: firstSecondWord.start_time ?? null,
|
36011
|
-
end_time: lastSecondWord.end_time ?? null
|
36012
|
-
};
|
36013
|
-
newData.corrected_segments.splice(segmentIndex, 1, firstSegment, secondSegment);
|
36014
|
-
return newData;
|
36015
|
-
};
|
36016
|
-
const deleteSegment = (data, segmentIndex) => {
|
36017
|
-
const newData = { ...data };
|
36018
|
-
const deletedSegment = newData.corrected_segments[segmentIndex];
|
36019
|
-
newData.corrected_segments = newData.corrected_segments.filter((_, index) => index !== segmentIndex);
|
36020
|
-
newData.anchor_sequences = newData.anchor_sequences.map((anchor) => ({
|
36021
|
-
...anchor,
|
36022
|
-
transcribed_word_ids: anchor.transcribed_word_ids.filter(
|
36023
|
-
(wordId) => !deletedSegment.words.some((deletedWord) => deletedWord.id === wordId)
|
36024
|
-
)
|
36025
|
-
}));
|
36026
|
-
newData.gap_sequences = newData.gap_sequences.map((gap2) => ({
|
36027
|
-
...gap2,
|
36028
|
-
transcribed_word_ids: gap2.transcribed_word_ids.filter(
|
36029
|
-
(wordId) => !deletedSegment.words.some((deletedWord) => deletedWord.id === wordId)
|
36030
|
-
)
|
36031
|
-
}));
|
36032
|
-
return newData;
|
36033
|
-
};
|
36034
|
-
const updateSegment = (data, segmentIndex, updatedSegment) => {
|
36035
|
-
const newData = { ...data };
|
36036
|
-
updatedSegment.words = updatedSegment.words.map((word) => ({
|
36037
|
-
...word,
|
36038
|
-
id: word.id || nanoid()
|
36039
|
-
}));
|
36040
|
-
newData.corrected_segments[segmentIndex] = updatedSegment;
|
36041
|
-
return newData;
|
36042
|
-
};
|
36043
|
-
function mergeSegment(data, segmentIndex, mergeWithNext) {
|
36044
|
-
const segments = [...data.corrected_segments];
|
36045
|
-
const targetIndex = mergeWithNext ? segmentIndex + 1 : segmentIndex - 1;
|
36046
|
-
if (targetIndex < 0 || targetIndex >= segments.length) {
|
36047
|
-
return data;
|
36048
|
-
}
|
36049
|
-
const baseSegment = segments[segmentIndex];
|
36050
|
-
const targetSegment = segments[targetIndex];
|
36051
|
-
const mergedSegment = {
|
36052
|
-
id: nanoid(),
|
36053
|
-
words: mergeWithNext ? [...baseSegment.words, ...targetSegment.words] : [...targetSegment.words, ...baseSegment.words],
|
36054
|
-
text: mergeWithNext ? `${baseSegment.text} ${targetSegment.text}` : `${targetSegment.text} ${baseSegment.text}`,
|
36055
|
-
start_time: Math.min(
|
36056
|
-
baseSegment.start_time ?? Infinity,
|
36057
|
-
targetSegment.start_time ?? Infinity
|
36058
|
-
),
|
36059
|
-
end_time: Math.max(
|
36060
|
-
baseSegment.end_time ?? -Infinity,
|
36061
|
-
targetSegment.end_time ?? -Infinity
|
36062
|
-
)
|
36063
|
-
};
|
36064
|
-
const minIndex = Math.min(segmentIndex, targetIndex);
|
36065
|
-
segments.splice(minIndex, 2, mergedSegment);
|
36066
|
-
return {
|
36067
|
-
...data,
|
36068
|
-
corrected_segments: segments
|
36069
|
-
};
|
36070
|
-
}
|
36071
|
-
function findAndReplace(data, findText, replaceText, options = {
|
36072
|
-
caseSensitive: false,
|
36073
|
-
useRegex: false,
|
36074
|
-
fullTextMode: false
|
36075
|
-
}) {
|
36076
|
-
const newData = { ...data };
|
36077
|
-
if (options.fullTextMode) {
|
36078
|
-
newData.corrected_segments = data.corrected_segments.map((segment) => {
|
36079
|
-
let pattern;
|
36080
|
-
if (options.useRegex) {
|
36081
|
-
pattern = new RegExp(findText, options.caseSensitive ? "g" : "gi");
|
36082
|
-
} else {
|
36083
|
-
const escapedFindText = findText.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
36084
|
-
pattern = new RegExp(escapedFindText, options.caseSensitive ? "g" : "gi");
|
36085
|
-
}
|
36086
|
-
const segmentText = segment.text;
|
36087
|
-
if (!pattern.test(segmentText)) {
|
36088
|
-
return segment;
|
36089
|
-
}
|
36090
|
-
pattern.lastIndex = 0;
|
36091
|
-
const newSegmentText = segmentText.replace(pattern, replaceText);
|
36092
|
-
const newWordTexts = newSegmentText.trim().split(/\s+/).filter((text) => text.length > 0);
|
36093
|
-
const newWords = [];
|
36094
|
-
if (newWordTexts.length === segment.words.length) {
|
36095
|
-
for (let i = 0; i < newWordTexts.length; i++) {
|
36096
|
-
newWords.push({
|
36097
|
-
...segment.words[i],
|
36098
|
-
text: newWordTexts[i]
|
36099
|
-
});
|
36100
|
-
}
|
36101
|
-
} else if (newWordTexts.length < segment.words.length) {
|
36102
|
-
let oldWordIndex = 0;
|
36103
|
-
for (let i = 0; i < newWordTexts.length; i++) {
|
36104
|
-
while (oldWordIndex < segment.words.length && segment.words[oldWordIndex].text.trim() === "") {
|
36105
|
-
oldWordIndex++;
|
36106
|
-
}
|
36107
|
-
if (oldWordIndex < segment.words.length) {
|
36108
|
-
newWords.push({
|
36109
|
-
...segment.words[oldWordIndex],
|
36110
|
-
text: newWordTexts[i]
|
36111
|
-
});
|
36112
|
-
oldWordIndex++;
|
36113
|
-
} else {
|
36114
|
-
newWords.push({
|
36115
|
-
id: nanoid(),
|
36116
|
-
text: newWordTexts[i],
|
36117
|
-
start_time: null,
|
36118
|
-
end_time: null
|
36119
|
-
});
|
36120
|
-
}
|
36121
|
-
}
|
36122
|
-
} else {
|
36123
|
-
for (let i = 0; i < newWordTexts.length; i++) {
|
36124
|
-
if (i < segment.words.length) {
|
36125
|
-
newWords.push({
|
36126
|
-
...segment.words[i],
|
36127
|
-
text: newWordTexts[i]
|
36128
|
-
});
|
36129
|
-
} else {
|
36130
|
-
newWords.push({
|
36131
|
-
id: nanoid(),
|
36132
|
-
text: newWordTexts[i],
|
36133
|
-
start_time: null,
|
36134
|
-
end_time: null
|
36135
|
-
});
|
36136
|
-
}
|
36137
|
-
}
|
36138
|
-
}
|
36139
|
-
return {
|
36140
|
-
...segment,
|
36141
|
-
words: newWords,
|
36142
|
-
text: newSegmentText
|
36143
|
-
};
|
36144
|
-
});
|
36145
|
-
} else {
|
36146
|
-
newData.corrected_segments = data.corrected_segments.map((segment) => {
|
36147
|
-
let newWords = segment.words.map((word) => {
|
36148
|
-
let pattern;
|
36149
|
-
if (options.useRegex) {
|
36150
|
-
pattern = new RegExp(findText, options.caseSensitive ? "g" : "gi");
|
36151
|
-
} else {
|
36152
|
-
const escapedFindText = findText.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
36153
|
-
pattern = new RegExp(escapedFindText, options.caseSensitive ? "g" : "gi");
|
36048
|
+
return;
|
36049
|
+
}
|
36050
|
+
if (word.text !== updatedWord.text || Math.abs((word.start_time ?? 0) - (updatedWord.start_time ?? 0)) > 1e-4 || Math.abs((word.end_time ?? 0) - (updatedWord.end_time ?? 0)) > 1e-4) {
|
36051
|
+
wordChanges.push({
|
36052
|
+
type: "modified",
|
36053
|
+
path: `Word ${wordIndex}`,
|
36054
|
+
oldValue: `"${word.text}" (${((_c2 = word.start_time) == null ? void 0 : _c2.toFixed(4)) ?? "N/A"} - ${((_d2 = word.end_time) == null ? void 0 : _d2.toFixed(4)) ?? "N/A"})`,
|
36055
|
+
newValue: `"${updatedWord.text}" (${((_e2 = updatedWord.start_time) == null ? void 0 : _e2.toFixed(4)) ?? "N/A"} - ${((_f2 = updatedWord.end_time) == null ? void 0 : _f2.toFixed(4)) ?? "N/A"})`
|
36056
|
+
});
|
36154
36057
|
}
|
36155
|
-
return {
|
36156
|
-
...word,
|
36157
|
-
text: word.text.replace(pattern, replaceText)
|
36158
|
-
};
|
36159
36058
|
});
|
36160
|
-
|
36161
|
-
|
36162
|
-
|
36163
|
-
|
36164
|
-
|
36165
|
-
|
36059
|
+
if (normalizedUpdated.words.length > normalizedOriginal.words.length) {
|
36060
|
+
for (let i = normalizedOriginal.words.length; i < normalizedUpdated.words.length; i++) {
|
36061
|
+
const word = normalizedUpdated.words[i];
|
36062
|
+
wordChanges.push({
|
36063
|
+
type: "added",
|
36064
|
+
path: `Word ${i}`,
|
36065
|
+
newValue: `"${word.text}" (${((_a2 = word.start_time) == null ? void 0 : _a2.toFixed(4)) ?? "N/A"} - ${((_b2 = word.end_time) == null ? void 0 : _b2.toFixed(4)) ?? "N/A"})`
|
36066
|
+
});
|
36067
|
+
}
|
36068
|
+
}
|
36069
|
+
if (normalizedOriginal.text !== normalizedUpdated.text || Math.abs((normalizedOriginal.start_time ?? 0) - (normalizedUpdated.start_time ?? 0)) > 1e-4 || Math.abs((normalizedOriginal.end_time ?? 0) - (normalizedUpdated.end_time ?? 0)) > 1e-4 || wordChanges.length > 0) {
|
36070
|
+
diffs.push({
|
36071
|
+
type: "modified",
|
36072
|
+
path: `Segment ${index}`,
|
36073
|
+
segmentIndex: index,
|
36074
|
+
oldValue: `"${normalizedOriginal.text}" (${((_c = normalizedOriginal.start_time) == null ? void 0 : _c.toFixed(4)) ?? "N/A"} - ${((_d = normalizedOriginal.end_time) == null ? void 0 : _d.toFixed(4)) ?? "N/A"})`,
|
36075
|
+
newValue: `"${normalizedUpdated.text}" (${((_e = normalizedUpdated.start_time) == null ? void 0 : _e.toFixed(4)) ?? "N/A"} - ${((_f = normalizedUpdated.end_time) == null ? void 0 : _f.toFixed(4)) ?? "N/A"})`,
|
36076
|
+
wordChanges: wordChanges.length > 0 ? wordChanges : void 0
|
36077
|
+
});
|
36078
|
+
}
|
36166
36079
|
});
|
36167
|
-
|
36168
|
-
|
36169
|
-
|
36170
|
-
|
36171
|
-
|
36172
|
-
|
36173
|
-
|
36080
|
+
if (updatedData.corrected_segments.length > originalData.corrected_segments.length) {
|
36081
|
+
for (let i = originalData.corrected_segments.length; i < updatedData.corrected_segments.length; i++) {
|
36082
|
+
const segment = updatedData.corrected_segments[i];
|
36083
|
+
diffs.push({
|
36084
|
+
type: "added",
|
36085
|
+
path: `Segment ${i}`,
|
36086
|
+
segmentIndex: i,
|
36087
|
+
newValue: `"${segment.text}" (${((_a = segment.start_time) == null ? void 0 : _a.toFixed(4)) ?? "N/A"} - ${((_b = segment.end_time) == null ? void 0 : _b.toFixed(4)) ?? "N/A"})`
|
36088
|
+
});
|
36089
|
+
}
|
36090
|
+
}
|
36091
|
+
return diffs;
|
36092
|
+
}, [originalData, updatedData]);
|
36093
|
+
const renderCompactDiff = (diff) => {
|
36094
|
+
var _a, _b, _c;
|
36095
|
+
if (diff.type !== "modified") {
|
36096
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
36097
|
+
Typography,
|
36098
|
+
{
|
36099
|
+
color: diff.type === "added" ? "success.main" : "error.main",
|
36100
|
+
sx: { mb: 0.5 },
|
36101
|
+
children: [
|
36102
|
+
diff.segmentIndex,
|
36103
|
+
": ",
|
36104
|
+
diff.type === "added" ? "+ " : "- ",
|
36105
|
+
diff.type === "added" ? diff.newValue : diff.oldValue
|
36106
|
+
]
|
36107
|
+
},
|
36108
|
+
diff.path
|
36109
|
+
);
|
36110
|
+
}
|
36111
|
+
const oldText = ((_a = diff.oldValue) == null ? void 0 : _a.split('"')[1]) || "";
|
36112
|
+
const newText = ((_b = diff.newValue) == null ? void 0 : _b.split('"')[1]) || "";
|
36113
|
+
const oldWords = oldText.split(" ");
|
36114
|
+
const newWords = newText.split(" ");
|
36115
|
+
const timingMatch = (_c = diff.newValue) == null ? void 0 : _c.match(/\(([\d.]+) - ([\d.]+)\)/);
|
36116
|
+
const timing = timingMatch ? `(${parseFloat(timingMatch[1]).toFixed(2)} - ${parseFloat(timingMatch[2]).toFixed(2)})` : "";
|
36117
|
+
const unifiedDiff = [];
|
36118
|
+
let i = 0, j = 0;
|
36119
|
+
while (i < oldWords.length || j < newWords.length) {
|
36120
|
+
if (i < oldWords.length && j < newWords.length && oldWords[i] === newWords[j]) {
|
36121
|
+
unifiedDiff.push({ type: "unchanged", text: oldWords[i] });
|
36122
|
+
i++;
|
36123
|
+
j++;
|
36124
|
+
} else if (i < oldWords.length && (!newWords[j] || oldWords[i] !== newWords[j])) {
|
36125
|
+
unifiedDiff.push({ type: "deleted", text: oldWords[i] });
|
36126
|
+
i++;
|
36127
|
+
} else if (j < newWords.length) {
|
36128
|
+
unifiedDiff.push({ type: "added", text: newWords[j] });
|
36129
|
+
j++;
|
36130
|
+
}
|
36131
|
+
}
|
36132
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs(Box, { sx: { mb: 0.5, display: "flex", alignItems: "center" }, children: [
|
36133
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs(Typography, { variant: "body2", color: "text.secondary", sx: { mr: 1, minWidth: "30px" }, children: [
|
36134
|
+
diff.segmentIndex,
|
36135
|
+
":"
|
36136
|
+
] }),
|
36137
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs(Box, { sx: { display: "flex", flexWrap: "wrap", flexGrow: 1, alignItems: "center" }, children: [
|
36138
|
+
unifiedDiff.map((word, idx) => /* @__PURE__ */ jsxRuntimeExports.jsx(
|
36139
|
+
Typography,
|
36140
|
+
{
|
36141
|
+
component: "span",
|
36142
|
+
color: word.type === "unchanged" ? "text.primary" : word.type === "deleted" ? "error.main" : "success.main",
|
36143
|
+
sx: {
|
36144
|
+
textDecoration: word.type === "deleted" ? "line-through" : "none",
|
36145
|
+
mr: 0.5
|
36146
|
+
},
|
36147
|
+
children: word.text
|
36148
|
+
},
|
36149
|
+
idx
|
36150
|
+
)),
|
36151
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(Typography, { variant: "body2", color: "text.secondary", sx: { ml: 1 }, children: timing })
|
36152
|
+
] })
|
36153
|
+
] }, diff.path);
|
36154
|
+
};
|
36155
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
36156
|
+
Dialog,
|
36157
|
+
{
|
36158
|
+
open,
|
36159
|
+
onClose,
|
36160
|
+
maxWidth: "md",
|
36161
|
+
fullWidth: true,
|
36162
|
+
children: [
|
36163
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(DialogTitle, { children: "Preview Video (With Vocals)" }),
|
36164
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
36165
|
+
DialogContent,
|
36166
|
+
{
|
36167
|
+
dividers: true,
|
36168
|
+
sx: {
|
36169
|
+
p: 0,
|
36170
|
+
// Remove default padding
|
36171
|
+
"&:first-of-type": { pt: 0 }
|
36172
|
+
// Remove default top padding
|
36173
|
+
},
|
36174
|
+
children: [
|
36175
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
36176
|
+
PreviewVideoSection,
|
36177
|
+
{
|
36178
|
+
apiClient,
|
36179
|
+
isModalOpen: open,
|
36180
|
+
updatedData,
|
36181
|
+
videoRef
|
36182
|
+
}
|
36183
|
+
),
|
36184
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(Box, { sx: { p: 2, mt: 0 }, children: differences.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsxs(Box, { children: [
|
36185
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(Typography, { color: "text.secondary", children: "No manual corrections detected. If everything looks good in the preview, click submit and the server will generate the final karaoke video." }),
|
36186
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs(Typography, { variant: "body2", color: "text.secondary", children: [
|
36187
|
+
"Total segments: ",
|
36188
|
+
updatedData.corrected_segments.length
|
36189
|
+
] })
|
36190
|
+
] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(Box, { children: [
|
36191
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs(Typography, { variant: "body2", color: "text.secondary", sx: { mb: 1 }, children: [
|
36192
|
+
differences.length,
|
36193
|
+
" segment",
|
36194
|
+
differences.length !== 1 ? "s" : "",
|
36195
|
+
" modified:"
|
36196
|
+
] }),
|
36197
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(Paper, { sx: { p: 2 }, children: differences.map(renderCompactDiff) })
|
36198
|
+
] }) })
|
36199
|
+
]
|
36200
|
+
}
|
36201
|
+
),
|
36202
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs(DialogActions, { children: [
|
36203
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
36204
|
+
Button,
|
36205
|
+
{
|
36206
|
+
onClick: onClose,
|
36207
|
+
color: "warning",
|
36208
|
+
startIcon: /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowBack, {}),
|
36209
|
+
sx: { mr: "auto" },
|
36210
|
+
children: "Cancel"
|
36211
|
+
}
|
36212
|
+
),
|
36213
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
36214
|
+
Button,
|
36215
|
+
{
|
36216
|
+
onClick: onSubmit,
|
36217
|
+
variant: "contained",
|
36218
|
+
color: "success",
|
36219
|
+
endIcon: /* @__PURE__ */ jsxRuntimeExports.jsx(CloudUpload, {}),
|
36220
|
+
children: "Complete Review"
|
36221
|
+
}
|
36222
|
+
)
|
36223
|
+
] })
|
36224
|
+
]
|
36225
|
+
}
|
36174
36226
|
);
|
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
36227
|
}
|
36195
36228
|
const generateStorageKey = (data) => {
|
36196
36229
|
var _a;
|
@@ -36519,7 +36552,11 @@ function Header({
|
|
36519
36552
|
isUpdatingHandlers,
|
36520
36553
|
onHandlerClick,
|
36521
36554
|
onFindReplace,
|
36522
|
-
onEditAll
|
36555
|
+
onEditAll,
|
36556
|
+
onUndo,
|
36557
|
+
onRedo,
|
36558
|
+
canUndo,
|
36559
|
+
canRedo
|
36523
36560
|
}) {
|
36524
36561
|
var _a, _b, _c;
|
36525
36562
|
const theme2 = useTheme();
|
@@ -36684,6 +36721,40 @@ function Header({
|
|
36684
36721
|
onChange: onModeChange
|
36685
36722
|
}
|
36686
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
|
+
] }),
|
36687
36758
|
!isReadOnly && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
36688
36759
|
Button,
|
36689
36760
|
{
|
@@ -37158,7 +37229,8 @@ const MemoizedTranscriptionView = reactExports.memo(function MemoizedTranscripti
|
|
37158
37229
|
onPlaySegment,
|
37159
37230
|
currentTime,
|
37160
37231
|
anchors,
|
37161
|
-
disableHighlighting
|
37232
|
+
disableHighlighting,
|
37233
|
+
onDataChange
|
37162
37234
|
}) {
|
37163
37235
|
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
37164
37236
|
TranscriptionView,
|
@@ -37172,7 +37244,8 @@ const MemoizedTranscriptionView = reactExports.memo(function MemoizedTranscripti
|
|
37172
37244
|
highlightInfo,
|
37173
37245
|
onPlaySegment,
|
37174
37246
|
currentTime: disableHighlighting ? void 0 : currentTime,
|
37175
|
-
anchors
|
37247
|
+
anchors,
|
37248
|
+
onDataChange
|
37176
37249
|
}
|
37177
37250
|
);
|
37178
37251
|
});
|
@@ -37224,7 +37297,11 @@ const MemoizedHeader = reactExports.memo(function MemoizedHeader2({
|
|
37224
37297
|
isUpdatingHandlers,
|
37225
37298
|
onHandlerClick,
|
37226
37299
|
onFindReplace,
|
37227
|
-
onEditAll
|
37300
|
+
onEditAll,
|
37301
|
+
onUndo,
|
37302
|
+
onRedo,
|
37303
|
+
canUndo,
|
37304
|
+
canRedo
|
37228
37305
|
}) {
|
37229
37306
|
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
37230
37307
|
Header,
|
@@ -37242,7 +37319,11 @@ const MemoizedHeader = reactExports.memo(function MemoizedHeader2({
|
|
37242
37319
|
isUpdatingHandlers,
|
37243
37320
|
onHandlerClick,
|
37244
37321
|
onFindReplace,
|
37245
|
-
onEditAll
|
37322
|
+
onEditAll,
|
37323
|
+
onUndo,
|
37324
|
+
onRedo,
|
37325
|
+
canUndo,
|
37326
|
+
canRedo
|
37246
37327
|
}
|
37247
37328
|
);
|
37248
37329
|
});
|
@@ -37258,7 +37339,6 @@ function LyricsAnalyzer({ data: initialData, onFileLoad, apiClient, isReadOnly,
|
|
37258
37339
|
return availableSources.length > 0 ? availableSources[0] : "";
|
37259
37340
|
});
|
37260
37341
|
const [isReviewComplete, setIsReviewComplete] = reactExports.useState(false);
|
37261
|
-
const [data, setData] = reactExports.useState(initialData);
|
37262
37342
|
const [originalData] = reactExports.useState(() => JSON.parse(JSON.stringify(initialData)));
|
37263
37343
|
const [interactionMode, setInteractionMode] = reactExports.useState("edit");
|
37264
37344
|
const [isShiftPressed, setIsShiftPressed] = reactExports.useState(false);
|
@@ -37279,12 +37359,27 @@ function LyricsAnalyzer({ data: initialData, onFileLoad, apiClient, isReadOnly,
|
|
37279
37359
|
const [isFindReplaceModalOpen, setIsFindReplaceModalOpen] = reactExports.useState(false);
|
37280
37360
|
const theme2 = useTheme();
|
37281
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]);
|
37282
37376
|
reactExports.useEffect(() => {
|
37283
37377
|
}, [initialData]);
|
37284
37378
|
reactExports.useEffect(() => {
|
37285
37379
|
const savedData = loadSavedData(initialData);
|
37286
37380
|
if (savedData && window.confirm("Found saved progress for this song. Would you like to restore it?")) {
|
37287
|
-
|
37381
|
+
setHistory([savedData]);
|
37382
|
+
setHistoryIndex(0);
|
37288
37383
|
}
|
37289
37384
|
}, [initialData]);
|
37290
37385
|
reactExports.useEffect(() => {
|
@@ -37337,7 +37432,7 @@ function LyricsAnalyzer({ data: initialData, onFileLoad, apiClient, isReadOnly,
|
|
37337
37432
|
var _a, _b, _c, _d, _e, _f, _g;
|
37338
37433
|
if (effectiveMode === "delete_word") {
|
37339
37434
|
const newData = deleteWord(data, info.word_id);
|
37340
|
-
|
37435
|
+
updateDataWithHistory(newData, "delete word");
|
37341
37436
|
handleFlash("word");
|
37342
37437
|
return;
|
37343
37438
|
}
|
@@ -37448,17 +37543,24 @@ function LyricsAnalyzer({ data: initialData, onFileLoad, apiClient, isReadOnly,
|
|
37448
37543
|
});
|
37449
37544
|
}
|
37450
37545
|
}
|
37451
|
-
}, [data, effectiveMode, setModalContent, handleFlash, deleteWord]);
|
37546
|
+
}, [data, effectiveMode, setModalContent, handleFlash, deleteWord, updateDataWithHistory]);
|
37452
37547
|
const handleUpdateSegment = reactExports.useCallback((updatedSegment) => {
|
37453
37548
|
if (!editModalSegment) return;
|
37454
|
-
const
|
37455
|
-
|
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");
|
37456
37558
|
setEditModalSegment(null);
|
37457
|
-
}, [
|
37559
|
+
}, [history, historyIndex, editModalSegment, updateDataWithHistory]);
|
37458
37560
|
const handleDeleteSegment = reactExports.useCallback((segmentIndex) => {
|
37459
37561
|
const newData = deleteSegment(data, segmentIndex);
|
37460
|
-
|
37461
|
-
}, [data]);
|
37562
|
+
updateDataWithHistory(newData, "delete segment");
|
37563
|
+
}, [data, updateDataWithHistory]);
|
37462
37564
|
const handleFinishReview = reactExports.useCallback(() => {
|
37463
37565
|
setIsReviewModalOpen(true);
|
37464
37566
|
}, []);
|
@@ -37483,7 +37585,8 @@ function LyricsAnalyzer({ data: initialData, onFileLoad, apiClient, isReadOnly,
|
|
37483
37585
|
const handleResetCorrections = reactExports.useCallback(() => {
|
37484
37586
|
if (window.confirm("Are you sure you want to reset all corrections? This cannot be undone.")) {
|
37485
37587
|
clearSavedData(initialData);
|
37486
|
-
|
37588
|
+
setHistory([JSON.parse(JSON.stringify(initialData))]);
|
37589
|
+
setHistoryIndex(0);
|
37487
37590
|
setModalContent(null);
|
37488
37591
|
setFlashingType(null);
|
37489
37592
|
setHighlightInfo(null);
|
@@ -37492,20 +37595,20 @@ function LyricsAnalyzer({ data: initialData, onFileLoad, apiClient, isReadOnly,
|
|
37492
37595
|
}, [initialData]);
|
37493
37596
|
const handleAddSegment = reactExports.useCallback((beforeIndex) => {
|
37494
37597
|
const newData = addSegmentBefore(data, beforeIndex);
|
37495
|
-
|
37496
|
-
}, [data]);
|
37598
|
+
updateDataWithHistory(newData, "add segment");
|
37599
|
+
}, [data, updateDataWithHistory]);
|
37497
37600
|
const handleSplitSegment = reactExports.useCallback((segmentIndex, afterWordIndex) => {
|
37498
37601
|
const newData = splitSegment(data, segmentIndex, afterWordIndex);
|
37499
37602
|
if (newData) {
|
37500
|
-
|
37603
|
+
updateDataWithHistory(newData, "split segment");
|
37501
37604
|
setEditModalSegment(null);
|
37502
37605
|
}
|
37503
|
-
}, [data]);
|
37606
|
+
}, [data, updateDataWithHistory]);
|
37504
37607
|
const handleMergeSegment = reactExports.useCallback((segmentIndex, mergeWithNext) => {
|
37505
37608
|
const newData = mergeSegment(data, segmentIndex, mergeWithNext);
|
37506
|
-
|
37609
|
+
updateDataWithHistory(newData, "merge segment");
|
37507
37610
|
setEditModalSegment(null);
|
37508
|
-
}, [data]);
|
37611
|
+
}, [data, updateDataWithHistory]);
|
37509
37612
|
const handleHandlerToggle = reactExports.useCallback(async (handler, enabled) => {
|
37510
37613
|
if (!apiClient) return;
|
37511
37614
|
try {
|
@@ -37517,7 +37620,7 @@ function LyricsAnalyzer({ data: initialData, onFileLoad, apiClient, isReadOnly,
|
|
37517
37620
|
currentEnabled.delete(handler);
|
37518
37621
|
}
|
37519
37622
|
const newData = await apiClient.updateHandlers(Array.from(currentEnabled));
|
37520
|
-
|
37623
|
+
updateDataWithHistory(newData, `toggle handler ${handler}`);
|
37521
37624
|
setModalContent(null);
|
37522
37625
|
setFlashingType(null);
|
37523
37626
|
setHighlightInfo(null);
|
@@ -37528,7 +37631,7 @@ function LyricsAnalyzer({ data: initialData, onFileLoad, apiClient, isReadOnly,
|
|
37528
37631
|
} finally {
|
37529
37632
|
setIsUpdatingHandlers(false);
|
37530
37633
|
}
|
37531
|
-
}, [apiClient, data.metadata.enabled_handlers, handleFlash]);
|
37634
|
+
}, [apiClient, data.metadata.enabled_handlers, handleFlash, updateDataWithHistory]);
|
37532
37635
|
const handleHandlerClick = reactExports.useCallback((handler) => {
|
37533
37636
|
setFlashingHandler(handler);
|
37534
37637
|
setFlashingType("handler");
|
@@ -37545,14 +37648,14 @@ function LyricsAnalyzer({ data: initialData, onFileLoad, apiClient, isReadOnly,
|
|
37545
37648
|
try {
|
37546
37649
|
setIsAddingLyrics(true);
|
37547
37650
|
const newData = await apiClient.addLyrics(source, lyrics);
|
37548
|
-
|
37651
|
+
updateDataWithHistory(newData, "add lyrics");
|
37549
37652
|
} finally {
|
37550
37653
|
setIsAddingLyrics(false);
|
37551
37654
|
}
|
37552
|
-
}, [apiClient]);
|
37655
|
+
}, [apiClient, updateDataWithHistory]);
|
37553
37656
|
const handleFindReplace = (findText, replaceText, options) => {
|
37554
37657
|
const newData = findAndReplace(data, findText, replaceText, options);
|
37555
|
-
|
37658
|
+
updateDataWithHistory(newData, "find/replace");
|
37556
37659
|
};
|
37557
37660
|
const handleEditAll = reactExports.useCallback(() => {
|
37558
37661
|
console.log("EditAll - Starting process");
|
@@ -37685,19 +37788,70 @@ function LyricsAnalyzer({ data: initialData, onFileLoad, apiClient, isReadOnly,
|
|
37685
37788
|
totalWordCount: updatedSegments.reduce((count, segment) => count + segment.words.length, 0),
|
37686
37789
|
originalTotalWordCount: data.corrected_segments.reduce((count, segment) => count + segment.words.length, 0)
|
37687
37790
|
});
|
37688
|
-
|
37791
|
+
const newData = {
|
37689
37792
|
...data,
|
37690
37793
|
corrected_segments: updatedSegments
|
37691
|
-
}
|
37794
|
+
};
|
37795
|
+
updateDataWithHistory(newData, "edit all");
|
37692
37796
|
setIsEditAllModalOpen(false);
|
37693
37797
|
setGlobalEditSegment(null);
|
37694
|
-
}, [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;
|
37695
37813
|
const metricClickHandlers = reactExports.useMemo(() => ({
|
37696
37814
|
anchor: () => handleFlash("anchor"),
|
37697
37815
|
corrected: () => handleFlash("corrected"),
|
37698
37816
|
uncorrected: () => handleFlash("uncorrected")
|
37699
37817
|
}), [handleFlash]);
|
37700
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]);
|
37701
37855
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs(Box, { sx: {
|
37702
37856
|
p: 1,
|
37703
37857
|
pb: 3,
|
@@ -37721,7 +37875,11 @@ function LyricsAnalyzer({ data: initialData, onFileLoad, apiClient, isReadOnly,
|
|
37721
37875
|
onHandlerClick: handleHandlerClick,
|
37722
37876
|
onAddLyrics: () => setIsAddLyricsModalOpen(true),
|
37723
37877
|
onFindReplace: () => setIsFindReplaceModalOpen(true),
|
37724
|
-
onEditAll: handleEditAll
|
37878
|
+
onEditAll: handleEditAll,
|
37879
|
+
onUndo: handleUndo,
|
37880
|
+
onRedo: handleRedo,
|
37881
|
+
canUndo,
|
37882
|
+
canRedo
|
37725
37883
|
}
|
37726
37884
|
),
|
37727
37885
|
/* @__PURE__ */ jsxRuntimeExports.jsxs(Grid, { container: true, direction: isMobile ? "column" : "row", children: [
|
@@ -37739,7 +37897,10 @@ function LyricsAnalyzer({ data: initialData, onFileLoad, apiClient, isReadOnly,
|
|
37739
37897
|
onPlaySegment: handlePlaySegment,
|
37740
37898
|
currentTime: currentAudioTime,
|
37741
37899
|
anchors: data.anchor_sequences,
|
37742
|
-
disableHighlighting: isAnyModalOpenMemo
|
37900
|
+
disableHighlighting: isAnyModalOpenMemo,
|
37901
|
+
onDataChange: (updatedData) => {
|
37902
|
+
updateDataWithHistory(updatedData, "direct data change");
|
37903
|
+
}
|
37743
37904
|
}
|
37744
37905
|
),
|
37745
37906
|
!isReadOnly && apiClient && /* @__PURE__ */ jsxRuntimeExports.jsxs(Box, { sx: {
|
@@ -38221,4 +38382,4 @@ ReactDOM$1.createRoot(document.getElementById("root")).render(
|
|
38221
38382
|
/* @__PURE__ */ jsxRuntimeExports.jsx(App, {})
|
38222
38383
|
] })
|
38223
38384
|
);
|
38224
|
-
//# sourceMappingURL=index-
|
38385
|
+
//# sourceMappingURL=index-BpvPgWoc.js.map
|