Greasy Fork

Greasy Fork is available in English.

全屏按钮(适用于移动设备)

一个功能强大的全屏按钮,通过油猴菜单进行配置。支持拖动、自动淡化、可选自动贴边、位置重置。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         全屏按钮(适用于移动设备)
// @name:zh-CN   全屏按钮(适用于移动设备)
// @name:en      Full screen button (for mobile devices)
// @namespace    http://tampermonkey.net/
// @version      3.3
// @description  一个功能强大的全屏按钮,通过油猴菜单进行配置。支持拖动、自动淡化、可选自动贴边、位置重置。
// @description:zh-CN  一个功能强大的全屏按钮,通过油猴菜单进行配置。支持拖动、自动淡化、可选自动贴边、位置重置。
// @description:en     A powerful fullscreen button, configurable via the Greasemonkey menu. Supports dragging, auto-fading, optional auto-sticking, and position reset.
// @author       凡留钰 + ChatGPT + Gemini
// @match        *://*/*
// @noframes
// @icon         https://greasyfork.s3.us-east-2.amazonaws.com/4eb17e88irkc3910fvbpp4f0h270
// @grant        GM_registerMenuCommand
// @grant        GM_unregisterMenuCommand
// @grant        GM_getValue
// @grant        GM_setValue
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';

    // [优化] 增加 document.body 存在性检查,提高脚本健壮性
    if (!document.body) {
        console.warn('[Fullscreen Button] Document body not found, script will not run.');
        return;
    }

    // --- 1. 配置中心 ---
    const CONFIG = {
        initialLeftPercent: 90, // 按钮默认位置_横向
        initialTopPercent: 90, // 按钮默认位置_纵向
        buttonSize: '6vw', // 按钮占页面百分比大小
        minSize: '24px', // 按钮像素大小_最小
        maxSize: '48px', // 按钮像素大小_最大
        fadeOpacity: 0.4, // 按钮透明度_淡出
        hoverOpacity: 0.8, // 按钮透明度_鼠标悬停
        fadeDelay: 3000, // 多久后按钮淡化,单位:ms
        snapTransition: 'left 0.3s ease-out, top 0.3s ease-out',
        opacityTransition: 'opacity 0.3s ease',
        storageKeySnapping: 'enableEdgeSnapping',
    };

    // --- 2. 状态管理器 ---
    const state = {
        dragging: false,
        moved: false,
        hideTimer: null,
        animationFrameId: null,
        leftPercent: CONFIG.initialLeftPercent,
        topPercent: CONFIG.initialTopPercent,
        menuCommandIds: [],
    };

    // --- 3. 创建并初始化按钮 ---
    const btn = document.createElement('button');
    // [优化] 对CSS属性进行逻辑分组,提高可读性
    Object.assign(btn.style, {
        // 定位与层级
        position: 'fixed',
        zIndex: '2147483647',
        // 尺寸
        width: CONFIG.buttonSize,
        height: CONFIG.buttonSize,
        minWidth: CONFIG.minSize,
        minHeight: CONFIG.minSize,
        maxWidth: CONFIG.maxSize,
        maxHeight: CONFIG.maxSize,
        // 外观
        backgroundImage: 'url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAACXBIWXMAAAsTAAALEwEAmpwYAAAT50lEQVR4nLVbe3BdxXn/7e45594r3SvJsjskgCFAKDaWbGxjZIP8SGrPkOmETmna0Gkyk4QhxJJsE1qadpqGmRoYaGqelmQKBDqTAh3oTLFiB9tjk6QF4hRsh2aYEp6q/KAjF3St+z7n7PaPPfs698oPbNYj6dxz9+x+32+/x+/bPSb4FNvmu+/Jzu7u6qWELhUQCykhVxBKPwugSwiRI4R4QggBICSElIQQHwkhDkOI/wZwIIzi1zYMDb39acpIzvWA3/vrvwnmXnj+Oo/RrxBCv+j53kXZTAYAEMcxOBcQgkPqLQAQ+Y8SEEJAKQWlDEJwVKrVSAjxG875jiiK/nXD0IaD51recwbAlvsf+ExbLnsLpfSb2Wz2EsYowjBEHMeJss0TK/UF5PeEEMiu8jOlFL7vgzGGarWKOI5fieJ4dOe+nz2z4/nn4nMh91kDcN/f/3B2RyH/V5TR7xTy+Xyj0UAUhkirrBROtLYmNwCcrFFCEGQyIISgVCq/HcfxfYODg0+crfxnBcDIyMh6xthdhUK+u16rIYrdRdFKn1QAI4INBCHyvuAcIMQZKwgCMMYwXSr9uhGGf75pw8a9n1SHTwTAgw89dGk2k3myo6OwqtFoIAxDva72ShMCgBAIIYypJz3VxMYgiH7Y6UcIBBfmWhh3yWQyiOIY1Ur1ifHDRzbdd8/d5U8dgJGR4a96nv9ke3tbrlqpppQwSqnfxPJ2Z4Wt3wLCcgcXHIhEectvFKAKlFyuDSemT7zfCMObNg5t+NWZ6EPPpPPwyMhd+Xzh2cD3c5VKRdhaC6WedY9oBc0d969RTsVJG0ji/pKrLwBCqJxPyJ9KpSLacrlLspnM/q3Dw984E51O2wJGt217srOj4xvVakWapNXUJ7nWwlLcrJTqJJC4RquH4fZLdyKEAonShKoYoa8FYx5hjKJUKv9gYGBg8+noxU6n07ZHtz3b1dn5tUql3GzvlpAEMpeDpJV3PN2AIKyAkAKCqB+imYJlQESDaN0ngnNwwVEo5L+wdt26YMdPduw7awC2bdv2RFdX59fL5ZJyRyNdcq2EdDSEFcnVNTH9VTN9hL62ERBCGFCTaxUM5X3jHoRIoKIwQnt7+8q169bGO3bs+MXJ9DupC4yOjm7u7Oz8fqVSNtEXKSGlZC1GapEEW1mPkzKs29YtNbxS3IBFIAR3gYMEg1KKIAhw4sT0NwcHB5+aSccZARgZGflKoZB/Lmw0EHPuqn46yraaSmmS9nvAAicVDFo1K5DIjODGHhWCGGMkiiJUq9W+jRs3tcwOLbPAQw8/dJHneT/inINzS2rb9B357A92pG+Bb6KoxXh1d+IMfpJx0kZoBrBTLYmjSGQzWQRB5pnv33lnWytdvVY3s9nso/n29kK5UhYERFqYIBp5tVBhFCaucbJlNY0xBkoZACuD2itPABFzRFGUplWJl7SizQS+7zlxxyJLpF6vi46OjksvEOJBAN9O69q0RKOjI3/W0dHx42q16qauVO84jrFo0SKcf/4FUuBTNM/zcOjQIRw9egSe5wMW29NjRjHmXjQXPT291pgzph0wxnD8+HG89vprYNQYs4JNpUhZbVKUyqXVGzdsdIKiYwF3bt6cY8y7J27B6dNNcIHPfe4SnHfeeadUXrV3330HcczhecmoxF3NmMfo7OrC3LlzT3vMWbNm4cCBAxBcBj4BaAuglEIIAS64yGUyJAiCHwLos593YsBnfmfOUKGQv6hRb8DxKZ2SVE+ZfppW/hRxkBBqgdmiXjx1UdjUJYoiU3MkPWwukkxMavUa8u35a4ZHhm+yn9cWsPnue9o8xjY1Gg1pdHba04WJLUYLaQlw/PhxvPXWWwiCwDEdxjwcP34cTC6/TnOmSBJgnocPP/xfHDh4EDyxQrWicRRhwYIF6OjoOAkoNukm4JobyDE45/AY+x6AZ5sAmDOn+0/b29svKJcrWpmmXJwOTC1ad3c3qtUq9u/fj2w263zn+x4Yk9xLj2uB53kM/zc5iQ+PHdN9uBBo1Ovo61s+s/LCpEGpOAcBAVVWkQDRaDTQ1tZ21fDI8A2DA4PbAcsFGKG3SN9PTEjAsvkk8lt625s8nHPU63UA0u/Wrl2LZcuuBiCQzWSQSX4oZZrL68pJXycEhrGkfwDfD0BA0N+/Etdee62er1qrgXOucDMSKiAITbkAIDhPSBOF53kDaiwKACOjo1cxRvsajYastAQHiAkiCsHUmplGCPbu24vjx4/rWytXrkJPTy+qtZr1jHB4fFPJI0wAEwKo12tYunQpli1bpvtMTk5i395W+x+GjZqqMWGO3FDoer0OAqx76OGHf1cDwCj9w1yuLdmwtGhmYg0C0hSTaCPxtvybEoLSdAljY2MoFov6/qpVq9DT04NarabXSf/V1YzN843QUvmr0ddngvbU1BTGfjKGUrkESltwOKFcleiNFJtCQ0hLaM/nqe95f6ABAMGXoii0ViTxcx1Elewy+LXa5PQDH+VyGdu3b8fUlAFh9erV6OnptUBQsVrbvZ5TWUC9XseSJUuxfPlyo3yxiLGxMVTKFfieb+us/xqGLNzsleobRzEopV8GAPrI1kcuJoT0hmEo0xSRBYbSXG46JNFaI9wMPgTg+xKEsbHtmJqackBYsGABavVaE7hCG4K8qNfrWLx4MVasWKGfLxaLGBvbjnK5DN83yisAZZIi2nJVk+6cWBelumaIohCU0iUPPPjg+ZQxtjCXzWZVR1VJybG5k0as6tOZyG4GhDEHhDVr1mDhwkUIw9DpTzTXF4jjCEuXLnUCXrFYxPax7SiXyjK1NjVDzRUgqiwWXMpPCUk2VyUonHNkM5n2wPeuopTQxTI1CSOKUAMnd5z4Z6owezUIpVCG6Ps+Si1AQGJZrgdp4wVAHICU8qXpEnzfNxyfGvCDwLdkE5bVKh4DZ99ATe7JlLyMffmGG9b7nt8jXYDoAQiRpzXSHYg2URUQ1MdisYhjx47h6NGjCSuT3zPGUKvVMD4+jgsvvBCvv/463vivN5BJCJJbW8gLSimOHj2Cer2BfD6PnT/didJ0CUEQaAtUfeM4xuTkJCYmJjA5OZmIphYnUZzKQK4Co80XPN9DFMeT5LHHHnslkwlWNBoN7ZqUyghq79KY3SCDfhiGOh/7vt+0MUEIQRzHoFSanXIt4hZ5Fg5GOfUMY0zX/OpBzoW2FEKIdg21nW5kFs7CJ2UthBDIZLKo1+u/9AjBHDuqE2JysZO2rKitylI3IAkNkk1NpQLcTVvC7mMjIL9UfRmjqZWXc1BCkMkEehzdR5gxDJ7Gf9V5pOI3AmI2FUK0q0ivoqkq8Sk1Z3XGBUwKM+gmTyacQeVhA6q1iWn5o9omtGMLnOetcYTadHWKHB2YVXaiisWCJEAabqPHFTLAQyBPuRAZl/MLUCon59yQIi2xRd61nehNiNTGJtxrd7MCZrOYKJAtVJ1GUsCnSZCxVAEkewCSouuaIAnAhFBwEySzFGpzxvJxl+goappEaxsQfcQNuOVymijZqyu/d/iErgtaPOpkCfV982fl32pslQMMQZKgyVhkog4llNSNDCbg6UETgWiKZNipC1CVGPSyultXFutTKVb7J3F1aQke0W7WRD+IkcG4jNCxSh+kWN1VmgdBRAkhJUqJCX7CUtwqgrgwPMEIZn6n4yZpkjSZnRhhHSCbAm/6UdsyZ3ATWNGeEOkCid/r6jE5XUoIUsWDwEeKMjqIqWuNLPS10iUMIycNujZthGw0QjDG9F5AOjYY15J/4pgjikIEQcYZz05vdhpU2UiParszzHU6IAohpjxAjDPG+uwon2ZVjo8lzhtzjosumot8vgDOOSYmJlCtVmXmgL0LE6Ov7xocOXIEE4cPI6OVsqS1VrnRaGD27Nm49JJLcODQQUDASaFCcGQyWVx++eUglKBWrWFiYsIBkVvlr+A8CYzUMESCJD3jqMc5f1OlL2WGlCgGJRzU9fEWJFlZsmSp3hR94YV/Q6lUAqVeIoTc3u7v70dvby96enqxa9eLOHbswyS/p/O0HLO7uxvXX389CoUC2vN5vPTSS/A8T4MQxxz5fB6rV68GAEyXSvifZ/UOl6G+gH65AkLoukCyW6F0fItGcXwgikLtF8bXjOIKGIcwAc6mqGFacsIoinDdddeht7cXgHyr4/OXfR5xnN5CF/oniiLMnz8fhUIBADBv3jysWbMGcRS5rM6ymrARGoGS79LhWgf3JEMAcr9QcH6AAjhUr9erjFGjaPJbEZhW5GamYCWEQCNs4Nprr8XChQv1/ffeew///vJ/wPN9GH92x/N9H/t/tR/vv/++vjt//nysWr0aURSCc5XcWsxtWRRXJbIye871fGr1a9Uqoig+SAcHBieEEL/2PF+vNnUqJ9sqmpJ0s/KNBlYsX4FFixbp+x988AH27Nkj/TlNYlRahASZxxx79uzB+Pi47nLllVdi5cpViPRJlAWb4UA6kVDLBeTCSfKjdGOeh5jz3778ystvUgDgXLzoeZ4saXXkJA4psmNBq8YT5ZcvX47Fixfr++Pj49i9ezcIIVYWsMZJ0q8alTEJ0O7du5LgJtuCBQvQ379SFmBqwwZmSdyXrezID0Mfkrl83wMg9j7z9DMxBYA4jrbXalVd5AiRRM4mn0u7gWlho4G+vj4sWbLEUX7X7l2JYsyJJ8RaOpVylTpJhMauXS/i8OEjeryenh709/c3baqohWpyKp2VXUov31/kzwPJnuDg4NDBOI5/GQRBYorJkZJKJ4qJtaDIEsAYV1/tbmBOTExg9+5dILbyxEhlNi1aW5TnMXAusGvXT3EsOScAILfLlq/Qr+TZShvam2zkclNG80T2IAhQq9Xfun9k2880AAAQRdHjskx00VOCqkFbsTHGGC677DL9eXx8HDt37kAUSSGjOEIcx026qoUXOidKoOM4RhTFCS8IMTY2hiNHjCVcfPHF8NjML7dIciY0F9FKCQHP98AFf+qd37zBHQAmDh99ulwuTaitJ0IIKLVP7O2sMPPKffTxx9i/fz86Ojoxe85sdHZ2YVZXF2Z1z9LbWvYbHmmwgyCDWbNmoaurC52dnZg9Zw4KhQJeffVVFIsnWs45Y2jW6U9aBfMYSqXyVLVafVx10Udj99x9V3V0dOT+IBM8ECV5Vwi5JwC9SoAMNy7VtVtHoYAbb7xRvwegGqUU+/btM+eGiWimipO+OW/ePPT392uKnWgCzlu/GmwTOM0B0rQ3MbNsJktq9eLI7d+9XZ/gODnpP187sLU0Pf12JhNA838raKXNv9XhhOd5CXNTb35T3U+VocqCtPkLax7dl1o/RI+bboxSpwAzLmxFBkLg+T6ZLk1PlqZLP3TktT/86InHo6VLFv9FLkdekAorCqwTI1SSEUKgVC47hcnJmh8EOiYAqXhKjKvFkRyr0WicckzP81Aul2WNn7iUvVBUBz+BIPBRqVb+9o477piyx2hpx9u2jT7b0dH51Uq5rGtjQ3WVAgLMYwj0dnVrlzA6ykJnxpcvEsE9j8H3A5d2O6nXyEIgX9MJw9ABEcL4PYQQuVyOlMqln99663fWNIHYSthisTjIKFuTyWbPazTqcjqirEGKTQgQRxEq6tX4FpHIIZSQLmAKLwsBYcaOoggNm9+niiaz82vqD+1aycD6MIdz+IFPypVytVqt3dxK1xmXbXjr1i+2tbftBWQFJidQ8cDta6dO1wctVMSpiLT7vBauFe0XzeCqBzRngYwPnu9h+sT0TYNDQ//Sas4ZX5YeHBraV6lUbwuCjFbG4jHurK2aFTBb0eeWZZVhxyb9CquHjaczpCqSkueTwi2by6FcLt81k/LAKV6V3blz5/5169Z2FQody6PIBDrl8m7JCW2apoKUX9iFn/xePezuMRgFzPPG1NGEPrHmcMcgaG9vR7FYfGr9+oFNJ9Px5JEraaOjI492dc36dqVc1nnXVjB9AgM0yaq/U/UGVFC1x7KClz1m63Hca/WXgKCtvQ0fT009P7B+4I9PpdtpAQAAIyMjj3R2dgzV6w3EcayDrK20Pah2AE1KUn3seAHbYqw7In3fzKc4vhWXBCGE5HJtKBaL/7x+/fqvnY5ep/0fJgYGBjZMTRX/khKCIAi0jvZqaAEtJWWf9NG6idhpxVR/OUD6vm1FSKo8ecEYI0EQYGrq43tPV3lritNvw1u3fskP/B+3t+e7q9WKETBxQ31MBhi7Tk3ZdDpk7zYnv+zjNmVBZq/SNb9cWw7lcrkWNsKbB4eGnj4Tfc4YAADYsuUfPptvzz+WL+R/P4piNMKG87r/yeKAamnzt6+b018aSLnsnueRwPdxYnr6F5Va9ebbb/vuO2eqyycCQLXh4eGv+753b6HQcX69XoMsok6uuK2hTazSgVRta3Grj7IEz/OQyWRQKk1/FMXxDwbWDwx/Uh1O67/MzNR27tz5Rt81y//RYywUQizMt+dzhBD9/wtaZntighhxrokGJ32yrJT3fR/ZbA61WrVSq9UemT5R+pNNmzb9/Gx0OCsLsNuWLVu629ravsUY+1Ymk5nv+z6iKEQUReCxtT8P288tQRxLMM7geR4C30cYR6jXau9yLv6pVq89cdum246eC7nPGQCq5XId5P77711DKfsjSsnvAbgil2sj6o0P9X+Jnfd4Eu5OmSx/CaUAF6hUqxCCv8e5eIlz/tyRD4/tvfvvNp/63fwzaOccALstuqKX3rLh1is8jy0lhC4hBPMAciEh6AZIGwi8pBAKAVQA8RFAjgohfss5PxTH0YHDE0fevPe+e09db3/C9v87Xq2hRPwUCQAAAABJRU5ErkJggg==")',
        backgroundSize: 'cover',
        borderRadius: '50%',
        boxShadow: '0 2px 8px rgba(0,0,0,0.3)',
        border: 'none',
        opacity: CONFIG.hoverOpacity,
        // 行为与过渡
        cursor: 'grab',
        userSelect: 'none',
        transition: CONFIG.opacityTransition,
    });
    document.body.appendChild(btn);

    // --- 4. 核心功能函数 ---
    function updatePosition() { const w = btn.offsetWidth, h = btn.offsetHeight; btn.style.left = `${(state.leftPercent / 100) * (window.innerWidth - w)}px`; btn.style.top = `${(state.topPercent / 100) * (window.innerHeight - h)}px`; }
    function clampAndUpdatePosition() { state.leftPercent = Math.max(0, Math.min(100, state.leftPercent)); state.topPercent = Math.max(0, Math.min(100, state.topPercent)); updatePosition(); }
    function updateDragPosition(e) { const touch = e.touches ? e.touches[0] : e; const w = btn.offsetWidth, h = btn.offsetHeight; state.leftPercent = (w > 0) ? ((touch.clientX - state.offsetX) / (window.innerWidth - w)) * 100 : 0; state.topPercent = (h > 0) ? ((touch.clientY - state.offsetY) / (window.innerHeight - h)) * 100 : 0; clampAndUpdatePosition(); state.animationFrameId = null; }
    function toggleFullscreen() { if (!document.fullscreenElement) document.documentElement.requestFullscreen().catch(err => console.error(`[Fullscreen Button] Error: ${err.message}`)); else document.exitFullscreen(); }

    // --- 5. 事件处理逻辑 ---
    function dragStart(e) { e.preventDefault(); const touch = e.touches ? e.touches[0] : e; state.dragging = true; state.moved = false; btn.style.transition = CONFIG.opacityTransition; const r = btn.getBoundingClientRect(); state.offsetX = touch.clientX - r.left; state.offsetY = touch.clientY - r.top; state.startX = touch.clientX; state.startY = touch.clientY; btn.style.cursor = 'grabbing'; clearHideTimer(); document.addEventListener('mousemove', dragMove, { passive: false }); document.addEventListener('mouseup', dragEnd, { passive: false }); document.addEventListener('touchmove', dragMove, { passive: false }); document.addEventListener('touchend', dragEnd, { passive: false }); }
    function dragMove(e) { if (!state.dragging) return; e.preventDefault(); const touch = e.touches ? e.touches[0] : e; if (!state.moved && (Math.abs(touch.clientX - state.startX) > 5 || Math.abs(touch.clientY - state.startY) > 5)) state.moved = true; if (!state.animationFrameId) state.animationFrameId = requestAnimationFrame(() => updateDragPosition(e)); }
    function dragEnd() { if (!state.dragging) return; state.dragging = false; document.removeEventListener('mousemove', dragMove); document.removeEventListener('mouseup', dragEnd); document.removeEventListener('touchmove', dragMove); document.removeEventListener('touchend', dragEnd); if (state.animationFrameId) { cancelAnimationFrame(state.animationFrameId); state.animationFrameId = null; } btn.style.cursor = 'grab'; if (state.moved) { if (GM_getValue(CONFIG.storageKeySnapping, true)) applyEdgeSnapping(); } else { toggleFullscreen(); } startHideTimer(); }

    // --- 6. 自动淡化逻辑 ---
    function startHideTimer() { clearTimeout(state.hideTimer); state.hideTimer = setTimeout(() => { btn.style.opacity = CONFIG.fadeOpacity; }, CONFIG.fadeDelay); }
    function clearHideTimer() { clearTimeout(state.hideTimer); btn.style.opacity = CONFIG.hoverOpacity; }

    // --- 7. 与油猴菜单交互的核心函数 ---

    function applyEdgeSnapping() { state.leftPercent = state.leftPercent > 50 ? 100 : 0; btn.style.transition = `${CONFIG.opacityTransition}, ${CONFIG.snapTransition}`; clampAndUpdatePosition(); }
    function resetButtonPosition() { state.leftPercent = CONFIG.initialLeftPercent; state.topPercent = CONFIG.initialTopPercent; if (GM_getValue(CONFIG.storageKeySnapping, true)) { applyEdgeSnapping(); } else { btn.style.transition = CONFIG.opacityTransition; clampAndUpdatePosition(); } startHideTimer(); /* [优化] 重置后也启动淡出计时器,统一行为 */ }

    /**
     * [优化] 提取出的切换自动贴边功能的具体实现函数
     */
    function toggleSnapping() {
        const isEnabled = GM_getValue(CONFIG.storageKeySnapping, true);
        const newValue = !isEnabled;
        GM_setValue(CONFIG.storageKeySnapping, newValue);

        if (newValue) {
            applyEdgeSnapping();
        } else {
            btn.style.transition = CONFIG.opacityTransition;
        }
        // 关键:再次调用主函数来刷新整个菜单
        updateAllMenuCommands();
    }

    /**
     * [结构优化] 统一更新所有油猴菜单命令
     */
    function updateAllMenuCommands() {
        state.menuCommandIds.forEach(id => GM_unregisterMenuCommand(id));
        state.menuCommandIds = [];

        // --- 菜单项1:重置按钮位置 ---
        const resetId = GM_registerMenuCommand('重置按钮位置', resetButtonPosition);
        state.menuCommandIds.push(resetId);

        // --- 菜单项2:切换自动贴边 ---
        const isSnappingEnabled = GM_getValue(CONFIG.storageKeySnapping, true);
        const snappingMenuText = (isSnappingEnabled ? '⬜️ 关闭' : '✅ 开启') + ' 自动贴边';
        // [优化] 此处直接调用已命名的回调函数,代码更清晰
        const snapId = GM_registerMenuCommand(snappingMenuText, toggleSnapping);
        state.menuCommandIds.push(snapId);
    }

    // --- 8. 初始化与事件绑定 ---
    btn.addEventListener('mousedown', dragStart);
    btn.addEventListener('touchstart', dragStart, { passive: false });
    ['mouseenter', 'touchstart'].forEach(evt => btn.addEventListener(evt, clearHideTimer));
    ['mouseleave', 'touchend'].forEach(evt => btn.addEventListener(evt, startHideTimer));
    btn.addEventListener('click', e => { e.preventDefault(); e.stopPropagation(); }, true);
    window.addEventListener('resize', clampAndUpdatePosition);
    window.addEventListener('orientationchange', clampAndUpdatePosition);

    updateAllMenuCommands();

    // --- 9. 首次运行 ---
    if (GM_getValue(CONFIG.storageKeySnapping, true)) {
        applyEdgeSnapping();
    } else {
        clampAndUpdatePosition();
    }
    startHideTimer();

})();