diff --git a/cmd/server/main.go b/cmd/server/main.go index b03e3fe..e23c3c1 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -96,7 +96,7 @@ func (a *API) HandleLogFiles(w http.ResponseWriter, r *http.Request) { }) } - // Сортируем по времени (новые сверху) + // Сортируем по времени sort.Slice(result, func(i, j int) bool { return result[i].ModTime.After(result[j].ModTime) }) @@ -104,7 +104,7 @@ func (a *API) HandleLogFiles(w http.ResponseWriter, r *http.Request) { writeJSON(w, result) } -// HandleLogEvents - чтение событий +// HandleLogEvents - чтение событий с пагинацией func (a *API) HandleLogEvents(w http.ResponseWriter, r *http.Request) { // Параметры пагинации page := 1 @@ -160,18 +160,19 @@ func (a *API) HandleLogEvents(w http.ResponseWriter, r *http.Request) { page = 1 } - // Вычисляем смещение (нумерация страниц с 1) - // Страница 1 → самые ранние записи (начало файла) - // Страница N → самые поздние записи (конец файла) - start := (page - 1) * pageSize + start := total - page*pageSize + if start < 0 { + start = 0 + } + end := start + pageSize if end > total { end = total } - // Получаем строки для текущей страницы + // Получаем строки для текущей страницы (в прямом порядке) var resultLines []string - if start < total { + if start < total && start >= 0 { resultLines = lines[start:end] } else { resultLines = []string{} @@ -203,11 +204,12 @@ func (a *API) HandleLogEvents(w http.ResponseWriter, r *http.Request) { "page": page, "page_size": pageSize, "total_pages": totalPages, - "has_previous": page > 1, - "has_next": page < totalPages, + "has_previous": page > 1, // Есть более старые записи + "has_next": page < totalPages, // Есть более новые записи "returned": len(events), "start_index": start + 1, "end_index": end, + "order": "newest_first", }) } diff --git a/cmd/server/web/css/base.css b/cmd/server/web/css/base.css index 2ffc672..c5a3240 100644 --- a/cmd/server/web/css/base.css +++ b/cmd/server/web/css/base.css @@ -1,4 +1,3 @@ -/* Базовые стили (сброс, body, общие настройки) */ /* ===== БАЗОВЫЕ СТИЛИ ===== */ :root { /* Основные шрифты */ @@ -7,13 +6,13 @@ --font-tech: 'Share Tech Mono', 'Courier New', monospace; /* Размеры */ - --font-size-xs: 10px; - --font-size-sm: 12px; - --font-size-base: 14px; - --font-size-lg: 16px; - --font-size-xl: 18px; - --font-size-2xl: 22px; - --font-size-3xl: 28px; + --font-size-xs: 11px; + --font-size-sm: 13px; + --font-size-base: 15px; + --font-size-lg: 17px; + --font-size-xl: 20px; + --font-size-2xl: 24px; + --font-size-3xl: 30px; /* Веса */ --font-weight-normal: 400; @@ -28,11 +27,11 @@ body { margin: 0; font-family: var(--font-mono); - background: #050805; + background: #0a0f0a; /* Чуть светлее для лучшего контраста */ color: #00ff88; padding: 12px; font-size: var(--font-size-base); - line-height: 1.6; + line-height: 1.7; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } @@ -40,15 +39,16 @@ body { /* ===== ЗАГОЛОВКИ ===== */ h1, h2, h3, h4, h5, h6 { font-family: var(--font-display); - letter-spacing: 0.05em; + letter-spacing: 0.08em; text-transform: uppercase; font-weight: var(--font-weight-bold); margin: 0; + color: #00ff88; } h1 { font-size: var(--font-size-2xl); - text-shadow: 0 0 20px rgba(0, 255, 136, 0.3); + text-shadow: 0 0 30px rgba(0, 255, 136, 0.2); } h2 { @@ -65,18 +65,19 @@ code, pre, .mono { font-weight: var(--font-weight-normal); } -/* ===== ТЕХНИЧЕСКИЙ ТЕКСТ (статусы, значения) ===== */ +/* ===== ТЕХНИЧЕСКИЙ ТЕКСТ ===== */ .status-text, .value-text, .signal-text, .tech-text { font-family: var(--font-tech); - letter-spacing: 0.02em; + letter-spacing: 0.03em; } /* ===== УЛУЧШЕНИЯ ЧИТАЕМОСТИ ===== */ .text-muted { opacity: 0.6; + color: #88ffbb; } .text-bold { @@ -85,4 +86,45 @@ code, pre, .mono { .text-semibold { font-weight: var(--font-weight-semibold); -} \ No newline at end of file +} + +/* ===== ССЫЛКИ ===== */ +a { + color: #00ff88; + text-decoration: none; + transition: all 0.3s ease; + font-weight: var(--font-weight-semibold); +} + +a:hover { + color: #66ffbb; + text-shadow: 0 0 25px rgba(0, 255, 136, 0.2); +} +/* ===== УЛУЧШЕННЫЙ СКРОЛЛ НА МОБИЛЬНЫХ ===== */ +* { + -webkit-tap-highlight-color: transparent; +} + +/* Плавный скролл */ +html { + scroll-behavior: smooth; +} + +/* Улучшенный скролл на мобильных */ +.panel, +.logs-content, +.logs-sidebar, +#history, +#dataTableContainer { + -webkit-overflow-scrolling: touch; + overscroll-behavior: contain; +} + +/* Убираем скролл-бары на мобильных где не нужно */ +@media (max-width: 768px) { + .panel::-webkit-scrollbar, + .logs-content::-webkit-scrollbar, + .logs-sidebar::-webkit-scrollbar { + width: 3px; + } +} diff --git a/cmd/server/web/css/header.css b/cmd/server/web/css/header.css index 93686b2..0c2ee7c 100644 --- a/cmd/server/web/css/header.css +++ b/cmd/server/web/css/header.css @@ -59,4 +59,143 @@ .cam-btn-icon { font-size: 16px; +} +/* ===== HEADER CONTROLS ===== */ +.header-controls { + display: flex; + gap: 12px; + align-items: center; + flex-wrap: wrap; + justify-content: flex-end; +} + +.header-link { + color: #00ff88; + text-decoration: none; + font-size: var(--font-size-base); + border: 1px solid rgba(0, 255, 136, 0.3); + padding: 6px 14px; + border-radius: 6px; + transition: all 0.3s ease; + font-family: var(--font-mono); + font-weight: var(--font-weight-semibold); + white-space: nowrap; +} + +.header-link:hover { + background: rgba(0, 255, 136, 0.1); + border-color: #00ff88; + box-shadow: 0 0 20px rgba(0, 255, 136, 0.05); +} + +.audio-indicator { + font-size: var(--font-size-lg); + padding: 4px 12px; + border-radius: 6px; + background: rgba(0, 0, 0, 0.4); + border: 1px solid #333; + min-width: 50px; + text-align: center; + font-family: var(--font-tech); + transition: all 0.3s ease; + user-select: none; +} + +.audio-indicator.active { + border-color: #00ff88; + box-shadow: 0 0 15px rgba(0, 255, 136, 0.3); + animation: pulse 1s ease-in-out infinite; +} + +/* ===== CAMERA CLOSE BUTTON ===== */ +.cam-close-btn { + background: rgba(255, 68, 68, 0.2); + color: #ff4466; + border: 1px solid rgba(255, 68, 68, 0.3); + border-radius: 4px; + padding: 4px 10px; + cursor: pointer; + font-size: var(--font-size-base); + font-family: var(--font-mono); + transition: all 0.3s ease; +} + +.cam-close-btn:hover { + background: rgba(255, 68, 68, 0.3); + border-color: #ff4466; + box-shadow: 0 0 20px rgba(255, 68, 68, 0.1); +} + +/* ===== МОБИЛЬНАЯ АДАПТАЦИЯ ХЕДЕРА ===== */ +@media (max-width: 768px) { + .header-bar { + display: flex; + flex-direction: column; + align-items: stretch; + gap: 8px; + margin-bottom: 12px; + } + + .header-bar h1 { + text-align: center; + font-size: var(--font-size-lg); + } + + .header-controls { + justify-content: center; + gap: 6px; + } + + .header-link { + font-size: var(--font-size-sm); + padding: 6px 10px; + } + + .header-time { + font-size: var(--font-size-sm); + padding: 4px 8px; + } + + .audio-indicator { + font-size: var(--font-size-base); + padding: 4px 8px; + min-width: 40px; + } + + .cam-btn { + font-size: var(--font-size-sm); + padding: 6px 10px; + } +} + +@media (max-width: 480px) { + .header-bar h1 { + font-size: var(--font-size-base); + } + + .header-controls { + gap: 4px; + flex-wrap: wrap; + } + + .header-link { + font-size: var(--font-size-xs); + padding: 4px 8px; + } + + .header-time { + font-size: var(--font-size-xs); + padding: 3px 6px; + } + + .audio-indicator { + font-size: var(--font-size-sm); + padding: 3px 6px; + min-width: 32px; + } + + .cam-btn { + font-size: var(--font-size-xs); + padding: 4px 8px; + } } \ No newline at end of file diff --git a/cmd/server/web/css/logs.css b/cmd/server/web/css/logs.css index 9b22ad8..6152fdd 100644 --- a/cmd/server/web/css/logs.css +++ b/cmd/server/web/css/logs.css @@ -1,4 +1,6 @@ -/* Стили страницы журнала */ +/* ===== СТИЛИ СТРАНИЦЫ ЖУРНАЛА ===== */ + +/* Общие улучшения читаемости */ .logs-container { display: flex; gap: 20px; @@ -17,36 +19,48 @@ overflow-y: auto; } +/* ===== ФАЙЛЫ В БОКОВОЙ ПАНЕЛИ ===== */ .log-file-item { - padding: 8px 12px; + padding: 10px 14px; margin: 4px 0; cursor: pointer; border-radius: 4px; border: 1px solid transparent; - transition: all 0.2s; + transition: all 0.2s ease; font-family: var(--font-mono); + background: rgba(0, 255, 136, 0.03); + transform: scale(1); } .log-file-item:hover { - background: rgba(0,255,136,0.1); + background: rgba(0, 255, 136, 0.15); border-color: #00ff88; + transform: scale(1.02); + box-shadow: 0 0 20px rgba(0, 255, 136, 0.03); } .log-file-item.active { - background: rgba(0,255,136,0.2); + background: rgba(0, 255, 136, 0.2); border-color: #00ff88; + box-shadow: 0 0 20px rgba(0, 255, 136, 0.05); + transform: scale(1); } .log-file-item .file-name { - font-size: var(--font-size-sm); + font-size: var(--font-size-base); font-family: var(--font-mono); + font-weight: var(--font-weight-semibold); + color: #00ff88; } .log-file-item .file-info { - font-size: var(--font-size-xs); - opacity: 0.6; + font-size: var(--font-size-sm); + opacity: 0.7; + color: #88ffbb; + margin-top: 2px; } +/* ===== СТАТИСТИКА ===== */ .log-stats { display: grid; grid-template-columns: repeat(4, 1fr); @@ -55,98 +69,169 @@ } .log-stat-card { - padding: 12px; - border: 1px solid #00ff88; - border-radius: 4px; + padding: 14px 12px; + border: 1px solid rgba(0, 255, 136, 0.3); + border-radius: 6px; text-align: center; - background: rgba(0,255,136,0.02); + background: rgba(0, 255, 136, 0.05); + transition: all 0.3s ease; + transform: scale(1); +} + +.log-stat-card:hover { + background: rgba(0, 255, 136, 0.1); + border-color: #00ff88; + box-shadow: 0 0 25px rgba(0, 255, 136, 0.05); + transform: scale(1.02); } .log-stat-card .value { font-family: var(--font-tech); font-size: var(--font-size-2xl); font-weight: var(--font-weight-bold); + color: #00ff88; + text-shadow: 0 0 20px rgba(0, 255, 136, 0.2); + display: block; + margin-bottom: 4px; } .log-stat-card .label { font-family: var(--font-mono); font-size: var(--font-size-xs); - opacity: 0.6; - margin-top: 4px; + opacity: 0.7; + color: #88ffbb; + letter-spacing: 0.05em; + text-transform: uppercase; } +/* ===== ТАБЛИЦА ДАННЫХ ===== */ .data-table { width: 100%; - font-size: var(--font-size-sm); + font-size: var(--font-size-base); border-collapse: collapse; font-family: var(--font-mono); } .data-table th { text-align: left; - padding: 6px 8px; - border-bottom: 1px solid #00ff88; + padding: 8px 12px; + border-bottom: 2px solid #00ff88; position: sticky; top: 0; background: #050805; font-family: var(--font-display); font-weight: var(--font-weight-bold); - letter-spacing: 0.05em; + letter-spacing: 0.08em; font-size: var(--font-size-sm); + color: #00ff88; + text-shadow: 0 0 20px rgba(0, 255, 136, 0.1); } .data-table td { - padding: 4px 8px; - border-bottom: 1px solid rgba(0,255,136,0.1); + padding: 6px 12px; + border-bottom: 1px solid rgba(0, 255, 136, 0.08); font-family: var(--font-mono); + font-size: var(--font-size-base); + font-weight: var(--font-weight-normal); } .data-table tr:hover { - background: rgba(0,255,136,0.05); + background: rgba(0, 255, 136, 0.06); } -.strength-0 { color: #666; } -.strength-1 { color: #88ff88; } -.strength-2 { color: #ffaa44; } -.strength-3 { color: #ff4444; } +/* ===== УЛУЧШЕННЫЕ ЦВЕТА ДЛЯ АМПЛИТУДЫ ===== */ +.strength-0 { + color: #666666; + opacity: 0.5; +} +.strength-1 { + color: #66ff88; + font-weight: var(--font-weight-semibold); + text-shadow: 0 0 15px rgba(102, 255, 136, 0.2); +} + +.strength-2 { + color: #ffbb44; + font-weight: var(--font-weight-bold); + text-shadow: 0 0 20px rgba(255, 187, 68, 0.25); +} + +.strength-3 { + color: #ff3355; + font-weight: var(--font-weight-bold); + text-shadow: 0 0 20px rgba(255, 51, 85, 0.4), 0 0 40px rgba(255, 51, 85, 0.2); + background: rgba(255, 51, 85, 0.08); + border-radius: 3px; + padding: 0 4px; +} + +/* ===== СПИСОК СОБЫТИЙ ===== */ .events-list { font-family: var(--font-mono); - font-size: var(--font-size-sm); - line-height: 1.6; + font-size: var(--font-size-base); + line-height: 1.8; +} + +.events-list > div { + padding: 4px 8px; + border-bottom: 1px solid rgba(0, 255, 136, 0.04); + transition: background 0.2s ease; +} + +.events-list > div:hover { + background: rgba(0, 255, 136, 0.04); } .events-list .event-time { - opacity: 0.5; + opacity: 0.6; font-family: var(--font-tech); + color: #88ffbb; + font-size: var(--font-size-sm); + margin-right: 8px; } +.events-list .event-message { + color: #00ff88; + font-weight: var(--font-weight-normal); +} + +/* ===== ТАБЫ ===== */ .tabs { display: flex; gap: 8px; margin-bottom: 16px; + padding: 4px 0; } .tab-btn { - padding: 6px 16px; + padding: 8px 20px; background: transparent; - color: #00ff88; - border: 1px solid #00ff88; - border-radius: 4px; + color: #88ffbb; + border: 1px solid rgba(0, 255, 136, 0.3); + border-radius: 6px; cursor: pointer; font-family: var(--font-mono); font-size: var(--font-size-base); - transition: all 0.3s ease; -} - -.tab-btn.active { - background: rgba(0,255,136,0.2); - box-shadow: 0 0 20px rgba(0, 255, 136, 0.1); + font-weight: var(--font-weight-semibold); + transition: all 0.2s ease; + letter-spacing: 0.03em; + transform: scale(1); } .tab-btn:hover { - background: rgba(0,255,136,0.1); - transform: translateY(-1px); + background: rgba(0, 255, 136, 0.1); + border-color: #00ff88; + transform: scale(1.04); + box-shadow: 0 0 25px rgba(0, 255, 136, 0.05); +} + +.tab-btn.active { + background: rgba(0, 255, 136, 0.15); + border-color: #00ff88; + color: #00ff88; + box-shadow: 0 0 30px rgba(0, 255, 136, 0.05); + transform: scale(1); } .tab-content { @@ -160,9 +245,11 @@ /* ===== ПАГИНАЦИЯ ===== */ .pagination-container { margin: 16px 0; - padding: 12px; - border-top: 1px solid rgba(0, 255, 136, 0.2); - border-bottom: 1px solid rgba(0, 255, 136, 0.2); + padding: 14px; + border-top: 1px solid rgba(0, 255, 136, 0.15); + border-bottom: 1px solid rgba(0, 255, 136, 0.15); + background: rgba(0, 255, 136, 0.02); + border-radius: 4px; } .pagination-controls { @@ -171,94 +258,220 @@ justify-content: center; gap: 8px; flex-wrap: wrap; + padding: 4px 0; } .pagination-btn { - padding: 6px 14px; - background: rgba(0, 255, 136, 0.1); + padding: 8px 16px; + background: rgba(0, 255, 136, 0.08); color: #00ff88; - border: 1px solid #00ff88; - border-radius: 4px; + border: 1px solid rgba(0, 255, 136, 0.3); + border-radius: 6px; cursor: pointer; font-family: var(--font-mono); font-size: var(--font-size-base); - transition: all 0.2s; - min-width: 40px; + font-weight: var(--font-weight-semibold); + transition: all 0.2s ease; + min-width: 44px; + transform: scale(1); } .pagination-btn:hover:not(:disabled) { - background: rgba(0, 255, 136, 0.3); - transform: scale(1.05); + background: rgba(0, 255, 136, 0.2); + border-color: #00ff88; + transform: scale(1.04); + box-shadow: 0 0 25px rgba(0, 255, 136, 0.05); } .pagination-btn:disabled { - opacity: 0.3; + opacity: 0.25; cursor: not-allowed; - transform: none; + transform: none !important; + box-shadow: none !important; } .pagination-info { - padding: 0 12px; - color: #00ff88; + padding: 0 16px; + color: #88ffbb; font-size: var(--font-size-base); font-family: var(--font-mono); - min-width: 200px; + min-width: 220px; text-align: center; + font-weight: var(--font-weight-semibold); } .pagination-goto { display: flex; align-items: center; - gap: 4px; + gap: 6px; margin-left: 8px; } .pagination-input { width: 60px; - padding: 4px 8px; - background: rgba(0, 0, 0, 0.6); + padding: 6px 8px; + background: rgba(0, 0, 0, 0.7); color: #00ff88; - border: 1px solid #00ff88; + border: 1px solid rgba(0, 255, 136, 0.3); border-radius: 4px; font-family: var(--font-mono); font-size: var(--font-size-base); + font-weight: var(--font-weight-bold); text-align: center; } .pagination-input:focus { outline: none; - box-shadow: 0 0 10px rgba(0, 255, 136, 0.3); + border-color: #00ff88; + box-shadow: 0 0 20px rgba(0, 255, 136, 0.15); } .pagination-goto-btn { - padding: 4px 12px; + padding: 6px 14px; font-size: var(--font-size-sm); + background: rgba(0, 255, 136, 0.12); } .loading-indicator { text-align: center; - padding: 20px; - color: #00ff88; - opacity: 0.7; + padding: 30px; + color: #88ffbb; + opacity: 0.8; font-family: var(--font-mono); + font-size: var(--font-size-lg); +} + +/* ===== СКРОЛЛБАРЫ ===== */ +::-webkit-scrollbar { + width: 8px; + height: 8px; +} + +::-webkit-scrollbar-track { + background: rgba(0, 255, 136, 0.05); + border-radius: 4px; +} + +::-webkit-scrollbar-thumb { + background: rgba(0, 255, 136, 0.3); + border-radius: 4px; +} + +::-webkit-scrollbar-thumb:hover { + background: rgba(0, 255, 136, 0.5); } /* ===== АДАПТИВНОСТЬ ===== */ @media (max-width: 768px) { + .logs-container { + flex-direction: column; + height: auto; + gap: 12px; + } + + .logs-sidebar { + flex: 0 0 auto; + border-right: none; + border-bottom: 1px solid rgba(0, 255, 136, 0.2); + padding-right: 0; + padding-bottom: 12px; + max-height: 180px; + overflow-y: auto; + } + + .logs-sidebar h3 { + font-size: var(--font-size-sm); + margin-bottom: 8px; + } + + .logs-content { + overflow-y: visible; + } + + .log-file-item { + padding: 8px 12px; + margin: 3px 0; + min-height: 44px; + display: flex; + justify-content: space-between; + align-items: center; + flex-wrap: wrap; + } + + .log-file-item .file-name { + font-size: var(--font-size-sm); + } + + .log-file-item .file-info { + font-size: var(--font-size-xs); + } + + .log-stats { + grid-template-columns: repeat(2, 1fr); + gap: 8px; + } + + .log-stat-card { + padding: 10px 8px; + } + + .log-stat-card .value { + font-size: var(--font-size-xl); + } + + .log-stat-card .label { + font-size: var(--font-size-xs); + } + + .data-table { + font-size: var(--font-size-sm); + } + + .data-table th, + .data-table td { + padding: 4px 6px; + font-size: var(--font-size-sm); + } + + .tabs { + gap: 6px; + flex-wrap: wrap; + } + + .tab-btn { + padding: 6px 14px; + font-size: var(--font-size-sm); + min-height: 40px; + flex: 1; + min-width: 60px; + text-align: center; + } + + .events-list { + font-size: var(--font-size-sm); + line-height: 1.6; + } + + .pagination-container { + padding: 10px 8px; + margin: 12px 0; + } + .pagination-controls { gap: 4px; } .pagination-btn { - padding: 4px 10px; + padding: 6px 10px; font-size: var(--font-size-sm); - min-width: 32px; + min-height: 40px; + min-width: 36px; } .pagination-info { - font-size: var(--font-size-sm); - min-width: 120px; - padding: 0 8px; + font-size: var(--font-size-xs); + min-width: 80px; + padding: 0 6px; } .pagination-goto { @@ -266,21 +479,56 @@ } .pagination-input { - width: 50px; + width: 44px; + min-height: 36px; font-size: var(--font-size-sm); - padding: 3px 6px; + padding: 4px 6px; + } + + .pagination-goto-btn { + padding: 4px 10px; + font-size: var(--font-size-xs); + min-height: 36px; } } -@media (max-width: 500px) { - .pagination-controls { - flex-wrap: wrap; - justify-content: center; +@media (max-width: 480px) { + .log-stats { + grid-template-columns: 1fr 1fr; + gap: 6px; } - .pagination-info { - order: -1; - width: 100%; - margin-bottom: 4px; + .log-stat-card { + padding: 8px 6px; + } + + .log-stat-card .value { + font-size: var(--font-size-lg); + } + + .data-table th, + .data-table td { + padding: 3px 4px; + font-size: var(--font-size-xs); + } + + .tab-btn { + font-size: var(--font-size-xs); + padding: 4px 10px; + min-height: 34px; + } + + .pagination-btn { + padding: 4px 8px; + font-size: var(--font-size-xs); + min-height: 34px; + min-width: 30px; + } + + .pagination-input { + width: 38px; + min-height: 32px; + font-size: var(--font-size-xs); + padding: 3px 4px; } } \ No newline at end of file diff --git a/cmd/server/web/css/responsive.css b/cmd/server/web/css/responsive.css index 806c48d..e96ce01 100644 --- a/cmd/server/web/css/responsive.css +++ b/cmd/server/web/css/responsive.css @@ -1,65 +1,567 @@ -/* Медиа-запросы и адаптивность */ -/* ===== ГОРЯЧИЕ КЛАВИШИ ===== */ -.hotkey-hint { - font-size: 11px; - opacity: 0.4; - text-align: center; - padding: 4px 0; -} +/* ===== РЕСПОНСИВНЫЙ ДИЗАЙН ===== */ -.hotkey-hint kbd { - background: rgba(0, 255, 136, 0.1); - padding: 1px 6px; - border-radius: 3px; - border: 1px solid rgba(0, 255, 136, 0.2); - font-size: 10px; -} - -/* ===== MOBILE ===== */ -@media (max-width: 500px) { - .header-bar { - flex-direction: column; - gap: 8px; +/* ============================================ + ПЛАНШЕТЫ (768px - 1024px) + ============================================ */ +@media (max-width: 1024px) { + /* Уменьшаем отступы */ + body { + padding: 10px; } - h1 { - font-size: 18px; + /* Хедер */ + .header-bar { + gap: 10px; + grid-template-columns: 1fr auto; + } + + .header-bar h1 { + font-size: var(--font-size-xl); + } + + .header-time { + font-size: var(--font-size-sm); + padding: 4px 10px; + } + + .cam-btn { + font-size: var(--font-size-sm); + padding: 6px 12px; + } + + /* Карточки */ + .card { + padding: 12px 14px; + } + + .card h3 { + font-size: var(--font-size-base); } #status { - font-size: 22px; + font-size: var(--font-size-2xl); + } + + /* Прогресс-бары */ + .progress { + height: 28px; + padding: 4px; + } + + .progress-label { + font-size: var(--font-size-sm); + } + + /* Сигнальные иконки */ + .signal-icon { + width: 28px; + height: 28px; + } +} + +/* ============================================ + МОБИЛЬНЫЕ УСТРОЙСТВА (до 768px) + ============================================ */ +@media (max-width: 768px) { + /* ===== ОБЩИЕ ===== */ + body { + padding: 8px; + font-size: var(--font-size-base); + } + + /* Увеличиваем область касания для всех интерактивных элементов */ + button, + .cam-btn, + .tab-btn, + .pagination-btn, + .log-file-item { + min-height: 44px; + min-width: 44px; + } + + /* ===== ХЕДЕР ===== */ + .header-bar { + display: flex; + flex-direction: column; + align-items: stretch; + gap: 8px; + margin-bottom: 12px; + } + + .header-bar h1 { + font-size: var(--font-size-lg); + text-align: center; + letter-spacing: 0.05em; + } + + .header-bar > div { + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: 6px; + } + + .header-time { + font-size: var(--font-size-sm); + padding: 4px 10px; + flex: 1; + min-width: 80px; + } + + #audio-indicator { + font-size: var(--font-size-base); + padding: 4px 10px; + min-width: 40px; + } + + .cam-btn { + font-size: var(--font-size-sm); + padding: 8px 14px; + justify-content: center; + flex: 1; + min-width: 80px; + } + + /* Ссылка "Журнал" */ + .header-bar a[href="/logs.html"] { + font-size: var(--font-size-sm); + padding: 6px 12px; + text-align: center; + flex: 1; + min-width: 60px; + } + + /* ===== МАКЕТ ===== */ + .layout { + flex-direction: column; + height: auto; + gap: 8px; + } + + .panel { + height: auto; + overflow-y: visible; + } + + .left { + flex: 1 1 auto !important; + min-width: 0 !important; + padding: 0; + width: 100% !important; + } + + .right { + flex: 0 0 auto !important; + min-width: 100% !important; + width: 100% !important; + height: 40vh; + min-height: 300px; + opacity: 1 !important; + pointer-events: auto !important; + } + + .right.hidden { + display: none !important; + flex: 0 0 0 !important; + height: 0 !important; + min-height: 0 !important; + } + + .resizer { + display: none !important; + } + + /* ===== СЕТКА ===== */ + .grid { + grid-template-columns: 1fr; + gap: 8px; + } + + .wide { + grid-column: span 1; + } + + /* ===== КАРТОЧКИ ===== */ + .card { + padding: 12px; + border-radius: 8px; + } + + .card h3 { + font-size: var(--font-size-base); + margin-bottom: 8px; + } + + #status { + font-size: var(--font-size-2xl); + } + + #meta { + font-size: var(--font-size-sm); + } + + /* ===== ПРОГРЕСС-БАРЫ ===== */ + .progress-row { + margin-bottom: 12px; + } + + .progress-label { + font-size: var(--font-size-sm); + margin-bottom: 4px; } .progress { height: 30px; - padding: 5px; + padding: 4px; + border-width: 2px; } - .progress-label { - font-size: 15px; + .progress-with-signal { + flex-wrap: nowrap; + } + + .progress-90 { + flex: 1; + margin-right: 8px; + } + + .signal-icon { + width: 30px; + height: 30px; + flex-shrink: 0; + } + + .sig-bar { + min-width: 4px; + border-width: 1.5px; + } + + /* ===== АККОРДЕОН ===== */ + .accordion-header { + padding: 8px 0; + font-size: var(--font-size-base); + } + + .accordion-body { + max-height: 400px; } #history { - font-size: 16px; + font-size: var(--font-size-sm); + max-height: 300px; line-height: 1.5; - max-height: 55vh; } + /* ===== КАМЕРА ===== */ + .cam-modal { + height: 100%; + min-height: 300px; + } + + .cam-header { + padding: 6px 10px; + font-size: var(--font-size-sm); + } + + .cam-frame { + object-fit: cover; + } + + .crosshair-box { + width: 80px; + height: 80px; + border-width: 1.5px; + } + + /* ===== ГОРЯЧИЕ КЛАВИШИ ===== */ + .hotkey-hint { + font-size: var(--font-size-xs); + padding: 4px 0; + } + + .hotkey-hint kbd { + padding: 2px 6px; + font-size: var(--font-size-xs); + } +} + +/* ============================================ + МАЛЕНЬКИЕ ТЕЛЕФОНЫ (до 480px) + ============================================ */ +@media (max-width: 480px) { + body { + padding: 6px; + } + + /* Хедер */ + .header-bar h1 { + font-size: var(--font-size-base); + } + + .header-bar > div { + flex-wrap: wrap; + } + + .header-time { + font-size: var(--font-size-xs); + padding: 4px 8px; + min-width: 60px; + } + + #audio-indicator { + font-size: var(--font-size-sm); + padding: 4px 8px; + min-width: 35px; + } + + .cam-btn { + font-size: var(--font-size-xs); + padding: 6px 10px; + min-width: 60px; + } + + /* Карточки */ + .card { + padding: 10px; + border-radius: 6px; + } + + .card h3 { + font-size: var(--font-size-sm); + } + + #status { + font-size: var(--font-size-xl); + } + + #meta { + font-size: var(--font-size-xs); + } + + /* Прогресс-бары */ + .progress { + height: 24px; + padding: 3px; + border-width: 2px; + } + + .progress-label { + font-size: var(--font-size-xs); + } + + .signal-icon { + width: 24px; + height: 24px; + } + + .sig-bar { + min-width: 3px; + border-width: 1px; + } + + .sig-bar-1 { height: 30%; } + .sig-bar-2 { height: 60%; } + .sig-bar-3 { height: 90%; } + + /* Камера */ + .right { + height: 35vh; + min-height: 200px; + } + + .cam-header { + padding: 4px 8px; + font-size: var(--font-size-xs); + } + + .crosshair-box { + width: 60px; + height: 60px; + } + + /* Аккордеон */ + #history { + font-size: var(--font-size-xs); + max-height: 200px; + } +} + +/* ============================================ + ОРИЕНТАЦИЯ (ALBUM / PORTRAIT) + ============================================ */ +@media (max-width: 768px) and (orientation: landscape) { .layout { - flex-direction: column; - height: auto; + flex-direction: row; + height: calc(100vh - 80px); } .left { - min-height: 50vh; + flex: 1 1 60% !important; + min-width: 0 !important; + padding-right: 8px; + overflow-y: auto; + height: 100%; } .right { - flex: 0 0 50vh !important; - min-width: 100% !important; + flex: 0 0 40% !important; + min-width: 0 !important; + height: 100%; + min-height: 0; } - .resizer { - display: none; + .grid { + grid-template-columns: 1fr 1fr; + gap: 6px; + } + + .wide { + grid-column: span 2; + } + + #status { + font-size: var(--font-size-xl); + } +} + +/* ============================================ + УЛУЧШЕНИЯ ДЛЯ ТАЧ-ИНТЕРФЕЙСА + ============================================ */ +@media (hover: none) and (pointer: coarse) { + /* Увеличиваем область касания */ + button, + .cam-btn, + .tab-btn, + .pagination-btn, + .log-file-item, + .accordion-header, + .pagination-goto-btn { + min-height: 44px; + min-width: 44px; + padding: 8px 16px; + } + + /* Убираем hover-эффекты, оставляем active */ + .cam-btn:hover, + .tab-btn:hover, + .pagination-btn:hover, + .log-file-item:hover { + transform: none; + } + + .cam-btn:active, + .tab-btn:active, + .pagination-btn:active, + .log-file-item:active { + transform: scale(0.95); + background: rgba(0, 255, 136, 0.15); + } + + /* Увеличиваем отступы для скролла */ + .panel, + .logs-content, + .logs-sidebar { + -webkit-overflow-scrolling: touch; + scroll-behavior: smooth; + } + + /* Увеличиваем размер полей ввода */ + .pagination-input { + min-height: 44px; + font-size: var(--font-size-base); + padding: 8px 12px; + } + + /* Увеличиваем чекбоксы и переключатели */ + input[type="checkbox"], + input[type="radio"] { + width: 20px; + height: 20px; + } +} + +/* ============================================ + ТЁМНАЯ ТЕМА (опционально) + ============================================ */ +@media (prefers-color-scheme: dark) { + body { + background: #0a0f0a; + } +} + +/* ============================================ + ПЕЧАТЬ + ============================================ */ +@media print { + .header-bar, + .cam-btn, + .resizer, + .right, + .pagination-container, + .hotkey-hint { + display: none !important; + } + + .layout { + display: block !important; + height: auto !important; + } + + .panel { + height: auto !important; + overflow: visible !important; + } + + .card { + break-inside: avoid; + border: 1px solid #00ff88; + } +} +/* ===== ДОПОЛНИТЕЛЬНЫЕ УЛУЧШЕНИЯ ДЛЯ ХЕДЕРА ===== */ +@media (max-width: 768px) { + .header-bar { + display: flex !important; + flex-direction: column !important; + align-items: stretch !important; + gap: 8px !important; + } + + .header-controls { + display: flex !important; + flex-wrap: wrap !important; + justify-content: center !important; + gap: 6px !important; + } +} + +@media (max-width: 480px) { + .header-controls { + gap: 4px !important; + } +} + +/* ===== УЛУЧШЕНИЯ ДЛЯ ТАЧ-ИНТЕРФЕЙСА ===== */ +@media (hover: none) and (pointer: coarse) { + .header-link, + .cam-btn, + .cam-close-btn { + min-height: 44px !important; + min-width: 44px !important; + padding: 8px 14px !important; + } + + .header-link:active, + .cam-btn:active { + transform: scale(0.95); + } +} + +/* ===== КНОПКА ЗАКРЫТИЯ КАМЕРЫ НА МОБИЛЬНЫХ ===== */ +@media (max-width: 768px) { + .cam-close-btn { + font-size: var(--font-size-lg); + padding: 6px 14px; + min-height: 44px; + min-width: 44px; } } \ No newline at end of file diff --git a/cmd/server/web/dashboard.html b/cmd/server/web/dashboard.html index 5202f4a..1edc0a1 100644 --- a/cmd/server/web/dashboard.html +++ b/cmd/server/web/dashboard.html @@ -1,13 +1,18 @@ -
- -