# More than a
Simple Bible Fetcher
An [Obsidian](https://obsidian.md) plugin that inserts Bible passages from the
free [bolls.life](https://bolls.life) API as Markdown quotes, and then goes
further: read, annotate and revisit, with colour-coded notes on any verse.
Type a reference, pick a translation, and the passage is inserted at the cursor
as a blockquote, one verse per line with a bold verse number:
```markdown
> **John 3,16** (The Holy Bible, Berean Standard Bible)
>
> **16** For God so loved the world that He gave His one and only Son, that everyone who believes in Him shall not perish but have eternal life.
> **17** For God did not send His Son into the world to condemn the world, but to save the world through Him.
```
Several references at once, separated by `;`:
```markdown
> **Genesis 1,1** (The Holy Bible, Berean Standard Bible)
>
> **1** In the beginning God created the heavens and the earth.
> **Psalm 23,1** (The Holy Bible, Berean Standard Bible)
>
> **1** The LORD is my shepherd; I shall not want.
```
A chapter range is expanded into one section per chapter:
```markdown
> **Psalm 23** (The Holy Bible, Berean Standard Bible)
>
> **1** The LORD is my shepherd; I shall not want.
> **2** He makes me lie down in green pastures.
> **Psalm 24** (The Holy Bible, Berean Standard Bible)
>
> **1** The earth is the LORD's, and everything in it.
```
Prefer running prose instead of one line per verse? Switch the format:
```markdown
> **John 3,16** (The Holy Bible, Berean Standard Bible)
>
> **16** For God so loved the world that He gave His one and only Son ...
```
Want the heading to link back to bolls.life? Turn on the source link:
```markdown
> [**John 3,16** (The Holy Bible, Berean Standard Bible)](https://bolls.life/BSB/43/3/16/)
>
> **16** For God so loved the world that He gave His one and only Son ...
```
Annotate any verse and the command creates a note for your thoughts, embedded
under the quote as a colour-coded callout:
```markdown
> **16** For God so loved the world ...
> [!zettel-yellow]
> ![[Bible Notes/John 3,16 (The Holy Bible, Berean Standard Bible)]]
```
## Features
- Insert passages via a command / hotkey.
- Reference parsing with book names, common abbreviations and book numbers
(`John 3:16`, `1 Cor 13`, `Ps 23`, `43 3:16`, `Gen 1:1`).
- Multiple references at once, separated by `;` (`John 3:16; Psalm 23`).
- Chapter ranges (`Psalm 3-5`), inserted as one section per chapter.
- Footnotes from the source are rendered as Markdown footnotes.
- Three preset output formats, a custom template, and an optional source link.
- Keyword search across a translation, inserting the passage you pick.
- Annotate a verse: create a separate note for your thoughts and embed it
under the verse, as a callout with a colour chosen from a dropdown or a
palette, optionally with a date line, and as an embed or a plain link.
- Parallel translations: the main translation plus any number of additional
translations at once.
- Every language offered by bolls.life, with the translation list and
book-name matching following the chosen language.
- An offline cache: everything fetched once is reused, so it keeps working
without a connection.
## Supported languages
Every language available on bolls.life can be selected. The translation list
and book-name matching follow the chosen language, so you can type book names
as that translation writes them (for example `Juan`, `Selon Jean` or `João`).
Books can always be given by their number (1-66).
German and English additionally understand common abbreviations
(`Joh`, `1 Mos`, `Psalm`, `John`), independently of the selected translation.
## Usage
1. Open a note and place the cursor where the passage should go.
2. Run a command from the command palette (`Ctrl/Cmd+P`), or assign it a
hotkey under **Settings → Hotkeys**:
- **Simple Bible Fetcher: Insert Bible quote**: enter a reference.
- **Simple Bible Fetcher: Insert Bible quote (parallel translations)**:
inserts the reference in every translation listed under
**Parallel translations**.
- **Simple Bible Fetcher: Search Bible (keyword)**: search the selected
translation for a word or phrase and pick a hit to insert.
- **Simple Bible Fetcher: Annotate verse (create note)**: put the cursor on
an inserted verse, or select one or more verses, then name the note and
pick a colour; it is created and embedded under the last selected verse
(see below).
3. Enter a reference (or search term) and press `Enter`.
### Reference syntax
| Input | Meaning |
| --------------------- | ----------------------------------- |
| `John 3:16` | single verse |
| `John 3:16-18` | a verse range |
| `John 3:16.18` | several single verses |
| `Psalm 23` | a whole chapter |
| `Psalm 3-5` | a chapter range |
| `43 3:16` | book by its number (1-66) |
| `John 3:16; Psalm 23` | several references (also with `;`) |
The `:` and `,` separators are interchangeable (`John 3:16` and `John 3,16`
both work). Book names are matched case-insensitively and ignore dots and
spaces, so
`1. Samuel`, `1 Samuel` and `1sam` all resolve to the same book. The heading
shows the book name as the chosen translation writes it.
A chapter range such as `Psalm 3-5` is shorthand for `Psalm 3; Psalm 4; Psalm 5`:
each chapter is inserted as its own section with its own heading, and its
footnotes follow that section. The order is the same whether you write
`Psalm 3-5` or `Psalm 5-3`. A chapter range cannot be combined with a verse
specification (`Psalm 3-5:2` is rejected); write the verses per chapter
instead.
## Text cleanup
The raw bolls.life text is normalized before it is inserted:
- Footnotes can be shown or stripped (see the **Footnotes** setting). When
shown, the source markers become Markdown footnotes (`[^1]`) and the matching
text from bolls.life is appended after each passage's section, with
cross-references linking back to bolls.life. Translations without footnotes
are unaffected.
- Formatting is preserved: source italics (``) and bold (``/``)
become Markdown `*…*` and `**…**`. Any other tags are stripped.
- Missing spaces are added after sentence punctuation, because the source
occasionally glues sentences together (`…mein Gott!Erleuchte…` becomes
`…mein Gott! Erleuchte…`).
- Square brackets are escaped if present (`[ertönt]` becomes `\[ertönt\]`), so
words the translation added for clarity render literally instead of being
treated as Markdown syntax.
## Settings
| Setting | Description |
| --------------------- | -------------------------------------------------------- |
| Language | Translation language, and the book names recognized while typing. |
| Translation | bolls.life translation, filtered to the selected language (e.g. `S00`, `LUT`; `BSB`, `WEB`). |
| Format | `Blockquote, bold verse number`, `One blockquote per verse`, `Blockquote, running text`, or `Custom template`. |
| Passage template | Template for the `Custom template` format (see below). Shown only for that format. |
| Verse template | Template applied to each verse in the `Custom template` format. Shown only for that format. |
| Verse separator | Text placed between verses. Use `\n` for a line break. Shown only for the `Custom template` format. |
| Footnotes | Render source footnotes as Markdown footnotes, or strip the markers. |
| Source link | Make the heading itself link to the passage on bolls.life. |
| Parallel translations | Additional translations inserted after the main one, comma-separated (e.g. `LUT, ELB`). The main translation is always included first (see below). |
| Note folder | Folder for **Annotate verse** notes, with vault-folder autocomplete. Blank means the vault root. |
| Note format | File name (and optional subfolders) for **Annotate verse** notes. Placeholders: `{title}`, `{reference}`, `{translation}`, `{book}`, `{chapter}`, `{verse}`, `{verses}`. A title is only asked for when the format contains `{title}`. |
| Note opens | Where a new annotation note opens: `Split right` (default), `New tab`, or `Do not open`. |
| Note opens on click | Where an embedded annotation note opens when you click it: `Current pane` (default, Obsidian's own behaviour), `Split right`, `New tab`, or `Do not open`. |
| Default note color | Colour preselected when creating an annotation note. One of the ten built-in post-it colours. |
| Note color picker | How the colour is chosen when creating a note: a `Dropdown` (default) or a `Palette` of swatches. |
| Date in annotation notes | Write a date line at the top of newly created annotation notes. |
| Date line | The date line's template. Use `{date}` and `{time}`, optionally with a pattern (`{date:DD.MM.YYYY}`); surrounding text is kept literally. Patterns use `YYYY`, `YY`, `MM`, `M`, `DD`, `D`, `HH`, `H`, `mm`, `m`, `ss`, `s`. Shown only when the date is enabled. |
| Note link style | How the annotation note appears under the verse: `Embedded` (default, `![[...]]`) or `Link` (`[[...]]`). |
## Custom template
With **Format → Custom template** you control the output yourself. The passage
template is rendered once per passage; `{verses}` is the verse template applied
to each verse and joined by the verse separator.
| Placeholder | Meaning |
| ----------------- | ---------------------------------------------------- |
| `{heading}` | Formatted heading, e.g. `**John 3,16** (BSB)`. |
| `{reference}` | Reference without markup, e.g. `John 3,16`. |
| `{book}` | Book name as the translation writes it. |
| `{chapter}` | Chapter number. |
| `{translation}` | Translation code, e.g. `BSB`. |
| `{translationName}` | Full translation name. |
| `{sourceUrl}` | bolls.life URL for the passage. |
| `{verses}` | All verses (passage template only). |
| `{number}`, `{text}` | Verse number and text (verse template only). |
The three presets are just built-in templates; the default custom template
reproduces `Blockquote, bold verse number`.
## Search
The **Search Bible (keyword)** command searches the whole selected translation
for a word or phrase. Matches are listed with their reference and a short text
preview in which the search term is highlighted, up to 100 results. Picking one
inserts that passage with the current format settings.
## Annotating verses
The **Annotate verse (create note)** command lets you attach your own thoughts
to a verse after you have inserted a passage. Put the cursor on the verse you
want to annotate, or select one or more verses, and run the command: it reads
the reference and its heading, creates a note and embeds it in a `zettel`
callout just below the quote. A selection spanning several verses becomes a
range such as `Johannes 3,16-18`. A selection that spans several chapters is
split per chapter, so each chapter gets its own note and callout directly below
that chapter's verses, for example `Markus 1,43-44` and `Lukas 1,1-2` from a
selection over both.
```markdown
> **16** Denn so hat Gott die Welt geliebt …
> [!zettel-yellow]
> ![[Bible Notes/Johannes 3,16 (Schlachter 2000)]]
```
The note is embedded inside a `zettel-` callout, drawn as a post-it with
an always-visible coloured border. When you create a note, the dialog offers the
ten built-in colours and preselects the **Default note color**:
`yellow`, `orange`, `red`, `green`, `teal`, `blue`, `purple`, `pink`, `gray` and
`brown`. The **Note color picker** setting switches between a dropdown and a
palette of swatches. To present the note as a plain `[[...]]` link instead of an
embedded `![[...]]` note, set **Note link style** to `Link`.
Each colour is defined in `styles.css` as a variable, so to use your own colours
override them in a CSS snippet, either the variable or the callout itself:
`body .callout[data-callout="zettel-yellow"] { --callout-color: 212, 160, 23; }`.
With **Date in annotation notes** enabled, every new note starts with the
**Date line**, for example `Erstellt am {date:DD.MM.YYYY}`, so you know when you
wrote the annotation. When the note opens, the cursor is placed right after the
date line, ready for you to type.
Where the note goes is set by two settings, like the Daily Notes plugin. The
**Note folder** field has the same vault-folder autocomplete (blank means the
vault root), and **Note format** is the file name, which understands `{title}`,
`{reference}`, `{translation}`, `{book}`, `{chapter}`, `{verse}` and `{verses}` and
may contain subfolders, for example `{book}/{chapter}/{reference}`. The default
format is `{title}`. When it contains `{title}` the command asks for a title
(prefilled with the reference and the translation, e.g.
`Johannes 3,16 (Schlachter 2000)`); without `{title}` no title is asked and the
note is named from the format.
The quote stays a quote; the embedded note is your commentary and is created
empty (unless a **Date line** is configured), so you just write. The reference
already shows as the note title. The note
opens right away, by default in a split to the right (the **Note opens** setting
switches between split, new tab or not at all). Clicking the embedded note can
open it in a split or a new tab too, via the **Note opens on click** setting
(`Current pane` keeps Obsidian's own click behaviour). Because the note is a
normal file
you can also link to it, search it and collect it with backlinks or Dataview. If
a note with that path already exists it is reused, not overwritten. This works
with the `Blockquote, bold verse number` and `One blockquote per verse` formats,
which label each verse with `**{number}**`; the running-text format has no
per-verse line to anchor to.
With a **Custom template** the command reads the rendered Markdown back, so it
requires the same two patterns: each verse line must contain `**{number}**` (for
example `**16** text`) and the passage heading must contain `**{reference}**`
(optionally followed by `(translation)`, as in `**John 3,16** (BSB)`). If your
template drops the bold verse number or the bold heading, the command finds no
verse and shows a notice instead. The default custom template keeps both and
works. The callout itself is a self-contained block, so it never swallows the
verses below it, whatever the format.
## Parallel translations
The **Parallel translations** setting takes a comma-separated list of
additional translation codes, for example `LUT, ELB`. The command
**Insert Bible quote (parallel translations + main)** always inserts the main
translation first, then every listed translation, in the order given. Entering
a single code therefore yields two translations, not one:
Main **Translation** `S00` plus **Parallel translations** `LUT` gives:
```markdown
> **Das Evangelium nach Johannes 3,16** (Schlachter 2000)
>
> **16** Denn so \[sehr\] hat Gott die Welt geliebt, dass er seinen eingeborenen Sohn gab, damit jeder, der an ihn glaubt, nicht verlorengeht, sondern ewiges Leben hat.
> **Johannes 3,16** (Luther (1912))
>
> **16** Also hat Gott die Welt geliebt, daß er seinen eingeborenen Sohn gab, auf daß alle, die an ihn glauben, nicht verloren werden, sondern das ewige Leben haben.
```
If a listed code equals the main translation, it is inserted only once. Each
translation is inserted with all of its references together, and each
reference's footnotes follow that reference's section. Multiple references are
still separated by `;`.
## Offline cache
Fetched chapters, book lists and translation metadata are cached and reused on
later runs, so passages you have already looked up work without a connection.
The cache lives in the plugin's own data (`data.json`) alongside the settings.
It is unlimited and has no expiry. To clear it, remove the `cache` key from that
file; deleting the whole file also resets the settings.
## Installation
### Manual
Copy `main.js`, `manifest.json` and `styles.css` into
`/.obsidian/plugins/simple-bible-fetcher/` and enable the plugin under
**Settings → Community plugins**.
### From source
```bash
npm install
npm run build
```
Then copy the three files above into your vault, or symlink the project folder
into `/.obsidian/plugins/simple-bible-fetcher`.
## Development
```bash
npm install
npm run dev # watch build
npm run build # typecheck + production build
```
## Tests
The test suite runs on [Vitest](https://vitest.dev) with jsdom. The Obsidian
API is replaced by a stub (`test/obsidian-stub.ts`), and the bolls.life HTTP
layer is driven by fixtures (`test/fixtures.ts`), so the tests never hit the
network.
```bash
npm test # run the suite once
npm run test:watch # watch mode
npm run test:coverage # run with a coverage report
npm run typecheck:test # type-check the tests too
```
The suite covers reference parsing and book resolution, text cleanup and
footnotes, the output presets and the custom template, parallel translations
and their footnote placement, keyword search, the persistent cache, settings
persistence and the plugin commands, modals and settings tab. Coverage is
enforced at 100 % for statements, branches, functions and lines; a drop fails
the coverage run.
Because the suite runs against a stub rather than a real vault, it proves the
logic, not the Obsidian integration. A manual smoke test in Obsidian is still
worth doing before a release.
## License
GPL-3.0-or-later. See [LICENSE](LICENSE).