1<!DOCTYPE html> 2<html lang="zh-CN"> 3<head> 4 <meta charset="UTF-8"> 5<meta name="viewport" content="width=device-width, initial-scale=1.0"> 6<meta http-equiv="X-UA-Compatible" content="ie=edge"> 7<link rel="stylesheet" href="/styles/main.min.css"> 8 9 10<link rel="icon" href="/favicon.ico" type="image/x-icon"> 11 12
13<script> 14(function() { 15 // Check for saved theme preference 16 var savedTheme = localStorage.getItem('theme'); 17 18 if (savedTheme) { 19 // Use saved preference 20 document.documentElement.setAttribute('data-theme', savedTheme); 21 } else { 22 // Check system preference 23 var prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches; 24 var theme = prefersDark ? 'dark' : 'light'; 25 document.documentElement.setAttribute('data-theme', theme); 26 } 27})(); 28</script>
28 29 30 31<meta name="description" content="æ¯ä¸æ®µæ è¡ï¼é½æç»ç¹ã"> 32<meta property="og:site_name" content="嵿´é"> 33<meta property="og:title" content="嵿´é"> 34<meta property="og:description" content="æ¯ä¸æ®µæ è¡ï¼é½æç»ç¹ã"> 35 36<meta property="og:image" content="/images/avatar.png"> 37 38<meta property="og:url" content="https://kemeow0815.github.io"> 39<meta property="og:type" content="website"> 40<meta name="twitter:card" content="summary_large_image"> 41<meta name="twitter:title" content="嵿´é"> 42<meta name="twitter:description" content="æ¯ä¸æ®µæ è¡ï¼é½æç»ç¹ã"> 43 44<meta name="twitter:image" content="/images/avatar.png"> 45 46 47 <meta name="generator" content="Gridea Pro 1.1.1"> 48 <title>嵿´é</title> 49 50<meta name="description" content="æ¯ä¸æ®µæ è¡ï¼é½æç»ç¹ã"> 51<link rel="canonical" href="https://kemeow0815.github.io/">
52<script type="application/ld+json">{"@context":"https://schema.org","@type":"WebSite","name":"嵿´é","url":"https://kemeow0815.github.io","description":"æ¯ä¸æ®µæ
è¡ï¼é½æç»ç¹ã","potentialAction":{"@type":"SearchAction","query-input":"required name=search_term_string","target":"https://kemeow0815.github.io/search?q={search_term_string}"}}</script>
52 53<meta property="og:title" content="嵿´é"> 54<meta property="og:description" content="æ¯ä¸æ®µæ è¡ï¼é½æç»ç¹ã"> 55<meta property="og:url" content="https://kemeow0815.github.io/"> 56<meta property="og:type" content="website"> 57<meta property="og:site_name" content="嵿´é"> 58<meta property="og:image" content="/images/avatar.png"> 59<meta name="twitter:card" content="summary_large_image"> 60<meta name="twitter:url" content="https://kemeow0815.github.io/"> 61<meta name="twitter:title" content="嵿´é"> 62<meta name="twitter:description" content="æ¯ä¸æ®µæ è¡ï¼é½æç»ç¹ã"> 63<meta name="twitter:image" content="/images/avatar.png"> 64</head> 65<body class="flavor-theme"> 66 67 <!-- å è½½é¡µé¢ --> 68 69 <div id="loading-overlay" class="loading-overlay"> 70 <div class="loading-content"> 71 72 <div class="loading-spinner"></div> 73 74 <div class="loading-title">å è½½ä¸...</div> 75 </div> 76 </div> 77 78 79 <!-- æµæèæ¯ --> 80 <div class="bg-container"> 81 <div class="bg-stars" id="bg-stars"></div> 82 </div> 83 84 <header class="site-header" id="siteHeader"> 85 <div class="header-inner"> 86 <div class="top-bar"> 87 88 89 <div class="top-bar__left"> 90 91 <a href="https://kemeow0815.github.io" class="site-avatar-link" aria-label="è¿åé¦é¡µ"> 92 <img class="site-avatar" src="https://jsd.268682.xyz/gh/Kemeow0815/img@main/img/kemeow0815.webp" alt="嵿´é" /> 93 </a> 94 95 <a href="https://kemeow0815.github.io" class="site-name-link">嵿´é</a> 96 </div> 97 98 99 <nav class="top-bar__nav" id="sectionBar"> 100 101 102 103 104 <a class="nav-item" href="https://kemeow0815.github.io/">ð é¦é¡µ</a> 105 106 107 108 109 <div class="nav-item-wrapper"> 110 <div class="nav-item nav-item--has-children" data-menu-id="menu-2"> 111 <a class="nav-item__link" href="javascript:void(0);">ðæç« </a> 112 <svg class="nav-item__arrow" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 113 <polyline points="6 9 12 15 18 9"></polyline> 114 </svg> 115 </div> 116 <div class="nav-submenu" id="submenu-menu-2"> 117 118 <a class="nav-submenu__item" href="/post">ðå ¨é¨æç« </a> 119 120 <a class="nav-submenu__item" href="/archives">ð¦å½æ¡£</a> 121 122 <a class="nav-submenu__item" href="/tags">ðæ ç¾</a> 123 124 </div> 125 </div> 126 127 128 129 130 <a class="nav-item" href="/memos">ðéªå¿µ</a> 131 132 133 134 135 <div class="nav-item-wrapper"> 136 <div class="nav-item nav-item--has-children" data-menu-id="menu-4"> 137 <a class="nav-item__link" href="javascript:void(0);">ð龿¥</a> 138 <svg class="nav-item__arrow" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 139 <polyline points="6 9 12 15 18 9"></polyline> 140 </svg> 141 </div> 142 <div class="nav-submenu" id="submenu-menu-4"> 143 144 <a class="nav-submenu__item" href="/links">ð§âð¤âð§åé¾</a> 145 146 <a class="nav-submenu__item" href="/post/fcircle">ð¬æåå</a> 147 148 </div> 149 </div> 150 151 152 153 154 <div class="nav-item-wrapper"> 155 <div class="nav-item nav-item--has-children" data-menu-id="menu-5"> 156 <a class="nav-item__link" href="javascript:void(0);">ð页é¢</a> 157 <svg class="nav-item__arrow" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 158 <polyline points="6 9 12 15 18 9"></polyline> 159 </svg> 160 </div> 161 <div class="nav-submenu" id="submenu-menu-5"> 162 163 <a class="nav-submenu__item" href="/post/sponsor">ðèµå©</a> 164 165 <a class="nav-submenu__item" href="/post/timetable">ðï¸è¯¾ç¨è¡¨</a> 166 167 </div> 168 </div> 169 170 171 172 173 <a class="nav-item" href="/post/about">ð« å ³äº</a> 174 175 176 177 </nav> 178 179 180 <div class="top-bar__right">
181 <button class="btn-icon btn-theme" id="btnTheme" type="button" aria-label="忢䏻é¢"> 182 183 <svg class="theme-icon-moon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 184 <path d="M20.742 13.045a8 8 0 0 1-2.077.271c-2.135 0-4.14-.83-5.646-2.336a8.03 8.03 0 0 1-2.064-7.723A1 1 0 0 0 9.73 2.034a10 10 0 0 0-4.489 2.582c-3.898 3.898-3.898 10.243 0 14.143a9.94 9.94 0 0 0 7.072 2.93a9.93 9.93 0 0 0 7.07-2.929a10 10 0 0 0 2.583-4.491a1 1 0 0 0-1.224-1.224m-2.772 4.301a7.95 7.95 0 0 1-5.656 2.343a7.95 7.95 0 0 1-5.658-2.344c-3.118-3.119-3.118-8.195 0-11.314a8 8 0 0 1 2.06-1.483a10.03 10.03 0 0 0 2.89 7.848a9.97 9.97 0 0 0 7.848 2.891a8 8 0 0 1-1.484 2.059"/> 185 </svg> 186 187 <svg class="theme-icon-sun" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 188 <circle cx="12" cy="12" r="4"/> 189 <path d="M12 2v2"/> 190 <path d="M12 20v2"/> 191 <path d="m4.93 4.93 1.41 1.41"/> 192 <path d="m17.66 17.66 1.41 1.41"/> 193 <path d="M2 12h2"/> 194 <path d="M20 12h2"/> 195 <path d="m6.34 17.66-1.41 1.41"/> 196 <path d="m19.07 4.93-1.41 1.41"/> 197 </svg> 198 </button> 199 <button class="btn-icon btn-search search-btn" id="btnSearch" type="button" aria-label="æç´¢"> 200 <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" 201 stroke-linecap="round" stroke-linejoin="round"> 202 <circle cx="11" cy="11" r="8"></circle> 203 <line x1="21" y1="21" x2="16.65" y2="16.65"></line> 204 </svg> 205 </button> 206 <button class="btn-icon btn-nav-toggle mobile-only" id="btnNavToggle" type="button" aria-label="忢坼èª"> 207 <div class="hamburger" id="hamburger"> 208 <span class="hamburger__line hamburger__line--top"></span> 209 <span class="hamburger__line hamburger__line--bottom"></span> 210 </div> 211 </button> 212 </div> 213 214 </div> 215 </div> 216</header> 217 218 219<div class="header-spacer"></div> 220
221<script> 222(function(){ 223 // Mobile nav toggle 224 var btn = document.getElementById('btnNavToggle'); 225 var nav = document.getElementById('sectionBar'); 226 var hamburger = document.getElementById('hamburger'); 227 if (!btn || !nav) return; 228 btn.addEventListener('click', function(){ 229 nav.classList.toggle('top-bar__nav--open'); 230 if (hamburger) hamburger.classList.toggle('is-active'); 231 }); 232 233 // Submenu toggle for mobile 234 var navItemsWithChildren = document.querySelectorAll('.nav-item--has-children'); 235 navItemsWithChildren.forEach(function(item) { 236 item.addEventListener('click', function(e) { 237 // Only handle click on mobile 238 if (window.innerWidth > 768) return; 239 240 e.preventDefault(); 241 e.stopPropagation(); 242 243 var menuId = this.getAttribute('data-menu-id'); 244 var submenu = document.getElementById('submenu-' + menuId); 245 246 // Close other open submenus 247 navItemsWithChildren.forEach(function(otherItem) { 248 if (otherItem !== item) { 249 var otherMenuId = otherItem.getAttribute('data-menu-id'); 250 var otherSubmenu = document.getElementById('submenu-' + otherMenuId); 251 if (otherSubmenu && otherSubmenu.classList.contains('nav-submenu--open')) { 252 otherSubmenu.classList.remove('nav-submenu--open'); 253 otherItem.classList.remove('nav-item--expanded'); 254 } 255 } 256 }); 257 258 // Toggle current submenu 259 if (submenu) { 260 submenu.classList.toggle('nav-submenu--open'); 261 this.classList.toggle('nav-item--expanded'); 262 } 263 }); 264 }); 265 266 // Close mobile nav when clicking on simple nav items 267 nav.querySelectorAll('.nav-item:not(.nav-item--has-children)').forEach(function(item){ 268 item.addEventListener('click', function(){ 269 nav.classList.remove('top-bar__nav--open'); 270 if (hamburger) hamburger.classList.remove('is-active'); 271 }); 272 }); 273 274 // Theme toggle with performance optimization 275 var themeBtn = document.getElementById('btnTheme'); 276 if (themeBtn) { 277 themeBtn.addEventListener('click', function() { 278 var currentTheme = document.documentElement.getAttribute('data-theme') || 'dark'; 279 var newTheme = currentTheme === 'dark' ? 'light' : 'dark'; 280 281 // Add class to disable transitions during theme change 282 document.body.classList.add('theme-changing'); 283 284 // Use requestAnimationFrame for smooth theme switch 285 requestAnimationFrame(function() { 286 document.documentElement.setAttribute('data-theme', newTheme); 287 localStorage.setItem('theme', newTheme); 288 289 // Remove the class after theme change is complete 290 requestAnimationFrame(function() { 291 document.body.classList.remove('theme-changing'); 292 }); 293 }); 294 }); 295 } 296})(); 297</script>
297 298 299 300 <main class="site-main" role="main"> 301 302 303 304 305<section class="hero-section" style="--hero-wave-color: #ffffff"> 306 <img class="hero-image" src="https://jsd.268682.xyz/gh/Kemeow0815/img@main/img/bg.webp" alt="" /> 307 <div class="hero-overlay"></div> 308 <div class="hero-content"> 309 310 <h2 class="hero-title clamp-3">æ¯ä¸æ®µæ è¡ï¼é½æç»ç¹ã</h2> 311 312 313 <p class="hero-subtitle">æå¨è¿ä¸ªå客å享æççæ´»æ¥å¸¸ã踩åè®°å½åèµæºå享ã</p> 314 315 316 </div> 317 318 <div class="hero-wave"> 319 <svg class="wave wave-1" viewBox="0 0 1200 120" preserveAspectRatio="none"> 320 <path d="M0,60 C200,90 400,30 600,60 C800,90 1000,30 1200,60 L1200,120 L0,120 Z" class="wave-path wave-path-1"/> 321 </svg> 322 <svg class="wave wave-2" viewBox="0 0 1200 120" preserveAspectRatio="none"> 323 <path d="M0,40 C150,70 350,10 600,40 C850,70 1050,10 1200,40 L1200,120 L0,120 Z" class="wave-path wave-path-2"/> 324 </svg> 325 <svg class="wave wave-3" viewBox="0 0 1200 120" preserveAspectRatio="none"> 326 <path d="M0,20 C300,50 500,0 600,20 C700,40 900,0 1200,20 L1200,120 L0,120 Z" class="wave-path wave-path-3"/> 327 </svg> 328 </div> 329 330</section> 331 332 333<div class="container"> 334 335 336 337<div class="bb-time-list" id="bbTimeList"> 338 <i class="bb-time-list__icon bb-time-list__icon--newspaper" onclick="window.location.href='https://memos.268682.xyz'"> 339 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> 340 <path d="M19 20H5a2 2 0 01-2-2V6a2 2 0 012-2h10a2 2 0 012 2v1m2 13a2 2 0 01-2-2V7m2 13a2 2 0 002-2V9a2 2 0 00-2-2h-2m-4-3H9M7 16h6M7 8h6v4H7V8z"/> 341 </svg> 342 </i> 343 <div class="bb-time-list__container" id="bbTalk" onclick="window.location.href='https://memos.268682.xyz'"> 344 <div class="bb-time-list__wrapper" id="bberTalk"> 345 <div class="bb-time-list__item">å è½½ä¸...</div> 346 </div> 347 </div> 348 <i class="bb-time-list__icon bb-time-list__icon--arrow" title="æ¥çæ´å¤" onclick="window.location.href='https://memos.268682.xyz'"> 349 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> 350 <path d="M9 5l7 7-7 7"/> 351 </svg> 352 </i> 353</div> 354
355<script> 356// BBTime Config Debug - Raw values from theme_config 357console.log('[BBTime Debug] Raw theme_config.bbTimeDataSource:', "memos"); 358console.log('[BBTime Debug] Raw theme_config.bbTimeMemosUrl:', "https://m.314926.xyz"); 359 360(function() { 361 // é ç½® - ä½¿ç¨æ´æç¡®çé»è®¤å¼å¤ç 362 var rawDataSource = "memos"; 363 var rawMemosUrl = "https://m.314926.xyz"; 364 var rawGithubUrl = "https://jsd.268682.xyz/gh/Kemeow0815/ghapi-json-generator@output/v2/repos/Kemeow0815/blog-timeline/issues%3F%3Fstate%3Dall%26labels%3D%E6%97%A5%E5%B8%B8%26per_page%3D20/data.json"; 365 var rawMemosToken = ""; 366 var rawMemosFilter = ""; 367 var rawCustomUrl = ""; 368 var rawInterval = "3"; 369 var rawMaxItems = "10"; 370 371 // å¤ç空å¼ï¼Pongo2 å¯è½è¾åºç©ºå符串è䏿¯ä½¿ç¨é»è®¤å¼ï¼ 372 var CONFIG = { 373 dataSource: rawDataSource || 'github', 374 githubUrl: rawGithubUrl || 'https://jsd.268682.xyz/gh/Kemeow0815/ghapi-json-generator@output/v2/repos/Kemeow0815/blog-timeline/issues%3F%3Fstate%3Dall%26labels%3D%E6%97%A5%E5%B8%B8%26per_page%3D20/data.json', 375 memosUrl: rawMemosUrl || '', 376 memosToken: rawMemosToken || '', 377 memosFilter: rawMemosFilter || '', 378 customUrl: rawCustomUrl || '', 379 interval: (parseInt(rawInterval) || 3) * 1000, 380 maxItems: parseInt(rawMaxItems) || 10 381 }; 382 383 // è°è¯ä¿¡æ¯ 384 console.log('[BBTime] Final CONFIG:', CONFIG); 385 console.log('[BBTime] dataSource:', CONFIG.dataSource); 386 console.log('[BBTime] memosUrl:', CONFIG.memosUrl); 387 388 var currentIndex = 0; 389 var items = []; 390 var timer = null; 391 392 // è·å API URL 393 function getApiUrl() { 394 switch (CONFIG.dataSource) { 395 case 'memos': 396 if (!CONFIG.memosUrl) { 397 console.error('Memos URL is not configured'); 398 return null; 399 } 400 var url = CONFIG.memosUrl.replace(/\/$/, '') + '/api/v1/memos?pageSize=' + CONFIG.maxItems; 401 if (CONFIG.memosFilter) { 402 url += '&filter=' + encodeURIComponent(CONFIG.memosFilter); 403 } 404 return url; 405 case 'custom': 406 return CONFIG.customUrl || null; 407 case 'github': 408 default: 409 return CONFIG.githubUrl; 410 } 411 } 412 413 // è·å请æ±å¤´ 414 function getHeaders() { 415 var headers = {}; 416 if (CONFIG.dataSource === 'memos' && CONFIG.memosToken) { 417 headers['Authorization'] = 'Bearer ' + CONFIG.memosToken; 418 } 419 return headers; 420 } 421 422 // è·åæ°æ® 423 async function fetchData() { 424 var apiUrl = getApiUrl(); 425 if (!apiUrl) { 426 console.error('No API URL configured'); 427 return []; 428 } 429 430 try { 431 var response = await fetch(apiUrl, { headers: getHeaders() }); 432 if (!response.ok) throw new Error('Network response was not ok'); 433 var data = await response.json(); 434 return data; 435 } catch (error) { 436 console.error('Failed to fetch bbTime data:', error); 437 return []; 438 } 439 } 440 441 // å¤ç GitHub Issues æ°æ® 442 function processGithubData(data) { 443 if (!Array.isArray(data)) return []; 444 return data.slice(0, CONFIG.maxItems).map(function(item) { 445 var content = item.body || item.title || ''; 446 content = content.replace(/<[^>]*>/g, ''); 447 if (content.length > 50) { 448 content = content.substring(0, 50) + '...'; 449 } 450 return { 451 content: content, 452 title: item.title || '', 453 hasImage: item.body && item.body.includes('<img'), 454 hasLink: item.body && item.body.includes('http'), 455 createdAt: item.created_at 456 }; 457 }).filter(function(item) { 458 return item.content.trim().length > 0; 459 }); 460 } 461 462 // å¤ç Memos æ°æ® 463 function processMemosData(data) { 464 // Memos API è¿åæ ¼å¼: { memos: [...] } 465 var memos = data.memos || data; 466 if (!Array.isArray(memos)) return []; 467 468 return memos.slice(0, CONFIG.maxItems).map(function(item) { 469 var content = item.content || ''; 470 // å»é¤ Markdown å¾çè¯æ³  471 content = content.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, ''); 472 // å»é¤ HTML æ ç¾ 473 content = content.replace(/<[^>]*>/g, ''); 474 // å»é¤å¤ä½ç©ºç½ 475 content = content.replace(/\s+/g, ' ').trim(); 476 477 if (content.length > 50) { 478 content = content.substring(0, 50) + '...'; 479 } 480 481 // æ£æ¥æ¯å¦æèµæºï¼å¾çï¼ 482 var hasImage = item.resources && item.resources.length > 0 && 483 item.resources.some(function(r) { return r.type && r.type.startsWith('image/'); }); 484 485 // æ£æ¥æ¯å¦æé¾æ¥ 486 var hasLink = /https?:\/\/[^\s]+/.test(item.content || ''); 487 488 return { 489 content: content, 490 title: '', 491 hasImage: hasImage, 492 hasLink: hasLink, 493 createdAt: item.createTime || item.create_time 494 }; 495 }).filter(function(item) { 496 return item.content.trim().length > 0; 497 }); 498 } 499 500 // å¤çèªå®ä¹ JSON æ°æ® 501 function processCustomData(data) { 502 if (!Array.isArray(data)) return []; 503 return data.slice(0, CONFIG.maxItems).map(function(item) { 504 var content = item.content || ''; 505 content = content.replace(/<[^>]*>/g, ''); 506 if (content.length > 50) { 507 content = content.substring(0, 50) + '...'; 508 } 509 return { 510 content: content, 511 title: item.title || '', 512 hasImage: item.hasImage || false, 513 hasLink: item.hasLink || false, 514 createdAt: item.date || item.createdAt || '' 515 }; 516 }).filter(function(item) { 517 return item.content.trim().length > 0; 518 }); 519 } 520 521 // å¤çæ°æ® 522 function processData(data) { 523 switch (CONFIG.dataSource) { 524 case 'memos': 525 return processMemosData(data); 526 case 'custom': 527 return processCustomData(data); 528 case 'github': 529 default: 530 return processGithubData(data); 531 } 532 } 533 534 // 渲æå项 535 function renderItem(item) { 536 var html = '<div class="bb-time-list__slide">'; 537 html += '<span class="bb-time-list__content">' + escapeHtml(item.content) + '</span>'; 538 539 if (item.hasImage) { 540 html += '<i class="bb-time-list__badge bb-time-list__badge--image"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/></svg></i>'; 541 } else if (item.hasLink) { 542 html += '<i class="bb-time-list__badge bb-time-list__badge--link"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M10 13a5 5 0 007.54.54l3-3a5 5 0 00-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 00-7.54-.54l-3 3a5 5 0 007.07 7.07l1.71-1.71"/></svg></i>'; 543 } 544 545 html += '</div>'; 546 return html; 547 } 548 549 // HTML è½¬ä¹ 550 function escapeHtml(text) { 551 var div = document.createElement('div'); 552 div.textContent = text; 553 return div.innerHTML; 554 } 555 556 // 渲æè½®æ 557 function renderCarousel() { 558 var container = document.getElementById('bberTalk'); 559 if (!container || items.length === 0) return; 560 561 if (items.length === 1) { 562 container.innerHTML = renderItem(items[0]); 563 return; 564 } 565 566 var displayItems = items.concat([items[0]]); 567 var html = displayItems.map(renderItem).join(''); 568 container.innerHTML = html; 569 } 570 571 // 忢尿å®ç´¢å¼ 572 function goToIndex(index) { 573 if (items.length <= 1) return; 574 575 var container = document.getElementById('bberTalk'); 576 if (!container) return; 577 578 var slides = container.querySelectorAll('.bb-time-list__slide'); 579 if (slides.length === 0) return; 580 581 if (index >= items.length) { 582 container.style.transition = 'transform 0.5s ease'; 583 container.style.transform = 'translateY(-' + (index * 100) + '%)'; 584 585 setTimeout(function() {
586 container.style.transition = 'none'; 587 container.style.transform = 'translateY(0)'; 588 currentIndex = 0; 589 }, 500); 590 } else { 591 container.style.transition = 'transform 0.5s ease'; 592 container.style.transform = 'translateY(-' + (index * 100) + '%)'; 593 currentIndex = index; 594 } 595 } 596 597 // ä¸ä¸æ¡ 598 function next() { 599 goToIndex(currentIndex + 1); 600 } 601 602 // å¼å§è½®æ 603 function start() { 604 if (timer) clearInterval(timer); 605 if (items.length > 1) { 606 timer = setInterval(next, CONFIG.interval); 607 } 608 } 609 610 // åæ¢è½®æ 611 function stop() { 612 if (timer) { 613 clearInterval(timer); 614 timer = null; 615 } 616 } 617 618 // åå§å 619 async function init() { 620 var data = await fetchData(); 621 items = processData(data); 622 623 if (items.length === 0) { 624 document.getElementById('bbTimeList').style.display = 'none'; 625 return; 626 } 627 628 renderCarousel(); 629 start(); 630 631 var container = document.getElementById('bbTimeList'); 632 if (container && items.length > 1) { 633 container.addEventListener('mouseenter', stop); 634 container.addEventListener('mouseleave', start); 635 } 636 } 637 638 if (document.readyState === 'loading') { 639 document.addEventListener('DOMContentLoaded', init); 640 } else { 641 init(); 642 } 643})(); 644</script>
644 645 646 647 648 649 650 <section class="section-popular"> 651 <div class="section-header"> 652 <h2 class="section-title">çé¨æç« </h2> 653 <a href="/post" class="section-more meta-text">æ¥çå ¨é¨ →</a> 654 </div> 655 <div class="compact-grid"> 656 657 <article class="post-card post-card--compact"> 658 <a href="/post/timetable-guide/" class="post-card__inner"> 659 <div class="post-card__body"> 660 <h3 class="post-card__title clamp-2">Astro å客课ç¨è¡¨é¡µé¢å®ç°æç¨</h3> 661 <div class="post-card__meta meta-text">2026-04-13</div> 662 </div> 663 664 <img class="post-card__thumb-square" src="https://imgbed.050815.xyz/file/cover/timetable/timetable__1_.webp" alt="Astro å客课ç¨è¡¨é¡µé¢å®ç°æç¨" loading="lazy" /> 665 666 </a> 667</article> 668 669 <article class="post-card post-card--compact"> 670 <a href="/post/use-neon-database-with-waline-on-vercel/" class="post-card__inner"> 671 <div class="post-card__body"> 672 <h3 class="post-card__title clamp-2">waline使ç¨neonæ°æ®åºåå¨vercelé¨ç½²</h3> 673 <div class="post-card__meta meta-text">2026-04-11</div> 674 </div> 675 676 <img class="post-card__thumb-square" src="https://jsd.268682.xyz/gh/zsxcoder/github-img@main/img/waline-neon-deploy.avif" alt="waline使ç¨neonæ°æ®åºåå¨vercelé¨ç½²" loading="lazy" /> 677 678 </a> 679</article> 680 681 <article class="post-card post-card--compact"> 682 <a href="/post/post-diff-guide/" class="post-card__inner"> 683 <div class="post-card__body"> 684 <h3 class="post-card__title clamp-2">æ°æç« éç¥è®°å½</h3> 685 <div class="post-card__meta meta-text">2026-04-11</div> 686 </div> 687 688 <img class="post-card__thumb-square" src="https://jsd.268682.xyz/gh/zsxcoder/github-img@main/img/post-diff-update.avif" alt="æ°æç« éç¥è®°å½" loading="lazy" /> 689 690 </a> 691</article> 692 </div> 693 </section> 694 695 696 <div class="content-grid"> 697 <div class="main-content"> 698 699 700 <section class="section-latest"> 701 <div class="section-header"> 702 <h2 class="section-title">ææ°æç« </h2> 703 </div> 704 <div class="post-list"> 705 706 <article class="post-card post-card--list"> 707 <div class="post-card__body"> 708 <a href="/post/timetable-guide/" class="post-card__title-link"> 709 <h3 class="post-card__title clamp-3">Astro å客课ç¨è¡¨é¡µé¢å®ç°æç¨</h3> 710 </a> 711 <a href="/post/timetable-guide/" class="post-card__excerpt clamp-2"> 712 Astro å客课ç¨è¡¨é¡µé¢å®ç°æç¨ 713åè¨ 714æ¬æè¯¦ç»è®²è§£å¦ä½ä½¿ç¨ Astro + TypeScript æå»ºä¸ä¸ªåè½å®æ´ç课ç¨è¡¨é¡µé¢ï¼å æ¬æ°æ®ç»æè®¾è®¡ãæ°æ®è§£æãç»ä»¶æ¶æçæ ¸å¿ç¥è¯ç¹ã 715æ´ä½æ¶æ 716课ç¨è¡¨é¡µé¢éç¨åå±è®¾è®¡ï¼ 717 718æ°æ®å± - ... 719 </a> 720 <div class="post-card__meta meta-text"> 721 2026-04-13 722 723 </div> 724 </div> 725 726 <a href="/post/timetable-guide/" class="post-card__thumb-link"> 727 <img class="post-card__thumb" src="https://imgbed.050815.xyz/file/cover/timetable/timetable__1_.webp" alt="Astro å客课ç¨è¡¨é¡µé¢å®ç°æç¨" loading="lazy" /> 728 </a> 729 730</article> 731 732 733 <article class="post-card post-card--list"> 734 <div class="post-card__body"> 735 <a href="/post/use-neon-database-with-waline-on-vercel/" class="post-card__title-link"> 736 <h3 class="post-card__title clamp-3">waline使ç¨neonæ°æ®åºåå¨vercelé¨ç½²</h3> 737 </a> 738 <a href="/post/use-neon-database-with-waline-on-vercel/" class="post-card__excerpt clamp-2"> 739 æç« ä»ç»äºä½¿ç¨Walineåneonæ°æ®åºå¨Vercelä¸é¨ç½²å客çè¿ç¨ãé¦å ï¼éè¦åå»ºæ°æ®åºå¹¶é ç½®å卿å¡ï¼ç¶åå®ä¹æ°æ®åºåç§°å¹¶æ§è¡å建表æä½ãæ¥ä¸æ¥ï¼éè¿ç¹å»é¡¶é¨æé®éæ°é¨ç½²ï¼ä½¿æ°æ®åºæå¡çæãæåï¼æ·»å ç¯å¢åéå¹¶è¿å ¥é¡¹ç®é¡µé¢è¿è¡... 740 </a> 741 <div class="post-card__meta meta-text"> 742 2026-04-11 743 744 </div> 745 </div> 746 747 <a href="/post/use-neon-database-with-waline-on-vercel/" class="post-card__thumb-link"> 748 <img class="post-card__thumb" src="https://jsd.268682.xyz/gh/zsxcoder/github-img@main/img/waline-neon-deploy.avif" alt="waline使ç¨neonæ°æ®åºåå¨vercelé¨ç½²" loading="lazy" /> 749 </a> 750 751</article> 752 753 754 <article class="post-card post-card--list"> 755 <div class="post-card__body"> 756 <a href="/post/post-diff-guide/" class="post-card__title-link"> 757 <h3 class="post-card__title clamp-3">æ°æç« éç¥è®°å½</h3> 758 </a> 759 <a href="/post/post-diff-guide/" class="post-card__excerpt clamp-2"> 760 æç« ä»ç»äºåå®¢æ°æç« éç¥åè½çå®ç°åçï¼å æ¬ RSS æ°æ®è·åãIndexedDB åå¨ãæç« åæ´æ£æµãå·®å¼å¯¹æ¯ç®æ³çæ ¸å¿é»è¾ãéè¿å¯¹æ¯æ°æ§æç« æ°æ®ï¼èªå¨æ£æµæ°å¢åæ´æ°çæç« ï¼å¹¶ä»¥å¨ç»å¼¹çªå½¢å¼éç¥ç¨æ·ã 761 762æ°æç« éç¥è®°å½ 763 764ææ¶åï¼... 765 </a> 766 <div class="post-card__meta meta-text"> 767 2026-04-11 768 769 </div> 770 </div> 771 772 <a href="/post/post-diff-guide/" class="post-card__thumb-link"> 773 <img class="post-card__thumb" src="https://jsd.268682.xyz/gh/zsxcoder/github-img@main/img/
773post-diff-update.avif" alt="æ°æç« éç¥è®°å½" loading="lazy" /> 774 </a> 775 776</article> 777 778 779 </div> 780 </section> 781 782 783 784 785 </div> 786 787 788 789 <aside class="sidebar"> 790 <div class="sidebar__section sidebar__about"> 791 792 <img class="sidebar__avatar" src="https://cdn.268682.xyz/images/avatar.png" alt="嵿´é" /> 793 794 <h3 class="sidebar__title">嵿´é</h3> 795 796 <p class="sidebar__desc">æ¯ä¸æ®µæ è¡ï¼é½æç»ç¹ã</p> 797 798 799 <p class="sidebar__bio">æ¬¢è¿æ¥å°å åµçå客ï¼</p> 800 801 </div> 802 803 804 <div class="sidebar__section"> 805 <h4 class="sidebar__section-title meta-text">课ç¨è¡¨</h4> 806 <link rel="stylesheet" href="/styles/timetable.css"> 807 <a href="/post/timetable" class="timetable-card" id="timetable-card" style="display: none;"> 808 <div class="timetable-card__inner"> 809 <div class="timetable-card__loading" id="timetable-card-loading"> 810 <div class="timetable-card__skeleton"></div> 811 <div class="timetable-card__skeleton timetable-card__skeleton--medium"></div> 812 <div class="timetable-card__skeleton timetable-card__skeleton--short"></div> 813 </div> 814 <div class="timetable-card__content" id="timetable-card-content" style="display: none;"> 815 <div class="timetable-card__status" id="timetable-card-status">å è½½ä¸...</div> 816 <div class="timetable-card__lines" id="timetable-card-lines"></div> 817 </div> 818 </div> 819</a> 820
821<script> 822(function() { 823 var timetableData = null; 824 825 // å è½½è¯¾è¡¨æ°æ® 826 async function loadTimetableData() { 827 try { 828 const paths = ['/timetable.json', '/assets/timetable.json']; 829 830 for (const path of paths) { 831 try { 832 const response = await fetch(path); 833 if (response.ok) { 834 timetableData = await response.json(); 835 return true; 836 } 837 } catch (e) { 838 console.log('Failed to load from', path); 839 } 840 } 841 842 return false; 843 } catch (error) { 844 console.error('Error loading timetable:', error); 845 return false; 846 } 847 } 848 849 // æå»ºæ å° 850 function buildMaps() { 851 var courseMap = {}; 852 if (timetableData && timetableData.courses) { 853 timetableData.courses.forEach(function(c) { 854 courseMap[c.id] = c; 855 }); 856 } 857 858 var timeMap = {}; 859 if (timetableData && timetableData.timeTable) { 860 timetableData.timeTable.forEach(function(t) { 861 timeMap[t.node] = t; 862 }); 863 } 864
865 return { courseMap: courseMap, timeMap: timeMap }; 866 } 867 868 // å¤çé¢è² 869 function parseColor(raw) { 870 if (!raw) return '#54675e'; 871 var str = String(raw); 872 if (str.length === 9 && str[0] === '#') { 873 return '#' + str.slice(3); 874 } 875 return str; 876 } 877 878 // 计ç®å½å卿¬¡ 879 function resolveCurrentWeek() { 880 if (!timetableData || !timetableData.settings || !timetableData.settings.startDate) return 1; 881 var startDateStr = timetableData.settings.startDate; 882 var parts = startDateStr.split('-'); 883 if (parts.length !== 3) return 1; 884 var startDate = new Date(parseInt(parts[0]), parseInt(parts[1]) - 1, parseInt(parts[2])); 885 var now = new Date(); 886 var diffTime = now.getTime() - startDate.getTime(); 887 var diffDays = Math.floor(diffTime / (1000 * 60 * 60 * 24)); 888 var week = Math.floor(diffDays / 7) + 1; 889 var maxWeek = timetableData.settings.maxWeek || 20; 890 return Math.max(1, Math.min(week, maxWeek)); 891 } 892 893 // è§£ææ¶é´ 894 function parseTimeToMinute(text) { 895 var parts = String(text || '').split(':'); 896 if (parts.length !== 2) return null; 897 var hour = parseInt(parts[0]); 898 var minute = parseInt(parts[1]); 899 if (isNaN(hour) || isNaN(minute)) return null; 900 return hour * 60 + minute; 901 } 902 903 // æåæ¶é´èå´ 904 function extractRangeMinutes(timeText) { 905 var match = String(timeText || '').match(/(\d{1,2}:\d{2})\s*-\s*(\d{1,2}:\d{2})/); 906 if (!match) return null; 907 var startMinute = parseTimeToMinute(match[1]); 908 var endMinute = parseTimeToMinute(match[2]); 909 if (startMinute === null || endMinute === null) return null; 910 return { startMinute: startMinute, endMinute: endMinute }; 911 } 912 913 // æ ¼å¼åæ¶é¿ 914 function formatDuration(totalSeconds) { 915 var safeSecs = Math.max(0, Math.floor(totalSeconds)); 916 var days = Math.floor(safeSecs / 86400); 917 var hours = Math.floor((safeSecs % 86400) / 3600); 918 var minutes = Math.floor((safeSecs % 3600) / 60); 919 var seconds = safeSecs % 60; 920 var parts = []; 921 if (days > 0) parts.push(days + '天'); 922 if (hours > 0) parts.push(hours + 'æ¶'); 923 if (minutes > 0) parts.push(minutes + 'åé'); 924 if (seconds > 0 || parts.length === 0) parts.push(seconds + 'ç§'); 925 return parts.join('ï¼'); 926 } 927 928 // è·å仿¥è¯¾ç¨ 929 function getTodayCourses(currentWeek, maps) { 930 var now = new Date(); 931 var day = now.getDay(); 932 if (day === 0) day = 7; 933 var todayCourses = []; 934 935 if (!timetableData || !timetableData.schedules) { 936 return todayCourses; 937 } 938 939 timetableData.schedules.forEach(function(schedule) { 940 if (schedule.day !== day) return; 941 if (schedule.startWeek > currentWeek || schedule.endWeek < currentWeek) return; 942 var courseDef = maps.courseMap[schedule.id]; 943 if (!courseDef) return; 944 var startNode = schedule.startNode; 945 var step = schedule.step || 1; 946 var endNode = startNode + step - 1; 947 var startNodeTime = maps.timeMap[startNode]; 948 var endNodeTime = maps.timeMap[endNode]; 949 if (!startNodeTime) return; 950 var startTime = startNodeTime.startTime; 951 var endTime = endNodeTime ? endNodeTime.endTime : startNodeTime.endTime; 952 var timeText = startTime + ' - ' + endTime; 953 var range = extractRangeMinutes(timeText); 954 if (!range) return; 955 todayCourses.push({ 956 courseId: schedule.id, 957 courseName: courseDef.courseName, 958 color: parseColor(courseDef.color), 959 teacher: schedule.teacher || 'æªå¡«å', 960 room: schedule.room || 'æªå¡«å', 961 day: day, 962 startNode: startNode, 963 endNode: endNode, 964 startWeek: schedule.startWeek, 965 endWeek: schedule.endWeek, 966 timeText: timeText, 967 startMinute: range.startMinute, 968 endMinute: range.endMinute 969 }); 970 }); 971 972 return todayCourses.sort(function(a, b) { 973 return a.startMinute - b.startMinute; 974 }); 975 } 976 977 // è·åæ¬å¨ææè¯¾ç¨ 978 function getAllCoursesThisWeek(currentWeek, maps) { 979 var allCourses = []; 980 if (!timetableData || !timetableData.schedules) { 981 return allCourses; 982 } 983 for (var d = 1; d <= 7; d++) {
984 timetableData.schedules.forEach(function(schedule) { 985 if (schedule.day !== d) return; 986 if (schedule.startWeek > currentWeek || schedule.endWeek < currentWeek) return; 987 var courseDef = maps.courseMap[schedule.id]; 988 if (!courseDef) return; 989 var startNode = schedule.startNode; 990 var step = schedule.step || 1; 991 var endNode = startNode + step - 1; 992 var startNodeTime = maps.timeMap[startNode]; 993 var endNodeTime = maps.timeMap[endNode]; 994 if (!startNodeTime) return; 995 var startTime = startNodeTime.startTime; 996 var endTime = endNodeTime ? endNodeTime.endTime : startNodeTime.endTime; 997 var timeText = startTime + ' - ' + endTime; 998 var range = extractRangeMinutes(timeText); 999 if (!range) return; 1000 allCourses.push({ 1001 courseId: schedule.id, 1002 courseName: courseDef.courseName, 1003 color: parseColor(courseDef.color), 1004 teacher: schedule.teacher || 'æªå¡«å', 1005 room: schedule.room || 'æªå¡«å', 1006 day: d, 1007 startNode: startNode, 1008 endNode: endNode, 1009 timeText: timeText, 1010 startMinute: range.startMinute, 1011 endMinute: range.endMinute 1012 }); 1013 }); 1014 } 1015 return allCourses.sort(function(a, b) { 1016 if (a.day !== b.day) return a.day - b.day; 1017 return a.startMinute - b.startMinute; 1018 }); 1019 } 1020
1021 // è§£æå®æ¶ç¶æ 1022 function resolveLiveState(currentWeek, maps) { 1023 var now = new Date(); 1024 var currentSecond = now.getHours() * 3600 + now.getMinutes() * 60 + now.getSeconds(); 1025 var currentMinute = Math.floor(currentSecond / 60); 1026 var day = now.getDay(); 1027 if (day === 0) day = 7; 1028 1029 if (day >= 6) { 1030 var allCourses = getAllCoursesThisWeek(currentWeek, maps); 1031 var nextWeekFirstCourse = null; 1032 for (var i = 0; i < allCourses.length; i++) { 1033 if (allCourses[i].day === 1) { 1034 nextWeekFirstCourse = allCourses[i]; 1035 break; 1036 } 1037 } 1038 if (nextWeekFirstCourse) { 1039 var daysUntilMonday = day === 6 ? 2 : 1; 1040 var secondsUntilCourse = daysUntilMonday * 86400 + nextWeekFirstCourse.startMinute * 60 - currentSecond; 1041 return { 1042 status: 'æ¬å¨è¯¾æ¯', 1043 lines: [ 1044 { label: 'ä¸å¨é¦èï¼', value: nextWeekFirstCourse.courseName + ' - ' + (nextWeekFirstCourse.room || 'æªç¥'), bold: true, color: nextWeekFirstCourse.color }, 1045 { label: 'è·ä¸è¯¾è¿æï¼', value: formatDuration(secondsUntilCourse), bold: true, countdown: true } 1046 ] 1047 }; 1048 } 1049 return { status: '卿«', lines: [] }; 1050 } 1051 1052 var courses = getTodayCourses(currentWeek, maps); 1053 1054 if (courses.length === 0) { 1055 var allCourses = getAllCoursesThisWeek(currentWeek, maps); 1056 var nextDayCourse = null; 1057 for (var i = 0; i < allCourses.length; i++) { 1058 if (allCourses[i].day > day) { 1059 nextDayCourse = allCourses[i]; 1060 break; 1061 } 1062 } 1063 if (nextDayCourse) { 1064 var daysUntil = nextDayCourse.day - day; 1065 var secondsUntilCourse = daysUntil * 86400 + nextDayCourse.startMinute * 60 - currentSecond; 1066 return { 1067 status: '仿¥è¯¾æ¯', 1068 lines: [ 1069 { label: 'ç¿æ¥é¦èï¼', value: nextDayCourse.courseName + ' - ' + (nextDayCourse.room || 'æªç¥'), bold: true, color: nextDayCourse.color }, 1070 { label: 'è·ä¸è¯¾è¿æï¼', value: formatDuration(secondsUntilCourse), bold: true, countdown: true } 1071 ] 1072 }; 1073 } 1074 return { status: '仿¥è¯¾æ¯', lines: [] }; 1075 } 1076 1077 for (var i = 0; i < courses.length; i++) { 1078 var current = courses[i]; 1079 var prev = i > 0 ? courses[i - 1] : null; 1080 var next = courses[i + 1] || null; 1081 1082 if (currentMinute >= current.startMinute && currentMinute < current.endMinute) { 1083 var remainSeconds = current.endMinute * 60 - currentSecond; 1084 var lines = [ 1085 { label: 'æ¬èï¼', value: current.courseName + ' - ' + (current.room || 'æªç¥'), bold: true, color: current.color }, 1086 { label: 'è·ä¸è¯¾è¿æï¼', value: formatDuration(remainSeconds), bold: true, countdown: true } 1087 ]; 1088 if (prev) { 1089 lines.unshift({ label: 'ä¸èï¼', value: prev.courseName + ' - ' + (prev.room || 'æªç¥'), strikethrough: true, color: prev.color }); 1090 } 1091 if (next) { 1092 lines.push({ label: 'ä¸èï¼', value: next.courseName + ' - ' + (next.room || 'æªç¥'), color: next.color }); 1093 } 1094 return { status: 'ä¸è¯¾', lines: lines }; 1095 } 1096 1097 if (next && currentMinute >= current.endMinute && currentMinute < next.startMinute) { 1098 var remainSeconds = next.startMinute * 60 - currentSecond; 1099 return { 1100 status: '课é´', 1101 lines: [ 1102 { label: 'ä¸èï¼', value: current.courseName + ' - ' + (current.room || 'æªç¥'), strikethrough: true, color: current.color }, 1103 { label: 'ä¸èï¼', value: next.courseName + ' - ' + (next.room || 'æªç¥'), bold: true, color: next.color }, 1104 { label: 'è·ä¸è¯¾è¿æï¼', value: formatDuration(remainSeconds), bold: true, countdown: true } 1105 ] 1106 }; 1107 } 1108 } 1109 1110 var lastCourse = courses[courses.length - 1]; 1111 if (currentMinute >= lastCourse.endMinute) { 1112 var allCourses = getAllCoursesThisWeek(currentWeek, maps); 1113 var nextDayCourse = null; 1114 for (var i = 0; i < allCourses.length; i++) { 1115 if (allCourses[i].day > day) { 1116 nextDayCourse = allCourses[i]; 1117 break; 1118 } 1119 } 1120 if (nextDayCourse) { 1121 var daysUntil = nextDayCourse.day - day; 1122 var secondsUntilCourse = daysUntil * 86400 + nextDayCourse.startMinute * 60 - currentSecond; 1123 return { 1124 status: '仿¥è¯¾æ¯', 1125 lines: [ 1126 { label: 'ç¿æ¥é¦èï¼', value: nextDayCourse.courseName + ' - ' + (nextDayCourse.room || 'æªç¥'), bold: true, color: nextDayCourse.color }, 1127 { label: 'è·ä¸è¯¾è¿æï¼', value: formatDuration(secondsUntilCourse), bold: true, countdown: true } 1128 ] 1129 }; 1130 } 1131 return { status: '仿¥è¯¾æ¯', lines: [] }; 1132 } 1133 1134 var firstCourse = courses[0]; 1135 var remainSeconds = firstCourse.startMinute * 60 - currentSecond; 1136 return { 1137 status: '课å', 1138 lines: [ 1139 { label: 'é¦èï¼', value: firstCourse.courseName + ' - ' + (firstCourse.room || 'æªç¥'), bold: true, color: firstCourse.color }, 1140 { label: 'è·ä¸è¯¾è¿æï¼', value: formatDuration(remainSeconds), bold: true, countdown: true } 1141 ] 1142 }; 1143 } 1144 1145 // æ´æ°å¡çæ¾ç¤º 1146 function updateCard(maps) {
1147 var currentWeek = resolveCurrentWeek(); 1148 var state = resolveLiveState(currentWeek, maps); 1149 1150 var loadingEl = document.getElementById('timetable-card-loading'); 1151 var contentEl = document.getElementById('timetable-card-content'); 1152 var statusEl = document.getElementById('timetable-card-status'); 1153 var linesEl = document.getElementById('timetable-card-lines'); 1154 1155 if (loadingEl) loadingEl.style.display = 'none'; 1156 if (contentEl) contentEl.style.display = 'block'; 1157 1158 if (statusEl) { 1159 statusEl.textContent = state.status; 1160 } 1161 1162 if (linesEl) { 1163 linesEl.innerHTML = ''; 1164 state.lines.forEach(function(line) { 1165 var lineDiv = document.createElement('div'); 1166 lineDiv.className = 'timetable-card__line'; 1167 1168 var labelSpan = document.createElement('span'); 1169 labelSpan.className = 'timetable-card__label'; 1170 labelSpan.textContent = line.label; 1171 lineDiv.appendChild(labelSpan); 1172 1173 var valueSpan = document.createElement('span'); 1174 valueSpan.className = 'timetable-card__value'; 1175 if (line.bold) valueSpan.classList.add('timetable-card__value--bold'); 1176 if (line.strikethrough) valueSpan.classList.add('timetable-card__value--strikethrough'); 1177 if (line.countdown) valueSpan.classList.add('timetable-card__countdown'); 1178 if (line.color) valueSpan.style.color = line.color; 1179 valueSpan.textContent = line.value; 1180 lineDiv.appendChild(valueSpan); 1181 1182 linesEl.appendChild(lineDiv); 1183 }); 1184 } 1185 } 1186 1187 // åå§å 1188 async function init() { 1189 var loaded = await loadTimetableData(); 1190 1191 if (!loaded) { 1192 // éèå¡ç 1193 var card = document.getElementById('timetable-card'); 1194 if (card) card.style.display = 'none'; 1195 return; 1196 } 1197 1198 // æ¾ç¤ºå¡ç 1199 var card = document.getElementById('timetable-card'); 1200 if (card) card.style.display = 'block'; 1201 1202 var maps = buildMaps(); 1203 updateCard(maps); 1204 setInterval(function() { updateCard(maps); }, 1000); 1205 } 1206 1207 if (document.readyState === 'loading') { 1208 document.addEventListener('DOMContentLoaded', init); 1209 } else { 1210 init(); 1211 } 1212})(); 1213</script>
1213 1214 1215 </div> 1216 1217 1218 <div class="sidebar__section"> 1219 <h4 class="sidebar__section-title meta-text">æ ç¾</h4> 1220 <div class="sidebar__tags"> 1221 1222 <a href="/tag/bo-ke/" class="tag-pill">å客</a> 1223 1224 <a href="/tag/gong-neng/" class="tag-pill">åè½</a> 1225 1226 <a href="/tag/w-a-l-i-n-e-ping-lun/" class="tag-pill">walineè¯è®º</a> 1227 1228 <a href="/tag/ye-mian/" class="tag-pill">页é¢</a> 1229 1230 </div> 1231 </div> 1232 1233 1234 </aside> 1235 1236 </div> 1237</div> 1238 1239 </main> 1240 1241 <footer class="site-footer"> 1242 <div class="footer-inner"> 1243 1244 1245 1246 1247 <nav class="footer-nav" aria-label="页è导èª" id="footer-nav-custom"> 1248 </nav> 1249
1249<script> 1250 (function() { 1251 var navData = [ 1252 {"name": "主页", "link": "https://www.kemiaosw.top"}, 1253 {"name": "BLog", "link": "https://kemeow0815.github.io/blog"}, 1254 {"name": "åé¾å±", "link": "https://www.268682.xyz"}, {"name": "Gwitter", "link": "https://gwitter.268682.xyz"} 1255]; 1256 if (Array.isArray(navData)) { 1257 var navHtml = ''; 1258 navData.forEach(function(item) { 1259 navHtml += '<a class="footer-nav__link" href="' + item.link + '">' + item.name + '</a>'; 1260 }); 1261 document.getElementById('footer-nav-custom').innerHTML = navHtml; 1262 } 1263 })(); 1264 </script>
1264 1265 1266 1267 1268 <div class="footer-social"> 1269 1270 <a class="footer-social__link" href="https://github.com/Kemeow0815" target="_blank" rel="noopener noreferrer" 1271 aria-label="GitHub"> 1272 <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"> 1273 <path 1274 d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0024 12c0-6.63-5.37-12-12-12z" /> 1275 </svg> 1276 </a> 1277 1278 1279 1280 1281 <a class="footer-social__link" href="https://t.me/Kemeow0815" target="_blank" rel="noopener noreferrer" 1282 aria-label="Telegram"> 1283 <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"> 1284 <path d="M12 0C5.37 0 0 5.37 0 12s5.37 12 12 12 12-5.37 12-12S18.63 0 12 0zm5.95 8.24l-2.17 10.24c-.16.73-.6.9-1.22.56l-3.37-2.48-1.63 1.56c-.18.18-.33.33-.68.33l.24-3.44 6.25-5.65c.27-.24-.06-.37-.42-.13l-7.72 4.86-3.33-1.04c-.72-.23-.74-.72.15-1.06l13.02-5.02c.6-.22 1.12.14.93 1.03z"/> 1285 </svg> 1286 </a> 1287 1288 1289 <a class="footer-social__link" href="https://qm.qq.com/q/kz5EuEimk0" target="_blank" rel="noopener noreferrer" 1290 aria-label="QQ"> 1291 <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"> 1292 <path d="M12.003 2c-2.265 0-6.29 1.364-6.29 7.325v1.195S3.55 14.96 3.55 17.474c0 .665.17 1.025.281 1.025.114 0 .902-.484 1.748-2.072 0 0-.18 2.197 1.904 3.967 0 0-1.77.495-1.77 1.182 0 .686 4.078.43 6.29.43 2.21 0 6.288.256 6.288-.43 0-.687-1.768-1.182-1.768-1.182 2.085-1.77 1.905-3.967 1.905-3.967.845 1.588 1.634 2.072 1.746 2.072.111 0 .283-.36.283-1.025 0-2.514-2.166-6.954-2.166-6.954V9.325C18.29 3.364 14.268 2 12.003 2z"/> 1293 </svg> 1294 </a> 1295 1296 1297 <a class="footer-social__link" href="https://space.bilibili.com/3546643173477234" target="_blank" rel="noopener noreferrer" 1298 aria-label="Bilibili"> 1299 <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"> 1300 <path d="M17.813 4.653h.854c1.51.054 2.769.578 3.773 1.574 1.004.995 1.524 2.249 1.56 3.76v7.36c-.036 1.51-.556 2.769-1.56 3.773s-2.262 1.524-3.773 1.56H5.333c-1.51-.036-2.769-.556-3.773-1.56S.036 18.858 0 17.347v-7.36c.036-1.511.556-2.765 1.56-3.76 1.004-.996 2.262-1.52 3.773-1.574h.774l-1.174-1.12a1.234 1.234 0 0 1-.373-.906c0-.356.124-.658.373-.907l.027-.027c.267-.249.573-.373.92-.373.347 0 .653.124.92.373L9.653 4.44c.071.071.134.142.187.213h4.267a.836.836 0 0 1 .16-.213l2.853-2.747c.267-.249.573-.373.92-.373.347 0 .662.151.929.4.267.249.391.551.391.907 0 .355-.124.657-.373.906zM5.333 7.24c-.746.018-1.373.276-1.88.773-.506.498-.769 1.13-.786 1.894v7.52c.017.764.28 1.395.786 1.893.507.498 1.134.756 1.88.773h13.334c.746-.017 1.373-.275 1.88-.773.506-.498.769-1.129.786-1.893v-7.52c-.017-.765-.28-1.396-.786-1.894-.507-.497-1.134-.755-1.88-.773zM8 11.107c.373 0 .684.124.933.373.25.249.383.569.4.96v1.173c-.017.391-.15.711-.4.96-.249.25-.56.374-.933.374s-.684-.125-.933-.374c-.25-.249-.383-.569-.4-.96V12.44c0-.373.129-.689.386-.947.258-.257.574-.386.947-.386zm8 0c.373 0 .684.124.933.373.25.249.383.569.4.96v1.173c-.017.391-.15.711-.4.96-.249.25-.56.374-.933.374s-.684-.125-.933-.374c-.25-.249-.383-.569-.4-.96V12.44c.017-.391.15-.711.4-.96.249-.249.56-.373.933-.373z"/> 1301 </svg> 1302 </a> 1303 1304 1305 <a class="footer-social__link" href="https://web.okjike.com/u/07152f0c-0f65-4501-855b-031f3e20e4a5" target="_blank" rel="noopener noreferrer" 1306 aria-label="å³å»"> 1307 <svg width="20" height="20" viewBox="0 0 1024 1024" fill="currentColor"> 1308 <path d="M660.39 123.63v463.59S645.04 726.4 558.98 771.43c-53.12 31.21-128.85 67.54-228.63 70.61l58.33 58.33s175-15.35 254.82-87.5 82.89-159.9 82.89-194.31V172.75l-66-49.12z" fill="#62B2E4"/> 1309 <path d="M660.39 123.63v463.58s-15.36 139.17-101.41 184.21c-53.12 31.22-128.85 67.54-228.62 70.62L297.6 702.85s70.62-13.31 117.71-28.64c47.07-15.36 72-95.19 72-95.19V123.63h173.08z" fill="#F3DB2B"/> 1310 </svg> 1311 </a> 1312 1313 1314 <a class="footer-social__link" href="mailto:[email protected]" aria-label="é®ç®±"> 1315 <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"> 1316 <path d="M20 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z"/> 1317 </svg> 1318 </a> 1319 1320 <a class="footer-social__link" href="/feed.xml" aria-label="RSS"> 1321 <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"> 1322 <path d="M6.18 15.64a2.18 2.18 0 110 4.36 2.18 2.18 0 010-4.36zM4 4.44A15.56 15.56 0 0119.56 20h-2.83A12.73 12.73 0 004 7.27V4.44zm0 5.66a9.9 9.9 0 019.9 9.9h-2.83A7.07 7.07 0 004 12.93V10.1z"/> 1323 </svg> 1324 </a> 1325 </div> 1326 1327 1328 <div class="footer-copyright"> 1329 <p>© 2026 嵿´é. All rights reserved.</p> 1330 </div> 1331 1332 1333 1334 <div class="footer-custom">
1335 <span id="runtime_span" style="text-align: center;"></span> 1336<br>
1337<script defer src="https://busuanzi.9420.ltd/js"></script>
1337 1338<div style="text-align: center;"> 1339 æ¬ç«æ»è®¿é®é <span id="busuanzi_site_pv"></span> 次 1340 æ¬ç«æ»è®¿å®¢æ° <span id="busuanzi_site_uv"></span> 人 1341</div> 1342<!-- å¼å ¥ CSS (HEADæ ç¾å ) --> 1343<link rel="stylesheet" href="https://cdn.268682.xyz/music/netease-mini-player-v2.css"> 1344 1345<!-- å¼å ¥ JS (BODYæ ç¾åºé¨) -->
1346<script src="https://cdn.268682.xyz/music/netease-mini-player-v2.js"></script>
1346 1347<div class="netease-mini-player" 1348 data-playlist-id="13681647281" 1349 data-embed="false" 1350 data-position="bottom-left" 1351 data-lyric="true" 1352 data-theme="auto" 1353 data-default-minimized="true" 1354></div> 1355 </div> 1356 1357 1358 1359 <div class="footer-powered"> 1360 <span>Powered by <a href="https://gridea.pro" target="_blank" rel="noopener noreferrer">Gridea Pro</a></span> 1361 </div> 1362 1363 </div> 1364</footer> 1365 1366 1367 <div class="search-overlay" id="searchOverlay" style="display:none;"> 1368 <div class="search-modal"> 1369 <div class="search-header"> 1370 <svg class="search-icon" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" 1371 stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 1372 <circle cx="11" cy="11" r="8"></circle> 1373 <line x1="21" y1="21" x2="16.65" y2="16.65"></line> 1374 </svg> 1375 <input type="text" class="search-input" id="searchInput" placeholder="æç´¢æç« ..." autocomplete="off" /> 1376 <span class="search-kbd">⌘K</span> 1377 <button class="search-close" id="searchClose" type="button" aria-label="å ³éæç´¢"> 1378 <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" 1379 stroke-linecap="round" stroke-linejoin="round"> 1380 <line x1="18" y1="6" x2="6" y2="18"></line> 1381 <line x1="6" y1="6" x2="18" y2="18"></line> 1382 </svg> 1383 </button> 1384 </div> 1385 <div class="search-results" id="searchResults"></div> 1386 </div> 1387</div> 1388
1389<script src="/scripts/search.js"></script>
1389 1390 1391 1392 1393 1394 1395 1396 1397
1397<script>function show_runtime() { 1398 var X = new Date("2024/11/1 12:00:00"); 1399 var Y = new Date(); 1400 var T = Y.getTime() - X.getTime(); 1401 var M = 24 * 60 * 60 * 1000; 1402 var a = T / M; 1403 var A = Math.floor(a); 1404 var b = (a - A) * 24; 1405 var B = Math.floor(b); 1406 var c = (b - B) * 60; 1407 var C = Math.floor((b - B) * 60); 1408 var D = Math.floor((c - C) * 60); 1409 document.getElementById("runtime_span").innerHTML = "â±ï¸æ¬ç«å·²è¿è¡ " + A + "天" + B + "å°æ¶" + C + "å" + D + "ç§"; 1410 setTimeout(show_runtime, 1000); 1411} 1412show_runtime();</script>
1412 1413 1414 1415 <!-- å 载页é¢èæ¬ --> 1416 1417
1417<script> 1418 (function() { 1419 // 页é¢å è½½å®æåéèå è½½å¨ç» 1420 function hideLoadingOverlay() { 1421 const overlay = document.getElementById('loading-overlay'); 1422 if (overlay) { 1423 overlay.classList.add('fade-out'); 1424 setTimeout(function() { 1425 overlay.style.display = 'none'; 1426 }, 600); 1427 } 1428 } 1429 1430 // çå¾ é¡µé¢å®å ¨å è½½ 1431 if (document.readyState === 'complete') { 1432 hideLoadingOverlay(); 1433 } else { 1434 window.addEventListener('load', hideLoadingOverlay); 1435 } 1436 })(); 1437 </script>
1437 1438 1439 1440 <!-- æµæèæ¯èæ¬ - æ§è½ä¼å --> 1441
1441<script> 1442 (function() { 1443 const starsContainer = document.getElementById('bg-stars'); 1444 if (!starsContainer) return; 1445 1446 // æ£æµç§»å¨ç«¯åæ§è½æ¨¡å¼ 1447 const isMobile = window.matchMedia('(max-width: 750px)').matches; 1448 const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches; 1449 1450 // æ ¹æ®è®¾å¤è°æ´æµææ°é 1451 const starCount = isMobile ? 15 : (prefersReducedMotion ? 0 : 25); 1452 1453 // ä½¿ç¨ DocumentFragment æ¹éæ·»å 1454 const fragment = document.createDocumentFragment(); 1455 1456 for (let i = 0; i < starCount; i++) { 1457 const star = document.createElement('div'); 1458 star.className = 'bg-star'; 1459 1460 // éæºåæ° - ä½¿ç¨æ´æ°é¿å è¿å¤éç» 1461 const topOffset = Math.floor(Math.random() * 130) + 'vh'; 1462 const fallDuration = (Math.floor(Math.random() * 8) + 6) + 's'; 1463 const fallDelay = (Math.floor(Math.random() * 15)) + 's'; 1464 1465 star.style.setProperty('--top-offset', topOffset); 1466 star.style.setProperty('--fall-duration', fallDuration); 1467 star.style.setProperty('--fall-delay', fallDelay); 1468 1469 fragment.appendChild(star); 1470 } 1471 1472 starsContainer.appendChild(fragment); 1473 })(); 1474 </script>
1474 1475
1476<script>console.log("\n%c ⦠Gridea Pro v1.1.1 %c · Theme: flavor-theme v1.0.0 · https://gridea.pro %c\n","color:#fadfa3;background:#111111;padding:9px 14px;border-radius:4px 0 0 4px;font-weight:500;font-size:12px","color:#e05c00;background:#fff3ec;padding:9px 14px;border-radius:0 4px 4px 0;font-size:11px","")</script>
1476 1477</body> 1478</html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.