Greasy Fork

来自缓存

Greasy Fork is available in English.

思源在线视频时间戳和截图

捕获视频时间戳和当前帧截图和点击跳转

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

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

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         思源在线视频时间戳和截图
// @namespace    https://github.com/KuiyueRO/siyuan-media-timestamp
// @version      1.2
// @description  捕获视频时间戳和当前帧截图和点击跳转
// @author       A_Cai
// @match        *://*/*
// @grant        GM_addStyle
// @grant        GM_xmlhttpRequest
// @connect      127.0.0.1
// @grant        GM_getValue
// @grant        GM_setValue
// ==/UserScript==


(function() {
    'use strict';

    // 添加样式
    GM_addStyle(`
        .settings-panel {
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background: #ffffff;
            padding: 24px;
            border-radius: 12px;
            box-shadow: 0 4px 24px rgba(0,0,0,0.15);
            z-index: 100000;
            display: none;
            width: 460px;
            max-height: 85vh;
            overflow-y: auto;
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
            box-sizing: border-box; // 添加这行确保padding不会影响总宽度
        }
        .settings-panel h3 {
            margin: 0 0 24px 0;
            color: #1a1a1a;
            font-size: 20px;
            font-weight: 600;
            display: flex;
            align-items: center;
            gap: 8px;
        }
        .settings-panel h3::before {
            content: '';
            display: inline-block;
            width: 4px;
            height: 20px;
            background: #4CAF50;
            border-radius: 2px;
        }
        .settings-section {
            padding: 16px;
            background: #f8f9fa;
            border-radius: 8px;
            margin-bottom: 16px;
            box-sizing: border-box;
        }
        .settings-section-title {
            font-size: 16px;
            font-weight: 500;
            color: #2c3e50;
            margin-bottom: 16px;
        }
        .settings-field {
            margin-bottom: 20px;
            padding: 0 12px;
            box-sizing: border-box;
        }
        .settings-field label {
            display: block;
            color: #2c3e50;
            font-size: 14px;
            font-weight: 500;
            margin-bottom: 8px;
            opacity: 0.85;
        }
        .settings-field label:hover {
            opacity: 1;
        }
        .settings-field input[type="text"],
        .settings-field select {
            width: calc(100% - 24px);
            padding: 10px 12px;
            border: 1px solid #e0e0e0;
            border-radius: 6px;
            font-size: 14px;
            transition: all 0.2s ease;
            background: #ffffff;
            box-sizing: border-box;
        }
        .settings-field input[type="text"]:hover,
        .settings-field select:hover {
            border-color: #d0d0d0;
        }
        .settings-field input[type="text"]:focus,
        .settings-field select:focus {
            border-color: #4CAF50;
            box-shadow: 0 0 0 2px rgba(76, 175, 80, 0.1);
            outline: none;
        }
        .select-wrapper {
            position: relative;
            width: 100%;
            box-sizing: border-box;
        }
        .select-wrapper::after {
            content: '';
            position: absolute;
            right: 16px; // 调整箭头位置
            top: 50%;
            transform: translateY(-50%);
            width: 0;
            height: 0;
            border-left: 5px solid transparent;
            border-right: 5px solid transparent;
            border-top: 5px solid #666;
            pointer-events: none;
            transition: all 0.2s ease;
        }
        .select-wrapper:hover::after {
            border-top-color: #333;
        }
        .custom-select {
            appearance: none;
            width: calc(100% - 24px) !important;
            padding-right: 36px !important; // 为下拉箭头留出更多空间
            cursor: pointer;
            box-sizing: border-box;
            background: #ffffff;
        }
        .match-list {
            background: #ffffff;
            border: 1px solid #e0e0e0;
            border-radius: 8px;
            padding: 16px;
            max-height: 180px;
            overflow-y: auto;
        }
        .match-item {
            display: flex;
            align-items: center;
            gap: 12px;
            margin-bottom: 12px;
            padding: 4px;
            background: transparent;
            border-radius: 6px;
            transition: all 0.2s ease;
        }
        .match-input {
            flex: 1;
            padding: 10px 12px;
            border: 1px solid #e0e0e0;
            border-radius: 6px;
            font-size: 14px;
            transition: all 0.3s ease;
            background: #ffffff;
        }
        .match-input:hover {
            border-color: #d0d0d0;
        }
        .match-input:focus {
            border-color: #4CAF50;
            box-shadow: 0 0 0 2px rgba(76, 175, 80, 0.1);
            outline: none;
        }
        .delete-match-btn {
            width: 24px;
            height: 24px;
            padding: 0;
            border: none;
            background: transparent;
            color: #999;
            font-size: 18px;
            cursor: pointer;
            display: flex;
            align-items: center;
            justify-content: center;
            transition: all 0.2s ease;
            border-radius: 4px;
        }
        .delete-match-btn:hover {
            color: #ff4444;
            background: transparent;
            transform: scale(1.1);
        }
        .add-match-btn {
            margin-top: 12px;
            padding: 10px;
            height: 40px;
            background: #f8f9fa;
            color: #666;
            border: 1px dashed #ddd;
            border-radius: 6px;
            cursor: pointer;
            width: 100%;
            font-size: 14px;
            display: flex;
            align-items: center;
            justify-content: center;
            gap: 8px;
            transition: all 0.2s ease;
        }
        .add-match-btn:hover {
            background: #f0f0f0;
            border-color: #999;
            color: #333;
            transform: translateY(-1px);
        }
        .add-match-btn svg {
            width: 14px;
            height: 14px;
            stroke: currentColor;
            transition: transform 0.2s ease;
        }
        .add-match-btn:hover svg {
            transform: scale(1.1);
        }
        .settings-buttons {
            margin-top: 24px;
            display: flex;
            justify-content: flex-end;
            gap: 12px;
        }
        .settings-btn {
            padding: 10px 20px;
            border: none;
            border-radius: 6px;
            font-size: 14px;
            font-weight: 500;
            cursor: pointer;
            transition: all 0.3s;
        }
        .settings-btn.primary {
            background: #4CAF50;
            color: white;
        }
        .settings-btn.secondary {
            background: #f5f5f5;
            color: #333;
        }
        .settings-btn:hover {
            transform: translateY(-1px);
            box-shadow: 0 2px 8px rgba(0,0,0,0.1);
        }
        .toast-notification {
            position: fixed;
            bottom: 20px;
            right: 20px;
            padding: 12px 24px;
            background: rgba(0, 0, 0, 0.8);
            color: white;
            border-radius: 6px;
            font-size: 14px;
            z-index: 10000;
            animation: slideIn 0.3s ease-out;
        }
        @keyframes slideIn {
            from {
                transform: translateY(100%);
                opacity: 0.2;
            }
            to {
                transform: translateY(0);
                opacity: 1;
            }
        }
        .timestamp-list-panel {
            background: rgba(24, 24, 27, 0.95);
            padding: 0;
            border-radius: 12px;
            box-shadow: 0 4px 20px rgba(0, 0, 0, 0.25);
            width: 300px;
            max-height: 400px;
            display: flex;
            flex-direction: column;
            overflow: hidden;
            backdrop-filter: blur(10px);
            -webkit-backdrop-filter: blur(10px);
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
            opacity: 0.95;
            transition: all 0.3s ease;
            color: rgba(255, 255, 255, 0.9);
            border: 1px solid rgba(255, 255, 255, 0.1);
            position: fixed;
            bottom: 20px;
            right: 20px;
            z-index: 999999;
        }

        /* Dark Reader 支持 */
        @media (prefers-color-scheme: dark) {
            .timestamp-list-panel {
                background: rgba(24, 24, 27, 0.95);
                color: rgba(255, 255, 255, 0.9);
                border: 1px solid rgba(255, 255, 255, 0.1);
            }
        }

        [data-darkreader-scheme="dark"] .timestamp-list-panel {
            background: rgba(24, 24, 27, 0.95);
            color: rgba(255, 255, 255, 0.9);
            border: 1px solid rgba(255, 255, 255, 0.1);
        }

        .timestamp-list-panel:hover,
        .timestamp-list-header:hover ~ * {
            opacity: 1 !important;
            box-shadow: 0 4px 24px rgba(0, 0, 0, 0.3);
        }

        .timestamp-list-header {
            cursor: grab;
            user-select: none;
            opacity: 1;
            transition: opacity 0.3s ease;
            border-bottom: 1px solid rgba(255, 255, 255, 0.1);
            padding: 12px 16px;
            margin-bottom: 0;
            display: flex;
            align-items: center;
            justify-content: space-between;
            background: rgba(36, 36, 42, 0.95);
        }

        .timestamp-list-header:hover {
            opacity: 1;
        }

        .timestamp-list-header:hover .timestamp-list-title {
            opacity: 1;
        }

        .timestamp-header-buttons {
            display: flex;
            gap: 8px;
            opacity: 1;
            transition: opacity 0.3s ease;
        }

        .timestamp-header-btn {
            opacity: 0.8;
            transition: all 0.2s ease;
            padding: 6px;
            background: rgba(255, 255, 255, 0.1);
            border: none;
            border-radius: 6px;
            cursor: pointer;
            display: flex;
            align-items: center;
            justify-content: center;
        }

        .timestamp-header-btn:hover {
            opacity: 1;
            background: rgba(255, 255, 255, 0.2);
            transform: translateY(-1px);
        }

        .timestamp-list-header:hover .timestamp-header-buttons {
            opacity: 1;
        }

        .timestamp-item {
            padding: 12px 16px;
            margin: 0;
            cursor: pointer;
            transition: all 0.2s;
            border-bottom: 1px solid rgba(255, 255, 255, 0.05);
            color: rgba(255, 255, 255, 0.9);
            display: flex;
        }

        .timestamp-item:hover {
            background: rgba(255, 255, 255, 0.05);
        }

        .timestamp-item.active {
            background: rgba(76, 175, 80, 0.15);
            border-left: 3px solid rgba(76, 175, 80, 0.8);
        }

        .no-timestamps {
            color: rgba(255, 255, 255, 0.6);
            text-align: center;
            padding: 20px;
            font-style: italic;
        }

        #timestamp-list {
            overflow-y: auto;
            flex: 1;
            max-height: 320px;
            scrollbar-width: thin;
            scrollbar-color: rgba(255, 255, 255, 0.2) transparent;
        }

        #timestamp-list::-webkit-scrollbar {
            width: 6px;
        }

        #timestamp-list::-webkit-scrollbar-track {
            background: transparent;
        }

        #timestamp-list::-webkit-scrollbar-thumb {
            background-color: rgba(255, 255, 255, 0.2);
            border-radius: 3px;
        }

        .timestamp-list-title {
            font-size: 14px;
            font-weight: 600;
            color: rgba(255, 255, 255, 0.9);
        }

        .timestamp-header-btn img {
            width: 16px;
            height: 16px;
            filter: invert(1);
        }
        .match-list-field {
            flex-direction: column !important;
            align-items: stretch !important;
        }

        .match-list {
            margin-top: 10px;
            max-height: 200px;
            overflow-y: auto;
            border: 1px solid #ddd;
            border-radius: 6px;
            padding: 8px;
        }

        .match-item {
            display: flex;
            align-items: center;
            margin-bottom: 8px;
            gap: 8px;
        }

        .match-input {
            flex: 1;
            padding: 8px;
            border: 1px solid #ddd;
            border-radius: 4px;
            font-size: 14px;
        }

        .delete-match-btn {
            padding: 4px 8px;
            background: #ff4444;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
        }

        .delete-match-btn:hover {
            background: #ff6666;
        }

        .add-match-btn {
            margin-top: 8px;
            padding: 8px;
            background: #4CAF50;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            width: 100%;
        }

        .add-match-btn:hover {
            background: #45a049;
        }

        .timestamp-item {
            display: flex;
            padding: 8px;
            margin: 4px 0;
            border-radius: 4px;
            transition: all 0.2s;
        }

        .timestamp-left {
            flex: 1;
            display: flex;
            flex-direction: column;
            gap: 4px;
        }

        .timestamp-text {
            font-weight: 500;
            cursor: pointer;
        }

        .timestamp-text:hover {
            color: #4CAF50;
        }

        .timestamp-note-container {
            display: flex;
            align-items: center;
        }

        .timestamp-note-input {
            width: 100%;
            min-height: 24px;
            padding: 6px 8px;
            border: 1px solid rgba(255, 255, 255, 0.1);
            border-radius: 4px;
            background: rgba(0, 0, 0, 0.1);
            color: var(--b3-theme-on-background);
            font-size: 14px;
            line-height: 1.5;
            font-family: var(--b3-font-family);
            transition: all 0.2s ease;
            resize: vertical;
            overflow-y: hidden;
            box-sizing: border-box;
        }

        .timestamp-note-input:hover {
            border-color: rgba(255, 255, 255, 0.2);
            background: rgba(0, 0, 0, 0.15);
        }

        .timestamp-note-input:focus {
            border-color: var(--b3-theme-primary);
            background: rgba(0, 0, 0, 0.2);
            outline: none;
            box-shadow: 0 0 0 2px rgba(var(--b3-theme-primary-rgb), 0.1);
        }

        .timestamp-note-container {
            margin: 4px 0;
            width: 100%;
            position: relative;
        }

        .timestamp-note-input::placeholder {
            color: rgba(255, 255, 255, 0.3);
            font-style: italic;
        }

        .timestamp-note-input::-webkit-scrollbar {
            width: 4px;
        }

        .timestamp-note-input::-webkit-scrollbar-thumb {
            background: rgba(255, 255, 255, 0.1);
            border-radius: 2px;
        }

        .timestamp-note-input::-webkit-scrollbar-thumb:hover {
            background: rgba(255, 255, 255, 0.2);
        }

        /* 拖拽相关样式 */
        .drag-handle {
            display: flex;
            align-items: center;
            justify-content: center;
            color: rgba(255, 255, 255, 0.7);
            cursor: grab;
        }

        .timestamp-list-panel.dragging {
            box-shadow: 0 8px 28px rgba(0, 0, 0, 0.4) !important;
            opacity: 0.95 !important;
        }

        .timestamp-list-header:hover .drag-handle {
            color: rgba(255, 255, 255, 0.9);
        }

        /* 类似GitHub Copilot的动画效果 */
        .timestamp-list-panel {
            animation: slide-up 0.3s ease-out;
        }

        @keyframes slide-up {
            from {
                transform: translateY(20px);
                opacity: 0;
            }
            to {
                transform: translateY(0);
                opacity: 0.95;
            }
        }

        /* 添加一个隐藏/显示面板的按钮和小药丸样式 */
        .timestamp-toggle-btn {
            position: fixed;
            bottom: 20px;
            right: 20px;
            width: 40px;
            height: 40px;
            background: rgba(24, 24, 27, 0.95);
            border-radius: 50%;
            display: flex;
            align-items: center;
            justify-content: center;
            cursor: pointer;
            z-index: 999998;
            border: 1px solid rgba(255, 255, 255, 0.1);
            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
            transition: all 0.2s ease;
            opacity: 0;
        }

        .timestamp-pills-container {
            position: fixed;
            bottom: 20px;
            right: 20px;
            display: flex;
            align-items: center;
            z-index: 999998;
            gap: 10px;
            opacity: 0;
            transition: all 0.3s ease;
        }

        .timestamp-pills-container.visible {
            opacity: 1;
        }

        .timestamp-pill {
            background: rgba(24, 24, 27, 0.95);
            border-radius: 50px;
            border: 1px solid rgba(255, 255, 255, 0.1);
            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
            display: flex;
            align-items: center;
            padding: 6px 14px;
            cursor: pointer;
            transition: all 0.2s ease;
        }

        .timestamp-pill:hover {
            transform: translateY(-2px);
            background: rgba(36, 36, 42, 0.95);
        }

        .timestamp-pill img {
            width: 18px;
            height: 18px;
            filter: invert(1);
            margin-right: 8px;
        }

        .timestamp-pill-text {
            color: rgba(255, 255, 255, 0.9);
            font-size: 13px;
            font-weight: 500;
        }

        .timestamp-expand-btn {
            background: rgba(24, 24, 27, 0.95);
            border-radius: 50%;
            width: 36px;
            height: 36px;
            display: flex;
            align-items: center;
            justify-content: center;
            cursor: pointer;
            border: 1px solid rgba(255, 255, 255, 0.1);
            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
            transition: all 0.2s ease;
        }

        .timestamp-expand-btn:hover {
            transform: scale(1.1);
            background: rgba(36, 36, 42, 0.95);
        }

        .timestamp-expand-btn img {
            width: 16px;
            height: 16px;
            filter: invert(1);
        }

        /* 底部按钮栏 */
        .timestamp-footer {
            display: flex;
            padding: 12px 16px;
            border-top: 1px solid rgba(255, 255, 255, 0.1);
            background: rgba(36, 36, 42, 0.95);
            justify-content: space-between;
        }

        .timestamp-footer-buttons {
            display: flex;
            gap: 8px;
        }

        .timestamp-footer-btn {
            opacity: 0.8;
            transition: all 0.2s ease;
            padding: 6px;
            background: rgba(255, 255, 255, 0.1);
            border: none;
            border-radius: 6px;
            cursor: pointer;
            display: flex;
            align-items: center;
            justify-content: center;
        }

        .timestamp-footer-btn:hover {
            opacity: 1;
            background: rgba(255, 255, 255, 0.2);
            transform: translateY(-1px);
        }

        .timestamp-footer-btn img {
            width: 16px;
            height: 16px;
            filter: invert(1);
        }

        .timestamp-panel-toggle {
            display: flex;
            align-items: center;
            gap: 6px;
            color: rgba(255, 255, 255, 0.7);
            font-size: 12px;
            cursor: pointer;
            padding: 4px 8px;
            border-radius: 4px;
            transition: all 0.2s ease;
        }

        .timestamp-panel-toggle:hover {
            color: rgba(255, 255, 255, 0.9);
            background: rgba(255, 255, 255, 0.05);
        }

        .timestamp-list-panel.hidden + .timestamp-toggle-btn {
            opacity: 1;
        }

        /* 时间戳项样式 */
        .timestamp-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 8px;
            cursor: pointer;
        }

        .timestamp-time {
            font-size: 12px;
            color: rgba(255, 255, 255, 0.6);
            background: rgba(255, 255, 255, 0.1);
            padding: 2px 6px;
            border-radius: 4px;
            transition: all 0.2s ease;
        }

        .timestamp-item:hover .timestamp-time {
            background: rgba(255, 255, 255, 0.15);
            color: rgba(255, 255, 255, 0.8);
        }

        .timestamp-item.active .timestamp-time {
            background: rgba(76, 175, 80, 0.2);
            color: rgba(76, 175, 80, 0.9);
        }
    `);

    // 配置管理
    const configManager = {
        defaults: {
            API_ENDPOINT: 'http://127.0.0.1:6806',
            API_TOKEN: '',
            TARGET_DOC_ID: '',
            NOTEBOOK_ID: '',
            NOTEBOOK_NAME: '',
            CREATE_NOTE_HOTKEY: '',
            TIMESTAMP_HOTKEY: '',
            SCREENSHOT_HOTKEY: '',
            MATCH_LIST: [
                'https://www.youtube.com/watch?v=',
                'https://www.bilibili.com/video/',
                'https://pan.baidu.com/play/'  // 添加百度网盘匹配
            ]
        },

        // 获取配置
        get: function() {
            const config = {};
            for (const [key, defaultValue] of Object.entries(this.defaults)) {
                config[key] = GM_getValue(key, defaultValue);
            }
            return config;
        },

        // 保存配置
        save: function(newConfig) {
            for (const [key, value] of Object.entries(newConfig)) {
                GM_setValue(key, value);
            }
        }
    };

    // 添加缓存机制
    const cache = {
        notebooks: null,
        notebookExpiry: 0,
        CACHE_DURATION: 5 * 60 * 1000, // 5分钟缓存

        async getNotebooks() {
            const now = Date.now();
            if (this.notebooks && now < this.notebookExpiry) {
                return this.notebooks;
            }

            const notebooks = await getNotebooks();
            this.notebooks = notebooks;
            this.notebookExpiry = now + this.CACHE_DURATION;
            return notebooks;
        },

        clearCache() {
            this.notebooks = null;
            this.notebookExpiry = 0;
        }
    };

    // 添加重试机制的 API 调用包装器
    async function retryApiCall(apiCall, maxRetries = 3, delay = 1000) {
        let lastError;

        for (let i = 0; i < maxRetries; i++) {
            try {
                return await apiCall();
            } catch (error) {
                lastError = error;
                if (i < maxRetries - 1) {
                    await new Promise(resolve => setTimeout(resolve, delay));
                }
            }
        }

        throw lastError;
    }

    // 使用示例
    async function getNotebooks() {
        return retryApiCall(async () => {
            const config = getConfig();
            return new Promise((resolve, reject) => {
                GM_xmlhttpRequest({
                    method: 'POST',
                    url: `${config.API_ENDPOINT}/api/notebook/lsNotebooks`,
                    headers: {
                        'Authorization': `Token ${config.API_TOKEN}`,
                        'Content-Type': 'application/json'
                    },
                    onload: function(response) {
                        if (response.status === 200) {
                            const result = JSON.parse(response.responseText);
                            if (result.code === 0) {
                                resolve(result.data.notebooks);
                            } else {
                                reject(new Error(result.msg));
                            }
                        } else {
                            reject(new Error('请求失败'));
                        }
                    },
                    onerror: reject
                });
            });
        });
    }

    // 创建设置面板
    function createSettingsPanel() {
        const panel = document.createElement('div');
        panel.className = 'settings-panel';
        const currentConfig = configManager.get();

        // 创建标题
        const title = document.createElement('h3');
        title.textContent = '思源笔记设置';
        panel.appendChild(title);

        // 基本设置部分
        const basicSection = document.createElement('div');
        basicSection.className = 'settings-section';

        const basicTitle = document.createElement('div');
        basicTitle.className = 'settings-section-title';
        basicTitle.textContent = '基本设置';
        basicSection.appendChild(basicTitle);

        // API 地址设置
        const apiField = document.createElement('div');
        apiField.className = 'settings-field';

        const apiLabel = document.createElement('label');
        apiLabel.textContent = 'API 地址';

        const apiInput = document.createElement('input');
        apiInput.type = 'text';
        apiInput.id = 'api-endpoint';
        apiInput.value = currentConfig.API_ENDPOINT;
        apiInput.placeholder = 'http://127.0.0.1:6806';

        // 添加API端点输入事件监听
        apiInput.addEventListener('change', async () => {
            // 获取当前的API端点和Token
            const apiEndpoint = apiInput.value.trim();
            const apiToken = panel.querySelector('#api-token').value.trim();

            if (apiEndpoint && apiToken) {
                // 临时保存配置以加载笔记本
                const tempConfig = {
                    ...currentConfig,
                    API_ENDPOINT: apiEndpoint,
                    API_TOKEN: apiToken
                };
                configManager.save(tempConfig);

                // 清空笔记本选择器并显示加载中状态
                const notebookSelect = panel.querySelector('#notebook-select');
                notebookSelect.innerHTML = '<option value="">加载中...</option>';

                try {
                    // 清除缓存并重新加载笔记本列表
                    cache.clearCache();
                    await loadNotebookList(panel);
                    showNotification('笔记本列表已更新');
                } catch (error) {
                    console.error('加载笔记本列表失败:', error);
                    notebookSelect.innerHTML = '<option value="">加载失败</option>';
                    showNotification('加载笔记本列表失败: ' + error.message);
                }
            }
        });

        apiField.appendChild(apiLabel);
        apiField.appendChild(apiInput);
        basicSection.appendChild(apiField);

        // API Token 设置
        const tokenField = document.createElement('div');
        tokenField.className = 'settings-field';

        const tokenLabel = document.createElement('label');
        tokenLabel.textContent = 'API Token';

        const tokenInput = document.createElement('input');
        tokenInput.type = 'text';
        tokenInput.id = 'api-token';
        tokenInput.value = currentConfig.API_TOKEN;
        tokenInput.placeholder = '输入你的 API Token';

        // 添加API Token输入事件监听
        tokenInput.addEventListener('change', async () => {
            // 获取当前的API端点和Token
            const apiEndpoint = panel.querySelector('#api-endpoint').value.trim();
            const apiToken = tokenInput.value.trim();

            if (apiEndpoint && apiToken) {
                // 临时保存配置以加载笔记本
                const tempConfig = {
                    ...currentConfig,
                    API_ENDPOINT: apiEndpoint,
                    API_TOKEN: apiToken
                };
                configManager.save(tempConfig);

                // 清空笔记本选择器并显示加载中状态
                const notebookSelect = panel.querySelector('#notebook-select');
                notebookSelect.innerHTML = '<option value="">加载中...</option>';

                try {
                    // 清除缓存并重新加载笔记本列表
                    cache.clearCache();
                    await loadNotebookList(panel);
                    showNotification('笔记本列表已更新');
                } catch (error) {
                    console.error('加载笔记本列表失败:', error);
                    notebookSelect.innerHTML = '<option value="">加载失败</option>';
                    showNotification('加载笔记本列表失败: ' + error.message);
                }
            }
        });

        tokenField.appendChild(tokenLabel);
        tokenField.appendChild(tokenInput);
        basicSection.appendChild(tokenField);

        // 笔记本选择
        const notebookField = document.createElement('div');
        notebookField.className = 'settings-field';

        const notebookLabel = document.createElement('label');
        notebookLabel.textContent = '选择笔记本';

        const selectWrapper = document.createElement('div');
        selectWrapper.className = 'select-wrapper';

        const notebookSelect = document.createElement('select');
        notebookSelect.id = 'notebook-select';
        notebookSelect.className = 'custom-select';

        const defaultOption = document.createElement('option');
        defaultOption.value = '';
        defaultOption.textContent = '加载中...';
        notebookSelect.appendChild(defaultOption);

        selectWrapper.appendChild(notebookSelect);

        // 添加刷新笔记本按钮
        const refreshButton = document.createElement('button');
        refreshButton.className = 'refresh-notebooks-btn';
        refreshButton.textContent = '刷新笔记本';
        refreshButton.onclick = async () => {
            const apiEndpoint = panel.querySelector('#api-endpoint').value.trim();
            const apiToken = panel.querySelector('#api-token').value.trim();

            if (!apiEndpoint || !apiToken) {
                showNotification('请先填写API地址和Token');
                return;
            }

            // 清空笔记本选择器并显示加载中状态
            notebookSelect.innerHTML = '<option value="">加载中...</option>';

            // 临时保存配置以加载笔记本
            const tempConfig = {
                ...currentConfig,
                API_ENDPOINT: apiEndpoint,
                API_TOKEN: apiToken
            };
            configManager.save(tempConfig);

            try {
                // 清除缓存并重新加载笔记本列表
                cache.clearCache();
                await loadNotebookList(panel);
                showNotification('笔记本列表已更新');
            } catch (error) {
                console.error('加载笔记本列表失败:', error);
                notebookSelect.innerHTML = '<option value="">加载失败</option>';
                showNotification('加载笔记本列表失败: ' + error.message);
            }
        };

        selectWrapper.appendChild(refreshButton);
        notebookField.appendChild(notebookLabel);
        notebookField.appendChild(selectWrapper);
        basicSection.appendChild(notebookField);

        panel.appendChild(basicSection);

        // 快捷键设置部分
        const hotkeySection = document.createElement('div');
        hotkeySection.className = 'settings-section';

        const hotkeyTitle = document.createElement('div');
        hotkeyTitle.className = 'settings-section-title';
        hotkeyTitle.textContent = '快捷键设置';
        hotkeySection.appendChild(hotkeyTitle);

        // 创建笔记快捷键
        const createNoteField = document.createElement('div');
        createNoteField.className = 'settings-field';

        const createNoteLabel = document.createElement('label');
        createNoteLabel.textContent = '创建笔记快捷键';

        const createNoteInput = document.createElement('input');
        createNoteInput.type = 'text';
        createNoteInput.id = 'create-note-hotkey';
        createNoteInput.value = currentConfig.CREATE_NOTE_HOTKEY;
        createNoteInput.placeholder = '点击设置快捷键';
        createNoteInput.readOnly = true;

        createNoteField.appendChild(createNoteLabel);
        createNoteField.appendChild(createNoteInput);
        hotkeySection.appendChild(createNoteField);

        // 时间戳快捷键
        const timestampField = document.createElement('div');
        timestampField.className = 'settings-field';

        const timestampLabel = document.createElement('label');
        timestampLabel.textContent = '时间戳快捷键';

        const timestampInput = document.createElement('input');
        timestampInput.type = 'text';
        timestampInput.id = 'timestamp-hotkey';
        timestampInput.value = currentConfig.TIMESTAMP_HOTKEY;
        timestampInput.placeholder = '点击设置快捷键';
        timestampInput.readOnly = true;

        timestampField.appendChild(timestampLabel);
        timestampField.appendChild(timestampInput);
        hotkeySection.appendChild(timestampField);

        // 截图+时间戳快捷键
        const screenshotField = document.createElement('div');
        screenshotField.className = 'settings-field';

        const screenshotLabel = document.createElement('label');
        screenshotLabel.textContent = '截图+时间戳快捷键';

        const screenshotInput = document.createElement('input');
        screenshotInput.type = 'text';
        screenshotInput.id = 'screenshot-hotkey';
        screenshotInput.value = currentConfig.SCREENSHOT_HOTKEY;
        screenshotInput.placeholder = '点击设置快捷键';
        screenshotInput.readOnly = true;

        screenshotField.appendChild(screenshotLabel);
        screenshotField.appendChild(screenshotInput);
        hotkeySection.appendChild(screenshotField);

        panel.appendChild(hotkeySection);

        // 网站匹配规则部分
        const matchSection = document.createElement('div');
        matchSection.className = 'settings-section';

        const matchTitle = document.createElement('div');
        matchTitle.className = 'settings-section-title';
        matchTitle.textContent = '网站匹配规则';
        matchSection.appendChild(matchTitle);

        const matchList = document.createElement('div');
        matchList.id = 'match-list';
        matchList.className = 'match-list';

        // 添加现有的匹配规则
        currentConfig.MATCH_LIST.forEach(match => {
            const matchItem = createMatchItem(match);
            matchList.appendChild(matchItem);
        });

        matchSection.appendChild(matchList);

        // 添加规则按钮
        const addMatchBtn = document.createElement('button');
        addMatchBtn.className = 'add-match-btn';

        const addBtnSvg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
        addBtnSvg.setAttribute('width', '16');
        addBtnSvg.setAttribute('height', '16');
        addBtnSvg.setAttribute('viewBox', '0 0 16 16');
        addBtnSvg.setAttribute('fill', 'none');

        const addBtnPath = document.createElementNS('http://www.w3.org/2000/svg', 'path');
        addBtnPath.setAttribute('d', 'M8 3v10M3 8h10');
        addBtnPath.setAttribute('stroke', 'currentColor');
        addBtnPath.setAttribute('stroke-width', '2');
        addBtnPath.setAttribute('stroke-linecap', 'round');

        addBtnSvg.appendChild(addBtnPath);
        addMatchBtn.appendChild(addBtnSvg);

        const addBtnText = document.createTextNode('添加匹配规则');
        addMatchBtn.appendChild(addBtnText);

        matchSection.appendChild(addMatchBtn);
        panel.appendChild(matchSection);

        // 按钮组
        const buttonsContainer = document.createElement('div');
        buttonsContainer.className = 'settings-buttons';

        const cancelBtn = document.createElement('button');
        cancelBtn.id = 'cancel-settings';
        cancelBtn.className = 'settings-btn secondary';
        cancelBtn.textContent = '取消';
        buttonsContainer.appendChild(cancelBtn);

        const saveBtn = document.createElement('button');
        saveBtn.id = 'save-settings';
        saveBtn.className = 'settings-btn primary';
        saveBtn.textContent = '保存';
        buttonsContainer.appendChild(saveBtn);

        panel.appendChild(buttonsContainer);

        document.body.appendChild(panel);

        // 加载笔记本列表
        loadNotebookList(panel);

        // 设置事件监听
        setupEventListeners(panel);

        // 初始化时尝试加载笔记本列表
        if (currentConfig.API_ENDPOINT && currentConfig.API_TOKEN) {
            try {
                // 延迟一点加载,确保面板已完全创建
                setTimeout(async () => {
                    await loadNotebookList(panel);
                }, 100);
            } catch (error) {
                console.error('初始加载笔记本列表失败:', error);
            }
        }

        return panel;
    }

    // 修改 loadNotebookList 使用缓存
    async function loadNotebookList(panel) {
        const select = panel.querySelector('#notebook-select');
        const currentConfig = configManager.get();

        try {
            const notebooks = await cache.getNotebooks();

            // 清空现有选项
            while (select.firstChild) {
                select.removeChild(select.firstChild);
            }

            // 添加新的选项
            notebooks.forEach(notebook => {
                const option = document.createElement('option');
                option.value = notebook.id;
                option.textContent = notebook.name;
                if (notebook.id === currentConfig.NOTEBOOK_ID) {
                    option.selected = true;
                }
                select.appendChild(option);
            });
        } catch (error) {
            // 创建错误提示选项
            const errorOption = document.createElement('option');
            errorOption.value = '';
            errorOption.textContent = `加载失败: ${error.message}`;

            // 清空现有选项
            while (select.firstChild) {
                select.removeChild(select.firstChild);
            }

            select.appendChild(errorOption);
        }
    }

    // 添加创建匹配项的辅助函数
    function createMatchItem(value) {
        const item = document.createElement('div');
        item.className = 'match-item';

        const input = document.createElement('input');
        input.type = 'text';
        input.className = 'match-input';
        input.value = value;
        input.placeholder = '输入匹配规则,例如: https://www.youtube.com/watch?v=';

        const deleteBtn = document.createElement('button');
        deleteBtn.className = 'delete-match-btn';
        deleteBtn.title = '移除规则';

        // 创建 × 符号
        const deleteText = document.createTextNode('×');
        deleteBtn.appendChild(deleteText);

        // 添加删除按钮的点击事件
        deleteBtn.onclick = () => {
            item.style.opacity = '0';
            setTimeout(() => item.remove(), 200);
        };

        item.appendChild(input);
        item.appendChild(deleteBtn);

        return item;
    }

    // 显示设置面板
    async function showSettings() {
        const panel = createSettingsPanel();
        panel.style.display = 'block';

        // 加载笔记本列表
        try {
            const notebooks = await getNotebooks();
            const notebookList = panel.querySelector('#notebook-list');
            const currentConfig = configManager.get();

            // 清空现有列表
            notebookList.innerHTML = '';

            // 添加笔记本选项
            notebooks.forEach(notebook => {
                const item = document.createElement('div');
                item.className = 'notebook-item';
                if (notebook.id === currentConfig.NOTEBOOK_ID) {
                    item.classList.add('selected');
                }
                item.dataset.id = notebook.id;
                item.dataset.name = notebook.name;
                item.textContent = notebook.name;
                notebookList.appendChild(item);
            });

            // 绑定点击事件
            notebookList.addEventListener('click', (e) => {
                const item = e.target.closest('.notebook-item');
                if (item) {
                    notebookList.querySelectorAll('.notebook-item').forEach(i => {
                        i.classList.remove('selected');
                    });
                    item.classList.add('selected');
                }
            });
        } catch (error) {
            panel.querySelector('#notebook-list').innerHTML = `
                <div class="notebook-item" style="color: red">
                    加载失败: ${error.message}
                </div>
            `;
        }
    }

    // 配置项
    function getConfig() {
        return configManager.get();
    }

    // 添加发送到思源的函数
    async function sendToSiYuan(content) {
        const config = getConfig();
        const data = {
            dataType: "markdown",
            data: content,
            parentID: config.TARGET_DOC_ID
        };

        try {
            const result = await new Promise((resolve, reject) => {
                GM_xmlhttpRequest({
                    method: 'POST',
                    url: `${config.API_ENDPOINT}/api/block/appendBlock`,
                    headers: {
                        'Authorization': `Token ${config.API_TOKEN}`,
                        'Content-Type': 'application/json'
                    },
                    data: JSON.stringify(data),
                    onload: function(response) {
                        if (response.status === 200) {
                            const result = JSON.parse(response.responseText);
                            if (result.code === 0) {
                                resolve(result);
                            } else {
                                reject(new Error(result.msg));
                            }
                        } else {
                            reject(new Error('请求失败'));
                        }
                    },
                    onerror: reject
                });
            });

            // 获取新创建的块ID
            const newBlockId = result.data[0].doOperations[0].id;

            // 设置自定义属性
            await setBlockAttrs({
                id: newBlockId,
                attrs: {
                    "custom-media": "timestamp"
                }
            });

            return result;
        } catch (error) {
            throw error;
        }
    }

    // 查找匹配的视频笔记
    async function findMatchingVideoNote(mediaUrl) {
        const config = getConfig();
        const sql = `SELECT block_id FROM attributes WHERE name = 'custom-type' AND value = 'MediaNote'
                     AND block_id IN (
                         SELECT block_id FROM attributes WHERE name = 'custom-mediaurl' AND value = '${mediaUrl}'
                     )`;

        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: 'POST',
                url: `${config.API_ENDPOINT}/api/query/sql`,
                headers: {
                    'Authorization': `Token ${config.API_TOKEN}`,
                    'Content-Type': 'application/json'
                },
                data: JSON.stringify({ stmt: sql }),
                onload: function(response) {
                    if (response.status === 200) {
                        const result = JSON.parse(response.responseText);
                        if (result.code === 0 && result.data.length > 0) {
                            resolve(result.data[0].block_id);
                        } else {
                            resolve(null);
                        }
                    } else {
                        reject(new Error('查询失败'));
                    }
                },
                onerror: reject
            });
        });
    }

    // 优化视频元素获取,支持不同网站的适配,包括全屏状态
    function getVideoElement() {
        // 检查当前网站
        const currentUrl = window.location.href;
        let videoElement = null;

        // 针对Bilibili的适配
        if (currentUrl.includes('bilibili.com')) {
            // 常规模式
            videoElement = document.querySelector('.bilibili-player-video video') ||
                          document.querySelector('.bpx-player-video-wrap video');

            // 全屏模式 - 多种检测方式
            if (!videoElement) {
                // 检查标准全屏
                if (document.fullscreenElement) {
                    videoElement = document.fullscreenElement.querySelector('video');
                }

                // 检查B站特有的全屏容器
                if (!videoElement) {
                    const bpxFullscreen = document.querySelector('.bpx-player-container[data-screen="full"]');
                    if (bpxFullscreen) {
                        videoElement = bpxFullscreen.querySelector('video');
                    }
                }

                // 检查网页全屏模式
                if (!videoElement) {
                    const webFullscreen = document.querySelector('.bilibili-player-video-web-fullscreen') ||
                                         document.querySelector('.bpx-player-container[data-screen="web"]');
                    if (webFullscreen) {
                        videoElement = webFullscreen.querySelector('video');
                    }
                }
            }
        }
        // 针对YouTube的适配
        else if (currentUrl.includes('youtube.com')) {
            // 常规模式
            videoElement = document.querySelector('.html5-main-video');

            // 全屏模式
            if (!videoElement && document.fullscreenElement) {
                videoElement = document.fullscreenElement.querySelector('video');
            }

            // 备用查找方法
            if (!videoElement) {
                const ytApp = document.querySelector('ytd-app');
                if (ytApp && ytApp.hasAttribute('is-fullscreen')) {
                    videoElement = document.querySelector('video');
                }
            }
        }
        // 针对百度网盘的适配
        else if (currentUrl.includes('pan.baidu.com')) {
            // 常规模式
            videoElement = document.querySelector('.vjs-tech') ||
                          document.querySelector('.video-player video');

            // 全屏模式
            if (!videoElement && document.fullscreenElement) {
                videoElement = document.fullscreenElement.querySelector('video');
            }

            // 百度网盘的内部全屏
            if (!videoElement) {
                const bdFullscreen = document.querySelector('.vp-fulled');
                if (bdFullscreen) {
                    videoElement = bdFullscreen.querySelector('video');
                }
            }
        }

        // 通用回退方案
        if (!videoElement) {
            videoElement = document.querySelector('video');
        }

        return videoElement;
    }

    // 修改获取时间戳功能
    async function getVideoTimestamp() {
        const video = getVideoElement();
        if (!video) {
            showNotification('未找到视频元素!');
            return;
        }

        const cleanedUrl = cleanUrl(window.location.href);
        const matchingNoteId = await findMatchingVideoNote(cleanedUrl);

        if (!matchingNoteId) {
            showNotification('请先创建视频笔记!');
            return;
        }

        // 更新配置中的目标文档ID
        const config = getConfig();
        configManager.save({
            ...config,
            TARGET_DOC_ID: matchingNoteId
        });

        const currentTime = video.currentTime;
        const timestamp = formatTime(currentTime);
        const timeUrl = generateTimeUrl(currentTime);
        const markdownLink = `[${timestamp}](${timeUrl})`;

        try {
            // 获取现有时间戳
            const existingTimestamps = await getExistingTimestamps(matchingNoteId);
            const existingTimestamp = existingTimestamps.find(ts => Math.abs(ts.time - currentTime) < 1);

            if (existingTimestamp) {
                showNotification('该时间戳已存在');
                return;
            }

            // 发送到思源
            const result = await new Promise((resolve, reject) => {
                GM_xmlhttpRequest({
                    method: 'POST',
                    url: `${config.API_ENDPOINT}/api/block/appendBlock`,
                    headers: {
                        'Authorization': `Token ${config.API_TOKEN}`,
                        'Content-Type': 'application/json'
                    },
                    data: JSON.stringify({
                        dataType: "markdown",
                        data: markdownLink,
                        parentID: matchingNoteId
                    }),
                    onload: function(response) {
                        if (response.status === 200) {
                            const result = JSON.parse(response.responseText);
                            if (result.code === 0) {
                                resolve(result);
                            } else {
                                reject(new Error(result.msg));
                            }
                        } else {
                            reject(new Error('请求失败'));
                        }
                    },
                    onerror: reject
                });
            });

            // 获取新创建的块ID
            const newBlockId = result.data[0].doOperations[0].id;

            // 设置自定义属性
            await setBlockAttrs({
                id: newBlockId,
                attrs: {
                    "custom-media": "timestamp"
                }
            });

            showNotification('已添加时间戳');
            await updateTimestampList();
        } catch (error) {
            showNotification('发送失败:' + error.message);
        }
    }

    // 添加文件上传函数
    async function uploadFile(blob, fileName) {
        const config = getConfig(); // 动态获取最新配置
        const formData = new FormData();
        formData.append('assetsDirPath', '/assets/');
        formData.append('file[]', blob, fileName);

        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: 'POST',
                url: `${config.API_ENDPOINT}/api/asset/upload`,
                headers: {
                    'Authorization': `Token ${config.API_TOKEN}`
                },
                data: formData,
                onload: function(response) {
                    if (response.status === 200) {
                        const result = JSON.parse(response.responseText);
                        if (result.code === 0) {
                            resolve(result.data.succMap[fileName]);
                        } else {
                            reject(new Error(result.msg));
                        }
                    } else {
                        reject(new Error('Upload failed'));
                    }
                },
                onerror: function(error) {
                    reject(error);
                }
            });
        });
    }

    // 添加一个检查截图块是否存在的辅助函数
    async function findScreenshotBlock(timestampBlockId) {
        const config = getConfig();
        const sql = `SELECT id FROM blocks
                     WHERE parent_id = '${timestampBlockId}'
                     AND id IN (
                         SELECT block_id FROM attributes
                         WHERE name = 'custom-media'
                         AND value = 'tsscreenshot'
                     )`;

        try {
            const result = await query(sql);
            return result.length > 0 ? result[0].id : null;
        } catch (error) {
            console.error('查询截图块失败:', error);
            return null;
        }
    }

    // 修改 isInSuperBlock 函数
    async function isInSuperBlock(blockId) {
        const config = getConfig();
        try {
            // 使用更简单的查询语句
            const sql = `
                WITH RECURSIVE parents AS (
                    SELECT id, parent_id, type
                    FROM blocks
                    WHERE id = '${blockId}'

                    UNION ALL

                    SELECT b.id, b.parent_id, b.type
                    FROM blocks b
                    JOIN parents p ON b.id = p.parent_id
                )
                SELECT p.id
                FROM parents p
                JOIN attributes a ON p.id = a.block_id
                WHERE p.type = 's'
                AND a.name = 'custom-media'
                AND a.value = 'mediacard'
                LIMIT 1
            `;

            const result = await query(sql);
            return result.length > 0;
        } catch (error) {
            console.error('检查超级块失败:', error);
            return false;
        }
    }

    // 添加一个获取父超级块ID的函数,修复SQL语法错误
    async function getParentSuperBlockId(blockId) {
        const config = getConfig();
        try {
            // 修复SQL语法,避免ON关键字错误
            const sql = `
                SELECT parent.id
                FROM blocks child, blocks parent, attributes attrs
                WHERE child.id = '${blockId}'
                AND child.parent_id = parent.id
                AND parent.id = attrs.block_id
                AND attrs.name = 'custom-media'
                AND attrs.value = 'mediacard'
                LIMIT 1
            `;

            const result = await query(sql);
            return result.length > 0 ? result[0].id : null;
        } catch (error) {
            console.error('获取父超级块ID失败:', error);
            return null;
        }
    }

    // 修改 getParentMediaCard 函数以解决循环引用问题
    async function getParentMediaCard(blockId) {
        const config = getConfig();
        try {
            const sql = `
                WITH RECURSIVE parents(id, parent_id, level, path) AS (
                    -- 基础查询:获取起始块
                    SELECT
                        b.id,
                        b.parent_id,
                        0 as level,
                        b.id as path
                    FROM blocks b
                    WHERE b.id = '${blockId}'

                    UNION ALL

                    -- 递归查询:获取父块
                    SELECT
                        b.id,
                        b.parent_id,
                        p.level + 1,
                        p.path || ',' || b.id
                    FROM blocks b
                    JOIN parents p ON b.id = p.parent_id
                    WHERE p.level < 10  -- 限制递归深度
                    AND p.path NOT LIKE '%' || b.id || '%'  -- 防止循环
                )
                SELECT DISTINCT p.id
                FROM parents p
                JOIN attributes a ON p.id = a.block_id
                WHERE a.name = 'custom-media'
                AND a.value = 'mediacard'
                ORDER BY p.level ASC
                LIMIT 1
            `;

            const result = await query(sql);
            return result.length > 0 ? result[0].id : null;
        } catch (error) {
            console.error('检查父块mediacard属性失败:', error);
            return null;
        }
    }

    // 修改 createMemoBlock 函数
    async function createMemoBlock(timestampBlockId, content) {
        const config = getConfig();

        try {
            // 1. 获取或创建超级块
            const superBlockId = await createOrGetSuperBlock(timestampBlockId);

            // 2. 创建备注块
            const memoResult = await new Promise((resolve, reject) => {
                GM_xmlhttpRequest({
                    method: 'POST',
                    url: `${config.API_ENDPOINT}/api/block/appendBlock`,
                    headers: {
                        'Authorization': `Token ${config.API_TOKEN}`,
                        'Content-Type': 'application/json'
                    },
                    data: JSON.stringify({
                        dataType: "markdown",
                        data: content,
                        parentID: superBlockId
                    }),
                    onload: async function(response) {
                        if (response.status === 200) {
                            const result = JSON.parse(response.responseText);
                            if (result.code === 0 && result.data && result.data.length > 0 &&
                                result.data[0].doOperations && result.data[0].doOperations.length > 0) {
                                const newBlockId = result.data[0].doOperations[0].id;
                                try {
                                    await setBlockAttrs({
                                        id: newBlockId,
                                        attrs: {
                                            "custom-media": "memos"
                                        }
                                    });
                                    resolve(newBlockId);
                                } catch (error) {
                                    reject(error);
                                }
                            } else {
                                console.error('API返回结果异常:', result);
                                reject(new Error(result.msg || '返回数据结构异常'));
                            }
                        } else {
                            reject(new Error('创建备注块失败'));
                        }
                    },
                    onerror: reject
                });
            });

            // 3. 清理临时块
            await removeTemporaryBlocks();

            return memoResult;
        } catch (error) {
            console.error('创建备注块失败:', error);
            throw error;
        }
    }

    // 修改 getVideoScreenshot 函数
    async function getVideoScreenshot() {
        const video = getVideoElement();
        if (!video) {
            showNotification('未找到视频元素!');
            return;
        }

        const cleanedUrl = cleanUrl(window.location.href);
        const matchingNoteId = await findMatchingVideoNote(cleanedUrl);

        if (!matchingNoteId) {
            showNotification('请先创建视频笔记!');
            return;
        }

        // 更新配置中的目标文档ID
        const config = getConfig();
        configManager.save({
            ...config,
            TARGET_DOC_ID: matchingNoteId
        });

        const currentTime = video.currentTime;
        const timestamp = formatTime(currentTime);
        const timeUrl = generateTimeUrl(currentTime);
        const markdownLink = `[${timestamp}](${timeUrl})`;

        try {
            // 获取现有时间戳
            const existingTimestamps = await getExistingTimestamps(matchingNoteId);
            const existingTimestamp = existingTimestamps.find(ts => Math.abs(ts.time - currentTime) < 1);

            // 创建截图相关数据
            const canvas = document.createElement('canvas');
            canvas.width = video.videoWidth;
            canvas.height = video.videoHeight;
            const ctx = canvas.getContext('2d');
            ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
            const blob = await new Promise(resolve => {
                canvas.toBlob(resolve, 'image/png');
            });
            const fileName = `screenshot-${Date.now()}.png`;
            const filePath = await uploadFile(blob, fileName);

            if (existingTimestamp) {
                try {
                    // 查找时间戳块对应的截图块
                    const timestampBlockId = await findTimestampBlockId(timeUrl, matchingNoteId);
                    if (!timestampBlockId) {
                        showNotification('时间戳块查找失败');
                        return;
                    }

                    // 检查时间戳块是否已在超级块内
                    const isInSuper = await isInSuperBlock(timestampBlockId);
                    let superBlockId;

                    if (isInSuper) {
                        // 如果已在超级块内,获取超级块ID
                        superBlockId = await getParentSuperBlockId(timestampBlockId);
                    } else {
                        // 如果不在超级块内,创建新的超级块
                        superBlockId = await new Promise((resolve, reject) => {
                            GM_xmlhttpRequest({
                                method: 'POST',
                                url: `${config.API_ENDPOINT}/api/block/insertBlock`,
                                headers: {
                                    'Authorization': `Token ${config.API_TOKEN}`,
                                    'Content-Type': 'application/json'
                                },
                                data: JSON.stringify({
                                    dataType: "markdown",
                                    data: `{{{row
                                        内容\n{: custom-media="temp" }\n
                                        }}}\n{: custom-media="mediacard" }\n`,
                                    previousID: timestampBlockId
                                }),
                                onload: function(response) {
                                    if (response.status === 200) {
                                        const result = JSON.parse(response.responseText);
                                        if (result.code === 0) {
                                            resolve(result.data[0].doOperations[0].id);
                                        } else {
                                            reject(new Error(result.msg));
                                        }
                                    } else {
                                        reject(new Error('创建超级块失败'));
                                    }
                                },
                                onerror: reject
                            });
                        });

                        // 移动时间戳块到超级块内
                        await new Promise((resolve, reject) => {
                            GM_xmlhttpRequest({
                                method: 'POST',
                                url: `${config.API_ENDPOINT}/api/block/moveBlock`,
                                headers: {
                                    'Authorization': `Token ${config.API_TOKEN}`,
                                    'Content-Type': 'application/json'
                                },
                                data: JSON.stringify({
                                    id: timestampBlockId,
                                    parentID: superBlockId
                                }),
                                onload: function(response) {
                                    if (response.status === 200) {
                                        const result = JSON.parse(response.responseText);
                                        if (result.code === 0) {
                                            resolve();
                                        } else {
                                            reject(new Error(result.msg));
                                        }
                                    } else {
                                        reject(new Error('移动时间戳块失败'));
                                    }
                                },
                                onerror: reject
                            });
                        });
                    }

                    // 添加截图块到超级块
                    const screenshotResult = await new Promise((resolve, reject) => {
                        GM_xmlhttpRequest({
                            method: 'POST',
                            url: `${config.API_ENDPOINT}/api/block/appendBlock`,
                            headers: {
                                'Authorization': `Token ${config.API_TOKEN}`,
                                'Content-Type': 'application/json'
                            },
                            data: JSON.stringify({
                                dataType: "markdown",
                                data: `![${timestamp}](${filePath})`,
                                parentID: superBlockId
                            }),
                            onload: function(response) {
                                if (response.status === 200) {
                                    const result = JSON.parse(response.responseText);
                                    if (result.code === 0) {
                                        resolve(result.data[0].doOperations[0].id);
                                    } else {
                                        reject(new Error(result.msg));
                                    }
                                } else {
                                    reject(new Error('添加截图块失败'));
                                }
                            },
                            onerror: reject
                        });
                    });

                    // 为截图块设置属性
                    await setBlockAttrs({
                        id: screenshotResult,
                        attrs: {
                            "custom-media": "tsscreenshot"
                        }
                    });

                    // 设置超级块属性
                    await setBlockAttrs({
                        id: superBlockId,
                        attrs: {
                            "layout": "row"
                        }
                    });

                    showNotification('已为现有时间戳添加截图');
                } catch (error) {
                    console.error('处理已有时间戳失败:', error);
                    showNotification('处理已有时间戳失败: ' + error.message);
                    return;
                }
            } else {
                // 创建新的超级块
                const superBlockResult = await new Promise((resolve, reject) => {
                    GM_xmlhttpRequest({
                        method: 'POST',
                        url: `${config.API_ENDPOINT}/api/block/appendBlock`,
                        headers: {
                            'Authorization': `Token ${config.API_TOKEN}`,
                            'Content-Type': 'application/json'
                        },
                        data: JSON.stringify({
                            dataType: "markdown",
                            data: `{{{row
                                内容\n{: custom-media="temp" }\n
                                }}}\n{: custom-media="mediacard" }\n`,
                            parentID: matchingNoteId
                        }),
                        onload: function(response) {
                            if (response.status === 200) {
                                const result = JSON.parse(response.responseText);
                                if (result.code === 0) {
                                    resolve(result.data[0].doOperations[0].id);
                                } else {
                                    reject(new Error(result.msg));
                                }
                            } else {
                                reject(new Error('创建超级块失败'));
                            }
                        },
                        onerror: reject
                    });
                });

                // 添加时间戳块
                const timestampResult = await new Promise((resolve, reject) => {
                    GM_xmlhttpRequest({
                        method: 'POST',
                        url: `${config.API_ENDPOINT}/api/block/appendBlock`,
                        headers: {
                            'Authorization': `Token ${config.API_TOKEN}`,
                            'Content-Type': 'application/json'
                        },
                        data: JSON.stringify({
                            dataType: "markdown",
                            data: markdownLink,
                            parentID: superBlockResult
                        }),
                        onload: async function(response) {
                            if (response.status === 200) {
                                const result = JSON.parse(response.responseText);
                                if (result.code === 0) {
                                    const newBlockId = result.data[0].doOperations[0].id;
                                    try {
                                        // 为时间戳块设置属性
                                        await setBlockAttrs({
                                            id: newBlockId,
                                            attrs: {
                                                "custom-media": "timestamp"
                                            }
                                        });
                                        resolve(newBlockId);
                                    } catch (error) {
                                        reject(error);
                                    }
                                } else {
                                    reject(new Error(result.msg));
                                }
                            } else {
                                reject(new Error('添加时间戳块失败'));
                            }
                        },
                        onerror: reject
                    });
                });

                // 添加截图块
                const screenshotResult = await new Promise((resolve, reject) => {
                    GM_xmlhttpRequest({
                        method: 'POST',
                        url: `${config.API_ENDPOINT}/api/block/appendBlock`,
                        headers: {
                            'Authorization': `Token ${config.API_TOKEN}`,
                            'Content-Type': 'application/json'
                        },
                        data: JSON.stringify({
                            dataType: "markdown",
                            data: `![${timestamp}](${filePath})`,
                            parentID: superBlockResult
                        }),
                        onload: function(response) {
                            if (response.status === 200) {
                                const result = JSON.parse(response.responseText);
                                if (result.code === 0) {
                                    resolve(result.data[0].doOperations[0].id);
                                } else {
                                    reject(new Error(result.msg));
                                }
                            } else {
                                reject(new Error('添加截图块失败'));
                            }
                        },
                        onerror: reject
                    });
                });

                // 为截图块设置属性
                await setBlockAttrs({
                    id: screenshotResult,
                    attrs: {
                        "custom-media": "tsscreenshot"
                    }
                });

                // 设置超级块属性
                await setBlockAttrs({
                    id: superBlockResult,
                    attrs: {
                        "layout": "row"
                    }
                });

                showNotification('已添加时间戳和截图');
            }

            // 清理临时块
            await removeTemporaryBlocks();

            await updateTimestampList();
        } catch (error) {
            showNotification('发送失败:' + error.message);
            console.error(error);
        }
    }

    // 生成带时间戳的URL
    function generateTimeUrl(seconds) {
        const currentUrl = window.location.href;
        const timeParam = Math.floor(seconds);

        if (currentUrl.includes('youtube.com')) {
            const urlObj = new URL(currentUrl);
            const videoId = urlObj.searchParams.get('v');
            return `https://youtu.be/${videoId}?t=${timeParam}`;
        } else if (currentUrl.includes('bilibili.com')) {
            const urlObj = new URL(currentUrl);
            const bvidMatch = urlObj.pathname.match(/\/video\/(BV[a-zA-Z0-9]+)/);
            if (bvidMatch) {
                const bvid = bvidMatch[1];
                return `https://www.bilibili.com/video/${bvid}?t=${timeParam}`;
            }
        } else if (currentUrl.includes('pan.baidu.com')) {
            // 保留所有必要的查询参数,只修改时间戳
            const urlObj = new URL(currentUrl);
            const path = urlObj.searchParams.get('path');
            let baseUrl = `https://pan.baidu.com/pfile/video?path=${encodeURIComponent(path)}`;
            // 添加其他可能需要的查询参数
            if (urlObj.searchParams.get('theme')) {
                baseUrl += `&theme=${urlObj.searchParams.get('theme')}`;
            }
            return `${baseUrl}#t=${timeParam}`;
        }
        // 默认格式
        const baseUrl = currentUrl.split('#')[0];
        return `${baseUrl}#t=${timeParam}`;
    }

    // 格式化时间
    function formatTime(seconds) {
        const hours = Math.floor(seconds / 3600);
        const minutes = Math.floor((seconds % 3600) / 60);
        const secs = Math.floor(seconds % 60);
        const ms = Math.floor((seconds % 1) * 1000);

        return `${padZero(hours)}:${padZero(minutes)}:${padZero(secs)}.${padZero(ms, 3)}`;
    }

    // 补零函数
    function padZero(num, length = 2) {
        return String(num).padStart(length, '0');
    }

    // 复制到剪贴板
    function copyToClipboard(text) {
        const textarea = document.createElement('textarea');
        textarea.value = text;
        document.body.appendChild(textarea);
        textarea.select();
        document.execCommand('copy');
        document.body.removeChild(textarea);
    }

    // 添加通知函数
    function showNotification(message, duration = 3000) {
        const toast = document.createElement('div');
        toast.className = 'toast-notification';
        toast.textContent = message;
        document.body.appendChild(toast);

        setTimeout(() => {
            toast.style.animation = 'slideIn 0.3s ease-out reverse';
            setTimeout(() => toast.remove(), 300);
        }, duration);
    }

    // 清理URL参数
    function cleanUrl(url) {
        const urlObj = new URL(url);
        if (urlObj.hostname.includes('bilibili.com')) {
            const bvidMatch = urlObj.pathname.match(/\/video\/(BV[a-zA-Z0-9]+)/);
            if (bvidMatch) {
                return `https://www.bilibili.com/video/${bvidMatch[1]}`;
            }
        } else if (urlObj.hostname.includes('youtube.com')) {
            const videoId = urlObj.searchParams.get('v');
            if (videoId) {
                return `https://www.youtube.com/watch?v=${videoId}`;
            }
        } else if (urlObj.hostname.includes('pan.baidu.com')) {
            // 保留百度网盘视频的必要参数
            const path = urlObj.searchParams.get('path');
            if (path) {
                return `https://pan.baidu.com/pfile/video?path=${encodeURIComponent(path)}`;
            }
        }
        return url.split('#')[0];  // 移除hash部分但保留其他查询参数
    }

    // 添加为现有块添加属性的函数
    async function addAttributesToExistingBlocks(docId) {
        const config = getConfig();
        try {
            // 获取文档下的所有块
            const sql = `SELECT * FROM blocks WHERE root_id = '${docId}' AND type = 'p' ORDER BY created`;
            const blocks = await new Promise((resolve, reject) => {
                GM_xmlhttpRequest({
                    method: 'POST',
                    url: `${config.API_ENDPOINT}/api/query/sql`,
                    headers: {
                        'Authorization': `Token ${config.API_TOKEN}`,
                        'Content-Type': 'application/json'
                    },
                    data: JSON.stringify({ stmt: sql }),
                    onload: function(response) {
                        if (response.status === 200) {
                            const result = JSON.parse(response.responseText);
                            if (result.code === 0) {
                                resolve(result.data);
                            } else {
                                reject(new Error(result.msg));
                            }
                        } else {
                            reject(new Error('查询失败'));
                        }
                    },
                    onerror: reject
                });
            });

            // 遍历所有块
            for (let i = 0; i < blocks.length; i++) {
                const block = blocks[i];
                const content = block.content;

                // 检查是否是时间戳块
                if (content.match(/\[\d{2}:\d{2}:\d{2}\.\d{3}\]/)) {
                    await setBlockAttrs({
                        id: block.id,
                        attrs: {
                            "custom-media": "timestamp"
                        }
                    });

                    // 检查下一个块是否是对应的截图
                    if (i + 1 < blocks.length && blocks[i + 1].content.startsWith('![')) {
                        await setBlockAttrs({
                            id: blocks[i + 1].id,
                            attrs: {
                                "custom-media": "tsScreenShot"
                            }
                        });
                    }
                }
            }
        } catch (error) {
            console.error('添加属性失败:', error);
            throw error;
        }
    }

    // 修改 createVideoNote 函数中获取标题的部分
    async function createVideoNote() {
        try {
            const config = getConfig();
            const cleanedUrl = cleanUrl(window.location.href);

            // 检查当前网址是否在匹配列表中
            const isUrlMatched = config.MATCH_LIST.some(pattern => cleanedUrl.includes(pattern));
            if (!isUrlMatched) {
                showNotification('当前网址不在匹配列表中');
                return;
            }

            // 检查是否已存在对应笔记
            const matchingNoteId = await findMatchingVideoNote(cleanedUrl);
            if (matchingNoteId) {
                try {
                    await addAttributesToExistingBlocks(matchingNoteId);
                    showNotification('已为现有时间戳添加属性');
                } catch (error) {
                    handleError(error, '添加属性');
                }
                return;
            }

            // 针对百度网盘特殊处理获取标题
            let title;
            if (cleanedUrl.includes('pan.baidu.com')) {
                // 尝试从工具栏标题获取
                const titleElement = document.querySelector('.vp-toolsbar__title');
                if (titleElement) {
                    title = titleElement.getAttribute('title') || titleElement.textContent;
                }
                // 如果还是获取不到,尝试从URL中获取
                if (!title) {
                    const urlParams = new URLSearchParams(window.location.search);
                    const path = urlParams.get('path');
                    if (path) {
                        title = decodeURIComponent(path.split('/').pop());
                    }
                }
            } else {
                title = document.title;
            }

            // 如果还是没有标题,使用默认标题
            if (!title) {
                title = '未命名视频笔记';
            }

            // 创建一个安全的文件路径(只保留基本字符)
            const safePath = title.replace(/[^\w\s\u4e00-\u9fa5]/g, '_');

            // 创建笔记内容(保留原始标题)
            const content = `# ${title}\n\n> 视频链接:[${title}](${cleanedUrl})`;

            // 使用 retryApiCall 包装 API 调用
            const response = await retryApiCall(async () => {
                // 创建文档
                const docData = {
                    notebook: config.NOTEBOOK_ID,
                    path: `/视频笔记/${safePath}`,
                    markdown: content
                };

                return new Promise((resolve, reject) => {
                    GM_xmlhttpRequest({
                        method: 'POST',
                        url: `${config.API_ENDPOINT}/api/filetree/createDocWithMd`,
                        headers: {
                            'Authorization': `Token ${config.API_TOKEN}`,
                            'Content-Type': 'application/json'
                        },
                        data: JSON.stringify(docData),
                        onload: function(response) {
                            if (response.status === 200) {
                                const result = JSON.parse(response.responseText);
                                if (result.code === 0) {
                                    resolve(result.data);
                                } else {
                                    reject(new Error(result.msg));
                                }
                            } else {
                                reject(new Error('请求失败'));
                            }
                        },
                        onerror: reject
                    });
                });
            });

            // 设置文档属性
            await retryApiCall(async () => {
                const attrs = {
                    id: response,
                    attrs: {
                        "custom-type": "MediaNote",
                        "custom-mediaurl": cleanedUrl
                    }
                };

                return new Promise((resolve, reject) => {
                    GM_xmlhttpRequest({
                        method: 'POST',
                        url: `${config.API_ENDPOINT}/api/attr/setBlockAttrs`,
                        headers: {
                            'Authorization': `Token ${config.API_TOKEN}`,
                            'Content-Type': 'application/json'
                        },
                        data: JSON.stringify(attrs),
                        onload: function(response) {
                            if (response.status === 200) {
                                const result = JSON.parse(response.responseText);
                                if (result.code === 0) {
                                    resolve();
                                } else {
                                    reject(new Error(result.msg));
                                }
                            } else {
                                reject(new Error('请求失败'));
                            }
                        },
                        onerror: reject
                    });
                });
            });

            // 更新配置中的目标文档ID
            configManager.save({
                ...config,
                TARGET_DOC_ID: response
            });

            showNotification('视频笔记创建成功');

            // 更新时间戳列表
            await debouncedUpdateTimestampList();

            // 更新创建按钮状态
            await updateCreateNoteButtonState();

        } catch (error) {
            handleError(error, '创建视频笔记');
        }
    }

    // 修改更新按钮状态的函数
    async function updateCreateNoteButtonState() {
        const createNoteBtn = document.querySelector('.timestamp-header-btn[title="创建视频笔记"]');
        if (!createNoteBtn) return;

        if (!getVideoElement()) {
            createNoteBtn.disabled = true;
            createNoteBtn.style.opacity = '0.5';
            createNoteBtn.title = '未找到视频';
            return;
        }

        try {
            const cleanedUrl = cleanUrl(window.location.href);
            const matchingNoteId = await findMatchingVideoNote(cleanedUrl);

            if (matchingNoteId) {
                createNoteBtn.disabled = true;
                createNoteBtn.style.opacity = '0.5';
                createNoteBtn.title = '已存在对应笔记';
            } else {
                createNoteBtn.disabled = false;
                createNoteBtn.style.opacity = '1';
                createNoteBtn.title = '创建视频笔记';
            }
        } catch (error) {
            console.error('检查现有笔记失败:', error);
            createNoteBtn.disabled = true;
            createNoteBtn.style.opacity = '0.5';
            createNoteBtn.title = '检查失败';
        }
    }

    // 初始检查按钮状态
    setTimeout(updateCreateNoteButtonState, 1000);

    // 定期检查更新按钮状态(每30秒)
    setInterval(updateCreateNoteButtonState, 30000);

    // 当URL变化时更新按钮状态
    let lastUrl = window.location.href;
    new MutationObserver(() => {
        if (lastUrl !== window.location.href) {
            lastUrl = window.location.href;
            setTimeout(updateCreateNoteButtonState, 1000);
        }
    }).observe(document, {subtree: true, childList: true});

    // 修改 getExistingTimestamps 函数,添加获取备注内容的功能
    async function getExistingTimestamps(docId) {
        const config = getConfig();

        try {
            // 获取文档块的 kramdown 源码
            const kramdownData = await new Promise((resolve, reject) => {
                GM_xmlhttpRequest({
                    method: 'POST',
                    url: `${config.API_ENDPOINT}/api/block/getBlockKramdown`,
                    headers: {
                        'Authorization': `Token ${config.API_TOKEN}`,
                        'Content-Type': 'application/json'
                    },
                    data: JSON.stringify({ id: docId }),
                    onload: function(response) {
                        if (response.status === 200) {
                            const result = JSON.parse(response.responseText);
                            if (result.code === 0) {
                                resolve(result.data);
                            } else {
                                reject(new Error(result.msg));
                            }
                        } else {
                            reject(new Error('请求失败'));
                        }
                    },
                    onerror: reject
                });
            });

            // 使用 parseTimestampLinks 解析时间戳
            const timestamps = parseTimestampLinks(kramdownData.kramdown);

            // 为每个时间戳获取对应的备注内容
            for (const ts of timestamps) {
                try {
                    // 获取时间戳块ID
                    const timestampBlockId = await findTimestampBlockId(ts.url, docId);
                    if (timestampBlockId) {
                        // 获取备注块内容,添加空值检查
                        const memoBlocks = await findAllMemoBlocks(timestampBlockId);
                        if (memoBlocks && memoBlocks.length > 0) {
                            // 获取所有备注块的内容
                            const memoContents = await Promise.all(memoBlocks.map(async (blockId) => {
                                try {
                                    const blockData = await getBlockKramdown(blockId);
                                    if (blockData && blockData.kramdown) {
                                        return blockData.kramdown.replace(/\{:[^\}]+\}/g, '').trim();
                                    }
                                    return '';
                                } catch (error) {
                                    console.error('获取备注块内容失败:', error);
                                    return '';
                                }
                            }));
                            // 过滤掉空字符串并合并
                            ts.note = memoContents.filter(content => content).join('\n');
                        } else {
                            ts.note = ''; // 如果没有备注块,设置为空字符串
                        }
                    }
                } catch (error) {
                    console.error('获取备注失败:', error);
                    ts.note = ''; // 发生错误时设置为空字符串
                }
            }

            // 按时间排序
            return timestamps.sort((a, b) => a.time - b.time);

        } catch (error) {
            console.error('获取时间戳失败:', error);
            throw error;
        }
    }

    // 添加获取块内容的辅助函数
    async function getBlockKramdown(blockId) {
        const config = getConfig();
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: 'POST',
                url: `${config.API_ENDPOINT}/api/block/getBlockKramdown`,
                headers: {
                    'Authorization': `Token ${config.API_TOKEN}`,
                    'Content-Type': 'application/json'
                },
                data: JSON.stringify({ id: blockId }),
                onload: function(response) {
                    if (response.status === 200) {
                        const result = JSON.parse(response.responseText);
                        if (result.code === 0) {
                            resolve(result.data);
                        } else {
                            reject(new Error(result.msg));
                        }
                    } else {
                        reject(new Error('获取块内容失败'));
                    }
                },
                onerror: reject
            });
        });
    }

    // 修改 findAllMemoBlocks 函数,添加空值检查
    async function findAllMemoBlocks(timestampBlockId) {
        const sql = `WITH RECURSIVE parents AS (
            SELECT id, parent_id, type
            FROM blocks
            WHERE id = '${timestampBlockId}'

            UNION ALL

            SELECT b.id, b.parent_id, b.type
            FROM blocks b
            JOIN parents p ON b.id = p.parent_id
            WHERE b.type = 's'
        )
        SELECT b.id
        FROM blocks b
        JOIN attributes a ON b.id = a.block_id
        WHERE b.parent_id IN (SELECT id FROM parents)
        AND a.name = 'custom-media'
        AND a.value = 'memos'
        ORDER BY b.created`;

        try {
            const result = await query(sql);
            // 添加空值检查
            if (!result) {
                return [];
            }
            return result.map(row => row.id || '').filter(id => id !== '');
        } catch (error) {
            console.error('查询备注块失败:', error);
            return [];
        }
    }

    // 从test.js中提取的辅助函数
    function parseTimestampLinks(kramdown) {
        const timestamps = [];
        // 按块分割内容
        const blocks = kramdown.split(/\n\s*\n/);

        blocks.forEach(block => {
            // 清理块属性标记
            const cleanBlock = block.replace(/\{:[^\}]+\}/g, '').trim();
            if (!cleanBlock) return;

            // 使用更严格的正则表达式匹配链接
            const regex = /(?<!\\)\[([^\]]+?)\]\(([^)]+?)\)/g;
            let match;

            while ((match = regex.exec(cleanBlock)) !== null) {
                const [fullMatch, text, href] = match;

                // 验证链接格式
                if (!isValidTimestampLink(href)) continue;

                try {
                    const timeValue = extractTime(href);
                    if (timeValue === null) continue;

                    // 清理和格式化文本
                    const cleanText = cleanTimestampText(text);

                    timestamps.push({
                        text: cleanText,
                        url: normalizeUrl(href),
                        time: timeValue,
                        originalText: text,
                        note: '' // 初始化为空字符串,后续会从备注块中获取
                    });
                } catch (e) {
                    console.warn('解析时间戳失败:', e, {text, href});
                }
            }
        });

        return timestamps;
    }

    // 修改 parseTimestampLinks 函数中的 isValidTimestampLink 函数
    function isValidTimestampLink(href) {
        // 检查是否是视频网站链接
        const validDomains = [
            'youtube.com', 'youtu.be',
            'bilibili.com', 'b23.tv',
            'pan.baidu.com'  // 添加百度网盘域名
        ];

        try {
            const url = new URL(href);
            const isDomainValid = validDomains.some(domain => url.hostname.includes(domain));
            // 修改时间戳检查逻辑,支持百度网盘的 #t= 格式
            const hasTimestamp = href.includes('?t=') || href.includes('&t=') || href.includes('#t=');

            return isDomainValid && hasTimestamp;
        } catch (e) {
            return false;
        }
    }

    // 修改 extractTime 函数
    function extractTime(url) {
        try {
            const urlObj = new URL(url);

            // 尝试从不同位置获取时间参数
            let timeStr = null;

            // 检查查询参数
            timeStr = urlObj.searchParams.get('t');

            // 检查哈希参数
            if (!timeStr && urlObj.hash) {
                const hashMatch = urlObj.hash.match(/[?&]t=(\d+)/);
                if (hashMatch) {
                    timeStr = hashMatch[1];
                } else {
                    // 处理 #t=xxx 格式
                    const simpleHashMatch = urlObj.hash.match(/#t=(\d+)/);
                    if (simpleHashMatch) {
                        timeStr = simpleHashMatch[1];
                    }
                }
            }

            // 处理时间格式
            if (timeStr) {
                // 处理 HH:MM:SS 格式
                if (timeStr.includes(':')) {
                    const parts = timeStr.split(':').map(Number);
                    let seconds = 0;
                    if (parts.length === 3) { // HH:MM:SS
                        seconds = parts[0] * 3600 + parts[1] * 60 + parts[2];
                    } else if (parts.length === 2) { // MM:SS
                        seconds = parts[0] * 60 + parts[1];
                    }
                    return seconds;
                }

                // 处理纯数字格式
                return parseInt(timeStr, 10);
            }

            return null;
        } catch (e) {
            console.warn('解析时间戳失败:', e, url);
            return null;
        }
    }

    // 修改 normalizeUrl 函数
    function normalizeUrl(url) {
        try {
            const urlObj = new URL(url);

            // 处理YouTube链接
            if (urlObj.hostname.includes('youtube.com') || urlObj.hostname.includes('youtu.be')) {
                const videoId = urlObj.searchParams.get('v') || urlObj.pathname.split('/').pop();
                const timestamp = extractTime(url);
                return `https://youtu.be/${videoId}?t=${timestamp}`;
            }

            // 处理Bilibili链接
            if (urlObj.hostname.includes('bilibili.com')) {
                const bvid = url.match(/BV[\w]+/)?.[0];
                const timestamp = extractTime(url);
                if (bvid) {
                    return `https://www.bilibili.com/video/${bvid}?t=${timestamp}`;
                }
            }

            // 百度网盘处理
            if (urlObj.hostname.includes('pan.baidu.com')) {
                const timestamp = extractTime(url);
                const path = urlObj.searchParams.get('path');
                let baseUrl = `https://pan.baidu.com/pfile/video?path=${encodeURIComponent(path)}`;
                // 添加其他可能需要的查询参数
                if (urlObj.searchParams.get('theme')) {
                    baseUrl += `&theme=${urlObj.searchParams.get('theme')}`;
                }
                return `${baseUrl}#t=${timestamp}`;
            }

            return url;
        } catch (e) {
            return url;
        }
    }

    function cleanTimestampText(text) {
        // 移除多余空格和特殊字符
        return text.trim()
                  .replace(/\s+/g, ' ')
                  .replace(/[\u200B-\u200D\uFEFF]/g, ''); // 移除零宽字符
    }

    // 修改更新时间戳列表的函数
    async function updateTimestampList() {
        // 如果正在编辑,跳过更新
        if (document.querySelector('.timestamp-note-input:focus')) {
            return;
        }

        const video = getVideoElement();
        if (!video) return;

        const cleanedUrl = cleanUrl(window.location.href);
        const matchingNoteId = await findMatchingVideoNote(cleanedUrl);
        const list = document.getElementById('timestamp-list');

        if (!list) return;

        if (!matchingNoteId) {
            const noTimestamps = document.createElement('div');
            noTimestamps.className = 'no-timestamps';
            noTimestamps.textContent = '请先创建视频笔记';
            list.replaceChildren(noTimestamps);
            return;
        }

        try {
            const timestamps = await getExistingTimestamps(matchingNoteId);

            if (timestamps.length === 0) {
                const noTimestamps = document.createElement('div');
                noTimestamps.className = 'no-timestamps';
                noTimestamps.textContent = '暂无时间戳记录';
                list.replaceChildren(noTimestamps);
                return;
            }

            // 清空现有列表
            list.replaceChildren();

            // 添加新的时间戳项
            timestamps.forEach(ts => {
                const item = document.createElement('div');
                item.className = 'timestamp-item';
                item.dataset.time = ts.time;

                // 创建左侧容器用于时间戳和备注
                const leftContainer = document.createElement('div');
                leftContainer.className = 'timestamp-left';

                // 创建时间戳文本元素
                const timestampText = document.createElement('div');
                timestampText.className = 'timestamp-text';
                timestampText.textContent = ts.text;

                // 创建时间链接
                const timeLink = document.createElement('div');
                timeLink.className = 'timestamp-time';
                timeLink.textContent = formatTime(ts.time);

                // 创建时间戳标题行,包含时间戳文本和时间
                const timestampHeader = document.createElement('div');
                timestampHeader.className = 'timestamp-header';
                timestampHeader.appendChild(timestampText);
                timestampHeader.appendChild(timeLink);

                leftContainer.appendChild(timestampHeader);

                // 创建备注容器
                const noteContainer = document.createElement('div');
                noteContainer.className = 'timestamp-note-container';

                // 创建备注文本/输入框
                const noteInput = document.createElement('textarea');
                noteInput.className = 'timestamp-note-input';
                noteInput.value = ts.note || '';
                noteInput.placeholder = '添加备注... (Enter 换行, Shift+Enter 发送)';
                noteInput.rows = 1;

                // 添加自动调整高度的函数
                function adjustHeight(element) {
                    element.style.height = 'auto';
                    element.style.height = (element.scrollHeight) + 'px';
                }

                // 监听输入事件来自动调整高度
                noteInput.addEventListener('input', () => {
                    adjustHeight(noteInput);
                });

                // 在初始化和值改变时调整高度
                noteInput.addEventListener('focus', () => {
                    adjustHeight(noteInput);
                });

                // 添加一个小延时来确保初始高度正确设置
                setTimeout(() => {
                    adjustHeight(noteInput);
                }, 0);

                // 修改按键事件处理,交换Enter和Shift+Enter的功能
                noteInput.addEventListener('keydown', async (e) => {
                    if (e.key === 'Enter') {
                        if (e.shiftKey) {
                            // Shift+Enter: 保存并失去焦点
                            e.preventDefault();
                            noteInput.blur();
                        } else {
                            // 普通Enter: 添加换行
                            return; // 允许默认的换行行为
                        }
                    }
                });

                // 修改失去焦点事件处理
                noteInput.addEventListener('blur', async () => {
                    // 分割并过滤空行,确保每行都是有效的备注内容
                    const newNotes = noteInput.value
                        .split('\n')
                        .map(note => note.trim())
                        .filter(note => note !== '');

                    try {
                        // 获取时间戳块的ID
                        const timestampBlockId = await findTimestampBlockId(ts.url, matchingNoteId);
                        if (!timestampBlockId) {
                            throw new Error('未找到对应的时间戳块');
                        }

                        // 更新备注块,直接传入数组
                        await updateMemoBlocks(timestampBlockId, newNotes);

                        showNotification('备注已更新');
                        ts.note = newNotes.join('\n');
                    } catch (error) {
                        showNotification('更新备注失败:' + error.message);
                        noteInput.value = ts.note || '';
                    }
                });

                noteContainer.appendChild(noteInput);
                leftContainer.appendChild(noteContainer);

                item.appendChild(leftContainer);

                // 添加点击事件跳转视频
                timestampHeader.addEventListener('click', () => {
                    if (video) {
                        video.currentTime = ts.time;
                        list.querySelectorAll('.timestamp-item').forEach(i => i.classList.remove('active'));
                        item.classList.add('active');
                    }
                });

                list.appendChild(item);
            });

        } catch (error) {
            console.error('获取时间戳失败:', error);
            showNotification('获取时间戳失败: ' + error.message);
        }
    }

    // 在创建工具栏之后添加时间戳列表面板
    const timestampPanel = createTimestampListPanel(); // 保存对面板的引用

    // 初始化
    setTimeout(async () => {
        await updateTimestampList();
        addVideoTimeUpdateHandler();
    }, 1000);

    // 使用防抖优化频繁操作
    function debounce(func, wait) {
        let timeout;
        return function executedFunction(...args) {
            const later = () => {
                clearTimeout(timeout);
                func(...args);
            };
            clearTimeout(timeout);
            timeout = setTimeout(later, wait);
        };
    }

    // 优化更新时间戳列表
    const debouncedUpdateTimestampList = debounce(updateTimestampList, 1000);

    // 优化视频时间更新处理
    function addVideoTimeUpdateHandler() {
        const video = getVideoElement();
        if (!video) return;

        // 增加进度节流,降低更新频率
        let lastUpdateTime = 0;
        const throttleInterval = 500; // 毫秒
        let lastHighlightedItem = null;

        const throttledTimeUpdate = (e) => {
            const now = Date.now();
            if (now - lastUpdateTime < throttleInterval) return;

            lastUpdateTime = now;
            const currentTime = Math.floor(video.currentTime);
            const items = document.querySelectorAll('.timestamp-item');

            // 清除之前的高亮
            if (lastHighlightedItem) {
                lastHighlightedItem.classList.remove('active');
            }

            // 寻找匹配的时间戳并高亮
            let highlightedItem = null;
            items.forEach(item => {
                const itemTime = parseInt(item.dataset.time);
                if (Math.abs(itemTime - currentTime) <= 1) {
                    item.classList.add('active');
                    highlightedItem = item;
                } else {
                    item.classList.remove('active');
                }
            });

            // 滚动到高亮的时间戳
            if (highlightedItem && highlightedItem !== lastHighlightedItem) {
                // 获取时间戳列表容器
                const container = document.getElementById('timestamp-list');
                if (container) {
                    // 计算滚动位置,使高亮项居中
                    const itemTop = highlightedItem.offsetTop;
                    const itemHeight = highlightedItem.offsetHeight;
                    const containerHeight = container.offsetHeight;
                    const scrollPosition = itemTop - (containerHeight / 2) + (itemHeight / 2);

                    // 使用平滑滚动
                    container.scrollTo({
                        top: Math.max(0, scrollPosition),
                        behavior: 'smooth'
                    });
                }

                // 更新lastHighlightedItem引用
                lastHighlightedItem = highlightedItem;
            }
        };

        video.addEventListener('timeupdate', throttledTimeUpdate);

        // 保存引用以便清理
        video._timestampUpdateHandler = throttledTimeUpdate;

        // 添加一次性事件监听器,当视频移除时清理事件
        const videoCleanup = () => {
            if (video._timestampUpdateHandler) {
                video.removeEventListener('timeupdate', video._timestampUpdateHandler);
                delete video._timestampUpdateHandler;
            }
        };

        video.addEventListener('emptied', videoCleanup);
    }

    // 减少轮询间隔时间,以减轻性能开销
    if (window._timestampUpdateInterval) {
        clearInterval(window._timestampUpdateInterval);
    }

    // 使用加倍的轮询间隔,减少刷新频率
    window._timestampUpdateInterval = setInterval(() => {
        // 仅在时间戳面板可见时才更新
        const panel = document.querySelector('.timestamp-list-panel');
        if (panel && panel.style.display !== 'none') {
            const list = document.getElementById('timestamp-list');
            if (list) {
                // 应用淡入淡出动画,使刷新看起来更加平滑
                list.animate([
                    { opacity: 1, filter: 'blur(0px)' },
                    { opacity: 0.95, filter: 'blur(0.5px)' },
                    { opacity: 1, filter: 'blur(0px)' }
                ], {
                    duration: 300,
                    easing: 'ease'
                });
            }

            // 一段时间后调用更新函数
            setTimeout(() => {
                updateTimestampList();
            }, 150);
        }
    }, 20000); // 增加到20秒更新一次

    // 在发送新时间戳后立即更新列表
    const originalSendToSiYuan = sendToSiYuan;
    sendToSiYuan = async function(content) {
        await originalSendToSiYuan(content);
        await updateTimestampList();
    };

    // 初始化
    setTimeout(async () => {
        await updateTimestampList();
        addVideoTimeUpdateHandler();
    }, 1000);

    // 添加时间戳列表拖动功能
    function makeDraggable(panel) {
        let isDragging = false;
        let currentX;
        let currentY;
        let initialX;
        let initialY;
        let xOffset = 0;
        let yOffset = 0;

        // 为面板添加硬件加速以提高拖拽性能
        panel.style.willChange = 'transform';
        panel.style.transform = 'translate(0px, 0px)';
        panel.style.transformStyle = 'preserve-3d';
        panel.style.backfaceVisibility = 'hidden';

        // 添加拖动手柄指示器到标题
        const header = panel.querySelector('.timestamp-list-header');
        const dragHandle = document.createElement('div');
        dragHandle.className = 'drag-handle';

        // 使用DOM API创建SVG而不是innerHTML
        const dragSvg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
        dragSvg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
        dragSvg.setAttribute("width", "14");
        dragSvg.setAttribute("height", "14");
        dragSvg.setAttribute("viewBox", "0 0 24 24");
        dragSvg.setAttribute("fill", "none");
        dragSvg.setAttribute("stroke", "currentColor");
        dragSvg.setAttribute("stroke-width", "2");
        dragSvg.setAttribute("stroke-linecap", "round");
        dragSvg.setAttribute("stroke-linejoin", "round");

        // 创建圆点
        const circles = [
            {cx: "9", cy: "12", r: "1"},
            {cx: "15", cy: "12", r: "1"},
            {cx: "9", cy: "6", r: "1"},
            {cx: "15", cy: "6", r: "1"},
            {cx: "9", cy: "18", r: "1"},
            {cx: "15", cy: "18", r: "1"}
        ];

        circles.forEach(attrs => {
            const circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
            circle.setAttribute("cx", attrs.cx);
            circle.setAttribute("cy", attrs.cy);
            circle.setAttribute("r", attrs.r);
            dragSvg.appendChild(circle);
        });

        dragHandle.appendChild(dragSvg);
        dragHandle.style.marginRight = '8px';
        dragHandle.style.opacity = '0.5';
        dragHandle.style.transition = 'opacity 0.2s ease';

        // 当鼠标悬停在头部时显示拖动手柄
        header.addEventListener('mouseenter', () => {
            dragHandle.style.opacity = '0.8';
        });

        header.addEventListener('mouseleave', () => {
            if (!isDragging) {
                dragHandle.style.opacity = '0.5';
            }
        });

        header.insertBefore(dragHandle, header.firstChild);

        // 移除面板的transition属性以消除拖拽延迟
        header.addEventListener('mousedown', () => {
            // 在拖拽开始前暂时移除transform的transition
            panel.style.transition = 'box-shadow 0.5s ease-in-out, opacity 0.3s ease-in-out';
        });

        header.addEventListener('mousedown', dragStart);
        document.addEventListener('mousemove', drag);
        document.addEventListener('mouseup', dragEnd);

        function dragStart(e) {
            if (e.target.classList.contains('timestamp-item') ||
                e.target.classList.contains('timestamp-header-btn') ||
                e.target.tagName.toLowerCase() === 'img') {
                return; // 如果点击的是时间戳项或按钮或图片,不启动拖动
            }

            initialX = e.clientX - xOffset;
            initialY = e.clientY - yOffset;

            isDragging = true;
            dragHandle.style.opacity = '1';

            // 修改光标样式
            header.style.cursor = 'grabbing';

            // 添加激活样式
            panel.classList.add('dragging');
        }

        function drag(e) {
            if (isDragging) {
                e.preventDefault();

                currentX = e.clientX - initialX;
                currentY = e.clientY - initialY;

                xOffset = currentX;
                yOffset = currentY;

                setTranslate(currentX, currentY, panel);
            }
        }

        function dragEnd() {
            initialX = currentX;
            initialY = currentY;

            isDragging = false;

            // 恢复光标样式
            const header = panel.querySelector('.timestamp-list-header');
            if (header) {
                header.style.cursor = 'grab';
            }

            // 移除激活样式
            panel.classList.remove('dragging');

            // 恢复拖动手柄的状态
            if (dragHandle) {
                dragHandle.style.opacity = '0.5';
            }

            // 恢复transition属性
            setTimeout(() => {
                panel.style.transition = 'box-shadow 0.5s ease-in-out, opacity 0.3s ease-in-out, transform 0.3s ease';
            }, 100);
        }

        function setTranslate(xPos, yPos, el) {
            // 使用matrix3d变换以获得更好的硬件加速
            el.style.transform = `translate3d(${xPos}px, ${yPos}px, 0)`;
        }
    }

    // 创建时间戳列表面板
    function createTimestampListPanel() {
        const panel = document.createElement('div');
        panel.className = 'timestamp-list-panel';

        // 更新初始高亮效果和不透明度的transition,确保拖拽流畅
        panel.style.transition = 'box-shadow 0.5s ease-in-out, opacity 0.3s ease-in-out, transform 0.3s ease';
        panel.style.boxShadow = '0 0 20px rgba(46, 204, 113, 0.6)';
        panel.style.opacity = '0.95';

        // 添加硬件加速相关属性
        panel.style.willChange = 'transform';
        panel.style.transform = 'translate3d(0px, 0px, 0)';
        panel.style.transformStyle = 'preserve-3d';
        panel.style.backfaceVisibility = 'hidden';

        // 添加鼠标悬停效果
        panel.addEventListener('mouseenter', () => {
            panel.style.opacity = '1';
        });
        panel.addEventListener('mouseleave', () => {
            panel.style.opacity = '0.85';
        });

        // 固定位置到右下角
        document.body.appendChild(panel);

        // 检查页面是否有视频元素来决定是否显示面板
        const video = getVideoElement();
        panel.style.display = video ? 'flex' : 'none';

        // 在视频首次播放时移除高亮效果
        if (video) {
            const removeHighlight = () => {
                panel.style.boxShadow = '0 4px 20px rgba(0, 0, 0, 0.25)';
                setTimeout(() => {
                    panel.style.opacity = '0.85';
                }, 500);
                video.removeEventListener('play', removeHighlight);
            };
            video.addEventListener('play', removeHighlight);
        }

        // 创建面板头部
        const header = document.createElement('div');
        header.className = 'timestamp-list-header';

        const title = document.createElement('div');
        title.className = 'timestamp-list-title';
        title.textContent = '视频时间戳';

        header.appendChild(title);

        // 创建时间戳列表容器
        const list = document.createElement('div');
        list.id = 'timestamp-list';

        // 创建底部按钮区域
        const footer = document.createElement('div');
        footer.className = 'timestamp-footer';

        const footerButtons = document.createElement('div');
        footerButtons.className = 'timestamp-footer-buttons';

        // 创建切换到药丸模式的按钮
        const toggleBtn = document.createElement('div');
        toggleBtn.className = 'timestamp-panel-toggle';

        // 使用DOM API创建SVG元素,而不是innerHTML
        const toggleSvg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
        toggleSvg.setAttribute("width", "14");
        toggleSvg.setAttribute("height", "14");
        toggleSvg.setAttribute("viewBox", "0 0 24 24");
        toggleSvg.setAttribute("fill", "none");
        toggleSvg.setAttribute("stroke", "currentColor");
        toggleSvg.setAttribute("stroke-width", "2");
        toggleSvg.setAttribute("stroke-linecap", "round");
        toggleSvg.setAttribute("stroke-linejoin", "round");

        const path1 = document.createElementNS("http://www.w3.org/2000/svg", "path");
        path1.setAttribute("d", "M18 6L6 18");
        const path2 = document.createElementNS("http://www.w3.org/2000/svg", "path");
        path2.setAttribute("d", "M6 6l12 12");

        toggleSvg.appendChild(path1);
        toggleSvg.appendChild(path2);

        const span = document.createElement('span');
        span.textContent = '最小化';

        toggleBtn.appendChild(toggleSvg);
        toggleBtn.appendChild(span);

        // 创建药丸容器
        const pillsContainer = document.createElement('div');
        pillsContainer.className = 'timestamp-pills-container';

        // 创建时间戳药丸
        const timestampPill = document.createElement('div');
        timestampPill.className = 'timestamp-pill';
        timestampPill.title = '获取时间戳';

        const tsImg = document.createElement('img');
        tsImg.src = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGNsYXNzPSJsdWNpZGUgbHVjaWRlLWdvYWwiPjxwYXRoIGQ9Ik0xMiAxM1YybDggNC04IDQiLz48cGF0aCBkPSJNMjAuNTYxIDEwLjIyMmE5IDkgMCAxIDEtMTIuNTUtNS4yOSIvPjxwYXRoIGQ9Ik04LjAwMiA5Ljk5N2E1IDUgMCAxIDAgOC45IDIuMDIiLz48L3N2Zz4=";
        tsImg.alt = "时间戳";

        const tsSpan = document.createElement('span');
        tsSpan.className = 'timestamp-pill-text';
        tsSpan.textContent = '获取时间戳';

        timestampPill.appendChild(tsImg);
        timestampPill.appendChild(tsSpan);
        timestampPill.addEventListener('click', getVideoTimestamp);

        // 创建截图药丸
        const screenshotPill = document.createElement('div');
        screenshotPill.className = 'timestamp-pill';
        screenshotPill.title = '获取时间戳+截图';

        const ssImg = document.createElement('img');
        ssImg.src = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGNsYXNzPSJsdWNpZGUgbHVjaWRlLWNhbWVyYSI+PHBhdGggZD0iTTE0LjUgNGgtNUw3IDdINGEyIDIgMCAwIDAtMiAydjlhMiAyIDAgMCAwIDIgMmgxNmEyIDIgMCAwIDAgMi0yVjlhMiAyIDAgMCAwLTItMmgtM2wtMi41LTN6Ii8+PGNpcmNsZSBjeD0iMTIiIGN5PSIxMyIgcj0iMyIvPjwvc3ZnPg==";
        ssImg.alt = "截图";

        const ssSpan = document.createElement('span');
        ssSpan.className = 'timestamp-pill-text';
        ssSpan.textContent = '时间戳+截图';

        screenshotPill.appendChild(ssImg);
        screenshotPill.appendChild(ssSpan);
        screenshotPill.addEventListener('click', getVideoScreenshot);

        // 创建展开按钮
        const expandBtn = document.createElement('div');
        expandBtn.className = 'timestamp-expand-btn';
        expandBtn.title = '展开面板';

        const expandImg = document.createElement('img');
        expandImg.src = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGNsYXNzPSJsdWNpZGUgbHVjaWRlLWV4cGFuZCI+PHBhdGggZD0ibTE1IDMgNiA2Ii8+PHBhdGggZD0iTTIxIDMtNiA2Ii8+PHBhdGggZD0iTTMgOGw2IDYiLz48cGF0aCBkPSJNMyAyMGwxMi0xMiIvPjwvc3ZnPg==";
        expandImg.alt = "展开";

        expandBtn.appendChild(expandImg);

        // 添加药丸到容器
        pillsContainer.appendChild(timestampPill);
        pillsContainer.appendChild(screenshotPill);
        pillsContainer.appendChild(expandBtn);
        document.body.appendChild(pillsContainer);

        // 添加最小化/展开功能
        toggleBtn.addEventListener('click', () => {
            panel.classList.add('hidden');

            // 隐藏面板元素
            panel.style.display = 'none';

            // 添加移除面板的延迟处理,确保动画完成后DOM才被移除
            setTimeout(() => {
                // 将面板从DOM中移除而不是仅仅隐藏
                if (panel.parentNode) {
                    panel.parentNode.removeChild(panel);
                }

                // 显示药丸容器
                pillsContainer.classList.add('visible');
            }, 300);
        });

        expandBtn.addEventListener('click', () => {
            // 如果面板已被移除,则重新添加到DOM
            if (!document.body.contains(panel)) {
                document.body.appendChild(panel);

                // 重新初始化拖拽
                makeDraggable(panel);

                // 让面板的CSS属性先为隐藏状态,然后用setTimeout触发显示动画
                panel.style.opacity = '0';
                panel.style.transform = 'translate3d(0px, 0px, 0)';
                setTimeout(() => {
                    panel.classList.remove('hidden');
                    panel.style.display = 'flex';
                    panel.style.opacity = '0.95';
                }, 50);
            } else {
                // 如果面板仍在DOM中,只是显示它
                panel.classList.remove('hidden');
                panel.style.display = 'flex';
            }

            // 隐藏药丸容器
            pillsContainer.classList.remove('visible');
        });

        // 创建功能按钮
        const buttonConfigs = [
            {
                icon: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGNsYXNzPSJsdWNpZGUgbHVjaWRlLXNldHRpbmdzIj48cGF0aCBkPSJNMTIuMjIgMmgtLjQ0YTIgMiAwIDAgMC0yIDJ2LjE4YTIgMiAwIDAgMS0xIDEuNzNsLS40My4yNWEyIDIgMCAwIDEtMiAwbC0uMTUtLjA4YTIgMiAwIDAgMC0yLjczLjczbC0uMjIuMzhhMiAyIDAgMCAwIC43MyAyLjczbC4xNS4xYTIgMiAwIDAgMSAxIDEuNzJ2LjUxYTIgMiAwIDAgMS0xIDEuNzRsLS4xNS4wOWEyIDIgMCAwIDAtLjczIDIuNzNsLjIyLjM4YTIgMiAwIDAgMCAyLjczLjczbC4xNS0uMDhhMiAyIDAgMCAxIDIgMGwuNDMuMjVhMiAyIDAgMCAxIDEgMS43M1YyMGEyIDIgMCAwIDAgMiAyaC40NGEyIDIgMCAwIDAgMi0ydi0uMThhMiAyIDAgMCAxIDEtMS43M2wuNDMtLjI1YTIgMiAwIDAgMSAyIDBsLjE1LjA4YTIgMiAwIDAgMCAyLjczLS43M2wuMjItLjM5YTIgMiAwIDAgMC0uNzMtMi43M2wtLjE1LS4wOGEyIDIgMCAwIDEtMS0xLjc0di0uNWEyIDIgMCAwIDEgMS0xLjc0bC4xNS0uMDlhMiAyIDAgMCAwIC43My0yLjczbC0uMjItLjM4YTIgMiAwIDAgMC0yLjczLS43M2wtLjE1LjA4YTIgMiAwIDAgMS0yIDBsLS40My0uMjVhMiAyIDAgMCAxLTEtMS43M1Y0YTIgMiAwIDAgMC0yLTJ6Ii8+PGNpcmNsZSBjeD0iMTIiIGN5PSIxMiIgcj0iMyIvPjwvc3ZnPg==',
                title: '设置',
                onClick: showSettings
            },
            {
                icon: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGNsYXNzPSJsdWNpZGUgbHVjaWRlLWZpbGUtcGx1cyI+PHBhdGggZD0iTTE1IDJINmEyIDIgMCAwIDAtMiAydjE2YTIgMiAwIDAgMCAyIDJoMTJhMiAyIDAgMCAwIDItMlY3WiIvPjxwYXRoIGQ9Ik0xNCAydjRhMiAyIDAgMCAwIDIgMmg0Ii8+PHBhdGggZD0iTTkgMTVoNiIvPjxwYXRoIGQ9Ik0xMiAxOHYtNiIvPjwvc3ZnPg==',
                title: '创建视频笔记',
                onClick: createVideoNote
            },
            {
                icon: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGNsYXNzPSJsdWNpZGUgbHVjaWRlLWdvYWwiPjxwYXRoIGQ9Ik0xMiAxM1YybDggNC04IDQiLz48cGF0aCBkPSJNMjAuNTYxIDEwLjIyMmE5IDkgMCAxIDEtMTIuNTUtNS4yOSIvPjxwYXRoIGQ9Ik04LjAwMiA5Ljk5N2E1IDUgMCAxIDAgOC45IDIuMDIiLz48L3N2Zz4=',
                title: '获取时间戳',
                onClick: getVideoTimestamp
            },
            {
                icon: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGNsYXNzPSJsdWNpZGUgbHVjaWRlLWNhbWVyYSI+PHBhdGggZD0iTTE0LjUgNGgtNUw3IDdINGEyIDIgMCAwIDAtMiAydjlhMiAyIDAgMCAwIDIgMmgxNmEyIDIgMCAwIDAgMi0yVjlhMiAyIDAgMCAwLTItMmgtM2wtMi41LTN6Ii8+PGNpcmNsZSBjeD0iMTIiIGN5PSIxMyIgcj0iMyIvPjwvc3ZnPg==',
                title: '获取时间戳+截图',
                onClick: getVideoScreenshot
            }
        ];

        buttonConfigs.forEach(config => {
            const button = document.createElement('button');
            button.className = 'timestamp-footer-btn';
            button.title = config.title;
            button.onclick = config.onClick;

            const img = document.createElement('img');
            img.src = config.icon;
            img.alt = config.title;

            button.appendChild(img);
            footerButtons.appendChild(button);
        });

        footer.appendChild(footerButtons);
        footer.appendChild(toggleBtn);

        panel.appendChild(header);
        panel.appendChild(list);
        panel.appendChild(footer);

        makeDraggable(panel);

        // 添加视频元素变化监听,使用更高效的监听策略
        let lastVideoState = !!getVideoElement();
        let observerThrottleTimer = null;

        const videoObserver = new MutationObserver(() => {
            // 使用节流来减少回调处理频率
            if (observerThrottleTimer) return;

            observerThrottleTimer = setTimeout(() => {
                const video = getVideoElement();
                const currentVideoState = !!video;

                // 只有视频状态发生变化时才更新UI
                if (currentVideoState !== lastVideoState) {
                    lastVideoState = currentVideoState;

                    if (video) {
                        if (!panel.classList.contains('hidden')) {
                            // 如果面板不在DOM中,重新添加
                            if (!document.body.contains(panel)) {
                                document.body.appendChild(panel);
                                makeDraggable(panel);
                            }

                            // 临时移除transition以避免显示延迟
                            const originalTransition = panel.style.transition;
                            panel.style.transition = 'opacity 0.3s ease-in-out';
                            panel.style.display = 'flex';

                            // 在显示后短暂延时恢复transition
                            setTimeout(() => {
                                panel.style.transition = originalTransition;
                            }, 50);

                            pillsContainer.classList.remove('visible');
                        } else {
                            pillsContainer.classList.add('visible');
                        }
                    } else {
                        panel.style.display = 'none';
                        pillsContainer.classList.remove('visible');
                    }

                    // 如果发现新的视频元素,添加播放监听器
                    if (video) {
                        // 先清除可能存在的旧监听器
                        video.removeEventListener('play', removeHighlight);

                        const removeHighlight = () => {
                            panel.style.boxShadow = '0 4px 20px rgba(0, 0, 0, 0.25)';

                            // 平滑过渡到较低透明度
                            const originalTransition = panel.style.transition;
                            panel.style.transition = 'opacity 0.5s ease-in-out, box-shadow 0.5s ease-in-out';

                            setTimeout(() => {
                                panel.style.opacity = '0.85';

                                // 恢复原始transition
                                setTimeout(() => {
                                    panel.style.transition = originalTransition;
                                }, 500);
                            }, 50);

                            video.removeEventListener('play', removeHighlight);
                        };
                        video.addEventListener('play', removeHighlight);

                        // 添加video timeupdate事件监听
                        addVideoTimeUpdateHandler();
                    }
                }

                observerThrottleTimer = null;
            }, 300); // 300ms节流间隔
        });

        // 使用更精确的观察目标和配置,减少不必要的调用
        videoObserver.observe(document.body, {
            childList: true,
            subtree: true,
            attributes: true,
            attributeFilter: ['data-screen'] // 特别关注B站全屏属性变化
        });

        // 在组件卸载时清理observer
        panel._videoObserver = videoObserver;
        panel._cleanupObserver = () => {
            if (panel._videoObserver) {
                panel._videoObserver.disconnect();
                panel._videoObserver = null;
            }
            if (observerThrottleTimer) {
                clearTimeout(observerThrottleTimer);
                observerThrottleTimer = null;
            }
        };

        return panel;
    }

    // 将 hotkeyHandler 定义在全局作用域
    let hotkeyHandler;

    // 定义全局编辑状态变量
    let isEditing = false;

    // 修改 setupHotkeys 函数
    function setupHotkeys() {
        const config = configManager.get();

        // 定义 hotkeyHandler
        hotkeyHandler = function(e) {
            const pressedKeys = [];
            if (e.ctrlKey) pressedKeys.push('Ctrl');
            if (e.altKey) pressedKeys.push('Alt');
            if (e.shiftKey) pressedKeys.push('Shift');
            if (e.key !== 'Control' && e.key !== 'Alt' && e.key !== 'Shift') {
                pressedKeys.push(e.key.toUpperCase());
            }
            const pressedHotkey = pressedKeys.join('+');

            // 检查是否在输入框中
            if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') {
                return;
            }

            if (pressedHotkey === config.CREATE_NOTE_HOTKEY || pressedHotkey === config.TIMESTAMP_HOTKEY || pressedHotkey === config.SCREENSHOT_HOTKEY) {
                e.preventDefault();
                e.stopPropagation();

                // 防止重复触发
                if (e.repeat) return;

                // 添加防抖机制
                if (window.__lastHotkeyPress && Date.now() - window.__lastHotkeyPress < 500) {
                    return;
                }
                window.__lastHotkeyPress = Date.now();

                const video = getVideoElement();
                if (!video) {
                    showNotification('未找到视频元素!');
                    return;
                }

                const cleanedUrl = cleanUrl(window.location.href);
                findMatchingVideoNote(cleanedUrl).then(matchingNoteId => {
                    if (!matchingNoteId) {
                        if (pressedHotkey === config.CREATE_NOTE_HOTKEY) {
                            createVideoNote();
                        } else {
                            showNotification('请先创建视频笔记!');
                        }
                    } else {
                        if (pressedHotkey === config.TIMESTAMP_HOTKEY) {
                            getVideoTimestamp();
                        } else if (pressedHotkey === config.SCREENSHOT_HOTKEY) {
                            getVideoScreenshot();
                        } else if (pressedHotkey === config.CREATE_NOTE_HOTKEY) {
                            createVideoNote();
                        }
                    }
                });
            }
        };

        // 确保只添加一次事件监听器
        if (!window.__hotkeyHandlerAdded) {
            document.removeEventListener('keydown', hotkeyHandler);
            document.addEventListener('keydown', hotkeyHandler);
            window.__hotkeyHandlerAdded = true;
        }
    }

    // 添加事件监听设置函数
    function setupEventListeners(panel) {
        // 设置快捷键事件监听
        const hotkeyInputs = ['create-note-hotkey', 'timestamp-hotkey', 'screenshot-hotkey'];
        hotkeyInputs.forEach(id => {
            const input = panel.querySelector(`#${id}`);
            if (!input) return;

            input.addEventListener('focus', () => {
                input.value = '请按下快捷键组合...';
            });

            input.addEventListener('keydown', (e) => {
                e.preventDefault();
                const keys = [];
                if (e.ctrlKey) keys.push('Ctrl');
                if (e.altKey) keys.push('Alt');
                if (e.shiftKey) keys.push('Shift');
                if (e.key !== 'Control' && e.key !== 'Alt' && e.key !== 'Shift') {
                    keys.push(e.key.toUpperCase());
                }
                input.value = keys.join('+');
            });
        });

        // 绑定保存按钮事件
        const saveBtn = panel.querySelector('#save-settings');
        if (saveBtn) {
            saveBtn.onclick = function() {
                const selectedNotebook = panel.querySelector('#notebook-select');
                const matchInputs = panel.querySelectorAll('.match-input');
                const apiEndpoint = panel.querySelector('#api-endpoint').value.trim();
                const apiToken = panel.querySelector('#api-token').value.trim();

                // 检查必填项
                if (!apiToken) {
                    showNotification('请输入 API Token');
                    return;
                }

                // 检查笔记本选择 - 只有在有选项可供选择时才强制要求
                const hasNotebookOptions = selectedNotebook.options.length > 0 &&
                                          selectedNotebook.options[0].value !== '' &&
                                          !selectedNotebook.options[0].textContent.includes('加载失败') &&
                                          !selectedNotebook.options[0].textContent.includes('加载中');

                if (hasNotebookOptions && !selectedNotebook.value) {
                    showNotification('请选择一个笔记本');
                    return;
                }

                const notebookOption = selectedNotebook.selectedOptions[0];
                const newConfig = {
                    API_ENDPOINT: apiEndpoint,
                    API_TOKEN: apiToken,
                    NOTEBOOK_ID: selectedNotebook.value || '',
                    NOTEBOOK_NAME: (notebookOption && notebookOption.textContent) || '',
                    CREATE_NOTE_HOTKEY: panel.querySelector('#create-note-hotkey').value,
                    TIMESTAMP_HOTKEY: panel.querySelector('#timestamp-hotkey').value,
                    SCREENSHOT_HOTKEY: panel.querySelector('#screenshot-hotkey').value,
                    MATCH_LIST: Array.from(matchInputs)
                        .map(input => input.value.trim())
                        .filter(value => value !== '')
                };

                configManager.save(newConfig);
                setupHotkeys();
                showNotification('设置已保存');
                panel.remove();
            };
        }

        // 绑定取消按钮事件
        const cancelBtn = panel.querySelector('#cancel-settings');
        if (cancelBtn) {
            cancelBtn.onclick = function() {
                panel.remove();
            };
        }

        // 为匹配规则添加按钮绑定事件
        const addMatchBtn = panel.querySelector('.add-match-btn');
        if (addMatchBtn) {
            addMatchBtn.onclick = () => {
                const matchList = panel.querySelector('#match-list');
                const matchItem = createMatchItem('');
                matchList.appendChild(matchItem);
            };
        }
    }

    // 初始化时设置快捷键
    setupHotkeys();

    // 添加辅助函数
    async function findTimestampBlockId(url, docId) {
        const sql = `SELECT id FROM blocks
                     WHERE root_id = '${docId}'
                     AND content LIKE '%${url}%'
                     AND type = 'p'
                     AND id IN (
                         SELECT block_id FROM attributes
                         WHERE name = 'custom-media'
                         AND value = 'timestamp'
                     )`;

        const result = await query(sql);
        return result.length > 0 ? result[0].id : null;
    }

    async function findMemoBlock(parentId) {
        const sql = `SELECT id FROM blocks
                     WHERE parent_id = '${parentId}'
                     AND type = 'p'
                     AND id IN (
                         SELECT block_id FROM attributes
                         WHERE name = 'custom-media'
                         AND value = 'memos'
                     )`;

        const result = await query(sql);
        return result.length > 0 ? result[0].id : null;
    }

    // 添加 SQL 查询函数
    async function query(sql) {
        const config = getConfig();
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: 'POST',
                url: `${config.API_ENDPOINT}/api/query/sql`,
                headers: {
                    'Authorization': `Token ${config.API_TOKEN}`,
                    'Content-Type': 'application/json'
                },
                data: JSON.stringify({ stmt: sql }),
                onload: function(response) {
                    if (response.status === 200) {
                        const result = JSON.parse(response.responseText);
                        if (result.code === 0) {
                            resolve(result.data);
                        } else {
                            reject(new Error(result.msg));
                        }
                    } else {
                        reject(new Error('查询失败'));
                    }
                },
                onerror: reject
            });
        });
    }

    // 添加设置块属性的函数
    async function setBlockAttrs(params) {
        const config = getConfig();
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: 'POST',
                url: `${config.API_ENDPOINT}/api/attr/setBlockAttrs`,
                headers: {
                    'Authorization': `Token ${config.API_TOKEN}`,
                    'Content-Type': 'application/json'
                },
                data: JSON.stringify(params),
                onload: function(response) {
                    if (response.status === 200) {
                        const result = JSON.parse(response.responseText);
                        if (result.code === 0) {
                            resolve(result.data);
                        } else {
                            reject(new Error(result.msg));
                        }
                    } else {
                        reject(new Error('设置属性失败'));
                    }
                },
                onerror: reject
            });
        });
    }

    // 添加查找和删除临时块的函数
    async function removeTemporaryBlocks() {
        const config = getConfig();
        try {
            // 1. 先用SQL查询找到所有带有临时标记的块
            const result = await new Promise((resolve, reject) => {
                GM_xmlhttpRequest({
                    method: 'POST',
                    url: `${config.API_ENDPOINT}/api/query/sql`,
                    headers: {
                        'Authorization': `Token ${config.API_TOKEN}`,
                        'Content-Type': 'application/json'
                    },
                    data: JSON.stringify({
                        stmt: "SELECT id FROM blocks WHERE id IN (SELECT block_id FROM attributes WHERE name = 'custom-media' AND value = 'temp')"
                    }),
                    onload: function(response) {
                        if (response.status === 200) {
                            const result = JSON.parse(response.responseText);
                            if (result.code === 0) {
                                resolve(result.data);
                            } else {
                                reject(new Error(result.msg));
                            }
                        } else {
                            reject(new Error('查询临时块失败'));
                        }
                    },
                    onerror: reject
                });
            });

            // 2. 删除找到的所有临时块
            for (const block of result) {
                await new Promise((resolve, reject) => {
                    GM_xmlhttpRequest({
                        method: 'POST',
                        url: `${config.API_ENDPOINT}/api/block/deleteBlock`,
                        headers: {
                            'Authorization': `Token ${config.API_TOKEN}`,
                            'Content-Type': 'application/json'
                        },
                        data: JSON.stringify({
                            id: block.id
                        }),
                        onload: function(response) {
                            if (response.status === 200) {
                                const result = JSON.parse(response.responseText);
                                if (result.code === 0) {
                                    resolve();
                                } else {
                                    reject(new Error(result.msg));
                                }
                            } else {
                                reject(new Error('删除临时块失败'));
                            }
                        },
                        onerror: reject
                    });
                });
            }

            console.log('临时块清理完成');
        } catch (error) {
            console.error('清理临时块失败:', error);
            throw error;
        }
    }

    // 添加更新块内容的函数
    async function updateBlock(params) {
        const config = getConfig();
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: 'POST',
                url: `${config.API_ENDPOINT}/api/block/updateBlock`,
                headers: {
                    'Authorization': `Token ${config.API_TOKEN}`,
                    'Content-Type': 'application/json'
                },
                data: JSON.stringify(params),
                onload: function(response) {
                    if (response.status === 200) {
                        const result = JSON.parse(response.responseText);
                        if (result.code === 0) {
                            resolve(result.data);
                        } else {
                            reject(new Error(result.msg));
                        }
                    } else {
                        reject(new Error('更新块失败'));
                    }
                },
                onerror: reject
            });
        });
    }

    // 添加统一的错误处理函数
    function handleError(error, operation) {
        console.error(`${operation} 失败:`, error);
        showNotification(`${operation}失败: ${error.message}`);
        throw error;
    }

    // 添加资源清理函数
    function cleanup() {
        // 清除所有事件监听器
        document.removeEventListener('keydown', hotkeyHandler);

        // 清除定时器
        if (window._timestampUpdateInterval) {
            clearInterval(window._timestampUpdateInterval);
            window._timestampUpdateInterval = null;
        }

        // 清理视频事件监听器
        const video = getVideoElement();
        if (video && video._timestampUpdateHandler) {
            video.removeEventListener('timeupdate', video._timestampUpdateHandler);
            delete video._timestampUpdateHandler;
        }

        // 清除缓存
        cache.clearCache();

        // 移除面板和相关资源
        const panel = document.querySelector('.timestamp-list-panel');
        if (panel) {
            // 调用面板的清理函数
            if (panel._cleanupObserver) {
                panel._cleanupObserver();
            }

            // 移除面板
            if (panel.parentNode) {
                panel.parentNode.removeChild(panel);
            }
        }

        // 移除药丸容器
        const pillsContainer = document.querySelector('.timestamp-pills-container');
        if (pillsContainer && pillsContainer.parentNode) {
            pillsContainer.parentNode.removeChild(pillsContainer);
        }
    }

    // 在页面卸载时清理资源
    window.addEventListener('unload', cleanup);

    // 修改 createOrGetSuperBlock 函数,将 config 作为参数传入
    async function createOrGetSuperBlock(blockId) {
        try {
            // 1. 检查是否已经在超级块内
            const isInSuper = await isInSuperBlock(blockId);
            if (isInSuper) {
                // 如果已经在超级块内,返回父超级块ID
                return await getParentSuperBlockId(blockId);
            }

            // 2. 创建新的超级块
            const superBlockId = await new Promise((resolve, reject) => {
                GM_xmlhttpRequest({
                    method: 'POST',
                    url: `${getConfig().API_ENDPOINT}/api/block/insertBlock`,
                    headers: {
                        'Authorization': `Token ${getConfig().API_TOKEN}`,
                        'Content-Type': 'application/json'
                    },
                    data: JSON.stringify({
                        dataType: "markdown",
                        data: `{{{row
                            内容\n{: custom-media="temp" }\n
                            }}}\n{: custom-media="mediacard" }\n`,
                        previousID: blockId
                    }),
                    onload: function(response) {
                        if (response.status === 200) {
                            const result = JSON.parse(response.responseText);
                            if (result.code === 0 && result.data && result.data.length > 0 &&
                                result.data[0].doOperations && result.data[0].doOperations.length > 0) {
                                resolve(result.data[0].doOperations[0].id);
                            } else {
                                console.error('API返回结果异常:', result);
                                reject(new Error(result.msg || '返回数据结构异常'));
                            }
                        } else {
                            reject(new Error('创建超级块失败'));
                        }
                    },
                    onerror: reject
                });
            });

            // 3. 移动原始块到超级块内
            await new Promise((resolve, reject) => {
                GM_xmlhttpRequest({
                    method: 'POST',
                    url: `${getConfig().API_ENDPOINT}/api/block/moveBlock`,
                    headers: {
                        'Authorization': `Token ${getConfig().API_TOKEN}`,
                        'Content-Type': 'application/json'
                    },
                    data: JSON.stringify({
                        id: blockId,
                        parentID: superBlockId
                    }),
                    onload: function(response) {
                        if (response.status === 200) {
                            const result = JSON.parse(response.responseText);
                            if (result.code === 0) {
                                resolve();
                            } else {
                                reject(new Error(result.msg));
                            }
                        } else {
                            reject(new Error('移动块失败'));
                        }
                    },
                    onerror: reject
                });
            });

            // 4. 设置超级块属性
            await setBlockAttrs({
                id: superBlockId,
                attrs: {
                    "layout": "row"
                }
            });

            // 5. 查找并删除临时占位块
            const sql = `SELECT b.id
                        FROM blocks b
                        JOIN attributes a ON b.id = a.block_id
                        WHERE b.parent_id = '${superBlockId}'
                        AND a.name = 'custom-media'
                        AND a.value = 'temp'`;

            try {
                const result = await query(sql);
                if (result && result.length > 0) {
                    for (const row of result) {
                        await deleteBlock({ id: row.id });
                    }
                }
            } catch (error) {
                console.error('删除临时占位块失败:', error);
                // 继续执行,不影响主流程
            }

            return superBlockId;
        } catch (error) {
            console.error('创建或获取超级块失败:', error);
            throw error;
        }
    }

    // 添加删除块的函数
    async function deleteBlock(params) {
        const config = getConfig();
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: 'POST',
                url: `${config.API_ENDPOINT}/api/block/deleteBlock`,
                headers: {
                    'Authorization': `Token ${config.API_TOKEN}`,
                    'Content-Type': 'application/json'
                },
                data: JSON.stringify(params),
                onload: function(response) {
                    if (response.status === 200) {
                        const result = JSON.parse(response.responseText);
                        if (result.code === 0) {
                            resolve(result.data);
                        } else {
                            reject(new Error(result.msg));
                        }
                    } else {
                        reject(new Error('删除块失败'));
                    }
                },
                onerror: reject
            });
        });
    }

    // 添加安全获取块ID的辅助函数
    function getBlockIDFromResult(result) {
        if (result &&
            result.code === 0 &&
            result.data &&
            result.data.length > 0 &&
            result.data[0].doOperations &&
            result.data[0].doOperations.length > 0 &&
            result.data[0].doOperations[0].id) {
            return result.data[0].doOperations[0].id;
        }
        console.error('API返回结果无法获取块ID:', result);
        throw new Error('返回数据结构异常,无法获取块ID');
    }

    // 修改备注块更新逻辑
    async function updateMemoBlocks(timestampBlockId, newNotes) {
        try {
            // 获取超级块
            const superBlockId = await createOrGetSuperBlock(timestampBlockId);

            // 获取现有的备注块
            const existingMemoBlocks = await findAllMemoBlocks(timestampBlockId);

            // 获取现有备注块的内容
            const existingContents = await Promise.all(existingMemoBlocks.map(async (blockId) => {
                try {
                    const blockData = await getBlockKramdown(blockId);
                    return {
                        id: blockId,
                        content: blockData.kramdown.replace(/\{:[^\}]+\}/g, '').trim()
                    };
                } catch (error) {
                    console.error('获取备注块内容失败:', error);
                    return { id: blockId, content: '' };
                }
            }));

            // 更新现有块和添加新块
            const processedNotes = new Set(); // 用于跟踪已处理的备注

            // 1. 首先更新已存在的块
            for (let i = 0; i < Math.min(existingContents.length, newNotes.length); i++) {
                const note = newNotes[i].trim();
                if (note && note !== existingContents[i].content) {
                    // 只有当内容不同时才更新
                    await updateBlock({
                        id: existingContents[i].id,
                        data: note,
                        dataType: "markdown"
                    });

                    // 确保更新后重新设置属性
                    await setBlockAttrs({
                        id: existingContents[i].id,
                        attrs: {
                            "custom-media": "memos"
                        }
                    });
                }
                processedNotes.add(note);
            }

            // 2. 如果有新的备注,创建新的块
            for (const note of newNotes) {
                const trimmedNote = note.trim();
                if (trimmedNote && !processedNotes.has(trimmedNote)) {
                    try {
                        await new Promise((resolve, reject) => {
                            GM_xmlhttpRequest({
                                method: 'POST',
                                url: `${getConfig().API_ENDPOINT}/api/block/appendBlock`,
                                headers: {
                                    'Authorization': `Token ${getConfig().API_TOKEN}`,
                                    'Content-Type': 'application/json'
                                },
                                data: JSON.stringify({
                                    dataType: "markdown",
                                    data: trimmedNote,
                                    parentID: superBlockId
                                }),
                                onload: async function(response) {
                                    if (response.status === 200) {
                                        const result = JSON.parse(response.responseText);
                                        if (result.code === 0 && result.data && result.data.length > 0 &&
                                            result.data[0].doOperations && result.data[0].doOperations.length > 0) {
                                            const newBlockId = result.data[0].doOperations[0].id;
                                            try {
                                                await setBlockAttrs({
                                                    id: newBlockId,
                                                    attrs: {
                                                        "custom-media": "memos"
                                                    }
                                                });
                                                resolve();
                                            } catch (error) {
                                                reject(error);
                                            }
                                        } else {
                                            console.error('API返回结果异常:', result);
                                            reject(new Error(result.msg || '返回数据结构异常'));
                                        }
                                    } else {
                                        reject(new Error('创建备注块失败'));
                                    }
                                },
                                onerror: reject
                            });
                        });
                    } catch (error) {
                        console.error('创建备注块失败:', error);
                        // 继续处理其他备注,不中断整个过程
                    }
                    processedNotes.add(trimmedNote);
                }
            }

            // 3. 如果现有块数量多于新备注数量,删除多余的块
            if (existingContents.length > newNotes.length) {
                for (let i = newNotes.length; i < existingContents.length; i++) {
                    await deleteBlock({ id: existingContents[i].id });
                }
            }

            return true;
        } catch (error) {
            console.error('更新备注块失败:', error);
            throw error;
        }
    }
})();