mirror of
https://github.com/kennethreitz/kjvstudy.org.git
synced 2026-06-05 23:00:16 +00:00
edecac493b
- New og_image.py module generates custom 1200x630 social images - Images include verse title, subtitle, verse text, and branding - Caches generated images to disk for performance - Routes: /og/verse/, /og/chapter/, /og/book/, /og/topic/, /og/story/, /og/guide/ - Updated templates: verse, chapter, book, topic, story, study guide - Images use Georgia serif font matching site typography - Cream background (#fffff8) and green accent (#4a7c59) When shared on social media, pages now show custom preview images with the actual verse text instead of the generic site image. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
373 lines
13 KiB
HTML
373 lines
13 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}{{ topic_name }} - Topical Index - KJV Study{% endblock %}
|
|
{% block description %}{{ topic.description }}{% endblock %}
|
|
|
|
{% block og_image %}https://kjvstudy.org/og/topic/{{ topic_name | urlencode }}.png{% endblock %}
|
|
{% block twitter_image %}https://kjvstudy.org/og/topic/{{ topic_name | urlencode }}.png{% endblock %}
|
|
|
|
{% block structured_data %}
|
|
<script type="application/ld+json">
|
|
{
|
|
"@context": "https://schema.org",
|
|
"@type": "Article",
|
|
"headline": {{ topic_name | tojson }},
|
|
"description": {{ topic.description | tojson }},
|
|
"author": {
|
|
"@type": "Organization",
|
|
"name": "KJV Study"
|
|
},
|
|
"publisher": {
|
|
"@type": "Organization",
|
|
"name": "KJV Study",
|
|
"url": "https://kjvstudy.org"
|
|
},
|
|
"mainEntityOfPage": {
|
|
"@type": "WebPage",
|
|
"@id": "https://kjvstudy.org/topics/{{ topic_name | urlencode }}"
|
|
}
|
|
}
|
|
</script>
|
|
<script type="application/ld+json">
|
|
{
|
|
"@context": "https://schema.org",
|
|
"@type": "BreadcrumbList",
|
|
"itemListElement": [
|
|
{
|
|
"@type": "ListItem",
|
|
"position": 1,
|
|
"name": "Home",
|
|
"item": "https://kjvstudy.org"
|
|
},
|
|
{
|
|
"@type": "ListItem",
|
|
"position": 2,
|
|
"name": "Topics",
|
|
"item": "https://kjvstudy.org/topics"
|
|
},
|
|
{
|
|
"@type": "ListItem",
|
|
"position": 3,
|
|
"name": {{ topic_name | tojson }}
|
|
}
|
|
]
|
|
}
|
|
</script>
|
|
{% endblock %}
|
|
|
|
{% block head %}
|
|
<style>
|
|
.topic-overview {
|
|
max-width: 60%;
|
|
font-size: 1.2rem;
|
|
line-height: 1.9;
|
|
margin: 1.5rem 0;
|
|
}
|
|
|
|
.subtopics-container {
|
|
max-width: 70%;
|
|
margin: 2rem 0;
|
|
}
|
|
|
|
.subtopic-section {
|
|
margin: 2.5rem 0;
|
|
}
|
|
|
|
.topic-actions {
|
|
display: flex;
|
|
gap: 0.75rem;
|
|
margin: 1.25rem 0;
|
|
}
|
|
|
|
.action-btn {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
padding: 0.45rem 0.9rem;
|
|
font-size: 0.9rem;
|
|
color: var(--text-secondary);
|
|
background: var(--code-bg);
|
|
border: 1px solid var(--border-color);
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
transition: all 0.2s;
|
|
text-decoration: none;
|
|
}
|
|
|
|
.action-btn:hover {
|
|
background: var(--bg-color);
|
|
border-color: var(--link-color);
|
|
color: var(--link-color);
|
|
}
|
|
|
|
.action-btn svg {
|
|
width: 16px;
|
|
height: 16px;
|
|
}
|
|
|
|
@media print {
|
|
.topic-actions {
|
|
display: none;
|
|
}
|
|
}
|
|
|
|
.subtopic-header {
|
|
font-size: 1.5rem;
|
|
font-weight: 600;
|
|
color: var(--link-color);
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
|
|
.subtopic-description {
|
|
font-style: italic;
|
|
color: var(--text-secondary);
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.verses-list {
|
|
list-style: none;
|
|
padding: 0;
|
|
margin: 1rem 0;
|
|
}
|
|
|
|
.verse-item {
|
|
margin: 1rem 0;
|
|
padding-left: 1.5rem;
|
|
border-left: 2px solid var(--border-color);
|
|
}
|
|
|
|
.verse-ref {
|
|
font-weight: 600;
|
|
font-size: 1.05rem;
|
|
}
|
|
|
|
.verse-ref a {
|
|
color: var(--link-color);
|
|
text-decoration: none;
|
|
}
|
|
|
|
.verse-ref a:hover {
|
|
color: var(--link-hover);
|
|
border-bottom: 1px solid var(--link-hover);
|
|
}
|
|
|
|
.verse-note {
|
|
color: var(--text-secondary);
|
|
font-style: italic;
|
|
margin-left: 0.5rem;
|
|
}
|
|
|
|
.intro-text {
|
|
max-width: 60%;
|
|
font-size: 1.2rem;
|
|
line-height: 1.9;
|
|
margin: 1rem 0;
|
|
}
|
|
|
|
/* Large font mode - template-specific rules (shared rules in base.html) */
|
|
[data-font-size="large"] .subtopic-header {
|
|
font-size: 1.8rem;
|
|
}
|
|
[data-font-size="large"] .subtopic-description {
|
|
font-size: 1.2rem;
|
|
}
|
|
[data-font-size="large"] .verse-item div {
|
|
font-size: 1.2rem;
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<h1>{{ topic_name }}</h1>
|
|
<script>document.body.dataset.resourceReader = "true";</script>
|
|
<p class="subtitle">{{ topic.description }}</p>
|
|
|
|
<div class="topic-actions">
|
|
<button class="action-btn" id="listen-btn">
|
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15.536 8.464a5 5 0 010 7.072m2.828-9.9a9 9 0 010 12.728M5.586 15H4a1 1 0 01-1-1v-4a1 1 0 011-1h1.586l4.707-4.707C10.923 3.663 12 4.109 12 5v14c0 .891-1.077 1.337-1.707.707L5.586 15z" />
|
|
</svg>
|
|
Listen
|
|
</button>
|
|
{% if pdf_available and pdf_url %}
|
|
<a href="{{ pdf_url }}" class="action-btn">
|
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
|
</svg>
|
|
Download PDF
|
|
</a>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<div>
|
|
<h2>Overview</h2>
|
|
{% if topic.overview %}
|
|
{{ topic.overview | safe }}
|
|
{% else %}
|
|
<div class="topic-overview">
|
|
This topical index organizes key Scripture passages related to {{ topic_name|lower }}. Each subtopic below addresses a specific aspect of this theme, with carefully selected verses that establish biblical teaching on the subject.
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<div>
|
|
<h2>Subtopics</h2>
|
|
|
|
<div class="subtopics-container">
|
|
{% for subtopic_name, subtopic_data in topic.subtopics.items() %}
|
|
<div class="subtopic-section">
|
|
<h3 class="subtopic-header">{{ subtopic_name }}</h3>
|
|
<p class="subtopic-description">{{ subtopic_data.description }}</p>
|
|
|
|
<ul class="verses-list">
|
|
{% for verse in subtopic_data.verses %}
|
|
<li class="verse-item">
|
|
<span class="verse-ref">
|
|
{% set reference = verse.reference if verse.reference is defined else (verse.ref if verse.ref is defined else verse) %}
|
|
{% if reference is not string %}
|
|
{% set reference = reference|string %}
|
|
{% endif %}
|
|
{% set reference = reference.strip() %}
|
|
{% if reference %}
|
|
{% set ref_parts = reference.rsplit(' ', 1) %}
|
|
{% if ref_parts|length == 2 %}
|
|
{% set book_name = ref_parts[0] %}
|
|
{% set chapter_verse = ref_parts[1] %}
|
|
{% if ':' in chapter_verse %}
|
|
{% set ch = chapter_verse.split(':')[0] %}
|
|
{% set v = chapter_verse.split(':')[1] %}
|
|
{% if '-' not in v %}
|
|
{# Single verse - make it a link #}
|
|
<a href="/book/{{ book_name }}/chapter/{{ ch }}/verse/{{ v }}">{{ reference }}</a>
|
|
{% else %}
|
|
{# Verse range - link to chapter with anchor #}
|
|
{% set v_start = v.split('-')[0] %}
|
|
{% set v_end = v.split('-')[1] %}
|
|
<a href="/book/{{ book_name }}/chapter/{{ ch }}#verse-{{ v_start }}-{{ v_end }}">{{ reference }}</a>
|
|
{% endif %}
|
|
{% else %}
|
|
{# No colon, just display text #}
|
|
{{ reference }}
|
|
{% endif %}
|
|
{% else %}
|
|
{{ reference }}
|
|
{% endif %}
|
|
{% endif %}
|
|
</span>
|
|
{% if verse.note is defined and verse.note %}
|
|
<span class="verse-note">— {{ verse.note }}</span>
|
|
{% endif %}
|
|
{% if verse.text is defined and verse.text %}
|
|
<div style="margin-top: 0.4rem; color: var(--text-secondary); line-height: 1.6;">
|
|
{{ verse.text }}
|
|
</div>
|
|
{% endif %}
|
|
</li>
|
|
{% endfor %}
|
|
</ul>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<h2>Study Guidance</h2>
|
|
<p class="intro-text"><span class="newthought">Topical study benefits</span> from reading verses in their broader context. Click any reference above to view the complete passage and surrounding verses. Consider comparing how different biblical authors address the same theme across various contexts and time periods.</p>
|
|
|
|
<p class="intro-text">Cross-reference study deepens understanding. Many verses listed here connect to other passages—use the cross-references provided on individual verse pages to trace theological themes throughout Scripture.</p>
|
|
</div>
|
|
|
|
<div>
|
|
<p><a href="/topics">← Back to all topics</a></p>
|
|
</div>
|
|
|
|
<script>
|
|
(function() {
|
|
// Listen button handler
|
|
var listenBtn = document.getElementById('listen-btn');
|
|
var isListening = false;
|
|
if (listenBtn) {
|
|
listenBtn.addEventListener('click', function() {
|
|
if (isListening) {
|
|
if (window.KJVSpeech) window.KJVSpeech.stop();
|
|
listenBtn.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15.536 8.464a5 5 0 010 7.072m2.828-9.9a9 9 0 010 12.728M5.586 15H4a1 1 0 01-1-1v-4a1 1 0 011-1h1.586l4.707-4.707C10.923 3.663 12 4.109 12 5v14c0 .891-1.077 1.337-1.707.707L5.586 15z" /></svg> Listen';
|
|
isListening = false;
|
|
return;
|
|
}
|
|
var paragraphs = document.querySelectorAll('.topic-overview, .intro-text, .subtopic-description, .verse-note');
|
|
var text = Array.from(paragraphs).map(function(p) {
|
|
return p.textContent.trim();
|
|
}).join(' ');
|
|
if (window.KJVSpeech && text) {
|
|
window.KJVSpeech.speak(text);
|
|
listenBtn.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 10a1 1 0 011-1h4a1 1 0 011 1v4a1 1 0 01-1 1h-4a1 1 0 01-1-1v-4z" /></svg> Stop';
|
|
isListening = true;
|
|
}
|
|
});
|
|
}
|
|
|
|
// Include overview, intro paragraphs, verse items, and action buttons
|
|
const elements = Array.from(document.querySelectorAll('.topic-overview, .intro-text, .verse-item, .action-btn'));
|
|
if (elements.length === 0) return;
|
|
|
|
let selectedIndex = -1;
|
|
|
|
function selectElement(index) {
|
|
if (selectedIndex >= 0 && selectedIndex < elements.length) {
|
|
elements[selectedIndex].style.outline = '';
|
|
elements[selectedIndex].style.outlineOffset = '';
|
|
elements[selectedIndex].classList.remove('selected');
|
|
}
|
|
selectedIndex = Math.max(0, Math.min(index, elements.length - 1));
|
|
elements[selectedIndex].style.outline = '2px solid #4a7c59';
|
|
elements[selectedIndex].style.outlineOffset = '8px';
|
|
elements[selectedIndex].classList.add('selected');
|
|
elements[selectedIndex].scrollIntoView({ behavior: 'auto', block: 'center' });
|
|
}
|
|
|
|
document.addEventListener('keydown', function(e) {
|
|
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
|
|
if (KJVNav.sidebarActive) return;
|
|
|
|
if (e.key === 'ArrowDown' || e.key === 'j') {
|
|
e.preventDefault();
|
|
if (KJVNav.isSelectionOffScreen(elements, selectedIndex)) {
|
|
selectElement(KJVNav.findFirstVisibleIndex(elements));
|
|
} else {
|
|
selectElement(selectedIndex < 0 ? 0 : selectedIndex + 1);
|
|
}
|
|
} else if (e.key === 'ArrowUp' || e.key === 'k') {
|
|
e.preventDefault();
|
|
if (KJVNav.isSelectionOffScreen(elements, selectedIndex)) {
|
|
selectElement(KJVNav.findFirstVisibleIndex(elements));
|
|
} else if (selectedIndex <= 0) {
|
|
selectElement(0);
|
|
} else {
|
|
selectElement(selectedIndex - 1);
|
|
}
|
|
} else if (e.key === 'ArrowLeft' || e.key === 'h') {
|
|
e.preventDefault();
|
|
history.back();
|
|
} else if (e.key === 'Enter' && selectedIndex >= 0) {
|
|
e.preventDefault();
|
|
var el = elements[selectedIndex];
|
|
if (el.tagName === 'A' && el.href) {
|
|
window.location.href = el.href;
|
|
} else {
|
|
var link = el.querySelector('.verse-ref a') || el.querySelector('a');
|
|
if (link) window.location.href = link.href;
|
|
}
|
|
} else if (e.key === 'Escape') {
|
|
e.preventDefault();
|
|
if (selectedIndex >= 0 && selectedIndex < elements.length) {
|
|
elements[selectedIndex].style.outline = '';
|
|
elements[selectedIndex].style.outlineOffset = '';
|
|
elements[selectedIndex].classList.remove('selected');
|
|
}
|
|
selectedIndex = -1;
|
|
}
|
|
});
|
|
})();
|
|
</script>
|
|
{% endblock %}
|