KG_Chat_Application

Enhance the chat abilities

当前为 2025-03-13 提交的版本,查看 最新版本

// ==UserScript==
// @name         KG_Chat_Application
// @namespace    klavogonki
// @version      1.0.6
// @description  Enhance the chat abilities
// @author       Patcher
// @match        *://klavogonki.ru/*
// @exclude      https://klavogonki.ru/g/?gmid=*
// @exclude      https://klavogonki.ru/chatlogs/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=klavogonki.ru
// @grant        none
// ==/UserScript==

/*
 * ATTENTION: The "eval" devtool has been used (maybe by default in mode: "development").
 * This devtool is neither made for production nor for readable output files.
 * It uses "eval()" calls to create a separate source file in the browser devtools.
 * If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
 * or disable the default devtool with "devtool: false".
 * If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
 */
/******/ (() => { // webpackBootstrap
/******/ 	"use strict";
/******/ 	var __webpack_modules__ = ({

/***/ "./main.js":
/*!*****************!*\
  !*** ./main.js ***!
  \*****************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _src_definitions_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./src/definitions.js */ \"./src/definitions.js\");\n/* harmony import */ var _src_xmppConnection_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./src/xmppConnection.js */ \"./src/xmppConnection.js\");\n/* harmony import */ var _src_userManager_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./src/userManager.js */ \"./src/userManager.js\");\n/* harmony import */ var _src_messageManager_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./src/messageManager.js */ \"./src/messageManager.js\");\n/* harmony import */ var _src_chatUI_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./src/chatUI.js */ \"./src/chatUI.js\");\n/* harmony import */ var _src_chatFeatures_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./src/chatFeatures.js */ \"./src/chatFeatures.js\");\n/* harmony import */ var _src_events_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./src/events.js */ \"./src/events.js\");\n/* harmony import */ var _src_xmppClient_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./src/xmppClient.js */ \"./src/xmppClient.js\");\n/* harmony import */ var _src_helpers_js__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./src/helpers.js */ \"./src/helpers.js\");\n/* harmony import */ var _src_auth_js__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./src/auth.js */ \"./src/auth.js\");\n/* harmony import */ var _src_components_helpPanel_js__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./src/components/helpPanel.js */ \"./src/components/helpPanel.js\");\n/* harmony import */ var _src_styles_emojiPanel_css__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./src/styles/emojiPanel.css */ \"./src/styles/emojiPanel.css\");\n/* harmony import */ var _src_styles_helpPanel_css__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./src/styles/helpPanel.css */ \"./src/styles/helpPanel.css\");\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n// Function to detect if running in an iframe\r\nfunction isInIframe() {\r\n  try {\r\n    return window !== window.top;\r\n  } catch (e) {\r\n    // If there's an error when trying to access window.top, \r\n    // it's likely due to cross-origin restrictions, which means we're in an iframe\r\n    return true;\r\n  }\r\n}\r\n\r\n// ------------------------- Auth Check ---------------------------\r\nfunction checkAuth() {\r\n  // First check if running in iframe\r\n  if (isInIframe()) {\r\n    console.error('Application cannot run in an iframe');\r\n    return false;\r\n  }\r\n\r\n  const params = new URLSearchParams(window.location.search);\r\n  if (window.location.pathname === '/g/' && params.has('gmid')) {\r\n    return false;\r\n  }\r\n  if (window.location.href.includes('/gamelist/')) {\r\n    (0,_src_auth_js__WEBPACK_IMPORTED_MODULE_9__.getAuthData)();\r\n    return false;\r\n  }\r\n  const authData = localStorage.getItem('klavoauth');\r\n  if (!authData || !_src_definitions_js__WEBPACK_IMPORTED_MODULE_0__.config.username || !_src_definitions_js__WEBPACK_IMPORTED_MODULE_0__.config.password) {\r\n    localStorage.removeItem('klavoauth');\r\n    window.location.href = 'https://klavogonki.ru/gamelist/';\r\n    return false;\r\n  }\r\n  return true;\r\n}\r\n\r\n// ------------------------- Main App ---------------------------\r\nasync function initializeApp() {\r\n  try {\r\n    // Add viewport meta tag\r\n    (0,_src_helpers_js__WEBPACK_IMPORTED_MODULE_8__.addViewportMeta)();\r\n\r\n    if (!checkAuth()) return;\r\n\r\n    // Initialize UI and features\r\n    (0,_src_chatUI_js__WEBPACK_IMPORTED_MODULE_4__.createChatUI)();\r\n    (0,_src_chatFeatures_js__WEBPACK_IMPORTED_MODULE_5__.addChatToggleFeature)();\r\n    (0,_src_events_js__WEBPACK_IMPORTED_MODULE_6__.setupDragHandlers)();\r\n    (0,_src_events_js__WEBPACK_IMPORTED_MODULE_6__.setupResizeHandlers)();\r\n    (0,_src_events_js__WEBPACK_IMPORTED_MODULE_6__.setupWindowResizeHandler)();\r\n\r\n    // Set up the messages panel observer\r\n    (0,_src_helpers_js__WEBPACK_IMPORTED_MODULE_8__.observeMessagesPanel)();\r\n\r\n    // Initialize managers and XMPP connection\r\n    const userManager = new _src_userManager_js__WEBPACK_IMPORTED_MODULE_2__[\"default\"]('user-list');\r\n    const messageManager = new _src_messageManager_js__WEBPACK_IMPORTED_MODULE_3__[\"default\"]('messages-panel', (0,_src_helpers_js__WEBPACK_IMPORTED_MODULE_8__.parseUsername)(_src_definitions_js__WEBPACK_IMPORTED_MODULE_0__.config.username));\r\n    const xmppConnection = new _src_xmppConnection_js__WEBPACK_IMPORTED_MODULE_1__[\"default\"]({\r\n      username: _src_definitions_js__WEBPACK_IMPORTED_MODULE_0__.config.username,\r\n      password: _src_definitions_js__WEBPACK_IMPORTED_MODULE_0__.config.password,\r\n      bindUrl: _src_definitions_js__WEBPACK_IMPORTED_MODULE_0__.XMPP_BIND_URL,\r\n      delay: _src_definitions_js__WEBPACK_IMPORTED_MODULE_0__.delay\r\n    });\r\n\r\n    const xmppClient = (0,_src_xmppClient_js__WEBPACK_IMPORTED_MODULE_7__.createXMPPClient)(\r\n      xmppConnection,\r\n      userManager,\r\n      messageManager,\r\n      _src_definitions_js__WEBPACK_IMPORTED_MODULE_0__.config.username\r\n    );\r\n\r\n    // Message sending setup\r\n    const input = document.getElementById('message-input');\r\n    const sendMessage = () => {\r\n      const text = input.value.trim();\r\n      if (!text) return;\r\n      xmppClient.sendMessage(text);\r\n      input.value = '';\r\n      input.focus();\r\n    };\r\n\r\n    document.getElementById('send-button').addEventListener('click', sendMessage);\r\n    input.addEventListener('keypress', e => e.key === 'Enter' && sendMessage());\r\n\r\n    // Set up private messaging events\r\n    (0,_src_helpers_js__WEBPACK_IMPORTED_MODULE_8__.setupPrivateMessageEvents)();\r\n    // New: Set up help command events (similar to /pm command)\r\n    _src_components_helpPanel_js__WEBPACK_IMPORTED_MODULE_10__.HelpPanel.setupHelpCommandEvents();\r\n\r\n    // Connect to XMPP and join the room\r\n    await xmppClient.connect();\r\n    window.xmppClient = xmppClient; // For debugging\r\n\r\n    // Detect /pm username command and activate private mode, and /exit to exit private mode\r\n    input.addEventListener('input', function (event) {\r\n      (0,_src_helpers_js__WEBPACK_IMPORTED_MODULE_8__.handlePrivateMessageInput)(event.target);\r\n    });\r\n  } catch (error) {\r\n    console.error('App init error:', error);\r\n    localStorage.removeItem('klavoauth');\r\n    window.location.href = 'https://klavogonki.ru/gamelist/';\r\n  }\r\n}\r\n\r\n// Start the app\r\ninitializeApp();\n\n//# sourceURL=webpack://tampermonkey-script/./main.js?");

/***/ }),

/***/ "./node_modules/css-loader/dist/cjs.js!./src/styles/emojiPanel.css":
/*!*************************************************************************!*\
  !*** ./node_modules/css-loader/dist/cjs.js!./src/styles/emojiPanel.css ***!
  \*************************************************************************/
/***/ ((module, __webpack_exports__, __webpack_require__) => {

eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   \"default\": () => (__WEBPACK_DEFAULT_EXPORT__)\n/* harmony export */ });\n/* harmony import */ var _node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../node_modules/css-loader/dist/runtime/noSourceMaps.js */ \"./node_modules/css-loader/dist/runtime/noSourceMaps.js\");\n/* harmony import */ var _node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../node_modules/css-loader/dist/runtime/api.js */ \"./node_modules/css-loader/dist/runtime/api.js\");\n/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);\n// Imports\n\n\nvar ___CSS_LOADER_EXPORT___ = _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0___default()));\n// Module\n___CSS_LOADER_EXPORT___.push([module.id, `:root {\r\n  --emoji-font: \"Noto Color Emoji\";\r\n}\r\n\r\n.emoji-panel {\r\n  opacity: 0;\r\n  transition: opacity 0.3s ease;\r\n  position: absolute !important;\r\n  top: 45% !important;\r\n  left: 50% !important;\r\n  transform: translate(-50%, -50%) !important;\r\n  background: #1e1e1e !important;\r\n  border: 1px solid #333 !important;\r\n  border-radius: 0.4em !important;\r\n  width: 380px;\r\n  height: 580px;\r\n  display: flex;\r\n  flex-direction: column;\r\n  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15) !important;\r\n  z-index: 1000;\r\n}\r\n\r\n.emoji-search-container {\r\n  padding: 1em !important;\r\n  border: none !important;\r\n}\r\n\r\n.emoji-search {\r\n  width: 100%;\r\n  padding: 8px;\r\n  border-radius: 4px;\r\n  background: #2a2a2a !important;\r\n  border: none !important;\r\n  border-radius: 0.2em !important;\r\n  color: #deb887 !important;\r\n  caret-color: #deb887 !important;\r\n  font-size: 0.9em !important;\r\n}\r\n\r\n.emoji-search:focus {\r\n  outline: none;\r\n  border-color: #666;\r\n}\r\n\r\n.emoji-categories {\r\n  position: sticky !important;\r\n  top: 0 !important;\r\n  display: grid !important;\r\n  grid-template-columns: repeat(auto-fill, minmax(32px, 1fr)) !important;\r\n  padding: 8px !important;\r\n  border-bottom: 1px solid #333 !important;\r\n  gap: 8px !important;\r\n  justify-content: center !important;\r\n  align-items: center !important;\r\n  overflow-x: auto !important;\r\n  scrollbar-width: thin !important;\r\n}\r\n\r\n.emoji-category-btn {\r\n  font-family: var(--emoji-font) !important;\r\n  position: relative !important;\r\n  background: none !important;\r\n  border: none !important;\r\n  padding: 4px !important;\r\n  cursor: pointer !important;\r\n  font-size: 1.5em !important;\r\n  transition: background-color 0.2s;\r\n  width: 100% !important;\r\n  height: 100% !important;\r\n  display: flex !important;\r\n  align-items: center !important;\r\n  justify-content: center !important;\r\n  aspect-ratio: 1 !important;\r\n  border-bottom: 3px solid transparent !important;\r\n}\r\n\r\n/* Active category button gets a 3px {color} border */\r\n.emoji-category-btn.active {\r\n  opacity: 1;\r\n  border-bottom: 3px solid goldenrod !important;\r\n}\r\n\r\n.emoji-category-btn:hover {\r\n  background-color: #333;\r\n}\r\n\r\n.emoji-container {\r\n  flex: 1;\r\n  overflow-y: auto;\r\n  overflow-x: hidden !important;\r\n  display: grid !important;\r\n  gap: 8px !important;\r\n  width: 100% !important;\r\n  max-width: 100% !important;\r\n  scrollbar-width: none !important;\r\n}\r\n\r\n.emoji-category-section {\r\n  margin-bottom: 10px;\r\n}\r\n\r\n.emoji-category-header {\r\n  padding: 8px !important;\r\n  color: #deb887 !important;\r\n  font-size: 0.9em !important;\r\n  position: sticky !important;\r\n  top: 0px !important;\r\n  background: #1e1e1e !important;\r\n  z-index: 1 !important;\r\n  border-bottom: 1px solid #333 !important;\r\n  width: 100% !important;\r\n  box-sizing: border-box !important;\r\n  margin: 0 !important;\r\n}\r\n\r\n.emoji-list {\r\n  display: grid;\r\n  grid-template-columns: repeat(auto-fill, minmax(32px, 1fr)) !important;\r\n  padding: 8px !important;\r\n  gap: 8px;\r\n  align-content: start;\r\n}\r\n\r\n.emoji-btn {\r\n  font-family: var(--emoji-font), sans-serif !important;\r\n  background: none;\r\n  border: none;\r\n  padding: 4px !important;\r\n  cursor: pointer;\r\n  font-size: 1.5em !important;\r\n  transition: background-color 0.2s;\r\n  width: 100% !important;\r\n  height: 100% !important;\r\n  display: flex !important;\r\n  align-items: center !important;\r\n  justify-content: center !important;\r\n  aspect-ratio: 1 !important;\r\n}\r\n\r\n.emoji-btn:hover {\r\n  background-color: #333;\r\n}\r\n\r\n.emoji-footer {\r\n  border-top: 1px solid #333 !important;\r\n  display: flex;\r\n  flex-direction: row;\r\n  justify-content: space-between;\r\n  align-items: center;\r\n  padding: 5px;\r\n}\r\n\r\n/* Info panel fixed at bottom with a 50px height */\r\n.emoji-info-panel {\r\n  height: 40px !important;\r\n  padding: 8px !important;\r\n  display: flex !important;\r\n  align-items: center !important;\r\n  gap: 8px !important;\r\n  color: #deb887 !important;\r\n  font-size: 0.9em !important;\r\n  background: #1e1e1e !important;\r\n}\r\n\r\n.emoji-language-select {\r\n  border-radius: 0.4em !important;\r\n  /* Rounded corners */\r\n  padding: 5px 10px;\r\n  /* Space inside the select */\r\n  font-size: 14px;\r\n  /* Font size for readability */\r\n  background-color: #2a2a2a !important;\r\n  /* Dark background to match search input */\r\n  border: 1px solid #444 !important;\r\n  /* Subtle dark border */\r\n  color: #deb887 !important;\r\n  /* Text color to match headers and info panel */\r\n  cursor: pointer;\r\n  /* Pointer cursor to indicate it's clickable */\r\n  transition: border-color 0.3s ease;\r\n  /* Smooth transition for border color */\r\n}\r\n\r\n/* Focus state */\r\n.emoji-language-select:focus {\r\n  outline: none;\r\n  /* Remove default outline */\r\n  border-color: goldenrod !important;\r\n  /* Match active category button */\r\n}\r\n\r\n/* Hover state */\r\n.emoji-language-select:hover {\r\n  border-color: #666 !important;\r\n  /* Lighten border on hover */\r\n}\r\n\r\n/* Optional: Style for options (limited control) */\r\n.emoji-language-select option {\r\n  font-size: 14px;\r\n  /* Match font size */\r\n  background-color: #2a2a2a !important;\r\n  /* Dark background for options */\r\n  color: #deb887 !important;\r\n  /* Text color for options */\r\n}\r\n\r\n.emoji-info-icon {\r\n  font-family: var(--emoji-font) !important;\r\n  font-size: 1.5em !important;\r\n}\r\n\r\n.emoji-info-keywords {\r\n  color: #888 !important;\r\n  font-style: italic !important;\r\n}\r\n\r\n/* Scrollbar styling */\r\n.emoji-container::-webkit-scrollbar,\r\n.emoji-categories::-webkit-scrollbar {\r\n  width: 6px;\r\n  height: 6px;\r\n}\r\n\r\n.emoji-container::-webkit-scrollbar-track,\r\n.emoji-categories::-webkit-scrollbar-track {\r\n  background: #1e1e1e;\r\n}\r\n\r\n.emoji-container::-webkit-scrollbar-thumb,\r\n.emoji-categories::-webkit-scrollbar-thumb {\r\n  background: #444;\r\n  border-radius: 3px;\r\n}\r\n\r\n.emoji-container::-webkit-scrollbar-thumb:hover,\r\n.emoji-categories::-webkit-scrollbar-thumb:hover {\r\n  background: #555;\r\n}`, \"\"]);\n// Exports\n/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);\n\n\n//# sourceURL=webpack://tampermonkey-script/./src/styles/emojiPanel.css?./node_modules/css-loader/dist/cjs.js");

/***/ }),

/***/ "./node_modules/css-loader/dist/cjs.js!./src/styles/helpPanel.css":
/*!************************************************************************!*\
  !*** ./node_modules/css-loader/dist/cjs.js!./src/styles/helpPanel.css ***!
  \************************************************************************/
/***/ ((module, __webpack_exports__, __webpack_require__) => {

eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   \"default\": () => (__WEBPACK_DEFAULT_EXPORT__)\n/* harmony export */ });\n/* harmony import */ var _node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../node_modules/css-loader/dist/runtime/noSourceMaps.js */ \"./node_modules/css-loader/dist/runtime/noSourceMaps.js\");\n/* harmony import */ var _node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../node_modules/css-loader/dist/runtime/api.js */ \"./node_modules/css-loader/dist/runtime/api.js\");\n/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);\n// Imports\n\n\nvar ___CSS_LOADER_EXPORT___ = _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0___default()));\n// Module\n___CSS_LOADER_EXPORT___.push([module.id, `.help-panel {\r\n  opacity: 0;\r\n  transition: opacity 0.3s ease;\r\n  position: fixed;\r\n  left: 50%;\r\n  top: 50%;\r\n  transform: translate(-50%, -50%);\r\n  height: fit-content;\r\n  max-height: 70vh !important;\r\n  width: fit-content;\r\n  overflow-y: auto;\r\n  scrollbar-width: none;\r\n  /* Existing styles */\r\n  background: #1e1e1e;\r\n  border: 1px solid #333 !important;\r\n  padding: 1em;\r\n  border-radius: 0.4em !important;\r\n  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3) !important;\r\n  color: #deb887 !important;\r\n  font-family:'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif;\r\n  z-index: 1100;\r\n}\r\n\r\n/* Header styling */\r\n.help-header {\r\n  margin-top: 0;\r\n  font-size: 1.5em;\r\n  text-align: center;\r\n  color: #ffa500 !important;\r\n}\r\n\r\n/* Subheader styling – now further differentiated */\r\n.help-subheader {\r\n  margin: 0.8em 0 0.3em 1em;\r\n  /* Added left margin of 1em */\r\n  font-size: 1.1em;\r\n  /* Smaller than header */\r\n  color: #ffcc00 !important;\r\n  font-weight: normal;\r\n  text-align: left;\r\n}\r\n\r\n/* Header for a specific section */\r\n.help-section-header {\r\n  margin-top: 0 !important;\r\n  margin-bottom: 1.5em !important;\r\n  font-size: 1.5em !important;\r\n  text-align: center;\r\n  color: #ffa500 !important;\r\n}\r\n\r\n/* Subheader for a specific section (different type) */\r\n.help-section-subheader {\r\n  margin: 0.8em 0 1.2em 1em;\r\n  /* left margin added */\r\n  font-size: 0.9em;\r\n  color: #ffcc00 !important;\r\n  font-weight: normal;\r\n  text-align: left;\r\n}\r\n\r\n/* List styling */\r\n.help-list {\r\n  list-style-type: none;\r\n  padding-left: 0;\r\n}\r\n\r\n/* List item styling */\r\n.help-list-item {\r\n  display: flex;\r\n  align-items: center;\r\n  flex-direction: row;\r\n  padding: 0.3em;\r\n  border-bottom: 1px dashed #444 !important;\r\n}\r\n\r\n.help-list-item:last-child {\r\n  border-bottom: none;\r\n}\r\n\r\n/* Hotkey text styling – updated to force command description wrap */\r\n.help-hotkey {\r\n  width: fit-content;\r\n  display: flex;\r\n  color: #7ed4ff !important;\r\n  font-family: monospace;\r\n  background-color: rgba(126, 212, 255, 0.1) !important;\r\n  border: 1px solid rgba(126, 212, 255, 0.4) !important;\r\n  border-left: 3px solid rgba(126, 212, 255, 0.4) !important;\r\n  border-radius: 0 0.2em 0.2em 0 !important;\r\n  padding: 2px 4px;\r\n  margin-right: 1em;\r\n}`, \"\"]);\n// Exports\n/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);\n\n\n//# sourceURL=webpack://tampermonkey-script/./src/styles/helpPanel.css?./node_modules/css-loader/dist/cjs.js");

/***/ }),

/***/ "./node_modules/css-loader/dist/cjs.js!./src/styles/style.css":
/*!********************************************************************!*\
  !*** ./node_modules/css-loader/dist/cjs.js!./src/styles/style.css ***!
  \********************************************************************/
/***/ ((module, __webpack_exports__, __webpack_require__) => {

eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   \"default\": () => (__WEBPACK_DEFAULT_EXPORT__)\n/* harmony export */ });\n/* harmony import */ var _node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../node_modules/css-loader/dist/runtime/noSourceMaps.js */ \"./node_modules/css-loader/dist/runtime/noSourceMaps.js\");\n/* harmony import */ var _node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../node_modules/css-loader/dist/runtime/api.js */ \"./node_modules/css-loader/dist/runtime/api.js\");\n/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);\n// Imports\n\n\nvar ___CSS_LOADER_EXPORT___ = _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0___default()));\n___CSS_LOADER_EXPORT___.push([module.id, \"@import url(https://fonts.googleapis.com/css2?family=Noto+Color+Emoji&display=swap);\"]);\n___CSS_LOADER_EXPORT___.push([module.id, \"@import url(https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap);\"]);\n___CSS_LOADER_EXPORT___.push([module.id, \"@import url(https://fonts.googleapis.com/css2?family=Iansui&family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap);\"]);\n// Module\n___CSS_LOADER_EXPORT___.push([module.id, `/* Variables */\r\n:root {\r\n  --border-radius: 0.2em;\r\n  --min-chat-width: 320px;\r\n  --min-chat-height: 200px;\r\n  --user-list-width: fit-content;\r\n  --emoji-font: \"Noto Color Emoji\";\r\n}\r\n\r\n/* Main chat container */\r\n#app-chat-container {\r\n  border-radius: 0.4em 0.4em 0 0 !important;\r\n  position: fixed;\r\n  bottom: 0;\r\n  left: 0;\r\n  height: 300px;\r\n  background: #1e1e1e !important;\r\n  border: 1px solid #333 !important;\r\n  display: flex;\r\n  font-family: sans-serif;\r\n  color: #deb887 !important;\r\n  z-index: 999;\r\n  min-width: var(--min-chat-width) !important;\r\n  min-height: var(--min-chat-height) !important;\r\n  box-sizing: border-box;\r\n  max-width: 100vw;\r\n  overflow: hidden;\r\n  transition: opacity 0.3s ease, transform 0.4s cubic-bezier(0.68, -0.55, 0.265, 1.55);\r\n}\r\n\r\n#app-chat-container a {\r\n  color: #82B32A !important;\r\n  transition: color 0.15s !important;\r\n}\r\n\r\n#app-chat-container a:hover {\r\n  color: #95cc30 !important;\r\n}\r\n\r\n/* Chat container states */\r\n#app-chat-container.maximized {\r\n  position: fixed;\r\n  z-index: 1010;\r\n}\r\n\r\n#app-chat-container:not(.visible-chat):not(.hidden-chat):not(.maximized):not(.floating-chat) {\r\n  display: none;\r\n  opacity: 0;\r\n}\r\n\r\n#app-chat-container.visible-chat {\r\n  transform: translateY(0) !important;\r\n}\r\n\r\n#app-chat-container.hidden-chat {\r\n  opacity: 1;\r\n  transform: translateY(calc(100% - 25px)) !important;\r\n}\r\n\r\n#app-chat-container.floating-chat {\r\n  border-radius: 0.4em !important;\r\n}\r\n\r\n/* Responsive styles */\r\n@media (max-width: 780px) {\r\n  #app-chat-container .chat-wrapper {\r\n    width: 100% !important;\r\n    border-right: none !important;\r\n  }\r\n}\r\n\r\n@media screen and (max-width: 768px),\r\n(hover: none),\r\n(pointer: coarse) {\r\n  body {\r\n    background-color: #1e1e1e !important;\r\n  }\r\n\r\n  #app-chat-container {\r\n    height: 100% !important;\r\n    width: 100vw !important;\r\n    min-height: 100% !important;\r\n    min-width: 100vw !important;\r\n    border: none !important;\r\n    border-radius: 0 !important;\r\n    overflow: hidden !important;\r\n  }\r\n\r\n  /* Hide non-essential elements on touch devices */\r\n  #app-chat-container .resize-handle,\r\n  #app-chat-container .font-size-control,\r\n  #app-chat-container .header-button,\r\n  #app-chat-container #send-button,\r\n  /* Unnecessary elements of klavogonki page */\r\n  .userpanel,\r\n  #userpanel-dummy,\r\n  #reformal_tab,\r\n  .ownbanner-back,\r\n  .feedback,\r\n  .bar,\r\n  #content,\r\n  #head,\r\n  #index,\r\n  #footer,\r\n  #google_esf {\r\n    display: none !important;\r\n  }\r\n}\r\n\r\n@media screen and (max-width: 367px) {\r\n  #app-chat-container .video-container {\r\n    transform-origin: left !important;\r\n    transform: scale(0.9) !important;\r\n  }\r\n}\r\n\r\n@media screen and (max-width: 350px) {\r\n  #app-chat-container .video-container {\r\n    transform-origin: left !important;\r\n    transform: scale(0.8) !important;\r\n  }\r\n}\r\n\r\n/* Font size control */\r\n#app-chat-container .font-size-control {\r\n  position: absolute !important;\r\n  top: 0 !important;\r\n  left: 0 !important;\r\n  display: flex !important;\r\n  align-items: center !important;\r\n  justify-content: center !important;\r\n  height: 25px !important;\r\n  padding: 0 10px !important;\r\n  gap: 5px !important;\r\n  z-index: 6 !important;\r\n}\r\n\r\n#app-chat-container .font-size-label {\r\n  color: #deb887 !important;\r\n  cursor: default !important;\r\n  user-select: none !important;\r\n}\r\n\r\n#app-chat-container .font-size-label.small {\r\n  font-size: 0.8em !important;\r\n}\r\n\r\n#app-chat-container .font-size-label.large {\r\n  font-size: 1.2em !important;\r\n}\r\n\r\n#app-chat-container .font-size-slider {\r\n  width: 80px !important;\r\n  height: 4px !important;\r\n  -webkit-appearance: none !important;\r\n  appearance: none !important;\r\n  background: #333 !important;\r\n  outline: none !important;\r\n  border-radius: 2px !important;\r\n  transition: opacity 0.2s !important;\r\n}\r\n\r\n#app-chat-container .font-size-slider::-webkit-slider-thumb {\r\n  -webkit-appearance: none !important;\r\n  appearance: none !important;\r\n  width: 10px !important;\r\n  height: 10px !important;\r\n  border-radius: 50% !important;\r\n  background: #deb887 !important;\r\n  cursor: pointer !important;\r\n}\r\n\r\n#app-chat-container .font-size-slider::-moz-range-thumb {\r\n  width: 10px !important;\r\n  height: 10px !important;\r\n  border-radius: 50% !important;\r\n  background: #deb887 !important;\r\n  cursor: pointer !important;\r\n  border: none !important;\r\n}\r\n\r\n/* Resize handles */\r\n#app-chat-container .resize-handle {\r\n  position: absolute !important;\r\n  background: transparent !important;\r\n  z-index: 1000 !important;\r\n}\r\n\r\n#app-chat-container .resize-handle.top {\r\n  top: -3px !important;\r\n  left: 0 !important;\r\n  right: 0 !important;\r\n  height: 6px !important;\r\n  cursor: ns-resize !important;\r\n}\r\n\r\n#app-chat-container .resize-handle.left {\r\n  left: -3px !important;\r\n  top: 0 !important;\r\n  bottom: 0 !important;\r\n  width: 6px !important;\r\n  cursor: ew-resize !important;\r\n}\r\n\r\n#app-chat-container .resize-handle.right {\r\n  right: -3px !important;\r\n  top: 0 !important;\r\n  bottom: 0 !important;\r\n  width: 6px !important;\r\n  cursor: ew-resize !important;\r\n}\r\n\r\n/* Chat wrapper: two-column layout */\r\n#app-chat-container .chat-wrapper {\r\n  display: flex !important;\r\n  flex-direction: row !important;\r\n  flex: 1 !important;\r\n  min-width: var(--min-chat-width) !important;\r\n  overflow: hidden !important;\r\n}\r\n\r\n/* Left column: messages & input */\r\n#app-chat-container .chat-content {\r\n  margin-top: 25px !important;\r\n  background: #1e1e1e !important;\r\n  display: flex !important;\r\n  flex-direction: column !important;\r\n  flex: 1 !important;\r\n  overflow: hidden !important;\r\n}\r\n\r\n/* Scrollable container settings */\r\n#app-chat-container .messages-panel {\r\n  flex: 1 !important;\r\n  overflow-y: auto !important;\r\n  overflow-x: hidden !important;\r\n  padding: 1em !important;\r\n  display: flex !important;\r\n  flex-direction: column !important;\r\n  gap: 0.2em !important;\r\n  scrollbar-width: thin !important;\r\n  scrollbar-color: #333 #1e1e1e !important;\r\n}\r\n\r\n/* Custom scrollbar styling for WebKit browsers */\r\n#app-chat-container .messages-panel::-webkit-scrollbar {\r\n  width: 8px !important;\r\n}\r\n\r\n#app-chat-container .messages-panel::-webkit-scrollbar-thumb {\r\n  background-color: #333 !important;\r\n}\r\n\r\n#app-chat-container .messages-panel::-webkit-scrollbar-thumb:hover {\r\n  background-color: #444 !important;\r\n}\r\n\r\n#app-chat-container .messages-panel::-webkit-scrollbar-track {\r\n  background-color: #1e1e1e !important;\r\n}\r\n\r\n/* Input container at bottom */\r\n#app-chat-container .input-container {\r\n  display: flex !important;\r\n  align-items: center !important;\r\n  padding: 1em !important;\r\n  gap: 0.5em !important;\r\n  border-top: 1px solid #333 !important;\r\n}\r\n\r\n#app-chat-container #message-input {\r\n  outline: none !important;\r\n  flex: 1 !important;\r\n  background: #2a2a2a !important;\r\n  color: #deb887 !important;\r\n  padding: 0.5em !important;\r\n  border-radius: var(--border-radius) !important;\r\n  min-width: 0 !important;\r\n  border: none !important;\r\n  position: relative;\r\n  font-family: inherit !important;\r\n  /* Remove any fixed font-size to properly inherit from container */\r\n  line-height: normal !important;\r\n  transition: font-size 0.2s ease !important;\r\n}\r\n\r\n#app-chat-container .length-field-popup {\r\n  position: absolute !important;\r\n  display: flex !important;\r\n  font-size: 12px !important;\r\n  font-weight: bold !important;\r\n  font-family: \"Montserrat\", Iansui, sans-serif !important;\r\n  bottom: 60px !important;\r\n  transition: left 100ms ease-out !important;\r\n  height: 20px !important;\r\n  align-items: center !important;\r\n  justify-content: center !important;\r\n  opacity: 0;\r\n  border: none !important;\r\n}\r\n\r\n.bounce-in {\r\n  animation: bounceIn 500ms forwards;\r\n}\r\n\r\n@keyframes bounceIn {\r\n  0% {\r\n    transform: translateY(0);\r\n    opacity: 0;\r\n  }\r\n\r\n  50% {\r\n    transform: translateY(-10px);\r\n    opacity: 1;\r\n  }\r\n\r\n  100% {\r\n    transform: translateY(0);\r\n    opacity: 1;\r\n  }\r\n}\r\n\r\n.bounce-out {\r\n  animation: bounceOut 500ms forwards;\r\n}\r\n\r\n@keyframes bounceOut {\r\n  0% {\r\n    transform: translateY(0);\r\n    opacity: 1;\r\n  }\r\n\r\n  50% {\r\n    transform: translateY(-10px);\r\n    opacity: 1;\r\n  }\r\n\r\n  100% {\r\n    transform: translateY(0);\r\n    opacity: 0;\r\n  }\r\n}\r\n\r\n#app-chat-container #message-input.private-mode {\r\n  background-color: #ff6b6b38 !important;\r\n  color: #ff6b6b !important;\r\n  caret-color: #ff6b6b !important;\r\n}\r\n\r\n#app-chat-container #message-input.private-mode::placeholder {\r\n  color: #ff6b6b99;\r\n}\r\n\r\n#app-chat-container #messages-panel.private-mode::after {\r\n  content: \"🔒\";\r\n  position: absolute;\r\n  right: 5px;\r\n  top: 5px;\r\n  font-size: 10px;\r\n  opacity: 0.5;\r\n}\r\n\r\n/* Right column: user list container */\r\n#app-chat-container .user-list-container {\r\n  margin-top: 25px;\r\n  width: var(--user-list-width) !important;\r\n  min-width: 180px !important;\r\n  max-width: var(--user-list-width) !important;\r\n  overflow-y: auto !important;\r\n  overflow-x: hidden !important;\r\n  padding: 1em !important;\r\n  background: #1e1e1e !important;\r\n  border-left: 1px solid #333 !important;\r\n}\r\n\r\n.reveal-userlist-btn {\r\n  position: absolute !important;\r\n  top: 50% !important;\r\n  right: -1px !important;\r\n  transform: translateY(-50%) !important;\r\n  z-index: 1000 !important;\r\n  display: flex !important;\r\n  align-items: center !important;\r\n  justify-content: center !important;\r\n  padding: 0.4em !important;\r\n  background: #222 !important;\r\n  font-size: 18px !important;\r\n  font-family: var(--emoji-font), sans-serif !important;\r\n  font-weight: bold !important;\r\n  border: 1px solid #333 !important;\r\n  border-radius: 0.4em 0 0 0.4em !important;\r\n  cursor: pointer !important;\r\n  transition: background 0.2s ease, opacity 0.2s ease !important;\r\n}\r\n\r\n/* Hover effect */\r\n.reveal-userlist-btn:hover {\r\n  background: #333 !important;\r\n}\r\n\r\n/* When user list is hidden */\r\n.hidden-userlist {\r\n  display: flex !important;\r\n}\r\n\r\n/* When user list is shown */\r\n.shown-userlist {\r\n  display: none !important;\r\n}\r\n\r\n/* Message styles */\r\n#app-chat-container .message {\r\n  padding: 0.2em 0.4em !important;\r\n  display: flex;\r\n  flex-direction: row;\r\n  border-radius: var(--border-radius) !important;\r\n  width: fit-content !important;\r\n  max-width: 100% !important;\r\n  word-break: break-word !important;\r\n}\r\n\r\n#app-chat-container .message-text .emoji-adjuster {\r\n  font-family: var(--emoji-font), sans-serif !important;\r\n  font-size: 1.5em !important;\r\n  margin: 0 0.1em !important;\r\n  display: inline-flex !important;\r\n}\r\n\r\n#app-chat-container .message-text .mention {\r\n  display: inline-flex !important;\r\n  font-family: Montserrat !important;\r\n  font-weight: 500 !important;\r\n}\r\n\r\n#app-chat-container .message.system {\r\n  background-color: #ffa50020 !important;\r\n  border: 1px solid #ffa60030 !important;\r\n  border-left: 3px solid #ffa500 !important;\r\n}\r\n\r\n#app-chat-container .message.system .time {\r\n  margin-right: unset !important;\r\n  color: #ffa50060 !important;\r\n}\r\n\r\n#app-chat-container .message.system .username {\r\n  display: none !important;\r\n}\r\n\r\n#app-chat-container .message.system .message-text {\r\n  color: #ffa500 !important;\r\n}\r\n\r\n#app-chat-container .message.sent {\r\n  background: #00ff0020 !important;\r\n  border: 1px solid #00ff0030 !important;\r\n  border-left: 3px solid #00ff00 !important;\r\n}\r\n\r\n#app-chat-container .message.sent .time {\r\n  color: #00ff0060 !important;\r\n}\r\n\r\n#app-chat-container .message.sent .username,\r\n#app-chat-container .message.sent .message-text {\r\n  color: #00ff00 !important;\r\n}\r\n\r\n#app-chat-container .message.received {\r\n  background-color: #ff4d4d20 !important;\r\n  border: 1px solid #ff4d4d30 !important;\r\n  border-left: 3px solid #ff4d4d !important;\r\n}\r\n\r\n#app-chat-container .message.received .time {\r\n  color: #ff4d4d60 !important;\r\n}\r\n\r\n#app-chat-container .message.received .username,\r\n#app-chat-container .message.received .message-text {\r\n  color: #ff4d4d !important;\r\n}\r\n\r\n/* Private message styling */\r\n.message.private-message {\r\n  background-color: #ffdcdc20;\r\n  border-left: 3px solid #ff6b6b50;\r\n}\r\n\r\n.message.private-message .message-text {\r\n  color: #ff6b6b !important;\r\n}\r\n\r\n.message.private-message:not(.sent) {\r\n  animation: privateMessagePulse 2s ease-in-out 1;\r\n}\r\n\r\n/* Sent private message styling */\r\n.message.private-message.sent {\r\n  background: #293e2938 !important;\r\n  border: 1px solid #293e2959 !important;\r\n}\r\n\r\n/* Received private message styling */\r\n.message.private-message.received {\r\n  background-color: #1e1e1e26 !important;\r\n  border: 1px solid #33333359 !important;\r\n}\r\n\r\n/* Add a subtle animation for received private messages */\r\n@keyframes privateMessagePulse {\r\n  0% {\r\n    background-color: #ffdcdc26;\r\n  }\r\n\r\n  50% {\r\n    background-color: #ffdcdc4d;\r\n  }\r\n\r\n  100% {\r\n    background-color: #ffdcdc26;\r\n  }\r\n}\r\n\r\n#app-chat-container .message-info {\r\n  margin-right: 1em !important;\r\n  white-space: nowrap !important;\r\n}\r\n\r\n/* Ensure time and username elements maintain their relative sizes */\r\n#app-chat-container .message-info .time {\r\n  font-size: 0.9em !important;\r\n  margin-right: 1em !important;\r\n  color: #666 !important;\r\n}\r\n\r\n#app-chat-container .username {\r\n  font-size: 1em !important;\r\n}\r\n\r\n/* Add to existing style.css content */\r\n#app-chat-container .username,\r\n#app-chat-container .time {\r\n  cursor: pointer !important;\r\n  transition: opacity 0.2s ease !important;\r\n}\r\n\r\n#app-chat-container .username:hover,\r\n#app-chat-container .time:hover {\r\n  opacity: 0.7 !important;\r\n}\r\n\r\n#app-chat-container .message-info .time {\r\n  margin-right: 1em !important;\r\n  font-size: 0.9em !important;\r\n  color: #666 !important;\r\n}\r\n\r\n/* User list item styles */\r\n#app-chat-container .user-item {\r\n  display: flex !important;\r\n  align-items: center !important;\r\n  padding: 0.2em !important;\r\n  margin-bottom: 0.2em !important;\r\n  border-radius: var(--border-radius) !important;\r\n  max-width: 100% !important;\r\n  text-overflow: ellipsis !important;\r\n}\r\n\r\n#app-chat-container .user-avatar {\r\n  display: flex !important;\r\n  justify-content: center !important;\r\n  align-items: center !important;\r\n  width: 24px !important;\r\n  height: 24px !important;\r\n  font-size: 18px !important;\r\n  border-radius: 0.1em !important;\r\n  margin-right: 1em !important;\r\n  text-align: center !important;\r\n  line-height: 24px !important;\r\n  flex-shrink: 0 !important;\r\n}\r\n\r\n#app-chat-container .user-avatar.image-avatar {\r\n  cursor: pointer !important;\r\n  transform-origin: left !important;\r\n  transition: transform 0.15s ease-out !important;\r\n}\r\n\r\n#app-chat-container .user-avatar.image-avatar:hover {\r\n  transform: scale(2) !important;\r\n}\r\n\r\n#app-chat-container .user-avatar.svg-avatar {\r\n  font-family: var(--emoji-font), sans-serif !important;\r\n}\r\n\r\n#app-chat-container .user-info {\r\n  flex: 1 !important;\r\n  min-width: 0 !important;\r\n  overflow: hidden !important;\r\n  text-overflow: ellipsis !important;\r\n  white-space: nowrap !important;\r\n}\r\n\r\n#app-chat-container .user-meta {\r\n  cursor: default;\r\n  font-size: 0.8em !important;\r\n  color: #b0b0b0 !important;\r\n  overflow: hidden !important;\r\n  text-overflow: ellipsis !important;\r\n  white-space: nowrap !important;\r\n}\r\n\r\n#app-chat-container .game-link {\r\n  color: #deb887 !important;\r\n  text-decoration: none !important;\r\n  transition: color 0.15s !important;\r\n}\r\n\r\n#app-chat-container .role-moderator {\r\n  color: #ff7e7e !important;\r\n}\r\n\r\n#app-chat-container .role-participant {\r\n  color: #7ed4ff !important;\r\n}\r\n\r\n#app-chat-container .role-visitor {\r\n  color: #b0b0b0 !important;\r\n}\r\n\r\n#app-chat-container .role {\r\n  font-family: var(--emoji-font), sans-serif !important;\r\n}\r\n\r\n#app-chat-container .role.participant {\r\n  filter: brightness(0.6) !important;\r\n}\r\n\r\n#app-chat-container .role.visitor {\r\n  filter: brightness(0.8) !important;\r\n}\r\n\r\n#app-chat-container .traffic-icon {\r\n  /* Set font-family to var(--emoji-font) or fallback to system emoji */\r\n  font-family: var(--emoji-font), sans-serif !important;\r\n}\r\n\r\n/* Header toggle button */\r\n#app-chat-container .header-button {\r\n  cursor: pointer !important;\r\n  position: absolute !important;\r\n  top: 0 !important;\r\n  width: 25px !important;\r\n  height: 25px !important;\r\n  z-index: 5 !important;\r\n}\r\n\r\n#app-chat-container .filled-button {\r\n  border: none !important;\r\n  outline: none !important;\r\n  background-color: transparent !important;\r\n  transition: all 0.15s ease-out;\r\n}\r\n\r\n#app-chat-container .filled-button:hover {\r\n  filter: brightness(1.2) !important;\r\n}\r\n\r\n#app-chat-container .emoji-trigger,\r\n#app-chat-container .private-mode-exit,\r\n#app-chat-container .send-button {\r\n  font-family: var(--emoji-font), sans-serif !important;\r\n  height: 28px !important;\r\n  width: 28px !important;\r\n  font-size: 1.5em !important;\r\n  display: flex !important;\r\n  align-items: center !important;\r\n  justify-content: center !important;\r\n  line-height: 1 !important;\r\n  cursor: pointer !important;\r\n  background: transparent !important;\r\n  border: none !important;\r\n  outline: none !important;\r\n  margin: 0 !important;\r\n  padding: 0 !important;\r\n}\r\n\r\n.chat-toggle-button {\r\n  right: 0 !important;\r\n}\r\n\r\n.chat-maximize-button {\r\n  right: 25px !important;\r\n}\r\n\r\n.chat-help-button {\r\n  color: #82B32A !important;\r\n  right: 50px !important;\r\n}\r\n\r\n/* Drag area for floating the chat */\r\n#app-chat-container .chat-drag-area {\r\n  border-radius: 0.4em 0.4em 0 0 !important;\r\n  position: absolute !important;\r\n  top: 0 !important;\r\n  left: 0 !important;\r\n  right: 0 !important;\r\n  height: 25px !important;\r\n  cursor: move !important;\r\n  background-color: #161616cc !important;\r\n}\r\n\r\n/* Dimming background style */\r\n.dimming-element {\r\n  position: fixed;\r\n  top: 0;\r\n  left: 0;\r\n  width: 100%;\r\n  height: 100%;\r\n  background-color: #00000080;\r\n  z-index: 1010 !important;\r\n  opacity: 0;\r\n  transition: opacity 0.3s ease;\r\n}\r\n\r\n/* convertImageLinksToImage */\r\n.clickable-thumbnail {\r\n  opacity: 1;\r\n  transition: opacity 0.15s ease-in-out;\r\n  border: none;\r\n  max-width: 150px !important;\r\n  max-height: 150px !important;\r\n  cursor: pointer;\r\n  background-color: transparent;\r\n  padding: 2px;\r\n  margin: 6px;\r\n  overflow-y: auto;\r\n}\r\n\r\n.clickable-thumbnail img {\r\n  border-radius: var(--border-radius) !important;\r\n  object-fit: contain;\r\n  height: 100%;\r\n  width: 100%;\r\n}\r\n\r\n.clickable-thumbnail:hover {\r\n  opacity: 0.8;\r\n}\r\n\r\n.scaled-thumbnail {\r\n  top: 50%;\r\n  left: 50%;\r\n  transform-origin: center center;\r\n  transform: translate(-50%, -50%) scale(1);\r\n  position: fixed;\r\n  opacity: 0;\r\n  z-index: 1015 !important;\r\n  transform-origin: center center;\r\n  max-height: 90vh;\r\n  max-width: 90vw;\r\n  cursor: pointer;\r\n  border-radius: 0.6em !important;\r\n  box-shadow: 0 4px 6px #0000004d, 0 1px 3px #00000047 !important;\r\n}\r\n\r\n/* convertVideoLinksToPlayer */\r\n.video-wrapper {\r\n  display: flex;\r\n  flex-direction: column;\r\n}\r\n\r\n.video-wrapper .processed-video {\r\n  margin-bottom: 0.6em !important;\r\n}\r\n\r\n.video-container {\r\n  border-radius: 0.4em !important;\r\n  display: flex;\r\n  border: none;\r\n  height: 200px !important;\r\n  width: 356px !important;\r\n}`, \"\"]);\n// Exports\n/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);\n\n\n//# sourceURL=webpack://tampermonkey-script/./src/styles/style.css?./node_modules/css-loader/dist/cjs.js");

/***/ }),

/***/ "./node_modules/css-loader/dist/runtime/api.js":
/*!*****************************************************!*\
  !*** ./node_modules/css-loader/dist/runtime/api.js ***!
  \*****************************************************/
/***/ ((module) => {

eval("\r\n\r\n/*\r\n  MIT License http://www.opensource.org/licenses/mit-license.php\r\n  Author Tobias Koppers @sokra\r\n*/\r\nmodule.exports = function (cssWithMappingToString) {\r\n  var list = [];\r\n\r\n  // return the list of modules as css string\r\n  list.toString = function toString() {\r\n    return this.map(function (item) {\r\n      var content = \"\";\r\n      var needLayer = typeof item[5] !== \"undefined\";\r\n      if (item[4]) {\r\n        content += \"@supports (\".concat(item[4], \") {\");\r\n      }\r\n      if (item[2]) {\r\n        content += \"@media \".concat(item[2], \" {\");\r\n      }\r\n      if (needLayer) {\r\n        content += \"@layer\".concat(item[5].length > 0 ? \" \".concat(item[5]) : \"\", \" {\");\r\n      }\r\n      content += cssWithMappingToString(item);\r\n      if (needLayer) {\r\n        content += \"}\";\r\n      }\r\n      if (item[2]) {\r\n        content += \"}\";\r\n      }\r\n      if (item[4]) {\r\n        content += \"}\";\r\n      }\r\n      return content;\r\n    }).join(\"\");\r\n  };\r\n\r\n  // import a list of modules into the list\r\n  list.i = function i(modules, media, dedupe, supports, layer) {\r\n    if (typeof modules === \"string\") {\r\n      modules = [[null, modules, undefined]];\r\n    }\r\n    var alreadyImportedModules = {};\r\n    if (dedupe) {\r\n      for (var k = 0; k < this.length; k++) {\r\n        var id = this[k][0];\r\n        if (id != null) {\r\n          alreadyImportedModules[id] = true;\r\n        }\r\n      }\r\n    }\r\n    for (var _k = 0; _k < modules.length; _k++) {\r\n      var item = [].concat(modules[_k]);\r\n      if (dedupe && alreadyImportedModules[item[0]]) {\r\n        continue;\r\n      }\r\n      if (typeof layer !== \"undefined\") {\r\n        if (typeof item[5] === \"undefined\") {\r\n          item[5] = layer;\r\n        } else {\r\n          item[1] = \"@layer\".concat(item[5].length > 0 ? \" \".concat(item[5]) : \"\", \" {\").concat(item[1], \"}\");\r\n          item[5] = layer;\r\n        }\r\n      }\r\n      if (media) {\r\n        if (!item[2]) {\r\n          item[2] = media;\r\n        } else {\r\n          item[1] = \"@media \".concat(item[2], \" {\").concat(item[1], \"}\");\r\n          item[2] = media;\r\n        }\r\n      }\r\n      if (supports) {\r\n        if (!item[4]) {\r\n          item[4] = \"\".concat(supports);\r\n        } else {\r\n          item[1] = \"@supports (\".concat(item[4], \") {\").concat(item[1], \"}\");\r\n          item[4] = supports;\r\n        }\r\n      }\r\n      list.push(item);\r\n    }\r\n  };\r\n  return list;\r\n};\n\n//# sourceURL=webpack://tampermonkey-script/./node_modules/css-loader/dist/runtime/api.js?");

/***/ }),

/***/ "./node_modules/css-loader/dist/runtime/noSourceMaps.js":
/*!**************************************************************!*\
  !*** ./node_modules/css-loader/dist/runtime/noSourceMaps.js ***!
  \**************************************************************/
/***/ ((module) => {

eval("\r\n\r\nmodule.exports = function (i) {\r\n  return i[1];\r\n};\n\n//# sourceURL=webpack://tampermonkey-script/./node_modules/css-loader/dist/runtime/noSourceMaps.js?");

/***/ }),

/***/ "./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js":
/*!****************************************************************************!*\
  !*** ./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js ***!
  \****************************************************************************/
/***/ ((module) => {

eval("\r\n\r\nvar stylesInDOM = [];\r\nfunction getIndexByIdentifier(identifier) {\r\n  var result = -1;\r\n  for (var i = 0; i < stylesInDOM.length; i++) {\r\n    if (stylesInDOM[i].identifier === identifier) {\r\n      result = i;\r\n      break;\r\n    }\r\n  }\r\n  return result;\r\n}\r\nfunction modulesToDom(list, options) {\r\n  var idCountMap = {};\r\n  var identifiers = [];\r\n  for (var i = 0; i < list.length; i++) {\r\n    var item = list[i];\r\n    var id = options.base ? item[0] + options.base : item[0];\r\n    var count = idCountMap[id] || 0;\r\n    var identifier = \"\".concat(id, \" \").concat(count);\r\n    idCountMap[id] = count + 1;\r\n    var indexByIdentifier = getIndexByIdentifier(identifier);\r\n    var obj = {\r\n      css: item[1],\r\n      media: item[2],\r\n      sourceMap: item[3],\r\n      supports: item[4],\r\n      layer: item[5]\r\n    };\r\n    if (indexByIdentifier !== -1) {\r\n      stylesInDOM[indexByIdentifier].references++;\r\n      stylesInDOM[indexByIdentifier].updater(obj);\r\n    } else {\r\n      var updater = addElementStyle(obj, options);\r\n      options.byIndex = i;\r\n      stylesInDOM.splice(i, 0, {\r\n        identifier: identifier,\r\n        updater: updater,\r\n        references: 1\r\n      });\r\n    }\r\n    identifiers.push(identifier);\r\n  }\r\n  return identifiers;\r\n}\r\nfunction addElementStyle(obj, options) {\r\n  var api = options.domAPI(options);\r\n  api.update(obj);\r\n  var updater = function updater(newObj) {\r\n    if (newObj) {\r\n      if (newObj.css === obj.css && newObj.media === obj.media && newObj.sourceMap === obj.sourceMap && newObj.supports === obj.supports && newObj.layer === obj.layer) {\r\n        return;\r\n      }\r\n      api.update(obj = newObj);\r\n    } else {\r\n      api.remove();\r\n    }\r\n  };\r\n  return updater;\r\n}\r\nmodule.exports = function (list, options) {\r\n  options = options || {};\r\n  list = list || [];\r\n  var lastIdentifiers = modulesToDom(list, options);\r\n  return function update(newList) {\r\n    newList = newList || [];\r\n    for (var i = 0; i < lastIdentifiers.length; i++) {\r\n      var identifier = lastIdentifiers[i];\r\n      var index = getIndexByIdentifier(identifier);\r\n      stylesInDOM[index].references--;\r\n    }\r\n    var newLastIdentifiers = modulesToDom(newList, options);\r\n    for (var _i = 0; _i < lastIdentifiers.length; _i++) {\r\n      var _identifier = lastIdentifiers[_i];\r\n      var _index = getIndexByIdentifier(_identifier);\r\n      if (stylesInDOM[_index].references === 0) {\r\n        stylesInDOM[_index].updater();\r\n        stylesInDOM.splice(_index, 1);\r\n      }\r\n    }\r\n    lastIdentifiers = newLastIdentifiers;\r\n  };\r\n};\n\n//# sourceURL=webpack://tampermonkey-script/./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js?");

/***/ }),

/***/ "./node_modules/style-loader/dist/runtime/insertBySelector.js":
/*!********************************************************************!*\
  !*** ./node_modules/style-loader/dist/runtime/insertBySelector.js ***!
  \********************************************************************/
/***/ ((module) => {

eval("\r\n\r\nvar memo = {};\r\n\r\n/* istanbul ignore next  */\r\nfunction getTarget(target) {\r\n  if (typeof memo[target] === \"undefined\") {\r\n    var styleTarget = document.querySelector(target);\r\n\r\n    // Special case to return head of iframe instead of iframe itself\r\n    if (window.HTMLIFrameElement && styleTarget instanceof window.HTMLIFrameElement) {\r\n      try {\r\n        // This will throw an exception if access to iframe is blocked\r\n        // due to cross-origin restrictions\r\n        styleTarget = styleTarget.contentDocument.head;\r\n      } catch (e) {\r\n        // istanbul ignore next\r\n        styleTarget = null;\r\n      }\r\n    }\r\n    memo[target] = styleTarget;\r\n  }\r\n  return memo[target];\r\n}\r\n\r\n/* istanbul ignore next  */\r\nfunction insertBySelector(insert, style) {\r\n  var target = getTarget(insert);\r\n  if (!target) {\r\n    throw new Error(\"Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.\");\r\n  }\r\n  target.appendChild(style);\r\n}\r\nmodule.exports = insertBySelector;\n\n//# sourceURL=webpack://tampermonkey-script/./node_modules/style-loader/dist/runtime/insertBySelector.js?");

/***/ }),

/***/ "./node_modules/style-loader/dist/runtime/insertStyleElement.js":
/*!**********************************************************************!*\
  !*** ./node_modules/style-loader/dist/runtime/insertStyleElement.js ***!
  \**********************************************************************/
/***/ ((module) => {

eval("\r\n\r\n/* istanbul ignore next  */\r\nfunction insertStyleElement(options) {\r\n  var element = document.createElement(\"style\");\r\n  options.setAttributes(element, options.attributes);\r\n  options.insert(element, options.options);\r\n  return element;\r\n}\r\nmodule.exports = insertStyleElement;\n\n//# sourceURL=webpack://tampermonkey-script/./node_modules/style-loader/dist/runtime/insertStyleElement.js?");

/***/ }),

/***/ "./node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js":
/*!**********************************************************************************!*\
  !*** ./node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js ***!
  \**********************************************************************************/
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {

eval("\r\n\r\n/* istanbul ignore next  */\r\nfunction setAttributesWithoutAttributes(styleElement) {\r\n  var nonce =  true ? __webpack_require__.nc : 0;\r\n  if (nonce) {\r\n    styleElement.setAttribute(\"nonce\", nonce);\r\n  }\r\n}\r\nmodule.exports = setAttributesWithoutAttributes;\n\n//# sourceURL=webpack://tampermonkey-script/./node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js?");

/***/ }),

/***/ "./node_modules/style-loader/dist/runtime/styleDomAPI.js":
/*!***************************************************************!*\
  !*** ./node_modules/style-loader/dist/runtime/styleDomAPI.js ***!
  \***************************************************************/
/***/ ((module) => {

eval("\r\n\r\n/* istanbul ignore next  */\r\nfunction apply(styleElement, options, obj) {\r\n  var css = \"\";\r\n  if (obj.supports) {\r\n    css += \"@supports (\".concat(obj.supports, \") {\");\r\n  }\r\n  if (obj.media) {\r\n    css += \"@media \".concat(obj.media, \" {\");\r\n  }\r\n  var needLayer = typeof obj.layer !== \"undefined\";\r\n  if (needLayer) {\r\n    css += \"@layer\".concat(obj.layer.length > 0 ? \" \".concat(obj.layer) : \"\", \" {\");\r\n  }\r\n  css += obj.css;\r\n  if (needLayer) {\r\n    css += \"}\";\r\n  }\r\n  if (obj.media) {\r\n    css += \"}\";\r\n  }\r\n  if (obj.supports) {\r\n    css += \"}\";\r\n  }\r\n  var sourceMap = obj.sourceMap;\r\n  if (sourceMap && typeof btoa !== \"undefined\") {\r\n    css += \"\\n/*# sourceMappingURL=data:application/json;base64,\".concat(btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap)))), \" */\");\r\n  }\r\n\r\n  // For old IE\r\n  /* istanbul ignore if  */\r\n  options.styleTagTransform(css, styleElement, options.options);\r\n}\r\nfunction removeStyleElement(styleElement) {\r\n  // istanbul ignore if\r\n  if (styleElement.parentNode === null) {\r\n    return false;\r\n  }\r\n  styleElement.parentNode.removeChild(styleElement);\r\n}\r\n\r\n/* istanbul ignore next  */\r\nfunction domAPI(options) {\r\n  if (typeof document === \"undefined\") {\r\n    return {\r\n      update: function update() {},\r\n      remove: function remove() {}\r\n    };\r\n  }\r\n  var styleElement = options.insertStyleElement(options);\r\n  return {\r\n    update: function update(obj) {\r\n      apply(styleElement, options, obj);\r\n    },\r\n    remove: function remove() {\r\n      removeStyleElement(styleElement);\r\n    }\r\n  };\r\n}\r\nmodule.exports = domAPI;\n\n//# sourceURL=webpack://tampermonkey-script/./node_modules/style-loader/dist/runtime/styleDomAPI.js?");

/***/ }),

/***/ "./node_modules/style-loader/dist/runtime/styleTagTransform.js":
/*!*********************************************************************!*\
  !*** ./node_modules/style-loader/dist/runtime/styleTagTransform.js ***!
  \*********************************************************************/
/***/ ((module) => {

eval("\r\n\r\n/* istanbul ignore next  */\r\nfunction styleTagTransform(css, styleElement) {\r\n  if (styleElement.styleSheet) {\r\n    styleElement.styleSheet.cssText = css;\r\n  } else {\r\n    while (styleElement.firstChild) {\r\n      styleElement.removeChild(styleElement.firstChild);\r\n    }\r\n    styleElement.appendChild(document.createTextNode(css));\r\n  }\r\n}\r\nmodule.exports = styleTagTransform;\n\n//# sourceURL=webpack://tampermonkey-script/./node_modules/style-loader/dist/runtime/styleTagTransform.js?");

/***/ }),

/***/ "./src/animations.js":
/*!***************************!*\
  !*** ./src/animations.js ***!
  \***************************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   addJumpEffect: () => (/* binding */ addJumpEffect),\n/* harmony export */   addPulseEffect: () => (/* binding */ addPulseEffect),\n/* harmony export */   addShakeEffect: () => (/* binding */ addShakeEffect)\n/* harmony export */ });\nfunction addJumpEffect(element, initialTranslateX = 0, initialTranslateY = 0) {\r\n  // Define keyframes with specified percentages, scale effect, and calc for Y translation\r\n  const keyframes = [\r\n    { transform: `translate(${initialTranslateX}%, calc(${initialTranslateY}%)) scale(1)` }, // 0%\r\n    { transform: `translate(${initialTranslateX}%, calc(${initialTranslateY}% - 60%)) scale(1.1)` }, // 20%\r\n    { transform: `translate(${initialTranslateX}%, calc(${initialTranslateY}% + 15%)) scale(1)` }, // 40%\r\n    { transform: `translate(${initialTranslateX}%, calc(${initialTranslateY}% - 20%)) scale(1.05)` }, // 60%\r\n    { transform: `translate(${initialTranslateX}%, calc(${initialTranslateY}% + 8%)) scale(1)` }, // 75%\r\n    { transform: `translate(${initialTranslateX}%, calc(${initialTranslateY}% - 10%)) scale(1.05)` }, // 85%\r\n    { transform: `translate(${initialTranslateX}%, calc(${initialTranslateY}% + 4%)) scale(1)` }, // 92%\r\n    { transform: `translate(${initialTranslateX}%, calc(${initialTranslateY}%)) scale(1)` } // 100%\r\n  ];\r\n\r\n  // Animation options\r\n  const options = {\r\n    duration: 500, // Total animation duration in ms (adjust as needed)\r\n    easing: 'ease', // Smooth easing between keyframes\r\n    iterations: 1 // Play once\r\n  };\r\n\r\n  // Start the animation\r\n  const animation = element.animate(keyframes, options);\r\n\r\n  // Optional: Return a promise that resolves when animation completes\r\n  return animation.finished;\r\n}\r\n\r\n// Helper function to add shake effect\r\nfunction addShakeEffect(element) {\r\n  element.classList.add('shake-effect');\r\n  setTimeout(() => {\r\n    element.classList.remove('shake-effect');\r\n  }, 500);\r\n}\r\n\r\nfunction addPulseEffect(element) {\r\n  element.classList.add('pulse-effect');\r\n  setTimeout(() => {\r\n    element.classList.remove('pulse-effect');\r\n  }, 500);\r\n}\n\n//# sourceURL=webpack://tampermonkey-script/./src/animations.js?");

/***/ }),

/***/ "./src/auth.js":
/*!*********************!*\
  !*** ./src/auth.js ***!
  \*********************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   getAuthData: () => (/* binding */ getAuthData)\n/* harmony export */ });\nfunction getAuthData() {\r\n  // Only proceed if on the gamelist page\r\n  if (!window.location.href.startsWith('https://klavogonki.ru/gamelist/')) return;\r\n\r\n  try {\r\n    // Find the script containing PageData\r\n    const script = Array.from(document.scripts).find(s => s.text.includes('PageData'));\r\n    if (!script) throw new Error('PageData script not found');\r\n\r\n    // Extract and parse the JSON-like data inside the script\r\n    const rawData = script.text.match(/\\.constant\\('PageData', ([\\s\\S]*?})\\)/)[1];\r\n    const parsedData = JSON.parse(\r\n      rawData\r\n        .replace(/(\\w+):/g, '\"$1\":') // Fix object keys\r\n        .replace(/'/g, '\"') // Fix string quotes\r\n    );\r\n\r\n    const username = `${parsedData.chatParams.user.id}#${parsedData.chatParams.user.login}`;\r\n    const password = parsedData.chatParams.pass;\r\n\r\n    // Redirect only if it hasn’t happened before\r\n    if (!localStorage.getItem('klavoauth')) {\r\n      // Always update klavoauth with the latest data\r\n      localStorage.setItem('klavoauth', JSON.stringify({ username, password }));\r\n      setTimeout(() => {\r\n        window.location.href = 'https://klavogonki.ru';\r\n      }, 500);\r\n    }\r\n  } catch (e) {\r\n    console.error('Auth error:', e);\r\n    localStorage.removeItem('klavoauth');\r\n    // Optional: Reset redirect flag to allow retry after error\r\n    // localStorage.removeItem('klavoauth_redirected');\r\n    alert(`Auth failed: ${e.message}\\nPlease refresh the page.`);\r\n  }\r\n}\n\n//# sourceURL=webpack://tampermonkey-script/./src/auth.js?");

/***/ }),

/***/ "./src/chatFeatures.js":
/*!*****************************!*\
  !*** ./src/chatFeatures.js ***!
  \*****************************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   addChatToggleFeature: () => (/* binding */ addChatToggleFeature),\n/* harmony export */   toggleChatVisibility: () => (/* binding */ toggleChatVisibility)\n/* harmony export */ });\n/* harmony import */ var _icons_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./icons.js */ \"./src/icons.js\");\n/* harmony import */ var _helpers_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./helpers.js */ \"./src/helpers.js\");\n/* harmony import */ var _chatUI_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./chatUI.js */ \"./src/chatUI.js\");\n\r\n\r\n\r\n\r\nfunction toggleChatVisibility() {\r\n  const chatContainer = document.getElementById('app-chat-container');\r\n  const toggleButton = document.querySelector('.chat-toggle-button');\r\n  if (!chatContainer) return;\r\n\r\n  // Prevent toggling visibility if chat is maximized\r\n  if (chatContainer.classList.contains('maximized')) {\r\n    (0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.showChatAlert)('Chat is currently maximized', {\r\n      type: 'warning',\r\n      duration: 1000\r\n    });\r\n    return;\r\n  }\r\n\r\n  const chatState = JSON.parse(localStorage.getItem('chatState')) || {};\r\n  const isFloating = chatState.floating || false;\r\n\r\n  if (isFloating) {\r\n    const isBecomingVisible = chatContainer.style.opacity === '0';\r\n    chatContainer.style.opacity = isBecomingVisible ? '1' : '0';\r\n    setTimeout(() => {\r\n      chatContainer.style.display = isBecomingVisible ? 'flex' : 'none';\r\n      toggleButton.innerHTML = isBecomingVisible ? _icons_js__WEBPACK_IMPORTED_MODULE_0__.closeSVG : _icons_js__WEBPACK_IMPORTED_MODULE_0__.openSVG;\r\n      (0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.saveChatState)({\r\n        ...chatState,\r\n        isVisible: isBecomingVisible\r\n      });\r\n      if (isBecomingVisible) {\r\n        (0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.focusTextInput)(); // Focus input after chat becomes visible\r\n      }\r\n    }, 300);\r\n  } else {\r\n    const isCurrentlyVisible = chatContainer.classList.contains('visible-chat');\r\n    const isBecomingVisible = !isCurrentlyVisible;\r\n    chatContainer.classList.remove('visible-chat', 'hidden-chat');\r\n    chatContainer.classList.add(isBecomingVisible ? 'visible-chat' : 'hidden-chat');\r\n    toggleButton.innerHTML = isBecomingVisible ? _icons_js__WEBPACK_IMPORTED_MODULE_0__.closeSVG : _icons_js__WEBPACK_IMPORTED_MODULE_0__.openSVG;\r\n    (0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.saveChatState)({\r\n      ...chatState,\r\n      isVisible: isBecomingVisible\r\n    });\r\n    if (isBecomingVisible) {\r\n      (0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.focusTextInput)(); // Focus input immediately when shown\r\n    }\r\n  }\r\n}\r\n\r\nfunction addChatToggleFeature() {\r\n  const chatContainer = document.getElementById('app-chat-container');\r\n  const closeButton = document.getElementById('chat-close-btn');\r\n  const draggableHeader = document.getElementById('chat-header');\r\n  if (!chatContainer) return;\r\n\r\n  // Restore initial visibility from saved state\r\n  const chatState = JSON.parse(localStorage.getItem('chatState')) || {};\r\n  const isFloating = chatState.floating || false;\r\n  const isVisible = chatState.isVisible !== false;\r\n\r\n  if (isFloating) {\r\n    chatContainer.style.display = isVisible ? 'flex' : 'none';\r\n    chatContainer.style.opacity = isVisible ? '1' : '0';\r\n  } else {\r\n    chatContainer.classList.remove('visible-chat', 'hidden-chat');\r\n    chatContainer.classList.add(isVisible ? 'visible-chat' : 'hidden-chat');\r\n  }\r\n\r\n  // Keyboard shortcuts\r\n  document.addEventListener('keydown', (e) => {\r\n    if (e.ctrlKey && e.shiftKey && e.code === 'Space') {\r\n      e.preventDefault();\r\n      (0,_chatUI_js__WEBPACK_IMPORTED_MODULE_2__.toggleChatMaximize)();\r\n    } else if (e.ctrlKey && e.code === 'Space') {\r\n      e.preventDefault();\r\n      toggleChatVisibility();\r\n    }\r\n  });\r\n\r\n  if (closeButton) {\r\n    closeButton.addEventListener('click', toggleChatVisibility);\r\n  }\r\n\r\n  if (draggableHeader) {\r\n    draggableHeader.addEventListener('dblclick', toggleChatVisibility);\r\n  }\r\n}\n\n//# sourceURL=webpack://tampermonkey-script/./src/chatFeatures.js?");

/***/ }),

/***/ "./src/chatUI.js":
/*!***********************!*\
  !*** ./src/chatUI.js ***!
  \***********************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   createChatUI: () => (/* binding */ createChatUI),\n/* harmony export */   toggleChatMaximize: () => (/* binding */ toggleChatMaximize)\n/* harmony export */ });\n/* harmony import */ var _chatFeatures_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./chatFeatures.js */ \"./src/chatFeatures.js\");\n/* harmony import */ var _helpers_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./helpers.js */ \"./src/helpers.js\");\n/* harmony import */ var _icons_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./icons.js */ \"./src/icons.js\");\n/* harmony import */ var _components_helpPanel_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./components/helpPanel.js */ \"./src/components/helpPanel.js\");\n/* harmony import */ var _components_emojiPanel_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./components/emojiPanel.js */ \"./src/components/emojiPanel.js\");\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\nfunction createChatUI() {\r\n  const chatContainer = document.createElement('div');\r\n  chatContainer.id = 'app-chat-container';\r\n  // Add resize handles\r\n  ['top', 'left', 'right'].forEach(type => {\r\n    const handle = document.createElement('div');\r\n    handle.className = `resize-handle ${type}`;\r\n    chatContainer.appendChild(handle);\r\n  });\r\n  // Chat wrapper for content and user list\r\n  const chatWrapper = document.createElement('div');\r\n  chatWrapper.className = 'chat-wrapper';\r\n  // Left side: messages panel and input\r\n  const chatContent = document.createElement('div');\r\n  chatContent.className = 'chat-content';\r\n  const messagesPanel = document.createElement('div');\r\n  messagesPanel.id = 'messages-panel';\r\n  messagesPanel.className = 'messages-panel';\r\n  const inputContainer = document.createElement('div');\r\n  inputContainer.className = 'input-container';\r\n  // Create emoji button\r\n  const emojiButton = document.createElement('button');\r\n  emojiButton.className = 'emoji-trigger filled-button';\r\n  emojiButton.innerHTML = \"🙂\";\r\n  emojiButton.classList.add('emoji-button');\r\n  emojiButton.title = 'Open emoji picker';\r\n\r\n  // Add these event listeners to change the emoji on hover\r\n  emojiButton.addEventListener('mouseover', () => {\r\n    emojiButton.innerHTML = \"🙃\";\r\n  });\r\n\r\n  emojiButton.addEventListener('mouseout', () => {\r\n    emojiButton.innerHTML = \"🙂\";\r\n  });\r\n\r\n  // Setup random emoji appearance with range (10min - 30min)\r\n  (0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.setupRandomEmojiAttention)(emojiButton, (0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.getRandomInterval)(600000, 1800000));\r\n\r\n  // Setup emoji panel toggle functionality\r\n  let emojiPanelInstance = null;\r\n  emojiButton.addEventListener('click', (e) => {\r\n    e.stopPropagation();\r\n    if (!emojiPanelInstance || !document.querySelector('.emoji-panel')) {\r\n      emojiPanelInstance = new _components_emojiPanel_js__WEBPACK_IMPORTED_MODULE_4__.EmojiPanel({\r\n        container: messagesPanel,\r\n        position: 'bottom',\r\n        emojiButton: emojiButton,\r\n        onEmojiSelect: (emoji) => {\r\n          const messageInput = document.getElementById('message-input');\r\n          if (messageInput) {\r\n            const cursorPos = messageInput.selectionStart;\r\n            const textBefore = messageInput.value.substring(0, cursorPos);\r\n            const textAfter = messageInput.value.substring(messageInput.selectionEnd);\r\n            messageInput.value = textBefore + emoji + textAfter;\r\n            const newCursorPos = cursorPos + emoji.length;\r\n            messageInput.setSelectionRange(newCursorPos, newCursorPos);\r\n            messageInput.focus();\r\n          }\r\n        },\r\n        onDestroy: () => {\r\n          emojiButton.title = 'Open emoji picker';\r\n          emojiPanelInstance = null;\r\n        }\r\n      });\r\n      emojiPanelInstance.init();\r\n      emojiButton.title = 'Close emoji picker';\r\n    } else {\r\n      emojiPanelInstance.destroy();\r\n    }\r\n  });\r\n  // Create message input\r\n  const messageInput = document.createElement('input');\r\n  messageInput.type = 'text';\r\n  messageInput.id = 'message-input';\r\n  messageInput.maxLength = 300;\r\n  // Create send button\r\n  const sendButton = document.createElement('button');\r\n  sendButton.id = 'send-button';\r\n  sendButton.className = 'filled-button send-button';\r\n  sendButton.innerHTML = _icons_js__WEBPACK_IMPORTED_MODULE_2__.sendSVG;\r\n  // Append elements in order\r\n  inputContainer.appendChild(emojiButton);\r\n  inputContainer.appendChild(messageInput);\r\n  inputContainer.appendChild(sendButton);\r\n  chatContent.appendChild(messagesPanel);\r\n  chatContent.appendChild(inputContainer);\r\n  // Right side: user list\r\n  const userListContainer = document.createElement('div');\r\n  userListContainer.className = 'user-list-container';\r\n  const userList = document.createElement('div');\r\n  userList.id = 'user-list';\r\n  userListContainer.appendChild(userList);\r\n  chatWrapper.appendChild(chatContent);\r\n  chatWrapper.appendChild(userListContainer);\r\n  chatContainer.appendChild(chatWrapper);\r\n  // Maximize button\r\n  const maximizeButton = document.createElement('button');\r\n  maximizeButton.className = 'filled-button header-button chat-maximize-button';\r\n  maximizeButton.innerHTML = _icons_js__WEBPACK_IMPORTED_MODULE_2__.expandSVG;\r\n  maximizeButton.addEventListener('click', toggleChatMaximize);\r\n  chatContainer.appendChild(maximizeButton);\r\n  // Help button next to maximize button\r\n  const helpButton = document.createElement('button');\r\n  helpButton.className = 'filled-button header-button chat-help-button';\r\n  helpButton.innerHTML = _icons_js__WEBPACK_IMPORTED_MODULE_2__.helpSVG; // Replace with desired icon if available\r\n  helpButton.title = 'Show chat help';\r\n  // Declare a variable to track the help panel instance.\r\n  let helpPanelInstance = null;\r\n\r\n  helpButton.addEventListener('click', (e) => {\r\n    console.log(\"Help button clicked.\");\r\n    e.stopPropagation();\r\n\r\n    // If a help panel exists, remove it and exit.\r\n    if (helpPanelInstance && document.querySelector('.help-panel')) {\r\n      helpPanelInstance.remove();\r\n      helpButton.title = 'Show chat help';\r\n      helpPanelInstance = null;\r\n      (0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.showChatAlert)('Help panel has been closed.', {\r\n        type: 'warning',\r\n        duration: 2000\r\n      });\r\n      return;\r\n    }\r\n\r\n    // Otherwise, create a new help panel.\r\n    console.log(\"Help panel does not exist. Creating help panel...\");\r\n    helpPanelInstance = new _components_helpPanel_js__WEBPACK_IMPORTED_MODULE_3__.HelpPanel({\r\n      helpButton: helpButton,\r\n      onDestroy: () => {\r\n        helpButton.title = 'Show chat help';\r\n        helpPanelInstance = null;\r\n      }\r\n    });\r\n    helpPanelInstance.init();\r\n    helpPanelInstance.show();\r\n    (0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.showChatAlert)('Help panel has been opened. Press \"?\" or \"ESC\" key, or click outside to close.', {\r\n      type: 'success',\r\n      duration: 2000\r\n    });\r\n    helpButton.title = 'Hide chat help';\r\n  });\r\n\r\n  chatContainer.appendChild(helpButton);\r\n  // Toggle visibility button\r\n  const toggleButton = document.createElement('button');\r\n  toggleButton.className = 'filled-button header-button chat-toggle-button';\r\n  toggleButton.innerHTML = _icons_js__WEBPACK_IMPORTED_MODULE_2__.closeSVG;\r\n  toggleButton.addEventListener('click', _chatFeatures_js__WEBPACK_IMPORTED_MODULE_0__.toggleChatVisibility);\r\n  chatContainer.appendChild(toggleButton);\r\n  // Draggable top area\r\n  const topArea = document.createElement('div');\r\n  topArea.className = 'chat-drag-area';\r\n  topArea.addEventListener('dblclick', _chatFeatures_js__WEBPACK_IMPORTED_MODULE_0__.toggleChatVisibility);\r\n  chatContainer.appendChild(topArea);\r\n  document.body.appendChild(chatContainer);\r\n  // Restore chat state and settings\r\n  (0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.restoreChatState)();\r\n  (0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.createFontSizeControl)();\r\n  (0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.restoreFontSize)();\r\n  // Force scroll to bottom once after chat creation\r\n  requestAnimationFrame(() => {\r\n    const messagesPanel = document.getElementById('messages-panel');\r\n    const messageInput = document.getElementById('message-input');\r\n    if (messagesPanel && messageInput) {\r\n      messagesPanel.scrollTop = messagesPanel.scrollHeight;\r\n      // Pass the input element and messages panel into the helper functions.\r\n      (0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.createLengthPopup)(messagesPanel);\r\n      (0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.initChatLengthPopupEvents)(messageInput);\r\n    }\r\n  });\r\n}\r\n\r\nlet originalChatState = null;\r\n\r\nfunction toggleChatMaximize() {\r\n  const chat = document.getElementById('app-chat-container');\r\n  const maximizeButton = document.querySelector('.chat-maximize-button');\r\n  if (!chat) return;\r\n  if (!chat.classList.contains('maximized')) {\r\n    const hasVisibilityClass = !chat.classList.contains('visible-chat') && !chat.classList.contains('hidden-chat');\r\n    originalChatState = (0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.getChatState)();\r\n    const calculateHeight = () => `${Math.floor(window.innerHeight * 0.9)}px`;\r\n    chat.style.cssText = `\r\n      width: 100vw !important;\r\n      height: ${calculateHeight()} !important;\r\n      max-width: 100vw !important;\r\n      min-width: 100vw !important;\r\n      position: fixed !important;\r\n      bottom: 0 !important;\r\n      left: 0 !important;\r\n      right: 0 !important;\r\n      top: auto !important;\r\n      margin: 0 !important;\r\n      transform: none !important;\r\n    `;\r\n    if (hasVisibilityClass) {\r\n      chat.classList.remove('visible-chat', 'hidden-chat');\r\n    }\r\n    chat.classList.add('maximized');\r\n    maximizeButton.classList.add('maximized');\r\n    maximizeButton.innerHTML = _icons_js__WEBPACK_IMPORTED_MODULE_2__.collapseSVG;\r\n    const resizeHandler = () => {\r\n      chat.style.height = calculateHeight();\r\n      chat.style.bottom = '0';\r\n      chat.style.top = 'auto';\r\n    };\r\n    window.addEventListener('resize', resizeHandler);\r\n    chat.maximizeResizeHandler = resizeHandler;\r\n    (0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.handleElementsBehavior)();\r\n    (0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.focusTextInput)();\r\n    (0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.restoreFontSize)();\r\n  } else {\r\n    const container = document.getElementById('messages-panel');\r\n    const distanceFromBottom = container.scrollHeight - container.scrollTop - container.clientHeight;\r\n    const shouldScrollToBottom = distanceFromBottom <= 300;\r\n    if (chat.maximizeResizeHandler) {\r\n      window.removeEventListener('resize', chat.maximizeResizeHandler);\r\n      delete chat.maximizeResizeHandler;\r\n    }\r\n    if (originalChatState) {\r\n      chat.style.width = `${originalChatState.width}px`;\r\n      chat.style.height = `${originalChatState.height}px`;\r\n      chat.style.left = `${originalChatState.left}px`;\r\n      chat.style.maxWidth = '';\r\n      chat.style.minWidth = '';\r\n      chat.style.position = 'fixed';\r\n      chat.style.right = '';\r\n      chat.style.margin = '';\r\n      chat.style.transform = '';\r\n      chat.style.top = 'auto';\r\n      if (originalChatState.floating) {\r\n        const viewportHeight = window.innerHeight;\r\n        const proposedTop = originalChatState.top;\r\n        if (proposedTop + originalChatState.height <= viewportHeight) {\r\n          chat.style.top = `${proposedTop}px`;\r\n        } else {\r\n          chat.style.bottom = '0';\r\n          chat.style.top = 'auto';\r\n        }\r\n      } else {\r\n        chat.style.bottom = '0';\r\n        chat.style.top = '';\r\n      }\r\n      const currentState = (0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.getChatState)();\r\n      const newState = {\r\n        ...currentState,\r\n        width: originalChatState.width,\r\n        height: originalChatState.height,\r\n        left: originalChatState.left,\r\n        top: originalChatState.top,\r\n        floating: originalChatState.floating,\r\n        isVisible: originalChatState.isVisible,\r\n      };\r\n      (0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.saveChatState)(newState);\r\n    }\r\n    chat.classList.remove('maximized');\r\n    maximizeButton.classList.remove('maximized');\r\n    maximizeButton.innerHTML = _icons_js__WEBPACK_IMPORTED_MODULE_2__.expandSVG;\r\n    requestAnimationFrame(() => {\r\n      (0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.handleElementsBehavior)();\r\n      if (shouldScrollToBottom) {\r\n        container.scrollTop = container.scrollHeight;\r\n      }\r\n      (0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.focusTextInput)();\r\n      (0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.restoreFontSize)();\r\n    });\r\n  }\r\n}\n\n//# sourceURL=webpack://tampermonkey-script/./src/chatUI.js?");

/***/ }),

/***/ "./src/components/emojiPanel.js":
/*!**************************************!*\
  !*** ./src/components/emojiPanel.js ***!
  \**************************************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   EmojiPanel: () => (/* binding */ EmojiPanel)\n/* harmony export */ });\n/* harmony import */ var _data_emojiData_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../data/emojiData.js */ \"./src/data/emojiData.js\");\n/* harmony import */ var _helpers_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../helpers.js */ \"./src/helpers.js\");\n\r\n\r\n\r\n// Create a single global shortcut handler\r\nconst setupGlobalEmojiShortcut = (() => {\r\n  let isSetup = false;\r\n\r\n  return function () {\r\n    if (!isSetup) {\r\n      document.addEventListener('keydown', (e) => {\r\n        if (e.ctrlKey && e.code === 'Semicolon') {\r\n          e.preventDefault();\r\n          if (!document.querySelector('.emoji-panel')) {\r\n            // Initialize or show the emoji panel with a callback to insert emoji\r\n            new EmojiPanel({\r\n              onEmojiSelect: (emoji) => {\r\n                const input = document.getElementById('message-input');\r\n                if (input) {\r\n                  input.value += emoji;\r\n                  // Optionally, dispatch an input event if your application needs it\r\n                  input.dispatchEvent(new Event('input', { bubbles: true }));\r\n                  input.focus(); // Add this line to restore focus\r\n                }\r\n              }\r\n            }).show();\r\n          }\r\n        }\r\n      });\r\n      isSetup = true;\r\n    }\r\n  };\r\n})();\r\n\r\n// Call this function immediately to set up the shortcut\r\nsetupGlobalEmojiShortcut();\r\n\r\n\r\nclass EmojiPanel {\r\n  static instance = null;\r\n\r\n  constructor(options = {}) {\r\n    if (EmojiPanel.instance) {\r\n      return EmojiPanel.instance;\r\n    }\r\n    EmojiPanel.instance = this;\r\n    this.options = {\r\n      onEmojiSelect: options.onEmojiSelect || (() => { }),\r\n      container: options.container || document.getElementById('messages-panel') || document.body,\r\n      position: options.position || 'bottom',\r\n      onDestroy: options.onDestroy,\r\n      emojiButton: options.emojiButton,\r\n    };\r\n\r\n    // DOM elements\r\n    this.container = null;\r\n    this.searchInput = null;\r\n    this.emojiContainer = null;\r\n    this.categoryNav = null;\r\n    this.infoPanel = null;\r\n    this.infoIcon = null;\r\n    this.infoKeywords = null;\r\n    this.languageSelect = null;\r\n\r\n    // Category definitions with icons\r\n    this.categories = {\r\n      recent: { icon: '🕒' },\r\n      smileys: { icon: '😊' },\r\n      nature: { icon: '🦊' },\r\n      food: { icon: '🍔' },\r\n      activities: { icon: '⚽' },\r\n      travel: { icon: '✈️' },\r\n      objects: { icon: '💡' },\r\n      symbols: { icon: '💕' },\r\n      flags: { icon: '🎌' }\r\n    };\r\n\r\n    // Localized category names\r\n    this.categoryLabels = {\r\n      en: {\r\n        recent: 'Recently Used',\r\n        smileys: 'Smileys & People',\r\n        nature: 'Animals & Nature',\r\n        food: 'Food & Drink',\r\n        activities: 'Activities',\r\n        travel: 'Travel & Places',\r\n        objects: 'Objects',\r\n        symbols: 'Symbols',\r\n        flags: 'Flags'\r\n      },\r\n      ru: {\r\n        recent: 'Недавно использованные',\r\n        smileys: 'Смайлы и люди',\r\n        nature: 'Животные и природа',\r\n        food: 'Еда и напитки',\r\n        activities: 'Активности',\r\n        travel: 'Путешествия и места',\r\n        objects: 'Объекты',\r\n        symbols: 'Символы',\r\n        flags: 'Флаги'\r\n      }\r\n    };\r\n\r\n    // UI Labels for static text elements\r\n    this.uiLabels = {\r\n      en: {\r\n        searchResults: 'Search Results'\r\n      },\r\n      ru: {\r\n        searchResults: 'Результаты поиска'\r\n      }\r\n    };\r\n\r\n    // Emoji data and keywords\r\n    this.emojiData = _data_emojiData_js__WEBPACK_IMPORTED_MODULE_0__.emojiData;\r\n    this.emojiKeywords = _data_emojiData_js__WEBPACK_IMPORTED_MODULE_0__.emojiKeywords;\r\n\r\n    // Recently used emojis (loaded from localStorage)\r\n    this.recentEmojis = this.loadRecentEmojis();\r\n\r\n    // Infinite scroll settings\r\n    this.batchSize = 50;\r\n    this.loadedIndices = {};\r\n    this.categorySections = {};\r\n\r\n    // Retrieve current language from localStorage (default to 'en')\r\n    this.currentLanguage = localStorage.getItem('emojiPanelLanguage') || 'en';\r\n  }\r\n\r\n  /** Initialize the emoji panel */\r\n  init() {\r\n    this.createPanel();\r\n    this.bindEvents();\r\n    this.loadAllEmojis();\r\n    return this;\r\n  }\r\n\r\n  /** Create the HTML structure of the emoji panel */\r\n  createPanel() {\r\n    if (document.querySelector('.emoji-panel')) {\r\n      return;\r\n    }\r\n    this.container = document.createElement('div');\r\n    this.container.className = 'emoji-panel';\r\n\r\n    // Search container\r\n    const searchContainer = document.createElement('div');\r\n    searchContainer.className = 'emoji-search-container';\r\n    this.searchInput = document.createElement('input');\r\n    this.searchInput.type = 'search';\r\n    this.searchInput.className = 'emoji-search';\r\n    searchContainer.appendChild(this.searchInput);\r\n\r\n    // Category navigation\r\n    this.categoryNav = document.createElement('div');\r\n    this.categoryNav.className = 'emoji-categories';\r\n    Object.entries(this.categories).forEach(([key, category]) => {\r\n      const categoryBtn = document.createElement('button');\r\n      categoryBtn.className = 'emoji-category-btn';\r\n      categoryBtn.dataset.category = key;\r\n      categoryBtn.innerHTML = category.icon;\r\n      categoryBtn.title = this.getLocalizedCategoryName(key);\r\n      this.categoryNav.appendChild(categoryBtn);\r\n    });\r\n\r\n    // Emoji grid container\r\n    this.emojiContainer = document.createElement('div');\r\n    this.emojiContainer.className = 'emoji-container';\r\n\r\n    // Info panel\r\n    this.infoPanel = document.createElement('div');\r\n    this.infoPanel.className = 'emoji-info-panel';\r\n    this.infoIcon = document.createElement('span');\r\n    this.infoIcon.className = 'emoji-info-icon';\r\n    this.infoKeywords = document.createElement('span');\r\n    this.infoKeywords.className = 'emoji-info-keywords';\r\n    this.infoPanel.appendChild(this.infoIcon);\r\n    this.infoPanel.appendChild(this.infoKeywords);\r\n\r\n    // Language selector\r\n    this.languageSelect = document.createElement('select');\r\n    this.languageSelect.className = 'emoji-language-select';\r\n    this.languageSelect.innerHTML = `\r\n      <option value=\"en\">EN</option>\r\n      <option value=\"ru\">RU</option>\r\n    `;\r\n    this.languageSelect.value = this.currentLanguage;\r\n\r\n    // Footer to hold info panel and language selector\r\n    const footer = document.createElement('div');\r\n    footer.className = 'emoji-footer';\r\n    footer.appendChild(this.infoPanel);\r\n    footer.appendChild(this.languageSelect);\r\n\r\n    // Assemble the panel\r\n    this.container.appendChild(searchContainer);\r\n    this.container.appendChild(this.categoryNav);\r\n    this.container.appendChild(this.emojiContainer);\r\n    this.container.appendChild(footer);\r\n    this.options.container.appendChild(this.container);\r\n    // Fade in the panel\r\n    (0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.adjustVisibility)(this.container, 'show', '1');\r\n    this.searchInput.focus();\r\n  }\r\n\r\n  /** Load initial batch of emojis for all categories */\r\n  loadAllEmojis() {\r\n    if (!this.emojiContainer) return; // Exit early if container is missing\r\n    this.emojiContainer.innerHTML = '';\r\n    this.loadedIndices = {};\r\n    this.categorySections = {};\r\n    Object.keys(this.categories).forEach(category => {\r\n      const section = document.createElement('div');\r\n      section.className = 'emoji-category-section';\r\n      section.id = `emoji-section-${category}`;\r\n      const header = document.createElement('div');\r\n      header.className = 'emoji-category-header';\r\n      header.textContent = this.getLocalizedCategoryName(category);\r\n      section.appendChild(header);\r\n      const emojiList = document.createElement('div');\r\n      emojiList.className = 'emoji-list';\r\n      section.appendChild(emojiList);\r\n      this.emojiContainer.appendChild(section);\r\n      this.loadedIndices[category] = 0;\r\n      this.categorySections[category] = { section, emojiList, header };\r\n      this.loadMoreEmojisForCategory(category);\r\n    });\r\n  }\r\n\r\n  /** Load more emojis for a specific category */\r\n  loadMoreEmojisForCategory(category) {\r\n    const data = category === 'recent' ? this.recentEmojis : (this.emojiData[category] || []);\r\n    const start = this.loadedIndices[category];\r\n    const batch = data.slice(start, start + this.batchSize);\r\n    if (!batch.length) return;\r\n    const container = this.categorySections[category].emojiList;\r\n    batch.forEach(emoji => {\r\n      const button = document.createElement('button');\r\n      button.className = 'emoji-btn';\r\n      button.textContent = emoji;\r\n      button.addEventListener('mouseenter', () => this.updateInfoPanel(emoji));\r\n      button.addEventListener('mouseleave', () => this.clearInfoPanel());\r\n      button.addEventListener('click', (e) => {\r\n        e.stopPropagation();\r\n        if (e.shiftKey && category === 'recent') {\r\n          e.preventDefault();\r\n          this.removeFromRecent(emoji);\r\n        } else {\r\n          this.addToRecent(emoji);\r\n          this.options.onEmojiSelect(emoji);\r\n          if (!e.ctrlKey) {\r\n            this.destroy();\r\n          } else {\r\n            this.searchInput.focus();\r\n          }\r\n        }\r\n      });\r\n      container.appendChild(button);\r\n    });\r\n    this.loadedIndices[category] += batch.length;\r\n  }\r\n\r\n  /** Search for emojis based on a search term */\r\n  searchEmojis(searchTerm) {\r\n    if (!this.emojiContainer) return; // Exit early if container is missing\r\n    this.emojiContainer.innerHTML = '';\r\n    const section = document.createElement('div');\r\n    section.className = 'emoji-category-section';\r\n    const header = document.createElement('div');\r\n    header.className = 'emoji-category-header';\r\n    header.textContent = this.uiLabels[this.currentLanguage].searchResults;\r\n    section.appendChild(header);\r\n    const emojiList = document.createElement('div');\r\n    emojiList.className = 'emoji-list';\r\n    section.appendChild(emojiList);\r\n    const results = [];\r\n    Object.keys(this.emojiData).forEach(category => {\r\n      const emojis = this.emojiData[category] || [];\r\n      emojis.forEach(emoji => {\r\n        const keywordsObj = this.emojiKeywords[emoji] || {};\r\n        const allKeywords = Object.values(keywordsObj).flat();\r\n        if (\r\n          emoji.includes(searchTerm) ||\r\n          allKeywords.some(keyword => keyword.toLowerCase().includes(searchTerm))\r\n        ) {\r\n          results.push(emoji);\r\n        }\r\n      });\r\n    });\r\n    results.forEach(emoji => {\r\n      const button = document.createElement('button');\r\n      button.className = 'emoji-btn';\r\n      button.textContent = emoji;\r\n      button.addEventListener('mouseenter', () => this.updateInfoPanel(emoji));\r\n      button.addEventListener('mouseleave', () => this.clearInfoPanel());\r\n      button.addEventListener('click', (e) => {\r\n        e.stopPropagation();\r\n        this.addToRecent(emoji);\r\n        this.options.onEmojiSelect(emoji);\r\n        if (!e.ctrlKey) {\r\n          this.destroy();\r\n        } else {\r\n          this.searchInput.focus();\r\n        }\r\n      });\r\n      emojiList.appendChild(button);\r\n    });\r\n    this.emojiContainer.appendChild(section);\r\n  }\r\n\r\n  /** Bind event listeners for the emoji panel */\r\n  bindEvents() {\r\n    // Close panel on clicking outside\r\n    this._documentClickHandler = (e) => {\r\n      if (!this.container.contains(e.target)) {\r\n        this.destroy();\r\n      }\r\n    };\r\n    document.addEventListener('click', this._documentClickHandler);\r\n\r\n    // Close panel on Escape key\r\n    this._emojiKeydownHandler = (e) => {\r\n      if (e.key === 'Escape') {\r\n        this.destroy();\r\n      }\r\n    };\r\n    document.addEventListener('keydown', this._emojiKeydownHandler);\r\n\r\n    // Handle 'q' key for closing panel\r\n    this._qKeydownHandler = (e) => {\r\n      if (e.code === 'KeyQ') {\r\n        if (document.activeElement === this.searchInput) {\r\n          const now = Date.now();\r\n          if (this._lastQPressTime && (now - this._lastQPressTime < 500)) {\r\n            e.preventDefault();\r\n            this.destroy();\r\n            this._lastQPressTime = 0;\r\n          } else {\r\n            this._lastQPressTime = now;\r\n          }\r\n        } else {\r\n          e.preventDefault();\r\n          this.destroy();\r\n        }\r\n      }\r\n    };\r\n    document.addEventListener('keydown', this._qKeydownHandler);\r\n\r\n    // Update view on search input change\r\n    this.searchInput.addEventListener('input', (e) => {\r\n      const searchTerm = e.target.value.trim().toLowerCase();\r\n      if (searchTerm) {\r\n        this.searchEmojis(searchTerm);\r\n        this.emojiContainer.classList.add('search-active');\r\n      } else {\r\n        this.loadAllEmojis();\r\n        this.emojiContainer.classList.remove('search-active');\r\n      }\r\n    });\r\n\r\n    // Handle Enter key in search input\r\n    this.searchInput.addEventListener('keydown', (e) => {\r\n      if (e.key === 'Enter') {\r\n        e.preventDefault();\r\n\r\n        // Ensure both elements exist before proceeding\r\n        if (!this.searchInput || !this.emojiContainer) return;\r\n\r\n        if (this.emojiContainer.classList.contains('search-active')) {\r\n          const firstEmojiBtn = this.emojiContainer?.querySelector('.emoji-btn');\r\n\r\n          if (firstEmojiBtn) {\r\n            const clickEvent = new MouseEvent('click', {\r\n              bubbles: true,\r\n              cancelable: true,\r\n              ctrlKey: e.ctrlKey\r\n            });\r\n            firstEmojiBtn.dispatchEvent(clickEvent);\r\n\r\n            // Ensure emojiContainer still exists before modifying it\r\n            if (this.emojiContainer) {\r\n              this.emojiContainer.classList.remove('search-active');\r\n              this.loadAllEmojis(); // Only if emojiContainer is still valid\r\n            }\r\n\r\n            // Ensure the search input exists before modifying it\r\n            if (this.searchInput) {\r\n              this.searchInput.value = '';\r\n            }\r\n\r\n            if (!e.ctrlKey) {\r\n              this.destroy();\r\n            } else if (this.searchInput) {\r\n              this.searchInput.focus();\r\n            }\r\n          }\r\n        }\r\n      }\r\n    });\r\n\r\n    // Category navigation clicks\r\n    this.categoryNav.addEventListener('click', (e) => {\r\n      const btn = e.target.closest('.emoji-category-btn');\r\n      if (btn) {\r\n        const category = btn.dataset.category;\r\n        const section = document.getElementById(`emoji-section-${category}`);\r\n        if (section && this.emojiContainer) {\r\n          // Add smooth scrolling behavior\r\n          this.emojiContainer.style.scrollBehavior = 'smooth';\r\n\r\n          // Instead of using scrollIntoView, manually set the scrollTop\r\n          // of the emoji container to the offsetTop of the section\r\n          this.emojiContainer.scrollTop = section.offsetTop - this.emojiContainer.offsetTop;\r\n\r\n          // Prevent default behavior to avoid any parent scrolling\r\n          e.preventDefault();\r\n          e.stopPropagation();\r\n        }\r\n      }\r\n    });\r\n\r\n    // Infinite scroll handler\r\n    this.emojiContainer.addEventListener('scroll', () => this.handleScroll());\r\n\r\n    // Language change handler\r\n    this.languageSelect.addEventListener('change', (e) => {\r\n      this.currentLanguage = e.target.value;\r\n      localStorage.setItem('emojiPanelLanguage', this.currentLanguage);\r\n      const currentEmoji = this.infoIcon.textContent;\r\n      if (currentEmoji) {\r\n        this.updateInfoPanel(currentEmoji);\r\n      }\r\n      this.updateCategoryLabels();\r\n      if (this.searchInput.value.trim()) {\r\n        this.searchEmojis(this.searchInput.value.trim().toLowerCase());\r\n      }\r\n    });\r\n\r\n    // Prevent closing when clicking inside panel\r\n    this.container.addEventListener('click', (e) => {\r\n      e.stopPropagation();\r\n    });\r\n  }\r\n\r\n  /** Handle scroll events for infinite scrolling and category highlighting */\r\n  handleScroll() {\r\n    Object.keys(this.categorySections).forEach(category => {\r\n      const { section } = this.categorySections[category];\r\n      const sectionRect = section.getBoundingClientRect();\r\n      const containerRect = this.emojiContainer.getBoundingClientRect();\r\n      if (sectionRect.bottom < containerRect.bottom + 100) {\r\n        this.loadMoreEmojisForCategory(category);\r\n      }\r\n    });\r\n    let activeCategory = null;\r\n    let minDistance = Infinity;\r\n    Object.keys(this.categorySections).forEach(category => {\r\n      const headerRect = this.categorySections[category].header.getBoundingClientRect();\r\n      const containerRect = this.emojiContainer.getBoundingClientRect();\r\n      const distance = Math.abs(headerRect.top - containerRect.top);\r\n      if (headerRect.top <= containerRect.top + 10 && distance < minDistance) {\r\n        minDistance = distance;\r\n        activeCategory = category;\r\n      }\r\n    });\r\n    if (activeCategory) {\r\n      this.highlightCategory(activeCategory);\r\n    }\r\n  }\r\n\r\n  /** Update category labels based on current language */\r\n  updateCategoryLabels() {\r\n    Object.keys(this.categories).forEach(category => {\r\n      const localizedName = this.getLocalizedCategoryName(category);\r\n      const btn = this.categoryNav.querySelector(`[data-category=\"${category}\"]`);\r\n      if (btn) btn.title = localizedName;\r\n      if (this.categorySections[category] && this.categorySections[category].header) {\r\n        this.categorySections[category].header.textContent = localizedName;\r\n      }\r\n    });\r\n  }\r\n\r\n  /** Update the info panel with emoji and keywords */\r\n  updateInfoPanel(emoji) {\r\n    // Check if the info panel elements exist before trying to update them\r\n    if (!this.infoIcon || !this.infoKeywords) return;\r\n\r\n    this.infoIcon.textContent = emoji;\r\n    const keywordsObj = this.emojiKeywords[emoji] || {};\r\n    const keywords = keywordsObj[this.currentLanguage] || [];\r\n    this.infoKeywords.textContent = keywords.join(', ');\r\n  }\r\n\r\n  /** Clear the info panel */\r\n  clearInfoPanel() {\r\n    // Check if the info panel elements exist before trying to clear them\r\n    if (!this.infoIcon || !this.infoKeywords) return;\r\n\r\n    this.infoIcon.textContent = '';\r\n    this.infoKeywords.textContent = '';\r\n  }\r\n\r\n  /** Add an emoji to the recent list and refresh the recent section */\r\n  addToRecent(emoji) {\r\n    this.recentEmojis = [\r\n      emoji,\r\n      ...this.recentEmojis.filter(e => e !== emoji)\r\n    ].slice(0, 25);\r\n    this.saveRecentEmojis();\r\n    if (this.categorySections['recent']) {\r\n      const recentList = this.categorySections['recent'].emojiList;\r\n      recentList.innerHTML = '';\r\n      this.loadedIndices['recent'] = 0;\r\n      this.loadMoreEmojisForCategory('recent');\r\n    }\r\n  }\r\n\r\n  /** Remove an emoji from the recent list and refresh the recent section */\r\n  removeFromRecent(emoji) {\r\n    this.recentEmojis = this.recentEmojis.filter(e => e !== emoji);\r\n    this.saveRecentEmojis();\r\n    if (this.categorySections['recent']) {\r\n      const recentList = this.categorySections['recent'].emojiList;\r\n      recentList.innerHTML = '';\r\n      this.loadedIndices['recent'] = 0;\r\n      this.loadMoreEmojisForCategory('recent');\r\n    }\r\n  }\r\n\r\n  /** Load recent emojis from localStorage */\r\n  loadRecentEmojis() {\r\n    try {\r\n      return JSON.parse(localStorage.getItem('recentEmojis')) || [];\r\n    } catch {\r\n      return [];\r\n    }\r\n  }\r\n\r\n  /** Save recent emojis to localStorage */\r\n  saveRecentEmojis() {\r\n    try {\r\n      localStorage.setItem('recentEmojis', JSON.stringify(this.recentEmojis));\r\n    } catch (e) {\r\n      console.error('Failed to save recent emojis:', e);\r\n    }\r\n  }\r\n\r\n  /** Highlight the active category in the navigation */\r\n  highlightCategory(categoryId) {\r\n    const buttons = this.categoryNav.querySelectorAll('.emoji-category-btn');\r\n    buttons.forEach(btn => {\r\n      btn.classList.toggle('active', btn.dataset.category === categoryId);\r\n    });\r\n  }\r\n\r\n  /** Show the emoji panel */\r\n  show() {\r\n    this.createPanel();\r\n    this.bindEvents();\r\n    this.loadAllEmojis();\r\n    this.searchInput.focus();\r\n  }\r\n\r\n  /** Completely remove the emoji panel from DOM and clean up */\r\n  destroy() {\r\n    document.removeEventListener('keydown', this._emojiKeydownHandler);\r\n    document.removeEventListener('keydown', this._qKeydownHandler);\r\n    document.removeEventListener('click', this._documentClickHandler);\r\n    if (this.container) {\r\n      // Fade out the panel; the helper will remove it after transition.\r\n      (0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.adjustVisibility)(this.container, 'hide', '0');\r\n    }\r\n    this.container = null;\r\n    this.searchInput = null;\r\n    this.emojiContainer = null;\r\n    this.categoryNav = null;\r\n    this.infoPanel = null;\r\n    this.infoIcon = null;\r\n    this.infoKeywords = null;\r\n    this.languageSelect = null;\r\n    EmojiPanel.instance = null;\r\n    if (this.options.emojiButton) {\r\n      this.options.emojiButton.title = 'Open emoji picker';\r\n    }\r\n    if (typeof this.options.onDestroy === 'function') {\r\n      this.options.onDestroy();\r\n    }\r\n  }\r\n\r\n  /** Toggle the visibility of the emoji panel */\r\n  toggle() {\r\n    if (document.querySelector('.emoji-panel')) {\r\n      this.destroy();\r\n    } else {\r\n      this.show();\r\n    }\r\n  }\r\n\r\n  /** Get the localized name for a category */\r\n  getLocalizedCategoryName(categoryKey) {\r\n    return this.categoryLabels[this.currentLanguage][categoryKey] || categoryKey;\r\n  }\r\n}\r\n\n\n//# sourceURL=webpack://tampermonkey-script/./src/components/emojiPanel.js?");

/***/ }),

/***/ "./src/components/helpPanel.js":
/*!*************************************!*\
  !*** ./src/components/helpPanel.js ***!
  \*************************************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   HelpPanel: () => (/* binding */ HelpPanel)\n/* harmony export */ });\n/* harmony import */ var _helpers_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../helpers.js */ \"./src/helpers.js\");\n\r\n\r\nclass HelpPanel {\r\n  constructor(options = {}) {\r\n    this.container = null;\r\n    this.options = {\r\n      container: options.container || document.body,\r\n      helpButton: options.helpButton,\r\n      onDestroy: options.onDestroy\r\n    };\r\n    // Set the class instance to this\r\n    HelpPanel.instance = this;\r\n  }\r\n\r\n  init() {\r\n    this.createPanel();\r\n    this.bindEvents();\r\n    return this;\r\n  }\r\n\r\n  createPanel() {\r\n    this.container = document.createElement('div');\r\n    this.container.className = 'help-panel';\r\n    this.content = document.createElement('div');\r\n    this.content.className = 'help-content';\r\n    this.updatePanelContent();\r\n    this.container.appendChild(this.content);\r\n    document.body.appendChild(this.container);\r\n    // Fade in the panel\r\n    (0,_helpers_js__WEBPACK_IMPORTED_MODULE_0__.adjustVisibility)(this.container, 'show', '1');\r\n  }\r\n\r\n  updatePanelContent() {\r\n    const lang = localStorage.getItem('emojiPanelLanguage') || 'en';\r\n    const helpTranslations = {\r\n      en: {\r\n        heading: \"Chat Commands & Hotkeys\",\r\n        sections: [\r\n          {\r\n            title: \"Chat Commands\",\r\n            items: [\r\n              { key: \"/help\", desc: \"Show this help panel\" },\r\n              { key: \"/me message\", desc: \"Send an action message\" },\r\n              { key: \"/pm username\", desc: \"Activate private chat mode with the specified user\" },\r\n              { key: \"/exit\", desc: \"Exit private chat mode\" }\r\n            ]\r\n          },\r\n          {\r\n            title: \"Chat Hotkeys\",\r\n            items: [\r\n              { key: \"Ctrl + Space\", desc: \"Hide/Show the chat\" },\r\n              { key: \"Shift + Ctrl + Space\", desc: \"Expand/Collapse the chat\" },\r\n              { key: \"Ctrl + Click\", desc: \"Activate private chat mode with the clicked user\" }\r\n            ]\r\n          },\r\n          {\r\n            heading: \"Emoji Panel Actions & Hotkeys\",\r\n            subSections: [\r\n              {\r\n                title: \"Emoji Panel Actions\",\r\n                items: [\r\n                  { key: \"Click an emoji\", desc: \"Insert the emoji\" },\r\n                  { key: \"Click outside panel\", desc: \"Closes the panel (emoji or help)\" }\r\n                ]\r\n              },\r\n              {\r\n                title: \"Emoji Panel Hotkeys\",\r\n                items: [\r\n                  { key: \"Ctrl + ;\", desc: \"Open the Emoji Panel\" },\r\n                  { key: \"Enter\", desc: \"Insert the emoji\" },\r\n                  { key: \"Ctrl + Enter\", desc: \"Insert the emoji keeping the panel open\" },\r\n                  { key: \"Ctrl + Click\", desc: \"Insert the emoji keeping the panel open\" },\r\n                  { key: \"Shift + Click\", desc: \"Remove emoji from recent list (in recent category)\" },\r\n                  { key: \"q\", desc: \"Hide the Emoji Panel (single press when search is not focused)\" },\r\n                  { key: \"qq\", desc: \"Hide the Emoji Panel (double press 'q' when search is focused)\" },\r\n                  { key: \"Esc\", desc: \"Close the panel (emoji or help)\" }\r\n                ]\r\n              }\r\n            ]\r\n          },\r\n          {\r\n            heading: \"Image Manipulations\",\r\n            subSections: [\r\n              {\r\n                title: \"Open/Close\",\r\n                items: [\r\n                  { key: \"(LMB) Click\", desc: \"Open the image\" },\r\n                  { key: \"Ctrl + (RMB)\", desc: \"Close the image and copy the link\" },\r\n                  { key: \"Space or ESC\", desc: \"Close the image\" }\r\n                ]\r\n              },\r\n              {\r\n                title: \"Movement and Scaling\",\r\n                items: [\r\n                  { key: \"Hold (MMB)\", desc: \"Drag the expanded image\" },\r\n                  { key: \"Scroll (MMB)\", desc: \"Zoom in/out the image\" },\r\n                  { key: \"Ctrl + (MMB)\", desc: \"Scale the image. Move the cursor up or down.\" }\r\n                ]\r\n              },\r\n              {\r\n                title: \"Navigation\",\r\n                items: [\r\n                  { key: \"Arrow keys (< >)\", desc: \"Switch between images\" },\r\n                  { key: \"(LMB), (RMB)\", desc: \"Switch between images\" }\r\n                ]\r\n              }\r\n            ]\r\n          }\r\n        ]\r\n      },\r\n      ru: {\r\n        heading: \"Команды чата и горячие клавиши\",\r\n        sections: [\r\n          {\r\n            title: \"Команды чата\",\r\n            items: [\r\n              { key: \"/help\", desc: \"Показать панель помощи\" },\r\n              { key: \"/me сообщение\", desc: \"Отправить сообщение действия\" },\r\n              { key: \"/pm username\", desc: \"Активировать приватный чат для указанного пользователя\" },\r\n              { key: \"/exit\", desc: \"Выйти из приватного чата\" }\r\n            ]\r\n          },\r\n          {\r\n            title: \"Горячие клавиши чата\",\r\n            items: [\r\n              { key: \"Ctrl + Space\", desc: \"Скрыть/Показать чат\" },\r\n              { key: \"Shift + Ctrl + Space\", desc: \"Развернуть/Свернуть чат\" },\r\n              { key: \"Ctrl + Click\", desc: \"Активировать приватный чат для выбранного пользователя\" }\r\n            ]\r\n          },\r\n          {\r\n            heading: \"Действия и горячие клавиши панели эмодзи\",\r\n            subSections: [\r\n              {\r\n                title: \"Действия панели эмодзи\",\r\n                items: [\r\n                  { key: \"Click an emoji\", desc: \"Вставить эмодзи\" },\r\n                  { key: \"Click outside panel\", desc: \"Закрыть панель (эмодзи или помощь)\" }\r\n                ]\r\n              },\r\n              {\r\n                title: \"Горячие клавиши панели эмодзи\",\r\n                items: [\r\n                  { key: \"Ctrl + ;\", desc: \"Открыть панель эмодзи\" },\r\n                  { key: \"Enter\", desc: \"Вставить эмодзи\" },\r\n                  { key: \"Ctrl + Enter\", desc: \"Вставить эмодзи, оставив панель открытой\" },\r\n                  { key: \"Ctrl + Click\", desc: \"Вставить эмодзи, оставив панель открытой\" },\r\n                  { key: \"Shift + Click\", desc: \"Удалить эмодзи из списка \\\"Недавно использованные\\\"\" },\r\n                  { key: \"q\", desc: \"Скрыть панель эмодзи (одиночный нажим, когда поиск не в фокусе)\" },\r\n                  { key: \"qq\", desc: \"Скрыть панель эмодзи (дважды нажмите 'q', когда поиск в фокусе)\" },\r\n                  { key: \"Esc\", desc: \"Закрыть (эмодзи или помощь)\" }\r\n                ]\r\n              }\r\n            ]\r\n          },\r\n          {\r\n            heading: \"Манипуляции с изображением\",\r\n            subSections: [\r\n              {\r\n                title: \"Открытие/Закрытие\",\r\n                items: [\r\n                  { key: \"(ЛКМ) Клик\", desc: \"Открыть изображение\" },\r\n                  { key: \"Ctrl + (ПКМ)\", desc: \"Закрыть изображение и скопировать ссылку\" },\r\n                  { key: \"Space или ESC\", desc: \"Закрыть изображение\" }\r\n                ]\r\n              },\r\n              {\r\n                title: \"Перемещение и масштабирование\",\r\n                items: [\r\n                  { key: \"Зажатая (СКМ)\", desc: \"Перемещайте развернутое изображение\" },\r\n                  { key: \"Прокрутка (СКМ)\", desc: \"Увеличивайте/уменьшайте изображение\" },\r\n                  { key: \"Ctrl + (СКМ)\", desc: \"Масштабируйте изображение. Курсор вверх или вниз.\" }\r\n                ]\r\n              },\r\n              {\r\n                title: \"Навигация\",\r\n                items: [\r\n                  { key: \"Стрелки (< >)\", desc: \"Переключение между изображениями\" },\r\n                  { key: \"(ЛКМ), (ПКМ)\", desc: \"Переключение между изображениями\" }\r\n                ]\r\n              }\r\n            ]\r\n          }\r\n        ]\r\n      }\r\n    };\r\n    const t = helpTranslations[lang];\r\n    let html = `<h5 class=\"help-section-header\">${t.heading}</h5>`;\r\n    t.sections.forEach(section => {\r\n      if (section.title) {\r\n        html += `<h6 class=\"help-section-subheader\">${section.title}</h6>`;\r\n      } else if (section.heading) {\r\n        html += `<h5 class=\"help-section-header\">${section.heading}</h5>`;\r\n      }\r\n      if (section.items) {\r\n        html += `<ul class=\"help-list\">`;\r\n        section.items.forEach(item => {\r\n          html += `<li class=\"help-list-item\"><strong class=\"help-hotkey\">${item.key}</strong> ${item.desc}</li>`;\r\n        });\r\n        html += `</ul>`;\r\n      }\r\n      if (section.subSections) {\r\n        section.subSections.forEach(sub => {\r\n          html += `<h6 class=\"help-section-subheader\">${sub.title}</h6>`;\r\n          html += `<ul class=\"help-list\">`;\r\n          sub.items.forEach(item => {\r\n            html += `<li class=\"help-list-item\"><strong class=\"help-hotkey\">${item.key}</strong> ${item.desc}</li>`;\r\n          });\r\n          html += `</ul>`;\r\n        });\r\n      }\r\n    });\r\n    this.content.innerHTML = html;\r\n  }\r\n\r\n  bindEvents() {\r\n    this._clickOutsideHandler = (e) => {\r\n      if (this.options.helpButton &&\r\n        (e.target === this.options.helpButton || this.options.helpButton.contains(e.target))) {\r\n        return;\r\n      }\r\n      if (this.container && !this.container.contains(e.target)) {\r\n        this.remove();\r\n        (0,_helpers_js__WEBPACK_IMPORTED_MODULE_0__.showChatAlert)('Help panel has been closed.', {\r\n          type: 'warning',\r\n          duration: 2000\r\n        });\r\n      }\r\n    };\r\n    document.addEventListener('click', this._clickOutsideHandler, true);\r\n\r\n    this._escHandler = (e) => {\r\n      if (e.key === 'Escape') {\r\n        this.remove();\r\n        (0,_helpers_js__WEBPACK_IMPORTED_MODULE_0__.showChatAlert)('Help panel has been closed.', {\r\n          type: 'warning',\r\n          duration: 2000\r\n        });\r\n      }\r\n    };\r\n    document.addEventListener('keydown', this._escHandler, true);\r\n\r\n    this._stopPropagationHandler = (e) => {\r\n      e.stopPropagation();\r\n    };\r\n    this.container.addEventListener('click', this._stopPropagationHandler);\r\n  }\r\n\r\n  remove() {\r\n    if (this._clickOutsideHandler) {\r\n      document.removeEventListener('click', this._clickOutsideHandler, true);\r\n      this._clickOutsideHandler = null;\r\n    }\r\n    if (this._escHandler) {\r\n      document.removeEventListener('keydown', this._escHandler, true);\r\n      this._escHandler = null;\r\n    }\r\n    if (this.container) {\r\n      this.container.removeEventListener('click', this._stopPropagationHandler);\r\n      this._stopPropagationHandler = null;\r\n    }\r\n    if (this.container) {\r\n      // Fade out the panel; the helper will remove the element after transition.\r\n      (0,_helpers_js__WEBPACK_IMPORTED_MODULE_0__.adjustVisibility)(this.container, 'hide', '0');\r\n      this.container = null;\r\n    }\r\n    if (typeof this.options.onDestroy === 'function') {\r\n      this.options.onDestroy();\r\n    }\r\n    HelpPanel.instance = null;\r\n  }\r\n\r\n  show() {\r\n    if (!this.container) {\r\n      this.init();\r\n    } else {\r\n      this.updatePanelContent();\r\n    }\r\n    if (!document.body.contains(this.container)) {\r\n      document.body.appendChild(this.container);\r\n      (0,_helpers_js__WEBPACK_IMPORTED_MODULE_0__.adjustVisibility)(this.container, 'show', '1');\r\n      (0,_helpers_js__WEBPACK_IMPORTED_MODULE_0__.showChatAlert)(\"Help panel is now visible.\");\r\n    }\r\n  }\r\n\r\n  toggle() {\r\n    if (this.container && document.body.contains(this.container)) {\r\n      this.remove();\r\n    } else {\r\n      this.show();\r\n    }\r\n  }\r\n\r\n  static setupHelpCommandEvents() {\r\n    const input = document.getElementById('message-input');\r\n    if (input) {\r\n      input.addEventListener('keydown', (e) => {\r\n        if (input.value.trim() === \"/help\" && e.code === 'Space') {\r\n          e.preventDefault();\r\n          if (!HelpPanel.instance) {\r\n            const helpPanelInstance = new HelpPanel({\r\n              onDestroy: () => {\r\n                // Callback if needed.\r\n              }\r\n            });\r\n            helpPanelInstance.init();\r\n            helpPanelInstance.show();\r\n            (0,_helpers_js__WEBPACK_IMPORTED_MODULE_0__.showChatAlert)(\"Help panel is now visible.\");\r\n          } else {\r\n            HelpPanel.instance.remove();\r\n          }\r\n          input.value = \"\";\r\n        }\r\n      });\r\n    }\r\n    return HelpPanel.instance;\r\n  }\r\n}\r\n\r\n// Static instance tracker\r\nHelpPanel.instance = null;\r\n\n\n//# sourceURL=webpack://tampermonkey-script/./src/components/helpPanel.js?");

/***/ }),

/***/ "./src/converters/image-converter.js":
/*!*******************************************!*\
  !*** ./src/converters/image-converter.js ***!
  \*******************************************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   convertImageLinksToImage: () => (/* binding */ convertImageLinksToImage)\n/* harmony export */ });\n/* harmony import */ var _helpers__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../helpers */ \"./src/helpers.js\");\n/* harmony import */ var _definitions__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../definitions */ \"./src/definitions.js\");\n// helpers\r\n // helpers\r\n\r\n// definitions\r\n\r\n\r\n// Image constants\r\nconst imageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'webp'];\r\nconst emojis = { image: '📸', domain: '🖥️', untrusted: '💀️️' };\r\nconst zoomLimits = { min: 0.2, max: 10, factor: 0.1 };\r\nconst navigationDelay = 50;\r\n\r\n// Image navigation state\r\nlet currentIndex = 0;\r\nlet isChangingImage = false;\r\nlet thumbnailLinks = [];\r\n\r\n// Expanded image reference\r\nlet expandedImage = null;\r\n\r\nconst getExtension = (url) => {\r\n  try {\r\n    return (url.match(/\\.([^?#.]+)(?:[?#]|$)/i)?.[1]?.toLowerCase() || '');\r\n  } catch (error) {\r\n    console.error(\"Error extracting extension:\", error.message);\r\n    return '';\r\n  }\r\n};\r\n\r\nconst isAllowedImageExtension = (url) => {\r\n  const extension = getExtension(url);\r\n  return { allowed: imageExtensions.includes(extension), extension };\r\n};\r\n\r\nconst createExpandedView = (src, clickedThumbnailIndex) => {\r\n  // Create and add expanded image to DOM\r\n  const imageElement = document.createElement('img');\r\n  imageElement.src = src;\r\n  imageElement.classList.add('scaled-thumbnail');\r\n\r\n  document.body.appendChild(imageElement);\r\n\r\n  currentIndex = clickedThumbnailIndex;\r\n\r\n  // Zoom and movement variables\r\n  let zoomScale = 1;\r\n  let isMMBPressed = false;\r\n  let lastMouseX = 0,\r\n      lastMouseY = 0;\r\n  let translateX = 0,\r\n      translateY = 0;\r\n  const movementSpeed = 5;\r\n\r\n  // Get or create the dimming element\r\n  let dimmingElement = document.querySelector('.dimming-element');\r\n  if (!dimmingElement) {\r\n    dimmingElement = document.createElement('div');\r\n    dimmingElement.classList.add('dimming-element');\r\n    document.body.appendChild(dimmingElement);\r\n  }\r\n\r\n  // Define closeExpandedView function\r\n  const closeExpandedView = (img) => {\r\n    (0,_helpers__WEBPACK_IMPORTED_MODULE_0__.adjustVisibility)(img, 'hide', '0');\r\n    if (!document.querySelector('.popup-panel') && dimmingElement) {\r\n      (0,_helpers__WEBPACK_IMPORTED_MODULE_0__.adjustVisibility)(dimmingElement, 'hide', '0');\r\n    }\r\n    (0,_helpers__WEBPACK_IMPORTED_MODULE_0__.removeBigImageEventListeners)();\r\n  };\r\n\r\n  // Define event listeners for the expanded image\r\n  _definitions__WEBPACK_IMPORTED_MODULE_1__.state.bigImageEvents['click'] = (event) => {\r\n    if (!imageElement.contains(event.target)) {\r\n      imageElement.remove();\r\n      (0,_helpers__WEBPACK_IMPORTED_MODULE_0__.removeBigImageEventListeners)();\r\n    }\r\n  };\r\n\r\n  _definitions__WEBPACK_IMPORTED_MODULE_1__.state.bigImageEvents['keydown'] = (event) => {\r\n    if (event.code === 'Escape' || event.code === 'Space') {\r\n      event.preventDefault();\r\n      closeExpandedView(imageElement);\r\n    } else if (event.code === 'ArrowLeft') {\r\n      navigateImages(-1);\r\n    } else if (event.code === 'ArrowRight') {\r\n      navigateImages(1);\r\n    }\r\n  };\r\n\r\n  _definitions__WEBPACK_IMPORTED_MODULE_1__.state.bigImageEvents['wheel'] = (event) => {\r\n    const direction = event.deltaY < 0 ? 1 : -1;\r\n    zoomScale += direction * zoomLimits.factor * zoomScale;\r\n    zoomScale = Math.max(zoomLimits.min, Math.min(zoomScale, zoomLimits.max));\r\n    \r\n    // Update transform to maintain center positioning while zooming\r\n    imageElement.style.transform = `translate(-50%, -50%) translate(${translateX}px, ${translateY}px) scale(${zoomScale})`;\r\n  };\r\n\r\n  _definitions__WEBPACK_IMPORTED_MODULE_1__.state.bigImageEvents['mousemove'] = (event) => {\r\n    if (isMMBPressed) {\r\n      if (event.ctrlKey) {\r\n        const deltaY = event.clientY - lastMouseY;\r\n        const zoomDirection = deltaY < 0 ? 1 : -1;\r\n        const zoomAmount = Math.abs(deltaY) * zoomLimits.factor * 0.05;\r\n        zoomScale += zoomDirection * zoomAmount * zoomScale;\r\n        zoomScale = Math.max(zoomLimits.min, Math.min(zoomScale, zoomLimits.max));\r\n      } else {\r\n        const deltaX = (event.clientX - lastMouseX) / zoomScale * movementSpeed;\r\n        const deltaY = (event.clientY - lastMouseY) / zoomScale * movementSpeed;\r\n        translateX += deltaX;\r\n        translateY += deltaY;\r\n      }\r\n      \r\n      // Update transform to maintain center positioning while moving and zooming\r\n      imageElement.style.transform = `translate(-50%, -50%) translate(${translateX}px, ${translateY}px) scale(${zoomScale})`;\r\n      \r\n      lastMouseX = event.clientX;\r\n      lastMouseY = event.clientY;\r\n    }\r\n  };\r\n\r\n  _definitions__WEBPACK_IMPORTED_MODULE_1__.state.bigImageEvents['mousedown'] = (event) => {\r\n    const { button, clientX, clientY, target, ctrlKey } = event;\r\n    if ((button === 0 || button === 2) && target !== imageElement) return;\r\n    if (button === 0) {\r\n      navigateImages(-1);\r\n    } else if (button === 2) {\r\n      event.preventDefault();\r\n      if (ctrlKey) {\r\n        navigator.clipboard.writeText(target.src).catch(console.error);\r\n        closeExpandedView(imageElement);\r\n      } else {\r\n        navigateImages(1);\r\n      }\r\n    } else if (button === 1) { // Middle mouse button\r\n      isMMBPressed = true;\r\n      lastMouseX = clientX;\r\n      lastMouseY = clientY;\r\n      event.preventDefault();\r\n    }\r\n  };\r\n\r\n  _definitions__WEBPACK_IMPORTED_MODULE_1__.state.bigImageEvents['mouseup'] = (event) => {\r\n    if (event.button === 1) {\r\n      isMMBPressed = false;\r\n    }\r\n  };\r\n\r\n  _definitions__WEBPACK_IMPORTED_MODULE_1__.state.bigImageEvents['contextmenu'] = (event) => event.preventDefault();\r\n\r\n  // Add the event listeners using your helper function\r\n  (0,_helpers__WEBPACK_IMPORTED_MODULE_0__.addBigImageEventListeners)();\r\n\r\n  // Show the dimming element using adjustVisibility\r\n  (0,_helpers__WEBPACK_IMPORTED_MODULE_0__.adjustVisibility)(dimmingElement, \"show\", \"1\");\r\n\r\n  // When clicking the dimming background, hide the image and the dimming element\r\n  dimmingElement.addEventListener('click', () => {\r\n    closeExpandedView(imageElement);\r\n  });\r\n\r\n  return imageElement;\r\n};\r\n\r\nconst navigateImages = (direction) => {\r\n  const newIndex = currentIndex + direction;\r\n  if (newIndex >= 0 && newIndex < thumbnailLinks.length && !isChangingImage) {\r\n    isChangingImage = true;\r\n    if (expandedImage) expandedImage.src = thumbnailLinks[newIndex].imgSrc;\r\n    setTimeout(() => (isChangingImage = false), navigationDelay);\r\n    currentIndex = newIndex;\r\n  }\r\n};\r\n\r\nfunction convertImageLinksToImage(containerType) {\r\n  const container = document.getElementById('messages-panel');\r\n  if (!container) return;\r\n\r\n  const refreshThumbnailLinks = () => {\r\n    thumbnailLinks = [];\r\n    container.querySelectorAll(\".clickable-thumbnail\").forEach((thumbnail, index) => {\r\n      const img = thumbnail.querySelector(\"img\");\r\n      if (img && thumbnail.dataset.sourceLink) {\r\n        thumbnailLinks.push({ link: thumbnail.dataset.sourceLink, imgSrc: img.src, index });\r\n      }\r\n    });\r\n  };\r\n\r\n  const links = container.querySelectorAll(\"a:not(.skipped):not(.processed-image)\");\r\n  if (!links.length) return;\r\n\r\n  links.forEach((link) => {\r\n    if (!link.href || !link.href.startsWith(\"http\")) return;\r\n    const { allowed, extension } = isAllowedImageExtension(link.href);\r\n    if (!allowed) return;\r\n\r\n    link.classList.add(\"media\");\r\n    const { isTrusted, domain } = (0,_helpers__WEBPACK_IMPORTED_MODULE_0__.isTrustedDomain)(link.href);\r\n    link.title = (0,_helpers__WEBPACK_IMPORTED_MODULE_0__.isEncodedURL)(link.href) ? (0,_helpers__WEBPACK_IMPORTED_MODULE_0__.decodeURL)(link.href) : link.href;\r\n\r\n    isTrusted\r\n      ? handleTrustedLink(link, extension, domain)\r\n      : handleUntrustedLink(link, extension, domain);\r\n  });\r\n\r\n  function createThumbnail(link, isUntrusted) {\r\n    const thumbnail = document.createElement(\"div\");\r\n    thumbnail.classList.add(\"clickable-thumbnail\");\r\n    thumbnail.dataset.sourceLink = link.href;\r\n\r\n    const img = document.createElement(\"img\");\r\n    img.src = link.href;\r\n\r\n    img.onload = () => {\r\n      thumbnail.appendChild(img);\r\n      link.parentNode.insertBefore(thumbnail, link.nextSibling);\r\n    };\r\n\r\n    img.onerror = () => {\r\n      console.error(\"Failed to load image:\", link.href);\r\n      link.classList.add(\"skipped\");\r\n    };\r\n\r\n    if (isUntrusted) {\r\n      if (!link.querySelector(\".clickable-thumbnail\")) {\r\n        link.addEventListener(\"click\", (e) => {\r\n          if (!link.querySelector(\".clickable-thumbnail\")) {\r\n            thumbnail.appendChild(img);\r\n            link.parentNode.insertBefore(thumbnail, link.nextSibling);\r\n          }\r\n        });\r\n      }\r\n    } else {\r\n      thumbnail.appendChild(img);\r\n      link.parentNode.insertBefore(thumbnail, link.nextSibling);\r\n    }\r\n\r\n    thumbnail.addEventListener(\"click\", (e) => {\r\n      e.stopPropagation();\r\n      refreshThumbnailLinks();\r\n      const clickedIndex = thumbnailLinks.findIndex(\r\n        (item) => item.link === link.href || item.imgSrc === img.src\r\n      );\r\n      expandedImage = createExpandedView(\r\n        img.src,\r\n        clickedIndex >= 0 ? clickedIndex : 0\r\n      );\r\n      (0,_helpers__WEBPACK_IMPORTED_MODULE_0__.adjustVisibility)(expandedImage, \"show\", \"1\");\r\n      const dimmingElement = document.querySelector('.dimming-element');\r\n      if (dimmingElement) {\r\n        (0,_helpers__WEBPACK_IMPORTED_MODULE_0__.adjustVisibility)(dimmingElement, \"show\", \"1\");\r\n      }\r\n    });\r\n  }\r\n\r\n  function handleUntrustedLink(link, extension, domain) {\r\n    link.classList.add(\"skipped\");\r\n    link.textContent = `${emojis.image} Image (${extension.toUpperCase()}) ${emojis.domain} Hostname (${domain}) ${emojis.untrusted} Untrusted`;\r\n    link.addEventListener(\"click\", (e) => {\r\n      if (!link.classList.contains(\"processed-image\")) {\r\n        e.preventDefault();\r\n        link.classList.remove(\"skipped\");\r\n        link.classList.add(\"processed-image\");\r\n        createThumbnail(link, true);\r\n      }\r\n    });\r\n  }\r\n\r\n  function handleTrustedLink(link, extension, domain) {\r\n    link.textContent = `${emojis.image} Image (${extension.toUpperCase()}) ${emojis.domain} Hostname (${domain})`;\r\n    link.classList.add(\"processed-image\");\r\n    createThumbnail(link, false);\r\n  }\r\n}\n\n//# sourceURL=webpack://tampermonkey-script/./src/converters/image-converter.js?");

/***/ }),

/***/ "./src/converters/video-converter.js":
/*!*******************************************!*\
  !*** ./src/converters/video-converter.js ***!
  \*******************************************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   convertVideoLinksToPlayer: () => (/* binding */ convertVideoLinksToPlayer)\n/* harmony export */ });\n/* harmony import */ var _helpers__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../helpers */ \"./src/helpers.js\");\n // helpers\r\n\r\nconst emojis = { image: '🎥', domain: '🖥️', untrusted: '💀️️' };\r\nconst allowedVideoExtensions = ['mp4', 'webm', 'ogg', 'mov', 'avi'];\r\n\r\nconst isAllowedVideoExtension = url => {\r\n  const ext = url.match(/\\.([^?#.]+)(?:[?#]|$)/i)?.[1]?.toLowerCase() || '';\r\n  return { allowed: allowedVideoExtensions.includes(ext), extension: ext };\r\n};\r\n\r\nfunction convertVideoLinksToPlayer(containerType) {\r\n  const container = document.getElementById('messages-panel');\r\n  if (!container) return;\r\n\r\n  const links = container.querySelectorAll(\"a:not(.skipped):not(.processed-video)\");\r\n  if (!links.length) return;\r\n\r\n  links.forEach(link => {\r\n    const url = link.href;\r\n    if (!url) return;\r\n\r\n    const videoInfo = getVideoInfo(url);\r\n    if (!videoInfo) return;\r\n\r\n    link.classList.add(\"media\");\r\n    const { isTrusted, domain } = (0,_helpers__WEBPACK_IMPORTED_MODULE_0__.isTrustedDomain)(url);\r\n\r\n    if (!isTrusted) {\r\n      link.classList.add(\"skipped\");\r\n      link.textContent = `${emojis.image} ${videoInfo.videoType} ${emojis.domain} Hostname (${domain}) ${emojis.untrusted} Untrusted`;\r\n      link.addEventListener(\"click\", e => {\r\n        if (!link.classList.contains(\"processed-video\")) {\r\n          e.preventDefault();\r\n          link.classList.remove(\"skipped\");\r\n          processVideoLink(link, url, domain, videoInfo);\r\n        }\r\n      });\r\n      return;\r\n    }\r\n\r\n    processVideoLink(link, url, domain, videoInfo);\r\n  });\r\n\r\n  function processVideoLink(link, url, domain, videoInfo) {\r\n    const { youtubeMatch, videoType, videoId } = videoInfo;\r\n    const videoCheck = isAllowedVideoExtension(url);\r\n    if (!youtubeMatch && !videoCheck.allowed) return;\r\n\r\n    link.classList.add(\"processed-video\");\r\n    const wrapper = document.createElement('div');\r\n    wrapper.classList.add(\"video-wrapper\");\r\n\r\n    const embed = document.createElement(youtubeMatch ? 'iframe' : 'video');\r\n    embed.classList.add(\"video-container\");\r\n\r\n    link.textContent = `${emojis.image} ${videoType} ${emojis.domain} Hostname (${domain})`;\r\n\r\n    if (youtubeMatch) {\r\n      embed.src = `https://www.youtube.com/embed/${videoId}`;\r\n      embed.allowFullscreen = true;\r\n    } else {\r\n      embed.src = url;\r\n      embed.controls = true;\r\n    }\r\n\r\n    link.title = (0,_helpers__WEBPACK_IMPORTED_MODULE_0__.isEncodedURL)(url) ? (0,_helpers__WEBPACK_IMPORTED_MODULE_0__.decodeURL)(url) : url;\r\n    link.style.display = 'inline-flex';\r\n\r\n    link.parentNode.insertBefore(wrapper, link);\r\n    wrapper.append(link, embed);\r\n  }\r\n\r\n  function getVideoInfo(url) {\r\n    const youtubeMatch = url.match(/(?:shorts\\/|live\\/|watch\\?v=|youtu\\.be\\/)([a-zA-Z0-9_-]{11})/i);\r\n\r\n    if (youtubeMatch) {\r\n      const videoId = youtubeMatch[1];\r\n      const videoType = url.includes('shorts/') ? 'Shorts' :\r\n        url.includes('live/') ? 'Live' :\r\n          url.includes('watch?v=') ? 'Watch' :\r\n            url.includes('youtu.be/') ? 'Share' : 'YouTube';\r\n      return { youtubeMatch: true, videoId, videoType };\r\n    }\r\n\r\n    const extension = url.split('.').pop().toLowerCase();\r\n    if (allowedVideoExtensions.includes(extension)) {\r\n      return { youtubeMatch: false, videoType: `Video (${extension.toUpperCase()})` };\r\n    }\r\n\r\n    return false;\r\n  }\r\n}\r\n\n\n//# sourceURL=webpack://tampermonkey-script/./src/converters/video-converter.js?");

/***/ }),

/***/ "./src/data/emojiData.js":
/*!*******************************!*\
  !*** ./src/data/emojiData.js ***!
  \*******************************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   emojiData: () => (/* binding */ emojiData),\n/* harmony export */   emojiKeywords: () => (/* binding */ emojiKeywords)\n/* harmony export */ });\n// emojiData.js\r\n\r\nconst emojiData = {\r\n  smileys: [\r\n    // Face smiling\r\n    '😀', '😃', '😄', '😆', '😁', '😅', '😂', '🤣', '🥲', '☺️', '😊', '😇', '🙂', '🙃', '😉', '😌', '😍', '🥰', '😙', '😚', '😗', '😘',\r\n    '😋', '🥸', '😵‍💫',\r\n    // Face affection\r\n    '😛', '😝', '😜', '🤪', '😎', '🤓', '🧐', '🤨', '🤩', '🥳', '😏', '😒', '😞', '😔', '😟', '😕', '🙁', '☹️', '😣', '😖', '😫',\r\n    // Face tongue\r\n    '😩', '🥺', '😢', '😭', '😤', '😠', '😡', '🤬', '🤯', '😳', '🥵', '🥶', '😱', '😨', '😰', '😥', '😓', '🤗', '🤔', '🤭', '🤫',\r\n    // Face negative\r\n    '🤥', '😶', '😐', '😑', '😬', '🙄', '😯', '😦', '😧', '😮', '😲', '🥱', '😴', '🤤', '😪', '😵', '🤐', '🥴', '🤢', '🤮', '🤧',\r\n    // Face costume\r\n    '😷', '🤒', '🤕', '🤑', '🤠', '😈', '👿', '👹', '👺', '🤡', '💩', '👻', '💀', '☠️', '👽', '👾', '🤖', '🎃',\r\n    // Cat faces\r\n    '😺', '😸', '😹', '😻', '😼', '😽', '🙀', '😿', '😾',\r\n    // Hand gestures\r\n    '👋', '🤚', '🖐️', '✋', '🖖', '👌', '🤌', '🤏', '✌️', '🤞', '🤟', '🤘', '🤙', '👈', '👉', '👆', '🖕', '👇', '☝️', '👍', '👎',\r\n    // Hand symbols\r\n    '✊', '👊', '🤛', '🤜', '👏', '🙌', '👐', '🤲', '🤝', '🙏',\r\n    // Body parts\r\n    '✍️', '💅', '🤳', '💪', '🦾', '🦿', '🦵', '🦶', '👂', '🦻', '👃', '🧠', '🫀', '🫁', '🦷', '🦴', '👀', '👁️', '👅', '👄',\r\n    // Person\r\n    '👶', '🧒', '👦', '👧', '🧑', '👱', '👨', '🧔', '👩', '🧓', '👴', '👵', '🙍', '🙎', '🙅', '🙆', '💁', '🙋', '🧏', '🙇', '🤦', '🤷',\r\n    // Professional\r\n    '👮', '🕵️', '💂', '🥷', '👷', '🤴', '👸', '👳', '👲', '🧕', '🤵', '👰', '🤰', '🤱', '👼', '🎅', '🤶', '🦸', '🦹', '🧙', '🧚', '🧛', '🧜'\r\n  ],\r\n\r\n  nature: [\r\n    // Mammals\r\n    '🐵', '🐒', '🦍', '🦧', '🐶', '🐕', '🦮', '🐕‍🦺', '🐩', '🐺', '🦊', '🦝', '🐱', '🐈', '🐈‍⬛', '🦁', '🐯', '🐅', '🐆', '🐴', '🐎',\r\n    '🦄', '🦓', '🦌', '🦬', '🐮', '🐂', '🐃', '🐄', '🐷', '🐖', '🐗', '🐽', '🐏', '🐑', '🐐', '🐪', '🐫', '🦙', '🦒', '🐘', '🦏',\r\n    '🦛', '🐭', '🐁', '🐀', '🐹', '🐰', '🐇', '🦫', '🦘', '🦡', '🐿️', '🦔', '🦦', '🦥', '🐼', '🦨', '🦘', '🦡',\r\n    // Birds\r\n    '🦃', '🐔', '🐓', '🐣', '🐤', '🐥', '🐦', '🐧', '🕊️', '🦅', '🦆', '🦢', '🦉', '🦤', '🪶', '🦩', '🦚', '🦜',\r\n    // Reptiles/Amphibians\r\n    '🐸', '🐊', '🐢', '🦎', '🐍', '🐲', '🐉', '🦕', '🦖',\r\n    // Marine\r\n    '🐳', '🐋', '🐬', '🦭', '🐟', '🐠', '🐡', '🦈', '🐙', '🐚', '🪸',\r\n    // Insects\r\n    '🐌', '🦋', '🐛', '🐜', '🐝', '🪲', '🐞', '🦗', '🪳', '🕷️', '🕸️', '🦂', '🦟', '🪰', '🪱',\r\n    // Plants\r\n    '🌸', '💮', '🏵️', '🌹', '🥀', '🌺', '🌻', '🌼', '🌷', '🌱', '🪴', '🌲', '🌳', '🌴', '🌵', '🌾', '🌿', '☘️', '🍀', '🍁', '🍂', '🍃'\r\n  ],\r\n\r\n  food: [\r\n    // Fruits\r\n    '🍎', '🍐', '🍊', '🍋', '🍌', '🍉', '🍇', '🍓', '🫐', '🍈', '🍒', '🍑', '🥭', '🍍', '🥥', '🥝',\r\n    // Vegetables\r\n    '🍅', '🍆', '🥑', '🥦', '🥬', '🥒', '🌶️', '🫑', '🥕', '🧄', '🧅', '🥔', '🍠', '🥐', '🥯', '🍞', '🥖', '🥨',\r\n    // Prepared\r\n    '🧀', '🥚', '🍳', '🥓', '🥩', '🍗', '🍖', '🦴', '🌭', '🍔', '🍟', '🍕', '🫓', '🥪', '🥙', '🧆', '🌮', '🌯', '🫔', '🥗',\r\n    // Asian\r\n    '🥘', '🫕', '🥫', '🍝', '🍜', '🍲', '🍛', '🍣', '🍱', '🥟', '🦪', '🍤', '🍙', '🍚', '🍘', '🍥', '🥠', '🥮',\r\n    // Sweets\r\n    '🍢', '🍡', '🍧', '🍨', '🍦', '🥧', '🧁', '🍰', '🎂', '🍮', '🍭', '🍬', '🍫', '🍿', '🍩', '🍪',\r\n    // Drink\r\n    '🫖', '☕', '🍵', '🧃', '🥤', '🧋', '🍶', '🍺', '🍻', '🥂', '🍷', '🥃', '🍸', '🍹', '🧉', '🍾'\r\n  ],\r\n\r\n  activities: [\r\n    // Sports\r\n    '⚽', '🏀', '🏈', '⚾', '🥎', '🎾', '🏐', '🏉', '🥏', '🎱', '🪀', '🏓', '🏸', '🏒', '🏑', '🥍', '🏏', '⛳', '🪁', '🎣',\r\n    '🤿', '🎽', '🛹', '🛼', '🛷', '⛸️', '🥌', '⛷️', '🏂', '🪂', '🏋️', '🤼', '🤸', '⛹️', '🤾', '🏌️', '🏇', '🧘', '🏄', '🏊',\r\n    // Activities\r\n    '🤽', '🚣', '🧗', '🚴', '🚵', '🎪', '🎭', '🎨', '🎬', '🎤', '🎧', '🎼', '🎹', '🥁', '🎷', '🎺', '🎸', '🎻', '🎲', '🎯',\r\n    '🎳', '🎮', '🎰', '🧩', '🎪', '🎫', '🎟️'\r\n  ],\r\n\r\n  travel: [\r\n    // Land transport\r\n    '🚗', '🚕', '🚙', '🚌', '🚎', '🏎️', '🚓', '🚑', '🚒', '🚐', '🛻', '🚚', '🚛', '🚜', '🛵', '🏍️', '🛺', '🚲', '🛴',\r\n    // Air transport\r\n    '✈️', '🛩️', '🛫', '🛬', '🚁', '🚀', '🛸',\r\n    // Water transport\r\n    '🛶', '⛵', '🚤', '🛥️', '🛳️', '⛴️', '🚢',\r\n    // Places\r\n    '🏰', '🏯', '🏟️', '🏖️', '🏝️', '🏜️', '🌋', '⛰️', '🏔️', '🗻', '🏕️', '🏭', '🏢', '🏬', '🏣', '🏤', '🏥', '🏦', '🏨',\r\n    '🏪', '🏫', '🏩', '💒', '⛪', '🕌', '🕍', '🛕', '⛩️', '🏛️'\r\n  ],\r\n\r\n  objects: [\r\n    // Tools\r\n    '📱', '💻', '⌨️', '🖥️', '🖨️', '🖱️', '🖲️', '🕹️', '🗜️', '💽', '💾', '💿', '📀', '📼', '📷', '📸', '📹', '🎥',\r\n    // Office\r\n    '📞', '☎️', '📟', '📠', '📺', '📻', '🎙️', '🎚️', '🎛️', '📡', '🔋', '🔌', '💡', '🔦', '🕯️',\r\n    // Household\r\n    '🧯', '🛢️', '💸', '💵', '💴', '💶', '💷', '🪙', '💰', '💳', '💎', '⚖️', '🪜', '🧰', '🔧', '🔨', '⚒️', '🛠️', '⛏️',\r\n    // Writing\r\n    '✏️', '🖊️', '🖋️', '✒️', '🖌️', '🖍️', '📝', '📚', '📖', '🔖', '📑', '🗒️', '📄', '📰', '🗞️', '📁', '📂', '🗂️',\r\n    // Clocks (additional clock emojis)\r\n    '🕐', '🕑', '🕒', '🕓', '🕔', '🕕', '🕖', '🕗', '🕘', '🕙', '🕚', '🕛', '🕰️', '⏰', '⏱️', '⏲️', '⌚'\r\n  ],\r\n\r\n  symbols: [\r\n    // Hearts\r\n    '❤️', '🧡', '💛', '💚', '💙', '💜', '🤎', '🖤', '🤍', '💔', '❣️', '💕', '💞', '💓', '💗', '💖', '💘', '💝', '💟',\r\n    // Religion\r\n    '☮️', '✝️', '☪️', '🕉️', '☸️', '✡️', '🔯', '🕎', '☯️', '☦️', '🛐', '⛎',\r\n    // Warning\r\n    '⚠️', '🚸', '⛔', '🚫', '☢️', '☣️',\r\n    // Math\r\n    '➕', '➖', '➗', '✖️', '♾️', '💲', '💱',\r\n    // Arrows\r\n    '⬆️', '↗️', '➡️', '↘️', '⬇️', '↙️', '⬅️', '↖️', '↕️', '↔️', '↩️', '↪️', '⤴️', '⤵️', '🔃', '🔄',\r\n    // Other\r\n    '🔆', '📶', '🎦', '🔅', '♻️', '✅', '❌', '❎', '➰', '➿', '〽️', '✳️', '✴️', '❇️', '©️', '®️', '™️'\r\n  ],\r\n\r\n  flags: [\r\n    // Special flags\r\n    '🏁', '🚩', '🎌', '🏴', '🏳️', '🏳️‍🌈', '🏳️‍⚧️', '🏴‍☠️',\r\n    // Country flags (sample)\r\n    '🇺🇸', '🇬🇧', '🇯🇵', '🇰🇷', '🇩🇪', '🇨🇳', '🇧🇷', '🇮🇳', '🇫🇷', '🇪🇸', '🇮🇹', '🇷🇺', '🇨🇦', '🇦🇺', '🇳🇿',\r\n    // More popular country flags\r\n    '🇲🇽', '🇦🇷', '🇵🇰', '🇪🇬', '🇸🇪', '🇳🇴', '🇳🇱', '🇨🇭', '🇹🇷', '🇮🇩', '🇸🇬', '🇮🇱', '🇵🇹', '🇵🇱', '🇹🇭'\r\n  ]\r\n};\r\n\r\nconst emojiKeywords = {\r\n  //------------------------- Smileys & Emotion -------------------------\r\n  // Face smiling\r\n  '😀': {\r\n    en: ['grinning', 'face', 'smile', 'happy', 'joy'],\r\n    ru: ['улыбающийся', 'лицо', 'улыбка', 'счастливый', 'радость']\r\n  },\r\n  '😃': {\r\n    en: ['smiley', 'face', 'happy', 'joy', 'laugh'],\r\n    ru: ['улыбчивый', 'лицо', 'счастливый', 'радость', 'смех']\r\n  },\r\n  '😄': {\r\n    en: ['laughing', 'face', 'happy', 'joy', 'grin'],\r\n    ru: ['смеющийся', 'лицо', 'счастливый', 'радость', 'ухмылка']\r\n  },\r\n  '😆': {\r\n    en: ['grinning face', 'laugh'],\r\n    ru: ['улыбка', 'смех']\r\n  },\r\n  '😁': {\r\n    en: ['beaming', 'face', 'grin', 'smile', 'happy'],\r\n    ru: ['сияющий', 'лицо', 'улыбка', 'счастье', 'радость']\r\n  },\r\n  '😅': {\r\n    en: ['sweat', 'nervous', 'face', 'laugh', 'relief'],\r\n    ru: ['пот', 'нервный', 'лицо', 'смех', 'облегчение']\r\n  },\r\n  '😂': {\r\n    en: ['tears', 'joy', 'face', 'laugh', 'happy'],\r\n    ru: ['слёзы', 'радость', 'лицо', 'смех', 'счастье']\r\n  },\r\n  '🤣': {\r\n    en: ['rolling', 'floor', 'laugh', 'funny', 'amused'],\r\n    ru: ['катающийся', 'пол', 'смех', 'смешной', 'развлечённый']\r\n  },\r\n  '🥲': {\r\n    en: ['smiling', 'tear', 'bittersweet', 'nostalgic', 'happy'],\r\n    ru: ['улыбающийся', 'слеза', 'горько-сладкий', 'ностальгический', 'счастливый']\r\n  },\r\n  '☺️': {\r\n    en: ['smile', 'blush', 'content', 'peaceful'],\r\n    ru: ['улыбка', 'румянец', 'довольный', 'спокойный']\r\n  },\r\n  '😊': {\r\n    en: ['smiling', 'happy', 'blushing', 'content'],\r\n    ru: ['улыбающийся', 'счастливый', 'румянец', 'довольный']\r\n  },\r\n  '😇': {\r\n    en: ['angel', 'halo', 'innocent', 'saint', 'pure'],\r\n    ru: ['ангел', 'ореол', 'невинный', 'святой', 'чистый']\r\n  },\r\n  '🙂': {\r\n    en: ['slight', 'smile', 'face', 'mild'],\r\n    ru: ['слегка', 'улыбка', 'лицо', 'умеренный']\r\n  },\r\n  '🙃': {\r\n    en: ['upside-down', 'silly', 'quirky', 'funny'],\r\n    ru: ['перевернутый', 'глупый', 'странный', 'смешной']\r\n  },\r\n  '😉': {\r\n    en: ['wink', 'flirt', 'playful', 'smile'],\r\n    ru: ['подмигивание', 'флирт', 'игривый', 'улыбка']\r\n  },\r\n  '😌': {\r\n    en: ['relieved', 'calm', 'content', 'satisfied'],\r\n    ru: ['облегчённый', 'спокойный', 'довольный', 'удовлетворённый']\r\n  },\r\n  '😍': {\r\n    en: ['heart', 'love', 'smiling', 'eyes', 'adore'],\r\n    ru: ['сердце', 'любовь', 'улыбающийся', 'глаза', 'обожать']\r\n  },\r\n  '🥰': {\r\n    en: ['love', 'hearts', 'affection', 'adoration', 'cuddle'],\r\n    ru: ['любовь', 'сердца', 'нежность', 'обожание', 'обниматься']\r\n  },\r\n  '😙': {\r\n    en: ['kiss', 'love', 'affection', 'flirt'],\r\n    ru: ['поцелуй', 'любовь', 'нежность', 'флирт']\r\n  },\r\n  '😗': {\r\n    en: ['kiss', 'face', 'smile', 'affection'],\r\n    ru: ['поцелуй', 'лицо', 'улыбка', 'нежность']\r\n  },\r\n  '😚': {\r\n    en: ['kiss', 'closed eyes', 'affection', 'love'],\r\n    ru: ['поцелуй', 'закрытые глаза', 'нежность', 'любовь']\r\n  },\r\n  '😘': {\r\n    en: ['kiss', 'love', 'affection', 'flirt'],\r\n    ru: ['поцелуй', 'любовь', 'нежность', 'флирт']\r\n  },\r\n  '😋': {\r\n    en: ['yum', 'delicious', 'tasty', 'savor', 'lick'],\r\n    ru: ['ням', 'вкусно', 'аппетитно', 'наслаждаться', 'лизать']\r\n  },\r\n  '🥸': {\r\n    en: ['disguise', 'glasses', 'funny', 'sneaky', 'face'],\r\n    ru: ['маскировка', 'очки', 'смешной', 'хитрый', 'лицо']\r\n  },\r\n  '😵‍💫': {\r\n    en: ['dizzy', 'spiral eyes', 'confused', 'hypnotized', 'disoriented'],\r\n    ru: ['головокружение', 'спиральные глаза', 'запутанный', 'загипнотизированный', 'дезориентированный']\r\n  },\r\n\r\n  // Face affection\r\n  '😛': {\r\n    en: ['tongue', 'playful', 'cheeky', 'silly'],\r\n    ru: ['язык', 'игривый', 'нахальный', 'глупый']\r\n  },\r\n  '😝': {\r\n    en: ['tongue', 'silly', 'wacky', 'fun'],\r\n    ru: ['язык', 'глупый', 'безумный', 'весёлый']\r\n  },\r\n  '😜': {\r\n    en: ['tongue', 'wink', 'playful', 'fun'],\r\n    ru: ['язык', 'подмигивание', 'игривый', 'весёлый']\r\n  },\r\n  '🤪': {\r\n    en: ['crazy', 'wacky', 'zany', 'quirky'],\r\n    ru: ['сумасшедший', 'безумный', 'чудаковатый', 'странный']\r\n  },\r\n  '😎': {\r\n    en: ['cool', 'sunglasses', 'confident', 'chill'],\r\n    ru: ['крутой', 'очки', 'уверенный', 'расслабленный']\r\n  },\r\n  '🤓': {\r\n    en: ['nerd', 'geek', 'glasses', 'studious'],\r\n    ru: ['ботан', 'задрот', 'очки', 'учёный']\r\n  },\r\n  '🧐': {\r\n    en: ['monocle', 'investigative', 'curious', 'thoughtful'],\r\n    ru: ['одноглазый', 'расследовательский', 'любопытный', 'задумчивый']\r\n  },\r\n  '🤨': {\r\n    en: ['skeptical', 'doubtful', 'uncertain', 'raised eyebrow'],\r\n    ru: ['скептический', 'сомневающийся', 'неуверенный', 'поднятая бровь']\r\n  },\r\n  '🤩': {\r\n    en: ['starstruck', 'amazed', 'excited', 'admire'],\r\n    ru: ['восхищённый', 'поражённый', 'взволнованный', 'обожать']\r\n  },\r\n  '🥳': {\r\n    en: ['party', 'celebrate', 'birthday', 'festive'],\r\n    ru: ['вечеринка', 'праздновать', 'день рождения', 'праздничный']\r\n  },\r\n  '😏': {\r\n    en: ['smirk', 'sly', 'mischievous', 'confident'],\r\n    ru: ['ухмылка', 'хитрый', 'озорной', 'уверенный']\r\n  },\r\n  '😒': {\r\n    en: ['unamused', 'displeased', 'bored', 'sigh'],\r\n    ru: ['неудовлетворённый', 'недовольный', 'скучный', 'вздох']\r\n  },\r\n  '😞': {\r\n    en: ['disappointed', 'sad', 'down', 'somber'],\r\n    ru: ['разочарованный', 'грустный', 'унылый', 'мрачный']\r\n  },\r\n  '😔': {\r\n    en: ['pensive', 'sad', 'reflective', 'mournful'],\r\n    ru: ['задумчивый', 'грустный', 'рефлексивный', 'скорбный']\r\n  },\r\n  '😟': {\r\n    en: ['worried', 'concerned', 'anxious', 'upset'],\r\n    ru: ['обеспокоенный', 'тревожный', 'беспокойный', 'расстроенный']\r\n  },\r\n  '😕': {\r\n    en: ['confused', 'perplexed', 'uncertain', 'baffled'],\r\n    ru: ['смущённый', 'озадаченный', 'неуверенный', 'в замешательстве']\r\n  },\r\n  '🙁': {\r\n    en: ['frowning', 'sad', 'disappointed', 'downcast'],\r\n    ru: ['хмурый', 'грустный', 'разочарованный', 'угнетённый']\r\n  },\r\n  '☹️': {\r\n    en: ['frowning', 'sad', 'unhappy', 'mournful'],\r\n    ru: ['хмурый', 'грустный', 'несчастный', 'скорбный']\r\n  },\r\n  '😣': {\r\n    en: ['strained', 'persevering', 'tired', 'discomfort'],\r\n    ru: ['напряжённый', 'терпеливый', 'уставший', 'дискомфорт']\r\n  },\r\n  '😖': {\r\n    en: ['confounded', 'annoyed', 'distressed', 'exasperated'],\r\n    ru: ['озадаченный', 'раздражённый', 'огорчённый', 'изнурённый']\r\n  },\r\n  '😫': {\r\n    en: ['tired', 'exhausted', 'weary', 'worn out'],\r\n    ru: ['усталый', 'измученный', 'изнурённый', 'измотанный']\r\n  },\r\n\r\n  // Face tongue\r\n  '😩': {\r\n    en: ['weary', 'tired', 'exhausted', 'overwhelmed'],\r\n    ru: ['изнурённый', 'уставший', 'измученный', 'перегруженный']\r\n  },\r\n  '🥺': {\r\n    en: ['pleading', 'begging', 'cute', 'vulnerable'],\r\n    ru: ['умоляющий', 'просьба', 'милый', 'уязвимый']\r\n  },\r\n  '😢': {\r\n    en: ['cry', 'sad', 'tear', 'sorrow'],\r\n    ru: ['плакать', 'грустный', 'слеза', 'печаль']\r\n  },\r\n  '😭': {\r\n    en: ['crying', 'tearful', 'sad', 'heartbroken'],\r\n    ru: ['плачущий', 'со слезами', 'грустный', 'с разбитым сердцем']\r\n  },\r\n  '😤': {\r\n    en: ['triumphant', 'exasperated', 'proud', 'angry'],\r\n    ru: ['триумфальный', 'раздражённый', 'гордый', 'сердитый']\r\n  },\r\n  '😠': {\r\n    en: ['angry', 'mad', 'annoyed', 'irate'],\r\n    ru: ['сердитый', 'злой', 'раздражённый', 'яростный']\r\n  },\r\n  '😡': {\r\n    en: ['pouting', 'mad', 'furious', 'irate'],\r\n    ru: ['надувшийся', 'злой', 'яростный', 'взбешённый']\r\n  },\r\n  '🤬': {\r\n    en: ['cursing', 'swearing', 'angry', 'foul language'],\r\n    ru: ['ругательства', 'мат', 'сердитый', 'неприличный']\r\n  },\r\n  '🤯': {\r\n    en: ['mind blown', 'shocked', 'amazed', 'stunned'],\r\n    ru: ['взрыв мозга', 'шокированный', 'поражённый', 'ошеломлённый']\r\n  },\r\n  '😳': {\r\n    en: ['flushed', 'embarrassed', 'shocked', 'awkward'],\r\n    ru: ['покрасневший', 'смущённый', 'шокированный', 'неловкий']\r\n  },\r\n  '🥵': {\r\n    en: ['hot', 'overheated', 'sweaty', 'exhausted'],\r\n    ru: ['горячий', 'перегретый', 'потный', 'изнурённый']\r\n  },\r\n  '🥶': {\r\n    en: ['cold', 'freezing', 'chilly', 'frozen'],\r\n    ru: ['холодный', 'замерзающий', 'прохладный', 'замороженный']\r\n  },\r\n  '😱': {\r\n    en: ['screaming', 'horror', 'shock', 'fear'],\r\n    ru: ['кричащий', 'ужас', 'шок', 'страх']\r\n  },\r\n  '😨': {\r\n    en: ['fearful', 'scared', 'anxious', 'nervous'],\r\n    ru: ['боязливый', 'испуганный', 'тревожный', 'нервный']\r\n  },\r\n  '😰': {\r\n    en: ['anxious', 'nervous', 'sweating', 'scared'],\r\n    ru: ['тревожный', 'нервный', 'потеющий', 'испуганный']\r\n  },\r\n  '😥': {\r\n    en: ['disappointed', 'sad', 'pensive', 'teary'],\r\n    ru: ['разочарованный', 'грустный', 'задумчивый', 'со слезами']\r\n  },\r\n  '😓': {\r\n    en: ['cold sweat', 'nervous', 'anxious', 'tired'],\r\n    ru: ['холодный пот', 'нервный', 'тревожный', 'усталый']\r\n  },\r\n  '🤗': {\r\n    en: ['hug', 'embrace', 'caring', 'love'],\r\n    ru: ['объятие', 'принимать', 'заботливый', 'любовь']\r\n  },\r\n  '🤔': {\r\n    en: ['thinking', 'pondering', 'curious', 'confused'],\r\n    ru: ['думающий', 'размышляющий', 'любопытный', 'смущённый']\r\n  },\r\n  '🤭': {\r\n    en: ['guilty', 'shy', 'embarrassed', 'oops'],\r\n    ru: ['виноватый', 'застенчивый', 'смущённый', 'упс']\r\n  },\r\n  '🤫': {\r\n    en: ['quiet', 'secret', 'hush', 'shh'],\r\n    ru: ['тихий', 'секрет', 'тишина', 'ш-ш']\r\n  },\r\n\r\n  // Face negative\r\n  '🤥': {\r\n    en: ['lying', 'fib', 'deceitful', 'dishonest'],\r\n    ru: ['врущий', 'ложь', 'обманчивый', 'нечестный']\r\n  },\r\n  '😶': {\r\n    en: ['speechless', 'mute', 'quiet', 'blank'],\r\n    ru: ['безмолвный', 'немой', 'тихий', 'пустой']\r\n  },\r\n  '😐': {\r\n    en: ['neutral', 'expressionless', 'indifferent', 'flat'],\r\n    ru: ['нейтральный', 'без выражения', 'безразличный', 'плоский']\r\n  },\r\n  '😑': {\r\n    en: ['deadpan', 'expressionless', 'blank', 'unemotional'],\r\n    ru: ['без эмоций', 'без выражения', 'пустой', 'неэмоциональный']\r\n  },\r\n  '😬': {\r\n    en: ['grimace', 'awkward', 'nervous', 'tense'],\r\n    ru: ['гримаса', 'неловко', 'нервный', 'напряжённый']\r\n  },\r\n  '🙄': {\r\n    en: ['eye roll', 'sarcastic', 'disdain', 'bored'],\r\n    ru: ['закатывание глаз', 'саркастичный', 'пренебрежительный', 'скучный']\r\n  },\r\n  '😯': {\r\n    en: ['hushed', 'surprised', 'shocked', 'amazed'],\r\n    ru: ['тихий', 'удивлённый', 'шокированный', 'поражённый']\r\n  },\r\n  '😦': {\r\n    en: ['frowning', 'dismayed', 'shocked', 'surprised'],\r\n    ru: ['хмурый', 'огорчённый', 'шокированный', 'удивлённый']\r\n  },\r\n  '😧': {\r\n    en: ['astonished', 'stunned', 'surprised', 'speechless'],\r\n    ru: ['изумлённый', 'ошеломлённый', 'удивлённый', 'безмолвный']\r\n  },\r\n  '😮': {\r\n    en: ['open mouth', 'surprised', 'shocked', 'amazed'],\r\n    ru: ['открытый рот', 'удивлённый', 'шокированный', 'поражённый']\r\n  },\r\n  '😲': {\r\n    en: ['astonished', 'stunned', 'shocked', 'in awe'],\r\n    ru: ['изумлённый', 'ошеломлённый', 'шокированный', 'в благоговении']\r\n  },\r\n  '🥱': {\r\n    en: ['yawning', 'sleepy', 'tired', 'bored'],\r\n    ru: ['зевота', 'сонный', 'усталый', 'скучный']\r\n  },\r\n  '😴': {\r\n    en: ['sleeping', 'tired', 'napping', 'dozing'],\r\n    ru: ['спящий', 'усталый', 'дремлющий', 'засыпающий']\r\n  },\r\n  '🤤': {\r\n    en: ['drooling', 'desire', 'craving', 'hungry'],\r\n    ru: ['слюнявый', 'желание', 'тяга', 'голодный']\r\n  },\r\n  '😪': {\r\n    en: ['sleepy', 'drowsy', 'tired', 'nodding'],\r\n    ru: ['сонный', 'вялый', 'усталый', 'кивающий']\r\n  },\r\n  '😵': {\r\n    en: ['dizzy', 'knocked out', 'stunned', 'confused'],\r\n    ru: ['головокружительный', 'вырубленный', 'ошеломлённый', 'сбитый с толку']\r\n  },\r\n  '🤐': {\r\n    en: ['zipper-mouth', 'secretive', 'quiet', 'mute'],\r\n    ru: ['закрытый рот', 'секретный', 'тихий', 'немой']\r\n  },\r\n  '🥴': {\r\n    en: ['woozy', 'tipsy', 'dizzy', 'unsteady'],\r\n    ru: ['ошеломлённый', 'подошедший', 'головокружительный', 'неустойчивый']\r\n  },\r\n  '🤢': {\r\n    en: ['nauseated', 'sick', 'disgusted', 'vomit'],\r\n    ru: ['тошнотворный', 'больной', 'отвратительный', 'рвота']\r\n  },\r\n  '🤮': {\r\n    en: ['vomiting', 'nauseous', 'sick', 'disgust'],\r\n    ru: ['рвота', 'тошнотворный', 'больной', 'отвратительный']\r\n  },\r\n  '🤧': {\r\n    en: ['sneezing', 'ill', 'sick', 'allergy'],\r\n    ru: ['чихание', 'болен', 'больной', 'аллергия']\r\n  },\r\n\r\n  // Face costume\r\n  '😷': {\r\n    en: ['mask', 'sick', 'ill', 'health'],\r\n    ru: ['маска', 'больной', 'нездоровый', 'здоровье']\r\n  },\r\n  '🤒': {\r\n    en: ['fever', 'sick', 'ill', 'unwell'],\r\n    ru: ['лихорадка', 'больной', 'нездоровый', 'неважно']\r\n  },\r\n  '🤕': {\r\n    en: ['injured', 'hurt', 'bandaged', 'pain'],\r\n    ru: ['раненый', 'повреждённый', 'забинтованный', 'боль']\r\n  },\r\n  '🤑': {\r\n    en: ['money', 'rich', 'greedy', 'cash'],\r\n    ru: ['деньги', 'богатый', 'жадный', 'наличные']\r\n  },\r\n  '🤠': {\r\n    en: ['cowboy', 'hat', 'western', 'fun'],\r\n    ru: ['ковбой', 'шляпа', 'вестерн', 'веселье']\r\n  },\r\n  '😈': {\r\n    en: ['devil', 'mischievous', 'naughty', 'sinister'],\r\n    ru: ['дьявол', 'озорной', 'непослушный', 'зловещий']\r\n  },\r\n  '👿': {\r\n    en: ['angry', 'devil', 'evil', 'fiendish'],\r\n    ru: ['сердитый', 'дьявол', 'злой', 'зловещий']\r\n  },\r\n  '👹': {\r\n    en: ['ogre', 'demon', 'monster', 'scary'],\r\n    ru: ['огр', 'демон', 'монстр', 'страшный']\r\n  },\r\n  '👺': {\r\n    en: ['goblin', 'troll', 'spooky', 'creepy'],\r\n    ru: ['гоблин', 'тролль', 'жуткий', 'страшный']\r\n  },\r\n  '🤡': {\r\n    en: ['clown', 'silly', 'funny', 'circus'],\r\n    ru: ['клоун', 'глупый', 'смешной', 'цирк']\r\n  },\r\n  '💩': {\r\n    en: ['poop', 'crap', 'feces', 'funny'],\r\n    ru: ['какашка', 'дерьмо', 'фекалии', 'смешной']\r\n  },\r\n  '👻': {\r\n    en: ['ghost', 'spirit', 'haunted', 'scary'],\r\n    ru: ['призрак', 'дух', 'обитающий', 'страшный']\r\n  },\r\n  '💀': {\r\n    en: ['skull', 'death', 'creepy', 'spooky'],\r\n    ru: ['череп', 'смерть', 'жуткий', 'страшный']\r\n  },\r\n  '☠️': {\r\n    en: ['skull', 'danger', 'death', 'poison'],\r\n    ru: ['череп', 'опасность', 'смерть', 'яд']\r\n  },\r\n  '👽': {\r\n    en: ['alien', 'extraterrestrial', 'space', 'ufo'],\r\n    ru: ['инопланетянин', 'внеземной', 'космос', 'НЛО']\r\n  },\r\n  '👾': {\r\n    en: ['alien', 'monster', 'video game', 'retro'],\r\n    ru: ['инопланетянин', 'монстр', 'видеоигра', 'ретро']\r\n  },\r\n  '🤖': {\r\n    en: ['robot', 'machine', 'tech', 'android'],\r\n    ru: ['робот', 'машина', 'технология', 'андроид']\r\n  },\r\n  '🎃': {\r\n    en: ['pumpkin', 'halloween', 'spooky', 'festive'],\r\n    ru: ['тыква', 'Хэллоуин', 'жуткий', 'праздничный']\r\n  },\r\n\r\n  // Cat faces\r\n  '😺': {\r\n    en: ['smiling', 'cat', 'happy', 'playful'],\r\n    ru: ['улыбающийся', 'кот', 'счастливый', 'игривый']\r\n  },\r\n  '😸': {\r\n    en: ['grinning', 'cat', 'joyful', 'cheerful'],\r\n    ru: ['широко улыбающийся', 'кот', 'радостный', 'весёлый']\r\n  },\r\n  '😹': {\r\n    en: ['tearful', 'joy', 'cat', 'laughing'],\r\n    ru: ['со слезами', 'радость', 'кот', 'смеющийся']\r\n  },\r\n  '😻': {\r\n    en: ['heart', 'cat', 'love', 'adorable'],\r\n    ru: ['сердце', 'кот', 'любовь', 'милый']\r\n  },\r\n  '😼': {\r\n    en: ['smirking', 'cat', 'mischievous', 'sly'],\r\n    ru: ['ухмыляющийся', 'кот', 'озорной', 'хитрый']\r\n  },\r\n  '😽': {\r\n    en: ['kissing', 'cat', 'affection', 'cute'],\r\n    ru: ['целующий', 'кот', 'нежность', 'милый']\r\n  },\r\n  '🙀': {\r\n    en: ['surprised', 'cat', 'scared', 'shocked'],\r\n    ru: ['испуганный', 'кот', 'испуганный', 'шокированный']\r\n  },\r\n  '😿': {\r\n    en: ['crying', 'cat', 'sad', 'tearful'],\r\n    ru: ['плачущий', 'кот', 'грустный', 'со слезами']\r\n  },\r\n  '😾': {\r\n    en: ['angry', 'cat', 'annoyed', 'displeased'],\r\n    ru: ['сердитый', 'кот', 'раздражённый', 'недовольный']\r\n  },\r\n\r\n  //------------------------- People & Body -------------------------\r\n  // Hand gestures\r\n  '👋': {\r\n    en: ['wave', 'hello', 'goodbye', 'greeting'],\r\n    ru: ['махание', 'привет', 'прощание', 'приветствие']\r\n  },\r\n  '🤚': {\r\n    en: ['raised hand', 'stop', 'palm'],\r\n    ru: ['поднятая рука', 'стой', 'ладонь']\r\n  },\r\n  '🖐️': {\r\n    en: ['hand', 'high five', 'greeting'],\r\n    ru: ['рука', 'дай пять', 'приветствие']\r\n  },\r\n  '✋': {\r\n    en: ['stop', 'palm', 'high five'],\r\n    ru: ['стой', 'ладонь', 'дай пять']\r\n  },\r\n  '🖖': {\r\n    en: ['vulcan salute', 'live long', 'sci-fi'],\r\n    ru: ['салют Вулканцев', 'живи долго', 'научная фантастика']\r\n  },\r\n  '👌': {\r\n    en: ['okay', 'perfect', 'good'],\r\n    ru: ['ок', 'идеально', 'хорошо']\r\n  },\r\n  '🤌': {\r\n    en: ['pinched', 'precise', 'delicious'],\r\n    ru: ['сжатый', 'точный', 'вкусный']\r\n  },\r\n  '🤏': {\r\n    en: ['small', 'tiny', 'minuscule'],\r\n    ru: ['маленький', 'крошечный', 'микроскопический']\r\n  },\r\n  '✌️': {\r\n    en: ['peace', 'victory', 'v sign'],\r\n    ru: ['мир', 'победа', 'знак победы']\r\n  },\r\n  '🤞': {\r\n    en: ['fingers crossed', 'hope', 'luck'],\r\n    ru: ['скрещенные пальцы', 'надежда', 'удача']\r\n  },\r\n  '🤟': {\r\n    en: ['I love you', 'rock on', 'sign language'],\r\n    ru: ['я тебя люблю', 'рок он', 'язык жестов']\r\n  },\r\n  '🤘': {\r\n    en: ['rock', 'metal', 'horns'],\r\n    ru: ['рок', 'металл', 'рога']\r\n  },\r\n  '🤙': {\r\n    en: ['call me', 'hang loose', 'shaka'],\r\n    ru: ['позвони мне', 'расслабься', 'шак']\r\n  },\r\n  '👈': {\r\n    en: ['point left', 'direction', 'arrow'],\r\n    ru: ['указание влево', 'направление', 'стрелка']\r\n  },\r\n  '👉': {\r\n    en: ['point right', 'direction', 'arrow'],\r\n    ru: ['указание вправо', 'направление', 'стрелка']\r\n  },\r\n  '👆': {\r\n    en: ['point up', 'direction', 'up'],\r\n    ru: ['указание вверх', 'направление', 'вверх']\r\n  },\r\n  '🖕': {\r\n    en: ['middle finger', 'offensive', 'rude'],\r\n    ru: ['средний палец', 'оскорбительный', 'грубый']\r\n  },\r\n  '👇': {\r\n    en: ['point down', 'direction', 'down'],\r\n    ru: ['указание вниз', 'направление', 'вниз']\r\n  },\r\n  '☝️': {\r\n    en: ['point up', 'number one', 'important'],\r\n    ru: ['указание вверх', 'номер один', 'важный']\r\n  },\r\n  '👍': {\r\n    en: ['thumbs up', 'good', 'approve'],\r\n    ru: ['палец вверх', 'хорошо', 'одобрить']\r\n  },\r\n  '👎': {\r\n    en: ['thumbs down', 'bad', 'disapprove'],\r\n    ru: ['палец вниз', 'плохо', 'не одобрять']\r\n  },\r\n\r\n  // Hand symbols\r\n  '✊': {\r\n    en: ['fist', 'power', 'solidarity'],\r\n    ru: ['кулак', 'сила', 'солидарность']\r\n  },\r\n  '👊': {\r\n    en: ['punch', 'fist bump', 'hit'],\r\n    ru: ['удар', 'кулачок', 'ударить']\r\n  },\r\n  '🤛': {\r\n    en: ['left fist', 'punch', 'strike'],\r\n    ru: ['левая рука', 'удар', 'нанести удар']\r\n  },\r\n  '🤜': {\r\n    en: ['right fist', 'punch', 'strike'],\r\n    ru: ['правая рука', 'удар', 'нанести удар']\r\n  },\r\n  '👏': {\r\n    en: ['clap', 'applause', 'bravo'],\r\n    ru: ['хлопки', 'аплодисменты', 'браво']\r\n  },\r\n  '🙌': {\r\n    en: ['celebrate', 'praise', 'hooray'],\r\n    ru: ['торжествовать', 'хвалить', 'ура']\r\n  },\r\n  '👐': {\r\n    en: ['open hands', 'embrace', 'welcome'],\r\n    ru: ['раскрытые руки', 'объятие', 'добро пожаловать']\r\n  },\r\n  '🤲': {\r\n    en: ['palms', 'offering', 'receive'],\r\n    ru: ['ладони', 'предложение', 'получать']\r\n  },\r\n  '🤝': {\r\n    en: ['handshake', 'agreement', 'cooperation'],\r\n    ru: ['рукопожатие', 'соглашение', 'сотрудничество']\r\n  },\r\n  '🙏': {\r\n    en: ['pray', 'thanks', 'please'],\r\n    ru: ['молиться', 'спасибо', 'пожалуйста']\r\n  },\r\n\r\n  // Body parts\r\n  '✍️': {\r\n    en: ['writing', 'pen', 'signature'],\r\n    ru: ['письмо', 'ручка', 'подпись']\r\n  },\r\n  '💅': {\r\n    en: ['nail polish', 'beauty', 'manicure'],\r\n    ru: ['лак для ногтей', 'красота', 'маникюр']\r\n  },\r\n  '🤳': {\r\n    en: ['selfie', 'photo', 'camera'],\r\n    ru: ['селфи', 'фото', 'камера']\r\n  },\r\n  '💪': {\r\n    en: ['flex', 'strong', 'muscle'],\r\n    ru: ['сгибать', 'сильный', 'мышцы']\r\n  },\r\n  '🦾': {\r\n    en: ['mechanical arm', 'robotic', 'cyborg'],\r\n    ru: ['механическая рука', 'роботизированный', 'киборг']\r\n  },\r\n  '🦿': {\r\n    en: ['mechanical leg', 'prosthetic', 'robotic'],\r\n    ru: ['механическая нога', 'протез', 'роботизированный']\r\n  },\r\n  '🦵': {\r\n    en: ['leg', 'limb', 'lower body'],\r\n    ru: ['нога', 'конечность', 'нижняя часть тела']\r\n  },\r\n  '🦶': {\r\n    en: ['foot', 'toes', 'step'],\r\n    ru: ['нога', 'пальцы ноги', 'шаг']\r\n  },\r\n  '👂': {\r\n    en: ['ear', 'listening', 'sound'],\r\n    ru: ['ухо', 'слушание', 'звук']\r\n  },\r\n  '🦻': {\r\n    en: ['hearing aid', 'listening', 'assistive'],\r\n    ru: ['слуховой аппарат', 'слушание', 'помощь']\r\n  },\r\n  '👃': {\r\n    en: ['nose', 'smell', 'scent'],\r\n    ru: ['нос', 'запах', 'аромат']\r\n  },\r\n  '🧠': {\r\n    en: ['brain', 'intelligence', 'mind'],\r\n    ru: ['мозг', 'интеллект', 'ум']\r\n  },\r\n  '🫀': {\r\n    en: ['heart (organ)', 'anatomy', 'biology'],\r\n    ru: ['сердце (орган)', 'анатомия', 'биология']\r\n  },\r\n  '🫁': {\r\n    en: ['lungs', 'breath', 'organ'],\r\n    ru: ['легкие', 'дыхание', 'орган']\r\n  },\r\n  '🦷': {\r\n    en: ['tooth', 'dental', 'smile'],\r\n    ru: ['зуб', 'стоматология', 'улыбка']\r\n  },\r\n  '🦴': {\r\n    en: ['bone', 'skeleton', 'hard'],\r\n    ru: ['кость', 'скелет', 'твердый']\r\n  },\r\n  '👀': {\r\n    en: ['eyes', 'look', 'see'],\r\n    ru: ['глаза', 'смотреть', 'видеть']\r\n  },\r\n  '👁️': {\r\n    en: ['eye', 'vision', 'watch'],\r\n    ru: ['глаз', 'зрение', 'наблюдать']\r\n  },\r\n  '👅': {\r\n    en: ['tongue', 'taste', 'lick'],\r\n    ru: ['язык', 'вкус', 'лизать']\r\n  },\r\n  '👄': {\r\n    en: ['lips', 'kiss', 'mouth'],\r\n    ru: ['губы', 'поцелуй', 'рот']\r\n  },\r\n\r\n  // Person\r\n  '👶': {\r\n    en: ['baby', 'infant', 'cute'],\r\n    ru: ['младенец', 'ребенок', 'милый']\r\n  },\r\n  '🧒': {\r\n    en: ['child', 'kid', 'youth'],\r\n    ru: ['ребенок', 'малыш', 'юный']\r\n  },\r\n  '👦': {\r\n    en: ['boy', 'child', 'kid'],\r\n    ru: ['мальчик', 'ребенок', 'малыш']\r\n  },\r\n  '👧': {\r\n    en: ['girl', 'child', 'kid'],\r\n    ru: ['девочка', 'ребенок', 'малышка']\r\n  },\r\n  '🧑': {\r\n    en: ['person', 'human', 'individual'],\r\n    ru: ['человек', 'личность', 'индивид']\r\n  },\r\n  '👱': {\r\n    en: ['blonde', 'person', 'light hair'],\r\n    ru: ['блондин', 'человек', 'светлые волосы']\r\n  },\r\n  '👨': {\r\n    en: ['man', 'male', 'guy'],\r\n    ru: ['мужчина', 'мужской', 'парень']\r\n  },\r\n  '🧔': {\r\n    en: ['bearded', 'man', 'facial hair'],\r\n    ru: ['бородатый', 'мужчина', 'борода']\r\n  },\r\n  '👩': {\r\n    en: ['woman', 'female', 'lady'],\r\n    ru: ['женщина', 'женский', 'дама']\r\n  },\r\n  '🧓': {\r\n    en: ['elderly', 'senior', 'aged'],\r\n    ru: ['пожилой', 'старший', 'в преклонном возрасте']\r\n  },\r\n  '👴': {\r\n    en: ['old man', 'elderly', 'senior'],\r\n    ru: ['старик', 'пожилой', 'старший']\r\n  },\r\n  '👵': {\r\n    en: ['old woman', 'elderly', 'senior'],\r\n    ru: ['старуха', 'пожилая', 'старшая']\r\n  },\r\n  '🙍': {\r\n    en: ['frowning', 'sad', 'displeased'],\r\n    ru: ['хмурый', 'грустный', 'недовольный']\r\n  },\r\n  '🙎': {\r\n    en: ['pouting', 'angry', 'displeased'],\r\n    ru: ['надувшийся', 'сердитый', 'недовольный']\r\n  },\r\n  '🙅': {\r\n    en: ['no', 'prohibited', 'refusal'],\r\n    ru: ['нет', 'запрещено', 'отказ']\r\n  },\r\n  '🙆': {\r\n    en: ['ok', 'acceptable', 'okay'],\r\n    ru: ['ок', 'приемлемо', 'хорошо']\r\n  },\r\n  '💁': {\r\n    en: ['information', 'help', 'assistance'],\r\n    ru: ['информация', 'помощь', 'поддержка']\r\n  },\r\n  '🙋': {\r\n    en: ['raising hand', 'question', 'volunteer'],\r\n    ru: ['поднимающая руку', 'вопрос', 'доброволец']\r\n  },\r\n  '🧏': {\r\n    en: ['deaf', 'listening', 'silent'],\r\n    ru: ['глухой', 'слушающий', 'безмолвный']\r\n  },\r\n  '🙇': {\r\n    en: ['bowing', 'apologetic', 'respect'],\r\n    ru: ['наклон', 'извиняющийся', 'уважение']\r\n  },\r\n  '🤦': {\r\n    en: ['facepalm', 'disbelief', 'oops'],\r\n    ru: ['лицо ладонь', 'недоверие', 'упс']\r\n  },\r\n  '🤷': {\r\n    en: ['shrug', 'uncertain', 'indifferent'],\r\n    ru: ['пожимание плечами', 'неуверенный', 'безразличный']\r\n  },\r\n\r\n  // Professional\r\n  '👮': {\r\n    en: ['police', 'officer', 'law'],\r\n    ru: ['полицейский', 'офицер', 'закон']\r\n  },\r\n  '🕵️': {\r\n    en: ['detective', 'spy', 'investigate'],\r\n    ru: ['детектив', 'шпион', 'расследование']\r\n  },\r\n  '💂': {\r\n    en: ['guard', 'soldier', 'military'],\r\n    ru: ['страж', 'солдат', 'военный']\r\n  },\r\n  '🥷': {\r\n    en: ['ninja', 'stealth', 'assassin'],\r\n    ru: ['ниндзя', 'скрытность', 'ассассин']\r\n  },\r\n  '👷': {\r\n    en: ['construction', 'worker', 'helmet'],\r\n    ru: ['строитель', 'рабочий', 'шлем']\r\n  },\r\n  '🤴': {\r\n    en: ['prince', 'royalty', 'king'],\r\n    ru: ['принц', 'королевская семья', 'король']\r\n  },\r\n  '👸': {\r\n    en: ['princess', 'royalty', 'queen'],\r\n    ru: ['принцесса', 'королевская семья', 'королева']\r\n  },\r\n  '👳': {\r\n    en: ['turban', 'cultural', 'tradition'],\r\n    ru: ['тюрбан', 'культура', 'традиция']\r\n  },\r\n  '👲': {\r\n    en: ['man with cap', 'cultural', 'traditional'],\r\n    ru: ['мужчина в кепке', 'культура', 'традиционный']\r\n  },\r\n  '🧕': {\r\n    en: ['woman with headscarf', 'cultural', 'modest'],\r\n    ru: ['женщина в платке', 'культурная', 'скромная']\r\n  },\r\n  '🤵': {\r\n    en: ['tuxedo', 'groom', 'formal'],\r\n    ru: ['смокинг', 'жених', 'официальный']\r\n  },\r\n  '👰': {\r\n    en: ['bride', 'wedding', 'formal'],\r\n    ru: ['невеста', 'свадьба', 'официальный']\r\n  },\r\n  '🤰': {\r\n    en: ['pregnant', 'expecting', 'mother'],\r\n    ru: ['беременная', 'ожидающая', 'мать']\r\n  },\r\n  '🤱': {\r\n    en: ['nursing', 'mother', 'baby'],\r\n    ru: ['кормящая', 'мать', 'ребенок']\r\n  },\r\n  '👼': {\r\n    en: ['angel', 'cherub', 'divine'],\r\n    ru: ['ангел', 'херувим', 'божественный']\r\n  },\r\n  '🎅': {\r\n    en: ['santa', 'christmas', 'jolly'],\r\n    ru: ['Санта', 'Рождество', 'радостный']\r\n  },\r\n  '🤶': {\r\n    en: ['mrs claus', 'christmas', 'holiday'],\r\n    ru: ['миссис Клаус', 'Рождество', 'праздник']\r\n  },\r\n  '🦸': {\r\n    en: ['superhero', 'power', 'hero'],\r\n    ru: ['супергерой', 'сила', 'герой']\r\n  },\r\n  '🦹': {\r\n    en: ['villain', 'bad', 'criminal'],\r\n    ru: ['злодей', 'плохой', 'преступник']\r\n  },\r\n  '🧙': {\r\n    en: ['wizard', 'magic', 'sorcery'],\r\n    ru: ['волшебник', 'магия', 'колдовство']\r\n  },\r\n  '🧚': {\r\n    en: ['fairy', 'magic', 'mystical'],\r\n    ru: ['фея', 'магия', 'мистический']\r\n  },\r\n  '🧛': {\r\n    en: ['vampire', 'dracula', 'undead'],\r\n    ru: ['вампир', 'Дракула', 'нежить']\r\n  },\r\n  '🧜': {\r\n    en: ['mermaid', 'mythical', 'ocean'],\r\n    ru: ['русалка', 'мифическая', 'океан']\r\n  },\r\n\r\n  //------------------------- Animals & Nature -------------------------\r\n  // Mammals\r\n  '🐵': {\r\n    en: ['monkey', 'ape', 'funny', 'mammal'],\r\n    ru: ['обезьяна', 'примат', 'смешной', 'млекопитающее']\r\n  },\r\n  '🐒': {\r\n    en: ['monkey', 'primate', 'curious'],\r\n    ru: ['обезьяна', 'примат', 'любопытный']\r\n  },\r\n  '🦍': {\r\n    en: ['gorilla', 'ape', 'strong', 'wild'],\r\n    ru: ['горилла', 'обезьяна', 'сильный', 'дикий']\r\n  },\r\n  '🦧': {\r\n    en: ['orangutan', 'ape', 'wild', 'mammal'],\r\n    ru: ['орангутан', 'обезьяна', 'дикий', 'млекопитающее']\r\n  },\r\n  '🐶': {\r\n    en: ['dog', 'puppy', 'pet', 'mammal'],\r\n    ru: ['собака', 'щенок', 'домашний питомец', 'млекопитающее']\r\n  },\r\n  '🐕': {\r\n    en: ['dog', 'canine', 'pet'],\r\n    ru: ['собака', 'псовой', 'домашний питомец']\r\n  },\r\n  '🦮': {\r\n    en: ['guide dog', 'service', 'assistance'],\r\n    ru: ['собака-поводырь', 'служебная', 'помощь']\r\n  },\r\n  '🐕‍🦺': {\r\n    en: ['service dog', 'working', 'assistance'],\r\n    ru: ['служебная собака', 'рабочая', 'помощь']\r\n  },\r\n  '🐩': {\r\n    en: ['poodle', 'dog', 'pet', 'fancy'],\r\n    ru: ['пудель', 'собака', 'домашний питомец', 'элегантный']\r\n  },\r\n  '🐺': {\r\n    en: ['wolf', 'wild', 'howl'],\r\n    ru: ['волк', 'дикий', 'воет']\r\n  },\r\n  '🦊': {\r\n    en: ['fox', 'cunning', 'wild'],\r\n    ru: ['лиса', 'хитрая', 'дикая']\r\n  },\r\n  '🦝': {\r\n    en: ['raccoon', 'mischievous', 'wild'],\r\n    ru: ['енот', 'озорной', 'дикий']\r\n  },\r\n  '🐱': {\r\n    en: ['cat', 'pet', 'feline'],\r\n    ru: ['кот', 'домашний питомец', 'кошачий']\r\n  },\r\n  '🐈': {\r\n    en: ['cat', 'feline', 'pet'],\r\n    ru: ['кот', 'кошачий', 'домашний питомец']\r\n  },\r\n  '🐈‍⬛': {\r\n    en: ['black cat', 'mysterious', 'feline'],\r\n    ru: ['чёрный кот', 'загадочный', 'кошачий']\r\n  },\r\n  '🦁': {\r\n    en: ['lion', 'king', 'wild', 'courage'],\r\n    ru: ['лев', 'король', 'дикий', 'отвага']\r\n  },\r\n  '🐯': {\r\n    en: ['tiger', 'wild', 'stripes', 'fierce'],\r\n    ru: ['тигр', 'дикий', 'полосатый', 'свирепый']\r\n  },\r\n  '🐅': {\r\n    en: ['tiger', 'stripes', 'wild'],\r\n    ru: ['тигр', 'полосатый', 'дикий']\r\n  },\r\n  '🐆': {\r\n    en: ['leopard', 'spots', 'wild', 'fast'],\r\n    ru: ['леопард', 'пятна', 'дикий', 'быстрый']\r\n  },\r\n  '🐴': {\r\n    en: ['horse', 'ride', 'equine'],\r\n    ru: ['лошадь', 'езда', 'конный']\r\n  },\r\n  '🐎': {\r\n    en: ['horse', 'racing', 'equine'],\r\n    ru: ['лошадь', 'гонки', 'конный']\r\n  },\r\n  '🦄': {\r\n    en: ['unicorn', 'magical', 'fantasy'],\r\n    ru: ['единорог', 'волшебный', 'фэнтези']\r\n  },\r\n  '🦓': {\r\n    en: ['zebra', 'stripes', 'wild'],\r\n    ru: ['зебра', 'полосатая', 'дикая']\r\n  },\r\n  '🦌': {\r\n    en: ['deer', 'antlers', 'forest'],\r\n    ru: ['олень', 'рога', 'лес']\r\n  },\r\n  '🦬': {\r\n    en: ['bison', 'buffalo', 'wild'],\r\n    ru: ['бизон', 'буйвол', 'дикий']\r\n  },\r\n  '🐮': {\r\n    en: ['cow', 'farm', 'bovine'],\r\n    ru: ['корова', 'ферма', 'коровий']\r\n  },\r\n  '🐂': {\r\n    en: ['ox', 'bull', 'bovine'],\r\n    ru: ['вол', 'бык', 'коровий']\r\n  },\r\n  '🐃': {\r\n    en: ['water buffalo', 'bovine', 'farm'],\r\n    ru: ['водный буйвол', 'коровий', 'ферма']\r\n  },\r\n  '🐄': {\r\n    en: ['cow', 'bovine', 'farm'],\r\n    ru: ['корова', 'коровий', 'ферма']\r\n  },\r\n  '🐷': {\r\n    en: ['pig', 'farm', 'oink'],\r\n    ru: ['свинья', 'ферма', 'хрюк']\r\n  },\r\n  '🐖': {\r\n    en: ['pig', 'swine', 'farm'],\r\n    ru: ['свинья', 'свинное животное', 'ферма']\r\n  },\r\n  '🐗': {\r\n    en: ['boar', 'wild', 'pig'],\r\n    ru: ['кабан', 'дикий', 'свинья']\r\n  },\r\n  '🐽': {\r\n    en: ['pig nose', 'snout'],\r\n    ru: ['свинной нос', 'хоботок']\r\n  },\r\n  '🐏': {\r\n    en: ['ram', 'sheep', 'male'],\r\n    ru: ['баран', 'овца', 'самец']\r\n  },\r\n  '🐑': {\r\n    en: ['sheep', 'wool', 'farm'],\r\n    ru: ['овца', 'шерсть', 'ферма']\r\n  },\r\n  '🐐': {\r\n    en: ['goat', 'farm', 'bleat'],\r\n    ru: ['коза', 'ферма', 'блеет']\r\n  },\r\n  '🐪': {\r\n    en: ['camel', 'desert', 'hump'],\r\n    ru: ['верблюд', 'пустыня', 'горб']\r\n  },\r\n  '🐫': {\r\n    en: ['camel', 'two-hump', 'desert'],\r\n    ru: ['двугорбый верблюд', 'двугорбый', 'пустыня']\r\n  },\r\n  '🦙': {\r\n    en: ['llama', 'alpaca', 'cute'],\r\n    ru: ['лама', 'альпака', 'милый']\r\n  },\r\n  '🦒': {\r\n    en: ['giraffe', 'tall', 'spots'],\r\n    ru: ['жираф', 'высокий', 'пятна']\r\n  },\r\n  '🐘': {\r\n    en: ['elephant', 'trunk', 'large'],\r\n    ru: ['слон', 'хобот', 'большой']\r\n  },\r\n  '🦏': {\r\n    en: ['rhinoceros', 'horn', 'tough'],\r\n    ru: ['носорог', 'рог', 'жесткий']\r\n  },\r\n  '🦛': {\r\n    en: ['hippopotamus', 'water', 'large'],\r\n    ru: ['бегемот', 'вода', 'большой']\r\n  },\r\n\r\n  // Birds\r\n  '🦃': {\r\n    en: ['turkey', 'bird', 'thanksgiving'],\r\n    ru: ['индейка', 'птица', 'День благодарения']\r\n  },\r\n  '🐔': {\r\n    en: ['chicken', 'rooster', 'hen'],\r\n    ru: ['курица', 'петух', 'курица (самка)']\r\n  },\r\n  '🐓': {\r\n    en: ['rooster', 'chicken', 'bird'],\r\n    ru: ['петух', 'курица', 'птица']\r\n  },\r\n  '🐣': {\r\n    en: ['hatching chick', 'baby', 'bird'],\r\n    ru: ['вылупляющийся цыплёнок', 'малыш', 'птица']\r\n  },\r\n  '🐤': {\r\n    en: ['chick', 'small', 'bird'],\r\n    ru: ['цыплёнок', 'маленький', 'птица']\r\n  },\r\n  '🐥': {\r\n    en: ['baby chicken', 'cute', 'bird'],\r\n    ru: ['цыплёнок', 'милый', 'птица']\r\n  },\r\n  '🐦': {\r\n    en: ['bird', 'tweet', 'wing'],\r\n    ru: ['птица', 'чирик', 'крыло']\r\n  },\r\n  '🐧': {\r\n    en: ['penguin', 'cold', 'bird'],\r\n    ru: ['пингвин', 'холодный', 'птица']\r\n  },\r\n  '🕊️': {\r\n    en: ['dove', 'peace', 'bird'],\r\n    ru: ['голубь', 'мир', 'птица']\r\n  },\r\n  '🦅': {\r\n    en: ['eagle', 'wild', 'bird'],\r\n    ru: ['орёл', 'дикий', 'птица']\r\n  },\r\n  '🦆': {\r\n    en: ['duck', 'water', 'bird'],\r\n    ru: ['утка', 'вода', 'птица']\r\n  },\r\n  '🦢': {\r\n    en: ['swan', 'graceful', 'bird'],\r\n    ru: ['лебедь', 'грациозный', 'птица']\r\n  },\r\n  '🦉': {\r\n    en: ['owl', 'wise', 'night', 'bird'],\r\n    ru: ['сова', 'мудрая', 'ночная', 'птица']\r\n  },\r\n  '🦤': {\r\n    en: ['dodo', 'extinct', 'bird'],\r\n    ru: ['додо', 'вымершая', 'птица']\r\n  },\r\n  '🪶': {\r\n    en: ['feather', 'light', 'bird'],\r\n    ru: ['перо', 'лёгкое', 'птица']\r\n  },\r\n  '🦩': {\r\n    en: ['flamingo', 'pink', 'bird'],\r\n    ru: ['фламинго', 'розовый', 'птица']\r\n  },\r\n  '🦚': {\r\n    en: ['peacock', 'colorful', 'bird'],\r\n    ru: ['павлин', 'яркий', 'птица']\r\n  },\r\n  '🦜': {\r\n    en: ['parrot', 'talkative', 'colorful'],\r\n    ru: ['попугай', 'болтливый', 'яркий']\r\n  },\r\n\r\n  // Reptiles/Amphibians\r\n  '🐸': {\r\n    en: ['frog', 'amphibian', 'green'],\r\n    ru: ['лягушка', 'амфибия', 'зелёная']\r\n  },\r\n  '🐊': {\r\n    en: ['crocodile', 'reptile', 'danger'],\r\n    ru: ['крокодил', 'рептилия', 'опасность']\r\n  },\r\n  '🐢': {\r\n    en: ['turtle', 'slow', 'reptile'],\r\n    ru: ['черепаха', 'медленная', 'рептилия']\r\n  },\r\n  '🦎': {\r\n    en: ['lizard', 'reptile', 'scaly'],\r\n    ru: ['ящерица', 'рептилия', 'чешуйчатая']\r\n  },\r\n  '🐍': {\r\n    en: ['snake', 'reptile', 'slither'],\r\n    ru: ['змея', 'рептилия', 'ползать']\r\n  },\r\n  '🐲': {\r\n    en: ['dragon face', 'mythical', 'dragon'],\r\n    ru: ['лицо дракона', 'мифический', 'дракон']\r\n  },\r\n  '🐉': {\r\n    en: ['dragon', 'mythical', 'fire'],\r\n    ru: ['дракон', 'мифический', 'огонь']\r\n  },\r\n  '🦕': {\r\n    en: ['dinosaur', 'sauropod', 'prehistoric'],\r\n    ru: ['динозавр', 'зауропод', 'доисторический']\r\n  },\r\n  '🦖': {\r\n    en: ['dinosaur', 'T-Rex', 'prehistoric'],\r\n    ru: ['динозавр', 'Ти-Рекс', 'доисторический']\r\n  },\r\n\r\n  // Marine\r\n  '🐳': {\r\n    en: ['whale', 'ocean', 'large'],\r\n    ru: ['кит', 'океан', 'большой']\r\n  },\r\n  '🐋': {\r\n    en: ['whale', 'ocean', 'big'],\r\n    ru: ['кит', 'океан', 'огромный']\r\n  },\r\n  '🐬': {\r\n    en: ['dolphin', 'ocean', 'friendly'],\r\n    ru: ['дельфин', 'океан', 'дружелюбный']\r\n  },\r\n  '🦭': {\r\n    en: ['seal', 'marine', 'cute'],\r\n    ru: ['тюлень', 'морской', 'милый']\r\n  },\r\n  '🐟': {\r\n    en: ['fish', 'ocean', 'swim'],\r\n    ru: ['рыба', 'океан', 'плавать']\r\n  },\r\n  '🐠': {\r\n    en: ['tropical fish', 'ocean', 'colorful'],\r\n    ru: ['тропическая рыба', 'океан', 'яркая']\r\n  },\r\n  '🐡': {\r\n    en: ['blowfish', 'puffer', 'ocean'],\r\n    ru: ['рыба-иглобрюх', 'фугу', 'океан']\r\n  },\r\n  '🦈': {\r\n    en: ['shark', 'ocean', 'dangerous'],\r\n    ru: ['акула', 'океан', 'опасная']\r\n  },\r\n  '🐙': {\r\n    en: ['octopus', 'marine', 'tentacles'],\r\n    ru: ['осьминог', 'морской', 'щупальца']\r\n  },\r\n  '🐚': {\r\n    en: ['shell', 'beach', 'ocean'],\r\n    ru: ['ракушка', 'пляж', 'океан']\r\n  },\r\n  '🪸': {\r\n    en: ['coral', 'reef', 'ocean'],\r\n    ru: ['коралл', 'риф', 'океан']\r\n  },\r\n\r\n  // Insects\r\n  '🐌': {\r\n    en: ['snail', 'slow', 'mollusk'],\r\n    ru: ['улитка', 'медленная', 'моллюск']\r\n  },\r\n  '🦋': {\r\n    en: ['butterfly', 'insect', 'colorful'],\r\n    ru: ['бабочка', 'насекомое', 'яркая']\r\n  },\r\n  '🐛': {\r\n    en: ['caterpillar', 'insect', 'larva'],\r\n    ru: ['гусеница', 'насекомое', 'личинка']\r\n  },\r\n  '🐜': {\r\n    en: ['ant', 'small', 'insect'],\r\n    ru: ['муравей', 'маленький', 'насекомое']\r\n  },\r\n  '🐝': {\r\n    en: ['bee', 'insect', 'honey'],\r\n    ru: ['пчела', 'насекомое', 'мёд']\r\n  },\r\n  '🪲': {\r\n    en: ['beetle', 'insect', 'bug'],\r\n    ru: ['жук', 'насекомое', 'баг']\r\n  },\r\n  '🐞': {\r\n    en: ['ladybug', 'insect', 'lucky'],\r\n    ru: ['божья коровка', 'насекомое', 'счастливая']\r\n  },\r\n  '🦗': {\r\n    en: ['cricket', 'insect', 'chirp'],\r\n    ru: ['сверчок', 'насекомое', 'щебет']\r\n  },\r\n  '🪳': {\r\n    en: ['cockroach', 'insect', 'pest'],\r\n    ru: ['таракан', 'насекомое', 'вредитель']\r\n  },\r\n  '🕷️': {\r\n    en: ['spider', 'arachnid', 'insect'],\r\n    ru: ['паук', 'паукообразное', 'насекомое']\r\n  },\r\n  '🕸️': {\r\n    en: ['web', 'spider', 'trap'],\r\n    ru: ['паутина', 'паук', 'ловушка']\r\n  },\r\n  '🦂': {\r\n    en: ['scorpion', 'insect', 'venom'],\r\n    ru: ['скорпион', 'насекомое', 'яд']\r\n  },\r\n  '🦟': {\r\n    en: ['mosquito', 'insect', 'bite'],\r\n    ru: ['комар', 'насекомое', 'укус']\r\n  },\r\n  '🪰': {\r\n    en: ['fly', 'insect', 'buzz'],\r\n    ru: ['муха', 'насекомое', 'жужжание']\r\n  },\r\n  '🪱': {\r\n    en: ['worm', 'earth', 'invertebrate'],\r\n    ru: ['червь', 'земля', 'беспозвоночное']\r\n  },\r\n\r\n  // Plants\r\n  '🌸': {\r\n    en: ['cherry blossom', 'flower', 'spring'],\r\n    ru: ['сакура', 'цветок', 'весна']\r\n  },\r\n  '💮': {\r\n    en: ['white flower', 'flower', 'symbol'],\r\n    ru: ['белый цветок', 'цветок', 'символ']\r\n  },\r\n  '🏵️': {\r\n    en: ['rosette', 'flower', 'decorative'],\r\n    ru: ['розетка', 'цветок', 'декоративный']\r\n  },\r\n  '🌹': {\r\n    en: ['rose', 'flower', 'love', 'romance'],\r\n    ru: ['роза', 'цветок', 'любовь', 'романтика']\r\n  },\r\n  '🥀': {\r\n    en: ['wilted flower', 'sad', 'decay'],\r\n    ru: ['увядший цветок', 'грусть', 'разложение']\r\n  },\r\n  '🌺': {\r\n    en: ['hibiscus', 'flower', 'tropical'],\r\n    ru: ['гибискус', 'цветок', 'тропический']\r\n  },\r\n  '🌻': {\r\n    en: ['sunflower', 'flower', 'summer'],\r\n    ru: ['подсолнух', 'цветок', 'лето']\r\n  },\r\n  '🌼': {\r\n    en: ['blossom', 'flower', 'spring'],\r\n    ru: ['цветение', 'цветок', 'весна']\r\n  },\r\n  '🌷': {\r\n    en: ['tulip', 'flower', 'spring'],\r\n    ru: ['тюльпан', 'цветок', 'весна']\r\n  },\r\n  '🌱': {\r\n    en: ['seedling', 'plant', 'growth'],\r\n    ru: ['сеянец', 'растение', 'рост']\r\n  },\r\n  '🪴': {\r\n    en: ['potted plant', 'indoor', 'green'],\r\n    ru: ['растение в горшке', 'в помещении', 'зелёное']\r\n  },\r\n  '🌲': {\r\n    en: ['evergreen', 'tree', 'forest'],\r\n    ru: ['вечнозелёное', 'дерево', 'лес']\r\n  },\r\n  '🌳': {\r\n    en: ['tree', 'nature', 'shade'],\r\n    ru: ['дерево', 'природа', 'тень']\r\n  },\r\n  '🌴': {\r\n    en: ['palm tree', 'tropical', 'beach'],\r\n    ru: ['пальма', 'тропический', 'пляж']\r\n  },\r\n  '🌵': {\r\n    en: ['cactus', 'desert', 'succulent'],\r\n    ru: ['кактус', 'пустыня', 'суккулент']\r\n  },\r\n  '🌾': {\r\n    en: ['sheaf', 'grain', 'farm'],\r\n    ru: ['сноп', 'зерно', 'ферма']\r\n  },\r\n  '🌿': {\r\n    en: ['herb', 'plant', 'leaf'],\r\n    ru: ['трава', 'растение', 'лист']\r\n  },\r\n  '☘️': {\r\n    en: ['shamrock', 'luck', 'clover'],\r\n    ru: ['клевер', 'удача', 'трилистник']\r\n  },\r\n  '🍀': {\r\n    en: ['four-leaf clover', 'luck', 'green'],\r\n    ru: ['клевер с четырьмя листьями', 'удача', 'зелёный']\r\n  },\r\n  '🍁': {\r\n    en: ['maple leaf', 'autumn', 'fall'],\r\n    ru: ['кленовый лист', 'осень', 'осенний']\r\n  },\r\n  '🍂': {\r\n    en: ['fallen leaf', 'autumn', 'nature'],\r\n    ru: ['опавший лист', 'осень', 'природа']\r\n  },\r\n  '🍃': {\r\n    en: ['leaf', 'wind', 'nature'],\r\n    ru: ['лист', 'ветер', 'природа']\r\n  },\r\n\r\n  //------------------------- Food & Drink -------------------------\r\n  // Fruits\r\n  '🍎': {\r\n    en: ['apple', 'red', 'fruit', 'healthy'],\r\n    ru: ['яблоко', 'красное', 'фрукт', 'полезное']\r\n  },\r\n  '🍐': {\r\n    en: ['pear', 'fruit', 'green', 'juicy'],\r\n    ru: ['груша', 'фрукт', 'зелёная', 'сочная']\r\n  },\r\n  '🍊': {\r\n    en: ['orange', 'fruit', 'citrus', 'vitamin C'],\r\n    ru: ['апельсин', 'фрукт', 'цитрус', 'витамин C']\r\n  },\r\n  '🍋': {\r\n    en: ['lemon', 'citrus', 'sour', 'yellow'],\r\n    ru: ['лимон', 'цитрус', 'кислый', 'жёлтый']\r\n  },\r\n  '🍌': {\r\n    en: ['banana', 'fruit', 'yellow', 'tropical'],\r\n    ru: ['банан', 'фрукт', 'жёлтый', 'тропический']\r\n  },\r\n  '🍉': {\r\n    en: ['watermelon', 'fruit', 'summer', 'refreshing'],\r\n    ru: ['арбуз', 'фрукт', 'лето', 'освежающий']\r\n  },\r\n  '🍇': {\r\n    en: ['grapes', 'fruit', 'purple', 'vine'],\r\n    ru: ['виноград', 'фрукт', 'фиолетовый', 'виноградная лоза']\r\n  },\r\n  '🍓': {\r\n    en: ['strawberry', 'fruit', 'red', 'sweet'],\r\n    ru: ['клубника', 'фрукт', 'красная', 'сладкая']\r\n  },\r\n  '🫐': {\r\n    en: ['blueberry', 'fruit', 'blue', 'healthy'],\r\n    ru: ['черника', 'фрукт', 'синяя', 'полезная']\r\n  },\r\n  '🍈': {\r\n    en: ['melon', 'fruit', 'sweet', 'green'],\r\n    ru: ['дыня', 'фрукт', 'сладкая', 'зелёная']\r\n  },\r\n  '🍒': {\r\n    en: ['cherry', 'fruit', 'red', 'sweet'],\r\n    ru: ['вишня', 'фрукт', 'красная', 'сладкая']\r\n  },\r\n  '🍑': {\r\n    en: ['peach', 'fruit', 'fuzzy', 'sweet'],\r\n    ru: ['персик', 'фрукт', 'шероховатый', 'сладкий']\r\n  },\r\n  '🥭': {\r\n    en: ['mango', 'fruit', 'tropical', 'yellow'],\r\n    ru: ['манго', 'фрукт', 'тропический', 'жёлтый']\r\n  },\r\n  '🍍': {\r\n    en: ['pineapple', 'fruit', 'tropical', 'spiky'],\r\n    ru: ['ананас', 'фрукт', 'тропический', 'колючий']\r\n  },\r\n  '🥥': {\r\n    en: ['coconut', 'tropical', 'fruit', 'white'],\r\n    ru: ['кокос', 'тропический', 'фрукт', 'белый']\r\n  },\r\n  '🥝': {\r\n    en: ['kiwi', 'fruit', 'green', 'tart'],\r\n    ru: ['киви', 'фрукт', 'зелёный', 'кисловатый']\r\n  },\r\n\r\n  // Vegetables\r\n  '🍅': {\r\n    en: ['tomato', 'vegetable', 'red', 'juicy'],\r\n    ru: ['помидор', 'овощ', 'красный', 'сочный']\r\n  },\r\n  '🍆': {\r\n    en: ['eggplant', 'vegetable', 'purple', 'aubergine'],\r\n    ru: ['баклажан', 'овощ', 'фиолетовый', 'баклажан']\r\n  },\r\n  '🥑': {\r\n    en: ['avocado', 'vegetable', 'green', 'healthy'],\r\n    ru: ['авокадо', 'овощ', 'зелёный', 'полезный']\r\n  },\r\n  '🥦': {\r\n    en: ['broccoli', 'vegetable', 'green', 'healthy'],\r\n    ru: ['брокколи', 'овощ', 'зелёный', 'полезный']\r\n  },\r\n  '🥬': {\r\n    en: ['leafy greens', 'vegetable', 'lettuce', 'healthy'],\r\n    ru: ['листья салата', 'овощ', 'салат', 'полезный']\r\n  },\r\n  '🥒': {\r\n    en: ['cucumber', 'vegetable', 'green', 'fresh'],\r\n    ru: ['огурец', 'овощ', 'зелёный', 'свежий']\r\n  },\r\n  '🌶️': {\r\n    en: ['chili pepper', 'spicy', 'red', 'hot'],\r\n    ru: ['чили', 'острый', 'красный', 'горячий']\r\n  },\r\n  '🫑': {\r\n    en: ['bell pepper', 'vegetable', 'colorful', 'sweet'],\r\n    ru: ['болгарский перец', 'овощ', 'разноцветный', 'сладкий']\r\n  },\r\n  '🥕': {\r\n    en: ['carrot', 'vegetable', 'orange', 'crunchy'],\r\n    ru: ['морковь', 'овощ', 'оранжевая', 'хрустящая']\r\n  },\r\n  '🧄': {\r\n    en: ['garlic', 'vegetable', 'aromatic', 'flavorful'],\r\n    ru: ['чеснок', 'овощ', 'ароматный', 'вкусный']\r\n  },\r\n  '🧅': {\r\n    en: ['onion', 'vegetable', 'strong', 'flavor'],\r\n    ru: ['лук', 'овощ', 'сильный', 'вкус']\r\n  },\r\n  '🥔': {\r\n    en: ['potato', 'vegetable', 'starchy', 'brown'],\r\n    ru: ['картофель', 'овощ', 'крахмалистый', 'коричневый']\r\n  },\r\n  '🍠': {\r\n    en: ['sweet potato', 'vegetable', 'orange', 'starchy'],\r\n    ru: ['батат', 'овощ', 'оранжевый', 'крахмалистый']\r\n  },\r\n\r\n  // Breads & Baked Goods\r\n  '🥐': {\r\n    en: ['croissant', 'bread', 'pastry', 'flaky'],\r\n    ru: ['круассан', 'хлеб', 'выпечка', 'слоёный']\r\n  },\r\n  '🥯': {\r\n    en: ['bagel', 'bread', 'round', 'chewy'],\r\n    ru: ['бейгл', 'хлеб', 'круглый', 'жевательный']\r\n  },\r\n  '🍞': {\r\n    en: ['bread', 'baked', 'loaf', 'toast'],\r\n    ru: ['хлеб', 'выпеченный', 'буханка', 'тост']\r\n  },\r\n  '🥖': {\r\n    en: ['baguette', 'bread', 'French', 'long'],\r\n    ru: ['багет', 'хлеб', 'французский', 'длинный']\r\n  },\r\n  '🥨': {\r\n    en: ['pretzel', 'snack', 'salted', 'twisted'],\r\n    ru: ['претцель', 'закуска', 'солёный', 'скрученный']\r\n  },\r\n\r\n  // Prepared Foods\r\n  '🧀': {\r\n    en: ['cheese', 'dairy', 'yellow', 'savory'],\r\n    ru: ['сыр', 'молочный продукт', 'жёлтый', 'пикантный']\r\n  },\r\n  '🥚': {\r\n    en: ['egg', 'protein', 'breakfast'],\r\n    ru: ['яйцо', 'белок', 'завтрак']\r\n  },\r\n  '🍳': {\r\n    en: ['fried egg', 'breakfast', 'cooked'],\r\n    ru: ['жареное яйцо', 'завтрак', 'приготовленное']\r\n  },\r\n  '🥓': {\r\n    en: ['bacon', 'meat', 'crispy', 'breakfast'],\r\n    ru: ['бекон', 'мясо', 'хрустящий', 'завтрак']\r\n  },\r\n  '🥩': {\r\n    en: ['steak', 'meat', 'protein', 'beef'],\r\n    ru: ['стейк', 'мясо', 'белок', 'говядина']\r\n  },\r\n  '🍗': {\r\n    en: ['chicken leg', 'meat', 'drumstick', 'grilled'],\r\n    ru: ['куриная ножка', 'мясо', 'барабанная палочка', 'гриль']\r\n  },\r\n  '🍖': {\r\n    en: ['meat on bone', 'barbecue', 'protein'],\r\n    ru: ['мясо на кости', 'барбекю', 'белок']\r\n  },\r\n  '🦴': {\r\n    en: ['bone', 'meat', 'dog', 'skeleton'],\r\n    ru: ['кость', 'мясо', 'собака', 'скелет']\r\n  },\r\n  '🌭': {\r\n    en: ['hot dog', 'fast food', 'sausage'],\r\n    ru: ['хот-дог', 'фастфуд', 'колбаска']\r\n  },\r\n  '🍔': {\r\n    en: ['burger', 'fast food', 'beef', 'cheese'],\r\n    ru: ['бургер', 'фастфуд', 'говядина', 'сыр']\r\n  },\r\n  '🍟': {\r\n    en: ['french fries', 'fast food', 'crispy', 'potato'],\r\n    ru: ['картофель фри', 'фастфуд', 'хрустящий', 'картофель']\r\n  },\r\n  '🍕': {\r\n    en: ['pizza', 'cheese', 'fast food', 'Italian'],\r\n    ru: ['пицца', 'сыр', 'фастфуд', 'итальянская']\r\n  },\r\n  '🫓': {\r\n    en: ['flatbread', 'bread', 'soft'],\r\n    ru: ['лепёшка', 'хлеб', 'мягкая']\r\n  },\r\n  '🥪': {\r\n    en: ['sandwich', 'bread', 'meal'],\r\n    ru: ['бутерброд', 'хлеб', 'приём пищи']\r\n  },\r\n  '🥙': {\r\n    en: ['pita', 'bread', 'stuffed', 'Greek'],\r\n    ru: ['пита', 'хлеб', 'фаршированная', 'греческая']\r\n  },\r\n  '🧆': {\r\n    en: ['falafel', 'vegetarian', 'fried'],\r\n    ru: ['фалафель', 'вегетарианский', 'жареный']\r\n  },\r\n  '🌮': {\r\n    en: ['taco', 'Mexican', 'spicy'],\r\n    ru: ['тако', 'мексиканская', 'острая']\r\n  },\r\n  '🌯': {\r\n    en: ['burrito', 'Mexican', 'stuffed'],\r\n    ru: ['буррито', 'мексиканская', 'фаршированная']\r\n  },\r\n  '🫔': {\r\n    en: ['tamale', 'Mexican', 'corn'],\r\n    ru: ['тамале', 'мексиканская', 'кукурузная']\r\n  },\r\n  '🥗': {\r\n    en: ['salad', 'healthy', 'vegetable'],\r\n    ru: ['салат', 'здоровый', 'овощной']\r\n  },\r\n\r\n  // Asian Foods\r\n  '🥘': {\r\n    en: ['paella', 'stew', 'seafood'],\r\n    ru: ['паэлья', 'тушёное блюдо', 'морепродукты']\r\n  },\r\n  '🫕': {\r\n    en: ['fondue', 'melted', 'cheese'],\r\n    ru: ['фондю', 'расплавленный', 'сыр']\r\n  },\r\n  '🥫': {\r\n    en: ['canned food', 'storage', 'preserved'],\r\n    ru: ['консервы', 'хранение', 'сохранённое']\r\n  },\r\n  '🍝': {\r\n    en: ['spaghetti', 'pasta', 'Italian'],\r\n    ru: ['спагетти', 'паста', 'итальянская']\r\n  },\r\n  '🍜': {\r\n    en: ['ramen', 'noodles', 'Asian'],\r\n    ru: ['рамен', 'лапша', 'азиатская']\r\n  },\r\n  '🍲': {\r\n    en: ['hotpot', 'stew', 'broth'],\r\n    ru: ['хотпот', 'тушёное блюдо', 'бульон']\r\n  },\r\n  '🍛': {\r\n    en: ['curry', 'spicy', 'rice'],\r\n    ru: ['карри', 'острая', 'рис']\r\n  },\r\n  '🍣': {\r\n    en: ['sushi', 'Japanese', 'fish'],\r\n    ru: ['суши', 'японская', 'рыба']\r\n  },\r\n  '🍱': {\r\n    en: ['bento box', 'Japanese', 'meal'],\r\n    ru: ['бенто', 'японская', 'еда']\r\n  },\r\n  '🥟': {\r\n    en: ['dumpling', 'Asian', 'stuffed'],\r\n    ru: ['пельмени', 'азиатские', 'фаршированные']\r\n  },\r\n  '🦪': {\r\n    en: ['oyster', 'seafood', 'shellfish'],\r\n    ru: ['устрица', 'морепродукт', 'раковина']\r\n  },\r\n  '🍤': {\r\n    en: ['shrimp tempura', 'fried', 'seafood'],\r\n    ru: ['креветка темпура', 'жареная', 'морепродукт']\r\n  },\r\n  '🍙': {\r\n    en: ['rice ball', 'Japanese', 'onigiri'],\r\n    ru: ['рисовый шар', 'японская', 'онигири']\r\n  },\r\n  '🍚': {\r\n    en: ['cooked rice', 'staple', 'Asian'],\r\n    ru: ['варёный рис', 'основа', 'азиатская']\r\n  },\r\n  '🍘': {\r\n    en: ['rice cracker', 'snack', 'Japanese'],\r\n    ru: ['рисовый крекер', 'закуска', 'японская']\r\n  },\r\n  '🍥': {\r\n    en: ['fish cake', 'Japanese', 'swirl'],\r\n    ru: ['рыбный пирожок', 'японская', 'спираль']\r\n  },\r\n  '🥠': {\r\n    en: ['fortune cookie', 'Chinese', 'paper'],\r\n    ru: ['печенье с предсказанием', 'китайское', 'бумажное']\r\n  },\r\n  '🥮': {\r\n    en: ['mooncake', 'Chinese', 'festival'],\r\n    ru: ['лунное печенье', 'китайское', 'фестиваль']\r\n  },\r\n\r\n  // Sweets & Desserts\r\n  '🍢': {\r\n    en: ['skewered snack', 'street food'],\r\n    ru: ['шампур', 'уличная еда']\r\n  },\r\n  '🍡': {\r\n    en: ['dango', 'Japanese', 'mochi'],\r\n    ru: ['данго', 'японское', 'мочи']\r\n  },\r\n  '🍧': {\r\n    en: ['shaved ice', 'dessert', 'cold'],\r\n    ru: ['измельчённый лёд', 'десерт', 'холодный']\r\n  },\r\n  '🍨': {\r\n    en: ['ice cream', 'cold', 'sweet'],\r\n    ru: ['мороженое', 'холодное', 'сладкое']\r\n  },\r\n  '🍦': {\r\n    en: ['soft serve', 'dessert', 'cold'],\r\n    ru: ['мягкое мороженое', 'десерт', 'холодное']\r\n  },\r\n  '🥧': {\r\n    en: ['pie', 'dessert', 'baked'],\r\n    ru: ['пирог', 'десерт', 'выпеченный']\r\n  },\r\n  '🧁': {\r\n    en: ['cupcake', 'sweet', 'frosting'],\r\n    ru: ['капкейк', 'сладкий', 'с глазурью']\r\n  },\r\n  '🍰': {\r\n    en: ['cake', 'dessert', 'slice'],\r\n    ru: ['торт', 'десерт', 'кусок']\r\n  },\r\n  '🎂': {\r\n    en: ['birthday cake', 'celebration', 'sweet'],\r\n    ru: ['торт ко дню рождения', 'празднование', 'сладкий']\r\n  },\r\n  '🍮': {\r\n    en: ['flan', 'custard', 'dessert'],\r\n    ru: ['флан', 'кастард', 'десерт']\r\n  },\r\n  '🍭': {\r\n    en: ['lollipop', 'candy', 'sweet'],\r\n    ru: ['леденец', 'конфета', 'сладкий']\r\n  },\r\n  '🍬': {\r\n    en: ['candy', 'sweet', 'sugar'],\r\n    ru: ['конфета', 'сладость', 'сахар']\r\n  },\r\n  '🍫': {\r\n    en: ['chocolate', 'sweet', 'cocoa'],\r\n    ru: ['шоколад', 'сладкий', 'какао']\r\n  },\r\n  '🍿': {\r\n    en: ['popcorn', 'snack', 'buttery'],\r\n    ru: ['попкорн', 'закуска', 'масляный']\r\n  },\r\n  '🍩': {\r\n    en: ['doughnut', 'sweet', 'fried'],\r\n    ru: ['пончик', 'сладкий', 'жареный']\r\n  },\r\n  '🍪': {\r\n    en: ['cookie', 'sweet', 'baked'],\r\n    ru: ['печенье', 'сладкое', 'выпеченное']\r\n  },\r\n\r\n  // Drinks\r\n  '🫖': {\r\n    en: ['teapot', 'tea', 'hot'],\r\n    ru: ['чайник', 'чай', 'горячий']\r\n  },\r\n  '☕': {\r\n    en: ['coffee', 'hot drink', 'caffeine'],\r\n    ru: ['кофе', 'горячий напиток', 'кофеин']\r\n  },\r\n  '🍵': {\r\n    en: ['green tea', 'hot', 'Japanese'],\r\n    ru: ['зелёный чай', 'горячий', 'японский']\r\n  },\r\n  '🧃': {\r\n    en: ['juice', 'drink', 'fruit'],\r\n    ru: ['сок', 'напиток', 'фруктовый']\r\n  },\r\n  '🥤': {\r\n    en: ['soft drink', 'straw', 'fast food'],\r\n    ru: ['безалкогольный напиток', 'с трубочкой', 'фастфуд']\r\n  },\r\n  '🧋': {\r\n    en: ['bubble tea', 'milk tea', 'boba'],\r\n    ru: ['бабл-ти', 'молочный чай', 'боба']\r\n  },\r\n  '🍶': {\r\n    en: ['sake', 'Japanese', 'rice wine'],\r\n    ru: ['саке', 'японский', 'рисовое вино']\r\n  },\r\n  '🍺': {\r\n    en: ['beer', 'drink', 'alcohol'],\r\n    ru: ['пиво', 'напиток', 'алкоголь']\r\n  },\r\n  '🍻': {\r\n    en: ['cheers', 'beer', 'drinking'],\r\n    ru: ['ура', 'пиво', 'выпивка']\r\n  },\r\n  '🥂': {\r\n    en: ['champagne', 'celebration', 'toast'],\r\n    ru: ['шампанское', 'празднование', 'тост']\r\n  },\r\n  '🍷': {\r\n    en: ['wine', 'drink', 'red'],\r\n    ru: ['вино', 'напиток', 'красное']\r\n  },\r\n  '🥃': {\r\n    en: ['whiskey', 'liquor', 'alcohol'],\r\n    ru: ['виски', 'ликёр', 'алкоголь']\r\n  },\r\n  '🍸': {\r\n    en: ['cocktail', 'martini', 'drink'],\r\n    ru: ['коктейль', 'мартини', 'напиток']\r\n  },\r\n  '🍹': {\r\n    en: ['tropical drink', 'cocktail', 'summer'],\r\n    ru: ['тропический напиток', 'коктейль', 'лето']\r\n  },\r\n  '🧉': {\r\n    en: ['mate', 'South American', 'tea'],\r\n    ru: ['мате', 'южноамериканский', 'чай']\r\n  },\r\n  '🍾': {\r\n    en: ['champagne bottle', 'celebration', 'party'],\r\n    ru: ['бутылка шампанского', 'празднование', 'вечеринка']\r\n  },\r\n\r\n  //------------------------- Activities & Objects -------------------------\r\n  // Sports\r\n  '⚽': {\r\n    en: ['soccer', 'football', 'sports', 'ball'],\r\n    ru: ['футбол', 'футбол', 'спорт', 'мяч']\r\n  },\r\n  '🏀': {\r\n    en: ['basketball', 'sports', 'hoop', 'dunk'],\r\n    ru: ['баскетбол', 'спорт', 'кольцо', 'данк']\r\n  },\r\n  '🏈': {\r\n    en: ['American football', 'sports', 'rugby'],\r\n    ru: ['американский футбол', 'спорт', 'регби']\r\n  },\r\n  '⚾': {\r\n    en: ['baseball', 'sports', 'bat'],\r\n    ru: ['бейсбол', 'спорт', 'бита']\r\n  },\r\n  '🥎': {\r\n    en: ['softball', 'sports', 'ball'],\r\n    ru: ['софтбол', 'спорт', 'мяч']\r\n  },\r\n  '🎾': {\r\n    en: ['tennis', 'sports', 'racket'],\r\n    ru: ['теннис', 'спорт', 'ракетка']\r\n  },\r\n  '🏐': {\r\n    en: ['volleyball', 'sports', 'beach'],\r\n    ru: ['волейбол', 'спорт', 'пляж']\r\n  },\r\n  '🏉': {\r\n    en: ['rugby', 'sports', 'oval ball'],\r\n    ru: ['регби', 'спорт', 'овальный мяч']\r\n  },\r\n  '🥏': {\r\n    en: ['frisbee', 'throw', 'flying disc'],\r\n    ru: ['фрисби', 'бросок', 'летающий диск']\r\n  },\r\n  '🎱': {\r\n    en: ['billiards', '8 ball', 'pool'],\r\n    ru: ['бильярд', 'восьмёрка', 'пул']\r\n  },\r\n  '🪀': {\r\n    en: ['yo-yo', 'toy', 'string'],\r\n    ru: ['йо-йо', 'игрушка', 'верёвка']\r\n  },\r\n  '🏓': {\r\n    en: ['ping pong', 'table tennis', 'sports'],\r\n    ru: ['пинг-понг', 'настольный теннис', 'спорт']\r\n  },\r\n  '🏸': {\r\n    en: ['badminton', 'racket', 'sports'],\r\n    ru: ['бадминтон', 'ракетка', 'спорт']\r\n  },\r\n  '🏒': {\r\n    en: ['hockey', 'ice hockey', 'sports'],\r\n    ru: ['хоккей', 'хоккей на льду', 'спорт']\r\n  },\r\n  '🏑': {\r\n    en: ['field hockey', 'sports', 'stick'],\r\n    ru: ['хоккей на траве', 'спорт', 'клюшка']\r\n  },\r\n  '🥍': {\r\n    en: ['lacrosse', 'sports', 'net'],\r\n    ru: ['лакросс', 'спорт', 'сеть']\r\n  },\r\n  '🏏': {\r\n    en: ['cricket', 'bat', 'sports'],\r\n    ru: ['крикет', 'бита', 'спорт']\r\n  },\r\n  '⛳': {\r\n    en: ['golf', 'sports', 'hole in one'],\r\n    ru: ['гольф', 'спорт', 'луночка']\r\n  },\r\n  '🪁': {\r\n    en: ['kite', 'flying', 'wind'],\r\n    ru: ['змей', 'летать', 'ветер']\r\n  },\r\n  '🎣': {\r\n    en: ['fishing', 'hook', 'water'],\r\n    ru: ['рыбалка', 'крючок', 'вода']\r\n  },\r\n  '🤿': {\r\n    en: ['diving', 'underwater', 'snorkel'],\r\n    ru: ['дайвинг', 'под водой', 'снорклинг']\r\n  },\r\n  '🎽': {\r\n    en: ['running', 'jersey', 'athlete'],\r\n    ru: ['бег', 'майка', 'спортсмен']\r\n  },\r\n  '🛹': {\r\n    en: ['skateboard', 'sports', 'extreme'],\r\n    ru: ['скейтборд', 'спорт', 'экстрим']\r\n  },\r\n  '🛼': {\r\n    en: ['roller skate', 'sports', 'wheels'],\r\n    ru: ['роликовые коньки', 'спорт', 'колёса']\r\n  },\r\n  '🛷': {\r\n    en: ['sled', 'winter', 'snow'],\r\n    ru: ['санки', 'зима', 'снег']\r\n  },\r\n  '⛸️': {\r\n    en: ['ice skate', 'winter', 'sports'],\r\n    ru: ['коньки', 'зима', 'спорт']\r\n  },\r\n  '🥌': {\r\n    en: ['curling', 'winter', 'stone'],\r\n    ru: ['керлинг', 'зима', 'камень']\r\n  },\r\n  '⛷️': {\r\n    en: ['skiing', 'winter', 'snow'],\r\n    ru: ['лыжи', 'зима', 'снег']\r\n  },\r\n  '🏂': {\r\n    en: ['snowboarding', 'sports', 'snow'],\r\n    ru: ['сноуборд', 'спорт', 'снег']\r\n  },\r\n  '🪂': {\r\n    en: ['parachute', 'skydiving', 'air'],\r\n    ru: ['парашют', 'скайдайвинг', 'воздух']\r\n  },\r\n  '🏋️': {\r\n    en: ['weightlifting', 'gym', 'strong'],\r\n    ru: ['тяжёлая атлетика', 'спортзал', 'сильный']\r\n  },\r\n  '🤼': {\r\n    en: ['wrestling', 'fight', 'grapple'],\r\n    ru: ['борьба', 'драка', 'схватка']\r\n  },\r\n  '🤸': {\r\n    en: ['gymnastics', 'acrobatics', 'flip'],\r\n    ru: ['гимнастика', 'акробатика', 'сальто']\r\n  },\r\n  '⛹️': {\r\n    en: ['basketball', 'dribbling', 'sports'],\r\n    ru: ['баскетбол', 'дриблинг', 'спорт']\r\n  },\r\n  '🤾': {\r\n    en: ['handball', 'sports', 'throw'],\r\n    ru: ['гандбол', 'спорт', 'бросок']\r\n  },\r\n  '🏌️': {\r\n    en: ['golf', 'swing', 'sports'],\r\n    ru: ['гольф', 'мах', 'спорт']\r\n  },\r\n  '🏇': {\r\n    en: ['horse racing', 'sports', 'jockey'],\r\n    ru: ['конные скачки', 'спорт', 'жокей']\r\n  },\r\n  '🧘': {\r\n    en: ['meditation', 'yoga', 'zen'],\r\n    ru: ['медитация', 'йога', 'дзен']\r\n  },\r\n  '🏄': {\r\n    en: ['surfing', 'wave', 'water'],\r\n    ru: ['серфинг', 'волна', 'вода']\r\n  },\r\n  '🏊': {\r\n    en: ['swimming', 'water', 'pool'],\r\n    ru: ['плавание', 'вода', 'бассейн']\r\n  },\r\n\r\n  // Activities (cultural/entertainment)\r\n  '🤽': {\r\n    en: ['water polo', 'sports', 'swimming'],\r\n    ru: ['водное поло', 'спорт', 'плавание']\r\n  },\r\n  '🚣': {\r\n    en: ['rowing', 'boat', 'water'],\r\n    ru: ['гребля', 'лодка', 'вода']\r\n  },\r\n  '🧗': {\r\n    en: ['rock climbing', 'sports', 'mountain'],\r\n    ru: ['скалолазание', 'спорт', 'гора']\r\n  },\r\n  '🚴': {\r\n    en: ['cycling', 'bike', 'sports'],\r\n    ru: ['велоспорт', 'велосипед', 'спорт']\r\n  },\r\n  '🚵': {\r\n    en: ['mountain biking', 'sports', 'outdoor'],\r\n    ru: ['маунтинбайк', 'спорт', 'на свежем воздухе']\r\n  },\r\n  '🎪': {\r\n    en: ['circus', 'tent', 'performance'],\r\n    ru: ['цирк', 'палатка', 'выступление']\r\n  },\r\n  '🎭': {\r\n    en: ['theater', 'drama', 'acting'],\r\n    ru: ['театр', 'драма', 'актерство']\r\n  },\r\n  '🎨': {\r\n    en: ['painting', 'art', 'colors'],\r\n    ru: ['живопись', 'искусство', 'цвета']\r\n  },\r\n  '🎬': {\r\n    en: ['film', 'clapperboard', 'movie'],\r\n    ru: ['фильм', 'хлопушка', 'кино']\r\n  },\r\n  '🎤': {\r\n    en: ['microphone', 'singing', 'music'],\r\n    ru: ['микрофон', 'пение', 'музыка']\r\n  },\r\n  '🎧': {\r\n    en: ['headphones', 'music', 'listening'],\r\n    ru: ['наушники', 'музыка', 'прослушивание']\r\n  },\r\n  '🎼': {\r\n    en: ['music', 'sheet', 'notes'],\r\n    ru: ['музыка', 'ноты', 'сценарий']\r\n  },\r\n  '🎹': {\r\n    en: ['piano', 'keyboard', 'music'],\r\n    ru: ['пианино', 'клавиатура', 'музыка']\r\n  },\r\n  '🥁': {\r\n    en: ['drums', 'music', 'percussion'],\r\n    ru: ['барабаны', 'музыка', 'ударные']\r\n  },\r\n  '🎷': {\r\n    en: ['saxophone', 'jazz', 'music'],\r\n    ru: ['саксофон', 'джаз', 'музыка']\r\n  },\r\n  '🎺': {\r\n    en: ['trumpet', 'brass', 'music'],\r\n    ru: ['труба', 'латунь', 'музыка']\r\n  },\r\n  '🎸': {\r\n    en: ['guitar', 'music', 'rock'],\r\n    ru: ['гитара', 'музыка', 'рок']\r\n  },\r\n  '🎻': {\r\n    en: ['violin', 'music', 'strings'],\r\n    ru: ['скрипка', 'музыка', 'струны']\r\n  },\r\n  '🎲': {\r\n    en: ['dice', 'game', 'board game'],\r\n    ru: ['кубики', 'игра', 'настольная игра']\r\n  },\r\n  '🎯': {\r\n    en: ['dart', 'target', 'game'],\r\n    ru: ['дротик', 'цель', 'игра']\r\n  },\r\n  '🎳': {\r\n    en: ['bowling', 'sports', 'pins'],\r\n    ru: ['боулинг', 'спорт', 'кегли']\r\n  },\r\n  '🎮': {\r\n    en: ['video game', 'console', 'gaming'],\r\n    ru: ['видеоигра', 'консоль', 'игры']\r\n  },\r\n  '🎰': {\r\n    en: ['slot machine', 'casino', 'gambling'],\r\n    ru: ['игровой автомат', 'казино', 'азартные игры']\r\n  },\r\n  '🧩': {\r\n    en: ['puzzle', 'pieces', 'brain teaser'],\r\n    ru: ['головоломка', 'кусочки', 'разминка для мозга']\r\n  },\r\n  '🎫': {\r\n    en: ['ticket', 'event', 'entry'],\r\n    ru: ['билет', 'событие', 'вход']\r\n  },\r\n  '🎟️': {\r\n    en: ['admission', 'ticket', 'event'],\r\n    ru: ['входной билет', 'билет', 'событие']\r\n  },\r\n\r\n  //------------------------- Travel & Places -------------------------\r\n  // Land Transport\r\n  '🚗': {\r\n    en: ['car', 'automobile', 'vehicle', 'transport'],\r\n    ru: ['автомобиль', 'машина', 'транспортное средство', 'транспорт']\r\n  },\r\n  '🚕': {\r\n    en: ['taxi', 'cab', 'transport', 'vehicle'],\r\n    ru: ['такси', 'маршрутка', 'транспорт', 'транспортное средство']\r\n  },\r\n  '🚙': {\r\n    en: ['suv', 'vehicle', 'transport'],\r\n    ru: ['внедорожник', 'транспортное средство', 'транспорт']\r\n  },\r\n  '🚌': {\r\n    en: ['bus', 'public transport', 'commute'],\r\n    ru: ['автобус', 'общественный транспорт', 'коммьют']\r\n  },\r\n  '🚎': {\r\n    en: ['trolleybus', 'public transport', 'electric'],\r\n    ru: ['троллейбус', 'общественный транспорт', 'электрический']\r\n  },\r\n  '🏎️': {\r\n    en: ['race car', 'sports car', 'fast', 'speed'],\r\n    ru: ['гоночный автомобиль', 'спортивный автомобиль', 'быстрый', 'скорость']\r\n  },\r\n  '🚓': {\r\n    en: ['police car', 'law enforcement', 'vehicle'],\r\n    ru: ['полицейская машина', 'правоохранительные органы', 'транспортное средство']\r\n  },\r\n  '🚑': {\r\n    en: ['ambulance', 'emergency', 'hospital'],\r\n    ru: ['скорая помощь', 'чрезвычайная ситуация', 'больница']\r\n  },\r\n  '🚒': {\r\n    en: ['fire truck', 'firefighter', 'emergency'],\r\n    ru: ['пожарная машина', 'пожарный', 'чрезвычайная ситуация']\r\n  },\r\n  '🚐': {\r\n    en: ['van', 'minibus', 'transport'],\r\n    ru: ['фургон', 'маршрутка', 'транспорт']\r\n  },\r\n  '🛻': {\r\n    en: ['pickup truck', 'off-road', 'vehicle'],\r\n    ru: ['пикап', 'для бездорожья', 'транспортное средство']\r\n  },\r\n  '🚚': {\r\n    en: ['delivery truck', 'freight', 'cargo'],\r\n    ru: ['грузовик', 'доставка', 'груз']\r\n  },\r\n  '🚛': {\r\n    en: ['truck', 'semi-trailer', 'transport'],\r\n    ru: ['грузовик', 'самосвал', 'транспорт']\r\n  },\r\n  '🚜': {\r\n    en: ['tractor', 'farming', 'agriculture'],\r\n    ru: ['трактор', 'сельское хозяйство', 'агрономия']\r\n  },\r\n  '🛵': {\r\n    en: ['scooter', 'motorbike', 'moped'],\r\n    ru: ['скутер', 'мотоцикл', 'мопед']\r\n  },\r\n  '🏍️': {\r\n    en: ['motorcycle', 'bike', 'racing'],\r\n    ru: ['мотоцикл', 'байк', 'гоночный']\r\n  },\r\n  '🛺': {\r\n    en: ['rickshaw', 'auto rickshaw', 'transport'],\r\n    ru: ['рикша', 'авто-рикша', 'транспорт']\r\n  },\r\n  '🚲': {\r\n    en: ['bicycle', 'bike', 'pedal', 'cycling'],\r\n    ru: ['велосипед', 'байк', 'педаль', 'велоспорт']\r\n  },\r\n  '🛴': {\r\n    en: ['kick scooter', 'scooter', 'ride'],\r\n    ru: ['самокат', 'скутер', 'поездка']\r\n  },\r\n\r\n  // Air Transport\r\n  '✈️': {\r\n    en: ['airplane', 'flight', 'travel', 'sky'],\r\n    ru: ['самолёт', 'рейс', 'путешествие', 'небо']\r\n  },\r\n  '🛩️': {\r\n    en: ['small airplane', 'aviation', 'sky'],\r\n    ru: ['малый самолёт', 'авиация', 'небо']\r\n  },\r\n  '🛫': {\r\n    en: ['departure', 'takeoff', 'airport'],\r\n    ru: ['вылет', 'взлёт', 'аэропорт']\r\n  },\r\n  '🛬': {\r\n    en: ['landing', 'arrival', 'airport'],\r\n    ru: ['посадка', 'прибытие', 'аэропорт']\r\n  },\r\n  '🚁': {\r\n    en: ['helicopter', 'aviation', 'air'],\r\n    ru: ['вертолёт', 'авиация', 'воздух']\r\n  },\r\n  '🚀': {\r\n    en: ['rocket', 'space', 'launch', 'NASA'],\r\n    ru: ['ракета', 'космос', 'запуск', 'НАСА']\r\n  },\r\n  '🛸': {\r\n    en: ['UFO', 'alien', 'spaceship', 'extraterrestrial'],\r\n    ru: ['НЛО', 'инопланетянин', 'космический корабль', 'внеземной']\r\n  },\r\n\r\n  // Water Transport\r\n  '🛶': {\r\n    en: ['canoe', 'boat', 'paddle', 'water'],\r\n    ru: ['каноэ', 'лодка', 'весло', 'вода']\r\n  },\r\n  '⛵': {\r\n    en: ['sailboat', 'yacht', 'sea'],\r\n    ru: ['парусник', 'яхта', 'море']\r\n  },\r\n  '🚤': {\r\n    en: ['motorboat', 'speedboat', 'ocean'],\r\n    ru: ['моторная лодка', 'скоростной катер', 'океан']\r\n  },\r\n  '🛥️': {\r\n    en: ['yacht', 'luxury', 'boat'],\r\n    ru: ['яхта', 'роскошь', 'лодка']\r\n  },\r\n  '🛳️': {\r\n    en: ['cruise ship', 'ocean', 'voyage'],\r\n    ru: ['круизный лайнер', 'океан', 'путешествие']\r\n  },\r\n  '⛴️': {\r\n    en: ['ferry', 'boat', 'transport'],\r\n    ru: ['паром', 'лодка', 'транспорт']\r\n  },\r\n  '🚢': {\r\n    en: ['ship', 'ocean', 'voyage'],\r\n    ru: ['корабль', 'океан', 'путешествие']\r\n  },\r\n\r\n  // Places\r\n  '🏰': {\r\n    en: ['castle', 'fortress', 'history'],\r\n    ru: ['замок', 'крепость', 'история']\r\n  },\r\n  '🏯': {\r\n    en: ['Japanese castle', 'Asia', 'samurai'],\r\n    ru: ['японский замок', 'Азия', 'самурай']\r\n  },\r\n  '🏟️': {\r\n    en: ['stadium', 'sports', 'arena'],\r\n    ru: ['стадион', 'спорт', 'арена']\r\n  },\r\n  '🏖️': {\r\n    en: ['beach', 'sun', 'vacation'],\r\n    ru: ['пляж', 'солнце', 'отпуск']\r\n  },\r\n  '🏝️': {\r\n    en: ['island', 'tropical', 'vacation'],\r\n    ru: ['остров', 'тропический', 'отпуск']\r\n  },\r\n  '🏜️': {\r\n    en: ['desert', 'sand', 'dry'],\r\n    ru: ['пустыня', 'песок', 'сухой']\r\n  },\r\n  '🌋': {\r\n    en: ['volcano', 'eruption', 'lava'],\r\n    ru: ['вулкан', 'извержение', 'лава']\r\n  },\r\n  '⛰️': {\r\n    en: ['mountain', 'hiking', 'nature'],\r\n    ru: ['гора', 'поход', 'природа']\r\n  },\r\n  '🏔️': {\r\n    en: ['snowy mountain', 'climbing', 'cold'],\r\n    ru: ['снежная гора', 'скалолазание', 'холод']\r\n  },\r\n  '🗻': {\r\n    en: ['Mount Fuji', 'Japan', 'scenic'],\r\n    ru: ['гора Фудзи', 'Япония', 'живописный']\r\n  },\r\n  '🏕️': {\r\n    en: ['camping', 'tent', 'outdoors'],\r\n    ru: ['кемпинг', 'палатка', 'на природе']\r\n  },\r\n  '🏭': {\r\n    en: ['factory', 'industry', 'building'],\r\n    ru: ['фабрика', 'индустрия', 'здание']\r\n  },\r\n  '🏢': {\r\n    en: ['office building', 'corporate', 'city'],\r\n    ru: ['офисное здание', 'корпоративный', 'город']\r\n  },\r\n  '🏬': {\r\n    en: ['shopping mall', 'retail', 'stores'],\r\n    ru: ['торговый центр', 'розничная торговля', 'магазины']\r\n  },\r\n  '🏣': {\r\n    en: ['post office', 'mail', 'building'],\r\n    ru: ['почтовое отделение', 'почта', 'здание']\r\n  },\r\n  '🏤': {\r\n    en: ['post office', 'mail', 'postal service'],\r\n    ru: ['почтовое отделение', 'почта', 'почтовая служба']\r\n  },\r\n  '🏥': {\r\n    en: ['hospital', 'healthcare', 'emergency'],\r\n    ru: ['больница', 'здравоохранение', 'чрезвычайная ситуация']\r\n  },\r\n  '🏦': {\r\n    en: ['bank', 'money', 'finance'],\r\n    ru: ['банк', 'деньги', 'финансы']\r\n  },\r\n  '🏨': {\r\n    en: ['hotel', 'accommodation', 'stay'],\r\n    ru: ['отель', 'размещение', 'проживание']\r\n  },\r\n  '🏪': {\r\n    en: ['convenience store', 'shopping', '24/7'],\r\n    ru: ['магазин', 'шоппинг', 'круглосуточно']\r\n  },\r\n  '🏫': {\r\n    en: ['school', 'education', 'learning'],\r\n    ru: ['школа', 'образование', 'обучение']\r\n  },\r\n  '🏩': {\r\n    en: ['love hotel', 'romantic', 'Japan'],\r\n    ru: ['любовный отель', 'романтический', 'Япония']\r\n  },\r\n  '💒': {\r\n    en: ['wedding', 'church', 'marriage'],\r\n    ru: ['свадьба', 'церковь', 'брак']\r\n  },\r\n  '⛪': {\r\n    en: ['church', 'Christianity', 'religion'],\r\n    ru: ['церковь', 'христианство', 'религия']\r\n  },\r\n  '🕌': {\r\n    en: ['mosque', 'Islam', 'prayer'],\r\n    ru: ['мечеть', 'ислам', 'молитва']\r\n  },\r\n  '🕍': {\r\n    en: ['synagogue', 'Judaism', 'religion'],\r\n    ru: ['синагога', 'иудаизм', 'религия']\r\n  },\r\n  '🛕': {\r\n    en: ['hindu temple', 'spiritual', 'India'],\r\n    ru: ['индуистский храм', 'духовный', 'Индия']\r\n  },\r\n  '⛩️': {\r\n    en: ['shrine', 'torii', 'Japan'],\r\n    ru: ['святилище', 'тории', 'Япония']\r\n  },\r\n  '🏛️': {\r\n    en: ['government building', 'politics', 'history'],\r\n    ru: ['правительственное здание', 'политика', 'история']\r\n  },\r\n\r\n  //------------------------- Objects & Symbols -------------------------\r\n  // Tools & Technology\r\n  '📱': {\r\n    en: ['smartphone', 'mobile', 'phone'],\r\n    ru: ['смартфон', 'мобильный', 'телефон']\r\n  },\r\n  '💻': {\r\n    en: ['laptop', 'computer', 'technology'],\r\n    ru: ['ноутбук', 'компьютер', 'технология']\r\n  },\r\n  '⌨️': {\r\n    en: ['keyboard', 'typing', 'computer'],\r\n    ru: ['клавиатура', 'набор текста', 'компьютер']\r\n  },\r\n  '🖥️': {\r\n    en: ['desktop', 'monitor', 'screen'],\r\n    ru: ['настольный компьютер', 'монитор', 'экран']\r\n  },\r\n  '🖨️': {\r\n    en: ['printer', 'print', 'office'],\r\n    ru: ['принтер', 'печать', 'офис']\r\n  },\r\n  '🖱️': {\r\n    en: ['mouse', 'computer', 'click'],\r\n    ru: ['мышь', 'компьютер', 'клик']\r\n  },\r\n  '🖲️': {\r\n    en: ['trackball', 'navigation', 'input'],\r\n    ru: ['трекбол', 'навигация', 'ввод']\r\n  },\r\n  '🕹️': {\r\n    en: ['joystick', 'gaming', 'console'],\r\n    ru: ['джойстик', 'игры', 'консоль']\r\n  },\r\n  '🗜️': {\r\n    en: ['clamp', 'tool', 'hardware'],\r\n    ru: ['зажим', 'инструмент', 'аппаратное обеспечение']\r\n  },\r\n  '💽': {\r\n    en: ['mini disc', 'storage', 'media'],\r\n    ru: ['мини-диск', 'хранение', 'медиа']\r\n  },\r\n  '💾': {\r\n    en: ['floppy disk', 'save', 'data'],\r\n    ru: ['флоппи-диск', 'сохранить', 'данные']\r\n  },\r\n  '💿': {\r\n    en: ['CD', 'compact disc', 'music'],\r\n    ru: ['CD', 'компакт-диск', 'музыка']\r\n  },\r\n  '📀': {\r\n    en: ['DVD', 'video', 'disc'],\r\n    ru: ['DVD', 'видео', 'диск']\r\n  },\r\n  '📼': {\r\n    en: ['VHS', 'video cassette', 'retro'],\r\n    ru: ['VHS', 'видеокассета', 'ретро']\r\n  },\r\n  '📷': {\r\n    en: ['camera', 'photo', 'photography'],\r\n    ru: ['камера', 'фото', 'фотография']\r\n  },\r\n  '📸': {\r\n    en: ['camera flash', 'photography', 'snapshot'],\r\n    ru: ['вспышка камеры', 'фотография', 'снимок']\r\n  },\r\n  '📹': {\r\n    en: ['video camera', 'recording', 'film'],\r\n    ru: ['видеокамера', 'запись', 'фильм']\r\n  },\r\n  '🎥': {\r\n    en: ['movie camera', 'film', 'cinema'],\r\n    ru: ['кино-камера', 'фильм', 'кино']\r\n  },\r\n\r\n  // Office & Communication\r\n  '📞': {\r\n    en: ['telephone', 'call', 'communication'],\r\n    ru: ['телефон', 'звонок', 'коммуникация']\r\n  },\r\n  '☎️': {\r\n    en: ['phone', 'landline', 'call'],\r\n    ru: ['телефон', 'стационарный', 'звонок']\r\n  },\r\n  '📟': {\r\n    en: ['pager', 'message', 'communication'],\r\n    ru: ['пейджер', 'сообщение', 'коммуникация']\r\n  },\r\n  '📠': {\r\n    en: ['fax machine', 'office', 'document'],\r\n    ru: ['факс', 'офис', 'документ']\r\n  },\r\n  '📺': {\r\n    en: ['TV', 'television', 'screen'],\r\n    ru: ['телевизор', 'теле', 'экран']\r\n  },\r\n  '📻': {\r\n    en: ['radio', 'broadcast', 'audio'],\r\n    ru: ['радио', 'трансляция', 'аудио']\r\n  },\r\n  '🎙️': {\r\n    en: ['microphone', 'recording', 'podcast'],\r\n    ru: ['микрофон', 'запись', 'подкаст']\r\n  },\r\n  '🎚️': {\r\n    en: ['control knob', 'audio', 'volume'],\r\n    ru: ['ручка управления', 'аудио', 'громкость']\r\n  },\r\n  '🎛️': {\r\n    en: ['sliders', 'equalizer', 'settings'],\r\n    ru: ['ползунки', 'эквалайзер', 'настройки']\r\n  },\r\n  '📡': {\r\n    en: ['satellite dish', 'broadcast', 'signal'],\r\n    ru: ['спутниковая антенна', 'трансляция', 'сигнал']\r\n  },\r\n  '🔋': {\r\n    en: ['battery', 'power', 'energy'],\r\n    ru: ['батарея', 'мощность', 'энергия']\r\n  },\r\n  '🔌': {\r\n    en: ['plug', 'electricity', 'charging'],\r\n    ru: ['штекер', 'электричество', 'зарядка']\r\n  },\r\n  '💡': {\r\n    en: ['light bulb', 'idea', 'illumination'],\r\n    ru: ['лампочка', 'идея', 'освещение']\r\n  },\r\n  '🔦': {\r\n    en: ['flashlight', 'torch', 'light'],\r\n    ru: ['фонарик', 'фонарь', 'свет']\r\n  },\r\n  '🕯️': {\r\n    en: ['candle', 'light', 'fire'],\r\n    ru: ['свеча', 'свет', 'огонь']\r\n  },\r\n\r\n  // Household & Money\r\n  '🧯': {\r\n    en: ['fire extinguisher', 'safety', 'fire'],\r\n    ru: ['огнетушитель', 'безопасность', 'огонь']\r\n  },\r\n  '🛢️': {\r\n    en: ['barrel', 'oil', 'fuel'],\r\n    ru: ['бочка', 'масло', 'топливо']\r\n  },\r\n  '💸': {\r\n    en: ['money with wings', 'cash', 'spending'],\r\n    ru: ['деньги с крыльями', 'наличные', 'расходы']\r\n  },\r\n  '💵': {\r\n    en: ['dollar bills', 'money', 'USD'],\r\n    ru: ['долларовые банкноты', 'деньги', 'USD']\r\n  },\r\n  '💴': {\r\n    en: ['yen', 'money', 'Japan'],\r\n    ru: ['иена', 'деньги', 'Япония']\r\n  },\r\n  '💶': {\r\n    en: ['euro', 'money', 'currency'],\r\n    ru: ['евро', 'деньги', 'валюта']\r\n  },\r\n  '💷': {\r\n    en: ['pound', 'money', 'UK'],\r\n    ru: ['фунт', 'деньги', 'Великобритания']\r\n  },\r\n  '🪙': {\r\n    en: ['coin', 'currency', 'money'],\r\n    ru: ['монета', 'валюта', 'деньги']\r\n  },\r\n  '💰': {\r\n    en: ['money bag', 'wealth', 'rich'],\r\n    ru: ['мешок с деньгами', 'богатство', 'богатый']\r\n  },\r\n  '💳': {\r\n    en: ['credit card', 'banking', 'payment'],\r\n    ru: ['кредитная карта', 'банковское дело', 'оплата']\r\n  },\r\n  '💎': {\r\n    en: ['gem', 'diamond', 'valuable'],\r\n    ru: ['драгоценный камень', 'бриллиант', 'ценный']\r\n  },\r\n  '⚖️': {\r\n    en: ['balance scale', 'justice', 'law'],\r\n    ru: ['весы', 'справедливость', 'закон']\r\n  },\r\n  '🪜': {\r\n    en: ['ladder', 'climb', 'height'],\r\n    ru: ['лестница', 'лазить', 'высота']\r\n  },\r\n  '🧰': {\r\n    en: ['toolbox', 'tools', 'repair'],\r\n    ru: ['ящик с инструментами', 'инструменты', 'ремонт']\r\n  },\r\n  '🔧': {\r\n    en: ['wrench', 'repair', 'tools'],\r\n    ru: ['гаечный ключ', 'ремонт', 'инструменты']\r\n  },\r\n  '🔨': {\r\n    en: ['hammer', 'build', 'fix'],\r\n    ru: ['молоток', 'строить', 'чинить']\r\n  },\r\n  '⚒️': {\r\n    en: ['hammer and pick', 'construction', 'work'],\r\n    ru: ['молот и кирка', 'строительство', 'работа']\r\n  },\r\n  '🛠️': {\r\n    en: ['tools', 'fix', 'maintenance'],\r\n    ru: ['инструменты', 'чинить', 'обслуживание']\r\n  },\r\n  '⛏️': {\r\n    en: ['pickaxe', 'mining', 'digging'],\r\n    ru: ['кирка', 'горное дело', 'копать']\r\n  },\r\n\r\n  // Writing & Reading\r\n  '✏️': {\r\n    en: ['pencil', 'writing', 'notes'],\r\n    ru: ['карандаш', 'написание', 'заметки']\r\n  },\r\n  '🖊️': {\r\n    en: ['pen', 'writing', 'signature'],\r\n    ru: ['ручка', 'написание', 'подпись']\r\n  },\r\n  '🖋️': {\r\n    en: ['fountain pen', 'calligraphy', 'writing'],\r\n    ru: ['перьевая ручка', 'каллиграфия', 'написание']\r\n  },\r\n  '✒️': {\r\n    en: ['nib', 'ink', 'writing'],\r\n    ru: ['остриё', 'чернила', 'написание']\r\n  },\r\n  '🖌️': {\r\n    en: ['paintbrush', 'art', 'painting'],\r\n    ru: ['кисть', 'искусство', 'живопись']\r\n  },\r\n  '🖍️': {\r\n    en: ['crayon', 'drawing', 'color'],\r\n    ru: ['мелки', 'рисование', 'цвет']\r\n  },\r\n  '📝': {\r\n    en: ['memo', 'notes', 'document'],\r\n    ru: ['заметка', 'записи', 'документ']\r\n  },\r\n  '📚': {\r\n    en: ['books', 'reading', 'library'],\r\n    ru: ['книги', 'чтение', 'библиотека']\r\n  },\r\n  '📖': {\r\n    en: ['open book', 'reading', 'story'],\r\n    ru: ['открытая книга', 'чтение', 'история']\r\n  },\r\n  '🔖': {\r\n    en: ['bookmark', 'reading', 'save'],\r\n    ru: ['закладка', 'чтение', 'сохранить']\r\n  },\r\n  '📑': {\r\n    en: ['bookmark tabs', 'pages', 'notes'],\r\n    ru: ['закладки', 'страницы', 'заметки']\r\n  },\r\n  '🗒️': {\r\n    en: ['spiral notepad', 'notes', 'journal'],\r\n    ru: ['спиральный блокнот', 'записи', 'журнал']\r\n  },\r\n  '📄': {\r\n    en: ['document', 'paper', 'page'],\r\n    ru: ['документ', 'бумага', 'страница']\r\n  },\r\n  '📰': {\r\n    en: ['newspaper', 'news', 'journalism'],\r\n    ru: ['газета', 'новости', 'журналистика']\r\n  },\r\n  '🗞️': {\r\n    en: ['rolled newspaper', 'press', 'print'],\r\n    ru: ['свернутая газета', 'пресса', 'печатное издание']\r\n  },\r\n  '📁': {\r\n    en: ['file folder', 'documents', 'storage'],\r\n    ru: ['папка', 'документы', 'хранение']\r\n  },\r\n  '📂': {\r\n    en: ['open folder', 'organization', 'files'],\r\n    ru: ['открытая папка', 'организация', 'файлы']\r\n  },\r\n  '🗂️': {\r\n    en: ['card index', 'catalog', 'records'],\r\n    ru: ['карточный индекс', 'каталог', 'записи']\r\n  },\r\n  '🕐': {\r\n    en: ['1 o\\'clock', 'clock face', 'time'],\r\n    ru: ['час', 'циферблат', 'время']\r\n  },\r\n  '🕑': {\r\n    en: ['2 o\\'clock', 'clock face', 'time'],\r\n    ru: ['два часа', 'циферблат', 'время']\r\n  },\r\n  '🕒': {\r\n    en: ['3 o\\'clock', 'clock face', 'time'],\r\n    ru: ['три часа', 'циферблат', 'время']\r\n  },\r\n  '🕓': {\r\n    en: ['4 o\\'clock', 'clock face', 'time'],\r\n    ru: ['четыре часа', 'циферблат', 'время']\r\n  },\r\n  '🕔': {\r\n    en: ['5 o\\'clock', 'clock face', 'time'],\r\n    ru: ['пять часов', 'циферблат', 'время']\r\n  },\r\n  '🕕': {\r\n    en: ['6 o\\'clock', 'clock face', 'time'],\r\n    ru: ['шесть часов', 'циферблат', 'время']\r\n  },\r\n  '🕖': {\r\n    en: ['7 o\\'clock', 'clock face', 'time'],\r\n    ru: ['семь часов', 'циферблат', 'время']\r\n  },\r\n  '🕗': {\r\n    en: ['8 o\\'clock', 'clock face', 'time'],\r\n    ru: ['восемь часов', 'циферблат', 'время']\r\n  },\r\n  '🕘': {\r\n    en: ['9 o\\'clock', 'clock face', 'time'],\r\n    ru: ['девять часов', 'циферблат', 'время']\r\n  },\r\n  '🕙': {\r\n    en: ['10 o\\'clock', 'clock face', 'time'],\r\n    ru: ['десять часов', 'циферблат', 'время']\r\n  },\r\n  '🕚': {\r\n    en: ['11 o\\'clock', 'clock face', 'time'],\r\n    ru: ['одиннадцать часов', 'циферблат', 'время']\r\n  },\r\n  '🕛': {\r\n    en: ['12 o\\'clock', 'clock face', 'time'],\r\n    ru: ['двенадцать часов', 'циферблат', 'время']\r\n  },\r\n  '🕰️': {\r\n    en: ['mantel clock', 'clock', 'time'],\r\n    ru: ['настенные часы', 'часы', 'время']\r\n  },\r\n  '⏰': {\r\n    en: ['alarm clock', 'wake up', 'time'],\r\n    ru: ['будильник', 'пробуждение', 'время']\r\n  },\r\n  '⏱️': {\r\n    en: ['stopwatch', 'timer', 'time'],\r\n    ru: ['секундомер', 'таймер', 'время']\r\n  },\r\n  '⏲️': {\r\n    en: ['kitchen timer', 'timer', 'time'],\r\n    ru: ['кухонный таймер', 'таймер', 'время']\r\n  },\r\n  '⌚': {\r\n    en: ['watch', 'smartwatch', 'time'],\r\n    ru: ['часы', 'умные часы', 'время']\r\n  },\r\n\r\n  //------------------------- Symbols & Signs -------------------------\r\n  // Hearts\r\n  '❤️': {\r\n    en: ['heart', 'love', 'affection'],\r\n    ru: ['сердце', 'любовь', 'нежность']\r\n  },\r\n  '🧡': {\r\n    en: ['orange heart', 'warmth', 'care'],\r\n    ru: ['оранжевое сердце', 'теплота', 'забота']\r\n  },\r\n  '💛': {\r\n    en: ['yellow heart', 'friendship', 'happiness'],\r\n    ru: ['жёлтое сердце', 'дружба', 'счастье']\r\n  },\r\n  '💚': {\r\n    en: ['green heart', 'envy', 'nature', 'eco'],\r\n    ru: ['зелёное сердце', 'зависть', 'природа', 'эко']\r\n  },\r\n  '💙': {\r\n    en: ['blue heart', 'loyalty', 'trust'],\r\n    ru: ['синее сердце', 'верность', 'доверие']\r\n  },\r\n  '💜': {\r\n    en: ['purple heart', 'compassion', 'admiration'],\r\n    ru: ['фиолетовое сердце', 'сострадание', 'восхищение']\r\n  },\r\n  '🤎': {\r\n    en: ['brown heart', 'earth', 'stability'],\r\n    ru: ['коричневое сердце', 'земля', 'устойчивость']\r\n  },\r\n  '🖤': {\r\n    en: ['black heart', 'mourning', 'dark'],\r\n    ru: ['чёрное сердце', 'скорбь', 'тёмный']\r\n  },\r\n  '🤍': {\r\n    en: ['white heart', 'pure', 'peace'],\r\n    ru: ['белое сердце', 'чистый', 'мир']\r\n  },\r\n  '💔': {\r\n    en: ['broken heart', 'sad', 'heartbreak'],\r\n    ru: ['разбитое сердце', 'грусть', 'сердечная боль']\r\n  },\r\n  '❣️': {\r\n    en: ['heart exclamation', 'love', 'emotion'],\r\n    ru: ['сердечный восклицательный знак', 'любовь', 'эмоция']\r\n  },\r\n  '💕': {\r\n    en: ['two hearts', 'romance', 'affection'],\r\n    ru: ['два сердца', 'романтика', 'нежность']\r\n  },\r\n  '💞': {\r\n    en: ['revolving hearts', 'love', 'relationship'],\r\n    ru: ['вращающиеся сердца', 'любовь', 'отношения']\r\n  },\r\n  '💓': {\r\n    en: ['beating heart', 'emotion', 'passion'],\r\n    ru: ['бьющееся сердце', 'эмоция', 'страсть']\r\n  },\r\n  '💗': {\r\n    en: ['growing heart', 'love', 'expanding'],\r\n    ru: ['растущее сердце', 'любовь', 'расширяющееся']\r\n  },\r\n  '💖': {\r\n    en: ['sparkling heart', 'admiration', 'shine'],\r\n    ru: ['блестящее сердце', 'восхищение', 'сияние']\r\n  },\r\n  '💘': {\r\n    en: ['heart with arrow', 'love', 'romance'],\r\n    ru: ['сердце со стрелой', 'любовь', 'романтика']\r\n  },\r\n  '💝': {\r\n    en: ['heart with ribbon', 'gift', 'affection'],\r\n    ru: ['сердце с лентой', 'подарок', 'нежность']\r\n  },\r\n  '💟': {\r\n    en: ['heart decoration', 'symbol', 'love'],\r\n    ru: ['украшение сердца', 'символ', 'любовь']\r\n  },\r\n\r\n  // Religion\r\n  '☮️': {\r\n    en: ['peace', 'pacifism', 'symbol'],\r\n    ru: ['мир', 'пацифизм', 'символ']\r\n  },\r\n  '✝️': {\r\n    en: ['cross', 'christianity', 'religion'],\r\n    ru: ['крест', 'христианство', 'религия']\r\n  },\r\n  '☪️': {\r\n    en: ['star and crescent', 'islam', 'faith'],\r\n    ru: ['звезда и полумесяц', 'ислам', 'вера']\r\n  },\r\n  '🕉️': {\r\n    en: ['om', 'hinduism', 'spiritual'],\r\n    ru: ['ом', 'индуизм', 'духовный']\r\n  },\r\n  '☸️': {\r\n    en: ['dharma wheel', 'buddhism', 'karma'],\r\n    ru: ['колесо дхармы', 'буддизм', 'карма']\r\n  },\r\n  '✡️': {\r\n    en: ['star of david', 'judaism', 'faith'],\r\n    ru: ['звезда Давида', 'иудаизм', 'вера']\r\n  },\r\n  '🔯': {\r\n    en: ['hexagram', 'mystic', 'spiritual'],\r\n    ru: ['шестиконечная звезда', 'мистический', 'духовный']\r\n  },\r\n  '🕎': {\r\n    en: ['menorah', 'hanukkah', 'jewish'],\r\n    ru: ['менора', 'Ханука', 'еврейский']\r\n  },\r\n  '☯️': {\r\n    en: ['yin yang', 'balance', 'harmony'],\r\n    ru: ['инь-ян', 'баланс', 'гармония']\r\n  },\r\n  '☦️': {\r\n    en: ['orthodox cross', 'christianity', 'faith'],\r\n    ru: ['православный крест', 'христианство', 'вера']\r\n  },\r\n  '🛐': {\r\n    en: ['place of worship', 'religion', 'faith'],\r\n    ru: ['место поклонения', 'религия', 'вера']\r\n  },\r\n  '⛎': {\r\n    en: ['ophiuchus', 'zodiac', 'astrology'],\r\n    ru: ['змееносец', 'зодиак', 'астрология']\r\n  },\r\n\r\n  // Warning & Restrictions\r\n  '⚠️': {\r\n    en: ['warning', 'caution', 'alert'],\r\n    ru: ['предупреждение', 'осторожность', 'тревога']\r\n  },\r\n  '🚸': {\r\n    en: ['children crossing', 'school', 'pedestrian'],\r\n    ru: ['дети на переходе', 'школа', 'пешеход']\r\n  },\r\n  '⛔': {\r\n    en: ['no entry', 'prohibited', 'restricted'],\r\n    ru: ['вход запрещён', 'запрещено', 'ограничено']\r\n  },\r\n  '🚫': {\r\n    en: ['prohibited', 'no', 'forbidden'],\r\n    ru: ['запрещено', 'нет', 'запрещено']\r\n  },\r\n  '☢️': {\r\n    en: ['radioactive', 'hazard', 'danger'],\r\n    ru: ['радиоактивный', 'опасность', 'угроза']\r\n  },\r\n  '☣️': {\r\n    en: ['biohazard', 'toxic', 'warning'],\r\n    ru: ['биологическая опасность', 'ядовитый', 'предупреждение']\r\n  },\r\n\r\n  // Math Symbols\r\n  '➕': {\r\n    en: ['plus', 'addition', 'math'],\r\n    ru: ['плюс', 'сложение', 'математика']\r\n  },\r\n  '➖': {\r\n    en: ['minus', 'subtraction', 'math'],\r\n    ru: ['минус', 'вычитание', 'математика']\r\n  },\r\n  '➗': {\r\n    en: ['division', 'divide', 'math'],\r\n    ru: ['деление', 'делить', 'математика']\r\n  },\r\n  '✖️': {\r\n    en: ['multiplication', 'times', 'math'],\r\n    ru: ['умножение', 'раз', 'математика']\r\n  },\r\n  '♾️': {\r\n    en: ['infinity', 'limitless', 'math'],\r\n    ru: ['бесконечность', 'безграничный', 'математика']\r\n  },\r\n  '💲': {\r\n    en: ['dollar', 'money', 'currency'],\r\n    ru: ['доллар', 'деньги', 'валюта']\r\n  },\r\n  '💱': {\r\n    en: ['currency exchange', 'finance', 'money'],\r\n    ru: ['обмен валюты', 'финансы', 'деньги']\r\n  },\r\n\r\n  // Arrows\r\n  '⬆️': {\r\n    en: ['up arrow', 'increase', 'direction'],\r\n    ru: ['стрелка вверх', 'увеличение', 'направление']\r\n  },\r\n  '↗️': {\r\n    en: ['up-right arrow', 'growth', 'move'],\r\n    ru: ['стрелка вверх-вправо', 'рост', 'движение']\r\n  },\r\n  '➡️': {\r\n    en: ['right arrow', 'next', 'forward'],\r\n    ru: ['стрелка вправо', 'далее', 'вперёд']\r\n  },\r\n  '↘️': {\r\n    en: ['down-right arrow', 'decrease', 'move'],\r\n    ru: ['стрелка вниз-вправо', 'уменьшение', 'движение']\r\n  },\r\n  '⬇️': {\r\n    en: ['down arrow', 'lower', 'decline'],\r\n    ru: ['стрелка вниз', 'понижение', 'снижение']\r\n  },\r\n  '↙️': {\r\n    en: ['down-left arrow', 'falling', 'move'],\r\n    ru: ['стрелка вниз-влево', 'падение', 'движение']\r\n  },\r\n  '⬅️': {\r\n    en: ['left arrow', 'back', 'previous'],\r\n    ru: ['стрелка влево', 'назад', 'предыдущий']\r\n  },\r\n  '↖️': {\r\n    en: ['up-left arrow', 'direction', 'move'],\r\n    ru: ['стрелка вверх-влево', 'направление', 'движение']\r\n  },\r\n  '↕️': {\r\n    en: ['vertical arrows', 'up down', 'change'],\r\n    ru: ['вертикальные стрелки', 'вверх вниз', 'изменение']\r\n  },\r\n  '↔️': {\r\n    en: ['horizontal arrows', 'left right', 'switch'],\r\n    ru: ['горизонтальные стрелки', 'влево вправо', 'переключение']\r\n  },\r\n  '↩️': {\r\n    en: ['back arrow', 'undo', 'return'],\r\n    ru: ['стрелка назад', 'отмена', 'возврат']\r\n  },\r\n  '↪️': {\r\n    en: ['right curved arrow', 'redirect', 'turn'],\r\n    ru: ['изогнутая стрелка вправо', 'перенаправление', 'поворот']\r\n  },\r\n  '⤴️': {\r\n    en: ['up-right arrow', 'diagonal', 'move'],\r\n    ru: ['диагональная стрелка вверх-вправо', 'диагональ', 'движение']\r\n  },\r\n  '⤵️': {\r\n    en: ['down-right arrow', 'diagonal', 'move'],\r\n    ru: ['диагональная стрелка вниз-вправо', 'диагональ', 'движение']\r\n  },\r\n  '🔃': {\r\n    en: ['repeat', 'cycle', 'refresh'],\r\n    ru: ['повтор', 'цикл', 'обновление']\r\n  },\r\n  '🔄': {\r\n    en: ['counterclockwise arrows', 'reload', 'sync'],\r\n    ru: ['стрелки против часовой стрелки', 'перезагрузка', 'синхронизация']\r\n  },\r\n\r\n  // Miscellaneous Symbols\r\n  '🔆': {\r\n    en: ['bright', 'high brightness', 'sun'],\r\n    ru: ['яркий', 'высокая яркость', 'солнце']\r\n  },\r\n  '📶': {\r\n    en: ['signal', 'network', 'connection'],\r\n    ru: ['сигнал', 'сеть', 'соединение']\r\n  },\r\n  '🎦': {\r\n    en: ['cinema', 'movies', 'entertainment'],\r\n    ru: ['кино', 'фильмы', 'развлечения']\r\n  },\r\n  '🔅': {\r\n    en: ['dim', 'low brightness', 'light'],\r\n    ru: ['тусклый', 'низкая яркость', 'свет']\r\n  },\r\n  '♻️': {\r\n    en: ['recycle', 'eco', 'sustainability'],\r\n    ru: ['переработка', 'эко', 'устойчивость']\r\n  },\r\n  '✅': {\r\n    en: ['check mark', 'yes', 'approved'],\r\n    ru: ['галочка', 'да', 'одобрено']\r\n  },\r\n  '❌': {\r\n    en: ['cross mark', 'no', 'wrong'],\r\n    ru: ['крестик', 'нет', 'неправильно']\r\n  },\r\n  '❎': {\r\n    en: ['negative cross', 'decline', 'cancel'],\r\n    ru: ['отрицательный крест', 'отказ', 'отмена']\r\n  },\r\n  '➰': {\r\n    en: ['curly loop', 'infinity', 'twist'],\r\n    ru: ['извилистая петля', 'бесконечность', 'скрутка']\r\n  },\r\n  '➿': {\r\n    en: ['double curly loop', 'loop', 'repeat'],\r\n    ru: ['двойная извилистая петля', 'петля', 'повтор']\r\n  },\r\n  '〽️': {\r\n    en: ['part alternation', 'music', 'symbol'],\r\n    ru: ['знак чередования', 'музыка', 'символ']\r\n  },\r\n  '✳️': {\r\n    en: ['eight-spoked asterisk', 'star', 'highlight'],\r\n    ru: ['восьмиконечная астериска', 'звезда', 'акцент']\r\n  },\r\n  '✴️': {\r\n    en: ['eight-pointed star', 'shine', 'special'],\r\n    ru: ['восьмиконечная звезда', 'сияние', 'особый']\r\n  },\r\n  '❇️': {\r\n    en: ['sparkle', 'highlight', 'shine'],\r\n    ru: ['сверкание', 'акцент', 'сияние']\r\n  },\r\n  '©️': {\r\n    en: ['copyright', 'legal', 'rights'],\r\n    ru: ['авторское право', 'юридический', 'права']\r\n  },\r\n  '®️': {\r\n    en: ['registered', 'trademark', 'brand'],\r\n    ru: ['зарегистрировано', 'торговая марка', 'бренд']\r\n  },\r\n  '™️': {\r\n    en: ['trademark', 'brand', 'symbol'],\r\n    ru: ['торговая марка', 'бренд', 'символ']\r\n  },\r\n\r\n  //------------------------- Flags -------------------------\r\n  // Special Flags\r\n  '🏁': {\r\n    en: ['checkered flag', 'finish line', 'racing'],\r\n    ru: ['шашечный флаг', 'финишная черта', 'гонки']\r\n  },\r\n  '🚩': {\r\n    en: ['triangular flag', 'mark', 'warning'],\r\n    ru: ['треугольный флаг', 'метка', 'предупреждение']\r\n  },\r\n  '🎌': {\r\n    en: ['crossed flags', 'celebration', 'Japan'],\r\n    ru: ['перекрещённые флаги', 'празднование', 'Япония']\r\n  },\r\n  '🏴': {\r\n    en: ['black flag', 'protest', 'symbol'],\r\n    ru: ['чёрный флаг', 'протест', 'символ']\r\n  },\r\n  '🏳️': {\r\n    en: ['white flag', 'surrender', 'peace'],\r\n    ru: ['белый флаг', 'капитуляция', 'мир']\r\n  },\r\n  '🏳️‍🌈': {\r\n    en: ['rainbow flag', 'LGBTQ+', 'pride'],\r\n    ru: ['радужный флаг', 'ЛГБТК+', 'гордость']\r\n  },\r\n  '🏳️‍⚧️': {\r\n    en: ['transgender flag', 'trans', 'pride'],\r\n    ru: ['трансгендерный флаг', 'транс', 'гордость']\r\n  },\r\n  '🏴‍☠️': {\r\n    en: ['pirate flag', 'skull', 'danger'],\r\n    ru: ['пиратский флаг', 'череп', 'опасность']\r\n  },\r\n\r\n  // Country Flags (Sample)\r\n  '🇺🇸': {\r\n    en: ['United States', 'USA', 'America'],\r\n    ru: ['Соединённые Штаты', 'США', 'Америка']\r\n  },\r\n  '🇬🇧': {\r\n    en: ['United Kingdom', 'UK', 'Britain'],\r\n    ru: ['Соединённое Королевство', 'Великобритания', 'Британия']\r\n  },\r\n  '🇯🇵': {\r\n    en: ['Japan', 'Japanese', 'Asia'],\r\n    ru: ['Япония', 'японский', 'Азия']\r\n  },\r\n  '🇰🇷': {\r\n    en: ['South Korea', 'Korea', 'Asian'],\r\n    ru: ['Южная Корея', 'Корея', 'азиатский']\r\n  },\r\n  '🇩🇪': {\r\n    en: ['Germany', 'Deutschland', 'Europe'],\r\n    ru: ['Германия', 'Германия', 'Европа']\r\n  },\r\n  '🇨🇳': {\r\n    en: ['China', 'Chinese', 'Asia'],\r\n    ru: ['Китай', 'китайский', 'Азия']\r\n  },\r\n  '🇧🇷': {\r\n    en: ['Brazil', 'Brasil', 'South America'],\r\n    ru: ['Бразилия', 'Бразилия', 'Южная Америка']\r\n  },\r\n  '🇮🇳': {\r\n    en: ['India', 'Indian', 'Asia'],\r\n    ru: ['Индия', 'индийский', 'Азия']\r\n  },\r\n  '🇫🇷': {\r\n    en: ['France', 'French', 'Europe'],\r\n    ru: ['Франция', 'французский', 'Европа']\r\n  },\r\n  '🇪🇸': {\r\n    en: ['Spain', 'Spanish', 'Europe'],\r\n    ru: ['Испания', 'испанский', 'Европа']\r\n  },\r\n  '🇮🇹': {\r\n    en: ['Italy', 'Italian', 'Europe'],\r\n    ru: ['Италия', 'итальянский', 'Европа']\r\n  },\r\n  '🇷🇺': {\r\n    en: ['Russia', 'Russian', 'Europe'],\r\n    ru: ['Россия', 'русский', 'Европа']\r\n  },\r\n  '🇨🇦': {\r\n    en: ['Canada', 'Canadian', 'North America'],\r\n    ru: ['Канада', 'канадский', 'Северная Америка']\r\n  },\r\n  '🇦🇺': {\r\n    en: ['Australia', 'Aussie', 'Oceania'],\r\n    ru: ['Австралия', 'австралиец', 'Океания']\r\n  },\r\n  '🇳🇿': {\r\n    en: ['New Zealand', 'Kiwi', 'Oceania'],\r\n    ru: ['Новая Зеландия', 'киви', 'Океания']\r\n  },\r\n  // Additional Country Flags (Extended)\r\n  '🇲🇽': {\r\n    en: ['Mexico', 'Mexican', 'Latin America'],\r\n    ru: ['Мексика', 'мексиканский', 'Латинская Америка']\r\n  },\r\n  '🇦🇷': {\r\n    en: ['Argentina', 'Argentinian', 'South America'],\r\n    ru: ['Аргентина', 'аргентинский', 'Южная Америка']\r\n  },\r\n  '🇵🇰': {\r\n    en: ['Pakistan', 'Pakistani', 'Asia'],\r\n    ru: ['Пакистан', 'пакистанский', 'Азия']\r\n  },\r\n  '🇪🇬': {\r\n    en: ['Egypt', 'Egyptian', 'Africa'],\r\n    ru: ['Египет', 'египетский', 'Африка']\r\n  },\r\n  '🇸🇪': {\r\n    en: ['Sweden', 'Swedish', 'Europe'],\r\n    ru: ['Швеция', 'шведский', 'Европа']\r\n  },\r\n  '🇳🇴': {\r\n    en: ['Norway', 'Norwegian', 'Europe'],\r\n    ru: ['Норвегия', 'норвежский', 'Европа']\r\n  },\r\n  '🇳🇱': {\r\n    en: ['Netherlands', 'Dutch', 'Europe'],\r\n    ru: ['Нидерланды', 'голландский', 'Европа']\r\n  },\r\n  '🇨🇭': {\r\n    en: ['Switzerland', 'Swiss', 'Europe'],\r\n    ru: ['Швейцария', 'швейцарский', 'Европа']\r\n  },\r\n  '🇹🇷': {\r\n    en: ['Turkey', 'Turkish', 'Eurasia'],\r\n    ru: ['Турция', 'турецкий', 'Евразия']\r\n  },\r\n  '🇮🇩': {\r\n    en: ['Indonesia', 'Indonesian', 'Asia'],\r\n    ru: ['Индонезия', 'индонезийский', 'Азия']\r\n  },\r\n  '🇸🇬': {\r\n    en: ['Singapore', 'Singaporean', 'Asia'],\r\n    ru: ['Сингапур', 'сингапурский', 'Азия']\r\n  },\r\n  '🇮🇱': {\r\n    en: ['Israel', 'Israeli', 'Middle East'],\r\n    ru: ['Израиль', 'израильский', 'Ближний Восток']\r\n  },\r\n  '🇵🇹': {\r\n    en: ['Portugal', 'Portuguese', 'Europe'],\r\n    ru: ['Португалия', 'португальский', 'Европа']\r\n  },\r\n  '🇵🇱': {\r\n    en: ['Poland', 'Polish', 'Europe'],\r\n    ru: ['Польша', 'польский', 'Европа']\r\n  },\r\n  '🇹🇭': {\r\n    en: ['Thailand', 'Thai', 'Asia'],\r\n    ru: ['Таиланд', 'тайский', 'Азия']\r\n  }\r\n};\r\n\n\n//# sourceURL=webpack://tampermonkey-script/./src/data/emojiData.js?");

/***/ }),

/***/ "./src/definitions.js":
/*!****************************!*\
  !*** ./src/definitions.js ***!
  \****************************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   BASE_URL: () => (/* binding */ BASE_URL),\n/* harmony export */   GAME_URL: () => (/* binding */ GAME_URL),\n/* harmony export */   XMPP_BIND_URL: () => (/* binding */ XMPP_BIND_URL),\n/* harmony export */   config: () => (/* binding */ config),\n/* harmony export */   delay: () => (/* binding */ delay),\n/* harmony export */   emojiFaces: () => (/* binding */ emojiFaces),\n/* harmony export */   reconnectionDelay: () => (/* binding */ reconnectionDelay),\n/* harmony export */   state: () => (/* binding */ state),\n/* harmony export */   trustedDomains: () => (/* binding */ trustedDomains),\n/* harmony export */   userListDelay: () => (/* binding */ userListDelay)\n/* harmony export */ });\n/* harmony import */ var _src_styles_style_css__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../src/styles/style.css */ \"./src/styles/style.css\");\n // styles\r\n\r\n// URL constants\r\nconst BASE_URL = 'https://klavogonki.ru';\r\nconst GAME_URL = `${BASE_URL}/g/?gmid=`;\r\nconst XMPP_BIND_URL = `${BASE_URL}/xmpp-httpbind/`;\r\n\r\n// Sleep time (ms)\r\nconst delay = 200;\r\nconst userListDelay = 5000;\r\nconst reconnectionDelay = 5000;\r\n\r\n// Configuration with localStorage integration\r\nconst config = {\r\n  get username() {\r\n    const data = localStorage.getItem('klavoauth');\r\n    return data ? JSON.parse(data).username : '';\r\n  },\r\n  get password() {\r\n    const data = localStorage.getItem('klavoauth');\r\n    return data ? JSON.parse(data).password : '';\r\n  }\r\n};\r\n\r\nconst emojiFaces = [\r\n  // People Emojis (Facial expressions)\r\n  '😀', '😁', '😂', '🤣', '😃', '😄', '😅', '😆',\r\n  '😉', '😊', '😋', '😎', '😏', '😐', '😑', '😒',\r\n  '😓', '😔', '😕', '😖', '😗', '😘', '😙', '😚',\r\n  '😜', '😝', '😛', '🤑', '🤗', '🤔', '🤐', '🤨',\r\n  '😣', '😥', '😮', '🤯', '😳', '😱', '😨', '😰',\r\n  '😢', '🤪', '😵', '😲', '🤤', '😷', '🤒', '🤕',\r\n  '🤢', '🤧', '😇', '🥳', '🥺', '😬', '😴', '😌',\r\n  '🤥', '🥴', '🥵', '🥶', '🤧', '🤭', '🤫', '😠',\r\n  '😡', '😳', '😞', '😟', '😕',\r\n\r\n  // Cat Emojis (Expressive faces of cats)\r\n  '🐱', '😺', '😸', '😹', '😻', '😼', '😽', '🙀', '😿', '😾',\r\n\r\n  // Other Animal Emojis (Various animals' faces)\r\n  '🐶', '🐭', '🐹', '🐰', '🦊', '🐻', '🐼',\r\n  '🐨', '🐯', '🦁', '🐮', '🐷', '🐸', '🐵',\r\n  '🙈', '🙉', '🙊', '🐔', '🦄'\r\n];\r\n\r\nlet state = {\r\n  bigImageEvents: {}\r\n};\r\n\r\n// List of trusted domains\r\nconst trustedDomains = [\r\n  'klavogonki.ru',\r\n  'youtube.com', // youtube main\r\n  'youtu.be', // youtube share\r\n  'imgur.com',\r\n  'pikabu.ru',\r\n  'userapi.com', // vk.com\r\n  'ibb.co', // imgbb.com\r\n  'yaplakal.com',\r\n  'freepik.com'\r\n];\n\n//# sourceURL=webpack://tampermonkey-script/./src/definitions.js?");

/***/ }),

/***/ "./src/events.js":
/*!***********************!*\
  !*** ./src/events.js ***!
  \***********************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   setupDragHandlers: () => (/* binding */ setupDragHandlers),\n/* harmony export */   setupResizeHandlers: () => (/* binding */ setupResizeHandlers),\n/* harmony export */   setupWindowResizeHandler: () => (/* binding */ setupWindowResizeHandler)\n/* harmony export */ });\n/* harmony import */ var _helpers_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./helpers.js */ \"./src/helpers.js\");\n\r\n\r\n// ------------------------- Drag Handlers (Floating) -------------------------\r\nlet isDragging = false,\r\n    dragStartX,\r\n    dragStartY,\r\n    dragStartLeft,\r\n    dragStartTop;\r\n\r\nfunction setupDragHandlers() {\r\n  document.addEventListener('mousedown', (e) => {\r\n    const dragArea = e.target.closest('.chat-drag-area');\r\n    if (!dragArea) return;\r\n\r\n    const chat = document.getElementById('app-chat-container');\r\n    let chatState = (0,_helpers_js__WEBPACK_IMPORTED_MODULE_0__.getChatState)();\r\n\r\n    isDragging = true;\r\n    dragStartX = e.clientX;\r\n    dragStartY = e.clientY;\r\n    dragStartLeft = chat.offsetLeft;\r\n    // Use the computed top if no inline style exists\r\n    dragStartTop = parseInt(chat.style.top) || chat.getBoundingClientRect().top;\r\n    \r\n    // If the chat isn’t already floating, convert it now\r\n    if (!chatState.floating) {\r\n      const newTop = window.innerHeight - chat.offsetHeight;\r\n      chat.style.top = newTop + 'px';\r\n      chat.style.bottom = '';\r\n      chatState.top = newTop;\r\n      chatState.floating = true;\r\n      chat.classList.add(\"floating-chat\");\r\n      (0,_helpers_js__WEBPACK_IMPORTED_MODULE_0__.saveChatState)(chatState);\r\n    }\r\n\r\n    document.body.style.userSelect = 'none';\r\n  });\r\n\r\n  document.addEventListener('mousemove', (e) => {\r\n    if (!isDragging) return;\r\n\r\n    const chat = document.getElementById('app-chat-container');\r\n    const viewportWidth = window.innerWidth;\r\n    const viewportHeight = window.innerHeight;\r\n    const deltaX = e.clientX - dragStartX;\r\n    const deltaY = e.clientY - dragStartY;\r\n    const newLeft = (0,_helpers_js__WEBPACK_IMPORTED_MODULE_0__.clamp)(dragStartLeft + deltaX, 0, viewportWidth - chat.offsetWidth);\r\n    // Ensure the chat does not go above the viewport (>= 0) or below viewport bottom\r\n    const newTop = (0,_helpers_js__WEBPACK_IMPORTED_MODULE_0__.clamp)(dragStartTop + deltaY, 0, viewportHeight - chat.offsetHeight);\r\n    \r\n    chat.style.left = newLeft + 'px';\r\n    chat.style.top = newTop + 'px';\r\n    \r\n    let chatState = (0,_helpers_js__WEBPACK_IMPORTED_MODULE_0__.getChatState)();\r\n    chatState.left = newLeft;\r\n    chatState.top = newTop;\r\n    chatState.floating = true;\r\n    (0,_helpers_js__WEBPACK_IMPORTED_MODULE_0__.saveChatState)(chatState);\r\n  });\r\n\r\n  document.addEventListener('mouseup', () => {\r\n    if (!isDragging) return;\r\n    isDragging = false;\r\n\r\n    const chat = document.getElementById('app-chat-container');\r\n    const viewportWidth = window.innerWidth;\r\n    const viewportHeight = window.innerHeight;\r\n    const chatRect = chat.getBoundingClientRect();\r\n    const SNAP_THRESHOLD = 50;\r\n    \r\n    const outOfBounds = chatRect.left < 0 || chatRect.top < 0 ||\r\n                        chatRect.right > viewportWidth || chatRect.bottom > viewportHeight;\r\n    const nearBottom = (viewportHeight - chatRect.bottom) < SNAP_THRESHOLD;\r\n    \r\n    let chatState = (0,_helpers_js__WEBPACK_IMPORTED_MODULE_0__.getChatState)();\r\n    if (outOfBounds || nearBottom) {\r\n      // Snap to bottom when out of bounds\r\n      chat.style.top = '';\r\n      chat.style.bottom = '0';\r\n      chatState.floating = false;\r\n      chat.classList.remove(\"floating-chat\");\r\n    } else {\r\n      chatState.floating = true;\r\n      chatState.top = chatRect.top;\r\n      chat.classList.add(\"floating-chat\");\r\n    }\r\n    \r\n    (0,_helpers_js__WEBPACK_IMPORTED_MODULE_0__.saveChatState)(chatState);\r\n    document.body.style.userSelect = '';\r\n  });\r\n}\r\n\r\n// ------------------------- Resize Handlers -------------------------\r\nlet isResizing = false,\r\n    resizeType = null,\r\n    startX,\r\n    startY,\r\n    startWidth,\r\n    startHeight,\r\n    startLeft,\r\n    startTop,\r\n    offsetY = 0;  // used to keep the cursor's relative position on the edge\r\n\r\nfunction setupResizeHandlers() {\r\n  document.addEventListener('mousedown', (e) => {\r\n    const handle = e.target.closest('.resize-handle');\r\n    if (!handle) return;\r\n    isResizing = true;\r\n    resizeType = handle.classList[1]; // e.g., 'top', 'left', 'right'\r\n    const chat = document.getElementById('app-chat-container');\r\n    startX = e.clientX;\r\n    startY = e.clientY;\r\n    startWidth = chat.offsetWidth;\r\n    startHeight = chat.offsetHeight;\r\n    startLeft = chat.offsetLeft;\r\n    // Use inline style top if available, otherwise the computed top\r\n    startTop = parseInt(chat.style.top) || chat.getBoundingClientRect().top;\r\n    offsetY = e.clientY - startTop;  // capture the cursor offset relative to chat's top edge\r\n    document.body.style.userSelect = 'none';\r\n  });\r\n\r\n  document.addEventListener('mousemove', (e) => {\r\n    if (!isResizing) return;\r\n    const chat = document.getElementById('app-chat-container');\r\n    const viewportWidth = window.innerWidth;\r\n    const viewportHeight = window.innerHeight;\r\n    const computedStyle = getComputedStyle(document.documentElement);\r\n    const minWidth = parseInt(computedStyle.getPropertyValue('--min-chat-width')) || 250;\r\n    const minHeight = parseInt(computedStyle.getPropertyValue('--min-chat-height')) || 200;\r\n    let chatState = (0,_helpers_js__WEBPACK_IMPORTED_MODULE_0__.getChatState)();\r\n\r\n    // Horizontal resizing (applies for left and right handles)\r\n    if (resizeType === 'left') {\r\n      const newWidth = Math.max(minWidth, startWidth - (e.clientX - startX));\r\n      const newLeft = (0,_helpers_js__WEBPACK_IMPORTED_MODULE_0__.clamp)(startLeft + (e.clientX - startX), 0, viewportWidth - newWidth);\r\n      chat.style.width = newWidth + 'px';\r\n      chat.style.left = newLeft + 'px';\r\n      chatState.width = newWidth;\r\n      chatState.left = newLeft;\r\n    } else if (resizeType === 'right') {\r\n      const maxWidth = viewportWidth - chat.getBoundingClientRect().left;\r\n      const newWidth = Math.min(maxWidth, Math.max(minWidth, startWidth + (e.clientX - startX)));\r\n      chat.style.width = newWidth + 'px';\r\n      chatState.width = newWidth;\r\n    }\r\n\r\n    // Vertical resizing for the three handles will use the cursor’s Y position.\r\n    // Calculate the desired new top edge based on the original offset.\r\n    const newTopCandidate = e.clientY - offsetY;\r\n\r\n    // For the top handle, the chat’s top edge should follow the cursor.\r\n    if (resizeType === 'top') {\r\n      if (chatState.floating === false) {\r\n        // Docked: bottom remains anchored at viewport bottom.\r\n        // When docked, the computed startTop equals (viewportHeight - startHeight)\r\n        let newTop = (0,_helpers_js__WEBPACK_IMPORTED_MODULE_0__.clamp)(newTopCandidate, 0, viewportHeight - minHeight);\r\n        let newHeight = viewportHeight - newTop; // bottom is fixed at viewport bottom\r\n        chat.style.top = newTop + 'px';\r\n        chat.style.height = newHeight + 'px';\r\n        chatState.top = newTop;\r\n        chatState.height = newHeight;\r\n      } else {\r\n        // Floating: simply follow the cursor for the top edge,\r\n        // ensuring we don’t shrink below minHeight.\r\n        let newTop = (0,_helpers_js__WEBPACK_IMPORTED_MODULE_0__.clamp)(newTopCandidate, 0, startTop + startHeight - minHeight);\r\n        let newHeight = startHeight - (newTop - startTop);\r\n        // Also do not allow height to exceed viewport height\r\n        newHeight = Math.min(newHeight, viewportHeight);\r\n        chat.style.top = newTop + 'px';\r\n        chat.style.height = newHeight + 'px';\r\n        chatState.top = newTop;\r\n        chatState.height = newHeight;\r\n      }\r\n    }\r\n    \r\n    // For left/right handles, we also want vertical resizing.\r\n    if (resizeType === 'left' || resizeType === 'right') {\r\n      if (chatState.floating === false) {\r\n        // Docked: bottom is fixed\r\n        let newTop = (0,_helpers_js__WEBPACK_IMPORTED_MODULE_0__.clamp)(newTopCandidate, 0, viewportHeight - minHeight);\r\n        let newHeight = viewportHeight - newTop;\r\n        chat.style.top = newTop + 'px';\r\n        chat.style.height = newHeight + 'px';\r\n        chatState.top = newTop;\r\n        chatState.height = newHeight;\r\n      } else {\r\n        // Floating: follow the cursor while keeping at least minHeight.\r\n        let newTop = (0,_helpers_js__WEBPACK_IMPORTED_MODULE_0__.clamp)(newTopCandidate, 0, startTop + startHeight - minHeight);\r\n        let newHeight = startHeight - (newTop - startTop);\r\n        newHeight = Math.min(newHeight, viewportHeight - newTop);\r\n        chat.style.top = newTop + 'px';\r\n        chat.style.height = newHeight + 'px';\r\n        chatState.top = newTop;\r\n        chatState.height = newHeight;\r\n      }\r\n    }\r\n\r\n    // Ensure height never exceeds the viewport height\r\n    if (chat.offsetHeight > viewportHeight) {\r\n      chat.style.height = viewportHeight + 'px';\r\n      if (chatState.floating === false) {\r\n        chat.style.top = '0px';  // if docked, force top to 0 so height = viewport height\r\n        chatState.top = 0;\r\n      }\r\n      chatState.height = viewportHeight;\r\n    }\r\n    \r\n    (0,_helpers_js__WEBPACK_IMPORTED_MODULE_0__.saveChatState)(chatState);\r\n    (0,_helpers_js__WEBPACK_IMPORTED_MODULE_0__.handleElementsBehavior)();\r\n  });\r\n\r\n  document.addEventListener('mouseup', () => {\r\n    isResizing = false;\r\n    document.body.style.userSelect = '';\r\n  });\r\n}\r\n\r\nfunction setupWindowResizeHandler() {\r\n  window.addEventListener('resize', () => {\r\n    (0,_helpers_js__WEBPACK_IMPORTED_MODULE_0__.restoreChatState)();\r\n    (0,_helpers_js__WEBPACK_IMPORTED_MODULE_0__.handleElementsBehavior)();\r\n  });\r\n}\r\n\n\n//# sourceURL=webpack://tampermonkey-script/./src/events.js?");

/***/ }),

/***/ "./src/helpers.js":
/*!************************!*\
  !*** ./src/helpers.js ***!
  \************************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   addBigImageEventListeners: () => (/* binding */ addBigImageEventListeners),\n/* harmony export */   addViewportMeta: () => (/* binding */ addViewportMeta),\n/* harmony export */   adjustVisibility: () => (/* binding */ adjustVisibility),\n/* harmony export */   applyFontSize: () => (/* binding */ applyFontSize),\n/* harmony export */   calibrateToMoscowTime: () => (/* binding */ calibrateToMoscowTime),\n/* harmony export */   checkImageExists: () => (/* binding */ checkImageExists),\n/* harmony export */   clamp: () => (/* binding */ clamp),\n/* harmony export */   createFontSizeControl: () => (/* binding */ createFontSizeControl),\n/* harmony export */   createLengthPopup: () => (/* binding */ createLengthPopup),\n/* harmony export */   decodeURL: () => (/* binding */ decodeURL),\n/* harmony export */   enterPrivateMode: () => (/* binding */ enterPrivateMode),\n/* harmony export */   exitPrivateMode: () => (/* binding */ exitPrivateMode),\n/* harmony export */   extractCleanUsername: () => (/* binding */ extractCleanUsername),\n/* harmony export */   extractTargetUsername: () => (/* binding */ extractTargetUsername),\n/* harmony export */   extractUserId: () => (/* binding */ extractUserId),\n/* harmony export */   fetchJSON: () => (/* binding */ fetchJSON),\n/* harmony export */   focusTextInput: () => (/* binding */ focusTextInput),\n/* harmony export */   getAuthData: () => (/* binding */ getAuthData),\n/* harmony export */   getChatState: () => (/* binding */ getChatState),\n/* harmony export */   getExactUserIdByName: () => (/* binding */ getExactUserIdByName),\n/* harmony export */   getRandomEmojiAvatar: () => (/* binding */ getRandomEmojiAvatar),\n/* harmony export */   getRandomInterval: () => (/* binding */ getRandomInterval),\n/* harmony export */   handleElementsBehavior: () => (/* binding */ handleElementsBehavior),\n/* harmony export */   handlePrivateMessageInput: () => (/* binding */ handlePrivateMessageInput),\n/* harmony export */   highlightMentionWords: () => (/* binding */ highlightMentionWords),\n/* harmony export */   initChatLengthPopupEvents: () => (/* binding */ initChatLengthPopupEvents),\n/* harmony export */   isEncodedURL: () => (/* binding */ isEncodedURL),\n/* harmony export */   isTrustedDomain: () => (/* binding */ isTrustedDomain),\n/* harmony export */   mentionColors: () => (/* binding */ mentionColors),\n/* harmony export */   observeMessagesPanel: () => (/* binding */ observeMessagesPanel),\n/* harmony export */   parseMessageText: () => (/* binding */ parseMessageText),\n/* harmony export */   parseUsername: () => (/* binding */ parseUsername),\n/* harmony export */   privateMessageState: () => (/* binding */ privateMessageState),\n/* harmony export */   removeBigImageEventListeners: () => (/* binding */ removeBigImageEventListeners),\n/* harmony export */   restoreChatState: () => (/* binding */ restoreChatState),\n/* harmony export */   restoreFontSize: () => (/* binding */ restoreFontSize),\n/* harmony export */   saveChatState: () => (/* binding */ saveChatState),\n/* harmony export */   scrollToBottom: () => (/* binding */ scrollToBottom),\n/* harmony export */   setupPrivateMessageEvents: () => (/* binding */ setupPrivateMessageEvents),\n/* harmony export */   setupRandomEmojiAttention: () => (/* binding */ setupRandomEmojiAttention),\n/* harmony export */   showChatAlert: () => (/* binding */ showChatAlert),\n/* harmony export */   sleep: () => (/* binding */ sleep),\n/* harmony export */   usernameColors: () => (/* binding */ usernameColors)\n/* harmony export */ });\n/* harmony import */ var _converters_image_converter_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./converters/image-converter.js */ \"./src/converters/image-converter.js\");\n/* harmony import */ var _converters_video_converter_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./converters/video-converter.js */ \"./src/converters/video-converter.js\");\n/* harmony import */ var _definitions_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./definitions.js */ \"./src/definitions.js\");\n/* harmony import */ var _icons_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./icons.js */ \"./src/icons.js\");\n/* harmony import */ var _animations_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./animations.js */ \"./src/animations.js\");\n/* harmony import */ var _data_emojiData_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./data/emojiData.js */ \"./src/data/emojiData.js\");\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\nconst getAuthData = () => {\r\n  const pageData = JSON.parse([...document.scripts]\r\n    .find(s => s.text.includes('PageData'))\r\n    ?.text.match(/\\.constant\\('PageData', ({[\\s\\S]*?})\\)/)?.[1]\r\n    .replace(/(\\w+):/g, '\"$1\":').replace(/'/g, '\"') || '{}');\r\n\r\n  return pageData?.chatParams && {\r\n    username: `${pageData.chatParams.user.id}#${pageData.chatParams.user.login}`,\r\n    password: pageData.chatParams.pass\r\n  };\r\n};\r\n\r\nfunction colorGenerator(config) {\r\n  // Load stored mapping from sessionStorage (if available) or initialize an empty object.\r\n  const storedMapping = sessionStorage.getItem('usernameColors');\r\n  const hueMap = storedMapping ? JSON.parse(storedMapping) : {};\r\n\r\n  return {\r\n    hueStep: config.hueStep || 30,\r\n    maxHue: config.maxHue || 360,\r\n    saturation: config.saturation || '80%',\r\n    lightness: config.lightness || '50%',\r\n\r\n    getColor(username) {\r\n      // Normalize the username to ensure consistency.\r\n      const key = username.trim().toLowerCase();\r\n\r\n      // Return the color if it already exists.\r\n      if (hueMap[key]) {\r\n        return hueMap[key];\r\n      }\r\n\r\n      // Otherwise, generate a new color.\r\n      const maxSteps = this.maxHue / this.hueStep;\r\n      const hue = Math.floor(Math.random() * maxSteps) * this.hueStep;\r\n      const color = `hsl(${hue}, ${this.saturation}, ${this.lightness})`;\r\n      hueMap[key] = color;\r\n\r\n      // Save the updated mapping back to sessionStorage.\r\n      sessionStorage.setItem('usernameColors', JSON.stringify(hueMap));\r\n      return color;\r\n    }\r\n  };\r\n}\r\n\r\nconst usernameColors = colorGenerator({\r\n  maxHue: 210,\r\n  hueStep: 30,\r\n  saturation: '80%',\r\n  lightness: '50%'\r\n});\r\n\r\nconst mentionColors = colorGenerator({\r\n  maxHue: 210,\r\n  hueStep: 30,\r\n  saturation: '80%',\r\n  lightness: '50%'\r\n});\r\n\r\nlet lastEmojiAvatar = null;\r\nfunction getRandomEmojiAvatar() {\r\n  let newEmoji;\r\n  do {\r\n    newEmoji = _definitions_js__WEBPACK_IMPORTED_MODULE_2__.emojiFaces[Math.floor(Math.random() * _definitions_js__WEBPACK_IMPORTED_MODULE_2__.emojiFaces.length)];\r\n  } while (newEmoji === lastEmojiAvatar);\r\n  lastEmojiAvatar = newEmoji;\r\n  return newEmoji;\r\n}\r\n\r\nfunction handleElementsBehavior() {\r\n  const wrapper = document.querySelector('#app-chat-container .chat-wrapper');\r\n  if (!wrapper) return;\r\n  const chatContainer = document.querySelector('#app-chat-container');\r\n  const isNarrow = wrapper.offsetWidth <= 780;\r\n  const isVeryNarrow = wrapper.offsetWidth <= 380;\r\n  const isExtremelyNarrow = wrapper.offsetWidth <= 340;\r\n  const isMaximized = chatContainer.classList.contains('maximized');\r\n  const userList = document.querySelector('#app-chat-container .user-list-container');\r\n\r\n  let isUserListOpen = false;\r\n\r\n  if (userList) {\r\n    if (isNarrow && !isMaximized) {\r\n      userList.style.position = 'absolute';\r\n      userList.style.height = '100%';\r\n      userList.style.top = '0';\r\n      userList.style.right = '0';\r\n      userList.style.transition = 'transform 0.3s ease';\r\n      userList.style.zIndex = '1001';\r\n      userList.style.transform = 'translateX(100%)';\r\n\r\n      let revealButton = document.querySelector('#app-chat-container .reveal-userlist-btn');\r\n      if (!revealButton) {\r\n        revealButton = document.createElement('button');\r\n        revealButton.className = 'reveal-userlist-btn hidden-userlist';\r\n        revealButton.textContent = '📋';\r\n\r\n        chatContainer.appendChild(revealButton);\r\n\r\n        function closeUserList(event) {\r\n          if (!userList.contains(event.target) && event.target !== revealButton) {\r\n            userList.style.transform = 'translateX(100%)';\r\n            revealButton.classList.remove('shown-userlist');\r\n            revealButton.classList.add('hidden-userlist');\r\n            isUserListOpen = false;\r\n            document.removeEventListener('click', closeUserList, true);\r\n          }\r\n        }\r\n\r\n        revealButton.addEventListener('click', (event) => {\r\n          event.stopPropagation();\r\n          if (!isUserListOpen) {\r\n            userList.style.transform = 'translateX(0)';\r\n            revealButton.classList.remove('hidden-userlist');\r\n            revealButton.classList.add('shown-userlist');\r\n            isUserListOpen = true;\r\n\r\n            setTimeout(() => {\r\n              document.addEventListener('click', closeUserList, true);\r\n            }, 10);\r\n          }\r\n        });\r\n      }\r\n    } else {\r\n      userList.style.position = '';\r\n      userList.style.height = '';\r\n      userList.style.top = '';\r\n      userList.style.right = '';\r\n      userList.style.transform = '';\r\n      userList.style.zIndex = '';\r\n\r\n      const revealButton = document.querySelector('#app-chat-container .reveal-userlist-btn');\r\n      if (revealButton) {\r\n        revealButton.remove();\r\n      }\r\n    }\r\n  }\r\n\r\n  document.querySelectorAll('#app-chat-container .message').forEach(msg => {\r\n    msg.style.flexDirection = (isNarrow && !isMaximized) ? 'column' : 'row';\r\n    msg.style.marginBottom = (isNarrow && !isMaximized) ? '0.4em' : '0';\r\n  });\r\n\r\n  document.querySelectorAll('#app-chat-container .video-container').forEach(video => {\r\n    if (isExtremelyNarrow) {\r\n      video.style.transformOrigin = 'left';\r\n      video.style.transform = 'scale(0.8)';\r\n    } else if (isVeryNarrow) {\r\n      video.style.transformOrigin = 'left';\r\n      video.style.transform = 'scale(0.9)';\r\n    } else {\r\n      video.style.transformOrigin = '';\r\n      video.style.transform = '';\r\n    }\r\n  });\r\n}\r\n\r\nfunction observeMessagesPanel() {\r\n  const messagesPanel = document.getElementById('messages-panel');\r\n  if (!messagesPanel) return;\r\n\r\n  const observer = new MutationObserver(() => {\r\n    handleElementsBehavior();\r\n    (0,_converters_video_converter_js__WEBPACK_IMPORTED_MODULE_1__.convertVideoLinksToPlayer)();\r\n    (0,_converters_image_converter_js__WEBPACK_IMPORTED_MODULE_0__.convertImageLinksToImage)();\r\n    scrollToBottom();\r\n  });\r\n\r\n  observer.observe(messagesPanel, { childList: true, subtree: true });\r\n}\r\n\r\nfunction restoreChatState() {\r\n  const chat = document.getElementById('app-chat-container');\r\n  const toggleButton = document.querySelector('.chat-toggle-button');\r\n  if (!chat || !toggleButton) return;\r\n\r\n  const state = getChatState();\r\n  const viewportWidth = window.innerWidth;\r\n  const viewportHeight = window.innerHeight;\r\n  const computedStyle = getComputedStyle(document.documentElement);\r\n  const minWidth = parseInt(computedStyle.getPropertyValue('--min-chat-width')) || 250;\r\n  const minHeight = parseInt(computedStyle.getPropertyValue('--min-chat-height')) || 200;\r\n\r\n  chat.style.width = Math.min(viewportWidth, Math.max(minWidth, state.width)) + 'px';\r\n  chat.style.height = Math.min(viewportHeight, Math.max(minHeight, state.height)) + 'px';\r\n  chat.style.left = clamp(state.left, 0, viewportWidth - chat.offsetWidth) + 'px';\r\n\r\n  if (state.floating) {\r\n    chat.style.top = clamp(state.top, 0, viewportHeight - chat.offsetHeight) + 'px';\r\n    chat.style.bottom = '';\r\n    chat.classList.add(\"floating-chat\");\r\n    chat.style.display = state.isVisible ? 'flex' : 'none';\r\n    chat.style.opacity = state.isVisible ? '1' : '0';\r\n    toggleButton.innerHTML = state.isVisible ? _icons_js__WEBPACK_IMPORTED_MODULE_3__.closeSVG : _icons_js__WEBPACK_IMPORTED_MODULE_3__.openSVG;\r\n  } else {\r\n    chat.style.bottom = '0';\r\n    chat.style.top = '';\r\n    chat.classList.remove(\"floating-chat\");\r\n    chat.classList.remove('visible-chat', 'hidden-chat');\r\n    chat.classList.add(state.isVisible ? 'visible-chat' : 'hidden-chat');\r\n    toggleButton.innerHTML = state.isVisible ? _icons_js__WEBPACK_IMPORTED_MODULE_3__.closeSVG : _icons_js__WEBPACK_IMPORTED_MODULE_3__.openSVG;\r\n  }\r\n\r\n  handleElementsBehavior();\r\n}\r\n\r\n// 1. Update helpers.js to include the font size in the getChatState function\r\n\r\n// In getChatState() function in helpers.js\r\nfunction getChatState() {\r\n  const savedState = localStorage.getItem('chatState');\r\n  const defaultState = {\r\n    height: 300,\r\n    width: Math.min(window.innerWidth, 600),\r\n    left: 0,\r\n    floating: false,\r\n    top: window.innerHeight - 300,\r\n    isVisible: true,\r\n    fontSizeMultiplier: 1.0 // Add the new default font size multiplier\r\n  };\r\n\r\n  return savedState ? { ...defaultState, ...JSON.parse(savedState) } : defaultState;\r\n}\r\n\r\n// 2. Add a function to apply font size changes\r\nfunction applyFontSize(multiplier) {\r\n  const chatContainer = document.getElementById('app-chat-container');\r\n  const messageInput = document.getElementById('message-input');\r\n  if (!chatContainer) return;\r\n\r\n  // Apply font size to the main container\r\n  chatContainer.style.fontSize = `${multiplier}em`;\r\n\r\n  // Apply base font size to message input without multiplier\r\n  // since it inherits the multiplier from the container\r\n  if (messageInput) {\r\n    messageInput.style.fontSize = '1em';\r\n  }\r\n\r\n  // Save the current multiplier in the chat state\r\n  const chatState = getChatState();\r\n  saveChatState({\r\n    ...chatState,\r\n    fontSizeMultiplier: multiplier\r\n  });\r\n}\r\n\r\n// 3. Add a function to restore font size from state on initialization\r\nfunction restoreFontSize() {\r\n  const chatState = getChatState();\r\n  applyFontSize(chatState.fontSizeMultiplier);\r\n}\r\n\r\n// 4. Function to create the font size slider\r\nfunction createFontSizeControl() {\r\n  const chatContainer = document.getElementById('app-chat-container');\r\n  if (!chatContainer) return;\r\n\r\n  const chatState = getChatState();\r\n\r\n  // Create font size control container\r\n  const fontSizeControl = document.createElement('div');\r\n  fontSizeControl.className = 'font-size-control';\r\n\r\n  // Create the slider\r\n  const fontSlider = document.createElement('input');\r\n  fontSlider.type = 'range';\r\n  fontSlider.min = '0.8';\r\n  fontSlider.max = '1.5';\r\n  fontSlider.step = '0.1';\r\n  fontSlider.value = chatState.fontSizeMultiplier;\r\n  fontSlider.className = 'font-size-slider';\r\n\r\n  // Prevent dragging the chat when interacting with the slider\r\n  fontSlider.addEventListener('mousedown', (e) => {\r\n    e.stopPropagation();\r\n  });\r\n\r\n  // Update font size on input change\r\n  fontSlider.addEventListener('input', (e) => {\r\n    const value = parseFloat(e.target.value);\r\n    applyFontSize(value);\r\n  });\r\n\r\n  // Append the slider to the control container\r\n  fontSizeControl.appendChild(fontSlider);\r\n\r\n  // Add the control container to the chat drag area\r\n  const dragArea = document.querySelector('.chat-drag-area');\r\n  if (dragArea) {\r\n    dragArea.appendChild(fontSizeControl);\r\n  }\r\n}\r\n\r\nfunction saveChatState(state) {\r\n  localStorage.setItem('chatState', JSON.stringify(state));\r\n}\r\n\r\nconst parseMessageText = text => {\r\n  let i = 0, urls = [];\r\n  text = text.replace(/(\\b(https?|ftp):\\/\\/[-A-Z0-9+&@#\\/%?=~_|!:,.;]*[-A-Z0-9+&@#\\/%=~_|])/ig, m => {\r\n    urls.push(m);\r\n    return `___URL${i++}___`;\r\n  });\r\n  text = text\r\n    .replace(/:(\\w+):/g, (_, e) => `<img src=\"https://klavogonki.ru/img/smilies/${e}.gif\" alt=\"${e}\" />`)\r\n    .replace(/(\\p{Emoji_Presentation}|\\p{Emoji}\\uFE0F)/gu, '<span class=\"emoji-adjuster\">$&</span>');\r\n  urls.forEach((url, idx) => {\r\n    text = text.replace(`___URL${idx}___`, `<a href=\"${url}\" target=\"_blank\">${url}</a>`);\r\n  });\r\n  return text;\r\n}\r\n\r\nfunction clamp(value, min, max) {\r\n  return Math.min(Math.max(value, min), max);\r\n}\r\n\r\nfunction parseUsername(username) {\r\n  if (typeof username !== 'string') return username;\r\n  return username.replace(/^\\d+#/, '');\r\n}\r\n\r\n// Extract userId from JID, handling formats like \"123456#Username\"\r\nfunction extractUserId(jid) {\r\n  if (!jid) return null;\r\n  const parts = jid.split('/');\r\n  if (parts.length < 2) return null;\r\n\r\n  const secondPart = parts[1];\r\n  return secondPart.split('#')[0]; // Get everything before the # character\r\n}\r\n\r\n// Extract clean username from the full JID or login string\r\nfunction extractCleanUsername(login) {\r\n  if (!login) return \"Unknown\";\r\n\r\n  // If login contains #, get everything after it\r\n  if (login.includes('#')) {\r\n    return login.split('#')[1];\r\n  }\r\n\r\n  // Replace the parseUsername call with direct implementation\r\n  return login.replace(/^\\d+#/, '');\r\n}\r\n\r\nfunction addBigImageEventListeners() {\r\n  Object.entries(_definitions_js__WEBPACK_IMPORTED_MODULE_2__.state.bigImageEvents).forEach(([event, handler]) => {\r\n    document.addEventListener(event, handler);\r\n  });\r\n}\r\n\r\nfunction removeBigImageEventListeners() {\r\n  Object.entries(_definitions_js__WEBPACK_IMPORTED_MODULE_2__.state.bigImageEvents).forEach(([event, handler]) => {\r\n    document.removeEventListener(event, handler);\r\n  });\r\n}\r\n\r\nfunction adjustVisibility(element, action, opacity) {\r\n  if (!element) return;\r\n  void element.offsetHeight; // Force reflow\r\n  element.style.transition = 'opacity 0.3s ease';\r\n  element.style.opacity = action === 'show' ? opacity : '0';\r\n  if (action === 'hide') {\r\n    element.addEventListener('transitionend', () => {\r\n      if (element.style.opacity === '0' && element.parentNode) {\r\n        element.parentNode.removeChild(element);\r\n      }\r\n    }, { once: true });\r\n  }\r\n}\r\n\r\nconst isTrustedDomain = url => {\r\n  try {\r\n    const { hostname } = new URL(url);\r\n    const domain = hostname.toLowerCase().split('.').slice(-2).join('.');\r\n    return { isTrusted: _definitions_js__WEBPACK_IMPORTED_MODULE_2__.trustedDomains.includes(domain), domain };\r\n  } catch (err) {\r\n    console.error(\"Error in isTrustedDomain:\", err.message);\r\n    return { isTrusted: false, domain: url };\r\n  }\r\n};\r\n\r\nfunction isEncodedURL(url) {\r\n  const urlPattern = /^https?:\\/\\//;\r\n  const encodedPattern = /%[0-9A-Fa-f]{2}/;\r\n  return urlPattern.test(url) && encodedPattern.test(url);\r\n}\r\n\r\nfunction decodeURL(url) {\r\n  const [base] = url.split('#');\r\n  return decodeURIComponent(base).replace(/ /g, '_');\r\n}\r\n\r\nfunction highlightMentionWords() {\r\n  const container = document.getElementById('messages-panel');\r\n  if (!container) return;\r\n\r\n  const storedKeywords = localStorage.getItem('mentionKeywords');\r\n  if (!storedKeywords) return;\r\n\r\n  let mentionKeywords;\r\n  try {\r\n    mentionKeywords = JSON.parse(storedKeywords);\r\n    if (!Array.isArray(mentionKeywords)) return;\r\n  } catch (e) {\r\n    return;\r\n  }\r\n\r\n  const globalProcessed = new WeakSet();\r\n  const messages = container.querySelectorAll('.message-text');\r\n\r\n  messages.forEach((message) => {\r\n    const walker = document.createTreeWalker(\r\n      message,\r\n      NodeFilter.SHOW_TEXT,\r\n      {\r\n        acceptNode: (node) => {\r\n          if (globalProcessed.has(node)) return NodeFilter.FILTER_SKIP;\r\n          const parent = node.parentElement;\r\n          if (parent.closest('.mention, .time, .username')) {\r\n            return NodeFilter.FILTER_SKIP;\r\n          }\r\n          return NodeFilter.FILTER_ACCEPT;\r\n        }\r\n      }\r\n    );\r\n\r\n    const nodes = [];\r\n    let currentNode;\r\n    while ((currentNode = walker.nextNode())) nodes.push(currentNode);\r\n\r\n    nodes.forEach((node) => {\r\n      if (!globalProcessed.has(node)) {\r\n        processNode(node, mentionKeywords);\r\n        globalProcessed.add(node);\r\n      }\r\n    });\r\n  });\r\n\r\n  function processNode(node, keywords) {\r\n    const regex = /(@?[\\wа-яА-ЯёЁ'-]+)|[\\s]+|[^@\\s\\wа-яА-ЯёЁ'-]+/gu;\r\n    const tokens = node.textContent.match(regex) || [];\r\n\r\n    const fragment = document.createDocumentFragment();\r\n\r\n    tokens.forEach(token => {\r\n      const isMatch = keywords.some(keyword =>\r\n        keyword.localeCompare(token, undefined, { sensitivity: 'accent' }) === 0\r\n      );\r\n\r\n      if (isMatch) {\r\n        const mentionSpan = document.createElement('span');\r\n        mentionSpan.className = 'mention';\r\n        token.split('').forEach(char => {\r\n          const charSpan = document.createElement('span');\r\n          charSpan.style.color = mentionColors.getColor(char);\r\n          charSpan.textContent = char;\r\n          mentionSpan.appendChild(charSpan);\r\n        });\r\n        fragment.appendChild(mentionSpan);\r\n      } else {\r\n        fragment.appendChild(document.createTextNode(token));\r\n      }\r\n    });\r\n\r\n    node.parentNode.replaceChild(fragment, node);\r\n  }\r\n}\r\n\r\nlet firstTime = true;\r\nconst scrollThreshold = 600;\r\n\r\nfunction scrollToBottom() {\r\n  const container = document.getElementById('messages-panel');\r\n  if (!container) return;\r\n\r\n  if (firstTime) {\r\n    container.scrollTop = container.scrollHeight;\r\n    firstTime = false;\r\n  } else {\r\n    const distanceFromBottom = container.scrollHeight - container.scrollTop - container.clientHeight;\r\n    if (distanceFromBottom <= scrollThreshold) {\r\n      container.scrollTop = container.scrollHeight;\r\n    }\r\n  }\r\n}\r\n\r\nfunction showChatAlert(message, options = {}) {\r\n  const dragArea = document.querySelector('.chat-drag-area');\r\n  if (!dragArea) return;\r\n\r\n  const existingAlert = dragArea.querySelector('.chat-dynamic-alert');\r\n  if (existingAlert && existingAlert.parentNode === dragArea) {\r\n    dragArea.removeChild(existingAlert);\r\n  }\r\n\r\n  const defaultOptions = { type: 'info', duration: 3000 };\r\n  const settings = { ...defaultOptions, ...options };\r\n\r\n  const colorMap = {\r\n    info: '#2196F3',\r\n    warning: '#FF9800',\r\n    error: '#F44336',\r\n    success: '#4CAF50'\r\n  };\r\n\r\n  const alertElement = document.createElement('div');\r\n  alertElement.className = 'chat-dynamic-alert';\r\n  alertElement.innerHTML = message;\r\n\r\n  alertElement.style.cssText = `\r\n    position: absolute;\r\n    white-space: nowrap;\r\n    top: 50%;\r\n    left: 50%;\r\n    transform: translate(-50%, -50%);\r\n    color: ${colorMap[settings.type] || colorMap.info};\r\n    padding: 5px 10px;\r\n    border-radius: 3px;\r\n    z-index: 1000;\r\n    font-family: \"Montserrat\", sans-serif;\r\n    font-size: 10px;\r\n    font-weight: 500;\r\n    opacity: 0;\r\n  `;\r\n\r\n  dragArea.appendChild(alertElement);\r\n\r\n  function animateAlert() {\r\n    requestAnimationFrame(() => {\r\n      alertElement.style.transition = 'all 0.3s cubic-bezier(0.68, -0.55, 0.27, 1.55)';\r\n      alertElement.style.opacity = '1';\r\n      alertElement.style.transform = 'translate(-50%, -50%)';\r\n\r\n      setTimeout(() => {\r\n        alertElement.style.transition = 'transform 0.05s ease-in-out';\r\n        const shakeSequence = [\r\n          { x: 5, delay: 0 },\r\n          { x: -7, delay: 50 },\r\n          { x: 9, delay: 100 },\r\n          { x: -6, delay: 150 },\r\n          { x: 4, delay: 200 },\r\n          { x: -3, delay: 250 },\r\n          { x: 0, delay: 300 }\r\n        ];\r\n\r\n        shakeSequence.forEach((move) => {\r\n          setTimeout(() => {\r\n            alertElement.style.transform = `translate(calc(-50% + ${move.x}px), -50%)`;\r\n          }, move.delay);\r\n        });\r\n      }, 300);\r\n\r\n      setTimeout(() => {\r\n        alertElement.style.transition = 'opacity 0.3s ease-in-out';\r\n        alertElement.style.opacity = '0';\r\n        setTimeout(() => {\r\n          if (alertElement && alertElement.parentNode === dragArea) {\r\n            dragArea.removeChild(alertElement);\r\n          }\r\n        }, 300);\r\n      }, settings.duration);\r\n    });\r\n  }\r\n\r\n  animateAlert();\r\n}\r\n\r\nfunction focusTextInput() {\r\n  const chatContainer = document.getElementById('app-chat-container');\r\n  const element = document.getElementById('message-input');\r\n  if (element && chatContainer && chatContainer.style.display !== 'none') {\r\n    element.focus();\r\n    return true;\r\n  }\r\n  return false;\r\n}\r\n\r\n// Helper to fetch JSON and validate response\r\nasync function fetchJSON(url) {\r\n  const response = await fetch(url);\r\n  if (!response.ok) throw new Error(`Failed to fetch ${url}`);\r\n  return response.json();\r\n}\r\n\r\n// Helper function to get Exact user ID by username via the search API\r\nasync function getExactUserIdByName(userName) {\r\n  // Define the search API URL\r\n  const searchApiUrl = `https://klavogonki.ru/api/profile/search-users?query=${encodeURIComponent(userName)}`;\r\n\r\n  try {\r\n    // Get search results from the API\r\n    const searchResults = await fetchJSON(searchApiUrl);\r\n\r\n    // Ensure search results exist and contain data\r\n    if (!searchResults.all?.length) {\r\n      throw new Error(`User ${userName} not found.`);\r\n    }\r\n\r\n    // Return the ID of the user with the exact matching login\r\n    const user = searchResults.all.find(user => user.login === userName);\r\n    if (!user) {\r\n      throw new Error(`Exact match for user ${userName} not found.`);\r\n    }\r\n\r\n    return user.id;\r\n  } catch (error) {\r\n    // console.error('Error getting user ID:', error);\r\n    showChatAlert(`Could not find user \"${userName}\"`, { type: 'error', duration: 5000 });\r\n    return null;\r\n  }\r\n}\r\n\r\n// Function to extract target username from message input\r\nfunction extractTargetUsername(input) {\r\n  const match = input.match(/<([^>]+)>/);\r\n  return match ? match[1] : null;\r\n}\r\n\r\n// State management for private messaging\r\nconst privateMessageState = {\r\n  isPrivateMode: false,\r\n  targetUsername: null,\r\n  targetId: null,\r\n  fullJid: null,\r\n\r\n  async setPrivateTarget(username) {\r\n    if (!username) {\r\n      this.exitPrivateMode();\r\n      return false;\r\n    }\r\n\r\n    try {\r\n      const userId = await getExactUserIdByName(username);\r\n      if (!userId) return false;\r\n\r\n      this.isPrivateMode = true;\r\n      this.targetUsername = username;\r\n      this.targetId = userId;\r\n      this.fullJid = `${userId}#${username}@jabber.klavogonki.ru/web`;\r\n\r\n      return true;\r\n    } catch (error) {\r\n      console.error('Error setting private target:', error);\r\n      return false;\r\n    }\r\n  },\r\n\r\n  exitPrivateMode() {\r\n    this.isPrivateMode = false;\r\n    this.targetUsername = null;\r\n    this.targetId = null;\r\n    this.fullJid = null;\r\n  }\r\n};\r\n\r\n// Toggle private message mode based on input value\r\nasync function handlePrivateMessageInput(inputElement) {\r\n  if (!inputElement) return;\r\n  const input = inputElement.value;\r\n  // Updated regex to include hyphens and other common username special characters\r\n  const privateModeRegex = /^\\/pm\\s+([\\wа-яА-ЯёЁ\\-\\.\\_\\+]+)\\s/;\r\n  const exitPrivateModeRegex = /^\\/exit\\s*$/;\r\n  const match = input.match(privateModeRegex);\r\n  if (match) {\r\n    const username = match[1];\r\n    const success = await privateMessageState.setPrivateTarget(username);\r\n    if (success) {\r\n      enterPrivateMode(username);\r\n      inputElement.value = input.replace(privateModeRegex, ''); // Remove the /pm username part\r\n    } else {\r\n      showChatAlert(`Could not find user \"${username}\"`, { type: 'error', duration: 3000 });\r\n      exitPrivateMode();\r\n    }\r\n  } else if (exitPrivateModeRegex.test(input)) {\r\n    exitPrivateMode();\r\n    inputElement.value = ''; // Clear the input\r\n  }\r\n}\r\n\r\nfunction enterPrivateMode(username) {\r\n  const messageInput = document.getElementById('message-input');\r\n  if (privateMessageState.isPrivateMode && privateMessageState.targetUsername !== username) {\r\n    exitPrivateMode();\r\n  }\r\n\r\n  if (!messageInput.classList.contains('private-mode') || privateMessageState.targetUsername !== username) {\r\n    messageInput.classList.add('private-mode');\r\n    messageInput.placeholder = `PM to ➡ ${username}`;\r\n\r\n    // Create or update exit button\r\n    let exitButton = document.querySelector('.private-mode-exit');\r\n    if (!exitButton) {\r\n      exitButton = document.createElement('span');\r\n      exitButton.className = 'private-mode-exit';\r\n\r\n      // Add click event to exit private mode\r\n      exitButton.addEventListener('click', () => {\r\n        exitPrivateMode();\r\n        messageInput.focus();\r\n      });\r\n\r\n      // Add the exit button to the UI near the input\r\n      const inputContainer = messageInput.parentElement;\r\n      inputContainer.insertBefore(exitButton, messageInput.nextSibling);\r\n    }\r\n\r\n    // Set default closed lock emoji and title\r\n    exitButton.innerHTML = \"🔒\";\r\n    exitButton.title = \"Exit private mode\";\r\n\r\n    // Change emoji on hover: open lock on mouseenter, closed lock on mouseleave\r\n    exitButton.addEventListener('mouseenter', () => {\r\n      exitButton.innerHTML = \"🔓\";\r\n    });\r\n\r\n    exitButton.addEventListener('mouseleave', () => {\r\n      exitButton.innerHTML = \"🔒\";\r\n    });\r\n\r\n    showChatAlert(`Private chat with ${username} activated`, { type: 'warning', duration: 3000 });\r\n    privateMessageState.isPrivateMode = true;\r\n    privateMessageState.targetUsername = username;\r\n  } else if (privateMessageState.targetUsername === username) {\r\n    messageInput.placeholder = `️PM to ➡ ${username}`;\r\n    showChatAlert(`Private chat with ${username} activated`, { type: 'warning', duration: 3000 });\r\n  }\r\n}\r\n\r\nfunction exitPrivateMode() {\r\n  const messageInput = document.getElementById('message-input');\r\n  if (messageInput.classList.contains('private-mode')) {\r\n    messageInput.classList.remove('private-mode');\r\n    messageInput.placeholder = ''; // Reset placeholder\r\n\r\n    // Remove the exit button\r\n    const exitButton = document.querySelector('.private-mode-exit');\r\n    if (exitButton) exitButton.remove();\r\n\r\n    privateMessageState.exitPrivateMode();\r\n    showChatAlert('Exited private chat mode', { type: 'success', duration: 3000 });\r\n  }\r\n}\r\n\r\n// Handle ESC key to exit private mode\r\nfunction setupPrivateMessageEvents() {\r\n  const input = document.getElementById('message-input');\r\n  if (!input) return;\r\n\r\n  // Add ESC key handler to exit private mode\r\n  input.addEventListener('keydown', (e) => {\r\n    if (e.key === 'Escape' && privateMessageState.isPrivateMode) {\r\n      exitPrivateMode();\r\n      e.preventDefault();\r\n    }\r\n  });\r\n\r\n  // Check for private message mode on input changes\r\n  input.addEventListener('input', () => {\r\n    handlePrivateMessageInput(input);\r\n  });\r\n}\r\n\r\n// Only the sleep function is exported from here.\r\nfunction sleep(ms) {\r\n  return new Promise(resolve => setTimeout(resolve, ms));\r\n}\r\n\r\n// Helper function to check if an image exists\r\nfunction checkImageExists(url) {\r\n  return new Promise((resolve) => {\r\n    const img = new Image();\r\n    img.onload = () => resolve(true);\r\n    img.onerror = () => resolve(false);\r\n    img.src = url;\r\n  });\r\n}\r\n\r\n// Function to randomize emoji and add shake effect\r\nfunction setupRandomEmojiAttention(emojiButton, frequency) {\r\n  // Get all emoji keys from the emojiKeywords object\r\n  const emojis = Object.keys(_data_emojiData_js__WEBPACK_IMPORTED_MODULE_5__.emojiKeywords);\r\n\r\n  // Original emoji to return to after attention-getting effect\r\n  const originalEmoji = \"🙂\";\r\n\r\n  // Function to select random emoji and apply shake effect\r\n  const showRandomEmoji = () => {\r\n    // Get a random emoji from the collection\r\n    const randomEmoji = emojis[Math.floor(Math.random() * emojis.length)];\r\n\r\n    // Set the random emoji\r\n    emojiButton.innerHTML = randomEmoji;\r\n\r\n    // Apply shake effect\r\n    (0,_animations_js__WEBPACK_IMPORTED_MODULE_4__.addShakeEffect)(emojiButton);\r\n\r\n    // Return to original emoji after animation completes\r\n    setTimeout(() => {\r\n      emojiButton.innerHTML = originalEmoji;\r\n    }, 1500);\r\n  };\r\n\r\n  // Set interval to periodically show random emoji\r\n  const intervalId = setInterval(showRandomEmoji, frequency);\r\n\r\n  // Store the intervalId on the element for potential cleanup\r\n  emojiButton.randomEmojiIntervalId = intervalId;\r\n\r\n  return intervalId;\r\n}\r\n\r\n/**\r\n * Generates a random number of milliseconds between the specified minimum and maximum values\r\n * @param {number} minMs - Minimum time in milliseconds\r\n * @param {number} maxMs - Maximum time in milliseconds\r\n * @returns {number} - A random number of milliseconds between minMs and maxMs (inclusive)\r\n */\r\nfunction getRandomInterval(minMs, maxMs) {\r\n  // Ensure min is not greater than max\r\n  if (minMs > maxMs) {\r\n    [minMs, maxMs] = [maxMs, minMs]; // Swap values if min > max\r\n  }\r\n\r\n  // Calculate a random value between min and max (inclusive)\r\n  return Math.floor(Math.random() * (maxMs - minMs + 1)) + minMs;\r\n}\r\n\r\nfunction addViewportMeta() {\r\n  if (!document.querySelector('meta[name=\"viewport\"]')) {\r\n    const viewportMeta = document.createElement('meta');\r\n    viewportMeta.name = 'viewport';\r\n    viewportMeta.content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no';\r\n    document.head.appendChild(viewportMeta);\r\n    console.log('Viewport meta tag added dynamically');\r\n  }\r\n}\r\n\r\n// Instead of getting the elements immediately, we declare module-level variables.\r\nlet chatField = null;\r\nlet messagesContainer = null;\r\nlet lengthPopup = null;\r\n\r\n/**\r\n * Create and append the length popup.\r\n * @param {HTMLElement} container - The container (messagesPanel) to which the popup should be appended.\r\n */\r\nfunction createLengthPopup(container) {\r\n  messagesContainer = container;\r\n  lengthPopup = document.createElement('div');\r\n  lengthPopup.className = 'length-field-popup';\r\n  messagesContainer.appendChild(lengthPopup);\r\n}\r\n\r\n// Create a canvas for text measurement.\r\nconst textMeasurementCanvas = document.createElement('canvas');\r\nconst textMeasurementContext = textMeasurementCanvas.getContext('2d');\r\n\r\nlet isPopupVisible = false;\r\nlet previousLength = 0;\r\nlet hidePopupTimeout;\r\n\r\nfunction updateLengthPopupColor(length) {\r\n  if (!lengthPopup) {\r\n    console.error('lengthPopup is not defined');\r\n    return;\r\n  }\r\n  let textColor;\r\n  if (length === 0) {\r\n    textColor = 'hsl(200, 20%, 50%)'; // Light Blue\r\n  } else if (length >= 1 && length <= 90) {\r\n    textColor = 'hsl(120, 100%, 40%)'; // Bright Green\r\n  } else if (length > 90 && length <= 100) {\r\n    const factor = (length - 90) / 10;\r\n    const h = Math.round(120 + factor * (60 - 120)); // Interpolating hue\r\n    textColor = `hsl(${h}, 100%, 40%)`;\r\n  } else if (length > 100 && length <= 190) {\r\n    textColor = 'hsl(60, 100%, 50%)'; // Bright Yellow\r\n  } else if (length > 190 && length <= 200) {\r\n    const factor = (length - 190) / 10;\r\n    const h = Math.round(60 + factor * (30 - 60)); // Interpolating hue\r\n    textColor = `hsl(${h}, 100%, 50%)`;\r\n  } else if (length > 200 && length <= 250) {\r\n    textColor = 'hsl(40, 100%, 50%)'; // Orange\r\n  } else if (length > 250 && length <= 300) {\r\n    const factor = (length - 250) / 50;\r\n    const h = Math.round(40 + factor * (0 - 40)); // Interpolating hue\r\n    textColor = `hsl(${h}, 100%, 70%)`;\r\n  } else {\r\n    textColor = 'hsl(0, 100%, 70%)'; // Red\r\n  }\r\n  lengthPopup.style.color = textColor;\r\n}\r\n\r\nfunction updatePopupMetrics(text) {\r\n  if (!chatField) {\r\n    console.error('chatField is not set.');\r\n    return;\r\n  }\r\n  // Get current font from input field.\r\n  const computedStyle = getComputedStyle(chatField);\r\n  textMeasurementContext.font = `${computedStyle.fontWeight} ${computedStyle.fontSize} ${computedStyle.fontFamily}`;\r\n  // Measure text.\r\n  const textWidth = textMeasurementContext.measureText(text).width;\r\n  // Calculate position.\r\n  const newLeft = chatField.offsetLeft + textWidth + 5;\r\n  const maxLeft = chatField.offsetLeft + chatField.offsetWidth - lengthPopup.offsetWidth;\r\n  lengthPopup.style.left = `${Math.min(newLeft, maxLeft)}px`;\r\n}\r\n\r\nconst arrowRightBold = \"➡\"; // Heavy right arrow\r\nconst arrowLeftBold = \"⬅\"; // Heavy left arrow\r\n\r\nfunction updateLengthPopup(length) {\r\n  let displayText =\r\n    length > previousLength ? `${length} ${arrowRightBold}` :\r\n    length < previousLength ? `${arrowLeftBold} ${length}` :\r\n    `${length}`;\r\n    \r\n  lengthPopup.textContent = displayText;\r\n  updateLengthPopupColor(length);\r\n  previousLength = length;\r\n}\r\n\r\nfunction togglePopup(show) {\r\n  if (isPopupVisible === show) return;\r\n  lengthPopup.classList.toggle('bounce-in', show);\r\n  lengthPopup.classList.toggle('bounce-out', !show);\r\n  isPopupVisible = show;\r\n  if (!show) setTimeout(() => lengthPopup.classList.remove('bounce-out'), 500);\r\n}\r\n\r\nfunction resetPopup() {\r\n  updateLengthPopup(0);\r\n  Object.assign(lengthPopup.style, { left: '0px', color: 'hsl(200, 20%, 50%)' });\r\n}\r\n\r\nfunction handleInputEvent() {\r\n  clearTimeout(hidePopupTimeout);\r\n  updateLengthPopup(chatField.value.length);\r\n  updatePopupMetrics(chatField.value);\r\n  togglePopup(true);\r\n  hidePopupTimeout = setTimeout(() => togglePopup(false), 1000);\r\n}\r\n\r\nfunction handleKeydownEvent(e) {\r\n  if (e.key !== 'Enter') return;\r\n  resetPopup();\r\n  togglePopup(true);\r\n  hidePopupTimeout = setTimeout(() => togglePopup(false), 1000);\r\n}\r\n\r\n/**\r\n * Initializes chat length popup events.\r\n * @param {HTMLElement} field - The chat input field.\r\n */\r\nfunction initChatLengthPopupEvents(field) {\r\n  chatField = field;\r\n  if (!chatField) {\r\n    console.error('chatField is null');\r\n    return;\r\n  }\r\n  // Only attach event listeners if the popup was created.\r\n  if (!lengthPopup) return;\r\n  chatField.addEventListener('input', handleInputEvent);\r\n  chatField.addEventListener('keydown', handleKeydownEvent);\r\n}\r\n\r\n/**\r\n * Converts a given local time to Moscow time (UTC+3) based on the system's timezone.\r\n *\r\n * How it works:\r\n * 1. Gets the system's local timezone offset in minutes (positive if behind UTC).\r\n * 2. Converts the local offset to total minutes from UTC.\r\n * 3. Defines Moscow's fixed offset as UTC+3 (180 minutes).\r\n * 4. Calculates the difference between Moscow's offset and the local offset.\r\n * 5. Parses the input time and converts it into total minutes since midnight.\r\n * 6. Adjusts the time by the calculated difference.\r\n * 7. Ensures the result stays within the 24-hour format (wrap-around handling).\r\n * 8. Converts the result back to HH:MM:SS format and returns it.\r\n *\r\n * @param {string} time - The local time in \"HH:MM:SS\" format.\r\n * @returns {string} - The converted time in Moscow time (HH:MM:SS).\r\n */\r\nfunction calibrateToMoscowTime(time) {\r\n  // Get local timezone offset in minutes (positive if local is behind UTC)\r\n  const localOffsetMinutes = new Date().getTimezoneOffset();\r\n\r\n  // Convert local offset to total minutes from UTC (local time = UTC + localTotalOffset)\r\n  const localTotalOffset = -localOffsetMinutes;\r\n\r\n  // Moscow is UTC+3 (180 minutes)\r\n  const moscowOffset = 3 * 60; // 180 minutes\r\n\r\n  // Calculate the adjustment needed: Moscow offset - local offset\r\n  const diffMinutes = moscowOffset - localTotalOffset;\r\n\r\n  // Parse input time\r\n  const [hours, minutes, seconds] = time.split(':').map(Number);\r\n\r\n  // Convert input time to total minutes since 00:00\r\n  const totalInputMinutes = hours * 60 + minutes;\r\n\r\n  // Adjust by diff and wrap within a single day (1440 minutes)\r\n  let adjustedMinutes = totalInputMinutes + diffMinutes;\r\n  adjustedMinutes = ((adjustedMinutes % 1440) + 1440) % 1440; // Ensure positive\r\n\r\n  // Convert back to hours and minutes\r\n  const adjustedHours = Math.floor(adjustedMinutes / 60);\r\n  const adjustedMins = adjustedMinutes % 60;\r\n\r\n  // Format the result with original seconds\r\n  return `${adjustedHours.toString().padStart(2, '0')}:` +\r\n    `${adjustedMins.toString().padStart(2, '0')}:` +\r\n    `${seconds.toString().padStart(2, '0')}`;\r\n}\n\n//# sourceURL=webpack://tampermonkey-script/./src/helpers.js?");

/***/ }),

/***/ "./src/icons.js":
/*!**********************!*\
  !*** ./src/icons.js ***!
  \**********************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   closeSVG: () => (/* binding */ closeSVG),\n/* harmony export */   collapseSVG: () => (/* binding */ collapseSVG),\n/* harmony export */   expandSVG: () => (/* binding */ expandSVG),\n/* harmony export */   helpSVG: () => (/* binding */ helpSVG),\n/* harmony export */   openSVG: () => (/* binding */ openSVG),\n/* harmony export */   sendSVG: () => (/* binding */ sendSVG)\n/* harmony export */ });\nconst svgUrl = \"http://www.w3.org/2000/svg\";\r\nconst iconSize = 24;\r\n\r\n// SVG icon\r\nconst buttonAccentColdColor = \"#096AD9\";\r\nconst buttonAccentWarmColor = \"#deb887\";\r\nconst buttonOpenedColor = \"#82B32A\";\r\nconst buttonClosedColor = \"#B34A2A\";\r\nconst sendSVG = `\r\n  <svg xmlns=\"${svgUrl}\" \r\n      width=\"${iconSize}\" \r\n      height=\"${iconSize}\" \r\n      viewBox=\"0 0 250 250\">\r\n    <path fill=\"${buttonAccentColdColor}\" d=\"M22.32 98.04l-19.04 -87.15c-0.75,-3.46 0.48,-6.84 3.29,-9 2.81,-2.17 6.39,-2.49 9.55,-0.87l225.95 116.02c3.07,1.57 4.87,4.52 4.87,7.96 0,3.44 -1.8,6.39 -4.87,7.96l-225.95 116.02c-3.16,1.62 -6.74,1.3 -9.55,-0.87 -2.81,-2.16 -4.04,-5.54 -3.29,-9l19.04 -87.15c0.79,-3.62 3.53,-6.26 7.18,-6.91l102.6 -18.19c0.91,-0.16 1.56,-0.94 1.56,-1.86 0,-0.92 -0.65,-1.7 -1.56,-1.86l-102.6 -18.19c-3.65,-0.65 -6.39,-3.29 -7.18,-6.91z\"/>\r\n  </svg>\r\n`;\r\n\r\nconst closeSVG = `\r\n  <svg xmlns=\"${svgUrl}\" \r\n       width=\"${iconSize / 1.6}\" \r\n       height=\"${iconSize / 1.6}\" \r\n       viewBox=\"0 0 250 250\" \r\n       style=\"shape-rendering:geometricPrecision; text-rendering:geometricPrecision; image-rendering:optimizeQuality; fill-rule:evenodd; clip-rule:evenodd\">\r\n      <path fill=\"${buttonOpenedColor}\" d=\"M46.62 0l156.76 0c25.64,0 46.62,20.98 46.62,46.62l0 156.75c0,25.65 -20.98,46.63 -46.62,46.63l-156.76 0c-25.64,0 -46.62,-20.98 -46.62,-46.63l0 -156.75c0,-25.64 20.98,-46.62 46.62,-46.62zm45.71 70.24l32.67 32.67 32.67 -32.67c2.73,-2.73 7.18,-2.73 9.91,0l12.18 12.18c2.73,2.73 2.73,7.18 0,9.91l-32.67 32.67 32.67 32.66c2.73,2.74 2.73,7.19 0,9.92l-12.18 12.18c-2.73,2.73 -7.18,2.73 -9.91,0l-32.67 -32.67 -32.67 32.67c-2.73,2.73 -7.18,2.73 -9.91,0l-12.18 -12.18c-2.73,-2.73 -2.73,-7.18 0,-9.92l32.67 -32.66 -32.67 -32.67c-2.73,-2.73 -2.73,-7.18 0,-9.91l12.18 -12.18c2.73,-2.73 7.18,-2.73 9.91,0z\"/>\r\n  </svg>\r\n`;\r\n\r\nconst openSVG = `\r\n  <svg xmlns=\"${svgUrl}\" \r\n       width=\"${iconSize / 1.6}\" \r\n       height=\"${iconSize / 1.6}\" \r\n       viewBox=\"0 0 250 250\" \r\n       style=\"shape-rendering:geometricPrecision; text-rendering:geometricPrecision; image-rendering:optimizeQuality; fill-rule:evenodd; clip-rule:evenodd\">\r\n      <path fill=\"${buttonClosedColor}\" d=\"M46.62 0l156.76 0c25.64,0 46.62,20.98 46.62,46.62l0 156.75c0,25.65 -20.98,46.63 -46.62,46.63l-156.76 0c-25.64,0 -46.62,-20.98 -46.62,-46.63l0 -156.75c0,-25.64 20.98,-46.62 46.62,-46.62zm15.5 135.79l57.92 -57.93c2.73,-2.73 7.19,-2.72 9.92,0.01l57.92 57.92c2.73,2.73 2.73,7.18 0,9.91l-12.18 12.18c-2.73,2.73 -7.18,2.73 -9.92,0l-35.82 -35.83c-2.73,-2.73 -7.19,-2.73 -9.92,0l-35.82 35.83c-2.74,2.73 -7.19,2.73 -9.92,0l-12.18 -12.18c-2.73,-2.73 -2.73,-7.18 0,-9.91z\"/>\r\n  </svg>\r\n`;\r\n\r\nconst collapseSVG = `\r\n  <svg xmlns=\"${svgUrl}\" \r\n       width=\"${iconSize / 1.6}\" \r\n       height=\"${iconSize / 1.6}\" \r\n       viewBox=\"0 0 250 250\"\r\n       style=\"shape-rendering:geometricPrecision; text-rendering:geometricPrecision; image-rendering:optimizeQuality; fill-rule:evenodd; clip-rule:evenodd\">\r\n    <path fill=\"${buttonOpenedColor}\" d=\"M46.62 0l156.76 0c25.64,0 46.62,20.98 46.62,46.62l0 156.75c0,25.65 -20.98,46.63 -46.62,46.63l-156.76 0c-25.64,0 -46.62,-20.98 -46.62,-46.63l0 -156.75c0,-25.64 20.98,-46.62 46.62,-46.62zm109.99 181.69l-75.07 0c-7.3,0 -13.23,-5.92 -13.23,-13.22l0 -75.08c0,-2.35 1.92,-4.28 4.28,-4.28l17.9 0c2.35,0 4.27,1.93 4.27,4.28l0 32.81c0,1.77 1.01,3.28 2.64,3.96 1.63,0.67 3.42,0.33 4.66,-0.93l59.68 -59.68c1.67,-1.65 4.38,-1.65 6.05,0l12.66 12.66c1.66,1.67 1.66,4.38 0,6.05l-59.68 59.68c-1.26,1.24 -1.6,3.03 -0.93,4.66 0.68,1.63 2.19,2.64 3.96,2.64l32.81 0c2.35,0 4.28,1.92 4.28,4.28l0 17.9c0,2.36 -1.93,4.28 -4.28,4.28z\"/>\r\n  </svg>\r\n`;\r\n\r\nconst expandSVG = `\r\n  <svg xmlns=\"${svgUrl}\" \r\n       width=\"${iconSize / 1.6}\" \r\n       height=\"${iconSize / 1.6}\" \r\n       viewBox=\"0 0 250 250\"\r\n       style=\"shape-rendering:geometricPrecision; text-rendering:geometricPrecision; image-rendering:optimizeQuality; fill-rule:evenodd; clip-rule:evenodd\">\r\n    <path fill=\"${buttonClosedColor}\" d=\"M46.62 0l156.76 0c25.64,0 46.62,20.98 46.62,46.62l0 156.75c0,25.65 -20.98,46.63 -46.62,46.63l-156.76 0c-25.64,0 -46.62,-20.98 -46.62,-46.63l0 -156.75c0,-25.64 20.98,-46.62 46.62,-46.62zm46.77 68.31l75.07 0c7.3,0 13.23,5.92 13.23,13.22l0 75.08c0,2.35 -1.92,4.28 -4.28,4.28l-17.9 0c-2.35,0 -4.27,-1.93 -4.27,-4.28l0 -32.81c0,-1.77 -1.01,-3.28 -2.64,-3.96 -1.63,-0.67 -3.42,-0.33 -4.66,0.93l-59.68 59.68c-1.67,1.65 -4.38,1.65 -6.05,0l-12.66 -12.66c-1.66,-1.67 -1.66,-4.38 0,-6.05l59.68 -59.68c1.26,-1.24 1.6,-3.03 0.93,-4.66 -0.68,-1.63 -2.19,-2.64 -3.96,-2.64l-32.81 0c-2.35,0 -4.28,-1.92 -4.28,-4.27l0 -17.9c0,-2.36 1.93,-4.28 4.28,-4.28z\"/>\r\n  </svg>\r\n`;\r\n\r\nconst helpSVG = `\r\n  <svg xmlns=\"${svgUrl}\" \r\n       width=\"${iconSize / 1.6}\" \r\n       height=\"${iconSize / 1.6}\" \r\n       viewBox=\"0 0 250 250\"\r\n       style=\"shape-rendering:geometricPrecision; text-rendering:geometricPrecision; image-rendering:optimizeQuality; fill-rule:evenodd; clip-rule:evenodd\">\r\n    <path fill=\"${buttonOpenedColor}\" d=\"M46.59 0l156.82 0c25.65,0 46.59,20.94 46.59,46.59l0 156.82c0,25.65 -20.94,46.59 -46.59,46.59l-156.82 0c-25.65,0 -46.59,-20.94 -46.59,-46.59l0 -156.82c0,-25.65 20.94,-46.59 46.59,-46.59zm130.96 87.85c0,6.57 -1.03,12.72 -3.08,18.06 -2.05,5.34 -4.93,9.85 -8.42,13.75 -3.69,3.9 -8,7.39 -13.13,10.47 -4.11,2.46 -8.83,4.93 -13.96,6.98 -1.64,0.82 -2.87,2.46 -2.87,4.51l0 15.19c0,2.67 -2.06,4.72 -4.73,4.72l-25.04 0c-2.66,0 -4.92,-2.05 -4.92,-4.72l0 -25.65c0,-2.26 1.44,-3.9 3.49,-4.52 2.87,-1.02 5.95,-2.05 9.23,-3.28 4.52,-1.85 8.62,-3.9 12.11,-6.57 3.9,-2.67 6.78,-5.75 9.24,-9.24 2.46,-3.49 3.49,-7.59 3.49,-12.31 0,-6.78 -2.05,-11.7 -6.36,-14.78 -4.31,-2.88 -10.06,-4.52 -17.65,-4.52 -5.75,0 -11.7,1.24 -18.07,3.7 -5.33,2.05 -9.85,4.31 -13.13,6.36 -1.03,0.62 -2.26,0.62 -3.29,0 -1.02,-0.62 -1.64,-1.64 -1.64,-2.87l0 -23.2c0,-2.05 1.23,-3.9 3.08,-4.51 4.1,-1.44 9.44,-3.08 15.8,-4.52 8.42,-2.05 17.24,-3.07 26.89,-3.07 8.42,0 16.01,1.02 22.58,3.07 6.36,2.06 12.11,4.72 16.62,8.42 4.52,3.49 7.8,7.8 10.27,12.72 2.26,4.73 3.49,10.06 3.49,15.81l0 0zm-46.19 114.12l-25.04 0c-2.67,0 -4.92,-2.05 -4.92,-4.72l0 -17.24c0,-2.67 2.25,-4.72 4.92,-4.72l25.04 0c2.67,0 4.72,2.05 4.72,4.72l0 17.24c0,2.67 -2.05,4.72 -4.72,4.72z\"/>\r\n  </svg>\r\n`;\r\n\n\n//# sourceURL=webpack://tampermonkey-script/./src/icons.js?");

/***/ }),

/***/ "./src/messageManager.js":
/*!*******************************!*\
  !*** ./src/messageManager.js ***!
  \*******************************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   \"default\": () => (/* binding */ MessageManager)\n/* harmony export */ });\n/* harmony import */ var _helpers_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./helpers.js */ \"./src/helpers.js\");\n\r\n\r\nclass MessageManager {\r\n  constructor(panelId = 'messages-panel', currentUsername = '') {\r\n    this.panel = document.getElementById(panelId);\r\n    this.messages = [];\r\n    this.messageIdCounter = 0;\r\n    this.currentUsername = currentUsername;\r\n    this.sentMessageTexts = new Set(); // Track recently sent messages\r\n    this.processedMessageIds = new Set(); // Now used exclusively for deduplication\r\n    this.chatHistory = new Map(); // Local in-memory map for chat history\r\n  }\r\n\r\n  processMessages(xmlResponse) {\r\n    if (!xmlResponse || typeof xmlResponse !== 'string') return;\r\n\r\n    const parser = new DOMParser();\r\n    const doc = parser.parseFromString(xmlResponse, \"text/xml\");\r\n    const messageElements = doc.getElementsByTagName(\"message\");\r\n    let newMessagesAdded = false;\r\n\r\n    Array.from(messageElements).forEach(msg => {\r\n      const bodyNode = msg.getElementsByTagName(\"body\")[0];\r\n      if (!bodyNode || !bodyNode.textContent) return;\r\n\r\n      const text = bodyNode.textContent.trim();\r\n      if (text === \"This room is not anonymous\") return;\r\n\r\n      const fromAttr = msg.getAttribute(\"from\");\r\n      const from = fromAttr ? fromAttr.split('#')[1]?.split('@')[0] || \"unknown\" : \"unknown\";\r\n      const cleanFrom = (0,_helpers_js__WEBPACK_IMPORTED_MODULE_0__.parseUsername)(from);\r\n\r\n      // Generate a unique id based solely on username and message text.\r\n      const uniqueId = `[${cleanFrom}] ${text}`;\r\n\r\n      // Skip this message if it has already been processed.\r\n      if (this.processedMessageIds.has(uniqueId)) return;\r\n\r\n      // Get timestamp from <delay> if available, otherwise use current time.\r\n      let timestamp = new Date().toISOString();\r\n      const delayNodes = msg.getElementsByTagName(\"delay\");\r\n      if (delayNodes.length && delayNodes[0].getAttribute(\"stamp\")) {\r\n        timestamp = delayNodes[0].getAttribute(\"stamp\");\r\n      }\r\n\r\n      const toAttr = msg.getAttribute(\"to\");\r\n      const type = msg.getAttribute(\"type\");\r\n      const isPrivate = type === 'chat';\r\n      let recipient = null;\r\n      if (isPrivate && toAttr) {\r\n        recipient = toAttr.split('#')[1]?.split('@')[0] || toAttr;\r\n        recipient = (0,_helpers_js__WEBPACK_IMPORTED_MODULE_0__.parseUsername)(recipient);\r\n      }\r\n\r\n      const messageObj = {\r\n        id: uniqueId,\r\n        from: cleanFrom,\r\n        text,\r\n        timestamp,\r\n        isPrivate,\r\n        recipient,\r\n        pending: false\r\n      };\r\n\r\n      this.messages.push(messageObj);\r\n      this.chatHistory.set(uniqueId, messageObj);\r\n      this.processedMessageIds.add(uniqueId);\r\n      newMessagesAdded = true;\r\n    });\r\n\r\n    if (newMessagesAdded) {\r\n      this.updatePanel();\r\n    }\r\n  }\r\n\r\n  // Method to add a sent message.\r\n  addSentMessage(text, options = {}) {\r\n    this.sentMessageTexts.add(text);\r\n\r\n    // Generate a unique ID using the same format as processMessages\r\n    const uniqueId = `[${this.currentUsername}] ${text}`;\r\n\r\n    const messageObj = {\r\n      id: uniqueId,\r\n      from: this.currentUsername,\r\n      text,\r\n      timestamp: new Date().toISOString(),\r\n      isPrivate: options.isPrivate || false,\r\n      recipient: options.recipient || null,\r\n      pending: options.pending || false\r\n    };\r\n\r\n    // Skip if this exact message has already been processed\r\n    if (this.processedMessageIds.has(uniqueId)) return;\r\n\r\n    this.messages.push(messageObj);\r\n    this.chatHistory.set(uniqueId, messageObj);\r\n    this.processedMessageIds.add(uniqueId);\r\n    this.updatePanel();\r\n\r\n    // Limit the size of the sent messages set.\r\n    if (this.sentMessageTexts.size > 20) {\r\n      const entries = Array.from(this.sentMessageTexts);\r\n      for (let i = 0; i < entries.length - 20; i++) {\r\n        this.sentMessageTexts.delete(entries[i]);\r\n      }\r\n    }\r\n\r\n    return uniqueId; // Return the ID so it can be used for updating pending status\r\n  }\r\n\r\n  // New: Update pending status of a message by ID.\r\n  updatePendingStatus(messageId, pendingStatus) {\r\n    const msg = this.chatHistory.get(messageId);\r\n    if (msg) {\r\n      msg.pending = pendingStatus;\r\n      this.updatePanel();\r\n    }\r\n  }\r\n\r\n  updatePanel() {\r\n    if (!this.panel) return;\r\n    // Ensure messages are in chronological order.\r\n    this.messages.sort((a, b) => new Date(a.timestamp) - new Date(b.timestamp));\r\n\r\n    // Get IDs of messages already rendered.\r\n    const renderedIds = new Set(\r\n      Array.from(this.panel.querySelectorAll('.message')).map(el => el.getAttribute('data-message-id'))\r\n    );\r\n\r\n    // Append only messages that haven't been rendered.\r\n    this.messages.forEach(msg => {\r\n      if (!renderedIds.has(msg.id)) {\r\n        const date = new Date(msg.timestamp);\r\n        const formattedTime = date.toLocaleTimeString('en-GB', { hour12: false });\r\n        const normalizedUsername = (0,_helpers_js__WEBPACK_IMPORTED_MODULE_0__.parseUsername)(msg.from);\r\n        const usernameColor = _helpers_js__WEBPACK_IMPORTED_MODULE_0__.usernameColors.getColor(normalizedUsername);\r\n\r\n        const messageDiv = document.createElement('div');\r\n        messageDiv.className = 'message';\r\n\r\n        if (msg.isPrivate) {\r\n          messageDiv.classList.add('private-message');\r\n          messageDiv.classList.add(msg.from === this.currentUsername ? 'sent' : 'received');\r\n          if (msg.recipient) {\r\n            messageDiv.setAttribute('data-recipient', msg.recipient);\r\n          }\r\n        }\r\n\r\n        if (msg.text.startsWith('/me ')) {\r\n          messageDiv.classList.add('system');\r\n          msg.text = `${msg.from} ${msg.text.substring(msg.text.indexOf(' ') + 1)}`;\r\n        }\r\n\r\n        if (msg.isSystem) {\r\n          messageDiv.classList.add('system');\r\n        }\r\n\r\n        messageDiv.setAttribute('data-message-id', msg.id);\r\n\r\n        const messageInfoDiv = document.createElement('div');\r\n        messageInfoDiv.className = 'message-info';\r\n\r\n        let usernameDisplay = msg.from;\r\n        if (msg.isPrivate) {\r\n          usernameDisplay = msg.from === this.currentUsername && msg.recipient\r\n            ? `→ ${msg.recipient}`\r\n            : `${msg.from} →`;\r\n        }\r\n\r\n        messageInfoDiv.innerHTML = `\r\n          <span class=\"time\">${formattedTime}</span>\r\n          <span class=\"username\" style=\"color: ${usernameColor}\">${usernameDisplay}</span>\r\n        `;\r\n\r\n        const messageTextDiv = document.createElement('div');\r\n        messageTextDiv.className = 'message-text';\r\n        messageTextDiv.innerHTML = (0,_helpers_js__WEBPACK_IMPORTED_MODULE_0__.parseMessageText)(msg.text);\r\n\r\n        // Append pending icon if message is pending.\r\n        if (msg.pending) {\r\n          const pendingIcon = document.createElement('span');\r\n          pendingIcon.className = 'pending-emoji';\r\n          pendingIcon.textContent = ' ⏱️';\r\n          messageTextDiv.appendChild(pendingIcon);\r\n        }\r\n\r\n        messageDiv.appendChild(messageInfoDiv);\r\n        messageDiv.appendChild(messageTextDiv);\r\n        this.panel.appendChild(messageDiv);\r\n      }\r\n    });\r\n\r\n    this.addDelegatedClickListeners();\r\n    (0,_helpers_js__WEBPACK_IMPORTED_MODULE_0__.highlightMentionWords)([this.currentUsername]);\r\n\r\n    requestAnimationFrame(() => {\r\n      (0,_helpers_js__WEBPACK_IMPORTED_MODULE_0__.scrollToBottom)();\r\n    });\r\n  }\r\n\r\n  addDelegatedClickListeners() {\r\n    // Attach the listener only once using a custom flag.\r\n    if (!this.panel._delegatedClickAttached) {\r\n      this.panel.addEventListener(\"click\", (event) => {\r\n        // --- Username click handling --- \r\n        const usernameEl = event.target.closest('.username');\r\n        if (usernameEl && this.panel.contains(usernameEl)) {\r\n          const usernameText = usernameEl.textContent.trim();\r\n          let selectedUsername = usernameText;\r\n          // Handle arrow formatting used in private messages.\r\n          if (selectedUsername.includes('→')) {\r\n            if (selectedUsername.startsWith('→')) {\r\n              selectedUsername = selectedUsername.replace('→', '').trim();\r\n            } else {\r\n              selectedUsername = selectedUsername.split('→')[0].trim();\r\n            }\r\n          }\r\n          const messageInput = document.getElementById('message-input');\r\n          if (event.ctrlKey) {\r\n            // Ctrl+Click: Prepare for a private message.\r\n            messageInput.value = `/pm ${selectedUsername} `;\r\n            (0,_helpers_js__WEBPACK_IMPORTED_MODULE_0__.handlePrivateMessageInput)(messageInput);\r\n          } else {\r\n            // Normal click: Append username if not already present.\r\n            const appendUsername = `${selectedUsername}, `;\r\n            if (!messageInput.value.includes(appendUsername)) {\r\n              messageInput.value += appendUsername;\r\n            }\r\n          }\r\n          messageInput.focus();\r\n        }\r\n\r\n        // --- Time element click handling --- \r\n        const timeEl = event.target.closest('.time');\r\n        if (timeEl && this.panel.contains(timeEl)) {\r\n          // Extract the local time text (assumed format \"HH:MM:SS\")\r\n          const localTime = timeEl.textContent.trim();\r\n          // Calibrate the time using the provided function.\r\n          const moscowTime = (0,_helpers_js__WEBPACK_IMPORTED_MODULE_0__.calibrateToMoscowTime)(localTime);\r\n          // Create today's date in the format \"YYYY-MM-DD\"\r\n          const today = new Intl.DateTimeFormat('en-CA').format(new Date());\r\n          // Build the URL to the chat logs with the calibrated time as the hash.\r\n          const url = `https://klavogonki.ru/chatlogs/${today}.html#${moscowTime}`;\r\n\r\n          // Open in a new tab.\r\n          window.open(url, '_blank');\r\n        }\r\n      });\r\n      // Use a unified flag to ensure the listener is attached only once.\r\n      this.panel._delegatedClickAttached = true;\r\n    }\r\n  }\r\n\r\n  getChatHistory() {\r\n    return Array.from(this.chatHistory.values());\r\n  }\r\n\r\n  clearMessages() {\r\n    const systemMessageId = `system_${Date.now()}`;\r\n    const systemMessage = {\r\n      id: systemMessageId,\r\n      from: \"System\",\r\n      text: \"Chat connection lost. Reconnecting...\",\r\n      timestamp: new Date().toISOString(),\r\n      isPrivate: false,\r\n      recipient: null,\r\n      isSystem: true,\r\n      pending: false\r\n    };\r\n\r\n    this.messages.push(systemMessage);\r\n    this.chatHistory.set(systemMessageId, systemMessage);\r\n    this.processedMessageIds.add(systemMessageId);\r\n    this.updatePanel();\r\n  }\r\n}\r\n\n\n//# sourceURL=webpack://tampermonkey-script/./src/messageManager.js?");

/***/ }),

/***/ "./src/styles/emojiPanel.css":
/*!***********************************!*\
  !*** ./src/styles/emojiPanel.css ***!
  \***********************************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   \"default\": () => (__WEBPACK_DEFAULT_EXPORT__)\n/* harmony export */ });\n/* harmony import */ var _node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! !../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js */ \"./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js\");\n/* harmony import */ var _node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _node_modules_style_loader_dist_runtime_styleDomAPI_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! !../../node_modules/style-loader/dist/runtime/styleDomAPI.js */ \"./node_modules/style-loader/dist/runtime/styleDomAPI.js\");\n/* harmony import */ var _node_modules_style_loader_dist_runtime_styleDomAPI_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_styleDomAPI_js__WEBPACK_IMPORTED_MODULE_1__);\n/* harmony import */ var _node_modules_style_loader_dist_runtime_insertBySelector_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! !../../node_modules/style-loader/dist/runtime/insertBySelector.js */ \"./node_modules/style-loader/dist/runtime/insertBySelector.js\");\n/* harmony import */ var _node_modules_style_loader_dist_runtime_insertBySelector_js__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_insertBySelector_js__WEBPACK_IMPORTED_MODULE_2__);\n/* harmony import */ var _node_modules_style_loader_dist_runtime_setAttributesWithoutAttributes_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! !../../node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js */ \"./node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js\");\n/* harmony import */ var _node_modules_style_loader_dist_runtime_setAttributesWithoutAttributes_js__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_setAttributesWithoutAttributes_js__WEBPACK_IMPORTED_MODULE_3__);\n/* harmony import */ var _node_modules_style_loader_dist_runtime_insertStyleElement_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! !../../node_modules/style-loader/dist/runtime/insertStyleElement.js */ \"./node_modules/style-loader/dist/runtime/insertStyleElement.js\");\n/* harmony import */ var _node_modules_style_loader_dist_runtime_insertStyleElement_js__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_insertStyleElement_js__WEBPACK_IMPORTED_MODULE_4__);\n/* harmony import */ var _node_modules_style_loader_dist_runtime_styleTagTransform_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! !../../node_modules/style-loader/dist/runtime/styleTagTransform.js */ \"./node_modules/style-loader/dist/runtime/styleTagTransform.js\");\n/* harmony import */ var _node_modules_style_loader_dist_runtime_styleTagTransform_js__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_styleTagTransform_js__WEBPACK_IMPORTED_MODULE_5__);\n/* harmony import */ var _node_modules_css_loader_dist_cjs_js_emojiPanel_css__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! !!../../node_modules/css-loader/dist/cjs.js!./emojiPanel.css */ \"./node_modules/css-loader/dist/cjs.js!./src/styles/emojiPanel.css\");\n\n      \n      \n      \n      \n      \n      \n      \n      \n      \n\nvar options = {};\n\noptions.styleTagTransform = (_node_modules_style_loader_dist_runtime_styleTagTransform_js__WEBPACK_IMPORTED_MODULE_5___default());\noptions.setAttributes = (_node_modules_style_loader_dist_runtime_setAttributesWithoutAttributes_js__WEBPACK_IMPORTED_MODULE_3___default());\n\n      options.insert = _node_modules_style_loader_dist_runtime_insertBySelector_js__WEBPACK_IMPORTED_MODULE_2___default().bind(null, \"head\");\n    \noptions.domAPI = (_node_modules_style_loader_dist_runtime_styleDomAPI_js__WEBPACK_IMPORTED_MODULE_1___default());\noptions.insertStyleElement = (_node_modules_style_loader_dist_runtime_insertStyleElement_js__WEBPACK_IMPORTED_MODULE_4___default());\n\nvar update = _node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0___default()(_node_modules_css_loader_dist_cjs_js_emojiPanel_css__WEBPACK_IMPORTED_MODULE_6__[\"default\"], options);\n\n\n\n\n       /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (_node_modules_css_loader_dist_cjs_js_emojiPanel_css__WEBPACK_IMPORTED_MODULE_6__[\"default\"] && _node_modules_css_loader_dist_cjs_js_emojiPanel_css__WEBPACK_IMPORTED_MODULE_6__[\"default\"].locals ? _node_modules_css_loader_dist_cjs_js_emojiPanel_css__WEBPACK_IMPORTED_MODULE_6__[\"default\"].locals : undefined);\n\n\n//# sourceURL=webpack://tampermonkey-script/./src/styles/emojiPanel.css?");

/***/ }),

/***/ "./src/styles/helpPanel.css":
/*!**********************************!*\
  !*** ./src/styles/helpPanel.css ***!
  \**********************************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   \"default\": () => (__WEBPACK_DEFAULT_EXPORT__)\n/* harmony export */ });\n/* harmony import */ var _node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! !../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js */ \"./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js\");\n/* harmony import */ var _node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _node_modules_style_loader_dist_runtime_styleDomAPI_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! !../../node_modules/style-loader/dist/runtime/styleDomAPI.js */ \"./node_modules/style-loader/dist/runtime/styleDomAPI.js\");\n/* harmony import */ var _node_modules_style_loader_dist_runtime_styleDomAPI_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_styleDomAPI_js__WEBPACK_IMPORTED_MODULE_1__);\n/* harmony import */ var _node_modules_style_loader_dist_runtime_insertBySelector_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! !../../node_modules/style-loader/dist/runtime/insertBySelector.js */ \"./node_modules/style-loader/dist/runtime/insertBySelector.js\");\n/* harmony import */ var _node_modules_style_loader_dist_runtime_insertBySelector_js__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_insertBySelector_js__WEBPACK_IMPORTED_MODULE_2__);\n/* harmony import */ var _node_modules_style_loader_dist_runtime_setAttributesWithoutAttributes_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! !../../node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js */ \"./node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js\");\n/* harmony import */ var _node_modules_style_loader_dist_runtime_setAttributesWithoutAttributes_js__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_setAttributesWithoutAttributes_js__WEBPACK_IMPORTED_MODULE_3__);\n/* harmony import */ var _node_modules_style_loader_dist_runtime_insertStyleElement_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! !../../node_modules/style-loader/dist/runtime/insertStyleElement.js */ \"./node_modules/style-loader/dist/runtime/insertStyleElement.js\");\n/* harmony import */ var _node_modules_style_loader_dist_runtime_insertStyleElement_js__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_insertStyleElement_js__WEBPACK_IMPORTED_MODULE_4__);\n/* harmony import */ var _node_modules_style_loader_dist_runtime_styleTagTransform_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! !../../node_modules/style-loader/dist/runtime/styleTagTransform.js */ \"./node_modules/style-loader/dist/runtime/styleTagTransform.js\");\n/* harmony import */ var _node_modules_style_loader_dist_runtime_styleTagTransform_js__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_styleTagTransform_js__WEBPACK_IMPORTED_MODULE_5__);\n/* harmony import */ var _node_modules_css_loader_dist_cjs_js_helpPanel_css__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! !!../../node_modules/css-loader/dist/cjs.js!./helpPanel.css */ \"./node_modules/css-loader/dist/cjs.js!./src/styles/helpPanel.css\");\n\n      \n      \n      \n      \n      \n      \n      \n      \n      \n\nvar options = {};\n\noptions.styleTagTransform = (_node_modules_style_loader_dist_runtime_styleTagTransform_js__WEBPACK_IMPORTED_MODULE_5___default());\noptions.setAttributes = (_node_modules_style_loader_dist_runtime_setAttributesWithoutAttributes_js__WEBPACK_IMPORTED_MODULE_3___default());\n\n      options.insert = _node_modules_style_loader_dist_runtime_insertBySelector_js__WEBPACK_IMPORTED_MODULE_2___default().bind(null, \"head\");\n    \noptions.domAPI = (_node_modules_style_loader_dist_runtime_styleDomAPI_js__WEBPACK_IMPORTED_MODULE_1___default());\noptions.insertStyleElement = (_node_modules_style_loader_dist_runtime_insertStyleElement_js__WEBPACK_IMPORTED_MODULE_4___default());\n\nvar update = _node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0___default()(_node_modules_css_loader_dist_cjs_js_helpPanel_css__WEBPACK_IMPORTED_MODULE_6__[\"default\"], options);\n\n\n\n\n       /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (_node_modules_css_loader_dist_cjs_js_helpPanel_css__WEBPACK_IMPORTED_MODULE_6__[\"default\"] && _node_modules_css_loader_dist_cjs_js_helpPanel_css__WEBPACK_IMPORTED_MODULE_6__[\"default\"].locals ? _node_modules_css_loader_dist_cjs_js_helpPanel_css__WEBPACK_IMPORTED_MODULE_6__[\"default\"].locals : undefined);\n\n\n//# sourceURL=webpack://tampermonkey-script/./src/styles/helpPanel.css?");

/***/ }),

/***/ "./src/styles/style.css":
/*!******************************!*\
  !*** ./src/styles/style.css ***!
  \******************************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   \"default\": () => (__WEBPACK_DEFAULT_EXPORT__)\n/* harmony export */ });\n/* harmony import */ var _node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! !../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js */ \"./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js\");\n/* harmony import */ var _node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _node_modules_style_loader_dist_runtime_styleDomAPI_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! !../../node_modules/style-loader/dist/runtime/styleDomAPI.js */ \"./node_modules/style-loader/dist/runtime/styleDomAPI.js\");\n/* harmony import */ var _node_modules_style_loader_dist_runtime_styleDomAPI_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_styleDomAPI_js__WEBPACK_IMPORTED_MODULE_1__);\n/* harmony import */ var _node_modules_style_loader_dist_runtime_insertBySelector_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! !../../node_modules/style-loader/dist/runtime/insertBySelector.js */ \"./node_modules/style-loader/dist/runtime/insertBySelector.js\");\n/* harmony import */ var _node_modules_style_loader_dist_runtime_insertBySelector_js__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_insertBySelector_js__WEBPACK_IMPORTED_MODULE_2__);\n/* harmony import */ var _node_modules_style_loader_dist_runtime_setAttributesWithoutAttributes_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! !../../node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js */ \"./node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js\");\n/* harmony import */ var _node_modules_style_loader_dist_runtime_setAttributesWithoutAttributes_js__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_setAttributesWithoutAttributes_js__WEBPACK_IMPORTED_MODULE_3__);\n/* harmony import */ var _node_modules_style_loader_dist_runtime_insertStyleElement_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! !../../node_modules/style-loader/dist/runtime/insertStyleElement.js */ \"./node_modules/style-loader/dist/runtime/insertStyleElement.js\");\n/* harmony import */ var _node_modules_style_loader_dist_runtime_insertStyleElement_js__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_insertStyleElement_js__WEBPACK_IMPORTED_MODULE_4__);\n/* harmony import */ var _node_modules_style_loader_dist_runtime_styleTagTransform_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! !../../node_modules/style-loader/dist/runtime/styleTagTransform.js */ \"./node_modules/style-loader/dist/runtime/styleTagTransform.js\");\n/* harmony import */ var _node_modules_style_loader_dist_runtime_styleTagTransform_js__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_styleTagTransform_js__WEBPACK_IMPORTED_MODULE_5__);\n/* harmony import */ var _node_modules_css_loader_dist_cjs_js_style_css__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! !!../../node_modules/css-loader/dist/cjs.js!./style.css */ \"./node_modules/css-loader/dist/cjs.js!./src/styles/style.css\");\n\n      \n      \n      \n      \n      \n      \n      \n      \n      \n\nvar options = {};\n\noptions.styleTagTransform = (_node_modules_style_loader_dist_runtime_styleTagTransform_js__WEBPACK_IMPORTED_MODULE_5___default());\noptions.setAttributes = (_node_modules_style_loader_dist_runtime_setAttributesWithoutAttributes_js__WEBPACK_IMPORTED_MODULE_3___default());\n\n      options.insert = _node_modules_style_loader_dist_runtime_insertBySelector_js__WEBPACK_IMPORTED_MODULE_2___default().bind(null, \"head\");\n    \noptions.domAPI = (_node_modules_style_loader_dist_runtime_styleDomAPI_js__WEBPACK_IMPORTED_MODULE_1___default());\noptions.insertStyleElement = (_node_modules_style_loader_dist_runtime_insertStyleElement_js__WEBPACK_IMPORTED_MODULE_4___default());\n\nvar update = _node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0___default()(_node_modules_css_loader_dist_cjs_js_style_css__WEBPACK_IMPORTED_MODULE_6__[\"default\"], options);\n\n\n\n\n       /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (_node_modules_css_loader_dist_cjs_js_style_css__WEBPACK_IMPORTED_MODULE_6__[\"default\"] && _node_modules_css_loader_dist_cjs_js_style_css__WEBPACK_IMPORTED_MODULE_6__[\"default\"].locals ? _node_modules_css_loader_dist_cjs_js_style_css__WEBPACK_IMPORTED_MODULE_6__[\"default\"].locals : undefined);\n\n\n//# sourceURL=webpack://tampermonkey-script/./src/styles/style.css?");

/***/ }),

/***/ "./src/userManager.js":
/*!****************************!*\
  !*** ./src/userManager.js ***!
  \****************************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   \"default\": () => (/* binding */ UserManager)\n/* harmony export */ });\n/* harmony import */ var _definitions__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./definitions */ \"./src/definitions.js\");\n/* harmony import */ var _helpers__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./helpers */ \"./src/helpers.js\");\n/* harmony import */ var _animations__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./animations */ \"./src/animations.js\");\n\r\n\r\n\r\n\r\nclass UserManager {\r\n  constructor(containerId = 'user-list') {\r\n    this.container = document.getElementById(containerId);\r\n    this.activeUsers = new Map();\r\n    this.isFirstLoad = true;\r\n    this.avatarCache = this.loadAvatarCache();\r\n    this.cacheDate = new Date().toDateString();\r\n\r\n    // Role-based icons\r\n    this.roleIcons = {\r\n      'visitor': '🐥',\r\n      'participant': '🗿',\r\n      'moderator': '⚔️️'\r\n    };\r\n\r\n    // Role priority for sorting\r\n    this.rolePriority = {\r\n      'moderator': 1,\r\n      'participant': 2,\r\n      'visitor': 3\r\n    };\r\n\r\n    // Attach event listeners\r\n    this.setupEventListeners();\r\n  }\r\n\r\n  loadAvatarCache() {\r\n    try {\r\n      const cacheData = localStorage.getItem('userAvatarCache');\r\n      if (cacheData) {\r\n        const cache = JSON.parse(cacheData);\r\n        if (cache.date === new Date().toDateString()) {\r\n          console.log(\"🗃️ Loaded avatar cache from localStorage\");\r\n          return cache.avatars || {};\r\n        } else {\r\n          console.log(\"🗃️ Avatar cache expired (new day), creating fresh cache\");\r\n          return {};\r\n        }\r\n      }\r\n    } catch (error) {\r\n      console.error(\"Error loading avatar cache:\", error);\r\n    }\r\n    return {};\r\n  }\r\n\r\n  saveAvatarCache() {\r\n    try {\r\n      localStorage.setItem('userAvatarCache', JSON.stringify({\r\n        date: this.cacheDate,\r\n        avatars: this.avatarCache\r\n      }));\r\n    } catch (error) {\r\n      console.error(\"Error saving avatar cache:\", error);\r\n    }\r\n  }\r\n\r\n  setupEventListeners() {\r\n    this.container.addEventListener('click', (event) => {\r\n      // Handle username clicks\r\n      if (event.target.classList.contains('username-clickable')) {\r\n        const userId = event.target.getAttribute('data-user-id');\r\n        if (userId) {\r\n          if (event.ctrlKey) {\r\n            // Ctrl+Click: Start private chat with user\r\n            const username = event.target.textContent.trim();\r\n            const messageInput = document.getElementById('message-input');\r\n            messageInput.value = `/pm ${username} `;\r\n            (0,_helpers__WEBPACK_IMPORTED_MODULE_1__.handlePrivateMessageInput)(messageInput);\r\n            messageInput.focus();\r\n          } else {\r\n            // Normal click: Navigate to profile\r\n            const userIdWithoutDomain = userId.split('/')[1].split('#')[0];\r\n            window.location.href = `https://klavogonki.ru/u/#/${userIdWithoutDomain}/`;\r\n          }\r\n        }\r\n      }\r\n\r\n      // Handle game indicator clicks\r\n      if (event.target.closest('.game-indicator')) {\r\n        const gameIndicator = event.target.closest('.game-indicator');\r\n        const gameId = gameIndicator.getAttribute('data-game-id');\r\n        if (gameId) {\r\n          event.stopPropagation();\r\n          window.location.href = `https://klavogonki.ru/g/?gmid=${gameId}`;\r\n        }\r\n      }\r\n    });\r\n  }\r\n\r\n  async updatePresence(xmlResponse) {\r\n    const parser = new DOMParser();\r\n    const doc = parser.parseFromString(xmlResponse, \"text/xml\");\r\n    const presences = doc.getElementsByTagName(\"presence\");\r\n\r\n    if (xmlResponse.includes('<presence id=\"pres_1\"')) {\r\n      console.log(\"🔄 Initial room join detected, requesting full roster\");\r\n      this.requestFullRoster();\r\n      return;\r\n    }\r\n\r\n    let changes = false;\r\n    const newUserJIDs = [];\r\n    const updatedUserJIDs = [];\r\n\r\n    for (let i = 0; i < presences.length; i++) {\r\n      const presence = presences[i];\r\n      const from = presence.getAttribute('from');\r\n      const type = presence.getAttribute('type');\r\n\r\n      // Skip if not from the conference room\r\n      if (!from || !from.includes('[email protected]/')) {\r\n        continue;\r\n      }\r\n\r\n      // Extract username from JID\r\n      const usernameFromJid = from.split('/').pop();\r\n      if (!usernameFromJid) continue;\r\n\r\n      // Skip Клавобот (unified check)\r\n      if (usernameFromJid.toLowerCase() === 'клавобот' || from.toLowerCase().includes('#клавобот')) {\r\n        continue;\r\n      }\r\n\r\n      // Handle user leaving\r\n      if (type === 'unavailable') {\r\n        if (this.activeUsers.has(from)) {\r\n          const departingUser = this.activeUsers.get(from);\r\n          const userId = (0,_helpers__WEBPACK_IMPORTED_MODULE_1__.extractUserId)(from);\r\n          const cleanLogin = (0,_helpers__WEBPACK_IMPORTED_MODULE_1__.extractCleanUsername)(departingUser.login);\r\n\r\n          if (!this.isFirstLoad) {\r\n            // console.log(`🚪 User left: ${cleanLogin} ID: (${userId})`);\r\n          }\r\n          this.activeUsers.delete(from);\r\n          changes = true;\r\n        }\r\n        continue;\r\n      }\r\n\r\n      const existingUser = this.activeUsers.get(from) || {};\r\n      const userId = (0,_helpers__WEBPACK_IMPORTED_MODULE_1__.extractUserId)(from);\r\n      const cachedAvatarInfo = this.avatarCache[userId];\r\n\r\n      // Initialize userData\r\n      let userData = {\r\n        jid: from,\r\n        login: usernameFromJid,\r\n        color: '#777',\r\n        // Normalize the username before calling getColor:\r\n        usernameColor: _helpers__WEBPACK_IMPORTED_MODULE_1__.usernameColors.getColor((0,_helpers__WEBPACK_IMPORTED_MODULE_1__.extractCleanUsername)(usernameFromJid)),\r\n        role: 'participant',\r\n        gameId: null,\r\n        avatar: null\r\n      };\r\n\r\n      // Process x elements\r\n      const xElements = presence.getElementsByTagName(\"x\");\r\n      let foundAvatar = false;\r\n\r\n      for (let j = 0; j < xElements.length; j++) {\r\n        const xmlns = xElements[j].getAttribute(\"xmlns\");\r\n        if (xmlns === \"klavogonki:userdata\") {\r\n          const userNode = xElements[j].getElementsByTagName(\"user\")[0];\r\n          if (userNode) {\r\n            const loginElement = userNode.getElementsByTagName(\"login\")[0];\r\n            // Later, when the login element is updated:\r\n            if (loginElement && loginElement.textContent) {\r\n              userData.login = loginElement.textContent;\r\n              // Again, normalize the username for color generation:\r\n              userData.usernameColor = _helpers__WEBPACK_IMPORTED_MODULE_1__.usernameColors.getColor((0,_helpers__WEBPACK_IMPORTED_MODULE_1__.extractCleanUsername)(userData.login));\r\n            }\r\n\r\n            const avatarElement = userNode.getElementsByTagName(\"avatar\")[0];\r\n            if (avatarElement && avatarElement.textContent) {\r\n              userData.avatar = avatarElement.textContent;\r\n              foundAvatar = true;\r\n            }\r\n\r\n            const moderatorNode = userNode.getElementsByTagName(\"moderator\")[0];\r\n            if (moderatorNode && moderatorNode.textContent === '1') {\r\n              userData.role = 'moderator';\r\n            }\r\n          }\r\n\r\n          const gameIdElement = xElements[j].getElementsByTagName(\"game_id\")[0];\r\n          if (gameIdElement && gameIdElement.textContent) {\r\n            userData.gameId = gameIdElement.textContent;\r\n          }\r\n        }\r\n\r\n        if (xmlns === \"http://jabber.org/protocol/muc#user\") {\r\n          const itemNode = xElements[j].getElementsByTagName(\"item\")[0];\r\n          if (itemNode) {\r\n            const role = itemNode.getAttribute(\"role\");\r\n            if (role && userData.role !== 'moderator') {\r\n              userData.role = role;\r\n            }\r\n          }\r\n        }\r\n      }\r\n\r\n      // If no avatar in update but exists in user data, keep it\r\n      if (!foundAvatar && existingUser && existingUser.avatar) {\r\n        userData.avatar = existingUser.avatar;\r\n      }\r\n\r\n      // Handle avatar (use cache or set default)\r\n      if (!userData.avatar && cachedAvatarInfo) {\r\n        if (cachedAvatarInfo.hasAvatar) {\r\n          userData.avatar = cachedAvatarInfo.avatarUrl;\r\n        }\r\n      }\r\n\r\n      const cleanLogin = (0,_helpers__WEBPACK_IMPORTED_MODULE_1__.extractCleanUsername)(userData.login);\r\n\r\n      // Determine if user is new or updated\r\n      if (!this.activeUsers.has(from)) {\r\n        if (!this.isFirstLoad) {\r\n          // console.log(`👤 User joined: ${cleanLogin} ID: (${userId})`);\r\n        }\r\n        this.activeUsers.set(from, userData);\r\n        changes = true;\r\n        newUserJIDs.push(from);\r\n      } else if (JSON.stringify(existingUser) !== JSON.stringify(userData)) {\r\n        this.activeUsers.set(from, userData);\r\n        changes = true;\r\n        updatedUserJIDs.push(from);\r\n      }\r\n    }\r\n\r\n    if (changes) {\r\n      this.updateUI(newUserJIDs, updatedUserJIDs);\r\n    }\r\n  }\r\n\r\n  updateUI(newUserJIDs = [], updatedUserJIDs = []) {\r\n    // Build map of existing DOM elements\r\n    const existingElements = new Map();\r\n    this.container.querySelectorAll('.user-item').forEach(el => {\r\n      existingElements.set(el.getAttribute('data-jid'), el);\r\n    });\r\n\r\n    // Sort users by role and username\r\n    const sortedUsers = Array.from(this.activeUsers.values()).sort((a, b) => {\r\n      const priorityDiff = this.rolePriority[a.role] - this.rolePriority[b.role];\r\n      return priorityDiff !== 0 ? priorityDiff :\r\n        (0,_helpers__WEBPACK_IMPORTED_MODULE_1__.extractCleanUsername)(a.login).localeCompare((0,_helpers__WEBPACK_IMPORTED_MODULE_1__.extractCleanUsername)(b.login));\r\n    });\r\n\r\n    // Build the updated list\r\n    const fragment = document.createDocumentFragment();\r\n    sortedUsers.forEach(user => {\r\n      let userElement = existingElements.get(user.jid);\r\n      const userId = (0,_helpers__WEBPACK_IMPORTED_MODULE_1__.extractUserId)(user.jid);\r\n      const cleanLogin = (0,_helpers__WEBPACK_IMPORTED_MODULE_1__.extractCleanUsername)(user.login);\r\n\r\n      // If element doesn't exist, create it\r\n      if (!userElement) {\r\n        userElement = document.createElement('div');\r\n        userElement.classList.add('user-item');\r\n        userElement.setAttribute('data-jid', user.jid);\r\n        const roleIcon = this.roleIcons[user.role] || '👤';\r\n\r\n        const avatarContainer = document.createElement('span');\r\n        avatarContainer.className = 'avatar-container';\r\n        this.setUserAvatar(avatarContainer, user, userId, cleanLogin);\r\n\r\n        // Create user info container\r\n        const userInfo = document.createElement('div');\r\n        userInfo.className = 'user-info';\r\n        userInfo.innerHTML = `\r\n          <div class=\"username\" style=\"color: ${user.usernameColor}\">\r\n            <span class=\"username-clickable\" data-user-id=\"${user.jid}\">${cleanLogin}</span>\r\n            <span class=\"role ${user.role}\">${roleIcon}</span>\r\n          </div>\r\n        `;\r\n\r\n        // Append avatar and user info\r\n        userElement.appendChild(avatarContainer);\r\n        userElement.appendChild(userInfo);\r\n      } else {\r\n        // Update existing element if needed\r\n        if (!userElement.querySelector('.avatar-container')) {\r\n          const avatarContainer = document.createElement('span');\r\n          avatarContainer.className = 'avatar-container';\r\n          this.setUserAvatar(avatarContainer, user, userId, cleanLogin);\r\n          userElement.insertBefore(avatarContainer, userElement.firstChild);\r\n        }\r\n\r\n        // Remove from map so remaining elements are those to be removed\r\n        existingElements.delete(user.jid);\r\n\r\n        // Update role icon if changed\r\n        const roleElement = userElement.querySelector('.role');\r\n        const newRoleIcon = this.roleIcons[user.role] || '👤';\r\n        if (roleElement && roleElement.textContent !== newRoleIcon) {\r\n          roleElement.textContent = newRoleIcon;\r\n          if (!roleElement.classList.contains(user.role)) {\r\n            roleElement.className = `role ${user.role}`;\r\n          }\r\n        }\r\n\r\n        // Update username color if needed\r\n        const usernameElement = userElement.querySelector('.username');\r\n        if (usernameElement && usernameElement.style.color !== user.usernameColor) {\r\n          usernameElement.style.color = user.usernameColor;\r\n        }\r\n      }\r\n\r\n      // Handle game indicator\r\n      this.updateGameIndicator(userElement, user);\r\n\r\n      // Append to fragment\r\n      fragment.appendChild(userElement);\r\n    });\r\n\r\n    // Clear container and append fragment\r\n    this.container.innerHTML = '';\r\n    this.container.appendChild(fragment);\r\n\r\n    // Remove elements for users no longer active\r\n    existingElements.forEach((el) => {\r\n      if (el && el.parentNode) {\r\n        el.remove();\r\n      }\r\n    });\r\n\r\n    // Apply shake effect for new users\r\n    if (!this.isFirstLoad) {\r\n      newUserJIDs.forEach(jid => {\r\n        const userElement = this.container.querySelector(`.user-item[data-jid=\"${jid}\"]`);\r\n        if (userElement && userElement.parentNode) {\r\n          (0,_animations__WEBPACK_IMPORTED_MODULE_2__.addShakeEffect)(userElement);\r\n        }\r\n      });\r\n    }\r\n\r\n    if (this.isFirstLoad) {\r\n      this.isFirstLoad = false;\r\n    }\r\n  }\r\n\r\n  setUserAvatar(avatarContainer, user, userId, cleanLogin) {\r\n    const cachedAvatarInfo = this.avatarCache[userId];\r\n\r\n    // Display avatar based on available information\r\n    if (user.avatar) {\r\n      const avatarUrl = `${_definitions__WEBPACK_IMPORTED_MODULE_0__.BASE_URL}/storage/avatars/${userId}_big.png`;\r\n      const avatarImg = document.createElement('img');\r\n      avatarImg.className = 'user-avatar image-avatar';\r\n      avatarImg.src = avatarUrl;\r\n      avatarImg.alt = `${cleanLogin}'s avatar`;\r\n\r\n      // Handle error by replacing with emoji\r\n      avatarImg.addEventListener('error', () => {\r\n        const fallbackEmoji = cachedAvatarInfo?.emoji || (0,_helpers__WEBPACK_IMPORTED_MODULE_1__.getRandomEmojiAvatar)();\r\n        avatarContainer.innerHTML = '';\r\n        const fallbackSpan = document.createElement('span');\r\n        fallbackSpan.className = 'user-avatar svg-avatar';\r\n        fallbackSpan.textContent = fallbackEmoji;\r\n        avatarContainer.appendChild(fallbackSpan);\r\n\r\n        // Update cache\r\n        this.avatarCache[userId] = {\r\n          hasAvatar: false,\r\n          emoji: fallbackEmoji\r\n        };\r\n        this.saveAvatarCache();\r\n      });\r\n\r\n      // On successful load, update cache\r\n      avatarImg.addEventListener('load', () => {\r\n        // Only log first time we detect an avatar\r\n        if (!cachedAvatarInfo || !cachedAvatarInfo.hasAvatar) {\r\n          // console.log(`🖼️ Using image avatar for User: ${cleanLogin} ID: (${userId})`);\r\n        }\r\n        this.avatarCache[userId] = {\r\n          hasAvatar: true,\r\n          avatarUrl: avatarUrl\r\n        };\r\n        this.saveAvatarCache();\r\n      });\r\n\r\n      avatarContainer.appendChild(avatarImg);\r\n\r\n    } else if (cachedAvatarInfo) {\r\n      // Use cached information\r\n      if (cachedAvatarInfo.hasAvatar) {\r\n        const avatarImg = document.createElement('img');\r\n        avatarImg.className = 'user-avatar image-avatar';\r\n        avatarImg.src = cachedAvatarInfo.avatarUrl;\r\n        avatarImg.alt = `${cleanLogin}'s avatar`;\r\n\r\n        // Handle error if cache is incorrect\r\n        avatarImg.addEventListener('error', () => {\r\n          const fallbackEmoji = (0,_helpers__WEBPACK_IMPORTED_MODULE_1__.getRandomEmojiAvatar)();\r\n          avatarContainer.innerHTML = '';\r\n          const fallbackSpan = document.createElement('span');\r\n          fallbackSpan.className = 'user-avatar svg-avatar';\r\n          fallbackSpan.textContent = fallbackEmoji;\r\n          avatarContainer.appendChild(fallbackSpan);\r\n\r\n          // Update cache\r\n          this.avatarCache[userId] = {\r\n            hasAvatar: false,\r\n            emoji: fallbackEmoji\r\n          };\r\n          this.saveAvatarCache();\r\n        });\r\n\r\n        avatarContainer.appendChild(avatarImg);\r\n      } else {\r\n        // Use cached emoji\r\n        const fallbackSpan = document.createElement('span');\r\n        fallbackSpan.className = 'user-avatar svg-avatar';\r\n        fallbackSpan.textContent = cachedAvatarInfo.emoji;\r\n        avatarContainer.appendChild(fallbackSpan);\r\n\r\n        // Only log first time we use an emoji avatar\r\n        if (!this.avatarCache[userId] || !this.avatarCache[userId].hasEmoji) {\r\n          // console.log(`😊 Using emoji avatar for User: ${cleanLogin} ID: (${userId}): ${cachedAvatarInfo.emoji}`);\r\n          // Mark that we've logged this emoji usage\r\n          this.avatarCache[userId].hasEmoji = true;\r\n          this.saveAvatarCache();\r\n        }\r\n      }\r\n    } else {\r\n      // No cached info - try to fetch avatar\r\n      const avatarUrl = `${_definitions__WEBPACK_IMPORTED_MODULE_0__.BASE_URL}/storage/avatars/${userId}_big.png`;\r\n      const avatarImg = document.createElement('img');\r\n      avatarImg.className = 'user-avatar image-avatar';\r\n      avatarImg.src = avatarUrl;\r\n      avatarImg.alt = `${cleanLogin}'s avatar`;\r\n\r\n      // Handle error\r\n      avatarImg.addEventListener('error', () => {\r\n        const fallbackEmoji = (0,_helpers__WEBPACK_IMPORTED_MODULE_1__.getRandomEmojiAvatar)();\r\n        avatarContainer.innerHTML = '';\r\n        const fallbackSpan = document.createElement('span');\r\n        fallbackSpan.className = 'user-avatar svg-avatar';\r\n        fallbackSpan.textContent = fallbackEmoji;\r\n        avatarContainer.appendChild(fallbackSpan);\r\n\r\n        // Log first time using emoji\r\n        console.log(`😊 Using emoji avatar for User: ${cleanLogin} ID: (${userId}): ${fallbackEmoji}`);\r\n\r\n        // Cache negative result\r\n        this.avatarCache[userId] = {\r\n          hasAvatar: false,\r\n          emoji: fallbackEmoji,\r\n          hasEmoji: true\r\n        };\r\n        this.saveAvatarCache();\r\n      });\r\n\r\n      // On successful load, cache positive result\r\n      avatarImg.addEventListener('load', () => {\r\n        console.log(`🖼️ Using image avatar for User: ${cleanLogin} ID: (${userId})`);\r\n        this.avatarCache[userId] = {\r\n          hasAvatar: true,\r\n          avatarUrl: avatarUrl\r\n        };\r\n        this.saveAvatarCache();\r\n      });\r\n\r\n      avatarContainer.appendChild(avatarImg);\r\n    }\r\n  }\r\n\r\n  updateGameIndicator(userElement, user) {\r\n    let gameIndicatorElement = userElement.querySelector('.game-indicator');\r\n\r\n    if (user.gameId) {\r\n      if (!gameIndicatorElement || gameIndicatorElement.getAttribute('data-game-id') !== user.gameId) {\r\n        const newIndicatorHTML = `<span class=\"game-indicator\" title=\"${user.gameId}\" data-game-id=\"${user.gameId}\">\r\n                                    <span class=\"traffic-icon\">🚦</span>\r\n                                  </span>`;\r\n        if (gameIndicatorElement) {\r\n          gameIndicatorElement.outerHTML = newIndicatorHTML;\r\n        } else {\r\n          const usernameContainer = userElement.querySelector('.username');\r\n          usernameContainer.insertAdjacentHTML('beforeend', newIndicatorHTML);\r\n        }\r\n      }\r\n    } else if (gameIndicatorElement && gameIndicatorElement.parentNode) {\r\n      gameIndicatorElement.remove();\r\n    }\r\n  }\r\n\r\n  async requestFullRoster() {\r\n    console.log(\"📑 Would request full roster here (using existing data for now)\");\r\n    this.updateUI();\r\n  }\r\n}\n\n//# sourceURL=webpack://tampermonkey-script/./src/userManager.js?");

/***/ }),

/***/ "./src/xmppClient.js":
/*!***************************!*\
  !*** ./src/xmppClient.js ***!
  \***************************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   createXMPPClient: () => (/* binding */ createXMPPClient)\n/* harmony export */ });\n/* harmony import */ var _definitions_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./definitions.js */ \"./src/definitions.js\");\n/* harmony import */ var _helpers_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./helpers.js */ \"./src/helpers.js\");\n\r\n\r\n\r\nfunction createXMPPClient(xmppConnection, userManager, messageManager, username) {\r\n  // Compact wrapper functions.\r\n  const safeUpdatePresence = (xmlResponse) =>\r\n    xmlResponse && userManager.updatePresence(xmlResponse);\r\n\r\n  const safeProcessMessages = (xmlResponse) =>\r\n    xmlResponse && messageManager.processMessages(xmlResponse);\r\n\r\n  const xmppClient = {\r\n    userManager,\r\n    messageManager,\r\n    presenceInterval: null,\r\n    isReconnecting: false,\r\n    isConnected: false,\r\n    // Queue of messages waiting to be sent.\r\n    messageQueue: [],\r\n\r\n    // Helper: Create the XML stanza for a message.\r\n    _createMessageStanza(text, messageId, isPrivate, fullJid) {\r\n      const userDataBlock = `\r\n    <x xmlns='klavogonki:userdata'>\r\n      <user>\r\n        <login>${username.replace(/^\\d+#/, '')}</login>\r\n        <avatar>/storage/avatars/${username.split('#')[0]}.png</avatar>\r\n        <background>#123456</background>\r\n      </user>\r\n    </x>`;\r\n\r\n      if (isPrivate && fullJid) {\r\n        return `\r\n      <body rid='${xmppConnection.nextRid()}' sid='${xmppConnection.sid}' xmlns='http://jabber.org/protocol/httpbind'>\r\n        <message from='${username}@jabber.klavogonki.ru/web'\r\n                 to='${fullJid}'\r\n                 type='chat'\r\n                 id='${messageId}'\r\n                 xmlns='jabber:client'>\r\n          <body>${text}</body>\r\n          ${userDataBlock}\r\n        </message>\r\n      </body>`;\r\n      } else {\r\n        return `\r\n      <body rid='${xmppConnection.nextRid()}' sid='${xmppConnection.sid}' xmlns='http://jabber.org/protocol/httpbind'>\r\n        <message to='[email protected]'\r\n                 type='groupchat'\r\n                 id='${messageId}'\r\n                 xmlns='jabber:client'>\r\n          <body>${text}</body>\r\n          ${userDataBlock}\r\n        </message>\r\n      </body>`;\r\n      }\r\n    },\r\n\r\n    // Process queued messages sequentially.\r\n    async processQueue() {\r\n      if (!this.isConnected || this.isReconnecting) return;\r\n\r\n      while (this.messageQueue.length > 0) {\r\n        const msg = this.messageQueue[0]; // Peek the first message.\r\n        const messageStanza = this._createMessageStanza(msg.text, msg.id, msg.isPrivate, msg.fullJid);\r\n        try {\r\n          await xmppConnection.sendRequestWithRetry(messageStanza);\r\n          // On success, remove the message from the queue.\r\n          this.messageQueue.shift();\r\n          // Optionally, update the UI to remove the pending flag.\r\n          messageManager.updatePendingStatus(msg.id, false);\r\n          safeProcessMessages(null);\r\n        } catch (error) {\r\n          console.error(`Failed to send queued message (${msg.id}): ${error.message}`);\r\n          // Stop processing; the message remains in the queue for later retry.\r\n          break;\r\n        }\r\n      }\r\n    },\r\n\r\n    async connect() {\r\n      try {\r\n        if (this.presenceInterval) {\r\n          clearInterval(this.presenceInterval);\r\n          this.presenceInterval = null;\r\n        }\r\n        let retries = 5;\r\n        while (retries > 0 && !this.isConnected) {\r\n          try {\r\n            const session = await xmppConnection.connect();\r\n            console.log('💬 Step 8: Joining chat room...');\r\n            const joinPayload = `<body rid='${xmppConnection.nextRid()}' sid='${session.sid}'\r\n                     xmlns='http://jabber.org/protocol/httpbind'>\r\n                <presence id='pres_1' xmlns='jabber:client' to='[email protected]/${username}'>\r\n                  <x xmlns='http://jabber.org/protocol/muc'/>\r\n                </presence>\r\n              </body>`;\r\n            const joinResponse = await xmppConnection.sendRequestWithRetry(joinPayload);\r\n            console.log('📥 Join response:', joinResponse);\r\n\r\n            safeUpdatePresence(joinResponse);\r\n            safeProcessMessages(joinResponse);\r\n\r\n            const infoPayload = `<body rid='${xmppConnection.nextRid()}' sid='${session.sid}'\r\n                     xmlns='http://jabber.org/protocol/httpbind'>\r\n                <iq type='get' id='info1' xmlns='jabber:client' to='[email protected]'>\r\n                  <query xmlns='http://jabber.org/protocol/disco#info'/>\r\n                </iq>\r\n              </body>`;\r\n            await xmppConnection.sendRequestWithRetry(infoPayload);\r\n            console.log('🚀 Step 10: Connected! Starting presence updates...');\r\n\r\n            this.isConnected = true;\r\n            if (this.isReconnecting) {\r\n              (0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.showChatAlert)(\"Chat connected successfully!\", { type: 'success' });\r\n              this.isReconnecting = false;\r\n            }\r\n            this.startPresencePolling(xmppConnection);\r\n            // Process any queued messages.\r\n            this.processQueue();\r\n            break;\r\n          } catch (error) {\r\n            console.error(`💥 Connection error: ${error.message}`);\r\n            retries--;\r\n            if (retries === 0) {\r\n              console.log('⏳ Scheduling reconnection attempt in 5 seconds...');\r\n              this.isReconnecting = true;\r\n              setTimeout(() => this.connect(), _definitions_js__WEBPACK_IMPORTED_MODULE_0__.reconnectionDelay);\r\n            } else {\r\n              console.log(`🔄 Retrying connection... (${retries} attempts left)`);\r\n              await new Promise(resolve => setTimeout(resolve, 5000));\r\n            }\r\n          }\r\n        }\r\n      } catch (error) {\r\n        console.error(`💥 Final connection error: ${error.message}`);\r\n        this.isConnected = false;\r\n        if (!this.isReconnecting) {\r\n          console.log('⏳ Scheduling reconnection attempt in 5 seconds...');\r\n          this.isReconnecting = true;\r\n          setTimeout(() => this.connect(), _definitions_js__WEBPACK_IMPORTED_MODULE_0__.reconnectionDelay);\r\n        }\r\n      }\r\n    },\r\n\r\n    startPresencePolling(xmppConnection) {\r\n      this.presenceInterval = setInterval(async () => {\r\n        if (!this.isConnected) {\r\n          console.log('⚠️ Skipping presence poll - not connected');\r\n          return;\r\n        }\r\n        try {\r\n          const xmlResponse = await xmppConnection.sendRequestWithRetry(\r\n            `<body rid='${xmppConnection.nextRid()}' sid='${xmppConnection.sid}' xmlns='http://jabber.org/protocol/httpbind'/>`\r\n          );\r\n          safeUpdatePresence(xmlResponse);\r\n          safeProcessMessages(xmlResponse);\r\n          this.isReconnecting = false;\r\n        } catch (error) {\r\n          console.error('Presence polling error:', error.message);\r\n          if (error.message.includes('404') && !this.isReconnecting) {\r\n            console.log('🛑 Connection lost (404). Reconnecting in 5 seconds...');\r\n            (0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.showChatAlert)(\"Chat connection lost. Reconnecting...\", { type: 'warning' });\r\n            messageManager.clearMessages();\r\n            this.isReconnecting = true;\r\n            this.isConnected = false;\r\n            clearInterval(this.presenceInterval);\r\n            this.presenceInterval = null;\r\n            setTimeout(() => this.connect(), _definitions_js__WEBPACK_IMPORTED_MODULE_0__.reconnectionDelay);\r\n          }\r\n        }\r\n      }, _definitions_js__WEBPACK_IMPORTED_MODULE_0__.userListDelay);\r\n    },\r\n\r\n    sendMessage(text) {\r\n      const messageId = `msg_${Date.now()}`;\r\n      let isPrivate = false;\r\n      let fullJid = null;\r\n      let recipient = null;\r\n\r\n      // Only mark as pending if we're disconnected\r\n      const isPending = !this.isConnected || this.isReconnecting;\r\n\r\n      // Use original condition for private messages.\r\n      if (_helpers_js__WEBPACK_IMPORTED_MODULE_1__.privateMessageState.isPrivateMode && _helpers_js__WEBPACK_IMPORTED_MODULE_1__.privateMessageState.fullJid) {\r\n        isPrivate = true;\r\n        fullJid = _helpers_js__WEBPACK_IMPORTED_MODULE_1__.privateMessageState.fullJid;\r\n        recipient = _helpers_js__WEBPACK_IMPORTED_MODULE_1__.privateMessageState.targetUsername;\r\n        // Only mark as pending if we're disconnected\r\n        messageManager.addSentMessage(text, {\r\n          isPrivate: true,\r\n          recipient,\r\n          pending: isPending\r\n        });\r\n      } else {\r\n        messageManager.addSentMessage(text, {\r\n          pending: isPending\r\n        });\r\n      }\r\n\r\n      // Enqueue the message\r\n      this.messageQueue.push({\r\n        text,\r\n        id: messageId,\r\n        isPrivate,\r\n        fullJid,\r\n        recipient,\r\n        pending: isPending\r\n      });\r\n\r\n      // Process the queue if connected.\r\n      if (this.isConnected && !this.isReconnecting) {\r\n        this.processQueue();\r\n      }\r\n    }\r\n  };\r\n\r\n  return xmppClient;\r\n}\r\n\n\n//# sourceURL=webpack://tampermonkey-script/./src/xmppClient.js?");

/***/ }),

/***/ "./src/xmppConnection.js":
/*!*******************************!*\
  !*** ./src/xmppConnection.js ***!
  \*******************************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   \"default\": () => (/* binding */ XMPPConnection)\n/* harmony export */ });\nclass XMPPConnection {\r\n  constructor({ username, password, bindUrl, delay = 1000 }) {\r\n    this.username = username;\r\n    this.password = password;\r\n    this.bindUrl = bindUrl;\r\n    this.delay = delay;\r\n    this.sid = null;\r\n    this.rid = Math.floor(Date.now() / 1000);\r\n  }\r\n\r\n  nextRid() {\r\n    return ++this.rid;\r\n  }\r\n\r\n  async sendRequest(payload) {\r\n    const response = await fetch(this.bindUrl, {\r\n      method: 'POST',\r\n      headers: {\r\n        'Content-Type': 'text/xml; charset=UTF-8',\r\n        'User-Agent': 'Mozilla/5.0'\r\n      },\r\n      body: payload\r\n    });\r\n    if (!response.ok) {\r\n      throw new Error(`HTTP error! status: ${response.status}`);\r\n    }\r\n    return await response.text();\r\n  }\r\n\r\n  async sendRequestWithRetry(payload, maxRetries = 5) {\r\n    let lastError;\r\n    let baseWaitTime = this.delay;\r\n    for (let attempt = 1; attempt <= maxRetries; attempt++) {\r\n      try {\r\n        return await this.sendRequest(payload);\r\n      } catch (error) {\r\n        lastError = error;\r\n        if (error.message.includes('429')) {\r\n          const waitTime = baseWaitTime * Math.pow(2, attempt);\r\n          console.log(`⏱️ Rate limited (attempt ${attempt}/${maxRetries}). Waiting ${waitTime}ms...`);\r\n          await this.sleep(waitTime);\r\n        } else {\r\n          throw error;\r\n        }\r\n      }\r\n    }\r\n    throw new Error(`Max retries reached. Last error: ${lastError.message}`);\r\n  }\r\n\r\n  sleep(ms) {\r\n    return new Promise(resolve => setTimeout(resolve, ms));\r\n  }\r\n\r\n  base64Encode(str) {\r\n    const encoder = new TextEncoder();\r\n    const data = encoder.encode(str);\r\n    return btoa(String.fromCharCode(...data));\r\n  }\r\n\r\n  async connect() {\r\n    console.log('🌐 Step 1: Connecting to XMPP server...');\r\n    const initPayload = `<body xmlns='http://jabber.org/protocol/httpbind'\r\n               rid='${this.nextRid()}'\r\n               to='jabber.klavogonki.ru'\r\n               xml:lang='en'\r\n               wait='60'\r\n               hold='1'\r\n               ver='1.6'\r\n               xmpp:version='1.0'\r\n               xmlns:xmpp='urn:xmpp:xbosh'/>`;\r\n    const initResponse = await this.sendRequestWithRetry(initPayload);\r\n    this.sid = initResponse.match(/sid=['\"]([^'\"]+)['\"]/)[1];\r\n    console.log(`🔑 Step 2: Session ID received: ${this.sid}`);\r\n    await this.sleep(this.delay / 8);\r\n    console.log('🔐 Step 3: Authenticating...');\r\n    const authString = this.base64Encode('\\x00' + this.username + '\\x00' + this.password);\r\n    const authPayload = `<body rid='${this.nextRid()}' sid='${this.sid}'\r\n               xmlns='http://jabber.org/protocol/httpbind'>\r\n          <auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='PLAIN'>${authString}</auth>\r\n        </body>`;\r\n    const authResponse = await this.sendRequestWithRetry(authPayload);\r\n    if (!authResponse.includes('<success')) {\r\n      throw new Error('❌ Authentication failed');\r\n    }\r\n    console.log('✅ Step 4: Authentication successful!');\r\n    await this.sleep(this.delay / 8);\r\n    console.log('🔄 Step 5: Restarting stream...');\r\n    const restartPayload = `<body rid='${this.nextRid()}' sid='${this.sid}'\r\n               xmlns='http://jabber.org/protocol/httpbind'\r\n               to='jabber.klavogonki.ru'\r\n               xmpp:restart='true'\r\n               xmlns:xmpp='urn:xmpp:xbosh'/>`;\r\n    await this.sendRequestWithRetry(restartPayload);\r\n    await this.sleep(this.delay / 8);\r\n    console.log('📦 Step 6: Binding resource...');\r\n    const bindPayload = `<body rid='${this.nextRid()}' sid='${this.sid}'\r\n               xmlns='http://jabber.org/protocol/httpbind'>\r\n          <iq type='set' id='bind_1' xmlns='jabber:client'>\r\n            <bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>\r\n              <resource>web</resource>\r\n            </bind>\r\n          </iq>\r\n        </body>`;\r\n    await this.sendRequestWithRetry(bindPayload);\r\n    await this.sleep(this.delay / 8);\r\n    console.log('🔌 Step 7: Establishing session...');\r\n    const sessionPayload = `<body rid='${this.nextRid()}' sid='${this.sid}'\r\n               xmlns='http://jabber.org/protocol/httpbind'>\r\n          <iq type='set' id='session_1' xmlns='jabber:client'>\r\n            <session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>\r\n          </iq>\r\n        </body>`;\r\n    await this.sendRequestWithRetry(sessionPayload);\r\n    await this.sleep(this.delay / 8);\r\n    // Return session details for further use.\r\n    return { sid: this.sid, rid: this.rid };\r\n  }\r\n}\r\n\n\n//# sourceURL=webpack://tampermonkey-script/./src/xmppConnection.js?");

/***/ })

/******/ 	});
/************************************************************************/
/******/ 	// The module cache
/******/ 	var __webpack_module_cache__ = {};
/******/ 	
/******/ 	// The require function
/******/ 	function __webpack_require__(moduleId) {
/******/ 		// Check if module is in cache
/******/ 		var cachedModule = __webpack_module_cache__[moduleId];
/******/ 		if (cachedModule !== undefined) {
/******/ 			return cachedModule.exports;
/******/ 		}
/******/ 		// Create a new module (and put it into the cache)
/******/ 		var module = __webpack_module_cache__[moduleId] = {
/******/ 			id: moduleId,
/******/ 			// no module.loaded needed
/******/ 			exports: {}
/******/ 		};
/******/ 	
/******/ 		// Execute the module function
/******/ 		__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
/******/ 	
/******/ 		// Return the exports of the module
/******/ 		return module.exports;
/******/ 	}
/******/ 	
/************************************************************************/
/******/ 	/* webpack/runtime/compat get default export */
/******/ 	(() => {
/******/ 		// getDefaultExport function for compatibility with non-harmony modules
/******/ 		__webpack_require__.n = (module) => {
/******/ 			var getter = module && module.__esModule ?
/******/ 				() => (module['default']) :
/******/ 				() => (module);
/******/ 			__webpack_require__.d(getter, { a: getter });
/******/ 			return getter;
/******/ 		};
/******/ 	})();
/******/ 	
/******/ 	/* webpack/runtime/define property getters */
/******/ 	(() => {
/******/ 		// define getter functions for harmony exports
/******/ 		__webpack_require__.d = (exports, definition) => {
/******/ 			for(var key in definition) {
/******/ 				if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
/******/ 					Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
/******/ 				}
/******/ 			}
/******/ 		};
/******/ 	})();
/******/ 	
/******/ 	/* webpack/runtime/hasOwnProperty shorthand */
/******/ 	(() => {
/******/ 		__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
/******/ 	})();
/******/ 	
/******/ 	/* webpack/runtime/make namespace object */
/******/ 	(() => {
/******/ 		// define __esModule on exports
/******/ 		__webpack_require__.r = (exports) => {
/******/ 			if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ 				Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ 			}
/******/ 			Object.defineProperty(exports, '__esModule', { value: true });
/******/ 		};
/******/ 	})();
/******/ 	
/******/ 	/* webpack/runtime/nonce */
/******/ 	(() => {
/******/ 		__webpack_require__.nc = undefined;
/******/ 	})();
/******/ 	
/************************************************************************/
/******/ 	
/******/ 	// startup
/******/ 	// Load entry module and return exports
/******/ 	// This entry module can't be inlined because the eval devtool is used.
/******/ 	var __webpack_exports__ = __webpack_require__("./main.js");
/******/ 	
/******/ })()
;