From 5b8f11dbb6ad137a33dbf9bbfee9f216973c8601 Mon Sep 17 00:00:00 2001 From: core Date: Mon, 1 Jun 2026 21:49:56 +0500 Subject: [PATCH] live: group paging next to play/stop; add grid-split menu before fullscreen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The grid control footer is regrouped: ⏮ play/stop ⏭ now sit together in the centre (left section empty). On the right, before fullscreen, a new ▦ split button opens an upward popup menu with the layouts 4/9/16/25/36/49/64 that calls setDim; the menu respects maxDim (16+ mode), highlights the active layout, and closes on outside click. Co-Authored-By: Claude Opus 4.8 (1M context) --- VERSION | 2 +- frontend/index.html | 19 +++++++++++++++---- frontend/static/app.js | 23 ++++++++++++++++++++--- frontend/static/style.css | 16 ++++++++++++++++ 4 files changed, 52 insertions(+), 8 deletions(-) diff --git a/VERSION b/VERSION index 3905667..fd3c1fe 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.0.139 +0.0.140 diff --git a/frontend/index.html b/frontend/index.html index 249e7ed..efb002b 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -29,14 +29,25 @@
diff --git a/frontend/static/app.js b/frontend/static/app.js index 6cda69f..48e11b5 100644 --- a/frontend/static/app.js +++ b/frontend/static/app.js @@ -517,8 +517,8 @@ const Live = { }, applyMaxDim() { - // скрыть пункты подменю с числом больше лимита (режим 16+) - document.querySelectorAll(".subnav .subitem[data-grid]").forEach((a) => { + // скрыть пункты с числом больше лимита (режим 16+) — и в сайдбаре, и в меню «Раскладка» + document.querySelectorAll(".subnav .subitem[data-grid], .gb-split-menu button[data-grid]").forEach((a) => { a.hidden = +a.dataset.grid > this.maxDim; }); }, @@ -537,7 +537,7 @@ const Live = { localStorage.setItem("corevision.grid", String(this.dim)); this.page = 0; // смена размера сетки → первая страница this.fillSelected(); // selected = камеры текущей страницы - document.querySelectorAll("#live-subnav .subitem, .subnav .subitem").forEach((a) => + document.querySelectorAll("#live-subnav .subitem, .subnav .subitem, .gb-split-menu button").forEach((a) => a.classList.toggle("active", +a.dataset.grid === this.dim)); if (rerender) this.renderGrid(); this.updatePager(); @@ -605,6 +605,23 @@ const Live = { if (gbPlay) gbPlay.onclick = (e) => { e.preventDefault(); this.setPlaying(!this.playing); }; const gbFs = document.getElementById("gb-fs"); if (gbFs) gbFs.onclick = (e) => { e.preventDefault(); fsMain(); }; + // меню «Раскладка» (window split): 4/9/16/25/36/49/64 → setDim + const gbSplit = document.getElementById("gb-split"); + const splitMenu = document.getElementById("gb-split-menu"); + if (gbSplit && splitMenu) { + gbSplit.onclick = (e) => { e.preventDefault(); splitMenu.classList.toggle("open"); }; + splitMenu.onclick = (e) => { + const b = e.target.closest("button[data-grid]"); + if (!b) return; + e.preventDefault(); + history.replaceState(null, "", "/?grid=" + b.dataset.grid); + this.setDim(+b.dataset.grid); + splitMenu.classList.remove("open"); + }; + document.addEventListener("click", (e) => { // клик вне меню — закрыть + if (!e.target.closest(".gb-split")) splitMenu.classList.remove("open"); + }); + } this.updatePlayBtn(); this.updatePager(); }, diff --git a/frontend/static/style.css b/frontend/static/style.css index 14ec8a1..27dad85 100644 --- a/frontend/static/style.css +++ b/frontend/static/style.css @@ -190,6 +190,22 @@ footer.grid-bar { .grid-bar .gb-btn:hover:not(:disabled) { border-color: #c0392b; color: #fff; } .grid-bar .gb-btn:disabled { opacity: .3; cursor: default; } .grid-bar .gb-play { min-width: 42px; font-size: 15px; } +/* кнопка «Раскладка» (window split) + всплывающее меню (раскрывается вверх) */ +.grid-bar .gb-split { position: relative; display: inline-flex; } +.grid-bar .gb-split-menu { + display: none; position: absolute; bottom: calc(100% + 6px); right: 0; z-index: 50; + flex-direction: column; gap: 2px; padding: 4px; min-width: 60px; + background: var(--panel); border: 1px solid var(--border); border-radius: 8px; + box-shadow: 0 6px 20px rgba(0, 0, 0, .5); +} +.grid-bar .gb-split-menu.open { display: flex; } +.grid-bar .gb-split-menu button { + height: 24px; padding: 0 10px; border: 1px solid transparent; border-radius: 5px; + background: none; color: var(--text); cursor: pointer; font-size: 13px; + font-variant-numeric: tabular-nums; text-align: center; +} +.grid-bar .gb-split-menu button:hover { background: var(--panel-2); } +.grid-bar .gb-split-menu button.active { background: var(--accent); border-color: #c0392b; color: #fff; } .tile { position: relative; background: #05070a; border: 1px solid var(--border); display: flex; align-items: center; justify-content: center; overflow: hidden;