Redesign the study side: Outline, Study, Script and Export stages
Build and Push Docker Image / build-and-push (push) Successful in 4m15s

Studies now move through a stage bar in the reader's themes. Outline splits
chunks by clicking between verses (join merges notes) and suggests chunks from
section headings. Study is three panes: outline, passage with an Observe /
Interpret / Apply accordion, and commentary / word study / cross-refs / people
& places for the chunk; original words are added by clicking them in the
interlinear. Script drafts an outline from notes; Export holds files and the
share link. Home, Import and the Pronunciation guide are restyled. Study
projects are BSB only.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
nmemmertandClaude Sonnet 5.5 committed 2026-09-29 10:13:46 -04:00
1 parent b1bc1c8ea4
commit 31abee0695
31 files changed
+1911 -2460

No files matched your search

+30
View File
@@ -0,0 +1,30 @@
// People, places and events (Theographic data) tied to the chunk's verses.
import { loadTheographic, useAsync } from '../../hooks/useVerseStudy.js';
import { PersonCard } from '../StudyPanel.jsx';
export default function PeopleChunkTab({ book, chapter, chunk }) {
const { status, data } = useAsync(() => loadTheographic(book, chapter), [book, chapter]);
if (status === 'loading') return <p className="text-sm text-[var(--r-sub)]">Loading…</p>;
if (status === 'empty') return <p className="text-sm text-[var(--r-sub)]">No people or places data for this chapter.</p>;
const inChunk = (x) => x.verses?.some((n) => n >= chunk.startVerse && n <= chunk.endVerse);
const people = data.people.filter(inChunk);
const places = [...new Map(data.places.filter(inChunk).map((p) => [p.name, p])).values()];
const events = data.events.filter(inChunk);
if (!people.length && !places.length && !events.length) return <p className="text-sm text-[var(--r-sub)]">Nothing tagged in these verses.</p>;
const head = 'mb-1 text-[11px] uppercase tracking-wide text-[var(--r-sub)]';
return (
<div className="space-y-4">
{people.length > 0 && <div><p className={head}>People</p><ul className="space-y-1">{people.map((p) => <PersonCard key={p.id} person={p} />)}</ul></div>}
{places.length > 0 && (
<div><p className={head}>Places</p>
<ul className="space-y-1">{places.map((p) => (
<li key={p.id} className="flex items-center justify-between rounded-xl bg-[var(--r-bg)] px-3 py-2 text-sm">
<span>{p.name}{p.featureType && <span className="ml-1 text-xs text-[var(--r-sub)]">{p.featureType}</span>}</span>
{p.latitude != null && <a href={`https://www.openstreetmap.org/?mlat=${p.latitude}&mlon=${p.longitude}#map=8/${p.latitude}/${p.longitude}`} target="_blank" rel="noopener noreferrer" className="text-xs text-[var(--r-accent)] hover:underline">Map ↗</a>}
</li>))}</ul></div>
)}
{events.length > 0 && <div><p className={head}>Events</p><ul className="space-y-1">{events.map((e) => <li key={e.id} className="rounded-xl bg-[var(--r-bg)] px-3 py-2 text-sm">{e.name}{e.startDate && <span className="ml-1 text-xs text-[var(--r-sub)]">approx. AD {e.startDate}</span>}</li>)}</ul></div>}
<p className="text-[10px] text-[var(--r-sub)]">People, places and events: Theographic Bible Metadata, CC BY-SA 4.0.</p>
</div>
);
}