diff --git a/CHANGELOG.md b/CHANGELOG.md index 15c1440..7552718 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## 0.1.3 - 2026-07-12 + +- Phase 1 page-reader prototype: replaced the sidebar-first app with a Bible-page study desk. +- Added view modes for Bible page, Bible plus study margin, and Bible plus explicit notes workspace. +- Added a two-column page display, dynamic study margin, notes workspace, and footnote strip. +- Kept pages as dynamic verse chunks that can cross chapter boundaries. + ## 0.1.2 - 2026-07-12 - Wired Libre Study to import the LibreBible Strong's dictionary package alongside KJV Strong's links. diff --git a/package-lock.json b/package-lock.json index 86ad69c..07fa1ea 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "libre-study", - "version": "0.1.2", + "version": "0.1.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "libre-study", - "version": "0.1.2", + "version": "0.1.3", "dependencies": { "@tauri-apps/api": "^2.11.1", "react": "^19.2.7", diff --git a/package.json b/package.json index 868f421..498afd6 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "libre-study", "private": true, - "version": "0.1.2", + "version": "0.1.3", "type": "module", "scripts": { "dev": "vite --host 127.0.0.1", diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index aa7101e..a874e2c 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -77,7 +77,7 @@ checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3" [[package]] name = "app" -version = "0.1.2" +version = "0.1.3" dependencies = [ "log", "rusqlite", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 3cbec10..3bdd8c5 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "app" -version = "0.1.2" +version = "0.1.3" description = "A Tauri App" authors = ["you"] license = "" diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 611f975..77c6990 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.2", + "version": "0.1.3", "identifier": "org.librestudy.desktop", "build": { "frontendDist": "../dist", diff --git a/src/App.css b/src/App.css index 97e2ac6..9db7a71 100644 --- a/src/App.css +++ b/src/App.css @@ -1,93 +1,91 @@ .app-shell { display: grid; - grid-template-columns: 360px minmax(0, 1fr); + grid-template-columns: 58px minmax(0, 1fr); min-height: 100vh; - background: #f7f7f4; + background: #ece9df; color: #202124; } -.sidebar { +.tool-rail { display: flex; flex-direction: column; - gap: 22px; - min-height: 100vh; - padding: 24px; - background: #20231f; - color: #f6f3eb; - border-right: 1px solid #d8d5cb; - box-sizing: border-box; -} - -.brand-block { - display: flex; - gap: 14px; + gap: 12px; align-items: center; + padding: 18px 10px; + background: #1f231f; + border-right: 1px solid #d1cab9; } .brand-mark { display: grid; place-items: center; - flex: 0 0 44px; - width: 44px; - height: 44px; + width: 42px; + height: 42px; border-radius: 8px; background: #d6a84f; color: #161711; font-weight: 800; - font-size: 16px; + font-size: 15px; } -.brand-block h1, -.reader h2, -.panel h3, -.strong-entry h4 { +.tool-rail button { + width: 38px; + height: 38px; + padding: 0; + border-color: #3c4239; + background: #2c302a; + color: #f6f3eb; +} + +.reader { + display: grid; + grid-template-rows: auto minmax(0, 1fr) auto; + gap: 16px; + min-width: 0; + height: 100vh; + padding: 18px; + box-sizing: border-box; +} + +.top-bar { + display: grid; + grid-template-columns: minmax(260px, 1fr) minmax(320px, 520px); + gap: 18px; + align-items: end; +} + +.version-block h1, +.study-margin h2, +.notes-workspace h2, +.strong-focus h4 { margin: 0; letter-spacing: 0; } -.brand-block h1 { - color: #ffffff; - font-size: 22px; - line-height: 1.1; +.version-block h1 { + margin-top: 3px; + font-size: 24px; + line-height: 1.15; } -.brand-block p, -.empty-text, -.result small, -.panel-note, -.translit { - margin: 0; - color: #a9ada3; - font-size: 13px; - line-height: 1.4; -} - -.search-box, -.tag-filter, -.result-list { - display: flex; - flex-direction: column; - gap: 10px; -} - -.search-box label, -.section-title { - color: #d9d4c4; +.eyebrow { + color: #657061; font-size: 12px; - font-weight: 700; + font-weight: 800; letter-spacing: 0; text-transform: uppercase; } -.search-row, -.tag-editor { - display: flex; +.search-box { + display: grid; + grid-template-columns: minmax(0, 1fr) auto; gap: 8px; } -input { - min-width: 0; +input, +textarea { width: 100%; + min-width: 0; border: 1px solid #bdb8aa; border-radius: 6px; padding: 10px 12px; @@ -98,6 +96,12 @@ input { box-sizing: border-box; } +textarea { + min-height: 420px; + resize: none; + line-height: 1.5; +} + button { border: 1px solid transparent; border-radius: 6px; @@ -111,120 +115,145 @@ button { } button:focus-visible, -input:focus-visible { +input:focus-visible, +textarea:focus-visible { outline: 2px solid #5d7f71; outline-offset: 2px; } -.tag-list, -.reader-tags { - display: flex; - flex-wrap: wrap; - gap: 8px; -} - -.tag-chip { - padding: 6px 9px; - background: transparent; - color: #f6f3eb; - border: 1px solid #5d7f71; - font-size: 12px; -} - -.result-list { +.study-desk { + display: grid; + grid-template-columns: minmax(520px, 1.3fr) minmax(220px, 0.38fr) minmax(320px, 0.7fr); + gap: 16px; min-height: 0; - overflow: auto; - padding-right: 4px; } -.result { - display: flex; - flex-direction: column; - gap: 4px; - width: 100%; - padding: 12px; - text-align: left; - background: #2c302a; - color: #f8f5ed; - border: 1px solid #3d4339; +.study-desk.page { + grid-template-columns: minmax(680px, 980px); + justify-content: center; } -.result.active { - border-color: #d6a84f; - background: #363b33; +.study-desk.study { + grid-template-columns: minmax(560px, 1fr) minmax(260px, 360px); } -.result span { - font-weight: 800; +.study-desk.notes { + grid-template-columns: minmax(520px, 1.3fr) minmax(220px, 0.38fr) minmax(320px, 0.7fr); } -.reader { - display: flex; - flex-direction: column; - gap: 22px; - padding: 30px; - min-width: 0; - box-sizing: border-box; -} - -.reader-header { - display: flex; - justify-content: space-between; - gap: 20px; - align-items: flex-start; -} - -.eyebrow { - color: #657061; - font-size: 12px; - font-weight: 800; - letter-spacing: 0; - text-transform: uppercase; -} - -.reader h2 { - margin-top: 4px; - color: #202124; - font-size: 34px; - line-height: 1.1; -} - -.reader-tags span { - border-radius: 999px; - color: white; - font-size: 12px; - font-weight: 700; - padding: 5px 9px; -} - -.verse-card, -.panel, +.bible-page, +.study-margin, +.notes-workspace, +.footnote-strip, .status-panel { - border: 1px solid #dad6cc; + border: 1px solid #d4cdbd; border-radius: 8px; - background: #fffef9; + background: #fffef8; box-shadow: 0 12px 30px rgba(44, 48, 42, 0.08); } -.verse-card { - padding: 30px; +.bible-page { + display: grid; + grid-template-rows: auto minmax(0, 1fr) auto; + min-height: 0; + padding: 26px 30px; } -.verse-card p { - margin: 0; - color: #1f211e; +.bible-page header, +.bible-page footer { + display: flex; + justify-content: space-between; + gap: 16px; + align-items: center; + color: #6b675d; + font-size: 12px; + font-weight: 800; + text-transform: uppercase; +} + +.bible-page footer { + padding-top: 16px; + border-top: 1px solid #e8e2d4; + text-transform: none; +} + +.bible-page footer span { + color: #6b675d; + font-size: 13px; + font-weight: 700; +} + +.page-columns { + column-count: 2; + column-gap: 34px; + column-rule: 1px solid #eee7d8; + overflow: hidden; + padding: 22px 0; font-family: Georgia, 'Times New Roman', serif; - font-size: 31px; - line-height: 1.55; +} + +.page-verse { + break-inside: avoid; + margin: 0 0 9px; + padding: 3px 4px; + color: #1f211e; + font-size: 18px; + line-height: 1.58; + cursor: pointer; +} + +.page-verse.selected { + border-radius: 5px; + background: #f4ecd8; +} + +.page-verse sup { + margin-right: 5px; + color: #8a6d2e; + font-family: ui-sans-serif, system-ui, sans-serif; + font-size: 10px; + font-weight: 800; +} + +.study-margin, +.notes-workspace { + min-height: 0; + overflow: auto; + padding: 16px; +} + +.panel-header { + display: flex; + justify-content: space-between; + gap: 12px; + align-items: center; + padding-bottom: 12px; + border-bottom: 1px solid #e4e0d6; +} + +.panel-header h2, +.notes-workspace h2 { + font-size: 18px; +} + +.panel-header span, +.margin-reference, +.translit { + color: #657061; + font-size: 13px; + line-height: 1.4; +} + +.margin-reference { + margin: 12px 0; + font-weight: 800; } .linked-word-list { display: flex; flex-wrap: wrap; gap: 8px; - margin-top: 22px; - padding-top: 18px; - border-top: 1px solid #e4e0d6; + margin: 0 0 14px; } .word-link { @@ -249,55 +278,16 @@ input:focus-visible { background: #e7f0eb; } -.study-grid { - display: grid; - grid-template-columns: minmax(0, 1.35fr) minmax(280px, 0.65fr); - gap: 18px; - align-items: start; -} - -.panel { - padding: 18px; -} - -.panel-header { - display: flex; - justify-content: space-between; - align-items: center; - gap: 12px; - padding-bottom: 14px; - border-bottom: 1px solid #e4e0d6; -} - -.panel h3 { - color: #2a2d28; - font-size: 18px; -} - -.panel-header span { - color: #657061; - font-size: 13px; - font-weight: 800; -} - -.strong-list { - display: grid; - gap: 12px; - padding-top: 14px; -} - .strong-focus { display: grid; gap: 9px; - margin-top: 14px; padding: 16px; border: 1px solid #b9cabf; border-radius: 8px; background: #eef6f1; } -.strong-focus div, -.strong-entry div { +.strong-focus div { display: flex; justify-content: space-between; gap: 12px; @@ -307,7 +297,6 @@ input:focus-visible { } .strong-focus h4 { - margin: 0; color: #1f211e; font-size: 22px; } @@ -326,62 +315,82 @@ input:focus-visible { color: #ffffff; } -.strong-entry { - border: 1px solid #e4e0d6; - border-radius: 8px; - padding: 14px; - background: #fbfaf4; - cursor: pointer; +.notes-workspace { + display: flex; + flex-direction: column; + gap: 14px; } -.strong-entry.active { - border-color: #2f6f5e; - background: #f3faf6; +.tag-editor, +.reader-tags { + display: flex; + gap: 8px; } -.strong-entry h4 { - margin-top: 8px; - color: #1f211e; - font-size: 18px; +.reader-tags { + flex-wrap: wrap; } -.strong-entry p { - margin: 8px 0 0; - color: #3f443d; - font-size: 14px; - line-height: 1.5; -} - -.tag-editor { - margin-top: 16px; -} - -.panel-note { - margin-top: 14px; - color: #657061; +.reader-tags span { + border-radius: 999px; + color: white; + font-size: 12px; + font-weight: 700; + padding: 5px 9px; } +.footnote-strip, .status-panel { - padding: 28px; + padding: 14px 18px; color: #3f443d; } +.footnote-strip { + display: flex; + gap: 12px; + align-items: center; + min-height: 46px; +} + +.footnote-strip span { + color: #657061; +} + .status-panel.error { border-color: #b42318; color: #b42318; } -@media (max-width: 900px) { - .app-shell, - .study-grid { +@media (max-width: 1200px) { + .study-desk { + grid-template-columns: minmax(0, 1fr); + } + + .reader { + height: auto; + min-height: 100vh; + } + + .bible-page { + min-height: 760px; + } +} + +@media (max-width: 760px) { + .app-shell { grid-template-columns: 1fr; } - .sidebar { - min-height: auto; + .tool-rail { + flex-direction: row; + justify-content: flex-start; } - .reader-header { - flex-direction: column; + .top-bar { + grid-template-columns: 1fr; + } + + .page-columns { + column-count: 1; } } diff --git a/src/App.tsx b/src/App.tsx index 0527f95..7d476aa 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,6 +1,6 @@ import { useEffect, useMemo, useState } from 'react' import { addTagToVerse, getLibraryState, searchVerses } from './studyClient' -import type { Tag, Verse } from './types' +import type { Verse } from './types' import './App.css' const tagColors = ['#2f6f5e', '#735c0f', '#8a3ffc', '#b42318', '#245ba7'] @@ -10,12 +10,13 @@ function reference(verse: Verse) { } function App() { + const [viewMode, setViewMode] = useState<'page' | 'study' | 'notes'>('notes') const [query, setQuery] = useState('') const [verses, setVerses] = useState([]) - const [tags, setTags] = useState([]) const [selectedId, setSelectedId] = useState(null) const [selectedStrong, setSelectedStrong] = useState(null) const [tagInput, setTagInput] = useState('') + const [noteDraft, setNoteDraft] = useState('') const [loading, setLoading] = useState(true) const [error, setError] = useState(null) @@ -23,7 +24,6 @@ function App() { setError(null) const state = await getLibraryState() setVerses(state.verses) - setTags(state.tags) setSelectedId((current) => current ?? state.verses[0]?.id ?? null) } @@ -46,10 +46,6 @@ function App() { setVerses((current) => current.map((verse) => (verse.id === updated.id ? updated : verse)), ) - setTags((current) => { - const exists = current.some((tag) => tag.label.toLowerCase() === label.toLowerCase()) - return exists ? current : [...current, updated.tags[updated.tags.length - 1]] - }) setTagInput('') } @@ -70,7 +66,15 @@ function App() { selectedVerse.strongs[0] ) }, [selectedStrong, selectedVerse]) - const visibleVerses = useMemo(() => verses.slice(0, 250), [verses]) + const pageVerses = useMemo(() => { + if (!selectedVerse) return [] + const selectedIndex = verses.findIndex((verse) => verse.id === selectedVerse.id) + const start = Math.max(0, selectedIndex) + return verses.slice(start, start + 34) + }, [selectedVerse, verses]) + const pageRange = pageVerses.length + ? `${reference(pageVerses[0])} - ${reference(pageVerses[pageVerses.length - 1])}` + : 'No page loaded' function searchStrong(strong: string) { setQuery(strong) @@ -80,74 +84,11 @@ function App() { return (
- + ) : null} -
-
-

Tag This Verse

-
+ {viewMode === 'notes' ? ( +