diff --git a/Dockerfile b/Dockerfile index 508fa61..f20bd45 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,14 +2,15 @@ FROM python:3.13-alpine ENV PYTHONUNBUFFERED=1 \ - SITE_ROOT=/srv/site \ + SITE_ROOTS=/srv/assets:/srv/books \ PORT=8080 WORKDIR /srv +# Baked in so the image runs standalone; compose bind-mounts over all three +# for live edits without a rebuild. COPY server.py /srv/server.py -# Baked in so the image runs standalone; compose bind-mounts over this for -# live edits without a rebuild. -COPY books/ /srv/site/ +COPY assets/ /srv/assets/ +COPY books/ /srv/books/ RUN adduser -S -u 10001 bible USER 10001 diff --git a/assets/app.js b/assets/app.js index 048fa86..3b8dcfd 100644 --- a/assets/app.js +++ b/assets/app.js @@ -1,107 +1,28 @@ -// Bookmark UI. Deliberately small and dependency-free: the state is one -// verse reference, so it lives in localStorage rather than IndexedDB -- -// localStorage reads synchronously, which lets the index page paint the -// bookmark link on first paint instead of flashing an empty slot. +// Verse modal wiring. The modal markup lives in base.njk and the bookmark +// lives in the service worker, so all this file does is decide which verse +// was clicked, fill the form, and show it -- Bootstrap handles opening, +// dismissing and focus, and htmx carries the save. (function () { 'use strict'; - var KEY = 'bible:bookmark'; - var dialog = null; - var current = null; + var currentUrl = ''; - // localStorage throws (not returns null) in some private-browsing modes, - // so every access is guarded and the feature degrades to a no-op. - function readBookmark() { - try { return JSON.parse(localStorage.getItem(KEY) || 'null'); } - catch (e) { return null; } - } - - function writeBookmark(value) { - try { localStorage.setItem(KEY, JSON.stringify(value)); return true; } - catch (e) { return false; } - } - - // -- index page --------------------------------------------------------- - - function renderBookmarkLink() { - var slot = document.getElementById('bookmark'); - if (!slot) return; - var mark = readBookmark(); - slot.textContent = ''; - if (!mark || !mark.href) { slot.hidden = true; return; } - var label = document.createElement('span'); - label.className = 'bookmark-label'; - label.textContent = 'Bookmark'; - var link = document.createElement('a'); - link.className = 'bookmark-link'; - link.href = mark.href; - link.textContent = mark.ref; - slot.appendChild(label); - slot.appendChild(document.createTextNode(' ')); - slot.appendChild(link); - slot.hidden = false; - } - - // -- dialog ------------------------------------------------------------- - - function buildDialog() { - var d = document.createElement('dialog'); - d.id = 'verse-dialog'; - d.className = 'verse-dialog'; - d.innerHTML = - '
' + - 'Currently bookmarked.
' + - 'Preparing the offline copy…
+ +{{ text }}
diff --git a/assets/sw.js b/assets/sw.js new file mode 100644 index 0000000..a0ad43b --- /dev/null +++ b/assets/sw.js @@ -0,0 +1,291 @@ +// The app's renderer, and its backend. Hand-written: this file is the source, +// served as-is apart from the two lines server.py stamps (see below). +// +// The files on disk are fragments: a +//