UI: фиксы сайдбара (обрезка container-name), стабильное обновление Tail Lines в SingleView без переподключения; улучшен UX и счетчики

This commit is contained in:
Sergey Antropoff 2025-09-04 17:29:06 +03:00
parent 6e764533d9
commit 705bc17097
3 changed files with 84 additions and 19 deletions

View File

@ -1947,6 +1947,7 @@ a{color:var(--link)}
.container-list { .container-list {
flex: 1; flex: 1;
overflow-y: auto; overflow-y: auto;
overflow-x: hidden; /* запрещаем горизонтальный скролл и обрезаем переполнение */
padding: 16px; padding: 16px;
} }
@ -1959,6 +1960,10 @@ a{color:var(--link)}
cursor: pointer; cursor: pointer;
transition: all 0.2s ease; transition: all 0.2s ease;
position: relative; position: relative;
overflow: hidden;
min-width: 0;
width: 100%;
box-sizing: border-box;
} }
.container-item:hover { .container-item:hover {
@ -1990,12 +1995,33 @@ a{color:var(--link)}
display: flex; display: flex;
align-items: center; align-items: center;
gap: 8px; gap: 8px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
min-width: 0;
width: 100%;
}
.container-name i {
flex: 0 0 auto;
}
.container-name-text {
flex: 1 1 auto;
min-width: 0;
max-width: 100%;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
} }
.container-service { .container-service {
font-size: 11px; font-size: 11px;
color: var(--muted); color: var(--muted);
margin-bottom: 4px; margin-bottom: 4px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
} }
.container-status { .container-status {

View File

@ -143,6 +143,9 @@ function determineWsState() {
} else if (obj.ws.readyState === WebSocket.CLOSED || obj.ws.readyState === WebSocket.CLOSING) { } else if (obj.ws.readyState === WebSocket.CLOSED || obj.ws.readyState === WebSocket.CLOSING) {
closedConnections.push(id); closedConnections.push(id);
} }
} else if (obj && obj.ajaxOnly) {
// Не удаляем ajax-only объекты из state.open, они используются для AJAX обновлений
continue;
} else { } else {
closedConnections.push(id); closedConnections.push(id);
} }
@ -150,6 +153,8 @@ function determineWsState() {
// Удаляем закрытые соединения // Удаляем закрытые соединения
closedConnections.forEach(id => { closedConnections.forEach(id => {
const obj = state.open[id];
if (obj && obj.ajaxOnly) return; // сохраняем ajax-only объекты
delete state.open[id]; delete state.open[id];
}); });
@ -851,9 +856,9 @@ function buildTabs(){
svc.status === 'stopped' ? 'stopped' : 'paused'; svc.status === 'stopped' ? 'stopped' : 'paused';
item.innerHTML = ` item.innerHTML = `
<div class="container-name"> <div class="container-name" title="${escapeHtml(svc.name)}">
<i class="fas fa-cube"></i> <i class="fas fa-cube"></i>
${escapeHtml(svc.name)} <span class="container-name-text">${escapeHtml(svc.name)}</span>
</div> </div>
<div class="container-service"> <div class="container-service">
${escapeHtml(svc.service || svc.name)} ${escapeHtml(svc.service || svc.name)}
@ -2395,8 +2400,9 @@ function openWs(svc, panel){
els.logContent.innerHTML = ''; els.logContent.innerHTML = '';
} }
// Также очищаем legacy элемент лога // Также очищаем legacy элемент лога
if (obj.logEl) { const current = state.open[id];
obj.logEl.innerHTML = ''; if (current && current.logEl) {
current.logEl.innerHTML = '';
} }
// Принудительно проверяем состояние через AJAX через 500мс и 1 секунду // Принудительно проверяем состояние через AJAX через 500мс и 1 секунду
@ -4531,10 +4537,24 @@ if (els.snapshotBtn) {
}; };
} }
if (els.tail) { if (els.tail) {
els.tail.onchange = ()=> { els.tail.onchange = async ()=> {
Object.keys(state.open).forEach(id=>{ // Single View: не переподключаем WS, просто пересчитываем отображение и счетчики
if (!state.multiViewMode) {
if (els.logContent) {
updateLogVisibility(els.logContent);
} else {
refreshAllLogs();
}
// Обновляем счетчики и прокрутку
setTimeout(() => {
recalculateCounters();
scrollToBottom();
}, 50);
return;
}
for (const id of Object.keys(state.open)){
const svc = state.services.find(s=> s.id===id); const svc = state.services.find(s=> s.id===id);
if (!svc) return; if (!svc) continue;
// В multi view режиме используем openMultiViewWs // В multi view режиме используем openMultiViewWs
if (state.multiViewMode && state.selectedContainers.includes(id)) { if (state.multiViewMode && state.selectedContainers.includes(id)) {
@ -4543,17 +4563,23 @@ if (els.tail) {
} else { } else {
// В обычном режиме используем openWs // В обычном режиме используем openWs
const panel = els.grid.querySelector(`.panel[data-cid="${id}"]`); const panel = els.grid.querySelector(`.panel[data-cid="${id}"]`);
if (!panel) return; if (!panel) {
state.open[id].logEl.textContent=''; // В современном интерфейсе панели может не быть — переподключаем через switchToSingle только для текущего контейнера
if (state.current && state.current.id === id) {
closeWs(id);
// Переключаемся на тот же контейнер, чтобы пересоздать WS с новым tail
await switchToSingle(svc);
}
continue;
}
if (state.open[id] && state.open[id].logEl) {
state.open[id].logEl.textContent='';
}
closeWs(id); closeWs(id);
openWs(svc, panel); openWs(svc, panel);
} }
});
// Обновляем современный интерфейс
if (state.current && els.logContent) {
els.logContent.textContent = 'Reconnecting...';
} }
// MultiView: не трогаем здесь UI — trimming делается ниже в делегированном обработчике
// Пересчитываем счетчики после изменения Tail Lines // Пересчитываем счетчики после изменения Tail Lines
setTimeout(() => { setTimeout(() => {
@ -5437,11 +5463,24 @@ function reinitializeElements() {
* @param {Array} newLogs - Массив новых логов * @param {Array} newLogs - Массив новых логов
*/ */
function appendNewLogsForContainer(containerId, newLogs) { function appendNewLogsForContainer(containerId, newLogs) {
const obj = state.open[containerId]; let obj = state.open[containerId];
if (!obj) { if (!obj) {
console.warn(`AJAX Update: Object not found for container ${containerId}`); // Лениво инициализируем объект для AJAX-обновлений, когда WS ещё не открыт
return; const multiViewLog = document.querySelector(`.multi-view-log[data-container-id="${containerId}"]`);
const logEl = els.logContent || multiViewLog || null;
state.open[containerId] = {
ws: null,
logEl: logEl,
wrapEl: logEl ? logEl.parentElement : null,
counters: {dbg:0, info:0, warn:0, err:0, other:0},
pausedBuffer: [],
serviceName: containerId,
allLogs: [],
ajaxOnly: true
};
obj = state.open[containerId];
console.warn(`AJAX Update: Created ajaxOnly state for container ${containerId}`);
} }
// Обрабатываем каждую новую строку лога через handleLine // Обрабатываем каждую новую строку лога через handleLine

View File

@ -309,7 +309,7 @@
Автор: <a href="https://devops.org.ru" target="_blank">Сергей Антропов</a> Автор: <a href="https://devops.org.ru" target="_blank">Сергей Антропов</a>
</div> </div>
<div class="help-tooltip-version"> <div class="help-tooltip-version">
Версия 2.0 Версия 1.0
</div> </div>
</div> </div>
</div> </div>