GPT提示词浮窗

2024/8/9 23:35:04

目前為 2024-12-15 提交的版本,檢視 最新版本

// ==UserScript==
// @name        GPT提示词浮窗
// @namespace   Violentmonkey Scripts
// @match       https://chat.deepseek.com/*
// @match       https://demo.fuclaude.com/*
// @match       https://chatgpt.com/*
// @grant       GM_setValue
// @grant       GM_getValue
// @version     1.4
// @author      -
// @description 2024/8/9 23:35:04
// @license MIT
// ==/UserScript==
(function() {
    'use strict';

    // 保持原有的 Modal 相关函数
    function createModal() {
        const modalOverlay = document.createElement('div');
        modalOverlay.style.position = 'fixed';
        modalOverlay.style.top = '0';
        modalOverlay.style.left = '0';
        modalOverlay.style.width = '100%';
        modalOverlay.style.height = '100%';
        modalOverlay.style.backgroundColor = 'rgba(0, 0, 0, 0.5)';
        modalOverlay.style.display = 'flex';
        modalOverlay.style.justifyContent = 'center';
        modalOverlay.style.alignItems = 'center';
        modalOverlay.style.zIndex = '1001';

        const modalContent = document.createElement('div');
        modalContent.style.backgroundColor = '#2d3748';
        modalContent.style.padding = '20px';
        modalContent.style.borderRadius = '12px';
        modalContent.style.width = '400px';
        modalContent.style.maxWidth = '90%';
        modalContent.style.boxShadow = '0 4px 6px rgba(0, 0, 0, 0.1)';
        modalContent.style.position = 'relative';

        return { modalOverlay, modalContent };
    }

    // 修改输入模态框以支持标题和内容
    function createInputModal(type = 'add', initialTitle = '', initialContent = '') {
        return new Promise((resolve) => {
            const { modalOverlay, modalContent } = createModal();

            const titleElement = document.createElement('h3');
            titleElement.textContent = type === 'add' ? 'Add New Phrase' : 'Edit Phrase';
            titleElement.style.color = '#e2e8f0';
            titleElement.style.marginTop = '0';
            titleElement.style.marginBottom = '15px';
            titleElement.style.fontSize = '18px';

            // 标题输入
            const titleLabel = document.createElement('label');
            titleLabel.textContent = 'Title:';
            titleLabel.style.color = '#e2e8f0';
            titleLabel.style.display = 'block';
            titleLabel.style.marginBottom = '5px';

            const titleInput = document.createElement('input');
            titleInput.value = initialTitle;
            titleInput.style.width = '100%';
            titleInput.style.padding = '8px 12px';
            titleInput.style.borderRadius = '6px';
            titleInput.style.border = '1px solid #4a5568';
            titleInput.style.backgroundColor = '#3f495e';
            titleInput.style.color = '#e2e8f0';
            titleInput.style.fontSize = '14px';
            titleInput.style.marginBottom = '15px';
            titleInput.style.boxSizing = 'border-box';

            // 内容输入
            const contentLabel = document.createElement('label');
            contentLabel.textContent = 'Content:';
            contentLabel.style.color = '#e2e8f0';
            contentLabel.style.display = 'block';
            contentLabel.style.marginBottom = '5px';

            const contentInput = document.createElement('textarea');
            contentInput.value = initialContent;
            contentInput.style.width = '100%';
            contentInput.style.padding = '8px 12px';
            contentInput.style.borderRadius = '6px';
            contentInput.style.border = '1px solid #4a5568';
            contentInput.style.backgroundColor = '#3f495e';
            contentInput.style.color = '#e2e8f0';
            contentInput.style.fontSize = '14px';
            contentInput.style.minHeight = '100px';
            contentInput.style.resize = 'vertical';
            contentInput.style.marginBottom = '15px';
            contentInput.style.boxSizing = 'border-box';

            const buttonContainer = document.createElement('div');
            buttonContainer.style.display = 'flex';
            buttonContainer.style.gap = '10px';
            buttonContainer.style.justifyContent = 'flex-end';

            const cancelButton = document.createElement('button');
            cancelButton.textContent = 'Cancel';
            cancelButton.style.padding = '8px 16px';
            cancelButton.style.borderRadius = '6px';
            cancelButton.style.border = '1px solid #4a5568';
            cancelButton.style.backgroundColor = '#4a5568';
            cancelButton.style.color = '#e2e8f0';
            cancelButton.style.cursor = 'pointer';

            const saveButton = document.createElement('button');
            saveButton.textContent = 'Save';
            saveButton.style.padding = '8px 16px';
            saveButton.style.borderRadius = '6px';
            saveButton.style.border = 'none';
            saveButton.style.backgroundColor = '#4299e1';
            saveButton.style.color = 'white';
            saveButton.style.cursor = 'pointer';

            cancelButton.onclick = () => {
                document.body.removeChild(modalOverlay);
                resolve(null);
            };

            saveButton.onclick = () => {
                const title = titleInput.value.trim();
                const content = contentInput.value.trim();
                if (title && content) {
                    document.body.removeChild(modalOverlay);
                    resolve({ title, content });
                } else {
                    if (!title) titleInput.style.border = '1px solid #f56565';
                    if (!content) contentInput.style.border = '1px solid #f56565';
                }
            };

            buttonContainer.appendChild(cancelButton);
            buttonContainer.appendChild(saveButton);

            modalContent.appendChild(titleElement);
            modalContent.appendChild(titleLabel);
            modalContent.appendChild(titleInput);
            modalContent.appendChild(contentLabel);
            modalContent.appendChild(contentInput);
            modalContent.appendChild(buttonContainer);
            modalOverlay.appendChild(modalContent);
            document.body.appendChild(modalOverlay);

            titleInput.focus();
        });
    }

    // 创建浮动窗口
    const floatingWindow = document.createElement('div');
    floatingWindow.style.position = 'fixed';
    floatingWindow.style.top = '50%';
    floatingWindow.style.right = '0';
    floatingWindow.style.transform = 'translateY(-50%)';
    floatingWindow.style.backgroundColor = '#2d3748';
    floatingWindow.style.color = '#e2e8f0';
    floatingWindow.style.borderRadius = '8px 0 0 8px';
    floatingWindow.style.boxShadow = '0 4px 6px rgba(0, 0, 0, 0.1)';
    floatingWindow.style.zIndex = '1000';
    floatingWindow.style.transition = 'all 0.3s ease';
    floatingWindow.style.cursor = 'pointer';

    // 收起状态的标签
    const collapsedLabel = document.createElement('div');
    collapsedLabel.textContent = '提示词助手';
    collapsedLabel.style.padding = '10px';
    collapsedLabel.style.writingMode = 'vertical-lr';
    collapsedLabel.style.textOrientation = 'upright';

    // 网格容器
    const gridContainer = document.createElement('div');
    gridContainer.style.display = 'none'; // 初始隐藏
    gridContainer.style.gridTemplateColumns = 'repeat(2, 1fr)';
    gridContainer.style.gap = '10px';
    gridContainer.style.padding = '15px';
    gridContainer.style.maxWidth = '400px';
    gridContainer.style.maxHeight = '80vh';
    gridContainer.style.overflowY = 'auto';

    // 加载数据
    let phrases = GM_getValue('phrases', []);

    // 更新显示
    function updateGrid() {
        gridContainer.innerHTML = '';

        // 对短语进行排序(置顶的在前面,保持置顶项的相对顺序)
        const pinnedPhrases = phrases.filter(p => p.isPinned);
        const unpinnedPhrases = phrases.filter(p => !p.isPinned);
        const sortedPhrases = [...pinnedPhrases, ...unpinnedPhrases];

        sortedPhrases.forEach((phrase, index) => {
            const phraseBox = document.createElement('div');
            phraseBox.style.backgroundColor = '#3f495e';
            phraseBox.style.padding = '10px';
            phraseBox.style.borderRadius = '6px';
            phraseBox.style.position = 'relative';
            phraseBox.style.cursor = 'pointer';

            const title = document.createElement('div');
            title.textContent = phrase.title;
            title.style.marginRight = '25px'; // 为pin按钮留出空间

            const pinButton = document.createElement('button');
            pinButton.textContent = phrase.isPinned ? '📌' : '📍';
            pinButton.style.position = 'absolute';
            pinButton.style.top = '5px';
            pinButton.style.right = '5px';
            pinButton.style.border = 'none';
            pinButton.style.backgroundColor = 'transparent';
            pinButton.style.cursor = 'pointer';
            pinButton.style.fontSize = '14px';

            pinButton.onclick = (e) => {
                e.stopPropagation();
                const phraseIndex = phrases.findIndex(p =>
                    p.title === phrase.title && p.content === phrase.content
                );

                if (phraseIndex !== -1) {
                    const updatedPhrase = {...phrases[phraseIndex]};

                    // 如果要置顶,移动到所有置顶项的最后
                    if (!updatedPhrase.isPinned) {
                        const lastPinnedIndex = phrases.map(p => p.isPinned).lastIndexOf(true);
                        updatedPhrase.isPinned = true;

                        // 从原位置删除
                        phrases.splice(phraseIndex, 1);
                        // 插入到最后一个置顶项后面
                        phrases.splice(lastPinnedIndex + 1, 0, updatedPhrase);
                    } else {
                        // 取消置顶,移动到非置顶区域的开始
                        updatedPhrase.isPinned = false;
                        const firstUnpinnedIndex = phrases.findIndex(p => !p.isPinned);

                        // 从原位置删除
                        phrases.splice(phraseIndex, 1);
                        // 插入到非置顶区域的开始
                        phrases.splice(firstUnpinnedIndex === -1 ? phrases.length : firstUnpinnedIndex, 0, updatedPhrase);
                    }

                    savePhrases();
                    updateGrid();
                }
            };

            phraseBox.onclick = () => {
                navigator.clipboard.writeText(phrase.content);
                showTooltip(phrase.content);
            };

            phraseBox.appendChild(title);
            phraseBox.appendChild(pinButton);
            gridContainer.appendChild(phraseBox);
        });
    }

    // 保存数据
    function savePhrases() {
        GM_setValue('phrases', phrases);
    }

    // 添加按钮
    const addButton = document.createElement('button');
    addButton.textContent = '+ Add New';
    addButton.style.width = '100%';
    addButton.style.backgroundColor = '#4299e1';
    addButton.style.color = 'white';
    addButton.style.border = 'none';
    addButton.style.padding = '8px';
    addButton.style.borderRadius = '6px';
    addButton.style.marginTop = '10px';
    addButton.style.cursor = 'pointer';

    addButton.onclick = async () => {
        const result = await createInputModal('add');
        if (result) {
            phrases.push({
                title: result.title,
                content: result.content,
                isPinned: false
            });
            savePhrases();
            updateGrid();
        }
    };

    // 显示复制提示
    function showTooltip(text) {
        const tooltip = document.createElement('div');
        tooltip.textContent = 'Copied: ' + (text.length > 30 ? text.substring(0, 30) + '...' : text);
        tooltip.style.position = 'fixed';
        tooltip.style.top = '20px';
        tooltip.style.right = '20px';
        tooltip.style.backgroundColor = '#48bb78';
        tooltip.style.color = 'white';
        tooltip.style.padding = '8px 12px';
        tooltip.style.borderRadius = '6px';
        tooltip.style.zIndex = '1001';
        tooltip.style.animation = 'fadeInOut 2s ease-in-out';

        document.body.appendChild(tooltip);
        setTimeout(() => document.body.removeChild(tooltip), 2000);
    }

    // 添加展开/收起功能
    let isExpanded = false;

    function toggleWindow() {
        if (isExpanded) {
            gridContainer.style.display = 'none';
            collapsedLabel.style.display = 'block';
            floatingWindow.style.width = 'auto';
            addButton.style.display = 'none'; // 收起时隐藏添加按钮
        } else {
            gridContainer.style.display = 'grid';
            collapsedLabel.style.display = 'none';
            floatingWindow.style.width = '400px';
            addButton.style.display = 'block'; // 展开时显示添加按钮
        }
        isExpanded = !isExpanded;
    }

    floatingWindow.onmouseenter = () => {
        if (!isExpanded) toggleWindow();
    };

    floatingWindow.onmouseleave = () => {
        if (isExpanded) toggleWindow();
    };

    // 组装界面
    floatingWindow.appendChild(collapsedLabel);
    floatingWindow.appendChild(gridContainer);
    floatingWindow.appendChild(addButton);
    document.body.appendChild(floatingWindow);

    // 初始化显示
    updateGrid();
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址