Исправлена проблема с лишними пустыми строками в Single View и Multi View
- Радикально переработаны функции очистки пустых строк - Удалены переносы строк из HTML-шаблонов логов - Добавлены CSS стили для правильного отображения элементов .line - Увеличена частота периодической очистки до 2 секунд - Добавлена агрессивная очистка во всех ключевых точках приложения - Улучшена логика определения типа элемента для правильной очистки Автор: Сергей Антропов Сайт: https://devops.org.ru
This commit is contained in:
parent
012c31522c
commit
59e0810750
@ -1098,9 +1098,9 @@ main{display:none}
|
||||
.counter{font-size:11px;color:var(--muted)}
|
||||
.logwrap{flex:1;overflow:auto;padding:10px}
|
||||
.log{white-space:pre-wrap;word-break:break-word;font-size:12px;line-height:1.5;margin:0;tab-size:2}
|
||||
.line{color:var(--fg)} .ok{color:var(--ok)} .warn{color:var(--warn)} .err{color:var(--err)} .dbg{color:#7dcfff} .ts{color:var(--muted)}
|
||||
.line{color:var(--fg); display:block; margin:0; padding:0; line-height:1.4} .ok{color:var(--ok)} .warn{color:var(--warn)} .err{color:var(--err)} .dbg{color:#7dcfff} .ts{color:var(--muted)}
|
||||
/* Цвета для multi-view логов */
|
||||
.multi-view-log .line{color:var(--fg) !important}
|
||||
.multi-view-log .line{color:var(--fg) !important; display:block; margin:0; padding:0; line-height:1.4}
|
||||
.multi-view-log .ok{color:var(--ok) !important}
|
||||
.multi-view-log .warn{color:var(--warn) !important}
|
||||
.multi-view-log .err{color:var(--err) !important}
|
||||
@ -1108,7 +1108,7 @@ main{display:none}
|
||||
.multi-view-log .ts{color:var(--muted) !important}
|
||||
|
||||
/* Дополнительные стили для multi-view логов */
|
||||
.multi-view-log span.line{color:var(--fg) !important}
|
||||
.multi-view-log span.line{color:var(--fg) !important; display:block; margin:0; padding:0; line-height:1.4}
|
||||
.multi-view-log span.ok{color:var(--ok) !important}
|
||||
.multi-view-log span.warn{color:var(--warn) !important}
|
||||
.multi-view-log span.err{color:var(--err) !important}
|
||||
@ -1434,18 +1434,18 @@ function refreshAllLogs() {
|
||||
// Обновляем отображение
|
||||
obj.logEl.innerHTML = filteredHtml.join('');
|
||||
|
||||
// Сразу очищаем пустые строки в legacy панели
|
||||
cleanSingleViewEmptyLines(obj.logEl);
|
||||
cleanDuplicateLines(obj.logEl);
|
||||
|
||||
// Обновляем современный интерфейс
|
||||
if (state.current && state.current.id === id && els.logContent) {
|
||||
els.logContent.innerHTML = obj.logEl.innerHTML;
|
||||
|
||||
// Очищаем дублированные строки в Single View после обновления
|
||||
cleanDuplicateLines(els.logContent);
|
||||
cleanSingleViewEmptyLines(els.logContent);
|
||||
cleanDuplicateLines(els.logContent);
|
||||
}
|
||||
|
||||
// Очищаем дублированные строки в legacy панели
|
||||
cleanDuplicateLines(obj.logEl);
|
||||
cleanSingleViewEmptyLines(obj.logEl);
|
||||
});
|
||||
|
||||
// Обновляем мультипросмотр
|
||||
@ -1473,7 +1473,8 @@ function refreshAllLogs() {
|
||||
if (multiViewLog) {
|
||||
multiViewLog.innerHTML = filteredHtml.join('');
|
||||
|
||||
// Очищаем дублированные строки после обновления
|
||||
// Сразу очищаем пустые строки в мультипросмотре
|
||||
cleanMultiViewEmptyLines(multiViewLog);
|
||||
cleanMultiViewDuplicateLines(multiViewLog);
|
||||
}
|
||||
});
|
||||
@ -2566,25 +2567,26 @@ function processMultiViewLineBreaks(text) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Функция для очистки пустых строк в multi view
|
||||
* Удаляет пустые строки между "Connected" и началом логов
|
||||
* Функция для радикальной очистки пустых строк в multi view
|
||||
* Удаляет все пустые строки и лишние переносы строк
|
||||
* @param {HTMLElement} multiViewLog - элемент лога multi view
|
||||
*/
|
||||
function cleanMultiViewEmptyLines(multiViewLog) {
|
||||
if (!multiViewLog) return;
|
||||
|
||||
// Находим все строки в логе
|
||||
const lines = Array.from(multiViewLog.querySelectorAll('.line'));
|
||||
let removedCount = 0;
|
||||
|
||||
// Удаляем пустые строки (строки без текста или только с пробелами)
|
||||
// Удаляем все пустые строки (элементы .line без текста)
|
||||
const lines = Array.from(multiViewLog.querySelectorAll('.line'));
|
||||
lines.forEach(line => {
|
||||
const textContent = line.textContent || line.innerText || '';
|
||||
if (textContent.trim() === '') {
|
||||
line.remove();
|
||||
removedCount++;
|
||||
}
|
||||
});
|
||||
|
||||
// Также удаляем пустые текстовые узлы
|
||||
// Удаляем все текстовые узлы, которые содержат только пробелы и переносы строк
|
||||
const walker = document.createTreeWalker(
|
||||
multiViewLog,
|
||||
NodeFilter.SHOW_TEXT,
|
||||
@ -2595,12 +2597,27 @@ function cleanMultiViewEmptyLines(multiViewLog) {
|
||||
const textNodesToRemove = [];
|
||||
let node;
|
||||
while (node = walker.nextNode()) {
|
||||
if (node.textContent.trim() === '') {
|
||||
const content = node.textContent;
|
||||
// Удаляем все узлы, которые содержат только пробелы, переносы строк или табуляцию
|
||||
if (content.trim() === '') {
|
||||
textNodesToRemove.push(node);
|
||||
}
|
||||
}
|
||||
|
||||
textNodesToRemove.forEach(node => node.remove());
|
||||
|
||||
// Удаляем все пустые текстовые узлы между элементами .line
|
||||
const allNodes = Array.from(multiViewLog.childNodes);
|
||||
for (let i = allNodes.length - 1; i >= 0; i--) {
|
||||
const node = allNodes[i];
|
||||
if (node.nodeType === Node.TEXT_NODE && node.textContent.trim() === '') {
|
||||
node.remove();
|
||||
}
|
||||
}
|
||||
|
||||
if (removedCount > 0) {
|
||||
console.log(`cleanMultiViewEmptyLines: Удалено ${removedCount} пустых строк`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2623,7 +2640,8 @@ function cleanMultiViewDuplicateLines(multiViewLog) {
|
||||
const currentText = currentLine.textContent || currentLine.innerText || '';
|
||||
const previousText = previousLine.textContent || previousLine.innerText || '';
|
||||
|
||||
if (currentText.trim() === previousText.trim() && currentText.trim() !== '') {
|
||||
// Удаляем дублированные строки (включая пустые)
|
||||
if (currentText.trim() === previousText.trim()) {
|
||||
console.log(`cleanMultiViewDuplicateLines: Удаляем дублированную строку: ${currentText.substring(0, 50)}...`);
|
||||
currentLine.remove();
|
||||
removedCount++;
|
||||
@ -2631,6 +2649,9 @@ function cleanMultiViewDuplicateLines(multiViewLog) {
|
||||
}
|
||||
}
|
||||
|
||||
// После удаления дубликатов очищаем лишние пустые строки
|
||||
cleanMultiViewEmptyLines(multiViewLog);
|
||||
|
||||
if (removedCount > 0) {
|
||||
console.log(`cleanMultiViewDuplicateLines: Удалено ${removedCount} дублированных строк`);
|
||||
}
|
||||
@ -2656,7 +2677,8 @@ function cleanDuplicateLines(logElement) {
|
||||
const currentText = currentLine.textContent || currentLine.innerText || '';
|
||||
const previousText = previousLine.textContent || previousLine.innerText || '';
|
||||
|
||||
if (currentText.trim() === previousText.trim() && currentText.trim() !== '') {
|
||||
// Удаляем дублированные строки (включая пустые)
|
||||
if (currentText.trim() === previousText.trim()) {
|
||||
console.log(`cleanDuplicateLines: Удаляем дублированную строку: ${currentText.substring(0, 50)}...`);
|
||||
currentLine.remove();
|
||||
removedCount++;
|
||||
@ -2664,33 +2686,39 @@ function cleanDuplicateLines(logElement) {
|
||||
}
|
||||
}
|
||||
|
||||
// После удаления дубликатов очищаем лишние пустые строки
|
||||
if (logElement.classList.contains('multi-view-log')) {
|
||||
cleanMultiViewEmptyLines(logElement);
|
||||
} else {
|
||||
cleanSingleViewEmptyLines(logElement);
|
||||
}
|
||||
|
||||
if (removedCount > 0) {
|
||||
console.log(`cleanDuplicateLines: Удалено ${removedCount} дублированных строк`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Функция для очистки лишних пустых строк в Single View
|
||||
* Удаляет только лишние пустые строки, сохраняя переносы строк между логами
|
||||
* Функция для радикальной очистки пустых строк в Single View
|
||||
* Удаляет все пустые строки и лишние переносы строк
|
||||
* @param {HTMLElement} logElement - элемент лога Single View
|
||||
*/
|
||||
function cleanSingleViewEmptyLines(logElement) {
|
||||
if (!logElement) return;
|
||||
|
||||
// Находим все строки в логе
|
||||
const lines = Array.from(logElement.querySelectorAll('.line'));
|
||||
let removedCount = 0;
|
||||
|
||||
// Удаляем только полностью пустые строки (строки без текста)
|
||||
// Удаляем все пустые строки (элементы .line без текста)
|
||||
const lines = Array.from(logElement.querySelectorAll('.line'));
|
||||
lines.forEach(line => {
|
||||
const textContent = line.textContent || line.innerText || '';
|
||||
if (textContent.trim() === '' && textContent.length === 0) {
|
||||
if (textContent.trim() === '') {
|
||||
line.remove();
|
||||
removedCount++;
|
||||
}
|
||||
});
|
||||
|
||||
// Удаляем только полностью пустые текстовые узлы (не переносы строк)
|
||||
// Удаляем все текстовые узлы, которые содержат только пробелы и переносы строк
|
||||
const walker = document.createTreeWalker(
|
||||
logElement,
|
||||
NodeFilter.SHOW_TEXT,
|
||||
@ -2701,23 +2729,24 @@ function cleanSingleViewEmptyLines(logElement) {
|
||||
const textNodesToRemove = [];
|
||||
let node;
|
||||
while (node = walker.nextNode()) {
|
||||
// Удаляем только узлы, которые содержат только пробелы и переносы строк
|
||||
if (node.textContent.trim() === '' && node.textContent.length > 0) {
|
||||
// Проверяем, что это не просто перенос строки
|
||||
const content = node.textContent;
|
||||
if (content === '\n' || content === '\r\n') {
|
||||
// Оставляем переносы строк
|
||||
continue;
|
||||
}
|
||||
// Удаляем только если это множественные пробелы или табуляция
|
||||
if (content.match(/^[\s\t]+$/)) {
|
||||
textNodesToRemove.push(node);
|
||||
}
|
||||
const content = node.textContent;
|
||||
// Удаляем все узлы, которые содержат только пробелы, переносы строк или табуляцию
|
||||
if (content.trim() === '') {
|
||||
textNodesToRemove.push(node);
|
||||
}
|
||||
}
|
||||
|
||||
textNodesToRemove.forEach(node => node.remove());
|
||||
|
||||
// Удаляем все пустые текстовые узлы между элементами .line
|
||||
const allNodes = Array.from(logElement.childNodes);
|
||||
for (let i = allNodes.length - 1; i >= 0; i--) {
|
||||
const node = allNodes[i];
|
||||
if (node.nodeType === Node.TEXT_NODE && node.textContent.trim() === '') {
|
||||
node.remove();
|
||||
}
|
||||
}
|
||||
|
||||
if (removedCount > 0) {
|
||||
console.log(`cleanSingleViewEmptyLines: Удалено ${removedCount} пустых строк`);
|
||||
}
|
||||
@ -2737,6 +2766,40 @@ function normalizeSpaces(text) {
|
||||
return text.replace(/\s{2,}/g, ' ');
|
||||
}
|
||||
|
||||
/**
|
||||
* Функция для периодической очистки пустых строк
|
||||
* Вызывается автоматически каждые 2 секунды для поддержания чистоты логов
|
||||
*/
|
||||
function periodicCleanup() {
|
||||
// Очищаем пустые строки в Single View
|
||||
if (!state.multiViewMode && els.logContent) {
|
||||
cleanSingleViewEmptyLines(els.logContent);
|
||||
cleanDuplicateLines(els.logContent);
|
||||
}
|
||||
|
||||
// Очищаем пустые строки в мультипросмотре
|
||||
if (state.multiViewMode) {
|
||||
state.selectedContainers.forEach(containerId => {
|
||||
const multiViewLog = document.querySelector(`.multi-view-log[data-container-id="${containerId}"]`);
|
||||
if (multiViewLog) {
|
||||
cleanMultiViewEmptyLines(multiViewLog);
|
||||
cleanMultiViewDuplicateLines(multiViewLog);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Очищаем пустые строки в legacy панелях
|
||||
Object.values(state.open).forEach(obj => {
|
||||
if (obj.logEl) {
|
||||
cleanSingleViewEmptyLines(obj.logEl);
|
||||
cleanDuplicateLines(obj.logEl);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Запускаем периодическую очистку каждые 2 секунды
|
||||
setInterval(periodicCleanup, 2000);
|
||||
|
||||
/**
|
||||
* Функция для обработки специальных замен в MultiView логах
|
||||
* Выполняет специфичные замены для улучшения читаемости логов
|
||||
@ -3140,14 +3203,14 @@ function handleLine(id, line){
|
||||
if (cls==='err') obj.counters.err++;
|
||||
}
|
||||
|
||||
// Для Single View добавляем перенос строки после каждой строки лога
|
||||
const html = `<span class="line ${cls}">${ansiToHtml(normalizedLine)}</span>\n`;
|
||||
// Для Single View НЕ добавляем перенос строки после каждой строки лога
|
||||
const html = `<span class="line ${cls}">${ansiToHtml(normalizedLine)}</span>`;
|
||||
|
||||
// Сохраняем все логи в буфере (всегда)
|
||||
if (!obj.allLogs) obj.allLogs = [];
|
||||
// Для Single View сохраняем обработанную строку, для MultiView - оригинальную
|
||||
const processedLine = !state.multiViewMode ? processSingleViewSpecialReplacements(normalizedLine) : normalizedLine;
|
||||
const processedHtml = `<span class="line ${cls}">${ansiToHtml(processedLine)}</span>\n`;
|
||||
const processedHtml = `<span class="line ${cls}">${ansiToHtml(processedLine)}</span>`;
|
||||
obj.allLogs.push({html: processedHtml, line: processedLine, cls: cls});
|
||||
|
||||
// Ограничиваем размер буфера
|
||||
@ -3168,8 +3231,8 @@ function handleLine(id, line){
|
||||
return; // Пропускаем дублированную строку
|
||||
}
|
||||
|
||||
// Создаем HTML с обработанной строкой для Single View (с переносом строки)
|
||||
const singleViewHtml = `<span class="line ${cls}">${ansiToHtml(singleViewProcessedLine)}</span>\n`;
|
||||
// Создаем HTML с обработанной строкой для Single View (без переноса строки)
|
||||
const singleViewHtml = `<span class="line ${cls}">${ansiToHtml(singleViewProcessedLine)}</span>`;
|
||||
|
||||
obj.logEl.insertAdjacentHTML('beforeend', singleViewHtml);
|
||||
|
||||
@ -3226,7 +3289,7 @@ function handleLine(id, line){
|
||||
return; // Пропускаем дублированную строку
|
||||
}
|
||||
|
||||
const multiViewHtml = `<span class="line ${cls}">${ansiToHtml(processedLine)}</span>\n`;
|
||||
const multiViewHtml = `<span class="line ${cls}">${ansiToHtml(processedLine)}</span>`;
|
||||
|
||||
// Добавляем новую строку
|
||||
multiViewLog.insertAdjacentHTML('beforeend', multiViewHtml);
|
||||
@ -3376,6 +3439,11 @@ async function switchToSingle(svc){
|
||||
els.logContent.insertAdjacentHTML('beforeend', logEntry.html);
|
||||
}
|
||||
});
|
||||
|
||||
// Очищаем лишние пустые строки после восстановления логов
|
||||
cleanSingleViewEmptyLines(els.logContent);
|
||||
cleanDuplicateLines(els.logContent);
|
||||
|
||||
if (els.autoscroll && els.autoscroll.checked) {
|
||||
els.logContent.scrollTop = els.logContent.scrollHeight;
|
||||
}
|
||||
@ -3774,6 +3842,12 @@ async function refreshLogsAndCounters() {
|
||||
if (updatedContainer) {
|
||||
// Переключаемся на обновленный контейнер
|
||||
await switchToSingle(updatedContainer);
|
||||
|
||||
// Очищаем лишние пустые строки после переключения
|
||||
if (els.logContent) {
|
||||
cleanSingleViewEmptyLines(els.logContent);
|
||||
cleanDuplicateLines(els.logContent);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.log('No container selected');
|
||||
@ -3791,6 +3865,9 @@ els.clearBtn.onclick = ()=> {
|
||||
// Очищаем современный интерфейс
|
||||
if (els.logContent) {
|
||||
els.logContent.textContent = '';
|
||||
// Очищаем лишние пустые строки после очистки
|
||||
cleanSingleViewEmptyLines(els.logContent);
|
||||
cleanDuplicateLines(els.logContent);
|
||||
}
|
||||
|
||||
// Очищаем мультипросмотр
|
||||
@ -4480,6 +4557,14 @@ window.addEventListener('keydown', async (e)=>{
|
||||
|
||||
console.log('LogBoard+ инициализирован с исправлениями дублирования строк и правильными переносами строк в Single View и MultiView режимах');
|
||||
console.log('Для тестирования используйте: testDuplicateRemoval(), testSingleViewDuplicateRemoval(), testSingleViewEmptyLinesRemoval() или testSingleViewLineBreaks()');
|
||||
|
||||
// Запускаем первоначальную очистку пустых строк
|
||||
setTimeout(() => {
|
||||
if (!state.multiViewMode && els.logContent) {
|
||||
cleanSingleViewEmptyLines(els.logContent);
|
||||
cleanDuplicateLines(els.logContent);
|
||||
}
|
||||
}, 1000);
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
|
Loading…
x
Reference in New Issue
Block a user