// ==UserScript== // @name Lolz Transparent Chat — v2.9.10 (glass restore + mobile header H-scroll + buttons safe) // @namespace http://tampermonkey.net/ // @version 2.9.10 // @description Прозрачный чат (окно и /chatbox/) + корректные рамки упоминаний + фиксы кнопок + крестики + мобильный H-скролл шапки. Прозрачность восстановлена во всех контейнерах. // @match https://lzt.market/* // @match https://lolz.live/* // @match https://zelenka.guru/* // @grant none // @license MIT // ==/UserScript== (() => { 'use strict'; const BLUR_PX = 12; const ALPHA_GLASS = 0.01; // фон основного полотна/списка сообщений const ALPHA_INPUT = 0.01; // фон поля ввода const SCOPE_CLASS = 'kyan-chat-scope'; const IS_MOBILE = /Android|iPhone|iPad|Mobile/i.test(navigator.userAgent); const SUPPORTS_BLUR = (typeof CSS!=='undefined') && (CSS.supports('backdrop-filter','blur(1px)') || CSS.supports('-webkit-backdrop-filter','blur(1px)')); const EDGE_MOBILE = /\bEdgA\/|EdgiOS\//i.test(navigator.userAgent); const USE_BLUR = SUPPORTS_BLUR && !EDGE_MOBILE; const MENTION_BORDER = '3px solid rgba(34,142,93,0.85)'; function injectCSS(){ const old = document.getElementById('kyan-chat-style'); if (old) old.remove(); const css = ` .${SCOPE_CLASS} {} /* ===== Прозрачность внутренних контейнеров (ШАПКУ НЕ СТЕКЛИМ) ===== */ .${SCOPE_CLASS} .chat2-widget-inner, .${SCOPE_CLASS} .scrollable-content, .${SCOPE_CLASS} [class*="chat2-body"], .${SCOPE_CLASS} [class*="chat2-content"], .${SCOPE_CLASS} [class*="chat2-container"], .${SCOPE_CLASS} [class*="chat2-wrapper"], .${SCOPE_CLASS} [class*="messages"], .${SCOPE_CLASS} [class*="message-list"], .${SCOPE_CLASS} [class*="list"], .${SCOPE_CLASS} [class*="chat2-layout"], .${SCOPE_CLASS} [class*="pane"], .${SCOPE_CLASS} [class*="primary-darker"]:not(.chat2-header):not([class*="chat2-header"]), .${SCOPE_CLASS} [class*="primary-dark"]:not(.chat2-header):not([class*="chat2-header"]) { background: transparent !important; background-color: transparent !important; background-image: none !important; } /* Псевдоэлементы: обнуляем везде, кроме шапки */ .${SCOPE_CLASS} [class*="chat2-"]::before, .${SCOPE_CLASS} [class*="chat2-"]::after { background: transparent !important; } .${SCOPE_CLASS} .chat2-header *::before, .${SCOPE_CLASS} .chat2-header *::after { background: initial !important; } /* ===== Сообщения ===== */ .${SCOPE_CLASS} .chat2-message-block { background: transparent !important; border: 1px solid rgba(128,128,128,.45) !important; border-radius: 6px !important; } .${SCOPE_CLASS} .reply-message { background: transparent !important; border: 1px solid rgba(128,128,128,.45) !important; border-radius: 6px !important; } /* Панель «Ответ …» */ .${SCOPE_CLASS} .chat2-replying, .${SCOPE_CLASS} [class*="replying"] { background: rgba(0,0,0,.04) !important; border: 1px solid rgba(128,128,128,.35) !important; border-radius: 8px !important; box-shadow: none !important; } .${SCOPE_CLASS} .chat2-replying-author { background: initial !important; } .${SCOPE_CLASS} .chat2-replying * { border: none !important; box-shadow: none !important; } /* Заголовки/ники внутри сообщения */ .${SCOPE_CLASS} .chat2-message-header, .${SCOPE_CLASS} .chat2-message-header * { backdrop-filter: none !important; -webkit-backdrop-filter: none !important; filter: none !important; box-shadow: revert !important; } /* Поле ввода — почти прозрачное */ .${SCOPE_CLASS} .chat2-footer textarea, .${SCOPE_CLASS} .chat2-footer [contenteditable="true"], .${SCOPE_CLASS} .chat2-footer input[type="text"], .${SCOPE_CLASS} .chat2-footer input[type="search"], .${SCOPE_CLASS} .chat2-footer [class*="editor"] { background: rgba(0,0,0,${ALPHA_INPUT}) !important; border: none !important; border-radius: 8px !important; outline: none !important; box-shadow: none !important; } /* Футер — не ломаем иконки/кнопки */ .${SCOPE_CLASS} .chat2-footer button, .${SCOPE_CLASS} .chat2-footer [role="button"], .${SCOPE_CLASS} .chat2-footer svg { opacity: revert !important; filter: none !important; } /* Кнопка отправки */ .${SCOPE_CLASS} .submit-btn { background: initial !important; } /* Крестик «Отменить ответ» */ .${SCOPE_CLASS} .chat2-footer .chat2-cancel-reply { display:inline-flex !important; align-items:center; justify-content:center; width:22px; height:22px; border-radius:6px; background: transparent !important; color:#fff !important; opacity:1 !important; filter: drop-shadow(0 0 1px rgba(0,0,0,.7)) !important; } .${SCOPE_CLASS} .chat2-footer .chat2-cancel-reply:empty::before { content:'×'; font-size:16px; line-height:1; } /* ===== Шапка чата ===== — только рамка, дочерние не трогаем */ .${SCOPE_CLASS} .chat2-header, .${SCOPE_CLASS} .chat2-header.lztng-primary-dark, .${SCOPE_CLASS} [class*="chat2-header"].lztng-primary-dark { border: 1px solid rgba(128,128,128,.45) !important; border-radius: 8px !important; box-shadow: none !important; overflow: visible !important; background: initial !important; backdrop-filter: none !important; -webkit-backdrop-filter: none !important; } /* Упоминания — без заливки темы */ .${SCOPE_CLASS} .chat2-message.chat2-message-tagged, .${SCOPE_CLASS} .chat2-message.chat2-message-tagged .chat2-message-block, .${SCOPE_CLASS} .chat2-message.chat2-message-tagged .chat2-message-text, .${SCOPE_CLASS} .chat2-message.chat2-message-tagged .chat2-message-header { background: transparent !important; background-color: transparent !important; box-shadow: none !important; } .${SCOPE_CLASS} .reply-message.reply-message-your { border: ${MENTION_BORDER} !important; background: transparent !important; } /* Крестик «Редактирование сообщения» */ .${SCOPE_CLASS} .chat2-message-editing .chat2-cancel-editing { display:inline-flex !important; align-items:center; justify-content:center; width:22px; height:22px; border-radius:6px; background: transparent !important; color:#fff !important; opacity:1 !important; filter: drop-shadow(0 0 1px rgba(0,0,0,.7)) !important; cursor: pointer !important; } .${SCOPE_CLASS} .chat2-message-editing .chat2-cancel-editing:empty::before { content:'×'; font-size:16px; line-height:1; } /* Узел действий/кнопок в шапке — не обрезается */ .${SCOPE_CLASS} .chat2-header [class*="actions"], .${SCOPE_CLASS} .chat2-header .chat2-title-actions { position: relative !important; z-index: 10020 !important; pointer-events: auto !important; overflow: visible !important; } /* ===== МОБАЙЛ: шапка скроллится + горизонтальная прокрутка ===== */ @media (max-width: 768px) { .${SCOPE_CLASS} .chat2-header, .${SCOPE_CLASS} [class*="chat2-header"] { position: static !important; top: auto !important; bottom: auto !important; transform: none !important; z-index: auto !important; } .${SCOPE_CLASS} .chat2-header, .${SCOPE_CLASS} .chat2-header > * { overflow: visible !important; } .${SCOPE_CLASS} .chat2-header [class*="actions"], .${SCOPE_CLASS} .chat2-header .chat2-title-actions, .${SCOPE_CLASS} .chat2-header .actions, .${SCOPE_CLASS} .chat2-header .title-actions { display: flex !important; flex-wrap: nowrap !important; align-items: center !important; gap: .5rem !important; width: 100% !important; max-width: 100% !important; overflow-x: auto !important; overflow-y: hidden !important; white-space: nowrap !important; -webkit-overflow-scrolling: touch !important; touch-action: pan-x !important; overscroll-behavior-x: contain !important; scroll-behavior: smooth !important; scrollbar-width: thin; } .${SCOPE_CLASS} .chat2-header [class*="actions"] > *, .${SCOPE_CLASS} .chat2-header .chat2-title-actions > * { flex: 0 0 auto !important; min-width: max-content !important; } } `; const tag = document.createElement('style'); tag.id = 'kyan-chat-style'; tag.textContent = css; document.documentElement.appendChild(tag); } const qs = (r,s)=>r.querySelector(s); const qsa = (r,s)=>Array.from(r.querySelectorAll(s)); function inlineGlass(el, alpha, blur, keepBorder=false){ if(!el) return; el.style.setProperty('background', `rgba(0,0,0,${alpha})`, 'important'); el.style.setProperty('background-color', `rgba(0,0,0,${alpha})`, 'important'); el.style.setProperty('background-image', 'none', 'important'); if (USE_BLUR) { el.style.setProperty('backdrop-filter', `blur(${blur}px)`, 'important'); el.style.setProperty('-webkit-backdrop-filter', `blur(${blur}px)`, 'important'); } else { el.style.removeProperty('backdrop-filter'); el.style.removeProperty('-webkit-backdrop-filter'); } if(!keepBorder) el.style.setProperty('border', 'none', 'important'); } // На некоторых разметках нужные зоны отличаются — даём широкий охват const GLASS_SELECTORS = [ // корневые '#chat2-full', '[class^="chat2-floating"]', '.chat2', '[class*="chat2-container"]', '[class*="chatbox"]', // основные области '.scrollable-content', '[class*="chat2-body"]', '[class*="chat2-content"]', '[class*="messages"]', '[class*="message-list"]', '.chat2-widget-inner', // дополнительные обёртки, часто дают фон '[class*="primary-darker"]:not(.chat2-header):not([class*="chat2-header"])', '[class*="primary-dark"]:not(.chat2-header):not([class*="chat2-header"])', '[class*="pane"]', '[class*="layout"]', '[class*="wrapper"]' ]; function applyGlassSet(root){ // Всегда — сам root inlineGlass(root, ALPHA_GLASS, BLUR_PX, true); // Все целевые селекторы for (const sel of GLASS_SELECTORS){ qsa(root, sel).forEach(el => inlineGlass(el, ALPHA_GLASS, BLUR_PX, false)); } // Поднимаемся вверх от ленты, чтобы пробить фон родителей const scroll = findScroll(root); if (scroll){ let p = scroll.parentElement, i=0; while (p && p!==root && i<6){ inlineGlass(p, ALPHA_GLASS, BLUR_PX, true); p = p.parentElement; i++; } } } function findChatRoot(){ return qs(document,'[class^="chat2-floating"]') || qs(document,'#chat2-full') || qs(document,'.chat2') || qs(document,'[class*="chat2-container"]') || qs(document,'[class*="chatbox"]'); } function findScroll(root){ return qs(root,'.scrollable-content') || qs(root,'[class*="chat2-body"]') || qs(root,'[class*="chat2-content"]') || qs(root,'[class*="messages"]') || qs(root,'[class*="message-list"]'); } function getMsgBlock(msg){ return msg.querySelector('.chat2-message-block') || msg.querySelector('.chat2-message-text') || msg.querySelector('div'); } function setMentionBorderOnMessage(msg, on){ if (msg.classList.contains('reply-message') && msg.classList.contains('reply-message-your')){ if (on){ msg.style.setProperty('border', MENTION_BORDER, 'important'); msg.style.setProperty('background', 'transparent', 'important'); msg.style.setProperty('background-color', 'transparent', 'important'); } else { msg.style.removeProperty('border'); msg.style.removeProperty('background'); msg.style.removeProperty('background-color'); } return; } const block = getMsgBlock(msg); if (!block) return; if (on){ block.style.setProperty('border', MENTION_BORDER, 'important'); block.style.setProperty('background', 'transparent', 'important'); block.style.setProperty('background-color', 'transparent', 'important'); } else { block.style.removeProperty('border'); block.style.removeProperty('background'); block.style.removeProperty('background-color'); } } function refreshMentions(root){ qsa(root, '.chat2-message, [class*="chat2-message"]').forEach(msg=>{ const tagged = msg.classList.contains('chat2-message-tagged'); setMentionBorderOnMessage(msg, tagged); }); qsa(root, '.reply-message.reply-message-your').forEach(msg=>{ setMentionBorderOnMessage(msg, true); }); } // Наблюдение за DOM let mo; function watchAll(root, mode){ mo?.disconnect(); mo = new MutationObserver(()=>{ cancelAnimationFrame(watchAll._raf||0); watchAll._raf = requestAnimationFrame(()=>{ applyGlassSet(root); // держим прозрачность после любых перерисовок refreshMentions(root); // и зелёные рамки }); }); mo.observe(root, { childList:true, subtree:true, attributes:true, attributeFilter:['class'] }); // первичное применение applyGlassSet(root); refreshMentions(root); } let moLayout; function watchLayout(root, mode){ moLayout?.disconnect(); moLayout = new MutationObserver(()=>{ cancelAnimationFrame(watchLayout._raf||0); watchLayout._raf = requestAnimationFrame(()=>{ applyGlassSet(root); }); }); moLayout.observe(root, { childList:true, subtree:true }); } function init(){ const root = findChatRoot(); if (!root){ setTimeout(init, 250); return; } root.classList.add(SCOPE_CLASS); injectCSS(); // НЕ стеклим шапку — только рамка через CSS (прозрачность даём остальным) applyGlassSet(root); watchAll(root); const ON_CHATBOX = /\/chatbox\/?$/.test(location.pathname); watchLayout(root, ON_CHATBOX ? 'chatbox' : 'floating'); } if (document.readyState==='loading'){ document.addEventListener('DOMContentLoaded', init, { once:true }); } else { init(); } })();