From e36823d3ba742926dbb4987c96d94c04e4fa5b26 Mon Sep 17 00:00:00 2001
From: Tot Maxim
Date: Sat, 9 May 2026 18:20:00 +0300
Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?=
=?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D0=BA=D0=B0=D0=BC=D0=B5=D1=80=D1=8B=20?=
=?UTF-8?q?=D0=BD=D0=B0=20=D1=81=D0=B5=D1=80=D0=B2=D0=B5=D1=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
cmd/server/main.go | 10 ++-
cmd/server/web/css/style.css | 159 ++++++++++++++++++++++++++++++++++
cmd/server/web/dashboard.html | 98 +++++++++++++--------
cmd/server/web/js/app.js | 25 ++++++
cmd/server/web/js/camera.js | 73 ++++++++++++++++
cmd/server/web/js/layout.js | 92 ++++++++++++++++++++
6 files changed, 420 insertions(+), 37 deletions(-)
create mode 100644 cmd/server/web/js/camera.js
create mode 100644 cmd/server/web/js/layout.js
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 @@
GPIO МОНИТОРИНГ В РЕАЛЬНОМ ВРЕМЕНИ
-
+
+
-
+
+
+
+
-
-
УРОВНИ КАНАЛОВ
+
-
-
Канал 1 — 0
-
-
-
+
-
-
Звук
-
-
+
+
ГРАФИК КАНАЛА 1
+
-
-
-
-
ГРАФИК КАНАЛА 1
-
-
+
+
-
-
-
-
+
+
+
+
+
@@ -70,4 +96,4 @@