openvibers/openvibe.live Commits
Channel page: the chat no longer runs off the right edge at some widths (at 1181px+ the main column stopped scrolling on its own, and as a flex item it then refused to shrink below the one-row tabs; min-width 0). The chat can be hidden on desktop: a button at the left of the chat header slides it away so the stream or the offline page takes the full width, and a Chat tab at the middle of the window edge brings it back (it slides in). Remembered per browser; phones keep their own chat toggle.
Changes
@@@ -73,7 +73,9 @@height: calc(100vh - var(--navbar-h) - 44px); /* account for live tabs bar when visible */}.stream-main {-- flex: 1; overflow-y: auto;++ /* min-width 0: at 1181px+ it stops scrolling on its own (overflow visible), and a flex item then refuses to++ shrink below its content (the one-row tabs), pushing the chat off the right edge. */++ flex: 1; min-width: 0; overflow-y: auto;display: flex; flex-direction: column;}@@@ -1458,3 +1460,25 @@ body.controls-docked .messenger-toggle { z-index: 1100; }.ch-social-row input[data-f=url] { grid-column: 1 / -1; }.ch-socials-grid { grid-template-columns: 1fr; }}++++/* ── Hide / show the chat (desktop) ─────────────────────────── */++.chat-header .ch-chat-collapse { margin-right: 2px; }++@media (min-width: 769px) {++ #page-channel.chat-collapsed .stream-layout > .chat-sidebar,++ #page-channel.chat-collapsed .chat-resize-handle { display: none !important; }++ #page-channel.chat-revealing .stream-layout > .chat-sidebar { animation: chChatReveal .32s cubic-bezier(.22,1,.36,1); }++}++@keyframes chChatReveal { from { opacity: 0; transform: translateX(36px); } to { opacity: 1; transform: none; } }++.ch-chat-reopen {++ position: fixed; right: 0; top: 50%; transform: translateY(-50%); z-index: 60; /* mid-edge: clear of the header's buttons */++ display: inline-flex; align-items: center; gap: 7px; padding: 9px 12px 9px 12px;++ border: 1px solid var(--border); border-right: 0; border-radius: 12px 0 0 12px;++ background: color-mix(in srgb, var(--bg-card, var(--bg-secondary)) 90%, transparent); backdrop-filter: blur(10px);++ color: var(--text-secondary); font: 700 0.8rem/1 inherit; cursor: pointer; box-shadow: var(--shadow);++ transition: color .15s, border-color .15s, padding-right .18s ease;++}++.ch-chat-reopen:hover { color: var(--text-primary); border-color: var(--accent); padding-right: 16px; }++.ch-chat-reopen .fa-comments { color: var(--accent); }++.ch-chat-reopen[hidden] { display: none; }++@media (max-width: 768px) { .ch-chat-collapse, .ch-chat-reopen { display: none !important; } }++@media (prefers-reduced-motion: reduce) { #page-channel.chat-revealing .stream-layout > .chat-sidebar { animation: none; } }@@@ -1,4 +1,6 @@<!-- #page-channel markup, loaded on first visit to the route (public/features.json). The server inlines it for direct visits. -->++ <!-- Chat hidden (desktop): the stream or offline page takes the full width; this tab at the window edge brings it back. -->++ <button type="button" class="ch-chat-reopen" id="ch-chat-reopen" onclick="toggleChannelChat(false)" title="Show chat" hidden><i class="fa-solid fa-angles-left"></i><i class="fa-solid fa-comments"></i><span>Chat</span></button><!-- Live Stream Tabs (this channel's concurrent streams) --><div class="live-stream-tabs" id="live-stream-tabs" style="display:none" role="tablist" aria-label="Live streams"><div class="live-tabs-scroll" id="live-tabs-scroll"></div>@@@ -144,6 +146,7 @@<!-- Chat Sidebar --><div class="chat-sidebar" id="chat-sidebar"><div class="chat-header">++ <button type="button" class="chat-settings-btn ch-chat-collapse" onclick="toggleChannelChat(true)" title="Hide chat" aria-label="Hide chat"><i class="fa-solid fa-angles-right"></i></button><span>STREAM CHAT</span><div class="chat-header-actions"><button class="chat-settings-btn" onclick="toggleChatUsers(this)" title="Users">@@@ -268,6 +271,7 @@<!-- Streamer's persistent chat (works while offline) — sidebar, mirrors live layout --><div class="offline-global-chat chat-sidebar" id="offline-global-chat"><div class="chat-header">++ <button type="button" class="chat-settings-btn ch-chat-collapse" onclick="toggleChannelChat(true)" title="Hide chat" aria-label="Hide chat"><i class="fa-solid fa-angles-right"></i></button><span><i class="fa-solid fa-comments"></i> STREAM CHAT</span><div class="chat-header-actions"><button class="chat-settings-btn" onclick="toggleChatUsers(this)" title="Users">@@@ -563,7 +563,27 @@ async function setChannelClipsOfPage(page) {if (top) top.scrollIntoView({ behavior: 'smooth', block: 'start' });}++/** Hide or show the channel page's chat column on desktop (the stream or offline page then takes the full width). Remembered per browser. */++function toggleChannelChat(hide) {++ const page = document.getElementById('page-channel');++ if (!page) return;++ const next = typeof hide === 'boolean' ? hide : !page.classList.contains('chat-collapsed');++ const was = page.classList.contains('chat-collapsed');++ page.classList.toggle('chat-collapsed', next);++ if (was && !next) { page.classList.add('chat-revealing'); setTimeout(() => page.classList.remove('chat-revealing'), 400); }++ const re = document.getElementById('ch-chat-reopen');++ if (re) re.hidden = !next;++ try { localStorage.setItem('ov_ch_chat_hidden', next ? '1' : '0'); } catch { /* private mode */ }++ if (was !== next) window.dispatchEvent(new Event('resize')); // the player and the diagrams re-measure++}++function _restoreChannelChat() {++ let hidden = false;++ try { hidden = localStorage.getItem('ov_ch_chat_hidden') === '1'; } catch { /* */ }++ toggleChannelChat(hidden);++}++async function loadChannelPage(username, managedStreamRef = null, legacySessionId = null) {++ _restoreChannelChat();try {const isNewChannel = currentChannelUsername !== username;currentChannelUsername = username;
@@ -73,7 +73,9 @@
@@ -73,7 +73,9 @@
height: calc(100vh - var(--navbar-h) - 44px); /* account for live tabs bar when visible */
height: calc(100vh - var(--navbar-h) - 44px); /* account for live tabs bar when visible */
}
}
.stream-main {
.stream-main {
- flex: 1; overflow-y: auto;
+ /* min-width 0: at 1181px+ it stops scrolling on its own (overflow visible), and a flex item then refuses to
+ shrink below its content (the one-row tabs), pushing the chat off the right edge. */
+ flex: 1; min-width: 0; overflow-y: auto;
display: flex; flex-direction: column;
display: flex; flex-direction: column;
}
}
@@ -1458,3 +1460,25 @@ body.controls-docked .messenger-toggle { z-index: 1100; }
@@ -1458,3 +1460,25 @@ body.controls-docked .messenger-toggle { z-index: 1100; }
.ch-social-row input[data-f=url] { grid-column: 1 / -1; }
.ch-social-row input[data-f=url] { grid-column: 1 / -1; }
.ch-socials-grid { grid-template-columns: 1fr; }
.ch-socials-grid { grid-template-columns: 1fr; }
}
}
+
+/* ── Hide / show the chat (desktop) ─────────────────────────── */
+.chat-header .ch-chat-collapse { margin-right: 2px; }
+@media (min-width: 769px) {
+ #page-channel.chat-collapsed .stream-layout > .chat-sidebar,
+ #page-channel.chat-collapsed .chat-resize-handle { display: none !important; }
+ #page-channel.chat-revealing .stream-layout > .chat-sidebar { animation: chChatReveal .32s cubic-bezier(.22,1,.36,1); }
+}
+@keyframes chChatReveal { from { opacity: 0; transform: translateX(36px); } to { opacity: 1; transform: none; } }
+.ch-chat-reopen {
+ position: fixed; right: 0; top: 50%; transform: translateY(-50%); z-index: 60; /* mid-edge: clear of the header's buttons */
+ display: inline-flex; align-items: center; gap: 7px; padding: 9px 12px 9px 12px;
+ border: 1px solid var(--border); border-right: 0; border-radius: 12px 0 0 12px;
+ background: color-mix(in srgb, var(--bg-card, var(--bg-secondary)) 90%, transparent); backdrop-filter: blur(10px);
+ color: var(--text-secondary); font: 700 0.8rem/1 inherit; cursor: pointer; box-shadow: var(--shadow);
+ transition: color .15s, border-color .15s, padding-right .18s ease;
+}
+.ch-chat-reopen:hover { color: var(--text-primary); border-color: var(--accent); padding-right: 16px; }
+.ch-chat-reopen .fa-comments { color: var(--accent); }
+.ch-chat-reopen[hidden] { display: none; }
+@media (max-width: 768px) { .ch-chat-collapse, .ch-chat-reopen { display: none !important; } }
+@media (prefers-reduced-motion: reduce) { #page-channel.chat-revealing .stream-layout > .chat-sidebar { animation: none; } }
@@ -1,4 +1,6 @@
@@ -1,4 +1,6 @@
<!-- #page-channel markup, loaded on first visit to the route (public/features.json). The server inlines it for direct visits. -->
<!-- #page-channel markup, loaded on first visit to the route (public/features.json). The server inlines it for direct visits. -->
+ <!-- Chat hidden (desktop): the stream or offline page takes the full width; this tab at the window edge brings it back. -->
+ <button type="button" class="ch-chat-reopen" id="ch-chat-reopen" onclick="toggleChannelChat(false)" title="Show chat" hidden><i class="fa-solid fa-angles-left"></i><i class="fa-solid fa-comments"></i><span>Chat</span></button>
<!-- Live Stream Tabs (this channel's concurrent streams) -->
<!-- Live Stream Tabs (this channel's concurrent streams) -->
<div class="live-stream-tabs" id="live-stream-tabs" style="display:none" role="tablist" aria-label="Live streams">
<div class="live-stream-tabs" id="live-stream-tabs" style="display:none" role="tablist" aria-label="Live streams">
<div class="live-tabs-scroll" id="live-tabs-scroll"></div>
<div class="live-tabs-scroll" id="live-tabs-scroll"></div>
@@ -144,6 +146,7 @@
@@ -144,6 +146,7 @@
<!-- Chat Sidebar -->
<!-- Chat Sidebar -->
<div class="chat-sidebar" id="chat-sidebar">
<div class="chat-sidebar" id="chat-sidebar">
<div class="chat-header">
<div class="chat-header">
+ <button type="button" class="chat-settings-btn ch-chat-collapse" onclick="toggleChannelChat(true)" title="Hide chat" aria-label="Hide chat"><i class="fa-solid fa-angles-right"></i></button>
<span>STREAM CHAT</span>
<span>STREAM CHAT</span>
<div class="chat-header-actions">
<div class="chat-header-actions">
<button class="chat-settings-btn" onclick="toggleChatUsers(this)" title="Users">
<button class="chat-settings-btn" onclick="toggleChatUsers(this)" title="Users">
@@ -268,6 +271,7 @@
@@ -268,6 +271,7 @@
<!-- Streamer's persistent chat (works while offline) — sidebar, mirrors live layout -->
<!-- Streamer's persistent chat (works while offline) — sidebar, mirrors live layout -->
<div class="offline-global-chat chat-sidebar" id="offline-global-chat">
<div class="offline-global-chat chat-sidebar" id="offline-global-chat">
<div class="chat-header">
<div class="chat-header">
+ <button type="button" class="chat-settings-btn ch-chat-collapse" onclick="toggleChannelChat(true)" title="Hide chat" aria-label="Hide chat"><i class="fa-solid fa-angles-right"></i></button>
<span><i class="fa-solid fa-comments"></i> STREAM CHAT</span>
<span><i class="fa-solid fa-comments"></i> STREAM CHAT</span>
<div class="chat-header-actions">
<div class="chat-header-actions">
<button class="chat-settings-btn" onclick="toggleChatUsers(this)" title="Users">
<button class="chat-settings-btn" onclick="toggleChatUsers(this)" title="Users">
@@ -563,7 +563,27 @@ async function setChannelClipsOfPage(page) {
@@ -563,7 +563,27 @@ async function setChannelClipsOfPage(page) {
if (top) top.scrollIntoView({ behavior: 'smooth', block: 'start' });
if (top) top.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
}
+/** Hide or show the channel page's chat column on desktop (the stream or offline page then takes the full width). Remembered per browser. */
+function toggleChannelChat(hide) {
+ const page = document.getElementById('page-channel');
+ if (!page) return;
+ const next = typeof hide === 'boolean' ? hide : !page.classList.contains('chat-collapsed');
+ const was = page.classList.contains('chat-collapsed');
+ page.classList.toggle('chat-collapsed', next);
+ if (was && !next) { page.classList.add('chat-revealing'); setTimeout(() => page.classList.remove('chat-revealing'), 400); }
+ const re = document.getElementById('ch-chat-reopen');
+ if (re) re.hidden = !next;
+ try { localStorage.setItem('ov_ch_chat_hidden', next ? '1' : '0'); } catch { /* private mode */ }
+ if (was !== next) window.dispatchEvent(new Event('resize')); // the player and the diagrams re-measure
+}
+function _restoreChannelChat() {
+ let hidden = false;
+ try { hidden = localStorage.getItem('ov_ch_chat_hidden') === '1'; } catch { /* */ }
+ toggleChannelChat(hidden);
+}
+
async function loadChannelPage(username, managedStreamRef = null, legacySessionId = null) {
async function loadChannelPage(username, managedStreamRef = null, legacySessionId = null) {
+ _restoreChannelChat();
try {
try {
const isNewChannel = currentChannelUsername !== username;
const isNewChannel = currentChannelUsername !== username;
currentChannelUsername = username;
currentChannelUsername = username;