Preserve and display bilingual and phonetic lyric cues
This commit is contained in:
@@ -3298,7 +3298,7 @@ function fitLyricLines(list, { maxShare = 0.17, maxPx = 160, minPx = 18, uniform
|
||||
const fits = [];
|
||||
for (const el of rows) {
|
||||
// Size a grouped cue by its widest visual line, preserving tight breaks.
|
||||
const parts = el.textContent.split('\n');
|
||||
const parts = (el.querySelector('.lyric-primary') || el).textContent.split('\n');
|
||||
widthAt.text = parts.reduce((widest, part) => {
|
||||
widthAt.text = part; const width = widthAt(100);
|
||||
return width > widest.width ? { text: part, width } : widest;
|
||||
@@ -3483,7 +3483,7 @@ const Notes = (() => {
|
||||
if (l.kind === 'cue') {
|
||||
return `<div class="lyr-cue" data-i="${i}"><span>🥁 ${escapeHtml(l.text)}</span><span class="lyr-cue-in"></span></div>`;
|
||||
}
|
||||
return `<div class="lyr-line${time ? '' : ' untimed'}" data-i="${i}"><span class="lyr-time">${time}</span><span class="lyr-text">${escapeHtml(l.text)}</span></div>`;
|
||||
return `<div class="lyr-line${time ? '' : ' untimed'}" data-i="${i}"><span class="lyr-time">${time}</span><span class="lyr-text">${LyricsDisplay.html(l)}</span></div>`;
|
||||
}).join('');
|
||||
if (st.sync) renderSyncPointer();
|
||||
else tick(now(), true);
|
||||
@@ -4160,7 +4160,7 @@ const Notes = (() => {
|
||||
return;
|
||||
}
|
||||
list.innerHTML = items.map((l) =>
|
||||
`<div class="sl-line${l.t === null || l.t === undefined ? ' untimed' : ''}" data-i="${l.i}">${escapeHtml(l.text)}</div>`).join('');
|
||||
`<div class="sl-line${l.t === null || l.t === undefined ? ' untimed' : ''}" data-i="${l.i}">${LyricsDisplay.html(l)}</div>`).join('');
|
||||
paintFlags();
|
||||
loadFlags();
|
||||
applyFits();
|
||||
@@ -4271,7 +4271,7 @@ const Notes = (() => {
|
||||
return;
|
||||
}
|
||||
list.innerHTML = items.map((l) =>
|
||||
`<div class="sl-line${l.t === null || l.t === undefined ? ' untimed' : ''}" data-i="${l.i}">${escapeHtml(l.text)}</div>`).join('');
|
||||
`<div class="sl-line${l.t === null || l.t === undefined ? ' untimed' : ''}" data-i="${l.i}">${LyricsDisplay.html(l)}</div>`).join('');
|
||||
fitLyricLines(list, { maxShare: 0.15, maxPx: 96 });
|
||||
tickStage(now(), true);
|
||||
clearTimeout(stg.refit);
|
||||
@@ -6060,7 +6060,7 @@ const Presenter = (() => {
|
||||
const pad = Math.round(list.clientHeight / 2);
|
||||
list.style.paddingTop = pad + 'px';
|
||||
list.style.paddingBottom = pad + 'px';
|
||||
list.innerHTML = sung.map((l) => `<div class="sl-line" data-i="${l.i}">${escapeHtml(l.text)}</div>`).join('');
|
||||
list.innerHTML = sung.map((l) => `<div class="sl-line" data-i="${l.i}">${LyricsDisplay.html(l)}</div>`).join('');
|
||||
refit();
|
||||
st.idx = -2;
|
||||
tick(true);
|
||||
@@ -12449,6 +12449,7 @@ async function boot() {
|
||||
PartyDJ.register(() => $('navPartyBtn').click());
|
||||
SetlistImport.configure({ library: async () => { let uploads = []; try { const j = await webFetch('/api/uploads?limit=200'); uploads = j.uploads || []; } catch {} return [...data.history, ...data.playlists.flatMap(p => p.videos || []), ...uploads]; }, search: async q => (await API.search(q)).results || [], modal: showModal, close: closeModal, create: (name, videos) => { const pl = { id: uid(), name, videos: videos.map(v => ({ ...slim(v), tags: v.tags, key: v.key, chordPro: v.chordPro })) }; data.playlists.push(pl); persist(); view = { type: 'playlist', id: pl.id }; render(); toast(`Imported ${videos.length} songs`); } });
|
||||
Remote.boot();
|
||||
LyricsDisplay.register();
|
||||
LowerThird.register(() => $('remoteChip').click());
|
||||
Transition.renderButton();
|
||||
PiP.render();
|
||||
|
||||
Reference in New Issue
Block a user