Greasy Fork

Greasy Fork is available in English.

幻觉(Illusion)

幻觉(Illusion)是一个精简的跨平台 Prompts 管理工具,支持在以下 AI 平台使用:Google AI Studio, OpenAI ChatGPT, Anthropic Claude 和 DeepSeek Chat。

当前为 2025-02-20 提交的版本,查看 最新版本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         幻觉(Illusion)
// @icon         https://raw.githubusercontent.com/cattail-mutt/Illusion/refs/heads/main/resources/icons/illusion.png
// @namespace    LINUX_DO
// @version      1.2
// @description  幻觉(Illusion)是一个精简的跨平台 Prompts 管理工具,支持在以下 AI 平台使用:Google AI Studio, OpenAI ChatGPT, Anthropic Claude 和 DeepSeek Chat。
// @author       Mukai
// @license      MIT
// @match        https://aistudio.google.com/*
// @match        https://chatgpt.com/*
// @match        https://claude.ai/*
// @match        https://chat.deepseek.com/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_getResourceText
// @resource     PROMPTS https://raw.githubusercontent.com/cattail-mutt/Illusion/refs/heads/main/resources/config/prompts.yaml
// @resource     THEMES https://raw.githubusercontent.com/cattail-mutt/Illusion/refs/heads/main/resources/config/themes.json
// @resource     CSS https://raw.githubusercontent.com/cattail-mutt/Illusion/refs/heads/main/resources/styles/illusion.css
// @require      https://cdnjs.cloudflare.com/ajax/libs/js-yaml/4.1.0/js-yaml.min.js
// @homepage     http://greasyfork.icu/zh-CN/scripts/527451-%E5%B9%BB%E8%A7%89-illusion
// ==/UserScript==

(function() {
    'use strict';

    let modalRef = null;
    let overlayRef = null;
    let savedPrompts = {};
    
    window.onerror = function(msg, url, line, col, error) {
        console.error('[Illusion]错误:', {msg, url, line, col, error});
        return false;
    };
    
    const debug = {
        enabled: true,
        log: (...args) => debug.enabled && console.log('[Illusion]日志:', ...args),
        error: (...args) => console.error('[Illusion]错误:', ...args),
        warn: (...args) => console.warn('[Illusion]警告:', ...args),
        trace: (...args) => debug.enabled && console.trace('[Illusion]追踪:', ...args)
    };

    const initialPrompts = jsyaml.load(GM_getResourceText('PROMPTS'));
    const THEMECONFIG = JSON.parse(GM_getResourceText('THEMES'));
    debug.log('[Illusion]日志: PROMPTS解析结果:', initialPrompts);
    debug.log('[Illusion]日志: THEMES解析结果:', THEMECONFIG);

    function dispatchEvents(element, events) {
        events.forEach(eventName => {
            const event = eventName === 'input' 
                ? new InputEvent(eventName, { bubbles: true }) 
                : new Event(eventName, { bubbles: true });
            element.dispatchEvent(event);
        });
    }

    // ChatGPT 和 Claude 均使用了 ProseMirror 库构建富文本编辑器
    function createParagraph(line) {
        const p = document.createElement('p');
        if (line.trim()) {
            p.textContent = line;
        } else {
            p.innerHTML = '<br>';
        }
        return p;
    }

    const updateProseMirror = (editor, prompt) => {
        const paragraphs = Array.from(editor.querySelectorAll('p'));
        let currentContent = '';
        paragraphs.forEach(p => {
            const text = p.textContent.trim();
            if (text) {
                currentContent += text + '\n';
            } else {
                currentContent += '\n';
            }
        });
        let newContent = currentContent.trim();
        if (newContent) {
            newContent += '\n';
        }
        editor.innerHTML = '';
        if (newContent) {
            newContent.split('\n').forEach(line => {
                editor.appendChild(createParagraph(line));
            });
        }
        const lines = prompt.split('\n');
        lines.forEach((line, index) => {
            editor.appendChild(createParagraph(line));
            if (index < lines.length - 1 && !line.trim()) {
                const brP = document.createElement('p');
                brP.innerHTML = '<br>';
                editor.appendChild(brP);
            }
        });
        dispatchEvents(editor, ['input', 'change']);
    };

    // Gemini 和 DeepSeek 使用的均是纯文本输入框 <textarea>
    // DeepSeek 不支持对 textarea.value 直接更新
    const updateTextArea = async (textarea, prompt) => {
        const currentContent = textarea.value;
        const newContent = currentContent === '' 
            ? prompt 
            : currentContent + "\n" + prompt;
        const setter = Object.getOwnPropertyDescriptor(
            window.HTMLTextAreaElement.prototype,
            "value"
        ).set;
        setter.call(textarea, newContent);
        dispatchEvents(textarea, ['focus', 'input', 'change']);
    };

    const CONFIG = {
        debugEnabled: true,
        maxRetries: 3,
        retryDelay: 1000,
        initTimeout: 10000,
        eventDelay: 50,
        eventTimeout: 1000,
        sites: {
            CHATGPT: {
                id: 'chatgpt',
                icon: 'https://raw.githubusercontent.com/cattail-mutt/Illusion/refs/heads/main/resources/icons/chatgpt.svg',
                buttonSize: '48px',
                selector: 'div.ProseMirror[contenteditable=true]',
                setPrompt: updateProseMirror
            },
            CLAUDE: {
                id: 'claude',
                icon: `<svg xmlns="http://www.w3.org/2000/svg" style="flex:none;line-height:1" viewBox="0 0 24 24"><title>Claude</title><path d="M4.709 15.955l4.72-2.647.08-.23-.08-.128H9.2l-.79-.048-2.698-.073-2.339-.097-2.266-.122-.571-.121L0 11.784l.055-.352.48-.321.686.06 1.52.103 2.278.158 1.652.097 2.449.255h.389l.055-.157-.134-.098-.103-.097-2.358-1.596-2.552-1.688-1.336-.972-.724-.491-.364-.462-.158-1.008.656-.722.881.06.225.061.893.686 1.908 1.476 2.491 1.833.365.304.145-.103.019-.073-.164-.274-1.355-2.446-1.446-2.49-.644-1.032-.17-.619a2.97 2.97 0 01-.104-.729L6.283.134 6.696 0l.996.134.42.364.62 1.414 1.002 2.229 1.555 3.03.456.898.243.832.091.255h.158V9.01l.128-1.706.237-2.095.23-2.695.08-.76.376-.91.747-.492.584.28.48.685-.067.444-.286 1.851-.559 2.903-.364 1.942h.212l.243-.242.985-1.306 1.652-2.064.73-.82.85-.904.547-.431h1.033l.76 1.129-.34 1.166-1.064 1.347-.881 1.142-1.264 1.7-.79 1.36.073.11.188-.02 2.856-.606 1.543-.28 1.841-.315.833.388.091.395-.328.807-1.969.486-2.309.462-3.439.813-.042.03.049.061 1.549.146.662.036h1.622l3.02.225.79.522.474.638-.079.485-1.215.62-1.64-.389-3.829-.91-1.312-.329h-.182v.11l1.093 1.068 2.006 1.81 2.509 2.33.127.578-.322.455-.34-.049-2.205-1.657-.851-.747-1.926-1.62h-.128v.17l.444.649 2.345 3.521.122 1.08-.17.353-.608.213-.668-.122-1.374-1.925-1.415-2.167-1.143-1.943-.14.08-.674 7.254-.316.37-.729.28-.607-.461-.322-.747.322-1.476.389-1.924.315-1.53.286-1.9.17-.632-.012-.042-.14.018-1.434 1.967-2.18 2.945-1.726 1.845-.414.164-.717-.37.067-.662.401-.589 2.388-3.036 1.44-1.882.93-1.086-.006-.158h-.055L4.132 18.56l-1.13.146-.487-.456.061-.746.231-.243 1.908-1.312-.006.006z" fill="#D97757" fill-rule="nonzero"/></svg>`,
                buttonSize: '48px',
                selector: 'div.ProseMirror[contenteditable=true]',
                setPrompt: updateProseMirror
            },
            GEMINI: {
                id: 'gemini',
                icon: 'https://www.gstatic.com/lamda/images/gemini_sparkle_v002_d4735304ff6292a690345.svg',
                buttonSize: '48px',
                selector: 'textarea[aria-label="Type something"]',
                setPrompt: updateTextArea
            },
            DEEPSEEK: {
                id: 'deepseek',
                icon: 'https://raw.githubusercontent.com/cattail-mutt/Illusion/refs/heads/main/resources/icons/deepseek.svg',
                buttonSize: '48px',
                selector: 'textarea[id="chat-input"]',
                setPrompt: updateTextArea
            }
        }
    };

    function loadExternalCSS() {
        const style = document.createElement('style');
        style.textContent = GM_getResourceText('CSS');
        document.head.appendChild(style);
    }

    function applyThemeVariables() {
        const currentSite = getCurrentSite();
        const theme = THEMECONFIG[currentSite];
        const config = Object.values(CONFIG.sites).find(s => s.id === currentSite);
        const root = document.documentElement;
        root.style.setProperty('--button-size', config.buttonSize);
        root.style.setProperty('--button-bg', theme.button.bg);
        root.style.setProperty('--border-color', theme.border);
        root.style.setProperty('--button-hover', theme.button.hover);
        root.style.setProperty('--panel-bg', theme.panel.bg);
        root.style.setProperty('--panel-button-bg', theme.panel.buttonBg);
        root.style.setProperty('--panel-button-hover', theme.panel.buttonHover);
        root.style.setProperty('--text-color', theme.text);
        root.style.setProperty('--secondary-bg', theme.secondary);
    }

    function waitForElement(selector, maxTimeout = CONFIG.initTimeout) {
        return new Promise((resolve, reject) => {
            const element = document.querySelector(selector);
            if(element) {
                return resolve(element);
            }
            let timeout;
            const observer = new MutationObserver(() => {
                const el = document.querySelector(selector);
                if (el) {
                    observer.disconnect();
                    clearTimeout(timeout);
                    resolve(el);
                }
            });
            timeout = setTimeout(() => {
                observer.disconnect();
                reject(new Error(`Timeout waiting for ${selector}`));
            }, maxTimeout);
            observer.observe(document.body, {
                childList: true,
                subtree: true
            });
        });
    }

    function getCurrentSite() {
        const url = window.location.href;
        if(url.includes('aistudio.google.com')) return CONFIG.sites.GEMINI.id;
        if(url.includes('chatgpt.com')) return CONFIG.sites.CHATGPT.id;
        if(url.includes('claude.ai')) return CONFIG.sites.CLAUDE.id;
        if(url.includes('chat.deepseek.com')) return CONFIG.sites.DEEPSEEK.id;
    }

    function createElement(tag, attributes = {}) {
        const element = document.createElement(tag);
        Object.entries(attributes).forEach(([key, value]) => {
            if (key === 'className') {
                element.className = value;
            } else if (key === 'textContent') {
                element.textContent = value;
            } else if (key === 'style' && typeof value === 'object') {
                Object.assign(element.style, value);
            } else if (key === 'onclick') {
                element.addEventListener('click', value);
            } else if (key.startsWith('data-')) {
                element.setAttribute(key, value);
            } else if (key === 'html') {
                element.innerHTML = value;
            } else if (key === 'on' && typeof value === 'object') {
                Object.entries(value).forEach(([event, handler]) => {
                    element.addEventListener(event, handler);
                });
            } else {
                element.setAttribute(key, value);
            }
        });
        return element;
    }

    function loadPrompts() {
        const storedPrompts = GM_getValue('prompts');
        if (!storedPrompts) {
            savedPrompts = initialPrompts;
            GM_setValue('prompts', savedPrompts);
        } else {
            savedPrompts = storedPrompts;
        }
        return savedPrompts;
    }

    function saveNewPrompt(id, content) {
        savedPrompts[id] = content;
        GM_setValue('prompts', savedPrompts);
        updateDatalist();
        debug.log('New prompt saved:', id);
    }

    async function setPromptWithRetry(site, prompt, maxRetries = CONFIG.maxRetries) {
        return new Promise(async (resolve, reject) => {
            let attempts = 0;
            const trySetPrompt = async () => {
                try {
                    const config = Object.values(CONFIG.sites).find(s => s.id === site);
                    if(!config || !config.setPrompt) {
                        return reject(new Error(`No prompt setter configured for site: ${site}`));
                    }
                    const editor = document.querySelector(config.selector);
                    if(!editor) {
                        throw new Error('Editor element not found');
                    }
                    await config.setPrompt(editor, prompt);
                    resolve(true);
                } catch (err) {
                    if (attempts < maxRetries) {
                        attempts++;
                        setTimeout(trySetPrompt, CONFIG.retryDelay);
                    } else {
                        reject(err);
                    }
                }
            };
            trySetPrompt();
        });
    }

    function initializeUI() {
        loadExternalCSS();
        applyThemeVariables();
        const button = createButton();
        const panel = createPanel();
        const { modal, overlay } = createModal();
        setupEventListeners(button, panel, modal, overlay);
    }

    function createButton() {
        const button = createElement('div', {
            className: 'illusion-button',
            'data-tooltip': 'Illusion',
            'data-tooltip-position': 'left',
            'aria-label': 'Illusion'
        });
        const currentSite = getCurrentSite();
        const config = Object.values(CONFIG.sites).find(s => s.id === currentSite);
        if(config.icon.startsWith('http')) {
            const img = createElement('img', {
                src: config.icon,
                width: '24',
                height: '24',
                style: {
                    pointerEvents: 'none'
                }
            });
            button.appendChild(img);
        } else {
            button.innerHTML = config.icon;
            const svg = button.querySelector('svg');
            if(svg) {
                svg.style.width = '24px';
                svg.style.height = '24px';
                svg.style.pointerEvents = 'none';
            }
        }
        document.body.appendChild(button);
        makeDraggable(button);
        debug.log('Button created');
        return button;
    }

    function createPanel() {
        const panel = createElement('div', {
            className: 'illusion-panel'
        });
        const title = createElement('div', {
            className: 'panel-title',
            textContent: 'Illusion'
        });
        panel.appendChild(title);
        const inputGroup = createElement('div', {
            className: 'input-group'
        });
        const input = createElement('input', {
            className: 'prompt-input',
            type: 'text',
            list: 'prompt-options',
            placeholder: '查找 Prompt'
        });
        const datalist = createElement('datalist', {
            id: 'prompt-options'
        });
        Object.keys(savedPrompts).forEach(id => {
            const option = createElement('option', {
                value: id
            });
            datalist.appendChild(option);
        });
        inputGroup.appendChild(input);
        inputGroup.appendChild(datalist);
        panel.appendChild(inputGroup);
        const buttonGroup = createElement('div', {
            className: 'button-group'
        });
        const newButton = createElement('button', {
            className: 'panel-button',
            textContent: 'New Prompt',
            'data-action': 'new',
            onclick: () => {
                debug.log('New prompt button clicked');
                const modal = document.querySelector('.modal');
                const overlay = document.querySelector('.modal-overlay');
                if (modal && overlay) {
                    showNewPromptModal(modal, overlay);
                } else {
                    debug.error('Modal or overlay elements not found');
                }
            }
        });
        buttonGroup.appendChild(newButton);
        const manageButton = createElement('button', {
            className: 'panel-button',
            textContent: 'Manage',
            'data-action': 'manage',
            onclick: () => {
                debug.log('Manage button clicked');
                const modal = document.querySelector('.modal');
                const overlay = document.querySelector('.modal-overlay');
                if (modal && overlay) {
                    showManagePromptsModal(modal, overlay);
                } else {
                    debug.error('Modal or overlay elements not found');
                }
            }
        });
        buttonGroup.appendChild(manageButton);
        panel.appendChild(buttonGroup);
        document.body.appendChild(panel);
        debug.log('Panel created');
        return panel;
    }

    function createModal() {
        overlayRef = createElement('div', {
            className: 'modal-overlay'
        });
        document.body.appendChild(overlayRef);
        modalRef = createElement('div', {
            className: 'modal',
            role: 'dialog',
            'aria-modal': 'true'
        });
        document.body.appendChild(modalRef);
        return { modal: modalRef, overlay: overlayRef };
    }

    function createModalFooter(buttonConfigs, modal, overlay) {
        const footer = createElement('div', { className: 'modal-footer' });
        buttonConfigs.forEach(config => {
            const btn = createElement('button', {
                className: 'panel-button',
                textContent: config.text,
                onclick: () => {
                    config.onClick && config.onClick(modal, overlay);
                }
            });
            footer.appendChild(btn);
        });
        return footer;
    }

    function openModal(modal, overlay, container) {
        modal.textContent = '';
        modal.appendChild(container);
        modal.classList.add('visible');
        overlay.classList.add('visible');
    }

    function hideModal(modal, overlay) {
        modal.classList.remove('visible');
        overlay.classList.remove('visible');
    }

    function getEventPosition(e) {
        if (e.touches && e.touches.length) {
            return { x: e.touches[0].clientX, y: e.touches[0].clientY };
        }
        return { x: e.clientX, y: e.clientY };
    }

    function makeDraggable(button) {
        let isDragging = false;
        let startX, startY;
        let initialX, initialY;
        let lastValidX, lastValidY;
        let dragThrottle;

        function setButtonPosition(x, y) {
            button.style.left = x + 'px';
            button.style.top = y + 'px';
            GM_setValue('buttonPosition', { x, y });
        }

        function dragStart(e) {
            const pos = getEventPosition(e);
            startX = pos.x;
            startY = pos.y;
            
            const rect = button.getBoundingClientRect();
            initialX = rect.left;
            initialY = rect.top;
            
            isDragging = true;
            button.classList.remove('docked');
            button.classList.add('dragging');
        }

        function dragEnd() {
            if (!isDragging) return;
            isDragging = false;
            button.classList.remove('dragging');
    
            const rect = button.getBoundingClientRect();
            const viewportWidth = window.innerWidth;
            const threshold = viewportWidth * 0.3;
    
            if (rect.left > viewportWidth - threshold) {
                setButtonPosition(viewportWidth - rect.width, rect.top);
                button.classList.add('docked');
            } else if (rect.left < threshold) {
                setButtonPosition(0, rect.top);
                button.classList.add('docked');
            }
        }

        function drag(e) {
            if (!isDragging) return;
            e.preventDefault();
    
            if (dragThrottle) return;
            dragThrottle = setTimeout(() => {
                dragThrottle = null;
            }, 16);
            
            const { x: currentX, y: currentY } = getEventPosition(e);
            const deltaX = currentX - startX;
            const deltaY = currentY - startY;

            requestAnimationFrame(() => {
                setButtonPosition(initialX + deltaX, initialY + deltaY);
                lastValidX = initialX + deltaX;
                lastValidY = initialY + deltaY;
            });
        }

        button.addEventListener('touchstart', dragStart, { passive: false });
        button.addEventListener('touchend', dragEnd, { passive: false });
        button.addEventListener('touchmove', drag, { passive: false });
        button.addEventListener('mousedown', dragStart);
        document.addEventListener('mousemove', drag);
        document.addEventListener('mouseup', dragEnd);
        window.addEventListener('resize', () => {
            if (lastValidX !== undefined && lastValidY !== undefined) {
                setButtonPosition(lastValidX, lastValidY);
            }
        });
        const savedPosition = GM_getValue('buttonPosition');
        if (savedPosition) {
            setButtonPosition(savedPosition.x, savedPosition.y);
        } else {
            setButtonPosition(
                window.innerWidth - button.offsetWidth - 20, 
                window.innerHeight / 2 - button.offsetHeight / 2
            );
        }
    }

    function createModalContainer(title, contentElement, footerElement) {
        const container = createElement('div');
        const header = createElement('div', {
            className: 'modal-header',
            textContent: title
        });
        container.appendChild(header);
        container.appendChild(contentElement);
        container.appendChild(footerElement);
        return container;
    }

    function createFormGroup(labelText, inputOptions) {
        const group = createElement('div', { className: 'form-group' });
        const label = createElement('label', {
            className: 'form-label',
            textContent: labelText
        });
        let input;
        if (inputOptions.type === 'textarea') {
            input = createElement('textarea', inputOptions);
        } else {
            input = createElement('input', inputOptions);
        }
        group.appendChild(label);
        group.appendChild(input);
        return { group, input };
    }

    function createPromptModal({ title, promptId, promptContent, isEditable, onSave, modal, overlay }) {
        const content = createElement('div', { className: 'modal-content' });
        const { group: idGroup, input: idInput } = createFormGroup('Prompt ID', {
            className: 'form-input',
            type: 'text',
            value: promptId || '',
            placeholder: '输入Prompt的标识名称'
        });
        if (promptId && !isEditable) {
            idInput.disabled = true;
        }
        content.appendChild(idGroup);
        const { group: contentGroup, input: contentInput } = createFormGroup('Prompt Content', {
            className: 'form-textarea',
            type: 'textarea',
            value: promptContent || '',
            placeholder: '输入Prompt内容'
        });
        content.appendChild(contentGroup);
        const footerButtons = [
            {
                text: 'Cancel',
                onClick: (modal, overlay) => {
                    hideModal(modal, overlay);
                }
            },
            {
                text: promptId ? 'Update' : 'Save',
                onClick: () => {
                    const idVal = idInput.value.trim();
                    const contentVal = contentInput.value.trim();
                    onSave(idVal, contentVal);
                }
            }
        ];
        const footer = createModalFooter(footerButtons, modal, overlay);
        const container = createModalContainer(title, content, footer);
        openModal(modal, overlay, container);
        setTimeout(() => contentInput.focus(), 100);
    }

    function showNewPromptModal(modal, overlay) {
        createPromptModal({
            title: 'New Prompt',
            promptId: '',
            promptContent: '',
            isEditable: true,
            onSave: (id, content) => {
                if (id && content) {
                    try {
                        saveNewPrompt(id, content);
                        hideModal(modal, overlay);
                    } catch (err) {
                        debug.error('Error saving new prompt:', err);
                        alert('保存失败,请重试');
                    }
                } else {
                    alert('请填写所有必填字段');
                }
            },
            modal,
            overlay
        });
    }
    
    function showEditPromptModal(modal, overlay, id, content) {
        createPromptModal({
            title: 'Prompt Editing',
            promptId: id,
            promptContent: content,
            isEditable: false,
            onSave: (id, newContent) => {
                if (newContent) {
                    savedPrompts[id] = newContent;
                    GM_setValue('prompts', savedPrompts);
                    hideModal(modal, overlay);
                    showManagePromptsModal(modal, overlay);
                    updateDatalist();
                } else {
                    alert('内容不能为空');
                }
            },
            modal,
            overlay
        });
    }

    function showManagePromptsModal(modal, overlay) {
        const container = createElement('div');
        const header = createElement('div', {
            className: 'modal-header',
            textContent: 'Manage Prompts'
        });
        container.appendChild(header);
        const content = createElement('div', {
            className: 'modal-content'
        });
        Object.entries(savedPrompts).forEach(([id, promptContent]) => {
            const promptGroup = createElement('div', {
                className: 'form-group',
                style: {
                    display: 'flex',
                    alignItems: 'center',
                    justifyContent: 'space-between',
                    padding: '8px',
                    borderBottom: '1px solid #eee'
                }
            });
            const promptId = createElement('div', {
                className: 'form-label',
                style: { margin: '0', flex: '1' },
                textContent: id
            });
            const buttonGroup = createElement('div', {
                className: 'button-group',
                style: { marginLeft: '16px' }
            });
            const editButton = createElement('button', {
                className: 'panel-button',
                textContent: 'Edit',
                onclick: () => {
                    showEditPromptModal(modal, overlay, id, promptContent);
                }
            });
            const deleteButton = createElement('button', {
                className: 'panel-button',
                textContent: 'Delete',
                onclick: () => {
                    if (confirm(`确定要删除 "${id}" 吗?`)) {
                        delete savedPrompts[id];
                        GM_setValue('prompts', savedPrompts);
                        promptGroup.remove();
                        updateDatalist();
                    }
                }
            });
            buttonGroup.appendChild(editButton);
            buttonGroup.appendChild(deleteButton);
            promptGroup.appendChild(promptId);
            promptGroup.appendChild(buttonGroup);
            content.appendChild(promptGroup);
        });
        container.appendChild(content);
        const footer = createElement('div', {
            className: 'modal-footer'
        });
        const closeButton = createElement('button', {
            className: 'panel-button',
            textContent: 'Close',
            onclick: () => {
                hideModal(modal, overlay);
            }
        });
        footer.appendChild(closeButton);
        container.appendChild(footer);
        openModal(modal, overlay, container);
    }

    function updateDatalist() {
        const datalist = document.getElementById('prompt-options');
        if (!datalist) return;
        datalist.textContent = '';
        Object.keys(savedPrompts).forEach(id => {
            const option = createElement('option', { value: id });
            datalist.appendChild(option);
        });
    }

    function setupEventListeners(button, panel, modal, overlay) {
        panel.addEventListener('click', (e) => {
            e.stopPropagation();
        });

        button.addEventListener('click', (e) => {
            if(!e.target.classList.contains('dragging')) {
                e.stopPropagation();
                debug.log('Button clicked');
                panel.classList.toggle('visible');
            }
        });

        document.addEventListener('click', () => {
            if (panel.classList.contains('visible')) {
                debug.log('Clicking outside panel, hiding panel');
                panel.classList.remove('visible');
            }
        });

        panel.addEventListener('click', (e) => {
            const btn = e.target.closest('button[data-action]');
            if (!btn) return;
            
            const action = btn.dataset.action;
            debug.log('Panel button clicked:', action);
            
            try {
                if (action === 'new') {
                    showNewPromptModal(modalRef, overlayRef);
                } else if (action === 'manage') {
                    showManagePromptsModal(modalRef, overlayRef);
                }
            } catch (error) {
                debug.error('Error handling button click:', error);
            }
        });

        const promptInput = panel.querySelector('.prompt-input');

        promptInput.addEventListener('change', async (e) => {
            const selectedValue = e.target.value.trim();
            const promptContent = savedPrompts[selectedValue];
            if (promptContent) {
                debug.log('Setting prompt:', selectedValue);
                try {
                    const site = getCurrentSite();
                    const success = await setPromptWithRetry(site, promptContent);
                    if (success) {
                        debug.log('Set prompt: Done');
                    } else {
                        debug.error('Set prompt: Failed');
                        alert('Failed to set prompt. Please try again.');
                    }
                } catch(err) {
                    debug.error('Error setting prompt:', err);
                    alert('Error setting prompt: ' + err.message);
                }
            }
            e.target.value = '';
        });

        overlay.addEventListener('click', () => {
            hideModal(modal, overlay);
        });

        document.addEventListener('keydown', (e) => {
            if (e.key === 'Escape' && modal.classList.contains('visible')) {
                hideModal(modal, overlay);
            }
        });
    }

    async function initialize() {
        debug.log('Initializing...');
        try {
            const currentSite = getCurrentSite();
            if(!currentSite) {
                debug.error('Unsupported site');
                return;
            }
            savedPrompts = loadPrompts();
            const siteConfig = Object.values(CONFIG.sites).find(s => s.id === currentSite);
            if (!siteConfig) {
                debug.error('Site configuration not found for current site');
                return;
            }
            const editorSelector = siteConfig.selector;
            try {
                await waitForElement(editorSelector);
                debug.log('Editor element found');
            } catch(err) {
                debug.error('Editor element not found:', err);
                return;
            }
            initializeUI();
            debug.log('Initialization complete');
        } catch (error) {
            debug.error('Initialization failed:', error);
        }
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', initialize);
    } else {
        initialize();
    }
})();