2026-06-01 01:22:09 +00:00
|
|
|
# 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/](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](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.
|
2026-06-03 02:22:04 +00:00
|
|
|
- **`VerseSelection.ts`** — pure value object: a verse set within one book →
|
|
|
|
|
contiguous `BibleReference[]` runs + a display label (`Genesis 1:2-3, 5`).
|
|
|
|
|
- **`VerseSelectionService.ts`** — plugin-owned (`addChild`) holder of the *single*
|
|
|
|
|
active verse selection across panes + its owning controller; notifies subscribers.
|
2026-06-01 01:22:09 +00:00
|
|
|
|
|
|
|
|
### `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](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.
|
2026-06-03 02:22:04 +00:00
|
|
|
- **`VerseWrapper.ts`** — `wrapPassageVerses(passageEl)`: wraps each rendered verse in a
|
|
|
|
|
`.dj-verse` span (idempotent) so verses are selectable/highlightable.
|
|
|
|
|
- **`VerseSelectionController.ts`** — one per rendered passage: wraps verses, binds
|
|
|
|
|
gestures (desktop tap/shift, mobile long-press-drag), reflects the service's selection
|
|
|
|
|
(highlight) and owns the action bar.
|
|
|
|
|
- **`VerseActionBar.ts`** — floating bar shown while verses are selected; renders the
|
|
|
|
|
Copy / Insert (and optional Append) actions with the configurable `split`/`toggle`/
|
|
|
|
|
`submenu` format chooser.
|
|
|
|
|
- **`VerseActions.ts`** — turns a selection + format into the payload and performs Copy
|
|
|
|
|
(clipboard), Insert (Editor API), or Append (`Vault.process`); extracts verse text for
|
|
|
|
|
the blockquote format from the passage's own document.
|
|
|
|
|
- **`InsertTargetModal.ts`** — `FuzzySuggestModal<TFile>` note picker for "Append to note…".
|
2026-06-01 01:22:09 +00:00
|
|
|
|
|
|
|
|
### `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.
|
2026-06-03 02:22:04 +00:00
|
|
|
- **`VerseId.ts`** — `parseVerseId(id)`: ESV marker id (`v01001002-1`) → `{chapter, verse}`.
|
|
|
|
|
- **`VerseFormatter.ts`** — pure builders for the three insert formats (inline reference,
|
|
|
|
|
`bible` code block, blockquote-with-text + citation).
|
2026-06-01 01:22:09 +00:00
|
|
|
- **`FrontmatterUtil.ts`** — apply user-configured custom frontmatter to Bible
|
|
|
|
|
notes (`getCustomFrontmatterForReference`, `applyCustomFrontmatter`).
|
|
|
|
|
|
|
|
|
|
### `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
|
2026-06-03 02:22:04 +00:00
|
|
|
the whole passage inline (`processFullBiblePassage`) with navigation + heading. Non-
|
|
|
|
|
contiguous references (`Genesis 1:2-3, 5`) resolve via `getBibleContentList`, which parses
|
|
|
|
|
the list with `BibleReference.parseList` and concatenates each run's HTML.
|
|
|
|
|
|
|
|
|
|
## Verse selection
|
|
|
|
|
|
|
|
|
|
After a full passage renders, `processFullBiblePassage` wraps its verses
|
|
|
|
|
(`wrapPassageVerses` → `.dj-verse` spans) and attaches a `VerseSelectionController`.
|
|
|
|
|
Selecting verses (tap/shift on desktop, long-press-drag on mobile) updates a per-passage
|
|
|
|
|
`VerseSelection` and pushes it to the plugin-owned `VerseSelectionService`, which holds the
|
|
|
|
|
single active selection. The owning controller highlights its verses and shows a
|
|
|
|
|
`VerseActionBar`; Copy / Insert / Append (and the editor `Insert … here` menu + commands)
|
|
|
|
|
run through `VerseActions`, formatting via `VerseFormatter`. See
|
|
|
|
|
[docs/gotchas.md](docs/gotchas.md) for the wrapping/gesture/bar-lifecycle details.
|
2026-06-01 01:22:09 +00:00
|
|
|
|
|
|
|
|
## 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](docs/esv-api.md).
|
|
|
|
|
|
2026-06-03 00:11:38 +00:00
|
|
|
## Tests
|
|
|
|
|
|
|
|
|
|
`test/` holds the test suites (Node's built-in `node:test` runner via `tsx`), one
|
2026-06-03 03:15:56 +00:00
|
|
|
`*.test.ts` per unit — currently `BibleReference`, `BookNames`, `VerseFormatter`,
|
|
|
|
|
`VerseId`, and `VerseSelection`. Run with `npm test` (or `devbox run test`); they
|
|
|
|
|
also gate `npm run build` and `just release`. See [docs/testing.md](docs/testing.md).
|
2026-06-03 00:11:38 +00:00
|
|
|
|
2026-06-01 01:22:09 +00:00
|
|
|
## Build
|
|
|
|
|
|
|
|
|
|
esbuild bundles `main.ts` → `main.js` (CJS, `es2018`); `obsidian`, `electron`, and
|
2026-06-03 00:11:38 +00:00
|
|
|
CodeMirror packages are externalized. `npm run build` type-checks and runs tests
|
|
|
|
|
first. See [docs/build-and-release.md](docs/build-and-release.md).
|