Telegram-style чат + опциональная тёмная тема интерфейса. Автозагрузка до 10 страниц при открытии тикета.
// ==UserScript==
// @name HDE Chat Compact
// @namespace http://tampermonkey.net/
// @version 14.29
// @description Telegram-style чат + опциональная тёмная тема интерфейса. Автозагрузка до 10 страниц при открытии тикета.
// @author Eban
// @license MIT
// @match https://*.helpdeskeddy.com/*
// @grant none
// @run-at document-idle
// @language ru
// ==/UserScript==
(function () {
'use strict';
const STYLE_ID = 'hde-compact-v10-style';
const DONE_CLASS = 'hde-v10-done';
const DATE_SEP_CLASS = 'hde-date-separator';
const TICKET_SEP_CLASS = 'hde-ticket-separator';
const MENU_STYLE_ID = 'hde-tools-menu-style';
const MENU_PANEL_ID = 'hde-tools-panel';
const THEME_STYLE_ID = 'hde-tools-theme-style';
const DARK_HTML_CLASS = 'hde-tools-theme-dark';
const PINK_HTML_CLASS = 'hde-tools-theme-pink';
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_MODES = [THEME_MODE_STANDARD, THEME_MODE_DARK, THEME_MODE_PINK];
/** Увеличивать при добавлении полей в панель — пересборка после обновления скрипта */
const SETTINGS_PANEL_VERSION = '7';
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 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 CONFIG = {
compactChat: false,
autoLoadHistory: false,
autoLoadPreviousDialogs: false,
previousDialogsLimit: 1,
themeMode: THEME_MODE_STANDARD,
colleagueStatuses: false,
leftTabsMirror: false
};
function normalizeThemeMode(value) {
const mode = String(value || '').toLowerCase();
return THEME_MODES.includes(mode) ? mode : THEME_MODE_STANDARD;
}
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;
delete CONFIG.darkTheme;
} catch (e) {
console.warn('Ошибка загрузки конфига:', e);
}
}
function saveConfig() {
CONFIG.themeMode = normalizeThemeMode(CONFIG.themeMode);
const payload = Object.assign({}, CONFIG);
delete payload.darkTheme;
localStorage.setItem('hde-tools-config', JSON.stringify(payload));
}
// ─── Theme Engine: Dark Theme ────────────────────────────
// Структурировано секциями, чтобы было проще добавлять новые темы.
const DARK_THEME_CSS_BASE = `
html.${DARK_HTML_CLASS},
html.${DARK_HTML_CLASS} body {
background-color: #121418 !important;
color: #dcdfe6 !important;
scrollbar-color: #3d4654 #1a1f26;
}
html.${DARK_HTML_CLASS} {
--menu-background: #161a1f;
--menu-item-color: #e8eaed;
--menu-item-background: #161a1f;
--menu-item-color-hover: #e8f4f7;
--menu-item-background-hover: #1f2a32;
--ticket-user-post-color: #e8eaed;
--ticket-user-post-background: #2d333b;
--ticket-user-href-color: #7ec8e0;
--ticket-user-href-hover: #a6dff0;
--ticket-user-background: #1a1f26;
--ticket-user-href-border: 0px;
--ticket-staff-post-color: #f0f2f5;
--ticket-staff-post-background: #4a5563;
--ticket-staff-href-color: #fff;
--ticket-staff-href-hover: #e2e8f0;
--ticket-staff-own-post-color: #f0f2f5;
--ticket-staff-own-post-background: #3d4654;
--ticket-staff-comment-color: #f0fafc;
--ticket-staff-comment-background: #1a5f6e;
--ticket-staff-comment-href-color: #e0f7fa;
--ticket-staff-comment-href-hover: #fff;
--ticket-staff-own-comment-color: #e8f4f7;
--ticket-staff-own-comment-background: #154a56;
--ticket-post-input-color: #e4e7ed;
--ticket-post-input-background: #252b33;
--ticket-comment-input-color: #f0fafc;
--ticket-comment-input-background: #1a5f6e;
--ticket-primary-button-color: #0f1419;
--ticket-primary-button-background: #5eb3c9;
--ticket-primary-button-color-hover: #0f1419;
--ticket-primary-button-background-hover: #7ec8e0;
--ticket-secondary-button-color: #dcdfe6;
--ticket-secondary-button-background: #2a3038;
--ticket-secondary-button-color-hover: #7ec8e0;
--ticket-secondary-button-background-hover: #343b45;
}
`;
const DARK_THEME_CSS_COMPONENTS = `
html.${DARK_HTML_CLASS} #ticket-app .ticket-sidebar,
html.${DARK_HTML_CLASS} #ticket-app .ticket-sidebar__heading,
html.${DARK_HTML_CLASS} #ticket-app .ticket-sidebar__container {
background-color: #1a1f26 !important;
color: #dcdfe6 !important;
border-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ticket-sidebar__heading-text,
html.${DARK_HTML_CLASS} #ticket-app .ticket-sidebar__filter a {
color: #c0c4cc !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ticket-sidebar__heading-button,
html.${DARK_HTML_CLASS} #ticket-app .ticket-sidebar__heading-button i,
html.${DARK_HTML_CLASS} #ticket-app .ticket-sidebar__heading-button .el-icon-setting {
color: #c0c4cc !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ticket-sidebar__heading-button:hover,
html.${DARK_HTML_CLASS} #ticket-app .ticket-sidebar__heading-button:hover i,
html.${DARK_HTML_CLASS} #ticket-app .ticket-sidebar__heading-button:hover .el-icon-setting {
color: #e4e7ed !important;
}
html.${DARK_HTML_CLASS} .sidebar,
html.${DARK_HTML_CLASS} .sidebar__item,
html.${DARK_HTML_CLASS} .sidebar__item-icon,
html.${DARK_HTML_CLASS} .sidebar__item-name,
html.${DARK_HTML_CLASS} i.hde-macro {
color: #c0c4cc !important;
}
html.${DARK_HTML_CLASS} .sidebar__item:hover {
background-color: #252b33 !important;
}
html.${DARK_HTML_CLASS} .macro__row,
html.${DARK_HTML_CLASS} .macro__row.macro__row_heading,
html.${DARK_HTML_CLASS} .macro__col {
background-color: #1e2529 !important;
color: #c0c4cc !important;
border-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ticket-list__heading,
html.${DARK_HTML_CLASS} #ticket-app .ticket-topbar {
background-color: #1e2529 !important;
color: #dcdfe6 !important;
border-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ticket-user,
html.${DARK_HTML_CLASS} #ticket-app .ticket-modules {
background-color: #1a1f26 !important;
color: #dcdfe6 !important;
border-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ticket-list__filter-count span {
background: #2d333b !important;
color: #c0c4cc !important;
}
html.${DARK_HTML_CLASS} .menu__item-badge,
html.${DARK_HTML_CLASS} .menu__item-avatar-status {
color: #121418 !important;
font-weight: 700 !important;
}
html.${DARK_HTML_CLASS} .user-card-info,
html.${DARK_HTML_CLASS} .create-ticket-btn,
html.${DARK_HTML_CLASS} .etabs.user-card-tabs-nav,
html.${DARK_HTML_CLASS} .user-card-custom-title {
background-color: #1e2529 !important;
color: #dcdfe6 !important;
border-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} .etabs.user-card-tabs-nav li {
background-color: #252b33 !important;
border-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} .etabs.user-card-tabs-nav li.active {
background-color: #2d333b !important;
}
html.${DARK_HTML_CLASS} .etabs.user-card-tabs-nav li a,
html.${DARK_HTML_CLASS} .etabs.user-card-tabs-nav li i,
html.${DARK_HTML_CLASS} .user-card-name,
html.${DARK_HTML_CLASS} .user-card-name span,
html.${DARK_HTML_CLASS} .user-card-info a,
html.${DARK_HTML_CLASS} .user-card-info i,
html.${DARK_HTML_CLASS} #user-audit-btn {
color: #c0c4cc !important;
}
html.${DARK_HTML_CLASS} .etabs.user-card-tabs-nav li.active a,
html.${DARK_HTML_CLASS} .etabs.user-card-tabs-nav li.active i,
html.${DARK_HTML_CLASS} .hde-comment-dots.active {
color: #e4e7ed !important;
}
html.${DARK_HTML_CLASS} .user-card-toggle,
html.${DARK_HTML_CLASS} .user-card-toggle.pull-right,
html.${DARK_HTML_CLASS} .user-card-toggle.pull-right.ml-2 {
background-color: #2d333b !important;
color: #e4e7ed !important;
border: 1px solid #3d4654 !important;
}
html.${DARK_HTML_CLASS} .user-card-info input,
html.${DARK_HTML_CLASS} .user-card-custom-title input {
background-color: #252b33 !important;
color: #e4e7ed !important;
border-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} .user-card-custom-toggle .user-card-toggle-div table,
html.${DARK_HTML_CLASS} .user-card-custom-toggle .user-card-toggle-div table tr,
html.${DARK_HTML_CLASS} .user-card-custom-toggle .user-card-toggle-div table td {
background-color: #1e2529 !important;
color: #dcdfe6 !important;
border-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} .user-card-custom-toggle .user-card-toggle-div table tr:hover,
html.${DARK_HTML_CLASS} .user-card-custom-toggle .user-card-toggle-div table tr:hover td {
background-color: #252b33 !important;
color: #e4e7ed !important;
}
html.${DARK_HTML_CLASS} .user-card-custom-toggle .user-card-toggle-div table tr.border-top td,
html.${DARK_HTML_CLASS} .user-card-custom-toggle .user-card-toggle-div table tr.border-bottom td {
border-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} .user-card-custom-toggle .depart-ul li {
color: #c0c4cc !important;
}
html.${DARK_HTML_CLASS} i.hde-pencil,
html.${DARK_HTML_CLASS} i.icon-attachment {
color: #c0c4cc !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ticket-tabs {
background: #161a1f !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ticket-tabs__tab {
background: #252b33 !important;
color: #c0c4cc !important;
border-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ticket-tabs .ticket-tabs__tab-divider {
background: #3d4654 !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ticket-tabs__close-all,
html.${DARK_HTML_CLASS} #ticket-app .ticket-tabs__more-close-all,
html.${DARK_HTML_CLASS} .ticket-tabs__close-all,
html.${DARK_HTML_CLASS} .ticket-tabs__more-close-all {
background-color: #1e2529 !important;
border: 1px solid #3d4654 !important;
color: #c0c4cc !important;
box-shadow: none !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ticket-tabs__close-all:hover,
html.${DARK_HTML_CLASS} #ticket-app .ticket-tabs__more-close-all:hover,
html.${DARK_HTML_CLASS} #ticket-app .ticket-tabs__close-all:focus,
html.${DARK_HTML_CLASS} #ticket-app .ticket-tabs__more-close-all:focus,
html.${DARK_HTML_CLASS} .ticket-tabs__close-all:hover,
html.${DARK_HTML_CLASS} .ticket-tabs__more-close-all:hover,
html.${DARK_HTML_CLASS} .ticket-tabs__close-all:focus,
html.${DARK_HTML_CLASS} .ticket-tabs__more-close-all:focus {
background-color: #252b33 !important;
border-color: #4a5563 !important;
color: #e4e7ed !important;
outline: none !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ticket-tabs__tab:hover {
background: #2d333b !important;
color: #e4e7ed !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ticket-list__column-sla-overdue {
color: #d97a7a !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ticket-list__column-status[title*="offline" i],
html.${DARK_HTML_CLASS} #ticket-app .ticket-list__column-status[aria-label*="offline" i],
html.${DARK_HTML_CLASS} #ticket-app .ticket-list__column-status_offline,
html.${DARK_HTML_CLASS} #ticket-app .ticket-list__column-status.status-offline {
--status-background-color: #8f4a4a !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ticket-list__column-status[style*="--status-background-color:#D31616"],
html.${DARK_HTML_CLASS} #ticket-app .ticket-list__column-status[style*="--status-background-color: #D31616"],
html.${DARK_HTML_CLASS} #ticket-app .ticket-list__column-select-text[style*="--status-background-color:#D31616"],
html.${DARK_HTML_CLASS} #ticket-app .ticket-list__column-select-text[style*="--status-background-color: #D31616"] {
--status-background-color: #8f4a4a !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ticket-detail,
html.${DARK_HTML_CLASS} #ticket-app .ticket-container,
html.${DARK_HTML_CLASS} #ticket-app .el-main {
background-color: #121418 !important;
color: #dcdfe6 !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ticket-detail,
html.${DARK_HTML_CLASS} #ticket-app .ticket-container,
html.${DARK_HTML_CLASS} #ticket-app .el-main,
html.${DARK_HTML_CLASS} #ticket-app .ticket-conversation__messages {
background-color: #1a1f26 !important;
}
/* Metro: боковые колонки и блок полей — в theme жёстко #fff */
html.${DARK_HTML_CLASS} #ticket-app .ticket .ticket-left-block,
html.${DARK_HTML_CLASS} #ticket-app .ticket .ticket-right-block {
background-color: #1a1f26 !important;
border-color: #3d4654 !important;
color: #dcdfe6 !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ticket-fields {
background-color: #1e2529 !important;
border-color: #3d4654 !important;
color: #dcdfe6 !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ticket-fields__image {
border-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ticket-detail__title,
html.${DARK_HTML_CLASS} #ticket-app .ticket-right-block {
color: #dcdfe6 !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ticket-topbar,
html.${DARK_HTML_CLASS} #ticket-app .ticket .ticket-topbar {
background-color: #1e2529 !important;
border-bottom-color: #3d4654 !important;
color: #dcdfe6 !important;
}
html.${DARK_HTML_CLASS} #ticket-app .el-aside {
background-color: #1a1f26 !important;
color: #dcdfe6 !important;
}
html.${DARK_HTML_CLASS} #ticket-app .el-table {
background-color: #1a1f26 !important;
color: #dcdfe6 !important;
}
html.${DARK_HTML_CLASS} #ticket-app .el-table tr,
html.${DARK_HTML_CLASS} #ticket-app .el-table__body tr {
background-color: #1a1f26 !important;
}
html.${DARK_HTML_CLASS} #ticket-app .el-table th.el-table__cell {
background-color: #161a1f !important;
color: #c0c4cc !important;
border-bottom-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} #ticket-app .el-table td.el-table__cell {
border-bottom-color: #2d333b !important;
}
html.${DARK_HTML_CLASS} #ticket-app .el-table--striped .el-table__body tr.el-table__row--striped td {
background: #1e2529 !important;
}
html.${DARK_HTML_CLASS} #ticket-app .el-table--enable-row-hover .el-table__body tr:hover > td {
background-color: #252b33 !important;
}
html.${DARK_HTML_CLASS} #ticket-app .el-input__inner,
html.${DARK_HTML_CLASS} #ticket-app .el-textarea__inner {
background-color: #252b33 !important;
color: #e4e7ed !important;
border-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} .el-input__inner {
background-color: #252b33 !important;
color: #e4e7ed !important;
border-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} .el-input__inner:hover,
html.${DARK_HTML_CLASS} .el-input__inner:focus,
html.${DARK_HTML_CLASS} .el-input.is-focus .el-input__inner {
background-color: #2d333b !important;
color: #e4e7ed !important;
border-color: #4a5563 !important;
}
html.${DARK_HTML_CLASS} .el-checkbox__inner {
background-color: #252b33 !important;
border-color: #4a5563 !important;
}
html.${DARK_HTML_CLASS} .el-checkbox__inner:hover,
html.${DARK_HTML_CLASS} .el-checkbox__input:hover .el-checkbox__inner {
border-color: #7ec8e0 !important;
}
html.${DARK_HTML_CLASS} .el-checkbox__input.is-checked .el-checkbox__inner,
html.${DARK_HTML_CLASS} .el-checkbox__input.is-indeterminate .el-checkbox__inner {
background-color: #3d4654 !important;
border-color: #7ec8e0 !important;
}
html.${DARK_HTML_CLASS} .el-checkbox__input.is-checked + .el-checkbox__label,
html.${DARK_HTML_CLASS} .el-checkbox__label {
color: #dcdfe6 !important;
}
html.${DARK_HTML_CLASS} .el-checkbox__input.is-disabled .el-checkbox__inner {
background-color: #1e2529 !important;
border-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} .el-checkbox__input.is-disabled + span.el-checkbox__label,
html.${DARK_HTML_CLASS} .el-checkbox__input.is-disabled.is-checked + span.el-checkbox__label {
color: #8b9199 !important;
}
html.${DARK_HTML_CLASS} .select,
html.${DARK_HTML_CLASS} .select.is-active,
html.${DARK_HTML_CLASS} .select__options {
background-color: #1e2529 !important;
color: #dcdfe6 !important;
border-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} .select__options *,
html.${DARK_HTML_CLASS} .select * {
color: #dcdfe6 !important;
}
html.${DARK_HTML_CLASS} .select__options .is-active,
html.${DARK_HTML_CLASS} .select__options .active,
html.${DARK_HTML_CLASS} .select__options .selected,
html.${DARK_HTML_CLASS} .select__options *:hover {
background-color: #2d333b !important;
color: #e4e7ed !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ck.ck-toolbar,
html.${DARK_HTML_CLASS} #ticket-app .ck.ck-toolbar .ck-toolbar__items {
background-color: #1e2529 !important;
border-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ticket-editor__ckeditor .ck.ck-editor,
html.${DARK_HTML_CLASS} #ticket-app .ticket-editor__ckeditor .ck.ck-editor__top,
html.${DARK_HTML_CLASS} #ticket-app .ticket-editor__ckeditor .ck.ck-sticky-panel,
html.${DARK_HTML_CLASS} #ticket-app .ticket-editor__ckeditor .ck.ck-sticky-panel__content,
html.${DARK_HTML_CLASS} #ticket-app .ticket-editor__ckeditor .ck.ck-sticky-panel__placeholder {
background-color: #1e2529 !important;
border-color: #3d4654 !important;
box-shadow: none !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ck.ck-toolbar__separator,
html.${DARK_HTML_CLASS} #ticket-app .ck .ck-toolbar__separator {
background-color: #3d4654 !important;
border-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ck.ck-toolbar button,
html.${DARK_HTML_CLASS} #ticket-app .ck.ck-toolbar .ck-button {
color: #c0c4cc !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ck.ck-dropdown__panel,
html.${DARK_HTML_CLASS} #ticket-app .ck.ck-dropdown__panel .ck-list,
html.${DARK_HTML_CLASS} #ticket-app .ck.ck-dropdown__panel .ck-list__item {
background-color: #1e2529 !important;
border-color: #3d4654 !important;
color: #dcdfe6 !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ck.ck-dropdown__panel .ck-list-item-button,
html.${DARK_HTML_CLASS} #ticket-app .ck.ck-dropdown__panel .ck-button.ck-list-item-button {
background-color: #1e2529 !important;
color: #c0c4cc !important;
border-color: transparent !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ck.ck-dropdown__panel .ck-list-item-button:hover,
html.${DARK_HTML_CLASS} #ticket-app .ck.ck-dropdown__panel .ck-button.ck-list-item-button:hover,
html.${DARK_HTML_CLASS} #ticket-app .ck.ck-dropdown__panel .ck-list-item-button.ck-on,
html.${DARK_HTML_CLASS} #ticket-app .ck.ck-dropdown__panel .ck-list-item-button[aria-checked="true"] {
background-color: #252b33 !important;
color: #e4e7ed !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ck.ck-toolbar .ck-button:hover,
html.${DARK_HTML_CLASS} #ticket-app .ck.ck-toolbar .ck-splitbutton__action:hover,
html.${DARK_HTML_CLASS} #ticket-app .ck.ck-toolbar .ck-splitbutton__arrow:hover {
background-color: #252b33 !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ck.ck-button.ck-source-editing-button,
html.${DARK_HTML_CLASS} #ticket-app .ck.ck-button.ck-source-editing-button:hover,
html.${DARK_HTML_CLASS} #ticket-app .ck.ck-button.ck-source-editing-button:focus,
html.${DARK_HTML_CLASS} #ticket-app .ck.ck-button.ck-source-editing-button.ck-on,
html.${DARK_HTML_CLASS} #ticket-app .ck.ck-button.ck-source-editing-button.ck-off {
background-color: #252b33 !important;
color: #c0c4cc !important;
}
html.${DARK_HTML_CLASS} #ticket-app .el-select-dropdown,
html.${DARK_HTML_CLASS} #ticket-app .el-cascader__dropdown,
html.${DARK_HTML_CLASS} #ticket-app .el-select-dropdown__item,
html.${DARK_HTML_CLASS} #ticket-app .el-cascader-node {
background-color: #1e2529 !important;
color: #dcdfe6 !important;
}
html.${DARK_HTML_CLASS} .el-select-dropdown,
html.${DARK_HTML_CLASS} .el-cascader__dropdown,
html.${DARK_HTML_CLASS} .el-select-dropdown__item,
html.${DARK_HTML_CLASS} .el-cascader-node {
background-color: #1e2529 !important;
color: #dcdfe6 !important;
border-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} #ticket-app .el-cascader__suggestion-panel,
html.${DARK_HTML_CLASS} #ticket-app .el-cascader__suggestion-list,
html.${DARK_HTML_CLASS} #ticket-app .el-cascader__suggestion-item,
html.${DARK_HTML_CLASS} .el-cascader__suggestion-panel,
html.${DARK_HTML_CLASS} .el-cascader__suggestion-list,
html.${DARK_HTML_CLASS} .el-cascader__suggestion-item {
background-color: #1e2529 !important;
color: #dcdfe6 !important;
border-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} #ticket-app .el-cascader__suggestion-item:hover,
html.${DARK_HTML_CLASS} #ticket-app .el-cascader__suggestion-item:focus,
html.${DARK_HTML_CLASS} #ticket-app .el-cascader__suggestion-item.is-checked,
html.${DARK_HTML_CLASS} #ticket-app .el-cascader__suggestion-item.is-active,
html.${DARK_HTML_CLASS} .el-cascader__suggestion-item:hover,
html.${DARK_HTML_CLASS} .el-cascader__suggestion-item:focus,
html.${DARK_HTML_CLASS} .el-cascader__suggestion-item.is-checked,
html.${DARK_HTML_CLASS} .el-cascader__suggestion-item.is-active {
background-color: #252b33 !important;
color: #e4e7ed !important;
}
html.${DARK_HTML_CLASS} #ticket-app .el-scrollbar__view.el-select-dropdown__list,
html.${DARK_HTML_CLASS} #ticket-app .el-scrollbar__view.el-cascader-menu__list,
html.${DARK_HTML_CLASS} #ticket-app .el-cascader-menu {
background-color: #1e2529 !important;
color: #dcdfe6 !important;
border-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} .el-scrollbar__view.el-select-dropdown__list,
html.${DARK_HTML_CLASS} .el-scrollbar__view.el-cascader-menu__list,
html.${DARK_HTML_CLASS} .el-cascader-menu {
background-color: #1e2529 !important;
color: #dcdfe6 !important;
border-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} #ticket-app .el-select-dropdown__item:hover,
html.${DARK_HTML_CLASS} #ticket-app .el-select-dropdown__item.hover,
html.${DARK_HTML_CLASS} #ticket-app .el-select-dropdown__item.selected,
html.${DARK_HTML_CLASS} #ticket-app .el-select-dropdown__item.is-selected,
html.${DARK_HTML_CLASS} .el-select-dropdown__item:hover,
html.${DARK_HTML_CLASS} .el-select-dropdown__item.hover,
html.${DARK_HTML_CLASS} .el-select-dropdown__item.selected,
html.${DARK_HTML_CLASS} .el-select-dropdown__item.is-selected {
background-color: #2d333b !important;
color: #e4e7ed !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ticket-fields__field-input .el-select-dropdown__item,
html.${DARK_HTML_CLASS} #ticket-app .ticket-fields__field-input .el-select-dropdown__item > *,
html.${DARK_HTML_CLASS} #ticket-app .ticket-fields__field-input .el-cascader-menu__list .el-cascader-node,
html.${DARK_HTML_CLASS} #ticket-app .ticket-fields__field-input .el-cascader-menu__list .el-cascader-node > * {
background-color: transparent !important;
color: #dcdfe6 !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ticket-fields__field-input .el-select-dropdown__item:hover,
html.${DARK_HTML_CLASS} #ticket-app .ticket-fields__field-input .el-select-dropdown__item.hover,
html.${DARK_HTML_CLASS} #ticket-app .ticket-fields__field-input .el-select-dropdown__item.selected,
html.${DARK_HTML_CLASS} #ticket-app .ticket-fields__field-input .el-select-dropdown__item.is-selected,
html.${DARK_HTML_CLASS} #ticket-app .ticket-fields__field-input .el-select-dropdown__item:hover > *,
html.${DARK_HTML_CLASS} #ticket-app .ticket-fields__field-input .el-select-dropdown__item.hover > *,
html.${DARK_HTML_CLASS} #ticket-app .ticket-fields__field-input .el-select-dropdown__item.selected > *,
html.${DARK_HTML_CLASS} #ticket-app .ticket-fields__field-input .el-select-dropdown__item.is-selected > *,
html.${DARK_HTML_CLASS} #ticket-app .ticket-fields__field-input .el-cascader-node:hover,
html.${DARK_HTML_CLASS} #ticket-app .ticket-fields__field-input .el-cascader-node.in-active-path,
html.${DARK_HTML_CLASS} #ticket-app .ticket-fields__field-input .el-cascader-node.is-active {
background-color: #252b33 !important;
color: #e4e7ed !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ticket-fields__field-input .el-select-dropdown__item span[style*="color: rgb(0, 0, 0)"],
html.${DARK_HTML_CLASS} #ticket-app .ticket-fields__field-input .el-select-dropdown__item span[style*="color:#000"],
html.${DARK_HTML_CLASS} #ticket-app .ticket-fields__field-input .el-select-dropdown__item span[style*="color: #000000"] {
color: #e4e7ed !important;
}
html.${DARK_HTML_CLASS} #ticket-app .el-form-item__label {
color: #a8abb2 !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ticket-list .common-value,
html.${DARK_HTML_CLASS} #ticket-app .ticket-sidebar__filter-name,
html.${DARK_HTML_CLASS} #ticket-app .ticket-list .ticket-list-column-title__container .ticket-list-column-title__icon,
html.${DARK_HTML_CLASS} #ticket-app .ticket-list .ticket-list-column-title__post-count,
html.${DARK_HTML_CLASS} #ticket-app .user-color,
html.${DARK_HTML_CLASS} #ticket-app .user-color[data-v-e3f175be] {
color: #c0c4cc !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ticket-list .ticket-list-popover__watching-btn {
color: #c0c4cc !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ticket-list .ticket-list-column-title__info-button button,
html.${DARK_HTML_CLASS} #ticket-app .ticket-list .ticket-list-column-title__info-button button i {
color: #c0c4cc !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ticket-list .ticket-list_ticket-type-icon,
html.${DARK_HTML_CLASS} #ticket-app .ticket-list .ticket-list-column-title__new-window button,
html.${DARK_HTML_CLASS} #ticket-app .ticket-list .ticket-list-column-title__new-window button i,
html.${DARK_HTML_CLASS} #ticket-app .ticket-list .ticket-list-column-title__new-window .icon-new-tab {
color: #c0c4cc !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ticket-sidebar__filter.ticket-sidebar__filter_child {
border-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ticket-sidebar__filter.ticket-sidebar__filter_child:hover {
background-color: #252b33 !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ticket-sidebar__filter-button.router-link-exact-active,
html.${DARK_HTML_CLASS} #ticket-app .ticket-sidebar__filter-button.active {
background-color: #252b33 !important;
border: none !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ticket-audit__item {
background-color: #1e2529 !important;
color: #dcdfe6 !important;
border-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ticket-audit__item:hover {
background-color: #252b33 !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ticket-audit__item::before,
html.${DARK_HTML_CLASS} #ticket-app .ticket-audit__item::after,
html.${DARK_HTML_CLASS} #ticket-app .ticket-audit__line,
html.${DARK_HTML_CLASS} #ticket-app .ticket-audit hr {
border-color: #3d4654 !important;
background-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} #ticket-app .el-dialog {
background: #1e2529 !important;
border: 1px solid #3d4654 !important;
}
html.${DARK_HTML_CLASS} #ticket-app .el-dialog__title,
html.${DARK_HTML_CLASS} #ticket-app .el-dialog__body {
color: #dcdfe6 !important;
}
html.${DARK_HTML_CLASS} .el-dialog {
background: #1e2529 !important;
border: 1px solid #3d4654 !important;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45) !important;
}
html.${DARK_HTML_CLASS} .el-dialog__header,
html.${DARK_HTML_CLASS} .el-dialog__body,
html.${DARK_HTML_CLASS} .el-dialog__footer {
background-color: #1e2529 !important;
border-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} .el-dialog__header {
border-bottom: 1px solid #3d4654 !important;
}
html.${DARK_HTML_CLASS} .el-dialog__footer {
border-top: 1px solid #3d4654 !important;
}
html.${DARK_HTML_CLASS} .el-dialog__title,
html.${DARK_HTML_CLASS} .el-dialog__headerbtn .el-dialog__close,
html.${DARK_HTML_CLASS} .el-dialog__body,
html.${DARK_HTML_CLASS} .el-dialog__body * {
color: #dcdfe6 !important;
}
html.${DARK_HTML_CLASS} .el-dialog .el-dialog__headerbtn:hover .el-dialog__close {
color: #e4e7ed !important;
}
html.${DARK_HTML_CLASS} .el-dialog table,
html.${DARK_HTML_CLASS} .el-dialog thead,
html.${DARK_HTML_CLASS} .el-dialog tbody,
html.${DARK_HTML_CLASS} .el-dialog tr,
html.${DARK_HTML_CLASS} .el-dialog th,
html.${DARK_HTML_CLASS} .el-dialog td {
background-color: #1e2529 !important;
border-color: #3d4654 !important;
color: #dcdfe6 !important;
}
html.${DARK_HTML_CLASS} .el-dialog thead th,
html.${DARK_HTML_CLASS} .el-dialog tr:first-child th,
html.${DARK_HTML_CLASS} .el-dialog tr:first-child td {
background-color: #252b33 !important;
color: #c0c4cc !important;
}
html.${DARK_HTML_CLASS} .el-dialog tbody tr:hover,
html.${DARK_HTML_CLASS} .el-dialog tbody tr:hover td {
background-color: #252b33 !important;
color: #e4e7ed !important;
}
html.${DARK_HTML_CLASS} .el-dialog input,
html.${DARK_HTML_CLASS} .el-dialog .el-input__inner,
html.${DARK_HTML_CLASS} .el-dialog .el-date-editor .el-input__inner {
background-color: #252b33 !important;
border-color: #3d4654 !important;
color: #e4e7ed !important;
}
html.${DARK_HTML_CLASS} .staffs-contact-columns__content,
html.${DARK_HTML_CLASS} .staffs-contact-columns__column,
html.${DARK_HTML_CLASS} .sortable__columns {
background-color: #1e2529 !important;
border-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} .staffs-contact-columns__column_heading,
html.${DARK_HTML_CLASS} .staffs-contact-columns__column_footer {
background-color: #252b33 !important;
color: #e4e7ed !important;
}
html.${DARK_HTML_CLASS} .staffs-contact-columns__order,
html.${DARK_HTML_CLASS} .staffs-contact-columns__value,
html.${DARK_HTML_CLASS} .staffs-contact-columns__width,
html.${DARK_HTML_CLASS} .staffs-contact-columns__delete {
border-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} .staffs-contact-columns__column .el-input-number,
html.${DARK_HTML_CLASS} .staffs-contact-columns__column .el-input-number .el-input__inner,
html.${DARK_HTML_CLASS} .staffs-contact-columns__column .el-input-number__decrease,
html.${DARK_HTML_CLASS} .staffs-contact-columns__column .el-input-number__increase {
background-color: #252b33 !important;
border-color: #3d4654 !important;
color: #dcdfe6 !important;
}
html.${DARK_HTML_CLASS} .ticket-group-actions,
html.${DARK_HTML_CLASS} .ticket-group-actions .el-dialog__body,
html.${DARK_HTML_CLASS} .ticket-group-actions__content,
html.${DARK_HTML_CLASS} .ticket-group-actions__columns,
html.${DARK_HTML_CLASS} .ticket-group-actions__column,
html.${DARK_HTML_CLASS} .ticket-group-actions__footer,
html.${DARK_HTML_CLASS} .ticket-group-actions__upload-file,
html.${DARK_HTML_CLASS} .ticket-group-actions__send-mail,
html.${DARK_HTML_CLASS} .ticket-group-actions__submit-button {
background-color: #1e2529 !important;
color: #dcdfe6 !important;
border-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} .ticket-group-actions .ticket-group-actions__field-label,
html.${DARK_HTML_CLASS} .ticket-group-actions .ticket-group-actions__field-name {
color: #a8abb2 !important;
}
html.${DARK_HTML_CLASS} .ticket-group-actions .ticket-detail__tabs.el-tabs--card > .el-tabs__header,
html.${DARK_HTML_CLASS} .ticket-group-actions .ticket-detail__tabs.el-tabs--card > .el-tabs__header .el-tabs__nav,
html.${DARK_HTML_CLASS} .ticket-group-actions .ticket-detail__tabs.el-tabs--card > .el-tabs__header .el-tabs__item,
html.${DARK_HTML_CLASS} .ticket-group-actions .ticket-detail__tabs .el-tabs__content,
html.${DARK_HTML_CLASS} .ticket-group-actions .ticket-editor {
background-color: #1e2529 !important;
border-color: #3d4654 !important;
color: #dcdfe6 !important;
}
html.${DARK_HTML_CLASS} .ticket-group-actions .ticket-detail__tabs.el-tabs--card > .el-tabs__header .el-tabs__item.is-active {
background-color: #252b33 !important;
color: #e4e7ed !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ticket-list-columns .el-dialog {
background-color: #1e2529 !important;
border: 1px solid #3d4654 !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ticket-list-columns .el-dialog__header,
html.${DARK_HTML_CLASS} #ticket-app .ticket-list-columns .el-dialog__body,
html.${DARK_HTML_CLASS} #ticket-app .ticket-list-columns .el-dialog__footer {
background-color: #1e2529 !important;
color: #dcdfe6 !important;
border-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ticket-list-columns .ticket-list-columns__column {
background-color: #252b33 !important;
color: #dcdfe6 !important;
border-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ticket-list-columns .ticket-list-columns__column_heading {
background-color: transparent !important;
color: #a8abb2 !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ticket-list-columns .el-button.el-button--default {
background-color: #2d333b !important;
color: #dcdfe6 !important;
border-color: #4a5563 !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ticket-list-columns .el-button.el-button--default:hover {
background-color: #343b45 !important;
color: #e4e7ed !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ticket-list-columns .el-button.el-button--primary {
color: #0f1419 !important;
}
html.${DARK_HTML_CLASS} .ticket-filter-list,
html.${DARK_HTML_CLASS} .ticket-filter-list__filter {
background: #1e2529 !important;
background-color: #1e2529 !important;
color: #dcdfe6 !important;
}
html.${DARK_HTML_CLASS} .ticket-filter-list,
html.${DARK_HTML_CLASS} .ticket-filter-list__filter,
html.${DARK_HTML_CLASS} .ticket-filter-list__row,
html.${DARK_HTML_CLASS} .ticket-filter-list__row_heading,
html.${DARK_HTML_CLASS} .ticket-filter-list__col,
html.${DARK_HTML_CLASS} .ticket-filter-list__col_name,
html.${DARK_HTML_CLASS} .ticket-filter-list__col_filter-type,
html.${DARK_HTML_CLASS} .ticket-filter-list__col_activity {
border-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} .ticket-filter-list__row {
background: #252b33 !important;
background-color: #252b33 !important;
color: #dcdfe6 !important;
border-bottom-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} .ticket-filter-list__row_heading {
background: #1e2529 !important;
background-color: #1e2529 !important;
color: #a8abb2 !important;
border-bottom-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} .ticket-filter-list__row:hover {
background: #2d333b !important;
background-color: #2d333b !important;
}
html.${DARK_HTML_CLASS} .ticket-filter-list__col,
html.${DARK_HTML_CLASS} .ticket-filter-list__col_name,
html.${DARK_HTML_CLASS} .ticket-filter-list__col_filter-type,
html.${DARK_HTML_CLASS} .ticket-filter-list__col_activity {
color: #c0c4cc !important;
}
html.${DARK_HTML_CLASS} .ticket-filter-list .ticket-filter-list__col,
html.${DARK_HTML_CLASS} .ticket-filter-list .ticket-filter-list__col * {
background-color: transparent !important;
}
html.${DARK_HTML_CLASS} .ticket-filter-list .el-button,
html.${DARK_HTML_CLASS} .ticket-filter-list button {
background-color: #2d333b !important;
color: #dcdfe6 !important;
border-color: #4a5563 !important;
}
html.${DARK_HTML_CLASS} .ticket-filter-list .el-button:hover,
html.${DARK_HTML_CLASS} .ticket-filter-list button:hover {
background-color: #343b45 !important;
color: #e4e7ed !important;
}
html.${DARK_HTML_CLASS} .ticket-filter-list .el-button--primary {
color: #0f1419 !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ticket .ticket-user__field,
html.${DARK_HTML_CLASS} #ticket-app .ticket .ticket-user__fields,
html.${DARK_HTML_CLASS} #ticket-app .ticket .ticket-user__fields-heading,
html.${DARK_HTML_CLASS} #ticket-app .ticket-user__fields-heading {
border-bottom: none !important;
border-bottom-color: #1a1f26 !important;
background-color: #1a1f26 !important;
margin-bottom: 0 !important;
padding-bottom: 0 !important;
box-shadow: none !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ticket-user__fields-heading::before,
html.${DARK_HTML_CLASS} #ticket-app .ticket-user__fields-heading::after {
border-bottom: none !important;
border-bottom-color: #1a1f26 !important;
background-color: #1a1f26 !important;
box-shadow: none !important;
content: none !important;
}
html.${DARK_HTML_CLASS} .el-message-box {
background: #1e2529 !important;
border: 1px solid #3d4654 !important;
}
html.${DARK_HTML_CLASS} .el-message-box__title,
html.${DARK_HTML_CLASS} .el-message-box__message,
html.${DARK_HTML_CLASS} .el-message-box__content,
html.${DARK_HTML_CLASS} .el-message-box__headerbtn .el-message-box__close {
color: #dcdfe6 !important;
}
html.${DARK_HTML_CLASS} .el-message-box__input input,
html.${DARK_HTML_CLASS} .el-message-box__input textarea {
background-color: #252b33 !important;
color: #e4e7ed !important;
border-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} .el-message-box .el-button.el-button--default,
html.${DARK_HTML_CLASS} .el-button.el-button--default.el-button--small {
background-color: #252b33 !important;
color: #dcdfe6 !important;
border-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} .el-message-box .el-button.el-button--default:hover,
html.${DARK_HTML_CLASS} .el-button.el-button--default.el-button--small:hover {
background-color: #2d333b !important;
color: #e4e7ed !important;
border-color: #4a5563 !important;
}
html.${DARK_HTML_CLASS} .el-tag.el-tag--info,
html.${DARK_HTML_CLASS} .el-tag.el-tag--info.el-tag--mini,
html.${DARK_HTML_CLASS} .el-tag.el-tag--info.el-tag--mini.el-tag--light {
background-color: #252b33 !important;
color: #c0c4cc !important;
border-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} .el-picker-panel,
html.${DARK_HTML_CLASS} .el-date-picker,
html.${DARK_HTML_CLASS} .el-picker-panel__sidebar,
html.${DARK_HTML_CLASS} .el-date-picker__time-header,
html.${DARK_HTML_CLASS} .el-time-panel {
background-color: #1e2529 !important;
color: #dcdfe6 !important;
border-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} .el-picker-panel [class*="time"],
html.${DARK_HTML_CLASS} .el-picker-panel [class*="date"],
html.${DARK_HTML_CLASS} .el-picker-panel__content,
html.${DARK_HTML_CLASS} .el-date-table td,
html.${DARK_HTML_CLASS} .el-time-spinner__item {
color: #dcdfe6 !important;
}
html.${DARK_HTML_CLASS} .el-picker-panel__footer {
background-color: #1e2529 !important;
border-top-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} .el-picker-panel__footer .el-button {
background-color: #252b33 !important;
color: #c0c4cc !important;
border-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} .el-picker-panel__footer .el-button.el-button--text {
background-color: transparent !important;
color: #c0c4cc !important;
}
html.${DARK_HTML_CLASS} .el-picker-panel__footer .el-button:hover {
background-color: #2d333b !important;
color: #e4e7ed !important;
}
html.${DARK_HTML_CLASS} .el-time-spinner__item:hover,
html.${DARK_HTML_CLASS} .el-date-table td.available:hover {
background-color: #2d333b !important;
color: #e4e7ed !important;
}
html.${DARK_HTML_CLASS} .profile,
html.${DARK_HTML_CLASS} .sidebar {
background-color: #1a1f26 !important;
color: #dcdfe6 !important;
border-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} .profile * ,
html.${DARK_HTML_CLASS} .sidebar * {
color: inherit;
}
html.${DARK_HTML_CLASS} .el-plus-input__inner {
background-color: #252b33 !important;
color: #e4e7ed !important;
border-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} .el-plus-button,
html.${DARK_HTML_CLASS} .el-plus-radio-button__inner {
border-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} .el-plus-button:not(.el-plus-button--primary):not(.el-plus-button--danger) {
background-color: #252b33 !important;
color: #dcdfe6 !important;
}
html.${DARK_HTML_CLASS} .el-plus-radio-button__inner {
background-color: #252b33 !important;
color: #dcdfe6 !important;
}
html.${DARK_HTML_CLASS} .el-plus-button.el-plus-button--primary,
html.${DARK_HTML_CLASS} .el-plus-button.el-plus-button--danger,
html.${DARK_HTML_CLASS} .el-plus-radio-button.is-active .el-plus-radio-button__inner,
html.${DARK_HTML_CLASS} .el-plus-radio-button__orig-radio:checked + .el-plus-radio-button__inner {
color: #121418 !important;
font-weight: 600 !important;
}
html.${DARK_HTML_CLASS} .el-popover,
html.${DARK_HTML_CLASS} .el-popper,
html.${DARK_HTML_CLASS} .el-popconfirm {
background-color: #1e2529 !important;
color: #dcdfe6 !important;
border-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} .el-popover[x-placement^="top"] .popper__arrow,
html.${DARK_HTML_CLASS} .el-popover[x-placement^="bottom"] .popper__arrow,
html.${DARK_HTML_CLASS} .el-popover[x-placement^="left"] .popper__arrow,
html.${DARK_HTML_CLASS} .el-popover[x-placement^="right"] .popper__arrow {
border-top-color: #3d4654 !important;
border-bottom-color: #3d4654 !important;
border-left-color: #3d4654 !important;
border-right-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} .el-popover .popper__arrow::after {
border-top-color: #1e2529 !important;
border-bottom-color: #1e2529 !important;
border-left-color: #1e2529 !important;
border-right-color: #1e2529 !important;
}
html.${DARK_HTML_CLASS} .el-popover[data-popper-placement^="top"] .popper__arrow,
html.${DARK_HTML_CLASS} .el-popover[data-popper-placement^="bottom"] .popper__arrow,
html.${DARK_HTML_CLASS} .el-popover[data-popper-placement^="left"] .popper__arrow,
html.${DARK_HTML_CLASS} .el-popover[data-popper-placement^="right"] .popper__arrow,
html.${DARK_HTML_CLASS} .el-popper[data-popper-placement^="top"] .popper__arrow,
html.${DARK_HTML_CLASS} .el-popper[data-popper-placement^="bottom"] .popper__arrow,
html.${DARK_HTML_CLASS} .el-popper[data-popper-placement^="left"] .popper__arrow,
html.${DARK_HTML_CLASS} .el-popper[data-popper-placement^="right"] .popper__arrow {
border-top-color: #3d4654 !important;
border-bottom-color: #3d4654 !important;
border-left-color: #3d4654 !important;
border-right-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} .el-popper[data-popper-placement] .popper__arrow::after {
border-top-color: #1e2529 !important;
border-bottom-color: #1e2529 !important;
border-left-color: #1e2529 !important;
border-right-color: #1e2529 !important;
}
html.${DARK_HTML_CLASS} .ticket-tabs__close-all-popper,
html.${DARK_HTML_CLASS} .ticket-tabs__close-all-popper .el-popconfirm,
html.${DARK_HTML_CLASS} .ticket-tabs__close-all-popper .el-popconfirm__action {
background-color: #1e2529 !important;
border-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} .ticket-tabs__close-all-popper .el-popconfirm__action {
border-top: 1px solid #3d4654 !important;
}
html.${DARK_HTML_CLASS} .el-popconfirm__main,
html.${DARK_HTML_CLASS} .el-popconfirm__main *,
html.${DARK_HTML_CLASS} .el-popover *,
html.${DARK_HTML_CLASS} .el-popper * {
color: #dcdfe6 !important;
}
html.${DARK_HTML_CLASS} .el-upload-dragger {
background-color: #1e2529 !important;
color: #c0c4cc !important;
border: 1px dashed #3d4654 !important;
}
html.${DARK_HTML_CLASS} .el-upload-dragger:hover {
background-color: #252b33 !important;
border-color: #4a5563 !important;
}
html.${DARK_HTML_CLASS} #ticket-app .el-card {
background: #1a1f26 !important;
border-color: #3d4654 !important;
color: #dcdfe6 !important;
}
html.${DARK_HTML_CLASS} #ticket-app .el-tabs__item {
color: #a8abb2 !important;
}
html.${DARK_HTML_CLASS} #ticket-app .el-tabs__item.is-active {
color: #7ec8e0 !important;
}
html.${DARK_HTML_CLASS} .el-tabs__item {
color: #a8abb2 !important;
opacity: 1 !important;
-webkit-text-fill-color: #a8abb2 !important;
}
html.${DARK_HTML_CLASS} .el-tabs__item.is-active {
color: #e4e7ed !important;
opacity: 1 !important;
-webkit-text-fill-color: #e4e7ed !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ticket-detail__tabs.el-tabs--card > .el-tabs__header,
html.${DARK_HTML_CLASS} #ticket-app .ticket-detail__tabs.el-tabs--card > .el-tabs__header .el-tabs__nav,
html.${DARK_HTML_CLASS} #ticket-app .ticket-detail__tabs.el-tabs--card > .el-tabs__header .el-tabs__item {
border-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ticket-detail__tabs.el-tabs--card > .el-tabs__header .el-tabs__item {
background-color: #1e2529 !important;
color: #c0c4cc !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ticket-detail__tabs.el-tabs--card > .el-tabs__header .el-tabs__item.is-active {
background-color: #252b33 !important;
color: #e4e7ed !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ticket-detail__tabs .el-tabs__nav-wrap::after {
background-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} #ticket-app .el-pagination,
html.${DARK_HTML_CLASS} #ticket-app .el-pagination button,
html.${DARK_HTML_CLASS} #ticket-app .el-pager li {
color: #c0c4cc !important;
background: transparent !important;
}
html.${DARK_HTML_CLASS} #ticket-app .el-loading-mask {
background-color: rgba(18, 20, 24, 0.85) !important;
}
html.${DARK_HTML_CLASS} .dashboard,
html.${DARK_HTML_CLASS} .dashboard__header,
html.${DARK_HTML_CLASS} .dashboard-tabs,
html.${DARK_HTML_CLASS} .dashboard-overview,
html.${DARK_HTML_CLASS} .dashboard-overview__ticket-stats,
html.${DARK_HTML_CLASS} .dashboard-overview__filters,
html.${DARK_HTML_CLASS} .dashboard-overview__tickets-by-channels,
html.${DARK_HTML_CLASS} .dashboard-overview__chart,
html.${DARK_HTML_CLASS} .dashboard-overview__ticket-stats-chart {
background-color: #121418 !important;
color: #dcdfe6 !important;
border-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} .dashboard .el-tabs__header,
html.${DARK_HTML_CLASS} .dashboard .el-tabs__nav-wrap::after,
html.${DARK_HTML_CLASS} .dashboard .el-tabs__item {
border-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} .dashboard .el-tabs__item {
color: #c0c4cc !important;
}
html.${DARK_HTML_CLASS} .dashboard .el-tabs__item.is-active {
color: #e4e7ed !important;
}
html.${DARK_HTML_CLASS} .dashboard .el-input__inner,
html.${DARK_HTML_CLASS} .dashboard .el-select .el-input__inner,
html.${DARK_HTML_CLASS} .dashboard .el-select__tags,
html.${DARK_HTML_CLASS} .dashboard .el-select-dropdown,
html.${DARK_HTML_CLASS} .dashboard .el-select-dropdown__item,
html.${DARK_HTML_CLASS} .dashboard .el-scrollbar__view.el-select-dropdown__list {
background-color: #1e2529 !important;
color: #dcdfe6 !important;
border-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} .dashboard .el-select-dropdown__item.hover,
html.${DARK_HTML_CLASS} .dashboard .el-select-dropdown__item:hover,
html.${DARK_HTML_CLASS} .dashboard .el-select-dropdown__item.selected,
html.${DARK_HTML_CLASS} .dashboard .el-select-dropdown__item.is-selected {
background-color: #252b33 !important;
color: #e4e7ed !important;
}
html.${DARK_HTML_CLASS} .dashboard .dashboard-staffs__header,
html.${DARK_HTML_CLASS} .dashboard .dashboard-staffs__content,
html.${DARK_HTML_CLASS} .dashboard .dashboard-staffs__row,
html.${DARK_HTML_CLASS} .dashboard .dashboard-staffs__column {
background-color: #121418 !important;
color: #c0c4cc !important;
border-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} .dashboard .dashboard-staffs__content .dashboard-staffs__row:hover,
html.${DARK_HTML_CLASS} .dashboard .dashboard-staffs__content .dashboard-staffs__row:focus,
html.${DARK_HTML_CLASS} .dashboard .dashboard-staffs__content .dashboard-staffs__row:focus-within {
background-color: #252b33 !important;
}
html.${DARK_HTML_CLASS} .dashboard .dashboard-staffs__content .dashboard-staffs__row:hover .dashboard-staffs__column,
html.${DARK_HTML_CLASS} .dashboard .dashboard-staffs__content .dashboard-staffs__row:focus .dashboard-staffs__column,
html.${DARK_HTML_CLASS} .dashboard .dashboard-staffs__content .dashboard-staffs__row:focus-within .dashboard-staffs__column {
background-color: transparent !important;
color: #e4e7ed !important;
}
html.${DARK_HTML_CLASS} .dashboard .dashboard-staffs__content .dashboard-staffs__row:hover .el-button--text,
html.${DARK_HTML_CLASS} .dashboard .dashboard-staffs__content .dashboard-staffs__row:hover .el-button--text span,
html.${DARK_HTML_CLASS} .dashboard .dashboard-staffs__content .dashboard-staffs__row:hover .el-button--text i {
color: #9fd3e4 !important;
}
html.${DARK_HTML_CLASS} .dashboard .dashboard-overview__ticket-stats-col[style] {
background: #1e2529 !important;
border-color: #3d4654 !important;
color: #e4e7ed !important;
}
html.${DARK_HTML_CLASS} .dashboard .dashboard-overview__ticket-stats-col[style*="211, 22, 22"] {
background: #8f4a4a !important;
border-color: #8f4a4a !important;
color: #f2dede !important;
}
html.${DARK_HTML_CLASS} .dashboard .dashboard-overview__ticket-stats-label,
html.${DARK_HTML_CLASS} .dashboard .dashboard-overview__ticket-stats-value,
html.${DARK_HTML_CLASS} .dashboard [class*="dashboard-overview"] {
color: #c0c4cc !important;
}
html.${DARK_HTML_CLASS} .dashboard [style*="z-index: 9999999"][style*="background-color: rgb(255, 255, 255)"] {
background-color: #1e2529 !important;
border-color: #3d4654 !important;
color: #dcdfe6 !important;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45) !important;
}
html.${DARK_HTML_CLASS} .dashboard [style*="z-index: 9999999"][style*="background-color: rgb(255, 255, 255)"] * {
color: #dcdfe6 !important;
}
html.${DARK_HTML_CLASS} .dashboard canvas {
filter: saturate(0.82) brightness(0.92);
}
html.${DARK_HTML_CLASS} .contact-users,
html.${DARK_HTML_CLASS} .contact-users__filters,
html.${DARK_HTML_CLASS} .contacts__content,
html.${DARK_HTML_CLASS} .contacts__row,
html.${DARK_HTML_CLASS} .contacts__column,
html.${DARK_HTML_CLASS} .contacts__row_header,
html.${DARK_HTML_CLASS} .pagination,
html.${DARK_HTML_CLASS} .pagination__total {
background-color: #121418 !important;
color: #dcdfe6 !important;
border-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} .contact-users .el-tabs__header,
html.${DARK_HTML_CLASS} .contact-users .el-tabs__nav-wrap::after,
html.${DARK_HTML_CLASS} .contact-users .el-tabs__item {
border-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} .contact-users .el-tabs__item {
color: #a8abb2 !important;
}
html.${DARK_HTML_CLASS} .contact-users .el-tabs__item.is-active {
color: #e4e7ed !important;
}
html.${DARK_HTML_CLASS} .contact-users .el-tabs__nav .el-tabs__item.is-top,
html.${DARK_HTML_CLASS} .contact-users .el-tabs__nav .el-tabs__item[id^="tab-"] {
color: #a8abb2 !important;
opacity: 1 !important;
-webkit-text-fill-color: #a8abb2 !important;
}
html.${DARK_HTML_CLASS} .contact-users .el-tabs__nav .el-tabs__item.is-top.is-active,
html.${DARK_HTML_CLASS} .contact-users .el-tabs__nav .el-tabs__item[id^="tab-"].is-active {
color: #e4e7ed !important;
opacity: 1 !important;
-webkit-text-fill-color: #e4e7ed !important;
}
html.${DARK_HTML_CLASS} .contact-users #tab-users,
html.${DARK_HTML_CLASS} .contact-users #tab-companies,
html.${DARK_HTML_CLASS} .contact-users #tab-equipments {
color: #a8abb2 !important;
opacity: 1 !important;
-webkit-text-fill-color: #a8abb2 !important;
}
html.${DARK_HTML_CLASS} .contact-users #tab-users.is-active,
html.${DARK_HTML_CLASS} .contact-users #tab-companies.is-active,
html.${DARK_HTML_CLASS} .contact-users #tab-equipments.is-active {
color: #e4e7ed !important;
opacity: 1 !important;
-webkit-text-fill-color: #e4e7ed !important;
}
html.${DARK_HTML_CLASS} .contact-users__filters,
html.${DARK_HTML_CLASS} .contact-users__filters .el-row,
html.${DARK_HTML_CLASS} .contact-users__filters [class*="tabs"],
html.${DARK_HTML_CLASS} .contact-users__filters [class*="tabs"] * {
border-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} .contacts__row_header,
html.${DARK_HTML_CLASS} .contacts__row {
border-color: #2d333b !important;
}
html.${DARK_HTML_CLASS} .contacts__row:hover {
background-color: #1e2529 !important;
}
html.${DARK_HTML_CLASS} .contacts__column .el-button--text,
html.${DARK_HTML_CLASS} .contacts__column .el-button--text span,
html.${DARK_HTML_CLASS} .contacts__column .el-button--text i {
color: #c0c4cc !important;
}
html.${DARK_HTML_CLASS} .contacts__column .el-button--text:hover,
html.${DARK_HTML_CLASS} .contacts__column .el-button--text:hover span,
html.${DARK_HTML_CLASS} .contacts__column .el-button--text:hover i {
color: #e4e7ed !important;
}
html.${DARK_HTML_CLASS} .contact-users .el-switch__core {
background-color: #252b33 !important;
border-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} .contact-users .el-switch.is-checked .el-switch__core {
background-color: #23869b !important;
border-color: #2da1b8 !important;
}
html.${DARK_HTML_CLASS} .contact-users .el-pagination button,
html.${DARK_HTML_CLASS} .contact-users .el-pager li {
background-color: transparent !important;
color: #c0c4cc !important;
}
html.${DARK_HTML_CLASS} .contact-users .el-pager li.active {
color: #e4e7ed !important;
border-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ticket-editor__ckeditor .ck-content,
html.${DARK_HTML_CLASS} #ticket-app .ticket-editor__ckeditor_comment .ck-content {
background-color: #252b33 !important;
color: #e4e7ed !important;
border-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ticket-cc__copy-append,
html.${DARK_HTML_CLASS} #ticket-app .ticket-cc__copy-prepend {
background-color: #252b33 !important;
border-color: #3d4654 !important;
color: #a8abb2 !important;
}
html.${DARK_HTML_CLASS} #ticket-app .el-button.ticket-editor__add-files-button,
html.${DARK_HTML_CLASS} #ticket-app .el-button.ticket-editor__add-files-button.el-button--text {
background-color: #1e2529 !important;
color: #c0c4cc !important;
}
html.${DARK_HTML_CLASS} #ticket-app .el-button.ticket-editor__add-files-button:hover {
background-color: #2d333b !important;
color: #e4e7ed !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ticket-conversation__messages {
background-color: #1a1f26 !important;
}
html.${DARK_HTML_CLASS} .${DATE_SEP_CLASS} {
color: #8b9199 !important;
}
html.${DARK_HTML_CLASS} .${DATE_SEP_CLASS}::before,
html.${DARK_HTML_CLASS} .${DATE_SEP_CLASS}::after {
background: #3d4654 !important;
}
html.${DARK_HTML_CLASS} .${TICKET_SEP_CLASS} {
color: #a8abb2 !important;
}
html.${DARK_HTML_CLASS} .${TICKET_SEP_CLASS}::after {
background: #3d4654 !important;
}
html.${DARK_HTML_CLASS} .ticket-conversation__message_user .hde-name-inside {
color: #7ec8e0 !important;
}
html.${DARK_HTML_CLASS} .ticket-conversation__message_user .hde-time-inside {
color: #c0c4cc !important;
opacity: 0.75 !important;
}
html.${DARK_HTML_CLASS} .hde-tools-panel {
background: #1e2529 !important;
color: #e4e7ed !important;
box-shadow: 0 4px 20px rgba(0,0,0,0.55) !important;
border: 1px solid #3d4654 !important;
}
html.${DARK_HTML_CLASS} .hde-tools-panel h3 {
color: #e4e7ed !important;
border-bottom-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} .hde-tools-field input[type="number"],
html.${DARK_HTML_CLASS} .hde-tools-field select {
background: #252b33 !important;
color: #e4e7ed !important;
border-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} .hde-staffs-drawer__overlay {
background: rgba(15, 20, 25, 0.65) !important;
}
html.${DARK_HTML_CLASS} .hde-staffs-drawer__panel {
background: #1e2529 !important;
border-left-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} .hde-staffs-drawer__header {
color: #e4e7ed !important;
border-bottom-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} .hde-staffs-drawer__close {
color: #c0c4cc !important;
}
html.${DARK_HTML_CLASS} .hde-staffs-drawer__close:hover {
color: #e4e7ed !important;
}
html.${DARK_HTML_CLASS} .hde-staffs-drawer__frame {
background: #1e2529 !important;
}
html.${DARK_HTML_CLASS} .hde-tools-link-btn {
color: #c0c4cc !important;
border: none !important;
background: transparent !important;
}
html.${DARK_HTML_CLASS} .hde-tools-link-btn:hover {
color: #e4e7ed !important;
}
html.${DARK_HTML_CLASS} .hde-history-close {
background: #252b33 !important;
color: #c0c4cc !important;
border-bottom-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ticket-detail__history-tickets,
html.${DARK_HTML_CLASS} #ticket-app .ticket-detail__history-container,
html.${DARK_HTML_CLASS} #ticket-app .ticket-detail__history-ticket {
background-color: #1e2529 !important;
color: #dcdfe6 !important;
border-color: #3d4654 !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ticket-detail__history-ticket_current {
background-color: #252b33 !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ticket-detail__history-ticket a,
html.${DARK_HTML_CLASS} #ticket-app .ticket-detail__history-ticket i,
html.${DARK_HTML_CLASS} #ticket-app .ticket-detail__history-ticket span {
color: #c0c4cc !important;
}
html.${DARK_HTML_CLASS} #ticket-app .ticket-detail__history-ticket [style*="color: #000000"],
html.${DARK_HTML_CLASS} #ticket-app .ticket-detail__history-ticket [style*="color:#000000"] {
color: #e4e7ed !important;
}
`;
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);
}
const PINK_THEME_CSS = buildPinkThemeCssFromDark(DARK_THEME_CSS);
const THEME_CSS = [DARK_THEME_CSS, PINK_THEME_CSS].join('\n\n');
function injectThemeStyles() {
const legacyStyle = document.getElementById(LEGACY_THEME_STYLE_ID);
if (legacyStyle) legacyStyle.remove();
if (document.getElementById(THEME_STYLE_ID)) return;
const style = document.createElement('style');
style.id = THEME_STYLE_ID;
style.textContent = THEME_CSS;
document.head.appendChild(style);
}
function applyThemeFromConfig() {
injectThemeStyles();
CONFIG.themeMode = normalizeThemeMode(CONFIG.themeMode);
const root = document.documentElement;
root.classList.remove(DARK_HTML_CLASS, PINK_HTML_CLASS, LEGACY_DARK_HTML_CLASS);
if (CONFIG.themeMode === THEME_MODE_DARK) {
root.classList.add(DARK_HTML_CLASS);
} else if (CONFIG.themeMode === THEME_MODE_PINK) {
root.classList.add(PINK_HTML_CLASS);
}
}
// ─── Стили ──────────────────────────────────────────────
const CSS = `
/* ── Layout ── */
.ticket-conversation__message-block {
display: flex !important; flex-direction: row !important;
align-items: flex-start !important; gap: 8px !important;
flex: 1 !important; overflow: visible !important;
}
.ticket-conversation__message_user > .ticket-conversation__message-block,
.ticket-conversation__message_comment > .ticket-conversation__message-block {
justify-content: flex-start !important;
}
.ticket-conversation__message_staff > .ticket-conversation__message-block {
justify-content: flex-end !important;
}
/* Avatar */
.ticket-conversation__message-image {
width: 30px !important; height: 30px !important;
padding: 2px !important; border-radius: 50% !important;
}
.ticket-conversation__message-image_user,
.ticket-conversation__message-image_staff { margin-top: 0 !important; }
/* Bubble / text */
.ticket-conversation__message-text {
font-size: 13px !important; margin: 0 !important; padding: 0 !important;
}
.ticket-conversation__message-html {
padding: 6px 10px !important; min-height: auto !important;
position: relative !important;
}
.ticket-conversation__message-html-emoji { font-size: 1.8rem !important; }
/* Meta — hidden (всё внутри пузыря теперь) */
.ticket-conversation__message-meta { display: none !important; }
/* Пагинация скрывается через отдельный стиль autoLoadHistory */
/* Имя в пузыре */
.hde-name-inside {
display: block !important; font-size: 12px !important;
font-weight: 600 !important; line-height: 1.2 !important;
margin-bottom: 2px !important;
font-family: 'Helvetica Neue', Helvetica, 'PingFang SC',
'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif !important;
}
.ticket-conversation__message_user .hde-name-inside { text-align: left !important; color: #1a627a !important; }
.ticket-conversation__message_staff .hde-name-inside,
.ticket-conversation__message-text_post-own .hde-name-inside { color: #e0e0e0 !important; text-align: right !important; }
.ticket-conversation__message-text_system .hde-name-inside { color: rgba(255,255,255,0.7) !important; }
/* Время в пузыре */
.hde-time-inside {
display: block !important; clear: both !important;
font-size: 9px !important; line-height: 1 !important;
text-align: right !important; margin: 2px 0 0 0 !important;
padding: 0 4px 1px 0 !important;
font-family: 'Helvetica Neue', Helvetica, 'PingFang SC',
'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif !important;
}
.ticket-conversation__message_user .hde-time-inside { color: #000000 !important; opacity: 0.4 !important; }
.ticket-conversation__message_staff .hde-time-inside,
.ticket-conversation__message-text_post-own .hde-time-inside { color: #fff !important; opacity: 0.5 !important; }
.hde-edited-inside {
display: none !important;
font-size: 9px !important;
line-height: 1 !important;
margin: 2px 0 0 0 !important;
padding: 0 0 1px 0 !important;
font-family: 'Helvetica Neue', Helvetica, 'PingFang SC',
'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif !important;
opacity: 0.55 !important;
}
.ticket-conversation__message_user .hde-edited-inside { color: #000000 !important; }
.ticket-conversation__message_staff .hde-edited-inside,
.ticket-conversation__message-text_post-own .hde-edited-inside { color: #fff !important; }
.ticket-conversation__message-html:has(.hde-edited-inside) .hde-edited-inside {
display: inline-block !important;
float: left !important;
margin-right: 5px !important;
}
.ticket-conversation__message-html:has(.hde-edited-inside) .hde-time-inside {
display: inline-block !important;
float: right !important;
clear: none !important;
margin-top: 2px !important;
}
.ticket-conversation__message-updated { display: none !important; }
/* Padding bubble */
.ticket-conversation__message-html:has(.hde-time-inside) { padding-bottom: 3px !important; }
.ticket-conversation__message-html:has(.hde-name-inside) { padding-top: 4px !important; }
/* Кнопки действий */
.ticket-conversation__actions {
clear: none !important; padding: 0 !important; margin: 0 !important;
line-height: 1 !important; display: inline-flex !important;
align-items: center !important; opacity: 0 !important;
transition: opacity 0.15s ease !important;
}
.ticket-conversation__message:hover .ticket-conversation__actions { opacity: 1 !important; }
.hde-actions-row { display: inline-flex !important; flex-direction: row !important; align-items: center !important; gap: 6px !important; }
.ticket-conversation__actions-btn { display: inline-flex !important; align-items: center !important; gap: 2px !important; flex: none !important; }
.ticket-conversation__actions_button { padding: 2px 3px !important; margin: 0 1px !important; }
/* Лайки */
.ticket-conversation__like {
display: inline-flex !important; width: auto !important; font-size: 10px !important;
margin: 0 !important; padding: 0 !important; gap: 2px !important;
}
.ticket-conversation__like button { padding: 2px 3px !important; margin: 0 !important; font-size: 12px !important; }
/* Кнопки под пузырём (вынесены из meta) */
.hde-actions-inline {
display: inline-flex !important; flex-direction: row !important;
align-items: center !important; gap: 6px !important;
align-self: center !important;
opacity: 0 !important; transition: opacity 0.15s ease !important;
}
.ticket-conversation__message:hover .hde-actions-inline { opacity: 1 !important; }
.ticket-conversation__message.hde-has-reaction > .ticket-conversation__message-block > .hde-actions-inline {
opacity: 1 !important;
}
.ticket-conversation__message_user > .ticket-conversation__message-block > .hde-actions-inline,
.ticket-conversation__message_comment > .ticket-conversation__message-block > .hde-actions-inline {
justify-content: flex-end !important; order: 2 !important;
}
.ticket-conversation__message_staff > .ticket-conversation__message-block > .hde-actions-inline {
justify-content: flex-start !important; order: -1 !important;
}
/* Разделитель даты */
.${DATE_SEP_CLASS} {
display: block !important; width: 100% !important;
text-align: center !important; clear: both !important;
margin: 14px 0 8px 0 !important; padding: 5px 0 !important;
font-size: 13px !important; line-height: 1.3 !important;
font-weight: 700 !important; letter-spacing: 1px !important;
font-family: 'Helvetica Neue', Helvetica, 'PingFang SC',
'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif !important;
color: #999 !important; position: relative !important;
}
.${DATE_SEP_CLASS}::before,
.${DATE_SEP_CLASS}::after {
content: '' !important; display: inline-block !important;
vertical-align: middle !important; width: 40px !important;
height: 1px !important; background: #ddd !important;
margin: 0 8px !important;
}
.${TICKET_SEP_CLASS} {
display: block !important;
width: 100% !important;
text-align: center !important;
margin: 16px 0 8px 0 !important;
font-size: 12px !important;
font-weight: 600 !important;
color: #7f8c8d !important;
font-family: inherit !important;
box-sizing: border-box !important;
line-height: 1.4 !important;
}
.${TICKET_SEP_CLASS}::after {
content: '' !important;
display: block !important;
width: 100% !important;
height: 1px !important;
background: #c8d6db !important;
margin-top: 5px !important;
}
/* Переопределяем line-height параграфов внутри пузырей */
#ticket-app .ticket .ticket-conversation__message-text p {
margin: 0 !important;
line-height: 1.1 !important;
}
/* Убираем разделитель/отступ у полей пользователя */
#ticket-app .ticket .ticket-user__field {
padding-bottom: 0 !important;
border-bottom: none !important;
margin-bottom: 0 !important;
}
#ticket-app .ticket .ticket-user__fields {
padding-bottom: 0 !important;
border-bottom: none !important;
}
#ticket-app .ticket .ticket-user__fields-heading {
border-bottom: none !important;
margin: 0 !important;
padding-bottom: 0 !important;
}
#ticket-app .ticket-user__fields-heading,
#ticket-app .ticket-user__fields-heading::before,
#ticket-app .ticket-user__fields-heading::after {
border-bottom: none !important;
margin-bottom: 0 !important;
box-shadow: none !important;
content: normal !important;
}
.ticket-tabs .ticket-tabs__more .ticket-tabs__more-close-all {
border-top: none !important;
}
.ticket-editor__with-borders {
border-top: none !important;
}
.el-tabs--card > .el-tabs__header .el-tabs__nav {
border-color: #e4e7ed !important;
}
/* Отступы между сообщениями */
.ticket-conversation__message { margin-top: 5px !important; }
.ticket-conversation__message:first-child { margin-top: 2px !important; }
`;
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 = `
.${TICKET_SEP_CLASS} {
display: block !important;
width: 100% !important;
text-align: center !important;
margin: 16px 0 8px 0 !important;
font-size: 12px !important;
font-weight: 600 !important;
color: #7f8c8d !important;
font-family: inherit !important;
box-sizing: border-box !important;
line-height: 1.4 !important;
}
.${TICKET_SEP_CLASS}::after {
content: '' !important;
display: block !important;
width: 100% !important;
height: 1px !important;
background: #c8d6db !important;
margin-top: 5px !important;
}
`;
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);
}
function removeCompactStyle() {
// Удаляем тег стилей
const styleEl = document.getElementById(STYLE_ID);
if (styleEl) styleEl.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());
// Возвращаем кнопки/лайки обратно в meta из hde-actions-inline
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,
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 } = 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 } = 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 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 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 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;
// Пропускаем уже обработанные, если grouped-статус не изменился
const wasGrouped = msgEl.classList.contains('hde-compact-grouped');
if (msgEl.classList.contains(DONE_CLASS) && wasGrouped === isGrouped && editedIsSynced && reactionVisibilitySynced) {
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 orig = origMetaText;
// Кнопки + лайки → выносим из meta в block
if (meta && (actions || likeBlock)) {
const actionsRow = ensureActionsInlineContainer(block, meta);
if (actions && actions.parentElement !== actionsRow && actions.parentElement === block) {
actionsRow.appendChild(actions);
}
if (likeBlock && likeBlock.parentElement !== actionsRow && likeBlock.parentElement === userBlock) {
actionsRow.appendChild(likeBlock);
}
}
msgEl.classList.toggle('hde-has-reaction', hasReactionSelected(likeBlock));
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;
}
// ─── Обработка всех сообщений ────────────────────────────
var _lastProcessedHash = ''; // хеш для пропуска идентичных прогонов
function processAllMessages() {
if (!CONFIG.compactChat) return;
const container = getMessagesContainer();
if (!container) return;
// Быстрый хеш: кол-во сообщений + их data-user-id + даты из meta
const msgs = container.querySelectorAll('.ticket-conversation__message');
let hash = msgs.length + '|';
msgs.forEach(function (m) { hash += (m.getAttribute('data-user-id') || '') + ','; });
// Добавляем текстовые даты из meta
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) 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;
/** Строит DOM-элемент сообщения из данных API (копируя реальную структуру HDE) */
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'; // default
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);
// HDE использует data-post-id или data-comment-id
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;
// ── user-block (аватар) ──
const userBlock = document.createElement('div');
userBlock.setAttribute('data-v-1638218a', '');
userBlock.setAttribute('data-v-169b4e77', '');
userBlock.className = 'ticket-conversation__message-user-block';
// Аватар — div с background-image (как в оригинале)
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);
// ── message-block ──
const block = document.createElement('div');
block.className = 'ticket-conversation__message-block';
// Meta (скрытая CSS)
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);
// Text wrapper
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');
}
// HTML bubble
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);
// ★ v12.9: Аватар ВСЕГДА перед block — одинаковый порядок для обоих типов!
// Оригинальный HDE DOM: msgEl > [userBlock-avatar, block-bubble]
// Позиционирование через CSS flex-direction на самом msgEl:
// _staff: row-reverse → [avatar|block] рендерится как [block | avatar] = справа ✅
// _user: row → [avatar|block] рендерится как [avatar | block] = слева ✅
msgEl.appendChild(userBlock); // аватар
msgEl.appendChild(block); // пузырь (всегда после аватара в DOM)
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;
}
/** Загружает страницу старых сообщений через API HDE */
async function loadOlderPage(pageNum) {
console.log('[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;
console.log('[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();
console.log('[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;
// ★ Вставка: API [новее→старее]; insertBefore перед одной refNode
// каждый новый толкает предыдущие вниз → итог [старее...новее | существующие]
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);
}
console.log('[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);
console.log('[HDE Compact] ← loadOk page=' + pageNum + ' total=' + _totalPages);
// Автозагрузка следующей страницы цепочкой (не более MAX_PAGES_TO_LOAD)
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) {
console.log('[HDE Compact] автозагрузка page=' + nextP + ' (загружено ' + loadedCount + '/' + MAX_PAGES_TO_LOAD + ')');
setTimeout(function () { loadOlderPage(nextP); }, 300);
} else if (loadedCount >= MAX_PAGES_TO_LOAD) {
console.log('[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' }
});
if (!resp.ok) throw new Error('Conversation HTTP ' + resp.status);
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;
}
async function loadPreviousTicketsHistory() {
if (!CONFIG.autoLoadHistory || !CONFIG.autoLoadPreviousDialogs) return;
injectTicketSepStyle();
const currentTicketId = getTicketId();
if (!currentTicketId || _historyLoadedForTicketId === currentTicketId) return;
const container = getMessagesContainer();
if (!container) return;
try {
const historyItems = await fetchHistoryTickets(currentTicketId);
if (!Array.isArray(historyItems) || historyItems.length === 0) {
_historyLoadedForTicketId = currentTicketId;
return;
}
// Берём N самых свежих прошлых диалогов.
// Независимо от порядка в ответе API — сортируем по id (старше = меньший id)
// и берём последние N (самые новые).
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());
// Якорь — первое сообщение текущего тикета (не кнопка истории / не hde-date-separator).
// Иначе insertBefore(..., firstChild) вешает прошлые диалоги не туда и порядок «ломается».
const insertAnchor = getPreviousDialogsInsertAnchor(container);
if (!insertAnchor) {
_historyLoadedForTicketId = currentTicketId;
return;
}
// Итерируем от самого НОВОГО к самому СТАРОМУ.
// Каждый блок вставляется перед одним и тем же insertAnchor (выше предыдущих вставок).
// Итог (сверху вниз): [самый старый прошлый] … [самый новый прошлый] [текущий тикет]
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;
// API отдаёт [новее -> старее], приводим к [старее -> новее]
allMessages.reverse();
const insertBefore = insertAnchor;
let insertedCount = 0;
// Дата создания тикета = createdAt самого первого сообщения.
// Страницы грузятся [новее→старее], поэтому самое старое — на последней странице,
// последний элемент массива на ней (до reverse). После всех загрузок и reverse
// это allMessages[0] — но только если мы загрузили последнюю страницу.
// Если totalPages > pagesToRead — загружаем последнюю страницу отдельно только ради даты.
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] // первый на последней странице = самый старый (API [новее→старее])
: (allMessages.length > 0 ? allMessages[0] : null); // fallback: первый после reverse
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');
// URL формат: /ru/ticket/list/filter/id/CURRENT/ticket/TARGET
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);
// Затем вставляем сообщения в обратном порядке (от новых к старым),
// чтобы после всех 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();
}
_historyLoadedForTicketId = currentTicketId;
processAllMessages();
} catch (err) {
console.warn('[HDE Compact] Ошибка подгрузки прошлых обращений:', err);
}
}
/** Запускает цепочку загрузки страниц начиная с nextPage, но не более MAX_PAGES_TO_LOAD */
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) {
console.log('[HDE Compact] достигнут лимит ' + MAX_PAGES_TO_LOAD + ' страниц');
return;
}
if (nextPage <= _totalPages || _totalPages <= 1) {
loadOlderPage(nextPage);
}
}
/** Парсим totalPages из пагинации на странице */
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 < 1000) 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);
}
// ─── Observer ────────────────────────────────────────────
// RAF-дебаунс: один запуск за кадр, не накапливаем очередь
var _rafId = null;
function scheduleProcess() {
if (_rafId) return;
_rafId = requestAnimationFrame(function () {
_rafId = null;
processAllMessages();
});
}
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']
});
}
// ─── Статусы коллег в sidebar ────────────────────────────
let _colleagueStatusesInterval = null;
let _colleagueStatusesInFlight = false;
let _colleagueStatusesRetryTimer = null;
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 sidebar = document.querySelector('.ticket-sidebar');
if (!sidebar) {
_colleagueStatusesInFlight = false;
return;
}
let container = sidebar.querySelector('.staffs-injected-block');
if (!container) {
container = document.createElement('div');
container.className = 'staffs-injected-block';
sidebar.appendChild(container);
}
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;
}
container.innerHTML = '';
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) {
container.innerHTML = '';
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, statusText, statusClass, ticketsCount });
});
let html = '';
groupOrder.forEach((groupName) => {
const totalTickets = groups[groupName].reduce((sum, member) => sum + member.ticketsCount, 0);
html += `<div class="staffs-injected-group-header">
<span class="staffs-injected-group-total">${totalTickets > 0 ? totalTickets : ''}</span>
<span class="staffs-injected-group-name">${groupName}</span>
</div>
<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">
<span class="${badgeClasses}">${badgeValue}</span>
<span class="staffs-injected-member-name ${member.statusClass}">${member.name}</span>
<span class="staffs-injected-member-status ${member.statusClass}">${member.statusText}</span>
</li>`;
});
html += '</ul>';
});
container.innerHTML = html;
} catch (e) {
container.innerHTML = '';
} finally {
finish();
}
};
iframe.onload = () => setTimeout(check, 2000);
iframe.onerror = () => {
container.innerHTML = '';
iframe.remove();
_colleagueStatusesInFlight = false;
};
}
function startColleagueStatuses() {
if (_colleagueStatusesInterval) return;
updateColleagueStatusesBlock();
_colleagueStatusesInterval = setInterval(updateColleagueStatusesBlock, 10000);
}
function syncColleagueStatusesModule() {
if (CONFIG.colleagueStatuses) startColleagueStatuses();
else stopColleagueStatuses();
}
function removeLeftTabsMirror() {
document.getElementById(LEFT_TABS_MIRROR_ID)?.remove();
document.getElementById(LEFT_TABS_COLUMN_ID)?.remove();
document.documentElement.classList.remove(LEFT_TABS_ONLY_CLASS);
}
let _leftTabsMirrorSyncScheduled = false;
let _leftTabsMirrorSyncInProgress = false;
let _leftTabsMirrorLastSignature = '';
const _leftTabsPreviewByName = Object.create(null);
function getLeftTabsSignature(entries) {
return entries.map((entry, idx) => {
const isActive = entry.isActive ? '1' : '0';
const hasBadge = entry.hasBadge ? '1' : '0';
return `${idx}:${entry.source}:${isActive}:${hasBadge}:${entry.name}:${entry.preview || ''}`;
}).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 extractCurrentTicketPreview() {
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 txt = bubbles[i]?.textContent?.replace(/\s+/g, ' ').trim();
if (!txt) continue;
return txt.length > 140 ? `${txt.slice(0, 140)}…` : txt;
}
return '';
}
function hasTicketTabBadge(tabEl) {
if (!tabEl) return false;
if (tabEl.querySelector('.ticket-tabs__tab-badge, .ticket-tabs__more-tab-badge, [class*="tab-badge"], [class*="unread"], [class*="urgent"], [class*="notify"], [class*="new"]')) {
return true;
}
if (tabEl.querySelector('[style*="f56c6c"], [style*="F56C6C"], [style*="fb0a0a"], [style*="FB0A0A"], [style*="rgb(245, 108, 108)"], [style*="rgb(251, 10, 10)"]')) {
return true;
}
const className = tabEl.className || '';
if (/(unread|urgent|notify|badge|new)/i.test(className)) return true;
const styleAttr = tabEl.getAttribute('style') || '';
if (/(f56c6c|fb0a0a|255,\s*0,\s*0|245,\s*108,\s*108|red)/i.test(styleAttr)) return true;
try {
const cs = window.getComputedStyle(tabEl);
const topBorderColor = cs.borderTopColor || '';
const topBorderWidth = parseFloat(cs.borderTopWidth || '0');
if (topBorderWidth > 0 && /(245,\s*108,\s*108|251,\s*10,\s*10|255,\s*0,\s*0)/.test(topBorderColor)) {
return true;
}
const pseudoHosts = [
tabEl,
tabEl.querySelector('.ticket-tabs__tab-name'),
tabEl.querySelector('.ticket-tabs__more-tab-name')
].filter(Boolean);
for (const host of pseudoHosts) {
const before = window.getComputedStyle(host, '::before');
const after = window.getComputedStyle(host, '::after');
if ((before.content && before.content !== 'none' && isRedLikeColor(before.backgroundColor))
|| (after.content && after.content !== 'none' && isRedLikeColor(after.backgroundColor))) {
return true;
}
}
const redDotLike = Array.from(tabEl.querySelectorAll('*')).some((el) => {
const s = window.getComputedStyle(el);
const w = parseFloat(s.width || '0');
const h = parseFloat(s.height || '0');
const br = parseFloat(s.borderRadius || '0');
return w > 0 && w <= 12 && h > 0 && h <= 12 && br >= 3 && isRedLikeColor(s.backgroundColor);
});
if (redDotLike) return true;
} catch (e) {}
return false;
}
function collectTicketTabEntries() {
const entries = [];
const visibleTabs = Array.from(document.querySelectorAll('.ticket-tabs .ticket-tabs__tab'))
.filter((el) => el.querySelector('.ticket-tabs__tab-name'));
visibleTabs.forEach((tabEl) => {
const name = tabEl.querySelector('.ticket-tabs__tab-name')?.textContent?.trim() || 'Тикет';
const removeBtn = tabEl.querySelector('.ticket-tabs__tab-remove');
const isActive = tabEl.classList.contains('ticket-tabs__tab_active');
const hasBadge = hasTicketTabBadge(tabEl);
const livePreview = isActive ? extractCurrentTicketPreview() : '';
const preview = livePreview || _leftTabsPreviewByName[name] || (hasBadge ? 'Новый ответ' : '');
if (preview) _leftTabsPreviewByName[name] = preview;
entries.push({
source: 'tab',
name,
isActive,
hasBadge,
preview,
onOpen: () => tabEl.click(),
onClose: () => removeBtn?.click()
});
});
const moreTabs = Array.from(document.querySelectorAll('.ticket-tabs__more-tab'));
moreTabs.forEach((moreTabEl) => {
const name = moreTabEl.querySelector('.ticket-tabs__more-tab-name')?.textContent?.trim()
|| moreTabEl.getAttribute('title')
|| 'Тикет';
const removeBtn = moreTabEl.querySelector('.ticket-tabs__more-tab-remove, .el-icon-close');
entries.push({
source: 'more',
name,
isActive: false,
hasBadge: hasTicketTabBadge(moreTabEl),
preview: _leftTabsPreviewByName[name] || '',
onOpen: () => moreTabEl.click(),
onClose: () => removeBtn?.click()
});
});
const byName = new Set();
return entries.filter((entry) => {
const key = `${entry.source}:${entry.name}`;
if (byName.has(key)) return false;
byName.add(key);
return true;
});
}
function scheduleLeftTabsMirrorSync() {
if (!CONFIG.leftTabsMirror || _leftTabsMirrorSyncScheduled) return;
_leftTabsMirrorSyncScheduled = true;
requestAnimationFrame(() => {
_leftTabsMirrorSyncScheduled = false;
syncLeftTabsMirror();
});
}
function syncLeftTabsMirror() {
if (_leftTabsMirrorSyncInProgress) return;
if (!CONFIG.leftTabsMirror) {
removeLeftTabsMirror();
_leftTabsMirrorLastSignature = '';
return;
}
document.documentElement.classList.remove(LEFT_TABS_ONLY_CLASS);
const metroContainer = Array.from(document.querySelectorAll('section.el-container.metro-padding'))
.find((el) => el.querySelector('.ticket-sidebar') && el.querySelector('section.el-container.ticket-container'));
const sidebar = metroContainer?.querySelector('.ticket-sidebar');
const ticketContainer = metroContainer?.querySelector('section.el-container.ticket-container');
if (!metroContainer || !sidebar || !ticketContainer) return;
const entries = collectTicketTabEntries();
if (!entries.length) {
removeLeftTabsMirror();
_leftTabsMirrorLastSignature = '';
return;
}
const signature = getLeftTabsSignature(entries);
let mirror = document.getElementById(LEFT_TABS_MIRROR_ID);
if (mirror && signature === _leftTabsMirrorLastSignature) return;
_leftTabsMirrorSyncInProgress = true;
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);
}
if (!mirror) {
mirror = document.createElement('div');
mirror.id = LEFT_TABS_MIRROR_ID;
mirror.className = 'hde-left-tabs-mirror';
mirror.innerHTML = `
<div class="hde-left-tabs-mirror__title">Открытые тикеты</div>
<div class="hde-left-tabs-mirror__list"></div>
`;
column.appendChild(mirror);
} else if (mirror.parentElement !== column) {
column.appendChild(mirror);
}
const list = mirror.querySelector('.hde-left-tabs-mirror__list');
if (!list) {
_leftTabsMirrorSyncInProgress = false;
return;
}
list.innerHTML = '';
entries.forEach((entry) => {
const item = document.createElement('button');
item.type = 'button';
item.className = `hde-left-tabs-mirror__item${entry.isActive ? ' is-active' : ''}`;
item.innerHTML = `
<span class="hde-left-tabs-mirror__indicator${entry.hasBadge ? ' has-badge' : ''}" aria-hidden="true"></span>
<span class="hde-left-tabs-mirror__content">
<span class="hde-left-tabs-mirror__name">${escapeHtml(entry.name)}</span>
<span class="hde-left-tabs-mirror__preview">${escapeHtml(entry.preview || (entry.hasBadge ? 'Новый ответ' : ''))}</span>
</span>
<span class="hde-left-tabs-mirror__close" aria-hidden="true">✕</span>
`;
item.addEventListener('click', () => entry.onOpen?.());
const close = item.querySelector('.hde-left-tabs-mirror__close');
close?.addEventListener('click', (e) => {
e.stopPropagation();
entry.onClose?.();
});
list.appendChild(item);
});
_leftTabsMirrorLastSignature = signature;
_leftTabsMirrorSyncInProgress = false;
}
function initLeftTabsMirrorObserver() {
if (window.__hdeLeftTabsMirrorObserver) return;
window.__hdeLeftTabsMirrorObserver = true;
const nodeTouchesTicketTabs = (node) => {
if (!(node instanceof Element)) return false;
if (node.closest(`#${LEFT_TABS_MIRROR_ID}`)) return false;
if (node.matches('.ticket-tabs, .ticket-tabs__tab, .ticket-tabs__tab-name, .ticket-tabs__tab-remove, .ticket-tabs__more-tab, .ticket-tabs__more-tab-name, .ticket-tabs__more-tab-remove, .ticket-tabs__show-more-popper')) return true;
return !!node.querySelector('.ticket-tabs, .ticket-tabs__tab, .ticket-tabs__tab-name, .ticket-tabs__tab-remove, .ticket-tabs__more-tab, .ticket-tabs__more-tab-name, .ticket-tabs__more-tab-remove, .ticket-tabs__show-more-popper');
};
new MutationObserver((mutations) => {
if (!CONFIG.leftTabsMirror) return;
for (const m of mutations) {
if (nodeTouchesTicketTabs(m.target)) {
scheduleLeftTabsMirrorSync();
return;
}
for (const n of m.addedNodes) {
if (nodeTouchesTicketTabs(n)) {
scheduleLeftTabsMirrorSync();
return;
}
}
for (const n of m.removedNodes) {
if (nodeTouchesTicketTabs(n)) {
scheduleLeftTabsMirrorSync();
return;
}
}
}
}).observe(document.body, {
childList: true,
subtree: true,
attributes: true,
attributeFilter: ['class', 'style']
});
if (!window.__hdeLeftTabsMirrorClickBound) {
window.__hdeLeftTabsMirrorClickBound = true;
document.addEventListener('click', (e) => {
const target = e.target;
if (!(target instanceof Element)) return;
if (target.closest('.ticket-tabs__tab, .ticket-tabs__tab-remove, .ticket-tabs__more-tab, .ticket-tabs__more-tab-remove, .hde-left-tabs-mirror__item')) {
setTimeout(scheduleLeftTabsMirrorSync, 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 = `
body,
input,
textarea,
select,
button,
.el-input__inner,
.el-select-dropdown__item,
.el-tag,
.el-cascader-node__label,
.el-checkbox__label {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif !important;
}
`;
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 = `
<div class="hde-staffs-drawer__overlay" data-role="overlay"></div>
<div class="hde-staffs-drawer__panel" role="dialog" aria-label="Управление статусами сотрудников">
<div class="hde-staffs-drawer__header">
<span>Статусы сотрудников</span>
<button type="button" class="hde-staffs-drawer__close" aria-label="Закрыть">×</button>
</div>
<iframe class="hde-staffs-drawer__frame" title="Staff statuses" loading="lazy"></iframe>
</div>
`;
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;
}
// ─── UI Настроек ─────────────────────────────────────────
function initSettingsMenu() {
// Не возвращаемся сразу, если кнопка есть — нам важно уметь переткнуть её при SPA-перерисовке.
// Но если кнопка уже есть и она уже стоит в правильном месте — выходим.
const existingBtn = document.getElementById('hde-tools-menu');
const menu = document.querySelector('.menu');
const menuPlugins = document.querySelector('.menu-plugins');
// Правильное место: внутрь '.menu' (или в крайнем случае рядом с menu__items)
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 (obs) {
if (document.querySelector('.menu') || document.querySelector('.menu-plugins')) {
obs.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 = `
.hde-tools-panel {
position: fixed; width: 280px;
background: #fff; color: #333; border-radius: 6px;
box-shadow: 0 4px 16px rgba(0,0,0,0.25); padding: 15px; display: none; z-index: 99999;
font-family: sans-serif; cursor: default;
}
.hde-tools-panel.active { display: block; }
.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; }
.hde-tools-toggle {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
align-items: center;
column-gap: 10px;
margin-bottom: 10px;
font-size: 13px;
cursor: pointer;
}
.hde-tools-note {
font-size: 11px;
opacity: 0.8;
}
.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; }
.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; }
.hde-tools-toggle input[type="checkbox"]:checked { background: #4caf50; }
.hde-tools-toggle input[type="checkbox"]:checked::after { left: 18px; }
.hde-tools-toggle-actions { display: inline-flex; align-items: center; margin-left: auto; }
.hde-tools-link-btn {
display: inline-flex;
align-items: center;
justify-content: center;
width: 24px;
height: 24px;
padding: 0;
border: none;
background: transparent;
color: #606266;
text-decoration: none;
line-height: 1 !important;
}
.hde-tools-link-btn i {
font-size: 18px;
line-height: 1;
}
.hde-tools-toggle-actions .hde-tools-link-btn {
margin-right: 8px;
}
.hde-tools-link-btn:hover {
color: #409eff;
}
.hde-tools-field { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; font-size: 13px; }
.hde-tools-field input[type="number"],
.hde-tools-field select {
width: 72px; padding: 4px 6px; font-size: 12px; border: 1px solid #dcdfe6;
border-radius: 6px; text-align: right; outline: none;
}
.hde-tools-field select {
width: 120px; text-align: left; background: #fff;
}
.hde-staffs-drawer {
position: fixed;
inset: 0;
z-index: 100001;
visibility: hidden;
pointer-events: none;
transition: visibility 0s linear 0.3s;
}
.hde-staffs-drawer.active {
visibility: visible;
pointer-events: auto;
transition-delay: 0s;
}
.hde-staffs-drawer__overlay {
position: absolute;
inset: 0;
background: rgba(15, 20, 25, 0.45);
opacity: 0;
transition: opacity 0.3s ease;
}
.hde-staffs-drawer__panel {
position: absolute;
top: 0;
right: 0;
width: min(900px, 88vw);
height: 100%;
background: #fff;
border-left: 1px solid #dcdfe6;
box-shadow: -8px 0 24px rgba(0,0,0,0.18);
display: flex;
flex-direction: column;
transform: translateX(100%);
transition: transform 0.3s ease;
}
.hde-staffs-drawer.active .hde-staffs-drawer__overlay {
opacity: 1;
}
.hde-staffs-drawer.active .hde-staffs-drawer__panel {
transform: translateX(0);
}
.hde-staffs-drawer__header {
height: 44px;
min-height: 44px;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 12px;
font-weight: 600;
border-bottom: 1px solid #ebeef5;
}
.hde-staffs-drawer__close {
width: 28px;
height: 28px;
border: none;
background: transparent;
color: #606266;
font-size: 22px;
line-height: 1;
cursor: pointer;
}
.hde-staffs-drawer__close:hover {
color: #303133;
}
.hde-staffs-drawer__frame {
width: 100%;
height: calc(100% - 44px);
border: 0;
background: #fff;
}
.hde-left-tabs-column {
flex: 0 0 180px;
width: 180px;
min-width: 180px;
padding: 0 8px;
margin-top: 8px;
padding-top: 8px;
box-sizing: border-box;
}
.hde-left-tabs-column .hde-left-tabs-mirror {
margin: 0;
}
.hde-left-tabs-mirror {
margin: 0 0 10px 0;
border: 1px solid #dcdfe6;
border-radius: 6px;
background: #fff;
overflow: hidden;
font-family: 'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif !important;
font-size: 12px !important;
color: #606266;
}
.hde-left-tabs-mirror,
.hde-left-tabs-mirror * {
font-family: 'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif !important;
}
.hde-left-tabs-mirror__title {
padding: 6px 8px;
font-size: 12px !important;
font-weight: 600;
color: #303133;
border-bottom: 1px solid #ebeef5;
}
.hde-left-tabs-mirror__list {
max-height: 100%;
overflow: auto;
}
.hde-left-tabs-mirror__item {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 8px;
width: 100%;
border: 0;
border-bottom: 1px solid #f0f2f5;
background: transparent;
color: #606266;
font-size: 12px !important;
font-weight: 400;
line-height: 1.4;
font-family: inherit !important;
text-align: left;
padding: 6px 8px;
cursor: pointer;
}
.hde-left-tabs-mirror__indicator {
width: 8px;
min-width: 8px;
height: 8px;
border-radius: 50%;
background: transparent;
margin-right: 6px;
margin-top: 4px;
}
.hde-left-tabs-mirror__indicator.has-badge {
background: #f56c6c;
}
.hde-left-tabs-mirror__content {
min-width: 0;
flex: 1;
display: flex;
flex-direction: column;
gap: 2px;
}
.hde-left-tabs-mirror__item:last-child {
border-bottom: 0;
}
.hde-left-tabs-mirror__item:hover {
background: #f5f7fa;
}
.hde-left-tabs-mirror__item.is-active {
background: #ecf5ff;
color: #409eff;
}
.hde-left-tabs-mirror__name {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
width: 100%;
font-family: inherit !important;
font-size: inherit !important;
}
.hde-left-tabs-mirror__preview {
display: block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
width: 100%;
font-size: 11px !important;
line-height: 1.25 !important;
opacity: 0.8;
}
.hde-left-tabs-mirror__close {
border: 0;
background: transparent;
color: inherit;
font-size: inherit !important;
font-family: inherit !important;
cursor: pointer;
padding: 0 2px;
}
html.${DARK_HTML_CLASS} .hde-left-tabs-mirror {
background: #1e2529;
border-color: #3d4654;
}
html.${DARK_HTML_CLASS} .hde-left-tabs-column {
border-right: 1px solid #2d333b;
}
html.${DARK_HTML_CLASS} .hde-left-tabs-mirror__title {
color: #e4e7ed;
border-bottom-color: #3d4654;
}
html.${DARK_HTML_CLASS} .hde-left-tabs-mirror__item {
color: #c0c4cc;
border-bottom-color: #2d333b;
}
html.${DARK_HTML_CLASS} .hde-left-tabs-mirror__item:hover {
background: #252b33;
}
html.${DARK_HTML_CLASS} .hde-left-tabs-mirror__item.is-active {
background: #2d333b;
color: #e4e7ed;
}
.staffs-injected-block {
border-radius: 3px;
padding-bottom: 10px;
margin-bottom: 12px;
text-align: center;
--staff-status-online: #3e9a55;
--staff-status-invisible: #3f78b8;
--staff-status-break: #c9872c;
--staff-status-offline: #c25353;
--staffs-muted: #c5c9d0;
--staffs-group-color: #555;
}
.staffs-injected-group-header {
display: flex;
align-items: center;
margin: 0;
font-weight: 700;
color: var(--staffs-group-color);
}
.staffs-injected-group-total {
flex: 0 0 auto;
padding-right: 0;
margin: 0 0 0 6px;
}
.staffs-injected-group-name {
flex: 1;
text-align: center;
}
.staffs-injected-list {
list-style: none;
padding: 0;
margin: 0 0 8px 0;
}
.staffs-injected-member-row {
display: flex;
align-items: center;
padding: 0;
}
.staffs-injected-badge {
display: inline-block;
width: 18px;
text-align: center;
font-size: 13px;
line-height: 1;
}
.staffs-injected-badge.no-tickets {
color: var(--staffs-muted);
}
.staffs-injected-member-name {
font-weight: 400;
flex: 1;
text-align: left;
}
.staffs-injected-member-status {
white-space: nowrap;
margin: 0 5px 0 0;
}
.staffs-injected-member-name.status-online,
.staffs-injected-member-status.status-online,
.staffs-injected-badge.status-online.has-tickets {
color: var(--staff-status-online);
}
.staffs-injected-member-name.status-invisible,
.staffs-injected-member-status.status-invisible,
.staffs-injected-badge.status-invisible.has-tickets {
color: var(--staff-status-invisible);
}
.staffs-injected-member-name.status-break,
.staffs-injected-member-status.status-break,
.staffs-injected-badge.status-break.has-tickets {
color: var(--staff-status-break);
}
.staffs-injected-member-name.status-offline,
.staffs-injected-member-status.status-offline,
.staffs-injected-badge.status-offline.has-tickets {
color: var(--staff-status-offline);
}
html.${DARK_HTML_CLASS} .staffs-injected-block {
--staff-status-online: #78c18b;
--staff-status-invisible: #7cb5ff;
--staff-status-break: #f4b562;
--staff-status-offline: #ef8a8a;
--staffs-muted: #6f7883;
--staffs-group-color: #c0c4cc;
}
html.${PINK_HTML_CLASS} .staffs-injected-block {
--staff-status-online: #7fae7f;
--staff-status-invisible: #8aa4d6;
--staff-status-break: #d5a06a;
--staff-status-offline: #cf7a8f;
--staffs-muted: #b79fb0;
--staffs-group-color: #7a5a6b;
}
/* Чтобы пункт меню/кнопка НИКОГДА не прятались (Vue может менять display/opacity) */
#hde-tools-menu { display: inline-flex !important; opacity: 1 !important; visibility: visible !important; }
#hde-tools-menu .menu__item { display: inline-flex !important; opacity: 1 !important; visibility: visible !important; }
#hde-tools-menu * { opacity: 1 !important; }
a.menu__item.menu__item_logo {
position: relative !important;
background-image: none !important;
background-color: transparent !important;
color: var(--menu-item-color, #606266) !important;
}
a.menu__item.menu__item_logo::before {
content: '';
position: absolute;
inset: 0;
background: currentColor;
-webkit-mask: url("//cdn5.helpdeskeddy.com//img/menu-logo-white.svg") center / contain no-repeat;
mask: url("//cdn5.helpdeskeddy.com//img/menu-logo-white.svg") center / contain no-repeat;
}
a.menu__item.menu__item_logo:hover {
color: var(--menu-item-color-hover, #409eff) !important;
}
a.menu__item[href="/ru/dashboard"],
a.menu__item[href="/ru/dashboard/"] {
display: none !important;
}
#hde-tools-btn {
flex-direction: column !important;
align-items: center !important;
justify-content: center !important;
gap: 4px !important;
text-align: center !important;
}
#hde-tools-btn .menu__item-icon,
#hde-tools-btn .menu__item-name {
display: block !important;
margin: 0 !important;
}
#hde-tools-btn .menu__item-icon {
position: relative;
width: 25px;
height: 25px;
}
#hde-tools-btn .menu__item-icon .hde-dino-source {
display: none !important;
}
#hde-tools-btn .menu__item-icon .hde-dino-mask {
position: absolute;
inset: 0;
background: currentColor;
-webkit-mask-size: contain;
mask-size: contain;
-webkit-mask-repeat: no-repeat;
mask-repeat: no-repeat;
-webkit-mask-position: center;
mask-position: center;
}
html.${LEFT_TABS_ONLY_CLASS} #ticket-app .ticket-topbar__tabs {
display: none !important;
}
html.${LEFT_TABS_ONLY_CLASS} .ticket-tabs__show-more-popper {
display: none !important;
}
`;
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 = `
<label class="hde-tools-field">
<span>Тема интерфейса</span>
<select id="hde-cfg-theme-mode">
<option value="${THEME_MODE_STANDARD}" ${CONFIG.themeMode === THEME_MODE_STANDARD ? 'selected' : ''}>Стандартная</option>
<option value="${THEME_MODE_DARK}" ${CONFIG.themeMode === THEME_MODE_DARK ? 'selected' : ''}>Тёмная</option>
<option value="${THEME_MODE_PINK}" ${CONFIG.themeMode === THEME_MODE_PINK ? 'selected' : ''}>Розовая</option>
</select>
</label>
<label class="hde-tools-toggle">
<span>Компактный чат</span>
<input type="checkbox" id="hde-cfg-compact" ${CONFIG.compactChat ? 'checked' : ''}>
</label>
<label class="hde-tools-toggle">
<span>Открытые тикеты слева <span class="hde-tools-note">(эксперимент, работает не стабильно)</span></span>
<input type="checkbox" id="hde-cfg-left-tabs-mirror" ${CONFIG.leftTabsMirror ? 'checked' : ''}>
</label>
<label class="hde-tools-toggle hde-tools-toggle-colleague">
<span>Статусы коллег</span>
<span class="hde-tools-toggle-actions">
<a
id="hde-colleague-staffs-link"
class="hde-tools-link-btn"
href="/ru/dashboard/staffs/"
target="_blank"
rel="noopener noreferrer"
title="Открыть аналитику сотрудников"
aria-label="Открыть аналитику сотрудников"
><i class="el-icon-setting"></i></a>
<input type="checkbox" id="hde-cfg-colleague-statuses" ${CONFIG.colleagueStatuses ? 'checked' : ''}>
</span>
</label>
<label class="hde-tools-toggle">
<span>Подгружать историю</span>
<input type="checkbox" id="hde-cfg-autoload" ${CONFIG.autoLoadHistory ? 'checked' : ''}>
</label>
<label class="hde-tools-toggle">
<span>Подгружать прошлые диалоги</span>
<input type="checkbox" id="hde-cfg-prev-dialogs" ${CONFIG.autoLoadPreviousDialogs ? 'checked' : ''}>
</label>
<label class="hde-tools-field">
<span>Кол-во подгружаемых диалогов</span>
<input type="number" id="hde-cfg-prev-limit" min="1" max="10" step="1" value="${getPreviousDialogsLimit()}">
</label>
`;
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';
}
// Клик по нативному menu__item — предотвращаем переход по href
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();
document.documentElement.classList.remove(LEFT_TABS_ONLY_CLASS);
syncLeftTabsMirror();
};
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();
// btnContainer мог уже существовать (SPA-перерисовка). Не дублируем btn.
if (!btnContainer.querySelector('#hde-tools-btn')) {
btnContainer.appendChild(btn);
}
if (!panel.parentElement) {
document.body.appendChild(panel);
}
// Вставляем на уровень выше, чем '.menu-plugins' (внутрь '.menu')
// Иначе SPA может выкинуть/пересобрать узел '.menu__items'.
if (menu && btnContainer && !menu.contains(btnContainer)) {
menu.appendChild(btnContainer);
} else if (!menu && (menuItems && !menuItems.contains(btnContainer))) {
menuItems.appendChild(btnContainer);
}
// Если '.menu-plugins' уже есть — можно попробовать вставить рядом, но без риска отката
// (главное — чтобы контейнер был в '.menu' и был виден сразу)
if (menuPlugins && menuItems && menu && btnContainer) {
try { menuItems.insertBefore(btnContainer, menuPlugins); } catch (e) {}
}
// Наблюдатель: если Vue снова пересобирает '.menu__items' — переткнём в ближайший '.menu'
// (а не в "первый найденный .menu" — это ломается, если элементов несколько)
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 });
}
}
// ─── Перемещение кнопки истории в .ticket-detail__title ────────
// Физически переносим в нужное место. Vue-обработчики сидят на элементе,
// поэтому дропдаун работает. Проблема закрытия — Vue слушает document click
// и проверяет contains() относительно исходного парента —
// после переноса это сохраняется, поэтому всё должно работать.
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 initHistoryButtonMover() {
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);
}
// ─── Init ────────────────────────────────────────────────
function init() {
loadConfig();
document.documentElement.classList.remove(LEFT_TABS_ONLY_CLASS);
applyThemeFromConfig();
initSettingsMenu();
initLeftTabsMirrorObserver();
syncLeftTabsMirror();
syncColleagueStatusesModule();
injectStyle();
if (CONFIG.autoLoadHistory) injectPaginationStyle();
processAllMessages();
initAutoLoad();
observeMessages();
moveHistoryButton();
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
window.addEventListener('beforeunload', stopColleagueStatuses);
// SPA навигация — перезапуск при смене URL
var lastUrl = location.href;
new MutationObserver(function () {
if (location.href !== lastUrl) {
lastUrl = location.href;
_loadedPages.clear(); // сбрасываем загруженные страницы
_historyLoadedForTicketId = null;
setTimeout(function () {
document.documentElement.classList.remove(LEFT_TABS_ONLY_CLASS);
applyThemeFromConfig();
syncLeftTabsMirror();
syncColleagueStatusesModule();
injectStyle();
processAllMessages();
initAutoLoad();
moveHistoryButton();
}, 500);
}
// Только прямые дети body — нам нужна лишь смена URL, не весь DOM
}).observe(document.body, { childList: true });
})();