公用JS函数文件记录

兼容版 √ - Edge Chrome + Safari

// assets/script.js

// =============================
// 全局滚动管理器
// =============================
const ScrollManager = (() => {
    let scrollLock = false;
    let lockTimeout = null;

    const getHeaderOffset = () => {
        const header = document.querySelector('.header-container');
        return header ? header.offsetHeight : 0;
    };

    const canScroll = () => !scrollLock;

    const smoothScrollTo = (targetY, options = {}) => {
        if (scrollLock && !options.force) return false;

        const duration = options.duration || 500;
        const offset = options.offset || 0;
        const onComplete = options.onComplete;

        scrollLock = true;
        clearTimeout(lockTimeout);

        const startY = window.scrollY;
        const distance = targetY - startY + offset;
        let startTime = null;

        const easeOutCubic = t => Math.min(1, 1.001 - Math.pow(2, -10 * t));

        const animate = (currentTime) => {
            if (!startTime) startTime = currentTime;
            const elapsed = currentTime - startTime;
            const progress = Math.min(elapsed / duration, 1);
            const eased = easeOutCubic(progress);

            window.scrollTo(0, startY + distance * eased);

            if (progress < 1) {
                requestAnimationFrame(animate);
            } else {
                lockTimeout = setTimeout(() => {
                    scrollLock = false;
                    if (onComplete) onComplete();
                }, 50);
            }
        };

        requestAnimationFrame(animate);
        return true;
    };

    const unlock = () => {
        scrollLock = false;
        clearTimeout(lockTimeout);
    };

    const isLocked = () => scrollLock;

    return {
        getHeaderOffset,
        canScroll,
        smoothScrollTo,
        unlock,
        isLocked
    };
})();

// =============================
// DOM加载完成初始化
// =============================
document.addEventListener('DOMContentLoaded', function() {
    console.log("✅ 博客脚本已加载");

    document.body.classList.add('page-loaded');

    // 核心功能优先执行
    initAlertAutoHide();
    initModals();
    initUserDropdown();
    cleanUrlParams();
    initReplyForm();
    
    // 延迟执行非阻塞任务
    // Safari对 requestIdleCallback 支持较好,但给予充足的 timeout
    if (window.requestIdleCallback) {
        requestIdleCallback(() => {
            initArticleTOC();
            initMainCommentsCollapse();
            initScrollButtons();
            initLikeButton();
            requestAnimationFrame(initCodeBlocks);
        }, { timeout: 2000 });
    } else {
        // 降级处理
        setTimeout(() => {
            initArticleTOC();
            initMainCommentsCollapse();
            initScrollButtons();
            initLikeButton();
            initCodeBlocks();
        }, 100);
    }

    initSubtitleManager();
    initSearchCooldown();
    initHeaderShrink();
    initGlobalDelegation();
});

// =============================
// 全局事件委托
// =============================
function initGlobalDelegation() {
    // 1. 评论折叠/展开
    document.addEventListener('click', (e) => {
        const btn = e.target.closest('.toggle-replies-btn, .toggle-main-comments-btn');
        if (!btn) return;

        const isSubReplies = btn.classList.contains('toggle-replies-btn');
        
        if (isSubReplies) {
            const moreDiv = btn.previousElementSibling;
            if (!moreDiv) return;
            
            const isExpanded = btn.dataset.state === 'expanded';
            const hiddenCount = btn.getAttribute('data-count') || '';
            const collapsedLabel = `更多回复 <span class="circled-number">${hiddenCount}</span>`;
            
            if (isExpanded) {
                moreDiv.style.display = 'none';
                btn.innerHTML = collapsedLabel;
                btn.dataset.state = 'collapsed';
            } else {
                moreDiv.style.display = 'block';
                btn.innerHTML = '收起回复';
                btn.dataset.state = 'expanded';
            }
        } else {
            const wrapper = btn.closest('.toggle-main-comments-wrapper');
            const moreDiv = wrapper ? wrapper.previousElementSibling : null;
            const hiddenCount = btn.getAttribute('data-count') || '';
            
            if (moreDiv) {
                const isExpanded = btn.dataset.state === 'expanded';
                if (isExpanded) {
                    moreDiv.style.display = 'none';
                    btn.innerHTML = `➕ 更多评论 <span class="circled-number">${hiddenCount}</span>`;
                    btn.dataset.state = 'collapsed';
                } else {
                    moreDiv.style.display = 'block';
                    btn.innerHTML = '➖ 收起评论';
                    btn.dataset.state = 'expanded';
                }
            }
        }
    });
    
    // 2. 脚注跳转
    document.addEventListener('click', (e) => {
        const a = e.target.closest('a[href^="#"]');
        if (!a) return;
        
        const targetId = a.hash.slice(1);
        if (!targetId) return;
        const el = document.getElementById(targetId);
        if (!el) return;

        e.preventDefault();
        if (!ScrollManager.canScroll()) return;
        
        const targetY = el.getBoundingClientRect().top + window.scrollY - 350;
        ScrollManager.smoothScrollTo(targetY, { duration: 500 });
    });
}

// =============================
// 1. 自动隐藏提示消息
// =============================
function initAlertAutoHide() {
    const alerts = document.querySelectorAll('.alert');
    if (!alerts.length) return;

    const removeAlert = (alert) => {
        alert.style.transition = "opacity 0.5s ease";
        alert.style.opacity = "0";
        setTimeout(() => alert.remove(), 500);
    };

    alerts.forEach(alert => setTimeout(() => removeAlert(alert), 3000));
}

// =============================
// 2. 清理 URL 参数
// =============================
function cleanUrlParams() {
    if (!window.location.search.includes('success=') && !window.location.search.includes('error=')) return;
    
    setTimeout(() => {
        const url = new URL(window.location.href);
        url.searchParams.delete('success');
        url.searchParams.delete('error');
        window.history.replaceState({}, '', url.pathname + url.search + url.hash);
    }, 3500);
}

// =============================
// 3. 评论回复表单逻辑
// =============================
function initReplyForm() {
    const mainFormContainer = document.getElementById('main-comment-form');
    const replyFormContainer = document.getElementById('reply-form-container');
    const cancelReplyBtn = document.getElementById('cancel-reply');
    const replyToSpan = document.getElementById('reply-to');
    const parentIdInput = document.getElementById('parent_id');
    
    let lastReplyCommentId = null;

    document.addEventListener('click', (e) => {
        const btn = e.target.closest('.reply-btn');
        if (!btn) return;

        const commentId = btn.getAttribute('data-comment-id');
        const authorName = btn.getAttribute('data-author');
        lastReplyCommentId = commentId;

        if (mainFormContainer) mainFormContainer.style.display = 'none';
        if (replyFormContainer) replyFormContainer.style.display = 'block';
        if (replyToSpan) replyToSpan.textContent = authorName;
        if (parentIdInput) parentIdInput.value = commentId;

        const replyForm = document.getElementById('reply-form');
        if (replyForm) {
            const textarea = replyForm.querySelector('textarea[name="content"]');
            if (textarea) textarea.value = '';

            requestAnimationFrame(() => {
                const maxScroll = Math.max(
                    document.body.scrollHeight,
                    document.documentElement.scrollHeight
                ) - window.innerHeight;
                
                ScrollManager.smoothScrollTo(maxScroll, {
                    duration: 600,
                    force: true
                });
            });
        }
    });

    if (cancelReplyBtn) {
        cancelReplyBtn.addEventListener('click', function() {
            if (replyFormContainer) replyFormContainer.style.display = 'none';
            if (parentIdInput) parentIdInput.value = '';
            if (mainFormContainer) mainFormContainer.style.display = 'block';
            
            const replyForm = document.getElementById('reply-form');
            if (replyForm) {
                const textarea = replyForm.querySelector('textarea[name="content"]');
                if (textarea) textarea.value = '';
            }

            if (lastReplyCommentId) {
                const targetComment = document.getElementById('comment-' + lastReplyCommentId);
                if (targetComment) {
                    const rect = targetComment.getBoundingClientRect();
                    const targetY = rect.top + window.scrollY - ScrollManager.getHeaderOffset() - 28;
                    ScrollManager.smoothScrollTo(targetY, { duration: 600, force: true });
                }
                lastReplyCommentId = null;
            }
        });
    }
}

// =============================
// 4. 主评论折叠功能
// =============================
function initMainCommentsCollapse() {
    const mainCommentsContainer = document.querySelector('.latest-comments-container .sidebar-section');
    if (!mainCommentsContainer) return;

    const allMainComments = mainCommentsContainer.querySelectorAll('.comment-container.level-0');
    const totalMainComments = allMainComments.length;
    
    if (totalMainComments <= 2) return;

    const moreMainComments = document.createElement('div');
    moreMainComments.className = 'more-main-comments';
    moreMainComments.style.display = 'none';
    
    const fragment = document.createDocumentFragment();
    for (let i = 2; i < totalMainComments; i++) {
        fragment.appendChild(allMainComments[i]);
    }
    moreMainComments.appendChild(fragment);
    allMainComments[1].parentNode.insertBefore(moreMainComments, allMainComments[1].nextSibling);
    
    const toggleMainBtn = document.createElement('div');
    toggleMainBtn.className = 'toggle-main-comments-wrapper';
    toggleMainBtn.style.padding = '10px 0';
    const hiddenMainCount = totalMainComments - 2;
    toggleMainBtn.innerHTML = `<button class="toggle-main-comments-btn" data-state="collapsed" data-count="${hiddenMainCount}">➕ 更多评论 <span class="circled-number">${hiddenMainCount}</span></button>`;
    
    moreMainComments.parentNode.insertBefore(toggleMainBtn, moreMainComments.nextSibling);
}

// =============================
// 5. 右侧滚动按钮
// =============================
function initScrollButtons() {
    const SCROLL_SPEED = 3;
    let scrollRafId = null;
    let stopScroll = () => { if (scrollRafId) cancelAnimationFrame(scrollRafId); scrollRafId = null; };

    const setupBtn = (id, isDown) => {
        const btn = document.getElementById(id);
        if (!btn) return;

        const continuousScroll = () => {
            if (ScrollManager.isLocked()) return stopScroll();
            window.scrollBy(0, isDown ? SCROLL_SPEED : -SCROLL_SPEED);
            scrollRafId = requestAnimationFrame(continuousScroll);
        };

        btn.addEventListener('mouseenter', () => {
            if (ScrollManager.isLocked()) return;
            stopScroll();
            scrollRafId = requestAnimationFrame(continuousScroll);
        });

        btn.addEventListener('mouseleave', stopScroll);
        
        btn.addEventListener('click', () => {
            stopScroll();
            const targetY = isDown ? Math.max(document.body.scrollHeight, document.documentElement.scrollHeight) - window.innerHeight : 0;
            ScrollManager.smoothScrollTo(targetY, { duration: 800, force: true });
        });
    };

    setupBtn('scrollUp', false);
    setupBtn('scrollDown', true);

    document.addEventListener('visibilitychange', () => document.hidden && stopScroll());
    console.log('✅ 滚动按钮已初始化');
}

// =============================
// 6. 点赞功能
// =============================
function initLikeButton() {
    const likeBtn = document.getElementById('like-btn');
    if (!likeBtn) return;
    
    likeBtn.addEventListener('click', function() {
        if (this.disabled) return;
        
        const articleId = this.getAttribute('data-article-id');
        const isLiked = this.classList.contains('liked');
        const action = isLiked ? 'unlike' : 'like';
        
        this.disabled = true;
        
        const formData = new FormData();
        formData.append('action', action);
        formData.append('article_id', articleId);
        
        fetch('ajax_like.php', { method: 'POST', body: formData })
            .then(response => response.json())
            .then(data => {
                if (data.success) {
                    this.classList.toggle('liked', action === 'like');
                    const countSpan = document.getElementById('like-count');
                    if (countSpan) countSpan.textContent = data.count;
                } else {
                    alert(data.message || '操作失败');
                }
            })
            .catch(error => {
                console.error('Error:', error);
                alert('网络错误,请重试');
            })
            .finally(() => {
                this.disabled = false;
            });
    });
}

// =============================
// 7. 代码块功能
// =============================
const CODE_CONFIG = {
    COLLAPSE_LINE_THRESHOLD: 5,
    COLLAPSE_LINES: 5,
    COPY_FEEDBACK_DURATION: 1200,
    RESIZE_DEBOUNCE_DELAY: 150
};

async function initCodeBlocks() {
    requestAnimationFrame(async () => {
        if (document.fonts && document.fonts.ready) {
            try {
                await document.fonts.ready;
            } catch (e) {
                console.warn('Fonts ready failed', e);
            }
        }

        const blocks = document.querySelectorAll(".article-content pre");
        if (blocks.length === 0) return;

        for (let i = 0; i < blocks.length; i++) {
            await processBlock(blocks[i], i);
        }
    });

    // Resize 处理
    let resizeTimer = null;
    window.addEventListener("resize", () => {
        clearTimeout(resizeTimer);
        resizeTimer = setTimeout(() => {
            document.querySelectorAll(".article-content pre.collapsed").forEach(pre => {
                const codeEl = pre.querySelector("code") || pre;
                const cs = getComputedStyle(codeEl);
                const fs = parseFloat(cs.fontSize) || 15;
                const lh = parseFloat(cs.lineHeight) || fs * 1.6;
                const ps = getComputedStyle(pre);
                const pt = parseFloat(ps.paddingTop) || 48;
                const pb = parseFloat(ps.paddingBottom) || 16;
                const newHeight = Math.ceil(pt + lh * CODE_CONFIG.COLLAPSE_LINES + pb);
                pre._collapsedHeight = newHeight;
                pre.style.maxHeight = newHeight + "px";
            });
        }, CODE_CONFIG.RESIZE_DEBOUNCE_DELAY);
    }, { passive: true });
}

// 代码块处理逻辑分离
async function processBlock(pre, index) {
    // 分片处理,避免在主线程阻塞太久(Safari 尤其敏感)
    if (index > 0 && index % 5 === 0) {
        await new Promise(resolve => setTimeout(resolve, 0));
    }

    const codeEl = pre.querySelector("code") || pre;
    // 使用 textContent 避免 innerText 触发重排
    let raw = (codeEl.textContent || "").replace(/^\n+|\n+$/g, "").replace(/\r\n/g, "\n");
    const lines = raw ? raw.split("\n") : [];

    // ==================== 生成行号 ====================
    if (!pre.hasAttribute('data-line-numbers')) {
        const lineNumbersText = lines.map((_, i) => i + 1).join('\n');
        pre.setAttribute('data-line-numbers', lineNumbersText);
    }

    // ==================== 工具栏 ====================
    if (!pre.querySelector(".code-tools")) {
        const shouldCollapse = lines.length > CODE_CONFIG.COLLAPSE_LINE_THRESHOLD;
        const toggleBtnHTML = shouldCollapse ? '<button class="code-btn code-toggle" type="button">展开</button>' : '';
        
        const tools = document.createElement("div");
        tools.className = "code-tools";
        tools.innerHTML = `
            <span class="code-title">CODE - iTxGo™️</span>
            <div style="display:flex; gap:8px; align-items:center;">
                ${toggleBtnHTML}
                <button class="code-btn code-copy" type="button" style="width:43px; min-width:43px; text-align:center;">复制</button>
            </div>
        `;
        pre.insertBefore(tools, pre.firstChild);
    }

    // ==================== 复制功能 ====================
    const copyBtn = pre.querySelector(".code-copy");
    if (copyBtn && !copyBtn.hasListener) {
        copyBtn.hasListener = true;
        // Safari 下使用 touchstart 或确保 click 有效
        copyBtn.addEventListener("click", async () => {
            try {
                await navigator.clipboard.writeText(raw);
                showCopyFeedback(copyBtn);
            } catch (e) {
                // 降级处理
                if (copyWithFallback(raw)) showCopyFeedback(copyBtn);
                else showCopyError(copyBtn, "复制失败");
            }
        });
    }

    // ==================== 折叠功能 ====================
    const shouldCollapse = lines.length > CODE_CONFIG.COLLAPSE_LINE_THRESHOLD;
    if (!shouldCollapse) {
        pre.classList.add("expanded");
        pre.classList.remove("collapsed");
        return;
    }

    // 计算折叠高度
    const cs = getComputedStyle(codeEl);
    const fontSize = parseFloat(cs.fontSize) || 15;
    const lineHeight = parseFloat(cs.lineHeight) || fontSize * 1.6;
    
    const ps = getComputedStyle(pre);
    const paddingTop = parseFloat(ps.paddingTop) || 48;
    const paddingBottom = parseFloat(ps.paddingBottom) || 16;
    const collapsedHeight = Math.ceil(paddingTop + lineHeight * CODE_CONFIG.COLLAPSE_LINES + paddingBottom);

    pre._collapsedHeight = collapsedHeight;
    pre.style.maxHeight = collapsedHeight + "px";
    pre.classList.add("collapsed");
    pre.classList.remove("expanded");

    // ==================== 折叠按钮事件 ====================
    const toggleBtn = pre.querySelector(".code-toggle");
    if (toggleBtn && !toggleBtn.hasListener) {
        toggleBtn.hasListener = true;
        toggleBtn.addEventListener("click", () => {
            const isCollapsed = pre.classList.contains("collapsed");
            if (isCollapsed) {
                pre.classList.replace("collapsed", "expanded");
                pre.style.maxHeight = "none";
                toggleBtn.textContent = "折叠";
            } else {
                pre.classList.replace("expanded", "collapsed");
                pre.style.maxHeight = pre._collapsedHeight + "px";
                toggleBtn.textContent = "展开";
            }
        });
    }
}

function copyWithFallback(text) {
    try {
        const textarea = document.createElement("textarea");
        textarea.value = text;
        // 防止 iOS 缩放和滚动
        textarea.style.position = "fixed";
        textarea.style.left = "-9999px";
        textarea.style.top = "0";
        document.body.appendChild(textarea);
        textarea.select();
        textarea.setSelectionRange(0, 99999); // 移动端兼容
        const success = document.execCommand("copy");
        document.body.removeChild(textarea);
        return success;
    } catch (err) {
        return false;
    }
}

function showCopyFeedback(btn) {
    const originalText = btn.textContent;
    btn.textContent = "✓";
    btn.disabled = true;
    btn.style.color = "#4ade80";
    setTimeout(() => {
        btn.textContent = originalText;
        btn.disabled = false;
        btn.style.color = "";
    }, CODE_CONFIG.COPY_FEEDBACK_DURATION);
}

function showCopyError(btn, message) {
    const originalText = btn.textContent;
    btn.textContent = "失败";
    btn.disabled = true;
    btn.style.color = "#ef4444";
    setTimeout(() => {
        btn.textContent = originalText;
        btn.disabled = false;
        btn.style.color = "";
    }, CODE_CONFIG.COPY_FEEDBACK_DURATION);
}

// =============================
// 8. 用户下拉菜单
// =============================
function initUserDropdown() {
    const userMenuContainer = document.querySelector('.user-menu-container');
    const userMenuTrigger = document.querySelector('.user-menu-trigger');
    const userDropdown = document.querySelector('.user-dropdown');
    
    if (!userMenuContainer || !userMenuTrigger) return;
    
    // 防止点击 a 标签默认跳转(如果有的话)
    userMenuTrigger.addEventListener('click', e => e.preventDefault());
    if (!userDropdown) return;
    
    // 点击外部关闭
    document.addEventListener('click', e => {
        if (!userMenuContainer.contains(e.target)) {
            userMenuContainer.classList.remove('dropdown-open');
        }
    });
    
    // ESC 关闭
    document.addEventListener('keydown', e => {
        if (e.key === 'Escape') userMenuContainer.classList.remove('dropdown-open');
    });
    
    // 鼠标移出延时关闭(防止误触)
    let leaveTimer = null;
    userMenuContainer.addEventListener('mouseleave', () => {
        leaveTimer = setTimeout(() => userMenuContainer.classList.remove('dropdown-open'), 300);
    });
    
    userMenuContainer.addEventListener('mouseenter', () => {
        clearTimeout(leaveTimer);
        leaveTimer = null;
    });
    
    // 高亮当前菜单项
    userDropdown.querySelectorAll('.user-dropdown-item').forEach(item => {
        if (window.location.pathname.includes(item.getAttribute('href'))) {
            item.style.fontWeight = '600';
            item.style.color = 'var(--color-text-primary)';
        }
    });
}

// =============================
// 9. 模态框功能
// =============================
function initModals() {
    const formStates = {
        loginForm: { originalText: '登录', isSubmitting: false },
        registerForm: { originalText: '注册', isSubmitting: false }
    };

    function getModalId(formId) { return formId.replace('Form', 'Modal'); }
    
    function openModal(modalId) {
        const modal = document.getElementById(modalId);
        if (!modal) return;
        modal.style.display = 'flex';
        const messageDiv = modal.querySelector('.modal-message');
        if (messageDiv) {
            messageDiv.innerHTML = '';
            messageDiv.className = 'modal-message';
        }
        resetForm(modalId.replace('Modal', 'Form'));
    }

    function closeModal(modalId) {
        const modal = document.getElementById(modalId);
        if (modal) modal.style.display = 'none';
    }

    function showMessage(modalId, message, isError = true) {
        const messageDiv = document.getElementById(modalId)?.querySelector('.modal-message');
        if (messageDiv) {
            messageDiv.textContent = message;
            messageDiv.className = `modal-message ${isError ? 'alert-error' : 'alert-success'}`;
        }
    }

    function resetForm(formId) {
        const form = document.getElementById(formId);
        const state = formStates[formId];
        if (form) form.reset();
        if (state && state.isSubmitting) {
            const submitBtn = form ? form.querySelector('button[type="submit"]') : null;
            if (submitBtn) {
                submitBtn.disabled = false;
                submitBtn.textContent = state.originalText;
            }
            state.isSubmitting = false;
        }
    }

    function handleFormSubmit(formId, endpoint, onSuccess) {
        const form = document.getElementById(formId);
        if (!form) return;
        
        const state = formStates[formId];
        const modalId = getModalId(formId);

        form.addEventListener('submit', function(e) {
            e.preventDefault();
            if (state.isSubmitting) return;

            const submitBtn = this.querySelector('button[type="submit"]');
            state.isSubmitting = true;
            submitBtn.disabled = true;
            submitBtn.textContent = `${state.originalText}中...`;

            fetch(endpoint, { method: 'POST', body: new FormData(this) })
                .then(r => r.json())
                .then(data => {
                    showMessage(modalId, data.message, !data.success);
                    if (data.success) {
                        if (onSuccess) onSuccess(data);
                    } else {
                        state.isSubmitting = false;
                        submitBtn.disabled = false;
                        submitBtn.textContent = state.originalText;
                    }
                })
                .catch(error => {
                    console.error(error);
                    showMessage(modalId, '网络错误,请重试。', true);
                    state.isSubmitting = false;
                    submitBtn.disabled = false;
                    submitBtn.textContent = state.originalText;
                });
        });
    }

    // 模态框交互事件绑定
    document.querySelectorAll('[data-modal]').forEach(link => {
        link.addEventListener('click', e => {
            e.preventDefault();
            openModal(link.getAttribute('data-modal'));
        });
    });

    document.querySelectorAll('.modal-close').forEach(btn => {
        btn.addEventListener('click', () => {
            closeModal(btn.getAttribute('data-modal'));
            resetForm(btn.getAttribute('data-modal').replace('Modal', 'Form'));
        });
    });

    document.querySelectorAll('.switch-modal').forEach(link => {
        link.addEventListener('click', e => {
            e.preventDefault();
            closeModal(link.getAttribute('data-from'));
            resetForm(link.getAttribute('data-from').replace('Modal', 'Form'));
            openModal(link.getAttribute('data-to'));
        });
    });

    handleFormSubmit('loginForm', 'ajax_login.php', data => {
        setTimeout(() => window.location.href = data.redirect || 'index.php', 1000);
    });

    handleFormSubmit('registerForm', 'ajax_register.php', () => {
        setTimeout(() => {
            closeModal('registerModal');
            openModal('loginModal');
        }, 1500);
    });

    document.addEventListener('keydown', e => {
        if (e.key === 'Escape') {
            document.querySelectorAll('.modal').forEach(modal => {
                if (modal.style.display === 'flex') {
                    closeModal(modal.id);
                    resetForm(modal.id.replace('Modal', 'Form'));
                }
            });
        }
    });
}

// =============================
// 10. 副标题动态内容
// =============================
function initSubtitleManager() {
    class SubtitleManager {
        constructor() {
            this.subtitleElement = document.getElementById('subtitle');
            this.subtitleTexts = [
                "👋 欢迎来到我的博客 :)",
				"⚠️ 网站完善中,敬请期待!",
				"🔥 热爱可抵岁月漫长…",
				"✨ 记录成长轨迹,分享生活微光。",
                "🌱 Every day, in every way, I am getting better and better.",
            ];
            this.init();
        }
        
        init() {
            if (!this.subtitleElement) return;
            // 批量设置样式
            Object.assign(this.subtitleElement.style, {
                minHeight: '16px',
                display: 'flex',
                alignItems: 'center',
                whiteSpace: 'nowrap',
                overflow: 'hidden',
                textOverflow: 'ellipsis'
            });
            this.setRandomSubtitle();
        }
        
        setRandomSubtitle() {
            const randomText = this.subtitleTexts[Math.floor(Math.random() * this.subtitleTexts.length)];
            this.subtitleElement.style.opacity = '0';
            setTimeout(() => {
                this.subtitleElement.textContent = randomText;
                this.subtitleElement.style.opacity = '1';
            }, 150);
        }
    }
    window.subtitleManager = new SubtitleManager();
}

// =============================
// 11. 文章目录生成
// =============================
function initArticleTOC() {
    const articleContent = document.querySelector('.article-content');
    const placeholderDiv = document.getElementById('toc-placeholder');
    
    if (!articleContent || !placeholderDiv) return;
    
    const headings = articleContent.querySelectorAll('h2, h3');
    
    if (headings.length === 0) {
        placeholderDiv.innerHTML = '<p style="text-align:center;color:#999;padding:20px">本文暂无目录...</p>';
        return;
    }
    
    const tocWrapper = document.createElement('div');
    tocWrapper.className = 'toc-wrapper';
    
    const fragment = document.createDocumentFragment();
    let firstH2 = true;

    headings.forEach((h, i) => {
        if (!h.id) h.id = `heading-${i}`;
        const level = h.tagName.toLowerCase();
        const isTitle = level === 'h2' && firstH2;
        if (isTitle) firstH2 = false;
        
        const tocItem = document.createElement('div');
        tocItem.className = `toc-item toc-${level}${isTitle ? ' toc-title' : ''}`;
        
        const tocLink = document.createElement('a');
        tocLink.href = `#${h.id}`;
        tocLink.className = 'toc-link';
        tocLink.dataset.id = h.id;
        tocLink.title = h.textContent.trim();
        
        const tocText = document.createElement('span');
        tocText.className = 'toc-text';
        tocText.textContent = h.textContent.trim();
        
        tocLink.appendChild(tocText);
        tocItem.appendChild(tocLink);
        fragment.appendChild(tocItem);
    });
    
    tocWrapper.appendChild(fragment);
    placeholderDiv.innerHTML = '';
    placeholderDiv.appendChild(tocWrapper);
    
    initTOCInteraction(headings);
}

// =============================
// 12. 目录交互功能
// =============================
function initTOCInteraction(headings) {
    const links = document.querySelectorAll('.toc-link');
    if (!links.length) return;
    
    let isTOCScrolling = false;
    let scrollEndTimer = null;
    
    const setActive = (link) => {
        const current = document.querySelector('.toc-link.active');
        if (current === link) return;
        if (current) current.classList.remove('active');
        if (link) link.classList.add('active');
    };
    
    // 使用 IntersectionObserver 替代 scroll 事件计算
    const observerOptions = {
        rootMargin: '-100px 0px -66% 0px', 
        threshold: 0
    };
    
    const observer = new IntersectionObserver(entries => {
        if (isTOCScrolling) return;
        
        let lastEntry = null;
        for (const entry of entries) {
            if (entry.isIntersecting) {
                lastEntry = entry;
            }
        }
        
        if (lastEntry) {
            const activeLink = document.querySelector(`.toc-link[data-id="${lastEntry.target.id}"]`);
            if (activeLink) setActive(activeLink);
        }
    }, observerOptions);
    
    headings.forEach(h => observer.observe(h));
    
    // 点击事件
    links.forEach(link => {
        link.addEventListener('click', (e) => {
            e.preventDefault();
            const target = document.getElementById(link.dataset.id);
            if (!target) return;
            
            isTOCScrolling = true;
            setActive(link);
            
            const highlight = document.querySelector('.heading-highlight');
            if (highlight) highlight.classList.remove('heading-highlight');
            
            const header = document.querySelector('.header-container');
            const currentHeaderHeight = header ? header.getBoundingClientRect().height : 80;
            
            const targetY = target.getBoundingClientRect().top 
                          + window.scrollY 
                          - currentHeaderHeight 
                          - 168;
            
            ScrollManager.smoothScrollTo(targetY, {
                duration: 600,
                onComplete: () => {
                    target.classList.add('heading-highlight');
                    setTimeout(() => {
                        target.classList.remove('heading-highlight');
                        isTOCScrolling = false;
                    }, 2500);
                }
            });
        });
    });
    
    // 降级:Observer 不支持时
    if (!('IntersectionObserver' in window)) {
        window.addEventListener('scroll', () => {
            if (isTOCScrolling) return;
            clearTimeout(scrollEndTimer);
            scrollEndTimer = setTimeout(() => {
                const scrollY = window.scrollY;
                let activeHeading = null;
                let minDistance = Infinity;
                
                headings.forEach(heading => {
                    const rect = heading.getBoundingClientRect();
                    const distance = Math.abs(rect.top - 200);
                    if (distance < minDistance && rect.top > 0) {
                        minDistance = distance;
                        activeHeading = heading;
                    }
                });
                
                if (!activeHeading) {
                    for (let i = headings.length - 1; i >= 0; i--) {
                        if (headings[i].getBoundingClientRect().top < window.innerHeight) {
                            activeHeading = headings[i];
                            break;
                        }
                    }
                }
                
                if (activeHeading) {
                    const activeLink = document.querySelector(`.toc-link[data-id="${activeHeading.id}"]`);
                    if (activeLink) setActive(activeLink);
                }
            }, 100);
        }, { passive: true });
    }
}

// =============================
// 13. 搜索冷却功能
// =============================
function initSearchCooldown() {
    const form = document.getElementById('searchForm');
    const input = document.getElementById('searchInput');
    const button = document.getElementById('searchButton');
    if (!form || !input || !button) return;

    const isAdmin = document.body.hasAttribute('data-is-admin');
    const cooldownEnd = parseInt(localStorage.getItem('search_cooldown_end') || '0', 10);
    const now = Date.now();

    if (!isAdmin && now < cooldownEnd) {
        startCountdown(Math.ceil((cooldownEnd - now) / 1000));
    } else {
        enableSearch();
    }

    form.addEventListener('submit', function (e) {
        if (!input.value.trim()) {
            e.preventDefault();
            return;
        }
        if (!isAdmin) {
            const newCooldownEnd = Date.now() + 30 * 1000;
            localStorage.setItem('search_cooldown_end', newCooldownEnd.toString());
        }
    });

    function startCountdown(seconds) {
        let remaining = seconds;
        updateUI();
        const interval = setInterval(() => {
            remaining--;
            if (remaining <= 0) {
                clearInterval(interval);
                enableSearch();
                localStorage.removeItem('search_cooldown_end');
            } else {
                updateUI();
            }
        }, 1000);

        function updateUI() {
            input.placeholder = `⏳ ${remaining} 秒...`;
            input.disabled = true;
            input.value = '';
            button.disabled = true;
        }
    }

    function enableSearch() {
        input.placeholder = '搜索文章...';
        input.disabled = false;
        button.disabled = false;
    }
}

// =============================
// 14. 导航栏滚动收缩功能 - 兼容版
// =============================
function initHeaderShrink() {
    
    const runInit = () => {
        const header = document.querySelector('.header-container');
        if (!header) return;

        const SHRINK_AT = 80;
        const EXPAND_AT = 20;

        let isShrunk = false;
        let lastScrollY = window.scrollY;
        let ticking = false;

        const updateHeaderState = () => {
            const scrollY = window.scrollY;
            const isScrollingDown = scrollY > lastScrollY;
            lastScrollY = scrollY;

            if (isScrollingDown && !isShrunk && scrollY > SHRINK_AT) {
                header.classList.add('shrunk');
                isShrunk = true;
            } else if (!isScrollingDown && isShrunk && scrollY < EXPAND_AT) {
                header.classList.remove('shrunk');
                isShrunk = false;
            }
            ticking = false;
        };

        const onScroll = () => {
            if (!ticking) {
                requestAnimationFrame(updateHeaderState);
                ticking = true;
            }
        };

        window.addEventListener('scroll', onScroll, { passive: true });
        
        console.log('✅ Header 收缩功能已就绪');
    };

    if ('requestIdleCallback' in window) {
        requestIdleCallback(runInit, { timeout: 100 });
    } else {
        setTimeout(runInit, 100);
    }
}

console.log('✅ 所有脚本模块已优化并加载完成');

原版 - Edge Chrome

// assets/script.js

// =============================
// 全局滚动管理器
// =============================
const ScrollManager = (() => {
    let scrollLock = false;
    let lockTimeout = null;

    const getHeaderOffset = () => {
        // 使用缓存高度,减少 getBoundingClientRect 调用
        const header = document.querySelector('.header-container');
        return header ? header.offsetHeight : 0;
    };

    const canScroll = () => !scrollLock;

    const smoothScrollTo = (targetY, options = {}) => {
        if (scrollLock && !options.force) return false;

        const duration = options.duration || 500;
        const offset = options.offset || 0;
        const onComplete = options.onComplete;

        scrollLock = true;
        clearTimeout(lockTimeout);

        const startY = window.scrollY;
        const distance = targetY - startY + offset;
        let startTime = null;

        const easeOutCubic = t => Math.min(1, 1.001 - Math.pow(2, -10 * t));

        const animate = (currentTime) => {
            if (!startTime) startTime = currentTime;
            const elapsed = currentTime - startTime;
            const progress = Math.min(elapsed / duration, 1);
            const eased = easeOutCubic(progress);

            window.scrollTo(0, startY + distance * eased);

            if (progress < 1) {
                requestAnimationFrame(animate);
            } else {
                lockTimeout = setTimeout(() => {
                    scrollLock = false;
                    onComplete?.();
                }, 50);
            }
        };

        requestAnimationFrame(animate);
        return true;
    };

    const unlock = () => {
        scrollLock = false;
        clearTimeout(lockTimeout);
    };

    const isLocked = () => scrollLock;

    return {
        getHeaderOffset,
        canScroll,
        smoothScrollTo,
        unlock,
        isLocked
    };
})();

// =============================
// DOM加载完成初始化
// =============================
document.addEventListener('DOMContentLoaded', function() {
    console.log("✅ 博客脚本已加载");

    document.body.classList.add('page-loaded');

    // 核心功能优先执行
    initAlertAutoHide();
    initModals();
    initUserDropdown();
    cleanUrlParams();
    initReplyForm();
    
    // 延迟执行非阻塞任务,避免长任务
    requestIdleCallback(() => {
        initArticleTOC();
        initMainCommentsCollapse();
        initScrollButtons();
        initLikeButton();
        // 代码块初始化最耗时,放入下一帧处理
        requestAnimationFrame(initCodeBlocks);
    }, { timeout: 2000 }); // timeout确保即使在繁忙时也会在2秒内执行

    // 副标题和搜索冷却
    initSubtitleManager();
    initSearchCooldown();
    initHeaderShrink();
    
    // 初始化通用事件委托
    initGlobalDelegation();
});

// =============================
// 全局事件委托
// =============================
function initGlobalDelegation() {
    // 1. 评论折叠/展开
    document.addEventListener('click', (e) => {
        // 查找最近的按钮
        const btn = e.target.closest('.toggle-replies-btn, .toggle-main-comments-btn');
        if (!btn) return;

        const isSubReplies = btn.classList.contains('toggle-replies-btn');
        
        if (isSubReplies) {
            const moreDiv = btn.previousElementSibling;
            if (!moreDiv) return;
            
            const isExpanded = btn.dataset.state === 'expanded';
            const hiddenCount = btn.getAttribute('data-count') || '';
            const collapsedLabel = `更多回复 <span class="circled-number">${hiddenCount}</span>`;
            
            if (isExpanded) {
                moreDiv.style.display = 'none';
                btn.innerHTML = collapsedLabel;
                btn.dataset.state = 'collapsed';
            } else {
                moreDiv.style.display = 'block';
                btn.innerHTML = '收起回复';
                btn.dataset.state = 'expanded';
            }
        } else {
            // 主评论折叠
            const wrapper = btn.closest('.toggle-main-comments-wrapper');
            const moreDiv = wrapper?.previousElementSibling;
            const hiddenCount = btn.getAttribute('data-count') || '';
            
            if (moreDiv) {
                const isExpanded = btn.dataset.state === 'expanded';
                if (isExpanded) {
                    moreDiv.style.display = 'none';
                    btn.innerHTML = `➕ 更多评论 <span class="circled-number">${hiddenCount}</span>`;
                    btn.dataset.state = 'collapsed';
                } else {
                    moreDiv.style.display = 'block';
                    btn.innerHTML = '➖ 收起评论';
                    btn.dataset.state = 'expanded';
                }
            }
        }
    });
    
    // 2. 脚注跳转
    document.addEventListener('click', (e) => {
        const a = e.target.closest('a[href^="#"]');
        if (!a) return;
        
        const targetId = a.hash.slice(1);
        if (!targetId) return;
        const el = document.getElementById(targetId);
        if (!el) return;

        e.preventDefault();
        if (!ScrollManager.canScroll()) return;
        
        const targetY = el.getBoundingClientRect().top + window.scrollY - 350;
        ScrollManager.smoothScrollTo(targetY, { duration: 500 });
    });
}

// =============================
// 1. 自动隐藏提示消息
// =============================
function initAlertAutoHide() {
    // 一次性获取所有
    const alerts = document.querySelectorAll('.alert');
    if (!alerts.length) return;

    const removeAlert = (alert) => {
        alert.style.transition = "opacity 0.5s ease";
        alert.style.opacity = "0";
        setTimeout(() => alert.remove(), 500);
    };

    alerts.forEach(alert => setTimeout(() => removeAlert(alert), 3000));
}

// =============================
// 2. 清理 URL 参数
// =============================
function cleanUrlParams() {
    if (!window.location.search.includes('success=') && !window.location.search.includes('error=')) return;
    
    // 使用 requestIdleCallback 推迟非关键操作
    setTimeout(() => {
        const url = new URL(window.location.href);
        url.searchParams.delete('success');
        url.searchParams.delete('error');
        window.history.replaceState({}, '', url.pathname + url.search + url.hash);
    }, 3500);
}

// =============================
// 3. 评论回复表单逻辑
// =============================
function initReplyForm() {
    const mainFormContainer = document.getElementById('main-comment-form');
    const replyFormContainer = document.getElementById('reply-form-container');
    const cancelReplyBtn = document.getElementById('cancel-reply');
    const replyToSpan = document.getElementById('reply-to');
    const parentIdInput = document.getElementById('parent_id');
    
    let lastReplyCommentId = null;

    // 使用事件委托
    document.addEventListener('click', (e) => {
        const btn = e.target.closest('.reply-btn');
        if (!btn) return;

        const commentId = btn.getAttribute('data-comment-id');
        const authorName = btn.getAttribute('data-author');
        lastReplyCommentId = commentId;

        if (mainFormContainer) mainFormContainer.style.display = 'none';
        if (replyFormContainer) replyFormContainer.style.display = 'block';
        if (replyToSpan) replyToSpan.textContent = authorName;
        if (parentIdInput) parentIdInput.value = commentId;

        const replyForm = document.getElementById('reply-form');
        if (replyForm) {
            const textarea = replyForm.querySelector('textarea[name="content"]');
            if (textarea) textarea.value = '';
            
            // 延迟滚动
            requestAnimationFrame(() => {
                const maxScroll = Math.max(
                    document.body.scrollHeight,
                    document.documentElement.scrollHeight
                ) - window.innerHeight;
                
                ScrollManager.smoothScrollTo(maxScroll, {
                    duration: 600,
                    force: true
                });
            }, 50);
        }
    });

    if (cancelReplyBtn) {
        cancelReplyBtn.addEventListener('click', function() {
            if (replyFormContainer) replyFormContainer.style.display = 'none';
            if (parentIdInput) parentIdInput.value = '';
            if (mainFormContainer) mainFormContainer.style.display = 'block';
            
            const replyForm = document.getElementById('reply-form');
            if (replyForm) {
                const textarea = replyForm.querySelector('textarea[name="content"]');
                if (textarea) textarea.value = '';
            }

            if (lastReplyCommentId) {
                const targetComment = document.getElementById('comment-' + lastReplyCommentId);
                if (targetComment) {
                    // 一次性读取,避免强制重排
                    const rect = targetComment.getBoundingClientRect();
                    const targetY = rect.top + window.scrollY - ScrollManager.getHeaderOffset() - 28;
                    ScrollManager.smoothScrollTo(targetY, { duration: 600, force: true });
                }
                lastReplyCommentId = null;
            }
        });
    }
}

// =============================
// 4. 主评论折叠功能
// =============================
function initMainCommentsCollapse() {
    const mainCommentsContainer = document.querySelector('.latest-comments-container .sidebar-section');
    if (!mainCommentsContainer) return;

    const allMainComments = mainCommentsContainer.querySelectorAll('.comment-container.level-0');
    const totalMainComments = allMainComments.length;
    
    if (totalMainComments <= 2) return;

    const moreMainComments = document.createElement('div');
    moreMainComments.className = 'more-main-comments';
    moreMainComments.style.display = 'none';
    
    // DocumentFragment 批量插入,减少重排
    const fragment = document.createDocumentFragment();
    for (let i = 2; i < totalMainComments; i++) {
        fragment.appendChild(allMainComments[i]);
    }
    moreMainComments.appendChild(fragment);
    allMainComments[1].parentNode.insertBefore(moreMainComments, allMainComments[1].nextSibling);
    
    const toggleMainBtn = document.createElement('div');
    toggleMainBtn.className = 'toggle-main-comments-wrapper';
    toggleMainBtn.style.padding = '10px 0';
    const hiddenMainCount = totalMainComments - 2;
    toggleMainBtn.innerHTML = `<button class="toggle-main-comments-btn" data-state="collapsed" data-count="${hiddenMainCount}">➕ 更多评论 <span class="circled-number">${hiddenMainCount}</span></button>`;
    
    moreMainComments.parentNode.insertBefore(toggleMainBtn, moreMainComments.nextSibling);
}

// =============================
// 5. 右侧滚动按钮
// =============================
function initScrollButtons() {
    const SCROLL_SPEED = 3;
    let scrollRafId = null;
    let stopScroll = () => { if (scrollRafId) cancelAnimationFrame(scrollRafId); scrollRafId = null; };

    const setupBtn = (id, isDown) => {
        const btn = document.getElementById(id);
        if (!btn) return;

        const continuousScroll = () => {
            if (ScrollManager.isLocked()) return stopScroll();
            window.scrollBy(0, isDown ? SCROLL_SPEED : -SCROLL_SPEED);
            scrollRafId = requestAnimationFrame(continuousScroll);
        };

        btn.addEventListener('mouseenter', () => {
            if (ScrollManager.isLocked()) return;
            stopScroll();
            scrollRafId = requestAnimationFrame(continuousScroll);
        });

        btn.addEventListener('mouseleave', stopScroll);
        
        btn.addEventListener('click', () => {
            stopScroll();
            const targetY = isDown ? Math.max(document.body.scrollHeight, document.documentElement.scrollHeight) - window.innerHeight : 0;
            ScrollManager.smoothScrollTo(targetY, { duration: 800, force: true });
        });
    };

    setupBtn('scrollUp', false);
    setupBtn('scrollDown', true);

    document.addEventListener('visibilitychange', () => document.hidden && stopScroll());
    console.log('✅ 滚动按钮已初始化');
}

// =============================
// 6. 点赞功能
// =============================
function initLikeButton() {
    const likeBtn = document.getElementById('like-btn');
    if (!likeBtn) return;
    
    likeBtn.addEventListener('click', function() {
        if (this.disabled) return; // 防止重复点击
        
        const articleId = this.getAttribute('data-article-id');
        const isLiked = this.classList.contains('liked');
        const action = isLiked ? 'unlike' : 'like';
        
        this.disabled = true;
        
        const formData = new FormData();
        formData.append('action', action);
        formData.append('article_id', articleId);
        
        fetch('ajax_like.php', { method: 'POST', body: formData })
            .then(response => response.json())
            .then(data => {
                if (data.success) {
                    this.classList.toggle('liked', action === 'like');
                    const countSpan = document.getElementById('like-count');
                    if (countSpan) countSpan.textContent = data.count;
                } else {
                    alert(data.message || '操作失败');
                }
            })
            .catch(error => {
                console.error('Error:', error);
                alert('网络错误,请重试');
            })
            .finally(() => {
                this.disabled = false;
            });
    });
}

// =============================
// 7. 代码块功能(修复版 - 兼容现有 CSS)
// =============================
const CODE_CONFIG = {
    COLLAPSE_LINE_THRESHOLD: 5,
    COLLAPSE_LINES: 5,
    COPY_FEEDBACK_DURATION: 1200,
    RESIZE_DEBOUNCE_DELAY: 150
};

async function initCodeBlocks() {
    requestAnimationFrame(async () => {
        if (document.fonts?.ready) await document.fonts.ready;

        const blocks = document.querySelectorAll(".article-content pre");
        if (blocks.length === 0) return;

        const processBlock = (pre, index) => {
            if (index > 0 && index % 5 === 0) {
                return new Promise(resolve => setTimeout(resolve, 0));
            }

            const codeEl = pre.querySelector("code") || pre;
            let raw = (codeEl.textContent || "").replace(/^\n+|\n+$/g, "").replace(/\r\n/g, "\n");
            const lines = raw ? raw.split("\n") : [];

            // ==================== 生成行号(用 data 属性) ====================
            if (!pre.hasAttribute('data-line-numbers')) {
                // 生成行号字符串:1\n2\n3\n...
                const lineNumbersText = lines.map((_, i) => i + 1).join('\n');
                pre.setAttribute('data-line-numbers', lineNumbersText);
            }

            // ==================== 工具栏 ====================
            if (!pre.querySelector(".code-tools")) {
                const shouldCollapse = lines.length > CODE_CONFIG.COLLAPSE_LINE_THRESHOLD;
                const toggleBtnHTML = shouldCollapse ? '<button class="code-btn code-toggle" type="button">展开</button>' : '';
                
                const tools = document.createElement("div");
                tools.className = "code-tools";
                tools.innerHTML = `
                    <span class="code-title">CODE - iTxGo™️</span>
                    <div style="display:flex; gap:8px; align-items:center;">
                        ${toggleBtnHTML}
                        <button class="code-btn code-copy" type="button" style="width:43px; min-width:43px; text-align:center;">复制</button>
                    </div>
                `;
                pre.insertBefore(tools, pre.firstChild);
            }

            // ==================== 复制功能 ====================
            const copyBtn = pre.querySelector(".code-copy");
            if (copyBtn && !copyBtn.hasListener) {
                copyBtn.hasListener = true;
                copyBtn.addEventListener("click", async () => {
                    try {
                        await navigator.clipboard.writeText(raw);
                        showCopyFeedback(copyBtn);
                    } catch (e) {
                        if (copyWithFallback(raw)) showCopyFeedback(copyBtn);
                        else showCopyError(copyBtn, "复制失败");
                    }
                });
            }

            // ==================== 折叠功能 ====================
            const shouldCollapse = lines.length > CODE_CONFIG.COLLAPSE_LINE_THRESHOLD;
            if (!shouldCollapse) {
                pre.classList.add("expanded");
                pre.classList.remove("collapsed");
                return;
            }

            // 计算折叠高度
            const cs = getComputedStyle(codeEl);
            const fontSize = parseFloat(cs.fontSize) || 15;
            const lineHeight = parseFloat(cs.lineHeight) || fontSize * 1.6;
            
            // 根据你的 CSS:
            // padding-top: 48px (工具栏) + CODE_CONFIG.COLLAPSE_LINES * lineHeight + padding-bottom
            const ps = getComputedStyle(pre);
            const paddingTop = parseFloat(ps.paddingTop) || 48;
            const paddingBottom = parseFloat(ps.paddingBottom) || 16;
            const collapsedHeight = Math.ceil(paddingTop + lineHeight * CODE_CONFIG.COLLAPSE_LINES + paddingBottom);

            pre._collapsedHeight = collapsedHeight;
            pre.style.maxHeight = collapsedHeight + "px";
            pre.classList.add("collapsed");
            pre.classList.remove("expanded");

            // ==================== 折叠按钮事件 ====================
            const toggleBtn = pre.querySelector(".code-toggle");
            if (toggleBtn && !toggleBtn.hasListener) {
                toggleBtn.hasListener = true;
                toggleBtn.addEventListener("click", () => {
                    const isCollapsed = pre.classList.contains("collapsed");
                    if (isCollapsed) {
                        pre.classList.replace("collapsed", "expanded");
                        pre.style.maxHeight = "none";
                        toggleBtn.textContent = "折叠";
                    } else {
                        pre.classList.replace("expanded", "collapsed");
                        pre.style.maxHeight = pre._collapsedHeight + "px";
                        toggleBtn.textContent = "展开";
                    }
                });
            }
        };

        for (let i = 0; i < blocks.length; i++) {
            await processBlock(blocks[i], i);
        }
    });

    // ==================== Resize 处理 ====================
    let resizeTimer = null;
    window.addEventListener("resize", () => {
        clearTimeout(resizeTimer);
        resizeTimer = setTimeout(() => {
            document.querySelectorAll(".article-content pre.collapsed").forEach(pre => {
                const codeEl = pre.querySelector("code") || pre;
                const cs = getComputedStyle(codeEl);
                const fs = parseFloat(cs.fontSize) || 15;
                const lh = parseFloat(cs.lineHeight) || fs * 1.6;
                const ps = getComputedStyle(pre);
                const pt = parseFloat(ps.paddingTop) || 48;
                const pb = parseFloat(ps.paddingBottom) || 16;
                const newHeight = Math.ceil(pt + lh * CODE_CONFIG.COLLAPSE_LINES + pb);
                pre._collapsedHeight = newHeight;
                pre.style.maxHeight = newHeight + "px";
            });
        }, CODE_CONFIG.RESIZE_DEBOUNCE_DELAY);
    }, { passive: true });
}

function copyWithFallback(text) {
    try {
        const textarea = document.createElement("textarea");
        textarea.value = text;
        textarea.style.position = "fixed";
        textarea.style.left = "-9999px";
        document.body.appendChild(textarea);
        textarea.select();
        const success = document.execCommand("copy");
        document.body.removeChild(textarea);
        return success;
    } catch (err) {
        return false;
    }
}

function showCopyFeedback(btn) {
    const originalText = btn.textContent;
    btn.textContent = "✓";
    btn.disabled = true;
    btn.style.color = "#4ade80";
    setTimeout(() => {
        btn.textContent = originalText;
        btn.disabled = false;
        btn.style.color = "";
    }, CODE_CONFIG.COPY_FEEDBACK_DURATION);
}

function showCopyError(btn, message) {
    const originalText = btn.textContent;
    btn.textContent = "失败";
    btn.disabled = true;
    btn.style.color = "#ef4444";
    setTimeout(() => {
        btn.textContent = originalText;
        btn.disabled = false;
        btn.style.color = "";
    }, CODE_CONFIG.COPY_FEEDBACK_DURATION);
}

// =============================
// 8. 用户下拉菜单
// =============================
function initUserDropdown() {
    const userMenuContainer = document.querySelector('.user-menu-container');
    const userMenuTrigger = document.querySelector('.user-menu-trigger');
    const userDropdown = document.querySelector('.user-dropdown');
    
    if (!userMenuContainer || !userMenuTrigger) return;
    
    userMenuTrigger.addEventListener('click', e => e.preventDefault());
    if (!userDropdown) return;
    
    document.addEventListener('click', e => {
        if (!userMenuContainer.contains(e.target)) {
            userMenuContainer.classList.remove('dropdown-open');
        }
    });
    
    document.addEventListener('keydown', e => {
        if (e.key === 'Escape') userMenuContainer.classList.remove('dropdown-open');
    });
    
    let leaveTimer = null;
    userMenuContainer.addEventListener('mouseleave', () => {
        leaveTimer = setTimeout(() => userMenuContainer.classList.remove('dropdown-open'), 300);
    });
    
    userMenuContainer.addEventListener('mouseenter', () => {
        clearTimeout(leaveTimer);
        leaveTimer = null;
    });
    
    // 高亮当前菜单项
    userDropdown.querySelectorAll('.user-dropdown-item').forEach(item => {
        if (window.location.pathname.includes(item.getAttribute('href'))) {
            item.style.fontWeight = '600';
            item.style.color = 'var(--color-text-primary)';
        }
    });
}

// =============================
// 9. 模态框功能
// =============================
function initModals() {
    const formStates = {
        loginForm: { originalText: '登录', isSubmitting: false },
        registerForm: { originalText: '注册', isSubmitting: false }
    };

    function getModalId(formId) { return formId.replace('Form', 'Modal'); }
    
    function openModal(modalId) {
        const modal = document.getElementById(modalId);
        if (!modal) return;
        modal.style.display = 'flex';
        const messageDiv = modal.querySelector('.modal-message');
        if (messageDiv) {
            messageDiv.innerHTML = '';
            messageDiv.className = 'modal-message';
        }
        resetForm(modalId.replace('Modal', 'Form'));
    }

    function closeModal(modalId) {
        const modal = document.getElementById(modalId);
        if (modal) modal.style.display = 'none';
    }

    function showMessage(modalId, message, isError = true) {
        const messageDiv = document.getElementById(modalId)?.querySelector('.modal-message');
        if (messageDiv) {
            messageDiv.textContent = message;
            messageDiv.className = `modal-message ${isError ? 'alert-error' : 'alert-success'}`;
        }
    }

    function resetForm(formId) {
        const form = document.getElementById(formId);
        const state = formStates[formId];
        if (form) form.reset();
        if (state?.isSubmitting) {
            const submitBtn = form?.querySelector('button[type="submit"]');
            if (submitBtn) {
                submitBtn.disabled = false;
                submitBtn.textContent = state.originalText;
            }
            state.isSubmitting = false;
        }
    }

    function handleFormSubmit(formId, endpoint, onSuccess) {
        const form = document.getElementById(formId);
        if (!form) return;
        
        const state = formStates[formId];
        const modalId = getModalId(formId);

        form.addEventListener('submit', function(e) {
            e.preventDefault();
            if (state.isSubmitting) return;

            const submitBtn = this.querySelector('button[type="submit"]');
            state.isSubmitting = true;
            submitBtn.disabled = true;
            submitBtn.textContent = `${state.originalText}中...`;

            fetch(endpoint, { method: 'POST', body: new FormData(this) })
                .then(r => r.json())
                .then(data => {
                    showMessage(modalId, data.message, !data.success);
                    if (data.success) {
                        onSuccess?.(data);
                    } else {
                        state.isSubmitting = false;
                        submitBtn.disabled = false;
                        submitBtn.textContent = state.originalText;
                    }
                })
                .catch(error => {
                    showMessage(modalId, '网络错误,请重试。', true);
                    state.isSubmitting = false;
                    submitBtn.disabled = false;
                    submitBtn.textContent = state.originalText;
                });
        });
    }

    // 模态框交互事件
    document.querySelectorAll('[data-modal]').forEach(link => {
        link.addEventListener('click', e => {
            e.preventDefault();
            openModal(link.getAttribute('data-modal'));
        });
    });

    document.querySelectorAll('.modal-close').forEach(btn => {
        btn.addEventListener('click', () => {
            closeModal(btn.getAttribute('data-modal'));
            resetForm(btn.getAttribute('data-modal').replace('Modal', 'Form'));
        });
    });

    document.querySelectorAll('.switch-modal').forEach(link => {
        link.addEventListener('click', e => {
            e.preventDefault();
            closeModal(link.getAttribute('data-from'));
            resetForm(link.getAttribute('data-from').replace('Modal', 'Form'));
            openModal(link.getAttribute('data-to'));
        });
    });

    handleFormSubmit('loginForm', 'ajax_login.php', data => {
        setTimeout(() => window.location.href = data.redirect || 'index.php', 1000);
    });

    handleFormSubmit('registerForm', 'ajax_register.php', () => {
        setTimeout(() => {
            closeModal('registerModal');
            openModal('loginModal');
        }, 1500);
    });

    document.addEventListener('keydown', e => {
        if (e.key === 'Escape') {
            document.querySelectorAll('.modal').forEach(modal => {
                if (modal.style.display === 'flex') {
                    closeModal(modal.id);
                    resetForm(modal.id.replace('Modal', 'Form'));
                }
            });
        }
    });
}

// =============================
// 10. 副标题动态内容
// =============================
function initSubtitleManager() {
    class SubtitleManager {
        constructor() {
            this.subtitleElement = document.getElementById('subtitle');
            this.subtitleTexts = [
                "👋 欢迎来到我的博客 :)",
                "⚠️ 网站正在建设中,敬请期待!",
                "🌱 Every day, in every way, I am getting better and better.",
                "❤️‍🔥 热爱可抵岁月漫长…",
                "✨ 记录成长轨迹,分享生活微光。",
            ];
            this.init();
        }
        
        init() {
            if (!this.subtitleElement) return;
            // 批量设置样式
            Object.assign(this.subtitleElement.style, {
                minHeight: '16px',
                display: 'flex',
                alignItems: 'center',
                whiteSpace: 'nowrap',
                overflow: 'hidden',
                textOverflow: 'ellipsis'
            });
            this.setRandomSubtitle();
        }
        
        setRandomSubtitle() {
            const randomText = this.subtitleTexts[Math.floor(Math.random() * this.subtitleTexts.length)];
            this.subtitleElement.style.opacity = '0';
            setTimeout(() => {
                this.subtitleElement.textContent = randomText;
                this.subtitleElement.style.opacity = '1';
            }, 150);
        }
    }
    window.subtitleManager = new SubtitleManager();
}

// =============================
// 11. 文章目录生成
// =============================
function initArticleTOC() {
    const articleContent = document.querySelector('.article-content');
    const placeholderDiv = document.getElementById('toc-placeholder');
    
    if (!articleContent || !placeholderDiv) return;
    
    const headings = articleContent.querySelectorAll('h2, h3');
    
    if (headings.length === 0) {
        placeholderDiv.innerHTML = '<p style="text-align:center;color:#999;padding:20px">本文暂无目录...</p>';
        return;
    }
    
    const tocWrapper = document.createElement('div');
    tocWrapper.className = 'toc-wrapper';
    
    // 使用 Fragment 减少重排
    const fragment = document.createDocumentFragment();
    let firstH2 = true;

    headings.forEach((h, i) => {
        if (!h.id) h.id = `heading-${i}`;
        const level = h.tagName.toLowerCase();
        const isTitle = level === 'h2' && firstH2;
        if (isTitle) firstH2 = false;
        
        const tocItem = document.createElement('div');
        tocItem.className = `toc-item toc-${level}${isTitle ? ' toc-title' : ''}`;
        
        const tocLink = document.createElement('a');
        tocLink.href = `#${h.id}`;
        tocLink.className = 'toc-link';
        tocLink.dataset.id = h.id;
        tocLink.title = h.textContent.trim();
        
        const tocText = document.createElement('span');
        tocText.className = 'toc-text';
        tocText.textContent = h.textContent.trim();
        
        tocLink.appendChild(tocText);
        tocItem.appendChild(tocLink);
        fragment.appendChild(tocItem);
    });
    
    tocWrapper.appendChild(fragment);
    placeholderDiv.innerHTML = '';
    placeholderDiv.appendChild(tocWrapper);
    
    initTOCInteraction(headings);
}

// =============================
// 12. 目录交互功能
// =============================
function initTOCInteraction(headings) {
    const links = document.querySelectorAll('.toc-link');
    if (!links.length) return;
    
    let isTOCScrolling = false;
    let scrollEndTimer = null;
    
    const setActive = (link) => {
        const current = document.querySelector('.toc-link.active');
        if (current === link) return;
        current?.classList.remove('active');
        link?.classList.add('active');
    };
    
    // 使用 IntersectionObserver 替代 scroll 事件计算,大幅提升性能
    const observerOptions = {
        rootMargin: '-100px 0px -66% 0px', // 视口中间偏上区域触发
        threshold: 0
    };
    
    const observer = new IntersectionObserver(entries => {
        if (isTOCScrolling) return; // 如果是点击触发的滚动,不更新高亮
        
        // 获取当前视口内的最后一个标题
        let lastEntry = null;
        for (const entry of entries) {
            if (entry.isIntersecting) {
                lastEntry = entry;
            }
        }
        
        if (lastEntry) {
            const activeLink = document.querySelector(`.toc-link[data-id="${lastEntry.target.id}"]`);
            if (activeLink) setActive(activeLink);
        }
    }, observerOptions);
    
    headings.forEach(h => observer.observe(h));
    
    // 点击事件
    links.forEach(link => {
        link.addEventListener('click', (e) => {
            e.preventDefault();
            const target = document.getElementById(link.dataset.id);
            if (!target) return;
            
            isTOCScrolling = true;
            setActive(link);
            
            const highlight = document.querySelector('.heading-highlight');
            highlight?.classList.remove('heading-highlight');
            
            // 实时获取导航栏高度
            const header = document.querySelector('.header-container');
            const currentHeaderHeight = header ? header.getBoundingClientRect().height : 80;
            
            const targetY = target.getBoundingClientRect().top 
                          + window.scrollY 
                          - currentHeaderHeight 
                          - 168;
            
            ScrollManager.smoothScrollTo(targetY, {
                duration: 600,
                onComplete: () => {
                    target.classList.add('heading-highlight');
                    // 延时恢复 observer 控制
                    setTimeout(() => {
                        target.classList.remove('heading-highlight');
                        isTOCScrolling = false;
                    }, 2500);
                }
            });
        });
    });
    
    // 备用:如果 Observer 不支持
    if (!('IntersectionObserver' in window)) {
        window.addEventListener('scroll', () => {
            if (isTOCScrolling) return;
            clearTimeout(scrollEndTimer);
            scrollEndTimer = setTimeout(() => {
                const scrollY = window.scrollY;
                let activeHeading = null;
                let minDistance = Infinity;
                
                headings.forEach(heading => {
                    const rect = heading.getBoundingClientRect();
                    const distance = Math.abs(rect.top - 200);
                    if (distance < minDistance && rect.top > 0) {
                        minDistance = distance;
                        activeHeading = heading;
                    }
                });
                
                if (!activeHeading) {
                    for (let i = headings.length - 1; i >= 0; i--) {
                        if (headings[i].getBoundingClientRect().top < window.innerHeight) {
                            activeHeading = headings[i];
                            break;
                        }
                    }
                }
                
                if (activeHeading) {
                    const activeLink = document.querySelector(`.toc-link[data-id="${activeHeading.id}"]`);
                    if (activeLink) setActive(activeLink);
                }
            }, 100);
        }, { passive: true });
    }
}

// =============================
// 13. 搜索冷却功能
// =============================
function initSearchCooldown() {
    const form = document.getElementById('searchForm');
    const input = document.getElementById('searchInput');
    const button = document.getElementById('searchButton');
    if (!form || !input || !button) return;

    const isAdmin = document.body.hasAttribute('data-is-admin');
    const cooldownEnd = parseInt(localStorage.getItem('search_cooldown_end') || '0', 10);
    const now = Date.now();

    if (!isAdmin && now < cooldownEnd) {
        startCountdown(Math.ceil((cooldownEnd - now) / 1000));
    } else {
        enableSearch();
    }

    form.addEventListener('submit', function (e) {
        if (!input.value.trim()) {
            e.preventDefault();
            return;
        }
        if (!isAdmin) {
            const newCooldownEnd = Date.now() + 30 * 1000;
            localStorage.setItem('search_cooldown_end', newCooldownEnd.toString());
        }
    });

    function startCountdown(seconds) {
        let remaining = seconds;
        updateUI();
        const interval = setInterval(() => {
            remaining--;
            if (remaining <= 0) {
                clearInterval(interval);
                enableSearch();
                localStorage.removeItem('search_cooldown_end');
            } else {
                updateUI();
            }
        }, 1000);

        function updateUI() {
            input.placeholder = `⏳ ${remaining} 秒...`;
            input.disabled = true;
            input.value = '';
            button.disabled = true;
        }
    }

    function enableSearch() {
        input.placeholder = '搜索文章...';
        input.disabled = false;
        button.disabled = false;
    }
}

// =============================
// 14. 导航栏滚动收缩功能 - 兼容版
// =============================
function initHeaderShrink() {
    
    const runInit = () => {
        const header = document.querySelector('.header-container');
        if (!header) return;

        const SHRINK_AT = 80;
        const EXPAND_AT = 20;

        let isShrunk = false;
        let lastScrollY = window.scrollY;
        let ticking = false;

        const updateHeaderState = () => {
            const scrollY = window.scrollY;
            const isScrollingDown = scrollY > lastScrollY;
            lastScrollY = scrollY;

            if (isScrollingDown && !isShrunk && scrollY > SHRINK_AT) {
                header.classList.add('shrunk');
                isShrunk = true;
            } else if (!isScrollingDown && isShrunk && scrollY < EXPAND_AT) {
                header.classList.remove('shrunk');
                isShrunk = false;
            }
            ticking = false;
        };

        const onScroll = () => {
            if (!ticking) {
                requestAnimationFrame(updateHeaderState);
                ticking = true;
            }
        };

        window.addEventListener('scroll', onScroll, { passive: true });
        
        console.log('✅ Header 收缩功能已就绪');
    };

    // 100ms后执行
    if ('requestIdleCallback' in window) {
        requestIdleCallback(runInit, { timeout: 100 });
    } else {
        // 兼容老旧浏览器
        setTimeout(runInit, 100);
    }
}

// =============================
// 结束日志
// =============================
console.log('✅ 所有脚本模块已优化并加载完成');