6.8 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:
- Inline references — an inline code span like
`John 3:16`becomes a styled span with a hover popup showing the verse text. - Full passages — a fenced
```biblecode block renders the whole passage inline, with navigation and a clickable heading. - 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.tsis a thin shim that re-exports the real plugin class.src/core/DisciplesJournalPlugin.ts(DisciplesJournalPlugin extends Plugin) is the composition root.onload():- loads settings (
loadSettingsmerges saved data overDEFAULT_SETTINGS), - constructs the services and components (wiring described below),
- registers the markdown processors, the Live Preview editor extension, the
Open Bible+Update frontmattercommands, a ribbon icon, the settings tab, and anactive-leaf-changehandler that re-applies styles.
- loads settings (
- 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— thePluginsubclass / 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 viaBookNamesand 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 aBibleApiResponse, trying in order: in-memory cache → local note frontmatter → ESV API download (only ifdownloadOnDemand).ESVApiService.ts— talks toapi.esv.orgvia Obsidian'srequestUrl. Converts raw API responses toBibleApiResponse(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.mdfor chapters,Book NvV[-E].mdfor 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 viasanitizeHTMLToDom, never rawinnerHTML.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 ownDocument, so styles are applied per-doc).BookSuggest.ts,OpenBibleModal.ts— theOpen Biblemodal and its book autocomplete.
utils/ — small helpers
BibleApiResponse.ts— result type wrapping either aBiblePassageor an error with anErrorType(auth, bad response, forbidden, …).BiblePassage.ts—{ reference, html }pairing for resolved content.BibleApiResponse/BiblePassageare 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 thebiblecode block.
settings/
DisciplesJournalSettings.ts— the settings interface,DEFAULT_SETTINGS, and theDisciplesJournalSettingsTabUI (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.
Build
esbuild bundles main.ts → main.js (CJS, es2018); obsidian, electron, and
CodeMirror packages are externalized. npm run build type-checks first. See
docs/build-and-release.md.