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.

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==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()
})()