diff --git a/static/css/app.css b/static/css/app.css
index 5b200a9..fd2bcc6 100644
--- a/static/css/app.css
+++ b/static/css/app.css
@@ -2130,7 +2130,7 @@ body.reader-immersive.reader-show-bottom .reader-overlay { bottom: var(--bar-h)
.footnote-popover-close { position:absolute; top:4px; right:6px; background:none; border:none; color:inherit; opacity:0.5; cursor:pointer; font-size:14px; padding:2px 4px; line-height:1; }
/* Highlight marks */
-.reader-no-bold * { font-weight: normal !important; }
+.reader-no-bold * { font-weight: inherit !important; }
.reader-content img { max-width: 100%; height: auto; display: block; }
/* color:inherit overrides the browser's UA-default `mark { color: black }` —
without it, highlighted text abruptly flips to black regardless of the
diff --git a/static/js/app.js b/static/js/app.js
index 64a27d3..cf513db 100644
--- a/static/js/app.js
+++ b/static/js/app.js
@@ -3254,7 +3254,7 @@ async function _syncPendingAnnotations() {
// Reader settings
let readerSettings = { fontSize: 16, lineHeight: 1.8, maxWidth: 65, theme: 'dark',
- fontFamily: 'serif', noBold: false,
+ fontFamily: 'serif', noBold: false, fontWeight: null,
pdfZoom: 100, pdfInverted: false, pdfPaginated: false, pdfSpread: false };
let readerSettingsPanelOpen = false;
let currentPdfDoc = null;
@@ -5026,7 +5026,7 @@ function jumpToTocEntry(href) {
function loadReaderSettings(bookId) {
// Reset to defaults, then apply per-book overrides
Object.assign(readerSettings, { fontSize: 16, lineHeight: 1.8, maxWidth: 65, theme: 'dark',
- fontFamily: 'serif', noBold: false,
+ fontFamily: 'serif', noBold: false, fontWeight: null,
pdfZoom: 100, pdfInverted: false, pdfPaginated: false, pdfSpread: false });
try {
const saved = JSON.parse(localStorage.getItem(`diora_reader_settings_${bookId}`) || '{}');
@@ -5060,6 +5060,16 @@ function applyReaderSettings(isPdf) {
};
contentEl.style.fontFamily = fontMap[readerSettings.fontFamily] || fontMap.serif;
contentEl.classList.toggle('reader-no-bold', !!readerSettings.noBold);
+ // Weight: null keeps the stylesheet's. --reader-weight lets old WebViews
+ // without variable-font support (which drop weights like 425) pick a
+ // static cut instead.
+ contentEl.style.fontWeight = '';
+ if (readerSettings.fontWeight) {
+ contentEl.style.fontWeight = String(readerSettings.fontWeight);
+ contentEl.style.setProperty('--reader-weight', String(readerSettings.fontWeight));
+ } else {
+ contentEl.style.removeProperty('--reader-weight');
+ }
if (_currentPositionAnchor && currentBookId) {
_suppressScrollJumpDetect();
requestAnimationFrame(() => restoreFromAnchor($('reader-content'), _currentPositionAnchor));
@@ -5083,6 +5093,19 @@ function applyReaderSettings(isPdf) {
if (isPdf && markerModeActive) toggleMarkerMode();
}
+// Font weight slider: the variable Source Serif 4 takes any weight, the
+// system fonts only their fixed cuts (multiples of 100).
+function _readerWeightStep() {
+ return readerSettings.fontFamily === 'serif' ? 25 : 100;
+}
+
+function _readerWeight() {
+ const step = _readerWeightStep();
+ const w = readerSettings.fontWeight
+ || parseInt(getComputedStyle($('reader-content')).fontWeight, 10) || 400;
+ return Math.round(w / step) * step;
+}
+
function toggleSettingsPanel() {
const overlay = $('reader-overlay');
const contentEl = $('reader-content');
@@ -5108,6 +5131,7 @@ function toggleSettingsPanel() {
+
@@ -5187,12 +5211,36 @@ function toggleSettingsPanel() {
saveReaderSettings();
});
+ const weightRange = panel.querySelector('#rs-weight');
+ const weightVal = panel.querySelector('#rs-weight-val');
+ weightRange.addEventListener('input', () => {
+ readerSettings.fontWeight = parseInt(weightRange.value, 10);
+ weightVal.textContent = readerSettings.fontWeight;
+ applyReaderSettings(false);
+ saveReaderSettings();
+ });
+ panel.querySelector('#rs-weight-minus').addEventListener('click', () => {
+ weightRange.value = Math.max(+weightRange.min, parseInt(weightRange.value, 10) - +weightRange.step);
+ weightRange.dispatchEvent(new Event('input'));
+ });
+ panel.querySelector('#rs-weight-plus').addEventListener('click', () => {
+ weightRange.value = Math.min(+weightRange.max, parseInt(weightRange.value, 10) + +weightRange.step);
+ weightRange.dispatchEvent(new Event('input'));
+ });
+
panel.querySelectorAll('[data-rs-font]').forEach(btn => {
btn.addEventListener('click', () => {
readerSettings.fontFamily = btn.dataset.rsFont;
panel.querySelectorAll('[data-rs-font]').forEach(b => b.classList.toggle('active', b === btn));
+ // The system fonts only have cuts at multiples of 100.
+ weightRange.step = _readerWeightStep();
+ if (readerSettings.fontWeight) {
+ readerSettings.fontWeight = Math.round(readerSettings.fontWeight / weightRange.step) * weightRange.step;
+ }
applyReaderSettings(false);
saveReaderSettings();
+ weightRange.value = _readerWeight();
+ weightVal.textContent = _readerWeight();
});
});
diff --git a/static/js/sw.js b/static/js/sw.js
index 24a7b9c..2b72b57 100644
--- a/static/js/sw.js
+++ b/static/js/sw.js
@@ -2,7 +2,7 @@
* diora service worker — caches the app shell for offline use.
*/
-const CACHE = 'diora-v46';
+const CACHE = 'diora-v47';
const PODCAST_CACHE = 'diora-podcast-v1';
const SHELL = [
'/static/css/app.css',