您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Enhanced UI for Tencent YuanBao chat
// ==UserScript== // @name Better Tencent YuanBao // @namespace http://tampermonkey.net/ // @version 2025-06-06 // @description Enhanced UI for Tencent YuanBao chat // @author AAur // @match https://yuanbao.tencent.com/chat/** // @icon https://www.google.com/s2/favicons?sz=64&domain=yuanbao.tencent.com // @grant none // @license MIT // ==/UserScript== (function() { 'use strict'; // ========== 通用工具函数 ========== const waitForElement = (selector) => { return new Promise(resolve => { const el = document.querySelector(selector); if (el) return resolve(el); const container = document.querySelector('.agent-chat__container') || document.body; const observer = new MutationObserver((_, obs) => { const target = document.querySelector(selector); if (target) { obs.disconnect(); resolve(target); } }); observer.observe(container, { childList: true, subtree: true }); }); }; const debounce = (fn, delay) => { let timer; return (...args) => { clearTimeout(timer); timer = setTimeout(() => fn.apply(this, args), delay); }; }; const createStyle = (css) => { const style = document.createElement('style'); style.textContent = css; document.head.appendChild(style); return style; }; // ========== 功能1: 底栏收起按钮 ========== const initToggleButton = async () => { const inputBox = await waitForElement('.agent-dialogue__content--common__input.agent-chat__input-box'); // 添加相关样式 createStyle(` #inputToggleBtn { position: fixed; left: 50%; transform: translateX(-50%); z-index: 9999; padding: 2px 15px; background: #3db057; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 14px; opacity: 0.7; transition: opacity 0.2s, top 0.2s; } #inputToggleBtn:hover { opacity: 1; } .hidden-input { display: none !important; } `); // 创建按钮 const toggleBtn = document.createElement('button'); toggleBtn.id = 'inputToggleBtn'; toggleBtn.textContent = 'Hide'; inputBox.parentNode.insertBefore(toggleBtn, inputBox); // 更新按钮位置函数 const updateButtonPosition = () => { const rect = inputBox.getBoundingClientRect(); const btnWidth = toggleBtn.offsetWidth; const leftPosition = rect.left + (rect.width - btnWidth) / 2; toggleBtn.style.left = `${leftPosition}px`; toggleBtn.style.top = `${rect.top + window.scrollY - 25}px`; }; // 监听输入框大小变化 const observeInputBoxChanges = () => { const resizeObserver = new ResizeObserver(debounce(() => { if (!inputBox.classList.contains('hidden-input')) { updateButtonPosition(); } }, 100)); resizeObserver.observe(inputBox); return resizeObserver; }; let boxObserver = observeInputBoxChanges(); updateButtonPosition(); // 按钮点击事件 toggleBtn.addEventListener('click', () => { if (inputBox.classList.contains('hidden-input')) { inputBox.classList.remove('hidden-input'); toggleBtn.textContent = 'Hide'; boxObserver = observeInputBoxChanges(); updateButtonPosition(); } else { inputBox.classList.add('hidden-input'); toggleBtn.textContent = 'Show'; boxObserver.disconnect(); toggleBtn.style.top = `${document.documentElement.scrollHeight - 40}px`; } }); // 窗口大小调整时更新按钮位置 window.addEventListener('resize', debounce(() => { if (inputBox.classList.contains('hidden-input')) { toggleBtn.style.top = `${document.documentElement.scrollHeight - 30}px`; } else { updateButtonPosition(); } }, 100)); }; // ========== 功能2: 整理有序列表 ========== const initOlProcessor = () => { // 创建按钮样式 createStyle(` #processOlBtn { position: fixed; bottom: 5px; right: 20px; z-index: 9999; padding: 5px 10px; background: #3db057; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 12px; opacity: 0.8; transition: opacity 0.2s; } #processOlBtn:hover { opacity: 1; } .numbered-item { display: block; margin-bottom: 8px; line-height: 1.5; position: relative; padding-left: 1.5em; } `); // 创建并添加整理按钮 const processOlBtn = document.createElement('button'); processOlBtn.id = 'processOlBtn'; processOlBtn.textContent = '整理OL'; document.body.appendChild(processOlBtn); // 计算节点内字符数 const countTextContent = (element) => { let count = 0; const walker = document.createTreeWalker( element, NodeFilter.SHOW_TEXT, null, false ); while (walker.nextNode()) { count += walker.currentNode.textContent.trim().length; } return count; }; // 转换单个OL元素 const processOlElement = (ol) => { // Mark as processed to avoid duplicate processing ol.classList.add('processed-ol'); // Get direct child LI elements only const lis = Array.from(ol.querySelectorAll(':scope > li')); const fragment = document.createDocumentFragment(); lis.forEach((li, index) => { // Create a new div to replace the li const numberedDiv = document.createElement('div'); numberedDiv.className = 'numbered-item'; // Find the first text node inside the li (ignoring whitespace) function findFirstTextNode(element) { // 遍历所有子节点 for (const child of element.childNodes) { if (child.nodeType === Node.TEXT_NODE && child.textContent.trim() !== '') { return child; // 找到目标文本节点 } else if (child.nodeType === Node.ELEMENT_NODE) { const found = findFirstTextNode(child); // 递归搜索子元素 if (found) return found; } } return null; // 未找到 } const firstTextNode = findFirstTextNode(li); if (firstTextNode) { // 在文本节点前插入序号(保留原文本) firstTextNode.textContent = `${index + 1}. ${firstTextNode.textContent.trim()}`; } else { // 如果没有文本节点,则在 <li> 开头插入序号 const textNode = document.createTextNode(`${index + 1}. `); li.prepend(textNode); } // Move all of li's children to the new div while (li.firstChild) { numberedDiv.appendChild(li.firstChild); } fragment.appendChild(numberedDiv); }); // Replace the OL with our new structure ol.replaceWith(fragment); }; // 处理所有OL元素 const processAllOls = () => { const ols = document.querySelectorAll('ol:not(.processed-ol)'); let processedCount = 0; ols.forEach(ol => { if (countTextContent(ol) > 200) { processOlElement(ol); processedCount++; } }); // 显示处理结果 if (processedCount > 0) { processOlBtn.textContent = `已整理${processedCount}个OL`; setTimeout(() => { processOlBtn.textContent = '整理OL'; }, 2000); } else { processOlBtn.textContent = '未发现需整理的OL'; setTimeout(() => { processOlBtn.textContent = '整理OL'; }, 2000); } }; // 观察内容变化并延迟处理 const observeContentChanges = () => { const contentElement = document.querySelector('.agent-dialogue__content--common__content'); if (!contentElement) return; let changeTimer; const observer = new MutationObserver((mutations) => { // 检查是否有实际内容变化 const hasRelevantChange = mutations.some(mutation => mutation.type === 'childList' || (mutation.type === 'characterData' && mutation.target.textContent.trim()) ); if (hasRelevantChange) { clearTimeout(changeTimer); changeTimer = setTimeout(() => { processAllOls(); }, 500); } }); observer.observe(contentElement, { childList: true, subtree: true, characterData: true }); return () => observer.disconnect(); }; // 初始化内容观察 let cleanupObserver; const initObserver = () => { if (cleanupObserver) cleanupObserver(); cleanupObserver = observeContentChanges(); }; // 延迟初始化观察器,确保元素已加载 setTimeout(initObserver, 1000); // 按钮点击事件 processOlBtn.addEventListener('click', processAllOls); // 返回initObserver以便外部调用 return initObserver; }; // ========== 主初始化函数 ========== const main = () => { const initOlObserver = initOlProcessor(); // 获取返回的initObserver函数 Promise.all([ initToggleButton(), initOlObserver(), initOlObserver && initOlObserver() // 如果initOlProcessor返回了initObserver就调用 ]).catch(error => { console.error('Better Tencent YuanBao initialization error:', error); }); }; // ========== 启动脚本 ========== if ('requestIdleCallback' in window) { window.requestIdleCallback(main); } else { setTimeout(main, 500); } })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址