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

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

View File

@@ -131,8 +131,7 @@ a{color:var(--link)}
padding: 0 !important;
}
.sidebar.collapsed ~ .main-content .single-view-content .log,
.sidebar.collapsed ~ .main-content .multi-view-log {
.sidebar.collapsed ~ .main-content .single-view-content .log {
height: calc(100vh - var(--header-height)) !important;
overflow: auto !important;
display: block !important;
@@ -141,6 +140,21 @@ a{color:var(--link)}
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:not(.collapsed) ~ .main-content .single-view-content .log,
.sidebar:not(.collapsed) ~ .main-content .multi-view-log {
@@ -155,11 +169,50 @@ a{color:var(--link)}
.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:not(.collapsed) ~ .main-content .multi-view-content {
height: 100% !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 режима */
@@ -177,6 +230,91 @@ a{color:var(--link)}
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 */
#multiViewPanel {
display: none !important;
@@ -3227,6 +3365,15 @@ async function setupMultiView() {
// Применяем настройки wrap lines
applyWrapSettings();
// Принудительно обновляем стили логов для multi-view
setTimeout(() => {
updateLogStyles();
// Дополнительная проверка для multi-view логов
console.log('setupMultiView: Force fixing multi-view styles');
forceFixMultiViewStyles();
}, 200);
// Подключаем WebSocket для каждого контейнера
console.log(`setupMultiView: Setting up WebSockets for ${state.selectedContainers.length} containers`);
state.selectedContainers.forEach((containerId, index) => {
@@ -3477,13 +3624,13 @@ function applyWrapSettings() {
const wrapEnabled = els.wrapToggle && els.wrapToggle.checked;
const wrapStyle = wrapEnabled ? 'pre-wrap' : 'pre';
// Применяем к обычному просмотру
document.querySelectorAll('.log').forEach(el => {
// Применяем к обычному просмотру (только к логам в основном контенте)
document.querySelectorAll('.main-content .log').forEach(el => {
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;
});
@@ -4072,6 +4219,23 @@ function periodicCleanup() {
// Запускаем периодическую очистку каждые 2 секунды
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 логах
* Выполняет специфичные замены для улучшения читаемости логов
@@ -4803,6 +4967,8 @@ async function switchToSingle(svc){
// Обновляем счетчики для нового контейнера
setTimeout(() => {
recalculateCounters();
// Применяем настройки wrap text после переключения контейнера
applyWrapSettings();
}, 500); // Небольшая задержка для завершения загрузки логов
await updateCounters(svc.id);
@@ -4835,6 +5001,9 @@ async function openMulti(ids){
// Добавляем обработчики для счетчиков после открытия мульти-контейнеров
addCounterClickHandlers();
// Применяем настройки wrap text после открытия мульти-контейнеров
applyWrapSettings();
}
// ----- Copy on selection -----
@@ -5202,6 +5371,8 @@ async function refreshLogsAndCounters() {
// Пересчитываем счетчики на основе отображаемых логов
setTimeout(() => {
recalculateMultiViewCounters();
// Применяем настройки wrap text после обновления
applyWrapSettings();
}, 1000); // Небольшая задержка для завершения переподключения
} else if (state.current) {
@@ -5233,6 +5404,8 @@ async function refreshLogsAndCounters() {
// Пересчитываем счетчики на основе отображаемых логов
setTimeout(() => {
recalculateCounters();
// Применяем настройки wrap text после обновления
applyWrapSettings();
}, 1000); // Небольшая задержка для завершения переподключения
} else {
@@ -5537,10 +5710,87 @@ function toggleSidebar() {
// Принудительно обновляем стили логов после переключения sidebar
setTimeout(() => {
updateLogStyles();
// Дополнительная проверка для multi-view логов
if (state.multiViewMode) {
console.log('Sidebar toggle: Force fixing multi-view styles');
forceFixMultiViewStyles();
}
}, 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() {
const isCollapsed = els.sidebar && els.sidebar.classList.contains('collapsed');
@@ -5558,43 +5808,39 @@ function updateLogStyles() {
});
// Обновляем стили для 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`);
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';
}
// Принудительно устанавливаем правильные стили независимо от состояния sidebar
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.transform = 'translateZ(0)';
log.style.setProperty('transform', 'translateZ(0)', 'important');
});
// Также обновляем стили для 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';
// В свернутом состоянии multi-view-content должен иметь правильную высоту
content.style.setProperty('height', 'calc(100vh - var(--header-height) - 60px)', 'important');
} else {
content.style.height = '100%';
content.style.overflow = 'hidden';
content.style.setProperty('height', '100%', 'important');
}
content.style.setProperty('overflow', 'hidden', 'important');
content.style.setProperty('display', 'flex', 'important');
content.style.setProperty('flex-direction', 'column', 'important');
});
// Применяем настройки wrap text
@@ -5602,34 +5848,14 @@ function updateLogStyles() {
console.log('Log styles updated, sidebar collapsed:', isCollapsed, 'multi-view logs found:', multiViewLogs.length);
// Принудительно исправляем стили multi-view логов
forceFixMultiViewStyles();
// Дополнительная проверка через 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)';
});
forceFixMultiViewStyles();
}, 500);
}
}
@@ -5812,6 +6038,14 @@ document.addEventListener('DOMContentLoaded', () => {
// Применяем настройки wrap text при загрузке
applyWrapSettings();
// Дополнительная проверка для multi-view логов при загрузке
setTimeout(() => {
if (state.multiViewMode) {
console.log('Initialization: Force fixing multi-view styles');
forceFixMultiViewStyles();
}
}, 1000);
});
if (els.snapshotBtn) {
@@ -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
window.addEventListener('keydown', async (e)=>{
// Проверяем, не находится ли фокус в поле ввода
@@ -6333,6 +6583,47 @@ window.addEventListener('keydown', async (e)=>{
window.cleanDuplicateLines = cleanDuplicateLines;
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('Для тестирования используйте: testDuplicateRemoval(), testSingleViewDuplicateRemoval(), testSingleViewEmptyLinesRemoval() или testSingleViewLineBreaks()');