Version history, notes on highlights and bookmarks, "you've studied this", prayer journal
Build and Push Docker Image / build-and-push (push) Successful in 5m13s
Build and Push Docker Image / build-and-push (push) Successful in 5m13s
- Earlier versions of a study: the server keeps compressed copies (about one per ten minutes of editing, the latest 30); Export shows what differs and puts one back (the replaced copy is kept) - Notes on highlights and bookmarks: synced, merged without loss, in exports and backups - Reader verse panel lists your studies and chunks that cover the verse, and opens them - Prayer journal: requests, who for, linked verses, days prayed, answers; synced, in backups and the account export; daily prayer reminder through ntfy - TODO.md lists the ideas not built yet Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
54cb1c217d
commit
207bfcb919
45 files changed
+1669
-75
No files matched your search
@@ -11,6 +11,7 @@ import { DiffText } from './DiffText.jsx';
|
||||
import { HIGHLIGHT_COLORS, colorTitle } from '../../utils/highlightColors.js';
|
||||
import { isWholeVerse } from '../../utils/highlightSpans.js';
|
||||
import { useIqAvailable } from '../../hooks/useIq.js';
|
||||
import MarkNote from './MarkNote.jsx';
|
||||
import VerseShareImage from '../VerseShareImage.jsx';
|
||||
import IqLexicon from '../iq/IqLexicon.jsx';
|
||||
import IqStudyAids from '../iq/IqStudyAids.jsx';
|
||||
@@ -155,7 +156,7 @@ function CompareTab({ book, chapter, verse, current }) {
|
||||
|
||||
export default function VerseSheet({
|
||||
verse, book, bookName, chapter, translation, words, crossRefs, crossRefsLoading, onLoadCrossRefs,
|
||||
bookmarkColor, onToggleBookmark, onCycleColor, onJump, onClose, request,
|
||||
bookmarkColor, onToggleBookmark, onCycleColor, bookmarkNote = '', onSetBookmarkNote, onSetHighlightNote, studiesHere = [], onOpenStudy, onPray, onJump, onClose, request,
|
||||
verseHighlights = [], colorLabels = {}, onHighlightVerse, onClearVerseHighlights, panelRef, resizeHandle,
|
||||
}) {
|
||||
const iqOn = useIqAvailable();
|
||||
@@ -204,6 +205,11 @@ export default function VerseSheet({
|
||||
<span className="block h-4 w-4 rounded-full border border-black/10" style={{ backgroundColor: bookmarkColor }} />
|
||||
</button>
|
||||
)}
|
||||
{onPray && (
|
||||
<button type="button" onClick={() => onPray(`${bookName} ${chapter}:${verse.number}`)} className={`${actionBtn} text-xs`} aria-label="Pray about this verse" title="Add a prayer about this verse">
|
||||
Pray
|
||||
</button>
|
||||
)}
|
||||
<button type="button" onClick={() => setImageOpen((o) => !o)} aria-pressed={imageOpen} className={`${actionBtn} text-xs`} aria-label="Share verse as image" title="Share as image">
|
||||
Image
|
||||
</button>
|
||||
@@ -240,6 +246,40 @@ export default function VerseSheet({
|
||||
<span className="basis-full text-[11px] text-[var(--r-sub)]">Or select words in the text to highlight just those.</span>
|
||||
</div>
|
||||
)}
|
||||
{studiesHere.length > 0 && onOpenStudy && (
|
||||
<div className="mb-3 rounded-xl border border-[var(--r-line)] p-3" aria-label="In your studies">
|
||||
<p className="mb-1.5 text-[11px] uppercase tracking-wide text-[var(--r-sub)]">You’ve studied this</p>
|
||||
<ul className="space-y-2">
|
||||
{studiesHere.slice(0, 5).map((e) => (
|
||||
<li key={`${e.projectId}/${e.chunkId}`} className="flex items-start justify-between gap-3 text-sm">
|
||||
<span className="min-w-0">
|
||||
<span className="font-medium">{e.title}</span> <span className="text-xs text-[var(--r-sub)]">{e.ref}</span>
|
||||
{e.snippet && <span className="mt-0.5 block truncate font-serif text-[13px] text-[var(--r-sub)]">{e.snippet}</span>}
|
||||
</span>
|
||||
<button type="button" onClick={() => onOpenStudy(e.projectId, e.chunkId)} className="shrink-0 text-xs font-medium text-[var(--r-accent)] hover:underline">Open</button>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
{studiesHere.length > 5 && <p className="mt-1.5 text-[11px] text-[var(--r-sub)]">and {studiesHere.length - 5} more</p>}
|
||||
</div>
|
||||
)}
|
||||
{(bookmarkColor && onSetBookmarkNote) || (verseHighlights.length > 0 && onSetHighlightNote) ? (
|
||||
<div className="mb-3 space-y-2 rounded-xl border border-[var(--r-line)] p-3" aria-label="Your notes on this verse">
|
||||
<p className="text-[11px] uppercase tracking-wide text-[var(--r-sub)]">Your notes</p>
|
||||
{bookmarkColor && onSetBookmarkNote && (
|
||||
<div>
|
||||
<p className="flex items-center gap-1.5 text-xs text-[var(--r-sub)]"><span className="h-2.5 w-2.5 rounded-full" style={{ backgroundColor: bookmarkColor }} />Bookmark</p>
|
||||
<MarkNote note={bookmarkNote} label="bookmark note" onSave={onSetBookmarkNote} className="mt-1" />
|
||||
</div>
|
||||
)}
|
||||
{onSetHighlightNote && verseHighlights.map((h) => (
|
||||
<div key={h.id}>
|
||||
<p className="flex items-center gap-1.5 truncate text-xs text-[var(--r-sub)]"><span className="h-2.5 w-2.5 shrink-0 rounded-full border border-black/10" style={{ backgroundColor: h.color }} /><span className="truncate">{isWholeVerse(h, verse.text) ? 'Whole verse' : `“${h.text}”`}</span></p>
|
||||
<MarkNote note={h.note} label="highlight note" onSave={(text) => onSetHighlightNote(h.id, text)} className="mt-1" />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : null}
|
||||
<div role="tablist" aria-label="About this verse" className="mb-3 flex gap-1.5 overflow-x-auto pb-1">
|
||||
{TABS.map(([id, label]) => (
|
||||
<button key={id} type="button" role="tab" aria-selected={tab === id} onClick={() => setTab(id)}
|
||||
|
||||
Reference in new issue
Block a user