// ==UserScript== // @name 通义千问提问自动折叠 // @namespace http://tampermonkey.net/ // @version 2.0 // @description 通义千问聊天页长文本智能折叠:高性能无内存泄漏,开关状态本地记忆,全场景自动折叠超长提问文本,需关闭开关后手动展开文本 // @author anyphasy (Updated) // @match https://www.qianwen.com/chat/* // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAG4AAABuCAYAAADGWyb7AAASjklEQVR4nO1de3Bc1Xn/nXPv3t2VVrIlWc9daS3Jlq3V28JgAuUZXiGEFgwzNMmENGlKA5OZthMync5k+k/btEP/oDShTRMgBGIClJdJbQNxwUN4GGTJli1jy7IlrZ5gg21Zq927957TP+5dSV7vSvu4L9n9zdxZrfbec75zv/Od833f+c53gP/HigSxm4Bl4K1av/2HlPhKOJfnFv9AQAUQQrVvVACoQIgogYgSIYKLCqtrYsrge9OffOXHdhBuNpzMOFLXOrBddDffnmsBnM1+eqLXFwQQNZAuR4DaTcASKBGlxms5V6LZXOCqDK7KnMcjhBZWVK97/Ud2N8QMiHYTkAaFwfbxt0AkHwFTAJJ1ByMgFOBMKui6E8BPAMSMJ9M+OFLiajbs+y/BVdMFrsq5ME2DxjjBVdNVGzr0AgC3oUTaDCcyrtTtC90OMGVB+cgVhIKrsssbuqMq9Pt/MIY8Z8BpjKP+ziPbCNzF2td8GZcAZ5LQeCUuIqlzGuPKPbTxOoAzw0okVARniigFv1S58Xd/a1i5NsNJjKPB1qO/5kSQNMYZJW0L8LjabsVFInVOYlyZKDVca6i0JUCoCK7KglR7RVXjK39tePk2wCmM8wU7xndxQkWzpE0DZy5P83UABHPKtw5OYBz1Nx99SRBrusBzs9kygt4pXJ6mm/3Ne38JZ7Q9ZzjB5VVe3y0PEwiS9tUsadPBmQIiSLORt/5l+vBNfwdATbpjuXdClvhMvpJ/jwGI50p6NkSaDeLvOPSqWwzdAa7KmlRYAc4AKqrKpwNMPTsJEKo5rUU3IYKkOa0JPd+OJFS/CAGhGq2EgggS0ZzcIoggAYSSxG/znVAb/ll8+uDwgaqrAZzJtwV2M251Q9fcCVB3sblzWypozDO3/AvrU+WJ3pF+/3UAzuZTup3jPKkN9fwU1LPa1LktffUUYErCKT1/gSnGXJwtME/rlJwrUUGq6fJvPPQM8hQaO53Mq0R3860AZ/m7tnJF8nBoYj0ASEJB8la3AlgN4ItcS7RL4lYHOybfIdRbav0QaScIBWcKpSX1Ne19/4k8pM6OFyYEWkZ3CmJVe37e/xUKoi830brLAKzKtRg7Xlqp5PV3G+P9X4nQGEeFkvq6loNPIUeps/rFkbqW3sfAqQh+KQ2RqcAZddVdjhylzuoXVyq6W++0VyFxAhJSV1QdbB3ZAU1RyQpWvryS+o7J3TDN+z9vIC9xOQmaOSK467b4OwdfRJa8sKoxYm3r1B4iVrXbY7Ml4EQGMkWi1W0AyrN5yqpGuER3aYNzhkgn0AAs2HeFFYGuod8iC35Y1wDHKSNOogUQUboWDmQcJcRVoLmCzMJiF9NKA2fxyIHXcOFKRVpYxThZoWfHzXXqJpBg4HJMdAKTdUc3m/t8/Mi1DwPgmT5pFePiox+tbonHBt9MqMLWVLuYicmXU8DZnLz/ZWQZsGv1sk5x/ab4NDExIGjlQOs8TD09MtxX1oIsGWf1i5tl8al+zdnqpF5vA7g2TMZmP3kLOYTHLydxLmjhbBQLTF7umcQ4PYvUy/T+xm4+xh01XFkNbbSJRfY+NX54y18AyFppW4oJxfWdXxwgwupgLqSpykTvyH7/VQDmkn4Sarun+lyoCF2aw6UmaVydmTzRV1yPHDejpH1pgeb3/5UIq4OcK9H5Vd3k1eJUF5jCeTwiiDVd1S3vPZqiaDXcU/U1bUXYTPPAyeBMnut9EYCcawnp1PMCl7vldoCzhcAXIFOvBwEVAaZIYtN1ADy4cGPhmEJnJkVWVG1tkJDd0KSNKacGx49c+yNkof4nIxUj3IGWQ88Roag656GMaMs2gli23t+055EUd8RHPypuUqKDb4IIEi4VydMVsqgy8AYunEKyQvIcR6qb9j7pLdr8rfwlQQ9JU0+PDPeVtqQhtLB+U3SCQPTkXo+5yFWJImk6PId87sS+gloAkXzoSmZMkcfXdqdhe9P0ld7atqEd4f7G23Ah8yJqbPg90bPh1vzqMg9GG7pzZz58GnkyDTifLlLd8u7PvJ6rHjBy3uFclQkRPVG5d9tE/6Y/TXGLr65l38+pWLkRPD7HuRJLBJ4CWmQU55wBqm5acKYNOZxzcEZAaOJzoSV6gCqoQOY74EKZGpjK521JpmpKlRoHV2KAGudckTlXYtA+Zf0elXNVAZh6nh1KCAWooAXVutyEiBIhLg+h7kIQt4+AUEILSmLq6L7JQ1c9hBzU/2QsZlxhfefZQW1uM3LNLOEhODc93LdqAzT7bilakidskuJ/uSBZeIwoM9v6Datz3qgObHz3ESLoWp6htpXmJaFCsb8q9PY/LnEjR+qGGdVYnnRZDUPrTDDILXrazYsF0UPSPFLoZgCS4eVfgqAAXIFQ39M0H/V/WeiBoEL5xqqGl/7K+PIvPZBAy+gHkqf2CvOXOrROocanDowcqLkcBm03cgBELGxPpki/DWvx34lhUwUwk0ulpGET54BV3gvNcyDP7X9hbKDz61j5zPPVXXZyn4uVrc/lYZUBw32kEDmYB1RmQ/9rXQCPtmNF8nbcU9H08t9YU6dpIDUb+p4Qedl6DlXmhDNOmJL2gionX1TgDMCaXCqnY33rblHVU8esWhnWfJ9M8XouvxcrOwNCmaeo/e7znRVLxHQSKp536fevbT/xLHKw8ymAuDw79C5ARWu89Zp5ILhquqpCu//J/PpMAfV3Hv0NeGJBOBd/rv4MLWsEUJQ1AQAwOXjlQ0p08E1CRI8l8Ri6eeB2tdyClSl1NR5h/U35uQa1SGYqFFUH28bfBlCSzdOJSmOjh5ruYOz0iDXBPHqCNLEiVNt84DdYWcyjwbbjz3LAgFhRPQxdqumq7Z58G1mk8VhcqSwrx/+AeYLMhp4graDtrur17/+7+fUZhnJBqr9GkzajNHGmiHxVAIAv0ycWM45P9Hc/uOA9sWrIZIrbF7oNQKHp9eWPorr2sd8Z+260eZIQb2l1695UEQMpkSzmZ2U2+gGQ2L9mNnQ/Ji32V7d+8G/m15cXaCA0vFN0+bsN37iiz5NuofFqZLhfLrlyNtZb/2U5OrB9YTuUyUgQLTZdD6DA9PpyR7lUULfF2CEyAV1REUsb61oG/xsZhE2muiE2dqjlXqbOTFqmqHBVpkJJvX/DOz8xt66cQYNtx7YtqP9mgTNBqulABnNdOs7G1NiJP1gWuKpLnegJ3QItuMhpWMjsZ5qXSRMSQgvW1LUd345lmJeOCB4+3PE9zpUo5jPamQldLRbXNNU29z0NLRDXKSgMto/v0rzCZseBau9BlOqvCXSd2IElhsyliDgtq0d2WRewmjAPOu6p2PCaU1LOU3/z4KtaYm8rd9IyxYXKEICytIQt8TQf3996P+fR0/pXi0LGmeJxd30NzpC6SnfhuhutTe2h71Kl3tLAptHtSMOj5YiZifPpAcvMAz0eU3QFNpevf/4h0+tbGkKga+QlLdDBjlB5ziSUNSJNRobliFHHetferMrjPdbMdQvweq68D/bmGiuXaN0WTSGxOtJa1+ZJwZpgW3gnUrjCMulFsyP9gdsXepzJzNPzJ4tSYHNl/XMPmlrXElTUtfQ/DgD2RllzRl0VzUghdZmK/0kWP7HHmq3ACXDmLrr6m7Anf3KpyxP6qu2Z/cAZIa6CuvbwDiR5VDIlSh0+0PAn2s4dwBKpA2eiy99ds/G9x2HtBkxvsG1sF593Pti5DUwLshJdgc2BruOvYdGCazZEnYnziT4r/ZicK1FP4ZV/vqbxyW+bX59Wac2Gnl8Ikgn+yJwp0m07fr6ikg1h6lhv8IZ47MhOq/yYRDdIvd6r74M1Q2aJx9ex1VmZ/TQ6qFDsD3Qem/djZkvcXPjgxrs4i5y0xI+pmwcu97oby9c//wNT6wJITVvvzwBBcl4yHQDgTER5E/SA4lyIm5Mj+1+2egO+17P5LphrHpR5XE7N7JfY+VTsrw0deh7Aqlx3EbnXdk7vo0JFyNgNImmQOCvg1LPfnx7+xuMm1ECDrUd3Cu71Nzl/hyyhqhI7m+sLl8FmTwKwKMwBADiTfFvugTlzXZngWXejPcZ2luCqLLjcxbkyrpCIlSEAGe8LzwuJ7OHuxutrNu55FMYyrzjYMfGGFhS+AlJ4ECpCPT2S00uvbPifhwkpWGNtMmxt9cBT+EcPGqioCIGW0TcEsbrTMep/WuhnF7DZz473lrTlQqjHW7zlG/ZN4kzxei7fCmOkrlLy1l7h/HkN+pTEWXRm92MAZrJ98STQ/P5jVCipt6WHJlYPpOCXKppe/mGepYm1m8Zft2WLY9bQhISxM+HJY3f+M5C9OVAgeTq32q8yc+bxbN6K/MyDSonUdK0MhUTb5RSd6XsZ+v7xbF6+uzZ0aBuoZ7WtPrxFfkz/hvcfR25Dpri2Y+J1XR9xdo4VrkWVxSIf/GLq2PXzO5wyffmkZkPPEy6vflyY7ZO45sd0+7Z8tyzw0605FOCnYnWnOaF2RkIbDTiPR8YPX/kDAPNab6YMKHb72v7YST68eT/mqqu3IrttSquCnZ+9YW6aYYOgu96U6OEdSEqrlQkTSHXr3kcJXAWO8uHpiorkad+6pvHJP8vwKRpoCb8pCGuaHNWWlNDnXi6fCw90fhdJWRsyIXy1V+q4236FJD283mu+iczaUiN5A5tXjvpPaFT5ZCdS7BNfrrG0ruXgkyCSz/5FxRTQwxxc7oZry4M///oyd4tr28MvWa7+c6ZwrsqcqzI4U867lsoZTQjliJ2dONDxHaTIkbJcrysW3euuXwmuILfvqnsAPIf0CQFKBFdgM7dSIdGd49l78jUhkSOjHyJNVoalGuBa2z70imOlLQF9T7nk3nBLVeO2B6aG7nssxV2eutahF3giD5hVQz4VJCU2/G5cHv6YUo+PEE8RoW4fiFRAiFRAiOgGET2EiG5AkLRPjbY4m9g/frjpbqTJSLRcZwg2dM312XP4bIbQe3V87uAr4YG2u7FIZdZB/BsHtrsLm2+3bm7TDOb4XM8z4YHL7kf6I6vTHUXNoRnaaQf25RgxFienhhzJsPPAWTx2eA8uZBoAVLp9VjONULC5z8MDl30PqU/vSOQTY/qlQmNUHFq63ziWyf21bEBseF/gq5xZHYaeIXRpU+Txnqmhe1MNkWJt98QOS4fHeW3w8E5cmNLYMGTSmM8UenbCuuiu7BGbPbgLqXNAVrlQ3WndMK9rgzx6eqK/+wGYmKUvk8ao4Z7Ka5g8dcDqMPQlwTUvjiIPvzt9/La/T3EHCbYe+eVCYlIraNKdwecObgdwzsyqMu2Fp4b7q2/QAmKtPBtnOVAxcubtp5B6HqkQ3U03W+f91+qJRw68OHl0c0rby0hkM3ycVmPhjxyhqOhzW2zm94+cHP32EynuKA52Tu/mlvkj9aGYK9Hw4Y77YUFyuawCYkcPNd6lKSo2Sl1CIYmd2DN+9Mupju6itS1juwWhImSZP1KvR57b9xwMSJSdCbJt1FmFzkzZL3WcRWd2/QdSD0dVLq+/21L1n1CRs7nPxw5veSgNTYYjWwYo4Z6KmwA156NF8oK+qChHB7Z/OvKX21LcQfydR560VPtNqP+z/a8iz0MgskEukjMRx8mjWrY9VV7SUWrwxaEt4kZnPnw1DW1+j9B0MwBYJ22EMvbFicmjV3zf/PoWkEvjWLin6rr6jrP9RCyqNpyiJUCI5ItFPv71ydHv/CrFz4WNmyL7FxQSi+Y2IkixcwO7YKKxnQr5HGRRHmg+9ltvYVkDmbfvFu9aTU6+CYDoBzZwzkEIAefnzweECgtKD6HgTFfztSWQM59/+Mz00C0/Rmpju7hxE/uCW3YGZcIf2bstPLDpW7A4TXG+zSRYSCCd6qCHXMpLN7lzLH9q79qGTcoR04dJfa5lymefDO+v7IQNuaXzbWAmL9NKhBUhckpkRp9WkgqEynMfvwCbEoJbfbitFQg1dLN+8/yT+pEz7Ex4uLc0BIvntgTs94IYj0FViJ7WnOImeE4WglNfgU1MAy5OxsVH9hZ0MHYmbHzCVF2LjHz0q6ljNzxsXLnZ42IcKhMINnSz48YVx/WDDGcmh/tW1cPmwy4uRolLYApM1pdWDJA63UMSn9tnm0KyGBezxAFAa+NlvJ/nvbNIm9eYcvLo8P7yNuRxmrBRuJglDgAGFSEeyXs1Q/d9ymz0YziAacDFL3EA0NnQzXoSRnP2j+vqP4+cGt5X1Ij0J05aiotd4gBgiLkI1ZLqZINFrjpQUZ4d3A2HMA24NCQOADY2dM69T0R3ceZzHWcJKY1Hj78zenDdV2Cj3ZaMS4VxgJak24Xz/aGJAFQs+t/iT67/HkHqmE3b8H/H3X4QCWWsBwAAAABJRU5ErkJggg== // @grant none // ==/UserScript== (function() { 'use strict'; // ==================== 彩色日志工具 ==================== const Logger = { // 基础前缀样式 baseStyle: 'background: #2c3e50; color: #fff; padding: 2px 4px; border-radius: 3px; font-weight: bold;', info: (msg, ...args) => { console.log(`%c[QW-Collapse] %cINFO%c ${msg}`, Logger.baseStyle, 'color: #3498db; font-weight: bold;', 'color: inherit;', ...args); }, action: (msg, ...args) => { console.log(`%c[QW-Collapse] %cACTION%c ${msg}`, Logger.baseStyle, 'color: #27ae60; font-weight: bold;', 'color: inherit;', ...args); }, warn: (msg, ...args) => { console.warn(`%c[QW-Collapse] %cWARN%c ${msg}`, Logger.baseStyle, 'color: #f39c12; font-weight: bold;', 'color: inherit;', ...args); }, error: (msg, ...args) => { console.error(`%c[QW-Collapse] %cERROR%c ${msg}`, Logger.baseStyle, 'color: #e74c3c; font-weight: bold;', 'color: inherit;', ...args); }, debug: (msg, ...args) => { console.debug(`%c[QW-Collapse] %cDEBUG%c ${msg}`, Logger.baseStyle, 'color: #9b59b6; font-weight: bold;', 'color: inherit;', ...args); } }; // ==================== 1. 常量定义(避免魔法值,减少冗余) ==================== const CONFIG = { // ==================== 本地存储相关 ==================== // 本地存储键名:用于在浏览器 localStorage 中记忆自动折叠功能的开关状态(true=开启, false=关闭) // 这样刷新页面后仍能保持用户之前的选择 STORAGE_KEY: 'qianwen_auto_collapse_v2_enabled', // ==================== 元素标识相关 ==================== // 开关按钮的唯一ID:用于创建或查找右上角的功能开关按钮,防止重复创建多个按钮 SWITCH_BTN_ID: 'qianwen-collapse-switch-v2', // 问题容器选择器:匹配所有包含提问内容的顶层 div // 特征:拥有 data-chat-list-key 属性,且属性值以 "-question" 结尾 QUESTION_CONTAINER_ATTR: 'div[data-chat-list-key$="-question"]', // 底部操作栏类名:包含复制、删除、折叠/展开图标的父容器 BOTTOM_ICON_CONTAINER_CLASS: '.qs-bottom', // 折叠/展开图标 SVG 类名:用于定位具体的箭头图标元素 COLLAPSE_SVG_CLASS: '.qs-bottom-icon-svg', // 旋转状态类名:当 SVG 元素包含此类名时,表示箭头已旋转(即问题处于“已折叠”状态) // 逻辑:不包含此类名 = 展开状态 = 需要执行折叠操作 ROTATED_CLASS: 'qs-bottom-icon-svg-rotate', // ==================== 性能优化相关 ==================== // 滚动防抖延迟(毫秒):当用户滚动页面时,等待这么多毫秒后再执行检查,避免滚动过程中频繁触发 SCROLL_DEBOUNCE_MS: 150, // 定时巡检间隔(毫秒):每隔多少秒强制检查一次页面上的未折叠问题,作为 MutationObserver 的兜底方案 POLL_INTERVAL_MS: 1000, // ==================== 样式位置相关 ==================== // 开关按钮的位置配置:固定在页面右上角 BTN_POSITION: { top: 13, // 距离顶部像素 right: 85, // 距离右侧像素 zIndex: 9999 // 层级,确保按钮浮于其他内容之上 } }; // ==================== 2. 状态管理(单一数据源) ==================== const state = { autoCollapseEnabled: false, // 当前功能开关状态 observer: null, // MutationObserver 实例 scrollTimer: null, // 滚动防抖定时器 ID pollTimer: null, // 定时巡检定时器 ID switchBtn: null // 开关按钮 DOM 元素引用 }; // ==================== 3. 本地存储(封装方法) ==================== const Storage = { get: () => { try { const saved = localStorage.getItem(CONFIG.STORAGE_KEY); // 如果从未设置过,默认开启;否则解析存储的值 const enabled = saved === null ? true : JSON.parse(saved); Logger.info(`读取本地配置: ${enabled ? '✅ 开启' : '❌ 关闭'}`); return enabled; } catch (e) { Logger.error('读取本地配置失败,默认开启', e); return true; } }, set: (enabled) => { localStorage.setItem(CONFIG.STORAGE_KEY, JSON.stringify(enabled)); Logger.info(`保存本地配置: ${enabled ? '✅ 开启' : '❌ 关闭'}`); } }; // ==================== 4. 核心折叠逻辑 ==================== function collapseUnfoldedText(triggerSource = 'Unknown') { if (!state.autoCollapseEnabled) { Logger.debug(`功能已关闭,跳过执行 (触发源: ${triggerSource})`); return; } Logger.info(`开始执行折叠检查... (触发源: ${triggerSource})`); // 批量获取所有问题容器 const containers = document.querySelectorAll(CONFIG.QUESTION_CONTAINER_ATTR); Logger.debug(`当前页面扫描到 ${containers.length} 个问题容器`); if (!containers.length) return; let foldedCount = 0; let skippedCount = 0; // 遍历每个容器 containers.forEach((container, index) => { // 1. 定位折叠按钮的 SVG 元素 // 路径: 容器 -> .qs-bottom -> .qs-bottom-icon-svg const collapseSvg = container.querySelector(`${CONFIG.BOTTOM_ICON_CONTAINER_CLASS} ${CONFIG.COLLAPSE_SVG_CLASS}`); if (!collapseSvg) { Logger.warn(`第 ${index + 1} 个容器未找到折叠按钮 SVG,可能结构已变`); return; } // 2. 判断状态 // 如果包含 'qs-bottom-icon-svg-rotate' 类,说明已经折叠了 const isAlreadyFolded = collapseSvg.classList.contains(CONFIG.ROTATED_CLASS); // 获取问题的唯一ID用于日志展示(截取前8位避免过长) const questionKey = container.getAttribute('data-chat-list-key'); const shortKey = questionKey ? questionKey.substring(0, 8) + '...' : 'Unknown'; if (isAlreadyFolded) { // 已折叠,跳过 skippedCount++; // 仅记录前两个,避免日志刷屏 if (skippedCount <= 2) { Logger.debug(`问题 [${shortKey}] 已处于折叠状态,跳过`); } } else { // 3. 执行折叠 // 找到 SVG 的父级按钮容器 (.qs-bottom-icon),通常点击事件绑定在这里或 SVG 上 const btnParent = collapseSvg.closest('.qs-bottom-icon'); if (btnParent) { Logger.action(`发现未折叠问题 [${shortKey}],执行点击折叠`); btnParent.click(); // 模拟点击 foldedCount++; } else { Logger.error(`找到 SVG 但无法定位父级按钮容器 [${shortKey}]`); } } }); if (foldedCount > 0 || skippedCount > 0) { Logger.info(`检查结束。本次折叠: ${foldedCount} 个, 跳过(已折叠): ${skippedCount} 个`); } } // ==================== 5. 开关按钮(封装创建/更新逻辑) ==================== const SwitchButton = { create: () => { // 避免重复创建 if (state.switchBtn && document.body.contains(state.switchBtn)) { Logger.debug('开关按钮已存在,跳过创建'); return state.switchBtn; } Logger.info('正在创建右上角开关按钮...'); const btn = document.createElement('button'); btn.id = CONFIG.SWITCH_BTN_ID; // 动态设置初始样式 const bgColor = state.autoCollapseEnabled ? '#1677ff' : '#f5f5f5'; const textColor = state.autoCollapseEnabled ? '#fff' : '#666'; const statusText = state.autoCollapseEnabled ? '自动折叠: 开' : '自动折叠: 关'; btn.style.cssText = ` position: fixed; top: ${CONFIG.BTN_POSITION.top}px; right: ${CONFIG.BTN_POSITION.right}px; z-index: ${CONFIG.BTN_POSITION.zIndex}; padding: 6px 10px; border: 1px solid rgba(0,0,0,0.1); border-radius: 4px; background: ${bgColor}; color: ${textColor}; font-size: 12px; cursor: pointer; box-shadow: 0 2px 4px rgba(0,0,0,0.1); transition: all 0.2s; user-select: none; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; `; btn.innerText = statusText; // 点击事件处理 let isClicking = false; btn.addEventListener('click', () => { if (isClicking) return; isClicking = true; Logger.action('用户点击了开关按钮'); // 切换状态 state.autoCollapseEnabled = !state.autoCollapseEnabled; // 更新按钮视觉样式 const newBg = state.autoCollapseEnabled ? '#1677ff' : '#f5f5f5'; const newColor = state.autoCollapseEnabled ? '#fff' : '#666'; const newText = state.autoCollapseEnabled ? '自动折叠: 开' : '自动折叠: 关'; btn.style.background = newBg; btn.style.color = newColor; btn.innerText = newText; // 持久化存储 Storage.set(state.autoCollapseEnabled); // 如果刚开启,立即执行一次折叠 if (state.autoCollapseEnabled) { Logger.info('功能已开启,立即执行一次全量折叠'); collapseUnfoldedText('Switch_On'); } // 简单的防抖,防止快速连点 setTimeout(() => isClicking = false, 200); }); document.body.appendChild(btn); state.switchBtn = btn; Logger.info('开关按钮创建完毕'); return btn; } }; // ==================== 6. 监控管理(统一启停) ==================== const Monitor = { init: () => { Logger.info('初始化 DOM 监控器...'); // 1. MutationObserver:监听 DOM 变化 state.observer = new MutationObserver((mutations) => { if (!state.autoCollapseEnabled) return; let hasAddedNodes = false; // 检查是否有新节点加入 for (const mutation of mutations) { if (mutation.addedNodes.length > 0) { hasAddedNodes = true; break; } } if (hasAddedNodes) { Logger.debug('Observer 检测到新节点插入,触发防抖折叠'); clearTimeout(state.scrollTimer); // 新消息出现时快速响应 state.scrollTimer = setTimeout(() => collapseUnfoldedText('Observer'), 50); } }); // 确定监听目标:优先监听聊天内容区域,降级到 body const targetNode = document.querySelector('.chat-content') || document.querySelector('#chat-container') || document.body; Logger.info(`监听目标元素: <${targetNode.tagName || 'Body'}>`); state.observer.observe(targetNode, { childList: true, // 监听子节点增删 subtree: true // 监听所有后代节点 }); // 2. 滚动监听:防止懒加载或动态渲染遗漏 window.addEventListener('scroll', Monitor.handleScroll, { passive: true }); Logger.info('滚动监听器已绑定'); // 3. 定时巡检:兜底策略 state.pollTimer = setInterval(() => { if (state.autoCollapseEnabled) { // 静默执行,避免日志过多,除非有实际操作 collapseUnfoldedText('Poller'); } }, CONFIG.POLL_INTERVAL_MS); Logger.info(`定时巡检已启动 (间隔: ${CONFIG.POLL_INTERVAL_MS}ms)`); }, // 滚动防抖处理 handleScroll: () => { if (!state.autoCollapseEnabled) return; clearTimeout(state.scrollTimer); state.scrollTimer = setTimeout(() => collapseUnfoldedText('Scroll'), CONFIG.SCROLL_DEBOUNCE_MS); }, // 销毁所有监控(防止内存泄漏) destroy: () => { Logger.warn('页面卸载,销毁监控器...'); if (state.observer) { state.observer.disconnect(); state.observer = null; } clearTimeout(state.scrollTimer); clearInterval(state.pollTimer); window.removeEventListener('scroll', Monitor.handleScroll); } }; // ==================== 7. 初始化入口 ==================== function init() { Logger.info('========================================'); Logger.info('脚本开始初始化...'); // 1. 加载配置 state.autoCollapseEnabled = Storage.get(); // 2. 创建 UI SwitchButton.create(); // 3. 启动监控 Monitor.init(); // 4. 初始执行 if (state.autoCollapseEnabled) { Logger.info('页面加载完成,执行初始折叠检查...'); // 延迟一点执行,确保首屏内容完全渲染 setTimeout(() => collapseUnfoldedText('Init_Load'), 500); } else { Logger.info('功能当前处于关闭状态,不执行初始折叠'); } // 页面关闭前清理 window.addEventListener('beforeunload', Monitor.destroy); Logger.info('========================================'); } // ==================== 8. 启动脚本 ==================== if (document.readyState === 'complete') { init(); } else { window.addEventListener('load', init); } })();