Files
pi-weekly-newspaper/templates/reader.html
2026-04-06 17:39:37 -04:00

110 lines
4.9 KiB
HTML

<!DOCTYPE html>
<html lang="en" data-theme="light">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ title }} — PI Weekly Reader</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css">
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
<style>
body { margin: 0; overflow: hidden; display: flex; flex-direction: column; height: 100vh; }
.reader-header {
display: flex; align-items: center; gap: 1rem;
padding: 0.5rem 1rem; border-bottom: 1px solid var(--pico-muted-border-color);
flex-shrink: 0;
}
.reader-header h2 { margin: 0; font-size: 1rem; flex: 1; }
.reader-body { display: flex; flex: 1; overflow: hidden; }
.toc-sidebar {
width: 260px; overflow-y: auto; border-right: 1px solid var(--pico-muted-border-color);
padding: 1rem; flex-shrink: 0; background: var(--pico-background-color);
}
.toc-sidebar.collapsed { display: none; }
.toc-sidebar h3 { font-size: 0.75rem; text-transform: uppercase; color: var(--pico-muted-color); margin-bottom: 0.5rem; }
.toc-item {
display: block; padding: 0.4rem 0.5rem; border-radius: var(--pico-border-radius);
font-size: 0.85rem; cursor: pointer; text-decoration: none; color: inherit;
}
.toc-item:hover { background: var(--pico-secondary-hover-background); }
.toc-item.active { background: var(--pico-primary-background); color: var(--pico-primary-inverse); }
#reader-viewport { flex: 1; overflow: hidden; }
.reader-footer {
display: flex; justify-content: space-between; align-items: center;
padding: 0.4rem 1rem; border-top: 1px solid var(--pico-muted-border-color);
flex-shrink: 0;
}
.reader-footer small { color: var(--pico-muted-color); }
</style>
</head>
<body>
<div class="reader-header">
<a href="/issues" class="outline" role="button" style="padding:0.3rem 0.8rem;">&larr; Issues</a>
<h2>{{ title }}</h2>
<button class="outline" onclick="toggleToc()" style="padding:0.3rem 0.8rem;">TOC</button>
</div>
<div class="reader-body">
<div class="toc-sidebar" id="toc-sidebar">
<h3>Table of Contents</h3>
<div id="toc-list"></div>
</div>
<div id="reader-viewport"></div>
</div>
<div class="reader-footer">
<button class="outline" id="prev-btn" onclick="rendition.prev()" style="padding:0.3rem 1rem;">&larr; Previous</button>
<small id="chapter-info"></small>
<button class="outline" id="next-btn" onclick="rendition.next()" style="padding:0.3rem 1rem;">Next &rarr;</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/epubjs/dist/epub.min.js"></script>
<script>
const book = ePub("/issues/{{ issue.id }}/download");
const rendition = book.renderTo("reader-viewport", {
width: "100%",
height: "100%",
spread: "none",
});
rendition.display();
book.loaded.navigation.then(function(nav) {
const tocList = document.getElementById("toc-list");
nav.toc.forEach(function(chapter, idx) {
const item = document.createElement("a");
item.className = "toc-item";
item.textContent = chapter.label.trim();
item.href = "#";
item.onclick = function(e) {
e.preventDefault();
rendition.display(chapter.href);
document.querySelectorAll(".toc-item").forEach(i => i.classList.remove("active"));
item.classList.add("active");
};
tocList.appendChild(item);
});
});
rendition.on("relocated", function(location) {
const current = book.navigation && book.navigation.toc
? book.navigation.toc.findIndex(ch => location.start.href.includes(ch.href.split('#')[0]))
: -1;
const total = book.navigation ? book.navigation.toc.length : 0;
if (current >= 0) {
document.getElementById("chapter-info").textContent =
`Chapter ${current + 1} of ${total}`;
document.querySelectorAll(".toc-item").forEach((item, idx) => {
item.classList.toggle("active", idx === current);
});
}
});
document.addEventListener("keydown", function(e) {
if (e.key === "ArrowLeft") rendition.prev();
if (e.key === "ArrowRight") rendition.next();
});
function toggleToc() {
document.getElementById("toc-sidebar").classList.toggle("collapsed");
}
</script>
</body>
</html>