给DeepSeek添加原生化的提示词管理功能

为 DeepSeek 添加提示词管理功能。全新Linear设计风格,TailwindCSS构建

// ==UserScript==
// @name         给DeepSeek添加原生化的提示词管理功能
// @namespace    http://tampermonkey.net/
// @version       1.8.9 
// @description  为 DeepSeek 添加提示词管理功能。全新Linear设计风格,TailwindCSS构建
// @author       gpt
// @match       https://chat.deepseek.com/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_addStyle
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // --- Script Configuration ---
    const SCRIPT_PREFIX = 'dspm_'; // Prefix for localStorage keys, IDs, and CSS classes

    // --- Helper Functions ---
    const $ = (selector, parent = document) => parent.querySelector(selector);
    const $$ = (selector, parent = document) => parent.querySelectorAll(selector);
    const debounce = (fn, delay) => {
        let timer;
        return (...args) => {
            clearTimeout(timer);
            timer = setTimeout(() => fn(...args), delay);
        };
    };

    // --- Dynamic CDN Injection ---
    function injectCDN(url, type) {
        return new Promise((resolve, reject) => {
            let element;
            if (type === 'script') {
                element = document.createElement('script');
                element.src = url;
            } else if (type === 'link') {
                element = document.createElement('link');
                element.href = url;
                element.rel = 'stylesheet';
            }
            if (element) {
                element.onload = resolve;
                element.onerror = (err) => {
                    console.error(`DSPM: Failed to load CDN ${type}: ${url}`, err);
                    reject(err);
                };
                document.head.appendChild(element);
            } else {
                reject(new Error('Invalid CDN type'));
            }
        });
    }

    // --- Theme Management ---
    const applyTheme = (theme) => {
        console.log(`DSPM: Applying theme: ${theme}`);
        if (theme === 'dark') {
            document.documentElement.classList.add('dark');
        } else {
            document.documentElement.classList.remove('dark');
        }
        GM_setValue(SCRIPT_PREFIX + 'theme', theme);
    };

    const getSavedTheme = () => GM_getValue(SCRIPT_PREFIX + 'theme', 'system');

    const initTheme = () => {
        const savedTheme = getSavedTheme();
        console.log(`DSPM: Initializing theme. Saved: ${savedTheme}`);
        if (savedTheme === 'system') {
            const systemPrefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
            console.log(`DSPM: System prefers dark: ${systemPrefersDark}`);
            applyTheme(systemPrefersDark ? 'dark' : 'light');
        } else {
            applyTheme(savedTheme);
        }
        window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => {
            console.log(`DSPM: System theme changed. Prefers dark: ${e.matches}`);
            if (getSavedTheme() === 'system') {
                applyTheme(e.matches ? 'dark' : 'light');
            }
        });
    };


    // --- Base Styles (Minimal, mostly for scrollbars and transitions) ---
    GM_addStyle(`
        .dspm-smooth-scroll { scroll-behavior: smooth; }
        .dspm-custom-scrollbar::-webkit-scrollbar { width: 8px; height: 8px; }
        .dspm-custom-scrollbar::-webkit-scrollbar-track { background: transparent; }
        .dspm-custom-scrollbar::-webkit-scrollbar-thumb {
            background-color: rgba(156, 163, 175, 0.5); /* Light mode thumb color */
            border-radius: 4px;
        }
        .dspm-custom-scrollbar::-webkit-scrollbar-thumb:hover { background-color: rgba(107, 114, 128, 0.7); }
        .dark .dspm-custom-scrollbar::-webkit-scrollbar-thumb {
            background-color: rgba(75, 85, 99, 0.6); /* Dark mode thumb color */
        }
        .dark .dspm-custom-scrollbar::-webkit-scrollbar-thumb:hover { background-color: rgba(55, 65, 81, 0.8); }
        .dspm-fade-in { animation: dspmFadeInAnimation 0.3s ease-out forwards; opacity: 0; }
        @keyframes dspmFadeInAnimation { to { opacity: 1; } }

        /* Management Panel Styles */
        .dspm-management-side-panel {
            transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
            transform: translateX(100%);
            height: 100vh !important; /* Ensure panel is full viewport height */
        }
        .dspm-management-side-panel.visible { transform: translateX(0); }

        /* Content area within the management panel */
        .dspm-management-content-area {
            height: calc(100vh - 62px) !important; /* Full height minus header (assuming header is 62px) */
            overflow-y: scroll !important; /* Ensure vertical scrollbar track is always visible */
        }

        .dspm-management-panel-toggle-btn { transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1), background-color 0.2s, color 0.2s; }
        .dspm-management-panel-toggle-btn.panel-visible { transform: translateX(-340px); /* Adjust if panel width changes */ }
        body { transition: background-color 0.3s ease-in-out, color 0.3s ease-in-out; }
    `);

    // --- UI Element Variables ---
    let usagePromptDropdown;
    let promptUsageButtonWrapper;
    let promptUsageButton;
    let managementSidePanel;
    let managementPanelToggleButton;
    let themeToggleButton;

    // --- Core Functions ---
    function createElements() {
        const buttonContainer = $('.ec4f5d61');
        if (!buttonContainer || $(`#${SCRIPT_PREFIX}usage_button`)) return;

        promptUsageButtonWrapper = document.createElement('div');
        promptUsageButtonWrapper.className = 'prompt-usage-button-wrapper inline-flex items-center relative';

        promptUsageButton = document.createElement('button');
        promptUsageButton.id = SCRIPT_PREFIX + 'usage_button';
        promptUsageButton.className = `prompt-usage-button flex items-center justify-center h-[34px] px-[14px] py-[8px] text-[14px] font-medium bg-white dark:bg-gray-800 text-[#4c4c4c] dark:text-gray-300 border border-[rgba(0,0,0,0.12)] dark:border-gray-600 rounded-[10px] hover:bg-[#E0E4ED] dark:hover:bg-gray-700 hover:text-gray-900 dark:hover:text-white focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:focus:ring-offset-gray-800 transition-all duration-150 ease-in-out transform hover:scale-105 ml-1 mr-2.5`;
        promptUsageButton.innerHTML = `<i class="fas fa-star w-[18px] h-[18px] mr-2"></i><span class="button-text-content">提示词库</span>`;
        promptUsageButtonWrapper.appendChild(promptUsageButton);

        const buttonsInContainer = buttonContainer.children;
        if (buttonsInContainer.length >= 1) buttonContainer.insertBefore(promptUsageButtonWrapper, buttonsInContainer[1]);
        else buttonContainer.appendChild(promptUsageButtonWrapper);

        usagePromptDropdown = document.createElement('div');
        usagePromptDropdown.id = SCRIPT_PREFIX + 'usage_dropdown';
        usagePromptDropdown.className = `usage-prompt-dropdown dspm-custom-scrollbar dspm-fade-in hidden absolute z-50 mt-2 w-72 md:w-80 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-md shadow-xl max-h-80 overflow-y-auto p-2 space-y-1`; // Dropdown uses overflow-y: auto
        document.body.appendChild(usagePromptDropdown);

        promptUsageButton.addEventListener('click', (e) => {
            e.stopPropagation();
            const isVisible = !usagePromptDropdown.classList.contains('hidden');
            if (isVisible) closeUsageDropdown();
            else openUsageDropdown();
        });

        managementPanelToggleButton = document.createElement('button');
        managementPanelToggleButton.id = SCRIPT_PREFIX + 'management_toggle_btn';
        managementPanelToggleButton.className = `dspm-management-panel-toggle-btn fixed top-1/2 -translate-y-1/2 right-0 z-40 p-2.5 bg-white dark:bg-gray-800 text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700 hover:text-gray-900 dark:hover:text-white border border-r-0 border-gray-300 dark:border-gray-600 rounded-l-md shadow-md focus:outline-none transition-all duration-150 ease-in-out transform hover:scale-105`;
        managementPanelToggleButton.title = '管理提示词';
        managementPanelToggleButton.innerHTML = `<i class="fas fa-cog fa-lg"></i>`;
        document.body.appendChild(managementPanelToggleButton);
        managementPanelToggleButton.addEventListener('click', (e) => { e.stopPropagation(); toggleManagementPanel(); });

        managementSidePanel = document.createElement('div');
        managementSidePanel.id = SCRIPT_PREFIX + 'management_side_panel';
        managementSidePanel.className = `dspm-management-side-panel fixed top-0 right-0 h-full w-full max-w-sm md:max-w-md z-50 bg-gray-50 dark:bg-gray-850 shadow-2xl flex flex-col border-l border-gray-200 dark:border-gray-700`;
        document.body.appendChild(managementSidePanel);

        document.addEventListener('click', (e) => {
            if (document.body.classList.contains(SCRIPT_PREFIX + 'modal-open')) {
                // If a modal is open, its own overlay click handler should manage closure.
                // This global listener should not interfere with modal overlay clicks.
                // The check e.target.id for overlays was removed here as it's redundant
                // with the modal's own specific overlay click handlers.
                return;
            }
            if (usagePromptDropdown && !usagePromptDropdown.classList.contains('hidden')) {
                if (!usagePromptDropdown.contains(e.target) && !promptUsageButton.contains(e.target)) {
                    closeUsageDropdown();
                }
            }
            if (managementSidePanel && managementSidePanel.classList.contains('visible')) {
                const importExportMenu = $(`#${SCRIPT_PREFIX}import_export_menu`);
                if (!managementSidePanel.contains(e.target) && !managementPanelToggleButton.contains(e.target) && !(importExportMenu && importExportMenu.contains(e.target))) {
                    toggleManagementPanel(false);
                }
            }
        });
    }

    function openUsageDropdown() {
        if (!usagePromptDropdown || !promptUsageButton) return;
        updateUsageDropdown();
        usagePromptDropdown.classList.remove('hidden');
        promptUsageButton.classList.add('active', 'bg-[#E0E4ED]', 'dark:bg-gray-700');
        updateUsageDropdownPosition();
    }

    function closeUsageDropdown() {
        if (!usagePromptDropdown || !promptUsageButton) return;
        usagePromptDropdown.classList.add('hidden');
        promptUsageButton.classList.remove('active', 'bg-[#E0E4ED]', 'dark:bg-gray-700');
    }

    function updateUsageDropdownPosition() {
        if (!usagePromptDropdown || !promptUsageButtonWrapper) return;
        const buttonRect = promptUsageButtonWrapper.getBoundingClientRect();
        const dropdownHeight = usagePromptDropdown.offsetHeight;
        const windowHeight = window.innerHeight;
        let top = buttonRect.bottom + 8;
        if (top + dropdownHeight > windowHeight - 20) top = buttonRect.top - dropdownHeight - 8;
        if (top < 20) top = 20;
        usagePromptDropdown.style.left = `${buttonRect.left}px`;
        usagePromptDropdown.style.top = `${top}px`;
        usagePromptDropdown.style.minWidth = `${buttonRect.width}px`;
    }

    function updateUsageDropdown() {
        if (!usagePromptDropdown) return;
        const savedPrompts = GM_getValue(SCRIPT_PREFIX + 'prompts', []);
        usagePromptDropdown.innerHTML = '';
        if (savedPrompts.length === 0) {
            usagePromptDropdown.innerHTML = `<div class="p-4 text-center text-sm text-gray-500 dark:text-gray-400">暂无提示词。请通过右侧 <i class="fas fa-cog mx-1"></i> 管理面板添加。</div>`;
            return;
        }
        savedPrompts.forEach(prompt => {
            const item = document.createElement('div');
            item.className = `p-2.5 rounded-md cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors duration-150`;
            item.innerHTML = `<div class="font-semibold text-sm text-gray-800 dark:text-gray-200 truncate">${prompt.title}</div><div class="text-xs text-gray-500 dark:text-gray-400 truncate mt-0.5">${prompt.content}</div>`;
            item.addEventListener('click', async () => {
                try {
                    const textArea = $('#chat-input');
                    if (!textArea) throw new Error('Chat input not found');
                    textArea.focus();
                    document.execCommand('selectAll', false, null);
                    document.execCommand('delete', false, null);
                    document.execCommand('insertText', false, prompt.content);
                    closeUsageDropdown();
                } catch (err) { console.error('DSPM: Failed to fill prompt:', err); alert('填充提示词失败。'); }
            });
            usagePromptDropdown.appendChild(item);
        });
    }

    function toggleManagementPanel(forceState) {
        if (!managementSidePanel || !managementPanelToggleButton) return;
        const show = forceState !== undefined ? forceState : !managementSidePanel.classList.contains('visible');
        if (show) {
            updateManagementPanelContent();
            managementSidePanel.classList.add('visible');
            managementPanelToggleButton.classList.add('panel-visible');
        } else {
            managementSidePanel.classList.remove('visible');
            managementPanelToggleButton.classList.remove('panel-visible');
        }
    }

    function updateManagementPanelContent() {
        if (!managementSidePanel) return;
        const savedPrompts = GM_getValue(SCRIPT_PREFIX + 'prompts', []);
        const currentTheme = document.documentElement.classList.contains('dark') ? 'dark' : 'light';

        managementSidePanel.innerHTML = `
            <div class="flex flex-col h-full">
                <div class="flex items-center justify-between p-4 border-b border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 sticky top-0 z-10">
                    <h3 class="text-lg font-semibold text-gray-900 dark:text-white">管理提示词</h3>
                    <div class="flex items-center space-x-1">
                        <button id="${SCRIPT_PREFIX}theme_toggle_btn" title="切换主题" class="p-2 rounded-md text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700 hover:text-gray-700 dark:hover:text-gray-200 transition-colors">
                            <i class="fas ${currentTheme === 'dark' ? 'fa-sun' : 'fa-moon'} fa-fw"></i>
                        </button>
                        <button id="${SCRIPT_PREFIX}import_export_btn" title="导入/导出" class="p-2 rounded-md text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700 hover:text-gray-700 dark:hover:text-gray-200 transition-colors">
                            <i class="fas fa-file-import fa-fw"></i>
                        </button>
                        <button id="${SCRIPT_PREFIX}clear_all_btn" title="清空全部提示词" class="p-2 rounded-md text-red-500 dark:text-red-400 hover:bg-red-100 dark:hover:bg-red-700/30 hover:text-red-700 dark:hover:text-red-300 transition-colors">
                            <i class="fas fa-trash-alt fa-fw"></i>
                        </button>
                        <button id="${SCRIPT_PREFIX}add_prompt_btn" class="px-3 py-2 text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:focus:ring-offset-gray-800 transition-all duration-150 ease-in-out transform hover:scale-105">
                            <i class="fas fa-plus mr-1.5"></i> 创建
                        </button>
                    </div>
                </div>
                <div class="dspm-management-content-area flex-grow p-4 dspm-custom-scrollbar dspm-smooth-scroll space-y-3">
                    ${savedPrompts.length === 0 ?
                        `<div class="text-center py-10">
                            <i class="fas fa-folder-open fa-3x text-gray-400 dark:text-gray-500 mb-3"></i>
                            <p class="text-sm text-gray-500 dark:text-gray-400">暂无提示词,点击“创建”添加一个吧!</p>
                        </div>` :
                        savedPrompts.map((prompt, index) => `
                            <div id="${SCRIPT_PREFIX}prompt_card_${index}" class="dspm-prompt-card bg-white dark:bg-gray-750 p-3.5 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 hover:shadow-md hover:border-gray-300 dark:hover:border-gray-600 transition-all duration-150 ease-in-out">
                                <div class="flex items-center justify-between">
                                    <h4 class="text-md font-semibold text-gray-800 dark:text-gray-100 truncate pr-2 flex-grow">${prompt.title}</h4>
                                    <button data-index="${index}" class="dspm-edit-prompt-btn p-1.5 rounded-md text-gray-400 dark:text-gray-500 hover:bg-gray-100 dark:hover:bg-gray-600 hover:text-indigo-600 dark:hover:text-indigo-400 transition-colors">
                                        <i class="fas fa-pen fa-sm"></i>
                                    </button>
                                </div>
                                <p class="text-sm text-gray-600 dark:text-gray-300 mt-1.5 break-words line-clamp-2">${prompt.content}</p>
                            </div>`).join('')
                    }
                </div>
            </div>`;

        themeToggleButton = $(`#${SCRIPT_PREFIX}theme_toggle_btn`, managementSidePanel);
        themeToggleButton.addEventListener('click', (e) => {
            e.stopPropagation();
            console.log('DSPM: Theme toggle clicked');
            const newTheme = document.documentElement.classList.contains('dark') ? 'light' : 'dark';
            applyTheme(newTheme);
            themeToggleButton.innerHTML = `<i class="fas ${newTheme === 'dark' ? 'fa-sun' : 'fa-moon'} fa-fw"></i>`;
        });
        $(`#${SCRIPT_PREFIX}add_prompt_btn`, managementSidePanel).addEventListener('click', (e) => { e.stopPropagation(); showEditPromptModal(); });
        $$('.dspm-edit-prompt-btn', managementSidePanel).forEach(button => {
            button.addEventListener('click', function(e) { e.stopPropagation(); showEditPromptModal(savedPrompts[parseInt(this.dataset.index)], parseInt(this.dataset.index)); });
        });
        setupImportExportMenu($(`#${SCRIPT_PREFIX}import_export_btn`, managementSidePanel));

        const clearAllBtn = $(`#${SCRIPT_PREFIX}clear_all_btn`, managementSidePanel);
        if (clearAllBtn) {
            clearAllBtn.addEventListener('click', (e) => {
                e.stopPropagation();
                showConfirmationDialog(
                    '确认清空全部提示词',
                    '确定要删除所有已保存的提示词吗?此操作无法撤销。',
                    () => {
                        console.log('DSPM: Clearing all prompts.');
                        GM_setValue(SCRIPT_PREFIX + 'prompts', []);
                        updateManagementPanelContent();
                        updateUsageDropdown();
                        alert('所有提示词已清空!');
                    },
                    '清空全部'
                );
            });
        }
    }

    function showEditPromptModal(prompt = null, index = null) {
        const isEditing = prompt !== null;
        const modalId = SCRIPT_PREFIX + 'edit_modal_overlay';
        if ($(`#${modalId}`)) $(`#${modalId}`).remove();

        const modalOverlay = document.createElement('div');
        modalOverlay.id = modalId;
        modalOverlay.className = `fixed inset-0 z-[100] flex items-center justify-center p-4 bg-gray-900 bg-opacity-60 dark:bg-opacity-75 dspm-fade-in`;
        modalOverlay.innerHTML = `<div class="bg-white dark:bg-gray-800 bg-opacity-100 dark:bg-opacity-100 rounded-lg shadow-xl w-full max-w-lg p-6 space-y-4 transform transition-all duration-300 ease-out scale-95 opacity-0" role="dialog" aria-modal="true"> <div class="flex items-center justify-between pb-3 border-b border-gray-200 dark:border-gray-700"><h3 class="text-xl font-semibold text-gray-900 dark:text-white">${isEditing ? '编辑指令' : '创建新指令'}</h3>${isEditing ? `<button id="${SCRIPT_PREFIX}delete_prompt_btn" class="px-3 py-1.5 text-xs font-medium text-red-600 dark:text-red-400 bg-red-100 dark:bg-red-900/50 hover:bg-red-200 dark:hover:bg-red-800/70 rounded-md transition-colors"><i class="fas fa-trash-alt mr-1"></i> 删除</button>` : ''}<button id="${SCRIPT_PREFIX}close_modal_btn" class="p-1.5 rounded-md text-gray-400 dark:text-gray-500 hover:bg-gray-100 dark:hover:bg-gray-700 hover:text-gray-600 dark:hover:text-gray-300 transition-colors"><i class="fas fa-times fa-lg"></i></button></div> <div class="space-y-4"><div><label for="${SCRIPT_PREFIX}prompt_title" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">指令标题 <span class="text-red-500">*</span></label><input type="text" id="${SCRIPT_PREFIX}prompt_title" value="${isEditing ? prompt.title : ''}" placeholder="例如:周报小助手" class="w-full px-3 py-2 text-sm border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400"></div><div><label for="${SCRIPT_PREFIX}prompt_content" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">指令内容 <span class="text-red-500">*</span></label><textarea id="${SCRIPT_PREFIX}prompt_content" rows="6" placeholder="请输入希望AI执行的具体指令内容..." class="w-full px-3 py-2 text-sm border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dspm-custom-scrollbar">${isEditing ? prompt.content : ''}</textarea></div></div> <div class="pt-4 flex justify-end space-x-3 border-t border-gray-200 dark:border-gray-700"><button id="${SCRIPT_PREFIX}cancel_modal_btn" class="px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-700 border border-gray-300 dark:border-gray-500 rounded-md hover:bg-gray-50 dark:hover:bg-gray-600 transition-colors">取消</button><button id="${SCRIPT_PREFIX}save_prompt_btn" class="px-4 py-2 text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:focus:ring-offset-gray-800 transition-all duration-150 ease-in-out transform hover:scale-105">${isEditing ? '保存修改' : '创建指令'}</button></div></div>`;
        document.body.appendChild(modalOverlay);
        document.body.classList.add(SCRIPT_PREFIX + 'modal-open');

        setTimeout(() => { const modalContent = $('div[role="dialog"]', modalOverlay); if (modalContent) { modalContent.classList.remove('scale-95', 'opacity-0'); modalContent.classList.add('scale-100', 'opacity-100'); } }, 10);

        const closeModal = () => {
            const modalContent = $('div[role="dialog"]', modalOverlay);
            if (modalContent) {
                modalContent.classList.add('scale-95', 'opacity-0');
                modalContent.classList.remove('scale-100', 'opacity-100');
                setTimeout(() => {
                    if (modalOverlay) modalOverlay.remove(); // Check if modalOverlay still exists
                    document.body.classList.remove(SCRIPT_PREFIX + 'modal-open');
                 }, 300);
            } else {
                if (modalOverlay) modalOverlay.remove(); // Check if modalOverlay still exists
                document.body.classList.remove(SCRIPT_PREFIX + 'modal-open');
            }
        };

        $(`#${SCRIPT_PREFIX}close_modal_btn`, modalOverlay).onclick = closeModal;
        $(`#${SCRIPT_PREFIX}cancel_modal_btn`, modalOverlay).onclick = closeModal;

        // Diagnostic log for edit/create modal overlay click
        modalOverlay.onclick = (e) => {
            console.log('DSPM: Edit Modal Overlay clicked. Target:', e.target, 'Expected Overlay:', modalOverlay);
            if (e.target === modalOverlay) {
                console.log('DSPM: Closing Edit Modal due to overlay click.');
                closeModal();
            } else {
                console.log('DSPM: Edit Modal Overlay click ignored, target was not overlay itself.');
            }
        };

        if (!isEditing && $('#chat-input')?.value.trim()) $(`#${SCRIPT_PREFIX}prompt_content`, modalOverlay).value = $('#chat-input').value.trim();
        $(`#${SCRIPT_PREFIX}prompt_title`, modalOverlay).focus();

        if (isEditing) {
            $(`#${SCRIPT_PREFIX}delete_prompt_btn`, modalOverlay).onclick = () => {
                showConfirmationDialog('确认删除', `确定要删除指令 “<strong>${prompt.title}</strong>” 吗?此操作无法撤销。`, () => {
                    const prompts = GM_getValue(SCRIPT_PREFIX + 'prompts', []);
                    prompts.splice(index, 1); GM_setValue(SCRIPT_PREFIX + 'prompts', prompts);
                    closeModal(); updateManagementPanelContent(); updateUsageDropdown();
                });
            };
        }
        $(`#${SCRIPT_PREFIX}save_prompt_btn`, modalOverlay).onclick = () => {
            const title = $(`#${SCRIPT_PREFIX}prompt_title`, modalOverlay).value.trim();
            const content = $(`#${SCRIPT_PREFIX}prompt_content`, modalOverlay).value.trim();
            if (!title || !content) { alert(title ? '指令内容不能为空!' : '指令标题不能为空!'); (title ? $(`#${SCRIPT_PREFIX}prompt_content`, modalOverlay) : $(`#${SCRIPT_PREFIX}prompt_title`, modalOverlay)).focus(); return; }
            const prompts = GM_getValue(SCRIPT_PREFIX + 'prompts', []);
            if (isEditing) prompts[index] = { title, content }; else prompts.push({ title, content });
            GM_setValue(SCRIPT_PREFIX + 'prompts', prompts);
            closeModal(); updateManagementPanelContent(); updateUsageDropdown();
        };
    }

    function showConfirmationDialog(title, message, onConfirm, confirmButtonText = '确认删除') {
        const dialogId = SCRIPT_PREFIX + 'confirm_dialog_overlay';
        if ($(`#${dialogId}`)) $(`#${dialogId}`).remove();

        const dialogOverlay = document.createElement('div');
        dialogOverlay.id = dialogId;
        dialogOverlay.className = `fixed inset-0 z-[110] flex items-center justify-center p-4 bg-gray-900 bg-opacity-70 dark:bg-opacity-80 dspm-fade-in`;
        dialogOverlay.innerHTML = `<div class="bg-white dark:bg-gray-800 bg-opacity-100 dark:bg-opacity-100 rounded-lg shadow-xl w-full max-w-md p-6 space-y-4 transform transition-all duration-300 ease-out scale-95 opacity-0"><h3 class="text-lg font-medium text-gray-900 dark:text-white">${title}</h3><p class="text-sm text-gray-600 dark:text-gray-300">${message}</p><div class="flex justify-end space-x-3 pt-3"><button id="${SCRIPT_PREFIX}confirm_cancel" class="px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-700 border border-gray-300 dark:border-gray-500 rounded-md hover:bg-gray-50 dark:hover:bg-gray-600 transition-colors">取消</button><button id="${SCRIPT_PREFIX}confirm_ok" class="px-4 py-2 text-sm font-medium text-white bg-red-600 hover:bg-red-700 rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 dark:focus:ring-offset-gray-800">${confirmButtonText}</button></div></div>`;
        document.body.appendChild(dialogOverlay);
        document.body.classList.add(SCRIPT_PREFIX + 'modal-open');

        setTimeout(() => { const dialogContent = $('div > div', dialogOverlay); if(dialogContent) { dialogContent.classList.remove('scale-95', 'opacity-0'); dialogContent.classList.add('scale-100', 'opacity-100'); } }, 10);


        const closeDialog = () => {
            const dialogContent = $('div > div', dialogOverlay);
            if(dialogContent) {
                dialogContent.classList.add('scale-95', 'opacity-0');
                dialogContent.classList.remove('scale-100', 'opacity-100');
            }
            setTimeout(() => {
                if (dialogOverlay) dialogOverlay.remove(); // Check if dialogOverlay still exists
                if (!$(`#${SCRIPT_PREFIX}edit_modal_overlay`)) {
                    document.body.classList.remove(SCRIPT_PREFIX + 'modal-open');
                }
            }, 300);
        };
        $(`#${SCRIPT_PREFIX}confirm_cancel`, dialogOverlay).onclick = closeDialog;
        $(`#${SCRIPT_PREFIX}confirm_ok`, dialogOverlay).onclick = () => { onConfirm(); closeDialog(); };

        // Diagnostic log for confirmation dialog overlay click
        dialogOverlay.onclick = (e) => {
            console.log('DSPM: Confirm Dialog Overlay clicked. Target:', e.target, 'Expected Overlay:', dialogOverlay);
            if (e.target === dialogOverlay) {
                console.log('DSPM: Closing Confirm Dialog due to overlay click.');
                closeDialog();
            } else {
                console.log('DSPM: Confirm Dialog Overlay click ignored, target was not overlay itself.');
            }
        };
    }

    function setupImportExportMenu(buttonElement) {
        if (!buttonElement) return;
        const newBtn = buttonElement.cloneNode(true);
        buttonElement.parentNode.replaceChild(newBtn, buttonElement);
        buttonElement = newBtn;

        buttonElement.addEventListener('click', (e) => {
            e.stopPropagation();
            const menuId = SCRIPT_PREFIX + 'import_export_menu';
            const existingMenu = $(`#${menuId}`);
            if (existingMenu) { existingMenu.remove(); return; }

            const menu = document.createElement('div'); menu.id = menuId;
            menu.className = `absolute z-[120] mt-1.5 w-48 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-md shadow-lg py-1 dspm-fade-in`;
            menu.innerHTML = `<button data-action="export" class="w-full text-left px-3 py-1.5 text-sm text-gray-700 dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-700 hover:text-gray-900 dark:hover:text-white transition-colors flex items-center"><i class="fas fa-file-export w-4 h-4 mr-2.5 text-gray-400 dark:text-gray-500"></i> 导出 JSON</button><button data-action="import" class="w-full text-left px-3 py-1.5 text-sm text-gray-700 dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-700 hover:text-gray-900 dark:hover:text-white transition-colors flex items-center"><i class="fas fa-file-import w-4 h-4 mr-2.5 text-gray-400 dark:text-gray-500"></i> 导入 JSON</button>`;
            document.body.appendChild(menu);
            const btnRect = buttonElement.getBoundingClientRect();
            menu.style.top = `${btnRect.bottom + 4}px`;
            menu.style.left = `${btnRect.right - menu.offsetWidth}px`;

            const closeMenu = () => menu.remove();
            const clickOutsideHandler = (event) => {
                if (!menu.contains(event.target) && !buttonElement.contains(event.target)) {
                    closeMenu();
                    document.removeEventListener('click', clickOutsideHandler, true);
                }
            };
            document.addEventListener('click', clickOutsideHandler, true);

            $$('button', menu).forEach(btn => {
                btn.onclick = () => {
                    console.log(`DSPM: Import/Export action: ${btn.dataset.action}`);
                    if (btn.dataset.action === 'export') {
                        const prompts = GM_getValue(SCRIPT_PREFIX + 'prompts', []);
                        if (prompts.length === 0) { alert("没有可导出的提示词。"); closeMenu(); return; }
                        const blob = new Blob([JSON.stringify(prompts, null, 2)], { type: 'application/json' });
                        const url = URL.createObjectURL(blob);
                        const a = document.createElement('a');
                        a.href = url; a.download = `deepseek-prompts-${new Date().toISOString().slice(0,10)}.json`;
                        document.body.appendChild(a);
                        a.click();
                        document.body.removeChild(a);
                        URL.revokeObjectURL(url);
                        console.log('DSPM: Export initiated.');
                    } else if (btn.dataset.action === 'import') {
                        const input = document.createElement('input');
                        input.type = 'file'; input.accept = '.json';
                        input.onchange = (ev) => {
                            const file = ev.target.files[0];
                            if (file) {
                                console.log(`DSPM: File selected for import: ${file.name}`);
                                const reader = new FileReader();
                                reader.onload = (le) => {
                                    try {
                                        const imported = JSON.parse(le.target.result);
                                        if (Array.isArray(imported) && imported.every(p => typeof p.title === 'string' && typeof p.content === 'string')) {
                                            GM_setValue(SCRIPT_PREFIX + 'prompts', imported);
                                            updateManagementPanelContent(); updateUsageDropdown();
                                            alert('提示词导入成功!');
                                            console.log('DSPM: Prompts imported successfully.');
                                        } else { alert('导入失败:文件格式无效。'); console.error('DSPM: Invalid import file format.');}
                                    } catch (err) { console.error("DSPM: Import error:", err); alert('导入失败:无法解析文件。'); }
                                };
                                reader.readAsText(file);
                            }
                        };
                        input.click();
                        console.log('DSPM: Import file dialog opened.');
                    }
                    closeMenu();
                };
            });
        });
    }

    async function initScript() {
        try {
            console.log('DSPM: Initializing script...');
            await injectCDN('https://cdn.tailwindcss.com/3.4.3', 'script');
            console.log('DSPM: TailwindCSS CDN loaded.');
            await injectCDN('https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css', 'link');
            console.log('DSPM: Font Awesome CDN loaded.');
            initTheme();
            createElements();
            console.log('DSPM: Script initialized successfully.');
        } catch (error) {
            console.error('DSPM: Failed to initialize script or load resources.', error);
            alert('提示词管理脚本加载资源失败,部分功能可能无法正常使用。请检查网络连接或浏览器控制台。');
        }
    }

    const observer = new MutationObserver(debounce(() => {
        if (!$(`#${SCRIPT_PREFIX}usage_button`) && $('.ec4f5d61')) {
            console.log("DSPM: Detected button container via MutationObserver, attempting to create elements.");
            createElements();
        }
    }, 500));
    observer.observe(document.body, { childList: true, subtree: true });

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', initScript);
    } else {
        initScript();
    }
    window.addEventListener('resize', debounce(() => {
        if (usagePromptDropdown && !usagePromptDropdown.classList.contains('hidden')) {
            updateUsageDropdownPosition();
        }
        const importExportMenu = $(`#${SCRIPT_PREFIX}import_export_menu`);
        if (importExportMenu) {
            const importExportBtn = $(`#${SCRIPT_PREFIX}import_export_btn`, managementSidePanel);
            if (importExportBtn) {
                const btnRect = importExportBtn.getBoundingClientRect();
                importExportMenu.style.top = `${btnRect.bottom + 4}px`;
                importExportMenu.style.left = `${btnRect.right - importExportMenu.offsetWidth}px`;
            }
        }
    }, 150));

})();