From 12bf56ed4e519d92733beeb2d6a9bb4443e28c0c Mon Sep 17 00:00:00 2001 From: Aarushi Keshari Date: Sat, 23 May 2026 12:21:53 +0530 Subject: [PATCH] feat: implement immersive reading quotes generator system with customizable dynamic transitions #749 --- frontend/css/style.css | 305 ++++++++++++++++---------------------- frontend/js/app.js | 160 +++++++++++++------- frontend/pages/index.html | 257 +++++++++++++++----------------- 3 files changed, 347 insertions(+), 375 deletions(-) diff --git a/frontend/css/style.css b/frontend/css/style.css index 43af0f6cc..fa4f11b44 100644 --- a/frontend/css/style.css +++ b/frontend/css/style.css @@ -460,7 +460,7 @@ textarea:focus { to { opacity: 1; - transform: translateY(0) scale(1); + transform: translateY(0 scale(1); } } @@ -3392,7 +3392,7 @@ textarea:focus { } /* ========================================= */ -/* BOOK SPINE VARIATIONS (#12) */ +/* BOOK SPINE VARIATIONS (#12) */ /* ========================================= */ /* --- Textures --- */ @@ -3980,11 +3980,11 @@ textarea:focus { } } -\n + /* ========================================= */ /* ========================================= */ -/* EXPANDED BOOK VIEW (#45) */ +/* EXPANDED BOOK VIEW (#45) */ /* ========================================= */ #book-detail-modal { @@ -6082,167 +6082,6 @@ body { cursor: none; } transition: transform 360ms cubic-bezier(.2, 1, .22, 1); } -/* ============================================================ - AMBIENT VOLUME SLIDER — SINGLE SOURCE OF TRUTH - Replace ALL existing slider/range rules in style.css with this block. - Remove every other rule targeting: - - .ambient-panel input[type="range"] - - .library-page .ambient-panel input[type="range"] - - #profile-page .ambient-panel input[type="range"] - - .ambient-panel .volume-control .range-wrap ... - - Any block with margin-top on ::-webkit-slider-thumb inside ambient-panel - ============================================================ */ - -/* 1. Volume control layout */ -.ambient-panel .volume-control { - display: grid; - grid-template-columns: auto 1fr; - align-items: center; - gap: 10px; -} - -.ambient-panel .volume-control label { - display: flex; - align-items: center; - gap: 10px; - color: var(--text-main); - font-size: 0.95rem; - white-space: nowrap; -} - -/* 2. The wrapper that stacks track, fill, and input */ -.ambient-panel .volume-control .range-wrap { - position: relative; - width: 100%; - height: 18px; - /* fixed height — track and thumb share this axis */ - display: flex; - align-items: center; - /* vertically centers the */ -} - -/* 3. Track and fill — absolutely positioned, vertically centred */ -.ambient-panel .volume-control .range-wrap .range-track, -.ambient-panel .volume-control .range-wrap .range-fill { - position: absolute; - left: 0; - top: 50%; - transform: translateY(-50%); - height: 6px; - border-radius: 999px; - pointer-events: none; -} - -.ambient-panel .volume-control .range-wrap .range-track { - right: 0; - background: rgba(0, 0, 0, 0.15); - z-index: 1; -} - -.ambient-panel .volume-control .range-wrap .range-fill { - background: var(--accent-gold); - width: 50%; - /* JS will update this dynamically */ - transition: width 240ms cubic-bezier(.2, 1, .22, 1); - z-index: 2; -} - -/* 4. The actual — transparent, sits on top */ -.ambient-panel .volume-control .range-wrap input[type="range"], -.library-page .ambient-panel .volume-control .range-wrap input[type="range"], -.profile-page .ambient-panel .volume-control .range-wrap input[type="range"], -#profile-page .ambient-panel .volume-control .range-wrap input[type="range"] { - -webkit-appearance: none !important; - appearance: none !important; - position: relative; - z-index: 3; - width: 100% !important; - height: 18px !important; - /* matches .range-wrap height */ - min-height: 0 !important; - min-width: 0 !important; - background: transparent !important; - border-radius: 999px; - outline: none; - cursor: pointer; - margin: 0 !important; - padding: 0 !important; -} - -/* 5. WebKit track — transparent (fill handled by .range-fill div) */ -.ambient-panel .volume-control .range-wrap input[type="range"]::-webkit-slider-runnable-track, -.library-page .ambient-panel .volume-control .range-wrap input[type="range"]::-webkit-slider-runnable-track, -.profile-page .ambient-panel .volume-control .range-wrap input[type="range"]::-webkit-slider-runnable-track, -#profile-page .ambient-panel .volume-control .range-wrap input[type="range"]::-webkit-slider-runnable-track { - height: 6px !important; - background: transparent !important; - border-radius: 999px; -} - -/* 6. WebKit thumb — THE KEY FIX: margin-top centers it on the 6px track */ -.ambient-panel .volume-control .range-wrap input[type="range"]::-webkit-slider-thumb, -.library-page .ambient-panel .volume-control .range-wrap input[type="range"]::-webkit-slider-thumb, -.profile-page .ambient-panel .volume-control .range-wrap input[type="range"]::-webkit-slider-thumb, -#profile-page .ambient-panel .volume-control .range-wrap input[type="range"]::-webkit-slider-thumb { - -webkit-appearance: none !important; - appearance: none !important; - width: 18px !important; - height: 18px !important; - border-radius: 50% !important; - background: var(--accent-gold) !important; - border: 3px solid rgba(255, 255, 255, 0.85) !important; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3) !important; - cursor: pointer !important; - margin-top: -6px !important; - /* (18px thumb - 6px track) / 2 = 6px */ - transform: none !important; - /* kill any conflicting translateY */ - transition: transform 260ms cubic-bezier(.2, 1, .22, 1), - box-shadow 260ms ease !important; -} - -/* Thumb hover */ -.ambient-panel .volume-control .range-wrap input[type="range"]:hover::-webkit-slider-thumb { - transform: scale(1.15) !important; - box-shadow: 0 3px 12px rgba(212, 175, 55, 0.5) !important; -} - -/* Thumb pop animation (JS adds .volume-animate class) */ -.ambient-panel .volume-control .range-wrap input[type="range"].volume-animate::-webkit-slider-thumb { - transform: scale(1.2) !important; - box-shadow: 0 0 14px rgba(212, 175, 55, 0.6) !important; -} - -/* 7. Firefox track */ -.ambient-panel .volume-control .range-wrap input[type="range"]::-moz-range-track, -.library-page .ambient-panel .volume-control .range-wrap input[type="range"]::-moz-range-track, -.profile-page .ambient-panel .volume-control .range-wrap input[type="range"]::-moz-range-track, -#profile-page .ambient-panel .volume-control .range-wrap input[type="range"]::-moz-range-track { - height: 6px !important; - background: transparent !important; - border-radius: 999px; -} - -/* 8. Firefox thumb */ -.ambient-panel .volume-control .range-wrap input[type="range"]::-moz-range-thumb, -.library-page .ambient-panel .volume-control .range-wrap input[type="range"]::-moz-range-thumb, -.profile-page .ambient-panel .volume-control .range-wrap input[type="range"]::-moz-range-thumb, -#profile-page .ambient-panel .volume-control .range-wrap input[type="range"]::-moz-range-thumb { - width: 18px !important; - height: 18px !important; - border-radius: 50% !important; - background: var(--accent-gold) !important; - border: 3px solid rgba(255, 255, 255, 0.85) !important; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3) !important; - cursor: pointer !important; - /* Firefox auto-centers the thumb — no margin-top needed */ -} - -.ambient-panel .volume-control .range-wrap input[type="range"].volume-animate::-moz-range-thumb { - transform: scale(1.2); - transition: transform 360ms cubic-bezier(.2, 1, .22, 1); -} - /* --- Mood Sync Theme System Overrides --- */ [data-mood] { --bg-color: var(--theme-bg) !important; @@ -6530,8 +6369,8 @@ body.night-mode .tooltip-author { .footer-logo { - height: 40px; - width: auto; + height: 40px; + width: auto; } .logo { @@ -6544,33 +6383,33 @@ body.night-mode .tooltip-author { .main-footer { - padding: 4rem 2rem 1rem; + padding: 4rem 2rem 1rem; } .footer-container { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); - gap: 2rem; + display: grid; + grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); + gap: 2rem; } .footer-container a { - text-decoration: none; - transition: color 0.3s ease; + text-decoration: none; + transition: color 0.3s ease; } .social-icons { - display: flex; - gap: 1rem; + display: flex; + gap: 1rem; } .footer-bottom { - text-align: center; - margin-top: 3rem; - padding-top: 1rem; - border-top: 1px solid #374151; + text-align: center; + margin-top: 3rem; + padding-top: 1rem; + border-top: 1px solid #374151; } /* ========================= Reader Perspectives @@ -6810,4 +6649,112 @@ body.night-mode .tooltip-author { border-color: var(--text-main); } +/* ============================================================ */ +/* NEW COMPONENT - READING QUOTES GENERATOR SYSTEM (GSSoC #749) */ +/* ============================================================ */ + +.quotes-generator-section { + text-align: center; + padding: 40px 20px; + animation: fadeIn 0.8s ease-out; +} + +.quote-card-container { + max-width: 650px; + margin: 2rem auto; + display: flex; + flex-direction: column; + align-items: center; + gap: 24px; +} + +.quote-card { + width: 100%; + padding: 45px; + border-radius: 24px; + background: var(--surface-soft, rgba(44, 36, 32, 0.4)); + backdrop-filter: blur(12px); + -webkit-backdrop-filter: blur(12px); + border: 1px solid var(--glass-border); + box-shadow: var(--shadow-soft); + position: relative; + transition: opacity 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94), + transform 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94); +} + +.quote-icon-top { + position: absolute; + top: 15px; + left: 20px; + font-size: 1.6rem; + opacity: 0.25; + color: var(--text-muted); +} + +.quote-icon-bottom { + position: absolute; + bottom: 15px; + right: 20px; + font-size: 1.6rem; + opacity: 0.25; + color: var(--text-muted); +} + +.quote-text { + font-family: 'Playfair Display', 'Georgia', serif; + font-size: 1.4rem; + font-style: italic; + line-height: 1.65; + margin-bottom: 18px; + color: var(--text-main); +} + +.quote-author { + font-size: 1.05rem; + font-weight: 600; + letter-spacing: 1px; + text-transform: uppercase; + color: var(--accent-gold); +} +/* Premium Rounded Button Overrides for Quotes System */ +.quotes-generator-section .inspire-btn { + display: inline-flex !important; + align-items: center !important; + justify-content: center !important; + gap: 10px !important; + padding: 14px 36px !important; + font-family: "Georgia", serif !important; + font-size: 1.05rem !important; + font-weight: 600 !important; + letter-spacing: 0.5px !important; + color: #ffffff !important; + + /* Futuristic Gradient Background */ + background: linear-gradient(135deg, #4f46e5, #7c3aed, #ec4899) !important; + background-size: 200% auto !important; + + /* Proper 3D Button Shape and Dimensions */ + border: none !important; + border-radius: 50px !important; /* Perfect capsule rounded shape */ + cursor: pointer !important; + position: relative !important; + z-index: 10 !important; + + /* Premium Shadow Lift and Smooth Animation */ + box-shadow: 0 6px 20px rgba(124, 58, 237, 0.4) !important; + transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94) !important; +} + +/* Hover effect for standard button feel */ +.quotes-generator-section .inspire-btn:hover { + background-position: right center !important; + transform: translateY(-3px) scale(1.04) !important; + box-shadow: 0 10px 25px rgba(124, 58, 237, 0.6) !important; +} + +/* Press effect to mimic real clicking */ +.quotes-generator-section .inspire-btn:active { + transform: translateY(-1px) scale(1.01) !important; + box-shadow: 0 4px 12px rgba(124, 58, 237, 0.4) !important; +} \ No newline at end of file diff --git a/frontend/js/app.js b/frontend/js/app.js index 992fd0d3a..06c731b50 100644 --- a/frontend/js/app.js +++ b/frontend/js/app.js @@ -12,60 +12,60 @@ * Key Components: * --------------- * 1. SafeStorage: - * A robust wrapper around `localStorage` with an `IndexedDB` fallback mechanism. - * This component is critical for offline-first capabilities and prevents the - * entire app from crashing when iOS/Safari or restrictive browser quotas prevent - * standard `localStorage` operations. - * - Automatically handles QuotaExceeded exceptions. - * - Provides asynchronous data restoration algorithms. - * - Integrates closely with the LibraryManager to store thousands of books safely. + * A robust wrapper around `localStorage` with an `IndexedDB` fallback mechanism. + * This component is critical for offline-first capabilities and prevents the + * entire app from crashing when iOS/Safari or restrictive browser quotas prevent + * standard `localStorage` operations. + * - Automatically handles QuotaExceeded exceptions. + * - Provides asynchronous data restoration algorithms. + * - Integrates closely with the LibraryManager to store thousands of books safely. * * 2. LibraryManager: - * The central state machine over the user's book collection. - * - Shelf Types: Manages three distinctive shelves: 'want', 'current', 'finished'. - * - Concurrency Control: Handles race conditions when syncing local states with - * the backend utilizing optimistic locking techniques. - * - Merging Strategy: In the event of a conflict between the client data and - * server data, it attempts a non-destructive merge, retaining the state with - * the highest integer version map. + * The central state machine over the user's book collection. + * - Shelf Types: Manages three distinctive shelves: 'want', 'current', 'finished'. + * - Concurrency Control: Handles race conditions when syncing local states with + * the backend utilizing optimistic locking techniques. + * - Merging Strategy: In the event of a conflict between the client data and + * server data, it attempts a non-destructive merge, retaining the state with + * the highest integer version map. * * 3. BookRenderer: - * An interface bridge to the DOM. Handles instantiation of HTML templates for - * individual 3D book instances, binding their unique event listeners, and - * applying their generated CSS styles and thematic properties. - * It integrates directly with `LibraryManager` to reflect real-time progress updates. + * An interface bridge to the DOM. Handles instantiation of HTML templates for + * individual 3D book instances, binding their unique event listeners, and + * applying their generated CSS styles and thematic properties. + * It integrates directly with `LibraryManager` to reflect real-time progress updates. * * 4. ThemeManager: - * Observes User Preferences and seamlessly toggles the UI's color palette between - * predefined themes (e.g., dark mode and light mode, wood mode), persisting - * these preferences to SafeStorage for a seamless experience across reloads. + * Observes User Preferences and seamlessly toggles the UI's color palette between + * predefined themes (e.g., dark mode and light mode, wood mode), persisting + * these preferences to SafeStorage for a seamless experience across reloads. * * API Architecture Details: * ------------------------- * - Google Books API: Facilitates the search and retrieval of rich book metadata - * including volume summaries, author info, and high-quality thumbnail images. + * including volume summaries, author info, and high-quality thumbnail images. * - Local Proxy/Backend: Certain complex interactions such as Machine Learning - * sentiment analysis (fetchAIVibe) are offloaded to `MOOD_API_BASE` to bypass - * client-side compute limitations and securely handle secret API keys. + * sentiment analysis (fetchAIVibe) are offloaded to `MOOD_API_BASE` to bypass + * client-side compute limitations and securely handle secret API keys. * * Security & Data Integrity Considerations: * ----------------------------------------- * - Data Sanitization: All text rendered from external APIs is strictly passed - * through the `escapeHTML` utility safely converting brackets to entities to - * prevent XSS (Cross-Site Scripting) vectors. + * through the `escapeHTML` utility safely converting brackets to entities to + * prevent XSS (Cross-Site Scripting) vectors. * - CSRF Protection: Interacts closely with the server-supplied `csrf_access_token` - * to securely validate state-mutating requests (POST, PUT, DELETE) preventing - * Cross Site Request Forgery attacks against logged-in users. + * to securely validate state-mutating requests (POST, PUT, DELETE) preventing + * Cross Site Request Forgery attacks against logged-in users. * * Coding Standards and Development Guidelines: * -------------------------------------------- * 1. Offline-First Philosophy: Ensure that actions (add, remove, update) are - * optimistically applied to local state before waiting for server resolution. + * optimistically applied to local state before waiting for server resolution. * 2. Safe Storage Wrapper: Always use `SafeStorage.set()` instead of native - * `localStorage.setItem()`. + * `localStorage.setItem()`. * 3. Centralized Styling: For broad CSS manipulations, modify standard tokens in - * `index.css` rather than directly overriding inline styles to maintain a - * dynamic and cohesive theme strategy. + * `index.css` rather than directly overriding inline styles to maintain a + * dynamic and cohesive theme strategy. * * File Structure: * --------------- @@ -519,7 +519,7 @@ const MOCK_BOOKS = [ id: "mock-dune", volumeInfo: { title: "Dune", - authors: ["Frank Herbert"], + author: ["Frank Herbert"], description: "A sweeping science fiction epic set on the desert planet Arrakis. Dune explores complex themes of politics, religion, and man's relationship with nature. Paul Atreides must navigate a treacherous path to becoming the mysterious Muad'Dib.", imageLinks: { thumbnail: "../assets/images/dune.jpg" } } @@ -528,7 +528,7 @@ const MOCK_BOOKS = [ id: "mock-1984", volumeInfo: { title: "1984", - authors: ["George Orwell"], + author: ["George Orwell"], description: "Orwell's chilling prophecy of a totalitarian future where Big Brother is always watching. A profound exploration of surveillance, truth, and the resilience of the human spirit.", imageLinks: { thumbnail: "../assets/images/1984.jpg" } } @@ -537,7 +537,7 @@ const MOCK_BOOKS = [ id: "mock-hobbit", volumeInfo: { title: "The Hobbit", - authors: ["J.R.R. Tolkien"], + author: ["J.R.R. Tolkien"], description: "In a hole in the ground there lived a hobbit. Join Bilbo Baggins on an unexpected journey across Middle-earth, encountering dragons, dwarves, and a rigorous test of courage.", imageLinks: { thumbnail: "../assets/images/hobbit.jpg" } } @@ -546,7 +546,7 @@ const MOCK_BOOKS = [ id: "mock-pride", volumeInfo: { title: "Pride and Prejudice", - authors: ["Jane Austen"], + author: ["Jane Austen"], description: "A timeless romance of manners and misunderstanding. Elizabeth Bennet's wit matches Mr. Darcy's pride in this sharp social commentary that remains one of the most loved novels in English literature.", imageLinks: { thumbnail: "../assets/images/pride.jpg" } } @@ -555,7 +555,7 @@ const MOCK_BOOKS = [ id: "mock-gatsby", volumeInfo: { title: "The Great Gatsby", - authors: ["F. Scott Fitzgerald"], + author: ["F. Scott Fitzgerald"], description: "The quintessential novel of the Jazz Age. Jay Gatsby's obsessive love for Daisy Buchanan drives a tragic tale of wealth, illusion, and the American Dream.", imageLinks: { thumbnail: "../assets/images/gatsby.jpg" } } @@ -564,7 +564,7 @@ const MOCK_BOOKS = [ id: "mock-sapiens", volumeInfo: { title: "Sapiens", - authors: ["Yuval Noah Harari"], + author: ["Yuval Noah Harari"], description: "A groundbreaking narrative of humanity's creation and evolution. Harari explores the ways in which biology and history have defined us and enhanced our understanding of what it means to be 'human'.", imageLinks: { thumbnail: "../assets/images/sapiens.jpg" } } @@ -573,7 +573,7 @@ const MOCK_BOOKS = [ id: "mock-hail-mary", volumeInfo: { title: "Project Hail Mary", - authors: ["Andy Weir"], + author: ["Andy Weir"], description: "A lone astronaut must save the earth from disaster in this gripping tale of survival and scientific discovery. Full of humor and hard science, it is a celebration of human ingenuity.", imageLinks: { thumbnail: "../assets/images/hail_mary.jpg" } } @@ -1246,8 +1246,7 @@ class BookRenderer {

Primary Moods

- -
+

Overall Sentiment

@@ -1259,12 +1258,10 @@ class BookRenderer {

Bookseller's Vibe

- -
+
- -
+ @@ -2919,7 +2916,7 @@ document.addEventListener('DOMContentLoaded', async () => { const a = document.createElement("a"); a.href = url; a.download = `bibliodrift_library_${new Date().toISOString().slice(0, 10)}.json`; - document.body.appendChild(a); + a.document.body.appendChild(a); a.click(); document.body.removeChild(a); @@ -3027,9 +3024,9 @@ document.addEventListener('DOMContentLoaded', async () => { // Re-rendering is now handled by libManager.init() asynchronously to ensure // data is loaded from IndexedDB backup if LocalStorage was wiped. // if (document.getElementById('shelf-want')) { - // libManager.renderShelf('want', 'shelf-want'); - // libManager.renderShelf('current', 'shelf-current'); - // libManager.renderShelf('finished', 'shelf-finished'); + // libManager.renderShelf('want', 'shelf-want'); + // libManager.renderShelf('current', 'shelf-current'); + // libManager.renderShelf('finished', 'shelf-finished'); // } @@ -3482,7 +3479,7 @@ document.addEventListener('DOMContentLoaded', async () => { saveBtn.addEventListener('click', async () => { const newProgress = parseInt(slider.value); saveBtn.disabled = true; - saveBtn.innerHTML = ''; + saveBtn.innerHTML = ''; try { await libManager.updateBook(book.id, { progress: newProgress }); book.progress = newProgress; @@ -3520,7 +3517,7 @@ document.addEventListener('DOMContentLoaded', async () => { { id: 'focused', icon: 'fa-glasses', title: 'Focused', desc: 'Reading 3 at once', condition: currentCount >= 3 } ]; - achievements.forEach(ach => { + achievements.forEach(ach => { const card = document.createElement('div'); card.className = `achievement-card ${ach.condition ? 'unlocked' : 'locked'}`; card.innerHTML = ` @@ -3571,20 +3568,17 @@ document.addEventListener('DOMContentLoaded', async () => { * ============================================================================== * SECURITY FIX: CSRF PROTECTION & SESSION MANAGEMENT * ============================================================================== - * - * Issue: + * * Issue: * ------ * The auth form has no CSRF protection — it directly POSTs credentials to the API * from the browser. - * - * Why it matters: + * * Why it matters: * --------------- * Without a CSRF token, a malicious third-party page could trick authenticated * users into making authenticated requests. Also, isLoggedIn was previously stored * as a plain string 'true' in localStorage, meaning any script could forge the * login state by setting this key. - * - * Fix: + * * Fix: * ---- * Move the authenticated session indicator to an HttpOnly cookie managed by the * backend, and validate the JWT on every protected API call (which is already @@ -4158,6 +4152,7 @@ class ReadingMoodQuizManager { this.section = document.getElementById('reading-mood-quiz') || document.getElementById('readingMoodQuiz'); if (!this.section) return; + this.quizAnswers = {}; this.quizOptionGroups = this.section.querySelectorAll('.quiz-options'); this.generateBtn = this.section.querySelector('#generateMoodTags'); this.results = this.section.querySelector('#quizResults'); @@ -4275,3 +4270,54 @@ function _startReadingMoodQuiz() { _startReadingMoodQuiz(); + +// ============================================================ +// NEW COMPONENT INTERACTION: READING QUOTES GENERATOR (GSSoC #749) +// ============================================================ +document.addEventListener('DOMContentLoaded', () => { + const dynamicQuotes = [ + { text: "“The more that you read, the more things you will know. The more that you learn, the more places you'll go.”", author: "Dr. Seuss" }, + { text: "“A room without books is like a body without a soul.”", author: "Marcus Tullius Cicero" }, + { text: "“I have always imagined that Paradise will be a kind of a library.”", author: "Jorge Luis Borges" }, + { text: "“Books are a uniquely portable magic.”", author: "Stephen King" }, + { text: "“There is no frigate like a book to take us lands away.”", author: "Emily Dickinson" }, + { text: "“Reading is escape, and the opposite of escape; it's a way to make contact with reality after a day of making things up.”", author: "Nora Ephron" }, + { text: "“A book is a garden, an orchard, a storehouse, a party, a company by the way, a counselor, a multitude of counselors.”", author: "Charles Baudelaire" }, + { text: "“To read is to fly: it is to soar to a point of vantage which gives a view over wide terrains of history.”", author: "A.C. Grayling" } + ]; + + const targetCard = document.getElementById('quoteCard'); + const targetText = document.getElementById('quoteDisplay'); + const targetAuthor = document.getElementById('authorDisplay'); + const actionButton = document.getElementById('inspireMeBtn'); + + // Multi-page safety script guard format + if (!actionButton || !targetText || !targetAuthor || !targetCard) return; + + let memoryIndex = 0; + + actionButton.addEventListener('click', () => { + // Add custom structural fade-out transition hook + targetCard.classList.add('quote-fade-out'); + + // 400ms delay perfectly synchronized with stable style.css timing rules + setTimeout(() => { + let nextRandomIndex; + + // Algorithm ensures duplicate sequence avoidance loops cleanly + do { + nextRandomIndex = Math.floor(Math.random() * dynamicQuotes.length); + } while (nextRandomIndex === memoryIndex); + + memoryIndex = nextRandomIndex; + const finalPick = dynamicQuotes[nextRandomIndex]; + + // Mutate string state properties securely + targetText.textContent = finalPick.text; + targetAuthor.textContent = `— ${finalPick.author}`; + + // Drop operational class to fade card interface elements back in seamlessly + targetCard.classList.remove('quote-fade-out'); + }, 400); + }); +}); \ No newline at end of file diff --git a/frontend/pages/index.html b/frontend/pages/index.html index f35d808fe..3d1886165 100644 --- a/frontend/pages/index.html +++ b/frontend/pages/index.html @@ -53,10 +53,8 @@ - - @@ -72,7 +70,6 @@ - - - @@ -126,7 +121,6 @@ Chat Chat -
Sign In Access account @@ -144,25 +138,40 @@

Find yourself in the pages.

A curated sanctuary for those who read through emotions.

- +
+
+

Daily Literary Inspiration

+ Words from timeless writers +
+ +
+
+
+

“The more that you read, the more things you will know. The more that you learn, the more places you'll go.”

+

— Dr. Seuss

+
+
+ + +
+
+
- -
+ -

Rainy Evening Reads

@@ -195,12 +204,6 @@

Thriller & Suspense

- - - - - -

Dark Academia

@@ -217,7 +220,6 @@

General Fiction

-

Browse by Genre

@@ -239,7 +241,6 @@

Browse by Genre

-
@@ -252,7 +253,6 @@

Genre Books

- - -
+
+
+

Reader Perspectives

+ + + Quiet thoughts from wandering readers + +
-
-

Reader Perspectives

- - - Quiet thoughts from wandering readers - -
- -
- -
-
-
🌧
- -
-

Midnight Reader

-

★★★★★

+
+
+
+
+
+

Midnight Reader

+

★★★★★

+
+

+ “BiblioDrift feels like wandering through a bookstore during rainfall.” +

-

- “BiblioDrift feels like wandering through a bookstore during rainfall.” -

-
- -
-
-
☕
- -
-

Slow Sunday Soul

-

★★★★★

+
+
+
+
+

Slow Sunday Soul

+

★★★★★

+
+

+ “I came for books and stayed for the atmosphere.” +

-

- “I came for books and stayed for the atmosphere.” -

-
- -
-
-
🌙
- -
-

Wandering Philosopher

-

★★★★★

+
+
+
+
+

Wandering Philosopher

+

★★★★★

+
+

+ “Elara somehow recommended a story that mirrored my entire week.” +

- -

- “Elara somehow recommended a story that mirrored my entire week.” -

+
+ +
+
+

Reading Mood Quiz

+

+ Answer a few quick questions to discover mood tags for personalized book + recommendations. +

-
- -
- - -
-
-

Reading Mood Quiz

-

- Answer a few quick questions to discover mood tags for personalized book - recommendations. -

- -
-

1. What is your current reading mood?

-
- - - - - -
+
+

1. What is your current reading mood?

+
+ + + + +
+
-
-

2. Which genre do you prefer right now?

-
- - - - - -
+
+

2. Which genre do you prefer right now?

+
+ + + + +
+
-
-

3. What reading pace do you want?

-
- - - -
+
+

3. What reading pace do you want?

+
+ + +
+
-
-

4. What story vibe are you looking for?

-
- - - - - -
+
+

4. What story vibe are you looking for?

+
+ + + + +
+
- + - + -
- + +
- @@ -428,13 +414,10 @@

Your Reading Mood Tags

- - -
- - - -