CMB - Custom Mpp Buttons by gtnntg

Custom buttons for Multiplayer Piano with panel navigation

  1. // ==UserScript==
  2. // @name CMB - Custom Mpp Buttons by gtnntg
  3. // @name:ru КПМ - Настраиваемые кнопки Mpp от gtnntg
  4. // @version 0.1.3
  5. // @devversion 0.6.3
  6. // @description Custom buttons for Multiplayer Piano with panel navigation
  7. // @description:ru Настраиваемые кнопки для Multiplayer Piano с системой навигации по панелям
  8. // @author gtnntg
  9. // @remixauthor /
  10. // @license MIT
  11. // @namespace https://vscode.dev/?connectTo=tampermonkey
  12. // @match *://multiplayerpiano.org/*
  13. // @match *://multiplayerpiano.net/*
  14. // @match *://piano.ourworldofpixels.com/*
  15. // @match *://playground-mpp.hyye.tk/*
  16. // @match *://rgbmpp.qwerty0301.repl.co/*
  17. // @match *://mpp.hyye.tk/*
  18. // @grant GM_setValue
  19. // @grant GM_getValue
  20. // ==/UserScript==
  21. /* globals MPP */
  22. /*---------[Author info]-------------
  23. [discord: gtnntg]
  24. [e-mail: developer.georgiyshvedov@mail.ru]
  25. [github: https://github.com/zeroxel]
  26. --------------------------------------*/
  27. /*---------[Remix Author info]-------------
  28. If you would like to modify the script or change it in any way.
  29. Please fill in your information
  30. You can use author as an example
  31. --------------------------------------*/
  32. /*---------[RU:info]------------
  33. настоящая версия скрипта: 0.6.0
  34.  
  35. Лицензия и авторское право:
  36. Copyright (C) 2024 Georgiy Shvedov (developer.georgiyshvedov@mail.ru)
  37.  
  38. Эта программа является свободным программным обеспечением: вы можете распространять ее и/или модифицировать
  39. ее в соответствии с условиями MIT License.
  40.  
  41. Эта программа распространяется в надежде, что она будет полезной,
  42. но БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ; даже без подразумеваемой гарантии
  43. ТОВАРНОГО ВИДА или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЕННЫХ ЦЕЛЕЙ. См.
  44. MIT License для получения более подробных сведений.
  45.  
  46. Вы должны были получить копию MIT License
  47. вместе с этой программой. Если нет, см.
  48. <https://opensource.org/licenses/MIT>.
  49. -----------------------------*/
  50.  
  51. /*---------[EN:info]------------
  52. Current script version: 0.6.0
  53.  
  54. License and Copyright:
  55. Copyright (C) 2024 Georgiy Shvedov (developer.georgiyshvedov@mail.ru)
  56.  
  57. This program is free software: you can redistribute it and/or modify
  58. it under the terms of the MIT License.
  59.  
  60. This program is distributed in the hope that it will be useful,
  61. but WITHOUT ANY WARRANTY; without even the implied warranty of
  62. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  63. MIT License for more details.
  64.  
  65. You should have received a copy of the MIT License
  66. along with this program. If not, see <https://opensource.org/licenses/MIT>.
  67. -----------------------------*/
  68.  
  69. //-------script-----------
  70. (function() {
  71. 'use strict';
  72.  
  73. // CSS для панелей и кнопок
  74. const CSS = `
  75. :root {
  76. --panel-bg-color: rgba(255, 255, 255, 0.9);
  77. --panel-header-color: rgba(51, 51, 51, 0.9);
  78. --panel-header-text-color: #ffffff;
  79. --button-bg-color: rgba(0, 123, 255, 0.9);
  80. --button-text-color: #ffffff;
  81. --category-bg-color: rgba(255, 255, 255, 0.5); /* Прозрачный фон для подзаголовков */
  82. --border-radius: 5px; /* Закругление краев */
  83. }
  84.  
  85. .custom-panel {
  86. position: fixed;
  87. top: 20px;
  88. left: 0;
  89. width: 250px;
  90. background-color: var(--panel-bg-color);
  91. border: 1px solid #ccc;
  92. box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
  93. z-index: 1000;
  94. display: none;
  95. overflow: hidden;
  96. border-radius: var(--border-radius);
  97. }
  98.  
  99. .panel-header {
  100. background-color: var(--panel-header-color);
  101. color: var(--panel-header-text-color);
  102. padding: 10px;
  103. font-weight: bold;
  104. cursor: move;
  105. border-radius: var(--border-radius) var(--border-radius) 0 0;
  106. }
  107.  
  108. .custom-button {
  109. background-color: var(--button-bg-color);
  110. color: var(--button-text-color);
  111. border: none;
  112. padding: 10px;
  113. margin: 5px;
  114. cursor: pointer;
  115. border-radius: var(--border-radius);
  116. display: block;
  117. width: calc(100% - 20px);
  118. box-sizing: border-box;
  119. }
  120.  
  121. .panel-category {
  122. background-color: var(--category-bg-color); /* Прозрачный фон */
  123. padding: 10px;
  124. font-weight: bold;
  125. border-bottom: 1px solid #ddd;
  126. margin-bottom: 5px;
  127. border-radius: var(--border-radius); /* Закругленные углы */
  128. }
  129.  
  130. .panel-content {
  131. padding: 10px;
  132. }
  133.  
  134. .slide-panel {
  135. position: fixed;
  136. top: 0;
  137. left: 0;
  138. width: 300px;
  139. height: 100%;
  140. background: var(--panel-bg-color);
  141. box-shadow: 2px 0 5px rgba(0, 0, 0, 0.2);
  142. transform: translateX(-100%);
  143. transition: transform 0.3s ease;
  144. overflow: hidden;
  145. z-index: 1001;
  146. display: flex;
  147. flex-direction: column;
  148. border-radius: var(--border-radius);
  149. }
  150.  
  151. .slide-panel.open {
  152. transform: translateX(0);
  153. }
  154.  
  155. .close-button {
  156. background: red;
  157. color: white;
  158. border: none;
  159. padding: 10px;
  160. cursor: pointer;
  161. position: absolute;
  162. top: 10px;
  163. right: 10px;
  164. border-radius: 50%;
  165. z-index: 1002;
  166. }
  167.  
  168. .toggle-button {
  169. position: fixed;
  170. top: 20px;
  171. left: 20px;
  172. background: var(--panel-header-color);
  173. color: var(--panel-header-text-color);
  174. border: none;
  175. padding: 10px;
  176. cursor: pointer;
  177. border-radius: var(--border-radius);
  178. z-index: 1002;
  179. }
  180.  
  181. .style-customizer {
  182. margin: 20px;
  183. padding: 10px;
  184. background: var(--panel-bg-color);
  185. border: 1px solid #ccc;
  186. box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
  187. border-radius: var(--border-radius);
  188. }
  189.  
  190. .style-section {
  191. margin-bottom: 15px;
  192. }
  193.  
  194. .field-container {
  195. margin-bottom: 10px;
  196. }
  197.  
  198. .field-container label {
  199. display: block;
  200. margin-bottom: 5px;
  201. }
  202.  
  203. .field-container input[type="color"],
  204. .field-container input[type="range"] {
  205. width: 100%;
  206. }
  207. `;
  208.  
  209. // Добавление стилей в документ
  210. const styleSheet = document.createElement('style');
  211. styleSheet.type = 'text/css';
  212. styleSheet.innerText = CSS;
  213. document.head.appendChild(styleSheet);
  214.  
  215. // Переменные для настроек стилей
  216. const DEFAULT_STYLES = {
  217. panelBackgroundColor: 'rgba(255, 255, 255, 0.9)',
  218. panelHeaderColor: 'rgba(51, 51, 51, 0.9)',
  219. panelHeaderTextColor: '#ffffff',
  220. buttonBackgroundColor: 'rgba(0, 123, 255, 0.9)',
  221. buttonTextColor: '#ffffff',
  222. categoryBackgroundColor: 'rgba(255, 255, 255, 0.5)', /* Прозрачный фон для подзаголовков */
  223. borderRadius: '5px'
  224. };
  225.  
  226. // Проверка активности скрипта
  227. const SCRIPT_ACTIVE_KEY = 'scriptActive';
  228. function isScriptActive() {
  229. return GM_getValue(SCRIPT_ACTIVE_KEY, true);
  230. }
  231.  
  232. // Функция для активации/деактивации скрипта
  233. function setScriptActive(state) {
  234. GM_setValue(SCRIPT_ACTIVE_KEY, state);
  235. if (state) {
  236. showPanels();
  237. createSlidePanel();
  238. } else {
  239. hidePanels();
  240. }
  241. }
  242.  
  243. // Получение и сохранение стилей
  244. function getStyles() {
  245. return GM_getValue('panelStyles', DEFAULT_STYLES);
  246. }
  247.  
  248. function setStyles(styles) {
  249. GM_setValue('panelStyles', styles);
  250. applyStyles();
  251. }
  252.  
  253. // Применение стилей
  254. function applyStyles() {
  255. const styles = getStyles();
  256. document.documentElement.style.setProperty('--panel-bg-color', styles.panelBackgroundColor);
  257. document.documentElement.style.setProperty('--panel-header-color', styles.panelHeaderColor);
  258. document.documentElement.style.setProperty('--panel-header-text-color', styles.panelHeaderTextColor);
  259. document.documentElement.style.setProperty('--button-bg-color', styles.buttonBackgroundColor);
  260. document.documentElement.style.setProperty('--button-text-color', styles.buttonTextColor);
  261. document.documentElement.style.setProperty('--category-bg-color', styles.categoryBackgroundColor); /* Прозрачный фон для подзаголовков */
  262. document.documentElement.style.setProperty('--border-radius', styles.borderRadius);
  263. }
  264.  
  265. // Создание панели
  266. function createPanel(title, id, categories) {
  267. const panel = document.createElement('div');
  268. panel.id = id;
  269. panel.className = 'custom-panel';
  270.  
  271. const header = document.createElement('div');
  272. header.className = 'panel-header';
  273. header.textContent = title;
  274. panel.appendChild(header);
  275.  
  276. // Создание контента для панелей
  277. const panelContent = document.createElement('div');
  278. panelContent.className = 'panel-content';
  279. categories.forEach(category => {
  280. const categoryHeader = document.createElement('div');
  281. categoryHeader.className = 'panel-category';
  282. categoryHeader.textContent = category.title;
  283. panelContent.appendChild(categoryHeader);
  284.  
  285. category.buttons.forEach(button => {
  286. const btn = createButton(button.text, button.action);
  287. panelContent.appendChild(btn);
  288. });
  289. });
  290.  
  291. panel.appendChild(panelContent);
  292. document.body.appendChild(panel);
  293. makeElementDraggable(panel, header);
  294.  
  295. return panel;
  296. }
  297.  
  298. // Функция для создания кнопок
  299. function createButton(text, onClick) {
  300. const button = document.createElement('button');
  301. button.textContent = text;
  302. button.className = 'custom-button';
  303. button.onclick = onClick;
  304. return button;
  305. }
  306.  
  307. // Создание и отображение панелей
  308. function showPanels() {
  309. const panelsConfig = [
  310. {
  311. title: 'Panel 1',
  312. id: 'custom-panel-1',
  313. categories: [
  314. {
  315. title: 'Category 1',
  316. buttons: [
  317. { text: 'Button 1', action: () => alert('Button 1 clicked') },
  318. { text: 'Button 2', action: () => alert('Button 2 clicked') }
  319. ]
  320. },
  321. {
  322. title: 'Category 2',
  323. buttons: [
  324. { text: 'Button 3', action: () => alert('Button 3 clicked') },
  325. { text: 'Button 4', action: () => alert('Button 4 clicked') }
  326. ]
  327. }
  328. ]
  329. },
  330. {
  331. title: 'Panel 2',
  332. id: 'custom-panel-2',
  333. categories: [
  334. {
  335. title: 'General',
  336. buttons: [
  337. { text: 'General 1', action: () => alert('General 1 clicked') },
  338. { text: 'General 2', action: () => alert('General 2 clicked') }
  339. ]
  340. }
  341. ]
  342. }
  343. ];
  344.  
  345. panelsConfig.forEach(config => {
  346. const panel = createPanel(config.title, config.id, config.categories);
  347. panel.style.display = 'block';
  348. });
  349.  
  350. applyStyles();
  351. }
  352.  
  353. // Скрытие панелей
  354. function hidePanels() {
  355. const panels = document.querySelectorAll('.custom-panel');
  356. panels.forEach(panel => panel.style.display = 'none');
  357. }
  358.  
  359. // Функция для создания выдвижной панели
  360. function createSlidePanel() {
  361. const slidePanel = document.createElement('div');
  362. slidePanel.id = 'slide-panel';
  363. slidePanel.className = 'slide-panel';
  364. document.body.appendChild(slidePanel);
  365.  
  366. const closeButton = document.createElement('button');
  367. closeButton.id = 'close-slide-panel';
  368. closeButton.className = 'close-button';
  369. closeButton.textContent = '✖';
  370. closeButton.onclick = () => {
  371. slidePanel.classList.remove('open');
  372. document.getElementById('toggle-slide-panel').style.display = 'block';
  373. };
  374. slidePanel.appendChild(closeButton);
  375.  
  376. const toggleButton = document.createElement('button');
  377. toggleButton.id = 'toggle-slide-panel';
  378. toggleButton.className = 'toggle-button';
  379. toggleButton.textContent = '☰';
  380. toggleButton.onclick = () => {
  381. slidePanel.classList.toggle('open');
  382. toggleButton.style.display = 'none';
  383. closeButton.style.display = 'block';
  384. };
  385. document.body.appendChild(toggleButton);
  386.  
  387. const panelButtons = document.createElement('div');
  388. panelButtons.className = 'panel-buttons';
  389. slidePanel.appendChild(panelButtons);
  390.  
  391. const panel1Button = createButton('Show Panel 1', () => togglePanel('custom-panel-1'));
  392. const panel2Button = createButton('Show Panel 2', () => togglePanel('custom-panel-2'));
  393. panelButtons.appendChild(panel1Button);
  394. panelButtons.appendChild(panel2Button);
  395.  
  396. createStyleCustomizer(slidePanel);
  397. }
  398.  
  399. // Функция для переключения видимости панелей
  400. function togglePanel(panelId) {
  401. const panel = document.getElementById(panelId);
  402. if (panel) {
  403. panel.style.display = panel.style.display === 'block' ? 'none' : 'block';
  404. }
  405. }
  406.  
  407. // Функция для создания кастомизатора стилей
  408. function createStyleCustomizer(parentElement) {
  409. const customizer = document.createElement('div');
  410. customizer.className = 'style-customizer';
  411. parentElement.appendChild(customizer);
  412.  
  413. const styleSections = [
  414. { name: 'Panel Background Color', key: 'panelBackgroundColor', type: 'color' },
  415. { name: 'Panel Header Color', key: 'panelHeaderColor', type: 'color' },
  416. { name: 'Panel Header Text Color', key: 'panelHeaderTextColor', type: 'color' },
  417. { name: 'Button Background Color', key: 'buttonBackgroundColor', type: 'color' },
  418. { name: 'Button Text Color', key: 'buttonTextColor', type: 'color' },
  419. { name: 'Category Background Color', key: 'categoryBackgroundColor', type: 'color' }, /* Новый параметр */
  420. { name: 'Border Radius', key: 'borderRadius', type: 'range', min: 0, max: 50, step: 1 }
  421. ];
  422.  
  423. styleSections.forEach(section => {
  424. const sectionDiv = document.createElement('div');
  425. sectionDiv.className = 'style-section';
  426.  
  427. const label = document.createElement('label');
  428. label.textContent = section.name;
  429. sectionDiv.appendChild(label);
  430.  
  431. const input = document.createElement('input');
  432. input.type = section.type;
  433. input.value = getStyles()[section.key];
  434. if (section.type === 'range') {
  435. input.min = section.min;
  436. input.max = section.max;
  437. input.step = section.step;
  438. }
  439. input.onchange = (e) => {
  440. const newStyles = getStyles();
  441. newStyles[section.key] = section.type === 'range' ? `${e.target.value}px` : e.target.value;
  442. setStyles(newStyles);
  443. };
  444. sectionDiv.appendChild(input);
  445.  
  446. customizer.appendChild(sectionDiv);
  447. });
  448. }
  449.  
  450. // Функция для перемещения элементов
  451. function makeElementDraggable(element, handle) {
  452. handle.onmousedown = function(e) {
  453. e.preventDefault();
  454. let shiftX = e.clientX - element.getBoundingClientRect().left;
  455. let shiftY = e.clientY - element.getBoundingClientRect().top;
  456.  
  457. function moveAt(pageX, pageY) {
  458. element.style.left = pageX - shiftX + 'px';
  459. element.style.top = pageY - shiftY + 'px';
  460. }
  461.  
  462. moveAt(e.pageX, e.pageY);
  463.  
  464. function onMouseMove(e) {
  465. moveAt(e.pageX, e.pageY);
  466. }
  467.  
  468. document.addEventListener('mousemove', onMouseMove);
  469.  
  470. handle.onmouseup = function() {
  471. document.removeEventListener('mousemove', onMouseMove);
  472. handle.onmouseup = null;
  473. };
  474. };
  475.  
  476. handle.ondragstart = function() {
  477. return false;
  478. };
  479. }
  480.  
  481. // Запуск скрипта
  482. setScriptActive(isScriptActive());
  483. })();