Add NLT, CSB and NASB via api.bible proxy; new app logo and icons
Build and Push Docker Image / build-and-push (push) Failing after 21s
Build and Push Docker Image / build-and-push (push) Failing after 21s
Reader and Reading Plans get a translation picker. Licensed translations are fetched through a server-side api.bible proxy so the key stays off the client. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
986d2e9331
commit
22c80d2bc3
13 files changed
+209
-20
No files matched your search
+25
-10
@@ -6,18 +6,20 @@ import AudioPlayer from '../components/AudioPlayer.jsx';
|
||||
import { bookOptions, BookmarkIcon, CopyIcon } from '../utils/bibleUtils.jsx';
|
||||
import { getPlan, getPlanSchedule, findDayIndexForRef } from '../utils/readingPlans.js';
|
||||
import DrawCanvas from './DrawCanvas.jsx';
|
||||
import { READER_TRANSLATIONS } from '../hooks/useBibleReader.js';
|
||||
|
||||
const NO_STROKES = []; // stable fallback so DrawCanvas doesn't redraw every render
|
||||
|
||||
// Copies "Book 3:16 BSB — text" to the clipboard.
|
||||
const copyVerse = (bookName, chapterNum, verseNum, text) => {
|
||||
navigator.clipboard?.writeText(`${bookName} ${chapterNum}:${verseNum} BSB — ${text}`);
|
||||
// Copies "Book 3:16 NLT — text" to the clipboard.
|
||||
const copyVerse = (bookName, chapterNum, verseNum, text, translation) => {
|
||||
navigator.clipboard?.writeText(`${bookName} ${chapterNum}:${verseNum} ${translation} — ${text}`);
|
||||
};
|
||||
|
||||
export default function ReaderPage() {
|
||||
const {
|
||||
goHome,
|
||||
readerBookAbbrev,
|
||||
readerTranslation, setReaderTranslation,
|
||||
readerChapter, setReaderChapter,
|
||||
readerVerses,
|
||||
readerTotalChapters,
|
||||
@@ -63,6 +65,7 @@ export default function ReaderPage() {
|
||||
} = useApp();
|
||||
|
||||
const readerBook = bookOptions.find((b) => b.abbrev === readerBookAbbrev);
|
||||
const readerNotice = READER_TRANSLATIONS.find((t) => t.id === readerTranslation)?.notice ?? '';
|
||||
|
||||
// Active reading plans whose schedule includes the chapter currently on screen.
|
||||
const planMatches = useMemo(() => {
|
||||
@@ -300,7 +303,7 @@ export default function ReaderPage() {
|
||||
<div className="mx-auto flex max-w-7xl flex-wrap items-center justify-between gap-4 px-4 py-5 sm:px-6 lg:px-8">
|
||||
<div>
|
||||
<p className="text-sm uppercase tracking-[0.24em] text-slate-300">Bible Study Project</p>
|
||||
<h1 className="mt-2 text-2xl font-semibold">Read the Bible (BSB)</h1>
|
||||
<h1 className="mt-2 text-2xl font-semibold">Read the Bible ({readerTranslation})</h1>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<button
|
||||
@@ -324,6 +327,16 @@ export default function ReaderPage() {
|
||||
|
||||
{/* Navigation + tools bar */}
|
||||
<div className="mb-4 flex flex-wrap items-center gap-3 rounded-3xl border border-slate-200 bg-white p-4 shadow-panel">
|
||||
<label className="text-sm text-slate-600">
|
||||
Translation{' '}
|
||||
<select
|
||||
value={readerTranslation}
|
||||
onChange={(e) => setReaderTranslation(e.target.value)}
|
||||
className="rounded-xl border border-slate-300 px-2 py-1 text-sm"
|
||||
>
|
||||
{READER_TRANSLATIONS.map((t) => <option key={t.id} value={t.id}>{t.id}</option>)}
|
||||
</select>
|
||||
</label>
|
||||
<label className="text-sm text-slate-600">
|
||||
Book{' '}
|
||||
<select
|
||||
@@ -442,7 +455,7 @@ export default function ReaderPage() {
|
||||
<button type="button"
|
||||
onClick={() => { setReaderSearchScope('bible'); if (bibleIndexStatus === 'idle') loadBibleIndex(); }}
|
||||
className={`px-2 py-1 text-xs font-semibold transition ${readerSearchScope === 'bible' ? 'bg-slate-900 text-white' : 'bg-white text-slate-600 hover:bg-slate-50'}`}>
|
||||
Whole Bible
|
||||
Whole Bible (BSB)
|
||||
</button>
|
||||
</div>
|
||||
<input
|
||||
@@ -577,8 +590,9 @@ export default function ReaderPage() {
|
||||
{!readerDrawMode && !readerPageMode && (
|
||||
<div className="rounded-3xl border border-slate-200 bg-white p-6 shadow-panel">
|
||||
<h2 className="mb-1 text-xl font-semibold text-slate-900">
|
||||
{readerBook?.name} {readerChapter} <span className="text-sm font-normal text-slate-500">(BSB)</span>
|
||||
{readerBook?.name} {readerChapter} <span className="text-sm font-normal text-slate-500">({readerTranslation})</span>
|
||||
</h2>
|
||||
{readerNotice && <p className="mb-2 text-[11px] leading-snug text-slate-400">{readerNotice}</p>}
|
||||
{readerInterlinear && (
|
||||
<p className="mb-4 text-xs text-slate-400">Click a verse number to see original words & pronunciation · bookmark icon to save · copy icon to copy</p>
|
||||
)}
|
||||
@@ -639,7 +653,7 @@ export default function ReaderPage() {
|
||||
</button>
|
||||
)}
|
||||
<button type="button"
|
||||
onClick={() => copyVerse(readerBook?.name, readerChapter, verse.number, verse.text)}
|
||||
onClick={() => copyVerse(readerBook?.name, readerChapter, verse.number, verse.text, readerTranslation)}
|
||||
className="rounded p-1 leading-none text-slate-400 hover:bg-slate-100 hover:text-slate-700"
|
||||
title="Copy verse">
|
||||
<CopyIcon />
|
||||
@@ -727,8 +741,9 @@ export default function ReaderPage() {
|
||||
onPointerUp={handleSelectionEnd}>
|
||||
<div className="px-6 pt-6 pb-3 break-inside-avoid">
|
||||
<h2 className="mb-1 text-xl font-semibold text-slate-900">
|
||||
{readerBook?.name} {readerChapter} <span className="text-sm font-normal text-slate-500">(BSB)</span>
|
||||
{readerBook?.name} {readerChapter} <span className="text-sm font-normal text-slate-500">({readerTranslation})</span>
|
||||
</h2>
|
||||
{readerNotice && <p className="mb-2 text-[11px] leading-snug text-slate-400">{readerNotice}</p>}
|
||||
{readerInterlinear
|
||||
? <p className="text-xs text-slate-400">Click a verse number to see original words & pronunciation</p>
|
||||
: <p className="text-xs text-slate-400">Hover a verse for actions</p>
|
||||
@@ -774,7 +789,7 @@ export default function ReaderPage() {
|
||||
</button>
|
||||
)}
|
||||
<button type="button"
|
||||
onClick={() => copyVerse(readerBook?.name, readerChapter, verse.number, verse.text)}
|
||||
onClick={() => copyVerse(readerBook?.name, readerChapter, verse.number, verse.text, readerTranslation)}
|
||||
className="rounded p-1 leading-none text-slate-400 hover:bg-slate-100 hover:text-slate-700"
|
||||
title="Copy verse">
|
||||
<CopyIcon />
|
||||
@@ -859,7 +874,7 @@ export default function ReaderPage() {
|
||||
onPointerUp={handleSelectionEnd}>
|
||||
<div className="mb-3">
|
||||
<span className="font-semibold text-slate-900">{readerBook?.name} {readerChapter}</span>
|
||||
<span className="ml-2 text-xs text-slate-400">BSB</span>
|
||||
<span className="ml-2 text-xs text-slate-400">{readerTranslation}</span>
|
||||
<p className="mt-1 text-xs text-slate-400 font-sans">Select text to highlight</p>
|
||||
</div>
|
||||
{!readerLoading && readerVerses.map((v) => (
|
||||
|
||||
Reference in new issue
Block a user