mirror of
https://github.com/kennethreitz/kjvstudy.org.git
synced 2026-06-05 23:00:16 +00:00
Add Stars page for bookmarking with notes and navigation badges
- Create /stars page with collapsible groups, notes, and keyboard nav - Add yellow star bookmark button in breadcrumb (☆/★) - Add nav badges showing star count and reading plan % complete - Change 's' shortcut from Stories to Stars - Update toast to say "Added to Starred Pages" - Persist group collapse state in localStorage - Support notes with monospace font and edit/add functionality - Update accessibility page and ? help modal 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -231,19 +231,19 @@ async def verse_of_the_day_page(request: Request):
|
||||
)
|
||||
|
||||
|
||||
@router.get("/bookmarks", response_class=HTMLResponse)
|
||||
async def bookmarks_page(request: Request):
|
||||
"""Bookmarks page - displays user's saved bookmarks from localStorage"""
|
||||
@router.get("/stars", response_class=HTMLResponse)
|
||||
async def stars_page(request: Request):
|
||||
"""Stars page - displays user's saved starred pages from localStorage"""
|
||||
books = bible.get_books()
|
||||
|
||||
breadcrumbs = [
|
||||
{"text": "Home", "url": "/"},
|
||||
{"text": "Bookmarks", "url": None}
|
||||
{"text": "Stars", "url": None}
|
||||
]
|
||||
|
||||
return templates.TemplateResponse(
|
||||
request,
|
||||
"bookmarks.html",
|
||||
"stars.html",
|
||||
{
|
||||
"books": books,
|
||||
"breadcrumbs": breadcrumbs
|
||||
|
||||
+142
-4
@@ -50,6 +50,49 @@ function isBookmarked(url) {
|
||||
return bookmarks.some(function(b) { return b.url === url; });
|
||||
}
|
||||
|
||||
function getPageExcerpt() {
|
||||
var article = document.querySelector('article');
|
||||
if (!article) return '';
|
||||
|
||||
var clone = article.cloneNode(true);
|
||||
clone.querySelectorAll('.breadcrumb, .breadcrumb-actions, .sidenote, .marginnote, .toc, script, style, nav, button, h1, h2, h3').forEach(function(el) {
|
||||
el.remove();
|
||||
});
|
||||
|
||||
var text = (clone.textContent || clone.innerText || '').trim();
|
||||
// Get first meaningful chunk
|
||||
text = text.replace(/\s+/g, ' ').substring(0, 300);
|
||||
return text;
|
||||
}
|
||||
|
||||
function getPageDescription() {
|
||||
var meta = document.querySelector('meta[name="description"]');
|
||||
return meta ? meta.getAttribute('content') : '';
|
||||
}
|
||||
|
||||
function getPageBreadcrumbs() {
|
||||
var breadcrumb = document.querySelector('.breadcrumb');
|
||||
if (!breadcrumb) return [];
|
||||
|
||||
var crumbs = [];
|
||||
breadcrumb.querySelectorAll('a, span:not(.breadcrumb-separator):not(.breadcrumb-actions)').forEach(function(el) {
|
||||
if (el.classList.contains('breadcrumb-actions')) return;
|
||||
if (el.closest('.breadcrumb-actions')) return;
|
||||
var text = el.textContent.trim();
|
||||
if (text && text !== '>' && text.length > 0) {
|
||||
crumbs.push({
|
||||
text: text,
|
||||
url: el.tagName === 'A' ? el.getAttribute('href') : null
|
||||
});
|
||||
}
|
||||
});
|
||||
// Drop the last item (it's the current page title)
|
||||
if (crumbs.length > 0) {
|
||||
crumbs.pop();
|
||||
}
|
||||
return crumbs;
|
||||
}
|
||||
|
||||
function toggleBookmark() {
|
||||
var btn = document.getElementById('bookmark-btn');
|
||||
var url = window.location.pathname;
|
||||
@@ -62,29 +105,117 @@ function toggleBookmark() {
|
||||
// Remove bookmark
|
||||
bookmarks.splice(existingIndex, 1);
|
||||
if (btn) btn.classList.remove('bookmarked');
|
||||
showBookmarkToast(false);
|
||||
} else {
|
||||
// Add bookmark
|
||||
// Add bookmark with description, breadcrumbs, and excerpt
|
||||
bookmarks.unshift({
|
||||
url: url,
|
||||
title: title,
|
||||
description: getPageDescription(),
|
||||
breadcrumbs: getPageBreadcrumbs(),
|
||||
excerpt: getPageExcerpt(),
|
||||
date: new Date().toISOString()
|
||||
});
|
||||
if (btn) btn.classList.add('bookmarked');
|
||||
showBookmarkToast(true);
|
||||
}
|
||||
|
||||
saveBookmarks(bookmarks);
|
||||
updateStarsBadge();
|
||||
}
|
||||
|
||||
// Check bookmark state on page load
|
||||
function showBookmarkToast(added) {
|
||||
var toast = document.getElementById('bookmark-toast');
|
||||
if (!toast) return;
|
||||
|
||||
if (added) {
|
||||
toast.innerHTML = 'Added to <a href="/stars">Starred Pages</a>';
|
||||
} else {
|
||||
toast.innerHTML = 'Removed from Starred Pages';
|
||||
}
|
||||
|
||||
toast.classList.add('show');
|
||||
|
||||
// Hide after 3 seconds
|
||||
setTimeout(function() {
|
||||
toast.classList.remove('show');
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
// Check bookmark state on page load and update nav badges
|
||||
(function() {
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
var btn = document.getElementById('bookmark-btn');
|
||||
if (btn && isBookmarked(window.location.pathname)) {
|
||||
btn.classList.add('bookmarked');
|
||||
}
|
||||
|
||||
// Update nav badges
|
||||
updateStarsBadge();
|
||||
updateReadingPlansBadge();
|
||||
});
|
||||
})();
|
||||
|
||||
function updateStarsBadge() {
|
||||
var badge = document.getElementById('stars-badge');
|
||||
if (!badge) return;
|
||||
|
||||
var bookmarks = getBookmarks();
|
||||
badge.textContent = bookmarks.length > 0 ? bookmarks.length : '';
|
||||
}
|
||||
|
||||
// Reading plans progress badge
|
||||
function updateReadingPlansBadge() {
|
||||
var badge = document.getElementById('reading-plans-badge');
|
||||
if (!badge) return;
|
||||
|
||||
// Reading plan IDs and their total days
|
||||
var planDays = {
|
||||
'chronological': 365,
|
||||
'one-year': 365,
|
||||
'new-testament': 90,
|
||||
'gospels-acts': 30,
|
||||
'psalms-proverbs': 31,
|
||||
'pentateuch': 40,
|
||||
'prophets': 60,
|
||||
'paul-epistles': 30,
|
||||
'minor-prophets': 14,
|
||||
'wisdom': 30,
|
||||
'historical': 45,
|
||||
'general-epistles': 14
|
||||
};
|
||||
|
||||
var totalCompleted = 0;
|
||||
var totalDays = 0;
|
||||
var activePlans = 0;
|
||||
|
||||
// Check each plan
|
||||
for (var planId in planDays) {
|
||||
var storageKey = 'reading-plan-' + planId;
|
||||
var saved = localStorage.getItem(storageKey);
|
||||
if (saved) {
|
||||
try {
|
||||
var data = JSON.parse(saved);
|
||||
if (data.completed && data.completed.length > 0) {
|
||||
activePlans++;
|
||||
totalCompleted += data.completed.length;
|
||||
totalDays += planDays[planId];
|
||||
}
|
||||
} catch (e) {
|
||||
// Invalid JSON, skip
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (activePlans === 0 || totalDays === 0) {
|
||||
badge.textContent = '';
|
||||
return;
|
||||
}
|
||||
|
||||
var percent = Math.round((totalCompleted / totalDays) * 100);
|
||||
badge.textContent = percent + '%';
|
||||
}
|
||||
|
||||
// Page speech toggle (for breadcrumb button) - triggers spacebar speech
|
||||
function togglePageSpeech() {
|
||||
// Simulate spacebar press to use existing speech system
|
||||
@@ -900,6 +1031,12 @@ document.addEventListener('keydown', function(e) {
|
||||
toggleDarkMode();
|
||||
}
|
||||
|
||||
// Cmd/Ctrl + S: Toggle star (overrides browser save)
|
||||
if ((e.metaKey || e.ctrlKey) && e.key === 's') {
|
||||
e.preventDefault();
|
||||
toggleBookmark();
|
||||
}
|
||||
|
||||
// Cmd/Ctrl + K or /: Focus search
|
||||
if (((e.metaKey || e.ctrlKey) && e.key === 'k') || e.key === '/') {
|
||||
e.preventDefault();
|
||||
@@ -955,7 +1092,7 @@ document.addEventListener('keydown', function(e) {
|
||||
break;
|
||||
case 's':
|
||||
e.preventDefault();
|
||||
window.location.href = '/stories';
|
||||
window.location.href = '/stars';
|
||||
break;
|
||||
case '/':
|
||||
e.preventDefault();
|
||||
@@ -1160,7 +1297,7 @@ function showKeyboardHelp() {
|
||||
'<div class="shortcut"><kbd>8</kbd><span>Proverbs</span></div>' +
|
||||
'<div class="shortcut"><kbd>9</kbd><span>Revelation</span></div>' +
|
||||
'<div class="shortcut"><kbd>b</kbd><span>Books</span></div>' +
|
||||
'<div class="shortcut"><kbd>s</kbd><span>Stories</span></div>' +
|
||||
'<div class="shortcut"><kbd>s</kbd><span>Stars</span></div>' +
|
||||
'<div class="shortcut"><kbd>r</kbd><span>Resources</span></div>' +
|
||||
'<div class="shortcut"><kbd>t</kbd><span>Topics</span></div>' +
|
||||
'<div class="shortcut"><kbd>p</kbd><span>PDF (when available)</span></div>' +
|
||||
@@ -1184,6 +1321,7 @@ function showKeyboardHelp() {
|
||||
'<div class="shortcut"><kbd>n</kbd><span>Navigate sidebar</span></div>' +
|
||||
'<div class="shortcut"><kbd>⌘</kbd>+<kbd>D</kbd><span>Toggle dark mode</span></div>' +
|
||||
'<div class="shortcut"><kbd>R</kbd><span>Toggle red letters</span></div>' +
|
||||
'<div class="shortcut"><kbd>⌘</kbd>+<kbd>S</kbd><span>Toggle star</span></div>' +
|
||||
'<div class="shortcut"><kbd>/</kbd><span>Search</span></div>' +
|
||||
'<div class="shortcut"><kbd>?</kbd><span>Show this help</span></div>' +
|
||||
'<div class="shortcut"><kbd>Esc</kbd><span>Close / Clear focus</span></div>' +
|
||||
|
||||
@@ -62,6 +62,10 @@
|
||||
<td><kbd>?</kbd></td>
|
||||
<td>Show keyboard shortcuts help</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><kbd>⌘</kbd>+<kbd>S</kbd></td>
|
||||
<td>Toggle star (bookmark current page)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -120,7 +124,7 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td><kbd>s</kbd></td>
|
||||
<td>Stories</td>
|
||||
<td>Stars (your bookmarks)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><kbd>c</kbd></td>
|
||||
|
||||
@@ -433,6 +433,44 @@
|
||||
|
||||
.breadcrumb-action-btn.bookmark-btn.bookmarked::before {
|
||||
content: '★';
|
||||
color: #eab308;
|
||||
}
|
||||
|
||||
.bookmark-toast {
|
||||
position: fixed;
|
||||
bottom: 2rem;
|
||||
left: 50%;
|
||||
transform: translateX(-50%) translateY(100px);
|
||||
background: var(--text-color);
|
||||
color: var(--bg-color);
|
||||
padding: 0.75rem 1.25rem;
|
||||
border-radius: 6px;
|
||||
font-size: 0.9rem;
|
||||
opacity: 0;
|
||||
transition: all 0.3s ease;
|
||||
z-index: 1000;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.bookmark-toast.show {
|
||||
transform: translateX(-50%) translateY(0);
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.bookmark-toast a {
|
||||
color: var(--bg-color) !important;
|
||||
margin-left: 0.5rem;
|
||||
text-decoration: underline;
|
||||
border-bottom: none !important;
|
||||
}
|
||||
|
||||
.bookmark-toast a:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .bookmark-toast a {
|
||||
color: var(--bg-color) !important;
|
||||
}
|
||||
|
||||
/* Large font size */
|
||||
@@ -719,6 +757,23 @@
|
||||
background: rgba(74, 124, 89, 0.08);
|
||||
}
|
||||
|
||||
/* Nav badge for counts */
|
||||
.nav-badge {
|
||||
display: inline-block;
|
||||
font-size: 0.7rem;
|
||||
font-weight: 500;
|
||||
background: var(--code-bg);
|
||||
color: var(--text-secondary);
|
||||
padding: 0.1rem 0.4rem;
|
||||
border-radius: 8px;
|
||||
margin-left: 0.35rem;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.nav-badge:empty {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Sidebar search box */
|
||||
.sidebar-search {
|
||||
margin-bottom: 1rem;
|
||||
@@ -1420,6 +1475,8 @@
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
<div class="bookmark-toast" id="bookmark-toast"></div>
|
||||
|
||||
<article role="main" id="main-content">
|
||||
{% if breadcrumbs %}
|
||||
<nav class="breadcrumb" aria-label="Breadcrumb" id="breadcrumb">
|
||||
@@ -1456,20 +1513,9 @@
|
||||
<ul>
|
||||
<li><a href="/" {% if request.url.path == "/" %}class="current"{% endif %}>Home</a></li>
|
||||
<li><a href="/books" {% if request.url.path == "/books" or request.url.path.startswith("/book/") %}class="current"{% endif %}>Books</a></li>
|
||||
<li><a href="/search">Search</a></li>
|
||||
</ul>
|
||||
|
||||
|
||||
<!-- Daily Reading -->
|
||||
<details class="resource-group" open aria-label="Daily reading resources">
|
||||
<summary aria-expanded="true">Daily Reading</summary>
|
||||
<ul>
|
||||
<li><a href="/verse-of-the-day" {% if request.url.path == "/verse-of-the-day" %}class="current"{% endif %}>Verse of the Day</a></li>
|
||||
<li><a href="/random-verse" {% if request.url.path == "/random-verse" %}class="current"{% endif %}>Random Verse</a></li>
|
||||
<li><a href="/reading-plans" {% if request.url.path.startswith("/reading-plans") %}class="current"{% endif %}>Reading Plans</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
|
||||
<!-- Study Resources -->
|
||||
<details class="resource-group" open aria-label="Study resources">
|
||||
<summary aria-expanded="true">Study Resources</summary>
|
||||
@@ -1478,6 +1524,7 @@
|
||||
<li><a href="/stories" {% if request.url.path.startswith("/stories") %}class="current"{% endif %}>Bible Stories</a></li>
|
||||
<li><a href="/topics" {% if request.url.path.startswith("/topics") %}class="current"{% endif %}>Topics</a></li>
|
||||
<li><a href="/resources" {% if request.url.path == "/resources" or request.url.path.startswith("/biblical-") or request.url.path.startswith("/names-of-") or request.url.path.startswith("/parables") or request.url.path.startswith("/the-twelve-apostles") or request.url.path.startswith("/women-of-the-bible") or request.url.path.startswith("/fruits-of-the-spirit") or request.url.path.startswith("/miracles-of-jesus") or request.url.path.startswith("/prayers-of-the-bible") or request.url.path.startswith("/beatitudes") or request.url.path.startswith("/ten-commandments") or request.url.path.startswith("/armor-of-god") or request.url.path.startswith("/i-am-statements") or request.url.path.startswith("/trinity") or request.url.path.startswith("/christology") or request.url.path.startswith("/soteriology") or request.url.path.startswith("/pneumatology") or request.url.path.startswith("/eschatology") or request.url.path.startswith("/ecclesiology") or request.url.path.startswith("/types-and-shadows") or request.url.path.startswith("/messianic-prophecies") or request.url.path.startswith("/blood-in-scripture") or request.url.path.startswith("/kingdom-of-god") or request.url.path.startswith("/spirits-and-demons") or request.url.path.startswith("/personifications") or request.url.path.startswith("/bibliology") or request.url.path.startswith("/theology-proper") or request.url.path.startswith("/anthropology") or request.url.path.startswith("/hamartiology") or request.url.path.startswith("/providence") or request.url.path.startswith("/grace") or request.url.path.startswith("/justification") or request.url.path.startswith("/sanctification") or request.url.path.startswith("/law-and-gospel") or request.url.path.startswith("/worship") or request.url.path.startswith("/tetragrammaton") %}class="current"{% endif %}>Resources</a></li>
|
||||
<li><a href="/random-verse" {% if request.url.path == "/random-verse" %}class="current"{% endif %}>Random Verse</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
|
||||
@@ -1490,6 +1537,15 @@
|
||||
</ul>
|
||||
</details>
|
||||
|
||||
<!-- Your Library -->
|
||||
<details class="resource-group" open aria-label="Your personal library">
|
||||
<summary aria-expanded="true">Your Library</summary>
|
||||
<ul>
|
||||
<li><a href="/reading-plans" {% if request.url.path.startswith("/reading-plans") %}class="current"{% endif %}>Reading Plans <span class="nav-badge" id="reading-plans-badge"></span></a></li>
|
||||
<li><a href="/stars" {% if request.url.path == "/stars" %}class="current"{% endif %}>Starred Pages <span class="nav-badge" id="stars-badge"></span></a></li>
|
||||
</ul>
|
||||
</details>
|
||||
|
||||
<!-- About -->
|
||||
<div class="nav-footer">
|
||||
<a href="/about" {% if request.url.path == "/about" %}class="current"{% endif %}>About</a>
|
||||
|
||||
@@ -1,199 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Bookmarks - KJV Study{% endblock %}
|
||||
{% block description %}Your saved bookmarks from KJV Study.{% endblock %}
|
||||
|
||||
{% block head %}
|
||||
<style>
|
||||
.bookmarks-container {
|
||||
max-width: 700px;
|
||||
}
|
||||
|
||||
.bookmarks-empty {
|
||||
text-align: center;
|
||||
padding: 3rem 1rem;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.bookmarks-empty p {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.bookmark-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 2rem 0;
|
||||
}
|
||||
|
||||
.bookmark-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 1rem;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 8px;
|
||||
margin-bottom: 0.75rem;
|
||||
background: var(--bg-color);
|
||||
transition: all 0.15s;
|
||||
}
|
||||
|
||||
.bookmark-item:hover {
|
||||
border-color: var(--link-color);
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.05);
|
||||
}
|
||||
|
||||
.bookmark-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.bookmark-title {
|
||||
font-size: 1.1rem;
|
||||
font-weight: 500;
|
||||
color: var(--text-color);
|
||||
text-decoration: none;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.bookmark-title:hover {
|
||||
color: var(--link-color);
|
||||
}
|
||||
|
||||
.bookmark-date {
|
||||
font-size: 0.85rem;
|
||||
color: var(--text-secondary);
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
|
||||
.bookmark-remove {
|
||||
background: none;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
padding: 0.4rem 0.8rem;
|
||||
cursor: pointer;
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.85rem;
|
||||
transition: all 0.15s;
|
||||
}
|
||||
|
||||
.bookmark-remove:hover {
|
||||
border-color: #c41e3a;
|
||||
color: #c41e3a;
|
||||
background: rgba(196, 30, 58, 0.05);
|
||||
}
|
||||
|
||||
.clear-all-btn {
|
||||
display: inline-block;
|
||||
margin-top: 1rem;
|
||||
padding: 0.5rem 1rem;
|
||||
background: none;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.9rem;
|
||||
transition: all 0.15s;
|
||||
}
|
||||
|
||||
.clear-all-btn:hover {
|
||||
border-color: #c41e3a;
|
||||
color: #c41e3a;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .bookmark-item {
|
||||
background: #1a1a1a;
|
||||
border-color: #333;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .bookmark-item:hover {
|
||||
border-color: var(--link-color);
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="bookmarks-container">
|
||||
<h1>Bookmarks</h1>
|
||||
|
||||
<div id="bookmarks-content">
|
||||
<!-- Populated by JavaScript -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
function getBookmarks() {
|
||||
try {
|
||||
return JSON.parse(localStorage.getItem('kjvBookmarks') || '[]');
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
function saveBookmarks(bookmarks) {
|
||||
localStorage.setItem('kjvBookmarks', JSON.stringify(bookmarks));
|
||||
}
|
||||
|
||||
function formatDate(dateStr) {
|
||||
var date = new Date(dateStr);
|
||||
return date.toLocaleDateString('en-US', {
|
||||
year: 'numeric',
|
||||
month: 'short',
|
||||
day: 'numeric'
|
||||
});
|
||||
}
|
||||
|
||||
function removeBookmark(url) {
|
||||
var bookmarks = getBookmarks();
|
||||
bookmarks = bookmarks.filter(function(b) { return b.url !== url; });
|
||||
saveBookmarks(bookmarks);
|
||||
renderBookmarks();
|
||||
}
|
||||
|
||||
function clearAllBookmarks() {
|
||||
if (confirm('Remove all bookmarks?')) {
|
||||
localStorage.removeItem('kjvBookmarks');
|
||||
renderBookmarks();
|
||||
}
|
||||
}
|
||||
|
||||
function renderBookmarks() {
|
||||
var container = document.getElementById('bookmarks-content');
|
||||
var bookmarks = getBookmarks();
|
||||
|
||||
if (bookmarks.length === 0) {
|
||||
container.innerHTML =
|
||||
'<div class="bookmarks-empty">' +
|
||||
'<p>No bookmarks yet.</p>' +
|
||||
'<p>Click the <strong>☆</strong> button on any page to save it here.</p>' +
|
||||
'</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
var html = '<ul class="bookmark-list">';
|
||||
bookmarks.forEach(function(bookmark) {
|
||||
html += '<li class="bookmark-item">' +
|
||||
'<div class="bookmark-info">' +
|
||||
'<a href="' + bookmark.url + '" class="bookmark-title">' + bookmark.title + '</a>' +
|
||||
'<div class="bookmark-date">Saved ' + formatDate(bookmark.date) + '</div>' +
|
||||
'</div>' +
|
||||
'<button class="bookmark-remove" onclick="removeBookmark(\'' + bookmark.url + '\')" title="Remove bookmark">Remove</button>' +
|
||||
'</li>';
|
||||
});
|
||||
html += '</ul>';
|
||||
|
||||
if (bookmarks.length > 1) {
|
||||
html += '<button class="clear-all-btn" onclick="clearAllBookmarks()">Clear all bookmarks</button>';
|
||||
}
|
||||
|
||||
container.innerHTML = html;
|
||||
}
|
||||
|
||||
// Make functions global for onclick handlers
|
||||
window.removeBookmark = removeBookmark;
|
||||
window.clearAllBookmarks = clearAllBookmarks;
|
||||
|
||||
// Initial render
|
||||
renderBookmarks();
|
||||
})();
|
||||
</script>
|
||||
{% endblock %}
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user