document.addEventListener("DOMContentLoaded", function () { const API_BASE = "https://assistentzerocoder.ru"; // Базовый URL API const HEALTH_ENDPOINT = `${API_BASE}/healthz`; const chatIcon = document.getElementById("chat-icon"); const chatWidget = document.getElementById("chat-widget"); const chatOverlay = document.getElementById("chat-overlay"); const chatMessages = document.getElementById("chat-messages"); const userInput = document.getElementById("user-message"); const sendButton = document.getElementById("send-button"); const closeChatButton = document.getElementById("close-chat"); if (!chatIcon || !chatWidget || !chatOverlay || !chatMessages || !userInput || !sendButton || !closeChatButton) { console.error("Ошибка: Один или несколько элементов чата не найдены."); return; } let isProcessing = false; // Хелперы для надёжного управления стилями c !important function setDisplayImportant(element, value) { try { element.style.setProperty("display", value, "important"); } catch (_) {} } function setOpacityImportant(element, value) { try { element.style.setProperty("opacity", String(value), "important"); } catch (_) {} } function setVisibilityImportant(element, value) { try { element.style.setProperty("visibility", value, "important"); } catch (_) {} } // По умолчанию скрываем иконку: она появится только если сервер здоров setDisplayImportant(chatIcon, "none"); // На случай, если CSS не загрузился: принудительно скрываем виджет и оверлей try { setDisplayImportant(chatWidget, "none"); setOpacityImportant(chatWidget, 0); setDisplayImportant(chatOverlay, "none"); setOpacityImportant(chatOverlay, 0); } catch (_) {} // Динамически подключаем CSS, если он не был добавлен в
try { const cssHref = `${API_BASE}/static/chat-style.css`; const alreadyLinked = Array.from(document.styleSheets || []).some(ss => { try { return ss.href && ss.href.indexOf(cssHref) !== -1; } catch (_) { return false; } }); if (!alreadyLinked) { const link = document.createElement("link"); link.rel = "stylesheet"; link.href = cssHref; link.crossOrigin = "anonymous"; document.head.appendChild(link); } } catch (_) {} // Грузим DOMPurify для санитизации Markdown, если не подключен try { if (!window.DOMPurify) { const s = document.createElement("script"); s.src = "https://cdn.jsdelivr.net/npm/dompurify@3.0.9/dist/purify.min.js"; s.defer = true; document.head.appendChild(s); } } catch (_) {} function shouldForceIcon() { try { if (window.ASSISTANT_SHOW_ICON === true) return true; const params = new URLSearchParams(window.location.search || ""); return params.get("showChatIcon") === "1"; } catch (_) { return false; } } // Функция открытия чата function openChat() { setDisplayImportant(chatWidget, "flex"); setDisplayImportant(chatOverlay, "block"); setVisibilityImportant(chatWidget, "visible"); setVisibilityImportant(chatOverlay, "visible"); setTimeout(() => { setOpacityImportant(chatWidget, 1); setOpacityImportant(chatOverlay, 0.5); }, 10); try { chatWidget.setAttribute("aria-hidden", "false"); } catch (_) {} try { chatOverlay.setAttribute("aria-hidden", "false"); } catch (_) {} } // Функция закрытия чата function closeChat() { setOpacityImportant(chatWidget, 0); setOpacityImportant(chatOverlay, 0); setTimeout(() => { setDisplayImportant(chatWidget, "none"); setDisplayImportant(chatOverlay, "none"); setVisibilityImportant(chatWidget, "hidden"); setVisibilityImportant(chatOverlay, "hidden"); }, 500); try { chatWidget.setAttribute("aria-hidden", "true"); } catch (_) {} try { chatOverlay.setAttribute("aria-hidden", "true"); } catch (_) {} } async function canOpenChat() { try { const res = await fetch(HEALTH_ENDPOINT, { method: "GET" }); if (!res.ok) return false; const data = await res.json(); return data.status === "ok"; } catch (e) { return false; } } // Открытие чата при клике по иконке - только если сервер здоров chatIcon.addEventListener("click", async function () { if (await canOpenChat()) { openChat(); } else { // Мягкое отключение: просто не открываем console.warn("Чат недоступен: сервер нездоров"); } }); // Закрытие по клику на затемнение вокруг виджета chatOverlay.addEventListener("click", function () { closeChat(); }); // Закрытие по Escape document.addEventListener("keydown", function (e) { if (e.key === "Escape") closeChat(); }); // Показываем иконку при успешном health-check; если включён форс-показ — сразу показываем (async () => { if (shouldForceIcon()) { setDisplayImportant(chatIcon, "flex"); setVisibilityImportant(chatIcon, "visible"); setOpacityImportant(chatIcon, 1); } else if (await canOpenChat()) { setDisplayImportant(chatIcon, "flex"); setVisibilityImportant(chatIcon, "visible"); setOpacityImportant(chatIcon, 1); } else { setDisplayImportant(chatIcon, "none"); setOpacityImportant(chatIcon, 0); setVisibilityImportant(chatIcon, "hidden"); } })(); // Периодическая проверка здоровья раз в 30 секунд, // чтобы автоматически показать иконку, когда сервер оживёт try { setInterval(async () => { if (shouldForceIcon()) { setDisplayImportant(chatIcon, "flex"); setVisibilityImportant(chatIcon, "visible"); setOpacityImportant(chatIcon, 1); return; } if (await canOpenChat()) { setDisplayImportant(chatIcon, "flex"); setVisibilityImportant(chatIcon, "visible"); setOpacityImportant(chatIcon, 1); } else { setDisplayImportant(chatIcon, "none"); setOpacityImportant(chatIcon, 0); setVisibilityImportant(chatIcon, "hidden"); } }, 30000); } catch (_) {} // Автопоказ виджета через 45 секунд, один раз за сессию, только если сервер здоров try { if (!sessionStorage.getItem("chat_auto_opened")) { setTimeout(async () => { try { if (await canOpenChat()) { openChat(); sessionStorage.setItem("chat_auto_opened", "1"); } } catch (_) {} }, 45000); } } catch (_) {} // Закрытие чата closeChatButton.addEventListener("click", function () { closeChat(); }); // Убираем автопоказ, чтобы не ломать страницы при недоступности // Отправка сообщения: обработка клика по кнопке и нажатия Enter sendButton.addEventListener("click", sendMessage); userInput.addEventListener("keydown", function (event) { if (event.key === "Enter" && !isProcessing) { sendMessage(); } }); // Обработчик кнопок с быстрыми вопросами document.querySelectorAll(".quick-question").forEach(button => { button.addEventListener("click", function () { const quickMessage = this.getAttribute("data-message") || ""; if (!quickMessage.trim()) return; // Не отправляем пустое сообщение userInput.value = quickMessage; sendMessage(); }); }); // Функция отправки сообщения с обработкой Markdown function sendMessage() { const message = userInput.value.trim(); if (!message || isProcessing) return; // Проверка длины сообщения: если больше 700 символов, показываем предупреждение и не отправляем if (message.length > 700) { alert("Сообщение слишком большое. Пожалуйста, сократите сообщение до 700 символов."); return; } console.log("Отправляем сообщение:", message); // Добавляем сообщение пользователя в чат chatMessages.insertAdjacentHTML("beforeend", ``); userInput.value = ""; sendButton.disabled = true; isProcessing = true; // Анимация "Ульяна печатает..." const typingMessage = `