Загрузка...

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

Thread in Extentions created by AnimeHeHe Oct 9, 2025. 335 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. llimonix
      avatarAnimeHeHe , ты бы еще громче музыку сделал
    2. God_likeGL Layer 1
      :fsdglgfdk19000: avatarllimonix , Сделала*
    3. llimonix
    4. View the next comments (1)
  2. God_likeGL
    God_likeGL Layer 1 Oct 10, 2025 Лучшие сервера: lolz.live/threads/5071761/ :+rep: 35,783 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,128 Jan 20, 2020
    Выдан префикс: Реализовано и тема была закрыта
     
Loading...