UI: фиксы сайдбара (обрезка container-name), стабильное обновление Tail Lines в SingleView без переподключения; улучшен UX и счетчики
This commit is contained in:
parent
6e764533d9
commit
705bc17097
@ -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 {
|
||||||
|
@ -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
|
||||||
|
@ -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>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user