Telegram-style чат + опциональная тёмная тема интерфейса. Автозагрузка до 10 страниц при открытии тикета.
// ==UserScript==
// @name HDE Chat Compact
// @namespace http://tampermonkey.net/
// @version 14.56
// @description Telegram-style чат + опциональная тёмная тема интерфейса. Автозагрузка до 10 страниц при открытии тикета.
// @author Eban
// @license MIT
// @match https://*.helpdeskeddy.com/*
// @grant none
// @run-at document-start
// @language ru
// ==/UserScript==
(function() {
"use strict";
const STYLE_ID = "hde-compact-v10-style";
const BUBBLE_CONTEXT_MENU_ID = "hde-bubble-context-menu";
const DONE_CLASS = "hde-v10-done";
const DATE_SEP_CLASS = "hde-date-separator";
const TICKET_SEP_CLASS = "hde-ticket-separator";
const LIKE_EMOJI_STYLE_ID = "hde-like-emoji-style";
const MENU_STYLE_ID = "hde-tools-menu-style";
const MENU_PANEL_ID = "hde-tools-panel";
const THEME_STYLE_ID = "hde-tools-theme-style";
const THEME_BOOT_STYLE_ID = "hde-tools-theme-boot-style";
const LEFT_TABS_BOOT_STYLE_ID = "hde-left-tabs-boot-style";
const DARK_HTML_CLASS = "hde-tools-theme-dark";
const PINK_HTML_CLASS = "hde-tools-theme-pink";
const MIDNIGHT_BLUE_HTML_CLASS = "hde-tools-theme-midnight-blue";
const TRICOLOR_HTML_CLASS = "hde-tools-theme-tricolor";
const MONOCHROME_HTML_CLASS = "hde-tools-theme-monochrome";
const LEGACY_DARK_HTML_CLASS = "hde-tools-dark";
const LEGACY_THEME_STYLE_ID = "hde-tools-dark-theme-style";
const THEME_MODE_STANDARD = "standard";
const THEME_MODE_DARK = "dark";
const THEME_MODE_PINK = "pink";
const THEME_MODE_MIDNIGHT_BLUE = "midnight-blue";
const THEME_MODE_TRICOLOR = "tricolor-contrast";
const THEME_MODE_MONOCHROME = "bw-contrast";
const THEME_MODES = [ THEME_MODE_STANDARD, THEME_MODE_DARK, THEME_MODE_PINK, THEME_MODE_MIDNIGHT_BLUE, THEME_MODE_TRICOLOR, THEME_MODE_MONOCHROME ];
const THEME_HTML_CLASSES = [ DARK_HTML_CLASS, PINK_HTML_CLASS, MIDNIGHT_BLUE_HTML_CLASS, TRICOLOR_HTML_CLASS, MONOCHROME_HTML_CLASS, LEGACY_DARK_HTML_CLASS ];
const THEME_MODE_TO_HTML_CLASS = {
[THEME_MODE_DARK]: DARK_HTML_CLASS,
[THEME_MODE_PINK]: PINK_HTML_CLASS,
[THEME_MODE_MIDNIGHT_BLUE]: MIDNIGHT_BLUE_HTML_CLASS,
[THEME_MODE_TRICOLOR]: TRICOLOR_HTML_CLASS,
[THEME_MODE_MONOCHROME]: MONOCHROME_HTML_CLASS
};
const THEME_MODE_TO_LABEL = {
[THEME_MODE_STANDARD]: "Стандартная",
[THEME_MODE_DARK]: "Тёмная",
[THEME_MODE_PINK]: "Розовая",
[THEME_MODE_MIDNIGHT_BLUE]: "Midnight Blue",
[THEME_MODE_TRICOLOR]: "Контрастная (бело-сине-красная)",
[THEME_MODE_MONOCHROME]: "Контрастная ч/б"
};
const THEME_BOOT_COLORS = {
[THEME_MODE_DARK]: {
bg: "#121418",
fg: "#dcdfe6"
},
[THEME_MODE_PINK]: {
bg: "#fbe9f2",
fg: "#6f4d60"
},
[THEME_MODE_MIDNIGHT_BLUE]: {
bg: "#0b1220",
fg: "#cfddf5"
},
[THEME_MODE_TRICOLOR]: {
bg: "#ffffff",
fg: "#1d3f73"
},
[THEME_MODE_MONOCHROME]: {
bg: "#0c0c0c",
fg: "#f1f1f1"
}
};
const SETTINGS_PANEL_VERSION = "12";
const STAFFS_DRAWER_ID = "hde-staffs-drawer";
const LEFT_TABS_MIRROR_ID = "hde-left-tabs-mirror";
const LEFT_TABS_COLUMN_ID = "hde-left-tabs-column";
const LEFT_TABS_ONLY_CLASS = "hde-left-tabs-only";
const LEFT_TABS_CACHE_KEY = "hde-left-tabs-cache-v1";
const LEFT_TABS_CACHE_KEY_LEGACY_V2 = "hde-left-tabs-cache-v2";
const LEFT_TABS_FREEZE_ICON_URL = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAACXBIWXMAAAsTAAALEwEAmpwYAAADrUlEQVR4nO2aTUhVQRSAvzTNfSsX0o8WUYG7pF0/EP2q/akVlLYIWiZmhAVChVAY5MIw+jGDxL1W0CozSVtWRlG6LCkKtUxBMwbOg8sw7755Pu/1+t58MIt35twzP/e+OTPnDDgcDofD4XA4QmUZcBkYBb4BF8kwjgNzWqkkg7hvmIAOMohHhgl4SJpSCPQDP4A2YAVw2zABrUAecAf4Jc8UkQY80wbaDbQYJuAW0KPJnpIGDBkG+9dS9p4l5trKgTpgg0d+zjAw21LrsaNs1gOlRJRLno7/ATZ66k4D40kMfAyo8Ty/GZj01DcQQT5qg2jS6tcAvRaDfwGs1p69qumothaNVeKzB4FmYKXIX2qdbDc8mwWcB6YMA5+SOqWj067p9olctX0DeCOutYCAyQE+aJ1R29ljQKMmVyt6PM4aJkDJ4qF7h0Zpc9TwZWQTICU+n+4X7feAj50ThueVLB4Dmu5nn35sIUDWJ7GQDfvYqUzyLDCcRLvrCJi7lh2Z8LFxxKCvZPGYsGzzHiH5+1PAiEWH1MKUb7BRbtBVMp38OOcGvai+VMdZQAMjVzYq0xY+vVYW0Bj7DXpKFiNHNlBjCWxPi23Vl1A5BDzXNieJyjtguzy/x1CvZIptomtrd1L6UhbW4Hcl0TlT6ZTPVZdXS9187f4DdoQxAU0+HeiWN9Elv/06q8tmE+h3ie0eH9vXwpiAvVqjM/LmijW9rXKmn0ux9IstL8XS5oymu4+QqJG33SwBDz+PUWHYJNkU9cxRsRGPQokjPAFOEmFygDPAd4uBq2jQBYkOpQ056TYB1RKuagn4L1Bh8Re4KQujijmEwoEIL4JqgV4SbnA2BTfYvdhucGeCwSUqnXKW0OVK9jjFr0X1LRTKUtwK7zbUK1lsK/x2Hlvhg4RMboCHoeVRPgxliRdI5ThcZtA1HWbyJf6YqJ0R+Qv5eYwF48ECBEQOG/SVLB6/LdtUwRrSMSQ2EpWQWMkiBUUHoxIUzTYkQBYrLF5lCIsPaYttIBTIwqSSEdc9iZE+y8RIvU9ipN4yMaKSMEjb6kT6WnQCT4z48Unr5JV5psZ6Rddv96mSM5GjwSc5WmPh071lXDvcbBKbsfrIXqoqlfye8hYx6pIYuF7UZsibHq+TNkLx9wuFnktM2wsStldkeiSMlTFXZIrEQ/yUS1J5ciFKn4BWuUDVJheqXgFrSVM6MumanO0EhJLYjApVmX5VFvHlXz2XpZeUa3M4HA6Hw+FgqfMfsRJ2ppkqjKEAAAAASUVORK5CYII=";
const MSG_BLOCK_SELECTOR = ":scope > .ticket-conversation__message-block";
const MSG_HTML_SELECTOR = ":scope > .ticket-conversation__message-text > .ticket-conversation__message-html";
const DEFAULT_PREVIOUS_TICKETS_TO_LOAD = 5;
const MAX_PAGES_PER_PREVIOUS_TICKET = 3;
const DEBUG_LOGS = false;
function debugLog(...args) {
if (!DEBUG_LOGS) return;
console.log(...args);
}
const CONFIG = {
compactChat: false,
autoLoadHistory: false,
autoLoadPreviousDialogs: false,
previousDialogsLimit: 1,
themeMode: THEME_MODE_STANDARD,
colleagueStatuses: false,
leftTabsMirror: false,
resizablePanels: false
};
function normalizeThemeMode(value) {
const mode = String(value || "").toLowerCase();
return THEME_MODES.includes(mode) ? mode : THEME_MODE_STANDARD;
}
function applyThemeClass(mode, root = document.documentElement) {
if (!root) return false;
root.classList.remove(...THEME_HTML_CLASSES);
const nextClass = THEME_MODE_TO_HTML_CLASS[mode];
if (!nextClass) return false;
root.classList.add(nextClass);
return true;
}
function buildThemeBootCss() {
return Object.entries(THEME_BOOT_COLORS).map(([mode, palette]) => {
const cls = THEME_MODE_TO_HTML_CLASS[mode];
if (!cls) return "";
return `html.${cls}, html.${cls} body { background: ${palette.bg} !important; color: ${palette.fg} !important; }`;
}).join("\n");
}
function renderThemeOptions(selectedMode) {
return THEME_MODES.map(mode => {
const label = THEME_MODE_TO_LABEL[mode] || mode;
const selected = selectedMode === mode ? "selected" : "";
return `<option value="${mode}" ${selected}>${label}</option>`;
}).join("");
}
function getEarlyThemeModeFromStorage() {
try {
const saved = localStorage.getItem("hde-tools-config");
if (!saved) return THEME_MODE_STANDARD;
const parsed = JSON.parse(saved);
if (parsed && typeof parsed.themeMode === "string") {
return normalizeThemeMode(parsed.themeMode);
}
if (parsed && typeof parsed.darkTheme === "boolean") {
return parsed.darkTheme ? THEME_MODE_DARK : THEME_MODE_STANDARD;
}
} catch (e) {}
return THEME_MODE_STANDARD;
}
function getEarlyLeftTabsMirrorFromStorage() {
try {
const saved = localStorage.getItem("hde-tools-config");
if (!saved) return false;
const parsed = JSON.parse(saved);
return !!(parsed && parsed.leftTabsMirror === true);
} catch (e) {}
return false;
}
function shouldHideNativeTicketTabs() {
return !!CONFIG.leftTabsMirror;
}
function syncNativeTicketTabsVisibilityClass(root = document.documentElement) {
if (!root) return;
root.classList.toggle(LEFT_TABS_ONLY_CLASS, shouldHideNativeTicketTabs());
}
function applyEarlyThemeBoot() {
const mode = getEarlyThemeModeFromStorage();
const root = document.documentElement;
if (!root) return;
if (!applyThemeClass(mode, root)) return;
if (document.getElementById(THEME_BOOT_STYLE_ID)) return;
const style = document.createElement("style");
style.id = THEME_BOOT_STYLE_ID;
style.textContent = buildThemeBootCss();
if (document.head) document.head.appendChild(style); else root.appendChild(style);
}
applyEarlyThemeBoot();
function applyEarlyLeftTabsBoot() {
const earlyLeftTabsMirror = getEarlyLeftTabsMirrorFromStorage();
if (!earlyLeftTabsMirror) return;
const root = document.documentElement;
if (!root) return;
root.classList.add(LEFT_TABS_ONLY_CLASS);
if (document.getElementById(LEFT_TABS_BOOT_STYLE_ID)) return;
const style = document.createElement("style");
style.id = LEFT_TABS_BOOT_STYLE_ID;
style.textContent = `\n html.${LEFT_TABS_ONLY_CLASS} #ticket-app .ticket-topbar__tabs {\n display: none !important;\n }\n html.${LEFT_TABS_ONLY_CLASS} .ticket-tabs__show-more-popper {\n display: none !important;\n }\n #${LEFT_TABS_COLUMN_ID} {\n flex: 0 0 180px;\n width: 180px;\n min-width: 180px;\n box-sizing: border-box;\n margin-top: 0;\n }\n #${LEFT_TABS_MIRROR_ID}.hde-left-tabs-mirror--skeleton {\n margin: 0;\n border-radius: 6px;\n overflow: hidden;\n background: #ffffff;\n }\n #${LEFT_TABS_MIRROR_ID}.hde-left-tabs-mirror--skeleton .hde-left-tabs-mirror__title {\n min-height: 44px;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 12px;\n font-weight: 600;\n color: #909399;\n background: #f5f7fa;\n }\n #${LEFT_TABS_MIRROR_ID}.hde-left-tabs-mirror--skeleton .hde-left-tabs-mirror__list {\n padding: 6px 0;\n }\n #${LEFT_TABS_MIRROR_ID}.hde-left-tabs-mirror--skeleton .hde-left-tabs-mirror__skeleton-item {\n height: 34px;\n margin: 0 8px 8px;\n border-radius: 8px;\n background: linear-gradient(90deg, #eef1f6 25%, #f5f7fa 45%, #eef1f6 65%);\n background-size: 220% 100%;\n animation: hdeLeftTabsSkeletonPulse 1.2s linear infinite;\n }\n #${LEFT_TABS_MIRROR_ID}.hde-left-tabs-mirror--skeleton .hde-left-tabs-mirror__skeleton-item:last-child {\n margin-bottom: 0;\n }\n html.${DARK_HTML_CLASS} #${LEFT_TABS_MIRROR_ID}.hde-left-tabs-mirror--skeleton {\n background: #1e2529;\n }\n html.${DARK_HTML_CLASS} #${LEFT_TABS_MIRROR_ID}.hde-left-tabs-mirror--skeleton .hde-left-tabs-mirror__title {\n color: #c0c4cc;\n background: #252b33;\n }\n html.${DARK_HTML_CLASS} #${LEFT_TABS_MIRROR_ID}.hde-left-tabs-mirror--skeleton .hde-left-tabs-mirror__skeleton-item {\n background: linear-gradient(90deg, #2a3038 25%, #343b45 45%, #2a3038 65%);\n }\n html.${PINK_HTML_CLASS} #${LEFT_TABS_MIRROR_ID}.hde-left-tabs-mirror--skeleton {\n background: #fff6fb;\n }\n html.${PINK_HTML_CLASS} #${LEFT_TABS_MIRROR_ID}.hde-left-tabs-mirror--skeleton .hde-left-tabs-mirror__title {\n color: #7a5a6b;\n background: #f8eaf2;\n }\n html.${PINK_HTML_CLASS} #${LEFT_TABS_MIRROR_ID}.hde-left-tabs-mirror--skeleton .hde-left-tabs-mirror__skeleton-item {\n background: linear-gradient(90deg, #f0deea 25%, #f6e8f0 45%, #f0deea 65%);\n }\n html.${MIDNIGHT_BLUE_HTML_CLASS} #${LEFT_TABS_MIRROR_ID}.hde-left-tabs-mirror--skeleton {\n background: #0f1728;\n }\n html.${MIDNIGHT_BLUE_HTML_CLASS} #${LEFT_TABS_MIRROR_ID}.hde-left-tabs-mirror--skeleton .hde-left-tabs-mirror__title {\n color: #b7cae7;\n background: #16243b;\n }\n html.${MIDNIGHT_BLUE_HTML_CLASS} #${LEFT_TABS_MIRROR_ID}.hde-left-tabs-mirror--skeleton .hde-left-tabs-mirror__skeleton-item {\n background: linear-gradient(90deg, #1b2d4a 25%, #23395f 45%, #1b2d4a 65%);\n }\n html.${TRICOLOR_HTML_CLASS} #${LEFT_TABS_MIRROR_ID}.hde-left-tabs-mirror--skeleton {\n background: #ffffff;\n }\n html.${TRICOLOR_HTML_CLASS} #${LEFT_TABS_MIRROR_ID}.hde-left-tabs-mirror--skeleton .hde-left-tabs-mirror__title {\n color: #2a4d82;\n background: #edf4ff;\n }\n html.${TRICOLOR_HTML_CLASS} #${LEFT_TABS_MIRROR_ID}.hde-left-tabs-mirror--skeleton .hde-left-tabs-mirror__skeleton-item {\n background: linear-gradient(90deg, #e7edf6 25%, #f1f5fb 45%, #e7edf6 65%);\n }\n html.${MONOCHROME_HTML_CLASS} #${LEFT_TABS_MIRROR_ID}.hde-left-tabs-mirror--skeleton {\n background: #151515;\n }\n html.${MONOCHROME_HTML_CLASS} #${LEFT_TABS_MIRROR_ID}.hde-left-tabs-mirror--skeleton .hde-left-tabs-mirror__title {\n color: #e6e6e6;\n background: #1f1f1f;\n }\n html.${MONOCHROME_HTML_CLASS} #${LEFT_TABS_MIRROR_ID}.hde-left-tabs-mirror--skeleton .hde-left-tabs-mirror__skeleton-item {\n background: linear-gradient(90deg, #252525 25%, #323232 45%, #252525 65%);\n }\n @keyframes hdeLeftTabsSkeletonPulse {\n 0% { background-position: 100% 0; }\n 100% { background-position: -100% 0; }\n }\n `;
if (document.head) document.head.appendChild(style); else root.appendChild(style);
}
applyEarlyLeftTabsBoot();
function ensureEarlyLeftTabsSkeleton(column) {
if (!(column instanceof Element)) return;
if (document.getElementById(LEFT_TABS_MIRROR_ID)) return;
const mirror = document.createElement("div");
mirror.id = LEFT_TABS_MIRROR_ID;
mirror.className = "hde-left-tabs-mirror hde-left-tabs-mirror--skeleton";
mirror.innerHTML = `\n <div class="hde-left-tabs-mirror__title">\n <span class="hde-left-tabs-mirror__title-text">Тикеты</span>\n </div>\n <div class="hde-left-tabs-mirror__list">\n <div class="hde-left-tabs-mirror__skeleton-item"></div>\n <div class="hde-left-tabs-mirror__skeleton-item"></div>\n <div class="hde-left-tabs-mirror__skeleton-item"></div>\n </div>\n `;
column.appendChild(mirror);
}
function loadConfig() {
try {
const saved = localStorage.getItem("hde-tools-config");
if (saved) {
Object.assign(CONFIG, JSON.parse(saved));
}
const limit = parseInt(CONFIG.previousDialogsLimit, 10);
CONFIG.previousDialogsLimit = Number.isFinite(limit) ? Math.min(10, Math.max(1, limit)) : DEFAULT_PREVIOUS_TICKETS_TO_LOAD;
const hasThemeMode = typeof CONFIG.themeMode === "string" && THEME_MODES.includes(CONFIG.themeMode.toLowerCase());
if (hasThemeMode) {
CONFIG.themeMode = normalizeThemeMode(CONFIG.themeMode);
} else if (typeof CONFIG.darkTheme === "boolean") {
CONFIG.themeMode = CONFIG.darkTheme ? THEME_MODE_DARK : THEME_MODE_STANDARD;
} else {
CONFIG.themeMode = THEME_MODE_STANDARD;
}
if (typeof CONFIG.colleagueStatuses !== "boolean") CONFIG.colleagueStatuses = false;
if (typeof CONFIG.leftTabsMirror !== "boolean") CONFIG.leftTabsMirror = false;
if (typeof CONFIG.resizablePanels !== "boolean") CONFIG.resizablePanels = false;
delete CONFIG.darkTheme;
delete CONFIG.hideNativeTicketTabs;
} catch (e) {
console.warn("Ошибка загрузки конфига:", e);
}
}
function saveConfig() {
CONFIG.themeMode = normalizeThemeMode(CONFIG.themeMode);
const payload = Object.assign({}, CONFIG);
delete payload.darkTheme;
delete payload.hideNativeTicketTabs;
localStorage.setItem("hde-tools-config", JSON.stringify(payload));
}
const DARK_THEME_CSS_BASE = `\nhtml.${DARK_HTML_CLASS},\nhtml.${DARK_HTML_CLASS} body {\n background-color: #121418 !important;\n color: #dcdfe6 !important;\n scrollbar-color: #3d4654 #1a1f26;\n}\nhtml.${DARK_HTML_CLASS} {\n --menu-background: #161a1f;\n --menu-item-color: #e8eaed;\n --menu-item-background: #161a1f;\n --menu-item-color-hover: #e8f4f7;\n --menu-item-background-hover: #1f2a32;\n --ticket-user-post-color: #e8eaed;\n --ticket-user-post-background: #2d333b;\n --ticket-user-href-color: #7ec8e0;\n --ticket-user-href-hover: #a6dff0;\n --ticket-user-background: #1a1f26;\n --ticket-user-href-border: 0px;\n --ticket-staff-post-color: #f0f2f5;\n --ticket-staff-post-background: #4a5563;\n --ticket-staff-href-color: #fff;\n --ticket-staff-href-hover: #e2e8f0;\n --ticket-staff-own-post-color: #f0f2f5;\n --ticket-staff-own-post-background: #3d4654;\n --ticket-staff-comment-color: #f0fafc;\n --ticket-staff-comment-background: #1a5f6e;\n --ticket-staff-comment-href-color: #e0f7fa;\n --ticket-staff-comment-href-hover: #fff;\n --ticket-staff-own-comment-color: #e8f4f7;\n --ticket-staff-own-comment-background: #154a56;\n --ticket-post-input-color: #e4e7ed;\n --ticket-post-input-background: #252b33;\n --ticket-comment-input-color: #f0fafc;\n --ticket-comment-input-background: #1a5f6e;\n --ticket-primary-button-color: #0f1419;\n --ticket-primary-button-background: #5eb3c9;\n --ticket-primary-button-color-hover: #0f1419;\n --ticket-primary-button-background-hover: #7ec8e0;\n --ticket-secondary-button-color: #dcdfe6;\n --ticket-secondary-button-background: #2a3038;\n --ticket-secondary-button-color-hover: #7ec8e0;\n --ticket-secondary-button-background-hover: #343b45;\n}\n`;
const DARK_THEME_CSS_COMPONENTS = `\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-sidebar,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-sidebar__heading,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-sidebar__container {\n background-color: #1a1f26 !important;\n color: #dcdfe6 !important;\n border-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-sidebar__heading-text,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-sidebar__filter a {\n color: #c0c4cc !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-sidebar__heading-button,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-sidebar__heading-button i,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-sidebar__heading-button .el-icon-setting {\n color: #c0c4cc !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-sidebar__heading-button:hover,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-sidebar__heading-button:hover i,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-sidebar__heading-button:hover .el-icon-setting {\n color: #e4e7ed !important;\n}\nhtml.${DARK_HTML_CLASS} .sidebar,\nhtml.${DARK_HTML_CLASS} .sidebar__item,\nhtml.${DARK_HTML_CLASS} .sidebar__item-icon,\nhtml.${DARK_HTML_CLASS} .sidebar__item-name,\nhtml.${DARK_HTML_CLASS} i.hde-macro {\n color: #c0c4cc !important;\n}\nhtml.${DARK_HTML_CLASS} .sidebar__item:hover {\n background-color: #252b33 !important;\n}\nhtml.${DARK_HTML_CLASS} .macro__row,\nhtml.${DARK_HTML_CLASS} .macro__row.macro__row_heading,\nhtml.${DARK_HTML_CLASS} .macro__col {\n background-color: #1e2529 !important;\n color: #c0c4cc !important;\n border-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-list__heading,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-topbar {\n background-color: #1e2529 !important;\n color: #dcdfe6 !important;\n border-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-user,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-modules {\n background-color: #1a1f26 !important;\n color: #dcdfe6 !important;\n border-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-list__filter-count span {\n background: #2d333b !important;\n color: #c0c4cc !important;\n}\nhtml.${DARK_HTML_CLASS} .menu__item-badge,\nhtml.${DARK_HTML_CLASS} .menu__item-avatar-status {\n color: #121418 !important;\n font-weight: 700 !important;\n}\nhtml.${DARK_HTML_CLASS} .user-card-info,\nhtml.${DARK_HTML_CLASS} .create-ticket-btn,\nhtml.${DARK_HTML_CLASS} .etabs.user-card-tabs-nav,\nhtml.${DARK_HTML_CLASS} .user-card-custom-title {\n background-color: #1e2529 !important;\n color: #dcdfe6 !important;\n border-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} .etabs.user-card-tabs-nav li {\n background-color: #252b33 !important;\n border-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} .etabs.user-card-tabs-nav li.active {\n background-color: #2d333b !important;\n}\nhtml.${DARK_HTML_CLASS} .etabs.user-card-tabs-nav li a,\nhtml.${DARK_HTML_CLASS} .etabs.user-card-tabs-nav li i,\nhtml.${DARK_HTML_CLASS} .user-card-name,\nhtml.${DARK_HTML_CLASS} .user-card-name span,\nhtml.${DARK_HTML_CLASS} .user-card-info a,\nhtml.${DARK_HTML_CLASS} .user-card-info i,\nhtml.${DARK_HTML_CLASS} #user-audit-btn {\n color: #c0c4cc !important;\n}\nhtml.${DARK_HTML_CLASS} .etabs.user-card-tabs-nav li.active a,\nhtml.${DARK_HTML_CLASS} .etabs.user-card-tabs-nav li.active i,\nhtml.${DARK_HTML_CLASS} .hde-comment-dots.active {\n color: #e4e7ed !important;\n}\nhtml.${DARK_HTML_CLASS} .user-card-toggle,\nhtml.${DARK_HTML_CLASS} .user-card-toggle.pull-right,\nhtml.${DARK_HTML_CLASS} .user-card-toggle.pull-right.ml-2 {\n background-color: #2d333b !important;\n color: #e4e7ed !important;\n border: 1px solid #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} .user-card-info input,\nhtml.${DARK_HTML_CLASS} .user-card-custom-title input {\n background-color: #252b33 !important;\n color: #e4e7ed !important;\n border-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} .user-card-custom-toggle .user-card-toggle-div table,\nhtml.${DARK_HTML_CLASS} .user-card-custom-toggle .user-card-toggle-div table tr,\nhtml.${DARK_HTML_CLASS} .user-card-custom-toggle .user-card-toggle-div table td {\n background-color: #1e2529 !important;\n color: #dcdfe6 !important;\n border-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} .user-card-custom-toggle .user-card-toggle-div table tr:hover,\nhtml.${DARK_HTML_CLASS} .user-card-custom-toggle .user-card-toggle-div table tr:hover td {\n background-color: #252b33 !important;\n color: #e4e7ed !important;\n}\nhtml.${DARK_HTML_CLASS} .user-card-custom-toggle .user-card-toggle-div table tr.border-top td,\nhtml.${DARK_HTML_CLASS} .user-card-custom-toggle .user-card-toggle-div table tr.border-bottom td {\n border-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} .user-card-custom-toggle .depart-ul li {\n color: #c0c4cc !important;\n}\nhtml.${DARK_HTML_CLASS} i.hde-pencil,\nhtml.${DARK_HTML_CLASS} i.icon-attachment {\n color: #c0c4cc !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-tabs {\n background: #161a1f !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-tabs__tab {\n background: #252b33 !important;\n color: #c0c4cc !important;\n border-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-tabs .ticket-tabs__tab-divider {\n background: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-tabs__close-all,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-tabs__more-close-all,\nhtml.${DARK_HTML_CLASS} .ticket-tabs__close-all,\nhtml.${DARK_HTML_CLASS} .ticket-tabs__more-close-all {\n background-color: #1e2529 !important;\n border: 1px solid #3d4654 !important;\n color: #c0c4cc !important;\n box-shadow: none !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-tabs__close-all:hover,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-tabs__more-close-all:hover,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-tabs__close-all:focus,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-tabs__more-close-all:focus,\nhtml.${DARK_HTML_CLASS} .ticket-tabs__close-all:hover,\nhtml.${DARK_HTML_CLASS} .ticket-tabs__more-close-all:hover,\nhtml.${DARK_HTML_CLASS} .ticket-tabs__close-all:focus,\nhtml.${DARK_HTML_CLASS} .ticket-tabs__more-close-all:focus {\n background-color: #252b33 !important;\n border-color: #4a5563 !important;\n color: #e4e7ed !important;\n outline: none !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-tabs__tab:hover {\n background: #2d333b !important;\n color: #e4e7ed !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-list__column-sla-overdue {\n color: #d97a7a !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .el-table__body tr.el-table__row.ticket-list__column-sla-reached > td,\nhtml.${DARK_HTML_CLASS} #ticket-app .el-table__body tr.el-table__row.ticket-list__column-parent-ticket.ticket-list__column-sla-reached > td {\n background-color: rgba(143, 74, 74, 0.1) !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .el-table__body tr.el-table__row.ticket-list__column-sla-reached:hover > td,\nhtml.${DARK_HTML_CLASS} #ticket-app .el-table__body tr.el-table__row.ticket-list__column-parent-ticket.ticket-list__column-sla-reached:hover > td {\n background-color: rgba(143, 74, 74, 0.1) !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .el-table__body tr.el-table__row.ticket-list__column-freeze > td,\nhtml.${DARK_HTML_CLASS} #ticket-app .el-table__body tr.el-table__row.ticket-list__column-parent-ticket.ticket-list__column-freeze > td {\n background-color: rgba(94, 157, 188, 0.1) !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .el-table__body tr.el-table__row.ticket-list__column-freeze:hover > td,\nhtml.${DARK_HTML_CLASS} #ticket-app .el-table__body tr.el-table__row.ticket-list__column-parent-ticket.ticket-list__column-freeze:hover > td {\n background-color: rgba(94, 157, 188, 0.1) !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-list__column-status[title*="offline" i],\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-list__column-status[aria-label*="offline" i],\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-list__column-status_offline,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-list__column-status.status-offline {\n --status-background-color: #8f4a4a !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-list__column-status[style*="--status-background-color:#D31616"],\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-list__column-status[style*="--status-background-color: #D31616"],\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-list__column-select-text[style*="--status-background-color:#D31616"],\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-list__column-select-text[style*="--status-background-color: #D31616"] {\n --status-background-color: #8f4a4a !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-detail,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-container,\nhtml.${DARK_HTML_CLASS} #ticket-app .el-main {\n background-color: #121418 !important;\n color: #dcdfe6 !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-detail,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-container,\nhtml.${DARK_HTML_CLASS} #ticket-app .el-main,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-conversation__messages {\n background-color: #1a1f26 !important;\n}\n/* Metro: боковые колонки и блок полей — в theme жёстко #fff */\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket .ticket-left-block,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket .ticket-right-block {\n background-color: #1a1f26 !important;\n border-color: #3d4654 !important;\n color: #dcdfe6 !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-fields {\n background-color: #1e2529 !important;\n border-color: #3d4654 !important;\n color: #dcdfe6 !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-fields__image {\n border-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-detail__title,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-right-block {\n color: #dcdfe6 !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-topbar,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket .ticket-topbar {\n background-color: #1e2529 !important;\n border-bottom-color: #3d4654 !important;\n color: #dcdfe6 !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .el-aside {\n background-color: #1a1f26 !important;\n color: #dcdfe6 !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .el-table {\n background-color: #1a1f26 !important;\n color: #dcdfe6 !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .el-table tr,\nhtml.${DARK_HTML_CLASS} #ticket-app .el-table__body tr {\n background-color: #1a1f26 !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .el-table th.el-table__cell {\n background-color: #161a1f !important;\n color: #c0c4cc !important;\n border-bottom-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .el-table td.el-table__cell {\n border-bottom-color: #2d333b !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .el-table--striped .el-table__body tr.el-table__row--striped td {\n background: #1e2529 !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .el-table--enable-row-hover .el-table__body tr:hover > td {\n background-color: #252b33 !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .el-input__inner,\nhtml.${DARK_HTML_CLASS} #ticket-app .el-textarea__inner {\n background-color: #252b33 !important;\n color: #e4e7ed !important;\n border-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} .el-input__inner {\n background-color: #252b33 !important;\n color: #e4e7ed !important;\n border-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} .el-input__inner:hover,\nhtml.${DARK_HTML_CLASS} .el-input__inner:focus,\nhtml.${DARK_HTML_CLASS} .el-input.is-focus .el-input__inner {\n background-color: #2d333b !important;\n color: #e4e7ed !important;\n border-color: #4a5563 !important;\n}\nhtml.${DARK_HTML_CLASS} .el-checkbox__inner {\n background-color: #252b33 !important;\n border-color: #4a5563 !important;\n}\nhtml.${DARK_HTML_CLASS} .el-checkbox__inner:hover,\nhtml.${DARK_HTML_CLASS} .el-checkbox__input:hover .el-checkbox__inner {\n border-color: #7ec8e0 !important;\n}\nhtml.${DARK_HTML_CLASS} .el-checkbox__input.is-checked .el-checkbox__inner,\nhtml.${DARK_HTML_CLASS} .el-checkbox__input.is-indeterminate .el-checkbox__inner {\n background-color: #3d4654 !important;\n border-color: #7ec8e0 !important;\n}\nhtml.${DARK_HTML_CLASS} .el-checkbox__input.is-checked + .el-checkbox__label,\nhtml.${DARK_HTML_CLASS} .el-checkbox__label {\n color: #dcdfe6 !important;\n}\nhtml.${DARK_HTML_CLASS} .el-checkbox__input.is-disabled .el-checkbox__inner {\n background-color: #1e2529 !important;\n border-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} .el-checkbox__input.is-disabled + span.el-checkbox__label,\nhtml.${DARK_HTML_CLASS} .el-checkbox__input.is-disabled.is-checked + span.el-checkbox__label {\n color: #8b9199 !important;\n}\nhtml.${DARK_HTML_CLASS} .select,\nhtml.${DARK_HTML_CLASS} .select.is-active,\nhtml.${DARK_HTML_CLASS} .select__options {\n background-color: #1e2529 !important;\n color: #dcdfe6 !important;\n border-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} .select__options *,\nhtml.${DARK_HTML_CLASS} .select * {\n color: #dcdfe6 !important;\n}\nhtml.${DARK_HTML_CLASS} .select__options .is-active,\nhtml.${DARK_HTML_CLASS} .select__options .active,\nhtml.${DARK_HTML_CLASS} .select__options .selected,\nhtml.${DARK_HTML_CLASS} .select__options *:hover {\n background-color: #2d333b !important;\n color: #e4e7ed !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ck.ck-toolbar,\nhtml.${DARK_HTML_CLASS} #ticket-app .ck.ck-toolbar .ck-toolbar__items {\n background-color: #1e2529 !important;\n border-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-editor__ckeditor .ck.ck-editor,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-editor__ckeditor .ck.ck-editor__top,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-editor__ckeditor .ck.ck-sticky-panel,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-editor__ckeditor .ck.ck-sticky-panel__content,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-editor__ckeditor .ck.ck-sticky-panel__placeholder {\n background-color: #1e2529 !important;\n border-color: #3d4654 !important;\n box-shadow: none !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ck.ck-toolbar__separator,\nhtml.${DARK_HTML_CLASS} #ticket-app .ck .ck-toolbar__separator {\n background-color: #3d4654 !important;\n border-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ck.ck-toolbar button,\nhtml.${DARK_HTML_CLASS} #ticket-app .ck.ck-toolbar .ck-button {\n color: #c0c4cc !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ck.ck-dropdown__panel,\nhtml.${DARK_HTML_CLASS} #ticket-app .ck.ck-dropdown__panel .ck-list,\nhtml.${DARK_HTML_CLASS} #ticket-app .ck.ck-dropdown__panel .ck-list__item {\n background-color: #1e2529 !important;\n border-color: #3d4654 !important;\n color: #dcdfe6 !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ck.ck-dropdown__panel .ck-list-item-button,\nhtml.${DARK_HTML_CLASS} #ticket-app .ck.ck-dropdown__panel .ck-button.ck-list-item-button {\n background-color: #1e2529 !important;\n color: #c0c4cc !important;\n border-color: transparent !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ck.ck-dropdown__panel .ck-list-item-button:hover,\nhtml.${DARK_HTML_CLASS} #ticket-app .ck.ck-dropdown__panel .ck-button.ck-list-item-button:hover,\nhtml.${DARK_HTML_CLASS} #ticket-app .ck.ck-dropdown__panel .ck-list-item-button.ck-on,\nhtml.${DARK_HTML_CLASS} #ticket-app .ck.ck-dropdown__panel .ck-list-item-button[aria-checked="true"] {\n background-color: #252b33 !important;\n color: #e4e7ed !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ck.ck-toolbar .ck-button:hover,\nhtml.${DARK_HTML_CLASS} #ticket-app .ck.ck-toolbar .ck-splitbutton__action:hover,\nhtml.${DARK_HTML_CLASS} #ticket-app .ck.ck-toolbar .ck-splitbutton__arrow:hover {\n background-color: #252b33 !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ck.ck-button.ck-source-editing-button,\nhtml.${DARK_HTML_CLASS} #ticket-app .ck.ck-button.ck-source-editing-button:hover,\nhtml.${DARK_HTML_CLASS} #ticket-app .ck.ck-button.ck-source-editing-button:focus,\nhtml.${DARK_HTML_CLASS} #ticket-app .ck.ck-button.ck-source-editing-button.ck-on,\nhtml.${DARK_HTML_CLASS} #ticket-app .ck.ck-button.ck-source-editing-button.ck-off {\n background-color: #252b33 !important;\n color: #c0c4cc !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .el-select-dropdown,\nhtml.${DARK_HTML_CLASS} #ticket-app .el-cascader__dropdown,\nhtml.${DARK_HTML_CLASS} #ticket-app .el-select-dropdown__item,\nhtml.${DARK_HTML_CLASS} #ticket-app .el-cascader-node {\n background-color: #1e2529 !important;\n color: #dcdfe6 !important;\n}\nhtml.${DARK_HTML_CLASS} .el-select-dropdown,\nhtml.${DARK_HTML_CLASS} .el-cascader__dropdown,\nhtml.${DARK_HTML_CLASS} .el-select-dropdown__item,\nhtml.${DARK_HTML_CLASS} .el-cascader-node {\n background-color: #1e2529 !important;\n color: #dcdfe6 !important;\n border-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .el-cascader__suggestion-panel,\nhtml.${DARK_HTML_CLASS} #ticket-app .el-cascader__suggestion-list,\nhtml.${DARK_HTML_CLASS} #ticket-app .el-cascader__suggestion-item,\nhtml.${DARK_HTML_CLASS} .el-cascader__suggestion-panel,\nhtml.${DARK_HTML_CLASS} .el-cascader__suggestion-list,\nhtml.${DARK_HTML_CLASS} .el-cascader__suggestion-item {\n background-color: #1e2529 !important;\n color: #dcdfe6 !important;\n border-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .el-cascader__suggestion-item:hover,\nhtml.${DARK_HTML_CLASS} #ticket-app .el-cascader__suggestion-item:focus,\nhtml.${DARK_HTML_CLASS} #ticket-app .el-cascader__suggestion-item.is-checked,\nhtml.${DARK_HTML_CLASS} #ticket-app .el-cascader__suggestion-item.is-active,\nhtml.${DARK_HTML_CLASS} .el-cascader__suggestion-item:hover,\nhtml.${DARK_HTML_CLASS} .el-cascader__suggestion-item:focus,\nhtml.${DARK_HTML_CLASS} .el-cascader__suggestion-item.is-checked,\nhtml.${DARK_HTML_CLASS} .el-cascader__suggestion-item.is-active {\n background-color: #252b33 !important;\n color: #e4e7ed !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .el-scrollbar__view.el-select-dropdown__list,\nhtml.${DARK_HTML_CLASS} #ticket-app .el-scrollbar__view.el-cascader-menu__list,\nhtml.${DARK_HTML_CLASS} #ticket-app .el-cascader-menu {\n background-color: #1e2529 !important;\n color: #dcdfe6 !important;\n border-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} .el-scrollbar__view.el-select-dropdown__list,\nhtml.${DARK_HTML_CLASS} .el-scrollbar__view.el-cascader-menu__list,\nhtml.${DARK_HTML_CLASS} .el-cascader-menu {\n background-color: #1e2529 !important;\n color: #dcdfe6 !important;\n border-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .el-select-dropdown__item:hover,\nhtml.${DARK_HTML_CLASS} #ticket-app .el-select-dropdown__item.hover,\nhtml.${DARK_HTML_CLASS} #ticket-app .el-select-dropdown__item.selected,\nhtml.${DARK_HTML_CLASS} #ticket-app .el-select-dropdown__item.is-selected,\nhtml.${DARK_HTML_CLASS} .el-select-dropdown__item:hover,\nhtml.${DARK_HTML_CLASS} .el-select-dropdown__item.hover,\nhtml.${DARK_HTML_CLASS} .el-select-dropdown__item.selected,\nhtml.${DARK_HTML_CLASS} .el-select-dropdown__item.is-selected {\n background-color: #2d333b !important;\n color: #e4e7ed !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-fields__field-input .el-select-dropdown__item,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-fields__field-input .el-select-dropdown__item > *,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-fields__field-input .el-cascader-menu__list .el-cascader-node,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-fields__field-input .el-cascader-menu__list .el-cascader-node > * {\n background-color: transparent !important;\n color: #dcdfe6 !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-fields__field-input .el-select-dropdown__item:hover,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-fields__field-input .el-select-dropdown__item.hover,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-fields__field-input .el-select-dropdown__item.selected,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-fields__field-input .el-select-dropdown__item.is-selected,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-fields__field-input .el-select-dropdown__item:hover > *,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-fields__field-input .el-select-dropdown__item.hover > *,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-fields__field-input .el-select-dropdown__item.selected > *,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-fields__field-input .el-select-dropdown__item.is-selected > *,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-fields__field-input .el-cascader-node:hover,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-fields__field-input .el-cascader-node.in-active-path,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-fields__field-input .el-cascader-node.is-active {\n background-color: #252b33 !important;\n color: #e4e7ed !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-fields__field-input .el-select-dropdown__item span[style*="color: rgb(0, 0, 0)"],\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-fields__field-input .el-select-dropdown__item span[style*="color:#000"],\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-fields__field-input .el-select-dropdown__item span[style*="color: #000000"] {\n color: #e4e7ed !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .el-form-item__label {\n color: #a8abb2 !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-list .common-value,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-sidebar__filter-name,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-list .ticket-list-column-title__container .ticket-list-column-title__icon,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-list .ticket-list-column-title__post-count,\nhtml.${DARK_HTML_CLASS} #ticket-app .user-color,\nhtml.${DARK_HTML_CLASS} #ticket-app .user-color[data-v-e3f175be] {\n color: #c0c4cc !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-list .ticket-list-popover__watching-btn {\n color: #c0c4cc !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-list .ticket-list-column-title__info-button button,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-list .ticket-list-column-title__info-button button i {\n color: #c0c4cc !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-list .ticket-list_ticket-type-icon,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-list .ticket-list-column-title__new-window button,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-list .ticket-list-column-title__new-window button i,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-list .ticket-list-column-title__new-window .icon-new-tab {\n color: #c0c4cc !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-sidebar__filter.ticket-sidebar__filter_child {\n border-color: #3d4654 !important;\n border-radius: 14px !important;\n overflow: hidden !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-sidebar__filter.ticket-sidebar__filter_child:hover {\n background-color: #252b33 !important;\n border-radius: 14px !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-sidebar__filter-button.router-link-exact-active,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-sidebar__filter-button.active {\n background-color: #252b33 !important;\n border-radius: 14px !important;\n border: none !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-sidebar__filter-button:hover {\n border-radius: 14px !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-audit__item {\n background-color: #1e2529 !important;\n color: #dcdfe6 !important;\n border-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-audit__item:hover {\n background-color: #252b33 !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-audit__item::before,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-audit__item::after,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-audit__line,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-audit hr {\n border-color: #3d4654 !important;\n background-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .el-dialog {\n background: #1e2529 !important;\n border: 1px solid #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .el-dialog__title,\nhtml.${DARK_HTML_CLASS} #ticket-app .el-dialog__body {\n color: #dcdfe6 !important;\n}\nhtml.${DARK_HTML_CLASS} .el-dialog {\n background: #1e2529 !important;\n border: 1px solid #3d4654 !important;\n box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45) !important;\n}\nhtml.${DARK_HTML_CLASS} .el-dialog__header,\nhtml.${DARK_HTML_CLASS} .el-dialog__body,\nhtml.${DARK_HTML_CLASS} .el-dialog__footer {\n background-color: #1e2529 !important;\n border-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} .el-dialog__header {\n border-bottom: 1px solid #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} .el-dialog__footer {\n border-top: 1px solid #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} .el-dialog__title,\nhtml.${DARK_HTML_CLASS} .el-dialog__headerbtn .el-dialog__close,\nhtml.${DARK_HTML_CLASS} .el-dialog__body,\nhtml.${DARK_HTML_CLASS} .el-dialog__body * {\n color: #dcdfe6 !important;\n}\nhtml.${DARK_HTML_CLASS} .el-dialog .el-dialog__headerbtn:hover .el-dialog__close {\n color: #e4e7ed !important;\n}\nhtml.${DARK_HTML_CLASS} .el-dialog table,\nhtml.${DARK_HTML_CLASS} .el-dialog thead,\nhtml.${DARK_HTML_CLASS} .el-dialog tbody,\nhtml.${DARK_HTML_CLASS} .el-dialog tr,\nhtml.${DARK_HTML_CLASS} .el-dialog th,\nhtml.${DARK_HTML_CLASS} .el-dialog td {\n background-color: #1e2529 !important;\n border-color: #3d4654 !important;\n color: #dcdfe6 !important;\n}\nhtml.${DARK_HTML_CLASS} .el-dialog thead th,\nhtml.${DARK_HTML_CLASS} .el-dialog tr:first-child th,\nhtml.${DARK_HTML_CLASS} .el-dialog tr:first-child td {\n background-color: #252b33 !important;\n color: #c0c4cc !important;\n}\nhtml.${DARK_HTML_CLASS} .el-dialog tbody tr:hover,\nhtml.${DARK_HTML_CLASS} .el-dialog tbody tr:hover td {\n background-color: #252b33 !important;\n color: #e4e7ed !important;\n}\nhtml.${DARK_HTML_CLASS} .el-dialog input,\nhtml.${DARK_HTML_CLASS} .el-dialog .el-input__inner,\nhtml.${DARK_HTML_CLASS} .el-dialog .el-date-editor .el-input__inner {\n background-color: #252b33 !important;\n border-color: #3d4654 !important;\n color: #e4e7ed !important;\n}\nhtml.${DARK_HTML_CLASS} .staffs-contact-columns__content,\nhtml.${DARK_HTML_CLASS} .staffs-contact-columns__column,\nhtml.${DARK_HTML_CLASS} .sortable__columns {\n background-color: #1e2529 !important;\n border-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} .staffs-contact-columns__column_heading,\nhtml.${DARK_HTML_CLASS} .staffs-contact-columns__column_footer {\n background-color: #252b33 !important;\n color: #e4e7ed !important;\n}\nhtml.${DARK_HTML_CLASS} .staffs-contact-columns__order,\nhtml.${DARK_HTML_CLASS} .staffs-contact-columns__value,\nhtml.${DARK_HTML_CLASS} .staffs-contact-columns__width,\nhtml.${DARK_HTML_CLASS} .staffs-contact-columns__delete {\n border-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} .staffs-contact-columns__column .el-input-number,\nhtml.${DARK_HTML_CLASS} .staffs-contact-columns__column .el-input-number .el-input__inner,\nhtml.${DARK_HTML_CLASS} .staffs-contact-columns__column .el-input-number__decrease,\nhtml.${DARK_HTML_CLASS} .staffs-contact-columns__column .el-input-number__increase {\n background-color: #252b33 !important;\n border-color: #3d4654 !important;\n color: #dcdfe6 !important;\n}\nhtml.${DARK_HTML_CLASS} .ticket-group-actions,\nhtml.${DARK_HTML_CLASS} .ticket-group-actions .el-dialog__body,\nhtml.${DARK_HTML_CLASS} .ticket-group-actions__content,\nhtml.${DARK_HTML_CLASS} .ticket-group-actions__columns,\nhtml.${DARK_HTML_CLASS} .ticket-group-actions__column,\nhtml.${DARK_HTML_CLASS} .ticket-group-actions__footer,\nhtml.${DARK_HTML_CLASS} .ticket-group-actions__upload-file,\nhtml.${DARK_HTML_CLASS} .ticket-group-actions__send-mail,\nhtml.${DARK_HTML_CLASS} .ticket-group-actions__submit-button {\n background-color: #1e2529 !important;\n color: #dcdfe6 !important;\n border-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} .ticket-group-actions .ticket-group-actions__field-label,\nhtml.${DARK_HTML_CLASS} .ticket-group-actions .ticket-group-actions__field-name {\n color: #a8abb2 !important;\n}\nhtml.${DARK_HTML_CLASS} .ticket-group-actions .ticket-detail__tabs.el-tabs--card > .el-tabs__header,\nhtml.${DARK_HTML_CLASS} .ticket-group-actions .ticket-detail__tabs.el-tabs--card > .el-tabs__header .el-tabs__nav,\nhtml.${DARK_HTML_CLASS} .ticket-group-actions .ticket-detail__tabs.el-tabs--card > .el-tabs__header .el-tabs__item,\nhtml.${DARK_HTML_CLASS} .ticket-group-actions .ticket-detail__tabs .el-tabs__content,\nhtml.${DARK_HTML_CLASS} .ticket-group-actions .ticket-editor {\n background-color: #1e2529 !important;\n border-color: #3d4654 !important;\n color: #dcdfe6 !important;\n}\nhtml.${DARK_HTML_CLASS} .ticket-group-actions .ticket-detail__tabs.el-tabs--card > .el-tabs__header .el-tabs__item.is-active {\n background-color: #252b33 !important;\n color: #e4e7ed !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-list-columns .el-dialog {\n background-color: #1e2529 !important;\n border: 1px solid #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-list-columns .el-dialog__header,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-list-columns .el-dialog__body,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-list-columns .el-dialog__footer {\n background-color: #1e2529 !important;\n color: #dcdfe6 !important;\n border-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-list-columns .ticket-list-columns__column {\n background-color: #252b33 !important;\n color: #dcdfe6 !important;\n border-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-list-columns .ticket-list-columns__column_heading {\n background-color: transparent !important;\n color: #a8abb2 !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-list-columns .el-button.el-button--default {\n background-color: #2d333b !important;\n color: #dcdfe6 !important;\n border-color: #4a5563 !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-list-columns .el-button.el-button--default:hover {\n background-color: #343b45 !important;\n color: #e4e7ed !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-list-columns .el-button.el-button--primary {\n color: #0f1419 !important;\n}\nhtml.${DARK_HTML_CLASS} .ticket-filter-list,\nhtml.${DARK_HTML_CLASS} .ticket-filter-list__filter {\n background: #1e2529 !important;\n background-color: #1e2529 !important;\n color: #dcdfe6 !important;\n}\nhtml.${DARK_HTML_CLASS} .ticket-filter-list,\nhtml.${DARK_HTML_CLASS} .ticket-filter-list__filter,\nhtml.${DARK_HTML_CLASS} .ticket-filter-list__row,\nhtml.${DARK_HTML_CLASS} .ticket-filter-list__row_heading,\nhtml.${DARK_HTML_CLASS} .ticket-filter-list__col,\nhtml.${DARK_HTML_CLASS} .ticket-filter-list__col_name,\nhtml.${DARK_HTML_CLASS} .ticket-filter-list__col_filter-type,\nhtml.${DARK_HTML_CLASS} .ticket-filter-list__col_activity {\n border-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} .ticket-filter-list__row {\n background: #252b33 !important;\n background-color: #252b33 !important;\n color: #dcdfe6 !important;\n border-bottom-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} .ticket-filter-list__row_heading {\n background: #1e2529 !important;\n background-color: #1e2529 !important;\n color: #a8abb2 !important;\n border-bottom-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} .ticket-filter-list__row:hover {\n background: #2d333b !important;\n background-color: #2d333b !important;\n}\nhtml.${DARK_HTML_CLASS} .ticket-filter-list__col,\nhtml.${DARK_HTML_CLASS} .ticket-filter-list__col_name,\nhtml.${DARK_HTML_CLASS} .ticket-filter-list__col_filter-type,\nhtml.${DARK_HTML_CLASS} .ticket-filter-list__col_activity {\n color: #c0c4cc !important;\n}\nhtml.${DARK_HTML_CLASS} .ticket-filter-list .ticket-filter-list__col,\nhtml.${DARK_HTML_CLASS} .ticket-filter-list .ticket-filter-list__col * {\n background-color: transparent !important;\n}\nhtml.${DARK_HTML_CLASS} .ticket-filter-list .el-button,\nhtml.${DARK_HTML_CLASS} .ticket-filter-list button {\n background-color: #2d333b !important;\n color: #dcdfe6 !important;\n border-color: #4a5563 !important;\n}\nhtml.${DARK_HTML_CLASS} .ticket-filter-list .el-button:hover,\nhtml.${DARK_HTML_CLASS} .ticket-filter-list button:hover {\n background-color: #343b45 !important;\n color: #e4e7ed !important;\n}\nhtml.${DARK_HTML_CLASS} .ticket-filter-list .el-button--primary {\n color: #0f1419 !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket .ticket-user__field,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket .ticket-user__fields,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket .ticket-user__fields-heading,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-user__fields-heading {\n border-bottom: none !important;\n border-bottom-color: #1a1f26 !important;\n background-color: #1a1f26 !important;\n margin-bottom: 0 !important;\n padding-bottom: 0 !important;\n box-shadow: none !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-user__fields-heading::before,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-user__fields-heading::after {\n border-bottom: none !important;\n border-bottom-color: #1a1f26 !important;\n background-color: #1a1f26 !important;\n box-shadow: none !important;\n content: none !important;\n}\nhtml.${DARK_HTML_CLASS} .el-message-box {\n background: #1e2529 !important;\n border: 1px solid #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} .el-message-box__title,\nhtml.${DARK_HTML_CLASS} .el-message-box__message,\nhtml.${DARK_HTML_CLASS} .el-message-box__content,\nhtml.${DARK_HTML_CLASS} .el-message-box__headerbtn .el-message-box__close {\n color: #dcdfe6 !important;\n}\nhtml.${DARK_HTML_CLASS} .el-message-box__input input,\nhtml.${DARK_HTML_CLASS} .el-message-box__input textarea {\n background-color: #252b33 !important;\n color: #e4e7ed !important;\n border-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} .el-message-box .el-button.el-button--default,\nhtml.${DARK_HTML_CLASS} .el-button.el-button--default.el-button--small {\n background-color: #252b33 !important;\n color: #dcdfe6 !important;\n border-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} .el-message-box .el-button.el-button--default:hover,\nhtml.${DARK_HTML_CLASS} .el-button.el-button--default.el-button--small:hover {\n background-color: #2d333b !important;\n color: #e4e7ed !important;\n border-color: #4a5563 !important;\n}\nhtml.${DARK_HTML_CLASS} .el-tag.el-tag--info,\nhtml.${DARK_HTML_CLASS} .el-tag.el-tag--info.el-tag--mini,\nhtml.${DARK_HTML_CLASS} .el-tag.el-tag--info.el-tag--mini.el-tag--light {\n background-color: #252b33 !important;\n color: #c0c4cc !important;\n border-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} .el-picker-panel,\nhtml.${DARK_HTML_CLASS} .el-date-picker,\nhtml.${DARK_HTML_CLASS} .el-picker-panel__sidebar,\nhtml.${DARK_HTML_CLASS} .el-date-picker__time-header,\nhtml.${DARK_HTML_CLASS} .el-time-panel {\n background-color: #1e2529 !important;\n color: #dcdfe6 !important;\n border-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} .el-picker-panel [class*="time"],\nhtml.${DARK_HTML_CLASS} .el-picker-panel [class*="date"],\nhtml.${DARK_HTML_CLASS} .el-picker-panel__content,\nhtml.${DARK_HTML_CLASS} .el-date-table td,\nhtml.${DARK_HTML_CLASS} .el-time-spinner__item {\n color: #dcdfe6 !important;\n}\nhtml.${DARK_HTML_CLASS} .el-picker-panel__footer {\n background-color: #1e2529 !important;\n border-top-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} .el-picker-panel__footer .el-button {\n background-color: #252b33 !important;\n color: #c0c4cc !important;\n border-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} .el-picker-panel__footer .el-button.el-button--text {\n background-color: transparent !important;\n color: #c0c4cc !important;\n}\nhtml.${DARK_HTML_CLASS} .el-picker-panel__footer .el-button:hover {\n background-color: #2d333b !important;\n color: #e4e7ed !important;\n}\nhtml.${DARK_HTML_CLASS} .el-time-spinner__item:hover,\nhtml.${DARK_HTML_CLASS} .el-date-table td.available:hover {\n background-color: #2d333b !important;\n color: #e4e7ed !important;\n}\nhtml.${DARK_HTML_CLASS} .profile,\nhtml.${DARK_HTML_CLASS} .sidebar {\n background-color: #1a1f26 !important;\n color: #dcdfe6 !important;\n border-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} .profile * ,\nhtml.${DARK_HTML_CLASS} .sidebar * {\n color: inherit;\n}\nhtml.${DARK_HTML_CLASS} .el-plus-input__inner {\n background-color: #252b33 !important;\n color: #e4e7ed !important;\n border-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} .el-plus-button,\nhtml.${DARK_HTML_CLASS} .el-plus-radio-button__inner {\n border-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} .el-plus-button:not(.el-plus-button--primary):not(.el-plus-button--danger) {\n background-color: #252b33 !important;\n color: #dcdfe6 !important;\n}\nhtml.${DARK_HTML_CLASS} .el-plus-radio-button__inner {\n background-color: #252b33 !important;\n color: #dcdfe6 !important;\n}\nhtml.${DARK_HTML_CLASS} .el-plus-button.el-plus-button--primary,\nhtml.${DARK_HTML_CLASS} .el-plus-button.el-plus-button--danger,\nhtml.${DARK_HTML_CLASS} .el-plus-radio-button.is-active .el-plus-radio-button__inner,\nhtml.${DARK_HTML_CLASS} .el-plus-radio-button__orig-radio:checked + .el-plus-radio-button__inner {\n color: #121418 !important;\n font-weight: 600 !important;\n}\nhtml.${DARK_HTML_CLASS} .el-popover,\nhtml.${DARK_HTML_CLASS} .el-popper,\nhtml.${DARK_HTML_CLASS} .el-popconfirm {\n background-color: #1e2529 !important;\n color: #dcdfe6 !important;\n border-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} .el-popover[x-placement^="top"] .popper__arrow,\nhtml.${DARK_HTML_CLASS} .el-popover[x-placement^="bottom"] .popper__arrow,\nhtml.${DARK_HTML_CLASS} .el-popover[x-placement^="left"] .popper__arrow,\nhtml.${DARK_HTML_CLASS} .el-popover[x-placement^="right"] .popper__arrow {\n border-top-color: #3d4654 !important;\n border-bottom-color: #3d4654 !important;\n border-left-color: #3d4654 !important;\n border-right-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} .el-popover .popper__arrow::after {\n border-top-color: #1e2529 !important;\n border-bottom-color: #1e2529 !important;\n border-left-color: #1e2529 !important;\n border-right-color: #1e2529 !important;\n}\nhtml.${DARK_HTML_CLASS} .el-popover[data-popper-placement^="top"] .popper__arrow,\nhtml.${DARK_HTML_CLASS} .el-popover[data-popper-placement^="bottom"] .popper__arrow,\nhtml.${DARK_HTML_CLASS} .el-popover[data-popper-placement^="left"] .popper__arrow,\nhtml.${DARK_HTML_CLASS} .el-popover[data-popper-placement^="right"] .popper__arrow,\nhtml.${DARK_HTML_CLASS} .el-popper[data-popper-placement^="top"] .popper__arrow,\nhtml.${DARK_HTML_CLASS} .el-popper[data-popper-placement^="bottom"] .popper__arrow,\nhtml.${DARK_HTML_CLASS} .el-popper[data-popper-placement^="left"] .popper__arrow,\nhtml.${DARK_HTML_CLASS} .el-popper[data-popper-placement^="right"] .popper__arrow {\n border-top-color: #3d4654 !important;\n border-bottom-color: #3d4654 !important;\n border-left-color: #3d4654 !important;\n border-right-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} .el-popper[data-popper-placement] .popper__arrow::after {\n border-top-color: #1e2529 !important;\n border-bottom-color: #1e2529 !important;\n border-left-color: #1e2529 !important;\n border-right-color: #1e2529 !important;\n}\nhtml.${DARK_HTML_CLASS} .ticket-tabs__close-all-popper,\nhtml.${DARK_HTML_CLASS} .ticket-tabs__close-all-popper .el-popconfirm,\nhtml.${DARK_HTML_CLASS} .ticket-tabs__close-all-popper .el-popconfirm__action {\n background-color: #1e2529 !important;\n border-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} .ticket-tabs__close-all-popper .el-popconfirm__action {\n border-top: 1px solid #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} .el-popconfirm__main,\nhtml.${DARK_HTML_CLASS} .el-popconfirm__main *,\nhtml.${DARK_HTML_CLASS} .el-popover *,\nhtml.${DARK_HTML_CLASS} .el-popper * {\n color: #dcdfe6 !important;\n}\nhtml.${DARK_HTML_CLASS} .el-upload-dragger {\n background-color: #1e2529 !important;\n color: #c0c4cc !important;\n border: 1px dashed #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} .el-upload-dragger:hover {\n background-color: #252b33 !important;\n border-color: #4a5563 !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .el-card {\n background: #1a1f26 !important;\n border-color: #3d4654 !important;\n color: #dcdfe6 !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .el-tabs__item {\n color: #a8abb2 !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .el-tabs__item.is-active {\n color: #7ec8e0 !important;\n}\nhtml.${DARK_HTML_CLASS} .el-tabs__item {\n color: #a8abb2 !important;\n opacity: 1 !important;\n -webkit-text-fill-color: #a8abb2 !important;\n}\nhtml.${DARK_HTML_CLASS} .el-tabs__item.is-active {\n color: #e4e7ed !important;\n opacity: 1 !important;\n -webkit-text-fill-color: #e4e7ed !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-detail__tabs.el-tabs--card > .el-tabs__header,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-detail__tabs.el-tabs--card > .el-tabs__header .el-tabs__nav,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-detail__tabs.el-tabs--card > .el-tabs__header .el-tabs__item {\n border-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-detail__tabs.el-tabs--card > .el-tabs__header .el-tabs__item {\n background-color: #1e2529 !important;\n color: #c0c4cc !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-detail__tabs.el-tabs--card > .el-tabs__header .el-tabs__item.is-active {\n background-color: #252b33 !important;\n color: #e4e7ed !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-detail__tabs .el-tabs__nav-wrap::after {\n background-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .el-pagination,\nhtml.${DARK_HTML_CLASS} #ticket-app .el-pagination button,\nhtml.${DARK_HTML_CLASS} #ticket-app .el-pager li {\n color: #c0c4cc !important;\n background: transparent !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .el-loading-mask {\n background-color: rgba(18, 20, 24, 0.85) !important;\n}\nhtml.${DARK_HTML_CLASS} .dashboard,\nhtml.${DARK_HTML_CLASS} .dashboard__header,\nhtml.${DARK_HTML_CLASS} .dashboard-tabs,\nhtml.${DARK_HTML_CLASS} .dashboard-overview,\nhtml.${DARK_HTML_CLASS} .dashboard-overview__ticket-stats,\nhtml.${DARK_HTML_CLASS} .dashboard-overview__filters,\nhtml.${DARK_HTML_CLASS} .dashboard-overview__tickets-by-channels,\nhtml.${DARK_HTML_CLASS} .dashboard-overview__chart,\nhtml.${DARK_HTML_CLASS} .dashboard-overview__ticket-stats-chart {\n background-color: #121418 !important;\n color: #dcdfe6 !important;\n border-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} .dashboard .el-tabs__header,\nhtml.${DARK_HTML_CLASS} .dashboard .el-tabs__nav-wrap::after,\nhtml.${DARK_HTML_CLASS} .dashboard .el-tabs__item {\n border-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} .dashboard .el-tabs__item {\n color: #c0c4cc !important;\n}\nhtml.${DARK_HTML_CLASS} .dashboard .el-tabs__item.is-active {\n color: #e4e7ed !important;\n}\nhtml.${DARK_HTML_CLASS} .dashboard .el-input__inner,\nhtml.${DARK_HTML_CLASS} .dashboard .el-select .el-input__inner,\nhtml.${DARK_HTML_CLASS} .dashboard .el-select__tags,\nhtml.${DARK_HTML_CLASS} .dashboard .el-select-dropdown,\nhtml.${DARK_HTML_CLASS} .dashboard .el-select-dropdown__item,\nhtml.${DARK_HTML_CLASS} .dashboard .el-scrollbar__view.el-select-dropdown__list {\n background-color: #1e2529 !important;\n color: #dcdfe6 !important;\n border-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} .dashboard .el-select-dropdown__item.hover,\nhtml.${DARK_HTML_CLASS} .dashboard .el-select-dropdown__item:hover,\nhtml.${DARK_HTML_CLASS} .dashboard .el-select-dropdown__item.selected,\nhtml.${DARK_HTML_CLASS} .dashboard .el-select-dropdown__item.is-selected {\n background-color: #252b33 !important;\n color: #e4e7ed !important;\n}\nhtml.${DARK_HTML_CLASS} .dashboard .dashboard-staffs__header,\nhtml.${DARK_HTML_CLASS} .dashboard .dashboard-staffs__content,\nhtml.${DARK_HTML_CLASS} .dashboard .dashboard-staffs__row,\nhtml.${DARK_HTML_CLASS} .dashboard .dashboard-staffs__column {\n background-color: #121418 !important;\n color: #c0c4cc !important;\n border-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} .dashboard .dashboard-staffs__content .dashboard-staffs__row:hover,\nhtml.${DARK_HTML_CLASS} .dashboard .dashboard-staffs__content .dashboard-staffs__row:focus,\nhtml.${DARK_HTML_CLASS} .dashboard .dashboard-staffs__content .dashboard-staffs__row:focus-within {\n background-color: #252b33 !important;\n}\nhtml.${DARK_HTML_CLASS} .dashboard .dashboard-staffs__content .dashboard-staffs__row:hover .dashboard-staffs__column,\nhtml.${DARK_HTML_CLASS} .dashboard .dashboard-staffs__content .dashboard-staffs__row:focus .dashboard-staffs__column,\nhtml.${DARK_HTML_CLASS} .dashboard .dashboard-staffs__content .dashboard-staffs__row:focus-within .dashboard-staffs__column {\n background-color: transparent !important;\n color: #e4e7ed !important;\n}\nhtml.${DARK_HTML_CLASS} .dashboard .dashboard-staffs__content .dashboard-staffs__row:hover .el-button--text,\nhtml.${DARK_HTML_CLASS} .dashboard .dashboard-staffs__content .dashboard-staffs__row:hover .el-button--text span,\nhtml.${DARK_HTML_CLASS} .dashboard .dashboard-staffs__content .dashboard-staffs__row:hover .el-button--text i {\n color: #9fd3e4 !important;\n}\nhtml.${DARK_HTML_CLASS} .dashboard .dashboard-overview__ticket-stats-col[style] {\n background: #1e2529 !important;\n border-color: #3d4654 !important;\n color: #e4e7ed !important;\n}\nhtml.${DARK_HTML_CLASS} .dashboard .dashboard-overview__ticket-stats-col[style*="211, 22, 22"] {\n background: #8f4a4a !important;\n border-color: #8f4a4a !important;\n color: #f2dede !important;\n}\nhtml.${DARK_HTML_CLASS} .dashboard .dashboard-overview__ticket-stats-label,\nhtml.${DARK_HTML_CLASS} .dashboard .dashboard-overview__ticket-stats-value,\nhtml.${DARK_HTML_CLASS} .dashboard [class*="dashboard-overview"] {\n color: #c0c4cc !important;\n}\nhtml.${DARK_HTML_CLASS} .dashboard [style*="z-index: 9999999"][style*="background-color: rgb(255, 255, 255)"] {\n background-color: #1e2529 !important;\n border-color: #3d4654 !important;\n color: #dcdfe6 !important;\n box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45) !important;\n}\nhtml.${DARK_HTML_CLASS} .dashboard [style*="z-index: 9999999"][style*="background-color: rgb(255, 255, 255)"] * {\n color: #dcdfe6 !important;\n}\nhtml.${DARK_HTML_CLASS} .dashboard canvas {\n filter: saturate(0.82) brightness(0.92);\n}\nhtml.${DARK_HTML_CLASS} .contact-users,\nhtml.${DARK_HTML_CLASS} .contact-users__filters,\nhtml.${DARK_HTML_CLASS} .contacts__content,\nhtml.${DARK_HTML_CLASS} .contacts__row,\nhtml.${DARK_HTML_CLASS} .contacts__column,\nhtml.${DARK_HTML_CLASS} .contacts__row_header,\nhtml.${DARK_HTML_CLASS} .pagination,\nhtml.${DARK_HTML_CLASS} .pagination__total {\n background-color: #121418 !important;\n color: #dcdfe6 !important;\n border-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} .contact-users .el-tabs__header,\nhtml.${DARK_HTML_CLASS} .contact-users .el-tabs__nav-wrap::after,\nhtml.${DARK_HTML_CLASS} .contact-users .el-tabs__item {\n border-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} .contact-users .el-tabs__item {\n color: #a8abb2 !important;\n}\nhtml.${DARK_HTML_CLASS} .contact-users .el-tabs__item.is-active {\n color: #e4e7ed !important;\n}\nhtml.${DARK_HTML_CLASS} .contact-users .el-tabs__nav .el-tabs__item.is-top,\nhtml.${DARK_HTML_CLASS} .contact-users .el-tabs__nav .el-tabs__item[id^="tab-"] {\n color: #a8abb2 !important;\n opacity: 1 !important;\n -webkit-text-fill-color: #a8abb2 !important;\n}\nhtml.${DARK_HTML_CLASS} .contact-users .el-tabs__nav .el-tabs__item.is-top.is-active,\nhtml.${DARK_HTML_CLASS} .contact-users .el-tabs__nav .el-tabs__item[id^="tab-"].is-active {\n color: #e4e7ed !important;\n opacity: 1 !important;\n -webkit-text-fill-color: #e4e7ed !important;\n}\nhtml.${DARK_HTML_CLASS} .contact-users #tab-users,\nhtml.${DARK_HTML_CLASS} .contact-users #tab-companies,\nhtml.${DARK_HTML_CLASS} .contact-users #tab-equipments {\n color: #a8abb2 !important;\n opacity: 1 !important;\n -webkit-text-fill-color: #a8abb2 !important;\n}\nhtml.${DARK_HTML_CLASS} .contact-users #tab-users.is-active,\nhtml.${DARK_HTML_CLASS} .contact-users #tab-companies.is-active,\nhtml.${DARK_HTML_CLASS} .contact-users #tab-equipments.is-active {\n color: #e4e7ed !important;\n opacity: 1 !important;\n -webkit-text-fill-color: #e4e7ed !important;\n}\nhtml.${DARK_HTML_CLASS} .contact-users__filters,\nhtml.${DARK_HTML_CLASS} .contact-users__filters .el-row,\nhtml.${DARK_HTML_CLASS} .contact-users__filters [class*="tabs"],\nhtml.${DARK_HTML_CLASS} .contact-users__filters [class*="tabs"] * {\n border-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} .contacts__row_header,\nhtml.${DARK_HTML_CLASS} .contacts__row {\n border-color: #2d333b !important;\n}\nhtml.${DARK_HTML_CLASS} .contacts__row:hover {\n background-color: #1e2529 !important;\n}\nhtml.${DARK_HTML_CLASS} .contacts__column .el-button--text,\nhtml.${DARK_HTML_CLASS} .contacts__column .el-button--text span,\nhtml.${DARK_HTML_CLASS} .contacts__column .el-button--text i {\n color: #c0c4cc !important;\n}\nhtml.${DARK_HTML_CLASS} .contacts__column .el-button--text:hover,\nhtml.${DARK_HTML_CLASS} .contacts__column .el-button--text:hover span,\nhtml.${DARK_HTML_CLASS} .contacts__column .el-button--text:hover i {\n color: #e4e7ed !important;\n}\nhtml.${DARK_HTML_CLASS} .contact-users .el-switch__core {\n background-color: #252b33 !important;\n border-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} .contact-users .el-switch.is-checked .el-switch__core {\n background-color: #23869b !important;\n border-color: #2da1b8 !important;\n}\nhtml.${DARK_HTML_CLASS} .contact-users .el-pagination button,\nhtml.${DARK_HTML_CLASS} .contact-users .el-pager li {\n background-color: transparent !important;\n color: #c0c4cc !important;\n}\nhtml.${DARK_HTML_CLASS} .contact-users .el-pager li.active {\n color: #e4e7ed !important;\n border-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-editor__ckeditor .ck-content,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-editor__ckeditor_comment .ck-content {\n background-color: #252b33 !important;\n color: #e4e7ed !important;\n border-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-cc__copy-append,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-cc__copy-prepend {\n background-color: #252b33 !important;\n border-color: #3d4654 !important;\n color: #a8abb2 !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .el-button.ticket-editor__add-files-button,\nhtml.${DARK_HTML_CLASS} #ticket-app .el-button.ticket-editor__add-files-button.el-button--text {\n background-color: #1e2529 !important;\n color: #c0c4cc !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .el-button.ticket-editor__add-files-button:hover {\n background-color: #2d333b !important;\n color: #e4e7ed !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-conversation__messages {\n background-color: #1a1f26 !important;\n}\nhtml.${DARK_HTML_CLASS} .${DATE_SEP_CLASS} {\n color: #8b9199 !important;\n}\nhtml.${DARK_HTML_CLASS} .${DATE_SEP_CLASS}::before,\nhtml.${DARK_HTML_CLASS} .${DATE_SEP_CLASS}::after {\n background: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} .${TICKET_SEP_CLASS} {\n color: #a8abb2 !important;\n}\nhtml.${DARK_HTML_CLASS} .${TICKET_SEP_CLASS}::after {\n background: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} .ticket-conversation__message_user .hde-name-inside {\n color: #7ec8e0 !important;\n}\nhtml.${DARK_HTML_CLASS} .ticket-conversation__message_user .hde-time-inside {\n color: #c0c4cc !important;\n opacity: 0.75 !important;\n}\nhtml.${DARK_HTML_CLASS} .hde-tools-panel {\n background: #1e2529 !important;\n color: #e4e7ed !important;\n box-shadow: 0 4px 20px rgba(0,0,0,0.55) !important;\n border: 1px solid #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} .hde-tools-panel h3 {\n color: #e4e7ed !important;\n border-bottom-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} .hde-tools-field input[type="number"],\nhtml.${DARK_HTML_CLASS} .hde-tools-field select {\n background: #252b33 !important;\n color: #e4e7ed !important;\n border-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} .hde-staffs-drawer__overlay {\n background: rgba(15, 20, 25, 0.65) !important;\n}\nhtml.${DARK_HTML_CLASS} .hde-staffs-drawer__panel {\n background: #1e2529 !important;\n border-left-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} .hde-staffs-drawer__header {\n color: #e4e7ed !important;\n border-bottom-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} .hde-staffs-drawer__close {\n color: #c0c4cc !important;\n}\nhtml.${DARK_HTML_CLASS} .hde-staffs-drawer__close:hover {\n color: #e4e7ed !important;\n}\nhtml.${DARK_HTML_CLASS} .hde-staffs-drawer__frame {\n background: #1e2529 !important;\n}\nhtml.${DARK_HTML_CLASS} .hde-tools-link-btn {\n color: #c0c4cc !important;\n border: none !important;\n background: transparent !important;\n}\nhtml.${DARK_HTML_CLASS} .hde-tools-link-btn:hover {\n color: #e4e7ed !important;\n}\nhtml.${DARK_HTML_CLASS} .hde-history-close {\n background: #252b33 !important;\n color: #c0c4cc !important;\n border-bottom-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-detail__history-tickets,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-detail__history-container,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-detail__history-ticket {\n background-color: #1e2529 !important;\n color: #dcdfe6 !important;\n border-color: #3d4654 !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-detail__history-ticket_current {\n background-color: #252b33 !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-detail__history-ticket a,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-detail__history-ticket i,\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-detail__history-ticket span {\n color: #c0c4cc !important;\n}\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-detail__history-ticket [style*="color: #000000"],\nhtml.${DARK_HTML_CLASS} #ticket-app .ticket-detail__history-ticket [style*="color:#000000"] {\n color: #e4e7ed !important;\n}\n`;
const DARK_THEME_CSS = [ DARK_THEME_CSS_BASE, DARK_THEME_CSS_COMPONENTS ].join("\n\n");
function escapeRegExp(value) {
return String(value).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
function remapCssColors(cssText, paletteMap) {
const entries = Object.entries(paletteMap).sort((a, b) => b[0].length - a[0].length);
let mapped = cssText;
entries.forEach(([from, to]) => {
mapped = mapped.replace(new RegExp(escapeRegExp(from), "gi"), to);
});
return mapped;
}
function buildPinkThemeCssFromDark(darkCssText) {
const pinkPaletteMap = {
"#121418": "#fff6fa",
"#161a1f": "#fdeff5",
"#1a1f26": "#fbe9f2",
"#1e2529": "#f7e1ec",
"#1f2a32": "#f5dbe8",
"#252b33": "#f1d3e2",
"#2a3038": "#edcade",
"#2d333b": "#e8c1d7",
"#343b45": "#e3b8d0",
"#3d4654": "#d2a9bf",
"#4a5563": "#c699b1",
"#154a56": "#c08aa8",
"#1a5f6e": "#b97aa0",
"#5eb3c9": "#be6f9d",
"#7ec8e0": "#c57bab",
"#a6dff0": "#d58fbb",
"#23869b": "#b56794",
"#409eff": "#bf73a0",
"#0f1419": "#5f4352",
"#dcdfe6": "#6f4d60",
"#e4e7ed": "#5f4352",
"#e8eaed": "#674b5b",
"#f0f2f5": "#604453",
"#c0c4cc": "#7a5a6b",
"#a8abb2": "#8c6a7c",
"#e8f4f7": "#6a4e5d",
"#f0fafc": "#67495a",
"#e2e8f0": "#6f5162",
"#f5f7fa": "#f7e6ee",
"#ecf5ff": "#f3dbe7",
"#ebeef5": "#e8c8d9",
"#8f4a4a": "#b9748f",
"#000000": "#5f4352",
"#ffffff": "#5f4352",
"#fff": "#5f4352"
};
const prefixed = darkCssText.replaceAll(`html.${DARK_HTML_CLASS}`, `html.${PINK_HTML_CLASS}`);
return remapCssColors(prefixed, pinkPaletteMap);
}
function buildMidnightBlueThemeCssFromDark(darkCssText) {
const midnightPaletteMap = {
"#121418": "#0b1220",
"#161a1f": "#0f1728",
"#1a1f26": "#111c2f",
"#1e2529": "#15223a",
"#1f2a32": "#172843",
"#252b33": "#1b2d4a",
"#2a3038": "#203255",
"#2d333b": "#23395f",
"#343b45": "#29446f",
"#3d4654": "#33517f",
"#4a5563": "#3f6292",
"#154a56": "#1d416f",
"#1a5f6e": "#245082",
"#5eb3c9": "#74c2ff",
"#7ec8e0": "#8dd0ff",
"#a6dff0": "#b0e2ff",
"#23869b": "#4ea4df",
"#409eff": "#63b4ff",
"#0f1419": "#08101b",
"#dcdfe6": "#cfddf5",
"#e4e7ed": "#d8e4f8",
"#e8eaed": "#e2ebfa",
"#f0f2f5": "#edf4ff",
"#c0c4cc": "#a8b8d6",
"#a8abb2": "#94a4c2",
"#e8f4f7": "#deefff",
"#f0fafc": "#e8f6ff",
"#e2e8f0": "#d5e2f6",
"#f5f7fa": "#f3f8ff",
"#ecf5ff": "#e6f2ff",
"#ebeef5": "#e4ecfa",
"#8f4a4a": "#b87f7f",
"#000000": "#08101b",
"#ffffff": "#e2ebfa",
"#fff": "#e2ebfa"
};
const prefixed = darkCssText.replaceAll(`html.${DARK_HTML_CLASS}`, `html.${MIDNIGHT_BLUE_HTML_CLASS}`);
return remapCssColors(prefixed, midnightPaletteMap);
}
function buildTricolorThemeCssFromDark(darkCssText) {
const tricolorPaletteMap = {
"#121418": "#ffffff",
"#161a1f": "#f9fbff",
"#1a1f26": "#f6f9ff",
"#1e2529": "#f3f6fb",
"#1f2a32": "#f0f4fa",
"#252b33": "#eef2f8",
"#2a3038": "#e7edf6",
"#2d333b": "#e2e9f4",
"#343b45": "#d8e1ef",
"#3d4654": "#c3cfdf",
"#4a5563": "#acbdd4",
"#154a56": "#b63b47",
"#1a5f6e": "#c94956",
"#5eb3c9": "#d85a66",
"#7ec8e0": "#e06e79",
"#a6dff0": "#ea8992",
"#23869b": "#bf4854",
"#409eff": "#245fb3",
"#0f1419": "#1d3f73",
"#dcdfe6": "#294d82",
"#e4e7ed": "#1d3f73",
"#e8eaed": "#234678",
"#f0f2f5": "#1f4578",
"#c0c4cc": "#4d6793",
"#a8abb2": "#6379a1",
"#e8f4f7": "#23497d",
"#f0fafc": "#20477a",
"#e2e8f0": "#2f5386",
"#f5f7fa": "#ffffff",
"#ecf5ff": "#f4f7ff",
"#ebeef5": "#e7effc",
"#8f4a4a": "#c53b49",
"#000000": "#1d3f73",
"#ffffff": "#1d3f73",
"#fff": "#1d3f73"
};
const prefixed = darkCssText.replaceAll(`html.${DARK_HTML_CLASS}`, `html.${TRICOLOR_HTML_CLASS}`);
return remapCssColors(prefixed, tricolorPaletteMap);
}
function buildMonochromeThemeCssFromDark(darkCssText) {
const monochromePaletteMap = {
"#121418": "#0c0c0c",
"#161a1f": "#101010",
"#1a1f26": "#131313",
"#1e2529": "#171717",
"#1f2a32": "#1b1b1b",
"#252b33": "#212121",
"#2a3038": "#252525",
"#2d333b": "#292929",
"#343b45": "#303030",
"#3d4654": "#3a3a3a",
"#4a5563": "#474747",
"#154a56": "#525252",
"#1a5f6e": "#5f5f5f",
"#5eb3c9": "#808080",
"#7ec8e0": "#9a9a9a",
"#a6dff0": "#b3b3b3",
"#c0c4cc": "#c8c8c8",
"#dcdfe6": "#e5e5e5",
"#e4e7ed": "#f1f1f1",
"#ecf5ff": "#f6f6f6",
"#409eff": "#8b8b8b",
"#337ecc": "#767676",
"#67c23a": "#9f9f9f",
"#e6a23c": "#8b8b8b",
"#f56c6c": "#a9a9a9",
"#ff8f8f": "#c0c0c0",
"#87bdf2": "#9d9d9d"
};
const prefixed = darkCssText.replaceAll(`html.${DARK_HTML_CLASS}`, `html.${MONOCHROME_HTML_CLASS}`);
return remapCssColors(prefixed, monochromePaletteMap);
}
const PINK_THEME_CSS = buildPinkThemeCssFromDark(DARK_THEME_CSS);
const MIDNIGHT_BLUE_THEME_CSS = buildMidnightBlueThemeCssFromDark(DARK_THEME_CSS);
const TRICOLOR_THEME_CSS = buildTricolorThemeCssFromDark(DARK_THEME_CSS);
const MONOCHROME_THEME_CSS = buildMonochromeThemeCssFromDark(DARK_THEME_CSS);
const THEME_CSS = [ DARK_THEME_CSS, PINK_THEME_CSS, MIDNIGHT_BLUE_THEME_CSS, TRICOLOR_THEME_CSS, MONOCHROME_THEME_CSS ].join("\n\n");
function injectThemeStyles() {
const legacyStyle = document.getElementById(LEGACY_THEME_STYLE_ID);
if (legacyStyle) legacyStyle.remove();
const bootStyle = document.getElementById(THEME_BOOT_STYLE_ID);
if (document.getElementById(THEME_STYLE_ID)) {
if (bootStyle) bootStyle.remove();
return;
}
const style = document.createElement("style");
style.id = THEME_STYLE_ID;
style.textContent = THEME_CSS;
const target = document.head || document.documentElement;
if (!target) return;
target.appendChild(style);
if (bootStyle) bootStyle.remove();
}
injectThemeStyles();
function applyThemeFromConfig() {
injectThemeStyles();
CONFIG.themeMode = normalizeThemeMode(CONFIG.themeMode);
applyThemeClass(CONFIG.themeMode);
}
const CSS = `\n /* ── Layout ── */\n .ticket-conversation__message-block {\n display: flex !important; flex-direction: row !important;\n align-items: flex-start !important; gap: 8px !important;\n flex: 1 !important; overflow: visible !important;\n margin-left: 0px !important;\n }\n .ticket-conversation__message_user > .ticket-conversation__message-block,\n .ticket-conversation__message_comment > .ticket-conversation__message-block {\n justify-content: flex-start !important;\n }\n .ticket-conversation__message_staff > .ticket-conversation__message-block {\n justify-content: flex-end !important;\n }\n\n /* Avatar */\n .ticket-conversation__message-image {\n width: 30px !important; height: 30px !important;\n padding: 2px !important; border-radius: 50% !important;\n }\n .ticket-conversation__message-image_user,\n .ticket-conversation__message-image_staff { margin-top: 0 !important; }\n\n /* Bubble / text */\n .ticket-conversation__message-text {\n font-size: 13px !important; margin: 0 !important; padding: 0 !important;\n }\n .ticket-conversation__message-html {\n padding: 6px 10px !important; min-height: auto !important;\n position: relative !important;\n border-radius: 12px !important;\n }\n /* Системные сообщения (уходят клиенту) */\n #ticket-app .ticket .ticket-conversation__message-text.ticket-conversation__message-text_staff.ticket-conversation__message-text_system > .ticket-conversation__message-html {\n background: #4a5563 !important;\n color: #f0f2f5 !important;\n border: 1px solid rgba(0, 0, 0, 0.08) !important;\n border-radius: 12px !important;\n overflow: hidden !important;\n }\n /* Системные комментарии (внутренние) */\n #ticket-app .ticket .ticket-conversation__message-text.ticket-conversation__message-text_comment.ticket-conversation__message-text_system > .ticket-conversation__message-html {\n background: #1a5f6e !important;\n color: #f0fafc !important;\n border: 1px solid rgba(0, 0, 0, 0.08) !important;\n border-radius: 12px !important;\n overflow: hidden !important;\n }\n #ticket-app .ticket .ticket-conversation__message-text.ticket-conversation__message-text_system > .ticket-conversation__message-html a {\n color: inherit !important;\n }\n /* У системных сообщений отключаем "хвосты"/углы базовой темы */\n #ticket-app .ticket .ticket-conversation__message-text.ticket-conversation__message-text_system {\n border-radius: 12px !important;\n background: transparent !important;\n }\n #ticket-app .ticket .ticket-conversation__message-text.ticket-conversation__message-text_system > .ticket-conversation__message-html {\n border-radius: 12px !important;\n }\n #ticket-app .ticket .ticket-conversation__message-text.ticket-conversation__message-text_system::before,\n #ticket-app .ticket .ticket-conversation__message-text.ticket-conversation__message-text_system::after,\n #ticket-app .ticket .ticket-conversation__message-text.ticket-conversation__message-text_system > .ticket-conversation__message-html::before,\n #ticket-app .ticket .ticket-conversation__message-text.ticket-conversation__message-text_system > .ticket-conversation__message-html::after {\n content: none !important;\n display: none !important;\n }\n html.${DARK_HTML_CLASS} #ticket-app .ticket .ticket-conversation__message-text.ticket-conversation__message-text_staff.ticket-conversation__message-text_system > .ticket-conversation__message-html {\n background: #4a5563 !important;\n color: #f0f2f5 !important;\n border-color: #3d4654 !important;\n }\n html.${DARK_HTML_CLASS} #ticket-app .ticket .ticket-conversation__message-text.ticket-conversation__message-text_comment.ticket-conversation__message-text_system > .ticket-conversation__message-html {\n background: #1a5f6e !important;\n color: #f0fafc !important;\n border-color: #154a56 !important;\n }\n html.${PINK_HTML_CLASS} #ticket-app .ticket .ticket-conversation__message-text.ticket-conversation__message-text_staff.ticket-conversation__message-text_system > .ticket-conversation__message-html {\n background: #d4b4c3 !important;\n color: #5f4352 !important;\n border-color: #c699b1 !important;\n }\n html.${PINK_HTML_CLASS} #ticket-app .ticket .ticket-conversation__message-text.ticket-conversation__message-text_comment.ticket-conversation__message-text_system > .ticket-conversation__message-html {\n background: #c08aa8 !important;\n color: #67495a !important;\n border-color: #b97aa0 !important;\n }\n html.${MIDNIGHT_BLUE_HTML_CLASS} #ticket-app .ticket .ticket-conversation__message-text.ticket-conversation__message-text_staff.ticket-conversation__message-text_system > .ticket-conversation__message-html {\n background: #23395f !important;\n color: #edf4ff !important;\n border-color: #33517f !important;\n }\n html.${MIDNIGHT_BLUE_HTML_CLASS} #ticket-app .ticket .ticket-conversation__message-text.ticket-conversation__message-text_comment.ticket-conversation__message-text_system > .ticket-conversation__message-html {\n background: #245082 !important;\n color: #e8f6ff !important;\n border-color: #1d416f !important;\n }\n .ticket-conversation__message-html-emoji { font-size: 1.8rem !important; }\n\n /* Meta — hidden (всё внутри пузыря теперь) */\n .ticket-conversation__message-meta { display: none !important; }\n\n /* Пагинация скрывается через отдельный стиль autoLoadHistory */\n\n /* Имя в пузыре */\n .hde-name-inside {\n display: block !important; font-size: 12px !important;\n font-weight: 600 !important; line-height: 1.2 !important;\n margin-bottom: 2px !important;\n font-family: 'Helvetica Neue', Helvetica, 'PingFang SC',\n 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif !important;\n }\n .ticket-conversation__message_user .hde-name-inside { text-align: left !important; color: #1a627a !important; }\n .ticket-conversation__message_staff .hde-name-inside,\n .ticket-conversation__message-text_post-own .hde-name-inside { color: #e0e0e0 !important; text-align: right !important; }\n .ticket-conversation__message-text_system .hde-name-inside { color: rgba(255,255,255,0.7) !important; }\n\n /* Время в пузыре */\n .hde-time-inside {\n display: block !important; clear: both !important;\n font-size: 9px !important; line-height: 1 !important;\n text-align: right !important; margin: 2px 0 0 0 !important;\n padding: 0 4px 1px 0 !important;\n font-family: 'Helvetica Neue', Helvetica, 'PingFang SC',\n 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif !important;\n }\n .ticket-conversation__message_user .hde-time-inside { color: #000000 !important; opacity: 0.4 !important; }\n .ticket-conversation__message_staff .hde-time-inside,\n .ticket-conversation__message-text_post-own .hde-time-inside { color: #fff !important; opacity: 0.5 !important; }\n .hde-edited-inside {\n display: none !important;\n font-size: 9px !important;\n line-height: 1 !important;\n margin: 2px 0 0 0 !important;\n padding: 0 0 1px 0 !important;\n font-family: 'Helvetica Neue', Helvetica, 'PingFang SC',\n 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif !important;\n opacity: 0.55 !important;\n }\n .ticket-conversation__message_user .hde-edited-inside { color: #000000 !important; }\n .ticket-conversation__message_staff .hde-edited-inside,\n .ticket-conversation__message-text_post-own .hde-edited-inside { color: #fff !important; }\n .ticket-conversation__message-html:has(.hde-edited-inside) .hde-edited-inside {\n display: inline-block !important;\n float: left !important;\n margin-right: 5px !important;\n }\n .ticket-conversation__message-html:has(.hde-edited-inside) .hde-time-inside {\n display: inline-block !important;\n float: right !important;\n clear: none !important;\n margin-top: 2px !important;\n }\n .ticket-conversation__message-updated { display: none !important; }\n\n /* Padding bubble */\n .ticket-conversation__message-html:has(.hde-time-inside) { padding-bottom: 3px !important; }\n .ticket-conversation__message-html:has(.hde-name-inside) { padding-top: 4px !important; }\n\n /* Кнопки действий */\n .ticket-conversation__actions {\n clear: none !important; padding: 0 !important; margin: 0 !important;\n line-height: 1 !important; display: inline-flex !important;\n align-items: center !important; opacity: 0 !important;\n transition: opacity 0.15s ease !important;\n }\n .ticket-conversation__message:hover .ticket-conversation__actions { opacity: 1 !important; }\n\n .hde-actions-row { display: inline-flex !important; flex-direction: row !important; align-items: center !important; gap: 6px !important; }\n .ticket-conversation__actions-btn { display: inline-flex !important; align-items: center !important; gap: 2px !important; flex: none !important; }\n .ticket-conversation__actions_button { padding: 2px 3px !important; margin: 0 1px !important; }\n\n /* Лайки */\n .ticket-conversation__like {\n display: inline-flex !important; width: auto !important; font-size: 10px !important;\n margin: 0 !important; padding: 0 !important; gap: 2px !important;\n }\n .ticket-conversation__like button { padding: 2px 3px !important; margin: 0 !important; font-size: 12px !important; }\n\n /* Кнопки под пузырём (вынесены из meta) */\n .hde-actions-inline {\n display: inline-flex !important; flex-direction: row !important;\n align-items: center !important; gap: 6px !important;\n align-self: center !important;\n opacity: 0 !important; transition: opacity 0.15s ease !important;\n }\n .ticket-conversation__message:hover .hde-actions-inline { opacity: 1 !important; }\n .ticket-conversation__message.hde-has-reaction > .ticket-conversation__message-block > .hde-actions-inline {\n opacity: 1 !important;\n }\n .ticket-conversation__message_user > .ticket-conversation__message-block > .hde-actions-inline,\n .ticket-conversation__message_comment > .ticket-conversation__message-block > .hde-actions-inline {\n justify-content: flex-end !important; order: 2 !important;\n }\n .ticket-conversation__message_staff > .ticket-conversation__message-block > .hde-actions-inline {\n justify-content: flex-start !important; order: -1 !important;\n }\n\n /* Разделитель даты */\n .${DATE_SEP_CLASS} {\n display: block !important; width: 100% !important;\n text-align: center !important; clear: both !important;\n margin: 0 !important; padding: 0 !important;\n font-size: 13px !important; line-height: 1.3 !important;\n font-weight: 700 !important; letter-spacing: 1px !important;\n font-family: 'Helvetica Neue', Helvetica, 'PingFang SC',\n 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif !important;\n color: #999 !important; position: relative !important;\n }\n .${DATE_SEP_CLASS}::before,\n .${DATE_SEP_CLASS}::after {\n content: none !important;\n display: none !important;\n }\n .${TICKET_SEP_CLASS} {\n display: block !important;\n width: 100% !important;\n text-align: center !important;\n margin: 16px 0 8px 0 !important;\n font-size: 12px !important;\n font-weight: 600 !important;\n color: #7f8c8d !important;\n font-family: inherit !important;\n box-sizing: border-box !important;\n line-height: 1.4 !important;\n }\n .${TICKET_SEP_CLASS}::after {\n content: '' !important;\n display: block !important;\n width: 100% !important;\n height: 1px !important;\n background: #c8d6db !important;\n margin-top: 5px !important;\n }\n\n\n\n /* Переопределяем line-height параграфов внутри пузырей */\n #ticket-app .ticket .ticket-conversation__message-text p {\n margin: 0 !important;\n line-height: 1.1 !important;\n }\n\n /* Убираем разделитель/отступ у полей пользователя */\n #ticket-app .ticket .ticket-user__field {\n padding-bottom: 0 !important;\n border-bottom: none !important;\n margin-bottom: 0 !important;\n }\n #ticket-app .ticket .ticket-user__fields {\n padding-bottom: 0 !important;\n border-bottom: none !important;\n }\n #ticket-app .ticket .ticket-user__fields-heading {\n border-bottom: none !important;\n margin: 0 !important;\n padding-bottom: 0 !important;\n }\n #ticket-app .ticket-user__fields-heading,\n #ticket-app .ticket-user__fields-heading::before,\n #ticket-app .ticket-user__fields-heading::after {\n border-bottom: none !important;\n margin-bottom: 0 !important;\n box-shadow: none !important;\n content: normal !important;\n }\n /* Длинные значения в compact не обрезаем троеточием, а переносим */\n #ticket-app .ticket-left-block .ticket-user .ticket-user__field-value-text {\n white-space: normal !important;\n overflow: visible !important;\n text-overflow: clip !important;\n overflow-wrap: anywhere !important;\n word-break: break-word !important;\n line-height: 1.25 !important;\n }\n #ticket-app .ticket-left-block .ticket-user .ticket-user__fields-heading,\n #ticket-app .ticket-left-block .ticket-user .ticket-user__field-label,\n #ticket-app .ticket-left-block .ticket-user .ticket-user__field-value.ticket-user__basic-fields-group {\n display: none !important;\n }\n #ticket-app .el-icon-copy-document {\n display: none !important;\n }\n #ticket-app .ticket-user__field-value-copy.copy-text {\n display: none !important;\n }\n .ticket-tabs .ticket-tabs__more .ticket-tabs__more-close-all {\n border-top: none !important;\n }\n .ticket-editor__with-borders {\n border-top: none !important;\n }\n .el-tabs--card > .el-tabs__header .el-tabs__nav {\n border-color: #e4e7ed !important;\n }\n\n /* Отступы между сообщениями */\n .ticket-conversation__message { margin-top: 5px !important; }\n .ticket-conversation__message:first-child { margin-top: 2px !important; }\n `;
const PAGINATION_STYLE_ID = "hde-pagination-hide-style";
const TICKET_SEP_STYLE_ID = "hde-ticket-sep-style";
function injectTicketSepStyle() {
if (document.getElementById(TICKET_SEP_STYLE_ID)) return;
const style = document.createElement("style");
style.id = TICKET_SEP_STYLE_ID;
style.textContent = `\n .${TICKET_SEP_CLASS} {\n display: block !important;\n width: 100% !important;\n text-align: center !important;\n margin: 16px 0 8px 0 !important;\n font-size: 12px !important;\n font-weight: 600 !important;\n color: #7f8c8d !important;\n font-family: inherit !important;\n box-sizing: border-box !important;\n line-height: 1.4 !important;\n }\n .${TICKET_SEP_CLASS}::after {\n content: '' !important;\n display: block !important;\n width: 100% !important;\n height: 1px !important;\n background: #c8d6db !important;\n margin-top: 5px !important;\n }\n `;
document.head.appendChild(style);
}
function injectPaginationStyle() {
if (document.getElementById(PAGINATION_STYLE_ID)) return;
const style = document.createElement("style");
style.id = PAGINATION_STYLE_ID;
style.textContent = ".ticket-conversation__pagination { display: none !important; }";
document.head.appendChild(style);
}
function removePaginationStyle() {
const el = document.getElementById(PAGINATION_STYLE_ID);
if (el) el.remove();
}
function injectStyle() {
if (!CONFIG.compactChat) return;
if (document.getElementById(STYLE_ID)) return;
const style = document.createElement("style");
style.id = STYLE_ID;
style.textContent = CSS;
document.head.appendChild(style);
ensureLikeEmojiStyle();
}
function ensureLikeEmojiStyle() {
let style = document.getElementById(LIKE_EMOJI_STYLE_ID);
if (!style) {
style = document.createElement("style");
style.id = LIKE_EMOJI_STYLE_ID;
document.head.appendChild(style);
}
style.textContent = `
#ticket-app .ticket-conversation__message-html {
overflow: visible !important;
}
#ticket-app .ticket-conversation__messages {
-ms-overflow-style: none !important;
scrollbar-width: none !important;
}
#ticket-app .ticket-conversation__messages::-webkit-scrollbar {
width: 0 !important;
height: 0 !important;
display: none !important;
}
#ticket-app .ticket-conversation__like i.hde-like,
#ticket-app .ticket-conversation__like i.hde-dislike {
font-size: 0 !important;
line-height: 0 !important;
width: 1em !important;
height: 1em !important;
display: inline-block !important;
margin: 0px 0px 0px 5px !important;
}
#ticket-app .ticket-conversation__like i.hde-like {
margin: 0px 10px 0px 5px !important;
}
#ticket-app .ticket-conversation__like i.hde-like::before {
content: "👍" !important;
font-size: 13px !important;
line-height: 1 !important;
}
#ticket-app .ticket-conversation__like i.hde-dislike::before {
content: "👎" !important;
font-size: 13px !important;
line-height: 1 !important;
}
#ticket-app .ticket-conversation__like .ticket-conversation__like-icon.active {
background: transparent !important;
padding: 0px 10px 0 0 !important;
}
#ticket-app .ticket-conversation__like button:hover,
#ticket-app .ticket-conversation__like button:focus,
#ticket-app .ticket-conversation__like button:active,
#ticket-app .ticket-conversation__like .ticket-conversation__like-button:hover,
#ticket-app .ticket-conversation__like .ticket-conversation__like-button:focus,
#ticket-app .ticket-conversation__like .ticket-conversation__like-button:active,
#ticket-app .ticket-conversation__like .ticket-conversation__dislike-button:hover,
#ticket-app .ticket-conversation__like .ticket-conversation__dislike-button:focus,
#ticket-app .ticket-conversation__like .ticket-conversation__dislike-button:active,
#ticket-app .ticket-conversation__like .ticket-conversation__like-icon:hover,
#ticket-app .ticket-conversation__like .ticket-conversation__like-icon:active,
#ticket-app .ticket-conversation__like .ticket-conversation__like-icon.active:hover,
#ticket-app .ticket-conversation__like .ticket-conversation__like-icon.active:active {
background: transparent !important;
box-shadow: none !important;
}
#ticket-app .ticket-conversation__like .ticket-conversation__dislike-button .ticket-conversation__like-icon,
#ticket-app .ticket-conversation__like .ticket-conversation__dislike-button:hover .ticket-conversation__like-icon,
#ticket-app .ticket-conversation__like .ticket-conversation__dislike-button:focus .ticket-conversation__like-icon,
#ticket-app .ticket-conversation__like .ticket-conversation__dislike-button:active .ticket-conversation__like-icon,
#ticket-app .ticket-conversation__like .ticket-conversation__dislike-button .ticket-conversation__like-icon:hover,
#ticket-app .ticket-conversation__like .ticket-conversation__dislike-button .ticket-conversation__like-icon:active {
background: transparent !important;
box-shadow: none !important;
}
#ticket-app .ticket .ticket-conversation__like button:hover .ticket-conversation__like-icon,
#ticket-app .ticket .ticket-conversation__like button:focus .ticket-conversation__like-icon,
#ticket-app .ticket .ticket-conversation__like button:active .ticket-conversation__like-icon {
background: transparent !important;
box-shadow: none !important;
}
#ticket-app .ticket-conversation__message:not(.hde-has-reaction) .ticket-conversation__like.ticket-conversation__show-on-hover {
display: none !important;
}
#ticket-app .hde-reaction-strip {
position: static !important;
display: flex !important;
width: fit-content !important;
min-width: 0 !important;
gap: 6px !important;
margin-top: 6px !important;
max-width: 100% !important;
flex-wrap: wrap !important;
clear: both !important;
z-index: 2 !important;
}
#ticket-app .hde-reaction-bubble {
display: inline-flex !important;
align-items: center !important;
justify-content: center !important;
min-height: 20px !important;
border-radius: 10px !important;
background: rgba(20,32,55,0.55) !important;
backdrop-filter: blur(2px) !important;
padding: 0 8px !important;
cursor: pointer !important;
}
#ticket-app .hde-reaction-inner {
display: inline-flex !important;
align-items: center !important;
gap: 6px !important;
}
#ticket-app .hde-reaction-emoji {
font-size: 13px !important;
line-height: 1 !important;
margin: 0 !important;
}
#ticket-app .hde-reaction-qty {
font-size: 12px !important;
font-weight: 700 !important;
font-family: 'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', '微软雅黑', Arial, sans-serif !important;
line-height: 1 !important;
color: currentColor !important;
}
.hde-delete-popconfirm-fixed {
overflow: visible !important;
}
.hde-delete-popconfirm-fixed .popper__arrow {
position: absolute !important;
display: block !important;
width: 0 !important;
height: 0 !important;
background: transparent !important;
border-color: transparent !important;
border-style: solid !important;
border-width: 6px !important;
border-top-width: 0 !important;
border-bottom-color: #ebeef5 !important;
top: -6px !important;
left: 24px !important;
margin: 0 !important;
transform: none !important;
filter: drop-shadow(0 2px 8px rgba(0,0,0,0.08)) !important;
pointer-events: none !important;
}
.hde-delete-popconfirm-fixed .popper__arrow::after {
content: " " !important;
position: absolute !important;
display: block !important;
width: 0 !important;
height: 0 !important;
left: 0 !important;
top: 1px !important;
margin-left: -6px !important;
border-color: transparent !important;
border-style: solid !important;
border-width: 6px !important;
border-top-width: 0 !important;
border-bottom-color: #ffffff !important;
transform: none !important;
}
`;
}
function removeCompactStyle() {
const styleEl = document.getElementById(STYLE_ID);
if (styleEl) styleEl.remove();
document.getElementById(LIKE_EMOJI_STYLE_ID)?.remove();
document.querySelectorAll("." + DATE_SEP_CLASS + ", ." + TICKET_SEP_CLASS).forEach(el => el.remove());
document.querySelectorAll("." + DONE_CLASS).forEach(msgEl => {
msgEl.classList.remove(DONE_CLASS, "hde-compact-grouped");
msgEl.querySelectorAll(".hde-name-inside, .hde-time-inside, .hde-edited-inside").forEach(el => el.remove());
const block = msgEl.querySelector(MSG_BLOCK_SELECTOR);
const meta = block && block.querySelector(":scope > .ticket-conversation__message-meta");
const actionsInline = block && block.querySelector(":scope > .hde-actions-inline");
if (actionsInline && meta) {
Array.from(actionsInline.children).forEach(child => meta.appendChild(child));
actionsInline.remove();
}
});
}
function getMessageParts(msgEl) {
const block = msgEl.querySelector(MSG_BLOCK_SELECTOR);
if (!block) return {
block: null,
htmlEl: null
};
return {
block: block,
htmlEl: block.querySelector(MSG_HTML_SELECTOR)
};
}
function extractTime(metaText) {
const match = metaText.match(/(\d{1,2}:\d{2})\s*$/);
return match ? match[1] : metaText;
}
function extractName(metaText) {
return metaText.replace(/\s+\d{1,2}\.\d{2}\.?\d{0,4}\s+\d{1,2}:\d{2}\s*$/, "").trim();
}
function extractDate(metaText) {
const m = metaText.match(/(\d{1,2}\.\d{2}\.?\d{0,4})/);
if (!m) return null;
const parts = m[1].split(".");
const day = parseInt(parts[0], 10);
const month = parseInt(parts[1], 10);
const year = parts.length > 2 ? parts[2] : (new Date).getFullYear();
return String(day).padStart(2, "0") + "." + String(month).padStart(2, "0") + "." + year;
}
function extractEditedTime(msgEl) {
const updatedEl = msgEl.querySelector(":scope > .ticket-conversation__message-block .ticket-conversation__message-updated");
if (!updatedEl) return null;
const text = (updatedEl.textContent || "").trim();
const m = text.match(/(\d{1,2}:\d{2})\s*$/);
return m ? m[1] : null;
}
function addInsideBubble(msgEl, className, textContent) {
const {htmlEl: htmlEl} = getMessageParts(msgEl);
if (!htmlEl) return null;
let el = htmlEl.querySelector(":scope > ." + className);
if (!el) {
el = document.createElement("span");
el.className = className;
if (className === "hde-name-inside") {
htmlEl.insertBefore(el, htmlEl.firstChild);
} else {
htmlEl.appendChild(el);
}
}
el.textContent = textContent;
return el;
}
function removeFromBubble(msgEl, className) {
const {htmlEl: htmlEl} = getMessageParts(msgEl);
if (!htmlEl) return;
const el = htmlEl.querySelector(":scope > ." + className);
if (el) el.remove();
}
function getMessageMetaText(msgEl) {
const block = msgEl.querySelector(MSG_BLOCK_SELECTOR);
if (!block) return "";
const meta = block.querySelector(":scope > .ticket-conversation__message-meta");
if (!meta) return "";
if (!meta.dataset.origText) {
meta.dataset.origText = meta.textContent.trim();
}
return meta.dataset.origText || "";
}
function ensureActionsInlineContainer(block, meta) {
let actionsRow = meta.querySelector(":scope > .hde-actions-row") || block.querySelector(":scope > .hde-actions-inline");
if (!actionsRow) {
actionsRow = document.createElement("div");
actionsRow.className = "hde-actions-inline";
block.appendChild(actionsRow);
return actionsRow;
}
if (!actionsRow.classList.contains("hde-actions-inline")) {
actionsRow.classList.remove("hde-actions-row");
actionsRow.classList.add("hde-actions-inline");
block.appendChild(actionsRow);
}
return actionsRow;
}
function getBubbleContextMenuPalette() {
const html = document.documentElement;
if (html.classList.contains(DARK_HTML_CLASS)) {
return {
background: "#1f2a3d",
text: "#e8edf7",
border: "rgba(255,255,255,0.16)",
hoverBg: "rgba(255,255,255,0.08)",
divider: "rgba(255,255,255,0.14)",
muted: "#b8c6df",
avatarBg: "rgba(255,255,255,0.12)"
};
}
if (html.classList.contains(PINK_HTML_CLASS)) {
return {
background: "#fff6fb",
text: "#6b4c5d",
border: "#e3ccd8",
hoverBg: "rgba(122,90,107,0.12)",
divider: "rgba(122,90,107,0.22)",
muted: "#8b687c",
avatarBg: "rgba(122,90,107,0.16)"
};
}
if (html.classList.contains(MIDNIGHT_BLUE_HTML_CLASS)) {
return {
background: "#15223a",
text: "#d4e4fb",
border: "#2a4266",
hoverBg: "rgba(212,228,251,0.12)",
divider: "rgba(212,228,251,0.2)",
muted: "#9bb3d4",
avatarBg: "rgba(212,228,251,0.14)"
};
}
if (html.classList.contains(TRICOLOR_HTML_CLASS)) {
return {
background: "#ffffff",
text: "#1d3f73",
border: "#bfd0ec",
hoverBg: "rgba(42,77,130,0.1)",
divider: "rgba(42,77,130,0.2)",
muted: "#5570a0",
avatarBg: "rgba(42,77,130,0.12)"
};
}
if (html.classList.contains(MONOCHROME_HTML_CLASS)) {
return {
background: "#1b1b1b",
text: "#f1f1f1",
border: "#3a3a3a",
hoverBg: "rgba(255,255,255,0.12)",
divider: "rgba(255,255,255,0.22)",
muted: "#bdbdbd",
avatarBg: "rgba(255,255,255,0.15)"
};
}
return {
background: "#ffffff",
text: "#303133",
border: "#dcdfe6",
hoverBg: "rgba(0,0,0,0.06)",
divider: "rgba(0,0,0,0.12)",
muted: "#606266",
avatarBg: "rgba(0,0,0,0.08)"
};
}
function applyBubbleContextMenuTheme(menu) {
if (!(menu instanceof HTMLElement)) return;
const palette = getBubbleContextMenuPalette();
menu.__hdePalette = palette;
menu.style.cssText = [
"display:none",
"position:fixed",
"z-index:1000001",
"min-width:170px",
"max-width:260px",
`background:${palette.background}`,
`color:${palette.text}`,
`border:1px solid ${palette.border}`,
"border-radius:8px",
"padding:6px",
"box-shadow:0 10px 26px rgba(0,0,0,0.45)",
"font-size:12px",
"line-height:1.25",
"user-select:none"
].join(";");
}
function getBubbleContextMenuRoot() {
let menu = document.getElementById(BUBBLE_CONTEXT_MENU_ID);
if (menu) return menu;
menu = document.createElement("div");
menu.id = BUBBLE_CONTEXT_MENU_ID;
applyBubbleContextMenuTheme(menu);
document.body.appendChild(menu);
return menu;
}
function hideBubbleContextMenu() {
const menu = document.getElementById(BUBBLE_CONTEXT_MENU_ID);
if (!menu) return;
menu.style.display = "none";
menu.innerHTML = "";
}
function addBubbleContextMenuItem(menu, label, onClick, closeOnClick = true) {
const palette = menu?.__hdePalette || getBubbleContextMenuPalette();
const item = document.createElement("button");
item.type = "button";
item.textContent = label;
item.style.cssText = [ "display:block", "width:100%", "border:0", "background:transparent", "color:inherit", "text-align:left", "padding:8px 10px", "border-radius:6px", "cursor:pointer", "font:inherit" ].join(";");
item.addEventListener("mouseenter", () => {
item.style.background = palette.hoverBg;
});
item.addEventListener("mouseleave", () => {
item.style.background = "transparent";
});
item.addEventListener("click", e => {
e.preventDefault();
e.stopPropagation();
try {
onClick?.();
} finally {
if (closeOnClick) hideBubbleContextMenu();
}
});
menu.appendChild(item);
}
function addBubbleContextMenuSectionTitle(menu, label) {
const palette = menu?.__hdePalette || getBubbleContextMenuPalette();
const title = document.createElement("div");
title.textContent = label;
title.style.cssText = [ "margin:6px 6px 4px 6px", "font-size:11px", "font-weight:600", `color:${palette.muted}` ].join(";");
menu.appendChild(title);
}
function addBubbleContextMenuUserRow(menu, user) {
const palette = menu?.__hdePalette || getBubbleContextMenuPalette();
const row = document.createElement("div");
row.style.cssText = [ "display:flex", "align-items:center", "gap:8px", "padding:4px 10px", "font-size:12px", "line-height:1.25" ].join(";");
const avatar = document.createElement("span");
avatar.style.cssText = [ "width:18px", "height:18px", "border-radius:50%", "flex:0 0 18px", `background:${palette.avatarBg}`, "background-size:cover", "background-position:center" ].join(";");
if (user.avatarUrl) avatar.style.backgroundImage = `url("${user.avatarUrl}")`;
const name = document.createElement("span");
name.textContent = user.name || "Неизвестно";
name.style.cssText = "flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;";
const reaction = document.createElement("span");
reaction.textContent = user.reaction || "";
reaction.style.cssText = "flex:0 0 auto;";
row.appendChild(avatar);
row.appendChild(name);
row.appendChild(reaction);
menu.appendChild(row);
}
function getReactionUsersFromButton(btn) {
if (!(btn instanceof HTMLElement)) return [];
const popoverId = btn.getAttribute("aria-describedby");
if (!popoverId) return [];
const popover = document.getElementById(popoverId);
if (!(popover instanceof HTMLElement)) return [];
const users = Array.from(popover.querySelectorAll(".ticket-conversation__like-user")).map(userEl => {
const text = (userEl.textContent || "").replace(/\s+/g, " ").trim();
const avatarEl = userEl.querySelector(".ticket-conversation__like-user-image, [style*='background-image']");
const inlineBg = avatarEl instanceof HTMLElement ? avatarEl.style.backgroundImage : "";
const match = inlineBg.match(/url\(["']?(.*?)["']?\)/i);
return {
name: text || "Неизвестно",
avatarUrl: match?.[1] || ""
};
}).filter(Boolean);
return users;
}
function showBubbleContextMenu(msgEl, x, y) {
if (!(msgEl instanceof HTMLElement)) return;
const menu = getBubbleContextMenuRoot();
applyBubbleContextMenuTheme(menu);
const likeButtons = Array.from(msgEl.querySelectorAll(".ticket-conversation__like button"));
let reactionUsersWarmupScheduled = false;
const warmupReactionUsersAndRerender = () => {
if (reactionUsersWarmupScheduled || !likeButtons.length) return;
reactionUsersWarmupScheduled = true;
const mutedPopovers = [];
const rememberAndMute = pop => {
if (!(pop instanceof HTMLElement)) return;
if (!mutedPopovers.some(entry => entry.el === pop)) {
mutedPopovers.push({
el: pop,
visibility: pop.style.visibility,
opacity: pop.style.opacity,
pointerEvents: pop.style.pointerEvents
});
}
pop.style.setProperty("visibility", "hidden", "important");
pop.style.setProperty("opacity", "0", "important");
pop.style.setProperty("pointer-events", "none", "important");
};
const muteKnownReactionPopovers = () => {
const reactionLists = Array.from(document.querySelectorAll(".ticket-conversation__like-users"));
reactionLists.forEach(listEl => {
const pop = listEl.closest(".el-popover, .el-popper, [x-placement]") || listEl.parentElement;
rememberAndMute(pop);
});
likeButtons.forEach(btn => {
if (!(btn instanceof HTMLElement)) return;
const popoverId = btn.getAttribute("aria-describedby");
if (!popoverId) return;
const pop = document.getElementById(popoverId);
rememberAndMute(pop);
});
};
const unmuteReactionPopovers = () => {
mutedPopovers.forEach(({el, visibility, opacity, pointerEvents}) => {
if (!(el instanceof HTMLElement)) return;
if (visibility) el.style.setProperty("visibility", visibility); else el.style.removeProperty("visibility");
if (opacity) el.style.setProperty("opacity", opacity); else el.style.removeProperty("opacity");
if (pointerEvents) el.style.setProperty("pointer-events", pointerEvents); else el.style.removeProperty("pointer-events");
});
};
likeButtons.forEach(btn => {
if (!(btn instanceof HTMLElement)) return;
btn.dispatchEvent(new MouseEvent("mouseenter", {
bubbles: true,
cancelable: true
}));
btn.dispatchEvent(new MouseEvent("mouseover", {
bubbles: true,
cancelable: true
}));
});
muteKnownReactionPopovers();
if (typeof queueMicrotask === "function") queueMicrotask(muteKnownReactionPopovers);
Promise.resolve().then(muteKnownReactionPopovers);
[ 16, 40, 80, 140, 200 ].forEach(delay => {
setTimeout(muteKnownReactionPopovers, delay);
});
let rafTicks = 0;
const rafMute = () => {
muteKnownReactionPopovers();
rafTicks += 1;
if (rafTicks < 16) requestAnimationFrame(rafMute);
};
requestAnimationFrame(rafMute);
const hideLoop = setInterval(muteKnownReactionPopovers, 20);
setTimeout(() => {
clearInterval(hideLoop);
unmuteReactionPopovers();
}, 240);
setTimeout(renderMenu, 80);
setTimeout(renderMenu, 180);
};
const triggerActionByTitle = title => {
const normalizedTitle = (title || "").trim();
const isVisibleEl = el => el instanceof HTMLElement && el.offsetParent !== null;
const dispatchNativeClick = el => {
if (!(el instanceof HTMLElement)) return false;
[ "pointerdown", "mousedown", "pointerup", "mouseup", "click" ].forEach(type => {
el.dispatchEvent(new MouseEvent(type, { bubbles: true, cancelable: true, view: window }));
});
return true;
};
const revealMessageActions = () => {
[ msgEl, msgEl.querySelector(".ticket-conversation__message-block"), msgEl.querySelector(".ticket-conversation__actions"), msgEl.querySelector(".ticket-conversation__actions-btn") ]
.forEach(node => {
if (!(node instanceof HTMLElement)) return;
node.dispatchEvent(new MouseEvent("mouseenter", { bubbles: true, cancelable: true, view: window }));
node.dispatchEvent(new MouseEvent("mouseover", { bubbles: true, cancelable: true, view: window }));
});
};
const openOtherActions = () => {
revealMessageActions();
const local = msgEl.querySelector(".ticket-conversation__actions_button .el-icon-more-outline")?.closest("[title], .ticket-conversation__actions_button");
if (local instanceof HTMLElement) {
dispatchNativeClick(local);
return true;
}
const globalCandidates = Array.from(document.querySelectorAll("[title], .ticket-conversation__actions_button")).filter(el => el instanceof HTMLElement);
const byTitle = globalCandidates.find(el => (el.getAttribute("title") || "").trim() === "Другие действия" && isVisibleEl(el));
if (byTitle instanceof HTMLElement) {
dispatchNativeClick(byTitle);
return true;
}
const byIcon = globalCandidates.find(el => el.querySelector?.(".el-icon-more-outline") && isVisibleEl(el));
if (byIcon instanceof HTMLElement) {
dispatchNativeClick(byIcon);
return true;
}
return false;
};
const findActionInMessageOrPopup = selector => {
const local = Array.from(msgEl.querySelectorAll(selector)).filter(el => el instanceof HTMLElement);
const localVisible = local.find(isVisibleEl);
if (localVisible) return localVisible;
if (local[0]) return local[0];
const global = Array.from(document.querySelectorAll(selector)).filter(el => el instanceof HTMLElement);
const globalVisible = global.find(isVisibleEl);
if (globalVisible) return globalVisible;
return global[0] || null;
};
const findActionByTitles = titles => {
const wanted = new Set((titles || []).map(v => String(v || "").trim()).filter(Boolean));
if (!wanted.size) return null;
const local = Array.from(msgEl.querySelectorAll("[title]")).filter(el => el instanceof HTMLElement);
const localVisible = local.find(el => wanted.has((el.getAttribute("title") || "").trim()) && isVisibleEl(el));
if (localVisible) return localVisible;
const localAny = local.find(el => wanted.has((el.getAttribute("title") || "").trim()));
if (localAny) return localAny;
const global = Array.from(document.querySelectorAll("[title]")).filter(el => el instanceof HTMLElement);
const globalVisible = global.find(el => wanted.has((el.getAttribute("title") || "").trim()) && isVisibleEl(el));
if (globalVisible) return globalVisible;
return global.find(el => wanted.has((el.getAttribute("title") || "").trim())) || null;
};
const clickByTitlesWithReveal = titles => {
const clickOnce = () => {
revealMessageActions();
const target = findActionByTitles(titles);
if (!(target instanceof HTMLElement)) return false;
return dispatchNativeClick(target);
};
if (clickOnce()) return true;
openOtherActions();
setTimeout(clickOnce, 0);
setTimeout(clickOnce, 120);
setTimeout(clickOnce, 260);
return false;
};
const clickActionWithReveal = selector => {
const clickOnce = () => {
const target = findActionInMessageOrPopup(selector);
if (!(target instanceof HTMLElement)) return false;
return dispatchNativeClick(target);
};
if (clickOnce()) return true;
openOtherActions();
setTimeout(clickOnce, 0);
setTimeout(clickOnce, 120);
setTimeout(clickOnce, 260);
return false;
};
const hasNativeDraftTab = () => {
const tabCandidates = Array.from(document.querySelectorAll(".ticket-tabs__tab, .ticket-tabs [class*='tab'], .ticket-tabs__name, .ticket-tab"));
return tabCandidates.some(el => /черновик/i.test((el.textContent || "").trim()));
};
const hasNativeDraftState = () => {
const href = location.pathname + location.search;
return /\/ticket\/create\/draft\/\d+/i.test(href) || hasNativeDraftTab();
};
const placeDeletePopoverNearMessage = deleteBtn => {
if (!(deleteBtn instanceof HTMLElement)) return false;
const popoverId = deleteBtn.getAttribute("aria-describedby");
if (!popoverId) return false;
const popover = document.getElementById(popoverId);
const bubble = msgEl.querySelector(".ticket-conversation__message-html");
if (!(popover instanceof HTMLElement) || !(bubble instanceof HTMLElement)) return false;
const rect = bubble.getBoundingClientRect();
const popRect = popover.getBoundingClientRect();
const pad = 8;
let left = rect.left + 8;
let top = rect.bottom + 8;
if (top + popRect.height + pad > window.innerHeight) {
top = rect.top - popRect.height - 8;
}
if (left + popRect.width + pad > window.innerWidth) {
left = window.innerWidth - popRect.width - pad;
}
if (left < pad) left = pad;
if (top < pad) top = pad;
const arrowLeft = Math.max(12, Math.min(rect.left + 24 - left, Math.max(popRect.width - 20, 12)));
popover.classList.add("hde-delete-popconfirm-fixed");
popover.style.left = `${left}px`;
popover.style.top = `${top}px`;
popover.style.transformOrigin = "left top";
popover.style.zIndex = "2005";
const arrow = popover.querySelector(".popper__arrow");
if (arrow instanceof HTMLElement) {
arrow.remove();
}
return true;
};
const resolveDraftIdFromAction = (selector, fallbackDraftId) => {
const fallback = String(fallbackDraftId || "");
const btn = msgEl.querySelector(selector);
const candidates = [ btn, btn?.closest("a"), btn?.parentElement, btn?.parentElement?.closest("a") ].filter(Boolean);
for (const node of candidates) {
if (!(node instanceof HTMLElement)) continue;
const attrs = Array.from(node.attributes || []).map(attr => String(attr?.value || ""));
const extra = [
node.getAttribute("href") || "",
node.getAttribute("data-href") || "",
node.getAttribute("data-url") || "",
node.getAttribute("formaction") || "",
node.getAttribute("onclick") || ""
];
const pool = attrs.concat(extra);
for (const raw of pool) {
if (!raw) continue;
const match = String(raw).match(/\/(?:create\/)?draft\/(\d+)/);
if (match?.[1]) return match[1];
}
}
return fallback;
};
const resolveMessagePostId = () => {
const direct = msgEl.getAttribute("data-post-id") || msgEl.getAttribute("data-comment-id") || "";
if (direct) return direct;
const source = msgEl.querySelector("[data-post-id], [data-comment-id]");
if (source instanceof HTMLElement) {
return source.getAttribute("data-post-id") || source.getAttribute("data-comment-id") || "";
}
return "";
};
const resolvePostIdFromAction = selector => {
const btn = msgEl.querySelector(selector);
const candidates = [ btn, btn?.closest("a"), btn?.parentElement, btn?.parentElement?.closest("a") ].filter(Boolean);
for (const node of candidates) {
if (!(node instanceof HTMLElement)) continue;
const attrs = Array.from(node.attributes || []).map(attr => String(attr?.value || ""));
const extra = [
node.getAttribute("href") || "",
node.getAttribute("data-href") || "",
node.getAttribute("data-url") || "",
node.getAttribute("formaction") || "",
node.getAttribute("onclick") || ""
];
const pool = attrs.concat(extra);
for (const raw of pool) {
if (!raw) continue;
const match = String(raw).match(/[?&]postId=(\d+)/i);
if (match?.[1]) return match[1];
}
}
return "";
};
const isCreateFromReply = normalizedTitle === "Создать заявку из ответа" || normalizedTitle === "Создать заявку из комментария";
if (isCreateFromReply) {
if (clickByTitlesWithReveal([ "Создать заявку из ответа", "Создать заявку из комментария" ])) return;
const postId = resolvePostIdFromAction(".ticket-conversation__actions-create-ticket-from-post-button, .ticket-conversation__actions-create-ticket-from-comment-button") || resolveMessagePostId();
const filterMatch = location.pathname.match(/\/filter\/id\/(\d+)/);
const filterId = filterMatch?.[1] || "1";
const draftId = resolveDraftIdFromAction(".ticket-conversation__actions-create-ticket-from-post-button, .ticket-conversation__actions-create-ticket-from-comment-button", "1");
const startHref = location.href;
const tryNavigateFromReply = () => {
if (!postId) return false;
location.href = `/ru/ticket/list/filter/id/${filterId}/ticket/create/draft/${encodeURIComponent(draftId)}?postId=${encodeURIComponent(postId)}`;
return true;
};
const clickFromReply = () => {
revealMessageActions();
const btn = findActionInMessageOrPopup(".ticket-conversation__actions-create-ticket-from-post-button, .ticket-conversation__actions-create-ticket-from-comment-button");
if (!(btn instanceof HTMLElement)) return false;
return dispatchNativeClick(btn);
};
if (clickFromReply()) {
setTimeout(() => {
if (location.href !== startHref || hasNativeDraftState()) return;
openOtherActions();
setTimeout(() => {
if (clickFromReply()) return;
setTimeout(() => {
if (clickFromReply()) return;
setTimeout(() => {
if (location.href !== startHref || hasNativeDraftState()) return;
tryNavigateFromReply();
}, 380);
}, 180);
}, 120);
}, 1100);
return;
}
openOtherActions();
setTimeout(() => {
if (clickFromReply()) return;
setTimeout(() => {
if (clickFromReply()) return;
setTimeout(() => {
if (clickFromReply()) return;
tryNavigateFromReply();
}, 220);
}, 120);
}, 0);
return;
}
if (normalizedTitle === "Создать подзаявку") {
if (clickByTitlesWithReveal([ "Создать подзаявку" ])) return;
const postId = resolvePostIdFromAction(".ticket-conversation__actions-create-subticket-button") || resolveMessagePostId();
const ticketMatch = location.pathname.match(/\/ticket\/(\d+)/);
const ticketId = ticketMatch?.[1] || "";
const filterMatch = location.pathname.match(/\/filter\/id\/(\d+)/);
const filterId = filterMatch?.[1] || "1";
const draftId = resolveDraftIdFromAction(".ticket-conversation__actions-create-subticket-button", "4");
const startHref = location.href;
const tryNavigateSubticket = () => {
if (!postId || !ticketId) return false;
location.href = `/ru/ticket/list/filter/id/${filterId}/ticket/create/draft/${encodeURIComponent(draftId)}?postId=${encodeURIComponent(postId)}&ticketId=${encodeURIComponent(ticketId)}`;
return true;
};
const clickSubticket = () => {
revealMessageActions();
const btn = findActionInMessageOrPopup(".ticket-conversation__actions-create-subticket-button");
if (!(btn instanceof HTMLElement)) return false;
return dispatchNativeClick(btn);
};
const afterClickOrContinue = onContinue => {
setTimeout(() => {
if (location.href !== startHref || hasNativeDraftState()) return;
onContinue?.();
}, 1100);
};
if (clickSubticket()) {
afterClickOrContinue(() => {
openOtherActions();
setTimeout(() => {
if (location.href !== startHref || hasNativeDraftState()) return;
if (clickSubticket()) {
afterClickOrContinue(() => {
if (location.href !== startHref || hasNativeDraftState()) return;
tryNavigateSubticket();
});
return;
}
tryNavigateSubticket();
}, 120);
});
return;
}
openOtherActions();
setTimeout(() => {
if (clickSubticket()) {
afterClickOrContinue(() => {
if (location.href !== startHref || hasNativeDraftState()) return;
tryNavigateSubticket();
});
return;
}
setTimeout(() => {
if (clickSubticket()) {
afterClickOrContinue(() => {
if (location.href !== startHref || hasNativeDraftState()) return;
tryNavigateSubticket();
});
return;
}
setTimeout(() => {
if (clickSubticket()) {
afterClickOrContinue(() => {
if (location.href !== startHref || hasNativeDraftState()) return;
tryNavigateSubticket();
});
return;
}
tryNavigateSubticket();
}, 220);
}, 120);
}, 0);
return;
}
if (normalizedTitle === "Создать родительскую заявку") {
if (clickByTitlesWithReveal([ "Создать родительскую заявку" ])) return;
const postId = resolvePostIdFromAction(".ticket-conversation__actions-create-parent-ticket-button") || resolveMessagePostId();
const ticketMatch = location.pathname.match(/\/ticket\/(\d+)/);
const childTicketId = ticketMatch?.[1] || "";
const filterMatch = location.pathname.match(/\/filter\/id\/(\d+)/);
const filterId = filterMatch?.[1] || "1";
const draftId = resolveDraftIdFromAction(".ticket-conversation__actions-create-parent-ticket-button", "1");
const startHref = location.href;
const tryNavigateParent = () => {
if (!postId || !childTicketId) return false;
location.href = `/ru/ticket/list/filter/id/${filterId}/ticket/create/draft/${encodeURIComponent(draftId)}?postId=${encodeURIComponent(postId)}&childTicketId=${encodeURIComponent(childTicketId)}`;
return true;
};
const clickParentTicket = () => {
revealMessageActions();
const btn = findActionInMessageOrPopup(".ticket-conversation__actions-create-parent-ticket-button");
if (!(btn instanceof HTMLElement)) return false;
return dispatchNativeClick(btn);
};
if (clickParentTicket()) {
setTimeout(() => {
if (location.href !== startHref || hasNativeDraftState()) return;
openOtherActions();
setTimeout(() => {
if (clickParentTicket()) return;
setTimeout(() => {
if (clickParentTicket()) return;
setTimeout(() => {
if (location.href !== startHref || hasNativeDraftState()) return;
tryNavigateParent();
}, 380);
}, 180);
}, 120);
}, 1100);
return;
}
openOtherActions();
setTimeout(() => {
if (clickParentTicket()) return;
setTimeout(() => {
if (clickParentTicket()) return;
setTimeout(() => {
if (clickParentTicket()) return;
tryNavigateParent();
}, 220);
}, 120);
}, 0);
return;
}
if (normalizedTitle === "Удалить") {
const findDeleteBtn = () => {
const candidates = Array.from(msgEl.querySelectorAll(".ticket-conversation__actions-delete-button")).filter(el => el instanceof HTMLElement);
return candidates[0] || null;
};
const clickDelete = () => {
const btn = findDeleteBtn();
if (!(btn instanceof HTMLElement)) return false;
btn.click();
setTimeout(() => placeDeletePopoverNearMessage(btn), 0);
setTimeout(() => placeDeletePopoverNearMessage(btn), 80);
setTimeout(() => placeDeletePopoverNearMessage(btn), 180);
return true;
};
if (clickDelete()) return;
const fallbackOther = msgEl.querySelector(".ticket-conversation__actions_button .el-icon-more-outline")?.closest("[title], .ticket-conversation__actions_button");
if (fallbackOther instanceof HTMLElement) fallbackOther.click();
setTimeout(() => {
if (clickDelete()) return;
setTimeout(clickDelete, 120);
}, 0);
return;
}
const titleAliases = {
"Создать заявку из ответа": [ "Создать заявку из ответа", "Создать заявку из комментария" ],
"Создать заявку из комментария": [ "Создать заявку из комментария", "Создать заявку из ответа" ]
};
const allowedTitles = titleAliases[title] || [ title ];
const selectorByTitle = {
"Редактировать": ".ticket-conversation__actions-edit-button",
"Цитировать": ".ticket-conversation__actions-quote-button",
"Создать заявку из комментария": ".ticket-conversation__actions-create-ticket-from-comment-button",
"Создать заявку из ответа": ".ticket-conversation__actions-create-ticket-from-post-button, .ticket-conversation__actions-create-ticket-from-comment-button",
"Создать подзаявку": ".ticket-conversation__actions-create-subticket-button",
"Создать родительскую заявку": ".ticket-conversation__actions-create-parent-ticket-button",
"Удалить": ".ticket-conversation__actions-delete-button"
};
const isVisible = el => el instanceof HTMLElement && el.offsetParent !== null;
const findAction = () => {
const selector = selectorByTitle[title];
if (selector) {
const nodes = Array.from(msgEl.querySelectorAll(selector)).filter(el => el instanceof HTMLElement);
if (nodes[0]) return nodes[0];
const visible = nodes.find(isVisible);
if (visible) return visible;
if (nodes[0]) return nodes[0];
}
const candidates = Array.from(msgEl.querySelectorAll(".ticket-conversation__actions-btn [title], .ticket-conversation__actions [title], .hde-actions-inline [title]")).filter(el => el instanceof HTMLElement);
const exactVisible = candidates.find(el => allowedTitles.includes((el.getAttribute("title") || "").trim()) && isVisible(el));
if (exactVisible) return exactVisible;
return candidates.find(el => allowedTitles.includes((el.getAttribute("title") || "").trim())) || null;
};
const clickIfFound = () => {
const target = findAction();
if (!(target instanceof HTMLElement)) return false;
target.click();
return true;
};
if (clickIfFound()) return;
const fallbackOther = msgEl.querySelector(".ticket-conversation__actions_button .el-icon-more-outline")?.closest("[title], .ticket-conversation__actions_button");
if (fallbackOther instanceof HTMLElement) fallbackOther.click();
setTimeout(() => {
if (clickIfFound()) return;
setTimeout(() => {
if (clickIfFound()) return;
setTimeout(clickIfFound, 220);
}, 120);
}, 0);
};
const collectActions = () => {
const actionCandidates = Array.from(msgEl.querySelectorAll(".ticket-conversation__actions-btn [title], .ticket-conversation__actions [title], .hde-actions-inline [title]")).filter(el => el instanceof HTMLElement);
const isVisible = el => el instanceof HTMLElement && el.offsetParent !== null;
const byTitle = title => {
const exact = actionCandidates.filter(el => (el.getAttribute("title") || "").trim() === title);
const visible = exact.find(isVisible);
return visible || exact[0] || null;
};
const bySelector = selector => {
const nodes = Array.from(msgEl.querySelectorAll(selector)).filter(el => el instanceof HTMLElement);
const visible = nodes.find(isVisible);
return visible || nodes[0] || null;
};
const pushUnique = (arr, el) => {
if (!el || !(el instanceof HTMLElement)) return;
if (!arr.includes(el)) arr.push(el);
};
const primaryActionButtons = [];
pushUnique(primaryActionButtons, bySelector(".ticket-conversation__actions-edit-button"));
pushUnique(primaryActionButtons, bySelector(".ticket-conversation__actions-quote-button"));
pushUnique(primaryActionButtons, bySelector(".ticket-conversation__actions-create-ticket-from-post-button, .ticket-conversation__actions-create-ticket-from-comment-button"));
pushUnique(primaryActionButtons, bySelector(".ticket-conversation__actions-create-subticket-button"));
pushUnique(primaryActionButtons, bySelector(".ticket-conversation__actions-create-parent-ticket-button"));
pushUnique(primaryActionButtons, bySelector(".ticket-conversation__actions-delete-button"));
[ "Редактировать", "Цитировать", "Создать заявку из комментария", "Создать заявку из ответа", "Создать подзаявку", "Создать родительскую заявку", "Удалить" ]
.forEach(title => pushUnique(primaryActionButtons, byTitle(title)));
const otherByTitle = byTitle("Другие действия");
const otherByIcon = msgEl.querySelector(".ticket-conversation__actions_button .el-icon-more-outline")?.closest("[title], .ticket-conversation__actions_button");
const otherActionsBtn = otherByTitle || (otherByIcon instanceof HTMLElement ? otherByIcon : null);
const extraActionButtons = actionCandidates.filter(el => {
const title = (el.getAttribute("title") || "").trim();
if (!title || title === "Другие действия") return false;
return !primaryActionButtons.includes(el);
});
return { primaryActionButtons, otherActionsBtn, extraActionButtons };
};
let otherExpanded = false;
const adjustMenuPosition = () => {
const pad = 8;
const rect = menu.getBoundingClientRect();
let left = parseFloat(menu.dataset.anchorX || `${x}`);
let top = parseFloat(menu.dataset.anchorY || `${y}`);
if (left + rect.width + pad > window.innerWidth) left = window.innerWidth - rect.width - pad;
if (top + rect.height + pad > window.innerHeight) top = window.innerHeight - rect.height - pad;
if (left < pad) left = pad;
if (top < pad) top = pad;
menu.style.left = `${left}px`;
menu.style.top = `${top}px`;
};
const renderMenu = () => {
const {primaryActionButtons, otherActionsBtn, extraActionButtons} = collectActions();
const palette = menu.__hdePalette || getBubbleContextMenuPalette();
menu.innerHTML = "";
if (likeButtons[0] || likeButtons[1]) {
const reactionsRow = document.createElement("div");
reactionsRow.style.cssText = "display:flex;gap:6px;margin-bottom:4px;";
const addReactionHalf = (emoji, onClick) => {
const item = document.createElement("button");
item.type = "button";
item.textContent = emoji;
item.style.cssText = [
"display:block",
"width:50%",
"border:0",
"background:transparent",
"color:inherit",
"text-align:center",
"padding:8px 0",
"border-radius:6px",
"cursor:pointer",
"font:inherit",
"font-size:14px",
"line-height:1"
].join(";");
item.addEventListener("mouseenter", () => {
item.style.background = palette.hoverBg;
});
item.addEventListener("mouseleave", () => {
item.style.background = "transparent";
});
item.addEventListener("click", e => {
e.preventDefault();
e.stopPropagation();
try {
onClick?.();
} finally {
hideBubbleContextMenu();
}
});
reactionsRow.appendChild(item);
};
if (likeButtons[0]) addReactionHalf("👍", () => likeButtons[0].click());
if (likeButtons[1]) addReactionHalf("👎", () => likeButtons[1].click());
menu.appendChild(reactionsRow);
}
const addedActionLabels = new Set();
const dispatchNativeActionClick = btn => {
if (!(btn instanceof HTMLElement)) return;
[ "pointerdown", "mousedown", "pointerup", "mouseup", "click" ].forEach(type => {
btn.dispatchEvent(new MouseEvent(type, { bubbles: true, cancelable: true, view: window }));
});
};
const addNativeActionButton = (label, sourceBtn) => {
const normalized = (label || "").trim();
if (!normalized || addedActionLabels.has(normalized)) return;
addedActionLabels.add(normalized);
if (!(sourceBtn instanceof HTMLElement)) {
addBubbleContextMenuItem(menu, normalized, () => triggerActionByTitle(normalized));
return;
}
addBubbleContextMenuItem(menu, normalized, () => {
dispatchNativeActionClick(sourceBtn);
if (normalized === "Удалить") {
setTimeout(() => placeDeletePopoverNearMessage(sourceBtn), 0);
setTimeout(() => placeDeletePopoverNearMessage(sourceBtn), 80);
}
});
};
primaryActionButtons.forEach((btn, idx) => {
const label = (btn.getAttribute("title") || btn.getAttribute("aria-label") || btn.textContent || "").trim() || `Действие ${idx + 1}`;
addNativeActionButton(label, btn);
});
[ "Создать заявку из ответа", "Создать подзаявку", "Создать родительскую заявку" ].forEach(label => {
if (addedActionLabels.has(label)) return;
addedActionLabels.add(label);
addBubbleContextMenuItem(menu, label, () => triggerActionByTitle(label));
});
if (otherExpanded && extraActionButtons.length) {
otherExpanded = false;
}
const likeUsers = likeButtons[0] ? getReactionUsersFromButton(likeButtons[0]) : [];
const dislikeUsers = likeButtons[1] ? getReactionUsersFromButton(likeButtons[1]) : [];
if ((likeButtons[0] || likeButtons[1]) && !likeUsers.length && !dislikeUsers.length) {
warmupReactionUsersAndRerender();
}
if (likeUsers.length || dislikeUsers.length) {
const divider = document.createElement("div");
divider.style.cssText = `height:1px;background:${palette.divider};margin:6px 4px;`;
menu.appendChild(divider);
if (likeUsers.length) {
likeUsers.forEach(user => addBubbleContextMenuUserRow(menu, {
name: user.name,
avatarUrl: user.avatarUrl,
reaction: "👍"
}));
}
if (dislikeUsers.length) {
dislikeUsers.forEach(user => addBubbleContextMenuUserRow(menu, {
name: user.name,
avatarUrl: user.avatarUrl,
reaction: "👎"
}));
}
}
if (menu.children.length) {
menu.style.display = "block";
adjustMenuPosition();
}
};
menu.dataset.anchorX = String(x);
menu.dataset.anchorY = String(y);
renderMenu();
}
function initBubbleContextMenu() {
if (window.__hdeBubbleContextMenuBound) return;
window.__hdeBubbleContextMenuBound = true;
document.addEventListener("contextmenu", e => {
if (!CONFIG.compactChat) return;
const target = e.target;
if (!(target instanceof Element)) return;
const bubble = target.closest(".ticket-conversation__message-text, .ticket-conversation__message-html");
if (!bubble) return;
const msgEl = bubble.closest(".ticket-conversation__message");
if (!(msgEl instanceof HTMLElement)) return;
e.preventDefault();
e.stopPropagation();
showBubbleContextMenu(msgEl, e.clientX, e.clientY);
});
document.addEventListener("click", () => hideBubbleContextMenu());
document.addEventListener("keydown", e => {
if (e.key === "Escape") hideBubbleContextMenu();
});
document.addEventListener("scroll", () => hideBubbleContextMenu(), true);
window.addEventListener("resize", () => hideBubbleContextMenu());
}
function hasReactionSelected(likeBlock) {
if (!likeBlock) return false;
if (!likeBlock.classList.contains("ticket-conversation__show-on-hover")) return true;
if (likeBlock.querySelector(".ticket-conversation__like-icon.active")) return true;
if (likeBlock.querySelector('[aria-pressed="true"]')) return true;
if (likeBlock.querySelector('.active, .is-active, .selected, [class*="active"], [class*="selected"]')) return true;
const text = (likeBlock.textContent || "").trim();
if (!text) return false;
const nums = text.match(/\d+/g);
if (!nums) return false;
return nums.some(function(n) {
return parseInt(n, 10) > 0;
});
}
function hasReactionOnLikeButton(btn) {
if (!(btn instanceof HTMLElement)) return false;
const icon = btn.querySelector(".ticket-conversation__like-icon");
if (icon?.classList.contains("active")) return true;
const qty = btn.querySelector(".ticket-conversation__like-qty");
const qtyText = (qty?.textContent || "").trim();
if (qtyText) {
const n = parseInt(qtyText, 10);
if (Number.isFinite(n) && n > 0) return true;
}
return false;
}
function parseReactionQty(btn) {
if (!(btn instanceof HTMLElement)) return 0;
const qtyText = (btn.querySelector(".ticket-conversation__like-qty")?.textContent || "").trim();
const parsed = parseInt(qtyText, 10);
if (Number.isFinite(parsed) && parsed > 0) return parsed;
return hasReactionOnLikeButton(btn) ? 1 : 0;
}
function removeCustomReactionStrip(htmlEl) {
if (!(htmlEl instanceof HTMLElement)) return;
htmlEl.querySelector(":scope > .hde-reaction-strip")?.remove();
}
function upsertCustomReactionStrip(htmlEl, likeBlock) {
if (!(htmlEl instanceof HTMLElement) || !(likeBlock instanceof HTMLElement)) return false;
const placeReactionUsersPopoverNearBubble = btn => {
if (!(btn instanceof HTMLElement) || !(htmlEl instanceof HTMLElement)) return false;
const popoverId = btn.getAttribute("aria-describedby");
if (!popoverId) return false;
const popover = document.getElementById(popoverId);
if (!(popover instanceof HTMLElement)) return false;
const rect = htmlEl.getBoundingClientRect();
const popRect = popover.getBoundingClientRect();
const pad = 8;
let left = rect.left + 8;
let top = rect.bottom + 8;
if (left + popRect.width + pad > window.innerWidth) {
left = window.innerWidth - popRect.width - pad;
}
if (left < pad) left = pad;
if (top + popRect.height + pad > window.innerHeight) {
top = Math.max(pad, rect.top - popRect.height - 8);
}
popover.style.left = `${left}px`;
popover.style.top = `${top}px`;
popover.style.transformOrigin = "left top";
popover.style.zIndex = "2013";
return true;
};
const activeButtons = Array.from(likeBlock.querySelectorAll("button")).filter(btn => hasReactionOnLikeButton(btn));
if (!activeButtons.length) {
removeCustomReactionStrip(htmlEl);
return false;
}
let strip = htmlEl.querySelector(":scope > .hde-reaction-strip");
if (!(strip instanceof HTMLElement)) {
strip = document.createElement("div");
strip.className = "hde-reaction-strip";
htmlEl.appendChild(strip);
}
strip.innerHTML = "";
activeButtons.forEach(btn => {
const isDislike = btn.classList.contains("ticket-conversation__dislike-button");
const bubble = document.createElement("div");
bubble.className = `hde-reaction-bubble ${isDislike ? "is-dislike" : "is-like"}`;
const inner = document.createElement("div");
inner.className = "hde-reaction-inner";
const emoji = document.createElement("span");
emoji.className = "hde-reaction-emoji";
emoji.textContent = isDislike ? "👎" : "👍";
const qty = document.createElement("span");
qty.className = "hde-reaction-qty";
qty.textContent = String(parseReactionQty(btn));
inner.appendChild(emoji);
inner.appendChild(qty);
bubble.appendChild(inner);
bubble.addEventListener("click", e => {
e.preventDefault();
e.stopPropagation();
btn.click();
});
bubble.addEventListener("mouseenter", () => {
btn.dispatchEvent(new MouseEvent("mouseenter", {
bubbles: true,
cancelable: true
}));
btn.dispatchEvent(new MouseEvent("mouseover", {
bubbles: true,
cancelable: true
}));
if (bubble.__hdePopoverSyncTimer) {
clearInterval(bubble.__hdePopoverSyncTimer);
bubble.__hdePopoverSyncTimer = null;
}
setTimeout(() => placeReactionUsersPopoverNearBubble(btn), 0);
setTimeout(() => placeReactionUsersPopoverNearBubble(btn), 60);
let ticks = 0;
bubble.__hdePopoverSyncTimer = setInterval(() => {
ticks += 1;
placeReactionUsersPopoverNearBubble(btn);
if (ticks >= 12) {
clearInterval(bubble.__hdePopoverSyncTimer);
bubble.__hdePopoverSyncTimer = null;
}
}, 80);
});
bubble.addEventListener("mouseleave", () => {
btn.dispatchEvent(new MouseEvent("mouseleave", {
bubbles: true,
cancelable: true
}));
btn.dispatchEvent(new MouseEvent("mouseout", {
bubbles: true,
cancelable: true
}));
if (bubble.__hdePopoverSyncTimer) {
clearInterval(bubble.__hdePopoverSyncTimer);
bubble.__hdePopoverSyncTimer = null;
}
});
strip.appendChild(bubble);
});
return true;
}
function getLikeBlock(msgEl) {
return msgEl.querySelector(":scope > .ticket-conversation__message-user-block > .ticket-conversation__like") || msgEl.querySelector(":scope > .ticket-conversation__message-block > .hde-actions-inline > .ticket-conversation__like");
}
function processMessage(msgEl, isGrouped) {
const origMetaText = getMessageMetaText(msgEl);
const currentBlockEl = msgEl.querySelector(MSG_BLOCK_SELECTOR);
const currentActionsInline = !!currentBlockEl?.querySelector(":scope > .hde-actions-inline");
const currentNativeActions = !!currentBlockEl?.querySelector(":scope > .ticket-conversation__actions");
const currentHasActions = currentActionsInline || currentNativeActions;
const editedTime = extractEditedTime(msgEl);
const expectedEditedText = editedTime ? "Изм. " + editedTime : "";
const currentEditedTextEl = msgEl.querySelector(":scope > .ticket-conversation__message-block > .ticket-conversation__message-text > .ticket-conversation__message-html > .hde-edited-inside");
const currentEditedText = currentEditedTextEl ? (currentEditedTextEl.textContent || "").trim() : "";
const editedIsSynced = expectedEditedText === currentEditedText;
const currentLikeBlock = getLikeBlock(msgEl);
const reactionShouldBeVisible = hasReactionSelected(currentLikeBlock);
const reactionVisibilitySynced = msgEl.classList.contains("hde-has-reaction") === reactionShouldBeVisible;
const currentHtmlEl = msgEl.querySelector(MSG_HTML_SELECTOR);
const hasNameInside = !!currentHtmlEl?.querySelector(":scope > .hde-name-inside");
const hasTimeInside = !!currentHtmlEl?.querySelector(":scope > .hde-time-inside");
const wasGrouped = msgEl.classList.contains("hde-compact-grouped");
const hasRequiredInside = wasGrouped ? hasTimeInside : hasNameInside && hasTimeInside;
const canFastSkip = msgEl.classList.contains(DONE_CLASS) && wasGrouped === isGrouped && editedIsSynced && reactionVisibilitySynced;
if (canFastSkip && hasRequiredInside && currentHasActions) {
return extractDate(origMetaText);
}
msgEl.classList.add(DONE_CLASS);
const block = msgEl.querySelector(MSG_BLOCK_SELECTOR);
if (!block) return;
const meta = block.querySelector(":scope > .ticket-conversation__message-meta");
const actions = block.querySelector(":scope > .ticket-conversation__actions");
const userBlock = msgEl.querySelector(":scope > .ticket-conversation__message-user-block");
const likeBlock = currentLikeBlock || getLikeBlock(msgEl);
const isUserOrCommentMessage = msgEl.classList.contains("ticket-conversation__message_user") || msgEl.classList.contains("ticket-conversation__message_comment");
const htmlEl = block.querySelector(MSG_HTML_SELECTOR);
const orig = origMetaText;
let actionsRow = null;
if (meta && (actions || likeBlock)) {
actionsRow = ensureActionsInlineContainer(block, meta);
if (isUserOrCommentMessage) {
if (likeBlock && likeBlock.parentElement !== actionsRow && likeBlock.parentElement === userBlock) {
actionsRow.appendChild(likeBlock);
}
if (actions && actions.parentElement !== actionsRow && actions.parentElement === block) {
actionsRow.appendChild(actions);
}
} else {
if (actions && actions.parentElement !== actionsRow && actions.parentElement === block) {
actionsRow.appendChild(actions);
}
if (likeBlock && likeBlock.parentElement !== actionsRow && likeBlock.parentElement === userBlock) {
actionsRow.appendChild(likeBlock);
}
}
actionsRow.style.setProperty("display", "none", "important");
}
const hasReaction = hasReactionSelected(likeBlock);
msgEl.classList.toggle("hde-has-reaction", hasReaction);
if (htmlEl instanceof HTMLElement) {
const ensureBubbleBottomMetaLayout = () => {
const stripEl = htmlEl.querySelector(":scope > .hde-reaction-strip");
if (stripEl instanceof HTMLElement) {
htmlEl.appendChild(stripEl);
}
const bottomTimeEl = htmlEl.querySelector(":scope > .hde-time-inside");
if (bottomTimeEl instanceof HTMLElement) {
bottomTimeEl.style.removeProperty("position");
bottomTimeEl.style.removeProperty("top");
bottomTimeEl.style.removeProperty("left");
bottomTimeEl.style.removeProperty("right");
bottomTimeEl.style.removeProperty("bottom");
bottomTimeEl.style.removeProperty("float");
bottomTimeEl.style.removeProperty("clear");
bottomTimeEl.style.setProperty("display", "block", "important");
bottomTimeEl.style.setProperty("text-align", "right", "important");
bottomTimeEl.style.setProperty("margin", "4px 0 0 0", "important");
bottomTimeEl.style.setProperty("padding", "0 4px 0 0", "important");
htmlEl.appendChild(bottomTimeEl);
}
const editedEl = htmlEl.querySelector(":scope > .hde-edited-inside");
if (editedEl instanceof HTMLElement) {
editedEl.style.removeProperty("float");
editedEl.style.removeProperty("clear");
editedEl.style.setProperty("display", "block", "important");
editedEl.style.setProperty("margin", "2px 0 0 0", "important");
editedEl.style.setProperty("padding", "0", "important");
// Keep "Изм." below reactions/time so reaction never drops under edited marker.
htmlEl.appendChild(editedEl);
}
};
if (hasReaction && likeBlock instanceof HTMLElement) {
if (actionsRow instanceof HTMLElement && likeBlock.parentElement !== actionsRow) actionsRow.appendChild(likeBlock);
likeBlock.style.setProperty("display", "none", "important");
htmlEl.style.setProperty("position", "relative", "important");
upsertCustomReactionStrip(htmlEl, likeBlock);
htmlEl.style.setProperty("padding-bottom", "3px", "important");
ensureBubbleBottomMetaLayout();
msgEl.classList.add("hde-like-in-bubble");
} else {
removeCustomReactionStrip(htmlEl);
if (likeBlock instanceof HTMLElement) {
likeBlock.style.setProperty("display", "none", "important");
if (actionsRow instanceof HTMLElement && likeBlock.parentElement !== actionsRow) actionsRow.appendChild(likeBlock);
}
htmlEl.style.setProperty("padding-bottom", "3px", "important");
ensureBubbleBottomMetaLayout();
msgEl.classList.remove("hde-like-in-bubble");
}
// Second pass after DOM settles (fixes wrong order right after reaction click).
setTimeout(ensureBubbleBottomMetaLayout, 0);
}
if (isGrouped) {
msgEl.classList.add("hde-compact-grouped");
addInsideBubble(msgEl, "hde-time-inside", extractTime(orig));
if (editedTime) {
addInsideBubble(msgEl, "hde-edited-inside", "Изм. " + editedTime);
} else {
removeFromBubble(msgEl, "hde-edited-inside");
}
removeFromBubble(msgEl, "hde-name-inside");
} else {
msgEl.classList.remove("hde-compact-grouped");
addInsideBubble(msgEl, "hde-name-inside", extractName(orig));
addInsideBubble(msgEl, "hde-time-inside", extractTime(orig));
if (editedTime) {
addInsideBubble(msgEl, "hde-edited-inside", "Изм. " + editedTime);
} else {
removeFromBubble(msgEl, "hde-edited-inside");
}
}
return extractDate(orig);
}
function insertDateSeparator(parent, dateStr, beforeEl) {
const sep = document.createElement("span");
sep.className = DATE_SEP_CLASS;
sep.textContent = dateStr;
parent.insertBefore(sep, beforeEl || null);
return sep;
}
function insertTicketSeparator(parent, label, beforeEl) {
const sep = document.createElement("span");
sep.className = TICKET_SEP_CLASS;
sep.textContent = label;
parent.insertBefore(sep, beforeEl || null);
return sep;
}
function clearPreviousDialogsFromView() {
const container = getMessagesContainer();
if (!container) return;
container.querySelectorAll("[data-hde-history-ticket-id]").forEach(el => el.remove());
container.querySelectorAll("." + TICKET_SEP_CLASS).forEach(el => el.remove());
processAllMessages();
}
function getPreviousDialogsLimit() {
const n = parseInt(CONFIG.previousDialogsLimit, 10);
if (!Number.isFinite(n)) return DEFAULT_PREVIOUS_TICKETS_TO_LOAD;
return Math.min(10, Math.max(1, n));
}
function getMessagesContainer() {
return document.querySelector(".ticket-conversation__messages");
}
function getTicketId() {
const m = location.pathname.match(/\/ticket\/(\d+)/);
return m ? parseInt(m[1], 10) : null;
}
function hasCompactLayoutAnomalies(container) {
if (!(container instanceof Element)) return false;
const messages = container.querySelectorAll(".ticket-conversation__message." + DONE_CLASS);
for (const msgEl of messages) {
const block = msgEl.querySelector(MSG_BLOCK_SELECTOR);
const htmlEl = block?.querySelector(MSG_HTML_SELECTOR);
if (!block || !htmlEl) continue;
const isGrouped = msgEl.classList.contains("hde-compact-grouped");
const hasNameInside = !!htmlEl.querySelector(":scope > .hde-name-inside");
const hasTimeInside = !!htmlEl.querySelector(":scope > .hde-time-inside");
const hasRequiredInside = isGrouped ? hasTimeInside : hasNameInside && hasTimeInside;
const hasActionsInline = !!block.querySelector(":scope > .hde-actions-inline");
const hasNativeActions = !!block.querySelector(":scope > .ticket-conversation__actions");
const hasAnyActions = hasActionsInline || hasNativeActions;
if (!hasRequiredInside || !hasAnyActions) {
return true;
}
}
return false;
}
function formatCompactUserFieldValueText() {
if (!CONFIG.compactChat) return;
document.querySelectorAll("#ticket-app .ticket-left-block .ticket-user .ticket-user__field-value-text").forEach(el => {
if (!(el instanceof HTMLElement)) return;
el.style.setProperty("display", "block", "important");
el.style.setProperty("max-width", "100%", "important");
el.style.setProperty("overflow", "hidden", "important");
el.style.setProperty("text-overflow", "clip", "important");
el.style.setProperty("white-space", "normal", "important");
el.style.setProperty("user-select", "text", "important");
el.style.setProperty("font-family", "'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', '微软雅黑', Arial, sans-serif", "important");
el.style.removeProperty("word-break");
el.style.removeProperty("overflow-wrap");
el.style.removeProperty("-webkit-line-clamp");
el.style.removeProperty("-webkit-box-orient");
const source = String(el.dataset.hdeOriginalFieldValue || el.textContent || "").trim();
if (!el.dataset.hdeOriginalFieldValue) el.dataset.hdeOriginalFieldValue = source;
if (!/(->|→)/.test(source)) return;
const parts = source.split(/\s*(?:->|→)\s*/).map(part => part.trim()).filter(Boolean);
if (parts.length < 2) return;
const renderSignature = parts.join("\n");
if (el.dataset.hdeFieldValueRenderedFrom === renderSignature) return;
el.textContent = "";
parts.forEach(part => {
const line = document.createElement("span");
line.className = "hde-field-value-line";
line.style.setProperty("display", "block", "important");
line.style.setProperty("max-width", "100%", "important");
line.style.setProperty("overflow", "hidden", "important");
line.style.setProperty("text-overflow", "ellipsis", "important");
line.style.setProperty("white-space", "nowrap", "important");
line.style.setProperty("user-select", "text", "important");
line.style.setProperty("font-family", "'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', '微软雅黑', Arial, sans-serif", "important");
line.style.setProperty("font-size", "12px", "important");
line.style.setProperty("font-style", "normal", "important");
line.style.setProperty("font-weight", "400", "important");
line.style.setProperty("line-height", "1.25", "important");
line.textContent = part;
el.appendChild(line);
});
el.dataset.hdeFieldValueRenderedFrom = renderSignature;
});
}
var _lastProcessedHash = "";
function processAllMessages() {
if (!CONFIG.compactChat) return;
formatCompactUserFieldValueText();
const container = getMessagesContainer();
if (!container) return;
const msgs = container.querySelectorAll(".ticket-conversation__message");
let hash = msgs.length + "|";
msgs.forEach(function(m) {
hash += (m.getAttribute("data-user-id") || "") + ",";
});
const metas = container.querySelectorAll(".ticket-conversation__message-meta");
metas.forEach(function(m) {
hash += (m.textContent || "").trim() + ";";
});
const updated = container.querySelectorAll(".ticket-conversation__message-updated");
updated.forEach(function(u) {
hash += (u.textContent || "").trim() + ";";
});
const likes = container.querySelectorAll(".ticket-conversation__like");
likes.forEach(function(likeEl) {
hash += (likeEl.textContent || "").trim() + ";";
});
const reactionButtons = container.querySelectorAll(".ticket-conversation__like button");
reactionButtons.forEach(function(btn) {
hash += (btn.className || "") + "|";
hash += (btn.getAttribute("aria-pressed") || "") + ";";
});
if (hash === _lastProcessedHash) {
const needRecovery = hasCompactLayoutAnomalies(container);
if (!needRecovery) {
return;
}
}
_lastProcessedHash = hash;
container.querySelectorAll("." + DATE_SEP_CLASS).forEach(el => el.remove());
const messages = container.querySelectorAll(".ticket-conversation__message");
let lastUserId = null;
let lastDate = null;
messages.forEach(msgEl => {
const userId = msgEl.getAttribute("data-user-id") || "-1";
const isComment = msgEl.classList.contains("ticket-conversation__message_comment");
const isSystem = userId === "-1" || userId === "-0" || userId === "-2";
const isSameUser = userId === lastUserId && !isSystem && !isComment;
const msgDate = processMessage(msgEl, isSameUser);
if (msgDate && msgDate !== lastDate) {
insertDateSeparator(container, msgDate, msgEl);
lastDate = msgDate;
} else if (msgDate) {
lastDate = msgDate;
}
lastUserId = userId;
});
}
const MAX_PAGES_TO_LOAD = 10;
let _loadingOlder = false;
let _loadedPages = new Set;
let _totalPages = 1;
let _currentTicketId = null;
let _historyLoadedForTicketId = null;
function buildMessageDOM(msg, users) {
const user = users[String(msg.userId)] || {};
const userName = user.name || "Unknown";
const userType = user.type || "staff";
const userImg = user.image || "";
let typeClass = "_staff";
if (userType === "user" || userType === "client") typeClass = "_user";
if (msg.type === "comment" || msg.type === "note") typeClass = "_comment";
if (msg.userId == -1 || msg.userId == -2) typeClass = "_comment";
const msgEl = document.createElement("div");
msgEl.setAttribute("data-v-1638218a", "");
msgEl.setAttribute("data-v-4a60d6b5", "");
msgEl.setAttribute("data-user-id", msg.userId);
if (typeClass === "_comment") {
msgEl.setAttribute("data-comment-id", msg.id);
} else {
msgEl.setAttribute("data-post-id", msg.id);
}
msgEl.className = "ticket-conversation__message ticket-conversation__message" + typeClass;
const userBlock = document.createElement("div");
userBlock.setAttribute("data-v-1638218a", "");
userBlock.setAttribute("data-v-169b4e77", "");
userBlock.className = "ticket-conversation__message-user-block";
const avatarDiv = document.createElement("div");
avatarDiv.className = "ticket-conversation__message-image ticket-conversation__message-image" + typeClass;
avatarDiv.style.cssText = 'background-image: url("' + userImg + '"); cursor: pointer;';
userBlock.appendChild(avatarDiv);
const block = document.createElement("div");
block.className = "ticket-conversation__message-block";
const meta = document.createElement("div");
meta.className = "ticket-conversation__message-meta ticket-conversation__message-meta" + typeClass;
meta.textContent = " " + userName + " " + msg.createdAt + " ";
meta.dataset.origText = userName + " " + msg.createdAt;
block.appendChild(meta);
const textWrap = document.createElement("div");
textWrap.className = "ticket-conversation__message-text ticket-conversation__message-text" + typeClass;
if (typeClass === "_comment") {
textWrap.classList.add("ticket-conversation__message-text_system");
}
const htmlDiv = document.createElement("div");
htmlDiv.className = "ticket-conversation__message-html";
htmlDiv.innerHTML = msg.text || "";
if (msg.files && msg.files.length > 0) {
msg.files.forEach(file => {
const fileLink = document.createElement("div");
fileLink.style.cssText = "margin-top:4px;";
if (file.previewType === "image" || file.icon === "icon-image") {
const img = document.createElement("img");
img.src = file.thumb || file.preview;
img.style.cssText = "max-width:200px;max-height:150px;border-radius:4px;cursor:pointer;";
img.onclick = () => window.open(file.download || file.preview, "_blank");
fileLink.appendChild(img);
} else {
const a = document.createElement("a");
a.href = file.download;
a.target = "_blank";
a.textContent = file.name || "Файл";
a.style.cssText = "color:#23869b;font-size:11px;text-decoration:none;display:inline-flex;align-items:center;gap:4px;";
fileLink.appendChild(a);
}
htmlDiv.appendChild(fileLink);
});
}
textWrap.appendChild(htmlDiv);
block.appendChild(textWrap);
msgEl.appendChild(userBlock);
msgEl.appendChild(block);
return msgEl;
}
function findFirstVisibleMessage(container) {
var msgs = container.querySelectorAll(":scope > .ticket-conversation__message");
var ctTop = container.getBoundingClientRect().top;
for (var i = 0; i < msgs.length; i++) {
var r = msgs[i].getBoundingClientRect();
if (r.top - ctTop >= -50) return msgs[i];
}
return null;
}
async function loadOlderPage(pageNum) {
debugLog("[HDE Compact] → loadOlderPage(" + pageNum + ") start");
var ticketId = getTicketId();
if (!ticketId) {
console.warn("[HDE Compact] нет ticketId");
return;
}
_loadingOlder = true;
showLoadIndicator();
try {
var url = "/ru/ticket/data/conversation/id/" + ticketId + "/?page=" + pageNum;
debugLog("[HDE Compact] fetch:", url);
var resp = await fetch(url, {
headers: {
Accept: "application/json",
"X-Requested-With": "XMLHttpRequest"
}
});
if (!resp.ok) throw new Error("HTTP " + resp.status);
var data = await resp.json();
debugLog("[HDE Compact] ответ: messages=", data.messages?.length, "pages=", data.pagination?.totalPages);
if (data.pagination) {
_totalPages = data.pagination.totalPages;
} else if (data.messages && data.messages.length > 0) {
if (pageNum >= _totalPages) _totalPages = pageNum + 1;
}
if (!data.messages || data.messages.length === 0) {
hideLoadIndicator();
return;
}
var container = getMessagesContainer();
if (!container) return;
var anchorMsg = findFirstVisibleMessage(container);
var anchorOffset = anchorMsg ? anchorMsg.getBoundingClientRect().top - container.getBoundingClientRect().top : 0;
var firstExisting = container.firstChild;
for (var mi = 0; mi < data.messages.length; mi++) {
var msg = data.messages[mi];
var eid = msg.type === "comment" || msg.type === "note" || msg.userId == -1 || msg.userId == -2 ? '[data-comment-id="' + msg.id + '"]' : '[data-post-id="' + msg.id + '"]';
if (container.querySelector(eid)) continue;
var el = buildMessageDOM(msg, data.users || {});
el.setAttribute("data-hde-loaded", "true");
container.insertBefore(el, firstExisting);
}
debugLog("[HDE Compact] сообщения вставлены");
var histBtn = container.querySelector(":scope > .ticket-detail__history");
if (histBtn && firstExisting) {
container.insertBefore(histBtn, container.firstChild);
}
requestAnimationFrame(function() {
if (anchorMsg && anchorMsg.parentElement) {
var nOff = anchorMsg.getBoundingClientRect().top - container.getBoundingClientRect().top;
container.scrollTop += nOff - anchorOffset;
}
});
processAllMessages();
_loadedPages.add(pageNum);
debugLog("[HDE Compact] ← loadOk page=" + pageNum + " total=" + _totalPages);
var nextP = pageNum + 1;
var loadedCount = _loadedPages.size - 1;
var shouldAuto = !_loadedPages.has(nextP) && (nextP <= _totalPages || data.messages.length > 0) && loadedCount < MAX_PAGES_TO_LOAD;
if (shouldAuto) {
debugLog("[HDE Compact] автозагрузка page=" + nextP + " (загружено " + loadedCount + "/" + MAX_PAGES_TO_LOAD + ")");
setTimeout(function() {
loadOlderPage(nextP);
}, 300);
} else if (loadedCount >= MAX_PAGES_TO_LOAD) {
debugLog("[HDE Compact] лимит " + MAX_PAGES_TO_LOAD + " страниц достигнут, остановка");
hideLoadIndicator();
}
} catch (err) {
console.warn("[HDE Compact] Ошибка загрузки:", err);
console.warn("[HDE Compact] ошибка загрузки:", err.message);
} finally {
_loadingOlder = false;
hideLoadIndicator();
}
}
function showLoadIndicator() {}
function hideLoadIndicator() {}
async function fetchHistoryTickets(ticketId) {
const url = "/ru/ticket/data/history/id/" + ticketId + "/?page=1";
const resp = await fetch(url, {
headers: {
Accept: "application/json",
"X-Requested-With": "XMLHttpRequest"
}
});
if (!resp.ok) throw new Error("History HTTP " + resp.status);
return resp.json();
}
async function fetchConversationPage(ticketId, pageNum) {
const url = "/ru/ticket/data/conversation/id/" + ticketId + "/?page=" + pageNum;
const resp = await fetch(url, {
headers: {
Accept: "application/json",
"X-Requested-With": "XMLHttpRequest"
}
});
const finalUrl = String(resp.url || "");
if (!resp.ok || /captcha|recaptcha|hcaptcha/i.test(finalUrl)) {
const err = new Error("Conversation HTTP " + resp.status);
err.status = Number(resp.status || 0);
err.url = finalUrl;
throw err;
}
return resp.json();
}
function getMessageSelectorByPayload(msg) {
const isComment = msg.type === "comment" || msg.type === "note" || msg.userId == -1 || msg.userId == -2;
return isComment ? '[data-comment-id="' + msg.id + '"]' : '[data-post-id="' + msg.id + '"]';
}
function getPreviousDialogsInsertAnchor(container) {
for (let c = container.firstChild; c; c = c.nextSibling) {
if (c.nodeType !== 1) continue;
if (c.classList && c.classList.contains("ticket-conversation__message")) {
if (!c.hasAttribute("data-hde-history-ticket-id")) return c;
}
}
return container.firstChild;
}
function captureScrollAnchor(container) {
if (!container) return null;
const anchorEl = findFirstVisibleMessage(container);
if (!anchorEl) {
return {
el: null,
offsetTop: 0,
scrollTop: container.scrollTop
};
}
return {
el: anchorEl,
offsetTop: anchorEl.getBoundingClientRect().top - container.getBoundingClientRect().top,
scrollTop: container.scrollTop
};
}
function restoreScrollAnchor(container, anchor) {
if (!container || !anchor) return;
if (anchor.el && anchor.el.parentElement) {
const nextOffsetTop = anchor.el.getBoundingClientRect().top - container.getBoundingClientRect().top;
container.scrollTop += nextOffsetTop - anchor.offsetTop;
return;
}
if (Number.isFinite(anchor.scrollTop)) {
container.scrollTop = anchor.scrollTop;
}
}
async function loadPreviousTicketsHistory() {
if (!CONFIG.autoLoadHistory || !CONFIG.autoLoadPreviousDialogs) return;
injectTicketSepStyle();
const currentTicketId = getTicketId();
if (!currentTicketId || _historyLoadedForTicketId === currentTicketId) return;
const container = getMessagesContainer();
if (!container) return;
const scrollAnchor = captureScrollAnchor(container);
try {
const historyItems = await fetchHistoryTickets(currentTicketId);
if (!Array.isArray(historyItems) || historyItems.length === 0) {
_historyLoadedForTicketId = currentTicketId;
return;
}
const candidates = historyItems.filter(item => item && item.id && item.id !== currentTicketId).filter(item => item.watchAccess !== false).sort((a, b) => parseInt(a.id, 10) - parseInt(b.id, 10)).slice(-getPreviousDialogsLimit());
const insertAnchor = getPreviousDialogsInsertAnchor(container);
if (!insertAnchor) {
_historyLoadedForTicketId = currentTicketId;
return;
}
for (let i = candidates.length - 1; i >= 0; i--) {
const t = candidates[i];
let allMessages = [];
let usersMap = {};
let pagesToRead = 1;
try {
const firstPage = await fetchConversationPage(t.id, 1);
usersMap = Object.assign({}, firstPage.users || {});
if (Array.isArray(firstPage.messages)) {
allMessages = allMessages.concat(firstPage.messages);
}
const totalPages = firstPage.pagination?.totalPages || 1;
pagesToRead = Math.min(totalPages, MAX_PAGES_PER_PREVIOUS_TICKET);
} catch (e) {
continue;
}
for (let p = 2; p <= pagesToRead; p++) {
try {
const pageData = await fetchConversationPage(t.id, p);
usersMap = Object.assign(usersMap, pageData.users || {});
if (Array.isArray(pageData.messages)) {
allMessages = allMessages.concat(pageData.messages);
}
} catch (e) {
break;
}
}
if (allMessages.length === 0) continue;
allMessages.reverse();
const insertBefore = insertAnchor;
let insertedCount = 0;
let ticketDateStr = "";
try {
const totalPagesForDate = (await fetchConversationPage(t.id, 1)).pagination?.totalPages || 1;
const lastPageData = totalPagesForDate > 1 ? await fetchConversationPage(t.id, totalPagesForDate) : null;
const oldestMsg = lastPageData && Array.isArray(lastPageData.messages) && lastPageData.messages.length > 0 ? lastPageData.messages[0] : allMessages.length > 0 ? allMessages[0] : null;
if (oldestMsg && oldestMsg.createdAt) {
const raw = String(oldestMsg.createdAt);
const m = raw.match(/(\d{1,2}\.\d{2}\.\d{2,4})\s+(\d{1,2}:\d{2})/);
if (m) ticketDateStr = " · " + m[1] + " " + m[2]; else {
const d = raw.match(/(\d{1,2}\.\d{2}\.\d{2,4})/);
if (d) ticketDateStr = " · " + d[1];
}
}
} catch (e) {}
const sep = document.createElement("span");
sep.className = TICKET_SEP_CLASS;
const sepLink = document.createElement("a");
const _base = location.pathname.match(/(\/ru\/ticket\/list\/filter\/id\/\d+)/);
const _ticketBase = _base ? _base[1] : "/ru/ticket/list";
sepLink.href = _ticketBase + "/ticket/" + t.id;
sepLink.target = "_blank";
sepLink.rel = "noopener noreferrer";
sepLink.textContent = "#" + (t.uniqueId || t.id);
sepLink.style.cssText = "color: inherit; text-decoration: none; border-bottom: 1px dashed currentColor;";
sepLink.addEventListener("mouseover", function() {
sepLink.style.opacity = "0.7";
});
sepLink.addEventListener("mouseout", function() {
sepLink.style.opacity = "1";
});
sep.appendChild(document.createTextNode("Обращение "));
sep.appendChild(sepLink);
sep.appendChild(document.createTextNode(ticketDateStr));
container.insertBefore(sep, insertBefore);
for (let mi = allMessages.length - 1; mi >= 0; mi--) {
const msg = allMessages[mi];
const selector = getMessageSelectorByPayload(msg);
if (container.querySelector(selector)) continue;
const el = buildMessageDOM(msg, usersMap);
el.setAttribute("data-hde-history-ticket-id", String(t.id));
container.insertBefore(el, sep);
insertedCount++;
}
if (insertedCount === 0) sep.remove(); else restoreScrollAnchor(container, scrollAnchor);
}
_historyLoadedForTicketId = currentTicketId;
processAllMessages();
requestAnimationFrame(() => {
restoreScrollAnchor(container, scrollAnchor);
});
} catch (err) {
console.warn("[HDE Compact] Ошибка подгрузки прошлых обращений:", err);
}
}
function startLoadChain() {
var ticketId = getTicketId();
if (!ticketId) return;
if (ticketId !== _currentTicketId) {
_currentTicketId = ticketId;
_loadedPages.clear();
var curPageMatch = location.pathname.match(/\/page\/(\d+)/);
var curPage = curPageMatch ? parseInt(curPageMatch[1], 10) : 1;
for (var i = 1; i <= curPage; i++) _loadedPages.add(i);
}
var nextPage = 1;
while (_loadedPages.has(nextPage)) nextPage++;
var alreadyLoaded = _loadedPages.size - 1;
if (alreadyLoaded >= MAX_PAGES_TO_LOAD) {
debugLog("[HDE Compact] достигнут лимит " + MAX_PAGES_TO_LOAD + " страниц");
return;
}
if (nextPage <= _totalPages || _totalPages <= 1) {
loadOlderPage(nextPage);
}
}
function detectTotalPages() {
if (!CONFIG.autoLoadHistory) return;
var pagerItems = document.querySelectorAll(".el-pager .number");
var maxPage = 1;
pagerItems.forEach(function(el) {
var n = parseInt(el.textContent.trim(), 10);
if (n && n > maxPage && n < 1e3) maxPage = n;
});
if (maxPage > 1) _totalPages = maxPage;
}
function initAutoLoad() {
if (!CONFIG.autoLoadHistory && !CONFIG.autoLoadPreviousDialogs) return;
_currentTicketId = getTicketId();
_loadedPages.clear();
var curPageMatch = location.pathname.match(/\/page\/(\d+)/);
var curPage = curPageMatch ? parseInt(curPageMatch[1], 10) : 1;
for (var i = 1; i <= curPage; i++) _loadedPages.add(i);
if (CONFIG.autoLoadHistory) detectTotalPages();
if (CONFIG.autoLoadHistory) setTimeout(startLoadChain, 800);
if (CONFIG.autoLoadHistory && CONFIG.autoLoadPreviousDialogs) setTimeout(loadPreviousTicketsHistory, 1200);
}
var _rafId = null;
function scheduleProcess() {
if (_rafId) return;
_rafId = requestAnimationFrame(function() {
_rafId = null;
processAllMessages();
syncResizablePanels();
if (CONFIG.leftTabsMirror) {
scheduleLeftTabsMirrorSync();
}
});
}
function observeMessages() {
var target = document.getElementById("ticket-app") || document.body;
new MutationObserver(scheduleProcess).observe(target, {
childList: true,
subtree: true,
characterData: true,
attributes: true,
attributeFilter: [ "class", "aria-pressed" ]
});
}
const RESIZABLE_PANELS_STORAGE_KEY = "hde-resizable-panels-width-v1";
const RESIZABLE_PANELS_STYLE_ID = "hde-resizable-panels-style";
const RESIZABLE_PANELS = [ {
key: "ticket-sidebar",
selector: "#ticket-app .ticket-sidebar",
min: 150,
max: 560
}, {
key: "left-tabs-column",
selector: `#${LEFT_TABS_COLUMN_ID}`,
min: 150,
max: 420
}, {
key: "ticket-left-block",
selector: "#ticket-app .ticket-left-block",
min: 150,
max: 560
} ];
function clearResizablePanelStateBySelector(selector) {
const el = document.querySelector(selector);
if (!(el instanceof HTMLElement)) return;
el.style.width = "";
el.style.minWidth = "";
el.style.flex = "";
el.classList.remove("hde-resizable-panel");
el.querySelectorAll(":scope > .hde-panel-resize-handle").forEach(handle => handle.remove());
delete el.dataset.hdePanelResizeBound;
}
function pinRightBlockToEdge() {
const rightBlock = document.querySelector("#ticket-app .ticket-right-block");
if (!(rightBlock instanceof HTMLElement)) return;
const measured = rightBlock.getBoundingClientRect().width || rightBlock.offsetWidth || 320;
const pinnedWidth = Math.min(560, Math.max(220, Math.round(measured)));
rightBlock.style.width = `${pinnedWidth}px`;
rightBlock.style.minWidth = `${pinnedWidth}px`;
rightBlock.style.maxWidth = `${pinnedWidth}px`;
rightBlock.style.marginLeft = "0";
rightBlock.style.marginRight = "0";
rightBlock.style.flex = `0 0 ${pinnedWidth}px`;
}
function getResizablePanelWidths() {
try {
const raw = localStorage.getItem(RESIZABLE_PANELS_STORAGE_KEY);
if (!raw) return {};
const parsed = JSON.parse(raw);
return parsed && typeof parsed === "object" ? parsed : {};
} catch (e) {
return {};
}
}
function saveResizablePanelWidths(widths) {
try {
localStorage.setItem(RESIZABLE_PANELS_STORAGE_KEY, JSON.stringify(widths || {}));
} catch (e) {}
}
function ensureResizablePanelsStyle() {
if (document.getElementById(RESIZABLE_PANELS_STYLE_ID)) return;
const style = document.createElement("style");
style.id = RESIZABLE_PANELS_STYLE_ID;
style.textContent = `
.hde-resizable-panel {
position: relative;
}
#ticket-app .ticket-left-block.hde-resizable-panel {
overflow-x: hidden !important;
}
.hde-panel-resize-handle {
position: absolute;
top: 0;
right: -3px;
width: 6px;
height: 100%;
cursor: ew-resize;
opacity: 0;
z-index: 8;
}
.hde-panel-resize-handle.is-left {
left: -3px;
right: auto;
}
.hde-panel-resize-handle::before {
content: '';
display: none;
}
`;
(document.head || document.documentElement)?.appendChild(style);
}
function clampPanelWidth(width, cfg) {
const w = Number(width);
if (!Number.isFinite(w)) return null;
return Math.min(cfg.max, Math.max(cfg.min, Math.round(w)));
}
function applyResizablePanelWidth(el, cfg, width) {
if (!(el instanceof HTMLElement)) return;
const safeWidth = clampPanelWidth(width, cfg);
if (!Number.isFinite(safeWidth)) return;
if (cfg.key === "ticket-central-block") {
el.style.width = "auto";
el.style.minWidth = `${safeWidth}px`;
el.style.flex = `1 1 ${safeWidth}px`;
return;
}
el.style.width = `${safeWidth}px`;
el.style.minWidth = `${safeWidth}px`;
el.style.flex = `0 0 ${safeWidth}px`;
}
function clampPanelWidthByLayout(el, cfg, width) {
const safeWidth = clampPanelWidth(width, cfg);
if (!Number.isFinite(safeWidth)) return null;
if (cfg.key !== "ticket-central-block") return safeWidth;
const parent = el.parentElement;
if (!(parent instanceof HTMLElement)) return safeWidth;
const parentWidth = parent.getBoundingClientRect().width || parent.clientWidth || 0;
if (!Number.isFinite(parentWidth) || parentWidth <= 0) return safeWidth;
const siblingsWidth = Array.from(parent.children).reduce((sum, node) => {
if (node === el || !(node instanceof HTMLElement)) return sum;
const rect = node.getBoundingClientRect();
const w = rect.width || node.offsetWidth || 0;
return sum + (Number.isFinite(w) ? w : 0);
}, 0);
const maxByLayout = Math.floor(parentWidth - siblingsWidth);
if (!Number.isFinite(maxByLayout) || maxByLayout <= 0) return safeWidth;
return Math.min(safeWidth, Math.max(cfg.min, maxByLayout));
}
function bindResizablePanel(el, cfg, widths) {
if (!(el instanceof HTMLElement)) return;
el.classList.add("hde-resizable-panel");
if (el.dataset.hdePanelResizeBound === "1") return;
el.dataset.hdePanelResizeBound = "1";
let handle = el.querySelector(":scope > .hde-panel-resize-handle");
if (!handle) {
handle = document.createElement("span");
handle.className = "hde-panel-resize-handle";
handle.setAttribute("aria-hidden", "true");
el.appendChild(handle);
}
if (cfg.handle === "left") handle.classList.add("is-left"); else handle.classList.remove("is-left");
handle.addEventListener("mousedown", event => {
if (!(event instanceof MouseEvent)) return;
event.preventDefault();
event.stopPropagation();
const startX = event.clientX;
const startWidth = el.getBoundingClientRect().width || el.offsetWidth || cfg.min;
const direction = cfg.handle === "left" ? -1 : 1;
const onMove = moveEvent => {
if (!(moveEvent instanceof MouseEvent)) return;
const rawWidth = startWidth + (moveEvent.clientX - startX) * direction;
const nextWidth = clampPanelWidthByLayout(el, cfg, rawWidth);
if (!Number.isFinite(nextWidth)) return;
applyResizablePanelWidth(el, cfg, nextWidth);
widths[cfg.key] = nextWidth;
saveResizablePanelWidths(widths);
};
const onUp = () => {
document.removeEventListener("mousemove", onMove);
document.removeEventListener("mouseup", onUp);
};
document.addEventListener("mousemove", onMove);
document.addEventListener("mouseup", onUp);
});
}
function syncResizablePanels() {
clearResizablePanelStateBySelector("#ticket-app .ticket-central-block");
clearResizablePanelStateBySelector("#ticket-app .ticket-right-block");
pinRightBlockToEdge();
if (!CONFIG.resizablePanels) {
RESIZABLE_PANELS.forEach(cfg => {
const el = document.querySelector(cfg.selector);
if (!(el instanceof HTMLElement)) return;
el.style.width = "";
el.style.minWidth = "";
el.style.flex = "";
el.classList.remove("hde-resizable-panel");
el.querySelectorAll(":scope > .hde-panel-resize-handle").forEach(handle => handle.remove());
delete el.dataset.hdePanelResizeBound;
});
return;
}
ensureResizablePanelsStyle();
const widths = getResizablePanelWidths();
delete widths["ticket-central-block"];
delete widths["ticket-right-block"];
let changed = false;
RESIZABLE_PANELS.forEach(cfg => {
const el = document.querySelector(cfg.selector);
if (!(el instanceof HTMLElement)) return;
let savedWidth = clampPanelWidth(widths[cfg.key], cfg);
if (!Number.isFinite(savedWidth)) {
const measuredWidth = el.getBoundingClientRect().width || el.offsetWidth;
const initialWidth = clampPanelWidth(measuredWidth, cfg);
if (Number.isFinite(initialWidth)) {
widths[cfg.key] = initialWidth;
savedWidth = initialWidth;
changed = true;
}
}
if (Number.isFinite(savedWidth)) applyResizablePanelWidth(el, cfg, savedWidth);
bindResizablePanel(el, cfg, widths);
});
if (changed) saveResizablePanelWidths(widths);
}
function resetResizablePanels() {
saveResizablePanelWidths({});
clearResizablePanelStateBySelector("#ticket-app .ticket-central-block");
clearResizablePanelStateBySelector("#ticket-app .ticket-right-block");
RESIZABLE_PANELS.forEach(cfg => {
const el = document.querySelector(cfg.selector);
if (!(el instanceof HTMLElement)) return;
el.style.width = "";
el.style.minWidth = "";
el.style.flex = "";
delete el.dataset.hdePanelResizeBound;
el.querySelectorAll(":scope > .hde-panel-resize-handle").forEach(handle => handle.remove());
});
syncResizablePanels();
}
let _colleagueStatusesInterval = null;
let _colleagueStatusesInFlight = false;
let _colleagueStatusesRetryTimer = null;
const COLLEAGUE_STATUSES_CACHE_KEY = "hde-colleague-statuses-cache-v1";
const COLLEAGUE_STATUSES_CACHE_MAX_AGE_MS = 6 * 60 * 60 * 1e3;
function getColleagueStatusesCache() {
try {
const raw = localStorage.getItem(COLLEAGUE_STATUSES_CACHE_KEY);
if (!raw) return null;
const parsed = JSON.parse(raw);
if (!parsed || typeof parsed !== "object") return null;
const html = typeof parsed.html === "string" ? parsed.html : "";
const savedAt = Number(parsed.savedAt || 0);
if (!html || !Number.isFinite(savedAt) || savedAt <= 0) return null;
if (Date.now() - savedAt > COLLEAGUE_STATUSES_CACHE_MAX_AGE_MS) return null;
return {
html: html,
savedAt: savedAt
};
} catch (e) {
return null;
}
}
function saveColleagueStatusesCache(html) {
try {
const clean = String(html || "");
if (!clean.trim()) return;
localStorage.setItem(COLLEAGUE_STATUSES_CACHE_KEY, JSON.stringify({
html: clean,
savedAt: Date.now()
}));
} catch (e) {}
}
function ensureColleagueStatusesContainer() {
const sidebar = document.querySelector(".ticket-sidebar");
if (!sidebar) return null;
let container = sidebar.querySelector(".staffs-injected-block");
if (!container) {
container = document.createElement("div");
container.className = "staffs-injected-block";
sidebar.appendChild(container);
}
return container;
}
function renderColleagueStatusesFromCache() {
const container = ensureColleagueStatusesContainer();
if (!container) return false;
if (container.innerHTML.trim()) return true;
const cached = getColleagueStatusesCache();
if (!cached?.html) return false;
container.innerHTML = cached.html;
applyColleagueMemberNameEllipsis(container);
return true;
}
function applyColleagueMemberNameEllipsis(container) {
if (!(container instanceof HTMLElement)) return;
container.querySelectorAll(".staffs-injected-member-row").forEach(row => {
if (!(row instanceof HTMLElement)) return;
row.style.minWidth = "0";
});
container.querySelectorAll(".staffs-injected-member-name").forEach(nameEl => {
if (!(nameEl instanceof HTMLElement)) return;
nameEl.style.display = "block";
nameEl.style.flex = "1 1 auto";
nameEl.style.minWidth = "0";
nameEl.style.whiteSpace = "nowrap";
nameEl.style.overflow = "hidden";
nameEl.style.textOverflow = "ellipsis";
});
}
function removeColleagueStatusesBlock() {
document.querySelectorAll(".staffs-injected-block").forEach(el => el.remove());
}
function stopColleagueStatuses() {
if (_colleagueStatusesInterval) {
clearInterval(_colleagueStatusesInterval);
_colleagueStatusesInterval = null;
}
if (_colleagueStatusesRetryTimer) {
clearTimeout(_colleagueStatusesRetryTimer);
_colleagueStatusesRetryTimer = null;
}
_colleagueStatusesInFlight = false;
removeColleagueStatusesBlock();
}
function updateColleagueStatusesBlock() {
if (_colleagueStatusesInFlight) return;
_colleagueStatusesInFlight = true;
const container = ensureColleagueStatusesContainer();
if (!container) {
_colleagueStatusesInFlight = false;
if (!_colleagueStatusesRetryTimer) {
_colleagueStatusesRetryTimer = setTimeout(() => {
_colleagueStatusesRetryTimer = null;
if (CONFIG.colleagueStatuses) updateColleagueStatusesBlock();
}, 600);
}
return;
}
const iframe = document.createElement("iframe");
iframe.style.display = "none";
iframe.src = "/ru/dashboard/staffs/";
document.body.appendChild(iframe);
let attempts = 0;
const finish = () => {
setTimeout(() => iframe.remove(), 500);
_colleagueStatusesInFlight = false;
};
const check = () => {
try {
const doc = iframe.contentDocument;
const staffsDiv = doc?.querySelector(".dashboard-staffs__content.ps");
const headerRow = doc?.querySelector(".dashboard-staffs__header .dashboard-staffs__row");
if (!staffsDiv || !headerRow) {
if (++attempts < 5) {
_colleagueStatusesRetryTimer = setTimeout(check, 1500);
return;
}
finish();
return;
}
const columns = Array.from(headerRow.querySelectorAll(".dashboard-staffs__column:not(.dashboard-staffs__column_columns-settings)"));
const nameIndex = columns.findIndex(col => col.textContent.trim() === "Имя");
const groupIndex = columns.findIndex(col => col.textContent.trim() === "Группа");
const statusIndex = columns.findIndex(col => col.textContent.trim() === "Статус");
const ticketsIndex = columns.findIndex(col => col.textContent.trim() === "Активные заявки");
if (nameIndex < 0 || groupIndex < 0 || statusIndex < 0 || ticketsIndex < 0) {
finish();
return;
}
const groups = {};
const groupOrder = [];
staffsDiv.querySelectorAll(".dashboard-staffs__row").forEach(row => {
const cols = Array.from(row.querySelectorAll(".dashboard-staffs__column:not(.dashboard-staffs__column_columns-settings)"));
const nameEl = cols[nameIndex]?.querySelector(".el-button span");
const name = nameEl?.textContent.trim() || "Неизвестно";
const group = cols[groupIndex]?.textContent.trim() || "Неизвестно";
let ticketsCount = 0;
const ticketsBtn = cols[ticketsIndex]?.querySelector(".el-button span");
if (ticketsBtn) {
const match = ticketsBtn.textContent.match(/\d+/);
ticketsCount = match ? parseInt(match[0], 10) : 0;
}
let statusText = "Неизвестно";
const selected = row.querySelector(".el-select-dropdown__item.selected span");
if (selected) {
statusText = selected.textContent.trim().replace(/^\[.*?\]\s*/, "").trim();
} else {
const input = row.querySelector(".el-input__inner");
if (input?.value) statusText = input.value.trim().replace(/^\[.*?\]\s*/, "").trim();
}
let statusClass = "status-offline";
if (statusText.includes("В сети")) statusClass = "status-online"; else if (statusText.includes("Невидимка")) statusClass = "status-invisible"; else if (statusText.includes("Перерыв") || statusText.includes("Обед")) statusClass = "status-break";
if (!groups[group]) {
groups[group] = [];
groupOrder.push(group);
}
groups[group].push({
name: name,
statusText: statusText,
statusClass: statusClass,
ticketsCount: ticketsCount
});
});
let html = "";
groupOrder.forEach(groupName => {
const totalTickets = groups[groupName].reduce((sum, member) => sum + member.ticketsCount, 0);
html += `<div class="staffs-injected-group-header">\n <span class="staffs-injected-group-total">${totalTickets > 0 ? totalTickets : ""}</span>\n <span class="staffs-injected-group-name">${groupName}</span>\n </div>\n <ul class="staffs-injected-list">`;
groups[groupName].forEach(member => {
const badgeValue = member.ticketsCount > 9 ? "9+" : member.ticketsCount > 0 ? member.ticketsCount : "";
const badgeClasses = `staffs-injected-badge ${member.statusClass} ${member.ticketsCount > 0 ? "has-tickets" : "no-tickets"}`;
html += `<li class="staffs-injected-member-row">\n <span class="${badgeClasses}">${badgeValue}</span>\n <span class="staffs-injected-member-name ${member.statusClass}">${member.name}</span>\n <span class="staffs-injected-member-status ${member.statusClass}">${member.statusText}</span>\n </li>`;
});
html += "</ul>";
});
container.innerHTML = html;
applyColleagueMemberNameEllipsis(container);
saveColleagueStatusesCache(html);
} catch (e) {
debugLog("[HDE Compact] коллеги: ошибка обновления блока", e);
} finally {
finish();
}
};
iframe.onload = () => setTimeout(check, 2e3);
iframe.onerror = () => {
iframe.remove();
_colleagueStatusesInFlight = false;
};
}
function startColleagueStatuses() {
if (_colleagueStatusesInterval) return;
renderColleagueStatusesFromCache();
updateColleagueStatusesBlock();
_colleagueStatusesInterval = setInterval(updateColleagueStatusesBlock, 1e4);
}
function syncColleagueStatusesModule() {
if (CONFIG.colleagueStatuses) startColleagueStatuses(); else stopColleagueStatuses();
}
function removeLeftTabsMirror() {
document.getElementById(LEFT_TABS_MIRROR_ID)?.remove();
document.getElementById(LEFT_TABS_COLUMN_ID)?.remove();
}
let _leftTabsMirrorSyncScheduled = false;
let _leftTabsMirrorSyncInProgress = false;
let _leftTabsMirrorSyncPending = false;
let _leftTabsMirrorLastSignature = "";
const _leftTabsPreviewByName = Object.create(null);
const _leftTabsPreviewByTicketId = Object.create(null);
const _leftTabsLastMessageTsByName = Object.create(null);
const _leftTabsLastMessageTsByTicketId = Object.create(null);
const _leftTabsLastMessageTimeTextByName = Object.create(null);
const _leftTabsLastMessageTimeTextByTicketId = Object.create(null);
const _leftTabsSeenOrderByName = Object.create(null);
const _leftTabsSeenOrderByTicketId = Object.create(null);
const _leftTabsSeenOrderByStoreKey = Object.create(null);
let _leftTabsSeenOrderSeq = 0;
const FILE_ONLY_PREVIEW = "📝";
let _leftTabsPreviewPollInterval = null;
let _leftTabsDepartmentPollInterval = null;
let _leftTabsPreviewPollInFlight = false;
let _leftTabsPreviewPollForcePending = false;
let _leftTabsPreviewPollForceScheduled = false;
const _leftTabsForcedPreviewTicketIds = new Set;
let _leftTabsPreviewLastRunAt = 0;
let _leftTabsPreviewBackoffUntil = 0;
let _leftTabsPreviewErrorStreak = 0;
let _leftTabsCaptchaCooldownUntil = 0;
const _leftTabsLastPollAtByTicketId = Object.create(null);
const _leftTabsRecentRequestTs = [];
let _leftTabsCacheSaveTimer = null;
const _leftTabsCacheSuppressByTicketId = Object.create(null);
const _leftTabsCacheSuppressByName = Object.create(null);
let _leftTabsCacheGlobalSuppressUntil = 0;
const _leftTabsHydratedByTicketId = Object.create(null);
const _leftTabsHydratedByName = Object.create(null);
let _leftTabsNativeTabsVm = null;
let _leftTabsOpenOrder = [];
const _leftTabsOpenMeta = Object.create(null);
const _leftTabsBadgeByTicketId = Object.create(null);
let _leftTabsBlockEnsureForClosedId = null;
let _leftTabsLastHrefForSync = location.href;
const LEFT_TABS_PREVIEW_POLL_MS = 8e3;
const LEFT_TABS_PREVIEW_MIN_GAP_MS = 5e3;
const LEFT_TABS_PREVIEW_FORCE_MIN_GAP_MS = 7e3;
const LEFT_TABS_MAX_REQUESTS_PER_RUN = 1;
const LEFT_TABS_PER_TICKET_MIN_GAP_MS = 45 * 1e3;
const LEFT_TABS_MAX_REQUESTS_PER_MINUTE = 6;
const LEFT_TABS_DEPARTMENT_REFRESH_MS = 1e4;
const LEFT_TABS_CAPTCHA_COOLDOWN_MS = 10 * 60 * 1e3;
const LEFT_TABS_ERROR_BASE_BACKOFF_MS = 2e3;
const LEFT_TABS_ERROR_MAX_BACKOFF_MS = 60 * 1e3;
const LEFT_TABS_CACHE_SUPPRESS_MS = 8e3;
function nowMs() {
return Date.now();
}
function pruneLeftTabsCacheSuppress() {
const now = nowMs();
for (const [k, until] of Object.entries(_leftTabsCacheSuppressByTicketId)) {
if (Number(until) <= now) delete _leftTabsCacheSuppressByTicketId[k];
}
for (const [k, until] of Object.entries(_leftTabsCacheSuppressByName)) {
if (Number(until) <= now) delete _leftTabsCacheSuppressByName[k];
}
}
function markLeftTabsCacheSuppress(entry, ms = LEFT_TABS_CACHE_SUPPRESS_MS) {
const until = nowMs() + ms;
const ticketId = Number.isFinite(entry?.ticketId) ? entry.ticketId : null;
const name = String(entry?.name || "").trim();
const norm = normalizeTabName(name);
if (Number.isFinite(ticketId)) _leftTabsCacheSuppressByTicketId[ticketId] = until;
if (name) _leftTabsCacheSuppressByName[name] = until;
if (norm) _leftTabsCacheSuppressByName[norm] = until;
}
function isLeftTabsCacheSuppressed(name, ticketId) {
pruneLeftTabsCacheSuppress();
const now = nowMs();
if (_leftTabsCacheGlobalSuppressUntil > now) return true;
if (Number.isFinite(ticketId) && Number(_leftTabsCacheSuppressByTicketId[ticketId] || 0) > now) return true;
const raw = String(name || "").trim();
const norm = normalizeTabName(raw);
return Number(_leftTabsCacheSuppressByName[raw] || 0) > now || Number(_leftTabsCacheSuppressByName[norm] || 0) > now;
}
function scheduleLeftTabsCacheSave() {
if (_leftTabsCacheSaveTimer) return;
_leftTabsCacheSaveTimer = setTimeout(() => {
_leftTabsCacheSaveTimer = null;
try {
const payload = {
previewByName: _leftTabsPreviewByName,
previewByTicketId: _leftTabsPreviewByTicketId,
timeTextByName: _leftTabsLastMessageTimeTextByName,
timeTextByTicketId: _leftTabsLastMessageTimeTextByTicketId,
tsByName: _leftTabsLastMessageTsByName,
tsByTicketId: _leftTabsLastMessageTsByTicketId,
openTabs: {
order: _leftTabsOpenOrder.slice(),
meta: {
..._leftTabsOpenMeta
},
badgeByTicketId: {
..._leftTabsBadgeByTicketId
}
}
};
localStorage.setItem(LEFT_TABS_CACHE_KEY, JSON.stringify(payload));
} catch (e) {}
}, 300);
}
function flushLeftTabsCacheNow() {
try {
if (_leftTabsCacheSaveTimer) {
clearTimeout(_leftTabsCacheSaveTimer);
_leftTabsCacheSaveTimer = null;
}
const payload = {
previewByName: _leftTabsPreviewByName,
previewByTicketId: _leftTabsPreviewByTicketId,
timeTextByName: _leftTabsLastMessageTimeTextByName,
timeTextByTicketId: _leftTabsLastMessageTimeTextByTicketId,
tsByName: _leftTabsLastMessageTsByName,
tsByTicketId: _leftTabsLastMessageTsByTicketId,
openTabs: {
order: _leftTabsOpenOrder.slice(),
meta: {
..._leftTabsOpenMeta
},
badgeByTicketId: {
..._leftTabsBadgeByTicketId
}
}
};
localStorage.setItem(LEFT_TABS_CACHE_KEY, JSON.stringify(payload));
} catch (e) {}
}
function clearLeftTabsCacheForEntry(entry) {
markLeftTabsCacheSuppress(entry);
const name = String(entry?.name || "").trim();
const norm = normalizeTabName(name);
const ticketId = Number.isFinite(entry?.ticketId) ? entry.ticketId : null;
if (name) {
delete _leftTabsPreviewByName[name];
delete _leftTabsLastMessageTimeTextByName[name];
delete _leftTabsLastMessageTsByName[name];
delete _leftTabsSeenOrderByName[name];
}
if (Number.isFinite(ticketId)) {
delete _leftTabsPreviewByTicketId[ticketId];
delete _leftTabsLastMessageTimeTextByTicketId[ticketId];
delete _leftTabsLastMessageTsByTicketId[ticketId];
delete _leftTabsSeenOrderByTicketId[ticketId];
delete _leftTabsHydratedByTicketId[ticketId];
_leftTabsOpenOrder = _leftTabsOpenOrder.filter(id => id !== ticketId);
delete _leftTabsOpenMeta[ticketId];
delete _leftTabsBadgeByTicketId[ticketId];
}
if (norm) delete _leftTabsHydratedByName[norm];
flushLeftTabsCacheNow();
}
function clearLeftTabsCacheAll() {
_leftTabsCacheGlobalSuppressUntil = nowMs() + LEFT_TABS_CACHE_SUPPRESS_MS;
pruneLeftTabsCacheSuppress();
const clearObj = obj => Object.keys(obj).forEach(k => {
delete obj[k];
});
clearObj(_leftTabsPreviewByName);
clearObj(_leftTabsPreviewByTicketId);
clearObj(_leftTabsLastMessageTimeTextByName);
clearObj(_leftTabsLastMessageTimeTextByTicketId);
clearObj(_leftTabsLastMessageTsByName);
clearObj(_leftTabsLastMessageTsByTicketId);
clearObj(_leftTabsSeenOrderByName);
clearObj(_leftTabsSeenOrderByTicketId);
clearObj(_leftTabsHydratedByTicketId);
clearObj(_leftTabsHydratedByName);
_leftTabsOpenOrder = [];
Object.keys(_leftTabsOpenMeta).forEach(k => {
delete _leftTabsOpenMeta[k];
});
Object.keys(_leftTabsBadgeByTicketId).forEach(k => {
delete _leftTabsBadgeByTicketId[k];
});
_leftTabsBlockEnsureForClosedId = null;
try {
localStorage.removeItem(LEFT_TABS_CACHE_KEY);
localStorage.removeItem(LEFT_TABS_CACHE_KEY_LEGACY_V2);
} catch (e) {}
flushLeftTabsCacheNow();
}
function loadLeftTabsCache() {
try {
const mergeObject = (target, source) => {
if (!source || typeof source !== "object") return;
for (const [k, v] of Object.entries(source)) {
if (v == null) continue;
target[k] = v;
}
};
let raw = localStorage.getItem(LEFT_TABS_CACHE_KEY);
if (!raw) {
const legacyV2 = localStorage.getItem(LEFT_TABS_CACHE_KEY_LEGACY_V2);
if (legacyV2) {
const v2 = JSON.parse(legacyV2);
mergeObject(_leftTabsPreviewByTicketId, v2.previewByTicketId);
mergeObject(_leftTabsLastMessageTimeTextByTicketId, v2.timeTextByTicketId);
mergeObject(_leftTabsLastMessageTsByTicketId, v2.tsByTicketId);
mergeObject(_leftTabsSeenOrderByTicketId, v2.seenOrderByTicketId);
try {
localStorage.removeItem(LEFT_TABS_CACHE_KEY_LEGACY_V2);
} catch (e) {}
flushLeftTabsCacheNow();
}
return;
}
const parsed = JSON.parse(raw);
mergeObject(_leftTabsPreviewByName, parsed.previewByName);
mergeObject(_leftTabsPreviewByTicketId, parsed.previewByTicketId);
mergeObject(_leftTabsLastMessageTimeTextByName, parsed.timeTextByName);
mergeObject(_leftTabsLastMessageTimeTextByTicketId, parsed.timeTextByTicketId);
mergeObject(_leftTabsLastMessageTsByName, parsed.tsByName);
mergeObject(_leftTabsLastMessageTsByTicketId, parsed.tsByTicketId);
if (parsed.openTabs && Array.isArray(parsed.openTabs.order)) {
_leftTabsOpenOrder = parsed.openTabs.order.map(id => Number(id)).filter(id => Number.isFinite(id) && id > 0);
const meta = parsed.openTabs.meta || {};
Object.keys(meta).forEach(k => {
const id = Number(k);
if (Number.isFinite(id) && id > 0) _leftTabsOpenMeta[id] = meta[k];
});
const badges = parsed.openTabs.badgeByTicketId || {};
Object.keys(badges).forEach(k => {
const id = Number(k);
if (Number.isFinite(id) && id > 0) _leftTabsBadgeByTicketId[id] = !!badges[k];
});
}
try {
localStorage.removeItem(LEFT_TABS_CACHE_KEY_LEGACY_V2);
} catch (e) {}
} catch (e) {}
}
loadLeftTabsCache();
function leftTabsFieldLabelIncludes(fieldEl, needle) {
if (!(fieldEl instanceof Element) || !needle) return false;
const lab = fieldEl.querySelector(".ticket-fields__field-name, .ticket-user__field-label");
const t = (lab?.textContent || "").replace(/\s+/g, " ").trim().toLowerCase();
return t.includes(String(needle).toLowerCase());
}
function extractTicketDepartmentFromPage() {
const fields = Array.from(document.querySelectorAll("#ticket-app .ticket-fields__field"));
for (const field of fields) {
if (!leftTabsFieldLabelIncludes(field, "департамент")) continue;
const input = field.querySelector(".ticket-fields__field-input .el-input__inner");
const v = input ? String(input.value || "").replace(/\s+/g, " ").trim() : "";
if (v && v !== "Выбрать") return v;
const sel = field.querySelector(".el-select-dropdown__item.selected, .el-select-dropdown__item.is-selected");
const fromLi = (sel?.textContent || "").replace(/\s+/g, " ").trim();
if (fromLi) return fromLi;
}
return "";
}
function extractTicketClientNameFromPage() {
const fields = Array.from(document.querySelectorAll("#ticket-app .ticket-user .ticket-user__field"));
for (const field of fields) {
if (!leftTabsFieldLabelIncludes(field, "клиент")) continue;
const input = field.querySelector(".el-input__inner");
const v = input ? String(input.value || "").replace(/\s+/g, " ").trim() : "";
if (v && !/^поиск/i.test(v)) return v;
const sel = field.querySelector(".el-select-dropdown__item.selected, .el-select-dropdown__item.is-selected");
const fromLi = (sel?.textContent || "").replace(/\s+/g, " ").trim();
if (fromLi) return fromLi;
}
return "";
}
function detectCurrentTicketFrozenFromPage() {
const root = document.querySelector("#ticket-app");
if (!(root instanceof Element)) return null;
if (root.querySelector(".ticket-fields__field-unfreeze-button")) return true;
if (root.querySelector(".ticket-fields__field-freeze-button")) return false;
return null;
}
function syncLeftTabsAfterNavigationShell() {
applyThemeFromConfig();
syncColleagueStatusesModule();
injectStyle();
processAllMessages();
initAutoLoad();
moveHistoryButton();
}
function buildPathToTicket(ticketId) {
if (!Number.isFinite(ticketId)) return null;
const path = location.pathname || "";
if (/\/ticket\/\d+/.test(path)) return path.replace(/\/ticket\/\d+/, `/ticket/${ticketId}`);
const listWithFilter = path.match(/^(.*\/ticket\/list\/filter\/id\/\d+)/);
if (listWithFilter) return `${listWithFilter[1]}/ticket/${ticketId}`;
const locPrefix = path.match(/^(\/[^/]+)/);
const loc = locPrefix ? locPrefix[1] : "/ru";
return `${loc}/ticket/list/filter/id/11/ticket/${ticketId}`;
}
function pathnameForVueRouterPush(router, absolutePathname) {
const p = String(absolutePathname || "").startsWith("/") ? String(absolutePathname) : `/${absolutePathname}`;
let base = router && router.options && router.options.base != null ? String(router.options.base) : "/";
if (!base || base === "/") return p;
base = base.replace(/\/+$/, "");
if (!base) return p;
if (p === base || p.startsWith(`${base}/`)) {
const rest = p.slice(base.length);
return rest.startsWith("/") ? rest : `/${rest}`;
}
return p;
}
function navigateToTicketId(ticketId) {
if (!Number.isFinite(ticketId)) return false;
if (Number.isFinite(ticketId) && _leftTabsBlockEnsureForClosedId === ticketId) {
_leftTabsBlockEnsureForClosedId = null;
}
const nextPath = buildPathToTicket(ticketId);
if (!nextPath) return false;
const nextFull = nextPath + (location.search || "");
if (`${location.pathname}${location.search}` === nextFull) {
ensureOpenTicketFromCurrentUrl();
return true;
}
const root = document.querySelector("#ticket-app");
try {
const vue = root && root.__vue__;
if (vue && vue.$router && typeof vue.$router.push === "function") {
const routerPath = pathnameForVueRouterPush(vue.$router, nextPath) || "/";
vue.$router.push(routerPath + (location.search || "")).catch(() => {});
setTimeout(() => {
ensureOpenTicketFromCurrentUrl();
syncLeftTabsMirror();
syncLeftTabsAfterNavigationShell();
}, 120);
return true;
}
} catch (e) {}
try {
window.history.pushState({}, "", nextFull);
window.dispatchEvent(new PopStateEvent("popstate", {
state: history.state
}));
} catch (e) {
window.location.assign(nextFull);
return true;
}
ensureOpenTicketFromCurrentUrl();
_leftTabsLastHrefForSync = location.href;
setTimeout(() => {
syncLeftTabsMirror();
syncLeftTabsAfterNavigationShell();
}, 80);
return true;
}
function navigateToTicketListHome() {
const m = location.pathname.match(/^(\/[^/]+)\/ticket\//);
const prefix = m ? m[1] : "/ru";
const nextFull = `${prefix}/ticket/list` + (location.search || "");
try {
window.history.pushState({}, "", nextFull);
window.dispatchEvent(new PopStateEvent("popstate", {
state: history.state
}));
} catch (e) {
window.location.assign(nextFull);
return;
}
_leftTabsLastHrefForSync = location.href;
setTimeout(() => {
syncLeftTabsMirror();
syncLeftTabsAfterNavigationShell();
}, 80);
}
function ensureOpenTicketFromCurrentUrl() {
const tid = getTicketId();
if (!Number.isFinite(tid)) {
_leftTabsBlockEnsureForClosedId = null;
return;
}
if (_leftTabsBlockEnsureForClosedId != null) {
if (tid === _leftTabsBlockEnsureForClosedId) {
return;
}
_leftTabsBlockEnsureForClosedId = null;
}
if (!_leftTabsOpenOrder.includes(tid)) _leftTabsOpenOrder.push(tid);
if (!_leftTabsOpenMeta[tid]) _leftTabsOpenMeta[tid] = {
name: "",
department: "",
frozen: false
};
const name = extractTicketClientNameFromPage();
const dep = extractTicketDepartmentFromPage();
const frozen = detectCurrentTicketFrozenFromPage();
if (name) _leftTabsOpenMeta[tid].name = name;
if (dep) _leftTabsOpenMeta[tid].department = dep;
if (typeof frozen === "boolean") _leftTabsOpenMeta[tid].frozen = frozen;
scheduleLeftTabsCacheSave();
}
function syncLeftTabsOnHrefChange() {
if (!CONFIG.leftTabsMirror) {
_leftTabsLastHrefForSync = location.href;
return;
}
if (location.href === _leftTabsLastHrefForSync) return;
_leftTabsLastHrefForSync = location.href;
_loadedPages.clear();
_historyLoadedForTicketId = null;
ensureOpenTicketFromCurrentUrl();
syncLeftTabsMirror();
scheduleImmediateLeftTabsPreviewPoll();
processAllMessages();
initAutoLoad();
}
function refreshLeftTabsDepartmentMeta() {
if (!CONFIG.leftTabsMirror) return;
const tid = getTicketId();
if (!Number.isFinite(tid)) return;
if (!_leftTabsOpenMeta[tid]) _leftTabsOpenMeta[tid] = {
name: "",
department: "",
frozen: false
};
let changed = false;
const dep = extractTicketDepartmentFromPage();
if (dep && _leftTabsOpenMeta[tid].department !== dep) {
_leftTabsOpenMeta[tid].department = dep;
changed = true;
}
const name = extractTicketClientNameFromPage();
if (name && _leftTabsOpenMeta[tid].name !== name) {
_leftTabsOpenMeta[tid].name = name;
changed = true;
}
const frozen = detectCurrentTicketFrozenFromPage();
if (typeof frozen === "boolean" && _leftTabsOpenMeta[tid].frozen !== frozen) {
_leftTabsOpenMeta[tid].frozen = frozen;
changed = true;
}
if (changed) {
scheduleLeftTabsCacheSave();
scheduleLeftTabsMirrorSync();
}
}
function inferUnreadBadgeFromConversation(payload, isActive) {
if (isActive) return false;
const messages = Array.isArray(payload?.messages) ? payload.messages : [];
const users = payload?.users || {};
for (let i = messages.length - 1; i >= 0; i--) {
const msg = messages[i];
if (!msg || msg.type === "comment" || msg.type === "note") continue;
const u = users[String(msg.userId)] || {};
const t = String(u.type || "").toLowerCase();
if (t === "user" || t === "client") return true;
return false;
}
return false;
}
function markLeftTabsHydrated(name, ticketId) {
if (Number.isFinite(ticketId)) _leftTabsHydratedByTicketId[ticketId] = 1;
const norm = normalizeTabName(name);
if (norm) _leftTabsHydratedByName[norm] = 1;
}
function isLeftTabsHydrated(name, ticketId) {
if (Number.isFinite(ticketId) && _leftTabsHydratedByTicketId[ticketId]) return true;
const norm = normalizeTabName(name);
return !!(norm && _leftTabsHydratedByName[norm]);
}
function ensureSeenOrder(name, ticketId, storeKey) {
const normalizedStoreKey = String(storeKey || "").trim();
if (normalizedStoreKey) {
if (_leftTabsSeenOrderByStoreKey[normalizedStoreKey] == null) _leftTabsSeenOrderByStoreKey[normalizedStoreKey] = _leftTabsSeenOrderSeq++;
return;
}
if (Number.isFinite(ticketId)) {
if (_leftTabsSeenOrderByTicketId[ticketId] == null) _leftTabsSeenOrderByTicketId[ticketId] = _leftTabsSeenOrderSeq++;
if (_leftTabsSeenOrderByName[name] == null) _leftTabsSeenOrderByName[name] = _leftTabsSeenOrderByTicketId[ticketId];
return;
}
if (_leftTabsSeenOrderByName[name] == null) _leftTabsSeenOrderByName[name] = _leftTabsSeenOrderSeq++;
}
function getSeenOrder(name, ticketId, storeKey) {
const normalizedStoreKey = String(storeKey || "").trim();
if (normalizedStoreKey && _leftTabsSeenOrderByStoreKey[normalizedStoreKey] != null) {
return _leftTabsSeenOrderByStoreKey[normalizedStoreKey];
}
if (Number.isFinite(ticketId) && _leftTabsSeenOrderByTicketId[ticketId] != null) {
return _leftTabsSeenOrderByTicketId[ticketId];
}
return _leftTabsSeenOrderByName[name] ?? Number.MAX_SAFE_INTEGER;
}
function getLeftTabsPreview(name, ticketId) {
if (Number.isFinite(ticketId)) return _leftTabsPreviewByTicketId[ticketId] || "";
const byName = name ? _leftTabsPreviewByName[name] || "" : "";
return byName || "";
}
function setLeftTabsPreview(name, ticketId, preview) {
if (!preview) return;
const next = String(preview).trim();
if (!next) return;
if (next === "Новый ответ") return;
if (!Number.isFinite(ticketId)) return;
if (isLeftTabsCacheSuppressed(name, ticketId)) return;
let changed = false;
if (_leftTabsPreviewByTicketId[ticketId] !== next) {
_leftTabsPreviewByTicketId[ticketId] = next;
changed = true;
}
if (changed) {
const nowTs = Date.now();
_leftTabsLastMessageTsByTicketId[ticketId] = Math.max(Number(_leftTabsLastMessageTsByTicketId[ticketId] || 0), nowTs);
flushLeftTabsCacheNow();
}
}
function getLeftTabsTimeText(name, ticketId) {
if (Number.isFinite(ticketId)) return _leftTabsLastMessageTimeTextByTicketId[ticketId] || "";
const byName = name ? _leftTabsLastMessageTimeTextByName[name] || "" : "";
return byName || "";
}
function setLeftTabsTimeText(name, ticketId, timeText) {
if (!timeText) return;
if (!Number.isFinite(ticketId)) return;
if (isLeftTabsCacheSuppressed(name, ticketId)) return;
_leftTabsLastMessageTimeTextByTicketId[ticketId] = timeText;
scheduleLeftTabsCacheSave();
}
function getLeftTabsTs(name, ticketId) {
if (!Number.isFinite(ticketId)) return 0;
return Number(_leftTabsLastMessageTsByTicketId[ticketId] || 0);
}
function setLeftTabsTs(name, ticketId, ts) {
if (!Number.isFinite(ts) || ts <= 0) return;
if (!Number.isFinite(ticketId)) return;
if (isLeftTabsCacheSuppressed(name, ticketId)) return;
_leftTabsLastMessageTsByTicketId[ticketId] = ts;
scheduleLeftTabsCacheSave();
}
function syncLeftTabsCacheFromEntries(entries) {
for (const entry of entries || []) {
if (!entry) continue;
const preview = String(entry.preview || "").trim();
if (preview && preview !== "Новый ответ") {
setLeftTabsPreview(entry.name, entry.ticketId, preview);
}
const timeText = String(entry.lastTimeText || "").trim();
if (timeText) {
setLeftTabsTimeText(entry.name, entry.ticketId, timeText);
}
const ts = Number(entry.lastMessageTs || 0);
if (Number.isFinite(ts) && ts > 0) {
setLeftTabsTs(entry.name, entry.ticketId, ts);
}
}
}
function sortLeftTabsEntries(entries) {
entries.sort((a, b) => {
const aTs = Number(a.lastMessageTs || getLeftTabsTs(a.name, a.ticketId) || 0);
const bTs = Number(b.lastMessageTs || getLeftTabsTs(b.name, b.ticketId) || 0);
if (aTs !== bTs) return bTs - aTs;
const aSeen = getSeenOrder(a.name, a.ticketId, a.storeKey);
const bSeen = getSeenOrder(b.name, b.ticketId, b.storeKey);
return aSeen - bSeen;
});
return entries;
}
function getLeftTabsSignature(entries) {
return entries.map((entry, idx) => {
const isActive = entry.isActive ? "1" : "0";
const hasBadge = entry.hasBadge ? "1" : "0";
const isFrozen = entry.isFrozen ? "1" : "0";
const tid = Number.isFinite(entry.ticketId) ? entry.ticketId : "";
const dep = entry.department || "";
return `${idx}:${tid}:${isActive}:${hasBadge}:${isFrozen}:${entry.name}:${dep}:${entry.preview || ""}:${entry.lastTimeText || ""}`;
}).join("|");
}
function escapeHtml(value) {
return String(value ?? "").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
}
function isRedLikeColor(color) {
if (!color) return false;
const c = String(color).toLowerCase();
if (c.includes("f56c6c") || c.includes("fb0a0a") || c.includes("ff0000")) return true;
const m = c.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);
if (!m) return false;
const r = Number(m[1]);
const g = Number(m[2]);
const b = Number(m[3]);
return r >= 170 && g <= 120 && b <= 120;
}
function extractDateTimeText(value) {
const str = String(value || "");
const full = str.match(/\b(\d{1,2}\.\d{1,2}\.\d{2,4}\s+\d{1,2}:\d{2}(?::\d{2})?)\b/);
if (full) return full[1];
const hm = str.match(/\b(\d{1,2}:\d{2})\b/);
return hm ? hm[1] : "";
}
function parseMessageDateToTs(value) {
if (!value) return 0;
if (Number.isFinite(value)) return Number(value);
const str = String(value).trim();
const full = str.match(/^(\d{1,2})\.(\d{1,2})\.(\d{2,4})\s+(\d{1,2}):(\d{2})(?::(\d{2}))?$/);
if (full) {
let year = Number(full[3]);
if (year < 100) year += 2e3;
const d = new Date(year, Number(full[2]) - 1, Number(full[1]), Number(full[4]), Number(full[5]), Number(full[6] || 0));
const ts = d.getTime();
return Number.isFinite(ts) ? ts : 0;
}
const hm = str.match(/^(\d{1,2}):(\d{2})$/);
if (hm) {
const now = new Date;
const d = new Date(now.getFullYear(), now.getMonth(), now.getDate(), Number(hm[1]), Number(hm[2]), 0);
const ts = d.getTime();
return Number.isFinite(ts) ? ts : 0;
}
const fallback = Date.parse(str);
return Number.isFinite(fallback) ? fallback : 0;
}
function formatMessageTimeLabel(value) {
const str = String(value || "").trim();
if (!str) return "";
const hm = str.match(/\b(\d{1,2}:\d{2})\b/);
return hm ? hm[1] : str;
}
function normalizePreviewText(value) {
return String(value || "")
.replace(/[\u3164\u200B-\u200D\u2060\uFEFF]/g, "")
.replace(/\s+/g, " ")
.trim();
}
function extractCurrentTicketPreviewMeta() {
const bubbles = Array.from(document.querySelectorAll("#ticket-app .ticket-conversation__messages .ticket-conversation__message .ticket-conversation__message-html"));
for (let i = bubbles.length - 1; i >= 0; i--) {
const bubble = bubbles[i];
if (!bubble) continue;
const clone = bubble.cloneNode(true);
clone.querySelectorAll(".hde-name-inside, .hde-time-inside, .hde-edited-inside, .hde-actions-inline, .ticket-conversation__message-updated").forEach(el => el.remove());
const txt = normalizePreviewText(clone.textContent);
const msgEl = bubble.closest(".ticket-conversation__message");
const hasFiles = !!msgEl?.querySelector(".ticket-conversation__message-files .ticket-conversation__message-file");
const timeInside = msgEl?.querySelector(".hde-time-inside")?.textContent?.trim() || "";
const metaRaw = msgEl?.querySelector(".ticket-conversation__message-meta")?.textContent || "";
const dateText = extractDateTimeText(metaRaw) || extractDateTimeText(timeInside);
const timeText = formatMessageTimeLabel(dateText || timeInside);
const ts = parseMessageDateToTs(dateText || timeInside);
if (hasFiles && (!txt || txt === "_")) {
return {
preview: FILE_ONLY_PREVIEW,
timeText: timeText,
ts: ts
};
}
if (!txt) continue;
return {
preview: txt.length > 140 ? `${txt.slice(0, 140)}…` : txt,
timeText: timeText,
ts: ts
};
}
return null;
}
function htmlToPlainText(html) {
if (!html) return "";
const div = document.createElement("div");
div.innerHTML = String(html);
return normalizePreviewText(div.textContent || "");
}
function pickLatestPreviewFromConversation(payload) {
const messages = Array.isArray(payload?.messages) ? payload.messages : [];
for (let i = messages.length - 1; i >= 0; i--) {
const msg = messages[i];
const txt = htmlToPlainText(msg?.text || "");
const hasFiles = Array.isArray(msg?.files) && msg.files.length > 0;
const rawTime = msg?.createdAt || msg?.date || msg?.created_at || "";
const timeText = formatMessageTimeLabel(rawTime);
const ts = parseMessageDateToTs(rawTime);
if (hasFiles && (!txt || txt === "_")) {
return {
preview: FILE_ONLY_PREVIEW,
timeText: timeText,
ts: ts
};
}
if (txt) {
return {
preview: txt.length > 140 ? `${txt.slice(0, 140)}…` : txt,
timeText: timeText,
ts: ts
};
}
}
return null;
}
function normalizeTabName(name) {
return String(name || "").replace(/\s+/g, " ").trim().toLowerCase();
}
function normalizeLooseTabName(name) {
return normalizeTabName(name).replace(/[.…]+$/g, "").replace(/[^\p{L}\p{N}\s-]/gu, "").trim();
}
function findTicketMetroLayout() {
const metroContainer = Array.from(document.querySelectorAll("section.el-container.metro-padding")).find(el => el.querySelector(".ticket-sidebar") && el.querySelector("section.el-container.ticket-container"));
if (!metroContainer) return null;
const sidebar = metroContainer.querySelector(".ticket-sidebar");
const ticketContainer = metroContainer.querySelector("section.el-container.ticket-container");
if (!sidebar || !ticketContainer) return null;
return {
metroContainer: metroContainer,
sidebar: sidebar,
ticketContainer: ticketContainer
};
}
function ensureLeftTabsColumnInLayout(metroContainer, ticketContainer) {
let column = document.getElementById(LEFT_TABS_COLUMN_ID);
if (!column) {
column = document.createElement("div");
column.id = LEFT_TABS_COLUMN_ID;
column.className = "hde-left-tabs-column";
}
if (column.parentElement !== metroContainer || column.nextElementSibling !== ticketContainer) {
metroContainer.insertBefore(column, ticketContainer);
}
return column;
}
function leftTabsLooksLikeNativeTabsVm(vm) {
if (!vm || typeof vm !== "object") return false;
const tabs = vm.ticketTabs;
if (!Array.isArray(tabs)) return false;
return tabs.every(tab => tab && typeof tab === "object" && "id" in tab && "type" in tab);
}
function getLeftTabsNativeTabsVm() {
if (leftTabsLooksLikeNativeTabsVm(_leftTabsNativeTabsVm)) return _leftTabsNativeTabsVm;
const root = document.querySelector(".ticket-topbar__tabs");
if (!(root instanceof Element)) return null;
const queue = [];
if (root.__vue__) queue.push(root.__vue__);
if (root.__vueParentComponent?.proxy) queue.push(root.__vueParentComponent.proxy);
let cur = root.parentElement;
let depth = 0;
while (cur && depth < 6) {
if (cur.__vue__) queue.push(cur.__vue__);
if (cur.__vueParentComponent?.proxy) queue.push(cur.__vueParentComponent.proxy);
cur = cur.parentElement;
depth += 1;
}
const visited = new WeakSet;
while (queue.length) {
const node = queue.shift();
if (!node || typeof node !== "object") continue;
if (visited.has(node)) continue;
visited.add(node);
if (leftTabsLooksLikeNativeTabsVm(node)) {
_leftTabsNativeTabsVm = node;
return node;
}
const buckets = [ node.$children, node.$parent, node.$root, node.$refs, node._provided ];
for (const bucket of buckets) {
if (!bucket) continue;
if (Array.isArray(bucket)) {
bucket.forEach(child => {
if (child && typeof child === "object") queue.push(child);
});
} else if (typeof bucket === "object") {
Object.values(bucket).forEach(child => {
if (child && typeof child === "object") queue.push(child);
});
}
}
}
return null;
}
function collectNativeTicketTabs() {
const vm = getLeftTabsNativeTabsVm();
const tabs = Array.isArray(vm?.ticketTabs) ? vm.ticketTabs : [];
return tabs.filter(tab => tab && typeof tab === "object" && String(tab.type) === "ticket").map(tab => {
const id = Number(tab.id);
return {
id: Number.isFinite(id) && id > 0 ? id : null,
type: String(tab.type || "ticket"),
title: String(tab.title || "").trim(),
updated: !!tab.updated
};
}).filter(tab => Number.isFinite(tab.id));
}
function syncOpenOrderFromNativeTabs(currentTicketId) {
const nativeTabs = collectNativeTicketTabs();
if (!nativeTabs.length) {
if (Number.isFinite(currentTicketId) && !_leftTabsOpenOrder.includes(currentTicketId)) {
_leftTabsOpenOrder.push(currentTicketId);
scheduleLeftTabsCacheSave();
}
return [];
}
const ids = nativeTabs.map(tab => tab.id);
const changed = ids.length !== _leftTabsOpenOrder.length || ids.some((id, idx) => id !== _leftTabsOpenOrder[idx]);
if (changed) {
_leftTabsOpenOrder = ids.slice();
scheduleLeftTabsCacheSave();
}
const badgeRaisedIds = [];
nativeTabs.forEach(tab => {
if (!_leftTabsOpenMeta[tab.id]) _leftTabsOpenMeta[tab.id] = {
name: "",
department: "",
frozen: false
};
if (tab.title) _leftTabsOpenMeta[tab.id].name = tab.title;
const prevBadge = !!_leftTabsBadgeByTicketId[tab.id];
_leftTabsBadgeByTicketId[tab.id] = !!tab.updated;
if (!prevBadge && !!tab.updated) badgeRaisedIds.push(tab.id);
});
badgeRaisedIds.forEach(ticketId => {
scheduleImmediateLeftTabsPreviewPollForTicket(ticketId);
});
return nativeTabs;
}
function normalizeLeftTabsOpenOrder() {
const normalized = [];
const seen = new Set;
for (const idRaw of _leftTabsOpenOrder) {
const id = Number(idRaw);
if (!Number.isFinite(id) || id <= 0) continue;
if (seen.has(id)) continue;
seen.add(id);
normalized.push(id);
}
if (normalized.length !== _leftTabsOpenOrder.length || normalized.some((id, idx) => id !== _leftTabsOpenOrder[idx])) {
_leftTabsOpenOrder = normalized;
scheduleLeftTabsCacheSave();
}
}
function getNextTicketIdAfterClose(closingTicketId) {
const orderCopy = _leftTabsOpenOrder.slice();
const idx = orderCopy.indexOf(closingTicketId);
if (idx < 0) return null;
return orderCopy[idx + 1] ?? orderCopy[idx - 1] ?? null;
}
function closeTicketViaNativeBridge(ticketId) {
const vm = getLeftTabsNativeTabsVm();
if (!vm || typeof vm.removeTab !== "function" || !Array.isArray(vm.ticketTabs)) return false;
const nativeTab = vm.ticketTabs.find(tab => Number(tab?.id) === Number(ticketId) && String(tab?.type) === "ticket");
if (!nativeTab) return false;
try {
vm.removeTab(nativeTab);
return true;
} catch (e) {
return false;
}
}
function collectTicketTabEntries() {
const currentTicketId = getTicketId();
ensureOpenTicketFromCurrentUrl();
const nativeTabs = syncOpenOrderFromNativeTabs(currentTicketId);
normalizeLeftTabsOpenOrder();
const nativeById = Object.create(null);
nativeTabs.forEach(tab => {
nativeById[tab.id] = tab;
});
const entries = _leftTabsOpenOrder.filter(ticketId => Number.isFinite(ticketId) && ticketId > 0).map(ticketId => {
const nativeTab = nativeById[ticketId] || null;
const meta = _leftTabsOpenMeta[ticketId] || {};
const nativeTitle = String(nativeTab?.title || "").trim();
const name = nativeTitle || meta.name && String(meta.name).trim() || `Заявка ${ticketId}`;
const department = String(meta.department || "").trim();
const isFrozen = !!meta.frozen;
const isActive = Number.isFinite(currentTicketId) && ticketId === currentTicketId;
const hasBadge = nativeTab ? !!nativeTab.updated : !!_leftTabsBadgeByTicketId[ticketId];
_leftTabsBadgeByTicketId[ticketId] = hasBadge;
const liveMeta = isActive ? extractCurrentTicketPreviewMeta() : null;
const cachedPreview = getLeftTabsPreview(name, ticketId);
const preview = liveMeta?.preview || cachedPreview || (hasBadge ? "Новый ответ" : "");
setLeftTabsPreview(name, ticketId, preview);
if (liveMeta?.timeText) setLeftTabsTimeText(name, ticketId, liveMeta.timeText);
if (liveMeta?.ts) setLeftTabsTs(name, ticketId, liveMeta.ts);
const storeKey = `id:${ticketId}`;
ensureSeenOrder(name, ticketId, storeKey);
return {
source: "registry",
domUid: "",
storeKey: storeKey,
name: name,
department: department,
isFrozen: isFrozen,
ticketId: ticketId,
isActive: isActive,
hasBadge: hasBadge,
preview: preview,
lastTimeText: getLeftTabsTimeText(name, ticketId),
lastMessageTs: getLeftTabsTs(name, ticketId),
onOpen: () => {
navigateToTicketId(ticketId);
},
onClose: () => {
const wasActive = getTicketId() === ticketId;
const nextId = getNextTicketIdAfterClose(ticketId);
if (closeTicketViaNativeBridge(ticketId)) {
clearLeftTabsCacheForEntry({
name: name,
ticketId: ticketId
});
scheduleLeftTabsMirrorSync();
return;
}
clearLeftTabsCacheForEntry({
name: name,
ticketId: ticketId
});
if (wasActive) {
_leftTabsBlockEnsureForClosedId = ticketId;
if (Number.isFinite(nextId)) navigateToTicketId(nextId); else navigateToTicketListHome();
}
scheduleLeftTabsMirrorSync();
}
};
});
return sortLeftTabsEntries(entries);
}
function scheduleLeftTabsMirrorSync() {
if (!CONFIG.leftTabsMirror || _leftTabsMirrorSyncScheduled) return;
_leftTabsMirrorSyncScheduled = true;
requestAnimationFrame(() => {
_leftTabsMirrorSyncScheduled = false;
syncLeftTabsMirror();
});
}
function scheduleImmediateLeftTabsPreviewPoll() {
if (!CONFIG.leftTabsMirror || _leftTabsPreviewPollForceScheduled) return;
_leftTabsPreviewPollForceScheduled = true;
requestAnimationFrame(() => {
_leftTabsPreviewPollForceScheduled = false;
pollLeftTabsPreviews(true);
});
}
function scheduleImmediateLeftTabsPreviewPollForTicket(ticketId) {
if (!Number.isFinite(ticketId) || ticketId <= 0) return;
_leftTabsForcedPreviewTicketIds.add(ticketId);
scheduleImmediateLeftTabsPreviewPoll();
}
function clickNativeCloseAllTabs() {
const nativeCloseAllBtn = document.querySelector(".ticket-tabs__close-all, .ticket-tabs__more-close-all");
if (!(nativeCloseAllBtn instanceof HTMLElement)) return false;
nativeCloseAllBtn.click();
return true;
}
function clickNativeCloseAllConfirm() {
const candidates = Array.from(document.querySelectorAll(".el-popover .el-button.el-button--primary.el-button--mini, .el-popconfirm .el-button.el-button--primary.el-button--mini"));
const confirmBtn = candidates.find(btn => /закрыть/i.test((btn.textContent || "").trim()));
if (!(confirmBtn instanceof HTMLElement)) return false;
confirmBtn.click();
return true;
}
function closeAllTabsFromMirror() {
const openedBefore = _leftTabsOpenOrder.map(id => Number(id)).filter(id => Number.isFinite(id) && id > 0);
let nativeCloseTriggered = false;
const vm = getLeftTabsNativeTabsVm();
if (vm && typeof vm.removeTab === "function" && Array.isArray(vm.ticketTabs)) {
const nativeTicketTabs = vm.ticketTabs.filter(tab => Number.isFinite(Number(tab?.id)) && String(tab?.type) === "ticket").slice();
nativeTicketTabs.forEach(tab => {
try {
vm.removeTab(tab);
nativeCloseTriggered = true;
} catch (e) {}
});
}
if (!nativeCloseTriggered && clickNativeCloseAllTabs()) {
nativeCloseTriggered = true;
setTimeout(() => {
clickNativeCloseAllConfirm();
}, 80);
setTimeout(() => {
clickNativeCloseAllConfirm();
}, 220);
}
if (nativeCloseTriggered) {
navigateToTicketListHome();
setTimeout(() => {
const remainingIds = new Set(collectNativeTicketTabs().map(tab => Number(tab.id)).filter(id => Number.isFinite(id) && id > 0));
if (remainingIds.size === 0) {
clearLeftTabsCacheAll();
} else {
openedBefore.forEach(id => {
if (remainingIds.has(id)) return;
const meta = _leftTabsOpenMeta[id] || {};
clearLeftTabsCacheForEntry({
ticketId: id,
name: meta.name || ""
});
});
}
scheduleLeftTabsMirrorSync();
}, 260);
return;
}
navigateToTicketListHome();
scheduleLeftTabsMirrorSync();
}
function leftTabsIsCaptchaLikely() {
const href = String(location.href || "");
if (/captcha|recaptcha|hcaptcha/i.test(href)) return true;
const title = String(document.title || "");
if (/captcha|капча|я не робот/i.test(title)) return true;
return false;
}
function markLeftTabsPollError(err) {
const now = Date.now();
_leftTabsPreviewErrorStreak += 1;
const backoff = Math.min(LEFT_TABS_ERROR_MAX_BACKOFF_MS, LEFT_TABS_ERROR_BASE_BACKOFF_MS * Math.pow(2, Math.min(_leftTabsPreviewErrorStreak, 5)));
_leftTabsPreviewBackoffUntil = now + backoff;
const msg = String(err?.message || err || "");
const status = Number(err?.status || 0);
const url = String(err?.url || "");
if (status === 403 || status === 429 || /captcha|recaptcha|hcaptcha/i.test(`${msg} ${url}`)) {
_leftTabsCaptchaCooldownUntil = now + LEFT_TABS_CAPTCHA_COOLDOWN_MS;
}
}
function markLeftTabsPollSuccess() {
_leftTabsPreviewErrorStreak = 0;
_leftTabsPreviewBackoffUntil = 0;
}
function pruneLeftTabsRecentRequests(now = Date.now()) {
const edge = now - 60 * 1e3;
while (_leftTabsRecentRequestTs.length && _leftTabsRecentRequestTs[0] < edge) {
_leftTabsRecentRequestTs.shift();
}
}
function canLeftTabsDoRequest(now = Date.now()) {
pruneLeftTabsRecentRequests(now);
return _leftTabsRecentRequestTs.length < LEFT_TABS_MAX_REQUESTS_PER_MINUTE;
}
function markLeftTabsRequest(now = Date.now()) {
pruneLeftTabsRecentRequests(now);
_leftTabsRecentRequestTs.push(now);
}
async function pollLeftTabsPreviews(force = false) {
if (!CONFIG.leftTabsMirror) return;
if (_leftTabsPreviewPollInFlight) {
if (force) _leftTabsPreviewPollForcePending = true;
return;
}
const now = Date.now();
if (_leftTabsCaptchaCooldownUntil > now) return;
if (_leftTabsPreviewBackoffUntil > now) return;
if (!force && document.hidden) return;
if (leftTabsIsCaptchaLikely()) {
_leftTabsCaptchaCooldownUntil = now + LEFT_TABS_CAPTCHA_COOLDOWN_MS;
return;
}
const hasForcedTickets = _leftTabsForcedPreviewTicketIds.size > 0;
const minGap = force ? LEFT_TABS_PREVIEW_FORCE_MIN_GAP_MS : LEFT_TABS_PREVIEW_MIN_GAP_MS;
if (!hasForcedTickets && now - _leftTabsPreviewLastRunAt < minGap) return;
_leftTabsPreviewLastRunAt = now;
_leftTabsPreviewPollInFlight = true;
try {
const entries = collectTicketTabEntries();
const known = entries.filter(e => Number.isFinite(e.ticketId));
if (!known.length) return;
const forcedTargets = [];
if (_leftTabsForcedPreviewTicketIds.size > 0) {
for (const forcedId of _leftTabsForcedPreviewTicketIds) {
const forcedEntry = known.find(e => e.ticketId === forcedId && !e.isActive);
if (forcedEntry) {
forcedTargets.push(forcedEntry);
} else {
_leftTabsForcedPreviewTicketIds.delete(forcedId);
}
}
}
const targets = forcedTargets.length ? forcedTargets : known.filter(e => e.hasBadge && !e.isActive);
if (!targets.length) return;
const nowTs = Date.now();
const dueTargets = forcedTargets.length ? forcedTargets : targets.filter(e => {
const lastAt = Number(_leftTabsLastPollAtByTicketId[e.ticketId] || 0);
if (!lastAt) return true;
return nowTs - lastAt >= LEFT_TABS_PER_TICKET_MIN_GAP_MS;
}).sort((a, b) => {
const aLast = Number(_leftTabsLastPollAtByTicketId[a.ticketId] || 0);
const bLast = Number(_leftTabsLastPollAtByTicketId[b.ticketId] || 0);
return aLast - bLast;
});
if (!dueTargets.length) return;
let changed = false;
const maxRequestsThisRun = Math.max(1, LEFT_TABS_MAX_REQUESTS_PER_RUN);
let requestsDone = 0;
for (const entry of dueTargets) {
if (requestsDone >= maxRequestsThisRun) break;
if (!canLeftTabsDoRequest()) break;
try {
markLeftTabsRequest();
const payload = await fetchConversationPage(entry.ticketId, 1);
requestsDone += 1;
_leftTabsLastPollAtByTicketId[entry.ticketId] = Date.now();
_leftTabsForcedPreviewTicketIds.delete(entry.ticketId);
const latestMeta = pickLatestPreviewFromConversation(payload);
markLeftTabsHydrated(entry.name, entry.ticketId);
const latest = latestMeta?.preview || "";
if (latest) {
const prevByIdPreview = Number.isFinite(entry.ticketId) ? String(_leftTabsPreviewByTicketId[entry.ticketId] || "") : "";
setLeftTabsPreview(entry.name, entry.ticketId, latest);
if (prevByIdPreview !== latest) changed = true;
}
if (latestMeta?.timeText) {
const prevByIdTimeText = Number.isFinite(entry.ticketId) ? String(_leftTabsLastMessageTimeTextByTicketId[entry.ticketId] || "") : "";
setLeftTabsTimeText(entry.name, entry.ticketId, latestMeta.timeText);
if (prevByIdTimeText !== latestMeta.timeText) changed = true;
}
if (latestMeta?.ts) {
const prevByIdTs = Number.isFinite(entry.ticketId) ? Number(_leftTabsLastMessageTsByTicketId[entry.ticketId] || 0) : 0;
setLeftTabsTs(entry.name, entry.ticketId, latestMeta.ts);
if (prevByIdTs !== latestMeta.ts) changed = true;
}
const nextBadge = inferUnreadBadgeFromConversation(payload, !!entry.isActive);
if (_leftTabsBadgeByTicketId[entry.ticketId] !== nextBadge) {
_leftTabsBadgeByTicketId[entry.ticketId] = nextBadge;
changed = true;
}
} catch (e) {
_leftTabsForcedPreviewTicketIds.delete(entry.ticketId);
markLeftTabsPollError(e);
}
}
if (requestsDone > 0) {
markLeftTabsPollSuccess();
}
if (changed) {
flushLeftTabsCacheNow();
}
scheduleLeftTabsMirrorSync();
} finally {
_leftTabsPreviewPollInFlight = false;
if (_leftTabsPreviewPollForcePending) {
_leftTabsPreviewPollForcePending = false;
const retryDelay = _leftTabsForcedPreviewTicketIds.size > 0 ? 120 : LEFT_TABS_PREVIEW_FORCE_MIN_GAP_MS;
setTimeout(() => {
pollLeftTabsPreviews(true);
}, retryDelay);
}
}
}
function syncLeftTabsPreviewPolling() {
if (!CONFIG.leftTabsMirror) {
if (_leftTabsPreviewPollInterval) {
clearInterval(_leftTabsPreviewPollInterval);
_leftTabsPreviewPollInterval = null;
}
_leftTabsForcedPreviewTicketIds.clear();
_leftTabsPreviewLastRunAt = 0;
return;
}
if (!_leftTabsPreviewPollInterval) {
_leftTabsPreviewPollInterval = setInterval(pollLeftTabsPreviews, LEFT_TABS_PREVIEW_POLL_MS);
pollLeftTabsPreviews(true);
return;
}
if (Date.now() - _leftTabsPreviewLastRunAt > LEFT_TABS_PREVIEW_POLL_MS * 2) {
pollLeftTabsPreviews(true);
}
}
function syncLeftTabsDepartmentPolling() {
if (!CONFIG.leftTabsMirror) {
if (_leftTabsDepartmentPollInterval) {
clearInterval(_leftTabsDepartmentPollInterval);
_leftTabsDepartmentPollInterval = null;
}
return;
}
if (!_leftTabsDepartmentPollInterval) {
_leftTabsDepartmentPollInterval = setInterval(refreshLeftTabsDepartmentMeta, LEFT_TABS_DEPARTMENT_REFRESH_MS);
refreshLeftTabsDepartmentMeta();
}
}
function syncLeftTabsMirror() {
if (_leftTabsMirrorSyncInProgress) {
_leftTabsMirrorSyncPending = true;
return;
}
syncNativeTicketTabsVisibilityClass();
if (!CONFIG.leftTabsMirror) {
removeLeftTabsMirror();
_leftTabsMirrorLastSignature = "";
syncLeftTabsPreviewPolling();
syncLeftTabsDepartmentPolling();
return;
}
const layout = findTicketMetroLayout();
if (!layout) {
removeLeftTabsMirror();
_leftTabsMirrorLastSignature = "";
syncLeftTabsPreviewPolling();
syncLeftTabsDepartmentPolling();
return;
}
const {metroContainer: metroContainer, ticketContainer: ticketContainer} = layout;
const entries = collectTicketTabEntries();
if (!entries.length) {
removeLeftTabsMirror();
_leftTabsMirrorLastSignature = "";
syncLeftTabsPreviewPolling();
syncLeftTabsDepartmentPolling();
return;
}
syncLeftTabsCacheFromEntries(entries);
const signature = getLeftTabsSignature(entries);
const mirror = document.getElementById(LEFT_TABS_MIRROR_ID);
if (mirror && signature === _leftTabsMirrorLastSignature) {
const col = ensureLeftTabsColumnInLayout(metroContainer, ticketContainer);
if (mirror.parentElement !== col) col.appendChild(mirror);
syncLeftTabsPreviewPolling();
syncLeftTabsDepartmentPolling();
return;
}
_leftTabsMirrorSyncInProgress = true;
try {
const column = ensureLeftTabsColumnInLayout(metroContainer, ticketContainer);
let mirrorEl = document.getElementById(LEFT_TABS_MIRROR_ID);
if (!mirrorEl) {
mirrorEl = document.createElement("div");
mirrorEl.id = LEFT_TABS_MIRROR_ID;
mirrorEl.className = "hde-left-tabs-mirror";
mirrorEl.innerHTML = `\n <div class="hde-left-tabs-mirror__title">\n <span class="hde-left-tabs-mirror__title-text">Тикеты</span>\n <button type="button" class="hde-left-tabs-mirror__close-all" title="Закрыть все тикеты" aria-label="Закрыть все тикеты">✕</button>\n </div>\n <div class="hde-left-tabs-mirror__list"></div>\n `;
column.appendChild(mirrorEl);
const closeAllBtn = mirrorEl.querySelector(".hde-left-tabs-mirror__close-all");
closeAllBtn?.addEventListener("click", e => {
e.stopPropagation();
closeAllTabsFromMirror();
});
} else if (mirrorEl.parentElement !== column) {
column.appendChild(mirrorEl);
}
mirrorEl.className = "hde-left-tabs-mirror";
const titleEl = mirrorEl.querySelector(".hde-left-tabs-mirror__title");
if (titleEl && !titleEl.querySelector(".hde-left-tabs-mirror__close-all")) {
const rawTitle = (titleEl.textContent || "").trim() || "Тикеты";
titleEl.innerHTML = `\n <span class="hde-left-tabs-mirror__title-text">${escapeHtml(rawTitle)}</span>\n <button type="button" class="hde-left-tabs-mirror__close-all" title="Закрыть все тикеты" aria-label="Закрыть все тикеты">✕</button>\n `;
titleEl.querySelector(".hde-left-tabs-mirror__close-all")?.addEventListener("click", e => {
e.stopPropagation();
closeAllTabsFromMirror();
});
}
const list = mirrorEl.querySelector(".hde-left-tabs-mirror__list");
if (!list) {
return;
}
list.innerHTML = "";
const duplicateNameCount = entries.reduce((acc, entry) => {
const key = normalizeTabName(entry.name || "");
if (!key) return acc;
acc[key] = (acc[key] || 0) + 1;
return acc;
}, Object.create(null));
entries.forEach(entry => {
const normName = normalizeTabName(entry.name || "");
const hasDuplicateName = normName && duplicateNameCount[normName] > 1;
const displayName = hasDuplicateName && Number.isFinite(entry.ticketId) ? `${entry.name} #${entry.ticketId}` : entry.name;
const showFreezeOverlay = entry.isFrozen && !entry.hasBadge;
const item = document.createElement("button");
item.type = "button";
item.className = `hde-left-tabs-mirror__item${entry.isActive ? " is-active" : ""}${showFreezeOverlay ? " is-frozen" : ""}`;
item.innerHTML = `\n <span class="hde-left-tabs-mirror__indicator${entry.hasBadge ? " has-badge" : ""}" aria-hidden="true"></span>\n ${showFreezeOverlay ? '<span class="hde-left-tabs-mirror__freeze-overlay" title="Заявка заморожена" aria-hidden="true"></span>' : ""}\n <span class="hde-left-tabs-mirror__content">\n <span class="hde-left-tabs-mirror__head">\n <span class="hde-left-tabs-mirror__name">${escapeHtml(displayName)}</span>\n <span class="hde-left-tabs-mirror__time">${escapeHtml(entry.lastTimeText || "")}</span>\n </span>\n <span class="hde-left-tabs-mirror__department">${escapeHtml(String(entry.department || "").trim())}</span>\n <span class="hde-left-tabs-mirror__preview">${escapeHtml(entry.preview || (entry.hasBadge ? "Новый ответ" : ""))}</span>\n </span>\n <span class="hde-left-tabs-mirror__close" aria-hidden="true" role="button" tabindex="0">✕</span>\n `;
item.addEventListener("click", e => {
if (e.target instanceof Element && e.target.closest(".hde-left-tabs-mirror__close")) {
return;
}
entry.onOpen?.();
});
const close = item.querySelector(".hde-left-tabs-mirror__close");
close?.addEventListener("click", e => {
e.stopPropagation();
e.preventDefault();
entry.onClose?.();
});
list.appendChild(item);
});
_leftTabsMirrorLastSignature = signature;
} finally {
_leftTabsMirrorSyncInProgress = false;
if (_leftTabsMirrorSyncPending) {
_leftTabsMirrorSyncPending = false;
requestAnimationFrame(() => {
syncLeftTabsMirror();
});
} else {
syncLeftTabsPreviewPolling();
syncLeftTabsDepartmentPolling();
}
}
}
function initLeftTabsMirrorObserver() {
if (window.__hdeLeftTabsMirrorObserver) return;
window.__hdeLeftTabsMirrorObserver = true;
const onLocationMaybeChanged = () => {
syncLeftTabsOnHrefChange();
};
window.addEventListener("popstate", onLocationMaybeChanged);
window.addEventListener("hashchange", onLocationMaybeChanged);
if (!window.__hdeLeftTabsHrefPollInterval) {
window.__hdeLeftTabsHrefPollInterval = setInterval(() => {
if (location.href !== _leftTabsLastHrefForSync) onLocationMaybeChanged();
}, 400);
}
if (!window.__hdeLeftTabsMirrorClickBound) {
window.__hdeLeftTabsMirrorClickBound = true;
document.addEventListener("click", e => {
const target = e.target;
if (!(target instanceof Element)) return;
if (target.closest(".hde-left-tabs-mirror__item")) {
setTimeout(scheduleLeftTabsMirrorSync, 0);
setTimeout(scheduleImmediateLeftTabsPreviewPoll, 0);
}
});
}
}
function applyStaffsFrameTypography(frame) {
const doc = frame?.contentDocument;
if (!doc?.head) return;
if (doc.getElementById("hde-staffs-frame-font-style")) return;
const style = doc.createElement("style");
style.id = "hde-staffs-frame-font-style";
style.textContent = `\n body,\n input,\n textarea,\n select,\n button,\n .el-input__inner,\n .el-select-dropdown__item,\n .el-tag,\n .el-cascader-node__label,\n .el-checkbox__label {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif !important;\n }\n `;
doc.head.appendChild(style);
}
function ensureStaffsDrawer() {
let drawer = document.getElementById(STAFFS_DRAWER_ID);
if (drawer) return drawer;
drawer = document.createElement("div");
drawer.id = STAFFS_DRAWER_ID;
drawer.className = "hde-staffs-drawer";
drawer.innerHTML = `\n <div class="hde-staffs-drawer__overlay" data-role="overlay"></div>\n <div class="hde-staffs-drawer__panel" role="dialog" aria-label="Управление статусами сотрудников">\n <div class="hde-staffs-drawer__header">\n <span>Статусы сотрудников</span>\n <button type="button" class="hde-staffs-drawer__close" aria-label="Закрыть">×</button>\n </div>\n <iframe class="hde-staffs-drawer__frame" title="Staff statuses" loading="lazy"></iframe>\n </div>\n `;
document.body.appendChild(drawer);
const closeBtn = drawer.querySelector(".hde-staffs-drawer__close");
const overlay = drawer.querySelector('[data-role="overlay"]');
const frame = drawer.querySelector(".hde-staffs-drawer__frame");
const closeDrawer = () => {
drawer.classList.remove("active");
if (CONFIG.colleagueStatuses) updateColleagueStatusesBlock();
};
if (frame && !frame.dataset.hdeFontBound) {
frame.dataset.hdeFontBound = "1";
frame.addEventListener("load", () => {
applyStaffsFrameTypography(frame);
});
}
closeBtn?.addEventListener("click", closeDrawer);
overlay?.addEventListener("click", closeDrawer);
if (!window.__hdeStaffsDrawerEscBound) {
window.__hdeStaffsDrawerEscBound = true;
document.addEventListener("keydown", e => {
if (e.key === "Escape") {
const d = document.getElementById(STAFFS_DRAWER_ID);
if (d?.classList.contains("active")) {
d.classList.remove("active");
if (CONFIG.colleagueStatuses) updateColleagueStatusesBlock();
}
}
});
}
return drawer;
}
function openStaffsDrawer() {
const drawer = preloadStaffsDrawer();
drawer.classList.add("active");
}
function preloadStaffsDrawer() {
const drawer = ensureStaffsDrawer();
const frame = drawer.querySelector(".hde-staffs-drawer__frame");
if (frame && !frame.src) frame.src = "/ru/dashboard/staffs/";
if (frame?.contentDocument?.readyState === "complete") applyStaffsFrameTypography(frame);
return drawer;
}
function initSettingsMenu() {
const existingBtn = document.getElementById("hde-tools-menu");
const menu = document.querySelector(".menu");
const menuPlugins = document.querySelector(".menu-plugins");
function isButtonInExpectedPlace() {
if (!existingBtn) return false;
if (menu && menu.contains(existingBtn)) return true;
return false;
}
if (existingBtn && isButtonInExpectedPlace()) {
const existingPanel = document.getElementById(MENU_PANEL_ID);
const hasColleagueToggle = !!existingPanel?.querySelector("#hde-cfg-colleague-statuses");
const hasLeftTabsToggle = !!existingPanel?.querySelector("#hde-cfg-left-tabs-mirror");
if (existingPanel && existingPanel.dataset.hdePanelVersion === SETTINGS_PANEL_VERSION && hasColleagueToggle && hasLeftTabsToggle) return;
}
if (!menu && !menuPlugins && !existingBtn) {
const observer = new MutationObserver(function() {
if (document.querySelector(".menu") || document.querySelector(".menu-plugins")) {
observer.disconnect();
initSettingsMenu();
}
});
observer.observe(document.body, {
childList: true,
subtree: true
});
return;
}
let style = document.getElementById(MENU_STYLE_ID);
if (!style) {
style = document.createElement("style");
style.id = MENU_STYLE_ID;
style.textContent = `\n\n .hde-tools-panel {\n position: fixed; width: 280px;\n background: #fff; color: #333; border-radius: 6px;\n box-shadow: 0 4px 16px rgba(0,0,0,0.25); padding: 15px; display: none; z-index: 99999;\n font-family: sans-serif; cursor: default;\n }\n .hde-tools-panel.active { display: block; }\n .hde-tools-panel h3 { margin: 0 0 15px 0; font-size: 14px; border-bottom: 1px solid #eee; padding-bottom: 8px; text-align: center; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; font-weight: 600; color: #333; }\n\n .hde-tools-toggle {\n display: grid;\n grid-template-columns: minmax(0, 1fr) auto;\n align-items: center;\n column-gap: 10px;\n margin-bottom: 10px;\n font-size: 13px;\n cursor: pointer;\n }\n .hde-tools-note {\n font-size: 11px;\n opacity: 0.8;\n }\n .hde-tools-toggle input[type="checkbox"] { appearance: none; width: 34px; height: 18px; background: #ddd; border-radius: 9px; position: relative; outline: none; cursor: pointer; transition: 0.3s; }\n .hde-tools-toggle input[type="checkbox"]::after { content: ''; position: absolute; left: 2px; top: 2px; width: 14px; height: 14px; background: #fff; border-radius: 50%; transition: 0.3s; }\n .hde-tools-toggle input[type="checkbox"]:checked { background: #4caf50; }\n .hde-tools-toggle input[type="checkbox"]:checked::after { left: 18px; }\n .hde-tools-toggle-actions { display: inline-flex; align-items: center; margin-left: auto; }\n .hde-tools-link-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 24px;\n height: 24px;\n padding: 0;\n border: none;\n background: transparent;\n color: #606266;\n text-decoration: none;\n line-height: 1 !important;\n }\n .hde-tools-link-btn i {\n font-size: 18px;\n line-height: 1;\n }\n .hde-tools-toggle-actions .hde-tools-link-btn {\n margin-right: 8px;\n }\n .hde-tools-link-btn:hover {\n color: #409eff;\n }\n .hde-tools-field { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; font-size: 13px; }\n .hde-tools-field input[type="number"],\n .hde-tools-field select {\n width: 72px; padding: 4px 6px; font-size: 12px; border: 1px solid #dcdfe6;\n border-radius: 6px; text-align: right; outline: none;\n }\n .hde-tools-field select {\n width: 120px; text-align: left; background: #fff;\n }\n .hde-staffs-drawer {\n position: fixed;\n inset: 0;\n z-index: 100001;\n visibility: hidden;\n pointer-events: none;\n transition: visibility 0s linear 0.3s;\n }\n .hde-staffs-drawer.active {\n visibility: visible;\n pointer-events: auto;\n transition-delay: 0s;\n }\n .hde-staffs-drawer__overlay {\n position: absolute;\n inset: 0;\n background: rgba(15, 20, 25, 0.45);\n opacity: 0;\n transition: opacity 0.3s ease;\n }\n .hde-staffs-drawer__panel {\n position: absolute;\n top: 0;\n right: 0;\n width: min(900px, 88vw);\n height: 100%;\n background: #fff;\n border-left: 1px solid #dcdfe6;\n box-shadow: -8px 0 24px rgba(0,0,0,0.18);\n display: flex;\n flex-direction: column;\n transform: translateX(100%);\n transition: transform 0.3s ease;\n }\n .hde-staffs-drawer.active .hde-staffs-drawer__overlay {\n opacity: 1;\n }\n .hde-staffs-drawer.active .hde-staffs-drawer__panel {\n transform: translateX(0);\n }\n .hde-staffs-drawer__header {\n height: 44px;\n min-height: 44px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0 12px;\n font-weight: 600;\n border-bottom: 1px solid #ebeef5;\n }\n .hde-staffs-drawer__close {\n width: 28px;\n height: 28px;\n border: none;\n background: transparent;\n color: #606266;\n font-size: 22px;\n line-height: 1;\n cursor: pointer;\n }\n .hde-staffs-drawer__close:hover {\n color: #303133;\n }\n .hde-staffs-drawer__frame {\n width: 100%;\n height: calc(100% - 44px);\n border: 0;\n background: #fff;\n }\n .hde-left-tabs-column {\n flex: 0 0 180px;\n width: 180px;\n min-width: 180px;\n box-sizing: border-box;\n background: #ffffff;\n display: flex;\n flex-direction: column;\n }\n .hde-left-tabs-column .hde-left-tabs-mirror {\n margin: 0;\n flex: 1 1 auto;\n min-height: 0;\n }\n .hde-left-tabs-mirror {\n margin: 0;\n border-radius: 6px;\n background: #fff;\n overflow: hidden;\n display: flex;\n flex-direction: column;\n height: 100%;\n max-height: none;\n font-family: 'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif !important;\n font-size: 12px !important;\n color: #606266;\n }\n .hde-left-tabs-mirror,\n .hde-left-tabs-mirror * {\n font-family: 'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif !important;\n }\n .hde-left-tabs-mirror__title {\n padding: 6px 8px;\n background: #F5F7FA;\n display: flex;\n width: 100%;\n box-sizing: border-box;\n min-height: 44px;\n max-height: 44px;\n align-items: center;\n justify-content: space-between;\n text-align: center;\n font-size: 12px !important;\n font-weight: 600;\n color: #303133;\n border-bottom: 1px solid #E5E5E5;\n }\n .hde-left-tabs-mirror__title-text {\n flex: 1;\n min-width: 0;\n text-align: center;\n padding-left: 22px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n .hde-left-tabs-mirror__close-all {\n position: relative;\n z-index: 2;\n flex: 0 0 auto;\n border: 0;\n background: transparent;\n color: inherit;\n cursor: pointer;\n font-size: 13px !important;\n font-weight: 700;\n line-height: 1;\n padding: 0 8px;\n opacity: .72;\n }\n .hde-left-tabs-mirror__close-all:hover {\n opacity: 1;\n }\n .hde-left-tabs-mirror__list {\n flex: 1 1 auto;\n min-height: 0;\n overflow-y: auto;\n overflow-x: hidden;\n background: #fff;\n -ms-overflow-style: none;\n scrollbar-width: none;\n }\n .hde-left-tabs-mirror__list::-webkit-scrollbar {\n width: 0;\n height: 0;\n display: none;\n }\n .hde-left-tabs-mirror__item {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n position: relative;\n width: 100%;\n border: 0;\n border-bottom: 1px solid #f0f2f5;\n background: transparent;\n color: #606266;\n font-size: 12px !important;\n font-weight: 400;\n line-height: 1.4;\n font-family: inherit !important;\n text-align: left;\n padding: 6px 0px;\n min-height: 56px;\n cursor: pointer;\n }\n .hde-left-tabs-mirror__indicator {\n width: 8px;\n min-width: 8px;\n height: 8px;\n border-radius: 50%;\n background: transparent;\n margin-right: 6px;\n margin-top: 4px;\n }\n .hde-left-tabs-mirror__indicator.has-badge {\n background: #f56c6c;\n }\n .hde-left-tabs-mirror__content {\n min-width: 0;\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 2px;\n }\n .hde-left-tabs-mirror__head {\n display: flex;\n align-items: baseline;\n gap: 6px;\n min-width: 0;\n width: 100%;\n }\n .hde-left-tabs-mirror__department {\n display: block;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n width: 100%;\n font-size: 10px !important;\n line-height: 1.2 !important;\n min-height: calc(10px * 1.2);\n opacity: 0.75;\n font-weight: 500;\n color: #909399;\n }\n .hde-left-tabs-mirror__department:empty {\n visibility: hidden;\n }\n .hde-left-tabs-mirror__item:last-child {\n border-bottom: 0;\n }\n .hde-left-tabs-mirror__item:hover {\n background: #f5f7fa;\n }\n .hde-left-tabs-mirror__item.is-active {\n background: #ecf5ff;\n color: #409eff;\n }\n .hde-left-tabs-mirror__name {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n min-width: 0;\n flex: 1;\n font-family: inherit !important;\n font-size: inherit !important;\n font-weight: 600;\n }\n .hde-left-tabs-mirror__freeze-overlay {\n position: absolute;\n left: -30px;\n top: 0px;\n bottom: 0px;\n width: 60px;\n pointer-events: none;\n opacity: 0.35;\n background-color: #00c3ff6b;\n filter: drop-shadow(0 0 1px rgba(0, 84, 120, 0.65)) drop-shadow(0 0 2px rgba(0, 84, 120, 0.35));\n -webkit-mask-image: url("${LEFT_TABS_FREEZE_ICON_URL}");\n mask-image: url("${LEFT_TABS_FREEZE_ICON_URL}");\n -webkit-mask-repeat: no-repeat;\n mask-repeat: no-repeat;\n -webkit-mask-size: 100% 100%;\n mask-size: 100% 100%;\n -webkit-mask-position: center;\n mask-position: center;\n }\n .hde-left-tabs-mirror__time {\n flex: 0 0 auto;\n font-size: 10px !important;\n line-height: 1.1 !important;\n opacity: 0.7;\n white-space: nowrap;\n margin-left: auto;\n padding-right: 4px;\n }\n .hde-left-tabs-mirror__preview {\n display: block;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n width: 100%;\n font-size: 11px !important;\n line-height: 1.25 !important;\n min-height: calc(11px * 1.25);\n opacity: 0.8;\n }\n .hde-left-tabs-mirror__preview:empty {\n visibility: hidden;\n }\n .hde-left-tabs-mirror__close {\n position: absolute;\n right: 2px;\n top: 50%;\n transform: translateY(-50%);\n z-index: 1;\n flex: 0 0 auto;\n align-self: flex-start;\n border: 0;\n background: transparent;\n color: inherit;\n font-size: inherit !important;\n font-family: inherit !important;\n cursor: pointer;\n padding: 2px 4px;\n margin: 0;\n opacity: 0;\n visibility: hidden;\n pointer-events: none;\n transition: opacity .15s ease, visibility .15s ease;\n }\n .hde-left-tabs-mirror__item:hover .hde-left-tabs-mirror__close,\n .hde-left-tabs-mirror__item:focus-within .hde-left-tabs-mirror__close {\n opacity: 1;\n visibility: visible;\n pointer-events: auto;\n }\n html.${DARK_HTML_CLASS} .hde-left-tabs-mirror {\n background: #1e2529;\n border-color: #3d4654;\n }\n html.${DARK_HTML_CLASS} .hde-left-tabs-column {\n border-right: 1px solid #2d333b;\n background: #1a1f26;\n }\n html.${DARK_HTML_CLASS} .hde-left-tabs-mirror__title {\n background: #252b33;\n color: #e4e7ed;\n border-bottom-color: #3d4654;\n }\n html.${DARK_HTML_CLASS} .hde-left-tabs-mirror__item {\n color: #c0c4cc;\n border-bottom-color: #2d333b;\n }\n html.${DARK_HTML_CLASS} .hde-left-tabs-mirror__list {\n background: #1a1f26;\n }\n html.${DARK_HTML_CLASS} .hde-left-tabs-mirror__item:hover {\n background: #252b33;\n }\n html.${DARK_HTML_CLASS} .hde-left-tabs-mirror__item.is-active {\n background: #2d333b;\n color: #e4e7ed;\n }\n html.${DARK_HTML_CLASS} .hde-left-tabs-mirror__department {\n color: #909399;\n opacity: 0.9;\n }\n html.${PINK_HTML_CLASS} .hde-left-tabs-column {\n border-right: 1px solid #e3ccd8;\n background: #fbe9f2;\n }\n html.${PINK_HTML_CLASS} .hde-left-tabs-mirror {\n background: #fff6fb;\n border: 1px solid #eed9e5;\n }\n html.${PINK_HTML_CLASS} .hde-left-tabs-mirror__title {\n background: #f8eaf2;\n color: #7a5a6b;\n border-bottom-color: #e3ccd8;\n }\n html.${PINK_HTML_CLASS} .hde-left-tabs-mirror__item {\n color: #7a5a6b;\n border-bottom-color: #edd9e5;\n }\n html.${PINK_HTML_CLASS} .hde-left-tabs-mirror__list {\n background: #fbe9f2;\n }\n html.${PINK_HTML_CLASS} .hde-left-tabs-mirror__item:hover {\n background: #f9edf4;\n }\n html.${PINK_HTML_CLASS} .hde-left-tabs-mirror__item.is-active {\n background: #f4e1eb;\n color: #6b4c5d;\n }\n html.${PINK_HTML_CLASS} .hde-left-tabs-mirror__department {\n color: #9a778b;\n opacity: 0.95;\n }\n html.${PINK_HTML_CLASS} .hde-left-tabs-mirror__preview {\n color: #8b687c;\n opacity: 0.92;\n }\n html.${PINK_HTML_CLASS} .hde-left-tabs-mirror__time {\n color: #a78699;\n }\n html.${PINK_HTML_CLASS} .hde-left-tabs-mirror__indicator.has-badge {\n background: #cf7a8f;\n }\n html.${PINK_HTML_CLASS} #ticket-app .el-loading-mask,\n html.${PINK_HTML_CLASS} .el-loading-mask {\n background-color: rgba(251, 233, 242, 0.92) !important;\n }\n html.${MIDNIGHT_BLUE_HTML_CLASS} .hde-left-tabs-column {\n border-right: 1px solid #2a4266;\n background: #111c2f;\n }\n html.${MIDNIGHT_BLUE_HTML_CLASS} .hde-left-tabs-mirror {\n background: #0f1728;\n border: 1px solid #2a4266;\n }\n html.${MIDNIGHT_BLUE_HTML_CLASS} .hde-left-tabs-mirror__title {\n background: #16243b;\n color: #d4e4fb;\n border-bottom-color: #2a4266;\n }\n html.${MIDNIGHT_BLUE_HTML_CLASS} .hde-left-tabs-mirror__item {\n color: #b7cae7;\n border-bottom-color: #233a5d;\n }\n html.${MIDNIGHT_BLUE_HTML_CLASS} .hde-left-tabs-mirror__list {\n background: #111c2f;\n }\n html.${MIDNIGHT_BLUE_HTML_CLASS} .hde-left-tabs-mirror__item:hover {\n background: #1b2d4a;\n }\n html.${MIDNIGHT_BLUE_HTML_CLASS} .hde-left-tabs-mirror__item.is-active {\n background: #23395f;\n color: #e5efff;\n }\n html.${MIDNIGHT_BLUE_HTML_CLASS} .hde-left-tabs-mirror__department {\n color: #89a2c5;\n opacity: 0.95;\n }\n html.${MIDNIGHT_BLUE_HTML_CLASS} .hde-left-tabs-mirror__preview {\n color: #9bb3d4;\n opacity: 0.93;\n }\n html.${MIDNIGHT_BLUE_HTML_CLASS} .hde-left-tabs-mirror__time {\n color: #7f98bb;\n }\n html.${MIDNIGHT_BLUE_HTML_CLASS} .hde-left-tabs-mirror__indicator.has-badge {\n background: #6cb8ff;\n }\n html.${TRICOLOR_HTML_CLASS} .hde-left-tabs-column {\n border-right: 1px solid #bfd0ec;\n background: #f6f9ff;\n }\n html.${TRICOLOR_HTML_CLASS} .hde-left-tabs-mirror {\n background: #ffffff;\n border: 1px solid #d7e3f8;\n }\n html.${TRICOLOR_HTML_CLASS} .hde-left-tabs-mirror__title {\n background: #edf4ff;\n color: #1d3f73;\n border-bottom-color: #bfd0ec;\n }\n html.${TRICOLOR_HTML_CLASS} .hde-left-tabs-mirror__item {\n color: #2a4d82;\n border-bottom-color: #e4ecfb;\n }\n html.${TRICOLOR_HTML_CLASS} .hde-left-tabs-mirror__list {\n background: #f6f9ff;\n }\n html.${TRICOLOR_HTML_CLASS} .hde-left-tabs-mirror__item:hover {\n background: #f3f7ff;\n }\n html.${TRICOLOR_HTML_CLASS} .hde-left-tabs-mirror__item.is-active {\n background: #e1ebff;\n color: #173968;\n }\n html.${TRICOLOR_HTML_CLASS} .hde-left-tabs-mirror__department {\n color: #5570a0;\n opacity: 0.95;\n }\n html.${TRICOLOR_HTML_CLASS} .hde-left-tabs-mirror__preview {\n color: #4d6793;\n opacity: 0.93;\n }\n html.${TRICOLOR_HTML_CLASS} .hde-left-tabs-mirror__time {\n color: #6a81ac;\n }\n html.${TRICOLOR_HTML_CLASS} .hde-left-tabs-mirror__indicator.has-badge {\n background: #d63445;\n }\n html.${MONOCHROME_HTML_CLASS} .hde-left-tabs-column {\n border-right: 1px solid #3a3a3a;\n background: #121212;\n }\n html.${MONOCHROME_HTML_CLASS} .hde-left-tabs-mirror {\n background: #171717;\n border: 1px solid #3a3a3a;\n }\n html.${MONOCHROME_HTML_CLASS} .hde-left-tabs-mirror__title {\n background: #202020;\n color: #efefef;\n border-bottom-color: #3a3a3a;\n }\n html.${MONOCHROME_HTML_CLASS} .hde-left-tabs-mirror__item {\n color: #dedede;\n border-bottom-color: #323232;\n }\n html.${MONOCHROME_HTML_CLASS} .hde-left-tabs-mirror__list {\n background: #121212;\n }\n html.${MONOCHROME_HTML_CLASS} .hde-left-tabs-mirror__item:hover {\n background: #232323;\n }\n html.${MONOCHROME_HTML_CLASS} .hde-left-tabs-mirror__item.is-active {\n background: #2b2b2b;\n color: #ffffff;\n }\n html.${MONOCHROME_HTML_CLASS} .hde-left-tabs-mirror__department {\n color: #bbbbbb;\n opacity: 0.95;\n }\n html.${MONOCHROME_HTML_CLASS} .hde-left-tabs-mirror__preview {\n color: #c4c4c4;\n opacity: 0.93;\n }\n html.${MONOCHROME_HTML_CLASS} .hde-left-tabs-mirror__time {\n color: #a6a6a6;\n }\n html.${MONOCHROME_HTML_CLASS} .hde-left-tabs-mirror__indicator.has-badge {\n background: #f1f1f1;\n }\n /* Унификация цвета стрелок popper под текущую тему */\n :root {\n --hde-popper-bg: #ffffff;\n --hde-popper-border: #dcdfe6;\n }\n html.${DARK_HTML_CLASS} {\n --hde-popper-bg: #1e2529;\n --hde-popper-border: #3d4654;\n }\n html.${PINK_HTML_CLASS} {\n --hde-popper-bg: #fff6fb;\n --hde-popper-border: #e3ccd8;\n }\n html.${MIDNIGHT_BLUE_HTML_CLASS} {\n --hde-popper-bg: #15223a;\n --hde-popper-border: #2a4266;\n }\n html.${TRICOLOR_HTML_CLASS} {\n --hde-popper-bg: #ffffff;\n --hde-popper-border: #bfd0ec;\n }\n html.${MONOCHROME_HTML_CLASS} {\n --hde-popper-bg: #1b1b1b;\n --hde-popper-border: #3a3a3a;\n }\n .el-popover,\n .el-popper {\n border-color: var(--hde-popper-border) !important;\n }\n .el-popover[x-placement^="top"] .popper__arrow,\n .el-popover[x-placement^="bottom"] .popper__arrow,\n .el-popover[x-placement^="left"] .popper__arrow,\n .el-popover[x-placement^="right"] .popper__arrow,\n .el-popper[data-popper-placement^="top"] .popper__arrow,\n .el-popper[data-popper-placement^="bottom"] .popper__arrow,\n .el-popper[data-popper-placement^="left"] .popper__arrow,\n .el-popper[data-popper-placement^="right"] .popper__arrow,\n .el-popover[data-popper-placement^="top"] .popper__arrow,\n .el-popover[data-popper-placement^="bottom"] .popper__arrow,\n .el-popover[data-popper-placement^="left"] .popper__arrow,\n .el-popover[data-popper-placement^="right"] .popper__arrow {\n border-top-color: var(--hde-popper-border) !important;\n border-bottom-color: var(--hde-popper-border) !important;\n border-left-color: var(--hde-popper-border) !important;\n border-right-color: var(--hde-popper-border) !important;\n }\n .el-popover .popper__arrow::after,\n .el-popper[data-popper-placement] .popper__arrow::after {\n border-top-color: var(--hde-popper-bg) !important;\n border-bottom-color: var(--hde-popper-bg) !important;\n border-left-color: var(--hde-popper-bg) !important;\n border-right-color: var(--hde-popper-bg) !important;\n }\n /* Popup уведомлений — явные цвета под темы */\n html.${DARK_HTML_CLASS} .notifications {\n background: #1e2529 !important;\n color: #e4e7ed !important;\n border: 1px solid #3d4654 !important;\n }\n html.${DARK_HTML_CLASS} .notifications__heading,\n html.${DARK_HTML_CLASS} .notifications__filters,\n html.${DARK_HTML_CLASS} .notifications__item {\n border-color: #3d4654 !important;\n }\n html.${DARK_HTML_CLASS} .notifications__heading-text,\n html.${DARK_HTML_CLASS} .notifications__item-name,\n html.${DARK_HTML_CLASS} .notifications__item-text,\n html.${DARK_HTML_CLASS} .notifications__close-button,\n html.${DARK_HTML_CLASS} .notifications__item-icon i {\n color: #e4e7ed !important;\n }\n html.${DARK_HTML_CLASS} .notifications__item-date {\n color: #a8abb2 !important;\n }\n html.${DARK_HTML_CLASS} .notifications__filter button {\n background: #252b33 !important;\n color: #c0c4cc !important;\n border: 1px solid #3d4654 !important;\n }\n html.${DARK_HTML_CLASS} .notifications__filter button.active,\n html.${DARK_HTML_CLASS} .notifications__filter button:hover {\n background: #2d333b !important;\n color: #e4e7ed !important;\n border-color: #4a5563 !important;\n }\n html.${DARK_HTML_CLASS} .notifications__item:hover,\n html.${DARK_HTML_CLASS} .notifications__item_unread {\n background: #252b33 !important;\n }\n\n html.${PINK_HTML_CLASS} .notifications {\n background: #fff6fb !important;\n color: #6b4c5d !important;\n border: 1px solid #e3ccd8 !important;\n }\n html.${PINK_HTML_CLASS} .notifications__heading,\n html.${PINK_HTML_CLASS} .notifications__filters,\n html.${PINK_HTML_CLASS} .notifications__item {\n border-color: #e3ccd8 !important;\n }\n html.${PINK_HTML_CLASS} .notifications__heading-text,\n html.${PINK_HTML_CLASS} .notifications__item-name,\n html.${PINK_HTML_CLASS} .notifications__item-text,\n html.${PINK_HTML_CLASS} .notifications__close-button,\n html.${PINK_HTML_CLASS} .notifications__item-icon i {\n color: #6b4c5d !important;\n }\n html.${PINK_HTML_CLASS} .notifications__item-date {\n color: #9a778b !important;\n }\n html.${PINK_HTML_CLASS} .notifications__filter button {\n background: #f8eaf2 !important;\n color: #7a5a6b !important;\n border: 1px solid #e3ccd8 !important;\n }\n html.${PINK_HTML_CLASS} .notifications__filter button.active,\n html.${PINK_HTML_CLASS} .notifications__filter button:hover {\n background: #f4e1eb !important;\n color: #6b4c5d !important;\n border-color: #dcbdd0 !important;\n }\n html.${PINK_HTML_CLASS} .notifications__item:hover,\n html.${PINK_HTML_CLASS} .notifications__item_unread {\n background: #fdf1f7 !important;\n }\n\n html.${MIDNIGHT_BLUE_HTML_CLASS} .notifications {\n background: #0f1728 !important;\n color: #d4e4fb !important;\n border: 1px solid #2a4266 !important;\n }\n html.${MIDNIGHT_BLUE_HTML_CLASS} .notifications__heading,\n html.${MIDNIGHT_BLUE_HTML_CLASS} .notifications__filters,\n html.${MIDNIGHT_BLUE_HTML_CLASS} .notifications__item {\n border-color: #2a4266 !important;\n }\n html.${MIDNIGHT_BLUE_HTML_CLASS} .notifications__heading-text,\n html.${MIDNIGHT_BLUE_HTML_CLASS} .notifications__item-name,\n html.${MIDNIGHT_BLUE_HTML_CLASS} .notifications__item-text,\n html.${MIDNIGHT_BLUE_HTML_CLASS} .notifications__close-button,\n html.${MIDNIGHT_BLUE_HTML_CLASS} .notifications__item-icon i {\n color: #d4e4fb !important;\n }\n html.${MIDNIGHT_BLUE_HTML_CLASS} .notifications__item-date {\n color: #89a2c5 !important;\n }\n html.${MIDNIGHT_BLUE_HTML_CLASS} .notifications__filter button {\n background: #16243b !important;\n color: #b7cae7 !important;\n border: 1px solid #2a4266 !important;\n }\n html.${MIDNIGHT_BLUE_HTML_CLASS} .notifications__filter button.active,\n html.${MIDNIGHT_BLUE_HTML_CLASS} .notifications__filter button:hover {\n background: #23395f !important;\n color: #e5efff !important;\n border-color: #3f6292 !important;\n }\n html.${MIDNIGHT_BLUE_HTML_CLASS} .notifications__item:hover,\n html.${MIDNIGHT_BLUE_HTML_CLASS} .notifications__item_unread {\n background: #1b2d4a !important;\n }\n html.${TRICOLOR_HTML_CLASS} .notifications {\n background: #ffffff !important;\n color: #1d3f73 !important;\n border: 1px solid #bfd0ec !important;\n }\n html.${TRICOLOR_HTML_CLASS} .notifications__heading,\n html.${TRICOLOR_HTML_CLASS} .notifications__filters,\n html.${TRICOLOR_HTML_CLASS} .notifications__item {\n border-color: #d6e2f7 !important;\n }\n html.${TRICOLOR_HTML_CLASS} .notifications__heading-text,\n html.${TRICOLOR_HTML_CLASS} .notifications__item-name,\n html.${TRICOLOR_HTML_CLASS} .notifications__item-text,\n html.${TRICOLOR_HTML_CLASS} .notifications__close-button,\n html.${TRICOLOR_HTML_CLASS} .notifications__item-icon i {\n color: #1d3f73 !important;\n }\n html.${TRICOLOR_HTML_CLASS} .notifications__item-date {\n color: #6a81ac !important;\n }\n html.${TRICOLOR_HTML_CLASS} .notifications__filter button {\n background: #edf4ff !important;\n color: #2a4d82 !important;\n border: 1px solid #bfd0ec !important;\n }\n html.${TRICOLOR_HTML_CLASS} .notifications__filter button.active,\n html.${TRICOLOR_HTML_CLASS} .notifications__filter button:hover {\n background: #e1ebff !important;\n color: #173968 !important;\n border-color: #a8c0e7 !important;\n }\n html.${TRICOLOR_HTML_CLASS} .notifications__item:hover,\n html.${TRICOLOR_HTML_CLASS} .notifications__item_unread {\n background: #f3f7ff !important;\n }\n html.${MONOCHROME_HTML_CLASS} .notifications {\n background: #171717 !important;\n color: #f1f1f1 !important;\n border: 1px solid #3a3a3a !important;\n }\n html.${MONOCHROME_HTML_CLASS} .notifications__heading,\n html.${MONOCHROME_HTML_CLASS} .notifications__filters,\n html.${MONOCHROME_HTML_CLASS} .notifications__item {\n border-color: #3a3a3a !important;\n }\n html.${MONOCHROME_HTML_CLASS} .notifications__heading-text,\n html.${MONOCHROME_HTML_CLASS} .notifications__item-name,\n html.${MONOCHROME_HTML_CLASS} .notifications__item-text,\n html.${MONOCHROME_HTML_CLASS} .notifications__close-button,\n html.${MONOCHROME_HTML_CLASS} .notifications__item-icon i {\n color: #f1f1f1 !important;\n }\n html.${MONOCHROME_HTML_CLASS} .notifications__item-date {\n color: #a9a9a9 !important;\n }\n html.${MONOCHROME_HTML_CLASS} .notifications__filter button {\n background: #222222 !important;\n color: #d8d8d8 !important;\n border: 1px solid #3a3a3a !important;\n }\n html.${MONOCHROME_HTML_CLASS} .notifications__filter button.active,\n html.${MONOCHROME_HTML_CLASS} .notifications__filter button:hover {\n background: #2c2c2c !important;\n color: #ffffff !important;\n border-color: #535353 !important;\n }\n html.${MONOCHROME_HTML_CLASS} .notifications__item:hover,\n html.${MONOCHROME_HTML_CLASS} .notifications__item_unread {\n background: #242424 !important;\n }\n /* Контрастные сине-красные акценты для основных action-кнопок */\n html.${TRICOLOR_HTML_CLASS} #ticket-app .el-button.el-button--primary,\n html.${TRICOLOR_HTML_CLASS} #ticket-app .ticket-editor__action_type_post,\n html.${TRICOLOR_HTML_CLASS} #ticket-app .ticket-editor__action_type_comment {\n background: #d63445 !important;\n border-color: #bf2f3f !important;\n color: #ffffff !important;\n }\n html.${TRICOLOR_HTML_CLASS} #ticket-app .el-button.el-button--primary:hover,\n html.${TRICOLOR_HTML_CLASS} #ticket-app .ticket-editor__action_type_post:hover,\n html.${TRICOLOR_HTML_CLASS} #ticket-app .ticket-editor__action_type_comment:hover {\n background: #c42f3f !important;\n border-color: #a92836 !important;\n }\n html.${MONOCHROME_HTML_CLASS} #ticket-app .el-button.el-button--primary,\n html.${MONOCHROME_HTML_CLASS} #ticket-app .ticket-editor__action_type_post,\n html.${MONOCHROME_HTML_CLASS} #ticket-app .ticket-editor__action_type_comment {\n background: #f1f1f1 !important;\n border-color: #d3d3d3 !important;\n color: #121212 !important;\n }\n html.${MONOCHROME_HTML_CLASS} #ticket-app .el-button.el-button--primary:hover,\n html.${MONOCHROME_HTML_CLASS} #ticket-app .ticket-editor__action_type_post:hover,\n html.${MONOCHROME_HTML_CLASS} #ticket-app .ticket-editor__action_type_comment:hover {\n background: #dcdcdc !important;\n border-color: #c4c4c4 !important;\n }\n /* Акцентные фильтры sidebar: сохраняем смысл цвета, но смягчаем оттенок в темах */\n html.${DARK_HTML_CLASS} #ticket-app .ticket-sidebar__filter-button[style*="rgb(255, 0, 0)"],\n html.${DARK_HTML_CLASS} #ticket-app .ticket-sidebar__filter-button[style*="rgb(225, 18, 18)"],\n html.${DARK_HTML_CLASS} #ticket-app .ticket-sidebar__filter-name[style*="rgb(255, 0, 0)"],\n html.${DARK_HTML_CLASS} #ticket-app .ticket-sidebar__filter-name[style*="rgb(225, 18, 18)"] {\n color: #ff8f8f !important;\n }\n html.${DARK_HTML_CLASS} #ticket-app .ticket-sidebar__filter-button[style*="rgb(81, 169, 251)"],\n html.${DARK_HTML_CLASS} #ticket-app .ticket-sidebar__filter-button[style*="rgb(13, 113, 207)"],\n html.${DARK_HTML_CLASS} #ticket-app .ticket-sidebar__filter-name[style*="rgb(81, 169, 251)"],\n html.${DARK_HTML_CLASS} #ticket-app .ticket-sidebar__filter-name[style*="rgb(13, 113, 207)"] {\n color: #87bdf2 !important;\n }\n html.${PINK_HTML_CLASS} #ticket-app .ticket-sidebar__filter-button[style*="rgb(255, 0, 0)"],\n html.${PINK_HTML_CLASS} #ticket-app .ticket-sidebar__filter-button[style*="rgb(225, 18, 18)"],\n html.${PINK_HTML_CLASS} #ticket-app .ticket-sidebar__filter-name[style*="rgb(255, 0, 0)"],\n html.${PINK_HTML_CLASS} #ticket-app .ticket-sidebar__filter-name[style*="rgb(225, 18, 18)"] {\n color: #c8657b !important;\n }\n html.${PINK_HTML_CLASS} #ticket-app .ticket-sidebar__filter-button[style*="rgb(81, 169, 251)"],\n html.${PINK_HTML_CLASS} #ticket-app .ticket-sidebar__filter-button[style*="rgb(13, 113, 207)"],\n html.${PINK_HTML_CLASS} #ticket-app .ticket-sidebar__filter-name[style*="rgb(81, 169, 251)"],\n html.${PINK_HTML_CLASS} #ticket-app .ticket-sidebar__filter-name[style*="rgb(13, 113, 207)"] {\n color: #6f9ccc !important;\n }\n html.${MIDNIGHT_BLUE_HTML_CLASS} #ticket-app .ticket-sidebar__filter-button[style*="rgb(255, 0, 0)"],\n html.${MIDNIGHT_BLUE_HTML_CLASS} #ticket-app .ticket-sidebar__filter-button[style*="rgb(225, 18, 18)"],\n html.${MIDNIGHT_BLUE_HTML_CLASS} #ticket-app .ticket-sidebar__filter-name[style*="rgb(255, 0, 0)"],\n html.${MIDNIGHT_BLUE_HTML_CLASS} #ticket-app .ticket-sidebar__filter-name[style*="rgb(225, 18, 18)"] {\n color: #ff9a9a !important;\n }\n html.${MIDNIGHT_BLUE_HTML_CLASS} #ticket-app .ticket-sidebar__filter-button[style*="rgb(81, 169, 251)"],\n html.${MIDNIGHT_BLUE_HTML_CLASS} #ticket-app .ticket-sidebar__filter-button[style*="rgb(13, 113, 207)"],\n html.${MIDNIGHT_BLUE_HTML_CLASS} #ticket-app .ticket-sidebar__filter-name[style*="rgb(81, 169, 251)"],\n html.${MIDNIGHT_BLUE_HTML_CLASS} #ticket-app .ticket-sidebar__filter-name[style*="rgb(13, 113, 207)"] {\n color: #8ec2ff !important;\n }\n html.${TRICOLOR_HTML_CLASS} #ticket-app .ticket-sidebar__filter-button[style*="rgb(255, 0, 0)"],\n html.${TRICOLOR_HTML_CLASS} #ticket-app .ticket-sidebar__filter-button[style*="rgb(225, 18, 18)"],\n html.${TRICOLOR_HTML_CLASS} #ticket-app .ticket-sidebar__filter-name[style*="rgb(255, 0, 0)"],\n html.${TRICOLOR_HTML_CLASS} #ticket-app .ticket-sidebar__filter-name[style*="rgb(225, 18, 18)"] {\n color: #d63445 !important;\n }\n html.${TRICOLOR_HTML_CLASS} #ticket-app .ticket-sidebar__filter-button[style*="rgb(81, 169, 251)"],\n html.${TRICOLOR_HTML_CLASS} #ticket-app .ticket-sidebar__filter-button[style*="rgb(13, 113, 207)"],\n html.${TRICOLOR_HTML_CLASS} #ticket-app .ticket-sidebar__filter-name[style*="rgb(81, 169, 251)"],\n html.${TRICOLOR_HTML_CLASS} #ticket-app .ticket-sidebar__filter-name[style*="rgb(13, 113, 207)"] {\n color: #1f6fc5 !important;\n }\n html.${MONOCHROME_HTML_CLASS} #ticket-app .ticket-sidebar__filter-button[style*="rgb(255, 0, 0)"],\n html.${MONOCHROME_HTML_CLASS} #ticket-app .ticket-sidebar__filter-button[style*="rgb(225, 18, 18)"],\n html.${MONOCHROME_HTML_CLASS} #ticket-app .ticket-sidebar__filter-name[style*="rgb(255, 0, 0)"],\n html.${MONOCHROME_HTML_CLASS} #ticket-app .ticket-sidebar__filter-name[style*="rgb(225, 18, 18)"] {\n color: #d6d6d6 !important;\n }\n html.${MONOCHROME_HTML_CLASS} #ticket-app .ticket-sidebar__filter-button[style*="rgb(81, 169, 251)"],\n html.${MONOCHROME_HTML_CLASS} #ticket-app .ticket-sidebar__filter-button[style*="rgb(13, 113, 207)"],\n html.${MONOCHROME_HTML_CLASS} #ticket-app .ticket-sidebar__filter-name[style*="rgb(81, 169, 251)"],\n html.${MONOCHROME_HTML_CLASS} #ticket-app .ticket-sidebar__filter-name[style*="rgb(13, 113, 207)"] {\n color: #a6a6a6 !important;\n }\n\n .staffs-injected-block {\n border-radius: 3px;\n padding-bottom: 10px;\n margin-bottom: 12px;\n text-align: center;\n --staff-status-online: #3e9a55;\n --staff-status-invisible: #3f78b8;\n --staff-status-break: #c9872c;\n --staff-status-offline: #c25353;\n --staffs-muted: #c5c9d0;\n --staffs-group-color: #555;\n }\n .staffs-injected-group-header {\n display: flex;\n align-items: center;\n margin: 0;\n font-weight: 700;\n color: var(--staffs-group-color);\n }\n .staffs-injected-group-total {\n flex: 0 0 auto;\n padding-right: 0;\n margin: 0 0 0 6px;\n }\n .staffs-injected-group-name {\n flex: 1;\n text-align: center;\n }\n .staffs-injected-list {\n list-style: none;\n padding: 0;\n margin: 0 0 8px 0;\n }\n .staffs-injected-member-row {\n display: flex;\n align-items: center;\n padding: 0;\n }\n .staffs-injected-badge {\n display: inline-block;\n width: 18px;\n text-align: center;\n font-size: 13px;\n line-height: 1;\n }\n .staffs-injected-badge.no-tickets {\n color: var(--staffs-muted);\n }\n .staffs-injected-member-name {\n font-weight: 400;\n flex: 1;\n text-align: left;\n }\n .staffs-injected-member-status {\n white-space: nowrap;\n margin: 0 5px 0 0;\n }\n .staffs-injected-member-name.status-online,\n .staffs-injected-member-status.status-online,\n .staffs-injected-badge.status-online.has-tickets {\n color: var(--staff-status-online);\n }\n .staffs-injected-member-name.status-invisible,\n .staffs-injected-member-status.status-invisible,\n .staffs-injected-badge.status-invisible.has-tickets {\n color: var(--staff-status-invisible);\n }\n .staffs-injected-member-name.status-break,\n .staffs-injected-member-status.status-break,\n .staffs-injected-badge.status-break.has-tickets {\n color: var(--staff-status-break);\n }\n .staffs-injected-member-name.status-offline,\n .staffs-injected-member-status.status-offline,\n .staffs-injected-badge.status-offline.has-tickets {\n color: var(--staff-status-offline);\n }\n\n html.${DARK_HTML_CLASS} .staffs-injected-block {\n --staff-status-online: #78c18b;\n --staff-status-invisible: #7cb5ff;\n --staff-status-break: #f4b562;\n --staff-status-offline: #ef8a8a;\n --staffs-muted: #6f7883;\n --staffs-group-color: #c0c4cc;\n }\n html.${PINK_HTML_CLASS} .staffs-injected-block {\n --staff-status-online: #7fae7f;\n --staff-status-invisible: #8aa4d6;\n --staff-status-break: #d5a06a;\n --staff-status-offline: #cf7a8f;\n --staffs-muted: #b79fb0;\n --staffs-group-color: #7a5a6b;\n }\n html.${MIDNIGHT_BLUE_HTML_CLASS} .staffs-injected-block {\n --staff-status-online: #7bcf9a;\n --staff-status-invisible: #8ec2ff;\n --staff-status-break: #e3b679;\n --staff-status-offline: #ff9a9a;\n --staffs-muted: #7f98bb;\n --staffs-group-color: #b7cae7;\n }\n html.${TRICOLOR_HTML_CLASS} .staffs-injected-block {\n --staff-status-online: #4cae7d;\n --staff-status-invisible: #1f6fc5;\n --staff-status-break: #c08a45;\n --staff-status-offline: #d63445;\n --staffs-muted: #6a81ac;\n --staffs-group-color: #2a4d82;\n }\n html.${MONOCHROME_HTML_CLASS} .staffs-injected-block {\n --staff-status-online: #dcdcdc;\n --staff-status-invisible: #c4c4c4;\n --staff-status-break: #adadad;\n --staff-status-offline: #8f8f8f;\n --staffs-muted: #787878;\n --staffs-group-color: #efefef;\n }\n\n /* Чтобы пункт меню/кнопка НИКОГДА не прятались (Vue может менять display/opacity) */\n #hde-tools-menu { display: inline-flex !important; opacity: 1 !important; visibility: visible !important; }\n #hde-tools-menu .menu__item { display: inline-flex !important; opacity: 1 !important; visibility: visible !important; }\n #hde-tools-menu * { opacity: 1 !important; }\n a.menu__item.menu__item_logo {\n position: relative !important;\n background-image: none !important;\n background-color: transparent !important;\n color: var(--menu-item-color, #606266) !important;\n }\n a.menu__item.menu__item_logo::before {\n content: '';\n position: absolute;\n inset: 0;\n background: currentColor;\n -webkit-mask: url("//cdn5.helpdeskeddy.com//img/menu-logo-white.svg") center / contain no-repeat;\n mask: url("//cdn5.helpdeskeddy.com//img/menu-logo-white.svg") center / contain no-repeat;\n }\n a.menu__item.menu__item_logo:hover {\n color: var(--menu-item-color-hover, #409eff) !important;\n }\n a.menu__item[href="/ru/dashboard"],\n a.menu__item[href="/ru/dashboard/"] {\n display: none !important;\n }\n #hde-tools-btn {\n flex-direction: column !important;\n align-items: center !important;\n justify-content: center !important;\n gap: 4px !important;\n text-align: center !important;\n }\n #hde-tools-btn .menu__item-icon,\n #hde-tools-btn .menu__item-name {\n display: block !important;\n margin: 0 !important;\n }\n\n #hde-tools-btn .menu__item-icon {\n position: relative;\n width: 25px;\n height: 25px;\n }\n #hde-tools-btn .menu__item-icon .hde-dino-source {\n display: none !important;\n }\n #hde-tools-btn .menu__item-icon .hde-dino-mask {\n position: absolute;\n inset: 0;\n background: currentColor;\n -webkit-mask-size: contain;\n mask-size: contain;\n -webkit-mask-repeat: no-repeat;\n mask-repeat: no-repeat;\n -webkit-mask-position: center;\n mask-position: center;\n }\n html.${LEFT_TABS_ONLY_CLASS} #ticket-app .ticket-topbar__tabs {\n display: none !important;\n }\n html.${LEFT_TABS_ONLY_CLASS} .ticket-tabs__show-more-popper {\n display: none !important;\n }\n `;
document.head.appendChild(style);
}
const menuItems = document.querySelector(".menu__items");
const btnContainer = existingBtn || document.createElement("div");
btnContainer.id = "hde-tools-menu";
btnContainer.style.position = "relative";
if (!existingBtn) {
const btn = document.createElement("a");
btn.className = "menu__item";
btn.href = "javascript:void(0)";
btn.id = "hde-tools-btn";
btn.title = "HDE Tools Settings";
btn.innerHTML = `<div data-v-808034fc="" class="menu__item-icon"><img width="25" height="25" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAFOElEQVR4nO2ae4jVRRTHz9WydbPWntKLyiyIMoLsjwqR7OWiZe8MooLe9MTIKLZcgtISgiIjUwMrSgx7/VFbLaZkBVbW+kel9jC3tujllrmaun7itN/f7ni7j9+9d+7uFfrC5d6Z35wzc34z851zzlyz/1EagLOB9cB3wFm2KwI4AeiiD5uA462WABwHLAHeAM7J08afO54C5uh3q9UQMsAnwZveAUwNG/ib17NOM9vbzBqAP1RXG7MCnKIBfQM0A9tlzJVBm/uT2QjqnlbdfVYLAO7SgB5X+VqVN5jZQap7XXUXB3KXqO41qwUAj2lAt6lqEPCB6tqBDs1QPnQCs4DTzWy3gTRktgZ0O3AzsI7y4cvzejMbMhCGJOt/SzCgtcADQKOZHWxmg3OI7g4cAZwBzATWBPJtzoT9bcjdwQC+Aib78ipDlS/JS4HV0uVnzoXVoFjn/1+BX4C3geuAy4Gt6niBmdVF6GtP4BnpdN3nWywA44qs7egUCjwk3ZujLTNgkpS+ZWYHaDYSJpofpZPc/c5THyujEABwrBR2aC3fqvKXZlZv1cMw7TvEZlH2iFOjY7K8V8dFVmXQd3CuzcN+JSu8Qwp/1/dqN7BA+4nA9zoUG0voZ2KW3GDRuWN8xYaY2R6BQseDRQbkA0mwvgRD2rPldM44ZhaSTcv5f5uZ+0pbVF5q/YdWfY+NplHT7jiySLtGvV2PCCeUoL8xWw44XH1+a5FQl7gi7mJY/2GoDOmqSAtwMvBSGKpGG2L6MfyLShTcC3T3qOn9Llkh8K4cw2k6qf38qVf0OE1EsrwqhgBjJL8NeETearmGLKU4qmbIaZLf4CFthQrdI5gAPK/TepM+PhPPaWPnZdCKDNGJ/oJ0eCw+t1KF5YII/Q5RKOqG9ML6GcTqV5sy1YwA7wPLygywciL6C0xpSBK2TordL9DiJFSOjv3lyF0DTElpSOJgtpQ98ixksdt2BXOZtMJXK0LLxp9Bm2XACs8eBqINYqRuM9snVWfFx5Lg0WC/3plW2HNSjveUGVys8sdBm5Wqeyd0WzyajLm8EPT73CAJmEo4yelOUfk8lWcHbY5WUsLxchIxAtNV1xzTEOAm4FP9XpNW+EYJdMgDXRAaFrQ7SRlDZPyYIByeFdmQBJ4IPDGtvEdnyyX4ufaLxyP75uhodBBfOzbGjLXpwyvAVR7olarjQGBVoOjFAm0bFM0ls+MkMMwiAKFSJb50UAoozXT6QXhoxAOxrtR4xH2rJ7WEfnYm8qWhm6gkmzgQOEz9t6dqDZxKfjjVjSjgkrjxP4qqnesv0+1UxaCPLVtLuXV1vOkHGTAjMOSKAnI++FzwiHI+MKpCQ+ZI38NpBUZJwBPVI4NEw7MpxD2BPQI4E7hHl5/dQTK6ucy0Z31AHp4vmJ5KT5C78j3iWFIO1UnXSOVvE4M8hTS8RB1NwcvdEYxpeFpBR1upHefROTZIvH2WlpLpOZvcZ0M3AuOCVdKW5d/tJDg+cDc+NLP9LC7zfCHdC1O0PyQ4YOfl0dPyH4rX/Z8nGRyvViPLDhwF/KY+bigyE1+r3Uee18p67u7ST3p+S7YRCWbEjOxyDPKC4IpiaI6N3RQspxX5wgC/yVKbH8LK5C01VcuAPH/nmKoUk7vlTwSZfsfcQqtCs+ubf2vy4v00X2Vmo/25mXkGfJ2ZbTazjZIbFGysvUQAyacsRouIxZlMpuePCMAxwKKAcncFdIk0ej3xneJenzKxlbsX/kluibaZ2V9m5o5bZyaT8T/IuNviM1cT+AdBA3PwGaO09gAAAABJRU5ErkJggg==" alt="kawaii-dinosaur--v2"></div><div data-v-808034fc="" class="menu__item-name"> HDE toys </div>`;
btnContainer.appendChild(btn);
}
const btn = btnContainer.querySelector("#hde-tools-btn") || btnContainer.querySelector("a");
const dinoIconWrap = btn.querySelector(".menu__item-icon");
const dinoSourceImg = dinoIconWrap?.querySelector("img");
if (dinoIconWrap && dinoSourceImg) {
dinoSourceImg.classList.add("hde-dino-source");
let dinoMask = dinoIconWrap.querySelector(".hde-dino-mask");
if (!dinoMask) {
dinoMask = document.createElement("span");
dinoMask.className = "hde-dino-mask";
dinoMask.setAttribute("aria-hidden", "true");
dinoIconWrap.appendChild(dinoMask);
}
const src = dinoSourceImg.currentSrc || dinoSourceImg.src;
dinoMask.style.webkitMaskImage = `url("${src}")`;
dinoMask.style.maskImage = `url("${src}")`;
}
const panel = document.getElementById(MENU_PANEL_ID) || document.createElement("div");
panel.id = MENU_PANEL_ID;
panel.className = "hde-tools-panel";
panel.innerHTML = `\n <label class="hde-tools-field">\n <span>Тема интерфейса</span>\n <select id="hde-cfg-theme-mode">\n ${renderThemeOptions(CONFIG.themeMode)}\n </select>\n </label>\n <label class="hde-tools-toggle">\n <span>Компактный чат</span>\n <input type="checkbox" id="hde-cfg-compact" ${CONFIG.compactChat ? "checked" : ""}>\n </label>\n <label class="hde-tools-toggle">\n <span>Левая панель тикетов</span>\n <input type="checkbox" id="hde-cfg-left-tabs-mirror" ${CONFIG.leftTabsMirror ? "checked" : ""}>\n </label>\n <label class="hde-tools-toggle">\n <span>Ресайз блоков</span>\n <input type="checkbox" id="hde-cfg-resizable-panels" ${CONFIG.resizablePanels ? "checked" : ""}>\n </label>\n <label class="hde-tools-field" id="hde-resize-reset-wrap" style="display:${CONFIG.resizablePanels ? "flex" : "none"};">\n <span>Сброс размеров блоков</span>\n <button type="button" id="hde-cfg-resize-reset" style="padding:4px 8px;font-size:12px;border:1px solid #dcdfe6;border-radius:6px;background:#fff;cursor:pointer;">Сброс</button>\n </label>\n <label class="hde-tools-toggle hde-tools-toggle-colleague">\n <span>Статусы коллег</span>\n <span class="hde-tools-toggle-actions">\n <a\n id="hde-colleague-staffs-link"\n class="hde-tools-link-btn"\n href="/ru/dashboard/staffs/"\n target="_blank"\n rel="noopener noreferrer"\n title="Открыть аналитику сотрудников"\n aria-label="Открыть аналитику сотрудников"\n ><i class="el-icon-setting"></i></a>\n <input type="checkbox" id="hde-cfg-colleague-statuses" ${CONFIG.colleagueStatuses ? "checked" : ""}>\n </span>\n </label>\n <label class="hde-tools-toggle">\n <span>Выводить диалог целиком</span>\n <input type="checkbox" id="hde-cfg-autoload" ${CONFIG.autoLoadHistory ? "checked" : ""}>\n </label>\n <label class="hde-tools-toggle">\n <span>Подгружать прошлые диалоги</span>\n <input type="checkbox" id="hde-cfg-prev-dialogs" ${CONFIG.autoLoadPreviousDialogs ? "checked" : ""}>\n </label>\n <label class="hde-tools-field">\n <span>Кол-во подгружаемых диалогов</span>\n <input type="number" id="hde-cfg-prev-limit" min="1" max="10" step="1" value="${getPreviousDialogsLimit()}">\n </label>\n `;
panel.dataset.hdePanelVersion = SETTINGS_PANEL_VERSION;
function positionPanel() {
const rect = btn.getBoundingClientRect();
panel.style.left = rect.right + 8 + "px";
panel.style.top = rect.top + "px";
}
if (!btn.dataset.hdeToolsBound) {
btn.dataset.hdeToolsBound = "1";
btn.addEventListener("click", e => {
e.preventDefault();
e.stopPropagation();
if (CONFIG.colleagueStatuses) preloadStaffsDrawer();
const isActive = panel.classList.contains("active");
if (isActive) {
panel.classList.remove("active");
} else {
positionPanel();
panel.classList.add("active");
}
});
}
if (!window.__hdeToolsDocClickBound) {
window.__hdeToolsDocClickBound = true;
document.addEventListener("click", e => {
const currentPanel = document.getElementById(MENU_PANEL_ID);
const currentBtn = document.getElementById("hde-tools-btn");
if (!currentPanel || !currentBtn) return;
if (!currentPanel.contains(e.target) && e.target !== currentBtn && !currentBtn.contains(e.target)) {
currentPanel.classList.remove("active");
}
});
}
panel.querySelector("#hde-cfg-theme-mode").onchange = e => {
CONFIG.themeMode = normalizeThemeMode(e.target.value);
saveConfig();
applyThemeFromConfig();
};
panel.querySelector("#hde-cfg-compact").onchange = e => {
CONFIG.compactChat = e.target.checked;
saveConfig();
if (CONFIG.compactChat) {
injectStyle();
processAllMessages();
} else {
alert("Для отключения компактного вида необходимо обновить страницу");
}
};
panel.querySelector("#hde-cfg-left-tabs-mirror").onchange = e => {
CONFIG.leftTabsMirror = e.target.checked;
saveConfig();
syncNativeTicketTabsVisibilityClass();
syncLeftTabsOnHrefChange();
syncLeftTabsMirror();
};
const resizeToggle = panel.querySelector("#hde-cfg-resizable-panels");
const resizeResetWrap = panel.querySelector("#hde-resize-reset-wrap");
const resizeResetBtn = panel.querySelector("#hde-cfg-resize-reset");
if (resizeToggle && resizeResetBtn) {
const resizeLabel = resizeToggle.closest("label");
let actionsWrap = resizeLabel?.querySelector(".hde-tools-toggle-actions");
if (!actionsWrap && resizeLabel) {
actionsWrap = document.createElement("span");
actionsWrap.className = "hde-tools-toggle-actions";
resizeLabel.appendChild(actionsWrap);
}
if (actionsWrap) {
resizeResetBtn.className = "hde-tools-link-btn";
resizeResetBtn.innerHTML = '<i class="el-icon-setting"></i>';
resizeResetBtn.removeAttribute("style");
resizeResetBtn.setAttribute("title", "сброс");
resizeResetBtn.setAttribute("aria-label", "сброс");
actionsWrap.appendChild(resizeResetBtn);
actionsWrap.appendChild(resizeToggle);
}
if (resizeResetWrap) resizeResetWrap.remove();
resizeResetBtn.style.display = CONFIG.resizablePanels ? "inline-flex" : "none";
}
resizeToggle.onchange = e => {
CONFIG.resizablePanels = e.target.checked;
saveConfig();
if (resizeResetBtn) resizeResetBtn.style.display = CONFIG.resizablePanels ? "inline-flex" : "none";
syncResizablePanels();
};
resizeResetBtn?.addEventListener("click", e => {
e.preventDefault();
e.stopPropagation();
resetResizablePanels();
});
panel.querySelector("#hde-cfg-colleague-statuses").onchange = e => {
CONFIG.colleagueStatuses = e.target.checked;
saveConfig();
syncColleagueStatusesModule();
syncColleagueStaffsLinkVisibility();
if (!CONFIG.colleagueStatuses) {
const drawer = document.getElementById(STAFFS_DRAWER_ID);
if (drawer) drawer.classList.remove("active");
}
};
panel.querySelector("#hde-cfg-autoload").onchange = e => {
CONFIG.autoLoadHistory = e.target.checked;
saveConfig();
syncPrevDialogsVisibility();
if (CONFIG.autoLoadHistory) {
injectPaginationStyle();
initAutoLoad();
} else {
removePaginationStyle();
}
};
panel.querySelector("#hde-cfg-prev-dialogs").onchange = e => {
CONFIG.autoLoadPreviousDialogs = e.target.checked;
const prevLimitField = panel.querySelector("#hde-cfg-prev-limit")?.closest("label");
if (prevLimitField) prevLimitField.style.display = CONFIG.autoLoadPreviousDialogs ? "flex" : "none";
saveConfig();
if (CONFIG.autoLoadPreviousDialogs) {
_historyLoadedForTicketId = null;
initAutoLoad();
} else {
clearPreviousDialogsFromView();
}
};
panel.querySelector("#hde-cfg-prev-limit").onchange = e => {
const nextValue = parseInt(e.target.value, 10);
CONFIG.previousDialogsLimit = Number.isFinite(nextValue) ? Math.min(10, Math.max(1, nextValue)) : DEFAULT_PREVIOUS_TICKETS_TO_LOAD;
e.target.value = String(CONFIG.previousDialogsLimit);
saveConfig();
if (CONFIG.autoLoadPreviousDialogs) {
_historyLoadedForTicketId = null;
clearPreviousDialogsFromView();
initAutoLoad();
}
};
const prevLimitField = panel.querySelector("#hde-cfg-prev-limit")?.closest("label");
const prevDialogsToggle = panel.querySelector("#hde-cfg-prev-dialogs");
const prevDialogsLabel = prevDialogsToggle && prevDialogsToggle.closest("label");
const colleagueStaffsLink = panel.querySelector("#hde-colleague-staffs-link");
if (colleagueStaffsLink && !colleagueStaffsLink.dataset.hdeBound) {
colleagueStaffsLink.dataset.hdeBound = "1";
colleagueStaffsLink.addEventListener("click", e => {
e.preventDefault();
e.stopPropagation();
openStaffsDrawer();
});
}
function syncPrevDialogsVisibility() {
const show = CONFIG.autoLoadHistory;
if (prevDialogsLabel) prevDialogsLabel.style.display = show ? "" : "none";
if (prevLimitField) prevLimitField.style.display = show && CONFIG.autoLoadPreviousDialogs ? "flex" : "none";
}
function syncColleagueStaffsLinkVisibility() {
if (!colleagueStaffsLink) return;
colleagueStaffsLink.style.display = CONFIG.colleagueStatuses ? "inline-flex" : "none";
}
syncPrevDialogsVisibility();
syncColleagueStaffsLinkVisibility();
if (!btnContainer.querySelector("#hde-tools-btn")) {
btnContainer.appendChild(btn);
}
if (!panel.parentElement) {
document.body.appendChild(panel);
}
if (menu && btnContainer && !menu.contains(btnContainer)) {
menu.appendChild(btnContainer);
} else if (!menu && (menuItems && !menuItems.contains(btnContainer))) {
menuItems.appendChild(btnContainer);
}
if (menuPlugins && menuItems && menu && btnContainer) {
try {
menuItems.insertBefore(btnContainer, menuPlugins);
} catch (e) {}
}
if (!window.__hdeToolsMenuObserver) {
window.__hdeToolsMenuObserver = true;
const menuObserver = new MutationObserver(function() {
const b = document.getElementById("hde-tools-menu");
if (!b) return;
const targetMenu = b.closest(".menu") || document.querySelector(".menu");
if (targetMenu && !targetMenu.contains(b)) {
try {
targetMenu.appendChild(b);
} catch (e) {}
}
});
menuObserver.observe(document.body, {
childList: true,
subtree: true
});
}
}
function moveHistoryButton() {
if (!CONFIG.autoLoadHistory || !CONFIG.autoLoadPreviousDialogs) return;
const origBtn = document.querySelector(".ticket-detail__history");
const titleEl = document.querySelector(".ticket-detail__title");
if (!origBtn || !titleEl) return;
if (titleEl.contains(origBtn)) return;
titleEl.appendChild(origBtn);
}
function onBodyReady(fn) {
if (document.body) {
fn();
return;
}
document.addEventListener("DOMContentLoaded", fn, {
once: true
});
}
(function bootLeftTabsColumnEarly() {
if (!getEarlyLeftTabsMirrorFromStorage()) return;
const tryInsert = () => {
const layout = findTicketMetroLayout();
if (!layout) return false;
const column = ensureLeftTabsColumnInLayout(layout.metroContainer, layout.ticketContainer);
ensureEarlyLeftTabsSkeleton(column);
return true;
};
onBodyReady(() => {
if (tryInsert()) return;
const observer = new MutationObserver(() => {
if (tryInsert()) observer.disconnect();
});
observer.observe(document.body, {
childList: true,
subtree: true
});
setTimeout(() => observer.disconnect(), 5e3);
});
})();
(function bootSettingsMenuEarly() {
if (window.__hdeToolsMenuEarlyBoot) return;
window.__hdeToolsMenuEarlyBoot = true;
const isReady = () => {
const btn = document.getElementById("hde-tools-btn");
const panel = document.getElementById(MENU_PANEL_ID);
return !!(btn && panel && btn.dataset.hdeToolsBound === "1");
};
const start = () => {
loadConfig();
initSettingsMenu();
if (window.__hdeToolsMenuEarlyRetryTimer) {
clearInterval(window.__hdeToolsMenuEarlyRetryTimer);
}
const startedAt = Date.now();
window.__hdeToolsMenuEarlyRetryTimer = setInterval(() => {
if (isReady() || Date.now() - startedAt > 5e3) {
clearInterval(window.__hdeToolsMenuEarlyRetryTimer);
window.__hdeToolsMenuEarlyRetryTimer = null;
return;
}
initSettingsMenu();
}, 80);
if (!window.__hdeToolsMenuEarlyObserver && document.body) {
window.__hdeToolsMenuEarlyObserver = new MutationObserver(() => {
if (!isReady()) initSettingsMenu();
});
window.__hdeToolsMenuEarlyObserver.observe(document.body, {
childList: true,
subtree: true
});
}
};
if (document.body) {
start();
return;
}
const root = document.documentElement;
if (!root) return;
const observer = new MutationObserver(() => {
if (!document.body) return;
observer.disconnect();
start();
});
observer.observe(root, {
childList: true
});
})();
(function initHistoryButtonMover() {
onBodyReady(function() {
moveHistoryButton();
if (!window.__hdeHistoryBtnObserver) {
window.__hdeHistoryBtnObserver = true;
new MutationObserver(function() {
moveHistoryButton();
injectHistoryCloseButton();
}).observe(document.body, {
childList: true,
subtree: true
});
}
});
})();
function injectHistoryCloseButton() {
if (!CONFIG.autoLoadHistory || !CONFIG.autoLoadPreviousDialogs) return;
const panel = document.querySelector(".ticket-detail__history-tickets");
if (!panel) return;
if (panel.querySelector(".hde-history-close")) return;
const btn = document.createElement("button");
btn.className = "hde-history-close";
btn.title = "Скрыть";
btn.textContent = "✕ Скрыть";
btn.style.cssText = [ "display:block", "width:100%", "margin:0", "padding:6px 12px", "background:#f5f7fa", "border:none", "border-bottom:1px solid #ebeef5", "cursor:pointer", "font-size:12px", "color:#606266", "text-align:left" ].join(";");
btn.addEventListener("mouseenter", function() {
btn.style.background = "#ecf5ff";
btn.style.color = "#409eff";
});
btn.addEventListener("mouseleave", function() {
btn.style.background = "#f5f7fa";
btn.style.color = "#606266";
});
btn.addEventListener("click", function(e) {
e.stopPropagation();
panel.style.display = "none";
});
panel.insertBefore(btn, panel.firstChild);
}
function init() {
loadConfig();
syncNativeTicketTabsVisibilityClass();
applyThemeFromConfig();
initSettingsMenu();
initLeftTabsMirrorObserver();
syncLeftTabsOnHrefChange();
syncLeftTabsMirror();
syncColleagueStatusesModule();
injectStyle();
syncResizablePanels();
if (CONFIG.autoLoadHistory) injectPaginationStyle();
processAllMessages();
initBubbleContextMenu();
initAutoLoad();
observeMessages();
moveHistoryButton();
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", init);
} else {
init();
}
window.addEventListener("beforeunload", () => {
flushLeftTabsCacheNow();
stopColleagueStatuses();
if (_leftTabsPreviewPollInterval) clearInterval(_leftTabsPreviewPollInterval);
if (_leftTabsDepartmentPollInterval) clearInterval(_leftTabsDepartmentPollInterval);
});
window.addEventListener("pagehide", () => {
flushLeftTabsCacheNow();
});
var lastUrl = location.href;
onBodyReady(function() {
new MutationObserver(function() {
if (location.href !== lastUrl) {
lastUrl = location.href;
_loadedPages.clear();
_historyLoadedForTicketId = null;
syncLeftTabsOnHrefChange();
setTimeout(function() {
syncNativeTicketTabsVisibilityClass();
applyThemeFromConfig();
syncLeftTabsOnHrefChange();
syncLeftTabsMirror();
syncColleagueStatusesModule();
injectStyle();
syncResizablePanels();
processAllMessages();
initAutoLoad();
moveHistoryButton();
}, 500);
}
}).observe(document.body, {
childList: true
});
});
})();