mirror of
https://github.com/afoim/fuwari.git
synced 2026-01-31 09:03:18 +08:00
749 lines
25 KiB
Plaintext
749 lines
25 KiB
Plaintext
---
|
||
import Analytics from "@vercel/analytics/astro";
|
||
import "@fontsource/roboto/400.css";
|
||
import "@fontsource/roboto/500.css";
|
||
import "@fontsource/roboto/700.css";
|
||
|
||
import { profileConfig, siteConfig } from "@/config";
|
||
import ConfigCarrier from "@components/ConfigCarrier.astro";
|
||
import {
|
||
AUTO_MODE,
|
||
BANNER_HEIGHT,
|
||
BANNER_HEIGHT_EXTEND,
|
||
BANNER_HEIGHT_HOME,
|
||
DARK_MODE,
|
||
DEFAULT_THEME,
|
||
LIGHT_MODE,
|
||
PAGE_WIDTH,
|
||
} from "../constants/constants";
|
||
import { defaultFavicons } from "../constants/icon";
|
||
import type { Favicon } from "../types/config";
|
||
import { url, pathsEqual } from "../utils/url-utils";
|
||
import "katex/dist/katex.css";
|
||
|
||
interface Props {
|
||
title?: string;
|
||
banner?: string;
|
||
description?: string;
|
||
lang?: string;
|
||
setOGTypeArticle?: boolean;
|
||
}
|
||
|
||
let { title, banner, description, lang, setOGTypeArticle } = Astro.props;
|
||
|
||
// apply a class to the body element to decide the height of the banner, only used for initial page load
|
||
// Swup can update the body for each page visit, but it's after the page transition, causing a delay for banner height change
|
||
// so use Swup hooks instead to change the height immediately when a link is clicked
|
||
const isHomePage = pathsEqual(Astro.url.pathname, url("/"));
|
||
|
||
// defines global css variables
|
||
// why doing this in Layout instead of GlobalStyles: https://github.com/withastro/astro/issues/6728#issuecomment-1502203757
|
||
const configHue = siteConfig.themeColor.hue;
|
||
if (!banner || typeof banner !== "string" || banner.trim() === "") {
|
||
banner = siteConfig.banner.src;
|
||
}
|
||
|
||
// TODO don't use post cover as banner for now
|
||
banner = siteConfig.banner.src;
|
||
|
||
const enableBanner = siteConfig.banner.enable;
|
||
|
||
let pageTitle: string;
|
||
if (title) {
|
||
pageTitle = `${title} - ${siteConfig.title}`;
|
||
} else {
|
||
if (siteConfig.subtitle && siteConfig.subtitle.trim() !== "") {
|
||
pageTitle = `${siteConfig.title} - ${siteConfig.subtitle}`;
|
||
} else {
|
||
pageTitle = siteConfig.title;
|
||
}
|
||
}
|
||
|
||
// Use siteConfig.description as fallback for meta description when no description is provided
|
||
if (!description) {
|
||
description = siteConfig.description || pageTitle;
|
||
}
|
||
|
||
const favicons: Favicon[] =
|
||
siteConfig.favicon.length > 0 ? siteConfig.favicon : defaultFavicons;
|
||
|
||
// const siteLang = siteConfig.lang.replace('_', '-')
|
||
if (!lang) {
|
||
lang = `${siteConfig.lang}`;
|
||
}
|
||
const siteLang = lang.replace("_", "-");
|
||
|
||
const bannerOffsetByPosition = {
|
||
top: `${BANNER_HEIGHT_EXTEND}vh`,
|
||
center: `${BANNER_HEIGHT_EXTEND / 2}vh`,
|
||
bottom: "0",
|
||
};
|
||
const bannerOffset =
|
||
bannerOffsetByPosition[siteConfig.banner.position || "center"];
|
||
---
|
||
|
||
<!DOCTYPE html>
|
||
<html lang={siteLang} class="bg-[var(--page-bg)] transition text-[12px] md:text-[16px]"
|
||
data-overlayscrollbars-initialize> <!-- 手机端适配 -->
|
||
|
||
<head>
|
||
|
||
<title>{pageTitle}</title>
|
||
|
||
<meta charset="UTF-8" />
|
||
<meta name="description" content={description}>
|
||
<meta name="author" content={profileConfig.name}>
|
||
{siteConfig.keywords && (
|
||
<meta name="keywords" content={siteConfig.keywords.join(", ")}>
|
||
)}
|
||
|
||
<meta property="og:site_name" content={siteConfig.title}>
|
||
<meta property="og:url" content={Astro.url}>
|
||
<meta property="og:title" content={pageTitle}>
|
||
<meta property="og:description" content={description}>
|
||
{setOGTypeArticle ? (
|
||
<meta property="og:type" content="article" />
|
||
) : (
|
||
<meta property="og:type" content="website" />
|
||
)}
|
||
|
||
<meta name="twitter:card" content="summary_large_image">
|
||
<meta property="twitter:url" content={Astro.url}>
|
||
<meta name="twitter:title" content={pageTitle}>
|
||
<meta name="twitter:description" content={description}>
|
||
|
||
<meta name="viewport" content="width=device-width" />
|
||
<meta name="generator" content={Astro.generator} />
|
||
<script src="/js/umami-share.js" defer></script>
|
||
<script data-swup-ignore-script is:inline src="https://pic.2x.nz/random.js"></script>
|
||
|
||
|
||
|
||
{/* <!-- Content Security Policy -->
|
||
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline' https://giscus.app https://hpic.072103.xyz https://umami.2x.nz https://hm.baidu.com https://www.googletagmanager.com https://www.google-analytics.com https://pagead2.googlesyndication.com https://googleads.g.doubleclick.net https://static.cloudflareinsights.com https://*.adtrafficquality.google; style-src 'self' 'unsafe-inline' https://giscus.app https://fonts.googleapis.com https://api.iconify.design; font-src 'self' https://fonts.gstatic.com https://api.iconify.design; img-src 'self' data: https: http:; connect-src 'self' https://umami.2x.nz https://hm.baidu.com https://www.google-analytics.com https://analytics.google.com https://api.iconify.design https://static.cloudflareinsights.com https://pic.2x.nz https://q2.qlogo.cn https://ep1.adtrafficquality.google https://googleads.g.doubleclick.net; frame-src 'self' https://giscus.app *.bilibili.com https://www.google.com https://googleads.g.doubleclick.net https://support.nodeget.com https://*.adtrafficquality.google; object-src 'none'; base-uri 'self'; form-action 'self';"> */}
|
||
{favicons.map(favicon => (
|
||
<link rel="icon"
|
||
href={favicon.src.startsWith('/') ? url(favicon.src) : favicon.src}
|
||
sizes={favicon.sizes}
|
||
media={favicon.theme && `(prefers-color-scheme: ${favicon.theme})`}
|
||
/>
|
||
))}
|
||
|
||
<!-- Set the theme before the page is rendered to avoid a flash -->
|
||
<script is:inline define:vars={{DEFAULT_THEME, LIGHT_MODE, DARK_MODE, AUTO_MODE, BANNER_HEIGHT_EXTEND, PAGE_WIDTH, configHue, forceDarkMode: siteConfig.themeColor.forceDarkMode}}>
|
||
// Force dark mode if configured
|
||
if (forceDarkMode) {
|
||
document.documentElement.classList.add('dark');
|
||
localStorage.setItem('theme', DARK_MODE);
|
||
} else {
|
||
// Load the theme from local storage
|
||
const theme = localStorage.getItem('theme') || AUTO_MODE;
|
||
switch (theme) {
|
||
case LIGHT_MODE:
|
||
document.documentElement.classList.remove('dark');
|
||
break
|
||
case DARK_MODE:
|
||
document.documentElement.classList.add('dark');
|
||
break
|
||
case AUTO_MODE:
|
||
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
||
document.documentElement.classList.add('dark');
|
||
} else {
|
||
document.documentElement.classList.remove('dark');
|
||
}
|
||
}
|
||
}
|
||
|
||
// Load the hue from local storage
|
||
const hue = localStorage.getItem('hue') || configHue;
|
||
document.documentElement.style.setProperty('--hue', hue);
|
||
|
||
// calculate the --banner-height-extend, which needs to be a multiple of 4 to avoid blurry text
|
||
let offset = Math.floor(window.innerHeight * (BANNER_HEIGHT_EXTEND / 100));
|
||
offset = offset - offset % 4;
|
||
document.documentElement.style.setProperty('--banner-height-extend', `${offset}px`);
|
||
|
||
// // Background image loading detection
|
||
// const bgUrl = getComputedStyle(document.documentElement).getPropertyValue('--bg-url').trim();
|
||
// const bgEnable = getComputedStyle(document.documentElement).getPropertyValue('--bg-enable').trim();
|
||
|
||
// if (bgUrl && bgUrl !== 'none' && bgEnable === '1') {
|
||
// const img = new Image();
|
||
// const urlMatch = bgUrl.match(/url\(["']?([^"')]+)["']?\)/);
|
||
// if (urlMatch) {
|
||
// img.onload = function() {
|
||
// // 背景图片完全加载后,显示背景并启用卡片透明效果
|
||
// document.body.classList.add('bg-loaded');
|
||
// document.documentElement.style.setProperty('--card-bg', 'var(--card-bg-transparent)');
|
||
// document.documentElement.style.setProperty('--float-panel-bg', 'var(--float-panel-bg-transparent)');
|
||
// };
|
||
// img.onerror = function() {
|
||
// // Keep cards opaque if background image fails to load
|
||
// console.warn('Background image failed to load, keeping cards opaque');
|
||
// };
|
||
// img.src = urlMatch[1];
|
||
// }
|
||
// }
|
||
</script>
|
||
<style define:vars={{
|
||
configHue,
|
||
'page-width': `${PAGE_WIDTH}rem`,
|
||
'bg-url': siteConfig.background.src ? `url(${siteConfig.background.src})` : 'none',
|
||
'bg-enable': siteConfig.background.enable ? '1' : '0',
|
||
'bg-position': siteConfig.background.position || 'center',
|
||
'bg-size': siteConfig.background.size || 'cover',
|
||
'bg-repeat': siteConfig.background.repeat || 'no-repeat',
|
||
'bg-attachment': siteConfig.background.attachment || 'fixed',
|
||
'bg-opacity': (siteConfig.background.opacity || 0.3).toString()
|
||
}}>
|
||
:root {
|
||
--bg-url: var(--bg-url);
|
||
--bg-enable: var(--bg-enable);
|
||
--bg-position: var(--bg-position);
|
||
--bg-size: var(--bg-size);
|
||
--bg-repeat: var(--bg-repeat);
|
||
--bg-attachment: var(--bg-attachment);
|
||
--bg-opacity: var(--bg-opacity);
|
||
}
|
||
|
||
/* Background image configuration */
|
||
body {
|
||
--bg-url: var(--bg-url);
|
||
--bg-enable: var(--bg-enable);
|
||
--bg-position: var(--bg-position);
|
||
--bg-size: var(--bg-size);
|
||
--bg-repeat: var(--bg-repeat);
|
||
--bg-attachment: var(--bg-attachment);
|
||
--bg-opacity: var(--bg-opacity);
|
||
}
|
||
|
||
#bg-box {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
background-position: var(--bg-position);
|
||
background-size: var(--bg-size);
|
||
background-repeat: var(--bg-repeat);
|
||
background-attachment: var(--bg-attachment);
|
||
opacity: 0;
|
||
pointer-events: none;
|
||
z-index: -1;
|
||
transition: opacity 0.3s ease-in-out;
|
||
}
|
||
|
||
#bg-box.loaded {
|
||
opacity: calc(var(--bg-opacity) * var(--bg-enable));
|
||
}
|
||
</style>
|
||
|
||
<slot name="head"></slot>
|
||
|
||
|
||
<link rel="alternate" type="application/rss+xml" title={profileConfig.name} href={`${Astro.site}rss.xml`}/>
|
||
<!-- Umami分析(自建) -->
|
||
<script defer src="https://umami.2x.nz/script.js" data-website-id="5d710dbd-3a2e-43e3-a553-97b415090c63" data-swup-ignore-script></script>
|
||
{/* - Umami分析(云-备用)
|
||
<script defer src="https://cloud.umami.is/script.js" data-website-id="5d710dbd-3a2e-43e3-a553-97b415090c63" data-swup-ignore-script></script> */}
|
||
<!-- 超级吊的Umami eop -->
|
||
<script defer src="https://eo-umami.acofork.com/script.js" data-website-id="3f525363-5e81-4653-9491-1bd205c2a571"></script>
|
||
<!-- 百度统计 -->
|
||
<script data-swup-ignore-script>
|
||
var _hmt = _hmt || [];
|
||
(function() {
|
||
var hm = document.createElement("script");
|
||
hm.src = "https://hm.baidu.com/hm.js?b219eaad631b87d273cfe72148b2138b";
|
||
var s = document.getElementsByTagName("script")[0];
|
||
s.parentNode.insertBefore(hm, s);
|
||
})();
|
||
</script>
|
||
|
||
<!-- clarity 分析 -->
|
||
<script type="text/javascript" data-swup-ignore-script>
|
||
(function(c,l,a,r,i,t,y){
|
||
c[a]=c[a]||function(){(c[a].q=c[a].q||[]).push(arguments)};
|
||
t=l.createElement(r);t.async=1;t.src="https://www.clarity.ms/tag/"+i;
|
||
y=l.getElementsByTagName(r)[0];y.parentNode.insertBefore(t,y);
|
||
})(window, document, "clarity", "script", "t8f0gmcwtx");
|
||
</script>
|
||
|
||
<!-- 谷歌分析 -->
|
||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-9Z4LT4H8KH" data-swup-ignore-script></script>
|
||
<script>
|
||
window.dataLayer = window.dataLayer || [];
|
||
function gtag(){dataLayer.push(arguments);}
|
||
gtag('js', new Date());
|
||
|
||
gtag('config', 'G-9Z4LT4H8KH');
|
||
</script>
|
||
|
||
<!-- Cloudflare Web Analytics --><script defer src='https://static.cloudflareinsights.com/beacon.min.js' data-cf-beacon='{"token": "15fe148e91b34f10a15652e1a74ab26c"}' data-swup-ignore-script></script><!-- End Cloudflare Web Analytics -->
|
||
</head>
|
||
<body class=" min-h-screen transition " class:list={[{"lg:is-home": isHomePage, "enable-banner": enableBanner}]}
|
||
data-overlayscrollbars-initialize
|
||
>
|
||
<div id="bg-box"></div>
|
||
<ConfigCarrier></ConfigCarrier>
|
||
<slot />
|
||
|
||
<!-- increase the page height during page transition to prevent the scrolling animation from jumping -->
|
||
<div id="page-height-extend" class="hidden h-[300vh]"></div>
|
||
|
||
<!-- 域名检测脚本 -->
|
||
<script is:inline define:vars={{officialSites: siteConfig.officialSites || []}}>
|
||
// 域名检测功能
|
||
function checkDomain() {
|
||
try {
|
||
// 获取当前访问的完整URL
|
||
const currentUrl = window.location.href;
|
||
// 获取当前域名
|
||
const currentDomain = window.location.hostname;
|
||
|
||
// 获取所有官方域名
|
||
const officialDomains = officialSites.map(site => {
|
||
try {
|
||
const url = typeof site === 'string' ? site : site.url;
|
||
return new URL(url).hostname;
|
||
} catch (e) {
|
||
return null;
|
||
}
|
||
}).filter(domain => domain !== null);
|
||
|
||
// 检查当前域名是否为官方域名或本地开发环境
|
||
const isOfficialDomain = officialDomains.some(officialDomain =>
|
||
currentDomain === officialDomain || currentDomain.endsWith('.' + officialDomain)
|
||
);
|
||
const isLocalDev = currentDomain === 'localhost' || currentDomain === '127.0.0.1';
|
||
|
||
// 如果当前域名不是官方域名且不是本地开发环境
|
||
if (!isOfficialDomain && !isLocalDev) {
|
||
// 创建警告弹窗
|
||
const shouldRedirect = confirm(
|
||
`⚠️ 域名安全警告\n\n` +
|
||
`您当前访问的域名:${currentDomain}\n` +
|
||
`官方域名:${officialDomains.join(', ')}\n\n` +
|
||
`您可能正在访问非官方网站,存在安全风险!\n\n` +
|
||
`点击"确定"跳转到官方网站\n` +
|
||
`点击"取消"继续访问当前网站(不推荐)`
|
||
);
|
||
|
||
// 如果用户选择跳转到官方网站
|
||
if (shouldRedirect) {
|
||
// 构建官方网站的对应页面URL(使用第一个官方网站)
|
||
const currentPath = window.location.pathname + window.location.search + window.location.hash;
|
||
const firstSite = officialSites[0];
|
||
const firstUrl = typeof firstSite === 'string' ? firstSite : firstSite.url;
|
||
const officialPageUrl = firstUrl + currentPath;
|
||
// 跳转到官方网站
|
||
window.location.href = officialPageUrl;
|
||
}
|
||
}
|
||
} catch (error) {
|
||
// 如果检测过程中出现错误,静默处理,不影响正常访问
|
||
console.warn('域名检测失败:', error);
|
||
}
|
||
}
|
||
|
||
// 页面加载完成后执行域名检测
|
||
if (document.readyState === 'loading') {
|
||
document.addEventListener('DOMContentLoaded', checkDomain);
|
||
} else {
|
||
checkDomain();
|
||
}
|
||
</script>
|
||
<Analytics />
|
||
</body>
|
||
</html>
|
||
|
||
<style is:global define:vars={{
|
||
bannerOffset,
|
||
'banner-height-home': `${BANNER_HEIGHT_HOME}vh`,
|
||
'banner-height': `${BANNER_HEIGHT}vh`,
|
||
}}>
|
||
@tailwind components;
|
||
@layer components {
|
||
.enable-banner.is-home #banner-wrapper {
|
||
@apply h-[var(--banner-height-home)] translate-y-[var(--banner-height-extend)]
|
||
}
|
||
.enable-banner #banner-wrapper {
|
||
@apply h-[var(--banner-height-home)]
|
||
}
|
||
|
||
.enable-banner.is-home #banner {
|
||
@apply h-[var(--banner-height-home)] translate-y-0
|
||
}
|
||
.enable-banner #banner {
|
||
@apply h-[var(--banner-height-home)] translate-y-[var(--bannerOffset)]
|
||
}
|
||
.enable-banner.is-home #main-grid {
|
||
@apply translate-y-[var(--banner-height-extend)];
|
||
}
|
||
.enable-banner #top-row {
|
||
@apply h-[calc(var(--banner-height-home)_-_4.5rem)] transition-all duration-300
|
||
}
|
||
.enable-banner.is-home #sidebar-sticky {
|
||
@apply top-[calc(1rem_-_var(--banner-height-extend))]
|
||
}
|
||
.navbar-hidden {
|
||
@apply opacity-0 -translate-y-16
|
||
}
|
||
}
|
||
</style>
|
||
|
||
<script>
|
||
import 'overlayscrollbars/overlayscrollbars.css';
|
||
import {
|
||
OverlayScrollbars,
|
||
// ScrollbarsHidingPlugin,
|
||
// SizeObserverPlugin,
|
||
// ClickScrollPlugin
|
||
} from 'overlayscrollbars';
|
||
import {getHue, getStoredTheme, setHue, setTheme, getBgBlur, setBgBlur, getHideBg, setHideBg} from "../utils/setting-utils";
|
||
import {pathsEqual, url} from "../utils/url-utils";
|
||
import {
|
||
BANNER_HEIGHT,
|
||
BANNER_HEIGHT_HOME,
|
||
BANNER_HEIGHT_EXTEND,
|
||
MAIN_PANEL_OVERLAPS_BANNER_HEIGHT
|
||
} from "../constants/constants";
|
||
import { siteConfig } from '../config';
|
||
|
||
/* Preload fonts */
|
||
// (async function() {
|
||
// try {
|
||
// await Promise.all([
|
||
// document.fonts.load("400 1em Roboto"),
|
||
// document.fonts.load("700 1em Roboto"),
|
||
// ]);
|
||
// document.body.classList.remove("hidden");
|
||
// } catch (error) {
|
||
// console.log("Failed to load fonts:", error);
|
||
// }
|
||
// })();
|
||
|
||
/* TODO This is a temporary solution for style flicker issue when the transition is activated */
|
||
/* issue link: https://github.com/withastro/astro/issues/8711, the solution get from here too */
|
||
/* update: fixed in Astro 3.2.4 */
|
||
/*
|
||
function disableAnimation() {
|
||
const css = document.createElement('style')
|
||
css.appendChild(
|
||
document.createTextNode(
|
||
`*{
|
||
-webkit-transition:none!important;
|
||
-moz-transition:none!important;
|
||
-o-transition:none!important;
|
||
-ms-transition:none!important;
|
||
transition:none!important
|
||
}`
|
||
)
|
||
)
|
||
document.head.appendChild(css)
|
||
|
||
return () => {
|
||
// Force restyle
|
||
;(() => window.getComputedStyle(document.body))()
|
||
|
||
// Wait for next tick before removing
|
||
setTimeout(() => {
|
||
document.head.removeChild(css)
|
||
}, 1)
|
||
}
|
||
}
|
||
*/
|
||
|
||
const bannerEnabled = !!document.getElementById('banner-wrapper')
|
||
|
||
function setClickOutsideToClose(panel: string, ignores: string[]) {
|
||
document.addEventListener("click", event => {
|
||
let panelDom = document.getElementById(panel);
|
||
let tDom = event.target;
|
||
if (!(tDom instanceof Node)) return; // Ensure the event target is an HTML Node
|
||
for (let ig of ignores) {
|
||
let ie = document.getElementById(ig)
|
||
if (ie == tDom || (ie?.contains(tDom))) {
|
||
return;
|
||
}
|
||
}
|
||
panelDom!.classList.add("float-panel-closed");
|
||
});
|
||
}
|
||
setClickOutsideToClose("display-setting", ["display-setting", "display-settings-switch"])
|
||
setClickOutsideToClose("nav-menu-panel", ["nav-menu-panel", "nav-menu-switch"])
|
||
setClickOutsideToClose("search-panel", ["search-panel", "search-bar", "search-switch"])
|
||
|
||
|
||
function loadTheme() {
|
||
const theme = getStoredTheme()
|
||
setTheme(theme)
|
||
}
|
||
|
||
function loadHue() {
|
||
setHue(getHue())
|
||
}
|
||
|
||
function loadBgBlur() {
|
||
setBgBlur(getBgBlur())
|
||
setHideBg(getHideBg())
|
||
}
|
||
|
||
function initCustomScrollbar() {
|
||
const bodyElement = document.querySelector('body');
|
||
if (!bodyElement) return;
|
||
OverlayScrollbars(
|
||
// docs say that a initialization to the body element would affect native functionality like window.scrollTo
|
||
// but just leave it here for now
|
||
{
|
||
target: bodyElement,
|
||
cancel: {
|
||
nativeScrollbarsOverlaid: true, // don't initialize the overlay scrollbar if there is a native one
|
||
}
|
||
}, {
|
||
scrollbars: {
|
||
theme: 'scrollbar-base scrollbar-auto py-1',
|
||
autoHide: 'move',
|
||
autoHideDelay: 500,
|
||
autoHideSuspend: false,
|
||
},
|
||
});
|
||
|
||
const katexElements = document.querySelectorAll('.katex-display') as NodeListOf<HTMLElement>;
|
||
katexElements.forEach((ele) => {
|
||
OverlayScrollbars(ele, {
|
||
scrollbars: {
|
||
theme: 'scrollbar-base scrollbar-auto py-1',
|
||
}
|
||
});
|
||
});
|
||
}
|
||
|
||
function showBanner() {
|
||
if (!siteConfig.banner.enable) return;
|
||
|
||
const banner = document.getElementById('banner');
|
||
if (!banner) {
|
||
console.error('Banner element not found');
|
||
return;
|
||
}
|
||
|
||
banner.classList.remove('opacity-0', 'scale-105');
|
||
}
|
||
|
||
function init() {
|
||
// disableAnimation()() // TODO
|
||
loadTheme();
|
||
loadHue();
|
||
loadBgBlur();
|
||
initCustomScrollbar();
|
||
showBanner();
|
||
}
|
||
|
||
/* Load settings when entering the site */
|
||
init();
|
||
|
||
const setup = () => {
|
||
// TODO: temp solution to change the height of the banner
|
||
/*
|
||
window.swup.hooks.on('animation:out:start', () => {
|
||
const path = window.location.pathname
|
||
const body = document.querySelector('body')
|
||
if (path[path.length - 1] === '/' && !body.classList.contains('is-home')) {
|
||
body.classList.add('is-home')
|
||
} else if (path[path.length - 1] !== '/' && body.classList.contains('is-home')) {
|
||
body.classList.remove('is-home')
|
||
}
|
||
})
|
||
*/
|
||
window.swup.hooks.on('link:click', () => {
|
||
// Remove the delay for the first time page load
|
||
document.documentElement.style.setProperty('--content-delay', '0ms')
|
||
|
||
// prevent elements from overlapping the navbar
|
||
if (!bannerEnabled) {
|
||
return
|
||
}
|
||
let threshold = window.innerHeight * (BANNER_HEIGHT / 100) - 72 - 16
|
||
let navbar = document.getElementById('navbar-wrapper')
|
||
if (!navbar || !document.body.classList.contains('lg:is-home')) {
|
||
return
|
||
}
|
||
if (document.body.scrollTop >= threshold || document.documentElement.scrollTop >= threshold) {
|
||
navbar.classList.add('navbar-hidden')
|
||
}
|
||
})
|
||
window.swup.hooks.on('content:replace', initCustomScrollbar)
|
||
window.swup.hooks.on('visit:start', (visit: {to: {url: string}}) => {
|
||
// change banner height immediately when a link is clicked
|
||
const bodyElement = document.querySelector('body')
|
||
if (pathsEqual(visit.to.url, url('/'))) {
|
||
bodyElement!.classList.add('lg:is-home');
|
||
} else {
|
||
bodyElement!.classList.remove('lg:is-home');
|
||
}
|
||
|
||
// increase the page height during page transition to prevent the scrolling animation from jumping
|
||
const heightExtend = document.getElementById('page-height-extend')
|
||
if (heightExtend) {
|
||
heightExtend.classList.remove('hidden')
|
||
}
|
||
|
||
// Hide the TOC while scrolling back to top
|
||
let toc = document.getElementById('toc-wrapper');
|
||
if (toc) {
|
||
toc.classList.add('toc-not-ready')
|
||
}
|
||
});
|
||
window.swup.hooks.on('page:view', () => {
|
||
// hide the temp high element when the transition is done
|
||
const heightExtend = document.getElementById('page-height-extend')
|
||
if (heightExtend) {
|
||
heightExtend.classList.remove('hidden')
|
||
}
|
||
});
|
||
window.swup.hooks.on('visit:end', (_visit: {to: {url: string}}) => {
|
||
setTimeout(() => {
|
||
const heightExtend = document.getElementById('page-height-extend')
|
||
if (heightExtend) {
|
||
heightExtend.classList.add('hidden')
|
||
}
|
||
|
||
// Just make the transition looks better
|
||
const toc = document.getElementById('toc-wrapper');
|
||
if (toc) {
|
||
toc.classList.remove('toc-not-ready')
|
||
}
|
||
}, 200)
|
||
});
|
||
}
|
||
if (window?.swup?.hooks) {
|
||
setup()
|
||
} else {
|
||
document.addEventListener('swup:enable', setup)
|
||
}
|
||
|
||
let backToTopBtn = document.getElementById('back-to-top-btn');
|
||
let goToCommentsBtn = document.getElementById('go-to-comments-btn');
|
||
let toc = document.getElementById('toc-wrapper');
|
||
let navbar = document.getElementById('navbar-wrapper')
|
||
function scrollFunction() {
|
||
let bannerHeight = window.innerHeight * (BANNER_HEIGHT / 100)
|
||
|
||
if (backToTopBtn) {
|
||
if (document.body.scrollTop > bannerHeight || document.documentElement.scrollTop > bannerHeight) {
|
||
backToTopBtn.classList.remove('hide')
|
||
} else {
|
||
backToTopBtn.classList.add('hide')
|
||
}
|
||
}
|
||
|
||
if (goToCommentsBtn) {
|
||
// Only show if comments exist and scrolled down
|
||
const commentsExist = !!document.getElementById('giscus-container');
|
||
if (commentsExist && (document.body.scrollTop > bannerHeight || document.documentElement.scrollTop > bannerHeight)) {
|
||
goToCommentsBtn.classList.remove('hide')
|
||
} else {
|
||
goToCommentsBtn.classList.add('hide')
|
||
}
|
||
}
|
||
|
||
if (bannerEnabled && toc) {
|
||
if (document.body.scrollTop > bannerHeight || document.documentElement.scrollTop > bannerHeight) {
|
||
toc.classList.remove('toc-hide')
|
||
} else {
|
||
toc.classList.add('toc-hide')
|
||
}
|
||
}
|
||
|
||
if (!bannerEnabled) return
|
||
if (navbar) {
|
||
const NAVBAR_HEIGHT = 72
|
||
const MAIN_PANEL_EXCESS_HEIGHT = MAIN_PANEL_OVERLAPS_BANNER_HEIGHT * 16 // The height the main panel overlaps the banner
|
||
|
||
let bannerHeight = BANNER_HEIGHT
|
||
if (document.body.classList.contains('lg:is-home') && window.innerWidth >= 1024) {
|
||
bannerHeight = BANNER_HEIGHT_HOME
|
||
}
|
||
let threshold = window.innerHeight * (bannerHeight / 100) - NAVBAR_HEIGHT - MAIN_PANEL_EXCESS_HEIGHT - 16
|
||
if (document.body.scrollTop >= threshold || document.documentElement.scrollTop >= threshold) {
|
||
navbar.classList.add('navbar-hidden')
|
||
} else {
|
||
navbar.classList.remove('navbar-hidden')
|
||
}
|
||
}
|
||
}
|
||
window.onscroll = scrollFunction
|
||
|
||
window.onresize = () => {
|
||
// calculate the --banner-height-extend, which needs to be a multiple of 4 to avoid blurry text
|
||
let offset = Math.floor(window.innerHeight * (BANNER_HEIGHT_EXTEND / 100));
|
||
offset = offset - offset % 4;
|
||
document.documentElement.style.setProperty('--banner-height-extend', `${offset}px`);
|
||
}
|
||
|
||
</script>
|
||
|
||
<script>
|
||
import { Fancybox } from "@fancyapps/ui"
|
||
import "@fancyapps/ui/dist/fancybox/fancybox.css"
|
||
|
||
const setup = () => {
|
||
Fancybox.bind(".custom-md img, #post-cover img", {
|
||
wheel: 'zoom',
|
||
clickContent: 'close',
|
||
dblclickContent: 'zoom',
|
||
click: 'close',
|
||
dblclick: 'zoom',
|
||
Panels: {
|
||
display: ['counter', 'zoom']
|
||
},
|
||
Images: {
|
||
panning: true,
|
||
zoom: true,
|
||
protect: false
|
||
}
|
||
})
|
||
|
||
window.swup.hooks.on("page:view", () => {
|
||
Fancybox.bind(".custom-md img, #post-cover img", {
|
||
wheel: 'zoom',
|
||
clickContent: 'close',
|
||
dblclickContent: 'zoom',
|
||
click: 'close',
|
||
dblclick: 'zoom',
|
||
Panels: {
|
||
display: ['counter', 'zoom']
|
||
},
|
||
Images: {
|
||
panning: true,
|
||
zoom: true,
|
||
protect: false
|
||
}
|
||
})
|
||
})
|
||
|
||
window.swup.hooks.on(
|
||
"content:replace",
|
||
() => {
|
||
Fancybox.unbind(".custom-md img, #post-cover img")
|
||
},
|
||
{ before: true },
|
||
)
|
||
}
|
||
|
||
if (window.swup) {
|
||
setup()
|
||
} else {
|
||
document.addEventListener("swup:enable", setup)
|
||
}
|
||
</script>
|
||
|
||
<svg style="position: absolute; width: 0; height: 0; overflow: hidden;" aria-hidden="true">
|
||
<defs>
|
||
<filter id="liquid-glass">
|
||
<feTurbulence type="fractalNoise" baseFrequency="0.05" numOctaves="2" result="noise" />
|
||
<feDisplacementMap in="SourceGraphic" in2="noise" scale="20" xChannelSelector="R" yChannelSelector="G" />
|
||
</filter>
|
||
</defs>
|
||
</svg>
|