feat: Добавлены кнопки уровней логирования в заголовки single-view и multi-view
- Добавлены кнопки LogLevels в заголовки контейнеров - Реализована индивидуальная фильтрация логов для каждого контейнера в multi-view режиме - Исправлена проблема с потерей цветов при фильтрации логов - Добавлена статистика по уровням логирования для каждого контейнера - Кнопки показывают количество логов каждого уровня - Каждый контейнер может иметь свои настройки фильтрации уровней - Сохранена цветовая схема при переключении уровней логирования
This commit is contained in:
parent
7007d4be74
commit
e5b0c3f553
@ -1244,6 +1244,120 @@ a{color:var(--link)}
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* Кнопки уровней логирования для заголовков */
|
||||
.single-view-levels,
|
||||
.multi-view-levels {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.level-btn {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
padding: 4px 6px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 4px;
|
||||
background: var(--panel);
|
||||
color: var(--fg);
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
font-size: 10px;
|
||||
min-width: 40px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.level-btn:hover {
|
||||
background: var(--chip);
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
.level-btn.active {
|
||||
background: var(--accent);
|
||||
color: #0b0d12;
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
.level-btn.disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.level-btn.disabled:hover {
|
||||
background: var(--panel);
|
||||
border-color: var(--border);
|
||||
}
|
||||
|
||||
.level-label {
|
||||
font-weight: 500;
|
||||
font-size: 9px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.level-value {
|
||||
font-weight: 600;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
/* Цвета для разных уровней */
|
||||
.level-btn.debug-btn {
|
||||
border-color: var(--ok);
|
||||
color: var(--ok);
|
||||
}
|
||||
|
||||
.level-btn.debug-btn:hover,
|
||||
.level-btn.debug-btn.active {
|
||||
background: var(--ok);
|
||||
color: #0b0d12;
|
||||
}
|
||||
|
||||
.level-btn.info-btn {
|
||||
border-color: var(--accent);
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.level-btn.info-btn:hover,
|
||||
.level-btn.info-btn.active {
|
||||
background: var(--accent);
|
||||
color: #0b0d12;
|
||||
}
|
||||
|
||||
.level-btn.warn-btn {
|
||||
border-color: var(--warn);
|
||||
color: var(--warn);
|
||||
}
|
||||
|
||||
.level-btn.warn-btn:hover,
|
||||
.level-btn.warn-btn.active {
|
||||
background: var(--warn);
|
||||
color: #0b0d12;
|
||||
}
|
||||
|
||||
.level-btn.error-btn {
|
||||
border-color: var(--err);
|
||||
color: var(--err);
|
||||
}
|
||||
|
||||
.level-btn.error-btn:hover,
|
||||
.level-btn.error-btn.active {
|
||||
background: var(--err);
|
||||
color: #0b0d12;
|
||||
}
|
||||
|
||||
.level-btn.other-btn {
|
||||
border-color: var(--muted);
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.level-btn.other-btn:hover,
|
||||
.level-btn.other-btn.active {
|
||||
background: var(--muted);
|
||||
color: #0b0d12;
|
||||
}
|
||||
|
||||
.single-view-content {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
@ -1856,6 +1970,28 @@ footer{position:fixed;right:10px;bottom:10px;opacity:.6;font-size:11px}
|
||||
<div class="single-view-panel" id="singleViewPanel">
|
||||
<div class="single-view-header">
|
||||
<h4 class="single-view-title" id="singleViewTitle">No container selected</h4>
|
||||
<div class="single-view-levels">
|
||||
<button class="level-btn debug-btn" data-level="debug" title="DEBUG">
|
||||
<span class="level-label">DEBUG</span>
|
||||
<span class="level-value" data-container="single">0</span>
|
||||
</button>
|
||||
<button class="level-btn info-btn" data-level="info" title="INFO">
|
||||
<span class="level-label">INFO</span>
|
||||
<span class="level-value" data-container="single">0</span>
|
||||
</button>
|
||||
<button class="level-btn warn-btn" data-level="warn" title="WARN">
|
||||
<span class="level-label">WARN</span>
|
||||
<span class="level-value" data-container="single">0</span>
|
||||
</button>
|
||||
<button class="level-btn error-btn" data-level="err" title="ERROR">
|
||||
<span class="level-label">ERROR</span>
|
||||
<span class="level-value" data-container="single">0</span>
|
||||
</button>
|
||||
<button class="level-btn other-btn" data-level="other" title="OTHER">
|
||||
<span class="level-label">OTHER</span>
|
||||
<span class="level-value" data-container="single">0</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="single-view-content">
|
||||
<pre class="log" id="logContent">No container selected</pre>
|
||||
@ -2083,6 +2219,218 @@ function allowedByLevel(cls){
|
||||
if (cls==='other') return state.levels.other; // Показываем неклассифицированные строки в зависимости от настройки
|
||||
return true;
|
||||
}
|
||||
|
||||
// Функция для проверки уровня логирования для конкретного контейнера
|
||||
function allowedByContainerLevel(cls, containerId) {
|
||||
// Если настройки контейнера не инициализированы, инициализируем их
|
||||
if (!state.containerLevels) {
|
||||
state.containerLevels = {};
|
||||
}
|
||||
if (!state.containerLevels[containerId]) {
|
||||
state.containerLevels[containerId] = {debug: true, info: true, warn: true, err: true, other: true};
|
||||
}
|
||||
|
||||
const containerLevels = state.containerLevels[containerId];
|
||||
let result;
|
||||
|
||||
if (cls==='dbg') result = containerLevels.debug;
|
||||
else if (cls==='err') result = containerLevels.err;
|
||||
else if (cls==='warn') result = containerLevels.warn;
|
||||
else if (cls==='ok') result = containerLevels.info;
|
||||
else if (cls==='other') result = containerLevels.other;
|
||||
else result = true;
|
||||
|
||||
console.log(`allowedByContainerLevel: containerId=${containerId}, cls=${cls}, result=${result}, levels=`, containerLevels);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Функция для обновления видимости логов в Single View
|
||||
function updateLogVisibility(logElement) {
|
||||
if (!logElement || !state.current) return;
|
||||
|
||||
const containerId = state.current.id;
|
||||
const obj = state.open[containerId];
|
||||
if (!obj || !obj.allLogs) return;
|
||||
|
||||
// Пересоздаем содержимое лога с учетом фильтров, сохраняя HTML-разметку
|
||||
let visibleHtml = '';
|
||||
obj.allLogs.forEach(logEntry => {
|
||||
const shouldShow = allowedByLevel(logEntry.cls) && applyFilter(logEntry.line);
|
||||
if (shouldShow) {
|
||||
visibleHtml += logEntry.html + '\n';
|
||||
}
|
||||
});
|
||||
|
||||
logElement.innerHTML = visibleHtml;
|
||||
|
||||
// Обновляем счетчики
|
||||
recalculateCounters();
|
||||
|
||||
// Обновляем состояние кнопок уровней логирования только для single-view
|
||||
const singleLevelBtns = document.querySelectorAll('.single-view-levels .level-btn');
|
||||
singleLevelBtns.forEach(btn => {
|
||||
const level = btn.getAttribute('data-level');
|
||||
const isActive = state.levels[level];
|
||||
btn.classList.toggle('active', isActive);
|
||||
btn.classList.toggle('disabled', !isActive);
|
||||
});
|
||||
}
|
||||
|
||||
// Функция для обновления видимости логов конкретного контейнера в Multi View
|
||||
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;
|
||||
|
||||
const obj = state.open[containerId];
|
||||
if (!obj || !obj.allLogs) return;
|
||||
|
||||
// Пересоздаем содержимое лога с учетом фильтров контейнера, сохраняя HTML-разметку
|
||||
let visibleHtml = '';
|
||||
obj.allLogs.forEach(logEntry => {
|
||||
const shouldShow = allowedByContainerLevel(logEntry.cls, containerId) && applyFilter(logEntry.line);
|
||||
if (shouldShow) {
|
||||
visibleHtml += logEntry.html + '\n';
|
||||
}
|
||||
});
|
||||
|
||||
logElement.innerHTML = visibleHtml;
|
||||
|
||||
// Обновляем счетчики для этого контейнера
|
||||
updateContainerCounters(containerId);
|
||||
|
||||
// Обновляем состояние кнопок уровней логирования только для этого контейнера
|
||||
const levelBtns = document.querySelectorAll(`.level-btn[data-container-id="${containerId}"]`);
|
||||
levelBtns.forEach(btn => {
|
||||
const level = btn.getAttribute('data-level');
|
||||
// Используем настройки контейнера, если они есть
|
||||
const containerLevels = state.containerLevels && state.containerLevels[containerId] ?
|
||||
state.containerLevels[containerId] : {debug: true, info: true, warn: true, err: true, other: true};
|
||||
const isActive = containerLevels[level];
|
||||
btn.classList.toggle('active', isActive);
|
||||
btn.classList.toggle('disabled', !isActive);
|
||||
});
|
||||
}
|
||||
|
||||
// Функция для обновления счетчиков конкретного контейнера
|
||||
function updateContainerCounters(containerId) {
|
||||
const obj = state.open[containerId];
|
||||
if (!obj || !obj.allLogs) return;
|
||||
|
||||
// Получаем значение Tail Lines
|
||||
const tailLines = parseInt(els.tail.value) || 50;
|
||||
|
||||
// Берем только последние N логов
|
||||
const visibleLogs = obj.allLogs.slice(-tailLines);
|
||||
|
||||
// Сбрасываем счетчики
|
||||
obj.counters = {dbg: 0, info: 0, warn: 0, err: 0, other: 0};
|
||||
|
||||
// Пересчитываем счетчики только для отображаемых логов
|
||||
visibleLogs.forEach(logEntry => {
|
||||
const shouldShow = allowedByContainerLevel(logEntry.cls, containerId) && applyFilter(logEntry.line);
|
||||
if (shouldShow) {
|
||||
if (logEntry.cls === 'dbg') obj.counters.dbg++;
|
||||
if (logEntry.cls === 'ok') obj.counters.info++;
|
||||
if (logEntry.cls === 'warn') obj.counters.warn++;
|
||||
if (logEntry.cls === 'err') obj.counters.err++;
|
||||
if (logEntry.cls === 'other') obj.counters.other++;
|
||||
}
|
||||
});
|
||||
|
||||
// Обновляем отображение счетчиков в кнопках заголовка
|
||||
const levelBtns = document.querySelectorAll(`.level-btn[data-container-id="${containerId}"]`);
|
||||
levelBtns.forEach(btn => {
|
||||
const level = btn.getAttribute('data-level');
|
||||
const valueEl = btn.querySelector('.level-value');
|
||||
if (valueEl) {
|
||||
switch (level) {
|
||||
case 'debug': valueEl.textContent = obj.counters.dbg; break;
|
||||
case 'info': valueEl.textContent = obj.counters.info; break;
|
||||
case 'warn': valueEl.textContent = obj.counters.warn; break;
|
||||
case 'err': valueEl.textContent = obj.counters.err; break;
|
||||
case 'other': valueEl.textContent = obj.counters.other; break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Функция для обновления счетчиков в кнопках заголовков
|
||||
function updateHeaderCounters(containerId, counters) {
|
||||
// Обновляем счетчики для single-view (если это текущий контейнер)
|
||||
if (state.current && state.current.id === containerId) {
|
||||
const singleLevelBtns = document.querySelectorAll('.single-view-levels .level-btn');
|
||||
singleLevelBtns.forEach(btn => {
|
||||
const level = btn.getAttribute('data-level');
|
||||
const valueEl = btn.querySelector('.level-value');
|
||||
if (valueEl) {
|
||||
switch (level) {
|
||||
case 'debug': valueEl.textContent = counters.dbg; break;
|
||||
case 'info': valueEl.textContent = counters.info; break;
|
||||
case 'warn': valueEl.textContent = counters.warn; break;
|
||||
case 'err': valueEl.textContent = counters.err; break;
|
||||
case 'other': valueEl.textContent = counters.other; break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Обновляем счетчики для multi-view (только для конкретного контейнера)
|
||||
if (state.multiViewMode && state.selectedContainers.includes(containerId)) {
|
||||
const multiLevelBtns = document.querySelectorAll(`.level-btn[data-container-id="${containerId}"]`);
|
||||
multiLevelBtns.forEach(btn => {
|
||||
const level = btn.getAttribute('data-level');
|
||||
const valueEl = btn.querySelector('.level-value');
|
||||
if (valueEl) {
|
||||
switch (level) {
|
||||
case 'debug': valueEl.textContent = counters.dbg; break;
|
||||
case 'info': valueEl.textContent = counters.info; break;
|
||||
case 'warn': valueEl.textContent = counters.warn; break;
|
||||
case 'err': valueEl.textContent = counters.err; break;
|
||||
case 'other': valueEl.textContent = counters.other; break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Функция для инициализации состояния кнопок уровней логирования
|
||||
function initializeLevelButtons() {
|
||||
// Инициализируем кнопки для single-view
|
||||
const singleLevelBtns = document.querySelectorAll('.single-view-levels .level-btn');
|
||||
singleLevelBtns.forEach(btn => {
|
||||
const level = btn.getAttribute('data-level');
|
||||
const isActive = state.levels[level];
|
||||
btn.classList.toggle('active', isActive);
|
||||
btn.classList.toggle('disabled', !isActive);
|
||||
});
|
||||
|
||||
// Инициализируем кнопки для multi-view (если есть)
|
||||
const multiLevelBtns = document.querySelectorAll('.multi-view-levels .level-btn');
|
||||
multiLevelBtns.forEach(btn => {
|
||||
const level = btn.getAttribute('data-level');
|
||||
const containerId = btn.getAttribute('data-container-id');
|
||||
|
||||
// Инициализируем настройки контейнера, если их нет
|
||||
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};
|
||||
}
|
||||
|
||||
// Используем настройки контейнера
|
||||
const isActive = state.containerLevels && state.containerLevels[containerId] ?
|
||||
state.containerLevels[containerId][level] : true;
|
||||
|
||||
btn.classList.toggle('active', isActive);
|
||||
btn.classList.toggle('disabled', !isActive);
|
||||
});
|
||||
}
|
||||
function applyFilter(line){
|
||||
if(!state.filter) return true;
|
||||
try{
|
||||
@ -2672,6 +3020,11 @@ async function updateMultiViewMode() {
|
||||
}
|
||||
|
||||
console.log(`Multi-view mode updated: multiViewMode = ${state.multiViewMode}`);
|
||||
|
||||
// Обновляем состояние кнопок уровней логирования при переключении режимов
|
||||
setTimeout(() => {
|
||||
initializeLevelButtons();
|
||||
}, 100);
|
||||
}
|
||||
|
||||
async function setupMultiView() {
|
||||
@ -2799,6 +3152,28 @@ function createMultiViewPanel(service) {
|
||||
panel.innerHTML = `
|
||||
<div class="multi-view-header">
|
||||
<h4 class="multi-view-title">${escapeHtml(service.name)}</h4>
|
||||
<div class="multi-view-levels">
|
||||
<button class="level-btn debug-btn" data-level="debug" data-container-id="${service.id}" title="DEBUG">
|
||||
<span class="level-label">DEBUG</span>
|
||||
<span class="level-value" data-container="${service.id}">0</span>
|
||||
</button>
|
||||
<button class="level-btn info-btn" data-level="info" data-container-id="${service.id}" title="INFO">
|
||||
<span class="level-label">INFO</span>
|
||||
<span class="level-value" data-container="${service.id}">0</span>
|
||||
</button>
|
||||
<button class="level-btn warn-btn" data-level="warn" data-container-id="${service.id}" title="WARN">
|
||||
<span class="level-label">WARN</span>
|
||||
<span class="level-value" data-container="${service.id}">0</span>
|
||||
</button>
|
||||
<button class="level-btn error-btn" data-level="err" data-container-id="${service.id}" title="ERROR">
|
||||
<span class="level-label">ERROR</span>
|
||||
<span class="level-value" data-container="${service.id}">0</span>
|
||||
</button>
|
||||
<button class="level-btn other-btn" data-level="other" data-container-id="${service.id}" title="OTHER">
|
||||
<span class="level-label">OTHER</span>
|
||||
<span class="level-value" data-container="${service.id}">0</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="multi-view-content">
|
||||
<div class="multi-view-log" data-container-id="${service.id}"></div>
|
||||
@ -2817,6 +3192,30 @@ function createMultiViewPanel(service) {
|
||||
console.error(`Failed to create multi-view log element for ${service.name}`);
|
||||
}
|
||||
|
||||
// Инициализируем состояние кнопок уровней логирования для этого контейнера
|
||||
setTimeout(() => {
|
||||
const levelBtns = panel.querySelectorAll('.level-btn');
|
||||
levelBtns.forEach(btn => {
|
||||
const level = btn.getAttribute('data-level');
|
||||
const containerId = btn.getAttribute('data-container-id');
|
||||
|
||||
// Инициализируем настройки контейнера, если их нет
|
||||
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};
|
||||
}
|
||||
|
||||
// Используем настройки контейнера
|
||||
const isActive = state.containerLevels && state.containerLevels[containerId] ?
|
||||
state.containerLevels[containerId][level] : true;
|
||||
|
||||
btn.classList.toggle('active', isActive);
|
||||
btn.classList.toggle('disabled', !isActive);
|
||||
});
|
||||
}, 100);
|
||||
|
||||
console.log(`Multi-view panel created for ${service.name}`);
|
||||
return panel;
|
||||
}
|
||||
@ -3949,8 +4348,15 @@ function handleLine(id, line){
|
||||
const cls = classify(normalizedLine);
|
||||
|
||||
// Обновляем счетчики только для отображаемых логов
|
||||
// Проверяем фильтры для отображения
|
||||
const shouldShow = allowedByLevel(cls) && applyFilter(normalizedLine);
|
||||
// Проверяем фильтры для отображения в зависимости от режима
|
||||
let shouldShow;
|
||||
if (state.multiViewMode && state.selectedContainers.includes(id)) {
|
||||
// Для multi-view используем настройки конкретного контейнера
|
||||
shouldShow = allowedByContainerLevel(cls, id) && applyFilter(normalizedLine);
|
||||
} else {
|
||||
// Для single-view используем глобальные настройки
|
||||
shouldShow = allowedByLevel(cls) && applyFilter(normalizedLine);
|
||||
}
|
||||
|
||||
// Обновляем счетчики только если строка будет отображаться
|
||||
if (obj.counters && shouldShow) {
|
||||
@ -3961,6 +4367,9 @@ function handleLine(id, line){
|
||||
if (cls==='other') obj.counters.other++;
|
||||
}
|
||||
|
||||
// Обновляем счетчики в кнопках заголовков
|
||||
updateHeaderCounters(id, obj.counters);
|
||||
|
||||
// Для Single View НЕ добавляем перенос строки после каждой строки лога
|
||||
const html = `<span class="line ${cls}">${ansiToHtml(normalizedLine)}</span>`;
|
||||
|
||||
@ -4024,7 +4433,10 @@ function handleLine(id, line){
|
||||
if (state.multiViewMode && state.selectedContainers.includes(id)) {
|
||||
const multiViewLog = document.querySelector(`.multi-view-log[data-container-id="${id}"]`);
|
||||
if (multiViewLog) {
|
||||
if (shouldShow) {
|
||||
// Проверяем фильтры для конкретного контейнера
|
||||
const shouldShowInMultiView = allowedByContainerLevel(cls, id) && applyFilter(normalizedLine);
|
||||
|
||||
if (shouldShowInMultiView) {
|
||||
// Применяем ограничение tail lines в multi view
|
||||
const tailLines = parseInt(els.tail.value) || 50;
|
||||
|
||||
@ -4155,6 +4567,28 @@ async function switchToSingle(svc){
|
||||
<div class="single-view-panel" id="singleViewPanel">
|
||||
<div class="single-view-header">
|
||||
<h4 class="single-view-title" id="singleViewTitle">${svc.name} (${svc.service || svc.name})</h4>
|
||||
<div class="single-view-levels">
|
||||
<button class="level-btn debug-btn" data-level="debug" title="DEBUG">
|
||||
<span class="level-label">DEBUG</span>
|
||||
<span class="level-value" data-container="single">0</span>
|
||||
</button>
|
||||
<button class="level-btn info-btn" data-level="info" title="INFO">
|
||||
<span class="level-label">INFO</span>
|
||||
<span class="level-value" data-container="single">0</span>
|
||||
</button>
|
||||
<button class="level-btn warn-btn" data-level="warn" title="WARN">
|
||||
<span class="level-label">WARN</span>
|
||||
<span class="level-value" data-container="single">0</span>
|
||||
</button>
|
||||
<button class="level-btn error-btn" data-level="err" title="ERROR">
|
||||
<span class="level-label">ERROR</span>
|
||||
<span class="level-value" data-container="single">0</span>
|
||||
</button>
|
||||
<button class="level-btn other-btn" data-level="other" title="OTHER">
|
||||
<span class="level-label">OTHER</span>
|
||||
<span class="level-value" data-container="single">0</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="single-view-content">
|
||||
<pre class="log" id="logContent">Connecting...</pre>
|
||||
@ -4258,6 +4692,12 @@ async function switchToSingle(svc){
|
||||
|
||||
// Добавляем обработчики для счетчиков после переключения контейнера
|
||||
addCounterClickHandlers();
|
||||
|
||||
// Обновляем состояние кнопок уровней логирования
|
||||
setTimeout(() => {
|
||||
initializeLevelButtons();
|
||||
}, 100);
|
||||
|
||||
} catch (error) {
|
||||
console.error('switchToSingle: Error occurred:', error);
|
||||
console.error('switchToSingle: Error stack:', error.stack);
|
||||
@ -4465,9 +4905,6 @@ async function updateMultiViewCounters() {
|
||||
// Используем новую функцию пересчета счетчиков
|
||||
recalculateMultiViewCounters();
|
||||
|
||||
// Обновляем видимость счетчиков
|
||||
updateCounterVisibility();
|
||||
|
||||
// Добавляем обработчики для счетчиков
|
||||
addCounterClickHandlers();
|
||||
|
||||
@ -4518,6 +4955,9 @@ function recalculateCounters() {
|
||||
if (cerr) cerr.textContent = obj.counters.err;
|
||||
if (cother) cother.textContent = obj.counters.other;
|
||||
|
||||
// Обновляем счетчики в кнопках заголовка single-view
|
||||
updateHeaderCounters(containerId, obj.counters);
|
||||
|
||||
console.log(`Counters recalculated for container ${containerId} (tail: ${tailLines}):`, obj.counters);
|
||||
}
|
||||
|
||||
@ -4552,7 +4992,7 @@ function recalculateMultiViewCounters() {
|
||||
|
||||
// Пересчитываем счетчики только для отображаемых логов
|
||||
visibleLogs.forEach(logEntry => {
|
||||
const shouldShow = allowedByLevel(logEntry.cls) && applyFilter(logEntry.line);
|
||||
const shouldShow = allowedByContainerLevel(logEntry.cls, containerId) && applyFilter(logEntry.line);
|
||||
if (shouldShow) {
|
||||
if (logEntry.cls === 'dbg') obj.counters.dbg++;
|
||||
if (logEntry.cls === 'ok') obj.counters.info++;
|
||||
@ -4562,6 +5002,9 @@ function recalculateMultiViewCounters() {
|
||||
}
|
||||
});
|
||||
|
||||
// Обновляем счетчики в кнопках заголовка для этого контейнера
|
||||
updateHeaderCounters(containerId, obj.counters);
|
||||
|
||||
// Добавляем к общим счетчикам
|
||||
totalDebug += obj.counters.dbg;
|
||||
totalInfo += obj.counters.info;
|
||||
@ -4588,6 +5031,7 @@ function recalculateMultiViewCounters() {
|
||||
|
||||
// Функция для обновления видимости счетчиков
|
||||
function updateCounterVisibility() {
|
||||
// Обновляем старые кнопки счетчиков (только для legacy интерфейса)
|
||||
const debugBtn = document.querySelector('.debug-btn');
|
||||
const infoBtn = document.querySelector('.info-btn');
|
||||
const warnBtn = document.querySelector('.warn-btn');
|
||||
@ -5120,6 +5564,9 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
els.optionsBtn.title = 'Показать настройки';
|
||||
localStorage.setItem('lb_options_hidden', 'true');
|
||||
}
|
||||
|
||||
// Инициализируем состояние кнопок уровней логирования
|
||||
initializeLevelButtons();
|
||||
}
|
||||
|
||||
// Обработчик для кнопки выхода
|
||||
@ -5590,6 +6037,62 @@ window.addEventListener('keydown', async (e)=>{
|
||||
}
|
||||
});
|
||||
|
||||
// Обработчики для кнопок уровней логирования в заголовках
|
||||
document.addEventListener('click', (e) => {
|
||||
if (e.target.closest('.level-btn')) {
|
||||
const levelBtn = e.target.closest('.level-btn');
|
||||
const level = levelBtn.getAttribute('data-level');
|
||||
const containerId = levelBtn.getAttribute('data-container-id');
|
||||
|
||||
console.log(`Клик по кнопке уровня логирования: level=${level}, containerId=${containerId}, multiViewMode=${state.multiViewMode}`);
|
||||
|
||||
// Переключаем состояние кнопки
|
||||
const isActive = levelBtn.classList.contains('active');
|
||||
levelBtn.classList.toggle('active');
|
||||
|
||||
// Обновляем состояние уровней логирования
|
||||
if (containerId) {
|
||||
// Для multi-view: конкретный контейнер
|
||||
if (!state.containerLevels) {
|
||||
state.containerLevels = {};
|
||||
}
|
||||
if (!state.containerLevels[containerId]) {
|
||||
state.containerLevels[containerId] = {debug: true, info: true, warn: true, err: true, other: true};
|
||||
}
|
||||
state.containerLevels[containerId][level] = !isActive;
|
||||
|
||||
// Обновляем видимость логов только для этого контейнера
|
||||
updateContainerLogVisibility(containerId);
|
||||
|
||||
// Пересчитываем счетчики только для этого контейнера
|
||||
setTimeout(() => {
|
||||
updateContainerCounters(containerId);
|
||||
}, 100);
|
||||
|
||||
// Обновляем видимость логов для всех контейнеров в multi-view
|
||||
// чтобы убедиться, что изменения применились только к нужному контейнеру
|
||||
state.selectedContainers.forEach(id => {
|
||||
if (id !== containerId) {
|
||||
updateContainerLogVisibility(id);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Для single-view: глобальные настройки
|
||||
state.levels[level] = !isActive;
|
||||
|
||||
// Обновляем видимость логов только для текущего контейнера
|
||||
if (state.current) {
|
||||
updateLogVisibility(els.logContent);
|
||||
}
|
||||
|
||||
// Пересчитываем счетчики только для текущего контейнера
|
||||
setTimeout(() => {
|
||||
recalculateCounters();
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Добавляем тестовые функции в глобальную область для отладки
|
||||
window.testDuplicateRemoval = testDuplicateRemoval;
|
||||
window.testSingleViewDuplicateRemoval = testSingleViewDuplicateRemoval;
|
||||
|
Loading…
x
Reference in New Issue
Block a user