Files
spsays-training/PROJECT_CONTEXT.md
2026-05-09 00:21:52 +05:30

34 KiB
Raw Permalink Blame History

Prabhupāda Says — Project Context

What this project is

A Next.js app presenting the books, letters, essays, lectures, and teachings of His Divine Grace A.C. Bhaktivedanta Swami Prabhupāda in a modern, accessible, and searchable format with proper Unicode Sanskrit (IAST + Devanagari).

Content is sourced from the pre-1980s Vedabase, extracted and indexed into MongoDB, and served via a Next.js frontend. Extraction scripts and raw data live separately in the extractor/ directory.


Tech Stack

  • Next.js 16 with Turbopack, React 19, TypeScript
  • Tailwind CSS v4 with @tailwindcss/postcss
  • shadcn/ui (base-nova style, lucide icons) — components: Button, Card, Badge, Tooltip, Input, Drawer
  • next-themes for dark/light/system mode
  • vaul for mobile drawer navigation
  • Prisma v6 with MongoDB (provider prisma-client-js)
  • MongoDB native driver (mongodb package) for seeding (Prisma requires replica set for writes)
  • Python 3 with BeautifulSoup4 + indic-transliteration for HTML extraction scripts
  • Database: MongoDB at DATABASE_URL env variable with ?authSource=admin

Project Structure

The project is split into two directories:

  • knowledgebase/ — Production Next.js app (~3.5MB excluding node_modules)
  • extractor/ — Extraction scripts, seed scripts, raw data, legacy fonts (~750MB)
knowledgebase/
├── app/
│   ├── page.tsx                → Landing page (Books, Essays, Magazines, Letters, Transcriptions, etc.)
│   ├── layout.tsx              → ThemeProvider + TooltipProvider + TextSelectionToolbar + TextHighlighter
│   ├── globals.css             → Vintage color palette + reading settings CSS
│   ├── about/page.tsx          → About page (mission, data source, roadmap)
│   ├── books/                  → 37 book routes (verse-based + prose)
│   │   ├── page.tsx            → Books index (searchable grid, diacritic-aware search)
│   │   ├── chapter-viewer.tsx  → Shared client component for verse-based books
│   │   ├── bg/, sb/, cc/, sb1962/, iso/, noi/, bs/, mm/, nbs/  → Verse-based books
│   │   ├── kb/, nod/, tlc/, ej/, ek/, mg/, bbd/, owk/, rv/    → Prose books
│   │   ├── poy/, pqpa/, ttp/, ochkm/, hkh/, lcl/, lob/        → More prose books
│   │   ├── ssr/, ds/           → Hierarchical prose books (sub-chapters)
│   │   └── sva/, gg/, mog/, cbhc/, bk/, pk/, va/              → Other books
│   ├── magazines/              → Magazine browsing (BTG 1944-1960, BTG 1966-1973)
│   ├── letters/
│   │   ├── page.tsx            → Letters index with 3-way nav (year/recipient/location)
│   │   ├── filter-year-browser.tsx → Shared component: sidebar years + month-grouped content
│   │   ├── [slug]/page.tsx     → Individual letter reader
│   │   ├── year/[year]/
│   │   │   ├── page.tsx        → Letters by year
│   │   │   └── year-month-browser.tsx → Sidebar months + letter list
│   │   ├── recipient/[name]/page.tsx → Letters by recipient (year sidebar + month groups)
│   │   └── location/[name]/page.tsx  → Letters by location (year sidebar + month groups)
│   ├── essays/                 → 23 essays across 6 groups
│   ├── journals/               → Jaladuta Diary, The Beginning
│   ├── other-documents/        → 12 miscellaneous documents
│   ├── glossary/
│   │   ├── page.tsx            → Redirects to /glossary/a
│   │   ├── glossary-client.tsx → Client component (sidebar terms, single entry view, prev/next nav)
│   │   └── [letter]/page.tsx   → Dynamic route per letter (A-Z)
│   ├── disciples/              → 4,747 initiated disciples directory
│   │   ├── page.tsx            → Disciples browser (letter nav, suffix filter, search, sort, pagination)
│   │   └── disciples-browser.tsx → Client component
│   ├── transcriptions/page.tsx → Coming Soon page
│   └── api/
│       ├── og/route.tsx        → Dynamic OG image generator (Noto Sans, Prabhupāda image)
│       ├── books/              → Book/verse API routes
│       └── glossary/route.ts   → Glossary API
├── components/
│   ├── ui/                     → shadcn components (button, card, badge, tooltip, input, drawer)
│   ├── selection-toolbar/      → Text selection toolbar sub-components
│   │   ├── shared.tsx          → KautilyaIcon, types, hooks (useMediaQuery, useCopyFeedback)
│   │   ├── floating-toolbar.tsx → Desktop floating pill / mobile bottom bar
│   │   ├── share-modal.tsx     → Share modal (desktop) / drawer (mobile)
│   │   ├── collections-drawer.tsx → Save to collection drawer
│   │   └── more-popover.tsx    → More options popover (desktop) / drawer (mobile)
│   ├── text-selection-toolbar.tsx → Main orchestrator (event listeners, state management)
│   ├── text-highlighter.tsx    → Processes incoming ?hl= URLs (scroll + highlight)
│   ├── page-header.tsx         → Shared header with ReadingSettings (max-w-5xl)
│   ├── reading-settings.tsx    → Theme/font/size/justify accessibility panel
│   ├── mobile-nav.tsx          → Left-slide drawer for mobile sidebar navigation
│   ├── site-footer.tsx         → Global footer (About + Home links)
│   ├── logo.tsx                → Theme-aware logo component
│   └── theme-provider.tsx      → next-themes wrapper
├── lib/
│   ├── db.ts                   → Prisma client singleton
│   ├── api.ts                  → Core data access (books, verses, prose chapters, glossary)
│   ├── constants.ts            → Shared constants (BOOK_NAMES, CC_LILAS)
│   ├── og.ts                   → OG image metadata builders (ogForVerse, ogForLetter, etc.)
│   ├── text-highlight.ts       → Text fragment encode/decode, highlight matching
│   ├── source-reference.ts     → Source reference from URL (for copy attribution)
│   ├── disciples.ts            → Disciples data access (by letter, place, year, search)
│   ├── letters.ts              → Letters data access (by year, recipient, location)
│   ├── utils.ts                → cn() helper
│   ├── [book].ts               → Per-book data access layers (nod, tlc, iso, noi, ej, ek, etc.)
│   ├── other-documents.ts      → Other Documents data access
│   ├── journals.ts             → Journals data access
│   └── magazines.ts            → Magazines data access
├── prisma/
│   └── schema.prisma           → All models (Verse, BookMeta, ProseChapter, Essay, Letter,
│                                  GlossaryEntry, Disciple, Transcription, etc.)
└── public/
    ├── fonts/Noto_Sans/static/ → NotoSans-Regular.ttf + NotoSans-Bold.ttf (OG images)
    ├── images/                 → OG card image (Prabhupāda)
    ├── logo_dark.png           → Logo for light theme
    ├── logo_light.png          → Logo for dark theme
    └── robots.txt

extractor/                      → Extraction & seeding tools (not deployed)
├── scripts/                    → Python extractors + TypeScript seed scripts
├── jsons/                      → Extracted JSON data (books, letters, essays, etc.)
├── html/                       → RTF→HTML converted files
├── rtfs/                       → Original RTF source files
├── fonts/                      → Legacy custom fonts (ScaGoudy, Indevr, Balaram, etc.)
├── app-rtfs/                   → RTF viewer pages (archived)
└── .venv/                      → Python virtual environment

Database Schema (Prisma + MongoDB)

type Synonym {
  word     String
  meanings String[]
}

model Verse {
  id              String    @id @default(auto()) @map("_id") @db.ObjectId
  book            String    // "bg" | "sb" | "cc" | "sb1962" | "iso" | "noi"
  division        String?   // null for BG/SB1962/ISO/NOI; canto for SB; lila for CC
  chapter         Int
  verse           Int
  verseEnd        Int?
  reference       String
  bookTitle       String
  devanagari      String
  bengali         String?   // only CC has Bengali
  transliteration String
  synonyms        Synonym[]
  translation     String
  purport         String[]

  @@unique([book, division, chapter, verse])
  @@index([book])
  @@index([book, division])
  @@index([book, division, chapter])
  @@map("verses")
}

model BookMeta {
  id   String @id @default(auto()) @map("_id") @db.ObjectId
  book String @unique
  data Json

  @@map("book_meta")
}

model ProseChapter {
  id         String   @id @default(auto()) @map("_id") @db.ObjectId
  book       String
  volume     Int      // 0 = no volumes (sentinel for non-volume books)
  chapter    Int?     // null for intro segments
  title      String
  type       String   // "chapter" | "intro-segment" | "conclusion"
  slug       String
  order      Int
  paragraphs String[]

  @@unique([book, volume, slug])
  @@index([book])
  @@index([book, volume])
  @@index([book, volume, type])
  @@map("prose_chapters")
}

model Essay {
  id         String   @id @default(auto()) @map("_id") @db.ObjectId
  slug       String   @unique
  title      String
  author     String
  year       Int?
  group      String?  // e.g., "two-essays"
  order      Int
  paragraphs String[]

  @@index([group])
  @@map("essays")
}

model OtherDocumentMeta {
  id   String @id @default(auto()) @map("_id") @db.ObjectId
  book String @unique
  data Json

  @@map("other_document_meta")
}

model OtherDocumentChapter {
  id         String   @id @default(auto()) @map("_id") @db.ObjectId
  book       String
  chapter    Int
  title      String
  type       String
  slug       String
  order      Int
  paragraphs String[]

  @@unique([book, slug])
  @@index([book])
  @@map("other_document_chapters")
}

model JournalMeta {
  id   String @id @default(auto()) @map("_id") @db.ObjectId
  book String @unique
  data Json

  @@map("journal_meta")
}

model JournalChapter {
  id         String   @id @default(auto()) @map("_id") @db.ObjectId
  book       String
  chapter    Int
  title      String
  type       String
  slug       String
  order      Int
  paragraphs String[]

  @@unique([book, slug])
  @@index([book])
  @@map("journal_chapters")
}

model MagazineMeta {
  id   String @id @default(auto()) @map("_id") @db.ObjectId
  book String @unique
  data Json

  @@map("magazine_meta")
}

model MagazineIssue {
  id             String   @id @default(auto()) @map("_id") @db.ObjectId
  book           String
  volume         Int
  part           Int
  title          String
  slug           String
  order          Int
  paragraphs     String[]
  paragraphsJson String?  // JSON string fallback for large issues (BSON wire protocol workaround)

  @@unique([book, slug])
  @@index([book])
  @@index([book, volume])
  @@map("magazine_issues")
}

model Letter {
  id               String   @id @default(auto()) @map("_id") @db.ObjectId
  slug             String   @unique
  recipients       String[]   // array of individual recipient names
  recipientDisplay String     // original combined string for display
  date             String?
  dateRef          String?
  year             Int?
  month            Int?
  day              Int?
  location         String?
  salutation       String?
  paragraphs       String[]

  @@index([year])
  @@index([recipients])
  @@index([location])
  @@index([year, month])
  @@map("letters")
}

model GlossaryEntry {
  id             String @id @default(auto()) @map("_id") @db.ObjectId
  term           String
  definition     String
  definitionHtml String

  @@unique([term])
  @@map("glossary")
}

model Disciple {
  id              String    @id @default(auto()) @map("_id") @db.ObjectId
  initiatedName   String
  suffix          String    // "Dasa" | "Devi Dasi" | "Sw"
  initiationDate  DateTime? // 1st of the month (exact day unknown unless verified)
  initiationPlace String?
  karmiName       String?   // pre-initiation (birth) name
  avatar          String?   // URL to profile photo
  about           String?   // biographical notes
  tags            String[]
  isVerified      Boolean   @default(false)

  @@index([initiatedName])
  @@index([initiationPlace])
  @@index([initiationDate])
  @@index([suffix])
  @@index([tags])
  @@map("disciples")
}

Disciples Collection

Scraped from https://sp.krishna.com/disciples_a through disciples_y (21 letter pages; F, Q, W, X, Z have no pages).

  • Total: 4,747 disciples
  • 3,296 Dasa, 1,450 Devi Dasi, 1 Svāmī
  • 4,554 have initiation dates (month + year); 193 are missing dates
  • 4,703 have initiation places; 44 are missing places
  • Initiation dates are set to the 1st day of the month (exact day unknown from this source)
  • On the UI, exact dates should only be shown when isVerified is true
  • karmiName, avatar, about, and tags are empty by default — intended for manual curation

Data files: data/disciples_a_b_c_d_e.json, data/disciples_g_to_l.json, data/disciples_m_n_o_p.json, data/disciples_r_s_t.json, data/disciples_u_v_y.json

Seed script: npx tsx scripts/seed-disciples.ts


Books Extracted

Verse-based Books (stored in verses collection)

Book Code Verses Chapters Division
Bhagavad-gītā As It Is bg 653 18 —
Śrīmad-Bhāgavatam sb 8,258 213 10 cantos
Śrī Caitanya-caritāmṛta cc 11,348 62 3 līlās
SB First Canto (1962 Edition) sb1962 762 19 —
Śrī Īśopaniṣad iso 19 — Invocation + 18 mantras
The Nectar of Instruction noi 11 — 11 texts
Śrī Brahma-saṁhitā bs 62 1 (Ch. 5) —
Mukunda-mālā-stotra mm 6 1 —
Nārada-bhakti-sūtra nbs 8 1 —

Prose Books (stored in prose_chapters collection)

Book Code Chapters Source
Kṛṣṇa Book kb 89 (2 volumes) other.html
The Nectar of Devotion nod 51 (4 parts) other.html
Teachings of Lord Caitanya tlc 32 (2 parts) other.html (1968 ed., plain English)
Easy Journey to Other Planets ej 2 other.html
Elevation to Kṛṣṇa Consciousness ek 6 other.html
Kṛṣṇa Consciousness: The Matchless Gift mg 7 other.html
Beyond Birth and Death bbd 5 other.html
On the Way to Kṛṣṇa owk 5 other.html
Rāja-vidyā: The King of Knowledge rv 8 other.html
The Perfection of Yoga poy 11 other.html
Perfect Questions Perfect Answers pqpa 9 other.html
Transcendental Teachings of Prahlāda Mahārāja ttp 5 other.html
On Chanting the Hare Kṛṣṇa Mantra ochkm 4 other.html
The Kṛṣṇa Consciousness Handbook hkh 19 other.html
The Science of Self-Realization ssr 50 sub-chapters (8 parents) other.html
Dialectic Spiritualism ds 36 sub-chapters (13 parents) other.html
Life Comes from Life lcl 16 other.html
Light of the Bhāgavata lob 48 other.html
Teachings of Lord Kapila tlk 18 other.html
Teachings of Queen Kuntī tqk 26 other.html
Gītār Gān gg 18 other.html (Bengali poetic BG)
Songs of the Vaiṣṇava Ācāryas sva 67 songs (4 parts, hierarchical) other.html
Message of Godhead mog 3 others-before-1977.html
Śrī Caitanya-bhāgavata Commentary cbhc 4 others-before-1977.html
Bhagavāner Kathā bk 2 others-before-1977.html (ScaGoudy)
Prayers of King Kulaśekhara (1958) pk 6 others-before-1977.html
Vaiśiṣṭyāṣṭakam va 8 others-before-1977.html

Essays (stored in essays collection)

Essay Slug Group Year
Krishna The Reservoir Of Pleasure krishna-reservoir-of-pleasure two-essays 1966
Who Is Crazy? who-is-crazy two-essays 1966
The Spiritual Masters the-spiritual-masters cc-essays 1960
The Absolute Truth the-absolute-truth cc-essays 1957
The Causes of Lord Caitanya's Incarnation causes-of-lord-caitanyas-incarnation cc-essays 1957
The Confidential Reasons for Lord Caitanya's Appearance confidential-reasons-for-lord-caitanyas-appearance cc-essays 1957
The Glories of Lord Nityānanda glories-of-lord-nityananda cc-essays 1957
Man Is the Architect of His Own Fortune man-is-the-architect-of-his-own-fortune pre-1977-essays —
What Is the Matter with the World? what-is-the-matter-with-the-world pre-1977-essays 1957
Religion Is Godly Life religion-is-godly-life pre-1977-essays —
Truth and Beauty truth-and-beauty pre-1977-essays 1958
Misuse of Public Fund in the Name of Religion misuse-of-public-fund-in-the-name-of-religion pre-1977-essays 1958
Perfection at Home perfection-at-home pre-1977-essays —
How Should One Cultivate Human Spirit how-should-one-cultivate-human-spirit pre-1977-essays 1961
Viswa Dharma viswa-dharma pre-1977-essays —
Glorification of Śrīla Bhaktisiddhānta Sarasvatī Ṭhākura glorification-of-srila-bhaktisiddhanta-sarasvati-thakura pre-1977-essays 1952
Who Is Crazy? (Back to Godhead #66) who-is-crazy-btg66 pre-1977-essays —
Manifestation of Dharma in Human Being manifestation-of-dharma-in-human-being pre-1977-essays 1972
Message of Krishna Consciousness message-of-krishna-consciousness pre-1977-essays 1972
A Wrong Civilization a-wrong-civilization pre-1977-essays 1936
Vedānta-sūtra (Typed Transcript) vedanta-sutra-typed typed-transcripts 1969
Shaktipujah shaktipujah reviews —
Devotee and the Divinity devotee-and-the-divinity standalone —

Other Documents (stored in other_document_meta + other_document_chapters collections)

Document Code Chapters
An Analysis of Bhagwat Geeta analysis-bg 3
Solution of Present Crisis by Bhagwat Geeta solution-crisis 4
Śrī Caitanya Mahāprabhu sri-caitanya 3
Conception of Geeta Nagari on the Principles of Gandhi geeta-nagari 1
The Real Peace Formula real-peace-formula 3
International Institution for God Consciousness intl-institution-god-consciousness 1
"Srimad Bhawatam" India's Message of Peace and Goodwill srimad-bhawatam-message 1
Viraha Aṣṭaka viraha-astaka 8
Vṛndāvana Bhajana vrndavana-bhajana 1
Prayer unto the Lotus Feet of Kṛṣṇa prayer-unto-lotus-feet-of-krsna 1
Märkine Bhägavata-dharma markine-bhagavata-dharma 1
Jaladuta Pamphlet jaladuta-pamphlet 1

Journals (stored in journal_meta + journal_chapters collections)

Journal Code Chapters Structure
The Jaladuta Diary, 1965 jaladuta-diary 85 Preface, Introduction, 83 day entries grouped by month (Apr–Nov 1965)
The Beginning — The 1966 New York Journal the-beginning 215 Introduction, 3 place-based chapters with day entries, Memoranda, Epilogue, 20 Excerpts

Magazines (stored in magazine_meta + magazine_issues collections)

Magazine Code Issues Articles Flag Source
Back to Godhead (1944–1960) btg 29 89 Pre 1978 btg-1960.html
Back to Godhead (1966–1973) btg-1978 30 1,035 Pre 1978 btg-1978.html

Letters (stored in letters collection)

Metric Value
Total letters 6,866
Unique recipients (individual) 2,243
Unique locations 164
Year range 1947–1977
Total paragraphs 52,261

Top recipients: Satsvarupa (182), Brahmananda (168), Hansadutta (117), Tamala Krsna (115), Gurudasa (103), Mukunda (105), Madhudvisa (90), Karandhara (90)

Top locations: Los Angeles (1,392), Bombay (1,086), Vrindaban (532), Mayapur (305), London (264), Calcutta (250), Honolulu (239)

Schema notes:

  • recipients is an array of individual names (e.g., ["Mukunda", "Janaki"])
  • recipientDisplay is the original combined string (e.g., "Mukunda, Janaki")
  • Searching for a recipient uses Prisma { has: name } to match any letter where that person appears
  • Multi-recipient groups are split by ,, ;, and, &
  • Trailing punctuation (:, ,, .) cleaned from names

Other

Content Collection
Glossary (2,749 entries) glossary

Font Encoding System

ScaGoudy → IAST mapping (used in BG, SB, CC, ISO, NOI, NoD, and most 1969+ books)

ä→ā  Ä→Ā  å→ṛ  Å→Ṛ  ñ→ṣ  Ñ→Ṣ  ö→ṭ  Ö→Ṭ  ë→ṇ  Ë→Ṇ  ò→ḍ  Ò→Ḍ
ç→ś  Ç→Ś  ì→ṅ  ï→ñ  ü→ū  Ü→Ū  é→ī  É→Ī  à→ṁ  ù→ḥ  è→ṝ  û→ṁl  ÿ→ḷ

Bengali fonts (Inbenr/Inbeni in CC)

Custom-encoded fonts where ASCII codepoints render as Bengali script. Fixed by fix-bengali-encoding.py.

RM Devanagari font (used in ISO, NOI)

Custom-encoded Devanagari font (p51 CSS class). The text appears as garbled ASCII without the font. Skipped during extraction — Unicode Devanagari is generated from IAST transliteration instead.

Devanagari generation

IAST transliteration → Devanagari via indic-transliteration library. Key fixes:

  • join_virama_words() — joins virāma + consonant AND virāma + vowel
  • normalize_devanagari_anusvara() — ङ् → ं before velars, ञ् → ं before palatals

Books without ScaGoudy encoding

  • TLC (1968 Edition) — plain English transliterations ("Krishna" not "Kṛṣṇa")
  • Two Essays (1966) — plain English
  • GG (Gītār Gān) — Bengali poetic text (ScaGoudy decoded for transliteration)
  • LCL (Life Comes from Life) — dialogue book, ScaGoudy decoded
  • MOG, CBHC, PK — pre-1977 handwritten documents, plain English
  • BK (Bhagavāner Kathā) — pre-1977, uses ScaGoudy encoding
  • CC Essays, Vedānta-sūtra transcript — plain English
  • All other books from other.html use ScaGoudy encoding

Book Types and URL Patterns

Verse-based books (use ChapterViewer client component)

Route Pattern Books
/books/bg/[chapter]/[verse] BG
/books/sb/[canto]/[chapter]/[verse] SB
/books/cc/[lila]/[chapter]/[verse] CC
/books/sb1962/[chapter]/[verse] SB1962
/books/iso/[mantra] Īśopaniṣad (verse + intro segments)
/books/noi/[text] Nectar of Instruction (verse + intro segments)
/books/bs/[chapter]/[verse] Brahma-saṁhitā (chapter 5, 62 verses)
/books/mm/[chapter]/[verse] Mukunda-mālā-stotra (6 mantras)
/books/nbs/[chapter]/[verse] Nārada-bhakti-sūtra (8 sūtras)

Prose books (use per-book data layers + reading pages)

Route Pattern Books
/books/kb/[volume]/[slug] Kṛṣṇa Book
/books/{code}/[chapter] NoD, TLC, EJ, EK, MG, BBD, OWK, RV, POY, PQPA, TTP, OCHKM, HKH, LCL, LOB, TLK, TQK, GG, MOG, CBHC, BK, PK

Hierarchical prose books (sub-chapters as separate pages)

Route Pattern Books
/books/ssr/[chapter] Science of Self-Realization (slugs: "1-1", "1-2", "intro-foreword", etc.)
/books/ds/[chapter] Dialectic Spiritualism (slugs: "1-1" = Socrates, "1-2" = Plato, etc.)
/books/sva/[chapter] Songs of the Vaiṣṇava Ācāryas (slugs: "1-1" = Śrī Guru Praṇāma, etc.)

Essays

Route Content
/essays Searchable essay grid (23 essays across 6 groups)
/essays/[slug] Individual essay reader

Other Documents

Route Content
/other-documents Searchable other documents grid (12 documents)
/other-documents/[slug] Document index (chapter listing)
/other-documents/[slug]/[chapter] Individual chapter reader

Journals

Route Content
/journals Journals index (grid)
/journals/[slug] Journal chapter listing (hierarchical: place → month → day)
/journals/[slug]/[chapter] Individual journal entry reader (slug-based, e.g., apr-3, sep-17, excerpt-...)

Magazines

Route Content
/magazines Magazines index (grouped by flag: "Pre 1978 Editions")
/magazines/[slug] Magazine issue listing (grouped by volume)
/magazines/[slug]/[issue] Individual issue reader (slug-based, e.g., v1-p1, no-17)

Letters

Route Content
/letters Letters index with 3-way navigation tabs
/letters?view=year Year grid (1947–1977) with search
/letters?view=recipient Recipient list (2,243 individuals) with search + pagination
/letters?view=location Location grid (164 locations) with search + pagination
/letters/year/1968 All letters from a specific year
/letters/recipient/Mukunda All letters to/including a specific recipient
/letters/location/Los%20Angeles All letters from a specific location
/letters/[slug] Individual letter reader with prev/next navigation

Other routes

Route Content
/ Landing page (responsive 3-column grid)
/books Searchable book grid
/glossary Searchable glossary (2,749 entries)
/journals Journals (2 journals, 300 chapters)
/magazines Magazines (2 magazines, 59 issues, Pre 1978)
/letters Letters (6,866 letters, 3-way navigation)
/rtfs RTF viewer (gated by ALLOW_RTF=true)
/api/verify-access Password protection endpoint

Volume Convention

Books without volumes use volume: 0 as a sentinel value in the prose_chapters collection. This satisfies the Prisma schema's @@unique([book, volume, slug]) constraint without requiring schema changes.

  • KB uses volume: 1 and volume: 2
  • All other prose books use volume: 0

Hierarchical Books (SSR, DS)

SSR and DS have parent chapters with sub-chapters stored as individual prose_chapters docs:

  • Sub-chapter slugs: "1-1", "1-2", "2-1", etc.
  • Parent chapter info stored in intro.json metadata:
    {
      "chapters": [
        {
          "number": 1,
          "title": "Learning the Science of the Soul",
          "subChapters": [
            { "slug": "1-1", "title": "Seeing the Aim of Human Life" },
            { "slug": "1-2", "title": "Your Original Consciousness Is Kṛṣṇa Consciousness" }
          ]
        }
      ]
    }
    
  • Sidebar UI shows parent headings (non-clickable) with sub-chapters indented below
  • Prev/next navigation skips parent headings

UI Features

Reading Settings (accessibility)

  • Theme: Light / Dark / System toggle
  • Serif Font: Toggle to switch reading content to Georgia/serif
  • Font Size: Slider (XS → XXL) using CSS zoom on main and aside
  • Justify Text: Toggle for justified text alignment on reading content
  • Settings persist in localStorage under spsays_reading_prefs
  • Applied via CSS classes on <html> element

Mobile Navigation

  • Left-slide drawer (vaul) triggered by a menu button next to the Previous button
  • Only visible below lg breakpoint
  • Shows the full sidebar navigation grouped by section
  • Implemented in all 24+ reader pages via shared MobileNav component
  • Books page: diacritic-aware search (typing "Krsna" matches "Kṛṣṇa")
  • Glossary page: full-text search across terms and definitions
  • Uses Unicode NFD normalization + manual IAST character stripping

Color Palette

  • Light mode ("Aged Parchment"): Warm cream backgrounds, deep sepia text, saffron/ochre primary
  • Dark mode ("Ancient Manuscript"): Deep warm brown backgrounds, parchment text, golden saffron primary

Text Selection Toolbar

  • Global floating toolbar appears on text selection across all reading pages (except RTFs)
  • Desktop: floating pill above selection; Mobile: fixed bottom bar
  • Actions: Ask Kautilya (Phase II), Share, Copy (with source reference), More
  • Share: modal (desktop) / bottom drawer (mobile) — share to feed, copy highlight link
  • More: popover (desktop) / bottom drawer (mobile) — bookmark, save to collection, copy link, mention
  • Copy appends source reference: title, chapter/verse, highlight URL
  • All toolbar surfaces use dark contrast treatment (bg-black, border-white/10)
  • Kautilya lotus icon for the AI companion button
  • On iOS Safari mobile, selection is saved/restored when drawers open/close to prevent focus conflicts
  • Selected text encoded as compact URL fragment: ?hl=prefix§suffix§length
  • Uses § delimiter (safe for Sanskrit, English, and all Vedic text)
  • Short selections (≤8 words) use full text as prefix
  • Incoming ?hl= URLs: text is found in DOM, wrapped in <mark>, scrolled to, highlighted permanently
  • TextHighlighter component runs on page load via useSearchParams
  • Server-side extractMatchedText() searches paragraphs for OG image quote extraction
  • Case-insensitive matching for both DOM highlighting and server-side text extraction
  • Cross-paragraph matching via position mapping (spaces inserted at element boundaries)

Dynamic OG Images

  • /api/og route generates 1200×630 PNG images using next/og (Satori)
  • Left panel: Prabhupāda image with quote mark and branding (from PNG)
  • Right panel: title (bold), detail, quote text, "Read more at prabhupadasays.org"
  • Font: Noto Sans Regular + Bold loaded from public/fonts/Noto_Sans/static/ — full IAST diacritical support
  • All 104 content pages have generateMetadata with contextual fallback descriptions
  • When ?hl= is present, the actual selected text is extracted from DB content for the OG quote
  • Global footer on every page with © year, About link, Home link

Consistent Container Width

  • All pages use max-w-5xl for both header and body containers

Environment Variables

DATABASE_URL=mongodb://user:pass@host:port/spsays?authSource=admin
NEXT_PUBLIC_SITE_URL=https://prabhupadasays.org

How to Run

# Development (from knowledgebase/)
npm run dev

# Prisma
npx prisma generate                  # Regenerate Prisma client after schema changes

# Build
npx next build

# Extraction & seeding scripts are in extractor/ — see extractor/scripts/

# BFF API server (from ../spsays-bff/)
make run                             # Build + start Go API on :8080

Verse JSON Schema (BG/SB/CC/SB1962/ISO/NOI)

{
  "book_title": "Śrīmad-Bhāgavatam",
  "canto": 1,
  "chapter": 1,
  "verse": 1,
  "verse_end": null,
  "reference": "SB 1.1.1",
  "devanagari": "ॐ नमो भगवते वासुदेवाय\nजन्माद्यस्य यतो ...",
  "bengali": null,
  "transliteration": "om namo bhagavate vāsudevāya\njanmādy asya yato ...",
  "synonyms": [
    { "word": "om", "meanings": ["O my Lord"] },
    { "word": "namaḥ", "meanings": ["offering my obeisances"] }
  ],
  "translation": "<b>I offer my obeisances unto Lord Śrī Kṛṣṇa...</b>",
  "purport": ["paragraph 1...", "paragraph 2..."]
}

Prose Chapter JSON Schema

{
  "book": "ek",
  "chapter": 1,
  "title": "Choosing Human and Animal Lives",
  "type": "chapter",
  "paragraphs": ["<b>heading</b>", "body text...", "<div style=\"text-align:center\"><i>verse</i></div>"]
}

Intro Segment JSON Schema

{
  "slug": "preface",
  "title": "Preface",
  "type": "intro-segment",
  "book": "ek",
  "order": 2,
  "paragraphs": ["paragraph 1...", "paragraph 2..."]
}

Essay JSON Schema

{
  "slug": "krishna-reservoir-of-pleasure",
  "title": "Krishna The Reservoir Of Pleasure",
  "author": "His Divine Grace A.C. Bhaktivedanta Swami Prabhupāda",
  "year": 1966,
  "group": "two-essays",
  "order": 1,
  "paragraphs": ["paragraph 1...", "paragraph 2..."]
}

Glossary JSON Schema

{
  "title": "Glossary of Vedic Terms",
  "description": "A comprehensive glossary...",
  "totalEntries": 2749,
  "letters": ["A", "B", "C", ...],
  "entries": [
    {
      "term": "Ācārya",
      "definition": "a spiritual master who teaches by his own example...",
      "definition_html": "a spiritual master who teaches by his own example..."
    }
  ]
}

Glossary is browsed via /glossary/{letter} routes (A-Z). Each letter page shows terms grouped by base letter (diacritical variants mapped: Ā→A, Ś→S, Ṛ→R, etc.). Single entry view with prev/next navigation.


Known Issues

  1. MongoDB BSON wire corruption: The MongoDB server occasionally corrupts large BSON documents. Workaround: seed using insertOne + $push for paragraphs one at a time.

  2. SB verse numbering: 5 duplicate verse reference tags in the HTML. Fixed by using the TEXT N header as authoritative.

  3. RTF routes gated: /rtfs only accessible when ALLOW_RTF=true.

  4. scripts/ excluded from tsconfig: Prevents TypeScript build errors from seed scripts.

  5. CSS zoom for font scaling: zoom property works in Chrome/Safari/Edge but may not work in older Firefox (< 126). A transform: scale() fallback is provided via @supports not (zoom: 1.1).

  6. Password protection requires ACCESS_KEY at runtime: The PasswordGate component checks the server at runtime via /api/verify-access. The env var must be available when the server is running, not just at build time.