修复 - 首页文章卡片开启"显示字数"后无效果:该配置项此前只有声明没有实现,现已在文章流卡片接入服务端字数统计(依赖 API 扩展包,未安装时自动隐藏) - Hero 下滑箭头滚动不到位:Hero 为固定背景(position: fixed),此前箭头额外减去了导航栏高度,导致内容区上方始终露出一段 Hero 渐变;现与滚动吸附逻辑对齐,内容区顶部直接对齐视口顶部 - 鼠标点击特效开启后无效果:特效类型默认值为 off(关闭),开启开关但不选择类型时静默不生效;默认值改为"社会主义核心价值观" 移除 - 移除鼠标光感跟随功能及对应配置项 优化 - 上下篇导航:新增方向箭头与悬停反馈(背景、位移、变色),移动端收紧内边距 - Hero 一言自动刷新改为淡出→换文案→淡入的过渡动画,避免生硬跳变 - README 全面更新:环境要求、插件适配表(新增微信读书与 API 扩展包)、自定义页面模板、功能说明、目录结构与开发注意事项
166 lines
6.9 KiB
Plaintext
166 lines
6.9 KiB
Plaintext
---
|
|
import "../styles/main.scss";
|
|
import Navbar from "../components/Navbar.astro";
|
|
import Footer from "../components/Footer.astro";
|
|
import ScrollReveal from "../components/ScrollReveal.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, layoutFragment = false } = Astro.props;
|
|
const fragmentAttr = layoutFragment ? "html (head, content)" : undefined;
|
|
---
|
|
|
|
<!doctype html>
|
|
<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" />
|
|
<meta name="theme-color" content="#faf7f2" />
|
|
<meta th:if="${theme.config?.basic?.site_description}" name="description" th:content="${theme.config?.basic?.site_description}" />
|
|
<script is:inline th:attr="data-scheme=${theme.config?.style?.color_scheme}">
|
|
(function () {
|
|
var stored = localStorage.getItem("wi-theme");
|
|
var prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
var scheme = null;
|
|
if (document.currentScript && document.currentScript.getAttribute("data-scheme")) {
|
|
scheme = document.currentScript.getAttribute("data-scheme");
|
|
}
|
|
var isDark = false;
|
|
if (stored === "dark") {
|
|
isDark = true;
|
|
} else if (stored === "light") {
|
|
isDark = false;
|
|
} else if (scheme === "dark") {
|
|
isDark = true;
|
|
} else if (scheme === "light") {
|
|
isDark = false;
|
|
} else if (!stored && prefersDark) {
|
|
isDark = true;
|
|
}
|
|
if (isDark) {
|
|
document.documentElement.classList.add("dark");
|
|
document.documentElement.setAttribute("data-color-scheme", "dark");
|
|
var tc = document.querySelector('meta[name="theme-color"]');
|
|
if (tc) tc.setAttribute("content", "#1a1614");
|
|
} else {
|
|
document.documentElement.classList.remove("dark");
|
|
document.documentElement.setAttribute("data-color-scheme", "light");
|
|
var tc = document.querySelector('meta[name="theme-color"]');
|
|
if (tc) tc.setAttribute("content", "#faf7f2");
|
|
}
|
|
})();
|
|
</script>
|
|
<link
|
|
rel="icon"
|
|
th:href="${theme.config?.basic?.favicon ?: '/favicon.ico'}"
|
|
/>
|
|
<link rel="alternate" type="application/rss+xml" th:title="${site.title}" th:href="@{/feed.xml}" />
|
|
<link rel="preconnect" href="https://cdn.jsdelivr.net" />
|
|
<link rel="dns-prefetch" href="https://cdn.jsdelivr.net" />
|
|
<link rel="stylesheet" th:href="@{/assets/fonts/LXGWWenKai/result.css}" media="print" onload="this.media='all'" />
|
|
<noscript><link rel="stylesheet" th:href="@{/assets/fonts/LXGWWenKai/result.css}" /></noscript>
|
|
<style is:inline th:if="${theme.config?.style?.accent_color}" th:utext="${':root { --accent: ' + theme.config?.style?.accent_color + '; --accent-hover: ' + theme.config?.style?.accent_color + '; }'}">/*accent*/</style>
|
|
<style is:inline th:if="${theme.config?.style?.border_radius == 'small'}" th:utext="${':root { --radius-sm: 4px; --radius-md: 8px; --radius-lg: 16px; }'}">/*radius*/</style>
|
|
<style is:inline th:if="${theme.config?.style?.border_radius == 'medium' or theme.config?.style?.border_radius == null}" th:utext="${':root { --radius-sm: 8px; --radius-md: 12px; --radius-lg: 24px; }'}">/*radius*/</style>
|
|
<style is:inline th:if="${theme.config?.style?.border_radius == 'large'}" th:utext="${':root { --radius-sm: 12px; --radius-md: 20px; --radius-lg: 36px; }'}">/*radius*/</style>
|
|
<style is:inline th:if="${theme.config?.style?.layout_container_width == 'narrow'}" th:utext="${'.wi-content-wrap { max-width: 680px; } .wi-content-wrap--wide { max-width: 1000px; } .wi-main--wide { max-width: 1200px; }'}">/*width*/</style>
|
|
<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 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>
|
|
<Navbar />
|
|
<main class:list={["wi-main", { "wi-main--wide": wide || home, "wi-main--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 }]}>
|
|
<slot />
|
|
</div>
|
|
)}
|
|
</main>
|
|
<Footer />
|
|
<MobileMenu />
|
|
<ScrollReveal th:if="${theme.config?.animation?.animation_enabled != false and theme.config?.animation?.animation_scroll_reveal != false}" />
|
|
<ClickEffect />
|
|
</body>
|
|
</html>
|
|
|
|
<style is:global>
|
|
html {
|
|
min-height: 100vh;
|
|
}
|
|
|
|
html.wi-theme-transition,
|
|
html.wi-theme-transition *,
|
|
html.wi-theme-transition *::before,
|
|
html.wi-theme-transition *::after {
|
|
transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease, box-shadow 0.3s ease, fill 0.3s ease, stroke 0.3s ease !important;
|
|
}
|
|
|
|
.wi-body {
|
|
min-height: 100vh;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.wi-main {
|
|
flex: 1;
|
|
}
|
|
|
|
.wi-reveal {
|
|
opacity: 0;
|
|
transform: translateY(20px);
|
|
transition:
|
|
opacity var(--duration-normal, 300ms) var(--ease-out-expo, cubic-bezier(0.16, 1, 0.3, 1)),
|
|
transform var(--duration-normal, 300ms) var(--ease-out-expo, cubic-bezier(0.16, 1, 0.3, 1));
|
|
}
|
|
|
|
.wi-revealed {
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
}
|
|
|
|
html.wi-no-animations .wi-reveal {
|
|
opacity: 1;
|
|
transform: none;
|
|
transition: none;
|
|
}
|
|
|
|
</style>
|