// ==UserScript== // @name Twitch Chat Stealth // @name:zh-TW Twitch 聊天室隱藏助手 // @name:zh-CN Twitch 聊天室隱藏助手 // @version 1.3 // @license MIT // @description Stealthily hide the chat input and leaderboard. Features a slim dashboard for custom colors and positions. // @description:zh-TW 隱藏聊天輸入框與排行榜。內建精簡設定面板,支援自定義顏色、位置、感應寬度與秒數。 // @description:zh-CN 隐藏聊天输入框与排行榜。内置精简设置面板,支持自定义颜色、位置、感应宽度与秒数。 // @author Scott // @match https://www.twitch.tv/popout/*/chat* // @match https://m.twitch.tv/*/chat* // @grant GM_addStyle // @grant GM_setValue // @grant GM_getValue // @grant GM_registerMenuCommand // @run-at document-idle // @namespace https://greasyfork.org/users/1284613 // @downloadURL https://update.greasyfork.icu/scripts/576650/Twitch%20Chat%20Stealth.user.js // @updateURL https://update.greasyfork.icu/scripts/576650/Twitch%20Chat%20Stealth.meta.js // ==/UserScript== (function() { 'use strict'; const store = { get: (key, def) => GM_getValue(key, def), set: (key, val) => { GM_setValue(key, val); applyDynamicStyles(); } }; const lang = (navigator.language || navigator.userLanguage).includes('zh') ? 'zh' : 'en'; const ui = { title: lang === 'zh' ? '聊天室設定' : 'Chat Config', hideRank: lang === 'zh' ? '屏蔽社群精華與排行榜' : 'Hide Community Highlights & Rank', pos: lang === 'zh' ? '感應條位置' : 'Bar Position', width: lang === 'zh' ? '感應條長度' : 'Bar Width', color: lang === 'zh' ? '感應條顏色' : 'Bar Color', delay: lang === 'zh' ? '自動收合秒數' : 'Auto-hide (sec)', close: lang === 'zh' ? '點擊空白處關閉設定' : 'Click outside to close', left: lang === 'zh' ? '靠左' : 'Left', right: lang === 'zh' ? '靠右' : 'Right', center: lang === 'zh' ? '置中' : 'Mid', full: lang === 'zh' ? '全寬' : 'Full', always: lang === 'zh' ? '永久顯示' : 'Always', custom: lang === 'zh' ? '自定義' : 'Custom' }; GM_addStyle(` .chat-input, [data-a-target="chat-input-container"] { transition: all 0.2s ease-out !important; overflow: hidden !important; } .stealth-mode { height: 0px !important; min-height: 0px !important; opacity: 0 !important; visibility: hidden !important; pointer-events: none !important; } .hide-rank-mode .eOHCrm.Layout-sc-1xcs6mc-0, .hide-rank-mode [data-a-target="community-highlight-stack__scroll-area"], .hide-rank-mode .chat-room__header { display: none !important; } #stealth-toggle { position: fixed; bottom: 0 !important; z-index: 999998; transition: opacity 0.2s; -webkit-tap-highlight-color: transparent; cursor: pointer; } #stealth-dash { position: fixed; top: 10%; left: -220px; width: 200px; background: rgba(24, 24, 27, 0.98); backdrop-filter: blur(10px); color: #efeff1; z-index: 1000000; border: 1px solid #333; border-radius: 0 8px 8px 0; box-shadow: 4px 0 20px rgba(0,0,0,0.6); transition: left 0.3s ease; padding: 15px; display: flex; flex-direction: column; gap: 15px; font-family: sans-serif; } #stealth-dash.open { left: 0; } #stealth-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; z-index: 999999; display: none; } #stealth-overlay.open { display: block; } .dash-item { display: flex; flex-direction: column; gap: 8px; } .dash-label { font-size: 13px; font-weight: bold; color: #efeff1; display: flex; justify-content: space-between; align-items: center; } /* 強制按鈕內容置中 */ .dash-btn-group { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; } .dash-btn { background: #3a3a3d; border: none; color: white; padding: 8px; cursor: pointer; border-radius: 4px; font-size: 12px; display: flex; align-items: center; justify-content: center; text-align: center; } .dash-btn.active { background: #9147ff; } .switch-container { display: flex; align-items: center; justify-content: space-between; cursor: pointer; width: 100%; } .switch-bg { position: relative; width: 36px; height: 18px; background: #3a3a3d; border-radius: 20px; transition: 0.3s; display: flex; align-items: center; padding: 0 2px; } .switch-dot { width: 14px; height: 14px; background: white; border-radius: 50%; transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1); } .active-bg { background: #9147ff; } .active-dot { transform: translateX(18px); } /* 自定義輸入框置中校準 */ .custom-container { display: flex; gap: 2px; background: #3a3a3d; border-radius: 4px; padding: 4px; align-items: center; justify-content: center; } .custom-input { background: #18181b; border: 1px solid #444; color: white; border-radius: 4px; padding: 2px 0; width: 35px; font-size: 11px; text-align: center; } input[type=range] { accent-color: #9147ff; cursor: pointer; } input[type=color] { background: none; border: 1px solid #444; width: 100%; height: 26px; cursor: pointer; border-radius: 4px; padding: 0; } `); function applyDynamicStyles() { const pos = store.get('pos', 'right'); const width = store.get('width', 30); const color = store.get('color', '#9147ff'); const hideRank = store.get('hideRank', false); const el = document.getElementById('stealth-toggle'); if (!el) return; let style = `opacity: 0.1; height: 5px; background: ${color}; `; if (pos === 'left') style += `left: 0; width: ${width}%; right: auto;`; else if (pos === 'center') style += `left: ${50 - width/2}%; width: ${width}%; right: auto;`; else if (pos === 'full') style += `left: 0; width: 100%; right: auto;`; else style += `right: 0; width: ${width}%; left: auto;`; el.style.cssText = style; document.body.classList.toggle('hide-rank-mode', hideRank); } function createDashboard() { const overlay = document.createElement('div'); overlay.id = 'stealth-overlay'; const dash = document.createElement('div'); dash.id = 'stealth-dash'; const currentDelay = store.get('delay', 5000); const isAlways = currentDelay === 999999; const isPreset = [3000, 5000].includes(currentDelay); dash.innerHTML = `
${ui.title}
${ui.hideRank}
${ui.pos}
${ui.delay}
${ui.custom}
${ui.width} ${store.get('width', 30)}%
${ui.color}
${ui.close}
`; document.body.appendChild(overlay); document.body.appendChild(dash); overlay.onclick = () => { dash.classList.remove('open'); overlay.classList.remove('open'); }; dash.querySelector('#sw-rank').onclick = function() { const now = !store.get('hideRank', false); this.querySelector('.switch-bg').classList.toggle('active-bg', now); this.querySelector('.switch-dot').classList.toggle('active-dot', now); store.set('hideRank', now); }; dash.querySelectorAll('.pos-btn').forEach(btn => { btn.onclick = () => { dash.querySelectorAll('.pos-btn').forEach(b => b.classList.remove('active')); btn.classList.add('active'); store.set('pos', btn.dataset.val); }; }); const delayBtns = dash.querySelectorAll('.delay-btn'); const customInp = dash.querySelector('#custom-ms'); delayBtns.forEach(btn => { btn.onclick = () => { delayBtns.forEach(b => b.classList.remove('active')); btn.classList.add('active'); customInp.value = ''; store.set('delay', parseInt(btn.dataset.ms)); }; }); customInp.onchange = () => { const val = parseFloat(customInp.value); if (val > 0) { delayBtns.forEach(b => b.classList.remove('active')); store.set('delay', val * 1000); } }; dash.querySelector('#range-width').oninput = (e) => { e.target.previousElementSibling.querySelector('span').innerText = e.target.value + '%'; store.set('width', e.target.value); }; dash.querySelector('#color-picker').oninput = (e) => store.set('color', e.target.value); } let isExpanded = false; let idleTimer = null; function toggle(forceExpand = null) { const container = document.querySelector('.chat-input') || document.querySelector('[data-a-target="chat-input-container"]'); if (!container) return; isExpanded = (forceExpand !== null) ? forceExpand : !isExpanded; if (isExpanded) { container.classList.remove('stealth-mode'); setTimeout(() => { (container.querySelector('[data-slate-editor="true"]') || container.querySelector('textarea'))?.focus(); }, 100); resetIdleTimer(); } else { container.classList.add('stealth-mode'); clearTimeout(idleTimer); } } function resetIdleTimer() { clearTimeout(idleTimer); const delay = store.get('delay', 5000); if (isExpanded && delay < 999999) { idleTimer = setTimeout(() => toggle(false), delay); } } function init() { const container = document.querySelector('.chat-input') || document.querySelector('[data-a-target="chat-input-container"]'); if (!container) { setTimeout(init, 1000); return; } createDashboard(); const btn = document.createElement('div'); btn.id = 'stealth-toggle'; btn.onclick = (e) => { e.stopPropagation(); toggle(); }; document.body.appendChild(btn); applyDynamicStyles(); toggle(false); GM_registerMenuCommand("⚙️ " + ui.title, () => { document.getElementById('stealth-dash').classList.add('open'); document.getElementById('stealth-overlay').classList.add('open'); }); document.addEventListener('click', (e) => { const delay = store.get('delay', 5000); if (isExpanded && delay < 999999 && !container.contains(e.target) && e.target.id !== 'stealth-toggle') { toggle(false); } }); } init(); })();