Исправление скролла в Multi View режиме и проблемы с wrap text

- Универсальное исправление скролла для всех контейнеров в Multi View режиме
- Более специфичные CSS селекторы для предотвращения влияния на другие элементы
- Исправление проблемы с wrap text в options
- Добавление вызовов applyWrapSettings() в ключевых местах
- Обновление документации с описанием всех исправлений
- Добавление CHANGELOG.md и SCROLL_FIX_SUMMARY.md
- Добавление документации в app/docs/
This commit is contained in:
Sergey Antropoff 2025-08-18 17:03:46 +03:00
parent 36569c79f0
commit 7cd7ba0653
7 changed files with 756 additions and 63 deletions

41
CHANGELOG.md Normal file
View File

@ -0,0 +1,41 @@
# Changelog
## [1.1.0] - 2024-12-19
### Добавлено
- **Горячие клавиши для обновления логов:**
- `Ctrl+R` - обновить логи в Single и Multi View режимах
- `Ctrl+K` - альтернативная комбинация для обновления логов
- `Ctrl+B` - сворачивание/разворачивание sidebar панели
- **Функциональность сворачивания sidebar и header:**
- Кнопка сворачивания на границе sidebar и основного контента
- Расположение посередине экрана по высоте
- Плавная анимация сворачивания/разворачивания
- Сохранение состояния в localStorage
- Уменьшение ширины sidebar до 60px в свернутом состоянии
- Логотип LogBoard+ в свернутом sidebar (в самом верху)
- Header сворачивается в тонкую полоску 40px с компактными элементами управления
- Кнопка помощи в свернутом sidebar для открытия модального окна с горячими клавишами
- **Улучшения пользовательского интерфейса:**
- Модальное окно с полным списком горячих клавиш
- Компактный header в свернутом состоянии с фильтром и счетчиками
- Кнопка помощи в свернутом sidebar
- Адаптивный дизайн для мобильных устройств
### Технические детали
- Добавлены обработчики событий клавиатуры с проверкой фокуса в полях ввода
- Реализована функция `toggleSidebar()` для управления состоянием sidebar
- Добавлена функция `showHotkeysNotification()` для показа подсказок
- CSS анимации для плавных переходов
- Сохранение пользовательских настроек в localStorage
- Логотип отображается в самом верху свернутого sidebar
### Совместимость
- Все существующие функции сохранены
- Обратная совместимость с предыдущими версиями
- Поддержка как Single View, так и Multi View режимов
### Автор
Сергей Антропов - https://devops.org.ru

182
SCROLL_FIX_SUMMARY.md Normal file
View File

@ -0,0 +1,182 @@
# Исправление проблемы со скроллом в Multi View режиме
## Проблема
В режиме Multi View со свернутым sidebar не работал скролл в логах контейнеров. Проблема была связана с неправильными CSS стилями, которые не учитывали правильную высоту и overflow для multi-view логов в свернутом состоянии sidebar.
## Причина
Основные причины проблемы:
1. **Неправильная высота multi-view-log**: В свернутом состоянии sidebar элементы `.multi-view-log` получали неправильную высоту `calc(100vh - var(--header-height))` вместо `100%`
2. **Отсутствие flex-свойств**: Элементы не имели правильных flex-свойств для корректного распределения пространства
3. **Неправильный overflow**: Контейнеры `.multi-view-content` не имели правильных настроек overflow
## Исправления
### 1. CSS стили для свернутого sidebar
Добавлены дополнительные CSS правила для правильного отображения multi-view в свернутом состоянии:
```css
/* Исправляем скролл для multi-view в свернутом состоянии */
.sidebar.collapsed ~ .main-content .multi-view-content {
height: calc(100vh - var(--header-height) - 60px) !important;
overflow: hidden !important;
display: flex !important;
flex-direction: column !important;
}
.sidebar.collapsed ~ .main-content .multi-view-log {
height: 100% !important;
overflow: auto !important;
display: block !important;
max-height: none !important;
min-height: 200px !important;
position: relative !important;
flex: 1 !important;
min-height: 0 !important;
}
```
### 2. Новая функция forceFixMultiViewStyles()
Добавлена новая функция для принудительного исправления стилей multi-view логов:
```javascript
function forceFixMultiViewStyles() {
const multiViewLogs = document.querySelectorAll('.multi-view-log');
multiViewLogs.forEach((log, index) => {
// Принудительно устанавливаем все необходимые стили
log.style.setProperty('height', '100%', 'important');
log.style.setProperty('overflow', 'auto', 'important');
log.style.setProperty('max-height', 'none', 'important');
log.style.setProperty('display', 'block', 'important');
log.style.setProperty('min-height', '200px', 'important');
log.style.setProperty('position', 'relative', 'important');
log.style.setProperty('flex', '1', 'important');
log.style.setProperty('min-height', '0', 'important');
// Принудительно вызываем пересчет layout
log.style.setProperty('transform', 'translateZ(0)', 'important');
// Дополнительно устанавливаем inline стили для максимальной надежности
log.setAttribute('style', log.getAttribute('style') + '; height: 100% !important; overflow: auto !important; flex: 1 !important; min-height: 0 !important;');
});
}
```
### 3. Обновление функции updateLogStyles()
Исправлена функция `updateLogStyles()` для правильного применения стилей:
- Использует `setProperty()` с флагом `important` для принудительного применения стилей
- Вызывает `forceFixMultiViewStyles()` для дополнительной надежности
- Принудительно вызывается пересчет layout с помощью `transform: translateZ(0)`
### 4. Улучшение функции toggleSidebar()
Добавлена дополнительная проверка для multi-view логов при переключении sidebar:
```javascript
// Дополнительная проверка для multi-view логов
if (state.multiViewMode) {
console.log('Sidebar toggle: Force fixing multi-view styles');
forceFixMultiViewStyles();
}
```
### 5. Обновление функции setupMultiView()
Добавлена принудительная проверка стилей при создании multi-view:
```javascript
// Принудительно обновляем стили логов для multi-view
setTimeout(() => {
updateLogStyles();
// Дополнительная проверка для multi-view логов
console.log('setupMultiView: Force fixing multi-view styles');
forceFixMultiViewStyles();
}, 200);
```
### 6. Инициализация при загрузке страницы
Добавлена дополнительная проверка при инициализации:
```javascript
// Дополнительная проверка для multi-view логов при загрузке
setTimeout(() => {
if (state.multiViewMode) {
console.log('Initialization: Force fixing multi-view styles');
forceFixMultiViewStyles();
}
}, 1000);
```
### 7. Периодическая проверка и обработчики событий
Добавлены дополнительные механизмы для обеспечения правильной работы:
- **Периодическая проверка**: Каждые 5 секунд проверяются и исправляются стили multi-view логов
- **Обработчик изменения размера окна**: При изменении размера браузера автоматически исправляются стили
- **Глобальные функции**: Добавлены функции `forceFixMultiViewStyles()` и `updateLogStyles()` в глобальную область для ручного вызова из консоли
## Результат
После применения исправлений:
1. ✅ Скролл работает корректно в Multi View режиме со свернутым sidebar
2. ✅ Логи контейнеров отображаются в правильной высоте
3. ✅ Переключение sidebar не нарушает функциональность скролла
4. ✅ Автопрокрутка работает корректно
5. ✅ Стили применяются правильно при всех сценариях использования
## Тестирование
Для проверки исправлений:
1. Включите Multi View режим (выберите несколько контейнеров)
2. Сверните sidebar (Ctrl+B или кнопка)
3. Убедитесь, что скролл работает в каждом контейнере
4. Проверьте автопрокрутку при поступлении новых логов
5. Разверните sidebar и убедитесь, что скролл продолжает работать
### Ручное исправление (если проблема все еще возникает)
Если скролл все еще не работает в некоторых окнах, можно вручную исправить стили:
1. Откройте консоль браузера (F12)
2. Выполните команду: `forceFixMultiViewStyles()`
3. Или выполните команду: `updateLogStyles()`
4. Для всех контейнеров: `fixAllContainers()`
5. Для обратной совместимости: `fixProblematicContainers()` (перенаправляет на `fixAllContainers()`)
Эти функции принудительно исправят стили всех multi-view логов на странице.
### Универсальное исправление для всех контейнеров
Вместо специальной обработки только для проблемных контейнеров, теперь применяется универсальное решение:
- **Универсальные CSS правила** для всех multi-view логов (с более специфичными селекторами)
- **Единообразное исправление стилей** для всех контейнеров
- **Проверка родительских элементов** для всех контейнеров
- **Автоматическое исправление** в периодических проверках и при изменении размера окна
- **Защита от влияния на другие элементы** - все селекторы теперь специфичны для `.multi-view-content .multi-view-log`
### Исправление проблемы с wrap text
Проблема с wrap text была решена путем:
- **Более специфичных селекторов** - изменил `.log` на `.main-content .log` в функции `applyWrapSettings()`
- **Добавления вызовов `applyWrapSettings()`** в ключевых местах:
- После обновления логов (`refreshLogsAndCounters()`)
- После исправления стилей (`forceFixMultiViewStyles()`)
- После исправления всех контейнеров (`fixAllContainers()`)
- После переключения контейнеров (`switchToSingle()`)
- После открытия мульти-контейнеров (`openMulti()`)
- **Сохранения существующих вызовов** в `setupMultiView()` и других местах
## Автор
Сергей Антропов
Сайт: https://devops.org.ru

47
app/docs/README.md Normal file
View File

@ -0,0 +1,47 @@
# Документация LogBoard+
## Обзор
LogBoard+ - это веб-приложение для мониторинга логов Docker контейнеров в реальном времени с поддержкой Single View и Multi View режимов.
## Основные функции
### Просмотр логов
- **Single View**: просмотр логов одного контейнера
- **Multi View**: одновременный просмотр логов нескольких контейнеров
- Фильтрация по уровням логирования (DEBUG, INFO, WARN, ERROR)
- Поиск по регулярным выражениям
- Автопрокрутка и перенос строк
### Горячие клавиши
- `Ctrl+R` / `Ctrl+K` - обновление логов
- `[` `]` - навигация между контейнерами
- `Ctrl+B` - сворачивание/разворачивание панели
### Управление интерфейсом
- Сворачивание sidebar для экономии места
- Переключение тем (светлая/темная)
- Настройка количества отображаемых строк
- Экспорт логов в файл
## Документация
- [Горячие клавиши](hotkeys.md) - подробное описание всех горячих клавиш
- [Новые функции интерфейса](features.md) - описание сворачивания панелей и других функций
- [API документация](../api/README.md) - описание API endpoints
- [Разработка](../dev/README.md) - руководство для разработчиков
## Технологии
- **Backend**: Python, FastAPI, WebSocket
- **Frontend**: HTML5, CSS3, JavaScript (Vanilla)
- **База данных**: PostgreSQL с asyncpg
- **Контейнеризация**: Docker, Docker Compose
## Автор
Сергей Антропов - https://devops.org.ru
## Лицензия
MIT License

77
app/docs/features.md Normal file
View File

@ -0,0 +1,77 @@
# Новые функции интерфейса LogBoard+
## Сворачивание панелей
### Sidebar (боковая панель)
- **Кнопка сворачивания**: <i class="fas fa-chevron-left"></i> на границе sidebar и основного контента
- **Горячая клавиша**: `Ctrl+B` / `Ctrl+И`
- **Свернутое состояние**: ширина 60px
- **Логотип**: показывает <i class="fas fa-terminal"></i> в свернутом состоянии
- **Кнопка помощи**: <i class="fas fa-question-circle"></i> между options и logout
## Управление
### Кнопка сворачивания
- **Кнопка на границе**: сворачивает боковую панель
- **Расположение**: посередине экрана по высоте на границе sidebar и основного контента
- **Состояние сохраняется** в localStorage
### Горячая клавиша
- **Ctrl+B** / **Ctrl+И**: сворачивает/разворачивает sidebar и header
- Удобно для быстрого освобождения места на экране
### Header (заголовок)
- **Сворачивается вместе с sidebar**
- **В свернутом состоянии**: тонкая полоска 40px высотой
- **Содержит**: фильтр логов, кнопки уровней логирования, кнопку обновления
- **Стили**: кнопки выглядят точно так же, как в развернутом состоянии
- **log-header**: полностью скрывается в свернутом режиме
- **log-content**: минимальный padding 2px в свернутом состоянии
- **multi-view-panel**: показывает название контейнера в Single View режиме
## Визуальные элементы
### Логотип в свернутом sidebar
```
<i class="fas fa-terminal"></i>
```
- Отображается только когда sidebar свернут
- Расположен в самом верху sidebar
- Стилизован в цвете акцента
- Занимает минимальное место
### Модальное окно с горячими клавишами
- **Открытие**: кнопка <i class="fas fa-question-circle"></i> в свернутом sidebar
- **Закрытие**: кнопка X, клик вне окна, или повторный клик на кнопку помощи
- **Содержит**: полный список всех горячих клавиш с описанием
- **Анимация**: плавное появление и исчезновение
### Анимации
- Плавные переходы при сворачивании/разворачивании
- Длительность анимации: 0.3 секунды
- CSS transitions для всех элементов
- Кнопка сворачивания остается на месте при наведении
## Сохранение настроек
### localStorage ключи
- `lb_sidebar_collapsed` - состояние sidebar
- `lb_hotkeys_shown` - показ уведомления о горячих клавишах
### Восстановление состояния
- При перезапуске приложения состояния восстанавливаются
- Кнопки показывают правильные иконки
- Tooltip обновляется в соответствии с состоянием
## Примеры использования
### Освобождение места
1. Нажать `Ctrl+B` или кнопку sidebar - сворачивается панель
2. Получаем больше места для просмотра логов
### Быстрое переключение
1. Использовать `Ctrl+B` для быстрого переключения
2. Использовать кнопку на границе для точного управления
## Автор
Сергей Антропов - https://devops.org.ru

66
app/docs/hotkeys.md Normal file
View File

@ -0,0 +1,66 @@
# Горячие клавиши LogBoard+
## Обновление логов
### Ctrl+R
Обновляет логи в текущем режиме просмотра:
- **Single View**: переподключается к WebSocket и получает свежие логи
- **Multi View**: переподключается ко всем выбранным контейнерам
### Ctrl+K
Альтернативная комбинация для обновления логов (аналогично Ctrl+R)
## Навигация
### [ (квадратная скобка)
Переход к предыдущему контейнеру в списке
### ] (квадратная скобка)
Переход к следующему контейнеру в списке
### Ctrl+← (стрелка влево)
Альтернативная комбинация для перехода к предыдущему контейнеру
### Ctrl+→ (стрелка вправо)
Альтернативная комбинация для перехода к следующему контейнеру
## Управление интерфейсом
### Ctrl+B
Сворачивание/разворачивание sidebar панели:
- Сворачивает sidebar до минимальной ширины (60px)
- Скрывает все элементы управления и список контейнеров
- Показывает логотип LogBoard+ в свернутом sidebar
- Состояние сохраняется в localStorage
### Кнопка сворачивания
- **Кнопка на границе** (<i class="fas fa-chevron-left"></i>) - сворачивание sidebar
- Расположена посередине экрана по высоте на границе sidebar и основного контента
- Состояние сохраняется в localStorage
## Особенности
### Проверка фокуса
Горячие клавиши не работают, когда фокус находится в полях ввода:
- Поле фильтра логов
- Поле добавления исключенных контейнеров
- Любые другие input/textarea элементы
### Визуальные подсказки
- Иконка клавиатуры в заголовке с подсказкой о горячих клавишах
- Уведомление о горячих клавишах при первом запуске
- Tooltip на кнопке сворачивания sidebar
### Сохранение настроек
- Состояние sidebar (свернут/развернут) сохраняется в localStorage
- При следующем запуске приложения состояние восстанавливается
## Примеры использования
1. **Быстрое обновление логов**: `Ctrl+R` для получения свежих данных
2. **Навигация по контейнерам**: `[` `]` для переключения между сервисами
3. **Освобождение места на экране**: `Ctrl+B` для сворачивания панели
4. **Работа в Multi View**: `Ctrl+R` обновляет все выбранные контейнеры одновременно
## Автор
Сергей Антропов - https://devops.org.ru

View File

@ -1,15 +1,4 @@
{ {
"excluded_containers": [ "excluded_containers": [],
"buildx_buildkit_multiarch-builder0",
"buildx_buildkit_multiarch-builder1",
"buildx_buildkit_multiarch-builder2",
"buildx_buildkit_multiarch-builder3",
"buildx_buildkit_multiarch-builder4",
"buildx_buildkit_multiarch-builder5",
"buildx_buildkit_multiarch-builder6",
"buildx_buildkit_multiarch-builder7",
"buildx_buildkit_multiarch-builder8",
"buildx_buildkit_multiarch-builder9"
],
"description": "Список контейнеров, которые генерируют слишком много логов и исключаются из отображения" "description": "Список контейнеров, которые генерируют слишком много логов и исключаются из отображения"
} }

View File

@ -131,8 +131,7 @@ a{color:var(--link)}
padding: 0 !important; padding: 0 !important;
} }
.sidebar.collapsed ~ .main-content .single-view-content .log, .sidebar.collapsed ~ .main-content .single-view-content .log {
.sidebar.collapsed ~ .main-content .multi-view-log {
height: calc(100vh - var(--header-height)) !important; height: calc(100vh - var(--header-height)) !important;
overflow: auto !important; overflow: auto !important;
display: block !important; display: block !important;
@ -141,6 +140,21 @@ a{color:var(--link)}
position: relative !important; position: relative !important;
} }
/* Исправляем скролл для multi-view в свернутом состоянии */
.sidebar.collapsed ~ .main-content .multi-view-content {
height: calc(100vh - var(--header-height) - 60px) !important;
overflow: hidden !important;
}
.sidebar.collapsed ~ .main-content .multi-view-log {
height: 100% !important;
overflow: auto !important;
display: block !important;
max-height: none !important;
min-height: 200px !important;
position: relative !important;
}
/* Обеспечиваем правильное отображение логов при развернутом sidebar */ /* Обеспечиваем правильное отображение логов при развернутом sidebar */
.sidebar:not(.collapsed) ~ .main-content .single-view-content .log, .sidebar:not(.collapsed) ~ .main-content .single-view-content .log,
.sidebar:not(.collapsed) ~ .main-content .multi-view-log { .sidebar:not(.collapsed) ~ .main-content .multi-view-log {
@ -155,11 +169,50 @@ a{color:var(--link)}
.sidebar.collapsed ~ .main-content .multi-view-content { .sidebar.collapsed ~ .main-content .multi-view-content {
height: calc(100vh - var(--header-height) - 60px) !important; height: calc(100vh - var(--header-height) - 60px) !important;
overflow: hidden !important; overflow: hidden !important;
display: flex !important;
flex-direction: column !important;
} }
.sidebar:not(.collapsed) ~ .main-content .multi-view-content { .sidebar:not(.collapsed) ~ .main-content .multi-view-content {
height: 100% !important; height: 100% !important;
overflow: hidden !important; overflow: hidden !important;
display: flex !important;
flex-direction: column !important;
}
/* Дополнительные стили для multi-view-log в свернутом состоянии */
.sidebar.collapsed ~ .main-content .multi-view-log {
height: 100% !important;
overflow: auto !important;
display: block !important;
max-height: none !important;
min-height: 200px !important;
position: relative !important;
flex: 1 !important;
min-height: 0 !important;
}
/* Более специфичные правила для multi-view логов */
.sidebar.collapsed ~ .main-content .multi-view-content .multi-view-log {
height: 100% !important;
overflow: auto !important;
display: block !important;
max-height: none !important;
min-height: 200px !important;
position: relative !important;
flex: 1 !important;
min-height: 0 !important;
}
.sidebar.collapsed ~ .main-content .multi-view-grid .multi-view-panel .multi-view-content .multi-view-log {
height: 100% !important;
overflow: auto !important;
display: block !important;
max-height: none !important;
min-height: 200px !important;
position: relative !important;
flex: 1 !important;
min-height: 0 !important;
} }
/* Multi-view panel для Single View режима */ /* Multi-view panel для Single View режима */
@ -177,6 +230,91 @@ a{color:var(--link)}
display: block; display: block;
} }
/* Дополнительные стили для обеспечения правильного скролла в multi-view */
.multi-view-grid {
display: grid;
gap: 2px;
height: 100%;
padding: 0px;
/* Равная высота строк для нескольких рядов (3+ окон) */
grid-auto-rows: 1fr;
align-items: stretch;
}
.multi-view-panel {
background: var(--panel);
border: 1px solid var(--border);
border-radius: 8px;
display: flex;
flex-direction: column;
overflow: hidden;
padding: 2px;
height: 100%;
}
.multi-view-content {
flex: 1;
overflow: hidden;
display: flex;
flex-direction: column;
}
.multi-view-log {
height: 100%;
margin: 0;
padding: 12px;
font-size: 11px;
line-height: 1.4;
white-space: pre;
word-break: break-word;
overflow: auto;
background: var(--bg);
color: var(--fg);
font-family: ui-monospace, Menlo, Consolas, monospace;
flex: 1;
min-height: 0;
}
/* Принудительный сброс стилей для multi-view логов */
.multi-view-log {
height: 100% !important;
overflow: auto !important;
display: block !important;
max-height: none !important;
min-height: 200px !important;
position: relative !important;
flex: 1 !important;
min-height: 0 !important;
}
/* Универсальные стили для всех multi-view логов */
.multi-view-content .multi-view-log {
height: 100% !important;
overflow: auto !important;
display: block !important;
max-height: none !important;
min-height: 200px !important;
position: relative !important;
flex: 1 !important;
min-height: 0 !important;
width: 100% !important;
box-sizing: border-box !important;
}
/* Универсальные стили для всех multi-view логов в свернутом состоянии */
.sidebar.collapsed ~ .main-content .multi-view-content .multi-view-log {
height: 100% !important;
overflow: auto !important;
display: block !important;
max-height: none !important;
min-height: 200px !important;
position: relative !important;
flex: 1 !important;
min-height: 0 !important;
width: 100% !important;
box-sizing: border-box !important;
}
/* Всегда скрываем одиночный блок multi-view панели в Single View */ /* Всегда скрываем одиночный блок multi-view панели в Single View */
#multiViewPanel { #multiViewPanel {
display: none !important; display: none !important;
@ -3227,6 +3365,15 @@ async function setupMultiView() {
// Применяем настройки wrap lines // Применяем настройки wrap lines
applyWrapSettings(); applyWrapSettings();
// Принудительно обновляем стили логов для multi-view
setTimeout(() => {
updateLogStyles();
// Дополнительная проверка для multi-view логов
console.log('setupMultiView: Force fixing multi-view styles');
forceFixMultiViewStyles();
}, 200);
// Подключаем WebSocket для каждого контейнера // Подключаем WebSocket для каждого контейнера
console.log(`setupMultiView: Setting up WebSockets for ${state.selectedContainers.length} containers`); console.log(`setupMultiView: Setting up WebSockets for ${state.selectedContainers.length} containers`);
state.selectedContainers.forEach((containerId, index) => { state.selectedContainers.forEach((containerId, index) => {
@ -3477,13 +3624,13 @@ function applyWrapSettings() {
const wrapEnabled = els.wrapToggle && els.wrapToggle.checked; const wrapEnabled = els.wrapToggle && els.wrapToggle.checked;
const wrapStyle = wrapEnabled ? 'pre-wrap' : 'pre'; const wrapStyle = wrapEnabled ? 'pre-wrap' : 'pre';
// Применяем к обычному просмотру // Применяем к обычному просмотру (только к логам в основном контенте)
document.querySelectorAll('.log').forEach(el => { document.querySelectorAll('.main-content .log').forEach(el => {
el.style.whiteSpace = wrapStyle; el.style.whiteSpace = wrapStyle;
}); });
// Применяем к мультипросмотру // Применяем к мультипросмотру
document.querySelectorAll('.multi-view-log').forEach(el => { document.querySelectorAll('.multi-view-content .multi-view-log').forEach(el => {
el.style.whiteSpace = wrapStyle; el.style.whiteSpace = wrapStyle;
}); });
@ -4072,6 +4219,23 @@ function periodicCleanup() {
// Запускаем периодическую очистку каждые 2 секунды // Запускаем периодическую очистку каждые 2 секунды
setInterval(periodicCleanup, 2000); setInterval(periodicCleanup, 2000);
// Запускаем периодическую проверку стилей multi-view логов каждые 5 секунд
setInterval(() => {
if (state.multiViewMode) {
const multiViewLogs = document.querySelectorAll('.multi-view-content .multi-view-log');
if (multiViewLogs.length > 0) {
console.log('Periodic check: Force fixing multi-view styles');
forceFixMultiViewStyles();
// Дополнительно исправляем все контейнеры
console.log('Periodic check: Fixing all containers');
if (window.fixAllContainers) {
window.fixAllContainers();
}
}
}
}, 5000);
/** /**
* Функция для обработки специальных замен в MultiView логах * Функция для обработки специальных замен в MultiView логах
* Выполняет специфичные замены для улучшения читаемости логов * Выполняет специфичные замены для улучшения читаемости логов
@ -4803,6 +4967,8 @@ async function switchToSingle(svc){
// Обновляем счетчики для нового контейнера // Обновляем счетчики для нового контейнера
setTimeout(() => { setTimeout(() => {
recalculateCounters(); recalculateCounters();
// Применяем настройки wrap text после переключения контейнера
applyWrapSettings();
}, 500); // Небольшая задержка для завершения загрузки логов }, 500); // Небольшая задержка для завершения загрузки логов
await updateCounters(svc.id); await updateCounters(svc.id);
@ -4835,6 +5001,9 @@ async function openMulti(ids){
// Добавляем обработчики для счетчиков после открытия мульти-контейнеров // Добавляем обработчики для счетчиков после открытия мульти-контейнеров
addCounterClickHandlers(); addCounterClickHandlers();
// Применяем настройки wrap text после открытия мульти-контейнеров
applyWrapSettings();
} }
// ----- Copy on selection ----- // ----- Copy on selection -----
@ -5202,6 +5371,8 @@ async function refreshLogsAndCounters() {
// Пересчитываем счетчики на основе отображаемых логов // Пересчитываем счетчики на основе отображаемых логов
setTimeout(() => { setTimeout(() => {
recalculateMultiViewCounters(); recalculateMultiViewCounters();
// Применяем настройки wrap text после обновления
applyWrapSettings();
}, 1000); // Небольшая задержка для завершения переподключения }, 1000); // Небольшая задержка для завершения переподключения
} else if (state.current) { } else if (state.current) {
@ -5233,6 +5404,8 @@ async function refreshLogsAndCounters() {
// Пересчитываем счетчики на основе отображаемых логов // Пересчитываем счетчики на основе отображаемых логов
setTimeout(() => { setTimeout(() => {
recalculateCounters(); recalculateCounters();
// Применяем настройки wrap text после обновления
applyWrapSettings();
}, 1000); // Небольшая задержка для завершения переподключения }, 1000); // Небольшая задержка для завершения переподключения
} else { } else {
@ -5537,10 +5710,87 @@ function toggleSidebar() {
// Принудительно обновляем стили логов после переключения sidebar // Принудительно обновляем стили логов после переключения sidebar
setTimeout(() => { setTimeout(() => {
updateLogStyles(); updateLogStyles();
// Дополнительная проверка для multi-view логов
if (state.multiViewMode) {
console.log('Sidebar toggle: Force fixing multi-view styles');
forceFixMultiViewStyles();
}
}, 100); }, 100);
} }
} }
// Функция для принудительного исправления стилей multi-view логов
function forceFixMultiViewStyles() {
const multiViewLogs = document.querySelectorAll('.multi-view-content .multi-view-log');
console.log(`Force fixing styles for ${multiViewLogs.length} multi-view logs`);
multiViewLogs.forEach((log, index) => {
const containerId = log.getAttribute('data-container-id');
console.log(`Force fixing multi-view log ${index + 1} for container: ${containerId}`);
// Универсальное исправление для всех контейнеров
log.style.setProperty('height', '100%', 'important');
log.style.setProperty('overflow', 'auto', 'important');
log.style.setProperty('max-height', 'none', 'important');
log.style.setProperty('display', 'block', 'important');
log.style.setProperty('min-height', '200px', 'important');
log.style.setProperty('position', 'relative', 'important');
log.style.setProperty('flex', '1', 'important');
log.style.setProperty('min-height', '0', 'important');
log.style.setProperty('width', '100%', 'important');
log.style.setProperty('box-sizing', 'border-box', 'important');
// Принудительно вызываем пересчет layout
log.style.setProperty('transform', 'translateZ(0)', 'important');
// Устанавливаем универсальные inline стили для всех контейнеров
const currentStyle = log.getAttribute('style') || '';
const newStyle = currentStyle + '; height: 100% !important; overflow: auto !important; flex: 1 !important; min-height: 0 !important; width: 100% !important; box-sizing: border-box !important; display: block !important; position: relative !important;';
log.setAttribute('style', newStyle);
// Проверяем и исправляем родительские элементы для всех контейнеров
const parentContent = log.closest('.multi-view-content');
if (parentContent) {
parentContent.style.setProperty('display', 'flex', 'important');
parentContent.style.setProperty('flex-direction', 'column', 'important');
parentContent.style.setProperty('overflow', 'hidden', 'important');
parentContent.style.setProperty('height', '100%', 'important');
}
const parentPanel = log.closest('.multi-view-panel');
if (parentPanel) {
parentPanel.style.setProperty('display', 'flex', 'important');
parentPanel.style.setProperty('flex-direction', 'column', 'important');
parentPanel.style.setProperty('overflow', 'hidden', 'important');
parentPanel.style.setProperty('height', '100%', 'important');
}
});
// Также исправляем стили для multi-view-content контейнеров
const multiViewContents = document.querySelectorAll('.multi-view-content');
multiViewContents.forEach(content => {
content.style.setProperty('display', 'flex', 'important');
content.style.setProperty('flex-direction', 'column', 'important');
content.style.setProperty('overflow', 'hidden', 'important');
content.style.setProperty('height', '100%', 'important');
});
// Универсальное исправление для всех контейнеров
multiViewLogs.forEach(log => {
console.log(`Universal fix for container:`, log.getAttribute('data-container-id'));
// Принудительно устанавливаем все стили заново для всех контейнеров
log.style.cssText = 'height: 100% !important; overflow: auto !important; max-height: none !important; display: block !important; min-height: 200px !important; position: relative !important; flex: 1 !important; min-height: 0 !important; width: 100% !important; box-sizing: border-box !important;';
// Принудительно вызываем пересчет layout
log.style.setProperty('transform', 'translateZ(0)', 'important');
});
// Применяем настройки wrap text после исправления стилей
applyWrapSettings();
}
// Функция для обновления стилей логов // Функция для обновления стилей логов
function updateLogStyles() { function updateLogStyles() {
const isCollapsed = els.sidebar && els.sidebar.classList.contains('collapsed'); const isCollapsed = els.sidebar && els.sidebar.classList.contains('collapsed');
@ -5558,43 +5808,39 @@ function updateLogStyles() {
}); });
// Обновляем стили для multi-view логов (более агрессивно) // Обновляем стили для multi-view логов (более агрессивно)
const multiViewLogs = document.querySelectorAll('.multi-view-log'); const multiViewLogs = document.querySelectorAll('.multi-view-content .multi-view-log');
console.log(`Found ${multiViewLogs.length} multi-view logs to update`); console.log(`Found ${multiViewLogs.length} multi-view logs to update`);
multiViewLogs.forEach((log, index) => { multiViewLogs.forEach((log, index) => {
const containerId = log.getAttribute('data-container-id'); const containerId = log.getAttribute('data-container-id');
console.log(`Updating multi-view log ${index + 1}/${multiViewLogs.length} for container: ${containerId}`); console.log(`Updating multi-view log ${index + 1}/${multiViewLogs.length} for container: ${containerId}`);
if (isCollapsed) { // Принудительно устанавливаем правильные стили независимо от состояния sidebar
log.style.height = 'calc(100vh - var(--header-height))'; log.style.setProperty('height', '100%', 'important');
log.style.overflow = 'auto'; log.style.setProperty('overflow', 'auto', 'important');
log.style.maxHeight = 'none'; log.style.setProperty('max-height', 'none', 'important');
log.style.display = 'block'; log.style.setProperty('display', 'block', 'important');
log.style.minHeight = '200px'; log.style.setProperty('min-height', '200px', 'important');
log.style.position = 'relative'; log.style.setProperty('position', 'relative', 'important');
} else { log.style.setProperty('flex', '1', 'important');
log.style.height = '100%'; log.style.setProperty('min-height', '0', 'important');
log.style.overflow = 'auto';
log.style.maxHeight = 'none';
log.style.display = 'block';
log.style.minHeight = '200px';
log.style.position = 'relative';
}
// Принудительно вызываем пересчет layout // Принудительно вызываем пересчет layout
log.style.transform = 'translateZ(0)'; log.style.setProperty('transform', 'translateZ(0)', 'important');
}); });
// Также обновляем стили для multi-view-content контейнеров // Также обновляем стили для multi-view-content контейнеров
const multiViewContents = document.querySelectorAll('.multi-view-content'); const multiViewContents = document.querySelectorAll('.multi-view-content');
multiViewContents.forEach(content => { multiViewContents.forEach(content => {
if (isCollapsed) { if (isCollapsed) {
content.style.height = 'calc(100vh - var(--header-height) - 60px)'; // В свернутом состоянии multi-view-content должен иметь правильную высоту
content.style.overflow = 'hidden'; content.style.setProperty('height', 'calc(100vh - var(--header-height) - 60px)', 'important');
} else { } else {
content.style.height = '100%'; content.style.setProperty('height', '100%', 'important');
content.style.overflow = 'hidden';
} }
content.style.setProperty('overflow', 'hidden', 'important');
content.style.setProperty('display', 'flex', 'important');
content.style.setProperty('flex-direction', 'column', 'important');
}); });
// Применяем настройки wrap text // Применяем настройки wrap text
@ -5602,34 +5848,14 @@ function updateLogStyles() {
console.log('Log styles updated, sidebar collapsed:', isCollapsed, 'multi-view logs found:', multiViewLogs.length); console.log('Log styles updated, sidebar collapsed:', isCollapsed, 'multi-view logs found:', multiViewLogs.length);
// Принудительно исправляем стили multi-view логов
forceFixMultiViewStyles();
// Дополнительная проверка через 500ms для multi view логов // Дополнительная проверка через 500ms для multi view логов
if (multiViewLogs.length > 0) { if (multiViewLogs.length > 0) {
setTimeout(() => { setTimeout(() => {
console.log('Performing delayed update for multi-view logs...'); console.log('Performing delayed update for multi-view logs...');
const delayedLogs = document.querySelectorAll('.multi-view-log'); forceFixMultiViewStyles();
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); }, 500);
} }
} }
@ -5813,6 +6039,14 @@ document.addEventListener('DOMContentLoaded', () => {
// Применяем настройки wrap text при загрузке // Применяем настройки wrap text при загрузке
applyWrapSettings(); applyWrapSettings();
// Дополнительная проверка для multi-view логов при загрузке
setTimeout(() => {
if (state.multiViewMode) {
console.log('Initialization: Force fixing multi-view styles');
forceFixMultiViewStyles();
}
}, 1000);
}); });
if (els.snapshotBtn) { if (els.snapshotBtn) {
els.snapshotBtn.onclick = ()=>{ els.snapshotBtn.onclick = ()=>{
@ -6015,6 +6249,22 @@ if (els.lvlOther) {
}; };
} }
// Обработчик изменения размера окна для обновления стилей multi-view логов
window.addEventListener('resize', () => {
if (state.multiViewMode) {
console.log('Window resize: Force fixing multi-view styles');
setTimeout(() => {
forceFixMultiViewStyles();
// Дополнительно исправляем все контейнеры
console.log('Window resize: Fixing all containers');
if (window.fixAllContainers) {
window.fixAllContainers();
}
}, 100);
}
});
// Hotkeys: [ ] — navigation between containers, Ctrl+R/Ctrl+K — refresh logs // Hotkeys: [ ] — navigation between containers, Ctrl+R/Ctrl+K — refresh logs
window.addEventListener('keydown', async (e)=>{ window.addEventListener('keydown', async (e)=>{
// Проверяем, не находится ли фокус в поле ввода // Проверяем, не находится ли фокус в поле ввода
@ -6333,6 +6583,47 @@ window.addEventListener('keydown', async (e)=>{
window.cleanDuplicateLines = cleanDuplicateLines; window.cleanDuplicateLines = cleanDuplicateLines;
window.cleanSingleViewEmptyLines = cleanSingleViewEmptyLines; window.cleanSingleViewEmptyLines = cleanSingleViewEmptyLines;
// Добавляем функции для исправления стилей в глобальную область
window.forceFixMultiViewStyles = forceFixMultiViewStyles;
window.updateLogStyles = updateLogStyles;
// Универсальная функция для исправления всех контейнеров
window.fixAllContainers = function() {
console.log('Fixing all multi-view containers');
const allLogs = document.querySelectorAll('.multi-view-content .multi-view-log');
allLogs.forEach(log => {
const containerId = log.getAttribute('data-container-id');
console.log(`Fixing container:`, containerId);
// Принудительно устанавливаем все стили заново
log.style.cssText = 'height: 100% !important; overflow: auto !important; max-height: none !important; display: block !important; min-height: 200px !important; position: relative !important; flex: 1 !important; min-height: 0 !important; width: 100% !important; box-sizing: border-box !important;';
// Принудительно вызываем пересчет layout
log.style.setProperty('transform', 'translateZ(0)', 'important');
// Проверяем родительские элементы
const parentContent = log.closest('.multi-view-content');
if (parentContent) {
parentContent.style.cssText = 'display: flex !important; flex-direction: column !important; overflow: hidden !important; height: 100% !important;';
}
const parentPanel = log.closest('.multi-view-panel');
if (parentPanel) {
parentPanel.style.cssText = 'display: flex !important; flex-direction: column !important; overflow: hidden !important; height: 100% !important;';
}
});
// Применяем настройки wrap text после исправления всех контейнеров
applyWrapSettings();
};
// Оставляем старую функцию для обратной совместимости
window.fixProblematicContainers = function() {
console.log('fixProblematicContainers is deprecated, use fixAllContainers instead');
window.fixAllContainers();
};
console.log('LogBoard+ инициализирован с исправлениями дублирования строк и правильными переносами строк в Single View и MultiView режимах'); console.log('LogBoard+ инициализирован с исправлениями дублирования строк и правильными переносами строк в Single View и MultiView режимах');
console.log('Для тестирования используйте: testDuplicateRemoval(), testSingleViewDuplicateRemoval(), testSingleViewEmptyLinesRemoval() или testSingleViewLineBreaks()'); console.log('Для тестирования используйте: testDuplicateRemoval(), testSingleViewDuplicateRemoval(), testSingleViewEmptyLinesRemoval() или testSingleViewLineBreaks()');