scotttomaszewski_obsidian-d.../docs/gotchas.md

83 lines
5.2 KiB
Markdown
Raw Normal View History

2026-06-01 01:22:09 +00:00
# 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](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
2026-06-03 03:15:56 +00:00
handler. (`DisciplesJournalPlugin.ts:73-77`, `BibleEventHandlers.ts`.)
2026-06-01 01:22:09 +00:00
## 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,
2026-06-03 03:15:56 +00:00
not your change. (`DisciplesJournalPlugin.ts:219-246`.)
2026-06-01 01:22:09 +00:00
## 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.
2026-06-03 03:15:56 +00:00
(`BibleContentService.ts:14,97-103`.)
2026-06-01 01:22:09 +00:00
## 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-verse` already exists) and a verse split across paragraphs yields two spans sharing
the same `data-verse` — both highlight together. `parseVerseId` ignores 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 calls `preventDefault` on `touchmove`). 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-`Document` and bottom-center docked.** It's appended to the
passage's own `doc.body` (popout-safe). It's docked at the bottom-center of the viewport
via CSS — **not** anchored to the passage — because a full chapter is taller than the
viewport, so anchoring to the passage's `getBoundingClientRect().bottom` rendered the bar
off-screen. Blockquote text is extracted from the owner's `sourceEl` (its own document) so
popouts stay correct — never query the global `document`.
- **Selection clears when its passage unloads.** `controller.onunload` calls
`service.clearIfOwner(this)`, so a note re-render or pane close drops a stale selection
and its bar.
- **"Insert" doesn't target the active pane.** When you click a verse, the active pane
*is* the passage's pane — usually a generated note under the Bible content folder, which
is the last place you want the verse inserted. So the plugin tracks the last active
**editable, non-generated** markdown note (`DisciplesJournalPlugin.handleActiveLeafChange`
`lastEditableLeaf`, filtered by `isBibleContentFile`) and `resolveInsertTarget()`
returns it. There's no public "leaves in MRU order" API — this `active-leaf-change`
tracking is the stand-in. Insert does **not** steal focus; it drops the text in and shows
a `Passage … inserted into note …` notice (important when the target pane is in the
background). The right-click "Insert here" path is unaffected — it always uses the clicked
editor.