Add Insights, Observe-first, compare, handout layouts, richer search, backup nudge

- Commentary and study aids wait for 15 words of your own Observe notes.
- Insights page: this week's own work, themes across studies, words you keep
  studying, what slipped in recalls, open follow-ups.
- Recall can record what slipped; session notes (teach it back, who came,
  group notes, follow-ups); "Pray about this" from Apply.
- Compare any two passages side by side.
- Handout layouts: standard, questions only, large print, note-taking.
- Search understands "in <book>", "last week/month/year" and a year.
- Home shows the last backup age and nudges after two weeks.
- Phone: the stage bar scrolls instead of clipping Script and Export.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
nmemmertandClaude Sonnet 5.5 committed 2026-10-14 12:42:11 -04:00
1 parent 2ddc4e1ff6
commit d8e522dd06
24 files changed
+540 -28

No files matched your search

+15 -3
View File
@@ -1,12 +1,13 @@
// Right-hand pane of the Study stage: commentary, word study, cross-references and
// people & places, all driven by the selected chunk.
import { useState } from 'react';
import { useEffect, useState } from 'react';
import CommentaryChunkTab from './CommentaryChunkTab.jsx';
import WordStudyTab from './WordStudyTab.jsx';
import CrossRefsTab from './CrossRefsTab.jsx';
import PeopleChunkTab from './PeopleChunkTab.jsx';
import IqChunkAids from '../iq/IqChunkAids.jsx';
import { useIqAvailable } from '../../hooks/useIq.js';
import { OBSERVE_FIRST_MIN, observeFirstDone, ownWords } from '../../utils/ownWork.js';
export const RESOURCE_TABS = [
{ id: 'commentary', label: 'Commentary' },
@@ -20,6 +21,10 @@ export default function ResourcePane({ tab, setTab, chunk, chapter, chapterIndex
const iq = useIqAvailable();
const tabs = RESOURCE_TABS.filter((t) => !t.needsIq || iq);
if (tab === 'aids' && !iq) tab = 'commentary';
// Commentary-type tabs wait for your own Observe notes (per chunk, until you choose to look anyway).
const [unlocked, setUnlocked] = useState(() => new Set());
useEffect(() => { setUnlocked(new Set()); }, [chunk.id]);
const held = (tab === 'commentary' || tab === 'aids') && !observeFirstDone(chunk) && !unlocked.has(chunk.id);
const counts = { words: chunk.greekWords.length, refs: (chunk.crossReferences ?? []).length };
return (
<div className="flex min-h-0 flex-1 flex-col">
@@ -32,11 +37,18 @@ export default function ResourcePane({ tab, setTab, chunk, chapter, chapterIndex
))}
</div>
<div role="tabpanel" className="min-h-0 flex-1 overflow-y-auto px-4 py-4">
{tab === 'commentary' && <CommentaryChunkTab resources={resources} chunk={chunk} project={project} chapterIndex={chapterIndex} updateChunk={updateChunk} notify={notify} />}
{held && (
<div role="note" className="rounded-xl border border-[var(--r-line)] bg-[var(--r-bar)] p-4 text-sm">
<p className="font-medium">Observe first</p>
<p className="mt-1 text-xs text-[var(--r-sub)]">Write what you notice in the text before reading what others say. Commentary opens after {OBSERVE_FIRST_MIN} words of your own Observe notes ({ownWords(chunk, 'observation')} so far).</p>
<button type="button" onClick={() => setUnlocked((u) => new Set(u).add(chunk.id))} className="mt-2 text-xs text-[var(--r-accent)] hover:underline">Show it anyway</button>
</div>
)}
{!held && tab === 'commentary' && <CommentaryChunkTab resources={resources} chunk={chunk} project={project} chapterIndex={chapterIndex} updateChunk={updateChunk} notify={notify} />}
{tab === 'words' && <WordStudyTab wordStudy={wordStudy} chunk={chunk} openWordId={openWordId} setOpenWordId={setOpenWordId} />}
{tab === 'refs' && <CrossRefsTab crossRefs={crossRefs} chunk={chunk} />}
{tab === 'people' && <PeopleChunkTab book={chapter.bookAbbrev} chapter={chapter.chapter} chunk={chunk} />}
{tab === 'aids' && <IqChunkAids chunk={chunk} chapter={chapter} updateChunk={updateChunk} notify={notify} />}
{!held && tab === 'aids' && <IqChunkAids chunk={chunk} chapter={chapter} updateChunk={updateChunk} notify={notify} />}
</div>
</div>
);