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

1893 lines
149 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/timeline/"><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/timeline/"><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">
<link rel="stylesheet" href="/blog/_astro/_page_.BQ6XwRvy.css">
<style>.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_.QUsIdifj.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-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}}
.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}
</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 --> <!-- Iconify图标库加载器 --><script>(function(){const preloadIcons = ["material-symbols:work","material-symbols:code","material-symbols:work","material-symbols:verified","material-symbols:school","material-symbols:work","material-symbols:emoji-events","material-symbols:code"];
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 class="flex w-full rounded-[var(--radius-large)] overflow-hidden relative min-h-32" data-astro-cid-qlh7ngej> <div class="card-base z-10 px-9 py-6 relative w-full" data-astro-cid-qlh7ngej> <!-- 页面标题 --> <div class="flex flex-col items-start justify-center mb-8" data-astro-cid-qlh7ngej> <h1 class="text-4xl font-bold text-black/90 dark:text-white/90 mb-2" data-astro-cid-qlh7ngej> 时间线 </h1> <p class="text-lg text-black/60 dark:text-white/60" data-astro-cid-qlh7ngej> 我的成长历程和重要里程碑 </p> </div> <!-- 统计信息 --> <div class="grid grid-cols-1 md:grid-cols-4 gap-4 mb-8" data-astro-cid-qlh7ngej> <div class="bg-gradient-to-br from-blue-50 to-blue-100 dark:from-blue-900/20 dark:to-blue-800/20 rounded-lg p-4" data-astro-cid-qlh7ngej> <div class="text-2xl font-bold text-blue-600 dark:text-blue-400" data-astro-cid-qlh7ngej>8</div> <div class="text-sm text-blue-600/70 dark:text-blue-400/70" data-astro-cid-qlh7ngej>总计</div> </div> <div class="bg-gradient-to-br from-green-50 to-green-100 dark:from-green-900/20 dark:to-green-800/20 rounded-lg p-4" data-astro-cid-qlh7ngej> <div class="text-2xl font-bold text-green-600 dark:text-green-400" data-astro-cid-qlh7ngej>3</div> <div class="text-sm text-green-600/70 dark:text-green-400/70" data-astro-cid-qlh7ngej>工作经历</div> </div> <div class="bg-gradient-to-br from-purple-50 to-purple-100 dark:from-purple-900/20 dark:to-purple-800/20 rounded-lg p-4" data-astro-cid-qlh7ngej> <div class="text-2xl font-bold text-purple-600 dark:text-purple-400" data-astro-cid-qlh7ngej>1</div> <div class="text-sm text-purple-600/70 dark:text-purple-400/70" data-astro-cid-qlh7ngej>项目数</div> </div> <div class="bg-gradient-to-br from-orange-50 to-orange-100 dark:from-orange-900/20 dark:to-orange-800/20 rounded-lg p-4" data-astro-cid-qlh7ngej> <div class="text-2xl font-bold text-orange-600 dark:text-orange-400" data-astro-cid-qlh7ngej> 4+
</div> <div class="text-sm text-orange-600/70 dark:text-orange-400/70" data-astro-cid-qlh7ngej>工作经验</div> </div> </div> <!-- 当前进行中的项目 --> <div class="mb-8" data-astro-cid-qlh7ngej> <h2 class="text-2xl font-bold text-black/90 dark:text-white/90 mb-4" data-astro-cid-qlh7ngej> 当前状态 </h2> <div class="grid grid-cols-1 md:grid-cols-2 gap-6" data-astro-cid-qlh7ngej> <div class="bg-white dark:bg-gray-800 rounded-lg border border-black/10 dark:border-white/10 p-6 hover:shadow-lg transition-shadow duration-300" data-astro-cid-qlh7ngej> <div class="flex items-start gap-4" data-astro-cid-qlh7ngej> <div class="w-12 h-12 rounded-lg flex items-center justify-center" style="background-color: #2563EB20" data-astro-cid-qlh7ngej> <span class="inline-flex items-center justify-center text-base text-2xl" style="color: #2563EB;" data-icon-container="icon-o2dscfp7p" data-astro-cid-qpfihmil> <!-- 加载状态指示器 --> <span class="icon-loading animate-pulse opacity-50" data-loading-indicator data-astro-cid-qpfihmil></span> <!-- 实际图标 --> <iconify-icon icon="material-symbols:work" class="icon-content opacity-0 transition-opacity duration-200" data-icon-element="true" loading="eager" data-astro-cid-qpfihmil="true"></iconify-icon> </span> <script>(function(){const iconId = "icon-o2dscfp7p";
const icon = "material-symbols:work";
// 图标加载和显示逻辑
(function() {
const container = document.querySelector(`[data-icon-container="${iconId}"]`);
if (!container) return;
const loadingIndicator = container.querySelector('[data-loading-indicator]');
const iconElement = container.querySelector('[data-icon-element]');
if (!loadingIndicator || !iconElement) return;
// 检查图标是否已经加载
function checkIconLoaded() {
// 检查iconify-icon元素是否已经渲染
const hasContent = iconElement.shadowRoot &&
iconElement.shadowRoot.children.length > 0;
if (hasContent) {
showIcon();
return true;
}
return false;
}
// 显示图标,隐藏加载指示器
function showIcon() {
loadingIndicator.style.display = 'none';
iconElement.classList.remove('opacity-0');
iconElement.classList.add('opacity-100');
}
// 显示加载指示器,隐藏图标
function showLoading() {
loadingIndicator.style.display = 'inline-flex';
iconElement.classList.remove('opacity-100');
iconElement.classList.add('opacity-0');
}
// 初始状态
showLoading();
// 监听图标加载事件
iconElement.addEventListener('load', () => {
showIcon();
});
// 监听图标加载错误
iconElement.addEventListener('error', () => {
// 保持显示fallback
console.warn(`Failed to load icon: ${icon}`);
});
// 使用MutationObserver监听shadow DOM变化
if (window.MutationObserver) {
const observer = new MutationObserver(() => {
if (checkIconLoaded()) {
observer.disconnect();
}
});
// 监听iconify-icon元素的变化
observer.observe(iconElement, {
childList: true,
subtree: true,
attributes: true
});
// 设置超时,避免无限等待
setTimeout(() => {
observer.disconnect();
if (!checkIconLoaded()) {
console.warn(`Icon load timeout: ${icon}`);
}
}, 5000);
}
// 立即检查一次(可能已经加载完成)
setTimeout(() => {
checkIconLoaded();
}, 100);
})();
})();</script> </div> <div class="flex-1" data-astro-cid-qlh7ngej> <div class="flex items-start justify-between mb-2" data-astro-cid-qlh7ngej> <h3 class="text-xl font-semibold text-black/90 dark:text-white/90" data-astro-cid-qlh7ngej> 全栈开发工程师 </h3> <span class="px-2 py-1 text-xs bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400 rounded-full" data-astro-cid-qlh7ngej> 当前状态 </span> </div> <div class="text-sm text-black/70 dark:text-white/70 mb-1" data-astro-cid-qlh7ngej> TechCorp Inc. • Senior Full Stack Developer </div> <div class="text-sm text-black/60 dark:text-white/60 mb-2" data-astro-cid-qlh7ngej>
📍 北京 </div> <p class="text-black/60 dark:text-white/60 mb-3 text-sm" data-astro-cid-qlh7ngej> 负责Web应用的前后端开发参与产品架构设计和技术选型带领团队完成多个重要项目。 </p> <div class="text-sm text-black/70 dark:text-white/70 mb-3" data-astro-cid-qlh7ngej> 2023年6月 - 至今 (2 年 11 个月)
</div> <div class="flex flex-wrap gap-1 mb-3" data-astro-cid-qlh7ngej> <span class="px-2 py-1 text-xs bg-indigo-100 text-indigo-700 dark:bg-indigo-900/30 dark:text-indigo-400 rounded" data-astro-cid-qlh7ngej> React </span><span class="px-2 py-1 text-xs bg-indigo-100 text-indigo-700 dark:bg-indigo-900/30 dark:text-indigo-400 rounded" data-astro-cid-qlh7ngej> Node.js </span><span class="px-2 py-1 text-xs bg-indigo-100 text-indigo-700 dark:bg-indigo-900/30 dark:text-indigo-400 rounded" data-astro-cid-qlh7ngej> TypeScript </span><span class="px-2 py-1 text-xs bg-indigo-100 text-indigo-700 dark:bg-indigo-900/30 dark:text-indigo-400 rounded" data-astro-cid-qlh7ngej> PostgreSQL </span><span class="px-2 py-1 text-xs bg-indigo-100 text-indigo-700 dark:bg-indigo-900/30 dark:text-indigo-400 rounded" data-astro-cid-qlh7ngej> AWS </span> </div> </div> </div> </div><div class="bg-white dark:bg-gray-800 rounded-lg border border-black/10 dark:border-white/10 p-6 hover:shadow-lg transition-shadow duration-300" data-astro-cid-qlh7ngej> <div class="flex items-start gap-4" data-astro-cid-qlh7ngej> <div class="w-12 h-12 rounded-lg flex items-center justify-center" style="background-color: #7C3AED20" data-astro-cid-qlh7ngej> <span class="inline-flex items-center justify-center text-base text-2xl" style="color: #7C3AED;" data-icon-container="icon-lf41ifgsv" data-astro-cid-qpfihmil> <!-- 加载状态指示器 --> <span class="icon-loading animate-pulse opacity-50" data-loading-indicator data-astro-cid-qpfihmil> R </span> <!-- 实际图标 --> <iconify-icon icon="material-symbols:verified" class="icon-content opacity-0 transition-opacity duration-200" data-icon-element="true" loading="eager" data-astro-cid-qpfihmil="true"></iconify-icon> </span> <script>(function(){const iconId = "icon-lf41ifgsv";
const icon = "material-symbols:verified";
// 图标加载和显示逻辑
(function() {
const container = document.querySelector(`[data-icon-container="${iconId}"]`);
if (!container) return;
const loadingIndicator = container.querySelector('[data-loading-indicator]');
const iconElement = container.querySelector('[data-icon-element]');
if (!loadingIndicator || !iconElement) return;
// 检查图标是否已经加载
function checkIconLoaded() {
// 检查iconify-icon元素是否已经渲染
const hasContent = iconElement.shadowRoot &&
iconElement.shadowRoot.children.length > 0;
if (hasContent) {
showIcon();
return true;
}
return false;
}
// 显示图标,隐藏加载指示器
function showIcon() {
loadingIndicator.style.display = 'none';
iconElement.classList.remove('opacity-0');
iconElement.classList.add('opacity-100');
}
// 显示加载指示器,隐藏图标
function showLoading() {
loadingIndicator.style.display = 'inline-flex';
iconElement.classList.remove('opacity-100');
iconElement.classList.add('opacity-0');
}
// 初始状态
showLoading();
// 监听图标加载事件
iconElement.addEventListener('load', () => {
showIcon();
});
// 监听图标加载错误
iconElement.addEventListener('error', () => {
// 保持显示fallback
console.warn(`Failed to load icon: ${icon}`);
});
// 使用MutationObserver监听shadow DOM变化
if (window.MutationObserver) {
const observer = new MutationObserver(() => {
if (checkIconLoaded()) {
observer.disconnect();
}
});
// 监听iconify-icon元素的变化
observer.observe(iconElement, {
childList: true,
subtree: true,
attributes: true
});
// 设置超时,避免无限等待
setTimeout(() => {
observer.disconnect();
if (!checkIconLoaded()) {
console.warn(`Icon load timeout: ${icon}`);
}
}, 5000);
}
// 立即检查一次(可能已经加载完成)
setTimeout(() => {
checkIconLoaded();
}, 100);
})();
})();</script> </div> <div class="flex-1" data-astro-cid-qlh7ngej> <div class="flex items-start justify-between mb-2" data-astro-cid-qlh7ngej> <h3 class="text-xl font-semibold text-black/90 dark:text-white/90" data-astro-cid-qlh7ngej> React开发者认证 </h3> <span class="px-2 py-1 text-xs bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400 rounded-full" data-astro-cid-qlh7ngej> 当前状态 </span> </div> <div class="text-sm text-black/70 dark:text-white/70 mb-1" data-astro-cid-qlh7ngej> Meta (Facebook) </div> <p class="text-black/60 dark:text-white/60 mb-3 text-sm" data-astro-cid-qlh7ngej> 通过了React官方认证考试证明了在React开发方面的专业能力。 </p> <div class="text-sm text-black/70 dark:text-white/70 mb-3" data-astro-cid-qlh7ngej> 2022年8月 - 至今 (3 年 9 个月)
</div> <div class="flex gap-3" data-astro-cid-qlh7ngej> <a href="https://certificates.example.com/react-cert" target="_blank" rel="noopener noreferrer" class="text-indigo-600 dark:text-indigo-400 hover:underline text-sm font-medium" data-astro-cid-qlh7ngej> Certificate </a> </div> </div> </div> </div><div class="bg-white dark:bg-gray-800 rounded-lg border border-black/10 dark:border-white/10 p-6 hover:shadow-lg transition-shadow duration-300" data-astro-cid-qlh7ngej> <div class="flex items-start gap-4" data-astro-cid-qlh7ngej> <div class="w-12 h-12 rounded-lg flex items-center justify-center" style="background-color: #7C3AED20" data-astro-cid-qlh7ngej> <span class="inline-flex items-center justify-center text-base text-2xl" style="color: #7C3AED;" data-icon-container="icon-ybab0bghj" data-astro-cid-qpfihmil> <!-- 加载状态指示器 --> <span class="icon-loading animate-pulse opacity-50" data-loading-indicator data-astro-cid-qpfihmil></span> <!-- 实际图标 --> <iconify-icon icon="material-symbols:code" class="icon-content opacity-0 transition-opacity duration-200" data-icon-element="true" loading="eager" data-astro-cid-qpfihmil="true"></iconify-icon> </span> <script>(function(){const iconId = "icon-ybab0bghj";
const icon = "material-symbols:code";
// 图标加载和显示逻辑
(function() {
const container = document.querySelector(`[data-icon-container="${iconId}"]`);
if (!container) return;
const loadingIndicator = container.querySelector('[data-loading-indicator]');
const iconElement = container.querySelector('[data-icon-element]');
if (!loadingIndicator || !iconElement) return;
// 检查图标是否已经加载
function checkIconLoaded() {
// 检查iconify-icon元素是否已经渲染
const hasContent = iconElement.shadowRoot &&
iconElement.shadowRoot.children.length > 0;
if (hasContent) {
showIcon();
return true;
}
return false;
}
// 显示图标,隐藏加载指示器
function showIcon() {
loadingIndicator.style.display = 'none';
iconElement.classList.remove('opacity-0');
iconElement.classList.add('opacity-100');
}
// 显示加载指示器,隐藏图标
function showLoading() {
loadingIndicator.style.display = 'inline-flex';
iconElement.classList.remove('opacity-100');
iconElement.classList.add('opacity-0');
}
// 初始状态
showLoading();
// 监听图标加载事件
iconElement.addEventListener('load', () => {
showIcon();
});
// 监听图标加载错误
iconElement.addEventListener('error', () => {
// 保持显示fallback
console.warn(`Failed to load icon: ${icon}`);
});
// 使用MutationObserver监听shadow DOM变化
if (window.MutationObserver) {
const observer = new MutationObserver(() => {
if (checkIconLoaded()) {
observer.disconnect();
}
});
// 监听iconify-icon元素的变化
observer.observe(iconElement, {
childList: true,
subtree: true,
attributes: true
});
// 设置超时,避免无限等待
setTimeout(() => {
observer.disconnect();
if (!checkIconLoaded()) {
console.warn(`Icon load timeout: ${icon}`);
}
}, 5000);
}
// 立即检查一次(可能已经加载完成)
setTimeout(() => {
checkIconLoaded();
}, 100);
})();
})();</script> </div> <div class="flex-1" data-astro-cid-qlh7ngej> <div class="flex items-start justify-between mb-2" data-astro-cid-qlh7ngej> <h3 class="text-xl font-semibold text-black/90 dark:text-white/90" data-astro-cid-qlh7ngej> 开始编程学习 </h3> <span class="px-2 py-1 text-xs bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400 rounded-full" data-astro-cid-qlh7ngej> 当前状态 </span> </div> <p class="text-black/60 dark:text-white/60 mb-3 text-sm" data-astro-cid-qlh7ngej> 第一次接触编程从HTML和CSS开始逐步学习JavaScript和其他技术。 </p> <div class="text-sm text-black/70 dark:text-white/70 mb-3" data-astro-cid-qlh7ngej> 2018年1月 - 至今 (8 年 5 个月)
</div> <div class="flex flex-wrap gap-1 mb-3" data-astro-cid-qlh7ngej> <span class="px-2 py-1 text-xs bg-indigo-100 text-indigo-700 dark:bg-indigo-900/30 dark:text-indigo-400 rounded" data-astro-cid-qlh7ngej> HTML </span><span class="px-2 py-1 text-xs bg-indigo-100 text-indigo-700 dark:bg-indigo-900/30 dark:text-indigo-400 rounded" data-astro-cid-qlh7ngej> CSS </span><span class="px-2 py-1 text-xs bg-indigo-100 text-indigo-700 dark:bg-indigo-900/30 dark:text-indigo-400 rounded" data-astro-cid-qlh7ngej> JavaScript </span> </div> </div> </div> </div> </div> </div> <!-- 时间线 --> <div class="mb-8" data-astro-cid-qlh7ngej> <h2 class="text-2xl font-bold text-black/90 dark:text-white/90 mb-6" data-astro-cid-qlh7ngej> 历史记录 </h2> <div class="relative" data-astro-cid-qlh7ngej> <!-- 时间线主轴 --> <div class="absolute left-6 top-0 bottom-0 w-0.5 bg-gray-200 dark:bg-gray-700" data-astro-cid-qlh7ngej></div> <div class="space-y-8" data-astro-cid-qlh7ngej> <div class="relative flex items-start gap-6" data-astro-cid-qlh7ngej> <!-- 时间线节点 --> <div class="relative z-10 w-12 h-12 rounded-full flex items-center justify-center" style="background-color: #059669" data-astro-cid-qlh7ngej> <span class="inline-flex items-center justify-center text-base text-xl text-white" style="color: white;" data-icon-container="icon-sgjfbtp1x" data-astro-cid-qpfihmil> <!-- 加载状态指示器 --> <span class="icon-loading animate-pulse opacity-50" data-loading-indicator data-astro-cid-qpfihmil> M </span> <!-- 实际图标 --> <iconify-icon icon="material-symbols:code" class="icon-content opacity-0 transition-opacity duration-200" data-icon-element="true" loading="eager" data-astro-cid-qpfihmil="true"></iconify-icon> </span> <script>(function(){const iconId = "icon-sgjfbtp1x";
const icon = "material-symbols:code";
// 图标加载和显示逻辑
(function() {
const container = document.querySelector(`[data-icon-container="${iconId}"]`);
if (!container) return;
const loadingIndicator = container.querySelector('[data-loading-indicator]');
const iconElement = container.querySelector('[data-icon-element]');
if (!loadingIndicator || !iconElement) return;
// 检查图标是否已经加载
function checkIconLoaded() {
// 检查iconify-icon元素是否已经渲染
const hasContent = iconElement.shadowRoot &&
iconElement.shadowRoot.children.length > 0;
if (hasContent) {
showIcon();
return true;
}
return false;
}
// 显示图标,隐藏加载指示器
function showIcon() {
loadingIndicator.style.display = 'none';
iconElement.classList.remove('opacity-0');
iconElement.classList.add('opacity-100');
}
// 显示加载指示器,隐藏图标
function showLoading() {
loadingIndicator.style.display = 'inline-flex';
iconElement.classList.remove('opacity-100');
iconElement.classList.add('opacity-0');
}
// 初始状态
showLoading();
// 监听图标加载事件
iconElement.addEventListener('load', () => {
showIcon();
});
// 监听图标加载错误
iconElement.addEventListener('error', () => {
// 保持显示fallback
console.warn(`Failed to load icon: ${icon}`);
});
// 使用MutationObserver监听shadow DOM变化
if (window.MutationObserver) {
const observer = new MutationObserver(() => {
if (checkIconLoaded()) {
observer.disconnect();
}
});
// 监听iconify-icon元素的变化
observer.observe(iconElement, {
childList: true,
subtree: true,
attributes: true
});
// 设置超时,避免无限等待
setTimeout(() => {
observer.disconnect();
if (!checkIconLoaded()) {
console.warn(`Icon load timeout: ${icon}`);
}
}, 5000);
}
// 立即检查一次(可能已经加载完成)
setTimeout(() => {
checkIconLoaded();
}, 100);
})();
})();</script> </div> <!-- 内容卡片 --> <div class="flex-1 bg-white dark:bg-gray-800 rounded-lg border border-black/10 dark:border-white/10 p-6 hover:shadow-lg transition-shadow duration-300" data-astro-cid-qlh7ngej> <div class="flex items-start justify-between mb-3" data-astro-cid-qlh7ngej> <div data-astro-cid-qlh7ngej> <h3 class="text-xl font-semibold text-black/90 dark:text-white/90 mb-1" data-astro-cid-qlh7ngej> Mizuki博客主题开源项目 </h3> </div> <span class="px-2 py-1 text-xs rounded-full bg-purple-100 text-purple-700 dark:bg-purple-900/30 dark:text-purple-400" data-astro-cid-qlh7ngej> 项目经历 </span> </div> <div class="flex items-center gap-4 mb-3 text-sm text-black/60 dark:text-white/60" data-astro-cid-qlh7ngej> <div data-astro-cid-qlh7ngej> 2024年1月 - 2024年6月 </div> <div data-astro-cid-qlh7ngej></div> <div data-astro-cid-qlh7ngej>6 个月</div> </div> <p class="text-black/70 dark:text-white/70 mb-4" data-astro-cid-qlh7ngej> 基于Astro框架开发的现代化博客主题获得了社区的广泛认可和使用。 </p> <div class="mb-4" data-astro-cid-qlh7ngej> <h4 class="text-sm font-semibold text-black/80 dark:text-white/80 mb-2" data-astro-cid-qlh7ngej> 成就荣誉 </h4> <ul class="space-y-1" data-astro-cid-qlh7ngej> <li class="text-sm text-black/70 dark:text-white/70 flex items-start gap-2" data-astro-cid-qlh7ngej> <span class="text-green-500 mt-1" data-astro-cid-qlh7ngej></span> <span data-astro-cid-qlh7ngej>GitHub获得500+ Stars</span> </li><li class="text-sm text-black/70 dark:text-white/70 flex items-start gap-2" data-astro-cid-qlh7ngej> <span class="text-green-500 mt-1" data-astro-cid-qlh7ngej></span> <span data-astro-cid-qlh7ngej>支持多语言和主题切换</span> </li><li class="text-sm text-black/70 dark:text-white/70 flex items-start gap-2" data-astro-cid-qlh7ngej> <span class="text-green-500 mt-1" data-astro-cid-qlh7ngej></span> <span data-astro-cid-qlh7ngej>完善的文档和示例</span> </li> </ul> </div> <div class="mb-4" data-astro-cid-qlh7ngej> <div class="flex flex-wrap gap-1" data-astro-cid-qlh7ngej> <span class="px-2 py-1 text-xs bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300 rounded" data-astro-cid-qlh7ngej> Astro </span><span class="px-2 py-1 text-xs bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300 rounded" data-astro-cid-qlh7ngej> TypeScript </span><span class="px-2 py-1 text-xs bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300 rounded" data-astro-cid-qlh7ngej> Tailwind CSS </span><span class="px-2 py-1 text-xs bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300 rounded" data-astro-cid-qlh7ngej> Svelte </span> </div> </div> <div class="flex gap-4" data-astro-cid-qlh7ngej> <a href="https://github.com/example/mizuki" target="_blank" rel="noopener noreferrer" class="text-blue-600 dark:text-blue-400 hover:underline text-sm font-medium flex items-center gap-1" data-astro-cid-qlh7ngej> 🔗 GitHub Repository </a><a href="https://mizuki-demo.example.com" target="_blank" rel="noopener noreferrer" class="text-blue-600 dark:text-blue-400 hover:underline text-sm font-medium flex items-center gap-1" data-astro-cid-qlh7ngej> 🌐 Live Demo </a> </div> </div> </div><div class="relative flex items-start gap-6" data-astro-cid-qlh7ngej> <!-- 时间线节点 --> <div class="relative z-10 w-12 h-12 rounded-full flex items-center justify-center" style="background-color: #2563EB" data-astro-cid-qlh7ngej> <span class="inline-flex items-center justify-center text-base text-xl text-white" style="color: white;" data-icon-container="icon-4ebbu7b2p" data-astro-cid-qpfihmil> <!-- 加载状态指示器 --> <span class="icon-loading animate-pulse opacity-50" data-loading-indicator data-astro-cid-qpfihmil></span> <!-- 实际图标 --> <iconify-icon icon="material-symbols:work" class="icon-content opacity-0 transition-opacity duration-200" data-icon-element="true" loading="eager" data-astro-cid-qpfihmil="true"></iconify-icon> </span> <script>(function(){const iconId = "icon-4ebbu7b2p";
const icon = "material-symbols:work";
// 图标加载和显示逻辑
(function() {
const container = document.querySelector(`[data-icon-container="${iconId}"]`);
if (!container) return;
const loadingIndicator = container.querySelector('[data-loading-indicator]');
const iconElement = container.querySelector('[data-icon-element]');
if (!loadingIndicator || !iconElement) return;
// 检查图标是否已经加载
function checkIconLoaded() {
// 检查iconify-icon元素是否已经渲染
const hasContent = iconElement.shadowRoot &&
iconElement.shadowRoot.children.length > 0;
if (hasContent) {
showIcon();
return true;
}
return false;
}
// 显示图标,隐藏加载指示器
function showIcon() {
loadingIndicator.style.display = 'none';
iconElement.classList.remove('opacity-0');
iconElement.classList.add('opacity-100');
}
// 显示加载指示器,隐藏图标
function showLoading() {
loadingIndicator.style.display = 'inline-flex';
iconElement.classList.remove('opacity-100');
iconElement.classList.add('opacity-0');
}
// 初始状态
showLoading();
// 监听图标加载事件
iconElement.addEventListener('load', () => {
showIcon();
});
// 监听图标加载错误
iconElement.addEventListener('error', () => {
// 保持显示fallback
console.warn(`Failed to load icon: ${icon}`);
});
// 使用MutationObserver监听shadow DOM变化
if (window.MutationObserver) {
const observer = new MutationObserver(() => {
if (checkIconLoaded()) {
observer.disconnect();
}
});
// 监听iconify-icon元素的变化
observer.observe(iconElement, {
childList: true,
subtree: true,
attributes: true
});
// 设置超时,避免无限等待
setTimeout(() => {
observer.disconnect();
if (!checkIconLoaded()) {
console.warn(`Icon load timeout: ${icon}`);
}
}, 5000);
}
// 立即检查一次(可能已经加载完成)
setTimeout(() => {
checkIconLoaded();
}, 100);
})();
})();</script> </div> <!-- 内容卡片 --> <div class="flex-1 bg-white dark:bg-gray-800 rounded-lg border border-black/10 dark:border-white/10 p-6 hover:shadow-lg transition-shadow duration-300" data-astro-cid-qlh7ngej> <div class="flex items-start justify-between mb-3" data-astro-cid-qlh7ngej> <div data-astro-cid-qlh7ngej> <h3 class="text-xl font-semibold text-black/90 dark:text-white/90 mb-1" data-astro-cid-qlh7ngej> 全栈开发工程师 </h3> <div class="text-sm text-black/70 dark:text-white/70" data-astro-cid-qlh7ngej> TechCorp Inc. • Senior Full Stack Developer </div> </div> <span class="px-2 py-1 text-xs rounded-full bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400" data-astro-cid-qlh7ngej> 工作经历 </span> </div> <div class="flex items-center gap-4 mb-3 text-sm text-black/60 dark:text-white/60" data-astro-cid-qlh7ngej> <div data-astro-cid-qlh7ngej> 2023年6月 - 至今 </div> <div data-astro-cid-qlh7ngej></div> <div data-astro-cid-qlh7ngej>2 年 11 个月</div> <div data-astro-cid-qlh7ngej></div> <div data-astro-cid-qlh7ngej>📍 北京</div> </div> <p class="text-black/70 dark:text-white/70 mb-4" data-astro-cid-qlh7ngej> 负责Web应用的前后端开发参与产品架构设计和技术选型带领团队完成多个重要项目。 </p> <div class="mb-4" data-astro-cid-qlh7ngej> <h4 class="text-sm font-semibold text-black/80 dark:text-white/80 mb-2" data-astro-cid-qlh7ngej> 成就荣誉 </h4> <ul class="space-y-1" data-astro-cid-qlh7ngej> <li class="text-sm text-black/70 dark:text-white/70 flex items-start gap-2" data-astro-cid-qlh7ngej> <span class="text-green-500 mt-1" data-astro-cid-qlh7ngej></span> <span data-astro-cid-qlh7ngej>主导开发了公司核心产品的前端架构重构</span> </li><li class="text-sm text-black/70 dark:text-white/70 flex items-start gap-2" data-astro-cid-qlh7ngej> <span class="text-green-500 mt-1" data-astro-cid-qlh7ngej></span> <span data-astro-cid-qlh7ngej>优化系统性能页面加载速度提升40%</span> </li><li class="text-sm text-black/70 dark:text-white/70 flex items-start gap-2" data-astro-cid-qlh7ngej> <span class="text-green-500 mt-1" data-astro-cid-qlh7ngej></span> <span data-astro-cid-qlh7ngej>建立了完善的代码审查和测试流程</span> </li> </ul> </div> <div class="mb-4" data-astro-cid-qlh7ngej> <div class="flex flex-wrap gap-1" data-astro-cid-qlh7ngej> <span class="px-2 py-1 text-xs bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300 rounded" data-astro-cid-qlh7ngej> React </span><span class="px-2 py-1 text-xs bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300 rounded" data-astro-cid-qlh7ngej> Node.js </span><span class="px-2 py-1 text-xs bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300 rounded" data-astro-cid-qlh7ngej> TypeScript </span><span class="px-2 py-1 text-xs bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300 rounded" data-astro-cid-qlh7ngej> PostgreSQL </span><span class="px-2 py-1 text-xs bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300 rounded" data-astro-cid-qlh7ngej> AWS </span> </div> </div> </div> </div><div class="relative flex items-start gap-6" data-astro-cid-qlh7ngej> <!-- 时间线节点 --> <div class="relative z-10 w-12 h-12 rounded-full flex items-center justify-center" style="background-color: #7C3AED" data-astro-cid-qlh7ngej> <span class="inline-flex items-center justify-center text-base text-xl text-white" style="color: white;" data-icon-container="icon-da6gh6h8h" data-astro-cid-qpfihmil> <!-- 加载状态指示器 --> <span class="icon-loading animate-pulse opacity-50" data-loading-indicator data-astro-cid-qpfihmil> R </span> <!-- 实际图标 --> <iconify-icon icon="material-symbols:verified" class="icon-content opacity-0 transition-opacity duration-200" data-icon-element="true" loading="eager" data-astro-cid-qpfihmil="true"></iconify-icon> </span> <script>(function(){const iconId = "icon-da6gh6h8h";
const icon = "material-symbols:verified";
// 图标加载和显示逻辑
(function() {
const container = document.querySelector(`[data-icon-container="${iconId}"]`);
if (!container) return;
const loadingIndicator = container.querySelector('[data-loading-indicator]');
const iconElement = container.querySelector('[data-icon-element]');
if (!loadingIndicator || !iconElement) return;
// 检查图标是否已经加载
function checkIconLoaded() {
// 检查iconify-icon元素是否已经渲染
const hasContent = iconElement.shadowRoot &&
iconElement.shadowRoot.children.length > 0;
if (hasContent) {
showIcon();
return true;
}
return false;
}
// 显示图标,隐藏加载指示器
function showIcon() {
loadingIndicator.style.display = 'none';
iconElement.classList.remove('opacity-0');
iconElement.classList.add('opacity-100');
}
// 显示加载指示器,隐藏图标
function showLoading() {
loadingIndicator.style.display = 'inline-flex';
iconElement.classList.remove('opacity-100');
iconElement.classList.add('opacity-0');
}
// 初始状态
showLoading();
// 监听图标加载事件
iconElement.addEventListener('load', () => {
showIcon();
});
// 监听图标加载错误
iconElement.addEventListener('error', () => {
// 保持显示fallback
console.warn(`Failed to load icon: ${icon}`);
});
// 使用MutationObserver监听shadow DOM变化
if (window.MutationObserver) {
const observer = new MutationObserver(() => {
if (checkIconLoaded()) {
observer.disconnect();
}
});
// 监听iconify-icon元素的变化
observer.observe(iconElement, {
childList: true,
subtree: true,
attributes: true
});
// 设置超时,避免无限等待
setTimeout(() => {
observer.disconnect();
if (!checkIconLoaded()) {
console.warn(`Icon load timeout: ${icon}`);
}
}, 5000);
}
// 立即检查一次(可能已经加载完成)
setTimeout(() => {
checkIconLoaded();
}, 100);
})();
})();</script> </div> <!-- 内容卡片 --> <div class="flex-1 bg-white dark:bg-gray-800 rounded-lg border border-black/10 dark:border-white/10 p-6 hover:shadow-lg transition-shadow duration-300" data-astro-cid-qlh7ngej> <div class="flex items-start justify-between mb-3" data-astro-cid-qlh7ngej> <div data-astro-cid-qlh7ngej> <h3 class="text-xl font-semibold text-black/90 dark:text-white/90 mb-1" data-astro-cid-qlh7ngej> React开发者认证 </h3> <div class="text-sm text-black/70 dark:text-white/70" data-astro-cid-qlh7ngej> Meta (Facebook) </div> </div> <span class="px-2 py-1 text-xs rounded-full bg-orange-100 text-orange-700 dark:bg-orange-900/30 dark:text-orange-400" data-astro-cid-qlh7ngej> 成就荣誉 </span> </div> <div class="flex items-center gap-4 mb-3 text-sm text-black/60 dark:text-white/60" data-astro-cid-qlh7ngej> <div data-astro-cid-qlh7ngej> 2022年8月 - 至今 </div> <div data-astro-cid-qlh7ngej></div> <div data-astro-cid-qlh7ngej>3 年 9 个月</div> </div> <p class="text-black/70 dark:text-white/70 mb-4" data-astro-cid-qlh7ngej> 通过了React官方认证考试证明了在React开发方面的专业能力。 </p> <div class="flex gap-4" data-astro-cid-qlh7ngej> <a href="https://certificates.example.com/react-cert" target="_blank" rel="noopener noreferrer" class="text-blue-600 dark:text-blue-400 hover:underline text-sm font-medium flex items-center gap-1" data-astro-cid-qlh7ngej> 🏆 Certificate </a> </div> </div> </div><div class="relative flex items-start gap-6" data-astro-cid-qlh7ngej> <!-- 时间线节点 --> <div class="relative z-10 w-12 h-12 rounded-full flex items-center justify-center" style="background-color: #DC2626" data-astro-cid-qlh7ngej> <span class="inline-flex items-center justify-center text-base text-xl text-white" style="color: white;" data-icon-container="icon-57510yuwy" data-astro-cid-qpfihmil> <!-- 加载状态指示器 --> <span class="icon-loading animate-pulse opacity-50" data-loading-indicator data-astro-cid-qpfihmil></span> <!-- 实际图标 --> <iconify-icon icon="material-symbols:work" class="icon-content opacity-0 transition-opacity duration-200" data-icon-element="true" loading="eager" data-astro-cid-qpfihmil="true"></iconify-icon> </span> <script>(function(){const iconId = "icon-57510yuwy";
const icon = "material-symbols:work";
// 图标加载和显示逻辑
(function() {
const container = document.querySelector(`[data-icon-container="${iconId}"]`);
if (!container) return;
const loadingIndicator = container.querySelector('[data-loading-indicator]');
const iconElement = container.querySelector('[data-icon-element]');
if (!loadingIndicator || !iconElement) return;
// 检查图标是否已经加载
function checkIconLoaded() {
// 检查iconify-icon元素是否已经渲染
const hasContent = iconElement.shadowRoot &&
iconElement.shadowRoot.children.length > 0;
if (hasContent) {
showIcon();
return true;
}
return false;
}
// 显示图标,隐藏加载指示器
function showIcon() {
loadingIndicator.style.display = 'none';
iconElement.classList.remove('opacity-0');
iconElement.classList.add('opacity-100');
}
// 显示加载指示器,隐藏图标
function showLoading() {
loadingIndicator.style.display = 'inline-flex';
iconElement.classList.remove('opacity-100');
iconElement.classList.add('opacity-0');
}
// 初始状态
showLoading();
// 监听图标加载事件
iconElement.addEventListener('load', () => {
showIcon();
});
// 监听图标加载错误
iconElement.addEventListener('error', () => {
// 保持显示fallback
console.warn(`Failed to load icon: ${icon}`);
});
// 使用MutationObserver监听shadow DOM变化
if (window.MutationObserver) {
const observer = new MutationObserver(() => {
if (checkIconLoaded()) {
observer.disconnect();
}
});
// 监听iconify-icon元素的变化
observer.observe(iconElement, {
childList: true,
subtree: true,
attributes: true
});
// 设置超时,避免无限等待
setTimeout(() => {
observer.disconnect();
if (!checkIconLoaded()) {
console.warn(`Icon load timeout: ${icon}`);
}
}, 5000);
}
// 立即检查一次(可能已经加载完成)
setTimeout(() => {
checkIconLoaded();
}, 100);
})();
})();</script> </div> <!-- 内容卡片 --> <div class="flex-1 bg-white dark:bg-gray-800 rounded-lg border border-black/10 dark:border-white/10 p-6 hover:shadow-lg transition-shadow duration-300" data-astro-cid-qlh7ngej> <div class="flex items-start justify-between mb-3" data-astro-cid-qlh7ngej> <div data-astro-cid-qlh7ngej> <h3 class="text-xl font-semibold text-black/90 dark:text-white/90 mb-1" data-astro-cid-qlh7ngej> 前端开发工程师 </h3> <div class="text-sm text-black/70 dark:text-white/70" data-astro-cid-qlh7ngej> WebTech Solutions • Frontend Developer </div> </div> <span class="px-2 py-1 text-xs rounded-full bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400" data-astro-cid-qlh7ngej> 工作经历 </span> </div> <div class="flex items-center gap-4 mb-3 text-sm text-black/60 dark:text-white/60" data-astro-cid-qlh7ngej> <div data-astro-cid-qlh7ngej> 2022年3月 - 2023年5月 </div> <div data-astro-cid-qlh7ngej></div> <div data-astro-cid-qlh7ngej>1 年 4 个月</div> <div data-astro-cid-qlh7ngej></div> <div data-astro-cid-qlh7ngej>📍 上海</div> </div> <p class="text-black/70 dark:text-white/70 mb-4" data-astro-cid-qlh7ngej> 专注于React生态系统开发负责多个企业级Web应用的前端架构和开发。 </p> <div class="mb-4" data-astro-cid-qlh7ngej> <h4 class="text-sm font-semibold text-black/80 dark:text-white/80 mb-2" data-astro-cid-qlh7ngej> 成就荣誉 </h4> <ul class="space-y-1" data-astro-cid-qlh7ngej> <li class="text-sm text-black/70 dark:text-white/70 flex items-start gap-2" data-astro-cid-qlh7ngej> <span class="text-green-500 mt-1" data-astro-cid-qlh7ngej></span> <span data-astro-cid-qlh7ngej>开发了公司主要产品的用户界面</span> </li><li class="text-sm text-black/70 dark:text-white/70 flex items-start gap-2" data-astro-cid-qlh7ngej> <span class="text-green-500 mt-1" data-astro-cid-qlh7ngej></span> <span data-astro-cid-qlh7ngej>建立了组件库和设计系统</span> </li><li class="text-sm text-black/70 dark:text-white/70 flex items-start gap-2" data-astro-cid-qlh7ngej> <span class="text-green-500 mt-1" data-astro-cid-qlh7ngej></span> <span data-astro-cid-qlh7ngej>提升了团队的开发效率和代码质量</span> </li> </ul> </div> <div class="mb-4" data-astro-cid-qlh7ngej> <div class="flex flex-wrap gap-1" data-astro-cid-qlh7ngej> <span class="px-2 py-1 text-xs bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300 rounded" data-astro-cid-qlh7ngej> React </span><span class="px-2 py-1 text-xs bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300 rounded" data-astro-cid-qlh7ngej> JavaScript </span><span class="px-2 py-1 text-xs bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300 rounded" data-astro-cid-qlh7ngej> CSS3 </span><span class="px-2 py-1 text-xs bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300 rounded" data-astro-cid-qlh7ngej> Webpack </span><span class="px-2 py-1 text-xs bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300 rounded" data-astro-cid-qlh7ngej> Jest </span> </div> </div> </div> </div><div class="relative flex items-start gap-6" data-astro-cid-qlh7ngej> <!-- 时间线节点 --> <div class="relative z-10 w-12 h-12 rounded-full flex items-center justify-center" style="background-color: #DC2626" data-astro-cid-qlh7ngej> <span class="inline-flex items-center justify-center text-base text-xl text-white" style="color: white;" data-icon-container="icon-fqcg9dnyv" data-astro-cid-qpfihmil> <!-- 加载状态指示器 --> <span class="icon-loading animate-pulse opacity-50" data-loading-indicator data-astro-cid-qpfihmil></span> <!-- 实际图标 --> <iconify-icon icon="material-symbols:emoji-events" class="icon-content opacity-0 transition-opacity duration-200" data-icon-element="true" loading="eager" data-astro-cid-qpfihmil="true"></iconify-icon> </span> <script>(function(){const iconId = "icon-fqcg9dnyv";
const icon = "material-symbols:emoji-events";
// 图标加载和显示逻辑
(function() {
const container = document.querySelector(`[data-icon-container="${iconId}"]`);
if (!container) return;
const loadingIndicator = container.querySelector('[data-loading-indicator]');
const iconElement = container.querySelector('[data-icon-element]');
if (!loadingIndicator || !iconElement) return;
// 检查图标是否已经加载
function checkIconLoaded() {
// 检查iconify-icon元素是否已经渲染
const hasContent = iconElement.shadowRoot &&
iconElement.shadowRoot.children.length > 0;
if (hasContent) {
showIcon();
return true;
}
return false;
}
// 显示图标,隐藏加载指示器
function showIcon() {
loadingIndicator.style.display = 'none';
iconElement.classList.remove('opacity-0');
iconElement.classList.add('opacity-100');
}
// 显示加载指示器,隐藏图标
function showLoading() {
loadingIndicator.style.display = 'inline-flex';
iconElement.classList.remove('opacity-100');
iconElement.classList.add('opacity-0');
}
// 初始状态
showLoading();
// 监听图标加载事件
iconElement.addEventListener('load', () => {
showIcon();
});
// 监听图标加载错误
iconElement.addEventListener('error', () => {
// 保持显示fallback
console.warn(`Failed to load icon: ${icon}`);
});
// 使用MutationObserver监听shadow DOM变化
if (window.MutationObserver) {
const observer = new MutationObserver(() => {
if (checkIconLoaded()) {
observer.disconnect();
}
});
// 监听iconify-icon元素的变化
observer.observe(iconElement, {
childList: true,
subtree: true,
attributes: true
});
// 设置超时,避免无限等待
setTimeout(() => {
observer.disconnect();
if (!checkIconLoaded()) {
console.warn(`Icon load timeout: ${icon}`);
}
}, 5000);
}
// 立即检查一次(可能已经加载完成)
setTimeout(() => {
checkIconLoaded();
}, 100);
})();
})();</script> </div> <!-- 内容卡片 --> <div class="flex-1 bg-white dark:bg-gray-800 rounded-lg border border-black/10 dark:border-white/10 p-6 hover:shadow-lg transition-shadow duration-300" data-astro-cid-qlh7ngej> <div class="flex items-start justify-between mb-3" data-astro-cid-qlh7ngej> <div data-astro-cid-qlh7ngej> <h3 class="text-xl font-semibold text-black/90 dark:text-white/90 mb-1" data-astro-cid-qlh7ngej> 黑客马拉松比赛获奖 </h3> <div class="text-sm text-black/70 dark:text-white/70" data-astro-cid-qlh7ngej> TechHackathon 2021 </div> </div> <span class="px-2 py-1 text-xs rounded-full bg-orange-100 text-orange-700 dark:bg-orange-900/30 dark:text-orange-400" data-astro-cid-qlh7ngej> 成就荣誉 </span> </div> <div class="flex items-center gap-4 mb-3 text-sm text-black/60 dark:text-white/60" data-astro-cid-qlh7ngej> <div data-astro-cid-qlh7ngej> 2021年11月 - 2021年11月 </div> <div data-astro-cid-qlh7ngej></div> <div data-astro-cid-qlh7ngej>1 个月</div> <div data-astro-cid-qlh7ngej></div> <div data-astro-cid-qlh7ngej>📍 上海</div> </div> <p class="text-black/70 dark:text-white/70 mb-4" data-astro-cid-qlh7ngej> 在48小时内开发了一个创新的Web应用获得了最佳技术实现奖。 </p> <div class="mb-4" data-astro-cid-qlh7ngej> <h4 class="text-sm font-semibold text-black/80 dark:text-white/80 mb-2" data-astro-cid-qlh7ngej> 成就荣誉 </h4> <ul class="space-y-1" data-astro-cid-qlh7ngej> <li class="text-sm text-black/70 dark:text-white/70 flex items-start gap-2" data-astro-cid-qlh7ngej> <span class="text-green-500 mt-1" data-astro-cid-qlh7ngej></span> <span data-astro-cid-qlh7ngej>获得最佳技术实现奖</span> </li><li class="text-sm text-black/70 dark:text-white/70 flex items-start gap-2" data-astro-cid-qlh7ngej> <span class="text-green-500 mt-1" data-astro-cid-qlh7ngej></span> <span data-astro-cid-qlh7ngej>团队协作完成复杂项目</span> </li><li class="text-sm text-black/70 dark:text-white/70 flex items-start gap-2" data-astro-cid-qlh7ngej> <span class="text-green-500 mt-1" data-astro-cid-qlh7ngej></span> <span data-astro-cid-qlh7ngej>在短时间内学习新技术</span> </li> </ul> </div> <div class="mb-4" data-astro-cid-qlh7ngej> <div class="flex flex-wrap gap-1" data-astro-cid-qlh7ngej> <span class="px-2 py-1 text-xs bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300 rounded" data-astro-cid-qlh7ngej> React </span><span class="px-2 py-1 text-xs bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300 rounded" data-astro-cid-qlh7ngej> Express.js </span><span class="px-2 py-1 text-xs bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300 rounded" data-astro-cid-qlh7ngej> MongoDB </span><span class="px-2 py-1 text-xs bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300 rounded" data-astro-cid-qlh7ngej> Socket.io </span> </div> </div> <div class="flex gap-4" data-astro-cid-qlh7ngej> <a href="https://hackathon-project.example.com" target="_blank" rel="noopener noreferrer" class="text-blue-600 dark:text-blue-400 hover:underline text-sm font-medium flex items-center gap-1" data-astro-cid-qlh7ngej> 🔗 Project Demo </a> </div> </div> </div><div class="relative flex items-start gap-6" data-astro-cid-qlh7ngej> <!-- 时间线节点 --> <div class="relative z-10 w-12 h-12 rounded-full flex items-center justify-center" style="background-color: #EA580C" data-astro-cid-qlh7ngej> <span class="inline-flex items-center justify-center text-base text-xl text-white" style="color: white;" data-icon-container="icon-5u5xoin35" data-astro-cid-qpfihmil> <!-- 加载状态指示器 --> <span class="icon-loading animate-pulse opacity-50" data-loading-indicator data-astro-cid-qpfihmil></span> <!-- 实际图标 --> <iconify-icon icon="material-symbols:work" class="icon-content opacity-0 transition-opacity duration-200" data-icon-element="true" loading="eager" data-astro-cid-qpfihmil="true"></iconify-icon> </span> <script>(function(){const iconId = "icon-5u5xoin35";
const icon = "material-symbols:work";
// 图标加载和显示逻辑
(function() {
const container = document.querySelector(`[data-icon-container="${iconId}"]`);
if (!container) return;
const loadingIndicator = container.querySelector('[data-loading-indicator]');
const iconElement = container.querySelector('[data-icon-element]');
if (!loadingIndicator || !iconElement) return;
// 检查图标是否已经加载
function checkIconLoaded() {
// 检查iconify-icon元素是否已经渲染
const hasContent = iconElement.shadowRoot &&
iconElement.shadowRoot.children.length > 0;
if (hasContent) {
showIcon();
return true;
}
return false;
}
// 显示图标,隐藏加载指示器
function showIcon() {
loadingIndicator.style.display = 'none';
iconElement.classList.remove('opacity-0');
iconElement.classList.add('opacity-100');
}
// 显示加载指示器,隐藏图标
function showLoading() {
loadingIndicator.style.display = 'inline-flex';
iconElement.classList.remove('opacity-100');
iconElement.classList.add('opacity-0');
}
// 初始状态
showLoading();
// 监听图标加载事件
iconElement.addEventListener('load', () => {
showIcon();
});
// 监听图标加载错误
iconElement.addEventListener('error', () => {
// 保持显示fallback
console.warn(`Failed to load icon: ${icon}`);
});
// 使用MutationObserver监听shadow DOM变化
if (window.MutationObserver) {
const observer = new MutationObserver(() => {
if (checkIconLoaded()) {
observer.disconnect();
}
});
// 监听iconify-icon元素的变化
observer.observe(iconElement, {
childList: true,
subtree: true,
attributes: true
});
// 设置超时,避免无限等待
setTimeout(() => {
observer.disconnect();
if (!checkIconLoaded()) {
console.warn(`Icon load timeout: ${icon}`);
}
}, 5000);
}
// 立即检查一次(可能已经加载完成)
setTimeout(() => {
checkIconLoaded();
}, 100);
})();
})();</script> </div> <!-- 内容卡片 --> <div class="flex-1 bg-white dark:bg-gray-800 rounded-lg border border-black/10 dark:border-white/10 p-6 hover:shadow-lg transition-shadow duration-300" data-astro-cid-qlh7ngej> <div class="flex items-start justify-between mb-3" data-astro-cid-qlh7ngej> <div data-astro-cid-qlh7ngej> <h3 class="text-xl font-semibold text-black/90 dark:text-white/90 mb-1" data-astro-cid-qlh7ngej> 软件开发实习生 </h3> <div class="text-sm text-black/70 dark:text-white/70" data-astro-cid-qlh7ngej> StartupTech • Software Development Intern </div> </div> <span class="px-2 py-1 text-xs rounded-full bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400" data-astro-cid-qlh7ngej> 工作经历 </span> </div> <div class="flex items-center gap-4 mb-3 text-sm text-black/60 dark:text-white/60" data-astro-cid-qlh7ngej> <div data-astro-cid-qlh7ngej> 2021年7月 - 2021年9月 </div> <div data-astro-cid-qlh7ngej></div> <div data-astro-cid-qlh7ngej>4 个月</div> <div data-astro-cid-qlh7ngej></div> <div data-astro-cid-qlh7ngej>📍 北京</div> </div> <p class="text-black/70 dark:text-white/70 mb-4" data-astro-cid-qlh7ngej> 在大学期间的实习经历参与了Web应用开发和数据库设计工作。 </p> <div class="mb-4" data-astro-cid-qlh7ngej> <h4 class="text-sm font-semibold text-black/80 dark:text-white/80 mb-2" data-astro-cid-qlh7ngej> 成就荣誉 </h4> <ul class="space-y-1" data-astro-cid-qlh7ngej> <li class="text-sm text-black/70 dark:text-white/70 flex items-start gap-2" data-astro-cid-qlh7ngej> <span class="text-green-500 mt-1" data-astro-cid-qlh7ngej></span> <span data-astro-cid-qlh7ngej>完成了用户管理系统的开发</span> </li><li class="text-sm text-black/70 dark:text-white/70 flex items-start gap-2" data-astro-cid-qlh7ngej> <span class="text-green-500 mt-1" data-astro-cid-qlh7ngej></span> <span data-astro-cid-qlh7ngej>学习了敏捷开发流程</span> </li><li class="text-sm text-black/70 dark:text-white/70 flex items-start gap-2" data-astro-cid-qlh7ngej> <span class="text-green-500 mt-1" data-astro-cid-qlh7ngej></span> <span data-astro-cid-qlh7ngej>获得了实习优秀表现奖</span> </li> </ul> </div> <div class="mb-4" data-astro-cid-qlh7ngej> <div class="flex flex-wrap gap-1" data-astro-cid-qlh7ngej> <span class="px-2 py-1 text-xs bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300 rounded" data-astro-cid-qlh7ngej> JavaScript </span><span class="px-2 py-1 text-xs bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300 rounded" data-astro-cid-qlh7ngej> Vue.js </span><span class="px-2 py-1 text-xs bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300 rounded" data-astro-cid-qlh7ngej> Node.js </span><span class="px-2 py-1 text-xs bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300 rounded" data-astro-cid-qlh7ngej> MySQL </span> </div> </div> </div> </div><div class="relative flex items-start gap-6" data-astro-cid-qlh7ngej> <!-- 时间线节点 --> <div class="relative z-10 w-12 h-12 rounded-full flex items-center justify-center" style="background-color: #059669" data-astro-cid-qlh7ngej> <span class="inline-flex items-center justify-center text-base text-xl text-white" style="color: white;" data-icon-container="icon-id0cgd4gj" data-astro-cid-qpfihmil> <!-- 加载状态指示器 --> <span class="icon-loading animate-pulse opacity-50" data-loading-indicator data-astro-cid-qpfihmil></span> <!-- 实际图标 --> <iconify-icon icon="material-symbols:school" class="icon-content opacity-0 transition-opacity duration-200" data-icon-element="true" loading="eager" data-astro-cid-qpfihmil="true"></iconify-icon> </span> <script>(function(){const iconId = "icon-id0cgd4gj";
const icon = "material-symbols:school";
// 图标加载和显示逻辑
(function() {
const container = document.querySelector(`[data-icon-container="${iconId}"]`);
if (!container) return;
const loadingIndicator = container.querySelector('[data-loading-indicator]');
const iconElement = container.querySelector('[data-icon-element]');
if (!loadingIndicator || !iconElement) return;
// 检查图标是否已经加载
function checkIconLoaded() {
// 检查iconify-icon元素是否已经渲染
const hasContent = iconElement.shadowRoot &&
iconElement.shadowRoot.children.length > 0;
if (hasContent) {
showIcon();
return true;
}
return false;
}
// 显示图标,隐藏加载指示器
function showIcon() {
loadingIndicator.style.display = 'none';
iconElement.classList.remove('opacity-0');
iconElement.classList.add('opacity-100');
}
// 显示加载指示器,隐藏图标
function showLoading() {
loadingIndicator.style.display = 'inline-flex';
iconElement.classList.remove('opacity-100');
iconElement.classList.add('opacity-0');
}
// 初始状态
showLoading();
// 监听图标加载事件
iconElement.addEventListener('load', () => {
showIcon();
});
// 监听图标加载错误
iconElement.addEventListener('error', () => {
// 保持显示fallback
console.warn(`Failed to load icon: ${icon}`);
});
// 使用MutationObserver监听shadow DOM变化
if (window.MutationObserver) {
const observer = new MutationObserver(() => {
if (checkIconLoaded()) {
observer.disconnect();
}
});
// 监听iconify-icon元素的变化
observer.observe(iconElement, {
childList: true,
subtree: true,
attributes: true
});
// 设置超时,避免无限等待
setTimeout(() => {
observer.disconnect();
if (!checkIconLoaded()) {
console.warn(`Icon load timeout: ${icon}`);
}
}, 5000);
}
// 立即检查一次(可能已经加载完成)
setTimeout(() => {
checkIconLoaded();
}, 100);
})();
})();</script> </div> <!-- 内容卡片 --> <div class="flex-1 bg-white dark:bg-gray-800 rounded-lg border border-black/10 dark:border-white/10 p-6 hover:shadow-lg transition-shadow duration-300" data-astro-cid-qlh7ngej> <div class="flex items-start justify-between mb-3" data-astro-cid-qlh7ngej> <div data-astro-cid-qlh7ngej> <h3 class="text-xl font-semibold text-black/90 dark:text-white/90 mb-1" data-astro-cid-qlh7ngej> 计算机科学与技术学士学位 </h3> <div class="text-sm text-black/70 dark:text-white/70" data-astro-cid-qlh7ngej> 北京理工大学 </div> </div> <span class="px-2 py-1 text-xs rounded-full bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400" data-astro-cid-qlh7ngej> 教育经历 </span> </div> <div class="flex items-center gap-4 mb-3 text-sm text-black/60 dark:text-white/60" data-astro-cid-qlh7ngej> <div data-astro-cid-qlh7ngej> 2018年9月 - 2022年6月 </div> <div data-astro-cid-qlh7ngej></div> <div data-astro-cid-qlh7ngej>3 年 11 个月</div> <div data-astro-cid-qlh7ngej></div> <div data-astro-cid-qlh7ngej>📍 北京</div> </div> <p class="text-black/70 dark:text-white/70 mb-4" data-astro-cid-qlh7ngej> 系统学习了计算机科学基础理论,包括数据结构、算法、操作系统、数据库等核心课程。 </p> <div class="mb-4" data-astro-cid-qlh7ngej> <h4 class="text-sm font-semibold text-black/80 dark:text-white/80 mb-2" data-astro-cid-qlh7ngej> 成就荣誉 </h4> <ul class="space-y-1" data-astro-cid-qlh7ngej> <li class="text-sm text-black/70 dark:text-white/70 flex items-start gap-2" data-astro-cid-qlh7ngej> <span class="text-green-500 mt-1" data-astro-cid-qlh7ngej></span> <span data-astro-cid-qlh7ngej>GPA: 3.8/4.0</span> </li><li class="text-sm text-black/70 dark:text-white/70 flex items-start gap-2" data-astro-cid-qlh7ngej> <span class="text-green-500 mt-1" data-astro-cid-qlh7ngej></span> <span data-astro-cid-qlh7ngej>获得校级优秀学生奖学金</span> </li><li class="text-sm text-black/70 dark:text-white/70 flex items-start gap-2" data-astro-cid-qlh7ngej> <span class="text-green-500 mt-1" data-astro-cid-qlh7ngej></span> <span data-astro-cid-qlh7ngej>参与多个课程项目和实习</span> </li> </ul> </div> <div class="mb-4" data-astro-cid-qlh7ngej> <div class="flex flex-wrap gap-1" data-astro-cid-qlh7ngej> <span class="px-2 py-1 text-xs bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300 rounded" data-astro-cid-qlh7ngej> Java </span><span class="px-2 py-1 text-xs bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300 rounded" data-astro-cid-qlh7ngej> C++ </span><span class="px-2 py-1 text-xs bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300 rounded" data-astro-cid-qlh7ngej> Python </span><span class="px-2 py-1 text-xs bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300 rounded" data-astro-cid-qlh7ngej> MySQL </span><span class="px-2 py-1 text-xs bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300 rounded" data-astro-cid-qlh7ngej> Linux </span> </div> </div> </div> </div><div class="relative flex items-start gap-6" data-astro-cid-qlh7ngej> <!-- 时间线节点 --> <div class="relative z-10 w-12 h-12 rounded-full flex items-center justify-center" style="background-color: #7C3AED" data-astro-cid-qlh7ngej> <span class="inline-flex items-center justify-center text-base text-xl text-white" style="color: white;" data-icon-container="icon-dtbl4i1n0" data-astro-cid-qpfihmil> <!-- 加载状态指示器 --> <span class="icon-loading animate-pulse opacity-50" data-loading-indicator data-astro-cid-qpfihmil></span> <!-- 实际图标 --> <iconify-icon icon="material-symbols:code" class="icon-content opacity-0 transition-opacity duration-200" data-icon-element="true" loading="eager" data-astro-cid-qpfihmil="true"></iconify-icon> </span> <script>(function(){const iconId = "icon-dtbl4i1n0";
const icon = "material-symbols:code";
// 图标加载和显示逻辑
(function() {
const container = document.querySelector(`[data-icon-container="${iconId}"]`);
if (!container) return;
const loadingIndicator = container.querySelector('[data-loading-indicator]');
const iconElement = container.querySelector('[data-icon-element]');
if (!loadingIndicator || !iconElement) return;
// 检查图标是否已经加载
function checkIconLoaded() {
// 检查iconify-icon元素是否已经渲染
const hasContent = iconElement.shadowRoot &&
iconElement.shadowRoot.children.length > 0;
if (hasContent) {
showIcon();
return true;
}
return false;
}
// 显示图标,隐藏加载指示器
function showIcon() {
loadingIndicator.style.display = 'none';
iconElement.classList.remove('opacity-0');
iconElement.classList.add('opacity-100');
}
// 显示加载指示器,隐藏图标
function showLoading() {
loadingIndicator.style.display = 'inline-flex';
iconElement.classList.remove('opacity-100');
iconElement.classList.add('opacity-0');
}
// 初始状态
showLoading();
// 监听图标加载事件
iconElement.addEventListener('load', () => {
showIcon();
});
// 监听图标加载错误
iconElement.addEventListener('error', () => {
// 保持显示fallback
console.warn(`Failed to load icon: ${icon}`);
});
// 使用MutationObserver监听shadow DOM变化
if (window.MutationObserver) {
const observer = new MutationObserver(() => {
if (checkIconLoaded()) {
observer.disconnect();
}
});
// 监听iconify-icon元素的变化
observer.observe(iconElement, {
childList: true,
subtree: true,
attributes: true
});
// 设置超时,避免无限等待
setTimeout(() => {
observer.disconnect();
if (!checkIconLoaded()) {
console.warn(`Icon load timeout: ${icon}`);
}
}, 5000);
}
// 立即检查一次(可能已经加载完成)
setTimeout(() => {
checkIconLoaded();
}, 100);
})();
})();</script> </div> <!-- 内容卡片 --> <div class="flex-1 bg-white dark:bg-gray-800 rounded-lg border border-black/10 dark:border-white/10 p-6 hover:shadow-lg transition-shadow duration-300" data-astro-cid-qlh7ngej> <div class="flex items-start justify-between mb-3" data-astro-cid-qlh7ngej> <div data-astro-cid-qlh7ngej> <h3 class="text-xl font-semibold text-black/90 dark:text-white/90 mb-1" data-astro-cid-qlh7ngej> 开始编程学习 </h3> </div> <span class="px-2 py-1 text-xs rounded-full bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400" data-astro-cid-qlh7ngej> 教育经历 </span> </div> <div class="flex items-center gap-4 mb-3 text-sm text-black/60 dark:text-white/60" data-astro-cid-qlh7ngej> <div data-astro-cid-qlh7ngej> 2018年1月 - 至今 </div> <div data-astro-cid-qlh7ngej></div> <div data-astro-cid-qlh7ngej>8 年 5 个月</div> </div> <p class="text-black/70 dark:text-white/70 mb-4" data-astro-cid-qlh7ngej> 第一次接触编程从HTML和CSS开始逐步学习JavaScript和其他技术。 </p> <div class="mb-4" data-astro-cid-qlh7ngej> <h4 class="text-sm font-semibold text-black/80 dark:text-white/80 mb-2" data-astro-cid-qlh7ngej> 成就荣誉 </h4> <ul class="space-y-1" data-astro-cid-qlh7ngej> <li class="text-sm text-black/70 dark:text-white/70 flex items-start gap-2" data-astro-cid-qlh7ngej> <span class="text-green-500 mt-1" data-astro-cid-qlh7ngej></span> <span data-astro-cid-qlh7ngej>完成了第一个个人网站</span> </li><li class="text-sm text-black/70 dark:text-white/70 flex items-start gap-2" data-astro-cid-qlh7ngej> <span class="text-green-500 mt-1" data-astro-cid-qlh7ngej></span> <span data-astro-cid-qlh7ngej>掌握了Web开发基础</span> </li><li class="text-sm text-black/70 dark:text-white/70 flex items-start gap-2" data-astro-cid-qlh7ngej> <span class="text-green-500 mt-1" data-astro-cid-qlh7ngej></span> <span data-astro-cid-qlh7ngej>培养了对编程的兴趣</span> </li> </ul> </div> <div class="mb-4" data-astro-cid-qlh7ngej> <div class="flex flex-wrap gap-1" data-astro-cid-qlh7ngej> <span class="px-2 py-1 text-xs bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300 rounded" data-astro-cid-qlh7ngej> HTML </span><span class="px-2 py-1 text-xs bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300 rounded" data-astro-cid-qlh7ngej> CSS </span><span class="px-2 py-1 text-xs bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300 rounded" data-astro-cid-qlh7ngej> JavaScript </span> </div> </div> </div> </div> </div> </div> </div> <!-- 类型统计 --> <div class="mt-12 pt-8 border-t border-black/10 dark:border-white/10" data-astro-cid-qlh7ngej> <h2 class="text-2xl font-bold text-black/90 dark:text-white/90 mb-6" data-astro-cid-qlh7ngej> 统计信息 </h2> <div class="grid grid-cols-1 md:grid-cols-2 gap-8" data-astro-cid-qlh7ngej> <!-- 按类型分布 --> <div data-astro-cid-qlh7ngej> <h3 class="text-lg font-semibold text-black/80 dark:text-white/80 mb-4" data-astro-cid-qlh7ngej> 按类型分组 </h3> <div class="space-y-3" data-astro-cid-qlh7ngej> <div class="flex items-center gap-3" data-astro-cid-qlh7ngej> <div class="w-20 text-sm text-black/70 dark:text-white/70" data-astro-cid-qlh7ngej> 教育经历 </div> <div class="flex-1 bg-gray-200 dark:bg-gray-700 rounded-full h-2" data-astro-cid-qlh7ngej> <div class="h-2 rounded-full transition-all duration-500 bg-blue-500" style="width: 25%" data-astro-cid-qlh7ngej></div> </div> <div class="w-12 text-sm text-black/70 dark:text-white/70 text-right" data-astro-cid-qlh7ngej> 2 </div> </div><div class="flex items-center gap-3" data-astro-cid-qlh7ngej> <div class="w-20 text-sm text-black/70 dark:text-white/70" data-astro-cid-qlh7ngej> 工作经历 </div> <div class="flex-1 bg-gray-200 dark:bg-gray-700 rounded-full h-2" data-astro-cid-qlh7ngej> <div class="h-2 rounded-full transition-all duration-500 bg-green-500" style="width: 38%" data-astro-cid-qlh7ngej></div> </div> <div class="w-12 text-sm text-black/70 dark:text-white/70 text-right" data-astro-cid-qlh7ngej> 3 </div> </div><div class="flex items-center gap-3" data-astro-cid-qlh7ngej> <div class="w-20 text-sm text-black/70 dark:text-white/70" data-astro-cid-qlh7ngej> 项目经历 </div> <div class="flex-1 bg-gray-200 dark:bg-gray-700 rounded-full h-2" data-astro-cid-qlh7ngej> <div class="h-2 rounded-full transition-all duration-500 bg-purple-500" style="width: 13%" data-astro-cid-qlh7ngej></div> </div> <div class="w-12 text-sm text-black/70 dark:text-white/70 text-right" data-astro-cid-qlh7ngej> 1 </div> </div><div class="flex items-center gap-3" data-astro-cid-qlh7ngej> <div class="w-20 text-sm text-black/70 dark:text-white/70" data-astro-cid-qlh7ngej> 成就荣誉 </div> <div class="flex-1 bg-gray-200 dark:bg-gray-700 rounded-full h-2" data-astro-cid-qlh7ngej> <div class="h-2 rounded-full transition-all duration-500 bg-orange-500" style="width: 25%" data-astro-cid-qlh7ngej></div> </div> <div class="w-12 text-sm text-black/70 dark:text-white/70 text-right" data-astro-cid-qlh7ngej> 2 </div> </div> </div> </div> <!-- 工作经验详情 --> <div data-astro-cid-qlh7ngej> <h3 class="text-lg font-semibold text-black/80 dark:text-white/80 mb-4" data-astro-cid-qlh7ngej> 工作经验 </h3> <div class="space-y-3" data-astro-cid-qlh7ngej> <div class="flex justify-between items-center" data-astro-cid-qlh7ngej> <span class="text-black/70 dark:text-white/70" data-astro-cid-qlh7ngej>总工作经验</span> <span class="font-semibold text-black/90 dark:text-white/90" data-astro-cid-qlh7ngej> 4 年 7 个月 </span> </div> <div class="flex justify-between items-center" data-astro-cid-qlh7ngej> <span class="text-black/70 dark:text-white/70" data-astro-cid-qlh7ngej>工作岗位数</span> <span class="font-semibold text-black/90 dark:text-white/90" data-astro-cid-qlh7ngej>3</span> </div> <div class="flex justify-between items-center" data-astro-cid-qlh7ngej> <span class="text-black/70 dark:text-white/70" data-astro-cid-qlh7ngej>当前状态</span> <span class="font-semibold text-black/90 dark:text-white/90" data-astro-cid-qlh7ngej> 在职 </span> </div> </div> </div> </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",()=>{window.__iconifyLoader&&window.__iconifyLoader.load().then(()=>{document.querySelectorAll(".timeline-item").forEach(o=>{o.dispatchEvent(new CustomEvent("iconify-ready"))})}).catch(e=>{console.error("Failed to load icons on timeline page:",e)})});typeof window<"u"&&window.addEventListener("pageshow",e=>{e.persisted&&window.__iconifyLoader&&setTimeout(()=>{window.__iconifyLoader.load().catch(console.error)},100)});</script>