connection fixes

This commit is contained in:
2025-10-06 09:41:23 +09:00
parent 4ceccae6ce
commit fa55367e68
361 changed files with 24633 additions and 6206 deletions

View File

@@ -21,7 +21,7 @@ class ConfigManager {
// Конфигурация по умолчанию
return {
server: {
url: 'http://localhost:3001',
url: 'http://localhost:3002',
autoConnect: false,
reconnectAttempts: 3,
reconnectInterval: 5000

File diff suppressed because it is too large Load Diff

View File

@@ -11,11 +11,101 @@
<!-- Header -->
<header class="app-header">
<div class="logo">
<h1>🔍 GodEye Operator</h1>
<!-- SVG Logo Icon -->
<svg width="32" height="32" viewBox="0 0 32 32" class="logo-icon">
<defs>
<linearGradient id="logoGradient" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#4CAF50"/>
<stop offset="100%" style="stop-color:#8BC34A"/>
</linearGradient>
</defs>
<circle cx="16" cy="16" r="15" fill="url(#logoGradient)" stroke="#2E7D32" stroke-width="2"/>
<circle cx="16" cy="16" r="8" fill="none" stroke="#fff" stroke-width="2"/>
<circle cx="16" cy="16" r="3" fill="#fff"/>
<path d="M8 8 L12 12 M24 8 L20 12 M8 24 L12 20 M24 24 L20 20" stroke="#fff" stroke-width="2" stroke-linecap="round"/>
</svg>
<h1>GodEye Operator</h1>
</div>
<div class="connection-status">
<span id="connection-indicator" class="status disconnected">● Отключен</span>
<span id="session-info"></span>
<div class="header-controls">
<!-- Connection Status & Info -->
<div class="connection-info">
<span id="connection-status-text">Не подключен</span>
<span id="ping-indicator">Ping: --</span>
</div>
<!-- Connection Button -->
<button id="connection-toggle" class="connection-toggle disconnected" title="Нажмите для подключения/отключения">
<svg id="connection-status-icon" width="16" height="16" viewBox="0 0 16 16" class="status-icon disconnected">
<circle cx="8" cy="8" r="6" fill="none" stroke="currentColor" stroke-width="2"/>
<circle cx="8" cy="8" r="2" fill="currentColor"/>
</svg>
<span id="connection-button-text">Отключен</span>
</button>
<!-- Settings Dropdown -->
<div class="settings-dropdown">
<button id="settings-toggle" class="settings-button" title="Настройки">
<svg width="20" height="20" viewBox="0 0 20 20">
<path fill="currentColor" d="M10 6a4 4 0 100 8 4 4 0 000-8zM8 10a2 2 0 114 0 2 2 0 01-4 0z"/>
<path fill-rule="evenodd" clip-rule="evenodd" fill="currentColor" d="M10 2a1 1 0 011 1v1.323l1.532.884a1 1 0 01.5.866v.654l1.532.884a1 1 0 010 1.732L13.032 10l1.532.884a1 1 0 010 1.732l-1.532.884v.654a1 1 0 01-.5.866L11 16.677V18a1 1 0 11-2 0v-1.323L7.468 15.793a1 1 0 01-.5-.866v-.654l-1.532-.884a1 1 0 010-1.732L6.968 10 5.436 9.116a1 1 0 010-1.732l1.532-.884v-.654a1 1 0 01.5-.866L9 4.323V3a1 1 0 011-1z"/>
</svg>
</button>
<div id="settings-menu" class="settings-menu">
<!-- Server URL -->
<div class="setting-item">
<label for="server-url">URL сервера:</label>
<input type="text" id="server-url" value="http://localhost:3001" placeholder="http://localhost:3001">
</div>
<!-- Auto-connect -->
<div class="setting-item">
<label>
<input type="checkbox" id="auto-connect"> Автоподключение
</label>
</div>
<!-- Advanced Settings Divider -->
<div class="setting-divider"></div>
<div class="setting-label">Расширенные настройки</div>
<!-- Connection Timeout -->
<div class="setting-item">
<label for="connection-timeout">Таймаут (мс):</label>
<input type="number" id="connection-timeout" value="10000" min="1000" max="60000">
</div>
<!-- Reconnect Attempts -->
<div class="setting-item">
<label for="reconnect-attempts">Попытки переподключения:</label>
<input type="number" id="reconnect-attempts" value="5" min="0" max="20">
</div>
<!-- Ping Interval -->
<div class="setting-item">
<label for="ping-interval">Интервал пинга (мс):</label>
<input type="number" id="ping-interval" value="25000" min="5000" max="120000">
</div>
<!-- Compression -->
<div class="setting-item">
<label>
<input type="checkbox" id="compression" checked> Сжатие данных
</label>
</div>
<!-- Debug Mode -->
<div class="setting-item">
<label>
<input type="checkbox" id="debug-mode"> Режим отладки
</label>
</div>
</div>
</div>
<!-- Session Info -->
<span id="session-info" class="session-info"></span>
</div>
</header>
@@ -99,69 +189,104 @@
<!-- Right Panel - Devices & Sessions -->
<div class="control-panel">
<!-- Connection Settings -->
<div class="connection-panel panel-section collapsible">
<div class="panel-header clickable" id="connection-toggle">
<h3>🔌 Подключение к серверу</h3>
<span class="collapse-icon collapsed"></span>
</div>
<div class="panel-content collapsed" id="connection-content">
<div class="input-group">
<label for="server-url">URL сервера:</label>
<input type="text" id="server-url" value="http://localhost:3001" placeholder="http://localhost:3001">
</div>
<div class="checkbox-group">
<label>
<input type="checkbox" id="auto-connect"> Автоматически подключаться при запуске
</label>
</div>
<div class="button-group">
<button id="connect-btn" class="btn-primary">Подключиться</button>
<button id="disconnect-btn" class="btn-secondary" disabled>Отключиться</button>
</div>
<div id="connection-info" class="connection-info">
<span id="connection-status-text">Не подключен</span>
<span id="ping-indicator">Ping: --</span>
</div>
</div>
<div class="sidebar-header">
<h2>Панель управления</h2>
<button id="sidebar-toggle" class="btn-collapse sidebar-collapse" title="Свернуть/развернуть сайдбар">
<span class="collapse-icon"></span>
</button>
</div>
<div id="sidebar-content" class="sidebar-content">
<!-- Available Devices -->
<div class="devices-panel panel-section">
<div class="devices-panel">
<div class="panel-header">
<h3>📱 Доступные устройства</h3>
<button id="refresh-devices" class="btn-icon" title="Обновить список">🔄</button>
<h3>Доступные устройства</h3>
<button id="devices-toggle" class="btn-collapse" title="Свернуть/развернуть">
<span class="collapse-icon"></span>
</button>
</div>
<div id="devices-list" class="devices-list compact-list">
<div class="no-devices">Нет подключенных устройств</div>
<div id="devices-content" class="panel-content">
<div class="devices-header" style="display: flex; justify-content: space-between; align-items: center;">
<span></span>
<button id="refresh-devices" class="btn-secondary btn-small" title="Обновить список устройств">🔄 Обновить</button>
</div>
<div id="devices-list" class="devices-list">
<div class="no-devices">Нет подключенных устройств</div>
</div>
</div>
</div>
<!-- Active Sessions -->
<div class="sessions-panel panel-section">
<h3>🔗 Активные сессии</h3>
<div id="sessions-list" class="sessions-list compact-list">
<div class="no-sessions">Нет активных сессий</div>
<div class="sessions-panel">
<div class="panel-header">
<h3>Активные сессии</h3>
<button id="sessions-toggle" class="btn-collapse" title="Свернуть/развернуть">
<span class="collapse-icon"></span>
</button>
</div>
<div id="sessions-content" class="panel-content">
<div id="sessions-list" class="sessions-list">
<div class="no-sessions">Нет активных сессий</div>
</div>
</div>
</div>
<!-- Session History -->
<div class="history-panel">
<div class="panel-header">
<h3>История сессий</h3>
<button id="history-toggle" class="btn-collapse" title="Свернуть/развернуть">
<span class="collapse-icon"></span>
</button>
</div>
<div id="history-content" class="history-content panel-content">
<div class="history-stats">
<div class="stat-item">
<span class="stat-label">Всего сессий:</span>
<span id="total-sessions">0</span>
</div>
<div class="stat-item">
<span class="stat-label">Успешных:</span>
<span id="successful-sessions">0</span>
</div>
<div class="stat-item">
<span class="stat-label">Общее время:</span>
<span id="total-duration">0:00</span>
</div>
</div>
<div class="history-controls">
<button id="export-history" class="btn-secondary btn-small">📊 Экспорт</button>
<button id="clear-history" class="btn-secondary btn-small">🗑️ Очистить</button>
</div>
<div id="history-list" class="history-list">
<div class="no-history">История пуста</div>
</div>
</div>
</div>
<!-- Logs -->
<div class="logs-panel panel-section collapsible">
<div class="panel-header clickable" id="logs-toggle">
<h3>📋 Журнал событий</h3>
<span class="collapse-icon"></span>
<div class="logs-panel">
<div class="panel-header">
<h3>Журнал событий</h3>
<button id="logs-toggle" class="btn-collapse" title="Свернуть/развернуть">
<span class="collapse-icon"></span>
</button>
</div>
<div class="panel-content" id="logs-content">
<div id="logs-container" class="logs-container"></div>
<div id="logs-content" class="panel-content">
<div id="logs-container" class="logs-container" style="max-height: 180px; overflow-y: auto;"></div>
<button id="clear-logs" class="btn-secondary btn-small">Очистить</button>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Toast Notifications Container -->
<div id="toast-container" class="toast-container"></div>
<!-- Scripts -->
<script src="/socket.io/socket.io.js"></script>
<script src="https://cdn.socket.io/4.8.1/socket.io.min.js"></script>
<script src="app.js"></script>
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: #1a1a1a;
background: linear-gradient(135deg, #1a1a1a 0%, #2d2d2d 100%);
color: #ffffff;
overflow: hidden;
}
@@ -20,34 +20,184 @@ body {
/* Header */
.app-header {
background: #2d2d2d;
padding: 10px 20px;
background: linear-gradient(135deg, #2d2d2d 0%, #3d3d3d 100%);
padding: 15px 25px;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 2px solid #333;
border-bottom: 2px solid #4CAF50;
box-shadow: 0 2px 10px rgba(0,0,0,0.3);
position: relative;
z-index: 1000;
}
.logo {
display: flex;
align-items: center;
gap: 12px;
}
.logo-icon {
animation: logoRotate 3s ease-in-out infinite;
}
@keyframes logoRotate {
0%, 100% { transform: rotate(0deg); }
50% { transform: rotate(5deg); }
}
.logo h1 {
font-size: 20px;
color: #4CAF50;
font-size: 22px;
font-weight: 700;
background: linear-gradient(135deg, #4CAF50, #8BC34A);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
text-shadow: 0 2px 4px rgba(0,0,0,0.3);
}
.connection-status {
.header-controls {
display: flex;
gap: 15px;
align-items: center;
gap: 20px;
}
.status {
padding: 4px 12px;
border-radius: 12px;
.connection-info {
display: flex;
flex-direction: column;
align-items: flex-end;
font-size: 12px;
font-weight: bold;
color: #888;
}
.status.connected {
background: #4CAF50;
.connection-toggle {
display: flex;
align-items: center;
gap: 8px;
padding: 8px 16px;
border: none;
border-radius: 20px;
font-size: 13px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 2px 8px rgba(0,0,0,0.2);
}
.connection-toggle.connected {
background: linear-gradient(135deg, #4CAF50, #66BB6A);
color: white;
box-shadow: 0 2px 12px rgba(76, 175, 80, 0.4);
}
.connection-toggle.disconnected {
background: linear-gradient(135deg, #f44336, #ef5350);
color: white;
box-shadow: 0 2px 12px rgba(244, 67, 54, 0.4);
}
.connection-toggle:hover {
transform: translateY(-1px);
box-shadow: 0 4px 16px rgba(0,0,0,0.3);
}
.settings-dropdown {
position: relative;
}
.settings-button {
background: none;
border: 1px solid #555;
border-radius: 8px;
padding: 8px;
color: #888;
cursor: pointer;
transition: all 0.2s ease;
}
.settings-button:hover {
background: rgba(255,255,255,0.1);
color: #fff;
border-color: #4CAF50;
}
.settings-menu {
position: absolute;
top: 100%;
right: 0;
background: linear-gradient(135deg, #2d2d2d 0%, #3d3d3d 100%);
border: 1px solid #555;
border-radius: 12px;
padding: 20px;
min-width: 300px;
max-height: 400px;
overflow-y: auto;
box-shadow: 0 8px 32px rgba(0,0,0,0.4);
z-index: 2000;
display: none;
}
.settings-menu.show {
display: block;
animation: settingsSlideIn 0.2s ease-out;
}
@keyframes settingsSlideIn {
0% {
opacity: 0;
transform: translateY(-10px);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
.setting-item {
margin-bottom: 15px;
}
.setting-item label {
display: block;
color: #ccc;
font-size: 13px;
margin-bottom: 5px;
}
.setting-item input[type="text"],
.setting-item input[type="number"] {
width: 100%;
padding: 8px 12px;
background: rgba(255,255,255,0.1);
border: 1px solid #555;
border-radius: 6px;
color: #fff;
font-size: 13px;
}
.setting-item input[type="checkbox"] {
margin-right: 8px;
}
.setting-divider {
height: 1px;
background: #555;
margin: 15px 0;
}
.setting-label {
font-weight: 600;
color: #4CAF50;
font-size: 12px;
text-transform: uppercase;
margin-bottom: 10px;
}
.session-info {
font-size: 12px;
color: #4CAF50;
font-weight: 500;
}
color: white;
}
@@ -306,91 +456,19 @@ body {
background: #252525;
display: flex;
flex-direction: column;
max-width: 380px;
min-width: 300px;
transition: max-width 0.3s ease;
}
.control-panel.logs-collapsed {
max-width: 320px;
max-width: 350px;
}
.control-panel > div {
padding: 15px;
border-bottom: 1px solid #333;
}
/* Panel Sections */
.panel-section {
padding: 12px;
transition: all 0.3s ease;
}
.panel-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
.panel-header.clickable {
cursor: pointer;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.panel-header.clickable:hover {
background: rgba(255, 255, 255, 0.05);
margin: -5px;
padding: 5px;
border-radius: 4px;
}
.collapse-icon {
font-size: 12px;
transition: transform 0.3s ease;
color: #888;
}
.collapse-icon.collapsed {
transform: rotate(180deg);
}
.panel-content {
transition: all 0.3s ease;
overflow: hidden;
}
.panel-content.collapsed {
max-height: 0;
padding: 0;
opacity: 0;
}
/* Connection Panel */
.connection-panel {
transition: all 0.3s ease;
}
.connection-panel.collapsed {
flex: none;
}
.connection-panel h3 {
margin-bottom: 8px;
margin-bottom: 15px;
color: #4CAF50;
font-size: 13px;
display: flex;
align-items: center;
gap: 6px;
justify-content: space-between;
width: 100%;
}
.connection-panel h3 span {
font-size: 10px;
margin-left: auto;
font-size: 14px;
}
.input-group {
@@ -457,53 +535,32 @@ button:disabled {
cursor: not-allowed;
}
/* Devices and Sessions Panels */
.devices-panel,
.sessions-panel {
flex: 1;
min-height: 0;
}
/* Devices Panel */
.devices-panel h3,
.sessions-panel h3 {
margin-bottom: 8px;
margin-bottom: 15px;
color: #2196F3;
font-size: 13px;
display: flex;
align-items: center;
gap: 6px;
font-size: 14px;
}
.compact-list {
max-height: 180px;
.devices-list,
.sessions-list {
max-height: 200px;
overflow-y: auto;
transition: max-height 0.3s ease;
}
.control-panel.logs-collapsed .compact-list {
max-height: 250px;
}
.device-item,
.session-item {
background: #3a3a3a;
margin-bottom: 6px;
padding: 8px;
margin-bottom: 8px;
padding: 10px;
border-radius: 4px;
border-left: 3px solid #4CAF50;
transition: all 0.2s ease;
}
.device-item:hover,
.session-item:hover {
background: #404040;
transform: translateY(-1px);
}
.device-info {
font-size: 11px;
margin-bottom: 4px;
line-height: 1.3;
font-size: 12px;
margin-bottom: 5px;
}
.device-info strong {
@@ -511,56 +568,28 @@ button:disabled {
}
.device-capabilities {
font-size: 10px;
font-size: 11px;
color: #888;
margin-bottom: 6px;
line-height: 1.2;
margin-bottom: 8px;
}
.device-actions {
display: flex;
gap: 4px;
flex-wrap: wrap;
gap: 5px;
}
.btn-device {
background: #2196F3;
border: none;
color: white;
padding: 3px 6px;
padding: 4px 8px;
border-radius: 3px;
cursor: pointer;
font-size: 9px;
transition: all 0.2s ease;
font-size: 10px;
}
.btn-device:hover {
background: #1976D2;
transform: scale(1.05);
}
.btn-device.btn-success {
background: #4CAF50;
}
.btn-device.btn-success:hover {
background: #45a049;
}
.btn-icon {
background: none;
border: none;
color: #888;
cursor: pointer;
font-size: 14px;
padding: 2px;
border-radius: 3px;
transition: all 0.2s ease;
}
.btn-icon:hover {
color: #fff;
background: rgba(255, 255, 255, 0.1);
}
.no-devices,
@@ -571,119 +600,29 @@ button:disabled {
padding: 20px;
}
/* Session styles */
.session-item {
border-left: 3px solid #2196F3;
}
.session-item.session-pending {
border-left-color: #FF9800;
}
.session-item.session-active {
border-left-color: #4CAF50;
}
.session-item.session-rejected {
border-left-color: #f44336;
}
.session-item.session-ended {
border-left-color: #666;
opacity: 0.7;
}
.session-item.active {
background: #4a4a4a;
border: 2px solid #4CAF50;
border-left: 3px solid #4CAF50;
}
.session-header {
font-size: 12px;
margin-bottom: 8px;
}
.session-header strong {
color: #4CAF50;
}
.status-pending { color: #FF9800; }
.status-active { color: #4CAF50; }
.status-rejected { color: #f44336; }
.status-ended { color: #666; }
.session-actions {
display: flex;
gap: 4px;
flex-wrap: wrap;
}
.btn-small {
padding: 2px 6px;
font-size: 10px;
border: none;
border-radius: 2px;
cursor: pointer;
color: white;
}
.btn-small.btn-primary {
background: #2196F3;
}
.btn-small.btn-success {
background: #4CAF50;
}
.btn-small.btn-secondary {
background: #757575;
}
.btn-small.btn-danger {
background: #f44336;
}
.btn-small:hover {
opacity: 0.8;
}
/* Logs Panel */
.logs-panel {
transition: all 0.3s ease;
}
.logs-panel.collapsed {
flex: none;
flex: 1;
display: flex;
flex-direction: column;
}
.logs-panel h3 {
margin-bottom: 8px;
margin-bottom: 15px;
color: #FF9800;
font-size: 13px;
display: flex;
align-items: center;
gap: 6px;
font-size: 14px;
}
.logs-container {
flex: 1;
background: #1a1a1a;
border: 1px solid #333;
border-radius: 4px;
padding: 6px;
padding: 8px;
font-family: 'Courier New', monospace;
font-size: 10px;
font-size: 11px;
overflow-y: auto;
margin-bottom: 8px;
max-height: 120px;
transition: max-height 0.3s ease;
}
.control-panel.logs-collapsed .logs-container {
max-height: 0;
padding: 0;
margin: 0;
border: none;
margin-bottom: 10px;
}
.log-entry {
@@ -906,4 +845,399 @@ button:disabled {
border-right: 1px solid #333;
border-bottom: none;
}
}
/* Toast Notifications */
.toast-container {
position: fixed;
top: 20px;
right: 20px;
z-index: 10000;
display: flex;
flex-direction: column;
gap: 10px;
pointer-events: none;
}
.toast {
background: linear-gradient(135deg, #2d2d2d 0%, #3d3d3d 100%);
color: white;
padding: 16px 20px;
border-radius: 12px;
box-shadow: 0 8px 32px rgba(0,0,0,0.3);
border-left: 4px solid #4CAF50;
min-width: 300px;
max-width: 400px;
position: relative;
pointer-events: auto;
opacity: 0;
transform: translateX(100%);
animation: toastSlideIn 0.3s ease-out forwards;
transition: all 0.3s ease;
}
.toast.removing {
animation: toastSlideOut 0.3s ease-in forwards;
}
.toast.success {
border-left-color: #4CAF50;
}
.toast.error {
border-left-color: #f44336;
}
.toast.warning {
border-left-color: #ff9800;
}
.toast.info {
border-left-color: #2196F3;
}
.toast-content {
display: flex;
align-items: flex-start;
gap: 12px;
}
.toast-icon {
width: 24px;
height: 24px;
flex-shrink: 0;
margin-top: 2px;
}
.toast-text {
flex: 1;
}
.toast-title {
font-weight: 600;
font-size: 14px;
margin-bottom: 4px;
}
.toast-message {
font-size: 13px;
opacity: 0.9;
line-height: 1.4;
}
.toast-close {
position: absolute;
top: 8px;
right: 8px;
background: none;
border: none;
color: #888;
cursor: pointer;
font-size: 18px;
width: 24px;
height: 24px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
transition: all 0.2s ease;
}
.toast-close:hover {
background: rgba(255,255,255,0.1);
color: white;
}
@keyframes toastSlideIn {
0% {
opacity: 0;
transform: translateX(100%);
}
100% {
opacity: 1;
transform: translateX(0);
}
}
@keyframes toastSlideOut {
0% {
opacity: 1;
transform: translateX(0);
}
100% {
opacity: 0;
transform: translateX(100%);
}
}
/* Advanced Settings */
.advanced-toggle {
margin: 10px 0;
}
.btn-link {
background: none;
border: none;
color: #4CAF50;
cursor: pointer;
font-size: 13px;
text-decoration: underline;
padding: 4px 0;
transition: color 0.2s ease;
}
.btn-link:hover {
color: #66BB6A;
}
.advanced-settings {
background: rgba(255,255,255,0.05);
border: 1px solid #333;
border-radius: 8px;
padding: 15px;
margin: 10px 0;
animation: slideDown 0.3s ease-out;
}
.advanced-settings.hidden {
display: none !important;
}
.settings-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 15px;
}
.settings-grid .input-group,
.settings-grid .checkbox-group {
margin: 0;
}
@media (max-width: 1200px) {
.settings-grid {
grid-template-columns: 1fr;
}
}
@keyframes slideDown {
0% {
opacity: 0;
transform: translateY(-10px);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
/* Session History */
.history-panel {
background: #2d2d2d;
border-radius: 12px;
margin-bottom: 20px;
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
overflow: hidden;
}
/* Collapsible Panels */
.panel-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 15px 20px;
background: linear-gradient(135deg, #3d3d3d, #4d4d4d);
border-bottom: 1px solid #555;
cursor: pointer;
transition: background 0.2s ease;
}
.panel-header:hover {
background: linear-gradient(135deg, #4d4d4d, #5d5d5d);
}
.panel-header h3 {
color: #4CAF50;
margin: 0;
font-size: 16px;
}
.panel-content {
padding: 20px;
transition: all 0.3s ease;
max-height: 500px;
overflow: hidden;
}
.panel-content.collapsed {
max-height: 0;
padding: 0 20px;
}
.btn-collapse {
background: none;
border: none;
color: #888;
cursor: pointer;
padding: 4px 8px;
border-radius: 4px;
transition: all 0.2s ease;
font-size: 16px;
}
.btn-collapse:hover {
background: rgba(255,255,255,0.1);
color: #fff;
}
.collapse-icon {
transition: transform 0.2s ease;
display: inline-block;
}
.collapse-icon.collapsed {
transform: rotate(180deg);
}
/* Sidebar Collapsible */
.sidebar-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 15px 20px;
background: linear-gradient(135deg, #3d3d3d, #4d4d4d);
border-bottom: 2px solid #4CAF50;
}
.sidebar-header h2 {
color: #4CAF50;
margin: 0;
font-size: 18px;
font-weight: 600;
}
.sidebar-collapse {
font-size: 18px;
}
.sidebar-content {
transition: all 0.3s ease;
overflow: hidden;
}
.control-panel.collapsed .sidebar-content {
display: none;
}
.control-panel.collapsed {
width: 60px;
min-width: 60px;
}
.control-panel.collapsed .sidebar-header {
padding: 15px 10px;
}
.control-panel.collapsed .sidebar-header h2 {
display: none;
}
.control-panel.collapsed .collapse-icon {
transform: rotate(180deg);
}
.history-panel {
background: #2d2d2d;
border-radius: 12px;
margin-bottom: 20px;
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
overflow: hidden;
}
.history-stats {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 15px;
margin-bottom: 15px;
}
.stat-item {
background: rgba(76, 175, 80, 0.1);
padding: 10px;
border-radius: 8px;
text-align: center;
border: 1px solid rgba(76, 175, 80, 0.3);
}
.stat-label {
display: block;
font-size: 12px;
color: #888;
margin-bottom: 4px;
}
.stat-item span:last-child {
font-size: 16px;
font-weight: 600;
color: #4CAF50;
}
.history-controls {
display: flex;
gap: 10px;
margin-bottom: 15px;
}
.history-list {
max-height: 200px;
overflow-y: auto;
}
.history-item {
background: rgba(255,255,255,0.05);
padding: 12px;
border-radius: 8px;
margin-bottom: 8px;
border-left: 3px solid #4CAF50;
transition: all 0.2s ease;
}
.history-item:hover {
background: rgba(255,255,255,0.08);
transform: translateY(-1px);
}
.history-item.failed {
border-left-color: #f44336;
}
.history-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 6px;
}
.history-device {
font-weight: 600;
color: #fff;
}
.history-duration {
font-size: 12px;
color: #4CAF50;
}
.history-details {
font-size: 12px;
color: #888;
display: flex;
justify-content: space-between;
}
.no-history {
text-align: center;
color: #666;
font-style: italic;
padding: 20px;
}