// ==UserScript== // @name 通义千问提问的问题自动收起长文本(高性能优化版) // @namespace http://tampermonkey.net/ // @version 1.1 // @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', // ==================== 元素标识相关 ==================== // 开关按钮的唯一ID:用于创建/定位自动折叠的开关按钮,避免重复创建 SWITCH_BTN_ID: 'qianwen-collapse-switch', // 提问文本容器的类名:匹配所有需要折叠的提问文本外层容器(多个类名用空格分隔) TEXT_CONTAINER_CLASS: 'questionItem-u8_ahH group foldItem-RqbfLz', // 文本折叠状态的标识类:判断文本是否已折叠(有此类名=已折叠,无需重复处理) FOLD_STATE_CLASS: 'foldTextContent-KB0vSs', // 内容盒子选择器:定位提问文本的核心区域(折叠操作的目标父容器) CONTENT_BOX_SELECTOR: 'div.contentBox-PXNuf2 > div.bubble-VIVxZ8', // 展开按钮的选择器:匹配“收起/展开”按钮的span元素(排除无关的同图标元素) EXPAND_SPAN_SELECTOR: 'button span[data-icon-type="qwpcicon-up"]:not(.size-4):not(.text-16)', // ==================== 性能优化相关 ==================== // 滚动防抖延迟(毫秒):滚动时触发折叠的延迟,避免高频触发(150ms是兼顾响应性和性能的最优值) SCROLL_DEBOUNCE_MS: 150, // 定时巡检间隔(毫秒):兜底检查未折叠文本的间隔,1秒一次既不卡顿也能覆盖特殊场景 POLL_INTERVAL_MS: 1000, // ==================== 样式位置相关 ==================== // 开关按钮的位置配置:固定在页面右上角,z-index确保不被遮挡 BTN_POSITION: { top: 13, // 距离顶部13px(避开页面原生按钮) right: 85, // 距离右侧85px(避开原生的“清空/导出”按钮) zIndex: 9999 // 层级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); } })();