Compare commits

..

2 Commits

Author SHA1 Message Date
neveler
493d605495
优化设置页 (#421)
All checks were successful
continuous-integration/drone/push Build is passing
2026-04-16 17:31:10 +08:00
neveler
f2dbe9e57a
临时禁用磁盘缓存 (#451) 2026-04-16 17:02:19 +08:00
7 changed files with 130 additions and 148 deletions

View File

@ -9,6 +9,7 @@
# data_dir: _data # data_dir: _data
# includes_dir: _includes # includes_dir: _includes
# cache_dir: .jekyll-cache # cache_dir: .jekyll-cache
disable_disk_cache: true
sass: sass:
# sass_dir: _sass # sass_dir: _sass
# minimal-mistakes # minimal-mistakes

View File

@ -5,24 +5,18 @@ appearance_color:
- light - light
- dark - dark
- auto - auto
appearance_skin_light: appearance_color_switcher:
type: radio type: radio
default: default default: enable
options: options:
- default - enable
- air - disable
- aqua appearance_skin:
- contrast type: multi-radio
- dark children:
- dirt light:
- neon default: default
- mint dark:
- plum
- sunrise
- catppuccin_latte
- catppuccin_mocha
appearance_skin_dark:
type: radio
default: dark default: dark
options: options:
- default - default

View File

@ -1,46 +1,55 @@
--- ---
layout: document layout: single
--- ---
{% for group in page.data %} {%- for group in page.data %}
<h2 id="{{ group.title }}">{{ group.title }}</h2>
{% capture notice %}
## {{ group.title }}
{% for pair in group.settings %} {% for pair in group.settings %}
{% assign name = pair[0] %} {% assign name = pair[0] %}
{% assign value = pair[1] %} {% assign value = pair[1] %}
{% assign setting = site.data.settings[name] %} {% assign setting = site.data.settings[name] %}
{% if setting.type == 'radio' %}
<div class="notice"> <div class="notice">
<ul class="task-list"> <h3 id="{{ name }}">{{ value.title }}</h3>
<li><strong>{{ value.title }}</strong></li> {% if value.description %}<p>{{ value.description }}</p>{% endif %}
<li>{{ value.description }}</li> {% if setting.type == 'radio' %}
{% for option in setting.options %} {% for option in setting.options %}
<li class="task-list-item"> <label>
<input type="radio" class="task-list-item-checkbox" name="{{ name }}" value="{{ option }}" id="{{ name }}_{{ option }}"{% if setting.default == option %} checked{% endif %}><label for="{{ name }}_{{ option }}">{{ value.options[option] }}</label> <input type="radio"{% if setting.default == option %} checked{% endif %} class="setting-item" name="{{ name }}" value="{{ option }}">
</li> {{ value.options[option] }}
</label>
{% endfor %} {% endfor %}
</ul> {% elsif setting.type == 'multi-radio' %}
<table class="setting-multi-radio">
<thead>
<tr>
<th>{{ value.title }}</th>
{% for item in setting.children %}
{% capture item_name %}{{ item[0] }}{% endcapture %}
<th>{{ value.children[item_name] }}</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for option in setting.options %}
<tr>
<th>{{ value.options[option] }}</th>
{% for item in setting.children %}
<td><input class="setting-item"{% if item[1].default == option %} checked{% endif %} type="radio" name="{{ name }}.{{ item[0] }}" value="{{ option }}"></td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
</div> </div>
{% endfor %}
{% endfor %}
<script> <script>
settings.onChange("{{ name }}", function (newValue, oldValue) { for (const settingItem of document.getElementsByClassName("setting-item")) {
var list = document.getElementsByName("{{ name }}"); settingItem.addEventListener("change", ({ target }) => settings.set(target.name, target.value));
for (var i = 0; i < list.length; i++) { settings.onChange(settingItem.name, (value) => settingItem.type === "radio" && (settingItem.checked = settingItem.value === value));
list[i].checked = list[i].value === newValue;
list[i].onchange = function () {
if (this.checked) {
settings.set(this.name, this.value);
} }
}
}
});
</script> </script>
{% endif %}
{% endfor %} <style>.notice label input { display: inline }</style>
{% endcapture %}
{{ notice | markdownify }}
{% endfor %}

View File

@ -47,10 +47,27 @@ blockquote {
word-break: break-word; word-break: break-word;
} }
.task-list-item label {
display: inline
}
.m0 { .m0 {
margin: 0 !important; margin: 0 !important;
} }
table.setting-multi-radio td {
border: 0;
text-align: center;
border-left: 1px solid mix(#000, $border-color, 25%);
}
input[type="radio"] {
width: 0;
margin: 0;
margin-inline-end: 1em;
font-family: var(--fa-family-classic);
}
input[type="radio"]::before {
content: "\f111";
}
input[type="radio"]:checked::before {
content: "\f192";
}

View File

@ -1,76 +1,36 @@
--- ---
layout: null layout: null
--- ---
(function (global) { (function () {
var PREFIX = "HMCL_DOCS_SETTINGS_"; const PREFIX = "HMCL_DOCS_SETTINGS_", data = {}, bus = new EventTarget(), configs = /*{%comment%}*/{}/*{%endcomment%}*/ /**{{'/'}}{{ site.data.settings | jsonify }}/**/;
var data = {}; window.addEventListener("storage", ({ key, newValue }) => key !== null && key.startsWith(PREFIX) && newValue !== data[key] && bus.dispatchEvent(new CustomEvent(key, { detail: (data[key] = newValue) })));
var events = {}; for (const [key, config] of Object.entries(configs)) {
var config = /*{%comment%}*/{}/*{%endcomment%}*//**{{'/'}}{{ site.data.settings | jsonify }}/**/; if (config.children === undefined) continue;
for (const [childKey, child] of Object.entries(config.children)) {
global.addEventListener("storage", function (event) { configs[`${key}.${childKey}`] = { ...config, ...child };
if (!event.key) return;
if (event.key.indexOf(PREFIX) !== 0) return;
var handlers = events[event.key];
if (!handlers) return;
var newValue = event.newValue;
var oldValue = event.oldValue;
if (oldValue === newValue) return;
data[event.key] = newValue;
for (var i = 0; i < handlers.length; i++) {
if (typeof handlers[i] === "function") {
handlers[i](newValue, oldValue);
}
}
});
var settings = {
set: function (key, value) {
if (config[key] === undefined) return;
var strKey = (PREFIX + key).toUpperCase();
var newValue = value + "";
data[strKey] = newValue;
localStorage.setItem(strKey, newValue);
var handlers = events[strKey];
if (!handlers) return;
for (var i = 0; i < handlers.length; i++) {
if (typeof handlers[i] === "function") {
handlers[i](newValue);
} }
} }
const formatKey = (key) => PREFIX + key.toUpperCase().replaceAll(".", "_");
window.settings = {
set(key, value) {
const name = formatKey(key);
localStorage.setItem(name, (data[name] = String(value)));
bus.dispatchEvent(new CustomEvent(name, { detail: data[name] }));
}, },
get(key) {
get: function (key, defaultValue) { const name = formatKey(key);
if (config[key] === undefined) return; if (data[name] !== undefined) return data[name];
var strKey = (PREFIX + key).toUpperCase(); const value = localStorage.getItem(name);
data.hasOwnProperty(strKey) || (data[strKey] = localStorage.getItem(strKey)); if (value !== null) return data[name] = value;
if (typeof defaultValue === "string" && data[strKey] === null) { const config = configs[key];
return defaultValue; if (config === undefined || typeof config.default !== "string") return null;
} return config.default;
return data[strKey];
}, },
onChange(key, handler) {
refresh: function (key) { const value = this.get(key);
if (config[key] === undefined) return; if (value === null) return;
settings.set(key, settings.get(key, config[key].default)); handler(value);
bus.addEventListener(formatKey(key), (event) => handler(event.detail));
}, },
onChange: function (key, handler) {
if (config[key] === undefined) return;
if (typeof handler !== "function") return;
var strKey = (PREFIX + key).toUpperCase();
if (config[key].type === "radio") {
handler(settings.get(key, config[key].default));
}
if (!events[strKey]) {
events[strKey] = [handler];
} else {
events[strKey].push(handler);
}
}
}; };
global.settings = settings; })();
})(window);

View File

@ -4,11 +4,14 @@ layout: null
window.addEventListener("DOMContentLoaded", function () { window.addEventListener("DOMContentLoaded", function () {
var skinLink = document.getElementById("skin"); var skinLink = document.getElementById("skin");
var darkModeQuery = window.matchMedia("(prefers-color-scheme: dark)"); var darkModeQuery = window.matchMedia("(prefers-color-scheme: dark)");
function applySkin(skin) {
skinLink.href = "{{ '/assets/css/skins/' | relative_url }}" + skin + ".css";
}
function applyDarkSkin() { function applyDarkSkin() {
skinLink.href = "{{ '/assets/css/skins/' | relative_url }}" + settings.get("appearance_skin_dark", "dark") + ".css"; applySkin(settings.get("appearance_skin.dark"));
} }
function applyLightSkin() { function applyLightSkin() {
skinLink.href = "{{ '/assets/css/skins/' | relative_url }}" + settings.get("appearance_skin_light", "default") + ".css"; applySkin(settings.get("appearance_skin.light"));
} }
function autoSchemeHandler() { function autoSchemeHandler() {
if (darkModeQuery.matches) { if (darkModeQuery.matches) {
@ -61,10 +64,15 @@ window.addEventListener("DOMContentLoaded", function () {
} }
} }
settings.onChange("appearance_color", applyTheme); settings.onChange("appearance_color", applyTheme);
settings.onChange("appearance_skin_dark", function () { settings.onChange("appearance_skin.dark", function () {
settings.refresh("appearance_color"); applyTheme(settings.get("appearance_color"));
}); });
settings.onChange("appearance_skin_light", function () { settings.onChange("appearance_skin.light", function () {
settings.refresh("appearance_color"); applyTheme(settings.get("appearance_color"));
});
settings.onChange("appearance_color_switcher", function (value) {
if (modeSwitcher !== null) {
modeSwitcher.style.display = value === "enable" ? "" : "none";
}
}); });
}); });

View File

@ -12,25 +12,18 @@ data:
light: 亮色 light: 亮色
dark: 暗色 dark: 暗色
auto: 自动 auto: 自动
appearance_skin_light: appearance_color_switcher:
title: 亮色皮肤 title: 颜色模式快捷开关
description: 亮色模式下应用的皮肤 description: 用于快捷切换站点主题颜色模式的开关,位于页面右上角
options: options:
default: 默认 (Default) enable: 启用
air: 天空 (Air) disable: 禁用
aqua: 水蓝 (Aque) appearance_skin:
contrast: 高对比 (Contrast) title: 皮肤
dark: 暗色 (Dark) description: 各颜色模式下应用的皮肤。
dirt: 泥土 (Dirt) children:
neon: 霓虹 (Neno) light: 亮色
mint: 薄荷 (Mint) dark: 暗色
plum: 梅紫 (Plum)
sunrise: 日出 (Sunrise)
catppuccin_latte: Catppuccin Latte
catppuccin_mocha: Catppuccin Mocha
appearance_skin_dark:
title: 暗色皮肤
description: 暗色模式下应用的皮肤。
options: options:
default: 默认 (Default) default: 默认 (Default)
air: 天空 (Air) air: 天空 (Air)