feat: Добавлено сохранение состояния кнопок loglevels в localStorage

- Добавлены функции saveLogLevelsState() и getLogLevelsStateFromStorage()
- Сохранение состояния при кликах по кнопкам loglevels
- Восстановление состояния при инициализации кнопок
- Сохранение настроек для Single View и Multi View режимов
- Индивидуальные настройки фильтрации для каждого контейнера
- Автоматическое восстановление настроек после обновления страницы

Автор: Сергей Антропов
Сайт: https://devops.org.ru
This commit is contained in:
Sergey Antropoff 2025-08-18 16:27:09 +03:00
parent e5b0c3f553
commit 36569c79f0

View File

@ -101,21 +101,65 @@ a{color:var(--link)}
.sidebar.collapsed ~ .main-content .header .header-filter {
flex: 1;
max-width: 200px;
margin: 0;
max-width: none;
margin: 0 8px;
min-width: 150px;
}
.sidebar.collapsed ~ .main-content .header .header-controls { display: flex; align-items: center; gap: 8px; margin-left: auto; }
/* Скрыть тему в компактном header */
.sidebar.collapsed ~ .main-content .header .theme-toggle { display: none; }
/* Скрыть кнопки loglevels в компактном header */
.sidebar.collapsed ~ .main-content .header .header-compact-controls { display: none; }
/* Скрыть весь log-header в компактном режиме */
.sidebar.collapsed ~ .main-content .log-header { display: none; }
/* Скрыть весь log-header */
.log-header { display: none; }
/* Минимальный padding для log-content в свернутом состоянии */
.sidebar.collapsed ~ .main-content .log-content {
padding: 2px;
padding: 0;
}
/* Обеспечиваем правильное отображение логов при свернутом sidebar */
.sidebar.collapsed ~ .main-content .log-area {
height: calc(100vh - var(--header-height)) !important;
overflow: hidden !important;
}
.sidebar.collapsed ~ .main-content .log-content {
overflow: visible !important;
height: 100% !important;
padding: 0 !important;
}
.sidebar.collapsed ~ .main-content .single-view-content .log,
.sidebar.collapsed ~ .main-content .multi-view-log {
height: calc(100vh - var(--header-height)) !important;
overflow: auto !important;
display: block !important;
max-height: none !important;
min-height: 200px !important;
position: relative !important;
}
/* Обеспечиваем правильное отображение логов при развернутом sidebar */
.sidebar:not(.collapsed) ~ .main-content .single-view-content .log,
.sidebar:not(.collapsed) ~ .main-content .multi-view-log {
height: 100% !important;
overflow: auto !important;
display: block !important;
min-height: 200px !important;
position: relative !important;
}
/* Стили для multi-view-content контейнеров */
.sidebar.collapsed ~ .main-content .multi-view-content {
height: calc(100vh - var(--header-height) - 60px) !important;
overflow: hidden !important;
}
.sidebar:not(.collapsed) ~ .main-content .multi-view-content {
height: 100% !important;
overflow: hidden !important;
}
/* Multi-view panel для Single View режима */
@ -236,64 +280,12 @@ a{color:var(--link)}
.sidebar:not(.collapsed) .help-btn { display: none; }
.sidebar.collapsed .help-btn { display: flex; }
/* Компактные контролы в header: по умолчанию скрыты, видны в свернутом режиме */
/* Компактные контролы в header: по умолчанию скрыты */
.header-compact-controls { display: none; align-items: center; gap: 6px; }
.sidebar.collapsed ~ .main-content .header .header-compact-controls { display: flex; }
/* Стили для компактных кнопок в header - такие же как в развернутом состоянии */
.sidebar.collapsed ~ .main-content .header .counter-btn {
display: inline-flex;
flex-direction: row;
align-items: center;
justify-content: center;
padding: 6px 12px;
margin: 0 4px;
border: none;
border-radius: 6px;
font-size: 11px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s ease;
min-width: 70px;
}
.sidebar.collapsed ~ .main-content .header .counter-btn:hover {
transform: translateY(-1px);
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
}
.sidebar.collapsed ~ .main-content .header .counter-label {
font-size: 10px;
opacity: 0.9;
margin-right: 4px;
}
.sidebar.collapsed ~ .main-content .header .counter-value {
font-size: 12px;
font-weight: bold;
}
.sidebar.collapsed ~ .main-content .header .log-refresh-btn {
background: var(--accent);
color: white;
border: none;
border-radius: 6px;
transition: all 0.2s ease;
padding: 6px 24px;
font-size: 11px;
font-weight: 500;
display: inline-flex;
align-items: center;
justify-content: center;
height: fit-content;
}
.sidebar.collapsed ~ .main-content .header .log-refresh-btn:hover {
background: var(--accent);
opacity: 0.8;
transform: translateY(-1px);
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
}
.options-btn:hover,
.help-btn:hover,
@ -668,13 +660,13 @@ a{color:var(--link)}
}
/* Стили для кнопки refresh */
#logRefreshBtn {
.log-refresh-btn {
background: var(--accent);
color: white;
border: none;
border-radius: 6px;
transition: all 0.2s ease;
padding: 6px 24px; /* Увеличиваем ширину в 2 раза */
padding: 6px 24px;
font-size: 11px;
font-weight: 500;
display: inline-flex;
@ -683,7 +675,7 @@ a{color:var(--link)}
height: fit-content;
}
#logRefreshBtn:hover {
.log-refresh-btn:hover {
background: var(--accent);
opacity: 0.8;
transform: translateY(-1px);
@ -737,48 +729,49 @@ a{color:var(--link)}
/* Цвета для разных уровней логов */
.debug-btn {
background: #6c757d;
background: #6c757d; /* Серый */
color: white;
}
.debug-btn:hover {
background: #5a6268;
background: #5a6268; /* Затемненный серый */
}
.info-btn {
background: #17a2b8;
background: var(--ok); /* Зеленый цвет */
color: white;
border: 1px solid var(--ok);
}
.info-btn:hover {
background: #138496;
background: #7ea855; /* Затемненный зеленый */
}
.warn-btn {
background: #ffc107;
background: #ffc107; /* Оставляем текущий цвет */
color: #212529;
}
.warn-btn:hover {
background: #e0a800;
background: #e0a800; /* Затемненный желтый */
}
.error-btn {
background: #dc3545;
background: #dc3545; /* Оставляем текущий цвет */
color: white;
}
.error-btn:hover {
background: #c82333;
background: #c82333; /* Затемненный красный */
}
.other-btn {
background: #6c757d;
color: white;
background: #f8f9fa; /* Самый светлый серый */
color: #212529;
}
.other-btn:hover {
background: #5a6268;
background: #dee2e6; /* Затемненный серый */
}
.btn-group {
@ -815,6 +808,7 @@ a{color:var(--link)}
align-items: center;
padding: 0 20px;
gap: 16px;
flex-wrap: wrap;
}
.header-title {
@ -947,7 +941,7 @@ a{color:var(--link)}
.header-filter {
flex: 1;
min-width: 200px;
max-width: 400px;
max-width: none;
padding: 8px 12px;
border: 1px solid var(--border);
border-radius: 6px;
@ -956,6 +950,7 @@ a{color:var(--link)}
font-size: 12px;
transition: border-color 0.2s ease;
margin: 0 16px;
width: 100%;
}
.header-filter:focus {
@ -967,6 +962,64 @@ a{color:var(--link)}
color: var(--muted);
}
/* Адаптивность для header-filter */
@media (max-width: 1200px) {
.header-filter {
min-width: 150px;
margin: 0 12px;
}
.sidebar.collapsed ~ .main-content .header .header-filter {
min-width: 120px;
margin: 0 6px;
}
}
@media (max-width: 768px) {
.header-filter {
min-width: 120px;
margin: 0 8px;
padding: 6px 10px;
font-size: 11px;
}
.header {
gap: 12px;
padding: 0 16px;
}
.sidebar.collapsed ~ .main-content .header .header-filter {
min-width: 100px;
margin: 0 4px;
padding: 4px 8px;
font-size: 10px;
}
}
@media (max-width: 480px) {
.header-filter {
min-width: 100px;
margin: 0 4px;
padding: 4px 8px;
font-size: 10px;
}
.header {
gap: 8px;
padding: 0 12px;
}
.header-controls {
gap: 8px;
}
/* Адаптивность для свернутого sidebar */
.sidebar.collapsed ~ .main-content .header .header-filter {
min-width: 100px;
margin: 0 4px;
}
}
.header-project-select {
padding: 6px 12px;
border: 1px solid var(--border);
@ -1304,58 +1357,58 @@ a{color:var(--link)}
/* Цвета для разных уровней */
.level-btn.debug-btn {
border-color: var(--ok);
color: var(--ok);
border-color: #6c757d; /* Серый */
color: #6c757d;
}
.level-btn.debug-btn:hover,
.level-btn.debug-btn.active {
background: var(--ok);
color: #0b0d12;
background: #5a6268; /* Затемненный серый */
color: white;
}
.level-btn.info-btn {
border-color: var(--accent);
color: var(--accent);
border-color: var(--ok); /* Зеленый цвет */
color: var(--ok);
}
.level-btn.info-btn:hover,
.level-btn.info-btn.active {
background: var(--accent);
color: #0b0d12;
background: #7ea855; /* Затемненный зеленый */
color: white;
}
.level-btn.warn-btn {
border-color: var(--warn);
border-color: var(--warn); /* Оставляем текущий цвет */
color: var(--warn);
}
.level-btn.warn-btn:hover,
.level-btn.warn-btn.active {
background: var(--warn);
background: #e0a800; /* Затемненный желтый */
color: #0b0d12;
}
.level-btn.error-btn {
border-color: var(--err);
border-color: var(--err); /* Оставляем текущий цвет */
color: var(--err);
}
.level-btn.error-btn:hover,
.level-btn.error-btn.active {
background: var(--err);
color: #0b0d12;
background: #c82333; /* Затемненный красный */
color: white;
}
.level-btn.other-btn {
border-color: var(--muted);
color: var(--muted);
border-color: #f8f9fa; /* Самый светлый серый */
color: #f8f9fa;
}
.level-btn.other-btn:hover,
.level-btn.other-btn.active {
background: var(--muted);
color: #0b0d12;
background: #dee2e6; /* Затемненный серый */
color: #212529;
}
.single-view-content {
@ -1426,7 +1479,7 @@ a{color:var(--link)}
.log-content {
flex: 1;
overflow: hidden;
padding: 16px;
padding: 0;
background: var(--bg);
}
@ -1918,10 +1971,10 @@ footer{position:fixed;right:10px;bottom:10px;opacity:.6;font-size:11px}
<span class="counter-label">OTHER</span>
<span class="counter-value cother">0</span>
</button>
<button class="btn btn-small log-refresh-btn" title="Обновить логи и счетчики">
<i class="fas fa-sync-alt"></i> Refresh
</button>
</div>
<button class="btn btn-small log-refresh-btn" title="Обновить логи и счетчики">
<i class="fas fa-sync-alt"></i> Refresh
</button>
<div class="theme-toggle">
<span>Theme</span>
<input id="themeSwitch" type="checkbox" />
@ -1932,34 +1985,6 @@ footer{position:fixed;right:10px;bottom:10px;opacity:.6;font-size:11px}
<!-- Log Area -->
<div class="log-area">
<div class="log-header">
<h3 class="log-title" id="logTitle">Select a container to view logs</h3>
<div class="log-controls">
<button class="counter-btn debug-btn" title="DEBUG">
<span class="counter-label">DEBUG</span>
<span class="counter-value cdbg">0</span>
</button>
<button class="counter-btn info-btn" title="INFO">
<span class="counter-label">INFO</span>
<span class="counter-value cinfo">0</span>
</button>
<button class="counter-btn warn-btn" title="WARN">
<span class="counter-label">WARN</span>
<span class="counter-value cwarn">0</span>
</button>
<button class="counter-btn error-btn" title="ERROR">
<span class="counter-label">ERROR</span>
<span class="counter-value cerr">0</span>
</button>
<button class="counter-btn other-btn" title="OTHER">
<span class="counter-label">OTHER</span>
<span class="counter-value cother">0</span>
</button>
<button id="logRefreshBtn" class="btn btn-small" title="Обновить логи и счетчики">
<i class="fas fa-sync-alt"></i> Refresh
</button>
</div>
</div>
<!-- Multi-view panel для Single View режима -->
<div class="multi-view-panel" id="multiViewPanel">
@ -2051,13 +2076,11 @@ const els = {
// New modern elements
containerList: document.getElementById('containerList'),
logTitle: document.getElementById('logTitle'),
logContent: document.getElementById('logContent'),
mobileToggle: document.getElementById('mobileToggle'),
optionsBtn: document.getElementById('optionsBtn'),
helpBtn: document.getElementById('helpBtn'),
logoutBtn: document.getElementById('logoutBtn'),
logRefreshBtn: document.getElementById('logRefreshBtn'),
sidebar: document.getElementById('sidebar'),
sidebarToggle: document.getElementById('sidebarToggle'),
header: document.getElementById('header'),
@ -2400,6 +2423,22 @@ function updateHeaderCounters(containerId, counters) {
// Функция для инициализации состояния кнопок уровней логирования
function initializeLevelButtons() {
// Восстанавливаем состояние кнопок loglevels из localStorage
const savedLevelsState = getLogLevelsStateFromStorage();
if (savedLevelsState) {
console.log('Restoring log levels state from localStorage');
// Восстанавливаем глобальные настройки для single-view
if (savedLevelsState.globalLevels) {
state.levels = { ...state.levels, ...savedLevelsState.globalLevels };
}
// Восстанавливаем настройки контейнеров для multi-view
if (savedLevelsState.containerLevels) {
state.containerLevels = { ...state.containerLevels, ...savedLevelsState.containerLevels };
}
}
// Инициализируем кнопки для single-view
const singleLevelBtns = document.querySelectorAll('.single-view-levels .level-btn');
singleLevelBtns.forEach(btn => {
@ -2430,6 +2469,12 @@ function initializeLevelButtons() {
btn.classList.toggle('active', isActive);
btn.classList.toggle('disabled', !isActive);
});
// Обновляем стили логов после инициализации кнопок
updateLogStyles();
// Применяем настройки wrap text
applyWrapSettings();
}
function applyFilter(line){
if(!state.filter) return true;
@ -2925,9 +2970,6 @@ function updateContainerSelectionUI() {
}
});
// Обновляем заголовок
updateLogTitle();
// Обновляем single-view-title если он существует
const singleViewTitle = document.getElementById('singleViewTitle');
if (singleViewTitle && state.selectedContainers.length === 1) {
@ -2973,6 +3015,58 @@ function getSelectedContainerFromStorage() {
return containerId;
}
// Функция для сохранения режима просмотра в localStorage
function saveViewMode(multiViewMode, selectedContainers) {
const viewModeData = {
multiViewMode: multiViewMode,
selectedContainers: selectedContainers || []
};
localStorage.setItem('lb_view_mode', JSON.stringify(viewModeData));
console.log('Saved view mode to localStorage:', viewModeData);
}
// Функция для восстановления режима просмотра из localStorage
function getViewModeFromStorage() {
const viewModeData = localStorage.getItem('lb_view_mode');
if (viewModeData) {
try {
const data = JSON.parse(viewModeData);
console.log('Retrieved view mode from localStorage:', data);
return data;
} catch (error) {
console.error('Error parsing view mode from localStorage:', error);
return null;
}
}
return null;
}
// Функция для сохранения состояния кнопок loglevels в localStorage
function saveLogLevelsState() {
const levelsData = {
globalLevels: state.levels,
containerLevels: state.containerLevels
};
localStorage.setItem('lb_log_levels', JSON.stringify(levelsData));
console.log('Saved log levels state to localStorage:', levelsData);
}
// Функция для восстановления состояния кнопок loglevels из localStorage
function getLogLevelsStateFromStorage() {
const levelsData = localStorage.getItem('lb_log_levels');
if (levelsData) {
try {
const data = JSON.parse(levelsData);
console.log('Retrieved log levels state from localStorage:', data);
return data;
} catch (error) {
console.error('Error parsing log levels state from localStorage:', error);
return null;
}
}
return null;
}
async function updateMultiViewMode() {
console.log(`updateMultiViewMode called: selectedContainers.length = ${state.selectedContainers.length}, containers:`, state.selectedContainers);
@ -2980,6 +3074,10 @@ async function updateMultiViewMode() {
state.multiViewMode = true;
state.current = null; // Сбрасываем текущий контейнер
console.log('Setting up multi-view mode');
// Сохраняем режим просмотра в localStorage
saveViewMode(true, state.selectedContainers);
await setupMultiView();
} else if (state.selectedContainers.length === 1) {
// Переключаемся в single view для одного контейнера
@ -2990,6 +3088,9 @@ async function updateMultiViewMode() {
console.log('Switching to single view for:', selectedService.name);
console.log('updateMultiViewMode: About to call switchToSingle - VERSION 2');
// Сохраняем режим просмотра в localStorage
saveViewMode(false, [selectedService.id]);
// Сохраняем выбранный контейнер в localStorage
saveSelectedContainer(selectedService.id);
@ -3002,6 +3103,10 @@ async function updateMultiViewMode() {
// Когда снимаем все галочки, переключаемся в single view
state.multiViewMode = false;
state.current = null;
// Сохраняем режим просмотра в localStorage
saveViewMode(false, []);
clearLogArea();
// Очищаем область логов и показываем пустое состояние
@ -3013,14 +3118,14 @@ async function updateMultiViewMode() {
}
}
// Обновляем заголовок
if (els.logTitle) {
els.logTitle.textContent = 'LogBoard+';
}
}
console.log(`Multi-view mode updated: multiViewMode = ${state.multiViewMode}`);
// Сохраняем состояние кнопок loglevels при переключении режимов
saveLogLevelsState();
// Обновляем состояние кнопок уровней логирования при переключении режимов
setTimeout(() => {
initializeLevelButtons();
@ -3140,6 +3245,11 @@ async function setupMultiView() {
setTimeout(() => {
recalculateMultiViewCounters();
}, 1000); // Небольшая задержка для завершения загрузки логов
// Применяем стили логов после настройки multi view
setTimeout(() => {
updateLogStyles();
}, 1500); // Задержка после настройки счетчиков
}
function createMultiViewPanel(service) {
@ -3217,6 +3327,12 @@ function createMultiViewPanel(service) {
}, 100);
console.log(`Multi-view panel created for ${service.name}`);
// Применяем стили к новой панели
setTimeout(() => {
updateLogStyles();
}, 200);
return panel;
}
@ -3355,46 +3471,7 @@ function clearLogArea() {
}
}
function updateLogTitle() {
const logTitle = document.getElementById('logTitle');
const multiViewPanelTitle = document.getElementById('multiViewPanelTitle');
const singleViewTitle = document.getElementById('singleViewTitle');
if (!logTitle) return;
console.log('updateLogTitle called, selected containers:', state.selectedContainers.length);
if (state.selectedContainers.length === 0) {
logTitle.textContent = 'LogBoard+';
if (multiViewPanelTitle) {
multiViewPanelTitle.textContent = 'LogBoard+';
}
if (singleViewTitle) {
singleViewTitle.textContent = 'No container selected';
}
console.log('Log title set to: LogBoard+');
} else if (state.selectedContainers.length === 1) {
const service = state.services.find(s => s.id === state.selectedContainers[0]);
if (service) {
logTitle.textContent = `${service.name} (${service.service || service.name})`;
if (multiViewPanelTitle) {
multiViewPanelTitle.textContent = `${service.name} (${service.service || service.name})`;
}
if (singleViewTitle) {
singleViewTitle.textContent = `${service.name} (${service.service || service.name})`;
}
console.log('Log title set to single container:', service.name);
}
} else {
logTitle.textContent = `Multi-view: ${state.selectedContainers.length} containers`;
if (multiViewPanelTitle) {
multiViewPanelTitle.textContent = `Multi-view: ${state.selectedContainers.length} containers`;
}
if (singleViewTitle) {
singleViewTitle.textContent = `Multi-view: ${state.selectedContainers.length} containers`;
}
console.log('Log title set to multi-view:', state.selectedContainers.length, 'containers');
}
}
function applyWrapSettings() {
const wrapEnabled = els.wrapToggle && els.wrapToggle.checked;
@ -3455,13 +3532,52 @@ async function fetchServices(){
buildTabs();
// Проверяем, находимся ли мы в Multi View режиме
if (state.multiViewMode && state.selectedContainers.length > 0) {
// В Multi View режиме не переключаемся в single view
console.log('fetchServices: Staying in Multi View mode');
} else if (!state.current && state.services.length) {
// Только если не в Multi View режиме и нет текущего контейнера
await switchToSingle(state.services[0]);
// Восстанавливаем режим просмотра из localStorage
const savedViewMode = getViewModeFromStorage();
if (savedViewMode) {
console.log('Restoring view mode from localStorage:', savedViewMode);
if (savedViewMode.multiViewMode && savedViewMode.selectedContainers.length > 1) {
// Восстанавливаем Multi View режим
console.log('Restoring Multi View mode with containers:', savedViewMode.selectedContainers);
state.multiViewMode = true;
state.selectedContainers = savedViewMode.selectedContainers;
// Отмечаем чекбоксы для выбранных контейнеров
savedViewMode.selectedContainers.forEach(containerId => {
const checkbox = document.querySelector(`.container-checkbox[data-container-id="${containerId}"]`);
if (checkbox) {
checkbox.checked = true;
const containerItem = checkbox.closest('.container-item');
if (containerItem) {
containerItem.classList.add('selected');
}
}
});
// Настраиваем Multi View
await setupMultiView();
} else if (savedViewMode.selectedContainers.length === 1) {
// Восстанавливаем Single View режим
console.log('Restoring Single View mode for container:', savedViewMode.selectedContainers[0]);
state.multiViewMode = false;
const selectedService = state.services.find(s => s.id === savedViewMode.selectedContainers[0]);
if (selectedService) {
await switchToSingle(selectedService);
}
} else {
// Нет сохраненного режима, используем первый контейнер
console.log('No saved view mode, using first container');
if (state.services.length) {
await switchToSingle(state.services[0]);
}
}
} else {
// Нет сохраненного режима, используем первый контейнер
console.log('No saved view mode found, using first container');
if (state.services.length) {
await switchToSingle(state.services[0]);
}
}
// Добавляем обработчики для счетчиков после загрузки сервисов
@ -4628,10 +4744,13 @@ async function switchToSingle(svc){
// Обновляем состояние выбранных контейнеров для корректного отображения заголовка
state.selectedContainers = [svc.id];
// Modern interface updates
if (els.logTitle) {
els.logTitle.textContent = `${svc.name} (${svc.service || svc.name})`;
}
// Сохраняем режим просмотра в localStorage
saveViewMode(false, [svc.id]);
// Сохраняем состояние кнопок loglevels в localStorage
saveLogLevelsState();
if (els.multiViewPanelTitle) {
els.multiViewPanelTitle.textContent = `${svc.name} (${svc.service || svc.name})`;
}
@ -4681,9 +4800,6 @@ async function switchToSingle(svc){
// Обновляем состояние чекбоксов после переключения контейнера
updateContainerSelectionUI();
// Обновляем заголовок
updateLogTitle();
// Обновляем счетчики для нового контейнера
setTimeout(() => {
recalculateCounters();
@ -5220,7 +5336,7 @@ els.refreshBtn.onclick = async () => {
};
// Обработчик для кнопок refresh логов (в log-header и в header)
document.querySelectorAll('#logRefreshBtn, .header-compact-controls .log-refresh-btn').forEach(btn=>{
document.querySelectorAll('.log-refresh-btn').forEach(btn=>{
btn.addEventListener('click', refreshLogsAndCounters);
});
@ -5417,6 +5533,104 @@ function toggleSidebar() {
els.sidebarToggle.title = 'Развернуть панель (Ctrl+B / Ctrl+И)';
localStorage.setItem('lb_sidebar_collapsed', 'true');
}
// Принудительно обновляем стили логов после переключения sidebar
setTimeout(() => {
updateLogStyles();
}, 100);
}
}
// Функция для обновления стилей логов
function updateLogStyles() {
const isCollapsed = els.sidebar && els.sidebar.classList.contains('collapsed');
// Обновляем стили для single-view логов
const singleViewLogs = document.querySelectorAll('.single-view-content .log');
singleViewLogs.forEach(log => {
if (isCollapsed) {
log.style.height = 'calc(100vh - var(--header-height))';
log.style.overflow = 'auto';
} else {
log.style.height = '100%';
log.style.overflow = 'auto';
}
});
// Обновляем стили для multi-view логов (более агрессивно)
const multiViewLogs = document.querySelectorAll('.multi-view-log');
console.log(`Found ${multiViewLogs.length} multi-view logs to update`);
multiViewLogs.forEach((log, index) => {
const containerId = log.getAttribute('data-container-id');
console.log(`Updating multi-view log ${index + 1}/${multiViewLogs.length} for container: ${containerId}`);
if (isCollapsed) {
log.style.height = 'calc(100vh - var(--header-height))';
log.style.overflow = 'auto';
log.style.maxHeight = 'none';
log.style.display = 'block';
log.style.minHeight = '200px';
log.style.position = 'relative';
} else {
log.style.height = '100%';
log.style.overflow = 'auto';
log.style.maxHeight = 'none';
log.style.display = 'block';
log.style.minHeight = '200px';
log.style.position = 'relative';
}
// Принудительно вызываем пересчет layout
log.style.transform = 'translateZ(0)';
});
// Также обновляем стили для multi-view-content контейнеров
const multiViewContents = document.querySelectorAll('.multi-view-content');
multiViewContents.forEach(content => {
if (isCollapsed) {
content.style.height = 'calc(100vh - var(--header-height) - 60px)';
content.style.overflow = 'hidden';
} else {
content.style.height = '100%';
content.style.overflow = 'hidden';
}
});
// Применяем настройки wrap text
applyWrapSettings();
console.log('Log styles updated, sidebar collapsed:', isCollapsed, 'multi-view logs found:', multiViewLogs.length);
// Дополнительная проверка через 500ms для multi view логов
if (multiViewLogs.length > 0) {
setTimeout(() => {
console.log('Performing delayed update for multi-view logs...');
const delayedLogs = document.querySelectorAll('.multi-view-log');
delayedLogs.forEach((log, index) => {
const containerId = log.getAttribute('data-container-id');
console.log(`Delayed update for multi-view log ${index + 1}/${delayedLogs.length} for container: ${containerId}`);
if (isCollapsed) {
log.style.height = 'calc(100vh - var(--header-height))';
log.style.overflow = 'auto';
log.style.maxHeight = 'none';
log.style.display = 'block';
log.style.minHeight = '200px';
log.style.position = 'relative';
} else {
log.style.height = '100%';
log.style.overflow = 'auto';
log.style.maxHeight = 'none';
log.style.display = 'block';
log.style.minHeight = '200px';
log.style.position = 'relative';
}
// Принудительно вызываем пересчет layout
log.style.transform = 'translateZ(0)';
});
}, 500);
}
}
@ -5593,6 +5807,11 @@ document.addEventListener('DOMContentLoaded', () => {
});
}
// Инициализируем стили логов при загрузке страницы
updateLogStyles();
// Применяем настройки wrap text при загрузке
applyWrapSettings();
});
if (els.snapshotBtn) {
@ -5873,7 +6092,7 @@ window.addEventListener('keydown', async (e)=>{
console.log('Elements found:', {
containerList: !!els.containerList,
logTitle: !!els.logTitle,
logContent: !!els.logContent,
mobileToggle: !!els.mobileToggle,
themeSwitch: !!els.themeSwitch
@ -6061,6 +6280,9 @@ window.addEventListener('keydown', async (e)=>{
}
state.containerLevels[containerId][level] = !isActive;
// Сохраняем состояние кнопок loglevels в localStorage
saveLogLevelsState();
// Обновляем видимость логов только для этого контейнера
updateContainerLogVisibility(containerId);
@ -6080,6 +6302,9 @@ window.addEventListener('keydown', async (e)=>{
// Для single-view: глобальные настройки
state.levels[level] = !isActive;
// Сохраняем состояние кнопок loglevels в localStorage
saveLogLevelsState();
// Обновляем видимость логов только для текущего контейнера
if (state.current) {
updateLogVisibility(els.logContent);