Listen to BSB Audio
@@ -317,3 +357,109 @@ export default function HomePage() {
);
}
+
+function ReadingPlanCard({ readingPlan, createReadingPlan, clearReadingPlan, openBibleReader }) {
+ const [showForm, setShowForm] = useState(false);
+ const [planBook, setPlanBook] = useState(bookOptions[0].abbrev);
+ const [planWeeks, setPlanWeeks] = useState(4);
+
+ if (readingPlan) {
+ const pct = readingPlan.totalChapters > 0
+ ? Math.round((readingPlan.chaptersRead.length / readingPlan.totalChapters) * 100)
+ : 0;
+ const daysLeft = Math.max(0, Math.ceil((readingPlan.targetDate - Date.now()) / 86400000));
+ const chapLeft = readingPlan.totalChapters - readingPlan.chaptersRead.length;
+ const paceNeeded = daysLeft > 0 ? (chapLeft / daysLeft).toFixed(1) : '—';
+ return (
+
+
+
+
Reading Plan
+
{readingPlan.bookName}
+
+ {readingPlan.chaptersRead.length} / {readingPlan.totalChapters} chapters · {daysLeft} day{daysLeft !== 1 ? 's' : ''} left · {paceNeeded} ch/day needed
+
+
+
+
+
+
+
+
+
{pct}%
+ {readingPlan.chaptersRead.length > 0 && (
+
+ Read: ch. {readingPlan.chaptersRead.slice(0, 12).join(', ')}{readingPlan.chaptersRead.length > 12 ? '…' : ''}
+
+ )}
+
+ );
+ }
+
+ return (
+
+ {showForm ? (
+
+ Read
+
+ in
+
+
+
+
+ ) : (
+
+
+
Reading Plan
+
Set a goal to read through a book, track chapters as you go.
+
+
+
+ )}
+
+ );
+}
diff --git a/src/pages/ReaderPage.jsx b/src/pages/ReaderPage.jsx
index 4e20343..1c2a719 100644
--- a/src/pages/ReaderPage.jsx
+++ b/src/pages/ReaderPage.jsx
@@ -48,6 +48,8 @@ export default function ReaderPage() {
readerTextHighlights,
addTextHighlight,
removeTextHighlight,
+ readingPlan,
+ markChapterRead,
} = useApp();
const readerBook = bookOptions.find((b) => b.abbrev === readerBookAbbrev);
@@ -286,6 +288,19 @@ export default function ReaderPage() {
+ {readingPlan && readingPlan.bookAbbrev === readerBookAbbrev && (() => {
+ const done = readingPlan.chaptersRead.includes(readerChapter);
+ return (
+
+ );
+ })()}
+ {/* Inline ink notes — collapsible, always available without switching to annotate mode */}
+
+
+ {!collapsedSections.inlineInk && (
+
+ updateChunk(selectedChunk.id, { inkStrokes: s })}
+ />
+
+ )}
+
+
{/* Cross-references */}