Greasy Fork 支持简体中文。

Small Window Preview - zscc

Drag a link to open it in a popup window with a preview before opening, using Edge's prerendering technology. Also, add an acrylic effect behind the window when it's open.

// ==UserScript==
// @name              Small Window Preview - zscc
// @name:zh-CN        小窗预览-船仓UI美化版
// @description:zh-CN 拖拽链接时在弹出窗口中打开链接,并在打开前提供预览,使用 Edge 的预读技术。同时在小窗口打开时在背后添加亚克力效果,可设置为长按触发.自动记录每个站点的小窗口大小.
// @description       Drag a link to open it in a popup window with a preview before opening, using Edge's prerendering technology. Also, add an acrylic effect behind the window when it's open.
// @version           2.5.1.8
// @author            hiisme & zscc.in
// @match             *://*/*
// @require           https://greasyfork.org/scripts/379483-sweetalert2/code/SweetAlert2.js
// @require           https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/js/all.min.js
// @grant             GM_registerMenuCommand
// @grant             GM_unregisterMenuCommand
// @grant             GM_getValue
// @grant             GM_setValue
// @grant             GM_info
// @require           https://unpkg.com/[email protected]/dist/sweetalert2.min.js
// @namespace         https://github.com/ChinaGodMan/UserScripts
// @supportURL        https://github.com/ChinaGodMan/UserScripts/issues
// @homepageURL       https://github.com/ChinaGodMan/UserScripts
// @icon              data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAACXBIWXMAAAsTAAALEwEAmpwYAAAEYklEQVR4nO3VW0yTZxzH8QI9ACbb4g3J3OR2Zm64uaVXu/BChkMUhAotUlrKUQUqqLhDRnDJLnZQoLvYxRLneQSB0tLSw1ugnE9KC7y05QwFVDxBdt/fUhhb2dS97+sLvEv4J89NE5rP98mfpzze9mzP9rxwxNe84shLrjnRhQFfUHE/gov6EFzUg+Az3QhRdyGksAMhBe3g57eBn28H/3QL+KeaIThpgyCPgCDXCmGuGcIcE4TZjRBlGSDKbIBIpYdIVY/QDC1ClXUIVdQgLP0OwuTVCJNXITztN7ymrPJFFusXPv6uNYbHZMS3vOLQr5y+oHMDCDp7F5sdEH7iNsJTb+INxW3f/m9bxbQDdl92z/HOO7DVATtkNxBZpPPSDhB+OejjSsDr8ls+2gG8Eie4ErBDeh20/V+MLONzchkXyCWUDC+hZOgZzg89xbnBpzg7+ATFzicocjxeOWcGHkE98AiF9xZRcG8R+XcfIr//IU73P8Cpvgc42Xcfeb33kduzsHJyuueR3T2PrK45ZHbOQdXpharDi4yOWSjbZ6Fom0F62zTSW6chb51Cmn2KfgCX8CdaJukHrMc/21J8KpMArtx8asskZM0TDAI4hJcxCeASXto0Tj+AS/gUG4MALuGTbWP0A7iEP04wCOASXkKMMgvgCl5iZRDAJXySxUM/gEv4RCYBXMIfM7vpB3AJn8Ak4J/4vJ555HTOIrt9ZuVktU0js20aqtYpZNinoLRPQtkyCUXLBNLtk6zi400uBgGBN9+7ALlpCOKfDfjoJx32a1bPh5p6fFD599n35zlwlUCqhWQNH88kIHBtcrq8OHTTDoFaQ/kc+JVAWvM4K/ijjSP0A9btfPc8MppGEVVeSzlgX6UWqbYxVvBHjAwC/vUP2zUHhc2DqApqEVEVWsiIUVbwcUaSfsBzX5tOL+RWN/ZeqqEQUAcpMcoK/rCBQcDznkpV+yxkRgd2f3ODUkCK1cMKPtYwzCyAKd5/3i9fDWADH9swRD/gVfBrAckWNyv4z/QMAl4FvxpQi+NmNyv4Q/pB+gFreKnBgbcvXn8pdlfZNez5vnrdZ+9droXE7GIFH6NjEKBqn6F087vKriJB2w+JwYl3f7yzLiDJ5GIF/6nOST9AanD8TuXmj2n7ILWNQtY0DkmDA3t+qP7rdyDRNMIKPrreuURPn18hivj6yiJl/NpTSYwhUT+AT36xIK62F0kWNxt4HNQ6qqjjS0uF/EKNjsrarMMHvDZJFhckFg8r+Git43FMjfOtTcOztfMHtc5l/82ziheoK0280iuhgX+WQkxEJNvGSNr4hmHPEa3rTeqrsQH4wAiJ1UP+L/GBEYlmN7m5eB6Px1dr6v7jV9Xof5WofFccMRGRYHaRm4b3D1+tmWEDvzZxxGBEvMlFbgreP8KiyncEhZoFJmvzsoijjSPkhuNfHMEcvzYS0/DOw0byYqyBLIs2De/kbfQICsr3+teJX1hZS3dttnL+AHpvNumR+ceNAAAAAElFTkSuQmCC
// @iconbak           https://github.com/ChinaGodMan/UserScripts/raw/main/docs/icon/Scripts%20Icons/icons8-POPUPWINDOW-48.png
// @license           MIT
// ==/UserScript==
(function () {
    // 确保 SweetAlert2 样式正确加载
    const style = document.createElement('style');
    style.textContent = `
    .swal2-popup {
        font-size: 1rem;
    }
    /* 如果需要的话,这里可以添加更多自定义样式 */
`;
    document.head.appendChild(style);

    const userLang = (navigator.languages && navigator.languages[0]) || navigator.language || 'en'
    const translations = {
        'en': {
            actionMode: 'Select Trigger Mode',
            actionMode1: 'Long Press',
            actionMode2: 'Drag',
            actionMode0: 'Both',
            longPressEffective: 'Long press effective time',
            setLongPressEffective: 'Enter the long press effective time (milliseconds):',
            longPressDuration: 'Long Press Duration',
            blurEnabled: 'Toggle Blur Effect',
            blurIntensity: 'Set Blur Intensity',
            closeOnMouseClick: 'Toggle Close on Mouse Click',
            closeOnScroll: 'Toggle Close on Scroll',
            windowWidth: 'Set Window Width',
            windowHeight: 'Set Window Height',
            setLongPressDuration: 'Enter Long Press Duration (milliseconds):',
            setBlurIntensityprompt: 'Enter Blur Intensity (0-10):',
            toggleActionMode: 'Select Trigger Mode:\n1: Long Press\n2: Drag\n0: Both',
            setWindowSizeprompt: 'Enter Window Size (pixels):',
            showCountdown: 'Show countdown progress bar',
            saveWindowConfig: 'Record window position',
            showCountdowndrag: 'Show drag timeout progress bar',
            dragTimeOut: 'Drag timeout duration',
            settings: '⚙️ Settings',
            saveBtn: 'Save',
            cancelBtn: 'Cancel'
        },
        'zh-CN,zh,zh-SG': {
            actionMode: '选择触发方式',
            actionMode1: '长按',
            actionMode2: '拖拽',
            actionMode0: '两者都用',
            longPressEffective: '长按生效时间',
            setLongPressEffective: '输入长按生效时间(毫秒):',
            longPressDuration: '长按触发时间',
            blurEnabled: '模糊效果',
            blurIntensity: '设置模糊强度',
            closeOnMouseClick: '点击关闭小窗',
            closeOnScroll: '滚动关闭小窗',
            windowWidth: '设置小窗宽度',
            windowHeight: '设置小窗高度',
            setLongPressDuration: '输入长按触发时间(毫秒):',
            setBlurIntensityprompt: '输入模糊强度(0-10):',
            toggleActionMode: '选择触发方式:\n1: 长按\n2: 拖拽\n0: 两者都用',
            setWindowSizeprompt: '輸入默认小窗口配置(像素):',
            showCountdown: '显示长按倒计时进度条',
            saveWindowConfig: '记录窗口位置',
            showCountdowndrag: '显示拖拽超时进度条',
            dragTimeOut: '拖拽超时时间',
            settings: '⚙️ 配置界面',
            saveBtn: '保存',
            cancelBtn: '取消'
        },
        'zh-TW,zh-HK,zh-MO': {
            actionMode: '選擇觸發方式',
            actionMode1: '長按',
            actionMode2: '拖曳',
            actionMode0: '兩者都用',
            longPressEffective: '长按生效时间',
            setLongPressEffective: '输入长按生效时间(毫秒):',
            longPressDuration: '長按觸發時間',
            blurEnabled: '切換模糊效果',
            blurIntensity: '設定模糊強度',
            closeOnMouseClick: '切換點擊關閉小窗',
            closeOnScroll: '切換滾動關閉小窗',
            windowWidth: '設定小窗寬度',
            windowHeight: '設定小窗高度',
            setLongPressDuration: '輸入長按觸發時間(毫秒):',
            setBlurIntensityprompt: '輸入模糊強度(0-10):',
            toggleActionMode: '選擇觸發方式:\n1: 長按\n2: 拖曳\n0: 兩者都用',
            setWindowSizeprompt: '輸入默认小窗口配置(像素):',
            showCountdown: '顯示倒數計時進度條',
            saveWindowConfig: '記錄窗口位置',
            showCountdowndrag: '顯示拖曳逾時進度條',
            dragTimeOut: '拖曳逾時時間'
        },
        'ja': {
            actionMode: 'トリガーモードの選択',
            actionMode1: '長押し',
            actionMode2: 'ドラッグ',
            actionMode0: '両方',
            longPressDuration: '長押しの時間',
            blurEnabled: 'ぼかし効果の切り替え',
            blurIntensity: 'ぼかしの強度を設定',
            closeOnMouseClick: 'マウスクリックで閉じる切り替え',
            closeOnScroll: 'スクロールで閉じる切り替え',
            windowWidth: 'ウィンドウ幅の設定',
            windowHeight: 'ウィンドウ高さの設定',
            setLongPressDuration: '長押しの時間(ミリ秒)を入力:',
            setBlurIntensityprompt: 'ぼかしの強度(0-10)を入力:',
            toggleActionMode: 'トリガーモードの選択:\n1: 長押し\n2: ドラッグ\n0: 両方',
            setWindowSizeprompt: 'ウィンドウサイズ(ピクセル)を入力:',
            showCountdown: 'カウントダウン進行状況を表示',
            saveWindowConfig: 'ウィンドウの位置を記録',
            showCountdowndrag: 'ドラッグタイムアウトの進行状況バーを表示',
            dragTimeOut: 'ドラッグタイムアウト時間'
        },
        'vi': {
            actionMode: 'Chọn chế độ kích hoạt',
            actionMode1: 'Nhấn lâu',
            actionMode2: 'Kéo thả',
            actionMode0: 'Cả hai',
            longPressDuration: 'Thời gian nhấn lâu',
            blurEnabled: 'Bật hiệu ứng mờ',
            blurIntensity: 'Cài đặt độ mờ',
            closeOnMouseClick: 'Bật/tắt đóng cửa sổ bằng nhấp chuột',
            closeOnScroll: 'Bật/tắt đóng cửa sổ khi cuộn',
            windowWidth: 'Cài đặt chiều rộng cửa sổ',
            windowHeight: 'Cài đặt chiều cao cửa sổ',
            setLongPressDuration: 'Nhập thời gian nhấn lâu (mili giây):',
            setBlurIntensityprompt: 'Nhập độ mờ (0-10):',
            toggleActionMode: 'Chọn chế độ kích hoạt:\n1: Nhấn lâu\n2: Kéo thả\n0: Cả hai',
            setWindowSizeprompt: 'Nhập kích thước cửa sổ (pixel):',
            showCountdown: 'Hiển thị thanh tiến trình đếm ngược',
            saveWindowConfig: 'Ghi lại vị trí cửa sổ',
            showCountdowndrag: 'Hiển thị thanh tiến trình quá hạn khi kéo thả',
            dragTimeOut: 'Thời gian quá hạn khi kéo thả'
        }
    }
    const getTranslations = (lang) => {
        for (const key in translations) {
            if (key === lang || key.split(',').includes(lang)) {
                return translations[key]
            }
        }
        return translations['en']
    }
    const translate = new Proxy(
        function (key) {
            const lang = userLang
            const strings = getTranslations(lang)
            return strings[key] || translations['en'][key]
        },
        {
            get(target, prop) {
                const lang = userLang
                const strings = getTranslations(lang)
                return strings[prop] || translations['en'][prop]
            }
        }
    )
    'use strict'
    const state = {
        isDragging: false,
        linkToPreload: null,
        popupWindow: null,
        acrylicOverlay: null,
        progressBar: null,
        dragprogressBar: null,
        dragintervalId: null,
        startTime: null
    }
    function getWindowConfig() {
        const windowConfigs = GM_getValue('SitewindowConfigs', [
        ])
        GM_setValue('SitewindowConfigs', windowConfigs
        )
        const currentHostName = window.location.hostname
        // 顶级规则,查找当前域名是否在设置内.....
        for (const config of windowConfigs) {
            if (typeof config.hostName === 'string') {
                if (config.hostName === currentHostName) {
                    return {
                        width: config.width || 870,
                        height: config.height || 530,
                        top: config.top || (window.screen.height - (config.height || 530)) / 3,
                        left: config.left || (window.screen.width - (config.width || 870)) / 2
                    }
                }
            } else if (Array.isArray(config.hostName)) {
                if (config.hostName.includes(currentHostName)) {
                    return {
                        width: config.width || 870,
                        height: config.height || 530,
                        top: config.top || (window.screen.height - (config.height || 530)) / 3,
                        left: config.left || (window.screen.width - (config.width || 870)) / 2
                    }
                }
            }
        }
        // 二级规则,如果开启了自定义设置,使用自定义.
        const customWindowWidth = GM_getValue('custom_windowWidth', 0)
        const customWindowHeight = GM_getValue('custom_windowHeight', 0)
        const customScreenLeft = GM_getValue('custom_screenLeft', 0)
        const customScreenTop = GM_getValue('custom_screenTop', 0)
        if (GM_getValue('saveWindowConfig', false)) {
            if (customWindowWidth !== 0 && customWindowHeight !== 0 && customScreenLeft !== 0 && customScreenTop !== 0) {
                return {
                    width: customWindowWidth,
                    height: customWindowHeight,
                    top: customScreenTop,
                    left: customScreenLeft
                }
            }
        }
        //三级级规则 以上规则全部找不到,窗口使用默认设置.
        return {
            width: 870,
            height: 530,
            top: (window.screen.height - 530) / 3,
            left: (window.screen.width - 870) / 2
        }
    }
    function reWindowConfig() {
        const windowConfig = getWindowConfig()
        config.windowWidth = windowConfig.width,
            config.windowHeight = windowConfig.height,
            config.screenLeft = windowConfig.left,
            config.screenTop = windowConfig.top
    }
    let config = {
    }
    function updateConfig() {
        config = {
            windowWidth: 0,
            windowHeight: 0,
            screenLeft: 0,
            screenTop: 0,
            blurIntensity: GM_getValue('blurIntensity', 5),
            blurEnabled: GM_getValue('blurEnabled', true),
            closeOnMouseClick: GM_getValue('closeOnMouseClick', true),
            closeOnScroll: GM_getValue('closeOnScroll', true),
            longPressEffective: GM_getValue('longPressEffective', 200), // 长按生效时长 (毫秒)//STUB - 也就是长按打开小窗口时间=longPressEffective+longPressDuration
            longPressDuration: GM_getValue('longPressDuration', 500), // 长按持续时间(毫秒)
            dragTimeOut: GM_getValue('dragTimeOut', 2000), // 拖拽超时时间(毫秒)
            actionMode: GM_getValue('actionMode', 0), // 0: 两者都用, 1: 长按, 2: 拖拽
            showCountdown: GM_getValue('showCountdown', true), // 是否显示倒计时进度条
            showCountdowndrag: GM_getValue('showCountdowndrag', true), // 是否显示拖拽倒计时进度条
            saveWindowConfig: GM_getValue('saveWindowConfig', true)//记住窗口位置,没啥用
        }
    }
    updateConfig()
    reWindowConfig()
function openSettings() {
    Swal.fire({
        title: '⚙️ 小窗设置',
        width: 800, // 减小弹窗宽度
        padding: '1em',
        showCancelButton: true,
        confirmButtonText: translate('saveBtn'),
        cancelButtonText: translate('cancelBtn'),
        customClass: {
            popup: 'custom-popup',
            confirmButton: 'custom-confirm-btn',
            cancelButton: 'custom-cancel-btn',
            actions: 'custom-actions',
            container: 'custom-container'
        },
        html: `<style>
            /* 调整弹窗整体布局 */
            .custom-popup {
                margin: 0;
                display: flex;
                flex-direction: column;
                max-height: 85vh; /* 限制最大高度 */
            }

            .swal2-html-container {
                margin: 0;
                padding: 1em;
                overflow-y: auto; /* 内容过多时显示滚动条 */
                flex: 1;
            }

            /* 设置容器样式 */
            .settings-container {
                display: flex;
                gap: 15px;
                justify-content: space-between;
                flex-wrap: wrap;
                padding-bottom: 1em; /* 为底部按钮留出空间 */
            }

            /* 设置卡片样式 */
            .settings-section {
                flex: 1;
                min-width: 240px; /* 减小最小宽度 */
                background: #ffffff;
                border-radius: 8px;
                padding: 15px;
                box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
                margin-bottom: 10px;
            }

            /* 标题样式优化 */
            .settings-section-title {
                font-size: 1.1em;
                color: #2196F3;
                margin-bottom: 15px;
                padding-bottom: 8px;
                border-bottom: 1px solid #e0e0e0;
            }

            /* 输入框和标签样式优化 */
            .settings-row {
                margin-bottom: 12px;
            }

            .settings-label {
                display: block;
                margin-bottom: 4px;
                color: #666;
                font-size: 0.9em;
            }

            .settings-input,
            select.settings-input {
                width: 100%;
                padding: 6px 10px;
                border: 1px solid #e0e0e0;
                border-radius: 4px;
                font-size: 0.9em;
                box-sizing: border-box;
            }

            /* 复选框容器样式优化 */
            .checkbox-wrapper {
                display: flex;
                align-items: center;
                margin: 8px 0;
            }

            /* 按钮容器固定在底部 */
            .custom-actions {
                padding: 1em;
                background: #f8f9fa;
                border-top: 1px solid #e9ecef;
                margin-top: auto; /* 推到底部 */
                display: flex;
                justify-content: center;
                gap: 10px;
            }

            /* 按钮样式优化 */
            .custom-confirm-btn,
            .custom-cancel-btn {
                min-width: 80px !important;
                padding: 8px 20px !important;
                font-size: 0.95em !important;
                border-radius: 4px !important;
                height: 36px !important;
                line-height: 1 !important;
            }

            .custom-confirm-btn {
                background: #2196F3 !important;
                color: white !important;
                border: none !important;
            }

            .custom-cancel-btn {
                background: #f8f9fa !important;
                color: #333 !important;
                border: 1px solid #ddd !important;
            }

            /* 按钮悬停效果 */
            .custom-confirm-btn:hover,
            .custom-cancel-btn:hover {
                transform: translateY(-1px);
                transition: all 0.2s;
            }

            .custom-confirm-btn:hover {
                background: #1976D2 !important;
                box-shadow: 0 2px 5px rgba(33, 150, 243, 0.3);
            }

            .custom-cancel-btn:hover {
                background: #e9ecef !important;
                box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
            }

            /* 响应式布局优化 */
            @media (max-width: 768px) {
                .settings-container {
                    flex-direction: column;
                }
            }
        </style>

        <div class="settings-container">
            <!-- 触发设置部分 -->
            <div class="settings-section trigger-section">
                <div class="settings-section-title">触发设置</div>
                <div class="settings-row">
                    <label class="settings-label">选择触发方式:</label>
                    <select class="settings-input" id="actionMode">
                        <option value="0" ${GM_getValue('actionMode', 0) == 0 ? 'selected' : ''}>两者都用</option>
                        <option value="1" ${GM_getValue('actionMode', 0) == 1 ? 'selected' : ''}>长按</option>
                        <option value="2" ${GM_getValue('actionMode', 0) == 2 ? 'selected' : ''}>拖拽</option>
                    </select>
                </div>
                <div class="settings-row">
                    <label class="settings-label">长按生效时间(毫秒):</label>
                    <input type="number" class="settings-input" id="longPressEffective" value="${GM_getValue('longPressEffective', 200)}">
                </div>
                <div class="settings-row">
                    <label class="settings-label">长按触发时间(毫秒):</label>
                    <input type="number" class="settings-input" id="longPressDuration" value="${GM_getValue('longPressDuration', 500)}">
                </div>
                <div class="settings-row">
                    <label class="settings-label">拖拽超时时间(毫秒):</label>
                    <input type="number" class="settings-input" id="dragTimeOut" value="${GM_getValue('dragTimeOut', 2000)}">
                </div>
            </div>

            <!-- 效果设置部分 -->
            <div class="settings-section effect-section">
                <div class="settings-section-title">效果设置</div>
                <div class="settings-row">
                    <label class="settings-label">模糊强度(0-10):</label>
                    <input type="number" class="settings-input" id="blurIntensity" value="${GM_getValue('blurIntensity', 5)}">
                </div>
                <div class="settings-row">
                    <div class="checkbox-wrapper">
                        <input type="checkbox" id="blurEnabled" ${GM_getValue('blurEnabled', true) ? 'checked' : ''}>
                        <label for="blurEnabled">启用模糊效果</label>
                    </div>
                </div>
                <div class="settings-row">
                    <div class="checkbox-wrapper">
                        <input type="checkbox" id="closeOnMouseClick" ${GM_getValue('closeOnMouseClick', true) ? 'checked' : ''}>
                        <label for="closeOnMouseClick">点击关闭小窗</label>
                    </div>
                </div>
                <div class="settings-row">
                    <div class="checkbox-wrapper">
                        <input type="checkbox" id="closeOnScroll" ${GM_getValue('closeOnScroll', true) ? 'checked' : ''}>
                        <label for="closeOnScroll">滚动关闭小窗</label>
                    </div>
                </div>
                <div class="settings-row">
                    <div class="checkbox-wrapper">
                        <input type="checkbox" id="showCountdown" ${GM_getValue('showCountdown', true) ? 'checked' : ''}>
                        <label for="showCountdown">显示长按倒计时进度条</label>
                    </div>
                </div>
                <div class="settings-row">
                    <div class="checkbox-wrapper">
                        <input type="checkbox" id="showCountdowndrag" ${GM_getValue('showCountdowndrag', true) ? 'checked' : ''}>
                        <label for="showCountdowndrag">显示拖拽超时进度条</label>
                    </div>
                </div>
            </div>

            <!-- 窗口设置部分 -->
            <div class="settings-section window-section">
                <div class="settings-section-title">窗口设置</div>
                <div class="settings-row">
                    <div class="checkbox-wrapper">
                        <input type="checkbox" id="saveWindowConfig" ${GM_getValue('saveWindowConfig', true) ? 'checked' : ''}>
                        <label for="saveWindowConfig">记录窗口位置</label>
                    </div>
                </div>
            </div>
        </div>`,
        preConfirm: () => {
            // 收集所有设置的值
            return {
                actionMode: parseInt(document.getElementById('actionMode').value),
                longPressEffective: parseInt(document.getElementById('longPressEffective').value),
                longPressDuration: parseInt(document.getElementById('longPressDuration').value),
                dragTimeOut: parseInt(document.getElementById('dragTimeOut').value),
                blurIntensity: parseInt(document.getElementById('blurIntensity').value),
                blurEnabled: document.getElementById('blurEnabled').checked,
                closeOnMouseClick: document.getElementById('closeOnMouseClick').checked,
                closeOnScroll: document.getElementById('closeOnScroll').checked,
                showCountdown: document.getElementById('showCountdown').checked,
                showCountdowndrag: document.getElementById('showCountdowndrag').checked,
                saveWindowConfig: document.getElementById('saveWindowConfig').checked
            }
        }
    }).then((result) => {
        if (result.isConfirmed) {
            // 保存所有设置
            const values = result.value;
            Object.entries(values).forEach(([key, value]) => {
                GM_setValue(key, value);
                config[key] = value; // 更新当前配置
            });

            // 更新配置并刷新界面
            updateConfig();
            updateMenuCommands();
            setupEventListeners(); // 重新设置事件监听器

            // 显示保存成功提示
            Swal.fire({
                title: '设置已保存!',
                icon: 'success',
                timer: 1500,
                showConfirmButton: false
            });
        }
    });
}

    function delay(ms) {
        return new Promise(resolve => setTimeout(resolve, ms))
    }
    async function preloadLink(link, attributes = {}) {
        const preloadElement = document.createElement('link')
        preloadElement.rel = 'preload'
        preloadElement.href = link
        preloadElement.as = '*/*'
        Object.assign(preloadElement, attributes)
        document.head.appendChild(preloadElement)
        await delay(1)
    }
    function createAcrylicOverlay() {
        const acrylicOverlay = document.createElement('div')
        acrylicOverlay.style.position = 'fixed'
        acrylicOverlay.style.top = '0'
        acrylicOverlay.style.left = '0'
        acrylicOverlay.style.width = '100%'
        acrylicOverlay.style.height = '100%'
        acrylicOverlay.style.zIndex = '9999'
        acrylicOverlay.style.backdropFilter = config.blurEnabled ? `blur(${config.blurIntensity}px)` : 'none'
        if (config.closeOnMouseClick) {
            acrylicOverlay.addEventListener('click', handleAcrylicOverlayClick)
        }
        document.body.appendChild(acrylicOverlay)
        return acrylicOverlay
    }
    function handleAcrylicOverlayClick(event) {
        if (event.target === state.acrylicOverlay) {
            closePopupWindow()
        }
    }
    function removeAcrylicOverlay() {
        if (state.acrylicOverlay) {
            document.body.removeChild(state.acrylicOverlay)
            state.acrylicOverlay = null
        }
    }
    window.addEventListener('message', (event) => {
        const message = event.data
        if (message.type === 'qinwuyuan') {
            const width = window.innerWidth
            const height = window.innerHeight
            const left = window.screenX
            const top = window.screenY
            if (config.saveWindowConfig) {
                saveWindowConfig(width, height, left, top, message.hostname)
                //  console.log(width, height, left, top, message.hostname)
            }
        }
    })
    function openPopupWindow(link) {
        reWindowConfig()//FIXME - 跨域窗口如果自己刷新了配置,重新刷新下
        if (!state.popupWindow || state.popupWindow.closed) {
            state.acrylicOverlay = createAcrylicOverlay()
            state.popupWindow = window.open(link, '_blank', `width=${config.windowWidth},height=${config.windowHeight},left=${config.screenLeft},top=${config.screenTop}`)
            state.popupWindowChecker = setInterval(() => {
                if (state.popupWindow) {//保证窗口存在时才检测,兼容下原来脚本点击原窗口焦点关闭覆盖层
                    if (state.popupWindow.closed) {
                        removeAcrylicOverlay()
                        clearInterval(state.popupWindowChecker)
                    } else {
                        try {
                            const width = state.popupWindow.innerWidth
                            const height = state.popupWindow.innerHeight
                            const left = state.popupWindow.screenX
                            const top = state.popupWindow.screenY
                            if (config.saveWindowConfig) {
                                saveWindowConfig(width, height, left, top)
                            }
                        } catch (error) {
                            console.warn('访问跨源窗口属性失败,让弹出窗口自己设置窗口大小...:')
                            const message = {
                                type: 'qinwuyuan',
                                hostname: window.location.hostname
                            }
                            state.popupWindow.postMessage(message, '*')
                        }
                    }
                }
            }, 200)
        }
    }
    function closePopupWindow() {
        if (state.popupWindow && !state.popupWindow.closed) {
            state.popupWindow.close()
            state.popupWindow = null
            removeAcrylicOverlay()
            if (state.linkToPreload) {
                removePreloadedLink(state.linkToPreload)
            }
            window.removeEventListener('scroll', closePopupOnScroll)
        }
    }
    function removePreloadedLink(link) {
        const preloadElement = document.querySelector(`link[href="${link}"]`)
        if (preloadElement) {
            document.head.removeChild(preloadElement)
        }
    }
    function closePopupOnScroll() {
        if (state.popupWindow && !state.popupWindow.closed) {
            closePopupWindow()
        }
    }
    function toggleActionMode() {
        const mode = prompt(translate('toggleActionMode'), config.actionMode)
        if (mode !== null) {
            config.actionMode = parseInt(mode, 10)
            GM_setValue('actionMode', config.actionMode)
            setupEventListeners()
            updateMenuCommands()
        }
    }
    function setLongPressDuration() {
        const duration = prompt(translate('setLongPressDuration'), config.longPressDuration)
        if (duration !== null) {
            config.longPressDuration = duration
            GM_setValue('longPressDuration', duration)
            updateMenuCommands()
        }
    }
    function setLongPressEffective() {
        const duration = prompt(translate('setLongPressEffective'), config.longPressEffective)
        if (duration !== null) {
            config.longPressEffective = duration
            GM_setValue('longPressEffective', duration)
            updateMenuCommands()
        }
    }
    function setdragTimeOut() {
        const duration = prompt(translate('dragTimeOut'), config.dragTimeOut)
        if (duration !== null) {
            config.dragTimeOut = duration
            GM_setValue('dragTimeOut', duration)
            updateMenuCommands()
        }
    }
    function toggleBlurEffect() {
        config.blurEnabled = !config.blurEnabled
        GM_setValue('blurEnabled', config.blurEnabled)
        updateMenuCommands()
    }
    function setBlurIntensity() {
        const intensity = prompt(translate('setBlurIntensityprompt'), config.blurIntensity)
        if (intensity !== null) {
            config.blurIntensity = parseInt(intensity, 10)
            GM_setValue('blurIntensity', config.blurIntensity)
            updateMenuCommands()
        }
    }
    function toggleCloseOnMouseClick() {
        config.closeOnMouseClick = !config.closeOnMouseClick
        GM_setValue('closeOnMouseClick', config.closeOnMouseClick)
        updateMenuCommands()
    }
    function toggleCloseOnScroll() {
        config.closeOnScroll = !config.closeOnScroll
        handleScrollCommand()
        GM_setValue('closeOnScroll', config.closeOnScroll)
        updateMenuCommands()
    }
    function handleScrollCommand() {
        if (config.closeOnScroll) {
            window.addEventListener('scroll', closePopupOnScroll, { once: true })
        } else {
            window.removeEventListener('scroll', closePopupOnScroll)
        }
    }
    function setWindowSize(dimension) {//!SECTION-已无实际意义,开启记录窗口位置后,哪里还需要手动配置.
        const size = prompt(`${translate('setWindowSizeprompt')} (${dimension})`, config[dimension === 'width' ? 'windowWidth' : 'windowHeight'])
        if (size !== null) {
            config[dimension === 'width' ? 'windowWidth' : 'windowHeight'] = parseInt(size, 10)
            GM_setValue(dimension === 'width' ? 'windowWidth' : 'windowHeight', config[dimension === 'width' ? 'windowWidth' : 'windowHeight'])
            updateMenuCommands()
            if (state.popupWindow && !state.popupWindow.closed) {
                state.popupWindow.resizeTo(config.windowWidth, config.windowHeight)
            }
        }
    }
    let registeredMenuCommands = {}
    function registerMenuCommand(label, action) {
        const menuCommandId = GM_registerMenuCommand(label, action)
        registeredMenuCommands[label] = menuCommandId
        return menuCommandId
    }
    function saveWindowConfig(width, height, left, top, HostName = window.location.hostname) {
        config.windowWidth = width
        config.windowHeight = height
        config.screenLeft = left
        config.screenTop = top
        const currentHostName = HostName
        let windowConfigs = GM_getValue('SitewindowConfigs', []
        )
        let configUpdated = false
        for (let config of windowConfigs) {
            if (typeof config.hostName === 'string') {
                if (config.hostName === currentHostName) {
                    config.width = width
                    config.height = height
                    config.top = top
                    config.left = left
                    configUpdated = true
                    break
                }
            } else if (Array.isArray(config.hostName)) {
                if (config.hostName.includes(currentHostName)) {
                    config.width = width
                    config.height = height
                    config.top = top
                    config.left = left
                    configUpdated = true
                    break
                }
            }
        }
        if (!configUpdated) {
            windowConfigs.push({
                name: `${currentHostName}`,
                hostName: currentHostName,
                width: width,
                height: height,
                top: top,
                left: left
            })
        }
        //ANCHOR -  开启记录窗口位置时.无法找到配置时,会推送一个新配置,当其他的网站没有自定义配置的也同样使用这一次的窗口.大小.
        GM_setValue('SitewindowConfigs', windowConfigs)
        GM_setValue('custom_windowWidth', width)
        GM_setValue('custom_windowHeight', height)
        GM_setValue('custom_screenLeft', left)
        GM_setValue('custom_screenTop', top)
        updateMenuCommands()
    }
    function toggleSwitch(property) {
        if (property in config) {
            config[property] = !config[property]
            GM_setValue(property, config[property])
            updateMenuCommands()
        }
    }
    function updateMenuCommands() {//LINK -
        const menuCommands = [
            { label: translate('settings'), action: openSettings },
            { label: translate('actionMode') + ` (${config.actionMode === 1 ? translate('actionMode1') : config.actionMode === 2 ? translate('actionMode2') : translate('actionMode0')})`, action: toggleActionMode },
            { label: translate('longPressEffective') + ` (${config.longPressEffective}ms)`, action: setLongPressEffective },
            { label: translate('longPressDuration') + ` (${config.longPressDuration}ms)`, action: setLongPressDuration },
            { label: translate('dragTimeOut') + ` (${config.dragTimeOut}ms)`, action: setdragTimeOut },
            { label: translate('blurEnabled') + ` (${config.blurEnabled ? '✅' : '❌'})`, action: toggleBlurEffect },
            { label: translate('blurIntensity') + ` (${config.blurIntensity})`, action: setBlurIntensity },
            { label: translate('closeOnMouseClick') + ` (${config.closeOnMouseClick ? '✅' : '❌'})`, action: toggleCloseOnMouseClick },
            { label: translate('closeOnScroll') + ` (${config.closeOnScroll ? '✅' : '❌'})`, action: toggleCloseOnScroll },
            /*     { label: translate('windowWidth') + ` (${config.windowWidth})`, action: () => { setWindowSize('width') } },//!SECTION -已无实际意义,脚本不会使用
                { label: translate('windowHeight') + ` (${config.windowHeight})`, action: () => { setWindowSize('height') } },//!SECTION -已无实际意义,脚本不会使用 */
            { label: translate('showCountdown') + ` (${config.showCountdown ? '✅' : '❌'})`, action: () => { toggleSwitch('showCountdown') } },
            { label: translate('showCountdowndrag') + ` (${config.showCountdowndrag ? '✅' : '❌'})`, action: () => { toggleSwitch('showCountdowndrag') } },
            { label: translate('saveWindowConfig') + ` (${config.saveWindowConfig ? '✅' : '❌'})`, action: () => { toggleSwitch('saveWindowConfig') } }
        ]
        for (const label in registeredMenuCommands) {
            GM_unregisterMenuCommand(registeredMenuCommands[label])
        }
        registeredMenuCommands = {}
        menuCommands.forEach((command) => {
            registerMenuCommand(command.label, command.action)
        })
    }
    updateMenuCommands()
    function toTitleCase(str) {
        return str.replace(/\w\S*/g, (txt) => { return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase() })
    }
    function setupEventListeners() {
        // 移除旧的事件监听器
        document.body.removeEventListener('dragstart', handleDragStart)
        document.body.removeEventListener('dragend', handleDragEnd)
        document.body.removeEventListener('mousedown', handleMouseDown)
        document.body.removeEventListener('mouseup', handleMouseUp)
        document.body.removeEventListener('mouseleave', handleMouseLeave)
        document.body.removeEventListener('wheel', handleWheel)
        document.body.removeEventListener('click', handleClick)
        // 根据 actionMode 配置添加事件监听器
        if (config.actionMode === 1 || config.actionMode === 0) {
            document.body.addEventListener('mousedown', handleMouseDown)
            document.body.addEventListener('mouseup', handleMouseUp)
            document.body.addEventListener('mouseleave', handleMouseLeave)
        }
        if (config.actionMode === 2 || config.actionMode === 0) {
            document.body.addEventListener('dragstart', handleDragStart)
            document.body.addEventListener('dragend', handleDragEnd)
        }
        document.body.addEventListener('wheel', handleWheel)
        document.body.addEventListener('click', handleClick)
    }
    // 事件处理函数
    function handleDragStart(event) {
        const linkElement = event.target.tagName === 'A' ? event.target : event.target.closest('a')
        if (linkElement) {
            if (config.showCountdowndrag && config.dragTimeOut != 0) {//超时选项,只要
                state.dragprogressBar = createProgressBar('#ff9800', '#f44336')
                state.dragprogressBar.style.display = 'block'
                state.dragprogressBar.style.width = '5%'
                state.startTime = Date.now()
                clearInterval(state.dragintervalId)
                state.dragintervalId = setInterval(function () {
                    const elapsed = Date.now() - state.startTime
                    const progress = Math.max(5 - (elapsed / config.dragTimeOut) * 5, 0) // 减小你妈
                    state.dragprogressBar.style.width = `${progress}%`
                    if (progress <= 0) {// 超时结束
                        state.isDragging = false
                        clearInterval(state.dragintervalId)
                        state.dragprogressBar.style.display = 'none'
                    }
                }, 100) //
                window.addEventListener('drag', function (event) {
                    // 保证进度条位置处于貂毛鼠标的下面
                    const x = event.clientX
                    const y = event.clientY + 30 // 偏移
                    state.dragprogressBar.style.left = `${x}px`
                    state.dragprogressBar.style.top = `${y}px`
                })
            }
            const link = linkElement.href
            state.isDragging = true
            state.linkToPreload = link
            preloadLink(state.linkToPreload, { importance: 'high' }).then(() => {
                if (config.closeOnScroll) {
                    window.addEventListener('scroll', closePopupOnScroll, { once: true })
                }
            })
        }
    }
    function handleDragEnd(event) {
        const x = event.clientX
        const y = event.clientY
        console.log(x, y)
        const elementAtPoint = document.elementFromPoint(x, y)
        if (state.dragprogressBar) {//显示超时进度条时
            clearInterval(state.dragintervalId)
            state.dragprogressBar.style.display = 'none'
        }
        if (y < 1) {//接近顶部
            state.isDragging = false
        }
        //if (!document.body.contains(elementAtPoint)) state.isDragging = false//移出到系统
        if (state.isDragging && state.linkToPreload) {
            state.isDragging = false
            openPopupWindow(state.linkToPreload)
            state.linkToPreload = null
        }
    }
    function createProgressBar(colorStart = '#4caf50', colorEnd = '#81c784') {
        if (!config.showCountdown && !config.showCountdowndrag) return null
        const progressBar = document.createElement('div')
        Object.assign(progressBar.style, {
            position: 'fixed',
            height: '6px',
            width: '5%',
            background: `linear-gradient(to right, ${colorStart}, ${colorEnd})`,
            borderRadius: '3px',
            boxShadow: '0 2px 5px rgba(0, 0, 0, 0.3)',
            zIndex: '9999'
        })
        document.body.appendChild(progressBar)
        return progressBar
    }
    let mouseDownTime = 0
    function handleMouseDown(event) {
    // 只响应鼠标左键 (0 表示左键)
    if (event.button !== 0) return;
    const linkElement = event.target.tagName === 'A' ? event.target : event.target.closest('a')
    if (linkElement) {
            let isDragging = false
            let isMouseDown = true
            const onMouseMove = () => {
                isDragging = true
                clearTimeout(state.pressTimer)
                progressBarremove()
            }
            const onMouseUp = () => {
                isMouseDown = false
                clearTimeout(state.pressTimer)
                progressBarremove()
            }
            document.addEventListener('dragstart', onMouseMove, { once: true })
            document.addEventListener('mouseup', onMouseUp, { once: true })
            document.addEventListener('keydown', onMouseUp, { once: true })
            setTimeout(() => {
                if (!isDragging && isMouseDown) { // 确保没有拖拽并且鼠标仍按下
                    state.progressBar = createProgressBar()
                    if (state.progressBar) {
                        const transitionDuration = Math.max(config.longPressDuration, 0) + 'ms'
                        state.progressBar.style.left = `${event.clientX}px`  // 设置进度条位置为鼠标下方
                        state.progressBar.style.top = `${event.clientY + 20}px`  // 偏移一点,避免挡住鼠标
                        state.progressBar.style.transition = `width ${transitionDuration} linear`
                        requestAnimationFrame(() => {
                            state.progressBar.style.width = '0'
                        })
                    }
                }
                /* //NOTE - 鼠标按下的时间才会触发子函数计时,
                长按触发打开预览窗口时长=鼠标按下的时间+长按触发时间=打开小窗时间.
                */
                onProgres()
            }, config.longPressEffective)
            function onProgres(params) {
                state.pressTimer = setTimeout(() => {
                    if (!isDragging && isMouseDown) { // 确保没有拖拽并且鼠标仍按下
                        const link = linkElement.href
                        state.linkToPreload = link
                        preloadLink(state.linkToPreload, { importance: 'high' }).then(() => {
                            openPopupWindow(state.linkToPreload)
                        })
                    }
                    progressBarremove()
                }, config.longPressDuration)
            }
        }
    }
    function handleMouseUp() {
        clearTimeout(state.pressTimer)
        state.pressTimer = null
        progressBarremove()
    }
    function progressBarremove() {
        if (state.progressBar) {
            state.progressBar.remove()
        }
    }
    function handleMouseLeave() {
        clearTimeout(state.pressTimer)
        state.pressTimer = null
    }
    function handleWheel() {
        if (config.closeOnScroll) {
            closePopupWindow()
        }
    }
    function handleClick(event) {
        if (event.target === state.acrylicOverlay) {
            removeAcrylicOverlay()
        }
    }
    setupEventListeners()
})()