mirror of
https://github.com/fishtank-dashboard/fishtank-dashboard.git
synced 2026-04-30 10:42:02 -04:00
navigation improvements
This commit is contained in:
committed by
GitHub
parent
b5ad1669de
commit
7276886cfd
@@ -1000,7 +1000,6 @@
|
|||||||
|
|
||||||
/* Inline chat panel */
|
/* Inline chat panel */
|
||||||
.chat-panel {
|
.chat-panel {
|
||||||
grid-area: chat;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
background: var(--panel);
|
background: var(--panel);
|
||||||
@@ -1131,6 +1130,188 @@
|
|||||||
z-index: 10;
|
z-index: 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Notification log */
|
||||||
|
#notifLog {
|
||||||
|
position: fixed;
|
||||||
|
top: 73px;
|
||||||
|
right: 0;
|
||||||
|
width: 320px;
|
||||||
|
max-height: calc(100vh - 73px);
|
||||||
|
background: var(--panel);
|
||||||
|
border-left: 1px solid var(--border);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
z-index: 200;
|
||||||
|
transform: translateX(100%);
|
||||||
|
transition: transform 0.2s ease;
|
||||||
|
}
|
||||||
|
#notifLog.open { transform: translateX(0); }
|
||||||
|
.notif-log-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
font-family: 'Bebas Neue', sans-serif;
|
||||||
|
font-size: 14px;
|
||||||
|
letter-spacing: 2px;
|
||||||
|
color: var(--accent);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.notif-log-clear {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: var(--muted);
|
||||||
|
font-family: 'Share Tech Mono', monospace;
|
||||||
|
font-size: 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 2px 6px;
|
||||||
|
}
|
||||||
|
.notif-log-clear:hover { color: var(--accent2); }
|
||||||
|
#notifLogFeed {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 8px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
#notifLogFeed::-webkit-scrollbar { width: 4px; }
|
||||||
|
#notifLogFeed::-webkit-scrollbar-track { background: transparent; }
|
||||||
|
#notifLogFeed::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }
|
||||||
|
.notif-log-item {
|
||||||
|
background: var(--bg);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 8px 10px;
|
||||||
|
}
|
||||||
|
.notif-log-item-msg { font-size: 12px; color: var(--text); }
|
||||||
|
.notif-log-item-sub { font-size: 10px; color: var(--muted); margin-top: 2px; }
|
||||||
|
.notif-log-item-time {
|
||||||
|
font-family: 'Share Tech Mono', monospace;
|
||||||
|
font-size: 9px;
|
||||||
|
color: var(--muted);
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
#notifLogBtn { position: relative; }
|
||||||
|
.notif-log-badge {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
top: -4px;
|
||||||
|
right: -4px;
|
||||||
|
background: var(--accent2);
|
||||||
|
color: #fff;
|
||||||
|
font-family: 'Share Tech Mono', monospace;
|
||||||
|
font-size: 9px;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 1px 5px;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Floor plan panel */
|
||||||
|
.floor-plan-panel {
|
||||||
|
flex-shrink: 0;
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
background: var(--panel);
|
||||||
|
padding: 6px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
.floor-plan-tabs {
|
||||||
|
display: flex;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
.floor-plan-tab {
|
||||||
|
flex: 1;
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
color: var(--muted);
|
||||||
|
font-family: 'Share Tech Mono', monospace;
|
||||||
|
font-size: 9px;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
padding: 3px 0;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 2px;
|
||||||
|
transition: all 0.15s;
|
||||||
|
}
|
||||||
|
.floor-plan-tab.active {
|
||||||
|
border-color: var(--accent);
|
||||||
|
color: var(--accent);
|
||||||
|
background: rgba(0,229,255,0.06);
|
||||||
|
}
|
||||||
|
.floor-plan-wrap {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.floor-plan-wrap img {
|
||||||
|
width: 100%;
|
||||||
|
display: block;
|
||||||
|
opacity: 0.85;
|
||||||
|
}
|
||||||
|
.floor-plan-btn {
|
||||||
|
position: absolute;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
color: #DC6F41;
|
||||||
|
font-family: 'Share Tech Mono', monospace;
|
||||||
|
font-size: 9px;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 2px;
|
||||||
|
line-height: 1.2;
|
||||||
|
text-align: center;
|
||||||
|
transition: background 0.15s;
|
||||||
|
font-weight: bold;
|
||||||
|
text-shadow: 0 0 4px rgba(0,0,0,0.8);
|
||||||
|
}
|
||||||
|
.floor-plan-btn:hover {
|
||||||
|
background: rgba(220,111,65,0.25);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
.floor-plan-btn.active-room {
|
||||||
|
background: rgba(220,111,65,0.35);
|
||||||
|
color: #fff;
|
||||||
|
outline: 1px solid #DC6F41;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Camera navigation overlay */
|
||||||
|
.cam-nav-overlay {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
.cam-nav-overlay svg {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.cam-nav-overlay polygon {
|
||||||
|
fill: transparent;
|
||||||
|
transition: fill 0.15s;
|
||||||
|
pointer-events: auto;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.cam-nav-overlay polygon:hover {
|
||||||
|
fill: rgba(255,255,255,0.18);
|
||||||
|
}
|
||||||
|
.cam-nav-tooltip {
|
||||||
|
position: absolute;
|
||||||
|
background: rgba(0,0,0,0.85);
|
||||||
|
color: var(--accent);
|
||||||
|
font-family: 'Share Tech Mono', monospace;
|
||||||
|
font-size: 10px;
|
||||||
|
padding: 4px 8px;
|
||||||
|
border-radius: 3px;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
pointer-events: none;
|
||||||
|
white-space: nowrap;
|
||||||
|
z-index: 20;
|
||||||
|
display: none;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
/* Viewer count badge */
|
/* Viewer count badge */
|
||||||
.cam-viewers {
|
.cam-viewers {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@@ -1407,7 +1588,6 @@
|
|||||||
|
|
||||||
/* Inline chat panel */
|
/* Inline chat panel */
|
||||||
.chat-panel {
|
.chat-panel {
|
||||||
grid-area: chat;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
background: var(--panel);
|
background: var(--panel);
|
||||||
@@ -1538,6 +1718,188 @@
|
|||||||
z-index: 10;
|
z-index: 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Notification log */
|
||||||
|
#notifLog {
|
||||||
|
position: fixed;
|
||||||
|
top: 73px;
|
||||||
|
right: 0;
|
||||||
|
width: 320px;
|
||||||
|
max-height: calc(100vh - 73px);
|
||||||
|
background: var(--panel);
|
||||||
|
border-left: 1px solid var(--border);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
z-index: 200;
|
||||||
|
transform: translateX(100%);
|
||||||
|
transition: transform 0.2s ease;
|
||||||
|
}
|
||||||
|
#notifLog.open { transform: translateX(0); }
|
||||||
|
.notif-log-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
font-family: 'Bebas Neue', sans-serif;
|
||||||
|
font-size: 14px;
|
||||||
|
letter-spacing: 2px;
|
||||||
|
color: var(--accent);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.notif-log-clear {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: var(--muted);
|
||||||
|
font-family: 'Share Tech Mono', monospace;
|
||||||
|
font-size: 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 2px 6px;
|
||||||
|
}
|
||||||
|
.notif-log-clear:hover { color: var(--accent2); }
|
||||||
|
#notifLogFeed {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 8px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
#notifLogFeed::-webkit-scrollbar { width: 4px; }
|
||||||
|
#notifLogFeed::-webkit-scrollbar-track { background: transparent; }
|
||||||
|
#notifLogFeed::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }
|
||||||
|
.notif-log-item {
|
||||||
|
background: var(--bg);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 8px 10px;
|
||||||
|
}
|
||||||
|
.notif-log-item-msg { font-size: 12px; color: var(--text); }
|
||||||
|
.notif-log-item-sub { font-size: 10px; color: var(--muted); margin-top: 2px; }
|
||||||
|
.notif-log-item-time {
|
||||||
|
font-family: 'Share Tech Mono', monospace;
|
||||||
|
font-size: 9px;
|
||||||
|
color: var(--muted);
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
#notifLogBtn { position: relative; }
|
||||||
|
.notif-log-badge {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
top: -4px;
|
||||||
|
right: -4px;
|
||||||
|
background: var(--accent2);
|
||||||
|
color: #fff;
|
||||||
|
font-family: 'Share Tech Mono', monospace;
|
||||||
|
font-size: 9px;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 1px 5px;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Floor plan panel */
|
||||||
|
.floor-plan-panel {
|
||||||
|
flex-shrink: 0;
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
background: var(--panel);
|
||||||
|
padding: 6px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
.floor-plan-tabs {
|
||||||
|
display: flex;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
.floor-plan-tab {
|
||||||
|
flex: 1;
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
color: var(--muted);
|
||||||
|
font-family: 'Share Tech Mono', monospace;
|
||||||
|
font-size: 9px;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
padding: 3px 0;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 2px;
|
||||||
|
transition: all 0.15s;
|
||||||
|
}
|
||||||
|
.floor-plan-tab.active {
|
||||||
|
border-color: var(--accent);
|
||||||
|
color: var(--accent);
|
||||||
|
background: rgba(0,229,255,0.06);
|
||||||
|
}
|
||||||
|
.floor-plan-wrap {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.floor-plan-wrap img {
|
||||||
|
width: 100%;
|
||||||
|
display: block;
|
||||||
|
opacity: 0.85;
|
||||||
|
}
|
||||||
|
.floor-plan-btn {
|
||||||
|
position: absolute;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
color: #DC6F41;
|
||||||
|
font-family: 'Share Tech Mono', monospace;
|
||||||
|
font-size: 9px;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 2px;
|
||||||
|
line-height: 1.2;
|
||||||
|
text-align: center;
|
||||||
|
transition: background 0.15s;
|
||||||
|
font-weight: bold;
|
||||||
|
text-shadow: 0 0 4px rgba(0,0,0,0.8);
|
||||||
|
}
|
||||||
|
.floor-plan-btn:hover {
|
||||||
|
background: rgba(220,111,65,0.25);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
.floor-plan-btn.active-room {
|
||||||
|
background: rgba(220,111,65,0.35);
|
||||||
|
color: #fff;
|
||||||
|
outline: 1px solid #DC6F41;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Camera navigation overlay */
|
||||||
|
.cam-nav-overlay {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
.cam-nav-overlay svg {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.cam-nav-overlay polygon {
|
||||||
|
fill: transparent;
|
||||||
|
transition: fill 0.15s;
|
||||||
|
pointer-events: auto;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.cam-nav-overlay polygon:hover {
|
||||||
|
fill: rgba(255,255,255,0.18);
|
||||||
|
}
|
||||||
|
.cam-nav-tooltip {
|
||||||
|
position: absolute;
|
||||||
|
background: rgba(0,0,0,0.85);
|
||||||
|
color: var(--accent);
|
||||||
|
font-family: 'Share Tech Mono', monospace;
|
||||||
|
font-size: 10px;
|
||||||
|
padding: 4px 8px;
|
||||||
|
border-radius: 3px;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
pointer-events: none;
|
||||||
|
white-space: nowrap;
|
||||||
|
z-index: 20;
|
||||||
|
display: none;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
/* Viewer count badge */
|
/* Viewer count badge */
|
||||||
.cam-viewers {
|
.cam-viewers {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@@ -1794,6 +2156,16 @@
|
|||||||
<button class="notif-close" onclick="hideNotif()">✕</button>
|
<button class="notif-close" onclick="hideNotif()">✕</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="notifLog">
|
||||||
|
<div class="notif-log-header">
|
||||||
|
<span>📣 NOTIFICATIONS</span>
|
||||||
|
<button class="notif-log-clear" onclick="clearNotifLog()">CLEAR</button>
|
||||||
|
</div>
|
||||||
|
<div id="notifLogFeed">
|
||||||
|
<div style="font-family:'Share Tech Mono',monospace;font-size:10px;color:var(--muted);text-align:center;padding:20px;">No notifications yet</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<header>
|
<header>
|
||||||
<div class="logo">FISH<span>TANK</span> // MONITOR</div>
|
<div class="logo">FISH<span>TANK</span> // MONITOR</div>
|
||||||
|
|
||||||
@@ -1802,6 +2174,7 @@
|
|||||||
<button class="stocks-collapse-btn" onclick="toggleLeftPanels()" id="leftCollapseBtn">◀ POLL / TTS</button>
|
<button class="stocks-collapse-btn" onclick="toggleLeftPanels()" id="leftCollapseBtn">◀ POLL / TTS</button>
|
||||||
<button class="stocks-collapse-btn" onclick="toggleStocks()" id="stocksCollapseBtn">▲ STOCKS</button>
|
<button class="stocks-collapse-btn" onclick="toggleStocks()" id="stocksCollapseBtn">▲ STOCKS</button>
|
||||||
<button class="stocks-collapse-btn" id="chatCollapseBtn" onclick="toggleChat()" title="Hide chat panel">💬 CHAT</button>
|
<button class="stocks-collapse-btn" id="chatCollapseBtn" onclick="toggleChat()" title="Hide chat panel">💬 CHAT</button>
|
||||||
|
<button class="stocks-collapse-btn" id="notifLogBtn" onclick="toggleNotifLog()" title="Notification log" style="position:relative;">🔔 NOTIFS<span class="notif-log-badge" id="notifLogBadge">0</span></button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="apiControl" style="display:flex;align-items:center;gap:8px;">
|
<div id="apiControl" style="display:flex;align-items:center;gap:8px;">
|
||||||
@@ -1877,7 +2250,18 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Camera Panel -->
|
<!-- Camera Panel -->
|
||||||
<div class="chat-panel" id="chatPanel" style="position:relative;">
|
<div style="grid-area:chat;display:flex;flex-direction:column;overflow:hidden;min-height:0;">
|
||||||
|
<div class="floor-plan-panel" id="floorPlanPanel">
|
||||||
|
<div class="floor-plan-tabs">
|
||||||
|
<button class="floor-plan-tab active" id="floorTabDown" onclick="setFloor('down')">DOWNSTAIRS</button>
|
||||||
|
<button class="floor-plan-tab" id="floorTabUp" onclick="setFloor('up')">UPSTAIRS</button>
|
||||||
|
</div>
|
||||||
|
<div class="floor-plan-wrap" id="floorPlanWrap">
|
||||||
|
<img id="floorPlanImg" src="https://cdn.fishtank.live/images/map/s5/lower.png" alt="Floor plan">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="chat-panel" id="chatPanel" style="position:relative;flex:1;min-height:0;">
|
||||||
<div id="inlineChatFeed">
|
<div id="inlineChatFeed">
|
||||||
<div class="empty"><div class="empty-icon">💬</div><span>Waiting for messages...</span></div>
|
<div class="empty"><div class="empty-icon">💬</div><span>Waiting for messages...</span></div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1889,6 +2273,7 @@
|
|||||||
<span style="margin-left:auto;" id="inlineChatCount">0 messages</span>
|
<span style="margin-left:auto;" id="inlineChatCount">0 messages</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="cameras-panel" style="grid-area:cameras;">
|
<div class="cameras-panel" style="grid-area:cameras;">
|
||||||
<div class="panel-header">
|
<div class="panel-header">
|
||||||
@@ -2470,7 +2855,7 @@
|
|||||||
["Dorm", "dmrm-5"],
|
["Dorm", "dmrm-5"],
|
||||||
["Director Mode", "dirc-5"],
|
["Director Mode", "dirc-5"],
|
||||||
["Confessional", "cfsl-5"],
|
["Confessional", "cfsl-5"],
|
||||||
["Balcony", "bkny-5"],
|
["East Wing", "bkny-5"],
|
||||||
["Foyer", "foyr-5"],
|
["Foyer", "foyr-5"],
|
||||||
["Closet", "dmcl-5"],
|
["Closet", "dmcl-5"],
|
||||||
["Glassroom", "gsrm-5"],
|
["Glassroom", "gsrm-5"],
|
||||||
@@ -2484,21 +2869,118 @@
|
|||||||
["Jacuzzi", "jckz-5"],
|
["Jacuzzi", "jckz-5"],
|
||||||
["Dining Room", "dnrm-5"],
|
["Dining Room", "dnrm-5"],
|
||||||
["Market", "mrke-5"],
|
["Market", "mrke-5"],
|
||||||
["Hallway Down", "hwdn-5"],
|
["Hallway", "hwdn-5"],
|
||||||
["Hallway Up", "hwup-5"],
|
["West Wing", "hwup-5"],
|
||||||
["Jungle Room", "br4j-5"],
|
["Jungle Room", "br4j-5"],
|
||||||
|
["Computer Lab", "bbcl-5"],
|
||||||
["Cameraman", "cameraman2-5"],
|
["Cameraman", "cameraman2-5"],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// Navigation polygons per camera slug
|
||||||
|
// Format: { label, slug, points (normalized 0-1) }
|
||||||
|
const CAM_NAV = {
|
||||||
|
'dmrm-5': [
|
||||||
|
{ label: 'Jacuzzi', slug: 'jckz-5', points: '0.8772,0.4250 0.9055,0.1417 0.9656,0.1885 0.9299,0.4598' },
|
||||||
|
{ label: 'Hallway', slug: 'hwdn-5', points: '0.5186,0.0084 0.5206,0.2797 0.4896,0.3001 0.4686,0.3097 0.4626,0.0180' },
|
||||||
|
{ label: 'Closet', slug: 'dmcl-5', points: '0.1546,0.5414 0.1283,0.3770 0.1060,0.1657 0.1762,0.1188 0.1925,0.3157 0.2012,0.4154 0.2134,0.4898' },
|
||||||
|
{ label: 'Dorm Alt', slug: 'dmrm2-5', points: '0.5274,0.0024 0.5287,0.0648 0.5942,0.0648 0.5936,0.0048' },
|
||||||
|
],
|
||||||
|
'dmcl-5': [
|
||||||
|
{ label: 'Dorm', slug: 'dmrm-5', points: '0.6854,0.0166 0.6273,0.5819 0.7248,0.6865 0.8317,0.1152' },
|
||||||
|
],
|
||||||
|
'brrr-5': [
|
||||||
|
{ label: 'Bar PTZ', slug: 'brpz-5', points: '0.8164,0.0024 0.8184,0.0852 0.7536,0.0864 0.7550,0.0000' },
|
||||||
|
{ label: 'Bar Alt', slug: 'brrr2-5', points: '0.3289,0.0636 0.2343,0.1008 0.2289,0.0036 0.3255,0.0036' },
|
||||||
|
{ label: 'Kitchen', slug: 'ktch-5', points: '0.9717,0.2641 0.9082,0.2029 0.8799,0.3866 0.8535,0.5258 0.9082,0.5822' },
|
||||||
|
{ label: 'Hallway', slug: 'hwdn-5', points: '0.9987,0.3349 0.9542,0.5474 0.8887,0.7611 0.8029,0.9652 0.8002,0.9712 0.7914,0.9976 0.7907,0.9940 0.9987,0.9940' },
|
||||||
|
],
|
||||||
|
'ktch-5': [
|
||||||
|
{ label: 'Bar', slug: 'brrr-5', points: '0.0100,0.7812 0.0120,0.9822 0.2588,0.9941 0.2602,0.7384' },
|
||||||
|
{ label: 'Dining Room', slug: 'dnrm-5', points: '0.4295,0.4934 0.3140,0.5582 0.1877,0.6351 0.0993,0.6867 0.1020,0.6879 0.0493,0.4526 0.0223,0.2545 0.0101,0.0480 0.0020,0.0564 0.0014,0.0012 0.2883,0.0012 0.4234,0.0000 0.4322,0.0000' },
|
||||||
|
{ label: 'Glassroom', slug: 'gsrm-5', points: '0.9798,0.2293 0.9994,0.2497 0.9987,0.3601 0.9987,0.4958 0.9717,0.6242 0.9697,0.6267 0.9326,0.5822 0.9272,0.6002 0.9191,0.5918 0.9116,0.5822' },
|
||||||
|
{ label: 'Bar', slug: 'brrr-5', points: '0.7057,0.9976 0.8373,0.7575 0.8590,0.7803 0.9278,0.6002 0.9332,0.5846 0.9731,0.6291 0.9974,0.5042 0.9987,0.9952' },
|
||||||
|
],
|
||||||
|
'hwdn-5': [
|
||||||
|
{ label: 'Foyer', slug: 'foyr-5', points: '0.2782,0.8800 0.2445,0.6267 0.2228,0.3553 0.2201,0.2221 0.1695,0.3433 0.1310,0.4526 0.1742,0.7479 0.2235,0.9640 0.2336,0.9952 0.2620,0.9952' },
|
||||||
|
{ label: 'Bar', slug: 'brrr-5', points: '0.8191,0.9988 0.6901,0.6279 0.5753,0.3373 0.5490,0.2713 0.5328,0.4766 0.5119,0.7107 0.4923,0.9328 0.5038,0.9988' },
|
||||||
|
{ label: 'Dorm', slug: 'dmrm-5', points: '0.4099,0.3193 0.4193,0.0144 0.3356,0.0156 0.3316,0.3157' },
|
||||||
|
],
|
||||||
|
'dnrm-5': [
|
||||||
|
{ label: 'West Wing', slug: 'hwup-5', points: '0.2006,0.0036 0.0014,0.0036 0.0007,0.2761 0.0000,0.3397 0.0115,0.4226 0.0257,0.4994 0.0358,0.5534 0.0513,0.6387 0.0527,0.6399 0.0939,0.6062 0.1310,0.5798 0.1486,0.5666 0.1533,0.5366 0.1722,0.4874 0.1918,0.4346 0.2107,0.3733 0.2282,0.3289 0.2289,0.2977 0.2093,0.3073' },
|
||||||
|
{ label: 'Kitchen', slug: 'ktch-5', points: '0.6415,0.3505 0.6550,0.1909 0.6692,0.0024 0.8009,0.0012 0.9157,0.0624 0.9164,0.0864 0.9076,0.1753 0.8914,0.3049 0.8569,0.4970 0.8563,0.5006 0.8549,0.5042 0.8535,0.5150' },
|
||||||
|
],
|
||||||
|
'mrke-5': [
|
||||||
|
{ label: 'Foyer', slug: 'foyr-5', points: '0.9981,0.2509 0.9461,0.1825 0.8617,0.7239 0.8806,0.9928 0.9069,0.9976 0.9981,0.7119' },
|
||||||
|
{ label: 'Market Alt', slug: 'mrke2-5', points: '0.3559,0.0024 0.3579,0.1068 0.5085,0.1092 0.5098,0.0012' },
|
||||||
|
],
|
||||||
|
'foyr-5': [
|
||||||
|
{ label: 'East Wing', slug: 'bkny-5', points: '0.3093,0.0036 0.4686,0.2881 0.5747,0.2569 0.6077,0.1068 0.6226,0.0012' },
|
||||||
|
{ label: 'Market', slug: 'mrke-5', points: '0.1148,0.8247 0.0338,0.2809 0.0358,0.0060 0.0014,0.0060 0.0027,0.9952 0.1546,0.9988' },
|
||||||
|
{ label: 'Hallway', slug: 'hwdn-5', points: '0.1681,0.3097 0.0682,0.3589 0.0864,0.5138 0.1749,0.4682' },
|
||||||
|
{ label: 'Bar', slug: 'brrr-5', points: '0.1762,0.0504 0.0594,0.0924 0.0696,0.3529 0.1702,0.3097' },
|
||||||
|
{ label: 'Glassroom', slug: 'gsrm-5', points: '0.7597,0.7599 0.8819,0.4094 0.7286,0.2293 0.6375,0.5846' },
|
||||||
|
],
|
||||||
|
'gsrm-5': [
|
||||||
|
{ label: 'Foyer', slug: 'foyr-5', points: '0.2553,0.5114 0.2357,0.2593 0.2222,0.0636 0.1134,0.1224 0.0358,0.1813 0.0675,0.4406 0.1053,0.6363' },
|
||||||
|
{ label: 'Kitchen', slug: 'ktch-5', points: '0.5672,0.3745 0.5780,0.2041 0.5895,0.0084 0.5760,0.0000 0.5281,0.0012 0.5112,0.3205' },
|
||||||
|
],
|
||||||
|
'codr-5': [
|
||||||
|
{ label: 'Confessional', slug: 'cfsl-5', points: '0.5517,0.6387 0.3545,0.9928 0.0020,0.9964 0.0007,0.0024 0.5436,0.0036' },
|
||||||
|
{ label: 'West Wing', slug: 'hwup-5', points: '0.7435,0.2653 0.8346,0.3109 0.9224,0.3433 0.9353,0.2053 0.8535,0.0012 0.7752,0.0000 0.7455,0.0060' },
|
||||||
|
],
|
||||||
|
'bkny-5': [
|
||||||
|
{ label: 'West Wing', slug: 'hwup-5', points: '0.3322,0.9988 0.3721,0.7695 0.6071,0.8067 0.6091,0.8992 0.5996,0.9184 0.6044,0.9976' },
|
||||||
|
{ label: 'Bar', slug: 'brrr-5', points: '0.6618,0.3049 0.6523,0.1104 0.6138,0.0588 0.6057,0.1333 0.5990,0.3709 0.5936,0.5726 0.6003,0.6987 0.6064,0.7863 0.6111,0.8944 0.6368,0.6375' },
|
||||||
|
{ label: 'Market', slug: 'mrke-5', points: '0.2829,0.4910 0.0716,0.4778 0.0736,0.4802 0.1283,0.7407 0.1513,0.8319 0.2087,0.8487 0.2141,0.8271 0.2046,0.8019 0.2188,0.7515 0.2100,0.7215 0.2255,0.6819 0.2195,0.6435' },
|
||||||
|
{ label: 'Foyer', slug: 'foyr-5', points: '0.1519,0.8283 0.1148,0.9448 0.0419,0.7347 0.0020,0.8475 0.0007,0.9976 0.2019,0.9988 0.2114,0.9796 0.1992,0.9520 0.2120,0.9076 0.2033,0.8752 0.2080,0.8535' },
|
||||||
|
{ label: 'Computer Lab', slug: 'bbcl-5', points: '0.4274,0.0120 0.4524,0.0060 0.4565,0.2377 0.4612,0.3782 0.4416,0.4850 0.4396,0.4874 0.4383,0.4910' },
|
||||||
|
],
|
||||||
|
'hwup-5': [
|
||||||
|
{ label: 'Corridor', slug: 'codr-5', points: '0.5227,0.3409 0.5206,0.0300 0.5774,0.0372 0.5726,0.3469' },
|
||||||
|
{ label: 'East Wing', slug: 'bkny-5', points: '0.6159,0.7851 0.4079,0.7863 0.3869,0.9976 0.6334,0.9964' },
|
||||||
|
{ label: 'Jungle Room', slug: 'br4j-5', points: '0.5861,0.5078 0.5922,0.0384 0.6145,0.0540 0.6064,0.4070 0.5936,0.5954' },
|
||||||
|
{ label: 'Dining Room', slug: 'dnrm-5', points: '0.5011,0.3397 0.5004,0.2017 0.4551,0.2041 0.4558,0.2353 0.4443,0.2881 0.4450,0.3397' },
|
||||||
|
],
|
||||||
|
'dmrm2-5': [
|
||||||
|
{ label: 'Dorm', slug: 'dmrm-5', points: '0.5254,0.0024 0.5254,0.0684 0.6213,0.0684 0.6213,0.0036' },
|
||||||
|
],
|
||||||
|
'brrr2-5': [
|
||||||
|
{ label: 'Bar', slug: 'brrr-5', points: '0.8272,0.4418 0.8421,0.3001 0.8448,0.2905 0.8542,0.2881 0.8556,0.2725 0.8481,0.2665 0.8427,0.2581 0.8515,0.0024 0.6348,0.0012 0.6314,0.1705 0.6388,0.2173 0.6483,0.2341 0.6483,0.3325 0.6753,0.4346' },
|
||||||
|
{ label: 'Kitchen', slug: 'ktch-5', points: '0.3079,0.0048 0.3167,0.1741 0.3282,0.3397 0.3741,0.3397 0.3896,0.3037 0.3768,0.3025 0.3640,0.0348 0.3626,0.0000' },
|
||||||
|
{ label: 'Hallway', slug: 'hwdn-5', points: '0.4734,0.2965 0.6186,0.3001 0.6186,0.2473 0.5639,0.2461 0.5314,0.2497 0.4767,0.2509 0.4734,0.2497' },
|
||||||
|
{ label: 'Foyer', slug: 'foyr-5', points: '0.6206,0.0240 0.5612,0.0204 0.5618,0.2449 0.6172,0.2461' },
|
||||||
|
],
|
||||||
|
'jckz-5': [
|
||||||
|
{ label: 'Dorm', slug: 'dmrm-5', points: '0.0844,0.0096 0.1067,0.2041 0.1351,0.3842 0.1452,0.4370 0.1074,0.4790 0.0797,0.5102 0.0439,0.3517 0.0081,0.1729 0.0007,0.1357 0.0000,0.0948' },
|
||||||
|
],
|
||||||
|
'brpz-5': [
|
||||||
|
{ label: 'Bar', slug: 'brrr-5', points: '0.9987,0.9160 0.0014,0.9160 0.0014,0.9988 0.5375,0.9988 0.9947,0.9988 0.9987,0.9976' },
|
||||||
|
],
|
||||||
|
'mrke2-5': [
|
||||||
|
{ label: 'Foyer', slug: 'foyr-5', points: '0.2512,0.1345 0.4356,0.0720 0.4369,0.4826 0.3640,0.5378 0.3181,0.5738 0.2971,0.5870 0.2816,0.6026' },
|
||||||
|
{ label: 'Market', slug: 'mrke-5', points: '0.5868,0.0000 0.5814,0.1357 0.7509,0.1609 0.7597,0.0024' },
|
||||||
|
],
|
||||||
|
'bbcl-5': [
|
||||||
|
{ label: 'East Wing', slug: 'bkny-5', points: '0.4626,0.0132 0.3815,0.0132 0.3815,0.3169 0.3876,0.4274 0.3863,0.4358 0.3890,0.4394 0.4599,0.3914' },
|
||||||
|
],
|
||||||
|
'cfsl-5': [
|
||||||
|
{ label: 'Corridor', slug: 'codr-5', points: '0.1364,0.9976 0.1364,0.0012 0.0020,0.0024 0.0014,0.9976' },
|
||||||
|
],
|
||||||
|
'br4j-5': [
|
||||||
|
{ label: 'West Wing', slug: 'hwup-5', points: '0.4869,0.3830 0.4923,0.0144 0.4227,0.0228 0.4214,0.1813 0.4220,0.4298' },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
const ROOM_NAMES = {
|
const ROOM_NAMES = {
|
||||||
"dmrm-5": "Dorm", "dirc-5": "Director Mode", "cfsl-5": "Confessional",
|
"dmrm-5": "Dorm", "dirc-5": "Director Mode", "cfsl-5": "Confessional",
|
||||||
"bkny-5": "Balcony", "foyr-5": "Foyer", "dmcl-5": "Closet",
|
"bkny-5": "East Wing", "foyr-5": "Foyer", "dmcl-5": "Closet",
|
||||||
"gsrm-5": "Glassroom", "brrr-5": "Bar", "codr-5": "Corridor",
|
"gsrm-5": "Glassroom", "brrr-5": "Bar", "codr-5": "Corridor",
|
||||||
"brpz-5": "Bar PTZ", "mrke2-5": "Market Alternate", "ktch-5": "Kitchen",
|
"brpz-5": "Bar PTZ", "mrke2-5": "Market Alternate", "ktch-5": "Kitchen",
|
||||||
"brrr2-5": "Bar Alternate", "dmrm2-5": "Dorm Alternate", "jckz-5": "Jacuzzi",
|
"brrr2-5": "Bar Alternate", "dmrm2-5": "Dorm Alternate", "jckz-5": "Jacuzzi",
|
||||||
"dnrm-5": "Dining Room", "mrke-5": "Market", "hwdn-5": "Hallway Down",
|
"dnrm-5": "Dining Room", "mrke-5": "Market", "hwdn-5": "Hallway",
|
||||||
"hwup-5": "Hallway Up",
|
"hwup-5": "West Wing",
|
||||||
"br4j-5": "Jungle Room",
|
"br4j-5": "Jungle Room",
|
||||||
|
"bbcl-5": "Computer Lab",
|
||||||
"cameraman2-5": "Cameraman", "site": "Site-wide",
|
"cameraman2-5": "Cameraman", "site": "Site-wide",
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -2592,6 +3074,118 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getVideoRect(wrap) {
|
||||||
|
// Calculate actual rendered video rect accounting for object-fit:contain pillarboxing
|
||||||
|
const video = wrap.querySelector('video');
|
||||||
|
if (!video || !video.videoWidth) return null;
|
||||||
|
const cW = wrap.clientWidth, cH = wrap.clientHeight;
|
||||||
|
const vRatio = video.videoWidth / video.videoHeight;
|
||||||
|
const cRatio = cW / cH;
|
||||||
|
let vW, vH, vX, vY;
|
||||||
|
if (vRatio > cRatio) {
|
||||||
|
// letterboxed top/bottom
|
||||||
|
vW = cW;
|
||||||
|
vH = cW / vRatio;
|
||||||
|
vX = 0;
|
||||||
|
vY = (cH - vH) / 2;
|
||||||
|
} else {
|
||||||
|
// pillarboxed left/right
|
||||||
|
vH = cH;
|
||||||
|
vW = cH * vRatio;
|
||||||
|
vX = (cW - vW) / 2;
|
||||||
|
vY = 0;
|
||||||
|
}
|
||||||
|
return { x: vX, y: vY, w: vW, h: vH };
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderNavOverlay(slug) {
|
||||||
|
const wrap = document.getElementById('camFeaturedWrap');
|
||||||
|
if (!wrap) return;
|
||||||
|
const existing = wrap.querySelector('.cam-nav-overlay');
|
||||||
|
if (existing) { if (existing._ro) existing._ro.disconnect(); existing.remove(); }
|
||||||
|
const existingTip = wrap.querySelector('.cam-nav-tooltip');
|
||||||
|
if (existingTip) existingTip.remove();
|
||||||
|
|
||||||
|
const navs = CAM_NAV[slug];
|
||||||
|
if (!navs || !navs.length) return;
|
||||||
|
|
||||||
|
const tip = document.createElement('div');
|
||||||
|
tip.className = 'cam-nav-tooltip';
|
||||||
|
wrap.appendChild(tip);
|
||||||
|
|
||||||
|
const div = document.createElement('div');
|
||||||
|
div.className = 'cam-nav-overlay';
|
||||||
|
|
||||||
|
const svgNS = 'http://www.w3.org/2000/svg';
|
||||||
|
const svg = document.createElementNS(svgNS, 'svg');
|
||||||
|
// Use pixel coords matching the container — we'll transform points ourselves
|
||||||
|
const cW = wrap.clientWidth, cH = wrap.clientHeight;
|
||||||
|
svg.setAttribute('viewBox', `0 0 ${cW} ${cH}`);
|
||||||
|
svg.setAttribute('preserveAspectRatio', 'none');
|
||||||
|
svg.style.cssText = 'position:absolute;inset:0;width:100%;height:100%;';
|
||||||
|
|
||||||
|
function buildPolygons() {
|
||||||
|
// Clear existing polygons
|
||||||
|
while (svg.firstChild) svg.removeChild(svg.firstChild);
|
||||||
|
|
||||||
|
const vRect = getVideoRect(wrap);
|
||||||
|
if (!vRect) return;
|
||||||
|
|
||||||
|
navs.forEach(({ label, slug: targetSlug, points }) => {
|
||||||
|
const poly = document.createElementNS(svgNS, 'polygon');
|
||||||
|
// Transform normalized 0-1 points to pixel coords within the actual video rect
|
||||||
|
const pts = points.split(' ').map(p => {
|
||||||
|
const [nx, ny] = p.split(',').map(Number);
|
||||||
|
return `${vRect.x + nx * vRect.w},${vRect.y + ny * vRect.h}`;
|
||||||
|
}).join(' ');
|
||||||
|
poly.setAttribute('points', pts);
|
||||||
|
|
||||||
|
poly.addEventListener('mouseenter', () => {
|
||||||
|
tip.textContent = '→ ' + (ROOM_NAMES[targetSlug] || label);
|
||||||
|
tip.style.display = 'block';
|
||||||
|
});
|
||||||
|
poly.addEventListener('mousemove', e => {
|
||||||
|
const rect = wrap.getBoundingClientRect();
|
||||||
|
let x = e.clientX - rect.left + 12;
|
||||||
|
let y = e.clientY - rect.top + 12;
|
||||||
|
if (x + 160 > cW) x = e.clientX - rect.left - 160;
|
||||||
|
tip.style.left = x + 'px';
|
||||||
|
tip.style.top = y + 'px';
|
||||||
|
});
|
||||||
|
poly.addEventListener('mouseleave', () => {
|
||||||
|
tip.style.display = 'none';
|
||||||
|
});
|
||||||
|
poly.addEventListener('click', () => {
|
||||||
|
switchToRoom(targetSlug);
|
||||||
|
});
|
||||||
|
|
||||||
|
svg.appendChild(poly);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
div.appendChild(svg);
|
||||||
|
wrap.appendChild(div);
|
||||||
|
|
||||||
|
function rebuild() {
|
||||||
|
svg.setAttribute('viewBox', `0 0 ${wrap.clientWidth} ${wrap.clientHeight}`);
|
||||||
|
buildPolygons();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build now if video already has dimensions, otherwise wait for it
|
||||||
|
const video = wrap.querySelector('video');
|
||||||
|
if (video && video.videoWidth) {
|
||||||
|
rebuild();
|
||||||
|
} else if (video) {
|
||||||
|
video.addEventListener('loadedmetadata', rebuild, { once: true });
|
||||||
|
video.addEventListener('canplay', rebuild, { once: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rebuild on container resize — pillarboxing changes when panels collapse
|
||||||
|
const ro = new ResizeObserver(rebuild);
|
||||||
|
ro.observe(wrap);
|
||||||
|
div._ro = ro;
|
||||||
|
}
|
||||||
|
|
||||||
function setFeaturedStream(idx) {
|
function setFeaturedStream(idx) {
|
||||||
const [name, slug] = CAMERAS[idx];
|
const [name, slug] = CAMERAS[idx];
|
||||||
const wrap = document.getElementById('camFeaturedWrap');
|
const wrap = document.getElementById('camFeaturedWrap');
|
||||||
@@ -2618,7 +3212,13 @@
|
|||||||
if (typeof updateViewerCounts === 'function' && window._lastPresenceData) {
|
if (typeof updateViewerCounts === 'function' && window._lastPresenceData) {
|
||||||
updateViewerCounts(window._lastPresenceData);
|
updateViewerCounts(window._lastPresenceData);
|
||||||
}
|
}
|
||||||
|
// Render immediately, then re-render once video dimensions are known
|
||||||
|
renderNavOverlay(slug);
|
||||||
|
if (window.renderFloorButtons) window.renderFloorButtons(window.currentFloor || 'down');
|
||||||
|
const featVid = wrap && wrap.querySelector('video');
|
||||||
|
if (featVid) {
|
||||||
|
featVid.addEventListener('loadedmetadata', () => renderNavOverlay(slug), { once: true });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setFeatured(i) {
|
function setFeatured(i) {
|
||||||
@@ -3799,16 +4399,77 @@
|
|||||||
if (inlineChatAutoScroll) inlineFeed.scrollTop = inlineFeed.scrollHeight;
|
if (inlineChatAutoScroll) inlineFeed.scrollTop = inlineFeed.scrollHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Floor plan ───────────────────────────────────────────────
|
||||||
|
const FLOOR_ROOMS = {
|
||||||
|
down: [
|
||||||
|
{ label: 'Dining\nRoom', slug: 'dnrm-5', style: 'bottom:0.75%;left:0%;height:50%;width:22%' },
|
||||||
|
{ label: 'Kitchen', slug: 'ktch-5', style: 'bottom:0.75%;left:22%;height:60%;width:22%' },
|
||||||
|
{ label: 'Bar', slug: 'brrr-5', style: 'bottom:0.75%;left:42.5%;height:60%;width:22%' },
|
||||||
|
{ label: 'Dorm', slug: 'dmrm-5', style: 'bottom:0.75%;left:75%;height:67%;width:22%' },
|
||||||
|
{ label: 'Glass\nroom', slug: 'gsrm-5', style: 'top:0.75%;left:27.5%;height:35%;width:15%' },
|
||||||
|
{ label: 'Foyer', slug: 'foyr-5', style: 'top:0%;left:42%;height:33%;width:20%' },
|
||||||
|
{ label: 'Market', slug: 'mrke-5', style: 'top:0.75%;left:62.5%;height:30%;width:15%' },
|
||||||
|
{ label: 'Jacuzzi', slug: 'jckz-5', style: 'top:0.75%;right:1%;height:30%;width:12%' },
|
||||||
|
{ label: 'Hallway', slug: 'hwdn-5', style: 'top:30%;left:42%;height:10%;width:33%' },
|
||||||
|
{ label: 'Closet', slug: 'dmcl-5', style: 'bottom:0.75%;left:65%;height:25%;width:10%' },
|
||||||
|
],
|
||||||
|
up: [
|
||||||
|
{ label: 'Corridor', slug: 'codr-5', style: 'top:0%;left:0%;height:55%;width:8%' },
|
||||||
|
{ label: 'Confessional', slug: 'cfsl-5', style: 'top:0%;left:0%;height:17%;width:17%' },
|
||||||
|
{ label: 'West Wing', slug: 'hwup-5', style: 'top:42%;left:15%;height:12%;width:25%' },
|
||||||
|
{ label: 'East Wing', slug: 'bkny-5', style: 'top:42%;left:42%;height:12%;width:25%' },
|
||||||
|
{ label: 'Jungle\nRoom', slug: 'br4j-5', style: 'top:0%;left:17%;height:42%;width:22%' },
|
||||||
|
{ label: 'Computer\nLab', slug: 'bbcl-5', style: 'top:0%;right:22%;height:42%;width:20%' },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
window.currentFloor = 'down';
|
||||||
|
let currentFloor = 'down';
|
||||||
|
|
||||||
|
window.renderFloorButtons = function renderFloorButtons(floor) {
|
||||||
|
const wrap = document.getElementById('floorPlanWrap');
|
||||||
|
if (!wrap) return;
|
||||||
|
wrap.querySelectorAll('.floor-plan-btn').forEach(b => b.remove());
|
||||||
|
const rooms = FLOOR_ROOMS[floor] || [];
|
||||||
|
const currentSlug = CAMERAS[featuredIdx] ? CAMERAS[featuredIdx][1] : null;
|
||||||
|
rooms.forEach(({ label, slug, style }) => {
|
||||||
|
const btn = document.createElement('button');
|
||||||
|
btn.className = 'floor-plan-btn' + (slug === currentSlug ? ' active-room' : '');
|
||||||
|
btn.style.cssText = style;
|
||||||
|
btn.innerHTML = label.replace('\n', '<br>');
|
||||||
|
btn.title = ROOM_NAMES[slug] || label;
|
||||||
|
btn.onclick = () => switchToRoom(slug);
|
||||||
|
wrap.appendChild(btn);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
window.setFloor = function(floor) {
|
||||||
|
currentFloor = floor;
|
||||||
|
window.currentFloor = floor;
|
||||||
|
document.getElementById('floorTabDown').classList.toggle('active', floor === 'down');
|
||||||
|
document.getElementById('floorTabUp').classList.toggle('active', floor === 'up');
|
||||||
|
document.getElementById('floorPlanImg').src = floor === 'down'
|
||||||
|
? 'https://cdn.fishtank.live/images/map/s5/lower.png'
|
||||||
|
: 'https://cdn.fishtank.live/images/map/s5/upper.png';
|
||||||
|
renderFloorButtons(floor);
|
||||||
|
};
|
||||||
|
|
||||||
|
setTimeout(() => renderFloorButtons('down'), 500);
|
||||||
|
// ── End floor plan ────────────────────────────────────────────
|
||||||
|
|
||||||
window.toggleChat = function() {
|
window.toggleChat = function() {
|
||||||
chatCollapsed = !chatCollapsed;
|
chatCollapsed = !chatCollapsed;
|
||||||
const main = document.querySelector('.main');
|
const main = document.querySelector('.main');
|
||||||
const btn = document.getElementById('chatCollapseBtn');
|
const btn = document.getElementById('chatCollapseBtn');
|
||||||
|
const fpPanel = document.getElementById('floorPlanPanel');
|
||||||
if (chatCollapsed) {
|
if (chatCollapsed) {
|
||||||
main.classList.add('chat-collapsed');
|
main.classList.add('chat-collapsed');
|
||||||
|
if (fpPanel) fpPanel.style.display = 'none';
|
||||||
btn.textContent = '💬 CHAT';
|
btn.textContent = '💬 CHAT';
|
||||||
btn.title = 'Show chat panel';
|
btn.title = 'Show chat panel';
|
||||||
} else {
|
} else {
|
||||||
main.classList.remove('chat-collapsed');
|
main.classList.remove('chat-collapsed');
|
||||||
|
if (fpPanel) fpPanel.style.display = '';
|
||||||
btn.textContent = '💬 CHAT';
|
btn.textContent = '💬 CHAT';
|
||||||
btn.title = 'Hide chat panel';
|
btn.title = 'Hide chat panel';
|
||||||
inlineFeed.scrollTop = inlineFeed.scrollHeight;
|
inlineFeed.scrollTop = inlineFeed.scrollHeight;
|
||||||
@@ -4049,9 +4710,53 @@
|
|||||||
subEl.style.display = subtitle ? '' : 'none';
|
subEl.style.display = subtitle ? '' : 'none';
|
||||||
popup.classList.add('show');
|
popup.classList.add('show');
|
||||||
if (notifTimer) clearTimeout(notifTimer);
|
if (notifTimer) clearTimeout(notifTimer);
|
||||||
notifTimer = setTimeout(hideNotif, 8000);
|
notifTimer = setTimeout(hideNotif, 15000);
|
||||||
// Play notification sound
|
// Play notification sound
|
||||||
try { new Audio('https://cdn.fishtank.live/sounds/xp.mp3').play(); } catch(e) {}
|
try { new Audio('https://cdn.fishtank.live/sounds/xp.mp3').play(); } catch(e) {}
|
||||||
|
|
||||||
|
// Add to log
|
||||||
|
const feed = document.getElementById('notifLogFeed');
|
||||||
|
if (feed) {
|
||||||
|
const empty = feed.querySelector('div[style*="No notifications"]');
|
||||||
|
if (empty) empty.remove();
|
||||||
|
const ts = new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' });
|
||||||
|
const item = document.createElement('div');
|
||||||
|
item.className = 'notif-log-item';
|
||||||
|
item.innerHTML = `<div class="notif-log-item-msg">${message}</div>` +
|
||||||
|
(subtitle ? `<div class="notif-log-item-sub">${subtitle}</div>` : '') +
|
||||||
|
`<div class="notif-log-item-time">${ts}</div>`;
|
||||||
|
feed.insertBefore(item, feed.firstChild);
|
||||||
|
while (feed.children.length > 50) feed.removeChild(feed.lastChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unread badge
|
||||||
|
if (!notifLogOpen) {
|
||||||
|
notifLogUnread++;
|
||||||
|
const badge = document.getElementById('notifLogBadge');
|
||||||
|
if (badge) { badge.textContent = notifLogUnread; badge.style.display = 'block'; }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let notifLogUnread = 0;
|
||||||
|
let notifLogOpen = false;
|
||||||
|
|
||||||
|
window.toggleNotifLog = function() {
|
||||||
|
notifLogOpen = !notifLogOpen;
|
||||||
|
const log = document.getElementById('notifLog');
|
||||||
|
if (log) log.classList.toggle('open', notifLogOpen);
|
||||||
|
if (notifLogOpen) {
|
||||||
|
notifLogUnread = 0;
|
||||||
|
const badge = document.getElementById('notifLogBadge');
|
||||||
|
if (badge) badge.style.display = 'none';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.clearNotifLog = function() {
|
||||||
|
const feed = document.getElementById('notifLogFeed');
|
||||||
|
if (feed) feed.innerHTML = `<div style="font-family:'Share Tech Mono',monospace;font-size:10px;color:var(--muted);text-align:center;padding:20px;">No notifications yet</div>`;
|
||||||
|
notifLogUnread = 0;
|
||||||
|
const badge = document.getElementById('notifLogBadge');
|
||||||
|
if (badge) badge.style.display = 'none';
|
||||||
};
|
};
|
||||||
|
|
||||||
window.hideNotif = function() {
|
window.hideNotif = function() {
|
||||||
|
|||||||
Reference in New Issue
Block a user