DeepSeek Chat 简约提示语助手

支持多步撤销、大窗口、完美输入体验

// ==UserScript==
// @name         DeepSeek Chat 简约提示语助手
// @namespace    http://tampermonkey.net/
// @version      1.5
// @description  支持多步撤销、大窗口、完美输入体验
// @author       Dost51552
// @match        https://chat.deepseek.com/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_addStyle
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // 默认预设提示语
    const defaultPrompts = [
        "请帮我总结这篇文章的主要内容",
        "用简洁的语言解释这个概念",
        "这段代码有什么问题?如何优化?",
        "请用中文回答我的问题",
    ];

    // 获取或初始化存储的提示语
    let savedPrompts = GM_getValue("savedPrompts", defaultPrompts);
    let deletedHistory = []; // 改用数组记录删除历史
    let undoTimeout = null;

    // 创建菜单样式(终极优化版)
    GM_addStyle(`
        #prompt-menu {
            position: fixed;
            bottom: 130px;
            right: 30px;
            width: 280px;
            background-color: rgb(38, 38, 38);
            border-radius: 8px;
            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
            font-family: "Microsoft YaHei", sans-serif;
            color: rgb(210, 210, 210);
            z-index: 9999;
            overflow: hidden;
            opacity: 0;
            transform: translateY(10px);
            transition: opacity 0.3s, transform 0.3s;
            pointer-events: none;
        }
        #prompt-menu.active {
            opacity: 1;
            transform: translateY(0);
            pointer-events: auto;
        }
        #prompt-menu-header {
            padding: 12px;
            background-color: rgb(30, 30, 30);
            font-weight: bold;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }
        #undo-btn {
            color: rgb(180, 180, 180);
            cursor: pointer;
            font-size: 12px;
            opacity: 0;
            transition: opacity 0.3s;
            margin-left: 10px;
            white-space: nowrap;
        }
        #undo-btn.visible {
            opacity: 1;
        }
        #undo-count {
            display: inline-block;
            min-width: 15px;
            text-align: center;
        }
        #prompt-menu-content {
            max-height: 300px;
            overflow-y: auto;
        }
        .prompt-item {
            padding: 12px 40px 12px 12px;
            border-bottom: 1px solid rgb(50, 50, 50);
            cursor: pointer;
            position: relative;
            transition: all 0.3s;
        }
        .prompt-item:hover {
            background-color: rgb(50, 50, 50);
        }
        .delete-btn {
            position: absolute;
            right: 12px;
            top: 50%;
            transform: translateY(-50%);
            color: rgb(160, 160, 160);
            width: 16px;
            height: 16px;
            display: flex;
            align-items: center;
            justify-content: center;
            border-radius: 50%;
            cursor: pointer;
            transition: all 0.2s;
        }
        .delete-btn:hover {
            color: rgb(210, 210, 210);
            background-color: rgba(255,255,255,0.1);
        }
        #prompt-menu-footer {
            padding: 12px;
            display: flex;
            gap: 8px;
            background-color: rgb(30, 30, 30);
        }
        #new-prompt-input {
            flex: 1;
            padding: 8px;
            background-color: rgb(50, 50, 50);
            border: 1px solid rgb(70, 70, 70);
            color: rgb(210, 210, 210);
            border-radius: 4px;
            font-family: "Microsoft YaHei", sans-serif;
        }
        #add-prompt-btn {
            padding: 0 12px;
            background-color: rgb(70, 70, 70);
            border: none;
            color: rgb(210, 210, 210);
            border-radius: 4px;
            cursor: pointer;
            transition: background-color 0.2s;
        }
        #add-prompt-btn:hover {
            background-color: rgb(90, 90, 90);
        }
        #menu-trigger {
            position: fixed;
            bottom: 80px;
            right: 30px;
            width: 44px;
            height: 44px;
            background-color: rgb(38, 38, 38);
            border: 2px solid rgb(42, 42, 42);
            border-radius: 50%;
            display: flex;
            align-items: center;
            justify-content: center;
            cursor: pointer;
            z-index: 10000;
            box-shadow: 0 2px 8px rgba(0,0,0,0.3);
            transition: all 0.3s;
        }
        #menu-trigger:hover {
            transform: scale(1.1);
            background-color: rgb(50, 50, 50);
        }
    `);

    // 创建悬浮触发器
    const trigger = document.createElement('div');
    trigger.id = 'menu-trigger';
    trigger.innerHTML = '⚡';
    document.body.appendChild(trigger);

    // 创建菜单 DOM
    const menu = document.createElement('div');
    menu.id = 'prompt-menu';
    menu.innerHTML = `
        <div id="prompt-menu-header">
            <span>预设提示语</span>
            <span id="undo-btn">撤销(<span id="undo-count">0</span>)</span>
        </div>
        <div id="prompt-menu-content"></div>
        <div id="prompt-menu-footer">
            <input type="text" id="new-prompt-input" placeholder="输入新提示语...">
            <button id="add-prompt-btn">添加</button>
        </div>
    `;
    document.body.appendChild(menu);

    const menuContent = document.getElementById('prompt-menu-content');
    const newPromptInput = document.getElementById('new-prompt-input');
    const addPromptBtn = document.getElementById('add-prompt-btn');
    const undoBtn = document.getElementById('undo-btn');
    const undoCount = document.getElementById('undo-count');

    // 菜单交互状态管理
    let menuTimeout;
    const showMenu = () => {
        clearTimeout(menuTimeout);
        menu.classList.add('active');
    };
    const hideMenu = () => {
        menuTimeout = setTimeout(() => {
            if (!menu.matches(':hover') && !trigger.matches(':hover')) {
                menu.classList.remove('active');
            }
        }, 300);
    };

    // 绑定悬停事件
    trigger.addEventListener('mouseenter', showMenu);
    menu.addEventListener('mouseenter', showMenu);
    trigger.addEventListener('mouseleave', hideMenu);
    menu.addEventListener('mouseleave', hideMenu);

    // 保存提示语数据
    const savePrompts = () => {
        GM_setValue("savedPrompts", savedPrompts);
    };

    // 更新撤销按钮状态
    const updateUndoButton = () => {
        undoCount.textContent = deletedHistory.length;
        if (deletedHistory.length > 0) {
            undoBtn.classList.add('visible');
        } else {
            undoBtn.classList.remove('visible');
        }
    };

    // 撤销删除操作
    undoBtn.addEventListener('click', () => {
        if (deletedHistory.length > 0) {
            const lastDeleted = deletedHistory.pop();
            savedPrompts.splice(lastDeleted.index, 0, lastDeleted.text);
            savePrompts();
            loadPrompts();
            updateUndoButton();

            // 重置计时器
            if (undoTimeout) clearTimeout(undoTimeout);
            undoTimeout = setTimeout(clearUndoHistory, 10000);
        }
    });

    // 清除撤销历史
    const clearUndoHistory = () => {
        deletedHistory = [];
        updateUndoButton();
        if (undoTimeout) clearTimeout(undoTimeout);
    };

    // 智能追加文本到输入框
    const appendToInput = (text) => {
        const chatInput = document.querySelector('#chat-input');
        if (!chatInput) return;

        const setNativeValue = (element, value) => {
            const { set: valueSetter } = Object.getOwnPropertyDescriptor(element, 'value') || {};
            const prototype = Object.getPrototypeOf(element);
            const { set: prototypeValueSetter } = Object.getOwnPropertyDescriptor(prototype, 'value') || {};

            if (prototypeValueSetter && valueSetter !== prototypeValueSetter) {
                prototypeValueSetter.call(element, value);
            } else if (valueSetter) {
                valueSetter.call(element, value);
            } else {
                element.value = value;
            }
        };

        const currentValue = chatInput.value.trim();
        const separator = currentValue ? '\n\n' : '';
        const newValue = `${currentValue}${separator}${text}`;

        setNativeValue(chatInput, newValue);
        chatInput.dispatchEvent(new Event('input', { bubbles: true }));
        chatInput.dispatchEvent(new Event('change', { bubbles: true }));
        chatInput.focus();
        navigator.clipboard.writeText(text);
    };

    // 加载提示语列表
    function loadPrompts() {
        menuContent.innerHTML = '';
        savedPrompts.forEach((prompt, index) => {
            const promptItem = document.createElement('div');
            promptItem.className = 'prompt-item';
            promptItem.innerHTML = `
                ${prompt}
                <div class="delete-btn">×</div>
            `;

            promptItem.addEventListener('click', (e) => {
                if (e.target.classList.contains('delete-btn')) return;
                appendToInput(prompt);

                promptItem.style.backgroundColor = 'rgb(60, 60, 60)';
                setTimeout(() => {
                    promptItem.style.backgroundColor = '';
                }, 300);
            });

            const deleteBtn = promptItem.querySelector('.delete-btn');
            deleteBtn.addEventListener('click', (e) => {
                e.stopPropagation();
                // 记录删除历史(包括内容和位置)
                deletedHistory.push({
                    text: prompt,
                    index: index,
                    time: Date.now()
                });
                savedPrompts.splice(index, 1);
                savePrompts();
                loadPrompts();
                updateUndoButton();

                if (undoTimeout) clearTimeout(undoTimeout);
                undoTimeout = setTimeout(clearUndoHistory, 10000);
            });

            menuContent.appendChild(promptItem);
        });
    }

    // 添加新提示语
    addPromptBtn.addEventListener('click', () => {
        const newPrompt = newPromptInput.value.trim();
        if (newPrompt) {
            savedPrompts.push(newPrompt);
            savePrompts();
            newPromptInput.value = '';
            loadPrompts();
            clearUndoHistory();
        }
    });

    newPromptInput.addEventListener('keypress', (e) => {
        if (e.key === 'Enter') {
            addPromptBtn.click();
        }
    });

    // 初始化加载
    loadPrompts();
})();