跳转到内容
打开/关闭菜单
  • 451 条目
  • 806 文件
  • 22 用户
  • 9167 编辑
Blockland Wiki
打开/关闭外观设置菜单
打开/关闭个人菜单
未登录
未登录用户的IP地址会在进行任意编辑后公开展示。

MediaWiki:Common.js:修订间差异

MediaWiki界面页面
ETTW​(留言 | 贡献)
无编辑摘要
ETTW​(留言 | 贡献)
无编辑摘要
第230行: 第230行:
         }
         }
          
          
         // 创建Liquid选项(状态3 - 高级液态玻璃)
         // 创建Liquid选项(状态3)
         const liquidOption = document.createElement('div');
         const liquidOption = document.createElement('div');
         liquidOption.className = 'citizen-client-prefs-radio';
         liquidOption.className = 'citizen-client-prefs-radio';
第278行: 第278行:
                 htmlElement.className = htmlElement.className.replace(/citizen-feature-performance-mode-clientpref-\d/g, '');
                 htmlElement.className = htmlElement.className.replace(/citizen-feature-performance-mode-clientpref-\d/g, '');
                 htmlElement.classList.add('citizen-feature-performance-mode-clientpref-' + value);
                 htmlElement.classList.add('citizen-feature-performance-mode-clientpref-' + value);
               
                // 状态3时初始化液态玻璃
                if (value === '3') {
                    initLiquidGlassEffect();
                } else {
                    destroyLiquidGlassEffect();
                }
             });
             });
         });
         });
第290行: 第297行:
      
      
})();
})();
// 高级液态玻璃效果
 
function initAdvancedLiquidGlass() {
// 液态玻璃效果
     if (window.liquidGlassAdvanced) {
function initLiquidGlassEffect() {
     if (window.liquidGlassEffect) {
         return; // 已经初始化
         return; // 已经初始化
     }
     }
      
      
     // 创建液态玻璃着色器
     // Utility functions
     class Shader {
     function smoothStep(a, b, t) {
         constructor(options = {}) {
         t = Math.max(0, Math.min(1, (t - a) / (b - a)));
            this.width = options.width || 100;
        return t * t * (3 - 2 * t);
            this.height = options.height || 100;
    }
            this.fragment = options.fragment || ((uv) => ({ type: 't', x: uv.x, y: uv.y }));
 
            this.canvasDPI = 1;
    function length(x, y) {
            this.id = 'liquid-glass-' + Math.random().toString(36).substr(2, 9);
        return Math.sqrt(x * x + y * y);
             this.offset = 10;
    }
             this.mouse = { x: 0, y: 0 };
 
    function roundedRectSDF(x, y, width, height, radius) {
        const qx = Math.abs(x) - width + radius;
        const qy = Math.abs(y) - height + radius;
        return Math.min(Math.max(qx, qy), 0) + length(Math.max(qx, 0), Math.max(qy, 0)) - radius;
    }
 
    function generateId() {
        return 'liquid-glass-' + Math.random().toString(36).substr(2, 9);
    }
 
    class LiquidGlassShader {
        constructor() {
            this.id = generateId();
             this.width = window.innerWidth;
            this.height = window.innerHeight;
            this.canvasDPI = 0.5; // 降低DPI以提高性能
           
             this.mouse = { x: 0.5, y: 0.5 };
             this.mouseUsed = false;
             this.mouseUsed = false;
            this.animationFrame = null;
              
              
             this.createElement();
             this.createSVGFilter();
            this.createCanvas();
             this.setupEventListeners();
             this.setupEventListeners();
             this.updateShader();
             this.updateShader();
            this.startAnimation();
         }
         }


         createElement() {
         createSVGFilter() {
            this.container = document.createElement('div');
            this.container.style.cssText = `
                position: fixed;
                top: 50%;
                left: 50%;
                transform: translate(-50%, -50%);
                width: ${this.width}px;
                height: ${this.height}px;
                overflow: hidden;
                border-radius: 150px;
                box-shadow: 0 4px 8px rgba(0, 0, 0, 0.25), 0 -10px 25px inset rgba(0, 0, 0, 0.15);
                cursor: grab;
                backdrop-filter: url(#${this.id}_filter) blur(0.25px) contrast(1.2) brightness(1.05) saturate(1.1);
                z-index: 9999;
                pointer-events: auto;
            `;
 
             this.svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
             this.svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
             this.svg.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
             this.svg.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
第340行: 第352行:
                 left: 0;
                 left: 0;
                 pointer-events: none;
                 pointer-events: none;
                 z-index: 9998;
                 z-index: 1;
             `;
             `;


第369行: 第381行:
             this.svg.appendChild(defs);
             this.svg.appendChild(defs);


            document.body.appendChild(this.svg);
        }
        createCanvas() {
             this.canvas = document.createElement('canvas');
             this.canvas = document.createElement('canvas');
             this.canvas.width = this.width * this.canvasDPI;
             this.canvas.width = this.width * this.canvasDPI;
第374行: 第390行:
             this.canvas.style.display = 'none';
             this.canvas.style.display = 'none';
             this.context = this.canvas.getContext('2d');
             this.context = this.canvas.getContext('2d');
        }
        constrainPosition(x, y) {
            const viewportWidth = window.innerWidth;
            const viewportHeight = window.innerHeight;
            const minX = this.offset;
            const maxX = viewportWidth - this.width - this.offset;
            const minY = this.offset;
            const maxY = viewportHeight - this.height - this.offset;
            const constrainedX = Math.max(minX, Math.min(maxX, x));
            const constrainedY = Math.max(minY, Math.min(maxY, y));
            return { x: constrainedX, y: constrainedY };
         }
         }


         setupEventListeners() {
         setupEventListeners() {
            let isDragging = false;
            let startX, startY, initialX, initialY;
            this.container.addEventListener('mousedown', (e) => {
                isDragging = true;
                this.container.style.cursor = 'grabbing';
                startX = e.clientX;
                startY = e.clientY;
                const rect = this.container.getBoundingClientRect();
                initialX = rect.left;
                initialY = rect.top;
                e.preventDefault();
            });
             document.addEventListener('mousemove', (e) => {
             document.addEventListener('mousemove', (e) => {
                if (isDragging) {
                 this.mouse.x = e.clientX / this.width;
                    const deltaX = e.clientX - startX;
                 this.mouse.y = e.clientY / this.height;
                    const deltaY = e.clientY - startY;
                 this.mouseUsed = true;
                    const newX = initialX + deltaX;
                    const newY = initialY + deltaY;
                    const constrained = this.constrainPosition(newX, newY);
                    this.container.style.left = constrained.x + 'px';
                    this.container.style.top = constrained.y + 'px';
                    this.container.style.transform = 'none';
                }
 
                const rect = this.container.getBoundingClientRect();
                 this.mouse.x = (e.clientX - rect.left) / rect.width;
                 this.mouse.y = (e.clientY - rect.top) / rect.height;
                  
                if (this.mouseUsed) {
                    this.updateShader();
                }
            });
 
            document.addEventListener('mouseup', () => {
                isDragging = false;
                this.container.style.cursor = 'grab';
             });
             });


             window.addEventListener('resize', () => {
             window.addEventListener('resize', () => {
                 const rect = this.container.getBoundingClientRect();
                 this.width = window.innerWidth;
                 const constrained = this.constrainPosition(rect.left, rect.top);
                this.height = window.innerHeight;
                 if (rect.left !== constrained.x || rect.top !== constrained.y) {
                 this.canvas.width = this.width * this.canvasDPI;
                    this.container.style.left = constrained.x + 'px';
                this.canvas.height = this.height * this.canvasDPI;
                    this.container.style.top = constrained.y + 'px';
                 this.svg.setAttribute('width', this.width.toString());
                    this.container.style.transform = 'none';
                this.svg.setAttribute('height', this.height.toString());
                }
                this.feImage.setAttribute('width', this.width.toString());
                this.feImage.setAttribute('height', this.height.toString());
             });
             });
        }
        smoothStep(a, b, t) {
            t = Math.max(0, Math.min(1, (t - a) / (b - a)));
            return t * t * (3 - 2 * t);
        }
        length(x, y) {
            return Math.sqrt(x * x + y * y);
        }
        roundedRectSDF(x, y, width, height, radius) {
            const qx = Math.abs(x) - width + radius;
            const qy = Math.abs(y) - height + radius;
            return Math.min(Math.max(qx, qy), 0) + this.length(Math.max(qx, 0), Math.max(qy, 0)) - radius;
         }
         }


         updateShader() {
         updateShader() {
            const mouseProxy = new Proxy(this.mouse, {
                get: (target, prop) => {
                    this.mouseUsed = true;
                    return target[prop];
                }
            });
            this.mouseUsed = false;
             const w = this.width * this.canvasDPI;
             const w = this.width * this.canvasDPI;
             const h = this.height * this.canvasDPI;
             const h = this.height * this.canvasDPI;
第471行: 第418行:
             let maxScale = 0;
             let maxScale = 0;
             const rawValues = [];
             const rawValues = [];
            // 获取鼠标位置(归一化到0-1)
            const mouseProxy = {
                x: this.mouse.x,
                y: this.mouse.y
            };


             for (let i = 0; i < data.length; i += 4) {
             for (let i = 0; i < data.length; i += 4) {
                 const x = (i / 4) % w;
                 const px = (i / 4) % w;
                 const y = Math.floor(i / 4 / w);
                 const py = Math.floor(i / 4 / w);
                 const pos = this.fragment(
                 const uvX = px / w;
                    { x: x / w, y: y / h },
                const uvY = py / h;
                    mouseProxy
 
                 );
                // 计算距离边缘的SDF
                 const dx = pos.x * w - x;
                const ix = uvX - 0.5;
                 const dy = pos.y * h - y;
                const iy = uvY - 0.5;
                const distanceToEdge = roundedRectSDF(ix, iy, 0.3, 0.2, 0.6);
                const displacement = smoothStep(0.8, 0, distanceToEdge - 0.15);
                 const scaled = smoothStep(0, 1, displacement);
 
                const posX = ix * scaled + 0.5;
                const posY = iy * scaled + 0.5;
 
                 const dx = posX * w - px;
                 const dy = posY * h - py;
 
                 maxScale = Math.max(maxScale, Math.abs(dx), Math.abs(dy));
                 maxScale = Math.max(maxScale, Math.abs(dx), Math.abs(dy));
                 rawValues.push(dx, dy);
                 rawValues.push(dx, dy);
第491行: 第454行:
                 const r = rawValues[index++] / maxScale + 0.5;
                 const r = rawValues[index++] / maxScale + 0.5;
                 const g = rawValues[index++] / maxScale + 0.5;
                 const g = rawValues[index++] / maxScale + 0.5;
                 data[i] = r * 255;
                 data[i] = Math.max(0, Math.min(255, r * 255));
                 data[i + 1] = g * 255;
                 data[i + 1] = Math.max(0, Math.min(255, g * 255));
                 data[i + 2] = 0;
                 data[i + 2] = 0;
                 data[i + 3] = 255;
                 data[i + 3] = 255;
第502行: 第465行:
         }
         }


         appendTo(parent) {
         startAnimation() {
             parent.appendChild(this.svg);
             const animate = () => {
             parent.appendChild(this.container);
                if (this.mouseUsed) {
                    this.updateShader();
                    this.mouseUsed = false;
                }
                this.animationFrame = requestAnimationFrame(animate);
             };
            animate();
         }
         }


         destroy() {
         destroy() {
            if (this.animationFrame) {
                cancelAnimationFrame(this.animationFrame);
            }
             this.svg.remove();
             this.svg.remove();
            this.container.remove();
             this.canvas.remove();
             this.canvas.remove();
        }
        getFilterId() {
            return `${this.id}_filter`;
         }
         }
     }
     }


     // 创建着色器
     // 初始化着色器
     const shader = new Shader({
     const shader = new LiquidGlassShader();
        width: 300,
    window.liquidGlassEffect = shader;
        height: 200,
 
        fragment: function(uv, mouse) {
    // 将SVG滤镜应用到菜单后面的内容
            const ix = uv.x - 0.5;
    const applyFilter = () => {
            const iy = uv.y - 0.5;
        const html = document.documentElement;
             const distanceToEdge = this.roundedRectSDF(
        if (html.classList.contains('citizen-feature-performance-mode-clientpref-3')) {
                 ix,
            // 应用到菜单后面的背景内容
                 iy,
             const menuCards = document.querySelectorAll(
                 0.3,
                 '.citizen-menu__card, ' +
                 0.2,
                 '#citizen-preferences__card, ' +
                0.6
                 '.citizen-command-palette, ' +
                 '.mwe-popups .mwe-popups-container'
             );
             );
             const displacement = this.smoothStep(0.8, 0, distanceToEdge - 0.15);
 
             const scaled = this.smoothStep(0, 1, displacement);
             menuCards.forEach(card => {
            return { type: 't', x: ix * scaled + 0.5, y: iy * scaled + 0.5 };
                card.style.filter = `url(#${shader.getFilterId()})`;
             });
        }
    };
 
    // 延迟应用以确保SVG已加载
    setTimeout(applyFilter, 100);
 
    // 监听DOM变化,动态应用滤镜
    const observer = new MutationObserver(() => {
        if (document.documentElement.classList.contains('citizen-feature-performance-mode-clientpref-3')) {
            applyFilter();
         }
         }
     });
     });


     shader.appendTo(document.body);
     observer.observe(document.body, {
    window.liquidGlassAdvanced = shader;
        childList: true,
     console.log('高级液态玻璃效果已启用');
        subtree: true
    });
 
     console.log('液态玻璃效果已启用');
}
}


function destroyAdvancedLiquidGlass() {
function destroyLiquidGlassEffect() {
     if (window.liquidGlassAdvanced) {
     if (window.liquidGlassEffect) {
         window.liquidGlassAdvanced.destroy();
         window.liquidGlassEffect.destroy();
         window.liquidGlassAdvanced = null;
         window.liquidGlassEffect = null;
         console.log('高级液态玻璃效果已关闭');
 
        // 移除滤镜
        const menuCards = document.querySelectorAll(
            '.citizen-menu__card, ' +
            '#citizen-preferences__card, ' +
            '.citizen-command-palette, ' +
            '.mwe-popups .mwe-popups-container'
        );
 
        menuCards.forEach(card => {
            card.style.filter = '';
        });
 
         console.log('液态玻璃效果已关闭');
     }
     }
}
}

2026年4月18日 (六) 15:52的版本

/* 这里的任何JavaScript将为所有用户在每次页面加载时加载。 */

// 侧边栏广告
$(function() {
    if ($('#p-navigation').length) {
        var adHtml = '<div id="sidebar-ad"></div>';
        $('#p-navigation').after(adHtml);
        $.get(mw.util.wikiScript('api'), {
            action: 'parse',
            text: '{{CitizenAd}}',
            format: 'json'
        }, function(data) {
            $('#sidebar-ad').html(data.parse.text['*']);
        });
    }
});

/**
 * Blockland Wiki Liquid Glass Lighting System
 */
(function() {
    'use strict';
    
    var isPerformanceMode = function() {
        return document.documentElement.classList.contains('citizen-feature-performance-mode-clientpref-1');
    };

    var GLASS_SELECTORS = '#mw-panel-toc, #citizen-sidebar-lastmod, .aero-news-box, .citizen-header, .citizen-menu__card, .citizen-toc-card, .citizen-command-palette, .infobox, table.ambox, .mwe-popups-container, .oo-ui-popupWidget-popup, .oo-ui-menuSelectWidget, .voy-box';

    var lastMouseX = 0;
    var lastMouseY = 0;

    var syncLight = function(e) {
        if (isPerformanceMode()) return;

        lastMouseX = e.clientX;
        lastMouseY = e.clientY;

        var elements = document.querySelectorAll(GLASS_SELECTORS);
        elements.forEach(function(el) {
            var rect = el.getBoundingClientRect();
            
            if (rect.width > 0 && rect.top < window.innerHeight && rect.bottom > 0) {
                var x = ((e.clientX - rect.left) / rect.width) * 100;
                var y = ((e.clientY - rect.top) / rect.height) * 100;
                
                el.style.setProperty('--mouse-x', x.toFixed(2) + '%');
                el.style.setProperty('--mouse-y', y.toFixed(2) + '%');
            }
        });
    };

    var initPopupLight = function(container) {
        if (isPerformanceMode()) return;
        
        var rect = container.getBoundingClientRect();
        if (rect.width > 0) {
            var x = ((lastMouseX - rect.left) / rect.width) * 100;
            var y = ((lastMouseY - rect.top) / rect.height) * 100;
            container.style.setProperty('--mouse-x', x.toFixed(2) + '%');
            container.style.setProperty('--mouse-y', y.toFixed(2) + '%');
        }
    };

    setInterval(function() {
        if (isPerformanceMode()) return; 
        var p = document.querySelector('.citizen-command-palette');
        if (p && !p.classList.contains('glass-show')) {
            p.classList.add('glass-show');
        }
    }, 100);

    var ticking = false;
    document.addEventListener('mousemove', function(e) {
        if (isPerformanceMode()) return;
        if (!ticking) {
            window.requestAnimationFrame(function() {
                syncLight(e);
                ticking = false;
            });
            ticking = true;
        }
    }, { passive: true });

    var modeObserver = new MutationObserver(function() {
        if (isPerformanceMode()) {
            document.querySelectorAll(GLASS_SELECTORS).forEach(function(el) {
                el.style.removeProperty('--mouse-x');
                el.style.removeProperty('--mouse-y');
            });
        }
    });
    modeObserver.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] });

    window._liquidGlassInitPopup = initPopupLight;
})();

/**
 * Section25. Wiki 弹出预览菜单动画控制
 */
(function() {
    'use strict';
    
    var style = document.createElement('style');
    style.textContent = `
        .mwe-popups .mwe-popups-container { 
            opacity: 0 !important; 
            transform: translateY(-12px) !important;
            transition: opacity 0.35s cubic-bezier(0.16, 1, 0.3, 1), 
                        transform 0.35s cubic-bezier(0.16, 1, 0.3, 1) !important;
        } 
        .mwe-popups.popup-show .mwe-popups-container { 
            opacity: 1 !important; 
            transform: translateY(0) !important;
        }
        .mwe-popups.flipped-y .mwe-popups-container {
            transform: translateY(12px) !important;
        }
        .mwe-popups.flipped-y.popup-show .mwe-popups-container {
            transform: translateY(0) !important;
        }
    `;
    document.head.appendChild(style);
    
    var seen = new WeakSet();
    
    new MutationObserver(function(muts) {
        muts.forEach(function(m) {
            m.addedNodes.forEach(function(node) {
                if (node.classList && node.classList.contains('mwe-popups') && !seen.has(node)) {
                    seen.add(node);
                    
                    var container = node.querySelector('.mwe-popups-container');
                    if (!container) return;
                    
                    if (window._liquidGlassInitPopup) {
                        window._liquidGlassInitPopup(container);
                    }
                    
                    node.style.pointerEvents = 'none';
                    container.offsetHeight;
                    
                    requestAnimationFrame(function() {
                        requestAnimationFrame(function() {
                            node.classList.add('popup-show');
                            setTimeout(function() {
                                node.style.pointerEvents = '';
                            }, 350);
                        });
                    });
                }
            });
        });
    }).observe(document.body, { childList: true, subtree: true });
})();
/**
 * Section19. 通知面板动画控制
 * 检测到面板后再开始动画
 */
(function() {
    'use strict';
    
    function handlePopup(popup) {
        if (popup.dataset.animatorInited) return;
        popup.dataset.animatorInited = 'true';

        const observer = new MutationObserver(() => {
            const inner = popup.querySelector('.oo-ui-popupWidget-popup');
            if (!inner) return;

            const isVisible = popup.style.display !== 'none' && popup.offsetParent !== null;
            
if (isVisible) {
    if (inner.getAttribute('data-is-active') !== 'true') {
        inner.setAttribute('data-is-active', 'true');

        // 1. 初始化:置于下方并透明
        inner.style.setProperty('transition', 'none', 'important');
        inner.style.setProperty('opacity', '0', 'important');
        inner.style.setProperty('transform', 'translateY(20px)', 'important');

        inner.offsetHeight; // 触发重绘

        requestAnimationFrame(() => {
            setTimeout(() => {
                // 2. 向上滑动并渐显
                inner.style.setProperty('transition', 'opacity 0.3s ease-out, transform 0.4s cubic-bezier(0.23, 1, 0.32, 1)', 'important');
                inner.style.setProperty('opacity', '1', 'important');
                inner.style.setProperty('transform', 'translateY(0)', 'important');
            }, 10);
        });
    }
            } else {
                // 关闭时直接重置,不加动画,确保下次打开干净利落
                if (inner.getAttribute('data-is-active') === 'true') {
                    inner.removeAttribute('data-is-active');
                    inner.style.removeProperty('opacity');
                    inner.style.removeProperty('transform');
                    inner.style.removeProperty('transition');
                    inner.style.removeProperty('transform-origin');
                }
            }
        });

        observer.observe(popup, { attributes: true, attributeFilter: ['style', 'class'] });
    }

    const scanner = new MutationObserver(() => {
        document.querySelectorAll('.mw-echo-ui-notificationBadgeButtonPopupWidget-popup').forEach(handlePopup);
    });
    scanner.observe(document.body, { childList: true, subtree: true });
    document.querySelectorAll('.mw-echo-ui-notificationBadgeButtonPopupWidget-popup').forEach(handlePopup);
})();

// 添加液态玻璃按钮
(function() {
    'use strict';
    
    function addLiquidGlassOption() {
        const form = document.querySelector('#skin-client-prefs-citizen-feature-performance-mode form');
        
        if (!form) {
            setTimeout(addLiquidGlassOption, 100);
            return;
        }
        
        // 检查是否已经添加过
        if (document.getElementById('skin-client-pref-citizen-feature-performance-mode-value-3')) {
            return;
        }
        
        // 创建Liquid选项(状态3)
        const liquidOption = document.createElement('div');
        liquidOption.className = 'citizen-client-prefs-radio';
        liquidOption.innerHTML = `
            <input name="skin-client-pref-citizen-feature-performance-mode-group" 
                   id="skin-client-pref-citizen-feature-performance-mode-value-3" 
                   type="radio" 
                   value="3" 
                   data-event-name="skin-client-pref-citizen-feature-performance-mode-value-3" 
                   class="citizen-client-prefs-radio__input">
            <span class="citizen-client-prefs-radio__icon"></span>
            <label for="skin-client-pref-citizen-feature-performance-mode-value-3" 
                   class="citizen-client-prefs-radio__label">Liquid</label>
        `;
        
        // 创建抽象玻璃选项(状态2)
        const abstractGlassOption = document.createElement('div');
        abstractGlassOption.className = 'citizen-client-prefs-radio';
        abstractGlassOption.innerHTML = `
            <input name="skin-client-pref-citizen-feature-performance-mode-group" 
                   id="skin-client-pref-citizen-feature-performance-mode-value-2" 
                   type="radio" 
                   value="2" 
                   data-event-name="skin-client-pref-citizen-feature-performance-mode-value-2" 
                   class="citizen-client-prefs-radio__input">
            <span class="citizen-client-prefs-radio__icon"></span>
            <label for="skin-client-pref-citizen-feature-performance-mode-value-2" 
                   class="citizen-client-prefs-radio__label">抽象玻璃</label>
        `;
        
        // 按顺序添加:状态3在前,状态2在后
        form.appendChild(liquidOption);
        form.appendChild(abstractGlassOption);
        
        console.log('液态玻璃按钮已添加');
        
        // 绑定所有单选按钮的事件
        const allRadios = document.querySelectorAll('input[name="skin-client-pref-citizen-feature-performance-mode-group"]');
        allRadios.forEach(radio => {
            radio.addEventListener('change', function() {
                const value = this.value;
                console.log('选中值:', value);
                localStorage.setItem('citizen-feature-performance-mode-clientpref-', value);
                
                // 更新 HTML class
                const htmlElement = document.documentElement;
                htmlElement.className = htmlElement.className.replace(/citizen-feature-performance-mode-clientpref-\d/g, '');
                htmlElement.classList.add('citizen-feature-performance-mode-clientpref-' + value);
                
                // 状态3时初始化液态玻璃
                if (value === '3') {
                    initLiquidGlassEffect();
                } else {
                    destroyLiquidGlassEffect();
                }
            });
        });
    }
    
    // 页面加载时执行
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', addLiquidGlassOption);
    } else {
        addLiquidGlassOption();
    }
    
})();

// 液态玻璃效果
function initLiquidGlassEffect() {
    if (window.liquidGlassEffect) {
        return; // 已经初始化
    }
    
    // Utility functions
    function smoothStep(a, b, t) {
        t = Math.max(0, Math.min(1, (t - a) / (b - a)));
        return t * t * (3 - 2 * t);
    }

    function length(x, y) {
        return Math.sqrt(x * x + y * y);
    }

    function roundedRectSDF(x, y, width, height, radius) {
        const qx = Math.abs(x) - width + radius;
        const qy = Math.abs(y) - height + radius;
        return Math.min(Math.max(qx, qy), 0) + length(Math.max(qx, 0), Math.max(qy, 0)) - radius;
    }

    function generateId() {
        return 'liquid-glass-' + Math.random().toString(36).substr(2, 9);
    }

    class LiquidGlassShader {
        constructor() {
            this.id = generateId();
            this.width = window.innerWidth;
            this.height = window.innerHeight;
            this.canvasDPI = 0.5; // 降低DPI以提高性能
            
            this.mouse = { x: 0.5, y: 0.5 };
            this.mouseUsed = false;
            this.animationFrame = null;
            
            this.createSVGFilter();
            this.createCanvas();
            this.setupEventListeners();
            this.updateShader();
            this.startAnimation();
        }

        createSVGFilter() {
            this.svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
            this.svg.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
            this.svg.setAttribute('width', '0');
            this.svg.setAttribute('height', '0');
            this.svg.style.cssText = `
                position: fixed;
                top: 0;
                left: 0;
                pointer-events: none;
                z-index: 1;
            `;

            const defs = document.createElementNS('http://www.w3.org/2000/svg', 'defs');
            const filter = document.createElementNS('http://www.w3.org/2000/svg', 'filter');
            filter.setAttribute('id', `${this.id}_filter`);
            filter.setAttribute('filterUnits', 'userSpaceOnUse');
            filter.setAttribute('colorInterpolationFilters', 'sRGB');
            filter.setAttribute('x', '0');
            filter.setAttribute('y', '0');
            filter.setAttribute('width', this.width.toString());
            filter.setAttribute('height', this.height.toString());

            this.feImage = document.createElementNS('http://www.w3.org/2000/svg', 'feImage');
            this.feImage.setAttribute('id', `${this.id}_map`);
            this.feImage.setAttribute('width', this.width.toString());
            this.feImage.setAttribute('height', this.height.toString());

            this.feDisplacementMap = document.createElementNS('http://www.w3.org/2000/svg', 'feDisplacementMap');
            this.feDisplacementMap.setAttribute('in', 'SourceGraphic');
            this.feDisplacementMap.setAttribute('in2', `${this.id}_map`);
            this.feDisplacementMap.setAttribute('xChannelSelector', 'R');
            this.feDisplacementMap.setAttribute('yChannelSelector', 'G');

            filter.appendChild(this.feImage);
            filter.appendChild(this.feDisplacementMap);
            defs.appendChild(filter);
            this.svg.appendChild(defs);

            document.body.appendChild(this.svg);
        }

        createCanvas() {
            this.canvas = document.createElement('canvas');
            this.canvas.width = this.width * this.canvasDPI;
            this.canvas.height = this.height * this.canvasDPI;
            this.canvas.style.display = 'none';
            this.context = this.canvas.getContext('2d');
        }

        setupEventListeners() {
            document.addEventListener('mousemove', (e) => {
                this.mouse.x = e.clientX / this.width;
                this.mouse.y = e.clientY / this.height;
                this.mouseUsed = true;
            });

            window.addEventListener('resize', () => {
                this.width = window.innerWidth;
                this.height = window.innerHeight;
                this.canvas.width = this.width * this.canvasDPI;
                this.canvas.height = this.height * this.canvasDPI;
                this.svg.setAttribute('width', this.width.toString());
                this.svg.setAttribute('height', this.height.toString());
                this.feImage.setAttribute('width', this.width.toString());
                this.feImage.setAttribute('height', this.height.toString());
            });
        }

        updateShader() {
            const w = this.width * this.canvasDPI;
            const h = this.height * this.canvasDPI;
            const data = new Uint8ClampedArray(w * h * 4);

            let maxScale = 0;
            const rawValues = [];

            // 获取鼠标位置(归一化到0-1)
            const mouseProxy = {
                x: this.mouse.x,
                y: this.mouse.y
            };

            for (let i = 0; i < data.length; i += 4) {
                const px = (i / 4) % w;
                const py = Math.floor(i / 4 / w);
                const uvX = px / w;
                const uvY = py / h;

                // 计算距离边缘的SDF
                const ix = uvX - 0.5;
                const iy = uvY - 0.5;
                const distanceToEdge = roundedRectSDF(ix, iy, 0.3, 0.2, 0.6);
                const displacement = smoothStep(0.8, 0, distanceToEdge - 0.15);
                const scaled = smoothStep(0, 1, displacement);

                const posX = ix * scaled + 0.5;
                const posY = iy * scaled + 0.5;

                const dx = posX * w - px;
                const dy = posY * h - py;

                maxScale = Math.max(maxScale, Math.abs(dx), Math.abs(dy));
                rawValues.push(dx, dy);
            }

            maxScale *= 0.5;

            let index = 0;
            for (let i = 0; i < data.length; i += 4) {
                const r = rawValues[index++] / maxScale + 0.5;
                const g = rawValues[index++] / maxScale + 0.5;
                data[i] = Math.max(0, Math.min(255, r * 255));
                data[i + 1] = Math.max(0, Math.min(255, g * 255));
                data[i + 2] = 0;
                data[i + 3] = 255;
            }

            this.context.putImageData(new ImageData(data, w, h), 0, 0);
            this.feImage.setAttributeNS('http://www.w3.org/1999/xlink', 'href', this.canvas.toDataURL());
            this.feDisplacementMap.setAttribute('scale', (maxScale / this.canvasDPI).toString());
        }

        startAnimation() {
            const animate = () => {
                if (this.mouseUsed) {
                    this.updateShader();
                    this.mouseUsed = false;
                }
                this.animationFrame = requestAnimationFrame(animate);
            };
            animate();
        }

        destroy() {
            if (this.animationFrame) {
                cancelAnimationFrame(this.animationFrame);
            }
            this.svg.remove();
            this.canvas.remove();
        }

        getFilterId() {
            return `${this.id}_filter`;
        }
    }

    // 初始化着色器
    const shader = new LiquidGlassShader();
    window.liquidGlassEffect = shader;

    // 将SVG滤镜应用到菜单后面的内容
    const applyFilter = () => {
        const html = document.documentElement;
        if (html.classList.contains('citizen-feature-performance-mode-clientpref-3')) {
            // 应用到菜单后面的背景内容
            const menuCards = document.querySelectorAll(
                '.citizen-menu__card, ' +
                '#citizen-preferences__card, ' +
                '.citizen-command-palette, ' +
                '.mwe-popups .mwe-popups-container'
            );

            menuCards.forEach(card => {
                card.style.filter = `url(#${shader.getFilterId()})`;
            });
        }
    };

    // 延迟应用以确保SVG已加载
    setTimeout(applyFilter, 100);

    // 监听DOM变化,动态应用滤镜
    const observer = new MutationObserver(() => {
        if (document.documentElement.classList.contains('citizen-feature-performance-mode-clientpref-3')) {
            applyFilter();
        }
    });

    observer.observe(document.body, {
        childList: true,
        subtree: true
    });

    console.log('液态玻璃效果已启用');
}

function destroyLiquidGlassEffect() {
    if (window.liquidGlassEffect) {
        window.liquidGlassEffect.destroy();
        window.liquidGlassEffect = null;

        // 移除滤镜
        const menuCards = document.querySelectorAll(
            '.citizen-menu__card, ' +
            '#citizen-preferences__card, ' +
            '.citizen-command-palette, ' +
            '.mwe-popups .mwe-popups-container'
        );

        menuCards.forEach(card => {
            card.style.filter = '';
        });

        console.log('液态玻璃效果已关闭');
    }
}