Render KJV footnotes and lemma margin line

This commit is contained in:
2026-07-12 09:10:01 -05:00
parent 7674510952
commit d710035045
11 changed files with 315 additions and 13 deletions
+1 -1
View File
@@ -77,7 +77,7 @@ checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3"
[[package]]
name = "app"
version = "0.1.6"
version = "0.1.7"
dependencies = [
"log",
"rusqlite",
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "app"
version = "0.1.6"
version = "0.1.7"
description = "A Tauri App"
authors = ["you"]
license = ""
+118 -1
View File
@@ -43,6 +43,7 @@ struct Verse {
verse: i64,
text: String,
strongs: Vec<StrongLink>,
footnotes: Vec<Footnote>,
tags: Vec<Tag>,
}
@@ -64,6 +65,17 @@ struct Tag {
color: String,
}
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
struct Footnote {
id: i64,
verse_id: i64,
note_index: i64,
caller: String,
reference: String,
text: String,
}
#[derive(Deserialize)]
struct ImportedVerse {
id: String,
@@ -81,6 +93,15 @@ struct ImportedStrongLink {
strong: String,
}
#[derive(Deserialize)]
struct ImportedFootnote {
verse_id: String,
note_index: i64,
caller: String,
reference: String,
text: String,
}
#[derive(Deserialize)]
struct LibreBibleIndexCatalog {
resources: Vec<LibreBibleResourceSummary>,
@@ -106,6 +127,7 @@ struct LibreBibleResourceCatalog {
struct LibreBiblePackageFiles {
verses_jsonl: Option<LibreBiblePackageFile>,
strongs_jsonl: Option<LibreBiblePackageFile>,
footnotes_jsonl: Option<LibreBiblePackageFile>,
entries_jsonl: Option<LibreBiblePackageFile>,
}
@@ -120,6 +142,7 @@ struct LibreBiblePackageSource {
package_dir: PathBuf,
verses_path: PathBuf,
strongs_path: Option<PathBuf>,
footnotes_path: Option<PathBuf>,
lexicon_path: Option<PathBuf>,
}
@@ -243,6 +266,16 @@ fn migrate(conn: &Connection) -> Result<(), String> {
FOREIGN KEY (strong) REFERENCES lexicon(strong)
);
CREATE TABLE IF NOT EXISTS verse_footnotes (
id INTEGER PRIMARY KEY,
verse_id INTEGER NOT NULL,
note_index INTEGER NOT NULL,
caller TEXT NOT NULL,
reference TEXT NOT NULL,
text TEXT NOT NULL,
FOREIGN KEY (verse_id) REFERENCES verses(id)
);
CREATE TABLE IF NOT EXISTS tags (
id INTEGER PRIMARY KEY,
label TEXT NOT NULL COLLATE NOCASE UNIQUE,
@@ -337,6 +370,7 @@ fn reset_resource_cache(conn: &Connection) -> Result<(), String> {
"
DELETE FROM verse_tags;
DELETE FROM verse_strongs;
DELETE FROM verse_footnotes;
DELETE FROM lexicon;
DELETE FROM verses;
DELETE FROM library_sources;
@@ -497,6 +531,9 @@ fn load_package_source_from_package_dir(
strongs_jsonl: Some(LibreBiblePackageFile {
path: "strongs-links.jsonl".to_string(),
}),
footnotes_jsonl: Some(LibreBiblePackageFile {
path: "footnotes.jsonl".to_string(),
}),
entries_jsonl: None,
};
@@ -534,6 +571,11 @@ fn package_source_from_files(
.as_ref()
.map(|file| package_dir.join(&file.path))
.filter(|path| path.exists());
let footnotes_path = files
.footnotes_jsonl
.as_ref()
.map(|file| package_dir.join(&file.path))
.filter(|path| path.exists());
let lexicon_path = files
.entries_jsonl
.as_ref()
@@ -546,6 +588,7 @@ fn package_source_from_files(
package_dir,
verses_path,
strongs_path,
footnotes_path,
lexicon_path,
})
}
@@ -694,6 +737,39 @@ fn import_package(
}
}
if let Some(footnotes_path) = package_source.footnotes_path {
let file = File::open(&footnotes_path)
.map_err(|err| format!("Unable to open {}: {err}", footnotes_path.display()))?;
let reader = BufReader::new(file);
for (index, line) in reader.lines().enumerate() {
let line = line.map_err(|err| err.to_string())?;
if line.trim().is_empty() {
continue;
}
let imported: ImportedFootnote =
serde_json::from_str(&line).map_err(|err| err.to_string())?;
let Some(verse_id) = verse_ids.get(&imported.verse_id) else {
continue;
};
tx.execute(
"INSERT OR REPLACE INTO verse_footnotes
(id, verse_id, note_index, caller, reference, text)
VALUES (?1, ?2, ?3, ?4, ?5, ?6)",
params![
(index + 1) as i64,
verse_id,
imported.note_index,
imported.caller,
imported.reference,
imported.text
],
)
.map_err(|err| err.to_string())?;
}
}
tx.commit().map_err(|err| err.to_string())
}
@@ -805,12 +881,14 @@ fn row_to_verse_shell(row: &rusqlite::Row<'_>) -> rusqlite::Result<Verse> {
verse: row.get(3)?,
text: row.get(4)?,
strongs: Vec::new(),
footnotes: Vec::new(),
tags: Vec::new(),
})
}
fn hydrate_verse(conn: &Connection, mut verse: Verse) -> Result<Verse, String> {
verse.strongs = load_strongs(conn, verse.id)?;
verse.footnotes = load_footnotes(conn, verse.id)?;
verse.tags = load_tags(conn, verse.id)?;
Ok(verse)
}
@@ -849,6 +927,38 @@ fn load_strongs(conn: &Connection, verse_id: i64) -> Result<Vec<StrongLink>, Str
Ok(entries)
}
fn load_footnotes(conn: &Connection, verse_id: i64) -> Result<Vec<Footnote>, String> {
let mut stmt = conn
.prepare(
"
SELECT id, verse_id, note_index, caller, reference, text
FROM verse_footnotes
WHERE verse_id = ?1
ORDER BY note_index, id
",
)
.map_err(|err| err.to_string())?;
let rows = stmt
.query_map(params![verse_id], |row| {
Ok(Footnote {
id: row.get(0)?,
verse_id: row.get(1)?,
note_index: row.get(2)?,
caller: row.get(3)?,
reference: row.get(4)?,
text: row.get(5)?,
})
})
.map_err(|err| err.to_string())?;
let mut entries = Vec::new();
for row in rows {
entries.push(row.map_err(|err| err.to_string())?);
}
Ok(entries)
}
fn add_column_if_missing(
conn: &Connection,
table: &str,
@@ -887,8 +997,11 @@ fn needs_resource_refresh(conn: &Connection) -> Result<bool, String> {
|row| row.get(0),
)
.map_err(|err| err.to_string())?;
let footnote_count: i64 = conn
.query_row("SELECT COUNT(*) FROM verse_footnotes", [], |row| row.get(0))
.map_err(|err| err.to_string())?;
Ok(lexicon_count < 10_000 || real_definition_count < 10_000)
Ok(lexicon_count < 10_000 || real_definition_count < 10_000 || footnote_count < 6_000)
}
fn find_catalog_lexicon_path(
@@ -1031,6 +1144,9 @@ mod tests {
let strongs_count: i64 = conn
.query_row("SELECT COUNT(*) FROM verse_strongs", [], |row| row.get(0))
.map_err(|err| err.to_string())?;
let footnotes_count: i64 = conn
.query_row("SELECT COUNT(*) FROM verse_footnotes", [], |row| row.get(0))
.map_err(|err| err.to_string())?;
let source_count: i64 = conn
.query_row("SELECT COUNT(*) FROM library_sources", [], |row| row.get(0))
.map_err(|err| err.to_string())?;
@@ -1047,6 +1163,7 @@ mod tests {
assert_eq!(verse_count, 31_102);
assert_eq!(strongs_count, 348_884);
assert_eq!(footnotes_count, 6_959);
assert_eq!(lexicon_count, 14_197);
assert!(g25_definition.contains("to love"));
assert_eq!(source_count, 1);
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "../node_modules/@tauri-apps/cli/config.schema.json",
"productName": "Libre Study",
"version": "0.1.6",
"version": "0.1.7",
"identifier": "org.librestudy.desktop",
"build": {
"frontendDist": "../dist",