From c438f62e52dcd1829972d777842d05af851c1875 Mon Sep 17 00:00:00 2001 From: Jason Date: Wed, 15 Jul 2026 17:26:25 -0500 Subject: [PATCH] Complete study resource depth --- CHANGELOG.md | 9 +++ ROADMAP.md | 16 +++-- docs/study-resource-depth.md | 37 ++++++++++ package-lock.json | 4 +- package.json | 2 +- src-tauri/Cargo.lock | 2 +- src-tauri/Cargo.toml | 2 +- src-tauri/tauri.conf.json | 2 +- src/App.css | 4 +- src/App.tsx | 134 ++++++++++++++++++++++++++++++++--- src/studyClient.ts | 3 + src/types.ts | 1 + src/workspace.ts | 10 +++ vite.config.ts | 4 ++ 14 files changed, 206 insertions(+), 24 deletions(-) create mode 100644 docs/study-resource-depth.md diff --git a/CHANGELOG.md b/CHANGELOG.md index b444b2a..d2e7e00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,15 @@ - No unreleased changes. +## 0.1.34 - 2026-07-15 + +- Completed Phase 7 study resource depth for the currently packaged LibreBible resources. +- Made map and audio Bible resources optional, collapsible, session-aware, and skipped when disabled. +- Added current-session and new-session switches for contextual maps and KJV audio Bible. +- Surfaced Strong's pronunciation metadata in the definition panel when the lexicon package provides it. +- Pinned the Vite HMR websocket host to `127.0.0.1` so the active HotView tab can receive updates when opened on that address. +- Documented the current resource behavior and the remaining blocker for licensed pronunciation audio packages. + ## 0.1.33 - 2026-07-15 - Completed Phase 6 notes/export foundation for the active HotView workflow. diff --git a/ROADMAP.md b/ROADMAP.md index aa591e9..10d6409 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -55,7 +55,7 @@ Work one phase at a time. A helper, spike, or supporting infrastructure task for - Phase 4: Shared app core. Complete. - Phase 5: Storage and search foundation. Complete. - Phase 6: Notes, exports, and optional sync. Complete. -- Phase 7: Study resource depth and polish. +- Phase 7: Study resource depth and polish. Complete. Supporting infrastructure already landed but not currently active as a phase: hosted web preview, hosted login, and deploy scripting. These remain parked until the user explicitly asks for a release/deploy pass. @@ -173,16 +173,20 @@ Definition of done: ## Phase 7: Study Resource Depth +Status: complete for the currently packaged LibreBible resources. + Goal: continue turning the page into a complete study workspace. +Current status: the study margin dynamically shows Strong's definitions, translation notes, Matthew Henry commentary, Easton/Smith dictionary entries, contextual place maps, related UBS route labels, KJV audio controls, and Strong's pronunciation text when the active packages provide it. Maps and audio are now optional, collapsible, session-aware resources. Licensed pronunciation audio clips are not packaged yet, so the app does not present a fake playback control for them. + Tasks: -- Add map support from LibreBible place/gazetteer packages. -- Show map availability when a visible verse mentions a known place. -- Add audio Bible support at chapter granularity first, then verse granularity when sources provide timestamps or verse files. -- Add pronunciation audio tied to English terms, Strong's numbers, lemmas, Greek, and Hebrew where licensing permits. +- Add map support from LibreBible place/gazetteer packages. Complete. +- Show map availability when a visible verse mentions a known place. Complete. +- Add audio Bible support at chapter granularity first, then verse granularity when sources provide timestamps or verse files. Complete for KJV chapter and verse-entry playback from the packaged source. +- Add pronunciation audio tied to English terms, Strong's numbers, lemmas, Greek, and Hebrew where licensing permits. Complete for available Strong's pronunciation text; playable clips remain blocked until a licensed pronunciation-audio package exists. - Add more commentaries, dictionaries, lexicons, cross-references, and study-note packages. -- Keep every resource optional, collapsible, session-aware, and license-attributed. +- Keep every resource optional, collapsible, session-aware, and license-attributed. Complete for currently visible resource panels. Definition of done: diff --git a/docs/study-resource-depth.md b/docs/study-resource-depth.md new file mode 100644 index 0000000..3050395 --- /dev/null +++ b/docs/study-resource-depth.md @@ -0,0 +1,37 @@ +# Study Resource Depth + +Phase 7 makes the study margin behave like a resource-aware workspace for the LibreBible packages that are currently available to HotView. + +## Current Resource Surface + +- Strong's links load through the active translation and show lemma, transliteration, definition, language, and pronunciation text when the lexicon entry provides it. +- Translation footnotes and study notes hydrate by visible chapter instead of requiring whole-library note loading. +- Matthew Henry Concise commentary loads for the selected verse range. +- Easton and Smith dictionary entries load from selected verse terms, linked English surfaces, and Strong's lemma surfaces. +- Context maps load OpenBible place matches for the visible page and highlight places tied to the selected verse. +- Related UBS Project MARBLE routes are listed when route titles match visible-page places. +- KJV audio loads chapter playlists from the packaged Eliran Wong source and can play the selected verse, the full chapter, or from the selected verse forward. + +## Session Behavior + +Maps and audio are first-class session resources now. Existing and new sessions can enable or disable: + +- Strong's links +- footnotes +- commentary +- dictionary +- maps +- audio Bible +- timeline recording + +Disabled maps and audio do not load their package data in the background. The map and audio cards also persist collapsed state through the shared study settings snapshot. + +HotView is expected to run on `127.0.0.1:5174` in the active local loop. Vite's HMR websocket is pinned to that host so live updates keep flowing when the browser tab uses the same address. + +## Pronunciation Boundary + +The current Strong's package includes pronunciation text for some entries, so the definition card can show that text. The data repo does not currently include licensed pronunciation audio files tied to Strong's numbers, lemmas, Greek, Hebrew, or English terms. Until that exists, the app should not show pronunciation playback controls. + +## Next Resource Work + +Future resource depth should add new LibreBible packages first, then wire them through the same app-level resource client. Good next candidates are cross-references, additional commentaries, additional lexicons, atlas images, and licensed pronunciation-audio metadata. diff --git a/package-lock.json b/package-lock.json index 39f2ebf..bc6406f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "libre-study", - "version": "0.1.33", + "version": "0.1.34", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "libre-study", - "version": "0.1.33", + "version": "0.1.34", "dependencies": { "@tauri-apps/api": "^2.11.1", "jszip": "^3.10.1", diff --git a/package.json b/package.json index 52d497c..7de14ad 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "libre-study", "private": true, - "version": "0.1.33", + "version": "0.1.34", "type": "module", "scripts": { "dev": "vite --host 0.0.0.0", diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 5e9de55..2eeaefa 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -77,7 +77,7 @@ checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3" [[package]] name = "app" -version = "0.1.33" +version = "0.1.34" dependencies = [ "log", "rusqlite", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 7ee0451..67334e4 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "app" -version = "0.1.33" +version = "0.1.34" description = "A Tauri App" authors = ["you"] license = "" diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index e9d152c..7215d82 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "../node_modules/@tauri-apps/cli/config.schema.json", "productName": "Libre Study", - "version": "0.1.33", + "version": "0.1.34", "identifier": "org.librestudy.desktop", "build": { "frontendDist": "../dist", diff --git a/src/App.css b/src/App.css index 7cfd572..2409924 100644 --- a/src/App.css +++ b/src/App.css @@ -608,7 +608,8 @@ textarea:focus-visible { .panel-header span, .margin-reference, -.translit { +.translit, +.pronunciation { color: #657061; font-size: 13px; line-height: 1.4; @@ -1759,6 +1760,7 @@ textarea:focus-visible { .theme-dark .panel-header span, .theme-dark .margin-reference, .theme-dark .translit, +.theme-dark .pronunciation, .theme-dark .eyebrow, .theme-dark .collapse-button, .theme-dark .mini-settings, diff --git a/src/App.tsx b/src/App.tsx index f522343..f18b510 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -239,6 +239,8 @@ function App() { const [newSessionStrongs, setNewSessionStrongs] = useState(true) const [newSessionCommentary, setNewSessionCommentary] = useState(true) const [newSessionDictionary, setNewSessionDictionary] = useState(true) + const [newSessionMaps, setNewSessionMaps] = useState(true) + const [newSessionAudio, setNewSessionAudio] = useState(true) const [newSessionTimeline, setNewSessionTimeline] = useState(true) const [exportFormat, setExportFormat] = useState('json') const [importStatus, setImportStatus] = useState('') @@ -256,6 +258,8 @@ function App() { const [studyNotesCollapsed, setStudyNotesCollapsed] = useState(false) const [commentaryCollapsed, setCommentaryCollapsed] = useState(false) const [dictionaryCollapsed, setDictionaryCollapsed] = useState(false) + const [mapCollapsed, setMapCollapsed] = useState(false) + const [audioCollapsed, setAudioCollapsed] = useState(false) const [showOriginalLine, setShowOriginalLine] = useState(true) const [showLinkedLine, setShowLinkedLine] = useState(true) const [showDefinitionPanel, setShowDefinitionPanel] = useState(true) @@ -308,6 +312,8 @@ function App() { setStudyNotesCollapsed(settings.studyNotesCollapsed ?? false) setCommentaryCollapsed(settings.commentaryCollapsed ?? false) setDictionaryCollapsed(settings.dictionaryCollapsed ?? false) + setMapCollapsed(settings.mapCollapsed ?? false) + setAudioCollapsed(settings.audioCollapsed ?? false) setShowOriginalLine(settings.showOriginalLine ?? true) setShowLinkedLine(settings.showLinkedLine ?? true) setShowDefinitionPanel(settings.showDefinitionPanel ?? true) @@ -455,6 +461,8 @@ function App() { studyNotesCollapsed, commentaryCollapsed, dictionaryCollapsed, + mapCollapsed, + audioCollapsed, showOriginalLine, showLinkedLine, showDefinitionPanel, @@ -468,6 +476,8 @@ function App() { commentaryCollapsed, definitionCollapsed, dictionaryCollapsed, + mapCollapsed, + audioCollapsed, lemmaFontSize, linkedCollapsed, linkedFontSize, @@ -721,6 +731,10 @@ function App() { setMapContext({ selectedPlaces: [], pagePlaces: [], relatedRoutes: [] }) return } + if (activeSession?.mapsEnabled === false) { + setMapContext({ selectedPlaces: [], pagePlaces: [], relatedRoutes: [] }) + return + } setMapResourcesLoading(true) try { @@ -737,7 +751,7 @@ function App() { return () => { cancelled = true } - }, [pageVerses, selectedVerse]) + }, [activeSession?.mapsEnabled, pageVerses, selectedVerse]) useEffect(() => { let cancelled = false @@ -747,6 +761,11 @@ function App() { setAudioContext(null) return } + if (activeSession?.audioEnabled === false) { + setAudioContext(null) + stopAudio() + return + } setAudioResourcesLoading(true) try { @@ -769,7 +788,7 @@ function App() { return () => { cancelled = true } - }, [activeSession?.baseTranslation, selectedVerse]) + }, [activeSession?.audioEnabled, activeSession?.baseTranslation, selectedVerse]) useEffect(() => { if (!audioRef.current) return @@ -1105,6 +1124,8 @@ function App() { strongsEnabled: newSessionStrongs, commentaryEnabled: newSessionCommentary, dictionaryEnabled: newSessionDictionary, + mapsEnabled: newSessionMaps, + audioEnabled: newSessionAudio, timelineEnabled: newSessionTimeline, } setSessions((current) => [nextSession, ...current]) @@ -1122,7 +1143,9 @@ function App() { typeof patch.strongsEnabled === 'boolean' || typeof patch.footnotesEnabled === 'boolean' || typeof patch.commentaryEnabled === 'boolean' || - typeof patch.dictionaryEnabled === 'boolean' + typeof patch.dictionaryEnabled === 'boolean' || + typeof patch.mapsEnabled === 'boolean' || + typeof patch.audioEnabled === 'boolean' ) { recordTimeline({ type: 'setting', label: 'Updated session resources' }, patch) } @@ -1719,6 +1742,18 @@ function App() { } function renderMapSection() { + if (activeSession?.mapsEnabled === false) { + return ( +
+
+ + Off +
+

Maps are disabled for this session.

+
+ ) + } + const places = mapContext.pagePlaces const selectedPlaces = mapContext.selectedPlaces const routes = mapContext.relatedRoutes @@ -1726,7 +1761,22 @@ function App() { return (
-
+
{ + setMapCollapsed((current) => !current) + recordDisplaySetting('Toggled context map accordion') + }} + onKeyDown={(event) => { + if (event.key === 'Enter' || event.key === ' ') { + event.preventDefault() + setMapCollapsed((current) => !current) + recordDisplaySetting('Toggled context map accordion') + } + }} + > {places.length @@ -1736,7 +1786,7 @@ function App() { : 'None'}
- {places.length ? ( + {!mapCollapsed && places.length ? ( <>
) } function renderAudioSection() { + if (activeSession?.audioEnabled === false) { + return ( +
+
+ + Off +
+

Audio Bible is disabled for this session.

+
+ ) + } + const selectedSegment = audioContext?.selectedSegment ?? null const chapterSegments = audioContext?.chapterPlaylist?.segments ?? [] const selectedIndex = selectedSegment @@ -1800,7 +1862,22 @@ function App() { return (
-
+
{ + setAudioCollapsed((current) => !current) + recordDisplaySetting('Toggled audio Bible accordion') + }} + onKeyDown={(event) => { + if (event.key === 'Enter' || event.key === ' ') { + event.preventDefault() + setAudioCollapsed((current) => !current) + recordDisplaySetting('Toggled audio Bible accordion') + } + }} + > {audioResourcesLoading @@ -1810,7 +1887,7 @@ function App() { : 'None'}
- {audioContext && selectedSegment ? ( + {!audioCollapsed && audioContext && selectedSegment ? ( <>
) } @@ -2023,6 +2100,9 @@ function App() {

{selectedStrongEntry.lemma}

{selectedStrongEntry.transliteration}

+ {selectedStrongEntry.pronunciation ? ( +

Pronunciation: {selectedStrongEntry.pronunciation}

+ ) : null}

{selectedStrongEntry.definition}