@@ -226,6 +226,38 @@ body {
|
|||||||
border-right: 1px solid var(--border-light);
|
border-right: 1px solid var(--border-light);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
transition: all var(--transition-normal);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar.collapsed {
|
||||||
|
width: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar.collapsed .sidebar-header h3 {
|
||||||
|
opacity: 0;
|
||||||
|
width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar.collapsed .sidebar-header {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar.collapsed .sidebar-controls {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar.collapsed #refresh-files {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar.collapsed .file-list {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar.collapsed #collapse-sidebar i {
|
||||||
|
transform: rotate(180deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-header {
|
.sidebar-header {
|
||||||
@@ -236,6 +268,12 @@ body {
|
|||||||
border-bottom: 1px solid var(--border-light);
|
border-bottom: 1px solid var(--border-light);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sidebar-controls {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--spacing-sm);
|
||||||
|
}
|
||||||
|
|
||||||
.sidebar-header h3 {
|
.sidebar-header h3 {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -549,10 +587,58 @@ input:checked+.slider:before {
|
|||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
transition: all var(--transition-fast);
|
transition: all var(--transition-fast);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
white-space: pre-wrap;
|
white-space: nowrap;
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
background: rgba(255, 255, 255, 0.02);
|
background: rgba(255, 255, 255, 0.02);
|
||||||
border-left: 3px solid transparent;
|
border-left: 3px solid transparent;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-line::after {
|
||||||
|
content: " ...";
|
||||||
|
opacity: 0.5;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-line.expanded::after {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-line:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-line.expanded {
|
||||||
|
background: rgba(37, 99, 235, 0.1);
|
||||||
|
border-left-color: var(--primary-color);
|
||||||
|
white-space: pre-wrap;
|
||||||
|
overflow: visible;
|
||||||
|
text-overflow: unset;
|
||||||
|
animation: expandLine var(--transition-normal) ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes expandLine {
|
||||||
|
from {
|
||||||
|
max-height: 1.5em;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
max-height: 1000px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-line.auto-collapse {
|
||||||
|
animation: collapseCountdown 30s linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes collapseCountdown {
|
||||||
|
0% {
|
||||||
|
box-shadow: inset 0 -3px 0 var(--primary-color);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
box-shadow: inset 0 -3px 0 transparent;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.log-line:hover {
|
.log-line:hover {
|
||||||
@@ -722,6 +808,10 @@ input:checked+.slider:before {
|
|||||||
.sidebar {
|
.sidebar {
|
||||||
width: 250px;
|
width: 250px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sidebar.collapsed {
|
||||||
|
width: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
.nav-content {
|
.nav-content {
|
||||||
padding: var(--spacing-md);
|
padding: var(--spacing-md);
|
||||||
@@ -754,6 +844,20 @@ input:checked+.slider:before {
|
|||||||
border-right: none;
|
border-right: none;
|
||||||
border-bottom: 1px solid var(--border-light);
|
border-bottom: 1px solid var(--border-light);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sidebar.collapsed {
|
||||||
|
height: 60px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar.collapsed .sidebar-header {
|
||||||
|
justify-content: flex-start;
|
||||||
|
padding: var(--spacing-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar.collapsed .sidebar-controls {
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
.nav-info {
|
.nav-info {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|||||||
@@ -38,9 +38,14 @@
|
|||||||
<aside class="sidebar" id="sidebar">
|
<aside class="sidebar" id="sidebar">
|
||||||
<div class="sidebar-header">
|
<div class="sidebar-header">
|
||||||
<h3><i class="fas fa-folder-open"></i> Available Files</h3>
|
<h3><i class="fas fa-folder-open"></i> Available Files</h3>
|
||||||
<button id="refresh-files" class="btn btn-icon" title="Refresh file list">
|
<div class="sidebar-controls">
|
||||||
<i class="fas fa-sync-alt"></i>
|
<button id="collapse-sidebar" class="btn btn-icon" title="Collapse sidebar">
|
||||||
</button>
|
<i class="fas fa-chevron-left"></i>
|
||||||
|
</button>
|
||||||
|
<button id="refresh-files" class="btn btn-icon" title="Refresh file list">
|
||||||
|
<i class="fas fa-sync-alt"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="file-list" id="file-list">
|
<div class="file-list" id="file-list">
|
||||||
<div class="loading">
|
<div class="loading">
|
||||||
|
|||||||
@@ -47,6 +47,8 @@ class LogTailMonitor {
|
|||||||
// Sidebar
|
// Sidebar
|
||||||
fileList: document.getElementById('file-list'),
|
fileList: document.getElementById('file-list'),
|
||||||
refreshFilesBtn: document.getElementById('refresh-files'),
|
refreshFilesBtn: document.getElementById('refresh-files'),
|
||||||
|
collapseSidebarBtn: document.getElementById('collapse-sidebar'),
|
||||||
|
sidebar: document.getElementById('sidebar'),
|
||||||
|
|
||||||
// Controls
|
// Controls
|
||||||
autoScrollToggle: document.getElementById('auto-scroll'),
|
autoScrollToggle: document.getElementById('auto-scroll'),
|
||||||
@@ -74,8 +76,9 @@ class LogTailMonitor {
|
|||||||
* Bind event listeners
|
* Bind event listeners
|
||||||
*/
|
*/
|
||||||
bindEvents() {
|
bindEvents() {
|
||||||
// File refresh
|
// File refresh and sidebar collapse
|
||||||
this.elements.refreshFilesBtn.addEventListener('click', () => this.loadFiles());
|
this.elements.refreshFilesBtn.addEventListener('click', () => this.loadFiles());
|
||||||
|
this.elements.collapseSidebarBtn.addEventListener('click', () => this.toggleSidebar());
|
||||||
|
|
||||||
// Controls
|
// Controls
|
||||||
this.elements.autoScrollToggle.addEventListener('change', (e) => {
|
this.elements.autoScrollToggle.addEventListener('change', (e) => {
|
||||||
@@ -148,6 +151,23 @@ class LogTailMonitor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle sidebar visibility
|
||||||
|
*/
|
||||||
|
toggleSidebar() {
|
||||||
|
this.elements.sidebar.classList.toggle('collapsed');
|
||||||
|
const isCollapsed = this.elements.sidebar.classList.contains('collapsed');
|
||||||
|
const icon = this.elements.collapseSidebarBtn.querySelector('i');
|
||||||
|
|
||||||
|
if (isCollapsed) {
|
||||||
|
icon.className = 'fas fa-chevron-right';
|
||||||
|
this.elements.collapseSidebarBtn.title = 'Expand sidebar';
|
||||||
|
} else {
|
||||||
|
icon.className = 'fas fa-chevron-left';
|
||||||
|
this.elements.collapseSidebarBtn.title = 'Collapse sidebar';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load available files from the server
|
* Load available files from the server
|
||||||
*/
|
*/
|
||||||
@@ -391,18 +411,37 @@ class LogTailMonitor {
|
|||||||
// Classify log level
|
// Classify log level
|
||||||
this.classifyLogLine(logLine, content);
|
this.classifyLogLine(logLine, content);
|
||||||
|
|
||||||
|
// Store timeout reference for cleanup
|
||||||
|
let collapseTimeout = null;
|
||||||
|
|
||||||
// Add click handler for expansion
|
// Add click handler for expansion
|
||||||
logLine.addEventListener('click', () => {
|
logLine.addEventListener('click', () => {
|
||||||
logLine.classList.toggle('expanded');
|
const wasExpanded = logLine.classList.contains('expanded');
|
||||||
this.elements.autoScrollToggle.checked = false;
|
|
||||||
this.autoScroll = false;
|
|
||||||
|
|
||||||
// Auto-collapse after 20 seconds
|
if (wasExpanded) {
|
||||||
setTimeout(() => {
|
// Collapse manually
|
||||||
logLine.classList.remove('expanded');
|
logLine.classList.remove('expanded', 'auto-collapse');
|
||||||
this.elements.autoScrollToggle.checked = true;
|
if (collapseTimeout) {
|
||||||
this.autoScroll = true;
|
clearTimeout(collapseTimeout);
|
||||||
}, 20000);
|
collapseTimeout = null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Expand
|
||||||
|
logLine.classList.add('expanded');
|
||||||
|
this.elements.autoScrollToggle.checked = false;
|
||||||
|
this.autoScroll = false;
|
||||||
|
|
||||||
|
// Add visual countdown indicator
|
||||||
|
logLine.classList.add('auto-collapse');
|
||||||
|
|
||||||
|
// Auto-collapse after 30 seconds
|
||||||
|
collapseTimeout = setTimeout(() => {
|
||||||
|
logLine.classList.remove('expanded', 'auto-collapse');
|
||||||
|
this.elements.autoScrollToggle.checked = true;
|
||||||
|
this.autoScroll = true;
|
||||||
|
collapseTimeout = null;
|
||||||
|
}, 30000);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return logLine;
|
return logLine;
|
||||||
|
|||||||
Referencia en una nueva incidencia
Block a user