BookPlayer affiche les paroles du morceau, plus le contenu chapitre
Le BookPlayer chargeait les .md via Nuxt Content — qui contenaient avant
les paroles par erreur. Maintenant que les .md ont le vrai contenu du
livre, le BookPlayer doit afficher les lyrics depuis bookplayer.config.yml.
Supprime queryCollection('book') du BookPlayer, remplace ContentRenderer
par un rendu HTML des paroles avec tags stylisés.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -87,7 +87,13 @@
|
||||
ref="contentEl"
|
||||
:style="contentStyle"
|
||||
>
|
||||
<ContentRenderer v-if="activeChapter" :value="activeChapter" />
|
||||
<div v-if="chapterLyrics" class="lyrics-content" v-html="chapterLyricsHtml" />
|
||||
<div v-else-if="chapterSong" class="lyrics-empty">
|
||||
<p class="op-40 italic">Paroles à venir pour « {{ chapterSong.title }} »</p>
|
||||
</div>
|
||||
<div v-else class="lyrics-empty">
|
||||
<p class="op-40 italic">Aucun morceau associé à ce chapitre</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Page turn shadow overlay (paginated only) -->
|
||||
<div v-if="!isScrollMode" class="reader-shadow" :class="{ visible: isTurning }" />
|
||||
@@ -194,27 +200,14 @@ const { init: initBookData, getSongs, getPrimarySong, getChapterForSong, getPlay
|
||||
const audioPlayer = useAudioPlayer()
|
||||
const playerStore = usePlayerStore()
|
||||
|
||||
// ── Content from Nuxt Content ──
|
||||
const chaptersContent = ref<any[]>([])
|
||||
// ── Content loaded flag (lyrics come from bookplayer config) ──
|
||||
const contentLoaded = ref(false)
|
||||
|
||||
async function loadContent() {
|
||||
if (contentLoaded.value) return
|
||||
try {
|
||||
const data = await queryCollection('book').order('order', 'ASC').all()
|
||||
chaptersContent.value = data
|
||||
contentLoaded.value = true
|
||||
}
|
||||
catch (err) {
|
||||
console.error('Failed to load book content:', err)
|
||||
}
|
||||
}
|
||||
|
||||
const activeChapter = computed(() => {
|
||||
if (chapterIdx.value < 0 || !chaptersContent.value.length) return null
|
||||
return chaptersContent.value[chapterIdx.value] ?? null
|
||||
})
|
||||
|
||||
// ── Chapter metadata ──
|
||||
const chapters = [
|
||||
{ slug: '01-introduction', title: 'Introduction' },
|
||||
@@ -256,6 +249,23 @@ const chapterSong = computed(() => {
|
||||
return getPrimarySong(chapters[chapterIdx.value].slug)
|
||||
})
|
||||
|
||||
const chapterLyrics = computed(() => {
|
||||
return chapterSong.value?.lyrics?.trim() || ''
|
||||
})
|
||||
|
||||
const chapterLyricsHtml = computed(() => {
|
||||
if (!chapterLyrics.value) return ''
|
||||
return chapterLyrics.value
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/\[([^\]]+)\]/g, '<span class="lyrics-tag">[$1]</span>')
|
||||
.replace(/\n\n/g, '</p><p>')
|
||||
.replace(/\n/g, '<br>')
|
||||
.replace(/^/, '<p>')
|
||||
.replace(/$/, '</p>')
|
||||
})
|
||||
|
||||
// ── CSS columns pagination ──
|
||||
const contentStyle = computed(() => {
|
||||
if (isScrollMode.value) return {}
|
||||
@@ -276,10 +286,9 @@ function recalcPages() {
|
||||
|
||||
let resizeObs: ResizeObserver | null = null
|
||||
|
||||
// Recalc when chapter content changes
|
||||
watch(activeChapter, async () => {
|
||||
// Recalc when chapter changes
|
||||
watch(chapterIdx, async () => {
|
||||
currentPage.value = 0
|
||||
// Wait for ContentRenderer to update DOM
|
||||
await nextTick()
|
||||
await nextTick()
|
||||
setTimeout(recalcPages, 100)
|
||||
@@ -357,7 +366,7 @@ function prevPage() {
|
||||
audioPlayer.loadAndPlay(song)
|
||||
}
|
||||
// After content loads, go to last page
|
||||
watch(activeChapter, async () => {
|
||||
watch(chapterIdx, async () => {
|
||||
await nextTick()
|
||||
await nextTick()
|
||||
setTimeout(() => {
|
||||
@@ -809,7 +818,33 @@ onUnmounted(() => {
|
||||
.reader-columns :deep(ol) {
|
||||
break-inside: avoid;
|
||||
}
|
||||
/* p with pre-line (lyrics) can be taller than a column — allow break */
|
||||
/* Lyrics content */
|
||||
.lyrics-content {
|
||||
white-space: pre-line;
|
||||
line-height: 1.9;
|
||||
font-size: clamp(0.9rem, 2vw, 1.05rem);
|
||||
}
|
||||
.lyrics-content :deep(.lyrics-tag) {
|
||||
display: block;
|
||||
margin-top: 1.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
opacity: 0.35;
|
||||
}
|
||||
.lyrics-content :deep(p) {
|
||||
break-inside: auto;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.lyrics-empty {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 200px;
|
||||
}
|
||||
|
||||
.reader-columns :deep(p) {
|
||||
break-inside: auto;
|
||||
overflow-y: auto;
|
||||
|
||||
Reference in New Issue
Block a user