4.2 KiB
Gotchas & funky logic
Non-obvious behavior that would cost the next agent grep-time or cause a wrong
"fix." API-specific quirks (the ESV 1-999 single-chapter workaround, the
frontmatter-as-cache model, :→v filename encoding) live in
esv-api.md; this file covers the cross-cutting runtime ones.
Hover-listener lifecycle is Component-owned (don't re-add global listeners)
BibleEventHandlers is a single, long-lived instance added via
this.addChild(...) in DisciplesJournalPlugin.onload. That registration is what
loads its document listeners/timer and, crucially, tears them down on plugin
unload. A previous version leaked global document listeners; do not reintroduce
per-render or per-reference global listeners — route hover behavior through this one
handler. (DisciplesJournalPlugin.ts:60-65, BibleEventHandlers.ts.)
Pop-out windows: styles don't fully carry over (known-incomplete)
Each Obsidian pop-out window is a separate Document, so updateBibleStyles
iterates all leaves and applies styles per-document. A code comment flags that this
still doesn't fully work — a freshly created pop-out doesn't get the style
vars ported over. If you touch styling and a pop-out looks unstyled, this is why,
not your change. (DisciplesJournalPlugin.ts:131-158.)
The passage cache is keyed by object identity — it effectively never hits
BibleContentService.passageCache is a Map<BibleReference, BiblePassage>. It's
set with the canonical passage.reference from an API response and looked
up with the inbound (freshly parsed) ref. Two equal-but-distinct
BibleReference objects are different map keys, so cross-call lookups essentially
always miss; the real cache is the saved note on disk. Harmless today, but if you
ever rely on this in-memory cache, key it by ref.toString() instead.
(BibleContentService.ts:14,72-80.)
scrollToVerse watches the DOM instead of guessing a delay
After openFile, a note body renders asynchronously, so BibleFiles.scrollToVerse
uses a MutationObserver to scroll the instant the .verse-N element appears,
with a 5000 ms fallback that disconnects the observer if it never does (wrong
ref, or an edit mode that produces no verse elements). Don't replace this with a
fixed setTimeout. (BibleFiles.ts:118-145.)
Verse selection: wrapping, gestures, and bar lifecycle
Verse selection is layered onto rendered passages by VerseSelectionController (one per
full bible passage, attached in BibleReferenceRenderer.processFullBiblePassage). A few
non-obvious things:
- Verses aren't pre-wrapped. ESV HTML marks a verse only with a
<b class="verse-num|chapter-num" id="vBBCCCVVV-N">; the verse's text then runs as loose inline nodes until the next marker, sometimes across<p>boundaries.wrapPassageVerses(VerseWrapper.ts) walks each block and wraps each verse's run in a<span class="dj-verse" data-chapter data-verse>. It's idempotent (bails if a.dj-versealready exists) and a verse split across paragraphs yields two spans sharing the samedata-verse— both highlight together.parseVerseIdignores the book digits; the book comes from the passage's canonical reference. - Mobile selection vs. scroll. Touch uses a long-press threshold (
LONG_PRESS_MS): a quick swipe scrolls normally; only a deliberate long-press engages drag-select (which then callspreventDefaultontouchmove). A short tap toggles a single verse. Don't remove the threshold or you'll hijack scrolling. - One selection, globally.
VerseSelectionService(plugin-owned,addChild) holds a single active selection + its owning controller. Only the owner renders highlight and the action bar, so selecting in one passage clears another. - The action bar is per-
Documentandfixed-positioned. It's appended to the passage's owndoc.body(popout-safe) and positioned from the passage'sgetBoundingClientRect. Blockquote text is extracted from the owner'ssourceEl(its own document) for the same reason — never query the globaldocument. - Selection clears when its passage unloads.
controller.onunloadcallsservice.clearIfOwner(this), so a note re-render or pane close drops a stale selection and its bar.