New Commit: Switch reader to scroll mode with page markers Replaces the old single-page/page-flip reader engine with a new continuous-scroll marker script that estimates pages by word count and tracks the active page while scrolling. Updates the reader template to default to `mode-scroll`, removes page-flip mode controls and related footer UI, and switches script loading from `read-single.js` to `read-scroll-markers.js`. Also adds CSS for inline page markers and a utility script to apply the template updates.
Repository: Hestenb62/_legacy_backup
Author: Hesten Berger
Commit Message: Switch reader to scroll mode with page markers Replaces the old single-page/page-flip reader engine with a new continuous-scroll marker script that estimates pages by word count and tracks the active page while scrolling. Updates the reader template to default to , removes page-flip mode controls and related footer UI, and switches script loading from to . Also adds CSS for inline page markers and a utility script to apply the template updates.
URL: https://github.com/Hestenb62/_legacy_backup/commit/45612163a0e1b29ed38f54628e87ead2627f420f
Code Changes:
diff --git a/assets/css/reader-main.css b/assets/css/reader-main.css
index a4e57e8..4bfc6cc 100644
--- a/assets/css/reader-main.css
+++ b/assets/css/reader-main.css
@@ -12,4 +12,33 @@
@import url('./reader/read-reader-academic-citation-modal.css');
@import url('./reader/read-book-sourcing-information-moda.css');
@import url('./reader/read-responsive-breakpoints-for-rea.css');
-@import url('./reader/read-realistic-single.css');
\ No newline at end of file
+@import url('./reader/read-realistic-single.css');
+/* Inline Page Markers for Scroll Mode */
+.inline-page-marker {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ margin: 3rem 0;
+ opacity: 0.7;
+ user-select: none;
+}
+
+.inline-page-marker .marker-line {
+ flex-grow: 1;
+ border: none;
+ border-top: 1px solid var(--color-border);
+ margin: 0;
+}
+
+.inline-page-marker .marker-pill {
+ margin: 0 1rem;
+ font-size: 0.8rem;
+ text-transform: uppercase;
+ letter-spacing: 2px;
+ color: var(--color-text-muted);
+ background: var(--color-bg-surface);
+ padding: 0.25rem 1rem;
+ border-radius: 1rem;
+ border: 1px solid var(--color-border);
+}
+
diff --git a/assets/js/reader/read-scroll-markers.js b/assets/js/reader/read-scroll-markers.js
new file mode 100644
index 0000000..722d342
--- /dev/null
+++ b/assets/js/reader/read-scroll-markers.js
@@ -0,0 +1,72 @@
+/**
+ * assets/js/reader/read-scroll-markers.js
+ * Continuous scroll mode engine with inline page markers.
+ */
+document.addEventListener("DOMContentLoaded", () => {
+ const bookContent = document.getElementById("book-content");
+ if (!bookContent) return;
+
+ const WORDS_PER_PAGE = 250;
+ let wordTally = 0;
+ let currentPage = 1;
+
+ // Traverse the content to inject markers
+ // We only want to inject markers BETWEEN block-level elements (paragraphs, blockquotes, etc)
+ const blockElements = Array.from(bookContent.children);
+
+ // We start at page 1 implicitly, so we don't need a marker at the very top.
+
+ blockElements.forEach(element => {
+ // Count words in this block
+ const text = element.innerText || element.textContent || '';
+ const words = text.trim().split(/\s+/).filter(w => w.length > 0).length;
+
+ wordTally += words;
+
+ // If this element pushed us over the word threshold for a page, inject a marker BEFORE it.
+ if (wordTally >= WORDS_PER_PAGE) {
+ currentPage++;
+ wordTally = 0; // Reset tally for the next page
+
+ const marker = document.createElement("div");
+ marker.className = "inline-page-marker";
+ marker.dataset.page = currentPage;
+ marker.innerHTML = `
+ <hr class="marker-line">
+ <span class="marker-pill">Page ${currentPage}</span>
+ <hr class="marker-line">
+ `;
+
+ // Insert the marker before the current element
+ bookContent.insertBefore(marker, element);
+ }
+ });
+
+ window.TOTAL_READER_PAGES = currentPage;
+ window.CURRENT_READER_PAGE = 1; // Default to 1
+
+ // Setup IntersectionObserver to update CURRENT_READER_PAGE as user scrolls
+ const markers = document.querySelectorAll(".inline-page-marker");
+ if (markers.length > 0) {
+ const observer = new IntersectionObserver((entries) => {
+ // We want to know the highest page number that is currently above or crossing the viewport
+ // Actually, a simpler way is: find the last marker whose bounding client rect top is < window.innerHeight / 2
+ // IntersectionObserver is good, but tracking the "active" page might be easier with a scroll listener.
+ }, { threshold: 0 });
+
+ // Use scroll event for more precise "current page" tracking
+ window.addEventListener("scroll", () => {
+ let activePage = 1;
+ const thresholdY = window.innerHeight / 3; // Top third of screen
+
+ markers.forEach(marker => {
+ const rect = marker.getBoundingClientRect();
+ if (rect.top <= thresholdY) {
+ activePage = parseInt(marker.dataset.page, 10);
+ }
+ });
+
+ window.CURRENT_READER_PAGE = activePage;
+ }, { passive: true });
+ }
+});
diff --git a/assets/js/reader/read-single.js b/assets/js/reader/read-single.js
deleted file mode 100644
index f7065da..0000000
--- a/assets/js/reader/read-single.js
+++ /dev/null
@@ -1,299 +0,0 @@
- /* ==========================================================================
- 8. Single-Page Realistic Book Mode & Page-Flip Engine
- ========================================================================== */
- function initSinglePageBookMode(meta, prefsKey) {
- const bookStage = document.getElementById("book-stage");
- const bookFrame = document.getElementById("book-frame");
- const bookViewport = document.getElementById("book-page-viewport");
- const bookContent = document.getElementById("book-content");
- const prevPageBtn = document.getElementById("book-page-prev-btn");
- const nextPageBtn = document.getElementById("book-page-next-btn");
- const pageIndicator = document.getElementById("book-page-indicator");
- const readingTimePill = document.getElementById("book-reading-time-pill");
- const scrubberTrack = document.getElementById("book-scrubber-track");
- const scrubberFill = document.getElementById("book-scrubber-fill");
- const toggleViewModeBtn = document.getElementById("toggle-view-mode-btn");
-
- if (!bookContent || !bookViewport) return;
-
- const VIEW_MODE_KEY = 'hesten_reader_view_mode';
- let currentViewMode = 'book';
- try {
- const savedMode = localStorage.getItem(VIEW_MODE_KEY);
- if (savedMode === 'scroll' || savedMode === 'book') {
- currentViewMode = savedMode;
- }
- } catch (e) {}
-
- let currentPage = 1;
- let totalPages = 1;
- let columnStride = 0;
- let totalWordCount = 0;
-
- // Standard Educational Grade-Level WPM Benchmark Mapper
- function getGradeLevelWpm(gradeStr) {
- if (!gradeStr) return 225;
- const str = String(gradeStr).toLowerCase();
- if (str.includes('k') || str.includes('1') || str.includes('2')) return 100;
- if (str.includes('3') || str.includes('4')) return 135;
- if (str.includes('5') || str.includes('6')) return 165;
- if (str.includes('7') || str.includes('8')) return 195;
- if (str.includes('9') || str.includes('10') || str.includes('11') || str.includes('12') || str.includes('high')) return 225;
- if (str.includes('college') || str.includes('adult') || str.includes('advanced')) return 260;
- return 225;
- }
-
- const gradeWpm = getGradeLevelWpm(meta.grade || 'Grades 9-12');
-
- // Count words in chapter
- function countWords() {
- const text = bookContent.innerText || bookContent.textContent || '';
- const words = text.trim().split(/\s+/).filter(w => w.length > 0);
- totalWordCount = Math.max(1, words.length);
- }
- countWords();
-
- function setViewMode(mode) {
- currentViewMode = mode;
- try {
- localStorage.setItem(VIEW_MODE_KEY, mode);
- } catch (e) {}
-
- document.body.classList.remove('mode-book', 'mode-scroll');
- document.body.classList.add(`mode-${mode}`);
-
- if (toggleViewModeBtn) {
- toggleViewModeBtn.innerHTML = mode === 'book' ? '<i class="fas fa-book-open"></i>' : '<i class="fas fa-scroll"></i>';
- toggleViewModeBtn.title = mode === 'book' ? 'Switch to Continuous Scroll View' : 'Switch to Realistic Book Page-Flip View';
- }
-
- document.querySelectorAll('.settings-mode').forEach(btn => {
- btn.classList.toggle('active', btn.dataset.mode === mode);
- });
-
- if (mode === 'book') {
- recalculatePagination();
- handleUrlHash();
- } else {
- bookContent.style.transform = '';
- bookContent.style.columnWidth = '';
- bookContent.style.columnGap = '';
- }
- }
-
- function recalculatePagination() {
- if (currentViewMode !== 'book') return;
-
- const viewportWidth = bookViewport.clientWidth;
- const viewportHeight = bookViewport.clientHeight;
- if (viewportWidth <= 0 || viewportHeight <= 0) return;
-
- const columnGap = 64; // matching CSS column-gap (4rem)
- bookContent.style.columnWidth = `${viewportWidth}px`;
- bookContent.style.columnGap = `${columnGap}px`;
-
- const contentScrollWidth = bookContent.scrollWidth;
- columnStride = viewportWidth + columnGap;
- totalPages = Math.max(1, Math.ceil((contentScrollWidth + (columnGap / 2)) / columnStride));
-
- if (currentPage > totalPages) {
- currentPage = totalPages;
- } else if (currentPage < 1) {
- currentPage = 1;
- }
-
- window.CURRENT_READER_PAGE = currentPage;
- window.TOTAL_READER_PAGES = totalPages;
-
- updatePageDisplay(false);
- }
-
- function updatePageDisplay(animate = false, direction = 'forward') {
- if (currentViewMode !== 'book') return;
-
- const offset = (currentPage - 1) * columnStride;
- bookContent.style.transform = `translateX(-${offset}px)`;
-
- if (pageIndicator) {
- pageIndicator.innerHTML = `<i class="fas fa-file-alt"></i> Page ${currentPage} of ${totalPages}`;
- }
-
- if (readingTimePill) {
- const wordsRemaining = Math.max(0, Math.round(totalWordCount * ((totalPages - currentPage) / totalPages)));
- const minutesRemaining = Math.ceil(wordsRemaining / gradeWpm);
- const timeText = minutesRemaining <= 1 ? '< 1 min left' : `~${minutesRemaining} min left`;
- readingTimePill.innerHTML = `<i class="fas fa-clock"></i> ${timeText}`;
- readingTimePill.title = `Estimated time remaining at ${gradeWpm} WPM (${meta.grade || 'Grades 9-12'} pace)`;
- }
-
- if (scrubberFill) {
- const pct = totalPages > 1 ? Math.round(((currentPage - 1) / (totalPages - 1)) * 100) : 100;
- scrubberFill.style.width = `${pct}%`;
- }
-
- if (prevPageBtn) {
- prevPageBtn.disabled = currentPage <= 1;
- prevPageBtn.classList.toggle('disabled', currentPage <= 1);
- }
- if (nextPageBtn) {
- nextPageBtn.disabled = currentPage >= totalPages;
- nextPageBtn.classList.toggle('disabled', currentPage >= totalPages);
- }
-
- if (animate && bookFrame) {
- const animClass = direction === 'forward' ? 'flip-forward' : 'flip-backward';
- bookFrame.classList.remove('flip-forward', 'flip-backward');
- void bookFrame.offsetWidth;
- bookFrame.classList.add(animClass);
- setTimeout(() => {
- bookFrame.classList.remove(animClass);
- }, 420);
- }
- }
-
- function goToPage(targetPage, updateHash = true, animate = true, direction = null) {
- const clamped = Math.max(1, Math.min(targetPage, totalPages));
- if (clamped === currentPage && !animate) return;
-
- const dir = direction || (clamped >= currentPage ? 'forward' : 'backward');
- currentPage = clamped;
- window.CURRENT_READER_PAGE = currentPage;
-
- updatePageDisplay(animate, dir);
-
- if (updateHash) {
- const targetHash = `#page-${currentPage}`;
- if (window.location.hash !== targetHash) {
- history.pushState(null, '', targetHash);
- }
- }
- }
-
- function handleUrlHash() {
- const hash = window.location.hash;
- if (hash && /^#page-\d+$/i.test(hash)) {
- const pageNum = parseInt(hash.replace('#page-', ''), 10);
- if (!isNaN(pageNum) && pageNum >= 1) {