diff --git a/cmd/server/main.go b/cmd/server/main.go index 934ff62..43b55d8 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -16,6 +16,7 @@ import ( ) // ===== EMBED WEB ===== +// //go:embed web/* var webFiles embed.FS @@ -75,6 +76,12 @@ func (a *API) HandleHistory(w http.ResponseWriter, r *http.Request) { }) } +func (a *API) HandleStream(w http.ResponseWriter, r *http.Request) { + writeJSON(w, map[string]string{ + "cam": "http://192.168.77.1:1984/stream.html?src=cam", + }) +} + // ===== PIPE READER ===== func startPipeReader(ctx context.Context, buf *adapter.RingBuffer, path string) { @@ -137,7 +144,6 @@ func main() { // ===== API ===== http.Handle("/api/", http.StripPrefix("/api", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - switch r.URL.Path { case "/latest": cors(api.HandleLatest)(w, r) @@ -145,6 +151,8 @@ func main() { cors(api.HandleHistory)(w, r) case "/health": cors(api.HandleHealth)(w, r) + case "/stream": + cors(api.HandleStream)(w, r) default: http.Error(w, "not found", 404) } diff --git a/cmd/server/web/css/style.css b/cmd/server/web/css/style.css index 2c2f5ec..37331c6 100644 --- a/cmd/server/web/css/style.css +++ b/cmd/server/web/css/style.css @@ -224,6 +224,165 @@ h1 { opacity: 0; } + +/* ОТКРЫТОЕ СОСТОЯНИЕ */ +.cam-modal.open { + transform: translateX(0); +} + +.cam-header { + display: flex; + justify-content: space-between; + padding: 8px; + color: white; + background: #222; +} + +.cam-frame { + flex: 1; + width: 100%; + border: none; + object-fit: contain; + background: #000; + min-height: 200px; +} + +.cam-loading { + display: flex; + align-items: center; + justify-content: center; + color: #00ff88; + font-size: 18px; +} + +.hidden { + display: none; +} + +/* ===== SPLIT LAYOUT ===== */ + +.layout { + display: flex; + height: 100vh; + width: 100vw; + overflow: hidden; +} + +.panel { + height: 100%; + overflow: auto; +} + +.left { + flex: 1; + min-width: 300px; + transition: all 0.3s ease; +} + +.right { + flex: 0 0 40%; + min-width: 300px; + transition: all 0.3s ease; +} + +.right.hidden { + flex: 0 0 0 !important; + min-width: 0 !important; + width: 0 !important; + overflow: hidden; + opacity: 0; +} + +/* DRAG BAR */ +.resizer { + width: 6px; + cursor: col-resize; + background: #00ff88; + opacity: 0.25; + transition: opacity 0.2s; +} + +.resizer:hover { + opacity: 1; +} + +.resizer.hidden { + display: none; +} + +/* ===== CAMERA ===== */ + +.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; +} + +.cam-loading { + display: flex; + align-items: center; + justify-content: center; + color: #00ff88; +} + +.cam-btn { + position: fixed; + top: 20px; + right: 20px; + z-index: 1001; + padding: 10px 15px; + background: rgba(0, 0, 0, 0.8); + color: #00ff88; + border: 1px solid #00ff88; + border-radius: 8px; + cursor: pointer; + font-family: monospace; + font-size: 14px; + display: flex; + align-items: center; + gap: 8px; + transition: all 0.3s; +} + +.cam-btn:hover { + background: rgba(0, 255, 136, 0.2); + box-shadow: 0 0 15px rgba(0, 255, 136, 0.3); +} + +.cam-btn-icon { + font-size: 18px; +} + +/* Панель камеры скрыта */ +.panel.right[style*="display: none"] { + display: none !important; +} + +/* Когда камера скрыта, левая панель на весь экран */ +.layout:has(.panel.right[style*="display: none"]) .left { + flex: 1; +} + +.layout:has(.panel.right[style*="display: none"]) .resizer { + display: none; +} + /* ===== MOBILE ===== */ @media (max-width: 500px) { h1 { diff --git a/cmd/server/web/dashboard.html b/cmd/server/web/dashboard.html index 659600c..81307bc 100644 --- a/cmd/server/web/dashboard.html +++ b/cmd/server/web/dashboard.html @@ -13,55 +13,81 @@