scotttomaszewski_obsidian-d.../ARCHITECTURE.md
2026-06-02 20:11:38 -04:00

7.1 KiB

Architecture

How Disciples Journal is put together. This is the orientation doc — read it before making structural changes. For user-facing features see README.md; for narrow topics see docs/.

Big picture

The plugin hooks into Obsidian's markdown pipeline in three places and turns Bible references into rich content:

  1. Inline references — an inline code span like `John 3:16` becomes a styled span with a hover popup showing the verse text.
  2. Full passages — a fenced ```bible code block renders the whole passage inline, with navigation and a clickable heading.
  3. Chapter notes — clicking a reference opens (or creates) a markdown note for that chapter under the configured Bible content folder.

Content is resolved from local notes first, then downloaded from the ESV API on demand (when enabled and a token is configured). Downloaded passages are persisted as markdown notes whose frontmatter holds the raw API response, so they double as the cache and the source of truth.

Entry point and lifecycle

  • main.ts is a thin shim that re-exports the real plugin class.
  • src/core/DisciplesJournalPlugin.ts (DisciplesJournalPlugin extends Plugin) is the composition root. onload():
    • loads settings (loadSettings merges saved data over DEFAULT_SETTINGS),
    • constructs the services and components (wiring described below),
    • registers the markdown processors, the Live Preview editor extension, the Open Bible + Update frontmatter commands, a ribbon icon, the settings tab, and an active-leaf-change handler that re-applies styles.
  • The hover-preview event handler is added via addChild(...) so its global document listeners/timers are unloaded with the plugin (avoids leaks).

Layers

Source is organized under src/ by responsibility:

core/ — plugin wiring and domain model

  • DisciplesJournalPlugin.ts — the Plugin subclass / composition root (settings, command registration, style refresh, openChapterNote, updateAllBibleNoteFrontmatter).
  • BibleReference.ts — the central value object: { book, chapter, verse?, endVerse?, endChapter? }. BibleReference.parse(str) understands the supported formats; the constructor normalizes the book name via BookNames and throws on an unknown book. See docs/reference-formats.md.
  • BibleMarkupProcessor.ts — the two registered markdown callbacks (processBibleCodeBlock, processInlineBibleReferences). It checks the relevant display setting, then delegates to the renderer and handles errors.
  • BibleEventHandlers.ts — the single, plugin-owned hover/popup lifecycle (mouseover/mouseout, show/hide timing). Loaded as a child component.

services/ — content resolution and storage

  • BibleContentService.ts — the resolution funnel. getBibleContent(ref) returns a BibleApiResponse, trying in order: in-memory cache → local note frontmatter → ESV API download (only if downloadOnDemand).
  • ESVApiService.ts — talks to api.esv.org via Obsidian's requestUrl. Converts raw API responses to BibleApiResponse (toBibleApiResponse, also reused when re-reading a saved note) and persists responses as markdown notes (saveESVApiResponseAsMdNote). Details in docs/esv-api.md.
  • BibleFiles.ts — path/file resolution for chapter & passage notes (static helpers) plus opening/creating notes and scroll-to-verse behavior (instance methods). Filenames: Book N.md for chapters, Book NvV[-E].md for verse ranges, under <bibleContentVaultPath>/<version>/<Book>/.
  • BookNames.ts — canonical book-name normalization and per-book chapter counts (used e.g. to work around an ESV API single-chapter-book quirk).

components/ — rendering and UI

  • BibleReferenceRenderer.ts — builds the DOM for inline references (processInlineCodeBlocks), full passages (processFullBiblePassage), and the hover popup (showVersePreview). HTML from the API is inserted via sanitizeHTMLToDom, never raw innerHTML.
  • BibleReferenceInlineExtension.ts — CodeMirror editor extension that makes references work in Live Preview (edit mode), not just reading mode.
  • BibleNavigation.ts — prev/next chapter navigation elements + navigation.
  • BibleStyles.ts — injects/refreshes themed CSS per document (each popout window has its own Document, so styles are applied per-doc).
  • BookSuggest.ts, OpenBibleModal.ts — the Open Bible modal and its book autocomplete.

utils/ — small helpers

  • BibleApiResponse.ts — result type wrapping either a BiblePassage or an error with an ErrorType (auth, bad response, forbidden, …).
  • BiblePassage.ts{ reference, html } pairing for resolved content.
  • BibleApiResponse/BiblePassage are what flow back out of the content service to the renderer.
  • FrontmatterUtil.ts — apply user-configured custom frontmatter to Bible notes (getCustomFrontmatterForReference, applyCustomFrontmatter).
  • BibleCodeblockFormatter.ts — formatting helper for the bible code block.

settings/

  • DisciplesJournalSettings.ts — the settings interface, DEFAULT_SETTINGS, and the DisciplesJournalSettingsTab UI (display toggles, fonts/colors, content vault path, ESV token + download-on-demand).

Data flow: rendering an inline reference

note renders
  → BibleMarkupProcessor.processInlineBibleReferences   (registered post-processor)
    → BibleReferenceRenderer.processInlineCodeBlocks
      → BibleReference.parse(text)
      → BibleContentService.getBibleContent(ref)
          ├─ in-memory passageCache?            → return
          ├─ local note exists? read frontmatter→ ESVApiService.toBibleApiResponse
          └─ downloadOnDemand? ESV API download → save note + cache
      → build span; on hover → BibleEventHandlers → showVersePreview popup

Full passages follow the same resolution path through getBibleContent, but render the whole passage inline (processFullBiblePassage) with navigation + heading.

Storage model

Downloaded passages are markdown notes whose frontmatter holds the raw ESV API JSON (query, canonical, parsed, passage_meta, passages) plus any custom frontmatter the user configured. The note body is a bible code block of the canonical reference. Because the frontmatter is the cache, re-opening a chapter reuses toBibleApiResponse on the stored data instead of re-hitting the API. See docs/esv-api.md.

Tests

test/ holds the test suites (Node's built-in node:test runner via tsx), one *.test.ts per unit — currently test/BibleReference.test.ts. Run with npm test (or devbox run test); they also gate npm run build and just release. See docs/testing.md.

Build

esbuild bundles main.tsmain.js (CJS, es2018); obsidian, electron, and CodeMirror packages are externalized. npm run build type-checks and runs tests first. See docs/build-and-release.md.