您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
让整个网络世界都变成超可爱的猫娘语调喵~增强版(优化版)
当前为
// ==UserScript== // @name 🐱 全世界都要变成可爱猫猫!增强版 // @version 4.2.0 // @description 让整个网络世界都变成超可爱的猫娘语调喵~增强版(优化版) // @author 超萌猫娘开发队 // @match *://*/* // @include *://*.bilibili.com/video/* // @include *://*.bilibili.com/anime/* // @include *://*.bilibili.com/bangumi/play/* // @exclude *://gf.qytechs.cn/* // @exclude *://*.gov/* // @exclude *://*.edu/* // @grant GM_getValue // @grant GM_setValue // @grant GM_addStyle // @grant GM_registerMenuCommand // @license MIT // @namespace https://gf.qytechs.cn/users/1503554 // ==/UserScript== (function() { 'use strict'; // ===== 版本管理 ===== const SCRIPT_VERSION = "4.2.0"; const isVersionUpdate = GM_getValue("SCRIPT_VERSION") !== SCRIPT_VERSION; if (isVersionUpdate) { GM_setValue("SCRIPT_VERSION", SCRIPT_VERSION); console.log('🎉 猫娘脚本已更新到版本', SCRIPT_VERSION); } // ===== 增强配置系统 ===== const defaultConfig = { // 性能配置 performance: { processInterval: 5000, maxProcessingTimeSlice: 8, batchSize: 5, observerThrottle: 1000, maxRetryAttempts: 10, idleCallbackTimeout: 2000, debounceDelay: 500 }, // 功能开关 features: { affectInput: false, bilibiliMergeALinks: true, bilibiliRandomizeUserNames: true, // 用户名前缀功能开关 autoProcessNewContent: true, shadowDomSupport: true, performanceMonitoring: false, debugMode: false, smartProcessing: true, enableBlacklist: true // 黑名单功能开关 }, // 站点配置 sites: { excludeDomains: [ 'github.com', 'stackoverflow.com', 'google.com', 'gov.cn', 'edu.cn', 'gf.qytechs.cn' ], bilibili: { smartPause: true, retryInterval: 1000, maxRetryDelay: 5000, commentSelector: 'bili-comment-thread-renderer', userNameSelector: '#user-name a', contentSelector: '#contents span' } }, // 黑名单配置 blacklist: { sites: [], // 格式: [{domain: 'example.com', type: 'site'|'page', url: '', expiry: timestamp, reason: ''}] defaultDuration: 24 * 60 * 60 * 1000, // 24小时 enabled: true }, // 快捷键配置 shortcuts: { toggleScript: "100C", resetProcessing: "101R", showSettings: "100S", debugMode: "101D", showBlacklist: "100B" // 显示黑名单面板 }, // 用户偏好 - 优化描述 preferences: { cuteLevel: 'normal', // low, normal, high customEndings: [], disabledWords: [], processingMode: 'contextual', // gentle(保守替换), contextual(上下文感知), aggressive(积极替换) intelligentReplacement: true, replacementIntensity: 0.3, // 替换强度 0.1-1.0 endingFrequency: 0.5, // 结尾词频率 0.1-1.0 decorativeFrequency: 0.2 // 装饰符频率 0.1-1.0 }, // 统计信息 stats: { processedElements: 0, replacedWords: 0, lastActive: new Date().toISOString(), installDate: new Date().toISOString(), sessionProcessed: 0, blacklistHits: 0 // 黑名单命中次数 } }; // 加载用户配置 let userConfig = GM_getValue("catgirlConfig") || {}; let CONFIG = Object.assign({}, defaultConfig, userConfig); // 如果是版本更新,合并新配置 if (isVersionUpdate) { CONFIG = Object.assign(defaultConfig, userConfig); GM_setValue("catgirlConfig", CONFIG); showUpdateNotification(); } // ===== 扩展的可爱元素库 ===== const cuteLibrary = { endings: { low: ['喵', '呢', '哦', '啊'], normal: ['喵~', 'にゃん', '喵呜', 'nya~', '喵喵', '呢~'], high: ['喵~♪', 'にゃん♡', '喵呜~', 'nya~♡', '喵喵desu', 'にゃ♡', 'mew~', '喵♪', 'nyaa~', '喵desu~', '喵呢~', '喵哈~'] }, userPrefixes: ['🏳️⚧️', '✨', '💕', '🌸', '🎀', '🌟'], decorativePrefixes: ['✨', '💫', '⭐', '🌸', '🎀', '💝'], emotionalEndings: { excited: ['喵!', 'にゃん!', '哇喵~', '好棒喵~'], calm: ['喵~', '呢~', '嗯喵', '是这样喵'], happy: ['开心喵~', '嘻嘻喵', '哈哈喵~', '好开心喵'], confused: ['诶喵?', '嗯?喵', '咦喵~', '不懂喵'], sad: ['呜呜喵', '难过喵', '555喵', '想哭喵'] } }; // ===== 黑名单管理器 ===== class BlacklistManager { constructor() { this.panel = null; this.isVisible = false; } isBlacklisted() { if (!CONFIG.blacklist.enabled) return false; const currentDomain = location.hostname; const currentUrl = location.href; const now = Date.now(); for (const item of CONFIG.blacklist.sites) { // 检查是否过期 if (item.expiry && item.expiry < now) { this.removeExpiredItem(item); continue; } // 检查域名匹配 if (item.type === 'site' && currentDomain.includes(item.domain)) { CONFIG.stats.blacklistHits++; return true; } // 检查页面匹配 if (item.type === 'page' && currentUrl === item.url) { CONFIG.stats.blacklistHits++; return true; } } return false; } addToBlacklist(type, duration, reason = '') { const now = Date.now(); const expiry = duration === -1 ? null : now + duration; const item = { id: this.generateId(), domain: location.hostname, url: type === 'page' ? location.href : '', type: type, expiry: expiry, reason: reason, addedAt: now }; CONFIG.blacklist.sites.push(item); GM_setValue("catgirlConfig", CONFIG); const durationText = duration === -1 ? '永久' : this.formatDuration(duration); const typeText = type === 'site' ? '整站' : '单页面'; showToast(`已将${typeText}加入黑名单 (${durationText})`, 'success'); } removeFromBlacklist(id) { CONFIG.blacklist.sites = CONFIG.blacklist.sites.filter(item => item.id !== id); GM_setValue("catgirlConfig", CONFIG); this.updateDisplay(); showToast('已从黑名单移除', 'success'); } removeExpiredItem(item) { CONFIG.blacklist.sites = CONFIG.blacklist.sites.filter(i => i.id !== item.id); GM_setValue("catgirlConfig", CONFIG); } generateId() { return Date.now().toString(36) + Math.random().toString(36).substr(2); } formatDuration(ms) { if (ms === -1) return '永久'; const days = Math.floor(ms / (24 * 60 * 60 * 1000)); const hours = Math.floor((ms % (24 * 60 * 60 * 1000)) / (60 * 60 * 1000)); if (days > 0) return `${days}天${hours}小时`; if (hours > 0) return `${hours}小时`; return '不到1小时'; } show() { if (!this.panel) this.create(); this.panel.style.display = 'block'; this.isVisible = true; this.updateDisplay(); } hide() { if (this.panel) { this.panel.style.display = 'none'; this.isVisible = false; } } create() { if (this.panel) return; this.panel = document.createElement('div'); this.panel.id = 'catgirl-blacklist'; this.panel.innerHTML = this.getHTML(); this.panel.style.cssText = this.getCSS(); document.body.appendChild(this.panel); this.bindEvents(); } getHTML() { return ` <div class="blacklist-header"> <h3>🚫 网站黑名单管理</h3> <button class="close-btn" data-action="close">×</button> </div> <div class="blacklist-content"> <div class="current-site-section"> <h4>🌐 当前网站操作</h4> <div class="current-site-info"> <strong>域名:</strong> <code>${location.hostname}</code><br> <strong>页面:</strong> <code>${location.pathname}</code> </div> <div class="blacklist-actions"> <div class="action-group"> <label>拉黑类型:</label> <select id="blacklist-type"> <option value="site">整个网站</option> <option value="page">仅当前页面</option> </select> </div> <div class="action-group"> <label>拉黑时长:</label> <select id="blacklist-duration"> <option value="3600000">1小时</option> <option value="21600000">6小时</option> <option value="86400000">1天</option> <option value="604800000">1周</option> <option value="2592000000">1个月</option> <option value="-1">永久</option> </select> </div> <div class="action-group"> <label>拉黑原因:</label> <input type="text" id="blacklist-reason" placeholder="可选,记录拉黑原因"> </div> <button id="add-to-blacklist" class="btn-danger">🚫 加入黑名单</button> </div> </div> <div class="blacklist-section"> <h4>📋 黑名单列表</h4> <div id="blacklist-items"></div> <div class="blacklist-stats"> <small>黑名单命中次数: <span id="blacklist-hits">${CONFIG.stats.blacklistHits}</span></small> </div> </div> <div class="blacklist-settings"> <h4>⚙️ 黑名单设置</h4> <label> <input type="checkbox" id="enable-blacklist" ${CONFIG.blacklist.enabled ? 'checked' : ''}> 启用黑名单功能 </label> </div> <div class="actions"> <button id="save-blacklist" class="btn-primary">💾 保存设置</button> <button id="clear-expired" class="btn-secondary">🧹 清理过期</button> </div> </div> `; } getCSS() { return ` position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 600px; max-height: 80vh; background: #ffffff; border-radius: 12px; box-shadow: 0 8px 32px rgba(0,0,0,0.3); z-index: 10000; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; display: none; overflow-y: auto; `; } bindEvents() { // 关闭按钮 this.panel.querySelector('[data-action="close"]').onclick = () => this.hide(); // 添加到黑名单 document.getElementById('add-to-blacklist').onclick = () => { const type = document.getElementById('blacklist-type').value; const duration = parseInt(document.getElementById('blacklist-duration').value); const reason = document.getElementById('blacklist-reason').value; this.addToBlacklist(type, duration, reason); this.updateDisplay(); }; // 保存设置 document.getElementById('save-blacklist').onclick = () => { CONFIG.blacklist.enabled = document.getElementById('enable-blacklist').checked; GM_setValue("catgirlConfig", CONFIG); showToast('黑名单设置已保存', 'success'); }; // 清理过期 document.getElementById('clear-expired').onclick = () => { this.clearExpired(); }; } updateDisplay() { const container = document.getElementById('blacklist-items'); if (!container) return; const now = Date.now(); const items = CONFIG.blacklist.sites.map(item => { const isExpired = item.expiry && item.expiry < now; const timeLeft = item.expiry ? this.formatDuration(item.expiry - now) : '永久'; const addedAt = new Date(item.addedAt).toLocaleString(); return ` <div class="blacklist-item ${isExpired ? 'expired' : ''}"> <div class="item-info"> <div class="item-domain">${item.domain}</div> <div class="item-details"> 类型: ${item.type === 'site' ? '整站' : '单页面'} | 剩余: ${isExpired ? '已过期' : timeLeft} | 添加: ${addedAt} ${item.reason ? `<br>原因: ${item.reason}` : ''} </div> </div> <button onclick="window.catgirlApp.blacklistManager.removeFromBlacklist('${item.id}')" class="remove-btn">移除</button> </div> `; }).join(''); container.innerHTML = items || '<div class="empty-state">暂无黑名单项目</div>'; // 更新统计 const hitsEl = document.getElementById('blacklist-hits'); if (hitsEl) hitsEl.textContent = CONFIG.stats.blacklistHits; } clearExpired() { const now = Date.now(); const before = CONFIG.blacklist.sites.length; CONFIG.blacklist.sites = CONFIG.blacklist.sites.filter(item => !item.expiry || item.expiry > now ); const after = CONFIG.blacklist.sites.length; const removed = before - after; GM_setValue("catgirlConfig", CONFIG); this.updateDisplay(); showToast(`已清理 ${removed} 个过期项目`, 'success'); } } // ===== 防抖工具类 ===== class DebounceUtils { static debounce(func, delay) { let timeoutId; return function (...args) { clearTimeout(timeoutId); timeoutId = setTimeout(() => func.apply(this, args), delay); }; } static throttleWithDebounce(func, throttleMs, debounceMs) { let lastCallTime = 0; let debounceTimer; return function (...args) { const now = Date.now(); clearTimeout(debounceTimer); if (now - lastCallTime >= throttleMs) { lastCallTime = now; func.apply(this, args); } else { debounceTimer = setTimeout(() => { lastCallTime = Date.now(); func.apply(this, args); }, debounceMs); } }; } } // ===== 增强的性能工具类 ===== class EnhancedPerformanceUtils { static createTimeSliceProcessor(items, processor, options = {}) { const { batchSize = CONFIG.performance.batchSize, maxTime = CONFIG.performance.maxProcessingTimeSlice, onProgress = null, onComplete = null } = options; const processedKeys = new Set(); const uniqueItems = items.filter(item => { const key = this.getItemKey(item); if (processedKeys.has(key)) return false; processedKeys.add(key); return true; }); if (uniqueItems.length === 0) { if (onComplete) onComplete(); return; } let index = 0; let startTime = Date.now(); const processNextBatch = () => { const batchStartTime = performance.now(); let processedInBatch = 0; while (index < uniqueItems.length && processedInBatch < batchSize && (performance.now() - batchStartTime) < maxTime) { try { processor(uniqueItems[index], index, uniqueItems); } catch (error) { console.error('🐱 处理项目出错:', error); } index++; processedInBatch++; } if (onProgress) { onProgress(index, uniqueItems.length, (index / uniqueItems.length) * 100); } if (index < uniqueItems.length) { if (window.requestIdleCallback) { requestIdleCallback(processNextBatch, { timeout: CONFIG.performance.idleCallbackTimeout }); } else { setTimeout(processNextBatch, 16); } } else { const duration = Date.now() - startTime; if (CONFIG.features.debugMode) { console.log(`🎉 完成处理 ${uniqueItems.length} 个项目,耗时 ${duration}ms`); } if (onComplete) onComplete(); } }; processNextBatch(); } static getItemKey(item) { if (item && item.nodeType === Node.ELEMENT_NODE) { return `${item.tagName}-${item.textContent ? item.textContent.slice(0, 20) : ''}-${item.offsetTop || 0}`; } return String(item); } } // ===== 状态管理类 ===== class StateManager { constructor() { this.state = { isEnabled: true, currentUrl: location.href, processingQueue: new Set(), urlChangeHandlers: [], lastProcessTime: 0, bilibili: { isCompleted: false, lastProcessedUrl: '', lastProcessedTime: 0, retryCount: 0, commentObserver: null, lastCommentCount: 0 } }; } onUrlChange(handler) { if (!this.state.urlChangeHandlers) { this.state.urlChangeHandlers = []; } this.state.urlChangeHandlers.push(handler); } checkUrlChange() { const newUrl = location.href; if (newUrl !== this.state.currentUrl) { if (CONFIG.features.debugMode) { console.log('🔄 页面切换:', this.state.currentUrl, '->', newUrl); } this.state.currentUrl = newUrl; this.state.bilibili.isCompleted = false; this.state.bilibili.lastProcessedUrl = newUrl; this.state.bilibili.retryCount = 0; this.state.bilibili.lastCommentCount = 0; if (this.state.urlChangeHandlers && Array.isArray(this.state.urlChangeHandlers)) { this.state.urlChangeHandlers.forEach(handler => { try { if (typeof handler === 'function') { handler(newUrl); } } catch (error) { console.error('🐱 URL变化处理器出错:', error); } }); } return true; } return false; } shouldProcess() { const now = Date.now(); const timeSinceLastProcess = now - this.state.lastProcessTime; if (timeSinceLastProcess < 1000) { return false; } this.state.lastProcessTime = now; return true; } shouldSkipBilibiliProcessing() { if (!this.isBilibili()) return true; const { isCompleted, lastProcessedUrl, lastProcessedTime } = this.state.bilibili; const now = Date.now(); return isCompleted && lastProcessedUrl === location.href && (now - lastProcessedTime) < 30000; } markBilibiliCompleted() { this.state.bilibili.isCompleted = true; this.state.bilibili.lastProcessedUrl = location.href; this.state.bilibili.lastProcessedTime = Date.now(); } isBilibili() { return location.hostname.includes('bilibili.com'); } checkBilibiliCommentChange() { if (!this.isBilibili()) return false; const commentThreads = document.querySelectorAll('bili-comment-thread-renderer'); const currentCount = commentThreads.length; if (currentCount !== this.state.bilibili.lastCommentCount) { this.state.bilibili.lastCommentCount = currentCount; this.state.bilibili.isCompleted = false; return true; } return false; } } // ===== 增强的文本处理器 ===== class EnhancedTextProcessor { constructor() { this.processedTexts = new Set(); this.replacementStats = new Map(); this.contextAnalyzer = new ContextAnalyzer(); } isProcessed(text) { return /喵[~~呜哈呢♪♡!]|nya|にゃん|meow|🏳️⚧️|已处理标记/i.test(text) || this.processedTexts.has(text); } getCuteEnding(context = 'normal') { const level = CONFIG.preferences.cuteLevel; const mode = CONFIG.preferences.processingMode; let endings = cuteLibrary.endings[level] || cuteLibrary.endings.normal; switch (mode) { case 'gentle': endings = endings.slice(0, Math.ceil(endings.length / 2)); break; case 'aggressive': endings = [...endings, ...cuteLibrary.emotionalEndings[context] || []]; break; case 'contextual': default: if (cuteLibrary.emotionalEndings[context]) { endings = [...endings, ...cuteLibrary.emotionalEndings[context]]; } break; } return endings[Math.floor(Math.random() * endings.length)]; } analyzeContext(text) { const excitedMarkers = /[!!??]{2,}|哇|呀|啊{2,}/; const happyMarkers = /笑|哈哈|嘻嘻|开心|快乐|爽|棒/; const sadMarkers = /哭|难过|伤心|555|呜呜|痛苦/; const angryMarkers = /生气|愤怒|气死|烦|讨厌|恶心/; const confusedMarkers = /[??]{2,}|什么|啥|诶|咦|奇怪/; if (excitedMarkers.test(text)) return 'excited'; if (happyMarkers.test(text)) return 'happy'; if (sadMarkers.test(text)) return 'sad'; if (angryMarkers.test(text)) return 'excited'; if (confusedMarkers.test(text)) return 'confused'; if (/[。.,,;;]/.test(text)) return 'calm'; return 'normal'; } processText(text, options = {}) { if (!text?.trim() || this.isProcessed(text)) return text; if (CONFIG.preferences.disabledWords.some(word => text.includes(word))) { return text; } this.processedTexts.add(text); let result = text; let replacementCount = 0; const context = this.analyzeContext(text); const cleanups = this.getCleanupRules(context); cleanups.forEach(([regex, replacement]) => { const matches = result.match(regex); if (matches) { const finalReplacement = this.getSmartReplacement(replacement, context); result = result.replace(regex, finalReplacement); replacementCount += matches.length; } }); if (CONFIG.preferences.processingMode !== 'gentle' || replacementCount > 0) { result = this.addCuteEndings(result, context); } if (CONFIG.preferences.processingMode === 'aggressive' && Math.random() < CONFIG.preferences.decorativeFrequency) { const prefix = cuteLibrary.decorativePrefixes[Math.floor(Math.random() * cuteLibrary.decorativePrefixes.length)]; result = `${prefix} ${result}`; } if (replacementCount > 0) { CONFIG.stats.replacedWords += replacementCount; CONFIG.stats.sessionProcessed += replacementCount; this.updateReplacementStats(text, result); } return result; } getSmartReplacement(baseReplacement, context) { if (!CONFIG.preferences.intelligentReplacement) return baseReplacement; const contextEnding = cuteLibrary.emotionalEndings[context]; if (contextEnding && Math.random() < 0.4) { const ending = contextEnding[Math.floor(Math.random() * contextEnding.length)]; return `${baseReplacement}${ending}`; } return baseReplacement; } addCuteEndings(text, context = 'normal') { const getCuteEnding = () => this.getCuteEnding(context); const addDesu = () => { const chance = CONFIG.preferences.cuteLevel === 'high' ? 0.4 : CONFIG.preferences.cuteLevel === 'normal' ? 0.2 : 0.1; return Math.random() < chance ? 'です' : ''; }; let probability = CONFIG.preferences.endingFrequency; switch (CONFIG.preferences.processingMode) { case 'gentle': probability *= 0.5; break; case 'aggressive': probability *= 2; break; } return text .replace(/([也矣兮乎者焉哉]|[啊吗呢吧哇呀哦嘛喔咯呜捏])([\s\p{P}]|$)/gu, (_, $1, $2) => `${getCuteEnding()}${addDesu()}${$2}`) .replace(/([的了辣])([\s\p{P}]|$)/gu, (_, $1, $2) => Math.random() < probability ? `${$1}${getCuteEnding()}${addDesu()}${$2}` : `${$1}${$2}`); } // ===== 大幅扩展的清理规则 - 针对B站和百度贴吧 ===== getCleanupRules(context = 'normal') { const baseRules = [ // ===== 极端攻击与侮辱性词汇 ===== [/操你妈|操你娘|操你全家|肏你妈|干你妈|干你娘|去你妈的|去你娘的|去你全家/gi, '去睡觉觉'], [/妈了个?逼|妈的?智障|妈的/gi, '喵喵喵'], [/狗娘养的|狗杂种|狗东西|狗逼|狗比/gi, '不太好的小家伙'], [/操你大爷|去你大爷的|你大爷的/gi, '去玩耍啦'], [/去你老师的|你全家死光|你妈死了|你妈没了/gi, '嗯...安静一点'], [/你妈妈叫你回家吃饭|你妈炸了/gi, '你妈妈叫你回家吃饭'], // ===== 性相关及不雅词汇 ===== [/鸡巴|鸡叭|鸡把|屌|吊|\bjb\b|\bJB\b|\bJj\b/gi, '小鱼干'], [/逼你|逼样|逼毛|逼崽子|什么逼|傻逼|煞逼|沙逼|装逼|牛逼|吹逼/gi, '小淘气'], [/肏|干你|草你|cao你|cao你妈|操逼|日你|日了|艹你/gi, '去玩耍啦'], [/生殖器|阴茎|阴道|性器官|做爱|啪啪|上床|嘿咻/gi, '小秘密'], // ===== B站特色脏话 ===== [/小鬼|小学生|初中生|孤儿|没爹|没妈|爹妈死了/gi, '小朋友'], [/死妈|死全家|死开|去死|死了算了/gi, '去睡觉觉'], [/脑瘫|弱智|智障|残疾|白痴|傻子|蠢货|蠢蛋/gi, '小糊涂虫'], [/废物|废柴|废狗|垃圾|拉圾|辣鸡|人渣|渣渣/gi, '要抱抱的小家伙'], [/恶心|想吐|反胃|讨厌死了|烦死了/gi, '有点不开心'], // ===== 百度贴吧常见脏话 ===== [/楼主是猪|楼主智障|楼主有病|lz有毒|楼主滚|lz滚/gi, '楼主很可爱'], [/水贴|灌水|刷屏|占楼|抢沙发|前排|火钳刘明/gi, '路过留名'], [/举报了|封号|删帖|水军|托儿|五毛|美分/gi, '认真讨论中'], [/撕逼|掐架|开撕|互喷|对骂|群嘲/gi, '友好交流'], // ===== 网络用语和缩写 ===== [/\bcnm\b|\bCNM\b|c\s*n\s*m/gi, '你好软糯'], [/\bnmsl\b|\bNMSL\b|n\s*m\s*s\s*l/gi, '你超棒棒'], [/\bmlgb\b|\bMLGB\b|m\s*l\s*g\s*b/gi, '哇好厉害'], [/tmd|TMD|t\s*m\s*d|他妈的/gi, '太萌啦'], [/wtf|WTF|w\s*t\s*f|what\s*the\s*fuck/gi, '哇好神奇'], [/\bf\*\*k|\bf\*ck|fuck|\bFC\b|\bF\*\b/gi, '哇哦'], [/\bsh\*t|shit|\bs\*\*t/gi, '小意外'], [/\bbitch|\bb\*tch|\bb\*\*\*\*/gi, '小坏蛋'], // ===== 地域攻击相关 ===== [/河南人|东北人|农村人|乡下人|山沟里|土包子/gi, '各地朋友'], [/北上广|屌丝|土豪|装富|穷逼|没钱|破产/gi, '普通人'], // ===== 游戏相关脏话 ===== [/菜鸡|菜逼|坑货|坑爹|坑队友|演员|挂逼|开挂/gi, '游戏新手'], [/noob|萌新杀手|虐菜|吊打|碾压|秒杀/gi, '游戏高手'], // ===== 饭圈和明星相关 ===== [/黑粉|脑残粉|私生饭|蹭热度|营销号|炒作|塌房/gi, '追星族'], [/爬|滚|死开|别来|有毒|拉黑|取关/gi, '不太喜欢'], // ===== 学历和职业攻击 ===== [/小学毕业|没文化|文盲|初中肄业|高中都没毕业/gi, '正在学习中'], [/打工仔|搬砖|送外卖|快递员|保安|清洁工/gi, '勤劳的人'], // ===== 年龄相关攻击 ===== [/老不死|老东西|老头子|老太婆|更年期|中年油腻/gi, '年长者'], [/熊孩子|小屁孩|幼稚|没长大|巨婴/gi, '年轻朋友'], // ===== 外貌身材攻击 ===== [/丑逼|长得丑|颜值低|矮子|胖子|瘦猴|秃头|光头/gi, '独特的人'], [/整容|假脸|网红脸|蛇精脸|锥子脸/gi, '美丽的人'], // ===== 常见口头禅和语气词 ===== [/我靠|我擦|我操|卧槽|握草|我草|尼玛|你妹/gi, '哇哦'], [/妈蛋|蛋疼|扯蛋|完蛋|滚蛋|鸡蛋|咸蛋/gi, '天哪'], [/见鬼|见了鬼|活见鬼|撞鬼了/gi, '好奇怪'], // ===== 网络流行语 ===== [/笑死我了|笑死|xswl|XSWL|笑尿了/gi, '好有趣'], [/绝绝子|yyds|YYDS|永远的神|真香|真tm香/gi, '超级棒棒'], [/emo了|emo|EMO|破防了|破大防|血压高/gi, '有点难过'], [/社死|社会性死亡|尴尬死了|丢人现眼/gi, '有点害羞'], // ===== B站弹幕常见词汇 ===== [/前方高能|高能预警|非战斗人员撤离|前排吃瓜/gi, '注意啦'], [/弹幕护体|弹幕保护|人类的本质|复读机/gi, '大家一起说'], [/鬼畜|魔性|洗脑|单曲循环|dssq|DSSQ/gi, '很有趣'], // ===== 百度贴吧表情包文字 ===== [/滑稽|斜眼笑|狗头保命|手动狗头|\[狗头\]|\[滑稽\]/gi, '嘿嘿嘿'], [/微笑|呵呵|嘿嘿|嘻嘻|哈哈|哈哈哈/gi, '开心笑'], // ===== 政治敏感和争议话题 ===== [/五毛党|美分党|公知|带路党|精神外国人|慕洋犬/gi, '不同观点的人'], [/粉红|小粉红|战狼|玻璃心|民族主义/gi, '爱国人士'], // ===== 其他常见不当用词 ===== [/有病|脑子有问题|神经病|精神病|疯子|疯了/gi, '想法特别'], [/你有毒|有毒|中毒了|下毒|毒瘤/gi, '很特殊'], [/癌症|艾滋|梅毒|性病|传染病/gi, '不舒服'], [/自杀|跳楼|上吊|服毒|割腕/gi, '要好好的'] ]; // 根据可爱程度调整规则严格度 if (CONFIG.preferences.cuteLevel === 'high') { // 高可爱程度下添加更多轻微词汇的替换 const extraRules = [ [/靠|擦|草|淦|艹/gi, '哎呀'], [/烦|闷|郁闷|无聊|没劲/gi, '有点小情绪'], [/累|疲惫|困|想睡|犯困/gi, '需要休息'], [/痛|疼|难受|不舒服|头疼/gi, '不太好'], [/怒|生气|愤怒|恼火|火大/gi, '有点不开心'], [/哭|难过|伤心|委屈|想哭/gi, '需要抱抱'], [/怕|害怕|恐惧|担心|紧张/gi, '有点紧张'], [/尴尬|囧|汗|无语|无奈/gi, '有点小尴尬'], [/晕|懵|蒙|糊涂|迷糊/gi, '有点迷茫'], [/急|着急|焦虑|慌|慌张/gi, '有点小急'], ]; baseRules.push(...extraRules); } // 根据上下文返回不同强度的规则 return baseRules.map(([regex, replacement]) => { const contextEnding = this.getCuteEnding(context); return [regex, `${replacement}${contextEnding}`]; }); } updateReplacementStats(original, processed) { const key = `${original.length}:${processed.length}`; this.replacementStats.set(key, (this.replacementStats.get(key) || 0) + 1); } } // ===== 上下文分析器类 ===== class ContextAnalyzer { constructor() { this.patterns = { excited: /[!!??]{2,}|哇|呀|啊{2,}/, happy: /笑|哈哈|嘻嘻|开心|快乐|爽|棒/, sad: /哭|难过|伤心|555|呜呜|痛苦/, angry: /生气|愤怒|气死|烦|讨厌|恶心/, confused: /[??]{2,}|什么|啥|诶|咦|奇怪/, calm: /[。.,,;;]/ }; } analyze(text) { for (const [emotion, pattern] of Object.entries(this.patterns)) { if (pattern.test(text)) { return emotion; } } return 'normal'; } } // ===== 增强设置面板类 ===== class SettingsPanel { constructor() { this.isVisible = false; this.panel = null; } create() { if (this.panel) return; this.panel = document.createElement('div'); this.panel.id = 'catgirl-settings'; this.panel.innerHTML = this.getHTML(); this.panel.style.cssText = this.getCSS(); document.body.appendChild(this.panel); this.bindEvents(); } getHTML() { return ` <div class="settings-header"> <h3>🐱 猫娘化设置面板</h3> <button class="close-btn" data-action="close">×</button> </div> <div class="settings-content"> <div class="settings-tabs"> <button class="tab-btn active" data-tab="basic">基础设置</button> <button class="tab-btn" data-tab="advanced">高级设置</button> <button class="tab-btn" data-tab="control">控制选项</button> <button class="tab-btn" data-tab="stats">统计信息</button> </div> <div class="tab-content" id="basic-tab"> <div class="setting-group"> <label>🎀 可爱程度: <select id="cute-level"> <option value="low">低 (温和可爱)</option> <option value="normal" selected>普通 (标准可爱)</option> <option value="high">高 (超级可爱,更多替换)</option> </select> </label> <small>控制添加可爱词汇的数量和频率,高档位会替换更多轻微词汇</small> </div> <div class="setting-group"> <label>⚙️ 处理模式: <select id="processing-mode"> <option value="gentle">保守 (只替换明显不当词汇)</option> <option value="contextual" selected>上下文感知 (根据语境智能处理)</option> <option value="aggressive">积极 (大量替换,最大化猫娘化)</option> </select> </label> <small>控制文本处理的策略和强度,上下文感知会分析语境选择合适的可爱词汇</small> </div> <div class="setting-group"> <label>📊 替换强度: <input type="range" id="replacement-intensity" min="0.1" max="1.0" step="0.1" value="${CONFIG.preferences.replacementIntensity}"> <span id="intensity-value">${Math.round(CONFIG.preferences.replacementIntensity * 100)}%</span> </label> <small>控制词汇替换的概率,数值越高替换越频繁</small> </div> <div class="setting-group"> <label>🎵 结尾词频率: <input type="range" id="ending-frequency" min="0.1" max="1.0" step="0.1" value="${CONFIG.preferences.endingFrequency}"> <span id="ending-value">${Math.round(CONFIG.preferences.endingFrequency * 100)}%</span> </label> <small>控制"喵~"等可爱结尾词的出现频率</small> </div> <div class="setting-group"> <label>✨ 装饰符频率: <input type="range" id="decorative-frequency" min="0.1" max="1.0" step="0.1" value="${CONFIG.preferences.decorativeFrequency}"> <span id="decorative-value">${Math.round(CONFIG.preferences.decorativeFrequency * 100)}%</span> </label> <small>控制"✨"等装饰性符号的出现频率</small> </div> </div> <div class="tab-content" id="advanced-tab" style="display: none;"> <div class="setting-group"> <label> <input type="checkbox" id="intelligent-replacement"> 🧠 智能替换 </label> <small>根据上下文情感智能选择替换词汇</small> </div> <div class="setting-group"> <label> <input type="checkbox" id="debug-mode"> 🐛 调试模式 </label> <small>显示详细的处理日志信息</small> </div> <div class="setting-group"> <label> <input type="checkbox" id="performance-monitoring"> 📊 性能监控 </label> <small>启用内存和性能监控</small> </div> <div class="setting-group"> <label>🕐 处理间隔 (毫秒): <input type="range" id="process-interval" min="1000" max="10000" step="500" value="${CONFIG.performance.processInterval}"> <span id="interval-value">${CONFIG.performance.processInterval}</span> </label> <small>控制自动处理的时间间隔,数值越小响应越快但消耗更多资源</small> </div> <div class="setting-group"> <label>📦 批处理大小: <input type="range" id="batch-size" min="3" max="20" step="1" value="${CONFIG.performance.batchSize}"> <span id="batch-value">${CONFIG.performance.batchSize}</span> </label> <small>单次处理的元素数量,数值越大处理越快但可能卡顿</small> </div> <div class="setting-group"> <label>⏱️ 防抖延迟 (毫秒): <input type="range" id="debounce-delay" min="100" max="2000" step="100" value="${CONFIG.performance.debounceDelay}"> <span id="debounce-value">${CONFIG.performance.debounceDelay}</span> </label> <small>防止重复处理的延迟时间,数值越大越省资源</small> </div> </div> <div class="tab-content" id="control-tab" style="display: none;"> <div class="setting-group"> <label> <input type="checkbox" id="affect-input"> 📝 影响输入框 </label> <small>是否处理输入框和文本域中的内容</small> </div> <div class="setting-group"> <label> <input type="checkbox" id="enable-user-prefix"> 👤 启用用户名前缀 </label> <small>为用户名添加可爱的表情符号前缀</small> </div> <div class="setting-group"> <label> <input type="checkbox" id="bilibili-merge-links"> 🔗 B站链接合并 </label> <small>将B站评论中的链接转换为纯文本</small> </div> <div class="setting-group"> <label> <input type="checkbox" id="auto-process-content"> 🔄 自动处理新内容 </label> <small>自动处理动态加载的新内容</small> </div> <div class="setting-group"> <label> <input type="checkbox" id="shadow-dom-support"> 🌐 Shadow DOM 支持 </label> <small>处理 Shadow DOM 中的内容(如B站评论区)</small> </div> <div class="setting-group"> <label> <input type="checkbox" id="smart-processing"> 🧠 智能处理 </label> <small>启用上下文感知和智能优化</small> </div> <div class="setting-group"> <h4>🚫 排除词汇</h4> <textarea id="disabled-words" placeholder="输入不想被替换的词汇,每行一个" rows="3">${CONFIG.preferences.disabledWords.join('\n')}</textarea> <small>这些词汇不会被猫娘化处理</small> </div> </div> <div class="tab-content" id="stats-tab" style="display: none;"> <div class="stats-grid"> <div class="stat-item"> <div class="stat-number" id="processed-count">${CONFIG.stats.processedElements}</div> <div class="stat-label">已处理元素</div> </div> <div class="stat-item"> <div class="stat-number" id="replaced-count">${CONFIG.stats.replacedWords}</div> <div class="stat-label">已替换词汇</div> </div> <div class="stat-item"> <div class="stat-number" id="session-count">${CONFIG.stats.sessionProcessed}</div> <div class="stat-label">本次会话</div> </div> <div class="stat-item"> <div class="stat-number" id="blacklist-hits-count">${CONFIG.stats.blacklistHits}</div> <div class="stat-label">黑名单命中</div> </div> </div> <div class="info-section"> <h4>📅 系统信息</h4> <p><strong>版本:</strong> ${SCRIPT_VERSION}</p> <p><strong>安装时间:</strong> ${new Date(CONFIG.stats.installDate).toLocaleString()}</p> <p><strong>最后活动:</strong> ${new Date(CONFIG.stats.lastActive).toLocaleString()}</p> <p><strong>当前网站:</strong> ${location.hostname}</p> <p><strong>运行状态:</strong> <span id="running-status">检测中...</span></p> </div> <div class="performance-section"> <h4>⚡ 性能信息</h4> <div id="performance-info"> <p>内存使用: <span id="memory-usage">获取中...</span></p> <p>处理速度: <span id="processing-speed">计算中...</span></p> </div> </div> </div> <div class="actions"> <button id="save-settings" class="btn-primary">💾 保存设置</button> <button id="reset-settings" class="btn-warning">🔄 重置设置</button> <button id="clear-cache" class="btn-secondary">🧹 清理缓存</button> </div> </div> `; } getCSS() { return ` position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 600px; max-height: 85vh; background: #ffffff; border-radius: 12px; box-shadow: 0 8px 32px rgba(0,0,0,0.3); z-index: 10000; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; display: none; overflow: hidden; `; } bindEvents() { // 关闭按钮 const closeBtn = this.panel.querySelector('[data-action="close"]'); if (closeBtn) { closeBtn.onclick = () => this.hide(); } // 标签切换 const tabBtns = this.panel.querySelectorAll('.tab-btn'); tabBtns.forEach(btn => { btn.onclick = () => this.switchTab(btn.dataset.tab); }); // 滑块事件 this.bindSliderEvents(); // 保存设置 const saveBtn = document.getElementById('save-settings'); if (saveBtn) { saveBtn.onclick = () => { this.saveSettings(); this.hide(); showToast('设置已保存喵~ ✨', 'success'); }; } // 重置设置 const resetBtn = document.getElementById('reset-settings'); if (resetBtn) { resetBtn.onclick = () => { if (confirm('确定要重置所有设置吗?这将清除所有自定义配置。')) { this.resetSettings(); showToast('设置已重置喵~ 🔄', 'info'); } }; } // 清理缓存 const clearBtn = document.getElementById('clear-cache'); if (clearBtn) { clearBtn.onclick = () => { this.clearCache(); showToast('缓存已清理喵~ 🧹', 'info'); }; } } bindSliderEvents() { const sliders = [ { id: 'replacement-intensity', valueId: 'intensity-value', isPercent: true }, { id: 'ending-frequency', valueId: 'ending-value', isPercent: true }, { id: 'decorative-frequency', valueId: 'decorative-value', isPercent: true }, { id: 'process-interval', valueId: 'interval-value', isPercent: false }, { id: 'batch-size', valueId: 'batch-value', isPercent: false }, { id: 'debounce-delay', valueId: 'debounce-value', isPercent: false } ]; sliders.forEach(({ id, valueId, isPercent }) => { const slider = document.getElementById(id); const valueSpan = document.getElementById(valueId); if (slider && valueSpan) { slider.oninput = (e) => { const value = parseFloat(e.target.value); valueSpan.textContent = isPercent ? `${Math.round(value * 100)}%` : value; }; } }); } switchTab(tabName) { // 切换按钮状态 this.panel.querySelectorAll('.tab-btn').forEach(btn => { btn.classList.remove('active'); }); this.panel.querySelector(`[data-tab="${tabName}"]`).classList.add('active'); // 切换内容 this.panel.querySelectorAll('.tab-content').forEach(content => { content.style.display = 'none'; }); const targetTab = document.getElementById(`${tabName}-tab`); if (targetTab) { targetTab.style.display = 'block'; } } show() { if (!this.panel) this.create(); this.loadCurrentSettings(); this.panel.style.display = 'block'; this.isVisible = true; this.updateStats(); this.updatePerformanceInfo(); } hide() { if (this.panel) { this.panel.style.display = 'none'; this.isVisible = false; } } loadCurrentSettings() { // 基础设置 const elements = { 'cute-level': CONFIG.preferences.cuteLevel, 'processing-mode': CONFIG.preferences.processingMode, 'replacement-intensity': CONFIG.preferences.replacementIntensity, 'ending-frequency': CONFIG.preferences.endingFrequency, 'decorative-frequency': CONFIG.preferences.decorativeFrequency, }; Object.entries(elements).forEach(([id, value]) => { const element = document.getElementById(id); if (element) { element.value = value; // 更新滑块显示 if (element.type === 'range') { const valueId = id.replace('-', '-').split('-')[0] + '-value'; const valueSpan = document.getElementById(valueId); if (valueSpan) { const isPercent = ['replacement-intensity', 'ending-frequency', 'decorative-frequency'].includes(id); valueSpan.textContent = isPercent ? `${Math.round(value * 100)}%` : value; } } } }); // 复选框设置 const checkboxes = { 'affect-input': CONFIG.features.affectInput, 'enable-user-prefix': CONFIG.features.bilibiliRandomizeUserNames, 'bilibili-merge-links': CONFIG.features.bilibiliMergeALinks, 'auto-process-content': CONFIG.features.autoProcessNewContent, 'shadow-dom-support': CONFIG.features.shadowDomSupport, 'smart-processing': CONFIG.features.smartProcessing, 'intelligent-replacement': CONFIG.preferences.intelligentReplacement, 'debug-mode': CONFIG.features.debugMode, 'performance-monitoring': CONFIG.features.performanceMonitoring, }; Object.entries(checkboxes).forEach(([id, checked]) => { const element = document.getElementById(id); if (element) element.checked = checked; }); // 高级设置 const advancedElements = { 'process-interval': CONFIG.performance.processInterval, 'batch-size': CONFIG.performance.batchSize, 'debounce-delay': CONFIG.performance.debounceDelay, }; Object.entries(advancedElements).forEach(([id, value]) => { const element = document.getElementById(id); if (element) { element.value = value; const valueSpan = document.getElementById(id.replace('-', '-').split('-')[0] + '-value'); if (valueSpan) valueSpan.textContent = value; } }); // 排除词汇 const disabledWordsEl = document.getElementById('disabled-words'); if (disabledWordsEl) { disabledWordsEl.value = CONFIG.preferences.disabledWords.join('\n'); } } saveSettings() { // 基础设置 CONFIG.preferences.cuteLevel = document.getElementById('cute-level')?.value || CONFIG.preferences.cuteLevel; CONFIG.preferences.processingMode = document.getElementById('processing-mode')?.value || CONFIG.preferences.processingMode; CONFIG.preferences.replacementIntensity = parseFloat(document.getElementById('replacement-intensity')?.value || CONFIG.preferences.replacementIntensity); CONFIG.preferences.endingFrequency = parseFloat(document.getElementById('ending-frequency')?.value || CONFIG.preferences.endingFrequency); CONFIG.preferences.decorativeFrequency = parseFloat(document.getElementById('decorative-frequency')?.value || CONFIG.preferences.decorativeFrequency); // 复选框设置 CONFIG.features.affectInput = document.getElementById('affect-input')?.checked || false; CONFIG.features.bilibiliRandomizeUserNames = document.getElementById('enable-user-prefix')?.checked || false; CONFIG.features.bilibiliMergeALinks = document.getElementById('bilibili-merge-links')?.checked || false; CONFIG.features.autoProcessNewContent = document.getElementById('auto-process-content')?.checked || false; CONFIG.features.shadowDomSupport = document.getElementById('shadow-dom-support')?.checked || false; CONFIG.features.smartProcessing = document.getElementById('smart-processing')?.checked || false; CONFIG.preferences.intelligentReplacement = document.getElementById('intelligent-replacement')?.checked || false; CONFIG.features.debugMode = document.getElementById('debug-mode')?.checked || false; CONFIG.features.performanceMonitoring = document.getElementById('performance-monitoring')?.checked || false; // 高级设置 CONFIG.performance.processInterval = parseInt(document.getElementById('process-interval')?.value || CONFIG.performance.processInterval); CONFIG.performance.batchSize = parseInt(document.getElementById('batch-size')?.value || CONFIG.performance.batchSize); CONFIG.performance.debounceDelay = parseInt(document.getElementById('debounce-delay')?.value || CONFIG.performance.debounceDelay); // 排除词汇 const disabledWordsEl = document.getElementById('disabled-words'); if (disabledWordsEl) { CONFIG.preferences.disabledWords = disabledWordsEl.value .split('\n') .map(word => word.trim()) .filter(word => word.length > 0); } GM_setValue("catgirlConfig", CONFIG); } resetSettings() { CONFIG = Object.assign({}, defaultConfig); CONFIG.stats = Object.assign({}, defaultConfig.stats, { installDate: GM_getValue("catgirlConfig")?.stats?.installDate || new Date().toISOString() }); GM_setValue("catgirlConfig", CONFIG); this.loadCurrentSettings(); } clearCache() { if (window.catgirlApp && window.catgirlApp.clearCache) { window.catgirlApp.clearCache(); } } updateStats() { const stats = { 'processed-count': CONFIG.stats.processedElements, 'replaced-count': CONFIG.stats.replacedWords, 'session-count': CONFIG.stats.sessionProcessed, 'blacklist-hits-count': CONFIG.stats.blacklistHits }; Object.entries(stats).forEach(([id, value]) => { const element = document.getElementById(id); if (element) element.textContent = value; }); // 更新运行状态 const statusEl = document.getElementById('running-status'); if (statusEl && window.catgirlApp) { const isRunning = window.catgirlApp.app.isRunning; statusEl.textContent = isRunning ? '运行中 ✅' : '已暂停 ⏸️'; statusEl.style.color = isRunning ? '#28a745' : '#ffc107'; } } updatePerformanceInfo() { // 更新内存使用 if (performance.memory) { const memoryEl = document.getElementById('memory-usage'); if (memoryEl) { const used = (performance.memory.usedJSHeapSize / 1024 / 1024).toFixed(1); const limit = (performance.memory.jsHeapSizeLimit / 1024 / 1024).toFixed(1); memoryEl.textContent = `${used}MB / ${limit}MB`; } } // 计算处理速度 const speedEl = document.getElementById('processing-speed'); if (speedEl) { const elementsPerSecond = CONFIG.stats.sessionProcessed > 0 ? Math.round(CONFIG.stats.sessionProcessed / ((Date.now() - performance.timeOrigin) / 1000)) : 0; speedEl.textContent = `${elementsPerSecond} 元素/秒`; } } } // ===== 主应用类 ===== class CatgirlApp { constructor() { this.stateManager = new StateManager(); this.textProcessor = new EnhancedTextProcessor(); this.settingsPanel = new SettingsPanel(); this.blacklistManager = new BlacklistManager(); this.processedElements = new WeakSet(); this.isRunning = false; this.intervalId = null; this.observer = null; this.lastProcessHash = ''; this.processLock = false; this.bilibiliCommentObserver = null; } async initialize() { // 检查黑名单 if (this.blacklistManager.isBlacklisted()) { if (CONFIG.features.debugMode) { console.log('🐱 当前网站已被加入黑名单,不启动喵~'); } return; } if (this.shouldExclude()) { if (CONFIG.features.debugMode) { console.log('🐱 域名已排除,不启动喵~'); } return; } console.log('🐱 增强版猫娘化系统启动喵~'); this.registerMenuCommands(); this.setupKeyboardShortcuts(); await this.waitForDOMReady(); this.stateManager.onUrlChange((newUrl) => { setTimeout(() => { if (location.href === newUrl) { this.processPage(); } }, CONFIG.performance.debounceDelay); }); this.start(); } shouldExclude() { return CONFIG.sites.excludeDomains.some(domain => location.hostname.includes(domain) ); } registerMenuCommands() { GM_registerMenuCommand("🐱 设置面板", () => this.settingsPanel.show()); GM_registerMenuCommand("🚫 网站黑名单", () => this.blacklistManager.show()); GM_registerMenuCommand("⛔ 屏蔽当前网站", () => this.showBlockSiteDialog()); GM_registerMenuCommand("🔄 重新处理", () => this.restart()); GM_registerMenuCommand("📊 显示统计", () => this.showStats()); GM_registerMenuCommand("🧹 清理缓存", () => this.clearCache()); } showBlockSiteDialog() { const domain = location.hostname; const options = [ { text: '屏蔽整个网站 1小时', type: 'site', duration: 3600000 }, { text: '屏蔽整个网站 1天', type: 'site', duration: 86400000 }, { text: '永久屏蔽整个网站', type: 'site', duration: -1 }, { text: '仅屏蔽当前页面 1小时', type: 'page', duration: 3600000 }, { text: '仅屏蔽当前页面 1天', type: 'page', duration: 86400000 }, ]; const choice = prompt( `选择屏蔽方式 (${domain}):\n\n` + options.map((opt, i) => `${i + 1}. ${opt.text}`).join('\n') + '\n\n请输入数字 (1-5):', '1' ); const index = parseInt(choice) - 1; if (index >= 0 && index < options.length) { const selected = options[index]; const reason = prompt('屏蔽原因 (可选):', '') || '用户手动屏蔽'; this.blacklistManager.addToBlacklist(selected.type, selected.duration, reason); // 如果是整站屏蔽,立即停止脚本 if (selected.type === 'site') { this.stop(); showToast('网站已屏蔽,脚本已停止运行', 'info', 5000); } } } setupKeyboardShortcuts() { let inputFocused = false; const handleFocusIn = DebounceUtils.debounce((e) => { if (e.target && e.target.matches && e.target.matches('input, textarea')) { inputFocused = true; } }, 100); const handleFocusOut = DebounceUtils.debounce((e) => { if (e.target && e.target.matches && e.target.matches('input, textarea')) { inputFocused = false; } }, 100); document.addEventListener('focusin', handleFocusIn); document.addEventListener('focusout', handleFocusOut); document.addEventListener('keydown', (e) => { if (inputFocused && !e.ctrlKey && !e.altKey) return; const key = `${e.altKey?1:0}${e.ctrlKey?1:0}${e.shiftKey?1:0}${e.key.toUpperCase()}`; switch (key) { case CONFIG.shortcuts.toggleScript: e.preventDefault(); this.toggle(); break; case CONFIG.shortcuts.showSettings: e.preventDefault(); this.settingsPanel.show(); break; case CONFIG.shortcuts.showBlacklist: e.preventDefault(); this.blacklistManager.show(); break; case CONFIG.shortcuts.resetProcessing: e.preventDefault(); this.restart(); break; case CONFIG.shortcuts.debugMode: e.preventDefault(); this.toggleDebug(); break; } }); } async start() { if (this.isRunning) return; this.isRunning = true; setTimeout(() => { if (this.isRunning) { this.processPage(); } }, 1000); if (CONFIG.performance.processInterval > 0) { const debouncedProcess = DebounceUtils.throttleWithDebounce( () => this.conditionalProcess(), CONFIG.performance.processInterval, CONFIG.performance.debounceDelay ); this.intervalId = setInterval(debouncedProcess, CONFIG.performance.processInterval); } this.setupMutationObserver(); if (this.stateManager.isBilibili()) { this.setupBilibiliCommentObserver(); } if (CONFIG.features.debugMode) { console.log('🎀 猫娘化系统已启动完成喵~'); } } conditionalProcess() { if (!this.isRunning || this.processLock) return; const urlChanged = this.stateManager.checkUrlChange(); const commentChanged = this.stateManager.checkBilibiliCommentChange(); const shouldProcess = this.stateManager.shouldProcess(); if (urlChanged || commentChanged || (!this.stateManager.shouldSkipBilibiliProcessing() && shouldProcess)) { this.processPage(); } } processPage() { if (!this.isRunning || !CONFIG.features.autoProcessNewContent || this.processLock) { return; } this.processLock = true; try { const elements = this.getProcessableElements(); const contentHash = this.generateContentHash(elements); if (contentHash === this.lastProcessHash) { return; } this.lastProcessHash = contentHash; if (elements.length === 0) { return; } EnhancedPerformanceUtils.createTimeSliceProcessor( elements, (element) => this.processElement(element), { onProgress: CONFIG.features.debugMode ? (current, total, percent) => { if (current % 50 === 0) { console.log(`🐱 处理进度: ${percent.toFixed(1)}% (${current}/${total})`); } } : null, onComplete: () => { CONFIG.stats.lastActive = new Date().toISOString(); GM_setValue("catgirlConfig", CONFIG); if (this.stateManager.isBilibili()) { this.processBilibiliSpecial(); } } } ); } finally { setTimeout(() => { this.processLock = false; }, 1000); } } getProcessableElements() { const baseSelector = 'title, h1, h2, h3, h4, h5, h6, p, article, section, blockquote, li, a, span, div:not([class*="settings"]):not([id*="catgirl"])'; const inputSelector = CONFIG.features.affectInput ? ', input, textarea' : ''; const elements = document.querySelectorAll(baseSelector + inputSelector); return Array.from(elements).filter(element => { return !this.processedElements.has(element) && !element.closest('#catgirl-settings, #catgirl-debug, #catgirl-blacklist') && this.shouldProcessElement(element); }); } shouldProcessElement(element) { if (!element.textContent?.trim()) return false; if (this.textProcessor.isProcessed(element.textContent)) return false; if (element.tagName && /^(SCRIPT|STYLE|NOSCRIPT)$/.test(element.tagName)) return false; if (element.offsetParent === null && element.style.display !== 'none') return false; return true; } generateContentHash(elements) { const textContent = elements.slice(0, 10).map(el => el.textContent?.slice(0, 50)).join('|'); return textContent.length + ':' + elements.length; } processElement(element) { if (!element || this.processedElements.has(element)) return; try { if (element.matches && element.matches('input, textarea') && CONFIG.features.affectInput) { if (element.value?.trim()) { element.value = this.textProcessor.processText(element.value); } } else { this.processElementText(element); } this.processedElements.add(element); CONFIG.stats.processedElements++; } catch (error) { if (CONFIG.features.debugMode) { console.error('🐱 处理元素出错:', error); } } } processElementText(element) { if (element.children.length === 0) { const newText = this.textProcessor.processText(element.textContent); if (newText !== element.textContent) { element.textContent = newText; } } else { const textNodes = this.getTextNodes(element); textNodes.forEach(node => { if (node.textContent?.trim()) { const newText = this.textProcessor.processText(node.textContent); if (newText !== node.textContent) { node.textContent = newText; } } }); } } getTextNodes(element) { const textNodes = []; const walker = document.createTreeWalker( element, NodeFilter.SHOW_TEXT, { acceptNode: function(node) { if (node.parentElement && /^(SCRIPT|STYLE|NOSCRIPT)$/.test(node.parentElement.tagName)) { return NodeFilter.FILTER_REJECT; } return node.textContent.trim() ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT; } } ); let node; while (node = walker.nextNode()) { textNodes.push(node); } return textNodes; } processBilibiliSpecial() { if (this.stateManager.shouldSkipBilibiliProcessing()) return; if (CONFIG.features.debugMode) { console.log('🎯 执行B站特殊处理'); } try { this.processBilibiliComments(); this.processBilibiliShadowDOM(); } catch (error) { if (CONFIG.features.debugMode) { console.error('🐱 B站处理出错:', error); } } this.stateManager.markBilibiliCompleted(); } processBilibiliComments() { const commentSelectors = [ '.reply-item .reply-content:not([data-catgirl-processed])', '.comment-item .comment-content:not([data-catgirl-processed])', '.bili-comment-content:not([data-catgirl-processed])', '.reply-content:not([data-catgirl-processed])', '.comment-content:not([data-catgirl-processed])', '[data-e2e="reply-item"] .reply-content:not([data-catgirl-processed])', '.bili-comment .comment-text:not([data-catgirl-processed])' ]; commentSelectors.forEach(selector => { const elements = document.querySelectorAll(selector); elements.forEach(element => { if (!this.processedElements.has(element)) { element.setAttribute('data-catgirl-processed', 'true'); this.processElement(element); } }); }); // 处理用户名 - 检查开关 if (CONFIG.features.bilibiliRandomizeUserNames) { const usernameSelectors = [ '.user-name:not([data-catgirl-processed])', '.reply-item .user-name:not([data-catgirl-processed])', '.comment-item .user-name:not([data-catgirl-processed])', '.bili-comment .user-name:not([data-catgirl-processed])', '.reply-author:not([data-catgirl-processed])', '.comment-author:not([data-catgirl-processed])' ]; usernameSelectors.forEach(selector => { const elements = document.querySelectorAll(selector); elements.forEach(element => { this.processBilibiliUsername(element); }); }); } } processBilibiliUsername(element) { if (!element || this.processedElements.has(element)) return; const userName = element.textContent?.trim(); if (!userName || !CONFIG.features.bilibiliRandomizeUserNames) return; if (this.hasUserPrefix(userName)) return; element.setAttribute('data-catgirl-processed', 'true'); this.processedElements.add(element); const processingType = Math.random(); if (processingType < 0.3) { const randomPrefix = this.getRandomUserPrefix(); element.textContent = `${randomPrefix}${userName}${randomPrefix}`; } else if (processingType < 0.6) { const randomPrefix = this.getRandomUserPrefix(); element.textContent = `${randomPrefix}${userName}`; } else if (processingType < 0.8) { const decorative = cuteLibrary.decorativePrefixes[Math.floor(Math.random() * cuteLibrary.decorativePrefixes.length)]; element.textContent = `${decorative}${userName}`; } if (CONFIG.features.debugMode) { console.log('🎀 处理用户名:', userName, '->', element.textContent); } } processBilibiliShadowDOM() { if (!CONFIG.features.shadowDomSupport) return; const biliComments = document.querySelector('bili-comments'); if (biliComments && biliComments.shadowRoot) { this.processElementsInShadowDOM(biliComments.shadowRoot); } const shadowHosts = document.querySelectorAll('bili-comment-thread-renderer, bili-comment-replies-renderer'); shadowHosts.forEach(host => { if (host.shadowRoot) { this.processElementsInShadowDOM(host.shadowRoot); } }); } processElementsInShadowDOM(shadowRoot) { try { const contentSelectors = [ '#contents span:not([data-catgirl-processed])', '.comment-text:not([data-catgirl-processed])', '.reply-content:not([data-catgirl-processed])', '.comment-content:not([data-catgirl-processed])' ]; contentSelectors.forEach(selector => { const elements = shadowRoot.querySelectorAll(selector); elements.forEach(element => { if (!this.processedElements.has(element)) { element.setAttribute('data-catgirl-processed', 'true'); this.processElement(element); } }); }); if (CONFIG.features.bilibiliRandomizeUserNames) { const usernameSelectors = [ '#user-name a:not([data-catgirl-processed])', '.user-name:not([data-catgirl-processed])', '.author-name:not([data-catgirl-processed])' ]; usernameSelectors.forEach(selector => { const elements = shadowRoot.querySelectorAll(selector); elements.forEach(element => { this.processBilibiliUsername(element); }); }); } const nestedHosts = shadowRoot.querySelectorAll('*'); nestedHosts.forEach(el => { if (el.shadowRoot && !el.hasAttribute('data-catgirl-shadow-processed')) { el.setAttribute('data-catgirl-shadow-processed', 'true'); this.processElementsInShadowDOM(el.shadowRoot); } }); } catch (error) { if (CONFIG.features.debugMode) { console.error('🐱 处理Shadow DOM出错:', error); } } } hasUserPrefix(userName) { if (!userName) return true; const hasPrefix = cuteLibrary.userPrefixes.some(prefix => userName.includes(prefix)); const hasDecorative = cuteLibrary.decorativePrefixes.some(prefix => userName.includes(prefix)); const isProcessed = /已处理|🏳️⚧️.*🏳️⚧️|✨.*✨|💕.*💕/.test(userName); const isTooLong = userName.length > 20; return hasPrefix || hasDecorative || isProcessed || isTooLong; } getRandomUserPrefix() { const prefixes = cuteLibrary.userPrefixes; return prefixes[Math.floor(Math.random() * prefixes.length)]; } setupBilibiliCommentObserver() { if (this.bilibiliCommentObserver) return; const targetNode = document.body; const config = { childList: true, subtree: true, attributes: false }; this.bilibiliCommentObserver = new MutationObserver( DebounceUtils.throttleWithDebounce((mutations) => { let hasCommentChanges = false; for (const mutation of mutations) { for (const node of mutation.addedNodes) { if (node.nodeType === Node.ELEMENT_NODE) { if (node.matches && ( node.matches('bili-comment-thread-renderer, bili-comment, .reply-item, .comment-item') || (node.querySelector && ( node.querySelector('bili-comment-thread-renderer') || node.querySelector('.reply-item') || node.querySelector('.comment-item') || node.querySelector('.bili-comment') )) )) { hasCommentChanges = true; break; } } } if (hasCommentChanges) break; } if (hasCommentChanges) { setTimeout(() => { if (CONFIG.features.debugMode) { console.log('🔄 检测到B站评论变化,开始处理'); } this.processBilibiliSpecial(); }, 1200); } }, 800, 400) ); this.bilibiliCommentObserver.observe(targetNode, config); if (CONFIG.features.debugMode) { console.log('🎯 B站评论观察器已启动'); } } setupMutationObserver() { if (this.observer) return; const throttledCallback = DebounceUtils.throttleWithDebounce( (mutations) => this.handleMutations(mutations), CONFIG.performance.observerThrottle, CONFIG.performance.debounceDelay ); this.observer = new MutationObserver(throttledCallback); this.observer.observe(document.body, { childList: true, subtree: true, attributes: false, characterData: false }); } handleMutations(mutations) { if (this.processLock) return; try { const elementsToProcess = new Set(); mutations.forEach(mutation => { mutation.addedNodes.forEach(node => { if (node.nodeType === Node.ELEMENT_NODE && this.shouldProcessElement(node)) { elementsToProcess.add(node); const children = node.querySelectorAll && node.querySelectorAll('*'); if (children) { Array.from(children).forEach(child => { if (this.shouldProcessElement(child)) { elementsToProcess.add(child); } }); } } }); }); if (elementsToProcess.size > 0) { const limitedElements = Array.from(elementsToProcess).slice(0, 20); EnhancedPerformanceUtils.createTimeSliceProcessor( limitedElements, (element) => this.processElement(element), { onComplete: () => { CONFIG.stats.lastActive = new Date().toISOString(); } } ); } } catch (error) { if (CONFIG.features.debugMode) { console.error('🐱 处理DOM变化出错:', error); } } } toggle() { if (this.isRunning) { this.stop(); showToast('猫娘化已暂停喵~ ⏸️', 'warning'); } else { this.start(); showToast('猫娘化已恢复喵~ ▶️', 'success'); } } stop() { this.isRunning = false; this.processLock = false; if (this.intervalId) { clearInterval(this.intervalId); this.intervalId = null; } if (this.observer) { this.observer.disconnect(); this.observer = null; } if (this.bilibiliCommentObserver) { this.bilibiliCommentObserver.disconnect(); this.bilibiliCommentObserver = null; } } restart() { this.stop(); this.processedElements = new WeakSet(); this.textProcessor.processedTexts = new Set(); this.lastProcessHash = ''; setTimeout(() => { this.start(); showToast('系统已重启喵~ 🔄', 'info'); }, 500); } toggleDebug() { CONFIG.features.debugMode = !CONFIG.features.debugMode; GM_setValue("catgirlConfig", CONFIG); showToast(`调试模式${CONFIG.features.debugMode ? '开启' : '关闭'}喵~ 🐛`, 'info'); } showStats() { const processedElements = (CONFIG.stats.processedElements || 0); const replacedWords = (CONFIG.stats.replacedWords || 0); const sessionProcessed = (CONFIG.stats.sessionProcessed || 0); const blacklistHits = (CONFIG.stats.blacklistHits || 0); const installDate = CONFIG.stats.installDate || new Date().toISOString(); const lastActive = CONFIG.stats.lastActive || new Date().toISOString(); const stats = ` 📊 猫娘化统计信息: • 已处理元素: ${processedElements.toLocaleString()} • 已替换词汇: ${replacedWords.toLocaleString()} • 本次会话: ${sessionProcessed.toLocaleString()} • 黑名单命中: ${blacklistHits.toLocaleString()} • 当前版本: ${SCRIPT_VERSION} • 安装时间: ${new Date(installDate).toLocaleString()} • 最后活动: ${new Date(lastActive).toLocaleString()} • 当前状态: ${this.isRunning ? '运行中 ✅' : '已暂停 ⏸️'} • 当前网站: ${location.hostname} `.trim(); alert(stats); } clearCache() { this.processedElements = new WeakSet(); this.textProcessor.processedTexts = new Set(); this.lastProcessHash = ''; CONFIG.stats.sessionProcessed = 0; // 清理处理标记 const processedElements = document.querySelectorAll('[data-catgirl-processed]'); processedElements.forEach(el => { el.removeAttribute('data-catgirl-processed'); }); const processedShadows = document.querySelectorAll('[data-catgirl-shadow-processed]'); processedShadows.forEach(el => { el.removeAttribute('data-catgirl-shadow-processed'); }); GM_setValue("catgirlConfig", CONFIG); showToast('缓存已清理,统计已重置喵~ 🧹', 'success'); } waitForDOMReady() { return new Promise(resolve => { if (document.readyState !== 'loading') { resolve(); } else { document.addEventListener('DOMContentLoaded', resolve, { once: true }); } }); } } // ===== 工具函数 ===== function showToast(message, type = 'info', duration = 3000) { const toast = document.createElement('div'); toast.className = 'catgirl-toast'; toast.innerHTML = ` <div class="toast-icon">${getToastIcon(type)}</div> <div class="toast-message">${message}</div> `; toast.classList.add(`toast-${type}`); document.body.appendChild(toast); setTimeout(() => toast.classList.add('show'), 10); setTimeout(() => { toast.classList.remove('show'); setTimeout(() => { if (toast.parentNode) { document.body.removeChild(toast); } }, 300); }, duration); } function getToastIcon(type) { const icons = { success: '✅', error: '❌', warning: '⚠️', info: 'ℹ️' }; return icons[type] || icons.info; } function showUpdateNotification() { showToast(`🎉 猫娘脚本已更新到 v${SCRIPT_VERSION} 喵~\n新增功能:黑名单管理、可视化控制、词汇扩展`, 'success', 5000); } // ===== 启动应用 ===== const catgirlApp = new CatgirlApp(); if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => catgirlApp.initialize(), { once: true }); } else { setTimeout(() => catgirlApp.initialize(), 100); } // ===== 样式注入 ===== GM_addStyle(` /* 设置面板样式 */ #catgirl-settings { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Microsoft YaHei', sans-serif; line-height: 1.5; } #catgirl-settings .settings-header, #catgirl-blacklist .blacklist-header { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 20px; border-radius: 12px 12px 0 0; display: flex; justify-content: space-between; align-items: center; box-shadow: 0 2px 10px rgba(0,0,0,0.1); } #catgirl-settings .settings-header h3, #catgirl-blacklist .blacklist-header h3 { margin: 0; font-size: 20px; font-weight: 600; } #catgirl-settings .close-btn, #catgirl-blacklist .close-btn { background: rgba(255,255,255,0.2); border: none; color: white; font-size: 24px; cursor: pointer; width: 32px; height: 32px; border-radius: 50%; transition: all 0.3s ease; display: flex; align-items: center; justify-content: center; } #catgirl-settings .close-btn:hover, #catgirl-blacklist .close-btn:hover { background: rgba(255,255,255,0.3); transform: scale(1.1); } #catgirl-settings .settings-content, #catgirl-blacklist .blacklist-content { padding: 24px; background: #ffffff; height: 590px; overflow-y: auto; border-radius: 0 0 12px 12px; } #catgirl-settings .settings-tabs { display: flex; margin-bottom: 24px; background: #f8f9fa; border-radius: 8px; padding: 4px; flex-wrap: wrap; } #catgirl-settings .tab-btn { flex: 1; padding: 12px 16px; border: none; background: transparent; cursor: pointer; border-radius: 6px; transition: all 0.3s ease; font-weight: 500; color: #6c757d; min-width: 80px; } #catgirl-settings .tab-btn.active { background: white; color: #495057; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } #catgirl-settings .tab-btn:hover:not(.active) { color: #495057; background: rgba(255,255,255,0.7); } #catgirl-settings .setting-group { margin-bottom: 20px; } #catgirl-settings label, #catgirl-blacklist label { display: block; margin-bottom: 8px; font-weight: 500; color: #333; font-size: 14px; } #catgirl-settings small, #catgirl-blacklist small { display: block; color: #6c757d; font-size: 12px; margin-top: 4px; line-height: 1.4; } #catgirl-settings select, #catgirl-settings input[type="text"], #catgirl-settings input[type="range"], #catgirl-settings textarea, #catgirl-blacklist select, #catgirl-blacklist input[type="text"], #catgirl-blacklist textarea { width: 100%; padding: 10px 12px; border: 2px solid #e9ecef; border-radius: 6px; font-size: 14px; transition: all 0.3s ease; background: #fff; box-sizing: border-box; } #catgirl-settings select:focus, #catgirl-settings input:focus, #catgirl-settings textarea:focus, #catgirl-blacklist select:focus, #catgirl-blacklist input:focus, #catgirl-blacklist textarea:focus { outline: none; border-color: #667eea; box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1); } #catgirl-settings input[type="checkbox"], #catgirl-blacklist input[type="checkbox"] { width: auto; margin-right: 8px; transform: scale(1.2); accent-color: #667eea; } #catgirl-settings input[type="range"] { height: 6px; -webkit-appearance: none; background: #e9ecef; border-radius: 3px; padding: 0; } #catgirl-settings input[type="range"]::-webkit-slider-thumb { -webkit-appearance: none; width: 20px; height: 20px; border-radius: 50%; background: #667eea; cursor: pointer; box-shadow: 0 2px 4px rgba(0,0,0,0.2); } #catgirl-settings .stats-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 16px; margin-bottom: 20px; } #catgirl-settings .stat-item { text-align: center; padding: 16px; background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%); border-radius: 8px; border: 1px solid #dee2e6; } #catgirl-settings .stat-number { font-size: 24px; font-weight: 700; color: #667eea; margin-bottom: 4px; } #catgirl-settings .stat-label { font-size: 12px; color: #6c757d; font-weight: 500; } #catgirl-settings .info-section, #catgirl-settings .performance-section { background: #f8f9fa; padding: 16px; border-radius: 8px; border-left: 4px solid #667eea; margin-bottom: 16px; } #catgirl-settings .info-section h4, #catgirl-settings .performance-section h4, #catgirl-blacklist h4 { margin: 0 0 12px 0; color: #495057; font-size: 16px; } #catgirl-settings .info-section p, #catgirl-settings .performance-section p { margin: 6px 0; color: #6c757d; font-size: 14px; } #catgirl-settings .actions, #catgirl-blacklist .actions { display: flex; gap: 12px; flex-wrap: wrap; margin-top: 24px; padding-top: 24px; border-top: 1px solid #e9ecef; } #catgirl-settings button, #catgirl-blacklist button { padding: 12px 20px; border: none; border-radius: 8px; cursor: pointer; font-size: 14px; font-weight: 500; transition: all 0.3s ease; display: flex; align-items: center; justify-content: center; gap: 6px; } #catgirl-settings .btn-primary, #catgirl-blacklist .btn-primary { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; flex: 1; } #catgirl-settings .btn-primary:hover, #catgirl-blacklist .btn-primary:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4); } #catgirl-settings .btn-warning { background: linear-gradient(135deg, #ffc107 0%, #ff8c00 100%); color: #212529; flex: 1; } #catgirl-settings .btn-warning:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(255, 193, 7, 0.4); } #catgirl-settings .btn-secondary, #catgirl-blacklist .btn-secondary { background: linear-gradient(135deg, #6c757d 0%, #495057 100%); color: white; flex: 1; } #catgirl-settings .btn-secondary:hover, #catgirl-blacklist .btn-secondary:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(108, 117, 125, 0.4); } /* 黑名单面板特殊样式 */ #catgirl-blacklist { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Microsoft YaHei', sans-serif; line-height: 1.5; } #catgirl-blacklist .current-site-section { background: #f8f9fa; padding: 16px; border-radius: 8px; margin-bottom: 20px; border-left: 4px solid #28a745; } #catgirl-blacklist .current-site-info { background: #e9ecef; padding: 12px; border-radius: 6px; margin-bottom: 16px; font-family: 'Courier New', monospace; font-size: 13px; } #catgirl-blacklist .action-group { margin-bottom: 12px; } #catgirl-blacklist .btn-danger { background: linear-gradient(135deg, #dc3545 0%, #c82333 100%); color: white; width: 100%; } #catgirl-blacklist .btn-danger:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(220, 53, 69, 0.4); } #catgirl-blacklist .blacklist-section { margin-bottom: 24px; } #catgirl-blacklist .blacklist-item { background: #fff; border: 1px solid #dee2e6; border-radius: 8px; padding: 16px; margin-bottom: 12px; display: flex; justify-content: space-between; align-items: center; transition: all 0.3s ease; } #catgirl-blacklist .blacklist-item:hover { box-shadow: 0 2px 8px rgba(0,0,0,0.1); } #catgirl-blacklist .blacklist-item.expired { background: #f8f9fa; border-color: #adb5bd; opacity: 0.7; } #catgirl-blacklist .item-domain { font-weight: 600; color: #495057; margin-bottom: 4px; } #catgirl-blacklist .item-details { font-size: 12px; color: #6c757d; line-height: 1.4; } #catgirl-blacklist .remove-btn { background: #dc3545; color: white; border: none; padding: 6px 12px; border-radius: 4px; cursor: pointer; font-size: 12px; transition: all 0.3s ease; } #catgirl-blacklist .remove-btn:hover { background: #c82333; transform: scale(1.05); } #catgirl-blacklist .empty-state { text-align: center; color: #6c757d; font-style: italic; padding: 32px; background: #f8f9fa; border-radius: 8px; } #catgirl-blacklist .blacklist-stats { text-align: center; padding-top: 16px; border-top: 1px solid #e9ecef; margin-top: 16px; } #catgirl-blacklist .blacklist-settings { background: #e3f2fd; padding: 16px; border-radius: 8px; border-left: 4px solid #2196f3; margin-bottom: 20px; } /* Toast 通知样式 */ .catgirl-toast { position: fixed; top: 20px; right: 20px; z-index: 10001; padding: 16px 20px; border-radius: 12px; font-size: 14px; font-weight: 500; max-width: 350px; box-shadow: 0 8px 32px rgba(0,0,0,0.15); transform: translateX(120%); transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1); backdrop-filter: blur(10px); display: flex; align-items: center; gap: 12px; } .catgirl-toast.show { transform: translateX(0); } .catgirl-toast.toast-success { background: linear-gradient(135deg, #28a745 0%, #20c997 100%); color: white; } .catgirl-toast.toast-error { background: linear-gradient(135deg, #dc3545 0%, #e74c3c 100%); color: white; } .catgirl-toast.toast-warning { background: linear-gradient(135deg, #ffc107 0%, #fd7e14 100%); color: #212529; } .catgirl-toast.toast-info { background: linear-gradient(135deg, #17a2b8 0%, #6f42c1 100%); color: white; } .catgirl-toast .toast-icon { font-size: 18px; } .catgirl-toast .toast-message { flex: 1; line-height: 1.4; } /* 响应式设计 */ @media (max-width: 700px) { #catgirl-settings, #catgirl-blacklist { width: 95vw; max-height: 90vh; } #catgirl-settings .settings-tabs { flex-direction: column; } #catgirl-settings .tab-btn { flex: none; } #catgirl-settings .actions, #catgirl-blacklist .actions { flex-direction: column; } #catgirl-settings .stats-grid { grid-template-columns: 1fr; } #catgirl-blacklist .blacklist-item { flex-direction: column; align-items: stretch; gap: 12px; } #catgirl-blacklist .remove-btn { align-self: flex-end; } } /* 滚动条美化 */ #catgirl-settings ::-webkit-scrollbar, #catgirl-blacklist ::-webkit-scrollbar { width: 8px; } #catgirl-settings ::-webkit-scrollbar-track, #catgirl-blacklist ::-webkit-scrollbar-track { background: #f1f1f1; border-radius: 4px; } #catgirl-settings ::-webkit-scrollbar-thumb, #catgirl-blacklist ::-webkit-scrollbar-thumb { background: #c1c1c1; border-radius: 4px; } #catgirl-settings ::-webkit-scrollbar-thumb:hover, #catgirl-blacklist ::-webkit-scrollbar-thumb:hover { background: #a8a8a8; } `); // ===== 全局调试接口 ===== window.catgirlApp = { app: catgirlApp, blacklistManager: catgirlApp.blacklistManager, config: CONFIG, version: SCRIPT_VERSION, clearCache: function() { catgirlApp.clearCache(); } }; })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址