Рефактор
This commit is contained in:
31
cmd/server/web/css/audio.css
Normal file
31
cmd/server/web/css/audio.css
Normal file
@@ -0,0 +1,31 @@
|
||||
/* Стили аудио-индикатора (pulse, индикатор) */
|
||||
/* ===== AUDIO INDICATOR ===== */
|
||||
@keyframes pulse {
|
||||
0%, 100% {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
50% {
|
||||
opacity: 0.6;
|
||||
transform: scale(0.95);
|
||||
}
|
||||
}
|
||||
|
||||
#audio-indicator {
|
||||
transition: all 0.3s ease;
|
||||
font-family: monospace;
|
||||
font-size: 16px;
|
||||
padding: 4px 12px;
|
||||
border-radius: 6px;
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
border: 1px solid #333;
|
||||
min-width: 50px;
|
||||
text-align: center;
|
||||
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;
|
||||
}
|
||||
13
cmd/server/web/css/base.css
Normal file
13
cmd/server/web/css/base.css
Normal file
@@ -0,0 +1,13 @@
|
||||
/* Базовые стили (сброс, body, общие настройки) */
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: monospace;
|
||||
background: #050805;
|
||||
color: #00ff88;
|
||||
padding: 12px;
|
||||
}
|
||||
80
cmd/server/web/css/camera.css
Normal file
80
cmd/server/web/css/camera.css
Normal file
@@ -0,0 +1,80 @@
|
||||
/* Стили камеры (cam-modal, cam-frame, crosshair) */
|
||||
/* ===== CAMERA OVERLAY ===== */
|
||||
.cam-modal {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: black;
|
||||
}
|
||||
|
||||
.cam-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 8px;
|
||||
background: #111;
|
||||
color: #00ff88;
|
||||
}
|
||||
|
||||
.cam-frame-wrapper {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
background: black;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.cam-frame {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
background: black;
|
||||
}
|
||||
|
||||
/* Перекрестие (crosshair) */
|
||||
.cam-crosshair {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
/* Вертикальная линия */
|
||||
.crosshair-v {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 1px;
|
||||
height: 100%;
|
||||
background: #00ff88;
|
||||
box-shadow: 0 0 4px rgba(0, 255, 136, 0.3);
|
||||
}
|
||||
|
||||
/* Горизонтальная линия */
|
||||
.crosshair-h {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 0;
|
||||
transform: translateY(-50%);
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
background: #00ff88;
|
||||
box-shadow: 0 0 4px rgba(0, 255, 136, 0.3);
|
||||
}
|
||||
|
||||
/* Центральный квадрат */
|
||||
.crosshair-box {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
border: 2px solid #00ff88;
|
||||
box-shadow:
|
||||
0 0 10px rgba(0, 255, 136, 0.3),
|
||||
inset 0 0 10px rgba(0, 255, 136, 0.1);
|
||||
}
|
||||
89
cmd/server/web/css/cards.css
Normal file
89
cmd/server/web/css/cards.css
Normal file
@@ -0,0 +1,89 @@
|
||||
/* ===== CARD ===== */
|
||||
.card {
|
||||
border: 1px solid #00ff88;
|
||||
border-radius: 10px;
|
||||
padding: 14px;
|
||||
background: rgba(0,255,136,0.05);
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.card h3 {
|
||||
margin: 0 0 10px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
/* ===== STATUS ===== */
|
||||
#status {
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.status-ok {
|
||||
color: #00ff88 !important;
|
||||
}
|
||||
|
||||
.status-lost {
|
||||
color: #ffffff !important;
|
||||
animation: blink 1.5s infinite;
|
||||
}
|
||||
|
||||
.status-error {
|
||||
color: #ff4444 !important;
|
||||
}
|
||||
|
||||
@keyframes blink {
|
||||
0%, 100% { opacity: 1; }
|
||||
50% { opacity: 0.2; }
|
||||
}
|
||||
|
||||
/* ===== META ===== */
|
||||
#meta {
|
||||
font-size: 14px;
|
||||
opacity: 0.8;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
/* ===== ACCORDION ===== */
|
||||
.accordion-header {
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
padding: 10px 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.accordion-header:hover {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.accordion-body {
|
||||
max-height: 520px;
|
||||
overflow: hidden;
|
||||
transition: max-height 0.3s ease, opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.accordion-body.collapsed {
|
||||
max-height: 0;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/* ===== LAST BYTE ===== */
|
||||
#last-byte {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #00ff88;
|
||||
}
|
||||
|
||||
/* ===== HISTORY ===== */
|
||||
#history {
|
||||
font-size: 13px;
|
||||
line-height: 1.35;
|
||||
max-height: 420px;
|
||||
overflow-y: auto;
|
||||
white-space: nowrap;
|
||||
padding-right: 4px;
|
||||
margin: 0;
|
||||
}
|
||||
91
cmd/server/web/css/components.css
Normal file
91
cmd/server/web/css/components.css
Normal file
@@ -0,0 +1,91 @@
|
||||
/* Компоненты (прогресс-бары, сигнальные иконки, перекрестие) */
|
||||
/* ===== PROGRESS BARS ===== */
|
||||
.progress-row {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.progress-label {
|
||||
font-size: 16px;
|
||||
margin-bottom: 6px;
|
||||
color: #ffffff;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.progress {
|
||||
width: 100%;
|
||||
height: 34px;
|
||||
padding: 6px;
|
||||
border: 3px solid #8a8a8a;
|
||||
background: #222;
|
||||
box-shadow:
|
||||
inset 0 0 6px rgba(255,255,255,0.15),
|
||||
0 0 4px rgba(0,0,0,0.8);
|
||||
}
|
||||
|
||||
.progress-with-signal {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
.progress-90 {
|
||||
flex: 0 0 86%;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.fill {
|
||||
height: 100%;
|
||||
width: 0;
|
||||
background: #e6852d;
|
||||
box-shadow:
|
||||
inset 0 0 4px rgba(255,255,255,0.2),
|
||||
0 0 6px rgba(230,133,45,0.5);
|
||||
transition: width 0.25s linear;
|
||||
}
|
||||
|
||||
.fill.no-connection {
|
||||
background: #555555 !important;
|
||||
box-shadow: none !important;
|
||||
width: 0 !important;
|
||||
}
|
||||
|
||||
/* ===== SIGNAL ICON ===== */
|
||||
.signal-icon {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
gap: 4px;
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.sig-bar {
|
||||
flex: 1;
|
||||
border-radius: 2px;
|
||||
min-width: 5px;
|
||||
border: 2px solid #e6852d;
|
||||
background-color: transparent;
|
||||
transition: background-color 0.3s, border-color 0.3s;
|
||||
}
|
||||
|
||||
.sig-bar-1 { height: 33%; }
|
||||
.sig-bar-2 { height: 66%; }
|
||||
.sig-bar-3 { height: 100%; }
|
||||
|
||||
.sig-bar.dim {
|
||||
background-color: transparent;
|
||||
border-color: #1a3a1a;
|
||||
}
|
||||
|
||||
.sig-bar.active-1,
|
||||
.sig-bar.active-2,
|
||||
.sig-bar.active-3 {
|
||||
background-color: #e6852d;
|
||||
border-color: #c46b1f;
|
||||
}
|
||||
|
||||
.sig-bar.no-connection {
|
||||
background-color: #555555 !important;
|
||||
border-color: #555555 !important;
|
||||
opacity: 0.3 !important;
|
||||
}
|
||||
52
cmd/server/web/css/header.css
Normal file
52
cmd/server/web/css/header.css
Normal file
@@ -0,0 +1,52 @@
|
||||
/* ===== HEADER BAR ===== */
|
||||
.header-bar {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto 1fr;
|
||||
align-items: center;
|
||||
margin-bottom: 15px;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 22px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.header-time {
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
padding: 6px 12px;
|
||||
border: 1px solid rgba(0, 255, 136, 0.35);
|
||||
border-radius: 8px;
|
||||
background: rgba(0, 255, 136, 0.05);
|
||||
}
|
||||
|
||||
.header-bar .cam-btn {
|
||||
justify-self: end;
|
||||
}
|
||||
|
||||
/* ===== CAMERA BUTTON ===== */
|
||||
.cam-btn {
|
||||
padding: 8px 16px;
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
color: #00ff88;
|
||||
border: 1px solid #00ff88;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
font-family: monospace;
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
white-space: nowrap;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.cam-btn:hover {
|
||||
background: rgba(0, 255, 136, 0.2);
|
||||
}
|
||||
|
||||
.cam-btn-icon {
|
||||
font-size: 16px;
|
||||
}
|
||||
78
cmd/server/web/css/layout.css
Normal file
78
cmd/server/web/css/layout.css
Normal file
@@ -0,0 +1,78 @@
|
||||
/* ===== SPLIT LAYOUT ===== */
|
||||
.layout {
|
||||
display: flex;
|
||||
height: calc(100vh - 100px);
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* панели */
|
||||
.panel {
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* LEFT */
|
||||
.left {
|
||||
flex: 1 1 auto;
|
||||
min-width: 300px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
/* RIGHT */
|
||||
.right {
|
||||
flex: 0 0 40%;
|
||||
min-width: 0;
|
||||
background: black;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.right.hidden {
|
||||
flex: 0 0 0 !important;
|
||||
width: 0 !important;
|
||||
min-width: 0 !important;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* ===== RESIZER ===== */
|
||||
.resizer {
|
||||
width: 8px;
|
||||
min-width: 8px;
|
||||
cursor: col-resize;
|
||||
background: #00ff88;
|
||||
opacity: 0.3;
|
||||
flex-shrink: 0;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.resizer:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.resizer.hidden {
|
||||
width: 0 !important;
|
||||
min-width: 0 !important;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* ===== GRID ===== */
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: 12px;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
@media (min-width: 900px) {
|
||||
.grid {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
.wide {
|
||||
grid-column: span 2;
|
||||
}
|
||||
}
|
||||
142
cmd/server/web/css/logs.css
Normal file
142
cmd/server/web/css/logs.css
Normal file
@@ -0,0 +1,142 @@
|
||||
/* Стили страницы журнала (дополнительные стили из logs.html) */
|
||||
/* Дополнительные стили для журнала */
|
||||
.logs-container {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
height: calc(100vh - 120px);
|
||||
}
|
||||
|
||||
.logs-sidebar {
|
||||
flex: 0 0 280px;
|
||||
border-right: 1px solid #00ff88;
|
||||
padding-right: 16px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.logs-content {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.log-file-item {
|
||||
padding: 8px 12px;
|
||||
margin: 4px 0;
|
||||
cursor: pointer;
|
||||
border-radius: 4px;
|
||||
border: 1px solid transparent;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.log-file-item:hover {
|
||||
background: rgba(0,255,136,0.1);
|
||||
border-color: #00ff88;
|
||||
}
|
||||
|
||||
.log-file-item.active {
|
||||
background: rgba(0,255,136,0.2);
|
||||
border-color: #00ff88;
|
||||
}
|
||||
|
||||
.log-file-item .file-name {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.log-file-item .file-info {
|
||||
font-size: 11px;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.log-stats {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 12px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.log-stat-card {
|
||||
padding: 12px;
|
||||
border: 1px solid #00ff88;
|
||||
border-radius: 4px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.log-stat-card .value {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.log-stat-card .label {
|
||||
font-size: 11px;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.data-table {
|
||||
width: 100%;
|
||||
font-size: 12px;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.data-table th {
|
||||
text-align: left;
|
||||
padding: 6px 8px;
|
||||
border-bottom: 1px solid #00ff88;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
background: #050805;
|
||||
}
|
||||
|
||||
.data-table td {
|
||||
padding: 4px 8px;
|
||||
border-bottom: 1px solid rgba(0,255,136,0.1);
|
||||
}
|
||||
|
||||
.data-table tr:hover {
|
||||
background: rgba(0,255,136,0.05);
|
||||
}
|
||||
|
||||
.strength-0 { color: #666; }
|
||||
.strength-1 { color: #88ff88; }
|
||||
.strength-2 { color: #ffaa44; }
|
||||
.strength-3 { color: #ff4444; }
|
||||
|
||||
.events-list {
|
||||
font-family: monospace;
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.events-list .event-time {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.tabs {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.tab-btn {
|
||||
padding: 6px 16px;
|
||||
background: transparent;
|
||||
color: #00ff88;
|
||||
border: 1px solid #00ff88;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.tab-btn.active {
|
||||
background: rgba(0,255,136,0.2);
|
||||
}
|
||||
|
||||
.tab-btn:hover {
|
||||
background: rgba(0,255,136,0.1);
|
||||
}
|
||||
|
||||
.tab-content {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tab-content.active {
|
||||
display: block;
|
||||
}
|
||||
65
cmd/server/web/css/responsive.css
Normal file
65
cmd/server/web/css/responsive.css
Normal file
@@ -0,0 +1,65 @@
|
||||
/* Медиа-запросы и адаптивность */
|
||||
/* ===== ГОРЯЧИЕ КЛАВИШИ ===== */
|
||||
.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;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
#status {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.progress {
|
||||
height: 30px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.progress-label {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
#history {
|
||||
font-size: 16px;
|
||||
line-height: 1.5;
|
||||
max-height: 55vh;
|
||||
}
|
||||
|
||||
.layout {
|
||||
flex-direction: column;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.left {
|
||||
min-height: 50vh;
|
||||
}
|
||||
|
||||
.right {
|
||||
flex: 0 0 50vh !important;
|
||||
min-width: 100% !important;
|
||||
}
|
||||
|
||||
.resizer {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@@ -1,516 +0,0 @@
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: monospace;
|
||||
background: #050805;
|
||||
color: #00ff88;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
/* ===== HEADER BAR ===== */
|
||||
.header-bar {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto 1fr;
|
||||
align-items: center;
|
||||
margin-bottom: 15px;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 22px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.header-time {
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
padding: 6px 12px;
|
||||
border: 1px solid rgba(0, 255, 136, 0.35);
|
||||
border-radius: 8px;
|
||||
background: rgba(0, 255, 136, 0.05);
|
||||
}
|
||||
|
||||
.header-bar .cam-btn {
|
||||
justify-self: end;
|
||||
}
|
||||
|
||||
/* ===== CAMERA BUTTON (теперь в header) ===== */
|
||||
.cam-btn {
|
||||
padding: 8px 16px;
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
color: #00ff88;
|
||||
border: 1px solid #00ff88;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
font-family: monospace;
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
white-space: nowrap;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.cam-btn:hover {
|
||||
background: rgba(0, 255, 136, 0.2);
|
||||
}
|
||||
|
||||
.cam-btn-icon {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
/* ===== GRID ===== */
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: 12px;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
@media (min-width: 900px) {
|
||||
.grid {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
.wide {
|
||||
grid-column: span 2;
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== CARD ===== */
|
||||
.card {
|
||||
border: 1px solid #00ff88;
|
||||
border-radius: 10px;
|
||||
padding: 14px;
|
||||
background: rgba(0,255,136,0.05);
|
||||
overflow: hidden;
|
||||
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.card h3 {
|
||||
margin: 0 0 10px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
/* ===== STATUS ===== */
|
||||
#status {
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.status-ok {
|
||||
color: #00ff88 !important;
|
||||
}
|
||||
|
||||
.status-lost {
|
||||
color: #ffffff !important;
|
||||
animation: blink 1.5s infinite;
|
||||
}
|
||||
|
||||
.status-error {
|
||||
color: #ff4444 !important;
|
||||
}
|
||||
|
||||
@keyframes blink {
|
||||
0%, 100% { opacity: 1; }
|
||||
50% { opacity: 0.2; }
|
||||
}
|
||||
|
||||
/* ===== META ===== */
|
||||
#meta {
|
||||
font-size: 14px;
|
||||
opacity: 0.8;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
/* ===== PROGRESS BARS ===== */
|
||||
.progress-row {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.progress-label {
|
||||
font-size: 16px;
|
||||
margin-bottom: 6px;
|
||||
color: #ffffff;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.progress {
|
||||
width: 100%;
|
||||
height: 34px;
|
||||
padding: 6px;
|
||||
border: 3px solid #8a8a8a;
|
||||
background: #222;
|
||||
box-shadow:
|
||||
inset 0 0 6px rgba(255,255,255,0.15),
|
||||
0 0 4px rgba(0,0,0,0.8);
|
||||
}
|
||||
|
||||
.progress-with-signal {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
.progress-90 {
|
||||
flex: 0 0 86%;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.fill {
|
||||
height: 100%;
|
||||
width: 0;
|
||||
background: #e6852d;
|
||||
box-shadow:
|
||||
inset 0 0 4px rgba(255,255,255,0.2),
|
||||
0 0 6px rgba(230,133,45,0.5);
|
||||
transition: width 0.25s linear;
|
||||
}
|
||||
|
||||
.fill.no-connection {
|
||||
background: #555555 !important;
|
||||
box-shadow: none !important;
|
||||
width: 0 !important;
|
||||
}
|
||||
|
||||
/* ===== SIGNAL ICON ===== */
|
||||
.signal-icon {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
gap: 4px;
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.sig-bar {
|
||||
flex: 1;
|
||||
border-radius: 2px;
|
||||
min-width: 5px;
|
||||
border: 2px solid #e6852d;
|
||||
background-color: transparent;
|
||||
transition: background-color 0.3s, border-color 0.3s;
|
||||
}
|
||||
|
||||
.sig-bar-1 { height: 33%; }
|
||||
.sig-bar-2 { height: 66%; }
|
||||
.sig-bar-3 { height: 100%; }
|
||||
|
||||
.sig-bar.dim {
|
||||
background-color: transparent;
|
||||
border-color: #1a3a1a;
|
||||
}
|
||||
|
||||
.sig-bar.active-1,
|
||||
.sig-bar.active-2,
|
||||
.sig-bar.active-3 {
|
||||
background-color: #e6852d;
|
||||
border-color: #c46b1f;
|
||||
}
|
||||
|
||||
.sig-bar.no-connection {
|
||||
background-color: #555555 !important;
|
||||
border-color: #555555 !important;
|
||||
opacity: 0.3 !important;
|
||||
}
|
||||
|
||||
/* ===== LAST BYTE ===== */
|
||||
#last-byte {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #00ff88;
|
||||
}
|
||||
|
||||
/* ===== CHART ===== */
|
||||
#signalChart {
|
||||
width: 100%;
|
||||
background: #050805;
|
||||
border: 1px solid #e6852d;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
/* ===== HISTORY ===== */
|
||||
#history {
|
||||
font-size: 13px;
|
||||
line-height: 1.35;
|
||||
max-height: 420px;
|
||||
overflow-y: auto;
|
||||
white-space: nowrap;
|
||||
padding-right: 4px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* ===== ACCORDION ===== */
|
||||
.accordion-header {
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
padding: 10px 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.accordion-header:hover {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.accordion-body {
|
||||
max-height: 520px;
|
||||
overflow: hidden;
|
||||
transition: max-height 0.3s ease, opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.accordion-body.collapsed {
|
||||
max-height: 0;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/* ===== SPLIT LAYOUT (FIXED) ===== */
|
||||
.layout {
|
||||
display: flex;
|
||||
height: calc(100vh - 100px);
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* панели */
|
||||
.panel {
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* LEFT */
|
||||
.left {
|
||||
flex: 1 1 auto;
|
||||
min-width: 300px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
/* RIGHT */
|
||||
.right {
|
||||
flex: 0 0 40%;
|
||||
min-width: 0;
|
||||
background: black;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.right.hidden {
|
||||
flex: 0 0 0 !important;
|
||||
width: 0 !important;
|
||||
min-width: 0 !important;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* ===== RESIZER ===== */
|
||||
.resizer {
|
||||
width: 8px;
|
||||
min-width: 8px;
|
||||
cursor: col-resize;
|
||||
background: #00ff88;
|
||||
opacity: 0.3;
|
||||
flex-shrink: 0;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.resizer:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.resizer.hidden {
|
||||
width: 0 !important;
|
||||
min-width: 0 !important;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* камера */
|
||||
.cam-modal {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: black;
|
||||
}
|
||||
|
||||
.cam-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 8px;
|
||||
background: #111;
|
||||
color: #00ff88;
|
||||
}
|
||||
|
||||
.cam-frame {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
object-fit: contain;
|
||||
background: black;
|
||||
}
|
||||
/* ===== CAMERA OVERLAY ===== */
|
||||
.cam-frame-wrapper {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
background: black;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.cam-frame {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
background: black;
|
||||
}
|
||||
|
||||
/* Перекрестие (crosshair) */
|
||||
.cam-crosshair {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
/* Вертикальная линия */
|
||||
.crosshair-v {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 1px;
|
||||
height: 100%;
|
||||
background: #00ff88;
|
||||
box-shadow: 0 0 4px rgba(0, 255, 136, 0.3);
|
||||
}
|
||||
|
||||
/* Горизонтальная линия */
|
||||
.crosshair-h {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 0;
|
||||
transform: translateY(-50%);
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
background: #00ff88;
|
||||
box-shadow: 0 0 4px rgba(0, 255, 136, 0.3);
|
||||
}
|
||||
|
||||
/* Центральный квадрат */
|
||||
.crosshair-box {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
border: 2px solid #00ff88;
|
||||
box-shadow:
|
||||
0 0 10px rgba(0, 255, 136, 0.3),
|
||||
inset 0 0 10px rgba(0, 255, 136, 0.1);
|
||||
}
|
||||
|
||||
/* ===== AUDIO INDICATOR ===== */
|
||||
@keyframes pulse {
|
||||
0%, 100% {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
50% {
|
||||
opacity: 0.6;
|
||||
transform: scale(0.95);
|
||||
}
|
||||
}
|
||||
|
||||
#audio-indicator {
|
||||
transition: all 0.3s ease;
|
||||
font-family: monospace;
|
||||
font-size: 16px;
|
||||
padding: 4px 12px;
|
||||
border-radius: 6px;
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
border: 1px solid #333;
|
||||
min-width: 50px;
|
||||
text-align: center;
|
||||
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;
|
||||
}
|
||||
|
||||
/* ===== ГОРЯЧИЕ КЛАВИШИ (подсказка) ===== */
|
||||
.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;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
#status {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.progress {
|
||||
height: 30px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.progress-label {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
#history {
|
||||
font-size: 16px;
|
||||
line-height: 1.5;
|
||||
max-height: 55vh;
|
||||
}
|
||||
|
||||
.layout {
|
||||
flex-direction: column;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.left {
|
||||
min-height: 50vh;
|
||||
}
|
||||
|
||||
.right {
|
||||
flex: 0 0 50vh !important;
|
||||
min-width: 100% !important;
|
||||
}
|
||||
|
||||
.resizer {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,14 @@
|
||||
<meta charset="UTF-8">
|
||||
<title>GPIO Панель</title>
|
||||
<link rel="icon" type="image/png" href="/favicon.png">
|
||||
<link rel="stylesheet" href="/css/style.css">
|
||||
<link rel="stylesheet" href="/css/base.css">
|
||||
<link rel="stylesheet" href="/css/header.css">
|
||||
<link rel="stylesheet" href="/css/layout.css">
|
||||
<link rel="stylesheet" href="/css/cards.css">
|
||||
<link rel="stylesheet" href="/css/components.css">
|
||||
<link rel="stylesheet" href="/css/camera.css">
|
||||
<link rel="stylesheet" href="/css/audio.css">
|
||||
<link rel="stylesheet" href="/css/responsive.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
@@ -1,187 +1,56 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Журнал логов</title>
|
||||
<link rel="icon" type="image/png" href="/favicon.png">
|
||||
<link rel="stylesheet" href="/css/style.css">
|
||||
<style>
|
||||
/* Дополнительные стили для журнала */
|
||||
.logs-container {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
height: calc(100vh - 120px);
|
||||
}
|
||||
<meta charset="UTF-8">
|
||||
<title>Журнал логов</title>
|
||||
<link rel="icon" type="image/png" href="/favicon.png">
|
||||
|
||||
.logs-sidebar {
|
||||
flex: 0 0 280px;
|
||||
border-right: 1px solid #00ff88;
|
||||
padding-right: 16px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
<!-- Базовые стили -->
|
||||
<link rel="stylesheet" href="/css/base.css">
|
||||
<link rel="stylesheet" href="/css/header.css">
|
||||
<link rel="stylesheet" href="/css/layout.css">
|
||||
<link rel="stylesheet" href="/css/cards.css">
|
||||
<link rel="stylesheet" href="/css/components.css">
|
||||
<link rel="stylesheet" href="/css/camera.css">
|
||||
<link rel="stylesheet" href="/css/audio.css">
|
||||
<link rel="stylesheet" href="/css/responsive.css">
|
||||
|
||||
.logs-content {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.log-file-item {
|
||||
padding: 8px 12px;
|
||||
margin: 4px 0;
|
||||
cursor: pointer;
|
||||
border-radius: 4px;
|
||||
border: 1px solid transparent;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.log-file-item:hover {
|
||||
background: rgba(0,255,136,0.1);
|
||||
border-color: #00ff88;
|
||||
}
|
||||
|
||||
.log-file-item.active {
|
||||
background: rgba(0,255,136,0.2);
|
||||
border-color: #00ff88;
|
||||
}
|
||||
|
||||
.log-file-item .file-name {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.log-file-item .file-info {
|
||||
font-size: 11px;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.log-stats {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 12px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.log-stat-card {
|
||||
padding: 12px;
|
||||
border: 1px solid #00ff88;
|
||||
border-radius: 4px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.log-stat-card .value {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.log-stat-card .label {
|
||||
font-size: 11px;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.data-table {
|
||||
width: 100%;
|
||||
font-size: 12px;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.data-table th {
|
||||
text-align: left;
|
||||
padding: 6px 8px;
|
||||
border-bottom: 1px solid #00ff88;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
background: #050805;
|
||||
}
|
||||
|
||||
.data-table td {
|
||||
padding: 4px 8px;
|
||||
border-bottom: 1px solid rgba(0,255,136,0.1);
|
||||
}
|
||||
|
||||
.data-table tr:hover {
|
||||
background: rgba(0,255,136,0.05);
|
||||
}
|
||||
|
||||
.strength-0 { color: #666; }
|
||||
.strength-1 { color: #88ff88; }
|
||||
.strength-2 { color: #ffaa44; }
|
||||
.strength-3 { color: #ff4444; }
|
||||
|
||||
.events-list {
|
||||
font-family: monospace;
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.events-list .event-time {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.tabs {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.tab-btn {
|
||||
padding: 6px 16px;
|
||||
background: transparent;
|
||||
color: #00ff88;
|
||||
border: 1px solid #00ff88;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.tab-btn.active {
|
||||
background: rgba(0,255,136,0.2);
|
||||
}
|
||||
|
||||
.tab-btn:hover {
|
||||
background: rgba(0,255,136,0.1);
|
||||
}
|
||||
|
||||
.tab-content {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tab-content.active {
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
<!-- Специфичные стили для страницы логов -->
|
||||
<link rel="stylesheet" href="/css/logs.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="header-bar">
|
||||
<h1>📋 ЖУРНАЛ ЛОГОВ</h1>
|
||||
<div id="server-time" class="header-time">--:--:--</div>
|
||||
<a href="/" style="color:#00ff88; text-decoration:none; font-size:14px;">← На главную</a>
|
||||
</div>
|
||||
|
||||
<div class="logs-container">
|
||||
<div class="logs-sidebar">
|
||||
<h3 style="margin-top:0;">📁 Файлы данных</h3>
|
||||
<div id="fileList"></div>
|
||||
<div class="header-bar">
|
||||
<h1>ЖУРНАЛ ЛОГОВ</h1>
|
||||
<div id="server-time" class="header-time">--:--:--</div>
|
||||
<a href="/" style="color:#00ff88; text-decoration:none; font-size:14px;">← На главную</a>
|
||||
</div>
|
||||
|
||||
<div class="logs-content">
|
||||
<div class="tabs">
|
||||
<button class="tab-btn active" data-tab="data">📊 Данные</button>
|
||||
<button class="tab-btn" data-tab="events">📋 События</button>
|
||||
<div class="logs-container">
|
||||
<div class="logs-sidebar">
|
||||
<h3 style="margin-top:0;">Файлы данных</h3>
|
||||
<div id="fileList"></div>
|
||||
</div>
|
||||
|
||||
<div id="tab-data" class="tab-content active">
|
||||
<div id="logStats" class="log-stats"></div>
|
||||
<div id="chartContainer" style="margin-bottom:16px;">
|
||||
<canvas id="logChart" height="200"></canvas>
|
||||
<div class="logs-content">
|
||||
<div class="tabs">
|
||||
<button class="tab-btn active" data-tab="data">Данные</button>
|
||||
<button class="tab-btn" data-tab="events">События</button>
|
||||
</div>
|
||||
<div id="dataTableContainer" style="max-height:400px; overflow-y:auto;"></div>
|
||||
</div>
|
||||
|
||||
<div id="tab-events" class="tab-content">
|
||||
<div id="eventsContent" class="events-list">Загрузка...</div>
|
||||
<div id="tab-data" class="tab-content active">
|
||||
<div id="logStats" class="log-stats"></div>
|
||||
<div id="chartContainer" style="margin-bottom:16px;">
|
||||
<canvas id="logChart" height="200"></canvas>
|
||||
</div>
|
||||
<div id="dataTableContainer" style="max-height:400px; overflow-y:auto;"></div>
|
||||
</div>
|
||||
|
||||
<div id="tab-events" class="tab-content">
|
||||
<div id="eventsContent" class="events-list">Загрузка...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="module">
|
||||
import { drawChart } from '/dist/chart.js';
|
||||
|
||||
Reference in New Issue
Block a user