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>
34 lines
2.0 KiB
React
34 lines
2.0 KiB
React
// A short note on a highlight or a bookmark: shown under it, added or edited in place.
|
|
import { useState } from 'react';
|
|
|
|
export default function MarkNote({ note = '', onSave, label = 'note', className = '' }) {
|
|
const [editing, setEditing] = useState(false);
|
|
const [draft, setDraft] = useState('');
|
|
const open = () => { setDraft(note); setEditing(true); };
|
|
const save = () => { onSave(draft.trim()); setEditing(false); };
|
|
|
|
if (editing) {
|
|
return (
|
|
<div className={className}>
|
|
<textarea autoFocus value={draft} maxLength={500} rows={2} aria-label={`Your ${label}`} onChange={(e) => setDraft(e.target.value)}
|
|
onKeyDown={(e) => { if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) { e.preventDefault(); save(); } else if (e.key === 'Escape') { e.stopPropagation(); setEditing(false); } }}
|
|
className="w-full resize-y rounded-lg border border-[var(--r-line)] bg-[var(--r-bg)] px-2.5 py-1.5 text-sm text-[var(--r-text)] focus:outline-none focus:ring-2 focus:ring-[var(--r-line)]" placeholder="What does this mean to you?" />
|
|
<div className="mt-1 flex items-center gap-3 text-xs">
|
|
<button type="button" onClick={save} className="font-medium text-[var(--r-accent)] hover:underline">Save</button>
|
|
<button type="button" onClick={() => setEditing(false)} className="text-[var(--r-sub)] hover:underline">Cancel</button>
|
|
<span className="ml-auto text-[10px] text-[var(--r-sub)]">{draft.length}/500</span>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
if (!note) {
|
|
return <div className={className}><button type="button" onClick={open} className="text-xs text-[var(--r-accent)] hover:underline">+ Add {label}</button></div>;
|
|
}
|
|
return (
|
|
<div className={`${className} flex items-start gap-2`}>
|
|
<p className="min-w-0 flex-1 whitespace-pre-wrap rounded-lg bg-[var(--r-bg)] px-2.5 py-1.5 text-[13px] italic text-[var(--r-text)]">{note}</p>
|
|
<button type="button" onClick={open} aria-label={`Edit ${label}`} className="shrink-0 text-xs text-[var(--r-sub)] hover:underline">Edit</button>
|
|
</div>
|
|
);
|
|
}
|