mirror of
https://github.com/wahyd4/wahyd4.github.com.git
synced 2026-08-15 16:45:56 +10:00
- Floating Aa button on left edge, aligned with sidebar-toggle - Panel: theme (light/paper/green/night), font (system/serif/sans/kai/mono), font size (A-/A+), line spacing (compact/cozy/relaxed), reset - localStorage persistence (toozhao-reading) - Replaces the old A-/A/A+ font-size control - Default font: Ubuntu + Noto Sans CJK (Linux system stack) for global, headings and posts (was Georgia/Noto Serif SC)
181 lines
6.9 KiB
Plaintext
181 lines
6.9 KiB
Plaintext
<script src="/js/tag-cloud.js"></script>
|
||
<script>
|
||
/* =========================================================
|
||
阅读设置面板(Aa 按钮)— 参考 Links Reading settings
|
||
支持:背景主题 / 字体类型 / 字号 / 行距 / 重置,localStorage 记忆
|
||
========================================================= */
|
||
(function () {
|
||
'use strict';
|
||
var LS_KEY = 'toozhao-reading';
|
||
var state = { theme: 'light', font: 'system', size: 1.05, spacing: 'cozy' };
|
||
|
||
var FONTS = {
|
||
system: '', /* 默认(Linux 系统字体) */
|
||
serif: 'Georgia, "Noto Serif SC", "Source Han Serif SC", "Songti SC", SimSun, serif',
|
||
sans: '"Noto Sans SC", "Source Han Sans SC", "Microsoft YaHei", "PingFang SC", "WenQuanYi Micro Hei", sans-serif',
|
||
kai: '"KaiTi", "STKaiti", "Kaiti SC", "AR PL UKai CN", serif',
|
||
mono: 'SFMono-Regular, Consolas, "Liberation Mono", Menlo, monospace'
|
||
};
|
||
var THEMES = { light: '明亮', paper: '纸感', green: '护眼', night: '夜间' };
|
||
var SPACING = {
|
||
compact: { label: '紧凑', lh: 1.6 },
|
||
cozy: { label: '舒适', lh: 1.9 },
|
||
relaxed: { label: '宽松', lh: 2.3 }
|
||
};
|
||
|
||
function loadState() {
|
||
try {
|
||
var raw = localStorage.getItem(LS_KEY);
|
||
if (!raw) return;
|
||
var s = JSON.parse(raw);
|
||
if (THEMES[s.theme]) state.theme = s.theme;
|
||
if (FONTS.hasOwnProperty(s.font)) state.font = s.font;
|
||
if (typeof s.size === 'number' && s.size >= 0.8 && s.size <= 1.5) state.size = s.size;
|
||
if (SPACING[s.spacing]) state.spacing = s.spacing;
|
||
} catch (e) { /* ignore */ }
|
||
}
|
||
|
||
function saveState() {
|
||
try { localStorage.setItem(LS_KEY, JSON.stringify(state)); } catch (e) { /* ignore */ }
|
||
}
|
||
|
||
function buildUI() {
|
||
if (document.getElementById('reading-toggle')) return;
|
||
|
||
var toggle = document.createElement('button');
|
||
toggle.id = 'reading-toggle';
|
||
toggle.className = 'reading-toggle';
|
||
toggle.title = '阅读设置';
|
||
toggle.textContent = 'Aa';
|
||
document.body.appendChild(toggle);
|
||
|
||
var panel = document.createElement('div');
|
||
panel.id = 'reading-panel';
|
||
panel.className = 'reading-panel';
|
||
|
||
// 主题(swatch 全部为硬编码静态 HTML,无用户输入,安全)
|
||
var rowT = document.createElement('div');
|
||
rowT.className = 'rp-row';
|
||
rowT.innerHTML = '<h4>主题</h4><div class="rp-themes">' +
|
||
['light', 'paper', 'green', 'night'].map(function (t) {
|
||
return '<button class="sw sw-' + t + '" data-theme="' + t + '" title="' + THEMES[t] + '"><span></span>' + THEMES[t] + '</button>';
|
||
}).join('') + '</div>';
|
||
|
||
var rowF = document.createElement('div');
|
||
rowF.className = 'rp-row';
|
||
rowF.innerHTML = '<h4>字体</h4><div class="rp-fonts">' +
|
||
['system', 'serif', 'sans', 'kai', 'mono'].map(function (f) {
|
||
var label = { system: '系统', serif: '宋体', sans: '黑体', kai: '楷体', mono: '等宽' }[f];
|
||
return '<button data-font="' + f + '">' + label + '</button>';
|
||
}).join('') + '</div>';
|
||
|
||
var rowS = document.createElement('div');
|
||
rowS.className = 'rp-row';
|
||
rowS.innerHTML = '<h4>字号</h4><div class="rp-size">' +
|
||
'<button class="rp-minus" title="减小">A−</button><span class="rp-size-val">17px</span><button class="rp-plus" title="增大">A+</button></div>';
|
||
|
||
var rowSp = document.createElement('div');
|
||
rowSp.className = 'rp-row';
|
||
rowSp.innerHTML = '<h4>行距</h4><div class="rp-spacing">' +
|
||
['compact', 'cozy', 'relaxed'].map(function (s) {
|
||
return '<button data-spacing="' + s + '">' + SPACING[s].label + '</button>';
|
||
}).join('') + '</div>';
|
||
|
||
var reset = document.createElement('button');
|
||
reset.className = 'rp-reset';
|
||
reset.textContent = '↺ 恢复默认';
|
||
reset.addEventListener('click', function () {
|
||
state = { theme: 'light', font: 'system', size: 1.05, spacing: 'cozy' };
|
||
saveState();
|
||
apply();
|
||
render();
|
||
});
|
||
|
||
panel.appendChild(rowT);
|
||
panel.appendChild(rowF);
|
||
panel.appendChild(rowS);
|
||
panel.appendChild(rowSp);
|
||
panel.appendChild(reset);
|
||
document.body.appendChild(panel);
|
||
|
||
toggle.addEventListener('click', function (e) {
|
||
e.stopPropagation();
|
||
panel.classList.toggle('open');
|
||
toggle.classList.toggle('active', panel.classList.contains('open'));
|
||
});
|
||
panel.addEventListener('click', function (e) { e.stopPropagation(); });
|
||
|
||
panel.querySelectorAll('.sw').forEach(function (b) {
|
||
b.addEventListener('click', function () { state.theme = b.dataset.theme; saveState(); apply(); render(); });
|
||
});
|
||
panel.querySelectorAll('.rp-fonts button').forEach(function (b) {
|
||
b.addEventListener('click', function () { state.font = b.dataset.font; saveState(); apply(); render(); });
|
||
});
|
||
panel.querySelectorAll('.rp-spacing button').forEach(function (b) {
|
||
b.addEventListener('click', function () { state.spacing = b.dataset.spacing; saveState(); apply(); render(); });
|
||
});
|
||
panel.querySelector('.rp-minus').addEventListener('click', function () {
|
||
state.size = Math.max(0.8, +(state.size - 0.05).toFixed(2)); saveState(); apply(); render();
|
||
});
|
||
panel.querySelector('.rp-plus').addEventListener('click', function () {
|
||
state.size = Math.min(1.5, +(state.size + 0.05).toFixed(2)); saveState(); apply(); render();
|
||
});
|
||
|
||
document.addEventListener('click', function (e) {
|
||
if (panel.classList.contains('open') && !panel.contains(e.target) && e.target !== toggle) {
|
||
panel.classList.remove('open');
|
||
toggle.classList.remove('active');
|
||
}
|
||
});
|
||
document.addEventListener('keydown', function (e) {
|
||
if (e.key === 'Escape' && panel.classList.contains('open')) {
|
||
panel.classList.remove('open');
|
||
toggle.classList.remove('active');
|
||
}
|
||
});
|
||
}
|
||
|
||
function apply() {
|
||
var body = document.body;
|
||
var pb = document.querySelector('.post-body');
|
||
|
||
body.setAttribute('data-rtheme', state.theme);
|
||
if (pb) {
|
||
pb.style.fontFamily = FONTS[state.font] || '';
|
||
pb.style.fontSize = state.size + 'em';
|
||
pb.style.lineHeight = SPACING[state.spacing].lh;
|
||
}
|
||
}
|
||
|
||
function render() {
|
||
var panel = document.getElementById('reading-panel');
|
||
if (!panel) return;
|
||
|
||
panel.querySelectorAll('.sw').forEach(function (b) {
|
||
b.classList.toggle('active', b.dataset.theme === state.theme);
|
||
});
|
||
panel.querySelectorAll('.rp-fonts button').forEach(function (b) {
|
||
b.classList.toggle('active', b.dataset.font === state.font);
|
||
});
|
||
panel.querySelectorAll('.rp-spacing button').forEach(function (b) {
|
||
b.classList.toggle('active', b.dataset.spacing === state.spacing);
|
||
});
|
||
var val = panel.querySelector('.rp-size-val');
|
||
if (val) val.textContent = Math.round(16 * state.size) + 'px';
|
||
}
|
||
|
||
function init() {
|
||
loadState();
|
||
buildUI();
|
||
apply();
|
||
render();
|
||
}
|
||
|
||
if (document.readyState === 'loading') {
|
||
document.addEventListener('DOMContentLoaded', init);
|
||
} else {
|
||
init();
|
||
}
|
||
})();
|
||
</script>
|