// ==UserScript== // @name 通义千问提问的问题自动收起长文本(高性能优化版) // @namespace http://tampermonkey.net/ // @version 1.0 // @description 通义千问聊天页长文本智能折叠:高性能无内存泄漏,开关状态本地记忆,全场景自动折叠超长提问文本,需关闭开关后手动展开文本 // @author anyphasy // @match https://www.qianwen.com/chat/* // @icon https://img.alicdn.com/imgextra/i4/O1CN01uar8u91DHWktnF2fl_!!6000000000191-2-tps-110-110.png // @grant none // ==/UserScript== (function() { 'use strict'; // ==================== 1. 常量定义(避免魔法值,减少冗余) ==================== const CONFIG = { STORAGE_KEY: 'qianwen_auto_collapse_enabled', SWITCH_BTN_ID: 'qianwen-collapse-switch', TEXT_CONTAINER_CLASS: 'questionItem-MPmrIl group foldItem-wi1bKO', FOLD_STATE_CLASS: 'foldTextContent-kvlC_1', CONTENT_BOX_SELECTOR: 'div.contentBox-t7l7vJ > div.bubble-uo23is', EXPAND_SPAN_SELECTOR: 'button span[data-icon-type="qwpcicon-up"]:not(.size-4):not(.text-16)', // 性能参数(合理值,避免频繁触发) SCROLL_DEBOUNCE_MS: 150, POLL_INTERVAL_MS: 1000, BTN_POSITION: { top: 13, right: 85, zIndex: 9999 } }; // ==================== 2. 状态管理(单一数据源,避免冗余变量) ==================== const state = { autoCollapseEnabled: false, observer: null, scrollTimer: null, pollTimer: null, switchBtn: null }; // ==================== 3. 本地存储(封装方法,减少重复代码) ==================== const Storage = { get: () => { const saved = localStorage.getItem(CONFIG.STORAGE_KEY); return saved === null ? true : JSON.parse(saved); }, set: (enabled) => { localStorage.setItem(CONFIG.STORAGE_KEY, JSON.stringify(enabled)); } }; // ==================== 4. 核心折叠逻辑(优化遍历,减少DOM查询) ==================== function collapseUnfoldedText() { if (!state.autoCollapseEnabled) return; // 批量获取所有容器,避免多次DOM查询 const allContainers = document.querySelectorAll(`div.${CONFIG.TEXT_CONTAINER_CLASS.replace(/ /g, '.')}`); if (!allContainers.length) return; // 遍历优化:一旦找到并折叠,立即跳出当前容器循环(减少无用操作) allContainers.forEach(container => { const contentBox = container.querySelector(CONFIG.CONTENT_BOX_SELECTOR); if (!contentBox || contentBox.classList.contains(CONFIG.FOLD_STATE_CLASS)) return; const expandSpans = container.querySelectorAll(CONFIG.EXPAND_SPAN_SELECTOR); for (const span of expandSpans) { if (span.parentElement?.tagName.toLowerCase() === 'button') { span.click(); console.log('[通义千问自动收起] 折叠文本成功'); break; // 一个容器只折叠一次,避免重复点击 } } }); } // ==================== 5. 开关按钮(封装创建/更新逻辑,避免冗余) ==================== const SwitchButton = { create: () => { // 避免重复创建 if (state.switchBtn) return state.switchBtn; const btn = document.createElement('button'); btn.id = CONFIG.SWITCH_BTN_ID; btn.style.cssText = ` position: fixed; top: ${CONFIG.BTN_POSITION.top}px; right: ${CONFIG.BTN_POSITION.right}px; z-index: ${CONFIG.BTN_POSITION.zIndex}; padding: 8px 12px; border: none; border-radius: 20px; background: ${state.autoCollapseEnabled ? '#4096ff' : '#999'}; color: white; font-size: 14px; cursor: pointer; box-shadow: 0 2px 8px rgba(0,0,0,0.15); transition: all 0.2s; user-select: none; `; btn.innerText = state.autoCollapseEnabled ? '自动折叠:开启' : '自动折叠:关闭'; // 点击事件(防抖,避免快速点击多次触发) let isClicking = false; btn.addEventListener('click', () => { if (isClicking) return; isClicking = true; // 切换状态 state.autoCollapseEnabled = !state.autoCollapseEnabled; // 更新样式 btn.style.background = state.autoCollapseEnabled ? '#4096ff' : '#999'; btn.innerText = state.autoCollapseEnabled ? '自动折叠:开启' : '自动折叠:关闭'; // 保存状态 Storage.set(state.autoCollapseEnabled); // 开启时立即折叠 if (state.autoCollapseEnabled) collapseUnfoldedText(); // 防抖释放 setTimeout(() => isClicking = false, 200); }); document.body.appendChild(btn); state.switchBtn = btn; return btn; }, update: () => { if (!state.switchBtn) return; state.switchBtn.style.background = state.autoCollapseEnabled ? '#4096ff' : '#999'; state.switchBtn.innerText = state.autoCollapseEnabled ? '自动折叠:开启' : '自动折叠:关闭'; } }; // ==================== 6. 监控管理(统一启停,避免内存泄漏) ==================== const Monitor = { // 初始化所有监控 init: () => { // 1. MutationObserver(优化监听范围,减少无用回调) state.observer = new MutationObserver((mutations) => { if (!state.autoCollapseEnabled) return; // 只监听关键变化:新增文本容器 或 折叠状态类变化 const needHandle = mutations.some(m => { // 新增文本容器 const hasNewContainer = Array.from(m.addedNodes).some(node => node.nodeType === 1 && node.matches(`div.${CONFIG.TEXT_CONTAINER_CLASS.replace(/ /g, '.')}`) ); // 折叠状态类变化 const hasClassChange = m.type === 'attributes' && m.attributeName === 'class'; return hasNewContainer || hasClassChange; }); if (needHandle) collapseUnfoldedText(); }); // 缩小监听范围:只监听聊天内容区域(而非整个body),大幅提升性能 const chatContent = document.querySelector('.chat-content') || document.body; state.observer.observe(chatContent, { childList: true, attributes: true, subtree: true, attributeFilter: ['class', 'data-icon-type'] // 只监听关键属性 }); // 2. 滚动监听(防抖优化,避免频繁触发) window.addEventListener('scroll', Monitor.handleScroll); // 3. 定时巡检(延长间隔,减少性能消耗) state.pollTimer = setInterval(() => { if (state.autoCollapseEnabled) collapseUnfoldedText(); }, CONFIG.POLL_INTERVAL_MS); }, // 滚动防抖处理 handleScroll: () => { if (!state.autoCollapseEnabled) return; clearTimeout(state.scrollTimer); state.scrollTimer = setTimeout(collapseUnfoldedText, CONFIG.SCROLL_DEBOUNCE_MS); }, // 销毁所有监控(避免内存泄漏) destroy: () => { // 停止Observer if (state.observer) { state.observer.disconnect(); state.observer = null; } // 清除滚动定时器 clearTimeout(state.scrollTimer); // 清除定时巡检 clearInterval(state.pollTimer); // 移除滚动监听 window.removeEventListener('scroll', Monitor.handleScroll); } }; // ==================== 7. 初始化/销毁(统一入口,避免冗余) ==================== function init() { // 初始化状态 state.autoCollapseEnabled = Storage.get(); // 创建开关按钮 SwitchButton.create(); // 初始化监控 Monitor.init(); // 初始执行一次折叠 if (state.autoCollapseEnabled) collapseUnfoldedText(); // 页面卸载时销毁所有监控(核心:防止内存泄漏) window.addEventListener('beforeunload', Monitor.destroy); } // ==================== 8. 页面加载完成后初始化 ==================== if (document.readyState === 'complete') { init(); } else { window.addEventListener('load', init); } })();