diff --git a/app/books/bbd/[chapter]/page.tsx b/app/books/bbd/[chapter]/page.tsx index f85c48e5..fb8c5532 100644 --- a/app/books/bbd/[chapter]/page.tsx +++ b/app/books/bbd/[chapter]/page.tsx @@ -9,6 +9,7 @@ import { type BbdChapter, type BbdSidebarItem, } from "@/lib/bbd"; +import { getProseChapter } from "@/lib/api"; import { ogForChapter, extractQuoteFromHl, buildOgMeta } from "@/lib/og"; import { BOOK_NAMES } from "@/lib/constants"; import { PageHeader } from "@/components/page-header"; @@ -29,8 +30,10 @@ export async function generateMetadata({ params: Promise<{ chapter: string }>; searchParams: Promise>; }): Promise { + const { chapter: slug } = await params; const sp = await searchParams; - const hlQuote = extractQuoteFromHl(sp); + const chapter = await getProseChapter("bbd", 0, slug); + const hlQuote = extractQuoteFromHl(sp, chapter?.paragraphs); if (hlQuote) { return buildOgMeta({ title: BOOK_NAMES["bbd"], quote: hlQuote }); } diff --git a/app/books/bg/[chapter]/[verse]/page.tsx b/app/books/bg/[chapter]/[verse]/page.tsx index aea29bd7..fdd1334c 100644 --- a/app/books/bg/[chapter]/[verse]/page.tsx +++ b/app/books/bg/[chapter]/[verse]/page.tsx @@ -18,7 +18,14 @@ export async function generateMetadata({ }): Promise { const { chapter, verse } = await params; const sp = await searchParams; - const quote = extractQuoteFromHl(sp); + const v = await getSingleVerse( + "bg", + null, + parseInt(chapter), + parseInt(verse), + ); + const paragraphs = v ? [v.translation, ...v.purport] : undefined; + const quote = extractQuoteFromHl(sp, paragraphs); if (quote) { return buildOgMeta({ title: "Bhagavad-gītā As It Is", diff --git a/app/books/bk/[chapter]/page.tsx b/app/books/bk/[chapter]/page.tsx index 5373b088..575d63d1 100644 --- a/app/books/bk/[chapter]/page.tsx +++ b/app/books/bk/[chapter]/page.tsx @@ -7,6 +7,7 @@ import { type BkChapter, type BkSidebarItem, } from "@/lib/bk"; +import { getProseChapter } from "@/lib/api"; import { ogForChapter, extractQuoteFromHl, buildOgMeta } from "@/lib/og"; import { BOOK_NAMES } from "@/lib/constants"; import { PageHeader } from "@/components/page-header"; @@ -25,8 +26,10 @@ export async function generateMetadata({ params: Promise<{ chapter: string }>; searchParams: Promise>; }): Promise { + const { chapter: slug } = await params; const sp = await searchParams; - const hlQuote = extractQuoteFromHl(sp); + const chapter = await getProseChapter("bk", 0, slug); + const hlQuote = extractQuoteFromHl(sp, chapter?.paragraphs); if (hlQuote) { return buildOgMeta({ title: BOOK_NAMES["bk"], quote: hlQuote }); } diff --git a/app/books/bs/[chapter]/[verse]/page.tsx b/app/books/bs/[chapter]/[verse]/page.tsx index 3af73cc2..f00415cd 100644 --- a/app/books/bs/[chapter]/[verse]/page.tsx +++ b/app/books/bs/[chapter]/[verse]/page.tsx @@ -123,7 +123,9 @@ export async function generateMetadata({ }): Promise { const { chapter, verse } = await params; const sp = await searchParams; - const quote = extractQuoteFromHl(sp); + const v = await getVerseWithFallback(parseInt(chapter), parseInt(verse)); + const paragraphs = v ? [v.translation, ...v.purport] : undefined; + const quote = extractQuoteFromHl(sp, paragraphs); if (quote) { return buildOgMeta({ title: "Śrī Brahma-saṁhitā", diff --git a/app/books/cbhc/[chapter]/page.tsx b/app/books/cbhc/[chapter]/page.tsx index 61ba6971..5b8f7e8c 100644 --- a/app/books/cbhc/[chapter]/page.tsx +++ b/app/books/cbhc/[chapter]/page.tsx @@ -7,6 +7,7 @@ import { type CbhcChapter, type CbhcSidebarItem, } from "@/lib/cbhc"; +import { getProseChapter } from "@/lib/api"; import { ogForChapter, extractQuoteFromHl, buildOgMeta } from "@/lib/og"; import { BOOK_NAMES } from "@/lib/constants"; import { PageHeader } from "@/components/page-header"; @@ -25,8 +26,10 @@ export async function generateMetadata({ params: Promise<{ chapter: string }>; searchParams: Promise>; }): Promise { + const { chapter: slug } = await params; const sp = await searchParams; - const hlQuote = extractQuoteFromHl(sp); + const chapter = await getProseChapter("cbhc", 0, slug); + const hlQuote = extractQuoteFromHl(sp, chapter?.paragraphs); if (hlQuote) { return buildOgMeta({ title: BOOK_NAMES["cbhc"], quote: hlQuote }); } diff --git a/app/books/cc/[lila]/[chapter]/[verse]/page.tsx b/app/books/cc/[lila]/[chapter]/[verse]/page.tsx index 7807e6bc..415279dd 100644 --- a/app/books/cc/[lila]/[chapter]/[verse]/page.tsx +++ b/app/books/cc/[lila]/[chapter]/[verse]/page.tsx @@ -32,7 +32,14 @@ export async function generateMetadata({ }): Promise { const { lila, chapter, verse } = await params; const sp = await searchParams; - const quote = extractQuoteFromHl(sp); + const v = await getSingleVerse( + "cc", + lila, + parseInt(chapter), + parseInt(verse), + ); + const paragraphs = v ? [v.translation, ...v.purport] : undefined; + const quote = extractQuoteFromHl(sp, paragraphs); if (quote) { const bookTitle = "Śrī Caitanya-caritāmṛta"; return buildOgMeta({ diff --git a/app/books/ds/[chapter]/page.tsx b/app/books/ds/[chapter]/page.tsx index 44e6f1ee..31eec0bf 100644 --- a/app/books/ds/[chapter]/page.tsx +++ b/app/books/ds/[chapter]/page.tsx @@ -9,6 +9,7 @@ import { type DsChapter, type DsSidebarItem, } from "@/lib/ds"; +import { getProseChapter } from "@/lib/api"; import { ogForChapter, extractQuoteFromHl, buildOgMeta } from "@/lib/og"; import { BOOK_NAMES } from "@/lib/constants"; import { PageHeader } from "@/components/page-header"; @@ -29,8 +30,10 @@ export async function generateMetadata({ params: Promise<{ chapter: string }>; searchParams: Promise>; }): Promise { + const { chapter: slug } = await params; const sp = await searchParams; - const hlQuote = extractQuoteFromHl(sp); + const chapter = await getProseChapter("ds", 0, slug); + const hlQuote = extractQuoteFromHl(sp, chapter?.paragraphs); if (hlQuote) { return buildOgMeta({ title: BOOK_NAMES["ds"], quote: hlQuote }); } diff --git a/app/books/ej/[chapter]/page.tsx b/app/books/ej/[chapter]/page.tsx index aa298f70..4b910b97 100644 --- a/app/books/ej/[chapter]/page.tsx +++ b/app/books/ej/[chapter]/page.tsx @@ -9,6 +9,7 @@ import { type EjChapter, type EjSidebarItem, } from "@/lib/ej"; +import { getProseChapter } from "@/lib/api"; import { ogForChapter, extractQuoteFromHl, buildOgMeta } from "@/lib/og"; import { BOOK_NAMES } from "@/lib/constants"; import { PageHeader } from "@/components/page-header"; @@ -29,8 +30,10 @@ export async function generateMetadata({ params: Promise<{ chapter: string }>; searchParams: Promise>; }): Promise { + const { chapter: slug } = await params; const sp = await searchParams; - const hlQuote = extractQuoteFromHl(sp); + const chapter = await getProseChapter("ej", 0, slug); + const hlQuote = extractQuoteFromHl(sp, chapter?.paragraphs); if (hlQuote) { return buildOgMeta({ title: BOOK_NAMES["ej"], quote: hlQuote }); } diff --git a/app/books/ek/[chapter]/page.tsx b/app/books/ek/[chapter]/page.tsx index b3ecbf17..fb6137b1 100644 --- a/app/books/ek/[chapter]/page.tsx +++ b/app/books/ek/[chapter]/page.tsx @@ -9,6 +9,7 @@ import { type EkChapter, type EkSidebarItem, } from "@/lib/ek"; +import { getProseChapter } from "@/lib/api"; import { ogForChapter, extractQuoteFromHl, buildOgMeta } from "@/lib/og"; import { BOOK_NAMES } from "@/lib/constants"; import { PageHeader } from "@/components/page-header"; @@ -29,8 +30,10 @@ export async function generateMetadata({ params: Promise<{ chapter: string }>; searchParams: Promise>; }): Promise { + const { chapter: slug } = await params; const sp = await searchParams; - const hlQuote = extractQuoteFromHl(sp); + const chapter = await getProseChapter("ek", 0, slug); + const hlQuote = extractQuoteFromHl(sp, chapter?.paragraphs); if (hlQuote) { return buildOgMeta({ title: BOOK_NAMES["ek"], quote: hlQuote }); } diff --git a/app/books/gg/[chapter]/page.tsx b/app/books/gg/[chapter]/page.tsx index 66aacbe3..e29ce60b 100644 --- a/app/books/gg/[chapter]/page.tsx +++ b/app/books/gg/[chapter]/page.tsx @@ -7,6 +7,7 @@ import { type GgChapter, type GgSidebarItem, } from "@/lib/gg"; +import { getProseChapter } from "@/lib/api"; import { ogForChapter, extractQuoteFromHl, buildOgMeta } from "@/lib/og"; import { BOOK_NAMES } from "@/lib/constants"; import { PageHeader } from "@/components/page-header"; @@ -25,8 +26,10 @@ export async function generateMetadata({ params: Promise<{ chapter: string }>; searchParams: Promise>; }): Promise { + const { chapter: slug } = await params; const sp = await searchParams; - const hlQuote = extractQuoteFromHl(sp); + const chapter = await getProseChapter("gg", 0, slug); + const hlQuote = extractQuoteFromHl(sp, chapter?.paragraphs); if (hlQuote) { return buildOgMeta({ title: BOOK_NAMES["gg"], quote: hlQuote }); } diff --git a/app/books/hkh/[chapter]/page.tsx b/app/books/hkh/[chapter]/page.tsx index ef0b99b5..a4f84255 100644 --- a/app/books/hkh/[chapter]/page.tsx +++ b/app/books/hkh/[chapter]/page.tsx @@ -9,6 +9,7 @@ import { type HkhChapter, type HkhSidebarItem, } from "@/lib/hkh"; +import { getProseChapter } from "@/lib/api"; import { ogForChapter, extractQuoteFromHl, buildOgMeta } from "@/lib/og"; import { BOOK_NAMES } from "@/lib/constants"; import { PageHeader } from "@/components/page-header"; @@ -29,8 +30,10 @@ export async function generateMetadata({ params: Promise<{ chapter: string }>; searchParams: Promise>; }): Promise { + const { chapter: slug } = await params; const sp = await searchParams; - const hlQuote = extractQuoteFromHl(sp); + const chapter = await getProseChapter("hkh", 0, slug); + const hlQuote = extractQuoteFromHl(sp, chapter?.paragraphs); if (hlQuote) { return buildOgMeta({ title: BOOK_NAMES["hkh"], quote: hlQuote }); } diff --git a/app/books/iso/[mantra]/page.tsx b/app/books/iso/[mantra]/page.tsx index d6542447..f7f1b1d3 100644 --- a/app/books/iso/[mantra]/page.tsx +++ b/app/books/iso/[mantra]/page.tsx @@ -46,7 +46,15 @@ export async function generateMetadata({ const { mantra: slug } = await params; const sp = await searchParams; - const quote = extractQuoteFromHl(sp); + const content = await getIsoContent(slug); + const isVerse_ = content && "mantra" in content && "devanagari" in content; + const paragraphs = content + ? isVerse_ + ? [(content as IsoVerse).translation, ...(content as IsoVerse).purport] + : (content as IsoIntroSegment).paragraphs + : undefined; + + const quote = extractQuoteFromHl(sp, paragraphs); if (quote) { return buildOgMeta({ title: "Śrī Īśopaniṣad", @@ -55,8 +63,6 @@ export async function generateMetadata({ }); } - const content = await getIsoContent(slug); - const isVerse_ = content && "mantra" in content && "devanagari" in content; const title = isVerse_ ? `${(content as IsoVerse).reference}: ${(content as IsoVerse).title}` : content && "title" in content diff --git a/app/books/kb/[volume]/[slug]/page.tsx b/app/books/kb/[volume]/[slug]/page.tsx index b58bad23..c2ce774b 100644 --- a/app/books/kb/[volume]/[slug]/page.tsx +++ b/app/books/kb/[volume]/[slug]/page.tsx @@ -24,7 +24,7 @@ export async function generateMetadata({ const sp = await searchParams; const vol = parseInt(volume); const chapter = !isNaN(vol) ? await getProseChapter("kb", vol, slug) : null; - const hlQuote = extractQuoteFromHl(sp); + const hlQuote = extractQuoteFromHl(sp, chapter?.paragraphs); if (hlQuote) { return buildOgMeta({ title: BOOK_NAMES["kb"], diff --git a/app/books/lcl/[chapter]/page.tsx b/app/books/lcl/[chapter]/page.tsx index d9a8d141..dbbd9ba4 100644 --- a/app/books/lcl/[chapter]/page.tsx +++ b/app/books/lcl/[chapter]/page.tsx @@ -9,6 +9,7 @@ import { type LclChapter, type LclSidebarItem, } from "@/lib/lcl"; +import { getProseChapter } from "@/lib/api"; import { ogForChapter, extractQuoteFromHl, buildOgMeta } from "@/lib/og"; import { BOOK_NAMES } from "@/lib/constants"; import { PageHeader } from "@/components/page-header"; @@ -29,8 +30,10 @@ export async function generateMetadata({ params: Promise<{ chapter: string }>; searchParams: Promise>; }): Promise { + const { chapter: slug } = await params; const sp = await searchParams; - const hlQuote = extractQuoteFromHl(sp); + const chapter = await getProseChapter("lcl", 0, slug); + const hlQuote = extractQuoteFromHl(sp, chapter?.paragraphs); if (hlQuote) { return buildOgMeta({ title: BOOK_NAMES["lcl"], quote: hlQuote }); } diff --git a/app/books/lob/[chapter]/page.tsx b/app/books/lob/[chapter]/page.tsx index 92f0a41f..89a7df3e 100644 --- a/app/books/lob/[chapter]/page.tsx +++ b/app/books/lob/[chapter]/page.tsx @@ -9,6 +9,7 @@ import { type LobChapter, type LobSidebarItem, } from "@/lib/lob"; +import { getProseChapter } from "@/lib/api"; import { ogForChapter, extractQuoteFromHl, buildOgMeta } from "@/lib/og"; import { BOOK_NAMES } from "@/lib/constants"; import { PageHeader } from "@/components/page-header"; @@ -25,8 +26,10 @@ export async function generateMetadata({ params: Promise<{ chapter: string }>; searchParams: Promise>; }): Promise { + const { chapter: slug } = await params; const sp = await searchParams; - const hlQuote = extractQuoteFromHl(sp); + const chapter = await getProseChapter("lob", 0, slug); + const hlQuote = extractQuoteFromHl(sp, chapter?.paragraphs); if (hlQuote) { return buildOgMeta({ title: BOOK_NAMES["lob"], quote: hlQuote }); } diff --git a/app/books/mg/[chapter]/page.tsx b/app/books/mg/[chapter]/page.tsx index d1f446e3..5b95b33c 100644 --- a/app/books/mg/[chapter]/page.tsx +++ b/app/books/mg/[chapter]/page.tsx @@ -9,6 +9,7 @@ import { type MgChapter, type MgSidebarItem, } from "@/lib/mg"; +import { getProseChapter } from "@/lib/api"; import { ogForChapter, extractQuoteFromHl, buildOgMeta } from "@/lib/og"; import { BOOK_NAMES } from "@/lib/constants"; import { PageHeader } from "@/components/page-header"; @@ -25,8 +26,10 @@ export async function generateMetadata({ params: Promise<{ chapter: string }>; searchParams: Promise>; }): Promise { + const { chapter: slug } = await params; const sp = await searchParams; - const hlQuote = extractQuoteFromHl(sp); + const chapter = await getProseChapter("mg", 0, slug); + const hlQuote = extractQuoteFromHl(sp, chapter?.paragraphs); if (hlQuote) { return buildOgMeta({ title: BOOK_NAMES["mg"], quote: hlQuote }); } diff --git a/app/books/mm/[chapter]/[verse]/page.tsx b/app/books/mm/[chapter]/[verse]/page.tsx index 3f98fd1a..92d2b4df 100644 --- a/app/books/mm/[chapter]/[verse]/page.tsx +++ b/app/books/mm/[chapter]/[verse]/page.tsx @@ -26,7 +26,9 @@ export async function generateMetadata({ }): Promise { const { chapter, verse } = await params; const sp = await searchParams; - const quote = extractQuoteFromHl(sp); + const v = await getVerseWithFallback(parseInt(chapter), parseInt(verse)); + const paragraphs = v ? [v.translation, ...v.purport] : undefined; + const quote = extractQuoteFromHl(sp, paragraphs); if (quote) { return buildOgMeta({ title: "Mukunda-mālā-stotra", diff --git a/app/books/nbs/[chapter]/[verse]/page.tsx b/app/books/nbs/[chapter]/[verse]/page.tsx index e16fadc8..b80b691c 100644 --- a/app/books/nbs/[chapter]/[verse]/page.tsx +++ b/app/books/nbs/[chapter]/[verse]/page.tsx @@ -123,7 +123,14 @@ export async function generateMetadata({ }): Promise { const { chapter, verse } = await params; const sp = await searchParams; - const quote = extractQuoteFromHl(sp); + const v = await getSingleVerse( + "nbs", + null, + parseInt(chapter), + parseInt(verse), + ); + const paragraphs = v ? [v.translation, ...v.purport] : undefined; + const quote = extractQuoteFromHl(sp, paragraphs); if (quote) { return buildOgMeta({ title: "Nārada-bhakti-sūtra", diff --git a/app/books/noi/[text]/page.tsx b/app/books/noi/[text]/page.tsx index ce984301..a0e2a5ca 100644 --- a/app/books/noi/[text]/page.tsx +++ b/app/books/noi/[text]/page.tsx @@ -46,7 +46,19 @@ export async function generateMetadata({ const { text: slug } = await params; const sp = await searchParams; - const quote = extractQuoteFromHl(sp); + const content = await getNoiContent(slug); + let paragraphs: string[] | undefined; + if (content) { + if ("devanagari" in content) { + // It's a verse + paragraphs = [content.translation, ...content.purport]; + } else if ("paragraphs" in content) { + // It's an intro segment + paragraphs = content.paragraphs; + } + } + + const quote = extractQuoteFromHl(sp, paragraphs); if (quote) { return buildOgMeta({ title: "The Nectar of Instruction", diff --git a/app/books/sb/[canto]/[chapter]/[verse]/page.tsx b/app/books/sb/[canto]/[chapter]/[verse]/page.tsx index 225431f6..1b909a38 100644 --- a/app/books/sb/[canto]/[chapter]/[verse]/page.tsx +++ b/app/books/sb/[canto]/[chapter]/[verse]/page.tsx @@ -31,7 +31,14 @@ export async function generateMetadata({ }): Promise { const { canto, chapter, verse } = await params; const sp = await searchParams; - const quote = extractQuoteFromHl(sp); + const v = await getSingleVerse( + "sb", + canto, + parseInt(chapter), + parseInt(verse), + ); + const paragraphs = v ? [v.translation, ...v.purport] : undefined; + const quote = extractQuoteFromHl(sp, paragraphs); if (quote) { return buildOgMeta({ title: "Śrīmad-Bhāgavatam", diff --git a/app/books/sb1962/[chapter]/[verse]/page.tsx b/app/books/sb1962/[chapter]/[verse]/page.tsx index a5e50a6b..8e06af9a 100644 --- a/app/books/sb1962/[chapter]/[verse]/page.tsx +++ b/app/books/sb1962/[chapter]/[verse]/page.tsx @@ -26,7 +26,14 @@ export async function generateMetadata({ }): Promise { const { chapter, verse } = await params; const sp = await searchParams; - const quote = extractQuoteFromHl(sp); + const v = await getSingleVerse( + "sb1962", + null, + parseInt(chapter), + parseInt(verse), + ); + const paragraphs = v ? [v.translation, ...v.purport] : undefined; + const quote = extractQuoteFromHl(sp, paragraphs); if (quote) { return buildOgMeta({ title: "Śrīmad-Bhāgavatam (1962)", diff --git a/app/essays/[slug]/page.tsx b/app/essays/[slug]/page.tsx index 7f060d7e..8025bf48 100644 --- a/app/essays/[slug]/page.tsx +++ b/app/essays/[slug]/page.tsx @@ -104,7 +104,7 @@ export async function generateMetadata({ const essay = await getEssay(slug); if (!essay) return {}; - const hlQuote = extractQuoteFromHl(sp); + const hlQuote = extractQuoteFromHl(sp, essay.paragraphs); if (hlQuote) { return buildOgMeta({ title: essay.title, diff --git a/app/journals/[slug]/[chapter]/page.tsx b/app/journals/[slug]/[chapter]/page.tsx index a7b20cf0..a9f39903 100644 --- a/app/journals/[slug]/[chapter]/page.tsx +++ b/app/journals/[slug]/[chapter]/page.tsx @@ -37,7 +37,7 @@ export async function generateMetadata({ const journalTitle = meta?.title || slug; const entryTitle = content?.title || chapterSlug; - const quote = extractQuoteFromHl(sp); + const quote = extractQuoteFromHl(sp, content?.paragraphs); if (quote) { return buildOgMeta({ title: journalTitle, diff --git a/app/letters/[slug]/page.tsx b/app/letters/[slug]/page.tsx index 6edd062b..24b98902 100644 --- a/app/letters/[slug]/page.tsx +++ b/app/letters/[slug]/page.tsx @@ -24,7 +24,7 @@ export async function generateMetadata({ if (!letter) return {}; const meta = [letter.date, letter.location].filter(Boolean).join(" · "); - const hlQuote = extractQuoteFromHl(sp); + const hlQuote = extractQuoteFromHl(sp, letter.paragraphs); if (hlQuote) { return buildOgMeta({ diff --git a/app/magazines/[slug]/[issue]/page.tsx b/app/magazines/[slug]/[issue]/page.tsx index 9ab951fd..d94888f7 100644 --- a/app/magazines/[slug]/[issue]/page.tsx +++ b/app/magazines/[slug]/[issue]/page.tsx @@ -29,19 +29,19 @@ export async function generateMetadata({ const sp = await searchParams; const bookCode = slugToBookCode(slug); - const quote = extractQuoteFromHl(sp); - if (quote) { - const meta = await getMagazineMeta(bookCode); - const title = meta?.title ?? slug; - return buildOgMeta({ title, quote }); - } - const [meta, content] = await Promise.all([ getMagazineMeta(bookCode), getMagazineContent(bookCode, issueSlug), ]); const magTitle = meta?.title ?? slug; + const paragraphs = content?.paragraphs; + + const quote = extractQuoteFromHl(sp, paragraphs); + if (quote) { + return buildOgMeta({ title: magTitle, quote }); + } + const issueTitle = content?.title ?? undefined; return ogForMagazine(magTitle, issueTitle); diff --git a/app/other-documents/[slug]/[chapter]/page.tsx b/app/other-documents/[slug]/[chapter]/page.tsx index 41b33ccb..54ad90f5 100644 --- a/app/other-documents/[slug]/[chapter]/page.tsx +++ b/app/other-documents/[slug]/[chapter]/page.tsx @@ -33,20 +33,19 @@ export async function generateMetadata({ const sp = await searchParams; const bookCode = slugToBookCode(slug); - const quote = extractQuoteFromHl(sp); - if (quote) { - const meta = await getOtherDocumentMeta(bookCode); - const content = await getOtherDocumentContent(bookCode, chapterSlug); - const title = meta?.title ?? slug; - const detail = content?.title ?? `Chapter ${chapterSlug}`; - return buildOgMeta({ title, detail, quote }); - } - const [meta, content] = await Promise.all([ getOtherDocumentMeta(bookCode), getOtherDocumentContent(bookCode, chapterSlug), ]); + const paragraphs = content?.paragraphs; + const quote = extractQuoteFromHl(sp, paragraphs); + if (quote) { + const title = meta?.title ?? slug; + const detail = content?.title ?? `Chapter ${chapterSlug}`; + return buildOgMeta({ title, detail, quote }); + } + return ogForOtherDocument(meta?.title ?? slug, content?.title ?? undefined); } diff --git a/lib/og.ts b/lib/og.ts index 978cefdf..80096a4a 100644 --- a/lib/og.ts +++ b/lib/og.ts @@ -6,7 +6,7 @@ */ import { BOOK_NAMES, CC_LILAS } from "@/lib/constants"; -import { decodeTextFragment } from "@/lib/text-highlight"; +import { decodeTextFragment, extractMatchedText } from "@/lib/text-highlight"; const SITE_URL = process.env.NEXT_PUBLIC_SITE_URL || "https://prabhupadasays.org"; @@ -305,13 +305,18 @@ export function ogForLettersByLocation(location: string, count: number) { /** * Extract a display quote from the `?hl=` search param. * - * The `hl` param encodes `prefix,suffix,length`. We reconstruct a readable - * snippet from the prefix + suffix: "first few words ... last few words" + * When `paragraphs` are provided (from the database), the full matched text + * is extracted by searching the content — giving the OG image the actual + * selected quote, not just the prefix/suffix. + * + * When `paragraphs` are not provided, falls back to reconstructing a + * readable snippet from prefix + suffix: "first few words ... last few words" * * Returns null if hl is missing or invalid. */ export function extractQuoteFromHl( searchParams: Record | URLSearchParams, + paragraphs?: string[], ): string | null { const raw = searchParams instanceof URLSearchParams @@ -325,6 +330,23 @@ export function extractQuoteFromHl( const fragment = decodeTextFragment(raw); if (!fragment) return null; + // If paragraphs are available, extract the full matched text + if (paragraphs && paragraphs.length > 0) { + const fullMatch = extractMatchedText(fragment, paragraphs); + if (fullMatch) { + // Truncate at word boundary if too long for OG image + if (fullMatch.length > 280) { + const truncated = fullMatch.slice(0, 277); + const lastSpace = truncated.lastIndexOf(" "); + return ( + (lastSpace > 200 ? truncated.slice(0, lastSpace) : truncated) + "..." + ); + } + return fullMatch; + } + } + + // Fallback: reconstruct from prefix + suffix const { prefix, suffix } = fragment; if (!prefix) return null; diff --git a/lib/text-highlight.ts b/lib/text-highlight.ts index 8f049b80..d23f2db3 100644 --- a/lib/text-highlight.ts +++ b/lib/text-highlight.ts @@ -1,5 +1,9 @@ // --------------------------------------------------------------------------- // Text Fragment Highlight — compact URL encoding for selected text +// +// These utilities work in both browser and server environments. +// The DOM-based findAndHighlightText() is browser-only. +// The string-based extractMatchedText() is server-safe (for OG image generation). // --------------------------------------------------------------------------- /** @@ -52,6 +56,67 @@ export function decodeTextFragment( return { prefix, suffix, length }; } +/** + * Server-side: extract the full matched text from an array of paragraph strings + * using a decoded text fragment (prefix, suffix, length). + * + * This is the server equivalent of findAndHighlightText() — instead of + * walking the DOM, it searches a flat array of HTML paragraph strings. + * HTML tags are stripped before matching. + * + * Returns the matched text string, or null if not found. + */ +export function extractMatchedText( + fragment: { prefix: string; suffix: string; length: number }, + paragraphs: string[], +): string | null { + // Strip HTML tags to get plain text + const plainParagraphs = paragraphs.map((p) => + p.replace(/<[^>]*>/g, "").replace(/&[^;]+;/g, " "), + ); + + // Build a single string with paragraph breaks + const fullText = plainParagraphs.join(" "); + const normalized = fullText.replace(/\s+/g, " "); + + const normalizedPrefix = fragment.prefix.replace(/\s+/g, " "); + let searchFrom = 0; + let matchStart = -1; + + while (searchFrom < normalized.length) { + const idx = normalized.indexOf(normalizedPrefix, searchFrom); + if (idx === -1) break; + + if (fragment.suffix) { + const normalizedSuffix = fragment.suffix.replace(/\s+/g, " "); + const expectedEnd = idx + fragment.length; + const suffixSearchStart = Math.max( + idx, + expectedEnd - normalizedSuffix.length - 20, + ); + const suffixSearchEnd = Math.min(normalized.length, expectedEnd + 20); + const searchWindow = normalized.slice(suffixSearchStart, suffixSearchEnd); + if (searchWindow.includes(normalizedSuffix)) { + matchStart = idx; + break; + } + } else { + matchStart = idx; + break; + } + searchFrom = idx + 1; + } + + if (matchStart === -1) return null; + + const matchEnd = Math.min(matchStart + fragment.length, normalized.length); + const matched = normalized.slice(matchStart, matchEnd).trim(); + + if (matched.length === 0) return null; + + return matched; +} + /** * Build the full URL with highlight fragment for the selected text. */