diff --git a/app/static/css/index.css b/app/static/css/index.css index 18859a6..40b02b0 100644 --- a/app/static/css/index.css +++ b/app/static/css/index.css @@ -617,6 +617,12 @@ a{color:var(--link)} user-select: none; } +/* Разрешаем выделение текста в области логов */ +.multi-view-panel .multi-view-log { + user-select: text; + cursor: text; +} + /* Стили для перетаскиваемого элемента */ .multi-view-panel.dragging { opacity: 0.7; @@ -664,6 +670,7 @@ a{color:var(--link)} .multi-view-header { cursor: grab; position: relative; + user-select: none; /* Запрещаем выделение в заголовке */ } .multi-view-header:active { @@ -2169,6 +2176,7 @@ a{color:var(--link)} gap: 4px; align-items: center; flex-shrink: 0; /* Предотвращает сжатие кнопок */ + user-select: none; /* Запрещаем выделение в кнопках */ } .level-btn { @@ -2664,3 +2672,4 @@ footer{position:fixed;right:10px;bottom:10px;opacity:.6;font-size:11px} .notification-close:hover { background: var(--chip); color: var(--fg); +} diff --git a/app/static/js/index.js b/app/static/js/index.js index b6fb301..773c37c 100644 --- a/app/static/js/index.js +++ b/app/static/js/index.js @@ -5,7 +5,7 @@ * Версия: 2.0 */ -console.log('LogBoard+ script loaded - VERSION 2'); +// LogBoard+ script loaded - VERSION 2 /** * Глобальное состояние приложения @@ -93,9 +93,6 @@ const els = { * @param {string} s - Состояние: 'on', 'off', 'err', 'available' */ function setWsState(s){ - console.log('setWsState: Устанавливаем состояние', s); - console.log('setWsState: Текущие соединения:', Object.keys(state.open)); - els.wsstate.textContent = 'ws: ' + s; // Удаляем все классы состояний @@ -120,12 +117,8 @@ function setWsState(s){ function determineWsState() { const openConnections = Object.keys(state.open); - console.log('determineWsState: Проверяем', openConnections.length, 'соединений'); - console.log('determineWsState: Все соединения:', openConnections); - // Если нет открытых соединений, проверяем сервер через AJAX if (openConnections.length === 0) { - console.log('determineWsState: Нет соединений, проверяем сервер'); // Асинхронно проверяем сервер, но возвращаем 'off' для немедленного отображения // Если сервер доступен, checkWebSocketStatus установит 'on' setTimeout(() => { @@ -143,36 +136,27 @@ function determineWsState() { for (const id of openConnections) { const obj = state.open[id]; if (obj && obj.ws) { - console.log(`determineWsState: Соединение ${id}, readyState:`, obj.ws.readyState, 'WebSocket:', obj.ws); - if (obj.ws.readyState === WebSocket.OPEN) { hasActiveConnection = true; - console.log(`determineWsState: Соединение ${id} активно`); } else if (obj.ws.readyState === WebSocket.CONNECTING) { hasConnecting = true; - console.log(`determineWsState: Соединение ${id} подключается`); } else if (obj.ws.readyState === WebSocket.CLOSED || obj.ws.readyState === WebSocket.CLOSING) { closedConnections.push(id); - console.log(`determineWsState: Соединение ${id} закрыто/закрывается`); } } else { - console.log(`determineWsState: Соединение ${id} не найдено или нет WebSocket, obj:`, obj); closedConnections.push(id); } } // Удаляем закрытые соединения closedConnections.forEach(id => { - console.log(`determineWsState: Удаляем закрытое соединение ${id}`); delete state.open[id]; }); // Если есть активные соединения или есть соединения в процессе установки if (hasActiveConnection || hasConnecting) { - console.log('determineWsState: Есть активные/подключающиеся соединения, возвращаем on'); return 'on'; } else { - console.log('determineWsState: Нет активных соединений, проверяем сервер'); // Асинхронно проверяем сервер, но возвращаем 'off' для немедленного отображения // Если сервер доступен, checkWebSocketStatus установит 'on' setTimeout(() => { @@ -187,23 +171,17 @@ async function checkWebSocketStatus() { try { const token = localStorage.getItem('access_token'); if (!token) { - console.log('checkWebSocketStatus: Нет токена, устанавливаем off'); setWsState('off'); return; } - - console.log('checkWebSocketStatus: Отправляем запрос к /api/websocket/status'); const response = await fetch('/api/websocket/status', { headers: { 'Authorization': `Bearer ${token}` } }); - console.log('checkWebSocketStatus: Получен ответ, статус:', response.status, response.statusText); - if (response.ok) { const data = await response.json(); - console.log('checkWebSocketStatus: Получен ответ от сервера:', data); if (data.status === 'available') { // Проверяем активные клиентские соединения @@ -219,17 +197,13 @@ async function checkWebSocketStatus() { } // Если сервер доступен, всегда показываем 'on' - console.log('checkWebSocketStatus: Сервер доступен, устанавливаем on'); setWsState('on'); } else if (data.status === 'no_containers') { - console.log('checkWebSocketStatus: Нет контейнеров, устанавливаем off'); setWsState('off'); } else { - console.log('checkWebSocketStatus: Ошибка сервера, устанавливаем err'); setWsState('err'); } } else { - console.log('checkWebSocketStatus: HTTP ошибка, устанавливаем err'); setWsState('err'); } } catch (error) { @@ -249,11 +223,8 @@ function startWebSocketStatusCheck() { // Проверяем каждые 3 секунды wsStatusInterval = setInterval(() => { - console.log('Автоматическая проверка состояния WebSocket'); checkWebSocketStatus(); }, 3000); - - console.log('Запущена автоматическая проверка состояния WebSocket'); } // Функция для остановки автоматической проверки @@ -261,7 +232,6 @@ function stopWebSocketStatusCheck() { if (wsStatusInterval) { clearInterval(wsStatusInterval); wsStatusInterval = null; - console.log('Остановлена автоматическая проверка состояния WebSocket'); } } @@ -270,8 +240,6 @@ function stopWebSocketStatusCheck() { * @param {boolean} enabled - Включено ли AJAX обновление */ function setAjaxUpdateState(enabled) { - console.log('setAjaxUpdateState: enabled =', enabled, 'els.ajaxUpdateBtn =', !!els.ajaxUpdateBtn); - if (els.ajaxUpdateBtn) { // Удаляем все классы состояний els.ajaxUpdateBtn.classList.remove('ajax-on', 'ajax-off'); @@ -280,14 +248,10 @@ function setAjaxUpdateState(enabled) { if (enabled) { els.ajaxUpdateBtn.classList.add('ajax-on'); els.ajaxUpdateBtn.textContent = 'update'; - console.log('setAjaxUpdateState: Устанавливаем зеленый цвет (ajax-on)'); } else { els.ajaxUpdateBtn.classList.add('ajax-off'); els.ajaxUpdateBtn.textContent = 'update'; - console.log('setAjaxUpdateState: Устанавливаем красный цвет (ajax-off)'); } - } else { - console.error('setAjaxUpdateState: Кнопка ajaxUpdateBtn не найдена!'); } } @@ -455,10 +419,7 @@ function classify(line){ return 'ok'; // LOG также раскрашиваем как INFO } - // Отладка для неклассифицированных логов - if (l.includes('log:') || l.includes('fatal:')) { - console.log('Unclassified LOG/FATAL line:', line); - } + // Отладка для неклассифицированных логов (убрано для снижения шума в консоли) return 'other'; } @@ -502,8 +463,6 @@ function allowedByContainerLevel(cls, containerId) { else if (cls==='other') result = containerLevels.other; else result = true; - console.log(`allowedByContainerLevel: containerId=${containerId}, cls=${cls}, result=${result}, levels=`, containerLevels); - return result; } @@ -547,8 +506,6 @@ function updateLogVisibility(logElement) { function updateContainerLogVisibility(containerId) { if (!state.multiViewMode) return; - console.log(`updateContainerLogVisibility: Обновляем видимость логов для контейнера ${containerId}`); - const logElement = document.querySelector(`.multi-view-log[data-container-id="${containerId}"]`); if (!logElement) return; @@ -666,28 +623,19 @@ function updateHeaderCounters(containerId, counters) { // Функция для инициализации состояния кнопок уровней логирования function initializeLevelButtons() { - console.log('initializeLevelButtons: Starting initialization...'); - console.log('initializeLevelButtons: Current multiViewMode:', state.multiViewMode); - console.log('initializeLevelButtons: Current selectedContainers:', state.selectedContainers); // Восстанавливаем состояние кнопок loglevels из localStorage const savedLevelsState = getLogLevelsStateFromStorage(); if (savedLevelsState) { - console.log('initializeLevelButtons: Restoring log levels state from localStorage:', savedLevelsState); - // Восстанавливаем глобальные настройки для single-view if (savedLevelsState.globalLevels) { state.levels = { ...state.levels, ...savedLevelsState.globalLevels }; - console.log('initializeLevelButtons: Restored global levels:', state.levels); } // Восстанавливаем настройки контейнеров для multi-view if (savedLevelsState.containerLevels) { state.containerLevels = { ...state.containerLevels, ...savedLevelsState.containerLevels }; - console.log('initializeLevelButtons: Restored container levels:', state.containerLevels); } - } else { - console.log('initializeLevelButtons: No saved levels state found in localStorage'); } // Инициализируем кнопки для single-view @@ -701,33 +649,25 @@ function initializeLevelButtons() { // Инициализируем кнопки для multi-view (если есть) const multiLevelBtns = document.querySelectorAll('.multi-view-levels .level-btn'); - console.log(`initializeLevelButtons: Found ${multiLevelBtns.length} multi-view level buttons`); multiLevelBtns.forEach((btn, index) => { const level = btn.getAttribute('data-level'); const containerId = btn.getAttribute('data-container-id'); - console.log(`initializeLevelButtons: Processing button ${index + 1}: level=${level}, containerId=${containerId}`); - // Инициализируем настройки контейнера, если их нет if (containerId && (!state.containerLevels || !state.containerLevels[containerId])) { if (!state.containerLevels) { state.containerLevels = {}; } state.containerLevels[containerId] = {debug: true, info: true, warn: true, err: true, other: true}; - console.log(`initializeLevelButtons: Initialized container levels for ${containerId}:`, state.containerLevels[containerId]); } // Используем настройки контейнера const isActive = state.containerLevels && state.containerLevels[containerId] ? state.containerLevels[containerId][level] : true; - console.log(`initializeLevelButtons: Setting button state: level=${level}, containerId=${containerId}, isActive=${isActive}`); - btn.classList.toggle('active', isActive); btn.classList.toggle('disabled', !isActive); - - console.log(`initializeLevelButtons: Button classes after toggle:`, btn.className); }); // Обновляем стили логов после инициализации кнопок @@ -739,7 +679,6 @@ function initializeLevelButtons() { // Устанавливаем обработчик событий для кнопок уровней логирования if (window.levelButtonClickHandler) { document.addEventListener('click', window.levelButtonClickHandler); - console.log('initializeLevelButtons: Level button click handler installed'); } } /** @@ -991,21 +930,17 @@ function buildTabs(){ if (e.shiftKey && lastSelectedContainerId && lastSelectedContainerId !== svc.id) { // Shift+клик с предыдущим выбором - диапазонный выбор - console.log('Shift+клик для диапазонного выбора:', lastSelectedContainerId, 'to', svc.id); selectContainerRange(lastSelectedContainerId, svc.id); } else if (e.shiftKey) { // Shift+клик - добавляем/убираем из мультивыбора - console.log('Shift+клик на миникарточку:', svc.name); toggleContainerSelection(svc.id); lastSelectedContainerId = svc.id; } else if (e.ctrlKey || e.metaKey) { // Ctrl/Cmd+клик - добавляем/убираем из мультивыбора - console.log('Ctrl+клик на миникарточку:', svc.name); toggleContainerSelection(svc.id); lastSelectedContainerId = svc.id; } else { // Обычный клик - переключаемся в single view - console.log('Обычный клик на миникарточку:', svc.name); lastSelectedContainerId = svc.id; await switchToSingle(svc); } @@ -1321,22 +1256,19 @@ function updateContainerSelectionUI() { // Обновляем чекбоксы и обычные карточки контейнеров const checkboxes = document.querySelectorAll('.container-checkbox'); - console.log('Found checkboxes:', checkboxes.length); checkboxes.forEach(checkbox => { const containerId = checkbox.getAttribute('data-container-id'); const containerItem = checkbox.closest('.container-item'); - console.log('Processing checkbox for container:', containerId, 'checked:', checkbox.checked, 'should be:', state.selectedContainers.includes(containerId)); + // Processing checkbox for container if (state.selectedContainers.includes(containerId)) { checkbox.checked = true; containerItem.classList.add('selected'); - console.log('Container selected:', containerId); } else { checkbox.checked = false; containerItem.classList.remove('selected'); - console.log('Container deselected:', containerId); } }); @@ -1717,8 +1649,6 @@ async function updateMultiViewMode() { state.multiViewMode = false; const selectedService = state.services.find(s => s.id === state.selectedContainers[0]); if (selectedService) { - console.log('Switching to single view for:', selectedService.name); - console.log('updateMultiViewMode: About to call switchToSingle - VERSION 2'); // Сохраняем режим просмотра в localStorage saveViewMode(false, [selectedService.id]); @@ -1754,7 +1684,7 @@ async function updateMultiViewMode() { updateActiveContainerUI(null); } - console.log(`Multi-view mode updated: multiViewMode = ${state.multiViewMode}`); + // Multi-view mode updated // Сохраняем состояние кнопок loglevels при переключении режимов saveLogLevelsState(); @@ -1774,19 +1704,14 @@ async function updateMultiViewMode() { * Открывает WebSocket соединения для всех выбранных контейнеров */ async function setupMultiView() { - console.log('setupMultiView called'); - // Проверяем, что у нас действительно больше одного контейнера if (state.selectedContainers.length <= 1) { - console.log('setupMultiView: Not enough containers for multi-view, switching to single view'); if (state.selectedContainers.length === 1) { const selectedService = state.services.find(s => s.id === state.selectedContainers[0]); if (selectedService) { - console.log('setupMultiView: Calling switchToSingle for:', selectedService.name); await switchToSingle(selectedService); } } else { - console.log('setupMultiView: No containers selected, clearing log area'); clearLogArea(); } return; @@ -1795,7 +1720,6 @@ async function setupMultiView() { // Дополнительная проверка - если уже есть мультипросмотр, удаляем его для пересоздания const existingMultiView = document.getElementById('multiViewGrid'); if (existingMultiView) { - console.log('setupMultiView: Multi-view already exists, removing for recreation'); existingMultiView.remove(); } @@ -1830,13 +1754,9 @@ async function setupMultiView() { else if (state.selectedContainers.length <= 6) columns = 3; else columns = 4; - console.log(`setupMultiView: Creating grid with ${columns} columns for ${state.selectedContainers.length} containers`); gridContainer.style.gridTemplateColumns = `repeat(${columns}, 1fr)`; - console.log(`setupMultiView: Grid template columns set to: repeat(${columns}, 1fr)`); // Создаем панели для каждого выбранного контейнера - console.log(`setupMultiView: Creating panels for ${state.selectedContainers.length} containers:`, state.selectedContainers); - console.log(`setupMultiView: Available services:`, state.services.map(s => ({ id: s.id, name: s.name }))); state.selectedContainers.forEach((containerId, index) => { const service = state.services.find(s => s.id === containerId); @@ -1846,24 +1766,12 @@ async function setupMultiView() { return; } - console.log(`setupMultiView: Creating panel ${index + 1} for service: ${service.name} (${containerId})`); const panel = createMultiViewPanel(service); gridContainer.appendChild(panel); - console.log(`setupMultiView: Panel ${index + 1} added to grid, total children: ${gridContainer.children.length}`); }); if (logContent) { logContent.appendChild(gridContainer); - console.log(`setupMultiView: Grid added to log content, grid children: ${gridContainer.children.length}`); - - // Проверяем, что все панели созданы правильно - const panels = gridContainer.querySelectorAll('.multi-view-panel'); - console.log(`setupMultiView: Total panels found in grid: ${panels.length}`); - panels.forEach((panel, index) => { - const containerId = panel.getAttribute('data-container-id'); - const title = panel.querySelector('.multi-view-title'); - console.log(`setupMultiView: Panel ${index + 1}: containerId=${containerId}, title="${title?.textContent}"`); - }); } else { console.error('setupMultiView: logContent not found'); } @@ -1879,36 +1787,27 @@ async function setupMultiView() { updateLogStyles(); // Дополнительная проверка для multi-view логов - console.log('setupMultiView: Force fixing multi-view styles'); forceFixMultiViewStyles(); }, 200); // Подключаем WebSocket для каждого контейнера - console.log(`setupMultiView: Setting up WebSockets for ${state.selectedContainers.length} containers`); state.selectedContainers.forEach((containerId, index) => { const service = state.services.find(s => s.id === containerId); if (service) { - console.log(`setupMultiView: Setting up WebSocket ${index + 1} for multi-view container: ${service.name} (${containerId})`); openMultiViewWs(service); } else { console.error(`setupMultiView: Service not found for container ID: ${containerId}`); } }); - console.log(`setupMultiView: Multi-view setup completed for ${state.selectedContainers.length} containers`); + // Multi-view setup completed // Применяем сохраненный порядок панелей setTimeout(() => { - console.log('setupMultiView: Starting panel order restoration...'); - console.log('setupMultiView: Current selectedContainers:', state.selectedContainers); - console.log('setupMultiView: Current multiViewMode:', state.multiViewMode); - // Сначала очищаем дубликаты, если они есть cleanupDuplicatePanels(); // Затем применяем порядок applyPanelOrder(); - - console.log('setupMultiView: Panel order restoration completed'); }, 100); // Небольшая задержка для завершения создания панелей // Обновляем счетчики для multi view @@ -1936,7 +1835,6 @@ function createMultiViewPanel(service) { const panel = document.createElement('div'); panel.className = 'multi-view-panel'; panel.setAttribute('data-container-id', service.id); - console.log(`createMultiViewPanel: Panel element created with data-container-id: ${service.id}`); panel.innerHTML = `