From 6d6a501b25bfac89e4cb7725b1d345861b4f85ec Mon Sep 17 00:00:00 2001 From: codex Date: Tue, 9 Jun 2026 22:33:43 +0200 Subject: [PATCH] Add collapsible YouTube panel --- public/app.js | 39 ++++++++++++++++++++++++++++++++++ public/index.html | 33 +++++++++++++++++++---------- public/styles.css | 54 ++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 107 insertions(+), 19 deletions(-) diff --git a/public/app.js b/public/app.js index 8d41509..c58e8b7 100644 --- a/public/app.js +++ b/public/app.js @@ -11,6 +11,7 @@ const youtubeInput = document.querySelector("#youtube-input"); const youtubeShell = document.querySelector("#youtube-shell"); const watchPanel = document.querySelector(".watch-panel"); const watchResizeHandle = document.querySelector("#watch-resize-handle"); +const watchCollapseButton = document.querySelector("#watch-collapse-button"); const playlistList = document.querySelector("#playlist-list"); const clearPlaylistButton = document.querySelector("#clear-playlist-button"); const cameraSelect = document.querySelector("#camera-select"); @@ -24,6 +25,7 @@ const nameStorageKey = "kkkmeet.displayName"; const cameraStorageKey = "kkkmeet.cameraDeviceId"; const micStorageKey = "kkkmeet.micDeviceId"; const watchHeightStorageKey = "kkkmeet.watchPanelHeight"; +const watchCollapsedStorageKey = "kkkmeet.watchPanelCollapsed"; const rtcConfig = { iceServers: [{ urls: "stun:stun.l.google.com:19302" }] }; @@ -49,6 +51,28 @@ function applyStoredWatchHeight() { if (savedHeight > 0) { stage.style.setProperty("--watch-panel-height", `${savedHeight}px`); } + setWatchCollapsed(localStorage.getItem(watchCollapsedStorageKey) === "true"); +} + +function setWatchCollapsed(collapsed) { + stage.classList.toggle("is-watch-collapsed", collapsed); + watchCollapseButton.textContent = collapsed ? "↓" : "↑"; + watchCollapseButton.setAttribute( + "aria-label", + collapsed ? "Expand YouTube panel" : "Collapse YouTube panel" + ); + watchCollapseButton.title = collapsed ? "Expand YouTube panel" : "Collapse YouTube panel"; + localStorage.setItem(watchCollapsedStorageKey, String(collapsed)); +} + +function toggleWatchCollapsed() { + if (!stage.classList.contains("is-watch-collapsed")) { + const height = Math.round(watchPanel.getBoundingClientRect().height); + if (height > 0) { + localStorage.setItem(watchHeightStorageKey, String(height)); + } + } + setWatchCollapsed(!stage.classList.contains("is-watch-collapsed")); } function setStatus(message) { @@ -752,6 +776,10 @@ navigator.mediaDevices?.addEventListener?.("devicechange", () => { applyStoredWatchHeight(); watchResizeHandle.addEventListener("pointerdown", (event) => { + if (event.target.closest("#watch-collapse-button") || stage.classList.contains("is-watch-collapsed")) { + return; + } + event.preventDefault(); watchResizeHandle.setPointerCapture(event.pointerId); stage.classList.add("is-resizing-watch"); @@ -779,3 +807,14 @@ watchResizeHandle.addEventListener("pointerdown", (event) => { watchResizeHandle.addEventListener("pointerup", stop); watchResizeHandle.addEventListener("pointercancel", stop); }); + +watchCollapseButton.addEventListener("click", (event) => { + event.stopPropagation(); + toggleWatchCollapsed(); +}); + +watchCollapseButton.addEventListener("keydown", (event) => { + if (event.key !== "Enter" && event.key !== " ") return; + event.preventDefault(); + toggleWatchCollapsed(); +}); diff --git a/public/index.html b/public/index.html index 676d17e..c5efbb4 100644 --- a/public/index.html +++ b/public/index.html @@ -22,16 +22,6 @@
-
- - -
@@ -42,6 +32,16 @@
    +
    + + +
    + > + + ↑ + +
    Waiting for others to join
    diff --git a/public/styles.css b/public/styles.css index c7b8790..eeb4b74 100644 --- a/public/styles.css +++ b/public/styles.css @@ -110,6 +110,10 @@ button[aria-pressed="false"] { padding: 18px 18px 12px; } +.stage.is-watch-collapsed { + grid-template-rows: 0 20px minmax(0, 1fr); +} + .watch-panel { display: grid; grid-template-columns: minmax(320px, 1fr) minmax(240px, 360px); @@ -120,17 +124,21 @@ button[aria-pressed="false"] { overflow: auto; } +.stage.is-watch-collapsed .watch-panel { + overflow: hidden; +} + .watch-main { display: grid; - grid-template-rows: auto minmax(0, 1fr); - gap: 10px; min-height: 0; } .youtube-form { display: flex; - align-items: end; + align-items: stretch; gap: 10px; + padding: 10px; + border-top: 1px solid rgba(255, 255, 255, 0.1); } .youtube-form input { @@ -171,6 +179,10 @@ button[aria-pressed="false"] { cursor: row-resize; } +.stage.is-watch-collapsed .watch-resize-handle { + cursor: default; +} + .watch-resize-handle::before { content: ""; position: absolute; @@ -183,6 +195,32 @@ button[aria-pressed="false"] { transform: translate(-50%, -50%); } +.watch-collapse-button { + position: absolute; + left: 50%; + top: 50%; + z-index: 1; + display: inline-grid; + place-items: center; + width: 34px; + height: 20px; + border-radius: 999px; + background: #1f8f5f; + color: #ffffff; + font-size: 14px; + font-weight: 800; + line-height: 1; + cursor: pointer; + transform: translate(-50%, -50%); +} + +.watch-collapse-button:hover, +.watch-collapse-button:focus-visible { + filter: brightness(1.12); + outline: 2px solid rgba(255, 255, 255, 0.42); + outline-offset: 2px; +} + .watch-resize-handle:hover::before, .watch-resize-handle:focus-visible::before, .stage.is-resizing-watch .watch-resize-handle::before { @@ -195,7 +233,7 @@ button[aria-pressed="false"] { .playlist-panel { display: grid; - grid-template-rows: auto minmax(0, 1fr); + grid-template-rows: auto minmax(0, 1fr) auto; min-height: 0; border-radius: 8px; background: rgba(255, 255, 255, 0.05); @@ -490,16 +528,16 @@ button[aria-pressed="false"] { grid-template-rows: var(--watch-panel-height, 430px) 14px minmax(0, 1fr); } + .stage.is-watch-collapsed { + grid-template-rows: 0 20px minmax(0, 1fr); + } + .watch-panel { grid-template-columns: 1fr; grid-template-rows: minmax(220px, 1fr) minmax(160px, auto); min-height: 430px; } - .youtube-form { - align-items: stretch; - } - .remote-grid { grid-template-columns: 1fr; grid-auto-rows: minmax(190px, 1fr);