Files
NebulaShell/website/blog/diary/index.html
2026-04-10 20:00:25 +08:00

722 lines
78 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html><html lang="zh-CN" class="bg-[var(--page-bg)] text-[14px] md:text-[16px]" data-overlayscrollbars-initialize data-astro-cid-sckkx6r4 style="--bannerOffset: 15vh;--banner-height-home: 65vh;--banner-height: 35vh;--configHue: 210;--page-width: 90rem;"> <head><title>日记 - FutureOSS Docs</title><meta charset="UTF-8"><meta name="description" content="即刻短文"><meta name="author" content="FutureOSS"><meta property="og:site_name" content="FutureOSS Docs"><meta property="og:url" content="https://oss-runtime.dev/blog/diary/"><meta property="og:title" content="日记 - FutureOSS Docs"><meta property="og:description" content="即刻短文"><meta property="og:type" content="website"><meta name="twitter:card" content="summary_large_image"><meta property="twitter:url" content="https://oss-runtime.dev/blog/diary/"><meta name="twitter:title" content="日记 - FutureOSS Docs"><meta name="twitter:description" content="即刻短文"><meta name="viewport" content="width=device-width"><meta name="generator" content="Astro v5.13.2"><link rel="icon" href="/blog/favicon/favicon-light-32.png" sizes="32x32" media="(prefers-color-scheme: light)"><link rel="icon" href="/blog/favicon/favicon-light-128.png" sizes="128x128" media="(prefers-color-scheme: light)"><link rel="icon" href="/blog/favicon/favicon-light-180.png" sizes="180x180" media="(prefers-color-scheme: light)"><link rel="icon" href="/blog/favicon/favicon-light-192.png" sizes="192x192" media="(prefers-color-scheme: light)"><link rel="icon" href="/blog/favicon/favicon-dark-32.png" sizes="32x32" media="(prefers-color-scheme: dark)"><link rel="icon" href="/blog/favicon/favicon-dark-128.png" sizes="128x128" media="(prefers-color-scheme: dark)"><link rel="icon" href="/blog/favicon/favicon-dark-180.png" sizes="180x180" media="(prefers-color-scheme: dark)"><link rel="icon" href="/blog/favicon/favicon-dark-192.png" sizes="192x192" media="(prefers-color-scheme: dark)"><!-- Set the theme before the page is rendered to avoid a flash --><script>(function(){const DEFAULT_THEME = "auto";
const LIGHT_MODE = "light";
const DARK_MODE = "dark";
const AUTO_MODE = "auto";
const BANNER_HEIGHT_EXTEND = 30;
const PAGE_WIDTH = 90;
const configHue = 210;
// Load the theme from local storage
const theme = localStorage.getItem('theme') || DEFAULT_THEME;
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`);
})();</script><!-- defines global css variables. This will be applied to <html> <body> and some other elements idk why --><link rel="alternate" type="application/rss+xml" title="FutureOSS" href="https://oss-runtime.dev/rss.xml"><link rel="stylesheet" href="/blog/_astro/Layout.DSulWsr7.css">
<link rel="stylesheet" href="/blog/_astro/Layout.CKPtf-VR.css">
<style>.card-base[data-astro-cid-pyfuvgrx]{background:var(--card-bg);border:1px solid var(--line-divider);transition:all .3s ease}.moments-header[data-astro-cid-pyfuvgrx]{padding:1rem;border-radius:8px;background:linear-gradient(135deg,var(--primary) 0%,var(--primary-dark, var(--primary)) 100%);color:#fff;position:relative;overflow:hidden}.header-content[data-astro-cid-pyfuvgrx]{display:flex;justify-content:space-between;align-items:center;position:relative;z-index:1}.moments-title[data-astro-cid-pyfuvgrx]{color:#fff;text-shadow:0 1px 2px rgba(0,0,0,.1)}.moments-subtitle[data-astro-cid-pyfuvgrx]{color:#ffffffe6}.stat-number[data-astro-cid-pyfuvgrx]{color:#fff}.stat-label[data-astro-cid-pyfuvgrx]{color:#fffc}.image-item[data-astro-cid-pyfuvgrx] img[data-astro-cid-pyfuvgrx]{transition:transform .3s ease}.image-item[data-astro-cid-pyfuvgrx]:hover img[data-astro-cid-pyfuvgrx]{transform:scale(1.05)}.action-btn[data-astro-cid-pyfuvgrx]{background:none;border:none;cursor:pointer;color:var(--text-muted)}.action-btn[data-astro-cid-pyfuvgrx]:hover{color:var(--primary)}@media(max-width:640px){.moments-header[data-astro-cid-pyfuvgrx]{padding:.75rem}.header-content[data-astro-cid-pyfuvgrx]{flex-direction:column;text-align:center;gap:.75rem}.moment-images[data-astro-cid-pyfuvgrx]{grid-template-columns:repeat(2,1fr)}.moment-footer[data-astro-cid-pyfuvgrx]{flex-direction:column;align-items:flex-start;gap:.5rem}}@media(min-width:641px)and (max-width:900px){.moments-header[data-astro-cid-pyfuvgrx]{padding:1.25rem}.header-content[data-astro-cid-pyfuvgrx]{display:flex;justify-content:space-between;align-items:center}.moment-item[data-astro-cid-pyfuvgrx]{padding:1.5rem}.moment-images[data-astro-cid-pyfuvgrx]{grid-template-columns:repeat(3,1fr);gap:.75rem;max-width:500px}.moment-text[data-astro-cid-pyfuvgrx]{font-size:1rem;line-height:1.7}.moment-footer[data-astro-cid-pyfuvgrx]{margin-top:1rem}}@media(min-width:901px){.moments-header[data-astro-cid-pyfuvgrx]{padding:1.5rem}.moment-item[data-astro-cid-pyfuvgrx]{padding:2rem}.moment-images[data-astro-cid-pyfuvgrx]{grid-template-columns:repeat(auto-fit,minmax(150px,1fr));max-width:600px;gap:1rem}.moment-text[data-astro-cid-pyfuvgrx]{font-size:1.1rem;line-height:1.8}}@media(max-width:480px){.card-base[data-astro-cid-pyfuvgrx]{margin:0 -.5rem}.moment-item[data-astro-cid-pyfuvgrx]{border-radius:8px}.moments-header[data-astro-cid-pyfuvgrx]{border-radius:6px}}
</style>
<link rel="stylesheet" href="/blog/_astro/_page_.QUsIdifj.css">
<link rel="stylesheet" href="/blog/_astro/_page_.BQ6XwRvy.css">
<link rel="stylesheet" href="/blog/_astro/_page_.D6v8Siar.css">
<link rel="stylesheet" href="/blog/_astro/about.DhJwTaWu.css">
<style>.card-base[data-astro-cid-fepk5tcl]{background:var(--card-bg);border:1px solid var(--line-divider)}
.card-base[data-astro-cid-sahthylw]{border-radius:var(--radius-large);border-width:1px;border-color:var(--line-divider);background-color:var(--card-bg);--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.gallery-group[data-astro-cid-sahthylw].expanded{--tw-shadow: 0 20px 25px -5px rgb(0 0 0 / .1), 0 8px 10px -6px rgb(0 0 0 / .1);--tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.gallery-content[data-astro-cid-sahthylw]{max-height:0;overflow:hidden;transition:max-height .3s ease-out}.gallery-group[data-astro-cid-sahthylw].expanded .gallery-content[data-astro-cid-sahthylw]{max-height:2000px}.gallery-image[data-astro-cid-sahthylw]:hover{--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}@media(max-width:768px){.gallery-content[data-astro-cid-sahthylw] .grid[data-astro-cid-sahthylw]{grid-template-columns:repeat(2,1fr)}}@media(max-width:480px){.gallery-content[data-astro-cid-sahthylw] .grid[data-astro-cid-sahthylw]{grid-template-columns:repeat(1,1fr)}}
.line-clamp-1[data-astro-cid-aid3sr62]{display:-webkit-box;-webkit-line-clamp:1;-webkit-box-orient:vertical;overflow:hidden}.line-clamp-2[data-astro-cid-aid3sr62]{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.line-clamp-2[data-astro-cid-xahix5fp]{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.line-clamp-2[data-astro-cid-qlh7ngej]{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
</style>
<link rel="stylesheet" href="/blog/_astro/_page_.C0I9pl0C.css"><script type="module" src="/blog/_astro/page.OjjRkIOC.js"></script></head> <body class=" min-h-screen enable-banner" data-overlayscrollbars-initialize data-astro-cid-sckkx6r4 style="--bannerOffset: 15vh;--banner-height-home: 65vh;--banner-height: 35vh;--configHue: 210;--page-width: 90rem;"> <!-- 页面顶部渐变高光效果 - 只在full和semifull模式下显示 --> <div class="top-gradient-highlight" data-astro-cid-sckkx6r4 style="--bannerOffset: 15vh;--banner-height-home: 65vh;--banner-height: 35vh;--configHue: 210;--page-width: 90rem;"></div> <div id="config-carrier" data-hue="210"></div> <!-- Iconify图标库加载器 --><script>(function(){const preloadIcons = [];
const timeout = 10000;
const retryCount = 3;
// 全局图标加载逻辑
(function() {
'use strict';
// 避免重复加载
if (window.__iconifyLoaderInitialized) {
return;
}
window.__iconifyLoaderInitialized = true;
// 图标加载器类
class IconifyLoader {
constructor() {
this.isLoaded = false;
this.isLoading = false;
this.loadPromise = null;
this.observers = new Set();
this.preloadQueue = new Set();
}
async load(options = {}) {
const { timeout: loadTimeout = timeout, retryCount: maxRetries = retryCount } = options;
if (this.isLoaded) {
return Promise.resolve();
}
if (this.isLoading && this.loadPromise) {
return this.loadPromise;
}
this.isLoading = true;
this.loadPromise = this.loadWithRetry(loadTimeout, maxRetries);
try {
await this.loadPromise;
this.isLoaded = true;
this.notifyObservers();
await this.processPreloadQueue();
} catch (error) {
console.error('Failed to load Iconify:', error);
throw error;
} finally {
this.isLoading = false;
}
}
async loadWithRetry(timeout, retryCount) {
for (let attempt = 1; attempt <= retryCount; attempt++) {
try {
await this.loadScript(timeout);
return;
} catch (error) {
console.warn(`Iconify load attempt ${attempt} failed:`, error);
if (attempt === retryCount) {
throw new Error(`Failed to load Iconify after ${retryCount} attempts`);
}
await new Promise(resolve => setTimeout(resolve, 1000));
}
}
}
loadScript(timeout) {
return new Promise((resolve, reject) => {
// 检查是否已经存在
if (this.isIconifyReady()) {
resolve();
return;
}
const existingScript = document.querySelector('script[src*="iconify-icon"]');
if (existingScript) {
this.waitForIconifyReady().then(resolve).catch(reject);
return;
}
const script = document.createElement('script');
script.src = 'https://code.iconify.design/iconify-icon/1.0.7/iconify-icon.min.js';
script.async = true;
script.crossOrigin = 'anonymous';
const timeoutId = setTimeout(() => {
script.remove();
reject(new Error('Script load timeout'));
}, timeout);
script.onload = () => {
clearTimeout(timeoutId);
this.waitForIconifyReady().then(resolve).catch(reject);
};
script.onerror = () => {
clearTimeout(timeoutId);
script.remove();
reject(new Error('Script load error'));
};
document.head.appendChild(script);
});
}
waitForIconifyReady(maxWait = 5000) {
return new Promise((resolve, reject) => {
const startTime = Date.now();
const checkReady = () => {
if (this.isIconifyReady()) {
resolve();
return;
}
if (Date.now() - startTime > maxWait) {
reject(new Error('Iconify initialization timeout'));
return;
}
setTimeout(checkReady, 50);
};
checkReady();
});
}
isIconifyReady() {
return typeof window !== 'undefined' &&
'customElements' in window &&
customElements.get('iconify-icon') !== undefined;
}
onLoad(callback) {
if (this.isLoaded) {
callback();
} else {
this.observers.add(callback);
}
}
notifyObservers() {
this.observers.forEach(callback => {
try {
callback();
} catch (error) {
console.error('Error in icon load observer:', error);
}
});
this.observers.clear();
}
addToPreloadQueue(icons) {
if (Array.isArray(icons)) {
icons.forEach(icon => this.preloadQueue.add(icon));
} else {
this.preloadQueue.add(icons);
}
if (this.isLoaded) {
this.processPreloadQueue();
}
}
async processPreloadQueue() {
if (this.preloadQueue.size === 0) return;
const iconsToLoad = Array.from(this.preloadQueue);
this.preloadQueue.clear();
await this.preloadIcons(iconsToLoad);
}
async preloadIcons(icons) {
if (!this.isLoaded || icons.length === 0) return;
return new Promise((resolve) => {
let loadedCount = 0;
const totalIcons = icons.length;
const tempElements = [];
const cleanup = () => {
tempElements.forEach(el => {
if (el.parentNode) {
el.parentNode.removeChild(el);
}
});
};
const checkComplete = () => {
loadedCount++;
if (loadedCount >= totalIcons) {
cleanup();
resolve();
}
};
icons.forEach(icon => {
const tempIcon = document.createElement('iconify-icon');
tempIcon.setAttribute('icon', icon);
tempIcon.style.cssText = 'position:absolute;top:-9999px;left:-9999px;width:1px;height:1px;opacity:0;pointer-events:none;';
tempIcon.addEventListener('load', checkComplete);
tempIcon.addEventListener('error', checkComplete);
tempElements.push(tempIcon);
document.body.appendChild(tempIcon);
});
// 设置超时清理
setTimeout(() => {
cleanup();
resolve();
}, 3000);
});
}
}
// 创建全局实例
window.__iconifyLoader = new IconifyLoader();
// 立即开始加载
window.__iconifyLoader.load().catch(error => {
console.error('Failed to initialize Iconify:', error);
});
// 如果有预加载图标,添加到队列
if (preloadIcons && preloadIcons.length > 0) {
window.__iconifyLoader.addToPreloadQueue(preloadIcons);
}
// 导出便捷函数到全局
window.loadIconify = () => window.__iconifyLoader.load();
window.preloadIcons = (icons) => window.__iconifyLoader.addToPreloadQueue(icons);
window.onIconifyReady = (callback) => window.__iconifyLoader.onLoad(callback);
// 页面可见性变化时重新检查
document.addEventListener('visibilitychange', () => {
if (!document.hidden && !window.__iconifyLoader.isLoaded) {
window.__iconifyLoader.load().catch(console.error);
}
});
})();
})();</script> <!-- 为不支持JavaScript的情况提供备用方案 --><noscript><style>
iconify-icon {
display: none;
}
.icon-fallback {
display: inline-block;
}
</style></noscript> <div id="top-row" class="z-50 pointer-events-none relative transition-all duration-700 max-w-[var(--page-width)] px-0 md:px-4 mx-auto" data-astro-cid-haiuh7kc> <div id="navbar-wrapper" class="pointer-events-auto sticky top-0 transition-all" data-astro-cid-haiuh7kc> <div id="navbar" class="z-50 onload-animation" data-transparent-mode="semifull" data-is-home="false"> <div class="absolute h-8 left-0 right-0 -top-8 bg-[var(--card-bg)] transition"></div> <!-- used for onload animation --> <div class="!overflow-visible max-w-[var(--page-width)] h-[4.5rem] mx-auto flex items-center justify-between px-4"> <a href="/blog/" class="btn-plain scale-animation rounded-lg h-[3.25rem] px-5 font-bold active:scale-95"> <div class="flex flex-row text-[var(--primary)] items-center text-md"> <svg width="1em" height="1em" class="text-[1.75rem] mb-1 mr-2" data-icon="material-symbols:home-outline-rounded"> <symbol id="ai:material-symbols:home-outline-rounded" viewBox="0 0 24 24"><path fill="currentColor" d="M6 19h3v-5q0-.425.288-.712T10 13h4q.425 0 .713.288T15 14v5h3v-9l-6-4.5L6 10zm-2 0v-9q0-.475.213-.9t.587-.7l6-4.5q.525-.4 1.2-.4t1.2.4l6 4.5q.375.275.588.7T20 10v9q0 .825-.588 1.413T18 21h-4q-.425 0-.712-.288T13 20v-5h-2v5q0 .425-.288.713T10 21H6q-.825 0-1.412-.587T4 19m8-6.75"/></symbol><use href="#ai:material-symbols:home-outline-rounded"></use> </svg> FutureOSS Docs </div> </a> <div class="hidden md:flex items-center space-x-1"> <div class="dropdown-container" data-dropdown data-astro-cid-qou34bit> <a aria-label="主页" href="/blog/" class="btn-plain scale-animation rounded-lg h-11 font-bold px-5 active:scale-95" data-astro-cid-qou34bit> <div class="flex items-center" data-astro-cid-qou34bit> <svg width="1em" height="1em" class="text-[1.1rem] mr-2" data-astro-cid-qou34bit="true" data-icon="material-symbols:home"> <symbol id="ai:material-symbols:home" viewBox="0 0 24 24"><path fill="currentColor" d="M4 21V9l8-6l8 6v12h-6v-7h-4v7z"/></symbol><use href="#ai:material-symbols:home"></use> </svg> 主页 </div> </a> </div> <script type="module">document.addEventListener("DOMContentLoaded",function(){const i=document.querySelectorAll("[data-dropdown]");i.forEach(t=>{const e=t.querySelector("[data-dropdown-trigger]"),o=t.querySelector("[data-dropdown-menu]"),n=t.querySelectorAll(".dropdown-item");!e||!o||(e.addEventListener("keydown",function(r){r.key==="Enter"||r.key===" "?(r.preventDefault(),d(t,e,o)):r.key==="ArrowDown"?(r.preventDefault(),u(t,e,o),n.length>0&&n[0].focus()):r.key==="Escape"&&a(t,e,o)}),n.forEach((r,l)=>{r.addEventListener("keydown",function(s){if(s.key==="ArrowDown"){s.preventDefault();const c=(l+1)%n.length;n[c].focus()}else if(s.key==="ArrowUp"){s.preventDefault();const c=(l-1+n.length)%n.length;n[c].focus()}else s.key==="Escape"&&(a(t,e,o),e.focus())})}))}),document.addEventListener("click",function(t){i.forEach(e=>{if(!e.contains(t.target)){const o=e.querySelector("[data-dropdown-trigger]"),n=e.querySelector("[data-dropdown-menu]");o&&n&&a(e,o,n)}})})});function d(i,t,e){t.getAttribute("aria-expanded")==="true"?a(i,t,e):u(i,t,e)}function u(i,t,e){t.setAttribute("aria-expanded","true"),e.classList.remove("opacity-0","invisible","pointer-events-none","translate-y-[-8px]"),e.classList.add("opacity-100","visible","pointer-events-auto","translate-y-0")}function a(i,t,e){t.setAttribute("aria-expanded","false"),e.classList.add("opacity-0","invisible","pointer-events-none","translate-y-[-8px]"),e.classList.remove("opacity-100","visible","pointer-events-auto","translate-y-0")}</script><div class="dropdown-container" data-dropdown data-astro-cid-qou34bit> <a aria-label="归档" href="/blog/archive/" class="btn-plain scale-animation rounded-lg h-11 font-bold px-5 active:scale-95" data-astro-cid-qou34bit> <div class="flex items-center" data-astro-cid-qou34bit> <svg width="1em" height="1em" class="text-[1.1rem] mr-2" data-astro-cid-qou34bit="true" data-icon="material-symbols:archive"> <symbol id="ai:material-symbols:archive" viewBox="0 0 24 24"><path fill="currentColor" d="m12 18l4-4l-1.4-1.4l-1.6 1.6V10h-2v4.2l-1.6-1.6L8 14zm-7 3q-.825 0-1.412-.587T3 19V6.525q0-.35.113-.675t.337-.6L4.7 3.725q.275-.35.687-.538T6.25 3h11.5q.45 0 .863.188t.687.537l1.25 1.525q.225.275.338.6t.112.675V19q0 .825-.587 1.413T19 21zm.4-15h13.2l-.85-1H6.25z"/></symbol><use href="#ai:material-symbols:archive"></use> </svg> 归档 </div> </a> </div> <div class="dropdown-container" data-dropdown data-astro-cid-qou34bit> <button class="btn-plain scale-animation rounded-lg h-11 font-bold px-5 active:scale-95 dropdown-trigger" aria-expanded="false" aria-haspopup="true" data-dropdown-trigger data-astro-cid-qou34bit> <div class="flex items-center" data-astro-cid-qou34bit> <svg width="1em" height="1em" class="text-[1.1rem] mr-2" data-astro-cid-qou34bit="true" data-icon="material-symbols:code"> <symbol id="ai:material-symbols:code" viewBox="0 0 24 24"><path fill="currentColor" d="m8 18l-6-6l6-6l1.425 1.425l-4.6 4.6L9.4 16.6zm8 0l-1.425-1.425l4.6-4.6L14.6 7.4L16 6l6 6z"/></symbol><use href="#ai:material-symbols:code"></use> </svg> 仓库 <svg width="1em" height="1em" class="text-[1.25rem] transition-transform duration-200 dropdown-arrow ml-1" data-astro-cid-qou34bit="true" data-icon="material-symbols:keyboard-arrow-down-rounded"> <symbol id="ai:material-symbols:keyboard-arrow-down-rounded" viewBox="0 0 24 24"><path fill="currentColor" d="M11.625 14.913q-.175-.063-.325-.213l-4.6-4.6q-.275-.275-.275-.7t.275-.7t.7-.275t.7.275l3.9 3.9l3.9-3.9q.275-.275.7-.275t.7.275t.275.7t-.275.7l-4.6 4.6q-.15.15-.325.213t-.375.062t-.375-.062"/></symbol><use href="#ai:material-symbols:keyboard-arrow-down-rounded"></use> </svg> </div> </button>
<div class="dropdown-menu" data-dropdown-menu data-astro-cid-qou34bit> <div class="dropdown-content" data-astro-cid-qou34bit> <a href="https://gitee.com/starlight-apk/feature-oss" target="_blank" class="dropdown-item" aria-label="Gitee" data-astro-cid-qou34bit> <div class="flex items-center" data-astro-cid-qou34bit> <svg width="1em" height="1em" viewBox="0 0 24 24" class="text-[1rem] mr-2" data-astro-cid-qou34bit="true" data-icon="mdi:git"> <use href="#ai:mdi:git"></use> </svg> <span data-astro-cid-qou34bit>Gitee</span> </div> <svg width="1em" height="1em" class="text-[0.75rem] text-black/25 dark:text-white/25" data-astro-cid-qou34bit="true" data-icon="fa6-solid:arrow-up-right-from-square"> <symbol id="ai:fa6-solid:arrow-up-right-from-square" viewBox="0 0 512 512"><path fill="currentColor" d="M320 0c-17.7 0-32 14.3-32 32s14.3 32 32 32h82.7L201.4 265.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L448 109.3V192c0 17.7 14.3 32 32 32s32-14.3 32-32V32c0-17.7-14.3-32-32-32zM80 32C35.8 32 0 67.8 0 112v320c0 44.2 35.8 80 80 80h320c44.2 0 80-35.8 80-80V320c0-17.7-14.3-32-32-32s-32 14.3-32 32v112c0 8.8-7.2 16-16 16H80c-8.8 0-16-7.2-16-16V112c0-8.8 7.2-16 16-16h112c17.7 0 32-14.3 32-32s-14.3-32-32-32z"/></symbol><use href="#ai:fa6-solid:arrow-up-right-from-square"></use> </svg> </a><a href="https://github.com/starlight-apk/feature-oss" target="_blank" class="dropdown-item" aria-label="GitHub" data-astro-cid-qou34bit> <div class="flex items-center" data-astro-cid-qou34bit> <svg width="0.97em" height="1em" viewBox="0 0 496 512" class="text-[1rem] mr-2" data-astro-cid-qou34bit="true" data-icon="fa6-brands:github"> <use href="#ai:fa6-brands:github"></use> </svg> <span data-astro-cid-qou34bit>GitHub</span> </div> <svg width="1em" height="1em" viewBox="0 0 512 512" class="text-[0.75rem] text-black/25 dark:text-white/25" data-astro-cid-qou34bit="true" data-icon="fa6-solid:arrow-up-right-from-square"> <use href="#ai:fa6-solid:arrow-up-right-from-square"></use> </svg> </a> </div> </div> </div> </div> <div class="flex"> <!--<SearchPanel client:load>--> <style>astro-island,astro-slot,astro-static-slot{display:contents}</style><script>(()=>{var e=async t=>{await(await t())()};(self.Astro||(self.Astro={})).only=e;window.dispatchEvent(new Event("astro:only"));})();</script><script>(()=>{var A=Object.defineProperty;var g=(i,o,a)=>o in i?A(i,o,{enumerable:!0,configurable:!0,writable:!0,value:a}):i[o]=a;var d=(i,o,a)=>g(i,typeof o!="symbol"?o+"":o,a);{let i={0:t=>m(t),1:t=>a(t),2:t=>new RegExp(t),3:t=>new Date(t),4:t=>new Map(a(t)),5:t=>new Set(a(t)),6:t=>BigInt(t),7:t=>new URL(t),8:t=>new Uint8Array(t),9:t=>new Uint16Array(t),10:t=>new Uint32Array(t),11:t=>1/0*t},o=t=>{let[l,e]=t;return l in i?i[l](e):void 0},a=t=>t.map(o),m=t=>typeof t!="object"||t===null?t:Object.fromEntries(Object.entries(t).map(([l,e])=>[l,o(e)]));class y extends HTMLElement{constructor(){super(...arguments);d(this,"Component");d(this,"hydrator");d(this,"hydrate",async()=>{var b;if(!this.hydrator||!this.isConnected)return;let e=(b=this.parentElement)==null?void 0:b.closest("astro-island[ssr]");if(e){e.addEventListener("astro:hydrate",this.hydrate,{once:!0});return}let c=this.querySelectorAll("astro-slot"),n={},h=this.querySelectorAll("template[data-astro-template]");for(let r of h){let s=r.closest(this.tagName);s!=null&&s.isSameNode(this)&&(n[r.getAttribute("data-astro-template")||"default"]=r.innerHTML,r.remove())}for(let r of c){let s=r.closest(this.tagName);s!=null&&s.isSameNode(this)&&(n[r.getAttribute("name")||"default"]=r.innerHTML)}let p;try{p=this.hasAttribute("props")?m(JSON.parse(this.getAttribute("props"))):{}}catch(r){let s=this.getAttribute("component-url")||"<unknown>",v=this.getAttribute("component-export");throw v&&(s+=` (export ${v})`),console.error(`[hydrate] Error parsing props for component ${s}`,this.getAttribute("props"),r),r}let u;await this.hydrator(this)(this.Component,p,n,{client:this.getAttribute("client")}),this.removeAttribute("ssr"),this.dispatchEvent(new CustomEvent("astro:hydrate"))});d(this,"unmount",()=>{this.isConnected||this.dispatchEvent(new CustomEvent("astro:unmount"))})}disconnectedCallback(){document.removeEventListener("astro:after-swap",this.unmount),document.addEventListener("astro:after-swap",this.unmount,{once:!0})}connectedCallback(){if(!this.hasAttribute("await-children")||document.readyState==="interactive"||document.readyState==="complete")this.childrenConnectedCallback();else{let e=()=>{document.removeEventListener("DOMContentLoaded",e),c.disconnect(),this.childrenConnectedCallback()},c=new MutationObserver(()=>{var n;((n=this.lastChild)==null?void 0:n.nodeType)===Node.COMMENT_NODE&&this.lastChild.nodeValue==="astro:end"&&(this.lastChild.remove(),e())});c.observe(this,{childList:!0}),document.addEventListener("DOMContentLoaded",e)}}async childrenConnectedCallback(){let e=this.getAttribute("before-hydration-url");e&&await import(e),this.start()}async start(){let e=JSON.parse(this.getAttribute("opts")),c=this.getAttribute("client");if(Astro[c]===void 0){window.addEventListener(`astro:${c}`,()=>this.start(),{once:!0});return}try{await Astro[c](async()=>{let n=this.getAttribute("renderer-url"),[h,{default:p}]=await Promise.all([import(this.getAttribute("component-url")),n?import(n):()=>()=>{}]),u=this.getAttribute("component-export")||"default";if(!u.includes("."))this.Component=h[u];else{this.Component=h;for(let f of u.split("."))this.Component=this.Component[f]}return this.hydrator=p,this.hydrate},e,this)}catch(n){console.error(`[astro-island] Error hydrating ${this.getAttribute("component-url")}`,n)}}attributeChangedCallback(){this.hydrate()}}d(y,"observedAttributes",["props"]),customElements.get("astro-island")||customElements.define("astro-island",y)}})();</script><astro-island uid="8ERU5" component-url="/blog/_astro/Search.C4siwRRu.js" component-export="default" renderer-url="/blog/_astro/client.svelte.9vP7DkWT.js" props="{}" ssr client="only" opts="{&quot;name&quot;:&quot;Search&quot;,&quot;value&quot;:&quot;svelte&quot;}"></astro-island> <astro-island uid="ZgLYMD" component-url="/blog/_astro/MobileTOC.DcxBJmw1.js" component-export="default" renderer-url="/blog/_astro/client.svelte.9vP7DkWT.js" props="{}" ssr client="only" opts="{&quot;name&quot;:&quot;MobileTOC&quot;,&quot;value&quot;:&quot;svelte&quot;}"></astro-island> <astro-island uid="pXLEo" component-url="/blog/_astro/TranslateButton.gpbH5i1G.js" component-export="default" renderer-url="/blog/_astro/client.svelte.9vP7DkWT.js" props="{}" ssr client="only" opts="{&quot;name&quot;:&quot;TranslateButton&quot;,&quot;value&quot;:&quot;svelte&quot;}"></astro-island> <button aria-label="Display Settings" class="btn-plain scale-animation rounded-lg h-11 w-11 active:scale-90" id="display-settings-switch"> <svg width="1em" height="1em" class="text-[1.25rem]" data-icon="material-symbols:palette-outline"> <symbol id="ai:material-symbols:palette-outline" viewBox="0 0 24 24"><path fill="currentColor" d="M12 22q-2.05 0-3.875-.788t-3.187-2.15t-2.15-3.187T2 12q0-2.075.813-3.9t2.2-3.175T8.25 2.788T12.2 2q2 0 3.775.688t3.113 1.9t2.125 2.875T22 11.05q0 2.875-1.75 4.413T16 17h-1.85q-.225 0-.312.125t-.088.275q0 .3.375.863t.375 1.287q0 1.25-.687 1.85T12 22m-4.425-9.425Q8 12.15 8 11.5t-.425-1.075T6.5 10t-1.075.425T5 11.5t.425 1.075T6.5 13t1.075-.425m3-4Q11 8.15 11 7.5t-.425-1.075T9.5 6t-1.075.425T8 7.5t.425 1.075T9.5 9t1.075-.425m5 0Q16 8.15 16 7.5t-.425-1.075T14.5 6t-1.075.425T13 7.5t.425 1.075T14.5 9t1.075-.425m3 4Q19 12.15 19 11.5t-.425-1.075T17.5 10t-1.075.425T16 11.5t.425 1.075T17.5 13t1.075-.425M12 20q.225 0 .363-.125t.137-.325q0-.35-.375-.825T11.75 17.3q0-1.05.725-1.675T14.25 15H16q1.65 0 2.825-.962T20 11.05q0-3.025-2.312-5.038T12.2 4Q8.8 4 6.4 6.325T4 12q0 3.325 2.338 5.663T12 20"/></symbol><use href="#ai:material-symbols:palette-outline"></use> </svg> </button> <astro-island uid="Z1LIWpd" component-url="/blog/_astro/LightDarkSwitch.DQTkCfco.js" component-export="default" renderer-url="/blog/_astro/client.svelte.9vP7DkWT.js" props="{}" ssr client="only" opts="{&quot;name&quot;:&quot;LightDarkSwitch&quot;,&quot;value&quot;:&quot;svelte&quot;}"></astro-island> <button aria-label="Menu" name="Nav Menu" class="btn-plain scale-animation rounded-lg w-11 h-11 active:scale-90 md:!hidden" id="nav-menu-switch"> <svg width="1em" height="1em" class="text-[1.25rem]" data-icon="material-symbols:menu-rounded"> <symbol id="ai:material-symbols:menu-rounded" viewBox="0 0 24 24"><path fill="currentColor" d="M4 18q-.425 0-.712-.288T3 17t.288-.712T4 16h16q.425 0 .713.288T21 17t-.288.713T20 18zm0-5q-.425 0-.712-.288T3 12t.288-.712T4 11h16q.425 0 .713.288T21 12t-.288.713T20 13zm0-5q-.425 0-.712-.288T3 7t.288-.712T4 6h16q.425 0 .713.288T21 7t-.288.713T20 8z"/></symbol><use href="#ai:material-symbols:menu-rounded"></use> </svg> </button> </div> <div id="nav-menu-panel" class="float-panel float-panel-closed absolute transition-all fixed right-4 px-2 py-2 max-h-[80vh] overflow-y-auto" data-astro-cid-h5lvqo6o> <div class="mobile-menu-item" data-astro-cid-h5lvqo6o> <!-- 普通链接项目 -->
<a href="/blog/" class="group flex justify-between items-center py-2 pl-3 pr-1 rounded-lg gap-8
hover:bg-[var(--btn-plain-bg-hover)] active:bg-[var(--btn-plain-bg-active)] transition
" data-astro-cid-h5lvqo6o> <div class="flex items-center transition text-black/75 dark:text-white/75 font-bold group-hover:text-[var(--primary)] group-active:text-[var(--primary)]" data-astro-cid-h5lvqo6o> <svg width="1em" height="1em" viewBox="0 0 24 24" class="text-[1.1rem] mr-2" data-astro-cid-h5lvqo6o="true" data-icon="material-symbols:home"> <use href="#ai:material-symbols:home"></use> </svg> 主页 </div> <svg width="1em" height="1em" class="transition text-[1.25rem] text-[var(--primary)]" data-astro-cid-h5lvqo6o="true" data-icon="material-symbols:chevron-right-rounded"> <symbol id="ai:material-symbols:chevron-right-rounded" viewBox="0 0 24 24"><path fill="currentColor" d="M12.6 12L8.7 8.1q-.275-.275-.275-.7t.275-.7t.7-.275t.7.275l4.6 4.6q.15.15.213.325t.062.375t-.062.375t-.213.325l-4.6 4.6q-.275.275-.7.275t-.7-.275t-.275-.7t.275-.7z"/></symbol><use href="#ai:material-symbols:chevron-right-rounded"></use> </svg> </a> </div><div class="mobile-menu-item" data-astro-cid-h5lvqo6o> <!-- 普通链接项目 -->
<a href="/blog/archive/" class="group flex justify-between items-center py-2 pl-3 pr-1 rounded-lg gap-8
hover:bg-[var(--btn-plain-bg-hover)] active:bg-[var(--btn-plain-bg-active)] transition
" data-astro-cid-h5lvqo6o> <div class="flex items-center transition text-black/75 dark:text-white/75 font-bold group-hover:text-[var(--primary)] group-active:text-[var(--primary)]" data-astro-cid-h5lvqo6o> <svg width="1em" height="1em" viewBox="0 0 24 24" class="text-[1.1rem] mr-2" data-astro-cid-h5lvqo6o="true" data-icon="material-symbols:archive"> <use href="#ai:material-symbols:archive"></use> </svg> 归档 </div> <svg width="1em" height="1em" viewBox="0 0 24 24" class="transition text-[1.25rem] text-[var(--primary)]" data-astro-cid-h5lvqo6o="true" data-icon="material-symbols:chevron-right-rounded"> <use href="#ai:material-symbols:chevron-right-rounded"></use> </svg> </a> </div><div class="mobile-menu-item" data-astro-cid-h5lvqo6o> <!-- 有子菜单的项目 -->
<div class="mobile-dropdown" data-mobile-dropdown data-astro-cid-h5lvqo6o> <button class="group flex justify-between items-center py-2 pl-3 pr-1 rounded-lg gap-8 w-full text-left
hover:bg-[var(--btn-plain-bg-hover)] active:bg-[var(--btn-plain-bg-active)] transition" data-mobile-dropdown-trigger aria-expanded="false" data-astro-cid-h5lvqo6o> <div class="flex items-center transition text-black/75 dark:text-white/75 font-bold group-hover:text-[var(--primary)] group-active:text-[var(--primary)]" data-astro-cid-h5lvqo6o> <svg width="1em" height="1em" viewBox="0 0 24 24" class="text-[1.1rem] mr-2" data-astro-cid-h5lvqo6o="true" data-icon="material-symbols:code"> <use href="#ai:material-symbols:code"></use> </svg> 仓库 </div> <svg width="1em" height="1em" viewBox="0 0 24 24" class="transition text-[1.25rem] text-[var(--primary)] mobile-dropdown-arrow duration-200" data-astro-cid-h5lvqo6o="true" data-icon="material-symbols:keyboard-arrow-down-rounded"> <use href="#ai:material-symbols:keyboard-arrow-down-rounded"></use> </svg> </button> <div class="mobile-submenu" data-mobile-submenu data-astro-cid-h5lvqo6o> <a href="https://gitee.com/starlight-apk/feature-oss" class="group flex justify-between items-center py-2 pl-6 pr-1 rounded-lg gap-8
hover:bg-[var(--btn-plain-bg-hover)] active:bg-[var(--btn-plain-bg-active)] transition" target="_blank" data-astro-cid-h5lvqo6o> <div class="flex items-center transition text-black/60 dark:text-white/60 font-medium group-hover:text-[var(--primary)] group-active:text-[var(--primary)]" data-astro-cid-h5lvqo6o> <svg width="1em" height="1em" viewBox="0 0 24 24" class="text-[1.1rem] mr-2" data-astro-cid-h5lvqo6o="true" data-icon="mdi:git"> <use href="#ai:mdi:git"></use> </svg> Gitee </div> <svg width="1em" height="1em" viewBox="0 0 512 512" class="transition text-[0.75rem] text-black/25 dark:text-white/25 -translate-x-1" data-astro-cid-h5lvqo6o="true" data-icon="fa6-solid:arrow-up-right-from-square"> <use href="#ai:fa6-solid:arrow-up-right-from-square"></use> </svg> </a><a href="https://github.com/starlight-apk/feature-oss" class="group flex justify-between items-center py-2 pl-6 pr-1 rounded-lg gap-8
hover:bg-[var(--btn-plain-bg-hover)] active:bg-[var(--btn-plain-bg-active)] transition" target="_blank" data-astro-cid-h5lvqo6o> <div class="flex items-center transition text-black/60 dark:text-white/60 font-medium group-hover:text-[var(--primary)] group-active:text-[var(--primary)]" data-astro-cid-h5lvqo6o> <svg width="0.97em" height="1em" viewBox="0 0 496 512" class="text-[1.1rem] mr-2" data-astro-cid-h5lvqo6o="true" data-icon="fa6-brands:github"> <use href="#ai:fa6-brands:github"></use> </svg> GitHub </div> <svg width="1em" height="1em" viewBox="0 0 512 512" class="transition text-[0.75rem] text-black/25 dark:text-white/25 -translate-x-1" data-astro-cid-h5lvqo6o="true" data-icon="fa6-solid:arrow-up-right-from-square"> <use href="#ai:fa6-solid:arrow-up-right-from-square"></use> </svg> </a> </div> </div> </div> </div> <script type="module">document.addEventListener("DOMContentLoaded",function(){const r=document.querySelectorAll("[data-mobile-dropdown]");r.forEach(e=>{const t=e.querySelector("[data-mobile-dropdown-trigger]"),d=e.querySelector("[data-mobile-submenu]");!t||!d||t.addEventListener("click",function(o){o.preventDefault();const u=e.getAttribute("data-expanded")==="true";r.forEach(a=>{if(a!==e){a.setAttribute("data-expanded","false");const i=a.querySelector("[data-mobile-dropdown-trigger]");i&&i.setAttribute("aria-expanded","false")}});const n=!u;e.setAttribute("data-expanded",n.toString()),t.setAttribute("aria-expanded",n.toString())})})});</script> <astro-island uid="i6lC1" component-url="/blog/_astro/DisplaySettings.BkjQOgCB.js" component-export="default" renderer-url="/blog/_astro/client.svelte.9vP7DkWT.js" props="{}" ssr client="only" opts="{&quot;name&quot;:&quot;DisplaySettings&quot;,&quot;value&quot;:&quot;svelte&quot;}"></astro-island> </div> </div> <script type="module">function c(){localStorage.theme==="dark"?(document.documentElement.classList.remove("dark"),localStorage.theme="light"):(document.documentElement.classList.add("dark"),localStorage.theme="dark")}function d(){let e=document.getElementById("scheme-switch");e&&(e.onclick=function(){c()});let l=document.getElementById("display-settings-switch");l&&(l.onclick=function(){let t=document.getElementById("display-setting");t&&t.classList.toggle("float-panel-closed")});let n=document.getElementById("nav-menu-switch");n&&(n.onclick=function(){let t=document.getElementById("nav-menu-panel");t&&t.classList.toggle("float-panel-closed")})}d();function o(){const e=document.getElementById("navbar");if(!e||e.getAttribute("data-transparent-mode")!=="semifull")return;if(!(e.getAttribute("data-is-home")==="true")){window.semifullScrollHandler&&(window.removeEventListener("scroll",window.semifullScrollHandler),window.semifullScrollHandler=null),e.classList.add("scrolled");return}e.classList.remove("scrolled");let t=!1;function i(){(window.pageYOffset||document.documentElement.scrollTop)>50?e.classList.add("scrolled"):e.classList.remove("scrolled"),t=!1}function s(){t||(requestAnimationFrame(i),t=!0)}window.semifullScrollHandler&&window.removeEventListener("scroll",window.semifullScrollHandler),window.semifullScrollHandler=s,window.addEventListener("scroll",s,{passive:!0}),i()}window.initSemifullScrollDetection=o;document.readyState==="loading"?document.addEventListener("DOMContentLoaded",o):o();</script> <script>(function(){const scriptUrl = "/blog/pagefind/pagefind.js";
async function loadPagefind() {
try {
const response = await fetch(scriptUrl, { method: 'HEAD' });
if (!response.ok) {
throw new Error(`Pagefind script not found: ${response.status}`);
}
const pagefind = await import(scriptUrl);
await pagefind.options({
excerptLength: 20
});
window.pagefind = pagefind;
document.dispatchEvent(new CustomEvent('pagefindready'));
console.log('Pagefind loaded and initialized successfully, event dispatched.');
} catch (error) {
console.error('Failed to load Pagefind:', error);
window.pagefind = {
search: () => Promise.resolve({ results: [] }),
options: () => Promise.resolve(),
};
document.dispatchEvent(new CustomEvent('pagefindloaderror'));
console.log('Pagefind load error, event dispatched.');
}
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', loadPagefind);
} else {
loadPagefind();
}
})();</script> </div> </div> <div id="banner-wrapper" class="absolute z-10 w-full transition duration-700 overflow-hidden mobile-hide-banner" style="top: -30vh" data-astro-cid-haiuh7kc> <!-- Single image mode -->
<div class="relative h-full w-full" data-astro-cid-haiuh7kc> <!-- Mobile: use mobile-specific image with same logic as desktop --> <div class="block lg:hidden object-cover h-full w-full transition duration-700 opacity-100 overflow-hidden relative"> <div class="transition absolute inset-0 dark:bg-black/10 bg-opacity-50 pointer-events-none"></div> <img src="/blog/assets/placeholder.jpg" alt="Mobile banner image of the blog" class="w-full h-full object-cover" style="object-position: center"> </div> <!-- Desktop: use desktop-specific image --> <div id="banner" class="hidden lg:block object-cover h-full transition duration-700 opacity-100 overflow-hidden relative"> <div class="transition absolute inset-0 dark:bg-black/10 bg-opacity-50 pointer-events-none"></div> <img src="/blog/assets/placeholder.jpg" alt="Desktop banner image of the blog" class="w-full h-full object-cover" style="object-position: center"> </div> </div> <!-- Home page text overlay --> <div class="banner-text-overlay absolute inset-0 z-20 flex items-center justify-center hidden" data-astro-cid-haiuh7kc> <div class="w-4/5 lg:w-3/4 text-center mb-0" data-astro-cid-haiuh7kc> <div class="flex flex-col" data-astro-cid-haiuh7kc> <h1 class="banner-title text-6xl lg:text-8xl font-bold text-white drop-shadow-lg mb-2 lg:mb-4" data-astro-cid-haiuh7kc> FutureOSS </h1> <h2 class="banner-subtitle text-xl lg:text-3xl text-white/90 drop-shadow-md" data-astro-cid-haiuh7kc> <span class="typewriter " data-text="[&#34;一切皆为插件的开发者工具运行时框架&#34;,&#34;插件热插拔 · 依赖自动解析 · 熔断降级 · 事件驱动&#34;]" data-speed="100" data-delete-speed="50" data-pause-time="2000" data-astro-cid-4iv2rnoh></span> <script type="module">class n{element;texts;currentTextIndex=0;speed;deleteSpeed;pauseTime;currentIndex=0;isDeleting=!1;timeoutId=null;constructor(t){this.element=t;const e=t.dataset.text||"";try{const i=JSON.parse(e);this.texts=Array.isArray(i)?i:[e]}catch{this.texts=[e]}this.speed=parseInt(t.dataset.speed||"100"),this.deleteSpeed=parseInt(t.dataset.deleteSpeed||"50"),this.pauseTime=parseInt(t.dataset.pauseTime||"2000"),this.texts.length>1&&!this.isTypewriterEnabled()?this.showRandomText():this.start()}isTypewriterEnabled(){return this.element.dataset.speed!==void 0||this.element.dataset.deleteSpeed!==void 0||this.element.dataset.pauseTime!==void 0}showRandomText(){const t=Math.floor(Math.random()*this.texts.length);this.element.textContent=this.texts[t]}start(){this.texts.length!==0&&this.type()}getCurrentText(){return this.texts[this.currentTextIndex]||""}type(){const t=this.getCurrentText();this.isDeleting?this.currentIndex>0?(this.currentIndex--,this.element.textContent=t.substring(0,this.currentIndex),this.timeoutId=window.setTimeout(()=>this.type(),this.deleteSpeed)):(this.isDeleting=!1,this.currentTextIndex=(this.currentTextIndex+1)%this.texts.length,this.timeoutId=window.setTimeout(()=>this.type(),this.speed)):this.currentIndex<t.length?(this.currentIndex++,this.element.textContent=t.substring(0,this.currentIndex),this.timeoutId=window.setTimeout(()=>this.type(),this.speed)):this.texts.length>1&&(this.isDeleting=!0,this.timeoutId=window.setTimeout(()=>this.type(),this.pauseTime))}destroy(){this.timeoutId&&clearTimeout(this.timeoutId)}}document.addEventListener("DOMContentLoaded",()=>{document.querySelectorAll(".typewriter").forEach(t=>{new n(t)})});document.addEventListener("swup:contentReplaced",()=>{document.querySelectorAll(".typewriter").forEach(t=>{new n(t)})});</script> </h2> </div> </div> </div> <!-- Water waves effect --> <div class="waves absolute -bottom-[1px] h-[10vh] max-h-[9.375rem] min-h-[3.125rem] w-full md:h-[15vh]" id="header-waves" style="transform: translateZ(0); will-change: fill;" data-astro-cid-haiuh7kc> <svg class="waves" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 20 150 32" preserveAspectRatio="none" shape-rendering="auto" style="transform: translateZ(0); backface-visibility: hidden;" data-astro-cid-haiuh7kc> <defs data-astro-cid-haiuh7kc> <path id="gentle-wave" d="M-160 44c30 0 58-18 88-18s 58 18 88 18 58-18 88-18 58 18 88 18 v48h-352z" data-astro-cid-haiuh7kc></path> </defs> <g class="parallax" style="transform: translateZ(0);" data-astro-cid-haiuh7kc> <use xlink:href="#gentle-wave" x="48" y="0" class="opacity-25 fill-[var(--page-bg)]" style="animation-delay: -2s; animation-duration: 7s; will-change: transform;" data-astro-cid-haiuh7kc></use> <use xlink:href="#gentle-wave" x="48" y="3" class="opacity-50 fill-[var(--page-bg)]" style="animation-delay: -3s; animation-duration: 10s; will-change: transform;" data-astro-cid-haiuh7kc></use> <use xlink:href="#gentle-wave" x="48" y="5" class="opacity-75 fill-[var(--page-bg)]" style="animation-delay: -4s; animation-duration: 13s; will-change: transform;" data-astro-cid-haiuh7kc></use> <use xlink:href="#gentle-wave" x="48" y="7" class="fill-[var(--page-bg)]" style="animation-delay: -5s; animation-duration: 20s; will-change: transform;" data-astro-cid-haiuh7kc></use> </g> </svg> </div> </div> <div class="absolute w-full z-30 pointer-events-none mobile-main-no-banner " style="top: calc(35vh - 3.5rem)" data-astro-cid-haiuh7kc> <!-- The pointer-events-none here prevent blocking the click event of the TOC --> <div class="relative max-w-[var(--page-width)] mx-auto pointer-events-auto" data-astro-cid-haiuh7kc> <div id="main-grid" class="transition duration-700 w-full left-0 right-0 grid grid-cols-1 md:grid-cols-[17.5rem_1fr] lg:grid-cols-[17.5rem_1fr] grid-rows-[auto_1fr_auto] lg:grid-rows-[auto] mx-auto gap-4 px-0 md:px-4 " data-astro-cid-haiuh7kc> <!-- Banner image credit --> <div id="sidebar" class="mb-4 row-start-2 row-end-3 col-span-2 onload-animation block md:block md:row-start-1 md:row-end-2 md:max-w-[17.5rem] md:col-start-1 md:col-end-2 lg:block lg:row-start-1 lg:row-end-2 lg:max-w-[17.5rem] lg:col-start-1 lg:col-end-2 w-full" data-astro-cid-gfmxq3pg> <!-- 顶部固定组件区域 --> <div class="flex flex-col w-full gap-4 mb-4" data-astro-cid-gfmxq3pg> <div class="card-base p-3"> <a aria-label="Go to About Page" href="/blog/about/" class="group block relative mx-auto mt-1 lg:mx-0 lg:mt-0 mb-3
max-w-[12rem] lg:max-w-none overflow-hidden rounded-xl active:scale-95"> <div class="absolute transition pointer-events-none group-hover:bg-black/30 group-active:bg-black/50
w-full h-full z-50 flex items-center justify-center"> <svg width="1.13em" height="1em" class="transition opacity-0 scale-90 group-hover:scale-100 group-hover:opacity-100 text-white text-5xl" data-icon="fa6-regular:address-card"> <symbol id="ai:fa6-regular:address-card" viewBox="0 0 576 512"><path fill="currentColor" d="M512 80c8.8 0 16 7.2 16 16v320c0 8.8-7.2 16-16 16H64c-8.8 0-16-7.2-16-16V96c0-8.8 7.2-16 16-16zM64 32C28.7 32 0 60.7 0 96v320c0 35.3 28.7 64 64 64h448c35.3 0 64-28.7 64-64V96c0-35.3-28.7-64-64-64zm144 224a64 64 0 1 0 0-128a64 64 0 1 0 0 128m-32 32c-44.2 0-80 35.8-80 80c0 8.8 7.2 16 16 16h192c8.8 0 16-7.2 16-16c0-44.2-35.8-80-80-80zm200-144c-13.3 0-24 10.7-24 24s10.7 24 24 24h80c13.3 0 24-10.7 24-24s-10.7-24-24-24zm0 96c-13.3 0-24 10.7-24 24s10.7 24 24 24h80c13.3 0 24-10.7 24-24s-10.7-24-24-24z"/></symbol><use href="#ai:fa6-regular:address-card"></use> </svg> </div> <div class="mx-auto lg:w-full h-full lg:mt-0 overflow-hidden relative"> <div class="transition absolute inset-0 dark:bg-black/10 bg-opacity-50 pointer-events-none"></div> <img src="/blog/_astro/avatar.DslDo0tY_Z2tanD.webp" alt="Profile Image of the Author" style="object-position: center" loading="lazy" decoding="async" fetchpriority="auto" width="640" height="640" class="w-full h-full object-cover"> </div> </a> <div class="px-2"> <div class="font-bold text-xl text-center mb-1 dark:text-neutral-50 transition">FutureOSS</div> <div class="h-1 w-5 bg-[var(--primary)] mx-auto rounded-full mb-2 transition"></div> <div class="text-center text-neutral-400 mb-2.5 transition">一切皆为插件的开发者工具运行时框架</div> <div class="flex gap-2 justify-center mb-1"> <a rel="me" aria-label="Gitee" href="https://gitee.com/starlight-apk/feature-oss" target="_blank" class="btn-regular rounded-lg h-10 w-10 active:scale-90"> <svg width="1em" height="1em" class="text-[1.5rem]" data-icon="mdi:git"> <symbol id="ai:mdi:git" viewBox="0 0 24 24"><path fill="currentColor" d="M2.6 10.59L8.38 4.8l1.69 1.7c-.24.85.15 1.78.93 2.23v5.54c-.6.34-1 .99-1 1.73a2 2 0 0 0 2 2a2 2 0 0 0 2-2c0-.74-.4-1.39-1-1.73V9.41l2.07 2.09c-.07.15-.07.32-.07.5a2 2 0 0 0 2 2a2 2 0 0 0 2-2a2 2 0 0 0-2-2c-.18 0-.35 0-.5.07L13.93 7.5a1.98 1.98 0 0 0-1.15-2.34c-.43-.16-.88-.2-1.28-.09L9.8 3.38l.79-.78c.78-.79 2.04-.79 2.82 0l7.99 7.99c.79.78.79 2.04 0 2.82l-7.99 7.99c-.78.79-2.04.79-2.82 0L2.6 13.41c-.79-.78-.79-2.04 0-2.82"/></symbol><use href="#ai:mdi:git"></use> </svg> </a><a rel="me" aria-label="GitHub" href="https://github.com/starlight-apk/feature-oss" target="_blank" class="btn-regular rounded-lg h-10 w-10 active:scale-90"> <svg width="0.97em" height="1em" class="text-[1.5rem]" data-icon="fa6-brands:github"> <symbol id="ai:fa6-brands:github" viewBox="0 0 496 512"><path fill="currentColor" d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6c-3.3.3-5.6-1.3-5.6-3.6c0-2 2.3-3.6 5.2-3.6c3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9c2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9c.3 2 2.9 3.3 5.9 2.6c2.9-.7 4.9-2.6 4.6-4.6c-.3-1.9-3-3.2-5.9-2.9M244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2c12.8 2.3 17.3-5.6 17.3-12.1c0-6.2-.3-40.4-.3-61.4c0 0-70 15-84.7-29.8c0 0-11.4-29.1-27.8-36.6c0 0-22.9-15.7 1.6-15.4c0 0 24.9 2 38.6 25.8c21.9 38.6 58.6 27.5 72.9 20.9c2.3-16 8.8-27.1 16-33.7c-55.9-6.2-112.3-14.3-112.3-110.5c0-27.5 7.6-41.3 23.6-58.9c-2.6-6.5-11.1-33.3 2.6-67.9c20.9-6.5 69 27 69 27c20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27c13.7 34.7 5.2 61.4 2.6 67.9c16 17.7 25.8 31.5 25.8 58.9c0 96.5-58.9 104.2-114.8 110.5c9.2 7.9 17 22.9 17 46.4c0 33.7-.3 75.4-.3 83.6c0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252C496 113.3 383.5 8 244.8 8M97.2 352.9c-1.3 1-1 3.3.7 5.2c1.6 1.6 3.9 2.3 5.2 1c1.3-1 1-3.3-.7-5.2c-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9c1.6 1 3.6.7 4.3-.7c.7-1.3-.3-2.9-2.3-3.9c-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2c2.3 2.3 5.2 2.6 6.5 1c1.3-1.3.7-4.3-1.3-6.2c-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2c-1.4-2.3-4-3.3-5.6-2"/></symbol><use href="#ai:fa6-brands:github"></use> </svg> </a> </div> </div> </div><!-- 组件显示现在由sidebarLayoutConfig统一控制无需检查config.enable --><widget-layout data-id="announcement" data-is-collapsed="undefined" class="pb-4 card-base onload-animation" style="animation-delay: 50ms; " data-astro-cid-ucso7hve="true"> <div class="font-bold transition text-lg text-neutral-900 dark:text-neutral-100 relative ml-8 mt-4 mb-2
before:w-1 before:h-4 before:rounded-md before:bg-[var(--primary)]
before:absolute before:left-[-16px] before:top-[5.5px]" data-astro-cid-ucso7hve style="">欢迎来到 FutureOSS</div> <div id="announcement" class="collapse-wrapper px-4 overflow-hidden" data-astro-cid-ucso7hve style=""> <div> <!-- 公告栏内容 --> <div class="text-neutral-600 dark:text-neutral-300 leading-relaxed mb-3"> 一切皆为插件的开发者工具运行时框架,支持插件热插拔、依赖自动解析、熔断降级、事件驱动等企业级稳定性机制。 </div> <!-- 可选链接和关闭按钮 --> <div class="flex items-center justify-between gap-3"> <div> <a href="https://gitee.com/starlight-apk/feature-oss/wikis/Home" target="_blank" rel="noopener noreferrer" class="btn-regular rounded-lg px-3 py-1.5 text-sm font-medium active:scale-95 transition-transform"> 快速开始 </a> </div> <button class="btn-regular rounded-lg h-8 w-8 text-sm hover:bg-red-100 dark:hover:bg-red-900/30 transition-colors" onclick="closeAnnouncement()" aria-label="关闭"> <svg width="0.75em" height="1em" class="text-sm" data-icon="fa6-solid:xmark"> <symbol id="ai:fa6-solid:xmark" viewBox="0 0 384 512"><path fill="currentColor" d="M342.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L192 210.7L86.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L146.7 256L41.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L192 301.3l105.4 105.3c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L237.3 256z"/></symbol><use href="#ai:fa6-solid:xmark"></use> </svg> </button> </div> </div> </div> </widget-layout> <script type="module">class d extends HTMLElement{constructor(){if(super(),this.dataset.isCollapsed!=="true")return;const e=this.dataset.id,t=this.querySelector(".expand-btn"),s=this.querySelector(`#${e}`);t.addEventListener("click",()=>{s.classList.remove("collapsed"),t.classList.add("hidden")})}}customElements.get("widget-layout")||customElements.define("widget-layout",d);</script> <script type="module">function n(){const e=document.querySelector('widget-layout[data-id="announcement"]');e&&(e.style.display="none",localStorage.setItem("announcementClosed","true"))}document.addEventListener("DOMContentLoaded",function(){const e=document.querySelector('widget-layout[data-id="announcement"]');e&&localStorage.getItem("announcementClosed")==="true"&&(e.style.display="none")});window.closeAnnouncement=n;</script> </div> <!-- 粘性组件区域 --> <div id="sidebar-sticky" class="transition-all duration-700 flex flex-col w-full gap-4 top-4 sticky top-4" data-astro-cid-gfmxq3pg> <widget-layout data-id="categories" data-is-collapsed="false" class="pb-4 card-base onload-animation" style="animation-delay: 150ms; --collapsedHeight: 7.5rem;" data-astro-cid-ucso7hve="true"> <div class="font-bold transition text-lg text-neutral-900 dark:text-neutral-100 relative ml-8 mt-4 mb-2
before:w-1 before:h-4 before:rounded-md before:bg-[var(--primary)]
before:absolute before:left-[-16px] before:top-[5.5px]" data-astro-cid-ucso7hve style="--collapsedHeight: 7.5rem;">分类</div> <div id="categories" class="collapse-wrapper px-4 overflow-hidden" data-astro-cid-ucso7hve style="--collapsedHeight: 7.5rem;"> <a href="/blog/archive/?category=Examples" aria-label="View all posts in the Examples category"> <button class="
w-full
h-10
rounded-lg
bg-none
hover:bg-[var(--btn-plain-bg-hover)]
active:bg-[var(--btn-plain-bg-active)]
transition-all
pl-2
hover:pl-3
text-neutral-700
hover:text-[var(--primary)]
dark:text-neutral-300
dark:hover:text-[var(--primary)]
"> <div class="flex items-center justify-between relative mr-2"> <div class="overflow-hidden text-left whitespace-nowrap overflow-ellipsis "> Examples </div> <div class="transition px-2 h-7 ml-4 min-w-[2rem] rounded-lg text-sm font-bold
text-[var(--btn-content)] dark:text-[var(--deep-text)]
bg-[oklch(0.95_0.025_var(--hue))] dark:bg-[var(--primary)]
flex items-center justify-center"> 4 </div> </div> </button> </a> </div> </widget-layout> <widget-layout data-id="tags" data-is-collapsed="false" class="pb-4 card-base onload-animation" style="animation-delay: 200ms; --collapsedHeight: 7.5rem;" data-astro-cid-ucso7hve="true"> <div class="font-bold transition text-lg text-neutral-900 dark:text-neutral-100 relative ml-8 mt-4 mb-2
before:w-1 before:h-4 before:rounded-md before:bg-[var(--primary)]
before:absolute before:left-[-16px] before:top-[5.5px]" data-astro-cid-ucso7hve style="--collapsedHeight: 7.5rem;">标签</div> <div id="tags" class="collapse-wrapper px-4 overflow-hidden" data-astro-cid-ucso7hve style="--collapsedHeight: 7.5rem;"> <div class="flex gap-2 flex-wrap"> <a href="/blog/archive/?tag=Blogging" aria-label="View all posts with the Blogging tag" class="btn-regular h-8 text-sm px-3 rounded-lg"> Blogging </a><a href="/blog/archive/?tag=Example" aria-label="View all posts with the Example tag" class="btn-regular h-8 text-sm px-3 rounded-lg"> Example </a><a href="/blog/archive/?tag=Markdown" aria-label="View all posts with the Markdown tag" class="btn-regular h-8 text-sm px-3 rounded-lg"> Markdown </a><a href="/blog/archive/?tag=Mermaid" aria-label="View all posts with the Mermaid tag" class="btn-regular h-8 text-sm px-3 rounded-lg"> Mermaid </a><a href="/blog/archive/?tag=Video" aria-label="View all posts with the Video tag" class="btn-regular h-8 text-sm px-3 rounded-lg"> Video </a> </div> </div> </widget-layout> </div> </div> <!-- 响应式样式和JavaScript --> <script type="module" src="/blog/_astro/SideBar.astro_astro_type_script_index_0_lang.Fy0FqEdY.js"></script> <main id="swup-container" class="transition-swup-fade overflow-hidden w-full col-span-2 md:col-start-2 md:col-end-3 lg:col-start-2 lg:col-end-3" data-astro-cid-haiuh7kc> <div id="content-wrapper" class="onload-animation" data-astro-cid-haiuh7kc> <!-- the overflow-hidden here prevent long text break the layout--> <!-- make id different from windows.swup global property --> <div class="flex w-full rounded-[var(--radius-large)] overflow-hidden relative min-h-32" data-astro-cid-pyfuvgrx> <div class="card-base z-10 px-4 py-4 md:px-6 md:py-5 relative w-full" data-astro-cid-pyfuvgrx> <div class="relative max-w-4xl" data-astro-cid-pyfuvgrx> <!-- 页面头部 --> <div class="moments-header mb-6" data-astro-cid-pyfuvgrx> <div class="header-content" data-astro-cid-pyfuvgrx> <div class="header-info" data-astro-cid-pyfuvgrx> <h1 class="moments-title text-xl md:text-2xl lg:text-3xl font-bold text-90 mb-1" data-astro-cid-pyfuvgrx>日记</h1> <p class="moments-subtitle text-sm md:text-base lg:text-lg text-75" data-astro-cid-pyfuvgrx>随时随地,分享生活</p> </div> <div class="header-stats" data-astro-cid-pyfuvgrx> <div class="stat-item text-center" data-astro-cid-pyfuvgrx> <span class="stat-number text-lg md:text-xl lg:text-2xl font-bold text-[var(--primary)]" data-astro-cid-pyfuvgrx>1</span> <span class="stat-label text-xs md:text-sm lg:text-base text-75" data-astro-cid-pyfuvgrx>条短文</span> </div> </div> </div> </div> <!-- 短文列表 --> <div class="moments-timeline" data-astro-cid-pyfuvgrx> <div class="timeline-list space-y-4" data-astro-cid-pyfuvgrx> <div class="moment-item card-base p-4 md:p-6 lg:p-8 hover:shadow-lg transition-all" key="1" data-astro-cid-pyfuvgrx> <div class="moment-content" data-astro-cid-pyfuvgrx> <p class="moment-text text-sm md:text-base lg:text-lg text-90 leading-relaxed mb-3 md:mb-4" data-astro-cid-pyfuvgrx>樱花下落速度的每秒五厘米!</p> <div class="moment-images grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-2 md:gap-3 lg:gap-4 mb-3 md:mb-4" data-astro-cid-pyfuvgrx> <div class="image-item relative rounded-md overflow-hidden aspect-square cursor-pointer hover:scale-105 transition-transform" key="0" data-astro-cid-pyfuvgrx> <div class="w-full h-full object-cover overflow-hidden relative"> <div class="transition absolute inset-0 dark:bg-black/10 bg-opacity-50 pointer-events-none"></div> <img src="/blog/images/diary/sakura.jpg" alt="图片" class="w-full h-full object-cover" style="object-position: center"> </div> </div><div class="image-item relative rounded-md overflow-hidden aspect-square cursor-pointer hover:scale-105 transition-transform" key="1" data-astro-cid-pyfuvgrx> <div class="w-full h-full object-cover overflow-hidden relative"> <div class="transition absolute inset-0 dark:bg-black/10 bg-opacity-50 pointer-events-none"></div> <img src="/blog/images/diary/1.jpg" alt="图片" class="w-full h-full object-cover" style="object-position: center"> </div> </div> </div> </div> <hr class="moment-divider border-t border-[var(--line-divider)] my-3 md:my-4" data-astro-cid-pyfuvgrx> <div class="moment-footer flex justify-between items-center" data-astro-cid-pyfuvgrx> <div class="moment-time flex items-center gap-1.5 text-75 text-xs md:text-sm lg:text-base" data-astro-cid-pyfuvgrx> <i class="time-icon text-xs md:text-sm" data-astro-cid-pyfuvgrx>🕐</i> <time datetime="2025-01-15T10:30:00Z" data-astro-cid-pyfuvgrx> 450天前 </time> </div> <div class="moment-actions" data-astro-cid-pyfuvgrx> <button class="action-btn reply-btn p-1.5 md:p-2 lg:p-3 rounded-md hover:bg-[var(--btn-regular-bg)] transition-colors" title="回复" data-astro-cid-pyfuvgrx> <i class="reply-icon text-xs md:text-sm lg:text-base" data-astro-cid-pyfuvgrx>💬</i> </button> </div> </div> </div> </div> </div> <!-- 底部提示 --> <div class="moments-tips text-center mt-6 md:mt-8 lg:mt-10 text-75 text-xs md:text-sm lg:text-base italic" data-astro-cid-pyfuvgrx> 只展示最近30条日记 </div> </div> </div> </div> <div class="footer col-span-2 onload-animation hidden lg:block" data-astro-cid-haiuh7kc> <!--<div class="border-t border-[var(&#45;&#45;primary)] mx-16 border-dashed py-8 max-w-[var(&#45;&#45;page-width)] flex flex-col items-center justify-center px-6">--><div class="transition border-t border-black/10 dark:border-white/15 my-10 border-dashed mx-32"></div> <!--<div class="transition bg-[oklch(92%_0.01_var(&#45;&#45;hue))] dark:bg-black rounded-2xl py-8 mt-4 mb-8 flex flex-col items-center justify-center px-6">--> <div class="transition border-dashed border-[oklch(85%_0.01_var(--hue))] dark:border-white/15 rounded-2xl mb-12 flex flex-col items-center justify-center px-6"> <div class="transition text-50 text-sm text-center">
&copy; <span id="copyright-year">2026</span> FutureOSS. All Rights Reserved. /
<a class="transition link text-[var(--primary)] font-medium" target="_blank" href="/blog/rss.xml">RSS</a> /
<a class="transition link text-[var(--primary)] font-medium" target="_blank" href="/blog/sitemap-index.xml">Sitemap</a><br>
Powered by
<a class="transition link text-[var(--primary)] font-medium" target="_blank" href="https://astro.build">Astro</a> &
<a class="transition link text-[var(--primary)] font-medium" target="_blank" href="https://github.com/matsuzaka-yuki/mizuki">Mizuki</a>&nbsp; Version <a class="transition link text-[var(--primary)] font-medium" target="_blank" href="https://github.com/matsuzaka-yuki/mizuki">4.0<br> </a></div><a class="transition link text-[var(--primary)] font-medium" target="_blank" href="https://github.com/matsuzaka-yuki/mizuki"></a></div> </div> </div> </main> <div class="footer col-span-2 onload-animation block lg:hidden" data-astro-cid-haiuh7kc> <!--<div class="border-t border-[var(&#45;&#45;primary)] mx-16 border-dashed py-8 max-w-[var(&#45;&#45;page-width)] flex flex-col items-center justify-center px-6">--><div class="transition border-t border-black/10 dark:border-white/15 my-10 border-dashed mx-32"></div> <!--<div class="transition bg-[oklch(92%_0.01_var(&#45;&#45;hue))] dark:bg-black rounded-2xl py-8 mt-4 mb-8 flex flex-col items-center justify-center px-6">--> <div class="transition border-dashed border-[oklch(85%_0.01_var(--hue))] dark:border-white/15 rounded-2xl mb-12 flex flex-col items-center justify-center px-6"> <div class="transition text-50 text-sm text-center">
&copy; <span id="copyright-year">2026</span> FutureOSS. All Rights Reserved. /
<a class="transition link text-[var(--primary)] font-medium" target="_blank" href="/blog/rss.xml">RSS</a> /
<a class="transition link text-[var(--primary)] font-medium" target="_blank" href="/blog/sitemap-index.xml">Sitemap</a><br>
Powered by
<a class="transition link text-[var(--primary)] font-medium" target="_blank" href="https://astro.build">Astro</a> &
<a class="transition link text-[var(--primary)] font-medium" target="_blank" href="https://github.com/matsuzaka-yuki/mizuki">Mizuki</a>&nbsp; Version <a class="transition link text-[var(--primary)] font-medium" target="_blank" href="https://github.com/matsuzaka-yuki/mizuki">4.0<br> </a></div><a class="transition link text-[var(--primary)] font-medium" target="_blank" href="https://github.com/matsuzaka-yuki/mizuki"></a></div> </div> </div> <!-- There can't be a filter on parent element, or it will break `fixed` --><div class="back-to-top-wrapper hidden lg:block" data-astro-cid-eymb5ayk> <div id="back-to-top-btn" class="back-to-top-btn hide flex items-center rounded-2xl overflow-hidden transition" onclick="backToTop()" data-astro-cid-eymb5ayk> <button aria-label="Back to Top" class="btn-card h-[3.75rem] w-[3.75rem]" data-astro-cid-eymb5ayk> <svg width="1em" height="1em" class="mx-auto" data-astro-cid-eymb5ayk="true" data-icon="material-symbols:keyboard-arrow-up-rounded"> <symbol id="ai:material-symbols:keyboard-arrow-up-rounded" viewBox="0 0 24 24"><path fill="currentColor" d="m12 10.8l-3.9 3.9q-.275.275-.7.275t-.7-.275t-.275-.7t.275-.7l4.6-4.6q.3-.3.7-.3t.7.3l4.6 4.6q.275.275.275.7t-.275.7t-.7.275t-.7-.275z"/></symbol><use href="#ai:material-symbols:keyboard-arrow-up-rounded"></use> </svg> </button> </div> </div> <script>
function backToTop() {
// 直接使用原生滚动避免OverlayScrollbars冲突
window.scroll({ top: 0, behavior: 'smooth' });
}
</script> </div> </div> <div class="absolute w-full z-0 hidden xl:block" data-astro-cid-haiuh7kc> <div class="relative max-w-[var(--page-width)] mx-auto" data-astro-cid-haiuh7kc> <!-- TOC component --> <div id="toc-wrapper" class="hidden lg:block transition absolute top-0 w-[var(--toc-width)] items-center -right-[var(--toc-width)] toc-hide" data-astro-cid-haiuh7kc> <div id="toc-inner-wrapper" class="fixed top-14 w-[var(--toc-width)] h-[calc(100vh_-_20rem)] overflow-y-scroll overflow-x-hidden hide-scrollbar" data-astro-cid-haiuh7kc> <div id="toc" class="w-full h-full transition-swup-fade " data-astro-cid-haiuh7kc> <div class="h-8 w-full" data-astro-cid-haiuh7kc></div> <table-of-contents id="toc" class="group"> <!-- TOC内容将由JavaScript动态生成 --> </table-of-contents> <script type="module" src="/blog/_astro/TOC.astro_astro_type_script_index_0_lang.CGKGUlSz.js"></script> <div class="h-8 w-full" data-astro-cid-haiuh7kc></div> </div> </div> </div> <!-- #toc needs to exist for Swup to work normally --> </div> </div> <!-- Music Player --> <script>(()=>{var e=async t=>{await(await t())()};(self.Astro||(self.Astro={})).load=e;window.dispatchEvent(new Event("astro:load"));})();</script><astro-island uid="Z1HtLzS" component-url="/blog/_astro/MusicPlayer.Ckr8jCXu.js" component-export="default" renderer-url="/blog/_astro/client.svelte.9vP7DkWT.js" props="{&quot;data-astro-cid-sckkx6r4&quot;:[0,true]}" ssr client="load" opts="{&quot;name&quot;:&quot;MusicPlayer&quot;,&quot;value&quot;:true}" await-children><!--[--><!--[-1--><!--]--><!--]--><!--astro:end--></astro-island> <!-- increase the page height during page transition to prevent the scrolling animation from jumping --> <div id="page-height-extend" class="hidden h-[300vh]" data-astro-cid-sckkx6r4 style="--bannerOffset: 15vh;--banner-height-home: 65vh;--banner-height: 35vh;--configHue: 210;--page-width: 90rem;"></div> <!-- Sakura Effect --> <script>(function(){const sakuraConfig = {"enable":true,"sakuraNum":21,"limitTimes":-1,"size":{"min":0.5,"max":1.1},"speed":{"horizontal":{"min":-1.7,"max":-1.2},"vertical":{"min":1.5,"max":2.2},"rotation":0.03},"zIndex":100};
// 樱花对象类
class Sakura {
constructor(x, y, s, r, fn, idx, img, limitArray, config) {
this.x = x;
this.y = y;
this.s = s;
this.r = r;
this.fn = fn;
this.idx = idx;
this.img = img;
this.limitArray = limitArray;
this.config = config;
}
draw(cxt) {
cxt.save();
cxt.translate(this.x, this.y);
cxt.rotate(this.r);
cxt.drawImage(this.img, 0, 0, 40 * this.s, 40 * this.s);
cxt.restore();
}
update() {
this.x = this.fn.x(this.x, this.y);
this.y = this.fn.y(this.y, this.y);
this.r = this.fn.r(this.r);
// 如果樱花越界,重新调整位置
if (
this.x > window.innerWidth ||
this.x < 0 ||
this.y > window.innerHeight ||
this.y < 0
) {
// 如果樱花不做限制
if (this.limitArray[this.idx] === -1) {
this.resetPosition();
}
// 否则樱花有限制
else {
if (this.limitArray[this.idx] > 0) {
this.resetPosition();
this.limitArray[this.idx]--;
}
}
}
}
resetPosition() {
this.r = getRandom('fnr', this.config);
if (Math.random() > 0.4) {
this.x = getRandom('x', this.config);
this.y = 0;
this.s = getRandom('s', this.config);
this.r = getRandom('r', this.config);
} else {
this.x = window.innerWidth;
this.y = getRandom('y', this.config);
this.s = getRandom('s', this.config);
this.r = getRandom('r', this.config);
}
}
}
// 樱花列表类
class SakuraList {
constructor() {
this.list = [];
}
push(sakura) {
this.list.push(sakura);
}
update() {
for (let i = 0, len = this.list.length; i < len; i++) {
this.list[i].update();
}
}
draw(cxt) {
for (let i = 0, len = this.list.length; i < len; i++) {
this.list[i].draw(cxt);
}
}
get(i) {
return this.list[i];
}
size() {
return this.list.length;
}
}
// 获取随机值的函数
function getRandom(option, config) {
let ret;
let random;
switch (option) {
case 'x':
ret = Math.random() * window.innerWidth;
break;
case 'y':
ret = Math.random() * window.innerHeight;
break;
case 's':
ret = config.size.min + Math.random() * (config.size.max - config.size.min);
break;
case 'r':
ret = Math.random() * 6;
break;
case 'fnx':
random = config.speed.horizontal.min + Math.random() * (config.speed.horizontal.max - config.speed.horizontal.min);
ret = function (x, y) {
return x + random;
};
break;
case 'fny':
random = config.speed.vertical.min + Math.random() * (config.speed.vertical.max - config.speed.vertical.min);
ret = function (x, y) {
return y + random;
};
break;
case 'fnr':
ret = function (r) {
return r + config.speed.rotation;
};
break;
}
return ret;
}
// 樱花管理器类
class SakuraManager {
constructor(config) {
this.config = config;
this.canvas = null;
this.ctx = null;
this.sakuraList = null;
this.animationId = null;
this.img = null;
this.isRunning = false;
}
// 初始化樱花特效
async init() {
if (!this.config.enable || this.isRunning) {
return;
}
// 创建图片对象
this.img = new Image();
this.img.src = '/sakura.png'; // 使用樱花图片
// 等待图片加载完成
await new Promise((resolve, reject) => {
if (this.img) {
this.img.onload = () => resolve();
this.img.onerror = () => reject(new Error('Failed to load sakura image'));
}
});
this.createCanvas();
this.createSakuraList();
this.startAnimation();
this.isRunning = true;
}
// 创建画布
createCanvas() {
this.canvas = document.createElement('canvas');
this.canvas.height = window.innerHeight;
this.canvas.width = window.innerWidth;
this.canvas.setAttribute('style', `position: fixed; left: 0; top: 0; pointer-events: none; z-index: ${this.config.zIndex};`);
this.canvas.setAttribute('id', 'canvas_sakura');
document.body.appendChild(this.canvas);
this.ctx = this.canvas.getContext('2d');
// 监听窗口大小变化
window.addEventListener('resize', this.handleResize.bind(this));
}
// 创建樱花列表
createSakuraList() {
if (!this.img || !this.ctx) return;
this.sakuraList = new SakuraList();
const limitArray = new Array(this.config.sakuraNum).fill(this.config.limitTimes);
for (let i = 0; i < this.config.sakuraNum; i++) {
const randomX = getRandom('x', this.config);
const randomY = getRandom('y', this.config);
const randomR = getRandom('r', this.config);
const randomS = getRandom('s', this.config);
const randomFnx = getRandom('fnx', this.config);
const randomFny = getRandom('fny', this.config);
const randomFnR = getRandom('fnr', this.config);
const sakura = new Sakura(
randomX,
randomY,
randomS,
randomR,
{
x: randomFnx,
y: randomFny,
r: randomFnR,
},
i,
this.img,
limitArray,
this.config
);
sakura.draw(this.ctx);
this.sakuraList.push(sakura);
}
}
// 开始动画
startAnimation() {
if (!this.ctx || !this.canvas || !this.sakuraList) return;
const animate = () => {
if (!this.ctx || !this.canvas || !this.sakuraList) return;
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.sakuraList.update();
this.sakuraList.draw(this.ctx);
this.animationId = requestAnimationFrame(animate);
};
this.animationId = requestAnimationFrame(animate);
}
// 处理窗口大小变化
handleResize() {
if (this.canvas) {
this.canvas.width = window.innerWidth;
this.canvas.height = window.innerHeight;
}
}
// 停止樱花特效
stop() {
if (this.animationId) {
cancelAnimationFrame(this.animationId);
this.animationId = null;
}
if (this.canvas) {
document.body.removeChild(this.canvas);
this.canvas = null;
}
window.removeEventListener('resize', this.handleResize.bind(this));
this.isRunning = false;
}
// 切换樱花特效
toggle() {
if (this.isRunning) {
this.stop();
} else {
this.init();
}
}
// 更新配置
updateConfig(newConfig) {
const wasRunning = this.isRunning;
if (wasRunning) {
this.stop();
}
this.config = newConfig;
if (wasRunning && newConfig.enable) {
this.init();
}
}
// 获取运行状态
getIsRunning() {
return this.isRunning;
}
}
// 创建全局樱花管理器实例
let globalSakuraManager = null;
// 初始化樱花特效
function initSakura(config) {
if (globalSakuraManager) {
globalSakuraManager.updateConfig(config);
} else {
globalSakuraManager = new SakuraManager(config);
if (config.enable) {
globalSakuraManager.init();
}
}
}
// 樱花特效初始化
(function() {
// 全局标记,确保樱花特效只初始化一次
if (window.sakuraInitialized) {
return;
}
// 初始化樱花特效的函数
const setupSakura = () => {
if (sakuraConfig.enable && !window.sakuraInitialized) {
initSakura(sakuraConfig);
window.sakuraInitialized = true;
}
};
// 页面加载完成后初始化樱花特效
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', setupSakura);
} else {
setupSakura();
}
})();
})();</script> <!-- Translate.js integration - lazy loading --> <script type="module">class d{constructor(){this.isInitialized=!1,this.clickHandler=null}init(){console.log("GalleryManager init called");const t=document.querySelectorAll(".gallery-group");if(console.log("Found gallery groups:",t.length),t.length===0){console.log("No gallery groups found, skipping initialization");return}this.cleanup(),console.log("Adding click event listener to document"),this.clickHandler=e=>this.handleClick(e),document.addEventListener("click",this.clickHandler),this.isInitialized=!0}cleanup(){this.clickHandler&&(document.removeEventListener("click",this.clickHandler),this.clickHandler=null),this.isInitialized=!1}handleClick(t){console.log("GalleryManager handleClick triggered",t.target);const e=t.target.closest(".gallery-header");if(e){console.log("Gallery header clicked",e),t.preventDefault(),t.stopPropagation(),this.toggleGallery(e);return}const n=t.target.closest(".gallery-image");if(n){console.log("Gallery image clicked",n),t.preventDefault(),t.stopPropagation();const l=n.getAttribute("data-image");l&&this.showImageModal(l)}}toggleGallery(t){console.log("toggleGallery called",t);const e=t.closest(".gallery-group"),n=e.querySelector(".gallery-content"),l=t.querySelector(".expand-icon");console.log("Found elements:",{group:e,content:n,icon:l}),console.log("Content hidden?",n?.classList.contains("hidden")),n&&n.classList.contains("hidden")?(console.log("Expanding gallery"),n.classList.remove("hidden"),e.classList.add("expanded"),l&&(l.style.transform="rotate(180deg)")):n?(console.log("Collapsing gallery"),n.classList.add("hidden"),e.classList.remove("expanded"),l&&(l.style.transform="rotate(0deg)")):console.error("Gallery content not found!")}showImageModal(t){const e=document.createElement("div");e.className="gallery-modal fixed inset-0 bg-black bg-opacity-90 flex items-center justify-center z-50 p-4",e.style.opacity="0",e.style.transition="opacity 0.3s ease";const n=document.createElement("div");n.className="relative max-w-full max-h-full";const l=document.createElement("img");l.src=t,l.alt="查看图片",l.className="max-w-full max-h-full object-contain rounded-lg",l.style.transform="scale(0.9)",l.style.transition="transform 0.3s ease";const o=document.createElement("button");o.className="close-btn absolute top-4 right-4 bg-black bg-opacity-50 text-white p-2 rounded-full hover:bg-opacity-70 transition-colors",o.innerHTML=`
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
</svg>
`,n.appendChild(l),n.appendChild(o),e.appendChild(n),document.body.appendChild(e),requestAnimationFrame(()=>{e.style.opacity="1",l.style.transform="scale(1)"});const a=()=>{e.style.opacity="0",l.style.transform="scale(0.9)",setTimeout(()=>{document.body.contains(e)&&document.body.removeChild(e),document.removeEventListener("keydown",c)},300)},c=s=>{s.key==="Escape"&&a()};o.addEventListener("click",a),e.addEventListener("click",s=>{s.target===e&&a()}),document.addEventListener("keydown",c)}}window.galleryManager=new d;const r=()=>{window.galleryManager.init()},i=()=>{r(),window.swup&&(window.swup.hooks.on("page:view",()=>{setTimeout(r,50)}),window.swup.hooks.on("content:replace",()=>{window.galleryManager.cleanup()},{before:!0}))};document.readyState==="loading"?document.addEventListener("DOMContentLoaded",i):i();window.swup||document.addEventListener("swup:enable",i);</script> </body> </html> <script type="module" src="/blog/_astro/Layout.astro_astro_type_script_index_1_lang.BilWV2C2.js"></script> <script type="module" src="/blog/_astro/Layout.astro_astro_type_script_index_2_lang.BTdkr2U1.js"></script> <script type="module">window.loadTranslateScript=function(){return window.translate||document.getElementById("translate-script")?Promise.resolve():new Promise((n,t)=>{const e=document.createElement("script");e.src="/translate.js",e.id="translate-script",e.async=!0,e.onload=()=>{typeof window.translate<"u"?n():t(new Error("translate.js loaded but window.translate not available"))},e.onerror=t,document.head.appendChild(e)})};</script> <script type="module">document.addEventListener("DOMContentLoaded",function(){document.querySelectorAll(".reply-btn").forEach(e=>{e.addEventListener("click",function(){console.log("回复功能待实现")})})});</script>