Загрузка...

UI Реализовано
Attaching nicknames/tags/icons to the screen when setting up a unique name

Thread in Extentions created by AnimeHeHe Oct 9, 2025. 329 views

  1. AnimeHeHe
    Надоело мне по сто раз мотать страничку вверх-вниз при внесении правок в уник, так что вот :animehehe:
    Для работы кода нужно расширение tampermonkey, скачиваем, вставляем в него скрипт, работаем


    Code
    // ==UserScript==
    // @name Редактор уника
    // @namespace http://tampermonkey.net/
    // @author AnimeHeHe
    // @match https://lolz.live/account/uniq
    // ==/UserScript==

    (function() {
    'use strict';

    let banner = null;
    let nicknameInput = null;
    let observer = null;
    let textareaObserver = null;
    let isUpdating = false;

    // Функция для получения ника пользователя с приоритетами
    function getUsername() {

    if (nicknameInput && nicknameInput.value.trim()) {
    return nicknameInput.value.trim();
    }


    const usernameSpan = document.querySelector('span.username.lztng-1wj82iv');
    if (usernameSpan) {
    return usernameSpan.textContent.trim();
    }

    return '';
    }


    function getTextareaValue(name) {
    const textarea = document.querySelector(`textarea[name="${name}"]`);
    return textarea ? textarea.value.trim() : '';
    }

    // Функция для создания поля ввода ника
    function createNicknameInput() {
    if (!nicknameInput) {
    nicknameInput = document.createElement('input');
    nicknameInput.type = 'text';
    nicknameInput.placeholder = 'Кастомный ник';
    nicknameInput.id = 'custom-nickname-input';
    nicknameInput.style.cssText = `
    position: fixed;
    bottom: 100px;
    left: 10px;
    z-index: 10001;
    width: 200px;
    padding: 8px;
    border: 1px solid #ccc;
    border-radius: 5px;
    font-family: -apple-system;
    font-size: 14px;
    opacity: 0.5;
    background: white;
    box-shadow: 0 2px 10px rgba(0,0,0,0.1);
    `;


    const savedNickname = localStorage.getItem('customUserNickname');
    if (savedNickname) {
    nicknameInput.value = savedNickname;
    }

    nicknameInput.addEventListener('input', function() {
    localStorage.setItem('customUserNickname', this.value);
    debouncedCreateBanner();
    });

    document.body.appendChild(nicknameInput);
    }
    return nicknameInput;
    }


    function debounce(func, wait) {
    let timeout;
    return function executedFunction(...args) {
    const later = () => {
    clearTimeout(timeout);
    func(...args);
    };
    clearTimeout(timeout);
    timeout = setTimeout(later, wait);
    };
    }


    function applyCSS(element, cssString) {
    if (!cssString) return;

    const tempDiv = document.createElement('div');
    tempDiv.style.cssText = cssString;

    for (let i = 0; i < tempDiv.style.length; i++) {
    const property = tempDiv.style[i];
    const value = tempDiv.style.getPropertyValue(property);
    element.style.setProperty(property, value);
    }
    }


    function createIconContainer(iconHTML, isAnimated) {
    const translateY = isAnimated ? 'transform: translateY(0px);transform: translateX(8px);' : '';
    return `
    <div style="
    width: 16px;
    height: 16px;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    position: relative;
    overflow: hidden;
    ${translateY}
    ">
    <div style="
    width: 16px;
    height: 16px;
    display: flex;
    align-items: center;
    justify-content: center;
    position: absolute;
    top: 0px;
    left: 0px;
    overflow: hidden;
    ">
    ${iconHTML}
    </div>
    </div>
    `;
    }


    function createBanner() {
    if (isUpdating) return;
    isUpdating = true;

    try {

    const usernameCSS = getTextareaValue('username_css');
    const bannerIcon = getTextareaValue('banner_icon');
    const bannerCSS = getTextareaValue('banner_css');
    const usernameIcon = getTextareaValue('username_icon');
    const username = getUsername();


    createNicknameInput();


    if (!banner) {
    banner = document.createElement('div');
    banner.id = 'custom-user-banner';
    document.body.appendChild(banner);
    }

    // Базовые стили баннера
    banner.style.cssText = `
    position: fixed;
    bottom: 60px;
    left: 10px;
    z-index: 10000;
    padding: 0;
    border-radius: 3px;
    font-family: -apple-system;
    font-size: 14px;
    color: white;
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: 200px;
    height: 30px;
    backdrop-filter: blur(10px);
    overflow: hidden;
    box-sizing: border-box;
    padding: 0 8px;
    `;


    let bannerContent = '';

    // Левая часть баннера - основная иконка (16x16)
    if (bannerIcon) {
    const isAnimated = bannerIcon.includes('animateTransform');
    bannerContent += createIconContainer(bannerIcon, isAnimated);
    } else {
    bannerContent += `<div style="width: 16px; height: 16px; flex-shrink: 0;"></div>`;
    }

    // Центральная часть - пустое пространство
    bannerContent += `<div style="flex: 1; min-width: 0;"></div>`;

    // Правая часть баннера - пустая
    bannerContent += `<div style="width: 16px; height: 16px; flex-shrink: 0;"></div>`;


    banner.innerHTML = bannerContent;


    if (bannerCSS) {
    applyCSS(banner, bannerCSS);
    }

    // Баннер размеры
    banner.style.width = '200px';
    banner.style.height = '30px';


    createUsernameDisplay(username, usernameCSS, usernameIcon);
    } finally {
    isUpdating = false;
    }
    }


    const debouncedCreateBanner = debounce(createBanner, 50);


    function createUsernameDisplay(username, usernameCSS, usernameIcon) {
    let usernameDisplay = document.getElementById('custom-username-display');

    if (!usernameDisplay) {
    usernameDisplay = document.createElement('div');
    usernameDisplay.id = 'custom-username-display';
    document.body.appendChild(usernameDisplay);
    }

    // Никнейм
    usernameDisplay.style.cssText = `
    position: fixed;
    bottom: 35px;
    left: 15px;
    z-index: 10000;
    width: 200px;
    height: 16.63px;
    text-align: center;
    font-family: apple-system, BlinkMacSystemFont, 'Open Sans', Helvetica Neue, sans-serif;
    font-size: 16px;
    font-weight: 600;
    color: rgb(214, 214, 214);
    padding: 0;
    display: flex !important;
    align-items: center;
    justify-content: center;
    visibility: visible !important;
    opacity: 1;
    line-height: 1;
    `;


    let displayContent = '';


    displayContent += `<span id="custom-username-text" style="display: inline-block; line-height: 1; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">${username}</span>`;


    if (usernameIcon) {
    const isAnimated = usernameIcon.includes('animateTransform');
    displayContent += createIconContainer(usernameIcon, isAnimated);
    }

    usernameDisplay.innerHTML = displayContent;


    const usernameText = usernameDisplay.querySelector('#custom-username-text');
    if (usernameText && usernameCSS) {
    applyCSS(usernameText, usernameCSS);
    }


    usernameDisplay.style.display = 'flex';
    usernameDisplay.style.visibility = 'visible';
    usernameDisplay.style.opacity = '1';
    }


    function observePage() {
    if (observer) {
    observer.disconnect();
    }

    observer = new MutationObserver(function(mutations) {
    let shouldUpdate = false;

    for (let mutation of mutations) {

    if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
    for (let node of mutation.addedNodes) {
    if (node.nodeType === 1) {
    if (node.matches && (
    node.matches('textarea.textCtrl.code-editor.lztng-n2rvjg') ||
    node.matches('span.username.lztng-1wj82iv')
    )) {
    shouldUpdate = true;
    break;
    }

    if (node.querySelector && (
    node.querySelector('textarea.textCtrl.code-editor.lztng-n2rvjg') ||
    node.querySelector('span.username.lztng-1wj82iv')
    )) {
    shouldUpdate = true;
    break;
    }
    }
    }
    }


    if (mutation.type === 'attributes' && mutation.target) {
    const target = mutation.target;
    if (target.matches && (
    target.matches('textarea.textCtrl.code-editor.lztng-n2rvjg') ||
    target.matches('span.username.lztng-1wj82iv') ||
    target.matches('input#custom-nickname-input')
    )) {
    shouldUpdate = true;
    break;
    }
    }


    if (mutation.type === 'characterData' && mutation.target) {
    const parent = mutation.target.parentNode;
    if (parent && parent.matches && parent.matches('span.username.lztng-1wj82iv')) {
    shouldUpdate = true;
    break;
    }
    }
    }

    if (shouldUpdate) {
    debouncedCreateBanner();
    }
    });


    observer.observe(document.body, {
    childList: true,
    subtree: true,
    attributes: true,
    attributeFilter: ['value', 'class', 'style'],
    characterData: true
    });
    }


    function observeTextareas() {
    if (textareaObserver) {
    textareaObserver.disconnect();
    }

    textareaObserver = new MutationObserver(function(mutations) {
    for (let mutation of mutations) {
    if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {

    const textareas = document.querySelectorAll('textarea.textCtrl.code-editor.lztng-n2rvjg');
    textareas.forEach(textarea => {
    if (!textarea.hasAttribute('data-custom-banner-listener')) {
    textarea.setAttribute('data-custom-banner-listener', 'true');
    textarea.addEventListener('input', debouncedCreateBanner);
    textarea.addEventListener('change', debouncedCreateBanner);
    }
    });
    }
    }
    });

    textareaObserver.observe(document.body, {
    childList: true,
    subtree: true
    });
    }


    function initElements() {

    const textareas = document.querySelectorAll('textarea.textCtrl.code-editor.lztng-n2rvjg');
    textareas.forEach(textarea => {
    if (!textarea.hasAttribute('data-custom-banner-listener')) {
    textarea.setAttribute('data-custom-banner-listener', 'true');
    textarea.addEventListener('input', debouncedCreateBanner);
    textarea.addEventListener('change', debouncedCreateBanner);
    }
    });


    debouncedCreateBanner();
    }


    function init() {

    const oldBanner = document.getElementById('custom-user-banner');
    const oldInput = document.getElementById('custom-nickname-input');
    const oldUsernameDisplay = document.getElementById('custom-username-display');

    if (oldBanner) oldBanner.remove();
    if (oldInput) oldInput.remove();
    if (oldUsernameDisplay) oldUsernameDisplay.remove();

    banner = null;
    nicknameInput = null;


    initElements();


    observePage();
    observeTextareas();


    let currentUrl = window.location.href;
    const checkUrlChange = setInterval(() => {
    if (window.location.href !== currentUrl) {
    currentUrl = window.location.href;
    setTimeout(() => {
    initElements();
    }, 1000);
    }
    }, 500);


    window.addEventListener('beforeunload', () => {
    clearInterval(checkUrlChange);
    });

    console.log('Баннер инициализирован (с фиксом для анимированных иконок)');
    }


    if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', init);
    } else {
    init();
    }


    document.addEventListener('click', function(e) {
    if (nicknameInput && !nicknameInput.contains(e.target) &&
    (!banner || !banner.contains(e.target))) {
    nicknameInput.style.opacity = '0.7';
    setTimeout(() => {
    if (nicknameInput) nicknameInput.style.opacity = '1';
    }, 3000);
    }
    });
    })();

    Касаемо содержания, дополнение позволяет настраивать уник и сразу видеть результат, будь то ник, лычка или иконка, также есть возможность ввести в поле любой ник и посмотреть, как он будет смотреться с уником


     
    This article was useful for you?
    You can thank the author of the topic by transferring funds to your balance
    Thank the author
    1. View previous comments (1)
    2. God_likeGL Layer 1
      :fsdglgfdk19000: avatarllimonix , Сделала*
    3. llimonix
    4. AnimeHeHe Topic starter
      avatarllimonix , в премьере тихо было :animehehe:
  2. God_likeGL
    God_likeGL Layer 1 Oct 10, 2025 Лучшие сервера: lolz.live/threads/5071761/ :+rep: 35,786 Oct 30, 2018
    :fsdglgfdk19000: Сделай ещё чуть побольше этот "пример" слева, ато я когда настраиваю ставлю 150% масштаба страницы чтоб каждую точечку настроить
     
    1. AnimeHeHe Topic starter
      avatarGod_likeGL Layer 1 , в коде настройки баннера подписаны, их можно кастомизировать (размер положение и тд)

      // Базовые стили баннера - под этой строчкой

      я исхожу из идеи идентичности оригинальному разделу :animehehe:
  3. MeloniuM
    Реализовано официально, спасибо за идею:coder:
     
  4. llimonix
    llimonix Oct 11, 2025
    Telegram: View @XomiachiyNovostnik
    29,129 Jan 20, 2020
    Выдан префикс: Реализовано и тема была закрыта
     
Loading...