Initial Libre Study app scaffold

This commit is contained in:
2026-07-11 21:36:30 -05:00
commit 470cde6865
46 changed files with 8226 additions and 0 deletions
+202
View File
@@ -0,0 +1,202 @@
import { invoke } from '@tauri-apps/api/core'
import type { LibraryState, Tag, Verse } from './types'
type TauriWindow = Window &
typeof globalThis & {
__TAURI_INTERNALS__?: unknown
}
const browserTagKey = 'libre-study-browser-tags'
const browserVerses: Verse[] = [
{
id: 1,
book: 'Genesis',
chapter: 1,
verse: 1,
text: 'In the beginning God created the heaven and the earth.',
tags: [],
strongs: [
{
strong: 'H7225',
lemma: 'רֵאשִׁית',
transliteration: 'reshith',
definition: 'Beginning, first, chief, the first part.',
language: 'Hebrew',
},
{
strong: 'H430',
lemma: 'אֱלֹהִים',
transliteration: 'elohim',
definition: 'God, gods, judges, rulers; plural form often used of the one true God.',
language: 'Hebrew',
},
],
},
{
id: 2,
book: 'Psalm',
chapter: 23,
verse: 1,
text: 'The LORD is my shepherd; I shall not want.',
tags: [],
strongs: [
{
strong: 'H3068',
lemma: 'יְהוָֹה',
transliteration: 'YHWH',
definition: 'The covenant name of the LORD.',
language: 'Hebrew',
},
{
strong: 'H7462',
lemma: 'רָעָה',
transliteration: 'raah',
definition: 'To shepherd, pasture, feed, tend a flock.',
language: 'Hebrew',
},
],
},
{
id: 3,
book: 'John',
chapter: 3,
verse: 16,
text: 'For God so loved the world, that he gave his only begotten Son, that whosoever believeth in him should not perish, but have everlasting life.',
tags: [],
strongs: [
{
strong: 'G25',
lemma: 'ἀγαπάω',
transliteration: 'agapao',
definition: 'To love, value, welcome, or take pleasure in.',
language: 'Greek',
},
{
strong: 'G4100',
lemma: 'πιστεύω',
transliteration: 'pisteuo',
definition: 'To believe, trust, have faith in.',
language: 'Greek',
},
],
},
{
id: 4,
book: 'Romans',
chapter: 8,
verse: 28,
text: 'And we know that all things work together for good to them that love God, to them who are the called according to his purpose.',
tags: [],
strongs: [
{
strong: 'G2822',
lemma: 'κλητός',
transliteration: 'kletos',
definition: 'Called, invited, appointed.',
language: 'Greek',
},
{
strong: 'G4286',
lemma: 'πρόθεσις',
transliteration: 'prothesis',
definition: 'Purpose, setting forth, deliberate plan.',
language: 'Greek',
},
],
},
]
function inTauri() {
return Boolean((window as TauriWindow).__TAURI_INTERNALS__)
}
function readBrowserTags(): Record<number, Tag[]> {
const raw = localStorage.getItem(browserTagKey)
if (!raw) return {}
try {
return JSON.parse(raw) as Record<number, Tag[]>
} catch {
return {}
}
}
function writeBrowserTags(tagsByVerse: Record<number, Tag[]>) {
localStorage.setItem(browserTagKey, JSON.stringify(tagsByVerse))
}
function hydrateBrowserVerses() {
const tagsByVerse = readBrowserTags()
return browserVerses.map((verse) => ({
...verse,
tags: tagsByVerse[verse.id] ?? [],
}))
}
function allBrowserTags() {
const unique = new Map<string, Tag>()
for (const tags of Object.values(readBrowserTags())) {
for (const tag of tags) {
unique.set(tag.label.toLowerCase(), tag)
}
}
return [...unique.values()].sort((a, b) => a.label.localeCompare(b.label))
}
export async function getLibraryState(): Promise<LibraryState> {
if (inTauri()) {
return invoke<LibraryState>('get_library_state')
}
return {
verses: hydrateBrowserVerses(),
tags: allBrowserTags(),
}
}
export async function searchVerses(query: string): Promise<Verse[]> {
if (inTauri()) {
return invoke<Verse[]>('search_verses', { query })
}
const trimmed = query.trim().toLowerCase()
const verses = hydrateBrowserVerses()
if (!trimmed) return verses
if (trimmed.startsWith('tag:')) {
const label = trimmed.slice(4)
return verses.filter((verse) =>
verse.tags.some((tag) => tag.label.toLowerCase() === label),
)
}
if (/^[gh]\d+$/.test(trimmed)) {
return verses.filter((verse) =>
verse.strongs.some((entry) => entry.strong.toLowerCase() === trimmed),
)
}
return verses.filter((verse) =>
`${verse.book} ${verse.chapter}:${verse.verse} ${verse.text}`
.toLowerCase()
.includes(trimmed),
)
}
export async function addTagToVerse(verseId: number, label: string, color: string) {
if (inTauri()) {
return invoke<Verse>('add_tag_to_verse', { verseId, label, color })
}
const tagsByVerse = readBrowserTags()
const current = tagsByVerse[verseId] ?? []
const existing = current.find((tag) => tag.label.toLowerCase() === label.toLowerCase())
const tag = existing ?? { id: Date.now(), label, color }
tagsByVerse[verseId] = existing ? current : [...current, tag]
writeBrowserTags(tagsByVerse)
const verse = hydrateBrowserVerses().find((item) => item.id === verseId)
if (!verse) throw new Error(`Verse ${verseId} was not found.`)
return verse
}