diff --git a/fishtank-dashboard.html b/fishtank-dashboard.html index 92b601d..f5b2789 100644 --- a/fishtank-dashboard.html +++ b/fishtank-dashboard.html @@ -161,9 +161,9 @@ position: relative; z-index: 1; display: grid; - grid-template-columns: 320px 1fr 320px; + grid-template-columns: 320px 1fr 300px; grid-template-rows: 320px 1fr; - grid-template-areas: "poll stocks camman" "tts cameras cameras"; + grid-template-areas: "poll stocks chat" "tts cameras chat"; gap: 1px; flex: 1; min-height: 0; @@ -173,24 +173,24 @@ } .main.stocks-collapsed { - grid-template-columns: 320px 1fr 0px; - grid-template-areas: "poll cameras cameras" "tts cameras cameras"; + grid-template-columns: 320px 1fr 300px; + grid-template-areas: "poll cameras chat" "tts cameras chat"; } .main.stocks-collapsed .stocks-hide { display: none; } .main.left-collapsed { - grid-template-columns: 0px 1fr 320px; - grid-template-areas: "stocks stocks camman" "cameras cameras camman"; + grid-template-columns: 0px 1fr 300px; + grid-template-areas: "stocks stocks chat" "cameras cameras chat"; } .main.left-collapsed .left-hide { display: none; } .main.left-collapsed.stocks-collapsed { - grid-template-columns: 0px 1fr 0px; - grid-template-areas: "cameras cameras cameras" "cameras cameras cameras"; + grid-template-columns: 0px 1fr 300px; + grid-template-areas: "cameras cameras chat" "cameras cameras chat"; } .main.left-collapsed.stocks-collapsed .stocks-hide { display: none; @@ -998,6 +998,139 @@ margin-top: 2px; } + /* Inline chat panel */ + .chat-panel { + grid-area: chat; + display: flex; + flex-direction: column; + background: var(--panel); + border-left: 1px solid var(--border); + overflow: hidden; + min-height: 0; + } + + .main.chat-collapsed .chat-panel { + display: none; + } + + .main.chat-collapsed { + grid-template-columns: 320px 1fr 0px !important; + grid-template-areas: "poll stocks ." "tts cameras ." !important; + } + .main.chat-collapsed.stocks-collapsed { + grid-template-areas: "poll cameras ." "tts cameras ." !important; + } + .main.chat-collapsed.left-collapsed { + grid-template-columns: 0px 1fr 0px !important; + grid-template-areas: "stocks stocks ." "cameras cameras ." !important; + } + .main.chat-collapsed.left-collapsed.stocks-collapsed { + grid-template-columns: 0px 1fr 0px !important; + grid-template-areas: "cameras cameras cameras" "cameras cameras cameras" !important; + } + + #inlineChatFeed { + flex: 1; + overflow-y: auto; + padding: 6px; + display: flex; + flex-direction: column; + gap: 2px; + min-height: 0; + } + + #inlineChatFeed::-webkit-scrollbar { width: 4px; } + #inlineChatFeed::-webkit-scrollbar-track { background: transparent; } + #inlineChatFeed::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; } + + #inlineChatFeed .msg { + padding: 3px 6px; + border-radius: 3px; + font-size: 11px; + line-height: 1.4; + border-left: 2px solid transparent; + } + + #inlineChatFeed .msg.fish { border-left-color: var(--accent3); background: rgba(255,230,0,0.04); } + #inlineChatFeed .msg.admin { border-left-color: var(--accent2); background: rgba(255,61,113,0.06); } + #inlineChatFeed .msg.mod { border-left-color: var(--green); background: rgba(0,255,136,0.05); } + + #inlineChatFeed .msg-header { + display: flex; + align-items: baseline; + gap: 4px; + flex-wrap: wrap; + } + + #inlineChatFeed .msg-user { + font-family: 'Share Tech Mono', monospace; + font-size: 10px; + font-weight: bold; + color: var(--accent); + } + + #inlineChatFeed .msg-endorsement { + font-family: 'Share Tech Mono', monospace; + font-size: 9px; + font-weight: bold; + letter-spacing: 1px; + opacity: 0.85; + } + + #inlineChatFeed .msg-time { + font-family: 'Share Tech Mono', monospace; + font-size: 9px; + color: var(--muted); + margin-left: auto; + } + + #inlineChatFeed .msg-text { + color: var(--text); + word-break: break-word; + } + + #inlineChatFeed .badge { + font-family: 'Share Tech Mono', monospace; + font-size: 8px; + padding: 1px 4px; + border-radius: 2px; + font-weight: bold; + letter-spacing: 0.5px; + } + + #inlineChatFeed .badge.admin { background: var(--accent2); color: #fff; } + #inlineChatFeed .badge.mod { background: var(--green); color: #000; } + #inlineChatFeed .badge.fish { background: var(--accent3); color: #000; } + #inlineChatFeed .badge.gm { background: var(--accent); color: #000; } + + .chat-footer { + padding: 4px 8px; + font-family: 'Share Tech Mono', monospace; + font-size: 9px; + color: var(--muted); + border-top: 1px solid var(--border); + display: flex; + align-items: center; + gap: 8px; + flex-shrink: 0; + } + + #inlineChatScrollBtn { + display: none; + position: absolute; + bottom: 28px; + right: 8px; + background: var(--accent); + color: var(--bg); + border: none; + border-radius: 3px; + font-family: 'Share Tech Mono', monospace; + font-size: 9px; + padding: 3px 8px; + cursor: pointer; + z-index: 10; + } + /* Viewer count badge */ .cam-viewers { position: absolute; @@ -1272,6 +1405,139 @@ margin-top: 2px; } + /* Inline chat panel */ + .chat-panel { + grid-area: chat; + display: flex; + flex-direction: column; + background: var(--panel); + border-left: 1px solid var(--border); + overflow: hidden; + min-height: 0; + } + + .main.chat-collapsed .chat-panel { + display: none; + } + + .main.chat-collapsed { + grid-template-columns: 320px 1fr 0px !important; + grid-template-areas: "poll stocks ." "tts cameras ." !important; + } + .main.chat-collapsed.stocks-collapsed { + grid-template-areas: "poll cameras ." "tts cameras ." !important; + } + .main.chat-collapsed.left-collapsed { + grid-template-columns: 0px 1fr 0px !important; + grid-template-areas: "stocks stocks ." "cameras cameras ." !important; + } + .main.chat-collapsed.left-collapsed.stocks-collapsed { + grid-template-columns: 0px 1fr 0px !important; + grid-template-areas: "cameras cameras cameras" "cameras cameras cameras" !important; + } + + #inlineChatFeed { + flex: 1; + overflow-y: auto; + padding: 6px; + display: flex; + flex-direction: column; + gap: 2px; + min-height: 0; + } + + #inlineChatFeed::-webkit-scrollbar { width: 4px; } + #inlineChatFeed::-webkit-scrollbar-track { background: transparent; } + #inlineChatFeed::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; } + + #inlineChatFeed .msg { + padding: 3px 6px; + border-radius: 3px; + font-size: 11px; + line-height: 1.4; + border-left: 2px solid transparent; + } + + #inlineChatFeed .msg.fish { border-left-color: var(--accent3); background: rgba(255,230,0,0.04); } + #inlineChatFeed .msg.admin { border-left-color: var(--accent2); background: rgba(255,61,113,0.06); } + #inlineChatFeed .msg.mod { border-left-color: var(--green); background: rgba(0,255,136,0.05); } + + #inlineChatFeed .msg-header { + display: flex; + align-items: baseline; + gap: 4px; + flex-wrap: wrap; + } + + #inlineChatFeed .msg-user { + font-family: 'Share Tech Mono', monospace; + font-size: 10px; + font-weight: bold; + color: var(--accent); + } + + #inlineChatFeed .msg-endorsement { + font-family: 'Share Tech Mono', monospace; + font-size: 9px; + font-weight: bold; + letter-spacing: 1px; + opacity: 0.85; + } + + #inlineChatFeed .msg-time { + font-family: 'Share Tech Mono', monospace; + font-size: 9px; + color: var(--muted); + margin-left: auto; + } + + #inlineChatFeed .msg-text { + color: var(--text); + word-break: break-word; + } + + #inlineChatFeed .badge { + font-family: 'Share Tech Mono', monospace; + font-size: 8px; + padding: 1px 4px; + border-radius: 2px; + font-weight: bold; + letter-spacing: 0.5px; + } + + #inlineChatFeed .badge.admin { background: var(--accent2); color: #fff; } + #inlineChatFeed .badge.mod { background: var(--green); color: #000; } + #inlineChatFeed .badge.fish { background: var(--accent3); color: #000; } + #inlineChatFeed .badge.gm { background: var(--accent); color: #000; } + + .chat-footer { + padding: 4px 8px; + font-family: 'Share Tech Mono', monospace; + font-size: 9px; + color: var(--muted); + border-top: 1px solid var(--border); + display: flex; + align-items: center; + gap: 8px; + flex-shrink: 0; + } + + #inlineChatScrollBtn { + display: none; + position: absolute; + bottom: 28px; + right: 8px; + background: var(--accent); + color: var(--bg); + border: none; + border-radius: 3px; + font-family: 'Share Tech Mono', monospace; + font-size: 9px; + padding: 3px 8px; + cursor: pointer; + z-index: 10; + } + /* Viewer count badge */ .cam-viewers { position: absolute; @@ -1472,7 +1738,6 @@ setApiStatus('none'); } initCameras(); - initCamerman(); // Apply saved thumbnail interval from dropdown const iv = document.getElementById('intervalSelect'); if (iv && parseInt(iv.value) !== 30000) changeInterval(iv.value); @@ -1487,67 +1752,6 @@ }, 500); }); - function initCamerman() { - const video = document.getElementById('cammanVideo'); - if (!video) return; - const idx = CAMERAS.findIndex(([,s]) => s === 'cameraman2-5'); - if (typeof Hls !== 'undefined' && Hls.isSupported()) { - if (hlsInstances['camman']) hlsInstances['camman'].destroy(); - const hls = new Hls({ lowLatencyMode: true, maxBufferLength: 8 }); - hls.loadSource('http://localhost:3000/cam/cameraman2-5/index.m3u8'); - hls.attachMedia(video); - hls.on(Hls.Events.MANIFEST_PARSED, () => video.play().catch(() => {})); - hls.on(Hls.Events.ERROR, (e, d) => { - if (d.fatal) { - const label = document.getElementById('cammanLabel'); - if (label) label.textContent = 'CAMERAMAN · RECONNECTING...'; - hls.destroy(); - setTimeout(() => { - if (video.isConnected) initCamerman(); - }, 3000); - } - }); - hlsInstances['camman'] = hls; - } else if (video.canPlayType('application/vnd.apple.mpegurl')) { - video.src = 'http://localhost:3000/cam/cameraman2-5/index.m3u8'; - video.play().catch(() => {}); - } - } - - // Update cameraman panel label when it gets swapped with featured - const _origSetFeatured = setFeatured; - setFeatured = function(i) { - _origSetFeatured(i); - // If cameraman is now in featured, show director in camman panel - // If cameraman is what's being swapped back, restore its own stream - const cammanIdx = CAMERAS.findIndex(([,s]) => s === 'cameraman2-5'); - const cammanVideo = document.getElementById('cammanVideo'); - const cammanLabel = document.getElementById('cammanLabel'); - if (!cammanVideo) return; - - if (i === cammanIdx) { - // Cameraman just went to featured - show director in camman panel - const dirSlug = CAMERAS[DEFAULT_IDX][1]; - if (hlsInstances['camman']) hlsInstances['camman'].destroy(); - const hls = new Hls({ lowLatencyMode: true, maxBufferLength: 8 }); - hls.loadSource('http://localhost:3000/cam/' + dirSlug + '/index.m3u8'); - hls.attachMedia(cammanVideo); - hls.on(Hls.Events.MANIFEST_PARSED, () => { cammanVideo.muted = true; cammanVideo.play().catch(() => {}); }); - hlsInstances['camman'] = hls; - cammanLabel.textContent = 'DIRECTOR MODE'; - } else if (directorCell === cammanIdx) { - // Director is in camman panel cell — keep it - } else { - // Restore camman panel to cameraman stream - if (hlsInstances['camman']) hlsInstances['camman'].destroy(); - const hls = new Hls({ lowLatencyMode: true, maxBufferLength: 8 }); - hls.loadSource('http://localhost:3000/cam/cameraman2-5/index.m3u8'); - hls.attachMedia(cammanVideo); - hls.on(Hls.Events.MANIFEST_PARSED, () => { cammanVideo.muted = true; cammanVideo.play().catch(() => {}); }); - hlsInstances['camman'] = hls; - cammanLabel.textContent = 'CAMERAMAN'; - } - };
@@ -1659,7 +1801,7 @@