CMB - Custom Mpp Buttons by gtnntg

Custom buttons for Multiplayer Piano with panel navigation

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

Bạn sẽ cần cài đặt một tiện ích mở rộng như Tampermonkey hoặc Violentmonkey để cài đặt kịch bản này.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(Tôi đã có Trình quản lý tập lệnh người dùng, hãy cài đặt nó!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         CMB - Custom Mpp Buttons by gtnntg
// @name:ru      КПМ - Настраиваемые кнопки Mpp от gtnntg
// @version      0.1.3
// @devversion   0.6.3
// @description  Custom buttons for Multiplayer Piano with panel navigation
// @description:ru Настраиваемые кнопки для Multiplayer Piano с системой навигации по панелям
// @author       gtnntg
// @remixauthor  /
// @license      MIT
// @namespace    https://vscode.dev/?connectTo=tampermonkey
// @match        *://multiplayerpiano.org/*
// @match        *://multiplayerpiano.net/*
// @match        *://piano.ourworldofpixels.com/*
// @match        *://playground-mpp.hyye.tk/*
// @match        *://rgbmpp.qwerty0301.repl.co/*
// @match        *://mpp.hyye.tk/*
// @grant        GM_setValue
// @grant        GM_getValue
// ==/UserScript==
/* globals MPP */
/*---------[Author info]-------------
 [discord: gtnntg]
 [e-mail: [email protected]]
 [github: https://github.com/zeroxel]
--------------------------------------*/
/*---------[Remix Author info]-------------
 If you would like to modify the script or change it in any way. 
 Please fill in your information
 You can use author as an example
--------------------------------------*/
/*---------[RU:info]------------
настоящая версия скрипта: 0.6.0

Лицензия и авторское право:
Copyright (C) 2024  Georgiy Shvedov ([email protected])

Эта программа является свободным программным обеспечением: вы можете распространять ее и/или модифицировать
ее в соответствии с условиями MIT License.

Эта программа распространяется в надежде, что она будет полезной,
но БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ; даже без подразумеваемой гарантии
ТОВАРНОГО ВИДА или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЕННЫХ ЦЕЛЕЙ. См.
MIT License для получения более подробных сведений.

Вы должны были получить копию MIT License
вместе с этой программой. Если нет, см.
<https://opensource.org/licenses/MIT>.
-----------------------------*/

/*---------[EN:info]------------
Current script version: 0.6.0

License and Copyright:
Copyright (C) 2024 Georgiy Shvedov ([email protected])

This program is free software: you can redistribute it and/or modify
it under the terms of the MIT License.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
MIT License for more details.

You should have received a copy of the MIT License
along with this program. If not, see <https://opensource.org/licenses/MIT>.
-----------------------------*/

//-------script-----------
(function() {
    'use strict';

    // CSS для панелей и кнопок
    const CSS = `
    :root {
        --panel-bg-color: rgba(255, 255, 255, 0.9);
        --panel-header-color: rgba(51, 51, 51, 0.9);
        --panel-header-text-color: #ffffff;
        --button-bg-color: rgba(0, 123, 255, 0.9);
        --button-text-color: #ffffff;
        --category-bg-color: rgba(255, 255, 255, 0.5); /* Прозрачный фон для подзаголовков */
        --border-radius: 5px; /* Закругление краев */
    }

    .custom-panel {
        position: fixed;
        top: 20px;
        left: 0;
        width: 250px;
        background-color: var(--panel-bg-color);
        border: 1px solid #ccc;
        box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
        z-index: 1000;
        display: none;
        overflow: hidden;
        border-radius: var(--border-radius);
    }

    .panel-header {
        background-color: var(--panel-header-color);
        color: var(--panel-header-text-color);
        padding: 10px;
        font-weight: bold;
        cursor: move;
        border-radius: var(--border-radius) var(--border-radius) 0 0;
    }

    .custom-button {
        background-color: var(--button-bg-color);
        color: var(--button-text-color);
        border: none;
        padding: 10px;
        margin: 5px;
        cursor: pointer;
        border-radius: var(--border-radius);
        display: block;
        width: calc(100% - 20px);
        box-sizing: border-box;
    }

    .panel-category {
        background-color: var(--category-bg-color); /* Прозрачный фон */
        padding: 10px;
        font-weight: bold;
        border-bottom: 1px solid #ddd;
        margin-bottom: 5px;
        border-radius: var(--border-radius); /* Закругленные углы */
    }

    .panel-content {
        padding: 10px;
    }

    .slide-panel {
        position: fixed;
        top: 0;
        left: 0;
        width: 300px;
        height: 100%;
        background: var(--panel-bg-color);
        box-shadow: 2px 0 5px rgba(0, 0, 0, 0.2);
        transform: translateX(-100%);
        transition: transform 0.3s ease;
        overflow: hidden;
        z-index: 1001;
        display: flex;
        flex-direction: column;
        border-radius: var(--border-radius);
    }

    .slide-panel.open {
        transform: translateX(0);
    }

    .close-button {
        background: red;
        color: white;
        border: none;
        padding: 10px;
        cursor: pointer;
        position: absolute;
        top: 10px;
        right: 10px;
        border-radius: 50%;
        z-index: 1002;
    }

    .toggle-button {
        position: fixed;
        top: 20px;
        left: 20px;
        background: var(--panel-header-color);
        color: var(--panel-header-text-color);
        border: none;
        padding: 10px;
        cursor: pointer;
        border-radius: var(--border-radius);
        z-index: 1002;
    }

    .style-customizer {
        margin: 20px;
        padding: 10px;
        background: var(--panel-bg-color);
        border: 1px solid #ccc;
        box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
        border-radius: var(--border-radius);
    }

    .style-section {
        margin-bottom: 15px;
    }

    .field-container {
        margin-bottom: 10px;
    }

    .field-container label {
        display: block;
        margin-bottom: 5px;
    }

    .field-container input[type="color"],
    .field-container input[type="range"] {
        width: 100%;
    }
    `;

    // Добавление стилей в документ
    const styleSheet = document.createElement('style');
    styleSheet.type = 'text/css';
    styleSheet.innerText = CSS;
    document.head.appendChild(styleSheet);

    // Переменные для настроек стилей
    const DEFAULT_STYLES = {
        panelBackgroundColor: 'rgba(255, 255, 255, 0.9)',
        panelHeaderColor: 'rgba(51, 51, 51, 0.9)',
        panelHeaderTextColor: '#ffffff',
        buttonBackgroundColor: 'rgba(0, 123, 255, 0.9)',
        buttonTextColor: '#ffffff',
        categoryBackgroundColor: 'rgba(255, 255, 255, 0.5)', /* Прозрачный фон для подзаголовков */
        borderRadius: '5px'
    };

    // Проверка активности скрипта
    const SCRIPT_ACTIVE_KEY = 'scriptActive';
    function isScriptActive() {
        return GM_getValue(SCRIPT_ACTIVE_KEY, true);
    }

    // Функция для активации/деактивации скрипта
    function setScriptActive(state) {
        GM_setValue(SCRIPT_ACTIVE_KEY, state);
        if (state) {
            showPanels();
            createSlidePanel();
        } else {
            hidePanels();
        }
    }

    // Получение и сохранение стилей
    function getStyles() {
        return GM_getValue('panelStyles', DEFAULT_STYLES);
    }

    function setStyles(styles) {
        GM_setValue('panelStyles', styles);
        applyStyles();
    }

    // Применение стилей
    function applyStyles() {
        const styles = getStyles();
        document.documentElement.style.setProperty('--panel-bg-color', styles.panelBackgroundColor);
        document.documentElement.style.setProperty('--panel-header-color', styles.panelHeaderColor);
        document.documentElement.style.setProperty('--panel-header-text-color', styles.panelHeaderTextColor);
        document.documentElement.style.setProperty('--button-bg-color', styles.buttonBackgroundColor);
        document.documentElement.style.setProperty('--button-text-color', styles.buttonTextColor);
        document.documentElement.style.setProperty('--category-bg-color', styles.categoryBackgroundColor); /* Прозрачный фон для подзаголовков */
        document.documentElement.style.setProperty('--border-radius', styles.borderRadius);
    }

    // Создание панели
    function createPanel(title, id, categories) {
        const panel = document.createElement('div');
        panel.id = id;
        panel.className = 'custom-panel';

        const header = document.createElement('div');
        header.className = 'panel-header';
        header.textContent = title;
        panel.appendChild(header);

        // Создание контента для панелей
        const panelContent = document.createElement('div');
        panelContent.className = 'panel-content';
        categories.forEach(category => {
            const categoryHeader = document.createElement('div');
            categoryHeader.className = 'panel-category';
            categoryHeader.textContent = category.title;
            panelContent.appendChild(categoryHeader);

            category.buttons.forEach(button => {
                const btn = createButton(button.text, button.action);
                panelContent.appendChild(btn);
            });
        });

        panel.appendChild(panelContent);
        document.body.appendChild(panel);
        makeElementDraggable(panel, header);

        return panel;
    }

    // Функция для создания кнопок
    function createButton(text, onClick) {
        const button = document.createElement('button');
        button.textContent = text;
        button.className = 'custom-button';
        button.onclick = onClick;
        return button;
    }

    // Создание и отображение панелей
    function showPanels() {
        const panelsConfig = [
            {
                title: 'Panel 1',
                id: 'custom-panel-1',
                categories: [
                    {
                        title: 'Category 1',
                        buttons: [
                            { text: 'Button 1', action: () => alert('Button 1 clicked') },
                            { text: 'Button 2', action: () => alert('Button 2 clicked') }
                        ]
                    },
                    {
                        title: 'Category 2',
                        buttons: [
                            { text: 'Button 3', action: () => alert('Button 3 clicked') },
                            { text: 'Button 4', action: () => alert('Button 4 clicked') }
                        ]
                    }
                ]
            },
            {
                title: 'Panel 2',
                id: 'custom-panel-2',
                categories: [
                    {
                        title: 'General',
                        buttons: [
                            { text: 'General 1', action: () => alert('General 1 clicked') },
                            { text: 'General 2', action: () => alert('General 2 clicked') }
                        ]
                    }
                ]
            }
        ];

        panelsConfig.forEach(config => {
            const panel = createPanel(config.title, config.id, config.categories);
            panel.style.display = 'block';
        });

        applyStyles();
    }

    // Скрытие панелей
    function hidePanels() {
        const panels = document.querySelectorAll('.custom-panel');
        panels.forEach(panel => panel.style.display = 'none');
    }

    // Функция для создания выдвижной панели
    function createSlidePanel() {
        const slidePanel = document.createElement('div');
        slidePanel.id = 'slide-panel';
        slidePanel.className = 'slide-panel';
        document.body.appendChild(slidePanel);

        const closeButton = document.createElement('button');
        closeButton.id = 'close-slide-panel';
        closeButton.className = 'close-button';
        closeButton.textContent = '✖';
        closeButton.onclick = () => {
            slidePanel.classList.remove('open');
            document.getElementById('toggle-slide-panel').style.display = 'block';
        };
        slidePanel.appendChild(closeButton);

        const toggleButton = document.createElement('button');
        toggleButton.id = 'toggle-slide-panel';
        toggleButton.className = 'toggle-button';
        toggleButton.textContent = '☰';
        toggleButton.onclick = () => {
            slidePanel.classList.toggle('open');
            toggleButton.style.display = 'none';
            closeButton.style.display = 'block';
        };
        document.body.appendChild(toggleButton);

        const panelButtons = document.createElement('div');
        panelButtons.className = 'panel-buttons';
        slidePanel.appendChild(panelButtons);

        const panel1Button = createButton('Show Panel 1', () => togglePanel('custom-panel-1'));
        const panel2Button = createButton('Show Panel 2', () => togglePanel('custom-panel-2'));
        panelButtons.appendChild(panel1Button);
        panelButtons.appendChild(panel2Button);

        createStyleCustomizer(slidePanel);
    }

    // Функция для переключения видимости панелей
    function togglePanel(panelId) {
        const panel = document.getElementById(panelId);
        if (panel) {
            panel.style.display = panel.style.display === 'block' ? 'none' : 'block';
        }
    }

    // Функция для создания кастомизатора стилей
    function createStyleCustomizer(parentElement) {
        const customizer = document.createElement('div');
        customizer.className = 'style-customizer';
        parentElement.appendChild(customizer);

        const styleSections = [
            { name: 'Panel Background Color', key: 'panelBackgroundColor', type: 'color' },
            { name: 'Panel Header Color', key: 'panelHeaderColor', type: 'color' },
            { name: 'Panel Header Text Color', key: 'panelHeaderTextColor', type: 'color' },
            { name: 'Button Background Color', key: 'buttonBackgroundColor', type: 'color' },
            { name: 'Button Text Color', key: 'buttonTextColor', type: 'color' },
            { name: 'Category Background Color', key: 'categoryBackgroundColor', type: 'color' }, /* Новый параметр */
            { name: 'Border Radius', key: 'borderRadius', type: 'range', min: 0, max: 50, step: 1 }
        ];

        styleSections.forEach(section => {
            const sectionDiv = document.createElement('div');
            sectionDiv.className = 'style-section';

            const label = document.createElement('label');
            label.textContent = section.name;
            sectionDiv.appendChild(label);

            const input = document.createElement('input');
            input.type = section.type;
            input.value = getStyles()[section.key];
            if (section.type === 'range') {
                input.min = section.min;
                input.max = section.max;
                input.step = section.step;
            }
            input.onchange = (e) => {
                const newStyles = getStyles();
                newStyles[section.key] = section.type === 'range' ? `${e.target.value}px` : e.target.value;
                setStyles(newStyles);
            };
            sectionDiv.appendChild(input);

            customizer.appendChild(sectionDiv);
        });
    }

    // Функция для перемещения элементов
    function makeElementDraggable(element, handle) {
        handle.onmousedown = function(e) {
            e.preventDefault();
            let shiftX = e.clientX - element.getBoundingClientRect().left;
            let shiftY = e.clientY - element.getBoundingClientRect().top;

            function moveAt(pageX, pageY) {
                element.style.left = pageX - shiftX + 'px';
                element.style.top = pageY - shiftY + 'px';
            }

            moveAt(e.pageX, e.pageY);

            function onMouseMove(e) {
                moveAt(e.pageX, e.pageY);
            }

            document.addEventListener('mousemove', onMouseMove);

            handle.onmouseup = function() {
                document.removeEventListener('mousemove', onMouseMove);
                handle.onmouseup = null;
            };
        };

        handle.ondragstart = function() {
            return false;
        };
    }

    // Запуск скрипта
    setScriptActive(isScriptActive());
})();