Load translations from LibreBible catalog
This commit is contained in:
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "libre-study",
|
"name": "libre-study",
|
||||||
"version": "0.1.18",
|
"version": "0.1.19",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "libre-study",
|
"name": "libre-study",
|
||||||
"version": "0.1.18",
|
"version": "0.1.19",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tauri-apps/api": "^2.11.1",
|
"@tauri-apps/api": "^2.11.1",
|
||||||
"react": "^19.2.7",
|
"react": "^19.2.7",
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "libre-study",
|
"name": "libre-study",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.1.18",
|
"version": "0.1.19",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite --host 127.0.0.1",
|
"dev": "vite --host 127.0.0.1",
|
||||||
|
|||||||
Generated
+1
-1
@@ -77,7 +77,7 @@ checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "app"
|
name = "app"
|
||||||
version = "0.1.18"
|
version = "0.1.19"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"rusqlite",
|
"rusqlite",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "app"
|
name = "app"
|
||||||
version = "0.1.18"
|
version = "0.1.19"
|
||||||
description = "A Tauri App"
|
description = "A Tauri App"
|
||||||
authors = ["you"]
|
authors = ["you"]
|
||||||
license = ""
|
license = ""
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "../node_modules/@tauri-apps/cli/config.schema.json",
|
"$schema": "../node_modules/@tauri-apps/cli/config.schema.json",
|
||||||
"productName": "Libre Study",
|
"productName": "Libre Study",
|
||||||
"version": "0.1.18",
|
"version": "0.1.19",
|
||||||
"identifier": "org.librestudy.desktop",
|
"identifier": "org.librestudy.desktop",
|
||||||
"build": {
|
"build": {
|
||||||
"frontendDist": "../dist",
|
"frontendDist": "../dist",
|
||||||
|
|||||||
+154
-26
@@ -4,10 +4,11 @@ import {
|
|||||||
getCommentaryForVerse,
|
getCommentaryForVerse,
|
||||||
getDictionaryEntriesForTerms,
|
getDictionaryEntriesForTerms,
|
||||||
getLibraryState,
|
getLibraryState,
|
||||||
|
getTranslations,
|
||||||
searchVerses,
|
searchVerses,
|
||||||
} from './studyClient'
|
} from './studyClient'
|
||||||
import type { DragEvent, MouseEvent as ReactMouseEvent, ReactNode } from 'react'
|
import type { DragEvent, MouseEvent as ReactMouseEvent, ReactNode } from 'react'
|
||||||
import type { CommentaryEntry, DictionaryEntry, Verse } from './types'
|
import type { CommentaryEntry, DictionaryEntry, TranslationResource, Verse } from './types'
|
||||||
import './App.css'
|
import './App.css'
|
||||||
|
|
||||||
const tagColors = ['#2f6f5e', '#735c0f', '#8a3ffc', '#b42318', '#245ba7']
|
const tagColors = ['#2f6f5e', '#735c0f', '#8a3ffc', '#b42318', '#245ba7']
|
||||||
@@ -16,6 +17,7 @@ const sessionStorageKey = 'libre-study.sessions.v1'
|
|||||||
const activeSessionStorageKey = 'libre-study.active-session.v1'
|
const activeSessionStorageKey = 'libre-study.active-session.v1'
|
||||||
const studySettingsStorageKey = 'libre-study.study-settings.v1'
|
const studySettingsStorageKey = 'libre-study.study-settings.v1'
|
||||||
const timelineLimit = 200
|
const timelineLimit = 200
|
||||||
|
const defaultTranslationId = 'kjv-eng-kjv2006'
|
||||||
type ViewMode = 'page' | 'study' | 'notes' | 'compare'
|
type ViewMode = 'page' | 'study' | 'notes' | 'compare'
|
||||||
type StrongDisplayMode = 'off' | 'highlight' | 'numbers'
|
type StrongDisplayMode = 'off' | 'highlight' | 'numbers'
|
||||||
type PageMotion = 'idle' | 'turn-forward' | 'turn-back'
|
type PageMotion = 'idle' | 'turn-forward' | 'turn-back'
|
||||||
@@ -54,7 +56,7 @@ function reference(verse: Verse) {
|
|||||||
return `${verse.book} ${verse.chapter}:${verse.verse}`
|
return `${verse.book} ${verse.chapter}:${verse.verse}`
|
||||||
}
|
}
|
||||||
|
|
||||||
function createSession(title = 'Study Session'): StudySession {
|
function createSession(title = 'Study Session', baseTranslation = defaultTranslationId): StudySession {
|
||||||
const now = new Date().toISOString()
|
const now = new Date().toISOString()
|
||||||
const id =
|
const id =
|
||||||
typeof crypto !== 'undefined' && 'randomUUID' in crypto
|
typeof crypto !== 'undefined' && 'randomUUID' in crypto
|
||||||
@@ -64,7 +66,7 @@ function createSession(title = 'Study Session'): StudySession {
|
|||||||
return {
|
return {
|
||||||
id,
|
id,
|
||||||
title,
|
title,
|
||||||
baseTranslation: 'kjv-eng-kjv2006',
|
baseTranslation,
|
||||||
footnotesEnabled: true,
|
footnotesEnabled: true,
|
||||||
strongsEnabled: true,
|
strongsEnabled: true,
|
||||||
commentaryEnabled: true,
|
commentaryEnabled: true,
|
||||||
@@ -227,6 +229,7 @@ function App() {
|
|||||||
const [themeMode, setThemeMode] = useState<ThemeMode>('light')
|
const [themeMode, setThemeMode] = useState<ThemeMode>('light')
|
||||||
const [libraryVerses, setLibraryVerses] = useState<Verse[]>([])
|
const [libraryVerses, setLibraryVerses] = useState<Verse[]>([])
|
||||||
const [verses, setVerses] = useState<Verse[]>([])
|
const [verses, setVerses] = useState<Verse[]>([])
|
||||||
|
const [translations, setTranslations] = useState<TranslationResource[]>([])
|
||||||
const [selectedId, setSelectedId] = useState<number | null>(null)
|
const [selectedId, setSelectedId] = useState<number | null>(null)
|
||||||
const [pageStartId, setPageStartId] = useState<number | null>(null)
|
const [pageStartId, setPageStartId] = useState<number | null>(null)
|
||||||
const [selectedStrong, setSelectedStrong] = useState<string | null>(null)
|
const [selectedStrong, setSelectedStrong] = useState<string | null>(null)
|
||||||
@@ -236,6 +239,7 @@ function App() {
|
|||||||
const [sessions, setSessions] = useState<StudySession[]>([])
|
const [sessions, setSessions] = useState<StudySession[]>([])
|
||||||
const [activeSessionId, setActiveSessionId] = useState('')
|
const [activeSessionId, setActiveSessionId] = useState('')
|
||||||
const [newSessionTitle, setNewSessionTitle] = useState('New KJV Study')
|
const [newSessionTitle, setNewSessionTitle] = useState('New KJV Study')
|
||||||
|
const [newSessionBaseTranslation, setNewSessionBaseTranslation] = useState(defaultTranslationId)
|
||||||
const [newSessionFootnotes, setNewSessionFootnotes] = useState(true)
|
const [newSessionFootnotes, setNewSessionFootnotes] = useState(true)
|
||||||
const [newSessionStrongs, setNewSessionStrongs] = useState(true)
|
const [newSessionStrongs, setNewSessionStrongs] = useState(true)
|
||||||
const [newSessionCommentary, setNewSessionCommentary] = useState(true)
|
const [newSessionCommentary, setNewSessionCommentary] = useState(true)
|
||||||
@@ -246,6 +250,7 @@ function App() {
|
|||||||
const [originalCollapsed, setOriginalCollapsed] = useState(false)
|
const [originalCollapsed, setOriginalCollapsed] = useState(false)
|
||||||
const [linkedCollapsed, setLinkedCollapsed] = useState(false)
|
const [linkedCollapsed, setLinkedCollapsed] = useState(false)
|
||||||
const [definitionCollapsed, setDefinitionCollapsed] = useState(false)
|
const [definitionCollapsed, setDefinitionCollapsed] = useState(false)
|
||||||
|
const [studyNotesCollapsed, setStudyNotesCollapsed] = useState(false)
|
||||||
const [commentaryCollapsed, setCommentaryCollapsed] = useState(false)
|
const [commentaryCollapsed, setCommentaryCollapsed] = useState(false)
|
||||||
const [dictionaryCollapsed, setDictionaryCollapsed] = useState(false)
|
const [dictionaryCollapsed, setDictionaryCollapsed] = useState(false)
|
||||||
const [showOriginalLine, setShowOriginalLine] = useState(true)
|
const [showOriginalLine, setShowOriginalLine] = useState(true)
|
||||||
@@ -264,9 +269,9 @@ function App() {
|
|||||||
const notesDragOpenTimeout = useRef<number | null>(null)
|
const notesDragOpenTimeout = useRef<number | null>(null)
|
||||||
const readerRef = useRef<HTMLElement | null>(null)
|
const readerRef = useRef<HTMLElement | null>(null)
|
||||||
|
|
||||||
async function loadLibrary() {
|
async function loadLibrary(translationId = activeSession?.baseTranslation ?? defaultTranslationId) {
|
||||||
setError(null)
|
setError(null)
|
||||||
const state = await getLibraryState()
|
const state = await getLibraryState(translationId)
|
||||||
setLibraryVerses(state.verses)
|
setLibraryVerses(state.verses)
|
||||||
setVerses(state.verses)
|
setVerses(state.verses)
|
||||||
const firstId = state.verses[0]?.id ?? null
|
const firstId = state.verses[0]?.id ?? null
|
||||||
@@ -352,7 +357,7 @@ function App() {
|
|||||||
setError(null)
|
setError(null)
|
||||||
const trimmed = nextQuery.trim()
|
const trimmed = nextQuery.trim()
|
||||||
setSelectedStrong(/^[gh]\d+$/i.test(trimmed) ? trimmed.toUpperCase() : null)
|
setSelectedStrong(/^[gh]\d+$/i.test(trimmed) ? trimmed.toUpperCase() : null)
|
||||||
const results = await searchVerses(nextQuery)
|
const results = await searchVerses(nextQuery, activeSession?.baseTranslation ?? defaultTranslationId)
|
||||||
setVerses(results)
|
setVerses(results)
|
||||||
const firstResult = results[0]
|
const firstResult = results[0]
|
||||||
setSelectedId(firstResult?.id ?? null)
|
setSelectedId(firstResult?.id ?? null)
|
||||||
@@ -375,7 +380,12 @@ function App() {
|
|||||||
if (!selectedId || !label) return
|
if (!selectedId || !label) return
|
||||||
|
|
||||||
const color = tagColors[Math.abs(label.length) % tagColors.length]
|
const color = tagColors[Math.abs(label.length) % tagColors.length]
|
||||||
const updated = await addTagToVerse(selectedId, label, color)
|
const updated = await addTagToVerse(
|
||||||
|
selectedId,
|
||||||
|
label,
|
||||||
|
color,
|
||||||
|
activeSession?.baseTranslation ?? defaultTranslationId,
|
||||||
|
)
|
||||||
|
|
||||||
setLibraryVerses((current) =>
|
setLibraryVerses((current) =>
|
||||||
current.map((verse) => (verse.id === updated.id ? updated : verse)),
|
current.map((verse) => (verse.id === updated.id ? updated : verse)),
|
||||||
@@ -400,6 +410,7 @@ function App() {
|
|||||||
originalCollapsed: boolean
|
originalCollapsed: boolean
|
||||||
linkedCollapsed: boolean
|
linkedCollapsed: boolean
|
||||||
definitionCollapsed: boolean
|
definitionCollapsed: boolean
|
||||||
|
studyNotesCollapsed: boolean
|
||||||
commentaryCollapsed: boolean
|
commentaryCollapsed: boolean
|
||||||
dictionaryCollapsed: boolean
|
dictionaryCollapsed: boolean
|
||||||
showOriginalLine: boolean
|
showOriginalLine: boolean
|
||||||
@@ -416,6 +427,7 @@ function App() {
|
|||||||
setOriginalCollapsed(parsed.originalCollapsed ?? false)
|
setOriginalCollapsed(parsed.originalCollapsed ?? false)
|
||||||
setLinkedCollapsed(parsed.linkedCollapsed ?? false)
|
setLinkedCollapsed(parsed.linkedCollapsed ?? false)
|
||||||
setDefinitionCollapsed(parsed.definitionCollapsed ?? false)
|
setDefinitionCollapsed(parsed.definitionCollapsed ?? false)
|
||||||
|
setStudyNotesCollapsed(parsed.studyNotesCollapsed ?? false)
|
||||||
setCommentaryCollapsed(parsed.commentaryCollapsed ?? false)
|
setCommentaryCollapsed(parsed.commentaryCollapsed ?? false)
|
||||||
setDictionaryCollapsed(parsed.dictionaryCollapsed ?? false)
|
setDictionaryCollapsed(parsed.dictionaryCollapsed ?? false)
|
||||||
setShowOriginalLine(parsed.showOriginalLine ?? true)
|
setShowOriginalLine(parsed.showOriginalLine ?? true)
|
||||||
@@ -447,7 +459,7 @@ function App() {
|
|||||||
nextSessions = nextSessions.map((session) => ({
|
nextSessions = nextSessions.map((session) => ({
|
||||||
...createSession(session.title),
|
...createSession(session.title),
|
||||||
...session,
|
...session,
|
||||||
baseTranslation: session.baseTranslation ?? 'kjv-eng-kjv2006',
|
baseTranslation: session.baseTranslation ?? defaultTranslationId,
|
||||||
footnotesEnabled: session.footnotesEnabled ?? true,
|
footnotesEnabled: session.footnotesEnabled ?? true,
|
||||||
strongsEnabled: session.strongsEnabled ?? true,
|
strongsEnabled: session.strongsEnabled ?? true,
|
||||||
commentaryEnabled: session.commentaryEnabled ?? true,
|
commentaryEnabled: session.commentaryEnabled ?? true,
|
||||||
@@ -455,14 +467,17 @@ function App() {
|
|||||||
timelineEnabled: session.timelineEnabled ?? true,
|
timelineEnabled: session.timelineEnabled ?? true,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
setSessions(nextSessions)
|
const selectedSessionId = nextSessions.some((session) => session.id === savedActiveSessionId)
|
||||||
setActiveSessionId(
|
? savedActiveSessionId ?? nextSessions[0].id
|
||||||
nextSessions.some((session) => session.id === savedActiveSessionId)
|
: nextSessions[0].id
|
||||||
? savedActiveSessionId ?? nextSessions[0].id
|
const selectedSession =
|
||||||
: nextSessions[0].id,
|
nextSessions.find((session) => session.id === selectedSessionId) ?? nextSessions[0]
|
||||||
)
|
|
||||||
|
|
||||||
loadLibrary()
|
setSessions(nextSessions)
|
||||||
|
setActiveSessionId(selectedSessionId)
|
||||||
|
getTranslations().then(setTranslations).catch((reason) => setError(String(reason)))
|
||||||
|
|
||||||
|
loadLibrary(selectedSession.baseTranslation)
|
||||||
.catch((reason) => setError(String(reason)))
|
.catch((reason) => setError(String(reason)))
|
||||||
.finally(() => setLoading(false))
|
.finally(() => setLoading(false))
|
||||||
}, [])
|
}, [])
|
||||||
@@ -488,6 +503,7 @@ function App() {
|
|||||||
originalCollapsed,
|
originalCollapsed,
|
||||||
linkedCollapsed,
|
linkedCollapsed,
|
||||||
definitionCollapsed,
|
definitionCollapsed,
|
||||||
|
studyNotesCollapsed,
|
||||||
commentaryCollapsed,
|
commentaryCollapsed,
|
||||||
dictionaryCollapsed,
|
dictionaryCollapsed,
|
||||||
showOriginalLine,
|
showOriginalLine,
|
||||||
@@ -515,6 +531,7 @@ function App() {
|
|||||||
showLinkedLine,
|
showLinkedLine,
|
||||||
showOriginalLine,
|
showOriginalLine,
|
||||||
splitPercent,
|
splitPercent,
|
||||||
|
studyNotesCollapsed,
|
||||||
themeMode,
|
themeMode,
|
||||||
])
|
])
|
||||||
|
|
||||||
@@ -527,6 +544,7 @@ function App() {
|
|||||||
setViewMode(activeSession.viewMode)
|
setViewMode(activeSession.viewMode)
|
||||||
setStrongDisplay(activeSession.strongDisplay)
|
setStrongDisplay(activeSession.strongDisplay)
|
||||||
setNoteDraft(activeSession.noteDraft)
|
setNoteDraft(activeSession.noteDraft)
|
||||||
|
loadLibrary(activeSession.baseTranslation).catch((reason) => setError(String(reason)))
|
||||||
setSelectedId((current) => activeSession.selectedId ?? current)
|
setSelectedId((current) => activeSession.selectedId ?? current)
|
||||||
setPageStartId((current) => activeSession.pageStartId ?? current)
|
setPageStartId((current) => activeSession.pageStartId ?? current)
|
||||||
setSelectedStrong(null)
|
setSelectedStrong(null)
|
||||||
@@ -550,6 +568,12 @@ function App() {
|
|||||||
() => sessions.find((session) => session.id === activeSessionId) ?? sessions[0],
|
() => sessions.find((session) => session.id === activeSessionId) ?? sessions[0],
|
||||||
[activeSessionId, sessions],
|
[activeSessionId, sessions],
|
||||||
)
|
)
|
||||||
|
const activeTranslation = useMemo(
|
||||||
|
() =>
|
||||||
|
translations.find((translation) => translation.id === activeSession?.baseTranslation) ??
|
||||||
|
translations.find((translation) => translation.id === defaultTranslationId),
|
||||||
|
[activeSession?.baseTranslation, translations],
|
||||||
|
)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let cancelled = false
|
let cancelled = false
|
||||||
@@ -623,6 +647,7 @@ function App() {
|
|||||||
? `${reference(pageVerses[0])} - ${reference(pageVerses[pageVerses.length - 1])}`
|
? `${reference(pageVerses[0])} - ${reference(pageVerses[pageVerses.length - 1])}`
|
||||||
: 'No page loaded'
|
: 'No page loaded'
|
||||||
const selectedFootnotes = selectedVerse?.footnotes ?? []
|
const selectedFootnotes = selectedVerse?.footnotes ?? []
|
||||||
|
const selectedStudyNotes = selectedVerse?.studyNotes ?? []
|
||||||
const pageFootnotes = useMemo(
|
const pageFootnotes = useMemo(
|
||||||
() => pageVerses.flatMap((verse) => verse.footnotes ?? []),
|
() => pageVerses.flatMap((verse) => verse.footnotes ?? []),
|
||||||
[pageVerses],
|
[pageVerses],
|
||||||
@@ -703,6 +728,38 @@ function App() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function changeTranslation(translationId: string) {
|
||||||
|
const translation =
|
||||||
|
translations.find((item) => item.id === translationId) ??
|
||||||
|
translations.find((item) => item.id === defaultTranslationId)
|
||||||
|
const state = await getLibraryState(translationId)
|
||||||
|
const firstVerse = state.verses[0] ?? null
|
||||||
|
|
||||||
|
setQuery('')
|
||||||
|
setSelectedStrong(null)
|
||||||
|
setLibraryVerses(state.verses)
|
||||||
|
setVerses(state.verses)
|
||||||
|
setSelectedId(firstVerse?.id ?? null)
|
||||||
|
setPageStartId(firstVerse?.id ?? null)
|
||||||
|
patchActiveSession({
|
||||||
|
baseTranslation: translationId,
|
||||||
|
selectedId: firstVerse?.id ?? null,
|
||||||
|
pageStartId: firstVerse?.id ?? null,
|
||||||
|
})
|
||||||
|
recordTimeline(
|
||||||
|
{
|
||||||
|
type: 'setting',
|
||||||
|
label: `Translation: ${translation?.abbreviation ?? translationId}`,
|
||||||
|
detail: translation?.title,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
baseTranslation: translationId,
|
||||||
|
selectedId: firstVerse?.id ?? null,
|
||||||
|
pageStartId: firstVerse?.id ?? null,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
function shiftPage(direction: -1 | 1) {
|
function shiftPage(direction: -1 | 1) {
|
||||||
if (!selectedVerse) return
|
if (!selectedVerse) return
|
||||||
const currentStartIndex = pageAnchors.findIndex((verse) => verse.id === pageStartId)
|
const currentStartIndex = pageAnchors.findIndex((verse) => verse.id === pageStartId)
|
||||||
@@ -851,7 +908,10 @@ function App() {
|
|||||||
|
|
||||||
function startNewSession() {
|
function startNewSession() {
|
||||||
const nextSession = {
|
const nextSession = {
|
||||||
...createSession(newSessionTitle.trim() || `Study Session ${sessions.length + 1}`),
|
...createSession(
|
||||||
|
newSessionTitle.trim() || `Study Session ${sessions.length + 1}`,
|
||||||
|
newSessionBaseTranslation,
|
||||||
|
),
|
||||||
footnotesEnabled: newSessionFootnotes,
|
footnotesEnabled: newSessionFootnotes,
|
||||||
strongsEnabled: newSessionStrongs,
|
strongsEnabled: newSessionStrongs,
|
||||||
commentaryEnabled: newSessionCommentary,
|
commentaryEnabled: newSessionCommentary,
|
||||||
@@ -860,6 +920,7 @@ function App() {
|
|||||||
}
|
}
|
||||||
setSessions((current) => [nextSession, ...current])
|
setSessions((current) => [nextSession, ...current])
|
||||||
setActiveSessionId(nextSession.id)
|
setActiveSessionId(nextSession.id)
|
||||||
|
loadLibrary(nextSession.baseTranslation).catch((reason) => setError(String(reason)))
|
||||||
setSessionModalOpen(false)
|
setSessionModalOpen(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1113,6 +1174,52 @@ function App() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderStudyNotesSection() {
|
||||||
|
if (activeSession?.footnotesEnabled === false) return null
|
||||||
|
|
||||||
|
return (
|
||||||
|
<article className="resource-card study-notes-card" aria-label="Translation study notes">
|
||||||
|
<header
|
||||||
|
className="margin-section-header"
|
||||||
|
role="button"
|
||||||
|
tabIndex={0}
|
||||||
|
onClick={() => {
|
||||||
|
setStudyNotesCollapsed((current) => !current)
|
||||||
|
recordDisplaySetting('Toggled translation study notes accordion')
|
||||||
|
}}
|
||||||
|
onKeyDown={(event) => {
|
||||||
|
if (event.key === 'Enter' || event.key === ' ') {
|
||||||
|
event.preventDefault()
|
||||||
|
setStudyNotesCollapsed((current) => !current)
|
||||||
|
recordDisplaySetting('Toggled translation study notes accordion')
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<button type="button" className="collapse-button">Translation Study Notes</button>
|
||||||
|
<span>{selectedStudyNotes.length ? `${selectedStudyNotes.length} notes` : 'None'}</span>
|
||||||
|
</header>
|
||||||
|
{!studyNotesCollapsed ? (
|
||||||
|
selectedStudyNotes.length ? (
|
||||||
|
<div className="translation-note-list">
|
||||||
|
{selectedStudyNotes.map((note) => (
|
||||||
|
<p
|
||||||
|
key={note.id}
|
||||||
|
draggable
|
||||||
|
onDragStart={(event) => startTextDrag(event, `${note.reference} ${note.text}`)}
|
||||||
|
>
|
||||||
|
<strong>{note.reference}</strong>
|
||||||
|
<span>{note.text}</span>
|
||||||
|
</p>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<p className="resource-disabled">No translation study notes for the selected verse.</p>
|
||||||
|
)
|
||||||
|
) : null}
|
||||||
|
</article>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
function renderCommentarySection() {
|
function renderCommentarySection() {
|
||||||
if (!showCommentaryPanel) return null
|
if (!showCommentaryPanel) return null
|
||||||
|
|
||||||
@@ -1248,7 +1355,7 @@ function App() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderBiblePage(label = 'KJV') {
|
function renderBiblePage(label = activeTranslation?.abbreviation ?? 'Bible') {
|
||||||
if (!selectedVerse) return null
|
if (!selectedVerse) return null
|
||||||
return (
|
return (
|
||||||
<article className="bible-page" aria-label={`${label} Bible page`}>
|
<article className="bible-page" aria-label={`${label} Bible page`}>
|
||||||
@@ -1368,6 +1475,7 @@ function App() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="margin-resource-grid">
|
<div className="margin-resource-grid">
|
||||||
|
{renderStudyNotesSection()}
|
||||||
{renderCommentarySection()}
|
{renderCommentarySection()}
|
||||||
{renderDictionarySection()}
|
{renderDictionarySection()}
|
||||||
</div>
|
</div>
|
||||||
@@ -1543,7 +1651,7 @@ function App() {
|
|||||||
<dl className="session-facts">
|
<dl className="session-facts">
|
||||||
<div>
|
<div>
|
||||||
<dt>Translation</dt>
|
<dt>Translation</dt>
|
||||||
<dd>KJV</dd>
|
<dd>{activeTranslation?.abbreviation ?? activeSession?.baseTranslation ?? 'n/a'}</dd>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<dt>Current page</dt>
|
<dt>Current page</dt>
|
||||||
@@ -1613,8 +1721,15 @@ function App() {
|
|||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
Base translation
|
Base translation
|
||||||
<select value="kjv-eng-kjv2006" onChange={() => null}>
|
<select
|
||||||
<option value="kjv-eng-kjv2006">King James Version</option>
|
value={newSessionBaseTranslation}
|
||||||
|
onChange={(event) => setNewSessionBaseTranslation(event.target.value)}
|
||||||
|
>
|
||||||
|
{translations.map((translation) => (
|
||||||
|
<option key={translation.id} value={translation.id}>
|
||||||
|
{translation.shortTitle}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
<div className="resource-toggles">
|
<div className="resource-toggles">
|
||||||
@@ -1702,7 +1817,11 @@ function App() {
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<dt>Current Bible package</dt>
|
<dt>Current Bible package</dt>
|
||||||
<dd>King James Version, public domain KJV text package with Strong's-linked study data.</dd>
|
<dd>
|
||||||
|
{activeTranslation
|
||||||
|
? `${activeTranslation.title}. ${activeTranslation.licenseName}`
|
||||||
|
: 'LibreBible translation package loaded from the local catalog.'}
|
||||||
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<dt>Software stack</dt>
|
<dt>Software stack</dt>
|
||||||
@@ -2053,8 +2172,17 @@ function App() {
|
|||||||
<div className="page-controls" aria-label="Page selectors">
|
<div className="page-controls" aria-label="Page selectors">
|
||||||
<label>
|
<label>
|
||||||
Translation
|
Translation
|
||||||
<select value="kjv-eng-kjv2006" onChange={() => null}>
|
<select
|
||||||
<option value="kjv-eng-kjv2006">KJV</option>
|
value={activeSession?.baseTranslation ?? defaultTranslationId}
|
||||||
|
onChange={(event) =>
|
||||||
|
changeTranslation(event.target.value).catch((reason) => setError(String(reason)))
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{translations.map((translation) => (
|
||||||
|
<option key={translation.id} value={translation.id}>
|
||||||
|
{translation.abbreviation}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
@@ -2129,7 +2257,7 @@ function App() {
|
|||||||
id="search"
|
id="search"
|
||||||
value={query}
|
value={query}
|
||||||
onChange={(event) => setQuery(event.target.value)}
|
onChange={(event) => setQuery(event.target.value)}
|
||||||
placeholder='Search KJV, reference, or Strong's'
|
placeholder={`Search ${activeTranslation?.abbreviation ?? 'Bible'}, reference, or Strong's`}
|
||||||
/>
|
/>
|
||||||
<button type="submit">Go</button>
|
<button type="submit">Go</button>
|
||||||
</form>
|
</form>
|
||||||
@@ -2149,7 +2277,7 @@ function App() {
|
|||||||
{viewMode === 'compare' ? (
|
{viewMode === 'compare' ? (
|
||||||
<div className="page-system compare-system" aria-label="Compare translations">
|
<div className="page-system compare-system" aria-label="Compare translations">
|
||||||
<div className="compare-pages">
|
<div className="compare-pages">
|
||||||
{renderBiblePage('KJV')}
|
{renderBiblePage()}
|
||||||
<article className="bible-page compare-placeholder" aria-label="Second translation placeholder">
|
<article className="bible-page compare-placeholder" aria-label="Second translation placeholder">
|
||||||
<header>
|
<header>
|
||||||
<span>Second Translation</span>
|
<span>Second Translation</span>
|
||||||
@@ -2168,7 +2296,7 @@ function App() {
|
|||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<div className={`page-system ${viewMode} ${pageMotion}`}>
|
<div className={`page-system ${viewMode} ${pageMotion}`}>
|
||||||
{renderBiblePage('KJV')}
|
{renderBiblePage()}
|
||||||
{renderFootnoteStrip()}
|
{renderFootnoteStrip()}
|
||||||
{renderPageTurner()}
|
{renderPageTurner()}
|
||||||
{renderBookTabs()}
|
{renderBookTabs()}
|
||||||
|
|||||||
+130
-29
@@ -1,5 +1,14 @@
|
|||||||
import { invoke } from '@tauri-apps/api/core'
|
import { invoke } from '@tauri-apps/api/core'
|
||||||
import type { CommentaryEntry, DictionaryEntry, Footnote, LibraryState, StrongLink, Tag, Verse } from './types'
|
import type {
|
||||||
|
CommentaryEntry,
|
||||||
|
DictionaryEntry,
|
||||||
|
Footnote,
|
||||||
|
LibraryState,
|
||||||
|
StrongLink,
|
||||||
|
Tag,
|
||||||
|
TranslationResource,
|
||||||
|
Verse,
|
||||||
|
} from './types'
|
||||||
|
|
||||||
type TauriWindow = Window &
|
type TauriWindow = Window &
|
||||||
typeof globalThis & {
|
typeof globalThis & {
|
||||||
@@ -8,9 +17,11 @@ type TauriWindow = Window &
|
|||||||
|
|
||||||
const browserTagKey = 'libre-study-browser-tags'
|
const browserTagKey = 'libre-study-browser-tags'
|
||||||
const libreBibleDevRoot = '/@fs/W:/libre-bible-data/packages/json'
|
const libreBibleDevRoot = '/@fs/W:/libre-bible-data/packages/json'
|
||||||
|
const defaultTranslationId = 'kjv-eng-kjv2006'
|
||||||
|
|
||||||
type ImportedVerse = {
|
type ImportedVerse = {
|
||||||
id: string
|
id: string
|
||||||
|
translation_id: string
|
||||||
book: string
|
book: string
|
||||||
chapter: number
|
chapter: number
|
||||||
verse: number
|
verse: number
|
||||||
@@ -28,11 +39,28 @@ type ImportedFootnote = {
|
|||||||
id: number
|
id: number
|
||||||
verse_id: string
|
verse_id: string
|
||||||
note_index: number
|
note_index: number
|
||||||
|
note_type?: string
|
||||||
|
label?: string
|
||||||
caller: string
|
caller: string
|
||||||
reference: string
|
reference: string
|
||||||
text: string
|
text: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ImportedCatalog = {
|
||||||
|
resources: Array<{
|
||||||
|
id: string
|
||||||
|
resource_type: string
|
||||||
|
title: string
|
||||||
|
short_title?: string
|
||||||
|
abbreviation?: string
|
||||||
|
license?: {
|
||||||
|
name?: string
|
||||||
|
attribution?: string
|
||||||
|
restricted_notes?: string
|
||||||
|
}
|
||||||
|
}>
|
||||||
|
}
|
||||||
|
|
||||||
type ImportedStrongEntry = {
|
type ImportedStrongEntry = {
|
||||||
strongs_number: string
|
strongs_number: string
|
||||||
language: 'hebrew' | 'greek'
|
language: 'hebrew' | 'greek'
|
||||||
@@ -85,9 +113,11 @@ type BrowserStrongData = {
|
|||||||
verseIdsByStrong: Map<string, Set<string>>
|
verseIdsByStrong: Map<string, Set<string>>
|
||||||
}
|
}
|
||||||
|
|
||||||
let browserLibraryCache: Promise<Verse[]> | null = null
|
let browserCatalogCache: Promise<ImportedCatalog> | null = null
|
||||||
let browserStrongDataCache: Promise<BrowserStrongData> | null = null
|
const browserLibraryCache = new Map<string, Promise<Verse[]>>()
|
||||||
let browserFootnotesCache: Promise<Map<string, Footnote[]>> | null = null
|
const browserStrongDataCache = new Map<string, Promise<BrowserStrongData>>()
|
||||||
|
const browserFootnotesCache = new Map<string, Promise<Map<string, Footnote[]>>>()
|
||||||
|
const browserStudyNotesCache = new Map<string, Promise<Map<string, Footnote[]>>>()
|
||||||
let browserCommentaryCache: Promise<Map<string, CommentaryEntry[]>> | null = null
|
let browserCommentaryCache: Promise<Map<string, CommentaryEntry[]>> | null = null
|
||||||
let browserDictionaryCache: Promise<Map<string, DictionaryEntry>> | null = null
|
let browserDictionaryCache: Promise<Map<string, DictionaryEntry>> | null = null
|
||||||
const browserVerseSourceIds = new Map<number, string>()
|
const browserVerseSourceIds = new Map<number, string>()
|
||||||
@@ -230,26 +260,33 @@ function writeBrowserTags(tagsByVerse: Record<number, Tag[]>) {
|
|||||||
localStorage.setItem(browserTagKey, JSON.stringify(tagsByVerse))
|
localStorage.setItem(browserTagKey, JSON.stringify(tagsByVerse))
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadBrowserVerses() {
|
async function loadBrowserVersesForTranslation(translationId = defaultTranslationId) {
|
||||||
browserLibraryCache ??= loadLibreBibleDevVerses().catch((error) => {
|
if (!browserLibraryCache.has(translationId)) {
|
||||||
|
browserLibraryCache.set(translationId, loadLibreBibleDevVerses(translationId).catch((error) => {
|
||||||
console.warn('Falling back to browser seed data.', error)
|
console.warn('Falling back to browser seed data.', error)
|
||||||
return fallbackBrowserVerses
|
return fallbackBrowserVerses
|
||||||
})
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
const verses = await browserLibraryCache
|
const verses = await browserLibraryCache.get(translationId)!
|
||||||
const strongData = await loadBrowserStrongData().catch((error) => {
|
const strongData = await loadBrowserStrongData(translationId).catch((error) => {
|
||||||
console.warn('Unable to load browser Strong data.', error)
|
console.warn('Unable to load browser Strong data.', error)
|
||||||
return null
|
return null
|
||||||
})
|
})
|
||||||
const footnotes = await loadBrowserFootnotes().catch((error) => {
|
const footnotes = await loadBrowserFootnotes(translationId).catch((error) => {
|
||||||
console.warn('Unable to load browser footnotes.', error)
|
console.warn('Unable to load browser footnotes.', error)
|
||||||
return new Map<string, Footnote[]>()
|
return new Map<string, Footnote[]>()
|
||||||
})
|
})
|
||||||
|
const studyNotes = await loadBrowserStudyNotes(translationId).catch((error) => {
|
||||||
|
console.warn('Unable to load browser study notes.', error)
|
||||||
|
return new Map<string, Footnote[]>()
|
||||||
|
})
|
||||||
const tagsByVerse = readBrowserTags()
|
const tagsByVerse = readBrowserTags()
|
||||||
return verses.map((verse) => ({
|
return verses.map((verse) => ({
|
||||||
...verse,
|
...verse,
|
||||||
strongs: strongData?.linksByVerse.get(browserVerseKey(verse)) ?? verse.strongs,
|
strongs: strongData?.linksByVerse.get(browserVerseKey(verse)) ?? verse.strongs,
|
||||||
footnotes: footnotes.get(browserVerseKey(verse)) ?? verse.footnotes ?? [],
|
footnotes: footnotes.get(browserVerseKey(verse)) ?? verse.footnotes ?? [],
|
||||||
|
studyNotes: studyNotes.get(browserVerseKey(verse)) ?? verse.studyNotes ?? [],
|
||||||
tags: tagsByVerse[verse.id] ?? [],
|
tags: tagsByVerse[verse.id] ?? [],
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
@@ -264,24 +301,41 @@ function allBrowserTags() {
|
|||||||
return [...unique.values()].sort((a, b) => a.label.localeCompare(b.label))
|
return [...unique.values()].sort((a, b) => a.label.localeCompare(b.label))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getLibraryState(): Promise<LibraryState> {
|
export async function getTranslations(): Promise<TranslationResource[]> {
|
||||||
|
if (inTauri()) return []
|
||||||
|
|
||||||
|
const catalog = await loadBrowserCatalog()
|
||||||
|
return catalog.resources
|
||||||
|
.filter((resource) => resource.resource_type === 'translation')
|
||||||
|
.map((resource) => ({
|
||||||
|
id: resource.id,
|
||||||
|
title: resource.title,
|
||||||
|
shortTitle: resource.short_title ?? resource.title,
|
||||||
|
abbreviation: resource.abbreviation ?? resource.short_title ?? resource.title,
|
||||||
|
licenseName: resource.license?.name ?? '',
|
||||||
|
attribution: resource.license?.attribution ?? '',
|
||||||
|
restrictions: resource.license?.restricted_notes ?? '',
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getLibraryState(translationId = defaultTranslationId): Promise<LibraryState> {
|
||||||
if (inTauri()) {
|
if (inTauri()) {
|
||||||
return invoke<LibraryState>('get_library_state')
|
return invoke<LibraryState>('get_library_state')
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
verses: await loadBrowserVerses(),
|
verses: await loadBrowserVersesForTranslation(translationId),
|
||||||
tags: allBrowserTags(),
|
tags: allBrowserTags(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function searchVerses(query: string): Promise<Verse[]> {
|
export async function searchVerses(query: string, translationId = defaultTranslationId): Promise<Verse[]> {
|
||||||
if (inTauri()) {
|
if (inTauri()) {
|
||||||
return invoke<Verse[]>('search_verses', { query })
|
return invoke<Verse[]>('search_verses', { query })
|
||||||
}
|
}
|
||||||
|
|
||||||
const trimmed = query.trim().toLowerCase()
|
const trimmed = query.trim().toLowerCase()
|
||||||
const verses = await loadBrowserVerses()
|
const verses = await loadBrowserVersesForTranslation(translationId)
|
||||||
if (!trimmed) return verses
|
if (!trimmed) return verses
|
||||||
|
|
||||||
if (trimmed.startsWith('tag:')) {
|
if (trimmed.startsWith('tag:')) {
|
||||||
@@ -292,7 +346,7 @@ export async function searchVerses(query: string): Promise<Verse[]> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (/^[gh]\d+$/.test(trimmed)) {
|
if (/^[gh]\d+$/.test(trimmed)) {
|
||||||
const strongData = await loadBrowserStrongData()
|
const strongData = await loadBrowserStrongData(translationId)
|
||||||
const matchingVerseIds = strongData.verseIdsByStrong.get(trimmed.toUpperCase()) ?? new Set()
|
const matchingVerseIds = strongData.verseIdsByStrong.get(trimmed.toUpperCase()) ?? new Set()
|
||||||
return verses.filter((verse) =>
|
return verses.filter((verse) =>
|
||||||
matchingVerseIds.has(browserVerseKey(verse)),
|
matchingVerseIds.has(browserVerseKey(verse)),
|
||||||
@@ -306,7 +360,12 @@ export async function searchVerses(query: string): Promise<Verse[]> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function addTagToVerse(verseId: number, label: string, color: string) {
|
export async function addTagToVerse(
|
||||||
|
verseId: number,
|
||||||
|
label: string,
|
||||||
|
color: string,
|
||||||
|
translationId = defaultTranslationId,
|
||||||
|
) {
|
||||||
if (inTauri()) {
|
if (inTauri()) {
|
||||||
return invoke<Verse>('add_tag_to_verse', { verseId, label, color })
|
return invoke<Verse>('add_tag_to_verse', { verseId, label, color })
|
||||||
}
|
}
|
||||||
@@ -318,7 +377,7 @@ export async function addTagToVerse(verseId: number, label: string, color: strin
|
|||||||
tagsByVerse[verseId] = existing ? current : [...current, tag]
|
tagsByVerse[verseId] = existing ? current : [...current, tag]
|
||||||
writeBrowserTags(tagsByVerse)
|
writeBrowserTags(tagsByVerse)
|
||||||
|
|
||||||
const verse = (await loadBrowserVerses()).find((item) => item.id === verseId)
|
const verse = (await loadBrowserVersesForTranslation(translationId)).find((item) => item.id === verseId)
|
||||||
if (!verse) throw new Error(`Verse ${verseId} was not found.`)
|
if (!verse) throw new Error(`Verse ${verseId} was not found.`)
|
||||||
return verse
|
return verse
|
||||||
}
|
}
|
||||||
@@ -352,9 +411,14 @@ export async function getDictionaryEntriesForTerms(terms: string[]): Promise<Dic
|
|||||||
return entries
|
return entries
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadLibreBibleDevVerses(): Promise<Verse[]> {
|
async function loadBrowserCatalog(): Promise<ImportedCatalog> {
|
||||||
|
browserCatalogCache ??= fetchJson<ImportedCatalog>(`${libreBibleDevRoot}/catalog.json`)
|
||||||
|
return browserCatalogCache
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadLibreBibleDevVerses(translationId = defaultTranslationId): Promise<Verse[]> {
|
||||||
const importedVerses = await fetchJsonl<ImportedVerse>(
|
const importedVerses = await fetchJsonl<ImportedVerse>(
|
||||||
`${libreBibleDevRoot}/kjv-eng-kjv2006/verses.jsonl`,
|
`${libreBibleDevRoot}/${translationId}/verses.jsonl`,
|
||||||
)
|
)
|
||||||
|
|
||||||
browserVerseSourceIds.clear()
|
browserVerseSourceIds.clear()
|
||||||
@@ -369,6 +433,7 @@ async function loadLibreBibleDevVerses(): Promise<Verse[]> {
|
|||||||
text: verse.text,
|
text: verse.text,
|
||||||
strongs: [],
|
strongs: [],
|
||||||
footnotes: [],
|
footnotes: [],
|
||||||
|
studyNotes: [],
|
||||||
tags: [],
|
tags: [],
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -442,16 +507,39 @@ async function loadLibreBibleDevDictionary(): Promise<Map<string, DictionaryEntr
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadBrowserFootnotes(): Promise<Map<string, Footnote[]>> {
|
async function loadBrowserFootnotes(translationId = defaultTranslationId): Promise<Map<string, Footnote[]>> {
|
||||||
browserFootnotesCache ??= loadLibreBibleDevFootnotes()
|
if (!browserFootnotesCache.has(translationId)) {
|
||||||
|
browserFootnotesCache.set(translationId, loadLibreBibleDevFootnotes(translationId))
|
||||||
|
}
|
||||||
|
|
||||||
return browserFootnotesCache
|
return browserFootnotesCache.get(translationId)!
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadLibreBibleDevFootnotes(): Promise<Map<string, Footnote[]>> {
|
async function loadBrowserStudyNotes(translationId = defaultTranslationId): Promise<Map<string, Footnote[]>> {
|
||||||
|
if (!browserStudyNotesCache.has(translationId)) {
|
||||||
|
browserStudyNotesCache.set(translationId, loadLibreBibleDevStudyNotes(translationId))
|
||||||
|
}
|
||||||
|
|
||||||
|
return browserStudyNotesCache.get(translationId)!
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadLibreBibleDevFootnotes(translationId = defaultTranslationId): Promise<Map<string, Footnote[]>> {
|
||||||
const importedFootnotes = await fetchJsonl<ImportedFootnote>(
|
const importedFootnotes = await fetchJsonl<ImportedFootnote>(
|
||||||
`${libreBibleDevRoot}/kjv-eng-kjv2006/footnotes.jsonl`,
|
`${libreBibleDevRoot}/${translationId}/footnotes.jsonl`,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
return mapImportedNotes(importedFootnotes)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadLibreBibleDevStudyNotes(translationId = defaultTranslationId): Promise<Map<string, Footnote[]>> {
|
||||||
|
const importedFootnotes = await fetchJsonl<ImportedFootnote>(
|
||||||
|
`${libreBibleDevRoot}/${translationId}/study-notes.jsonl`,
|
||||||
|
)
|
||||||
|
|
||||||
|
return mapImportedNotes(importedFootnotes)
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapImportedNotes(importedFootnotes: ImportedFootnote[]): Map<string, Footnote[]> {
|
||||||
const footnotes = new Map<string, Footnote[]>()
|
const footnotes = new Map<string, Footnote[]>()
|
||||||
|
|
||||||
for (const note of importedFootnotes) {
|
for (const note of importedFootnotes) {
|
||||||
@@ -463,6 +551,8 @@ async function loadLibreBibleDevFootnotes(): Promise<Map<string, Footnote[]>> {
|
|||||||
caller: note.caller,
|
caller: note.caller,
|
||||||
reference: note.reference,
|
reference: note.reference,
|
||||||
text: note.text,
|
text: note.text,
|
||||||
|
label: note.label,
|
||||||
|
noteType: note.note_type,
|
||||||
})
|
})
|
||||||
footnotes.set(note.verse_id, current)
|
footnotes.set(note.verse_id, current)
|
||||||
}
|
}
|
||||||
@@ -470,15 +560,17 @@ async function loadLibreBibleDevFootnotes(): Promise<Map<string, Footnote[]>> {
|
|||||||
return footnotes
|
return footnotes
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadBrowserStrongData(): Promise<BrowserStrongData> {
|
async function loadBrowserStrongData(translationId = defaultTranslationId): Promise<BrowserStrongData> {
|
||||||
browserStrongDataCache ??= loadLibreBibleDevStrongData()
|
if (!browserStrongDataCache.has(translationId)) {
|
||||||
|
browserStrongDataCache.set(translationId, loadLibreBibleDevStrongData(translationId))
|
||||||
|
}
|
||||||
|
|
||||||
return browserStrongDataCache
|
return browserStrongDataCache.get(translationId)!
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadLibreBibleDevStrongData(): Promise<BrowserStrongData> {
|
async function loadLibreBibleDevStrongData(translationId = defaultTranslationId): Promise<BrowserStrongData> {
|
||||||
const [importedLinks, importedEntries] = await Promise.all([
|
const [importedLinks, importedEntries] = await Promise.all([
|
||||||
fetchJsonl<ImportedStrongLink>(`${libreBibleDevRoot}/kjv-eng-kjv2006/strongs-links.jsonl`),
|
fetchJsonl<ImportedStrongLink>(`${libreBibleDevRoot}/${translationId}/strongs-links.jsonl`),
|
||||||
fetchJsonl<ImportedStrongEntry>(`${libreBibleDevRoot}/strongs-open-scriptures/entries.jsonl`),
|
fetchJsonl<ImportedStrongEntry>(`${libreBibleDevRoot}/strongs-open-scriptures/entries.jsonl`),
|
||||||
])
|
])
|
||||||
|
|
||||||
@@ -551,3 +643,12 @@ async function fetchJsonl<T>(url: string): Promise<T[]> {
|
|||||||
.filter((line) => line.trim().length > 0)
|
.filter((line) => line.trim().length > 0)
|
||||||
.map((line) => JSON.parse(line) as T)
|
.map((line) => JSON.parse(line) as T)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function fetchJson<T>(url: string): Promise<T> {
|
||||||
|
const response = await fetch(url)
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Unable to load ${url}: ${response.status} ${response.statusText}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.json() as Promise<T>
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ export type Verse = {
|
|||||||
text: string
|
text: string
|
||||||
strongs: StrongLink[]
|
strongs: StrongLink[]
|
||||||
footnotes?: Footnote[]
|
footnotes?: Footnote[]
|
||||||
|
studyNotes?: StudyNote[]
|
||||||
commentary?: CommentaryEntry[]
|
commentary?: CommentaryEntry[]
|
||||||
dictionary?: DictionaryEntry[]
|
dictionary?: DictionaryEntry[]
|
||||||
tags: Tag[]
|
tags: Tag[]
|
||||||
@@ -34,8 +35,12 @@ export type Footnote = {
|
|||||||
caller: string
|
caller: string
|
||||||
reference: string
|
reference: string
|
||||||
text: string
|
text: string
|
||||||
|
label?: string
|
||||||
|
noteType?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type StudyNote = Footnote
|
||||||
|
|
||||||
export type CommentaryEntry = {
|
export type CommentaryEntry = {
|
||||||
id: string
|
id: string
|
||||||
resourceId: string
|
resourceId: string
|
||||||
@@ -72,3 +77,13 @@ export type LibraryState = {
|
|||||||
verses: Verse[]
|
verses: Verse[]
|
||||||
tags: Tag[]
|
tags: Tag[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type TranslationResource = {
|
||||||
|
id: string
|
||||||
|
title: string
|
||||||
|
shortTitle: string
|
||||||
|
abbreviation: string
|
||||||
|
licenseName: string
|
||||||
|
attribution: string
|
||||||
|
restrictions: string
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user