diff --git a/app/components/book/BookPlayer.vue b/app/components/book/BookPlayer.vue index 29a90dd..f652877 100644 --- a/app/components/book/BookPlayer.vue +++ b/app/components/book/BookPlayer.vue @@ -87,7 +87,13 @@ ref="contentEl" :style="contentStyle" > - +
+
+

Paroles à venir pour « {{ chapterSong.title }} »

+
+
+

Aucun morceau associé à ce chapitre

+
@@ -194,27 +200,14 @@ const { init: initBookData, getSongs, getPrimarySong, getChapterForSong, getPlay const audioPlayer = useAudioPlayer() const playerStore = usePlayerStore() -// ── Content from Nuxt Content ── -const chaptersContent = ref([]) +// ── 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) - } + contentLoaded.value = true } -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, '[$1]') + .replace(/\n\n/g, '

') + .replace(/\n/g, '
') + .replace(/^/, '

') + .replace(/$/, '

') +}) + // ── 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;