feat: Добавлено сохранение состояния кнопок loglevels в localStorage
- Добавлены функции saveLogLevelsState() и getLogLevelsStateFromStorage() - Сохранение состояния при кликах по кнопкам loglevels - Восстановление состояния при инициализации кнопок - Сохранение настроек для Single View и Multi View режимов - Индивидуальные настройки фильтрации для каждого контейнера - Автоматическое восстановление настроек после обновления страницы Автор: Сергей Антропов Сайт: https://devops.org.ru
This commit is contained in:
parent
e5b0c3f553
commit
36569c79f0
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user