feat: 完善主题功能并适配插件 (v1.1.5)

功能新增
- 新增页面布局契约 templates/layout.html,插件前台页面可复用主题外观,避免回退到 Halo fallback 布局
- 新增微信读书页面模板 (weread.html),集成 halo-weread-plugin 书架
- 新增订阅页面模板 (page_subscriptions.html)
- 新增欢迎弹窗组件(IP 地理定位,多 API 容错)
- 新增点击特效组件(9 种效果)
- 页脚新增全站统计,支持开关与统计项配置,默认开启

插件适配
- 适配 extra-api:文章字数改服务端统计、正文可选中英混排与服务端代码高亮
- 修正各页面插件应用市场链接与插件名

修复
- 修复文章正文图片/视频无法居中(正文样式移至全局,scoped 样式对 Thymeleaf 注入内容无效)
- 修复正文 H1-H6 字号过大,重排层级与移动端断点
- 修复 th: 属性中文字符串缺少引号导致的 Thymeleaf 解析异常(/links、/moments 500)
- 修复 JS 内联表达式与 Thymeleaf 内联语法冲突,全部内联脚本加 th:inline="none"
- 修复移除 i18n 后 steam 页 Elvis 表达式回归问题
- 修正订阅页 th:attr 引号嵌套错误

优化
- Hero 移除灰色遮罩,下滑箭头改为可点击平滑滚动
- 优化目录样式:移除圆点与下拉箭头,支持两行显示,修复长目录截断
- 移除 i18n 多语言支持

清理
- 移除未使用的 Vue/图标依赖与废弃脚本、重复资源
This commit is contained in:
2026-09-26 11:18:52 +08:00
parent 9d0bfcfb4f
commit 430d107d44
78 changed files with 2959 additions and 36075 deletions
+28 -38
View File
@@ -5,19 +5,30 @@ import Footer from "../components/Footer.astro";
import ScrollReveal from "../components/ScrollReveal.astro";
import CursorGlow from "../components/CursorGlow.astro";
import MobileMenu from "../components/MobileMenu.astro";
import ClickEffect from "../components/ClickEffect.astro";
interface Props {
pageTitle?: string;
contentClass?: string;
wide?: boolean;
home?: boolean;
/** 渲染为 Halo 页面布局契约(templates/layout.html 的 html(head, content) 片段) */
layoutFragment?: boolean;
}
const { pageTitle, contentClass, wide, home } = Astro.props;
const { pageTitle, contentClass, wide, home, layoutFragment = false } = Astro.props;
const fragmentAttr = layoutFragment ? "html (head, content)" : undefined;
---
<!doctype html>
<html class={home ? 'wi-home-snap' : ''} style="overflow-x:hidden" th:lang="${#locale.toLanguageTag()}" th:classappend="${theme.config?.animation?.animation_enabled == false} ? 'wi-no-animations'" th:attr="data-lang=${theme.config?.basic?.language}">
<html
lang="zh-CN"
class={home ? 'wi-home-snap' : ''}
style="overflow-x:hidden"
th:classappend="${theme.config?.animation?.animation_enabled == false} ? 'wi-no-animations'"
xmlns:th={layoutFragment ? "https://www.thymeleaf.org" : undefined}
th:fragment={fragmentAttr}
>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
@@ -73,47 +84,31 @@ const { pageTitle, contentClass, wide, home } = Astro.props;
<style is:inline th:if="${theme.config?.style?.layout_container_width == 'medium' or theme.config?.style?.layout_container_width == null}" th:utext="${'.wi-content-wrap { max-width: 800px; } .wi-content-wrap--wide { max-width: 1200px; } .wi-main--wide { max-width: 1400px; }'}">/*width*/</style>
<style is:inline th:if="${theme.config?.style?.layout_container_width == 'wide'}" th:utext="${'.wi-content-wrap { max-width: 900px; } .wi-content-wrap--wide { max-width: 1400px; } .wi-main--wide { max-width: 1600px; }'}">/*width*/</style>
<style is:inline th:if="${theme.config?.style?.custom_css}" th:utext="${theme.config?.style?.custom_css}">/*custom*/</style>
{
layoutFragment && (
<title th:if="${head == null}" th:text="${site.title}"></title>
<th:block th:if="${head != null}">
<th:block th:replace="${head}" />
</th:block>
)
}
<slot name="head" />
</head>
<body class="wi-body">
<script is:inline>
<script is:inline th:inline="none">
window.addEventListener("unhandledrejection", function (e) {
if (e.reason && e.reason.message && e.reason.message.indexOf("Could not establish connection") !== -1) {
e.preventDefault();
}
});
</script>
<script is:inline>
(function(){
try{
var lang=document.documentElement.getAttribute("data-lang");
if(!lang||lang==="auto"||lang==="null"||lang===""){
var cookies=document.cookie.split(";");
for(var i=0;i<cookies.length;i++){
var c=cookies[i].trim();
if(c.indexOf("language=")===0){
document.cookie="language=;path=/;max-age=0;SameSite=Lax";
break;
}
}
return;
}
var tag=lang.replace(/_/g,"-");
var cookies=document.cookie.split(";");
var current="";
for(var i=0;i<cookies.length;i++){
var c=cookies[i].trim();
if(c.indexOf("language=")===0){current=c.substring(9);break;}
}
if(current!==tag){
document.cookie="language="+tag+";path=/;max-age=31536000;SameSite=Lax";
}
}catch(e){}
})();
</script>
<Navbar />
<main class:list={["wi-main", { "wi-main--wide": wide || home, "wi-main--home": home }]}>
{home ? (
{layoutFragment ? (
<div class:list={["wi-content-wrap", { "wi-content-wrap--wide": wide }]}>
<th:block th:replace="${content}" />
</div>
) : home ? (
<slot />
) : (
<div class:list={["wi-content-wrap", { "wi-content-wrap--wide": wide }]}>
@@ -125,6 +120,7 @@ const { pageTitle, contentClass, wide, home } = Astro.props;
<MobileMenu />
<ScrollReveal th:if="${theme.config?.animation?.animation_enabled != false and theme.config?.animation?.animation_scroll_reveal != false}" />
<CursorGlow th:if="${theme.config?.animation?.animation_enabled != false and theme.config?.animation?.animation_cursor_glow == true}" />
<ClickEffect />
</body>
</html>
@@ -169,10 +165,4 @@ html.wi-no-animations .wi-reveal {
transition: none;
}
.wi-post__content img {
max-width: 100%;
height: auto;
}
</style>