webAI聊天问题列表导航

通过点击按钮显示用户问题列表,支持导航到特定问题、分页功能、正序/倒序切换,自动加载历史记录

// ==UserScript==
// @name         webAI聊天问题列表导航
// @namespace    http://tampermonkey.net/
// @version      3.1.0
// @description  通过点击按钮显示用户问题列表,支持导航到特定问题、分页功能、正序/倒序切换,自动加载历史记录
// @author       yutao
// @match        https://grok.com/chat/*
// @match        https://github.com/copilot/*
// @match        https://yuanbao.tencent.com/chat/*
// @match        https://chat.qwenlm.ai/c/*
// @match        https://chat.qwen.ai/c/*
// @match        https://copilot.microsoft.com/chats/*
// @match        https://chatgpt.com/c/*
// @match        https://chat.deepseek.com/a/chat/*
// @match        https://tongyi.aliyun.com/*
// @match        https://www.doubao.com/*

// @grant        none
// MIT License
// 
// Copyright (c) [2025] [yutao]
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.@license
// ==/UserScript==

(function () {
    'use strict';

    // 配置对象,定义不同网站的聊天消息选择器和条件
    const config = {
        'tongyi.aliyun.com': {
            messageSelector: 'div[class^="questionItem-"]',
            textSelector: 'div[class^="bubble-"]',
            userCondition: (element) => {
                // 简化条件,只检查是否为问题项元素
                return element.className.indexOf('questionItem-') > -1;
            },
            scrollContainerSelector: 'div[class*="chat-container"], div[class*="contentWrapper"], div[class*="scroll"]'
        },
        "www.doubao.com": {
            messageSelector: 'div[data-testid="send_message"]',
            textSelector: 'div[data-testid="message_text_content"]',
            userCondition: (element) => true,
            scrollContainerSelector:
              'div[class*="scrollable-"][class*="show-scrollbar-"]',
          },
        'grok.com': {
            messageSelector: 'div.message-bubble',
            textSelector: 'span.whitespace-pre-wrap',
            userCondition: (element) => element.classList.contains('bg-foreground') &&
                window.getComputedStyle(element).backgroundColor !== 'rgb(224, 247, 250)',
            scrollContainerSelector: 'main > div > div:nth-child(1)'
        },
        'github.com': {
            messageSelector: 'div.UserMessage-module__container--cAvvK.ChatMessage-module__userMessage--xvIFp',
            textSelector: null,
            userCondition: (element) => element.classList.contains('ChatMessage-module__userMessage--xvIFp'),
            scrollContainerSelector: '.react-scroll-to-bottom--css-xgtui-79elbk'
        },
        'yuanbao.tencent.com': {
            messageSelector: 'div.agent-chat__bubble__content',
            textSelector: 'div.hyc-content-text',
            userCondition: (element) => true,
            scrollContainerSelector: '.agent-chat__bubble-wrap'
        },
        'chat.qwenlm.ai': {
            messageSelector: 'div.rounded-3xl.bg-gray-50.dark\\:bg-gray-850',
            textSelector: 'p',
            userCondition: (element) => true,
            scrollContainerSelector: 'main div.flex-col.overflow-auto'
        },
        'chat.qwen.ai': {
            messageSelector: 'div.dark\\:bg-gray-850, div[class*="message-item"], div[class*="user-message"], div.bg-blue-100',
            textSelector: 'div[class*="markdown-content"], p, div[class*="text"], span[class*="content"]',
            userCondition: (element) => {
                return element.classList.contains('bg-blue-100') || 
                       (element.querySelector('[class*="user-message"]') !== null) ||
                       (element.getAttribute('data-message-author-role') === 'user');
            },
            scrollContainerSelector: '#messages-container, div[class*="overflow-auto"][class*="flex-col"]'
        },
        'copilot.microsoft.com': {
            messageSelector: 'div.self-end.rounded-2xl',
            textSelector: null,
            userCondition: (element) => element.classList.contains('self-end'),
            scrollContainerSelector: '.overflow-y-auto.flex-1'
        },
        'chatgpt.com': {
            messageSelector: 'div.rounded-3xl.bg-token-message-surface',
            textSelector: 'div.whitespace-pre-wrap',
            userCondition: (element) => true,
            scrollContainerSelector: 'main div.overflow-y-auto'
        },
        'chat.deepseek.com': {
            messageSelector: 'div.fbb737a4',
            textSelector: null,
            userCondition: (element) => true,
            scrollContainerSelector: '.scroll-container'
        }
    };
    const genericConfig = {
        // 消息选择器:匹配常见的消息元素模式
        messageSelector: 'div[class*="message"], div[class*="chat"], div[class*="user"], div[class*="question"], div.bubble, article, div[role="listitem"]',
        
        // 文本选择器:匹配常见的文本容器
        textSelector: 'div[class*="text"], div[class*="content"], p, span[class*="content"], pre, div.markdown',
        
        // 用户消息条件:使用多种通用的方法识别用户消息
        userCondition: (element) => {
            // 检查常见的用户消息类名
            if (element.classList.toString().includes('user') || 
                element.classList.toString().includes('question') ||
                element.classList.toString().includes('self') ||
                element.classList.toString().includes('right')||
                element.classList.toString().includes('message'))
                return true;
                
            // 检查常见的用户角色属性
            if (element.getAttribute('data-role') === 'user' || 
                element.getAttribute('data-author') === 'user' ||
                element.getAttribute('data-message-author-role') === 'user')
                return true;
                
            // 检查布局特征 (右对齐通常表示用户消息)
            const style = window.getComputedStyle(element);
            if (style.justifyContent === 'flex-end' || 
                style.textAlign === 'right' ||
                style.alignSelf === 'flex-end')
                return true;
                
            // 检查文本内容特征:如果没有包含AI常用的前缀标识
            const text = element.textContent.trim().toLowerCase();
            if (text && text.length > 0 && 
                !text.startsWith('ai:') && 
                !text.startsWith('assistant:') &&
                !text.startsWith('bot:'))
                return true;
                
            return false;
        },
        
        // 滚动容器选择器:匹配常见的滚动容器
        scrollContainerSelector: 'div[class*="overflow"], div[class*="scroll"], div[class*="chat-container"], div[class*="message-container"], #messages-container, main'
    };

    // 获取当前域名并选择配置
    const hostname = window.location.hostname;
    
    // 获取当前网站的配置,如果没有特定配置则使用通用配置
    const currentConfig = config[hostname] || genericConfig;

    // 创建美化后的浮动按钮
    const button = document.createElement('button');
    button.textContent = '问题列表';
    button.style.position = 'fixed';
    button.style.bottom = '20px';
    button.style.right = '20px';
    button.style.zIndex = '1000';
    button.style.padding = '10px 15px';
    button.style.background = 'linear-gradient(135deg, #007BFF, #00C4FF)';
    button.style.color = '#fff';
    button.style.border = 'none';
    button.style.borderRadius = '8px';
    button.style.boxShadow = '0 2px 4px rgba(0,0,0,0.2)';
    button.style.cursor = 'pointer';
    button.style.fontFamily = 'Arial, sans-serif';
    button.style.fontSize = '14px';
    button.style.transition = 'transform 0.2s, box-shadow 0.2s';
    button.addEventListener('mouseover', () => {
        button.style.transform = 'scale(1.05)';
        button.style.boxShadow = '0 4px 8px rgba(0,0,0,0.3)';
    });
    button.addEventListener('mouseout', () => {
        button.style.transform = 'scale(1)';
        button.style.boxShadow = '0 2px 4px rgba(0,0,0,0.2)';
    });
    document.body.appendChild(button);

    // 创建美化后的悬浮窗
    const floatWindow = document.createElement('div');
    floatWindow.style.position = 'fixed';
    floatWindow.style.bottom = '70px';
    floatWindow.style.right = '20px';
    floatWindow.style.width = '320px';
    floatWindow.style.maxHeight = '420px';
    floatWindow.style.background = '#ffffff';
    floatWindow.style.border = '1px solid #e0e0e0';
    floatWindow.style.borderRadius = '10px';
    floatWindow.style.boxShadow = '0 4px 12px rgba(0,0,0,0.15)';
    floatWindow.style.padding = '15px';
    floatWindow.style.overflowY = 'auto';
    floatWindow.style.display = 'none';
    floatWindow.style.zIndex = '1000';
    floatWindow.style.fontFamily = 'Arial, sans-serif';
    floatWindow.style.transition = 'opacity 0.2s';
    document.body.appendChild(floatWindow);

    // 分页相关变量
    let questions = [];
    const pageSize = 10;
    let currentPage = 1;
    let isReversed = false;
    let isLoading = false; // 加载状态标志
    let autoLoadCompleted = false; // 标记自动加载是否已完成

    // 创建顶部按钮容器
    const topButtonContainer = document.createElement('div');
    topButtonContainer.style.display = 'flex';
    topButtonContainer.style.justifyContent = 'space-between';
    topButtonContainer.style.marginBottom = '15px';
    
    // 创建排序切换按钮
    const sortButton = document.createElement('button');
    sortButton.textContent = '正序';
    sortButton.style.padding = '5px 10px';
    sortButton.style.background = '#007BFF';
    sortButton.style.color = '#fff';
    sortButton.style.border = 'none';
    sortButton.style.borderRadius = '4px';
    sortButton.style.cursor = 'pointer';
    sortButton.style.fontSize = '12px';
    sortButton.style.transition = 'background 0.2s';
    sortButton.addEventListener('mouseover', () => {
        sortButton.style.background = '#0069d9';
    });
    sortButton.addEventListener('mouseout', () => {
        sortButton.style.background = '#007BFF';
    });
    sortButton.addEventListener('click', () => {
        isReversed = !isReversed;
        sortButton.textContent = isReversed ? '倒序' : '正序';
        findAllQuestionsWithDeduplication();
    });

    // 状态显示标签
    const statusLabel = document.createElement('div');
    statusLabel.textContent = '正在加载历史...';
    statusLabel.style.fontSize = '12px';
    statusLabel.style.color = '#666';
    statusLabel.style.padding = '5px 0';
    statusLabel.style.display = 'none'; // 默认隐藏

    // 将按钮添加到容器中
    topButtonContainer.appendChild(statusLabel);
    topButtonContainer.appendChild(sortButton);
    floatWindow.appendChild(topButtonContainer);

    // 创建分页控件
    const paginationContainer = document.createElement('div');
    paginationContainer.style.display = 'flex';
    paginationContainer.style.justifyContent = 'center';
    paginationContainer.style.marginTop = '10px';
    paginationContainer.style.gap = '5px';

    // 问题列表容器
    const listContainer = document.createElement('ul');
    listContainer.style.listStyle = 'none';
    listContainer.style.padding = '0';
    listContainer.style.margin = '0';
    floatWindow.appendChild(listContainer);
    floatWindow.appendChild(paginationContainer);

    // 创建问题计数显示区域
    const questionCountDisplay = document.createElement('div');
    questionCountDisplay.style.fontSize = '12px';
    questionCountDisplay.style.color = '#666';
    questionCountDisplay.style.textAlign = 'center';
    questionCountDisplay.style.margin = '5px 0 10px 0';
    floatWindow.insertBefore(questionCountDisplay, listContainer);

    // 更新问题计数显示
    function updateQuestionCountDisplay() {
        questionCountDisplay.textContent = `共找到 ${questions.length} 个问题`;
    }

    // 获取文本内容的辅助函数
    function getTextContent(element) {
        return element ? element.textContent.trim() : '';
    }

    // 查找所有用户问题并去重的函数
    function findAllQuestionsWithDeduplication() {
        const chatContainer = document.querySelector('.chat-container, #chat, main, article') || document.body;
        const potentialMessages = chatContainer.querySelectorAll(currentConfig.messageSelector);
        
        // 临时存储所有找到的问题
        const foundQuestions = [];
        const seenTexts = new Set(); // 用于去重
        
        for (let i = 0; i < potentialMessages.length; i++) {
            const element = potentialMessages[i];
            const textElement = currentConfig.textSelector ? element.querySelector(currentConfig.textSelector) : element;
            const text = getTextContent(textElement);
            
            // 如果文本内容有效且符合用户消息条件
            if (text && text.length > 2 && currentConfig.userCondition(element)) {
                // 使用文本内容进行去重
                if (!seenTexts.has(text)) {
                    seenTexts.add(text);
                    foundQuestions.push({ element, text });
                }
            }
        }
        
        // 更新全局问题列表
        questions = foundQuestions;
        
        // 确保排序正确
        if (isReversed) {
            questions.reverse();
        }
        
        // 更新界面
        updateQuestionCountDisplay();
        renderPage(currentPage);
        updatePagination();
    }

    // 加载历史记录的函数 - 直接使用配置的滚动容器
    async function loadHistoryRecords() {
        if (isLoading) return;
        
        isLoading = true;
        statusLabel.textContent = '正在加载历史...';
        statusLabel.style.display = 'block';
        
        // 保存当前滚动位置
        const scrollPosition = window.scrollY;
        
        // 记录当前问题数量
        const initialQuestionCount = questions.length;
        
        // 使用配置的滚动容器加载历史记录
        async function smartLoadHistory(attempts) {
            // 直接使用配置中指定的滚动容器
            const scrollContainer = document.querySelector(currentConfig.scrollContainerSelector);
            
            // 执行滚动操作
            if (scrollContainer) {
                // 保存原始滚动位置
                const originalScrollTop = scrollContainer.scrollTop;
                
                // 执行滚动到顶部
                const maxScrollAttempts = 3;
                for (let i = 0; i < maxScrollAttempts; i++) {
                    scrollContainer.scrollTo({top: 0, behavior: 'auto'});
                    
                    // 确保滚动生效
                    scrollContainer.scrollTop = 0; // 直接设置,确保滚动生效
                    await new Promise(resolve => setTimeout(resolve, 500));
                    
                    // 如果已经到顶部,退出循环
                    if (scrollContainer.scrollTop < 10) {
                        break;
                    }
                }
                
                // 等待内容加载
                await new Promise(resolve => setTimeout(resolve, 2000));
                
                // 触发滚动事件以确保内容加载
                scrollContainer.dispatchEvent(new Event('scroll', {bubbles: true}));
                
                // 再次等待
                await new Promise(resolve => setTimeout(resolve, 1000));
                
                // 如果是最后一次尝试,恢复滚动位置
                if (attempts >= 3) {
                    scrollContainer.scrollTo({top: originalScrollTop, behavior: 'auto'});
                }
            } else {
                // 如果未找到配置的滚动容器,使用全局滚动
                window.scrollTo({top: 0, behavior: 'auto'});
                await new Promise(resolve => setTimeout(resolve, 2000));
            }
            
            // 更新问题列表
            findAllQuestionsWithDeduplication();
            
            // 检查是否找到了新问题
            return questions.length > initialQuestionCount;
        }
        
        // 使用递归函数进行尝试加载
        const tryLoadHistory = async (attempts) => {
            // 执行智能加载
            const success = await smartLoadHistory(attempts);
            
            // 如果成功加载了新问题,或者已经尝试了足够多次,则完成加载
            if (success || attempts >= 3) {
                const newQuestions = questions.length - initialQuestionCount;
                
                // 更新状态
                isLoading = false;
                autoLoadCompleted = true;
                
                // 更新状态标签
                statusLabel.textContent = newQuestions > 0 
                    ? `已加载${newQuestions}条历史记录` 
                    : '未找到更多历史记录';
                
                // 延迟隐藏状态标签
                setTimeout(() => {
                    statusLabel.style.display = 'none';
                }, 3000);
                
                // 滚动回原来的位置
                window.scrollTo({top: scrollPosition, behavior: 'auto'});
            } else if (attempts < 3) {
                // 短暂延迟后再次尝试
                setTimeout(() => {
                    tryLoadHistory(attempts + 1);
                }, 2000);
            }
        };
        
        // 开始第一次尝试
        await tryLoadHistory(1);
    }

    // 使找到的问题定位在屏幕中
    function renderPage(page) {
        // 清空列表容器
        while (listContainer.firstChild) {
            listContainer.removeChild(listContainer.firstChild);
        }

        const start = (page - 1) * pageSize;
        const end = page * pageSize;
        const pageQuestions = questions.slice(start, end);

        pageQuestions.forEach((q, idx) => {
            const listItem = document.createElement('li');
            const shortText = q.text.substring(0, 20) + (q.text.length > 20 ? '...' : '');
            listItem.textContent = `${isReversed ? questions.length - start - idx : start + idx + 1}: ${shortText}`;
            listItem.style.padding = '8px 12px';
            listItem.style.cursor = 'pointer';
            listItem.style.fontSize = '13px';
            listItem.style.color = '#333';
            listItem.style.whiteSpace = 'nowrap';
            listItem.style.overflow = 'hidden';
            listItem.style.textOverflow = 'ellipsis';
            listItem.style.borderBottom = '1px solid #f0f0f0';
            listItem.style.transition = 'background 0.2s';
            listItem.style.borderRadius = '4px';
            listItem.title = q.text;
            listItem.addEventListener('mouseover', () => {
                listItem.style.background = '#f5f5f5';
            });
            listItem.addEventListener('mouseout', () => {
                listItem.style.background = 'none';
            });
            listItem.addEventListener('click', () => {
                q.element.scrollIntoView({ behavior: 'smooth', block: 'start' });
                floatWindow.style.opacity = '0';
                setTimeout(() => floatWindow.style.display = 'none', 200);
                button.textContent = '问题列表';
            });
            listContainer.appendChild(listItem);
        });
    }

    // 更新分页控件
    function updatePagination() {
        // 清空分页容器
        while (paginationContainer.firstChild) {
            paginationContainer.removeChild(paginationContainer.firstChild);
        }

        const totalPages = Math.ceil(questions.length / pageSize);
        if (totalPages) {
            const prevButton = document.createElement('button');
            prevButton.textContent = '上一页';
            prevButton.style.padding = '5px 10px';
            prevButton.style.border = 'none';
            prevButton.style.background = currentPage === 1 ? '#f0f0f0' : '#007BFF';
            prevButton.style.color = currentPage === 1 ? '#aaa' : '#fff';
            prevButton.style.cursor = currentPage === 1 ? 'not-allowed' : 'pointer';
            prevButton.style.borderRadius = '4px';
            prevButton.style.transition = 'background 0.2s';
            prevButton.disabled = currentPage === 1;
            prevButton.addEventListener('click', () => {
                if (currentPage > 1) {
                    currentPage--;
                    renderPage(currentPage);
                    updatePagination();
                }
            });
            paginationContainer.appendChild(prevButton);

            // 显示页码按钮,但限制最多显示5个
            const maxButtons = 5;
            let startPage = Math.max(1, Math.min(currentPage - Math.floor(maxButtons / 2), totalPages - maxButtons + 1));
            if (startPage < 1) startPage = 1;
            const endPage = Math.min(startPage + maxButtons - 1, totalPages);

            if (startPage > 1) {
                const firstPageButton = document.createElement('button');
                firstPageButton.textContent = '1';
                firstPageButton.style.padding = '5px 10px';
                firstPageButton.style.border = 'none';
                firstPageButton.style.background = '#f0f0f0';
                firstPageButton.style.color = '#333';
                firstPageButton.style.cursor = 'pointer';
                firstPageButton.style.borderRadius = '4px';
                firstPageButton.style.transition = 'background 0.2s';
                firstPageButton.addEventListener('click', () => {
                    currentPage = 1;
                    renderPage(currentPage);
                    updatePagination();
                });
                paginationContainer.appendChild(firstPageButton);

                if (startPage > 2) {
                    const ellipsis = document.createElement('span');
                    ellipsis.textContent = '...';
                    ellipsis.style.padding = '5px';
                    ellipsis.style.color = '#666';
                    paginationContainer.appendChild(ellipsis);
                }
            }

            for (let i = startPage; i <= endPage; i++) {
                const pageButton = document.createElement('button');
                pageButton.textContent = i;
                pageButton.style.padding = '5px 10px';
                pageButton.style.border = 'none';
                pageButton.style.background = currentPage === i ? '#007BFF' : '#f0f0f0';
                pageButton.style.color = currentPage === i ? '#fff' : '#333';
                pageButton.style.cursor = 'pointer';
                pageButton.style.borderRadius = '4px';
                pageButton.style.transition = 'background 0.2s';
                pageButton.addEventListener('click', () => {
                    currentPage = i;
                    renderPage(currentPage);
                    updatePagination();
                });
                paginationContainer.appendChild(pageButton);
            }

            if (endPage < totalPages) {
                if (endPage < totalPages - 1) {
                    const ellipsis = document.createElement('span');
                    ellipsis.textContent = '...';
                    ellipsis.style.padding = '5px';
                    ellipsis.style.color = '#666';
                    paginationContainer.appendChild(ellipsis);
                }

                const lastPageButton = document.createElement('button');
                lastPageButton.textContent = totalPages;
                lastPageButton.style.padding = '5px 10px';
                lastPageButton.style.border = 'none';
                lastPageButton.style.background = '#f0f0f0';
                lastPageButton.style.color = '#333';
                lastPageButton.style.cursor = 'pointer';
                lastPageButton.style.borderRadius = '4px';
                lastPageButton.style.transition = 'background 0.2s';
                lastPageButton.addEventListener('click', () => {
                    currentPage = totalPages;
                    renderPage(currentPage);
                    updatePagination();
                });
                paginationContainer.appendChild(lastPageButton);
            }

            const nextButton = document.createElement('button');
            nextButton.textContent = '下一页';
            nextButton.style.padding = '5px 10px';
            nextButton.style.border = 'none';
            nextButton.style.background = currentPage === totalPages ? '#f0f0f0' : '#007BFF';
            nextButton.style.color = currentPage === totalPages ? '#aaa' : '#fff';
            nextButton.style.cursor = currentPage === totalPages ? 'not-allowed' : 'pointer';
            nextButton.style.borderRadius = '4px';
            nextButton.style.transition = 'background 0.2s';
            nextButton.disabled = currentPage === totalPages;
            nextButton.addEventListener('click', () => {
                if (currentPage < totalPages) {
                    currentPage++;
                    renderPage(currentPage);
                    updatePagination();
                }
            });
            paginationContainer.appendChild(nextButton);
        }
    }

    // 点击切换悬浮窗显示状态
    button.addEventListener('click', () => {
        if (floatWindow.style.display === 'none' || floatWindow.style.display === '') {
            findAllQuestionsWithDeduplication();
            floatWindow.style.display = 'block';
            floatWindow.style.opacity = '1';
            button.textContent = '隐藏列表';
        } else {
            floatWindow.style.opacity = '0';
            setTimeout(() => {
                floatWindow.style.display = 'none';
                button.textContent = '问题列表';
            }, 200);
        }
    });

    // 监听用户输入新问题后触发查找
    function setupInputListener() {
        const input = document.querySelector('textarea, input[type="text"], [contenteditable]');
        if (input) {
            input.addEventListener('keypress', (e) => {
                if (e.key === 'Enter' && !e.shiftKey) {
                    setTimeout(findAllQuestionsWithDeduplication, 1000);
                }
            });
        }
        
        // 监听可能的发送按钮点击
        const sendButtons = document.querySelectorAll('button[type="submit"], button[aria-label*="send"], button[aria-label*="发送"]');
        sendButtons.forEach(btn => {
            btn.addEventListener('click', () => {
                setTimeout(findAllQuestionsWithDeduplication, 1000);
            });
        });
    }

    // 页面加载后初始化
    window.addEventListener('load', () => {
        // 先找一次所有问题
        setTimeout(() => {
            findAllQuestionsWithDeduplication();
            setupInputListener();
            
            // 添加键盘快捷键支持 - Alt+Q 显示/隐藏问题列表
            document.addEventListener('keydown', (e) => {
                if (e.altKey && e.key === 'q') {
                    button.click();
                }
            });
            
            // 自动加载历史记录(延迟执行,让页面先完成初始化)
            setTimeout(() => {
                loadHistoryRecords();
            }, 2000);
        }, 1000);
    });
    
    // MutationObserver 监听DOM变化,动态更新问题列表
    const observerConfig = { childList: true, subtree: true };
    const observer = new MutationObserver((mutationsList) => {
        // 检查是否需要更新问题列表
        for (const mutation of mutationsList) {
            if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
                // 检查是否添加了新的消息元素
                const hasNewMessages = Array.from(mutation.addedNodes).some(node => {
                    if (node.nodeType === Node.ELEMENT_NODE) {
                        return node.matches && node.matches(currentConfig.messageSelector) || 
                               node.querySelector && node.querySelector(currentConfig.messageSelector);
                    }
                    return false;
                });
                
                if (hasNewMessages) {
                    // 使用节流技术避免频繁更新
                    if (!observer.updateTimeout) {
                        observer.updateTimeout = setTimeout(() => {
                            findAllQuestionsWithDeduplication();
                            observer.updateTimeout = null;
                        }, 500);
                    }
                    break;
                }
            }
        }
    });
    
    // 开始观察DOM变化
    setTimeout(() => {
        const chatContainer = document.querySelector('.chat-container, #chat, main, article') || document.body;
        observer.observe(chatContainer, observerConfig);
    }, 1500);
})(); 

QingJ © 2025

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