mirror of
https://github.com/meowarex/TidaLuna-Plugins.git
synced 2026-06-18 03:43:10 +10:00
Merge pull request #74 from meowarex/dev
Apply Context Aware & Bubbled lyrics to Line
This commit is contained in:
@@ -224,34 +224,30 @@ export const Settings = () => {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{lyricsStyle >= 1 && (
|
<AnySwitch
|
||||||
<>
|
title="Context Aware Lyrics"
|
||||||
<AnySwitch
|
desc="Enables background vocal display & duet singer positioning"
|
||||||
title="Context Aware Lyrics"
|
checked={contextAwareLyrics}
|
||||||
desc="Enables background vocal display & duet singer positioning"
|
onChange={(_: unknown, checked: boolean) => {
|
||||||
checked={contextAwareLyrics}
|
settings.contextAwareLyrics = checked;
|
||||||
onChange={(_: unknown, checked: boolean) => {
|
setContextAwareLyrics(checked);
|
||||||
settings.contextAwareLyrics = checked;
|
if (window.updateLyricsStyle) {
|
||||||
setContextAwareLyrics(checked);
|
window.updateLyricsStyle();
|
||||||
if (window.updateLyricsStyle) {
|
}
|
||||||
window.updateLyricsStyle();
|
}}
|
||||||
}
|
/>
|
||||||
}}
|
<AnySwitch
|
||||||
/>
|
title="Bubbled Lyrics"
|
||||||
<AnySwitch
|
desc="Smooth bounce animation on line/word transitions"
|
||||||
title="Bubbled Lyrics"
|
checked={bubbledLyrics}
|
||||||
desc="Smooth bounce animation on line/word transitions"
|
onChange={(_: unknown, checked: boolean) => {
|
||||||
checked={bubbledLyrics}
|
settings.bubbledLyrics = checked;
|
||||||
onChange={(_: unknown, checked: boolean) => {
|
setBubbledLyrics(checked);
|
||||||
settings.bubbledLyrics = checked;
|
if (window.updateLyricsStyle) {
|
||||||
setBubbledLyrics(checked);
|
window.updateLyricsStyle();
|
||||||
if (window.updateLyricsStyle) {
|
}
|
||||||
window.updateLyricsStyle();
|
}}
|
||||||
}
|
/>
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
<AnySwitch
|
<AnySwitch
|
||||||
title="Sticky Lyrics"
|
title="Sticky Lyrics"
|
||||||
desc="auto-switches to Play Queue when lyrics aren't available (mirrored in lyrics dropdown)"
|
desc="auto-switches to Play Queue when lyrics aren't available (mirrored in lyrics dropdown)"
|
||||||
|
|||||||
@@ -1204,7 +1204,7 @@ function setupStickyLyricsObserver(): void {
|
|||||||
observe<HTMLElement>(unloads, '[data-test="lyrics-lines"]', () => {
|
observe<HTMLElement>(unloads, '[data-test="lyrics-lines"]', () => {
|
||||||
if (lyricsData) {
|
if (lyricsData) {
|
||||||
reapplyWordLyrics();
|
reapplyWordLyrics();
|
||||||
} else if (settings.lyricsStyle !== 0) {
|
} else {
|
||||||
onTrackChange();
|
onTrackChange();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -1823,41 +1823,64 @@ const buildWordSpans = (): {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const group of wordGroups) {
|
if (settings.lyricsStyle === 0) {
|
||||||
const groupIsBg = splitBg && syllabus[group[0]].isBackground;
|
// Line mode: one span per container (main / bg) — no word splitting
|
||||||
const targetContainer = groupIsBg ? bgContainer! : mainContainer;
|
const mainSyls = syllabus.filter(s => !splitBg || !s.isBackground);
|
||||||
const targetWords = groupIsBg ? lineBgWords : lineWords;
|
const bgSyls = splitBg ? syllabus.filter(s => s.isBackground) : [];
|
||||||
|
|
||||||
if (isSylMode) {
|
if (mainSyls.length > 0) {
|
||||||
const wordStartMs = syllabus[group[0]].time;
|
const text = mainSyls.map(s => s.text).join("").trim();
|
||||||
const groupSpans: HTMLSpanElement[] = [];
|
const first = mainSyls[0];
|
||||||
for (const si of group) {
|
const last = mainSyls[mainSyls.length - 1];
|
||||||
const syl = syllabus[si];
|
const span = makeSpan(text, first.time, false);
|
||||||
const span = makeSpan(syl.text.trimEnd(), wordStartMs, syl.isBackground);
|
mainContainer.appendChild(span);
|
||||||
span.addEventListener("mouseenter", () => {
|
lineWords.push({ el: span, start: first.time, end: last.time + last.duration, duration: (last.time + last.duration) - first.time });
|
||||||
for (const s of groupSpans) s.classList.add("rl-wbw-word-hover");
|
}
|
||||||
});
|
if (bgSyls.length > 0 && bgContainer) {
|
||||||
span.addEventListener("mouseleave", () => {
|
const text = bgSyls.map(s => s.text).join("").trim().replace(/[()]/g, "");
|
||||||
for (const s of groupSpans) s.classList.remove("rl-wbw-word-hover");
|
const first = bgSyls[0];
|
||||||
});
|
const last = bgSyls[bgSyls.length - 1];
|
||||||
groupSpans.push(span);
|
const span = makeSpan(text, first.time, true);
|
||||||
|
bgContainer.appendChild(span);
|
||||||
|
lineBgWords.push({ el: span, start: first.time, end: last.time + last.duration, duration: (last.time + last.duration) - first.time });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (const group of wordGroups) {
|
||||||
|
const groupIsBg = splitBg && syllabus[group[0]].isBackground;
|
||||||
|
const targetContainer = groupIsBg ? bgContainer! : mainContainer;
|
||||||
|
const targetWords = groupIsBg ? lineBgWords : lineWords;
|
||||||
|
|
||||||
|
if (isSylMode) {
|
||||||
|
const wordStartMs = syllabus[group[0]].time;
|
||||||
|
const groupSpans: HTMLSpanElement[] = [];
|
||||||
|
for (const si of group) {
|
||||||
|
const syl = syllabus[si];
|
||||||
|
const span = makeSpan(syl.text.trimEnd(), wordStartMs, syl.isBackground);
|
||||||
|
span.addEventListener("mouseenter", () => {
|
||||||
|
for (const s of groupSpans) s.classList.add("rl-wbw-word-hover");
|
||||||
|
});
|
||||||
|
span.addEventListener("mouseleave", () => {
|
||||||
|
for (const s of groupSpans) s.classList.remove("rl-wbw-word-hover");
|
||||||
|
});
|
||||||
|
groupSpans.push(span);
|
||||||
|
targetContainer.appendChild(span);
|
||||||
|
const entry: WordEntry = { el: span, start: syl.time, end: syl.time + syl.duration, duration: syl.duration };
|
||||||
|
targetWords.push(entry);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const mergedText = group.map(si => syllabus[si].text.trimEnd()).join("");
|
||||||
|
const first = syllabus[group[0]];
|
||||||
|
const last = syllabus[group[group.length - 1]];
|
||||||
|
const start = first.time;
|
||||||
|
const end = last.time + last.duration;
|
||||||
|
const bg = first.isBackground;
|
||||||
|
const span = makeSpan(mergedText, start, bg);
|
||||||
targetContainer.appendChild(span);
|
targetContainer.appendChild(span);
|
||||||
const entry: WordEntry = { el: span, start: syl.time, end: syl.time + syl.duration, duration: syl.duration };
|
const entry: WordEntry = { el: span, start, end, duration: end - start };
|
||||||
targetWords.push(entry);
|
targetWords.push(entry);
|
||||||
}
|
}
|
||||||
} else {
|
targetContainer.appendChild(document.createTextNode(" "));
|
||||||
const mergedText = group.map(si => syllabus[si].text.trimEnd()).join("");
|
|
||||||
const first = syllabus[group[0]];
|
|
||||||
const last = syllabus[group[group.length - 1]];
|
|
||||||
const start = first.time;
|
|
||||||
const end = last.time + last.duration;
|
|
||||||
const bg = first.isBackground;
|
|
||||||
const span = makeSpan(mergedText, start, bg);
|
|
||||||
targetContainer.appendChild(span);
|
|
||||||
const entry: WordEntry = { el: span, start, end, duration: end - start };
|
|
||||||
targetWords.push(entry);
|
|
||||||
}
|
}
|
||||||
targetContainer.appendChild(document.createTextNode(" "));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
wbwContainer.appendChild(lineDiv);
|
wbwContainer.appendChild(lineDiv);
|
||||||
@@ -2440,8 +2463,6 @@ const startTickLoop = (): void => {
|
|||||||
const onTrackChange = async (): Promise<void> => {
|
const onTrackChange = async (): Promise<void> => {
|
||||||
teardown();
|
teardown();
|
||||||
|
|
||||||
if (settings.lyricsStyle === 0) return;
|
|
||||||
|
|
||||||
const token = ++trackChangeToken;
|
const token = ++trackChangeToken;
|
||||||
|
|
||||||
const trackInfo = await getTrackInfo();
|
const trackInfo = await getTrackInfo();
|
||||||
@@ -2494,7 +2515,7 @@ const onTrackChange = async (): Promise<void> => {
|
|||||||
|
|
||||||
// Reapply word lyrics (for tab switch back)
|
// Reapply word lyrics (for tab switch back)
|
||||||
const reapplyWordLyrics = (): void => {
|
const reapplyWordLyrics = (): void => {
|
||||||
if (settings.lyricsStyle === 0 || !lyricsData) return;
|
if (!lyricsData) return;
|
||||||
|
|
||||||
clearTickLoop();
|
clearTickLoop();
|
||||||
clearScrollAnim();
|
clearScrollAnim();
|
||||||
@@ -2519,9 +2540,7 @@ const reapplyWordLyrics = (): void => {
|
|||||||
// Called by Settings or dropdown
|
// Called by Settings or dropdown
|
||||||
const toggle = (): void => {
|
const toggle = (): void => {
|
||||||
teardown();
|
teardown();
|
||||||
if (settings.lyricsStyle !== 0) {
|
onTrackChange();
|
||||||
onTrackChange();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
const updateLyricsStyleFromSettings = (): void => {
|
const updateLyricsStyleFromSettings = (): void => {
|
||||||
const segButtons = document.querySelectorAll(".rl-seg-btn");
|
const segButtons = document.querySelectorAll(".rl-seg-btn");
|
||||||
@@ -2538,7 +2557,7 @@ const updateLyricsStyleFromSettings = (): void => {
|
|||||||
onGlobalTrackChange(() => {
|
onGlobalTrackChange(() => {
|
||||||
cachedLyricsKey = null;
|
cachedLyricsKey = null;
|
||||||
cachedLyricsData = null;
|
cachedLyricsData = null;
|
||||||
if (settings.lyricsStyle !== 0) onTrackChange();
|
onTrackChange();
|
||||||
});
|
});
|
||||||
unloads.add(() => teardown());
|
unloads.add(() => teardown());
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user