deepseek-question-list

展示网页版deepseek当前对话的所有提问

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey, Greasemonkey или Violentmonkey.

Для установки этого скрипта вам необходимо установить расширение, такое как Tampermonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Violentmonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Userscripts.

Чтобы установить этот скрипт, сначала вы должны установить расширение браузера, например Tampermonkey.

Чтобы установить этот скрипт, вы должны установить расширение — менеджер скриптов.

(у меня уже есть менеджер скриптов, дайте мне установить скрипт!)

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

(у меня уже есть менеджер стилей, дайте мне установить скрипт!)

// ==UserScript==
// @name         deepseek-question-list
// @namespace    https://github.com/firesahc/deepseek-question-list
// @version      1.7.2
// @description  展示网页版deepseek当前对话的所有提问
// @author       firesahc
// @match        https://chat.deepseek.com/*
// @license      MIT
// @grant        GM_setValue
// @grant        GM_getValue
// ==/UserScript==

let observer = null;
let isObserving = false;
let debounceTimer = null;

function createParserInit() {
    const existingList = document.getElementById('xpath-parser-list');
    if (existingList) existingList.remove();

    const listContainer = document.createElement('div');
    listContainer.id = 'xpath-parser-list';
    listContainer.style.cssText = `
        top: 10px;
        right: 50px;
        gap: 8px;
        overflow-y: auto;
        background: white;
        z-index: 10000;
        font-family: Arial, sans-serif;
        font-size: 14px;
        display: flex;
        flex-direction: column;
    `;

    const topButtonBar = document.createElement('div');
    topButtonBar.style.cssText = `
        display: flex;
        gap: 8px;
        background: white;
        flex-wrap: wrap;
    `;

    const contentArea = document.createElement('div');
    contentArea.id = 'xpath-list-content';
    contentArea.style.cssText = `
        flex: 1;
        overflow-y: auto;
        padding: 4px;
    `;
    
    addTopButtons(topButtonBar, listContainer, contentArea);
    
    listContainer.appendChild(topButtonBar);
    listContainer.appendChild(contentArea);
    
    // 延迟启动观察器
    clearTimeout(debounceTimer);
    debounceTimer = setTimeout(() => {
        addQuestionCollapseButtons();

        // 将列表框添加到 class="c3ecdb44" 的元素内部
        const targetContainer = document.querySelector('.c3ecdb44');
        if (targetContainer) {
            targetContainer.appendChild(listContainer);
        }
    }, 350)
}

function startObservation(contentArea) {
    if (isObserving) return true;

    observer = new MutationObserver((mutations) => {
        let shouldParse = false;
        for (const mutation of mutations) {
            // 检查目标元素的类名
            const targetClass = mutation.target.className;
            
            // 情况1: 直接检测到 dad65929 的变化
            if (mutation.type === 'childList' &&
                typeof targetClass === 'string' &&
                targetClass.includes('dad65929')) {
                shouldParse = true;
                break;
            }
            
            // 情况2: 检测到滚动区域的变化,且涉及 dad65929 节点
            if (mutation.type === 'childList' &&
                typeof targetClass === 'string' &&
                targetClass.includes('_0f72b0b') &&
                targetClass.includes('ds-scroll-area')) {
                // 检查添加的节点
                if (mutation.addedNodes.length > 0) {
                    for (const node of mutation.addedNodes) {
                        if (node.nodeType === Node.ELEMENT_NODE &&
                            node.classList &&
                            node.classList.contains('dad65929')) {
                            shouldParse = true;
                            break;
                        }
                    }
                }
                // 检查移除的节点
                else if (mutation.removedNodes.length > 0) {
                    for (const node of mutation.removedNodes) {
                        if (node.nodeType === Node.ELEMENT_NODE &&
                            node.classList &&
                            node.classList.contains('dad65929')) {
                            shouldParse = true;
                            break;
                        }
                    }
                }
                if (shouldParse) break;
            }

            // 情况3: 检测到class属性移除了"_3111eee"
            if (mutation.type === 'attributes' &&
                typeof mutation.oldValue === 'string' &&
                mutation.oldValue.includes('_9663006') &&
                mutation.oldValue.includes('_3111eee')) {
                if (!targetClass.includes('_3111eee')) {
                    shouldParse = true;
                    break;
                }
            }
        }

        if (shouldParse) {
            clearTimeout(debounceTimer);
            debounceTimer = setTimeout(() => {
                const messageElements = parseElements(contentArea);
                contentArea.innerHTML = '';
                addListMessages(contentArea, messageElements);

                // 为每个目标元素添加收起按钮(仅当内容较长时)
                addElementCollapseButtons(messageElements);
            }, 300);
        }
    });

    // 获取目标元素
    const targetElement = document.querySelector('._0f72b0b.ds-scroll-area');
    if (!targetElement) {
        return false;
    }
    
    try {
        // 开始观察目标元素
        observer.observe(targetElement, {
            childList: true,
            subtree: true,
            attributes: true,
            attributeFilter: ['class'],
            attributeOldValue: true,
            characterData: false
        });
        isObserving = true;
        return true;
    } catch (error) {
        isObserving = false;
        return false;
    }
}

function stopObservation() {
    if (observer) {
        observer.disconnect();
        observer = null;
        isObserving = false;
        clearTimeout(debounceTimer);
    }
}

function parseElements(contentArea) {
    try {
        contentArea.innerHTML = '';
        const targetElements = document.querySelectorAll('.fbb737a4');
        if (targetElements.length === 0) {
            return;
        }

        const messageElements = [];
        targetElements.forEach(element => {
            messageElements.push({
                targetElement: element
            });
        });
        if (messageElements.length === 0) {
            return;
        }
        else{
            return messageElements;
        }
    } catch (error) {
        return;
    }
}

function addElementCollapseButtons(messageElements) {
    messageElements.forEach((item, index) => {
        const element = item.targetElement;
        
        // 检查是否已经添加过按钮
        if (element.hasAttribute('data-collapse-button-added')) {
            return;
        }

        // 如果内容高度不超过400px,不需要添加收起按钮
        if (element.scrollHeight <= 400) {
            return;
        }

        // 标记已添加按钮
        element.setAttribute('data-collapse-button-added', 'true');

        // 确保元素有相对定位,以便按钮可以绝对定位
        const originalPosition = element.style.position;
        if (!originalPosition || originalPosition === 'static') {
            element.style.position = 'relative';
        }

        // 创建收起按钮
        const collapseButton = document.createElement('button');
        collapseButton.textContent = '收起';
        collapseButton.style.cssText = `
            position: absolute;
            top: 5px;
            left: 5px;
            z-index: 1000;
            background: rgba(100, 100, 100, 0.8);
            color: white;
            border: none;
            border-radius: 4px;
            padding: 4px 8px;
            font-size: 12px;
            cursor: pointer;
            opacity: 0.8;
            transition: opacity 0.2s;
        `;

        // 存储原始高度和溢出状态
        const originalHeight = element.style.height;
        const originalOverflow = element.style.overflow;
        let isCollapsed = false;

        collapseButton.addEventListener('mouseenter', () => {
            collapseButton.style.opacity = '1';
        });

        collapseButton.addEventListener('mouseleave', () => {
            collapseButton.style.opacity = '0.8';
        });

        collapseButton.addEventListener('click', (e) => {
            e.stopPropagation();
            if (isCollapsed) {
                // 展开
                element.style.height = originalHeight || '';
                element.style.overflow = originalOverflow || '';
                collapseButton.textContent = '收起';
                isCollapsed = false;
            } else {
                // 收起
                element.style.height = '110px';
                element.style.overflow = 'hidden';
                collapseButton.textContent = '展开';
                isCollapsed = true;
            }
        });

        // 添加按钮到元素
        element.appendChild(collapseButton);
    });
}

function addQuestionCollapseButtons(){
    // 尝试查找目标元素
    const questionElement = document.querySelector('._871cbca');
    if (!questionElement) {
        return;
    }
    
    const toggleButton = document.createElement('button');
    // 获取目标容器元素
    const containerElement = document.querySelector('._7780f2e');
    if (!containerElement) {
        return;
    } else {
        toggleButton.textContent = '▼';
        // 设置容器元素为相对定位,以便按钮可以相对于它定位
        containerElement.style.position = 'relative';
        // 将按钮添加到容器元素内部
        containerElement.appendChild(toggleButton);
        // 设置按钮样式 - 在容器元素内部居中
        toggleButton.style.cssText = `
            position: absolute;
            bottom: 0; 
            left: 50%; /* 水平居中定位 */
            transform: translateX(-50%); /* 水平居中调整 */
            z-index: 1000;
            padding: 8px 20px;
            background-color: rgba(255, 255, 255, 0.3);
            color: #000;
            border: 1px solid rgba(0, 0, 0, 0.1);
            border-bottom: none;
            border-radius: 8px 8px 0 0;
            cursor: pointer;
            backdrop-filter: blur(12px);
            -webkit-backdrop-filter: blur(12px);
            font-size: 10px;
            font-weight: bold;
            box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1);
            transition: all 0.3s ease;
        `;
    }

    // 添加点击事件
    toggleButton.addEventListener('click', function () {
        if (questionElement.style.display === 'none') {
            questionElement.style.display = 'block';
            toggleButton.textContent = '▼';
        } else {
            questionElement.style.display = 'none';
            toggleButton.textContent = '▲';
        };
    });
}

function addListMessages(contentArea, messageElements) {
    const list = document.createElement('ul');
    list.style.cssText = `
        list-style: none;
        margin: 0;
        padding: 0;
    `;

    messageElements.forEach((item, index) => {
        const listItem = createListItem(item, index);
        list.appendChild(listItem);
    });

    contentArea.appendChild(list);
}

function createListItem(item, index) {
    const listItem = document.createElement('li');
    listItem.style.cssText = `
        margin-bottom: 4px;
        padding: 4px;
        border: 1px solid #e0e0e0;
        border-radius: 6px;
        background: #fafafa;
        cursor: pointer;
        transition: all 0.2s ease;
    `;

    listItem.addEventListener('mouseenter', () => {
        listItem.style.background = '#f0f8ff';
        listItem.style.borderColor = '#4CAF50';
    });

    listItem.addEventListener('mouseleave', () => {
        listItem.style.background = '#fafafa';
        listItem.style.borderColor = '#e0e0e0';
    });

    const indexInfo = document.createElement('div');
    indexInfo.style.cssText = `
        font-weight: bold;
        color: #2196F3;
        font-size: 14px;
    `;
    indexInfo.textContent = `问题 ${index + 1}`;

    const contentPreview = document.createElement('div');
    contentPreview.style.cssText = `
        color: #333;
        font-size: 13px;
        line-height: 1.4;
        background: white;
        padding: 4px;
        border-radius: 4px;
        border: 1px solid #e0e0e0;
    `;
    
    const textContent = item.targetElement.textContent?.trim() || '';
    contentPreview.textContent = textContent ?
        (textContent.length > 120 ? 
             textContent.substring(0, 120) + '...' : 
             textContent
        ) :
        '[空内容]';

    listItem.appendChild(indexInfo);
    listItem.appendChild(contentPreview);

    //点击跳转到问题起始
    listItem.addEventListener('click', () => {
        item.targetElement.scrollIntoView({ behavior: 'smooth', block: 'start' });
    });

    return listItem;
}

function addTopButtons(buttonContainer, listContainer, contentArea) {
    const buttonStyle = `
        padding: 6px 6px;
        border: none;
        border-radius: 4px;
        cursor: pointer;
        font-size: 12px;
        font-weight: bold;
        transition: all 0.2s ease;
        flex: 1;
        min-width: 30px;
    `;

    // 从油猴存储中读取 isContentVisible 的值,默认值为 true(显示状态)
    let isContentVisible = GM_getValue('isContentVisible', true);

    // 根据存储的值初始化内容区域的显示状态
    contentArea.style.display = isContentVisible ? 'block' : 'none';
    listContainer.style.padding = isContentVisible ? '6px' : '0px';
    listContainer.style.border = isContentVisible ? '2px solid #f5f5f5' : '';
    listContainer.style.position =isContentVisible ? '':' fixed';
    listContainer.style.width=isContentVisible ? '240px':' 100px';

    const parseButton = createButton(isObserving? '停止解析':'开始解析', '#2196F3', '#1976D2', () => {
        if (isObserving) {
            // 停止解析
            stopObservation();
            parseButton.textContent = '开始解析';
        } else {
            // 开始解析
            const success = startObservation(contentArea);
            if (success) {
                parseButton.textContent = '停止解析';
                // 立即执行一次解析
                const messageElements = parseElements(contentArea);
                contentArea.innerHTML = '';
                addListMessages(contentArea, messageElements);

                // 为每个目标元素添加收起按钮(仅当内容较长时)
                addElementCollapseButtons(messageElements);
            }
        }
    });

    const toggleButton = createButton(isContentVisible ? '隐藏列表' : '显示列表', '#FF9800', '#F57C00', () => {
        isContentVisible = !isContentVisible;
        toggleButton.textContent = isContentVisible ? '隐藏列表' : '显示列表';
        contentArea.style.display = isContentVisible ? 'block' : 'none';
        listContainer.style.padding = isContentVisible ? '6px' : '0px';
        listContainer.style.border = isContentVisible ? '2px solid #f5f5f5' : '';
        listContainer.style.position =isContentVisible ? '':' fixed';
        listContainer.style.width=isContentVisible ? '240px':' 100px';
        // 将新的 isContentVisible 值保存到油猴存储中
        GM_setValue('isContentVisible', isContentVisible);
    });

    buttonContainer.appendChild(parseButton);
    buttonContainer.appendChild(toggleButton);
}

function createButton(text, bgColor, hoverColor, clickHandler) {
    const button = document.createElement('button');
    button.textContent = text;
    button.style.cssText = `
        padding: 6px 6px;
        border: none;
        border-radius: 4px;
        cursor: pointer;
        font-size: 12px;
        font-weight: bold;
        transition: all 0.2s ease;
        flex: 1;
        min-width: 30px;
        background: ${bgColor};
        color: white;
    `;

    button.addEventListener('mouseenter', () => {
        button.style.background = hoverColor;
    });

    button.addEventListener('mouseleave', () => {
        button.style.background = bgColor;
    });

    button.addEventListener('click', clickHandler);
    return button;
}

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

window.createParser = createParserInit;
window.parseTarget = function() {
    const contentArea = document.getElementById('xpath-list-content');
    if (contentArea) {
        const messageElements = parseElements(contentArea);
        contentArea.innerHTML = '';
        addListMessages(contentArea, messageElements);

        // 为每个目标元素添加收起按钮(仅当内容较长时)
        addElementCollapseButtons(messageElements);
    }
};