fix(ux): move people-online badge into sub-tab header bar
- Move people badge + panel from fixed position to inline in .rstack-tab-row - Badge sits right of the layer toggle icon with a subtle separator - Panel drops down from badge position instead of floating fixed - Online/Offline toggle replaces Solo/Multi labels for clarity - Badge shows "Offline" with gray dot when in offline mode - Mobile: hide text label, show dots only - Tab bar gets flex:1 + min-width:0 to share row space Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
d458a00550
commit
b91233092b
|
|
@ -1439,7 +1439,7 @@ const ICON_FLAT = `<svg width="16" height="16" viewBox="0 0 16 16" fill="none" s
|
|||
// ── Styles ──
|
||||
|
||||
const STYLES = `
|
||||
:host { display: block; }
|
||||
:host { display: block; flex: 1; min-width: 0; }
|
||||
|
||||
/* ── Tab bar (flat mode) ── */
|
||||
|
||||
|
|
|
|||
|
|
@ -23,9 +23,7 @@
|
|||
html.rspace-embedded .rstack-tab-row { display: none !important; }
|
||||
html.rspace-embedded #toolbar { top: 16px !important; }
|
||||
html.rspace-embedded #community-info { display: none !important; }
|
||||
html.rspace-embedded #people-online-badge { top: 12px !important; }
|
||||
html.rspace-embedded #people-panel { top: 48px !important; }
|
||||
</style>
|
||||
</style>
|
||||
<script>if (window.self !== window.parent) document.documentElement.classList.add('rspace-embedded');</script>
|
||||
<style>
|
||||
* {
|
||||
|
|
@ -963,26 +961,25 @@
|
|||
|
||||
/* ── People Online badge ── */
|
||||
#people-online-badge {
|
||||
position: fixed;
|
||||
top: 68px;
|
||||
right: 16px;
|
||||
padding: 6px 12px;
|
||||
background: var(--rs-bg-surface);
|
||||
border-radius: 8px;
|
||||
box-shadow: var(--rs-shadow-sm);
|
||||
padding: 4px 10px;
|
||||
border-radius: 6px;
|
||||
font-size: 12px;
|
||||
color: var(--rs-text-muted);
|
||||
z-index: 1000;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
transition: box-shadow 0.15s;
|
||||
transition: background 0.15s;
|
||||
flex-shrink: 0;
|
||||
margin: 0 8px 0 4px;
|
||||
position: relative;
|
||||
border-left: 1px solid var(--rs-border-subtle, rgba(255,255,255,0.08));
|
||||
padding-left: 12px;
|
||||
}
|
||||
|
||||
#people-online-badge:hover {
|
||||
box-shadow: 0 2px 14px rgba(0, 0, 0, 0.18);
|
||||
background: var(--rs-bg-hover, rgba(255,255,255,0.08));
|
||||
}
|
||||
|
||||
#people-conn-status {
|
||||
|
|
@ -1024,17 +1021,18 @@
|
|||
|
||||
/* ── People Panel ── */
|
||||
#people-panel {
|
||||
position: fixed;
|
||||
top: 104px;
|
||||
right: 16px;
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
right: 0;
|
||||
width: 280px;
|
||||
max-height: calc(100vh - 120px);
|
||||
background: var(--rs-bg-surface);
|
||||
border-radius: 12px;
|
||||
box-shadow: var(--rs-shadow-lg);
|
||||
z-index: 1001;
|
||||
z-index: 10001;
|
||||
display: none;
|
||||
overflow: hidden;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
#people-panel.open {
|
||||
|
|
@ -1269,11 +1267,15 @@
|
|||
/* ── People panel mobile ── */
|
||||
@media (max-width: 640px) {
|
||||
#people-online-badge {
|
||||
right: 12px;
|
||||
top: 64px;
|
||||
margin-left: 2px;
|
||||
padding: 4px 6px;
|
||||
}
|
||||
#people-badge-text {
|
||||
display: none;
|
||||
}
|
||||
#people-panel {
|
||||
max-width: calc(100vw - 32px);
|
||||
right: -8px;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1995,6 +1997,18 @@
|
|||
<rstack-history-panel type="canvas"></rstack-history-panel>
|
||||
<div class="rstack-tab-row" data-theme="dark">
|
||||
<rstack-tab-bar space="" active="" view-mode="flat"></rstack-tab-bar>
|
||||
<div id="people-online-badge">
|
||||
<span class="dots" id="people-dots"></span>
|
||||
<span id="people-badge-text">1 online</span>
|
||||
<span id="people-conn-status"></span>
|
||||
</div>
|
||||
<div id="people-panel">
|
||||
<div id="people-panel-header">
|
||||
<h3>People Online</h3>
|
||||
<span class="count" id="people-count">0</span>
|
||||
</div>
|
||||
<div id="people-list"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="community-info">
|
||||
<h2 id="community-name">Loading...</h2>
|
||||
|
|
@ -2268,19 +2282,6 @@
|
|||
<div id="memory-list"></div>
|
||||
</div>
|
||||
|
||||
<div id="people-panel">
|
||||
<div id="people-panel-header">
|
||||
<h3>People Online</h3>
|
||||
<span class="count" id="people-count">0</span>
|
||||
</div>
|
||||
<div id="people-list"></div>
|
||||
</div>
|
||||
|
||||
<div id="people-online-badge">
|
||||
<span class="dots" id="people-dots"></span>
|
||||
<span id="people-badge-text">1 online</span>
|
||||
<span id="people-conn-status"></span>
|
||||
</div>
|
||||
|
||||
<div id="mp-notify">
|
||||
<span id="mp-notify-text"></span>
|
||||
|
|
@ -3437,26 +3438,35 @@
|
|||
|
||||
function renderPeopleBadge() {
|
||||
const totalCount = onlinePeers.size + 1; // +1 for self
|
||||
peopleBadgeText.textContent = totalCount === 1 ? "1 online" : `${totalCount} online`;
|
||||
peopleDots.innerHTML = "";
|
||||
// Self dot
|
||||
const selfDot = document.createElement("span");
|
||||
selfDot.className = "dot";
|
||||
selfDot.style.background = localColor;
|
||||
peopleDots.appendChild(selfDot);
|
||||
// Remote dots (up to 4)
|
||||
let dotCount = 0;
|
||||
for (const [, peer] of onlinePeers) {
|
||||
if (dotCount >= 4) break;
|
||||
const dot = document.createElement("span");
|
||||
dot.className = "dot";
|
||||
dot.style.background = peer.color || "#94a3b8";
|
||||
peopleDots.appendChild(dot);
|
||||
dotCount++;
|
||||
if (!isMultiplayer) {
|
||||
// Offline / solo mode
|
||||
peopleBadgeText.textContent = "Offline";
|
||||
const selfDot = document.createElement("span");
|
||||
selfDot.className = "dot";
|
||||
selfDot.style.background = "#64748b";
|
||||
peopleDots.appendChild(selfDot);
|
||||
} else {
|
||||
peopleBadgeText.textContent = totalCount === 1 ? "1 online" : `${totalCount} online`;
|
||||
// Self dot
|
||||
const selfDot = document.createElement("span");
|
||||
selfDot.className = "dot";
|
||||
selfDot.style.background = localColor;
|
||||
peopleDots.appendChild(selfDot);
|
||||
// Remote dots (up to 4)
|
||||
let dotCount = 0;
|
||||
for (const [, peer] of onlinePeers) {
|
||||
if (dotCount >= 4) break;
|
||||
const dot = document.createElement("span");
|
||||
dot.className = "dot";
|
||||
dot.style.background = peer.color || "#94a3b8";
|
||||
peopleDots.appendChild(dot);
|
||||
dotCount++;
|
||||
}
|
||||
}
|
||||
peopleCount.textContent = totalCount;
|
||||
peopleCount.textContent = isMultiplayer ? totalCount : "—";
|
||||
// Connection status indicator
|
||||
if (connState === "connected") {
|
||||
if (connState === "connected" || !isMultiplayer) {
|
||||
peopleConnStatus.classList.remove("visible");
|
||||
peopleConnStatus.innerHTML = "";
|
||||
} else {
|
||||
|
|
@ -3470,14 +3480,14 @@
|
|||
|
||||
function renderPeoplePanel() {
|
||||
peopleList.innerHTML = "";
|
||||
// Self row with mode toggle
|
||||
// Self row with online/offline toggle
|
||||
const selfRow = document.createElement("div");
|
||||
selfRow.className = "people-row";
|
||||
selfRow.innerHTML = `<span class="dot" style="background:${escapeHtml(localColor)}"></span>
|
||||
selfRow.innerHTML = `<span class="dot" style="background:${isMultiplayer ? escapeHtml(localColor) : '#64748b'}"></span>
|
||||
<span class="name">${escapeHtml(storedUsername)} <span class="you-tag">(you)</span></span>
|
||||
<span class="mode-toggle">
|
||||
<button class="mode-solo ${isMultiplayer ? '' : 'active'}">Solo</button>
|
||||
<button class="mode-multi ${isMultiplayer ? 'active' : ''}">Multi</button>
|
||||
<button class="mode-solo ${isMultiplayer ? '' : 'active'}">Offline</button>
|
||||
<button class="mode-multi ${isMultiplayer ? 'active' : ''}">Online</button>
|
||||
</span>`;
|
||||
selfRow.querySelector(".mode-solo").addEventListener("click", () => setMultiplayerMode(false));
|
||||
selfRow.querySelector(".mode-multi").addEventListener("click", () => setMultiplayerMode(true));
|
||||
|
|
|
|||
|
|
@ -154,6 +154,8 @@ body {
|
|||
-webkit-backdrop-filter: blur(12px);
|
||||
border-bottom: 1px solid var(--rs-border-subtle);
|
||||
background: var(--rs-glass-bg);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* ── Main content area ── */
|
||||
|
|
|
|||
Loading…
Reference in New Issue