fix(canvas): move offline status to people badge, toolbar minimize to bottom, zoom to bottom-right

- Offline/reconnecting indicator now shows inside the "N online" people
  badge instead of the shell header (hidden on canvas via CSS override)
- Toolbar collapse/minimize button moved from top to bottom of toolbar
  stack so it sits where the last tool icon was
- Zoom controls moved from bottom-left to bottom-right; on mobile they
  sit above the bottom toolbar to avoid overlap

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Jeff Emmett 2026-03-21 18:22:39 -07:00
parent dbe60f2711
commit ee1a791aea
1 changed files with 57 additions and 9 deletions

View File

@ -16,6 +16,8 @@
<script>(function(){var t=localStorage.getItem('canvas-theme');if(!t)t=matchMedia('(prefers-color-scheme:light)').matches?'light':'dark';document.documentElement.setAttribute('data-theme',t);var b=localStorage.getItem('canvas-bg')||'grid';document.documentElement.setAttribute('data-canvas-bg',b)})()</script>
<link rel="stylesheet" href="/theme.css?v=1" />
<style>
/* Hide shell offline indicator on canvas — shown in people badge instead */
rstack-offline-indicator { display: none !important; }
/* When loaded inside an iframe, hide shell chrome */
html.rspace-embedded .rstack-header { display: none !important; }
html.rspace-embedded .rstack-tab-row { display: none !important; }
@ -286,7 +288,7 @@
display: block;
}
/* Collapse/expand toggle — at top of toolbar */
/* Collapse/expand toggle — at bottom of toolbar */
#toolbar-collapse {
padding: 6px;
background: transparent;
@ -298,7 +300,7 @@
text-align: center;
color: var(--rs-text-muted);
cursor: pointer;
order: -1; /* always first */
order: 999; /* always last */
width: 36px;
height: 36px;
display: flex;
@ -562,11 +564,11 @@
50% { opacity: 0.5; }
}
/* ── Corner tools (zoom + feed) — bottom-left ── */
/* ── Corner tools (zoom + feed) — bottom-right ── */
#canvas-corner-tools {
position: fixed;
bottom: 16px;
left: 12px;
right: 16px;
display: flex;
flex-direction: column;
align-items: center;
@ -983,6 +985,30 @@
box-shadow: 0 2px 14px rgba(0, 0, 0, 0.18);
}
#people-conn-status {
display: none;
align-items: center;
gap: 4px;
font-size: 11px;
padding-left: 6px;
border-left: 1px solid var(--rs-border, rgba(255,255,255,0.1));
}
#people-conn-status.visible {
display: inline-flex;
}
#people-conn-status .conn-dot {
width: 6px;
height: 6px;
border-radius: 50%;
flex-shrink: 0;
}
#people-conn-status .conn-dot.pulse {
animation: pulse 1.2s ease-in-out infinite;
}
#people-dots {
display: flex;
gap: 3px;
@ -1810,10 +1836,9 @@
flex-shrink: 0;
}
/* Corner tools: horizontal on mobile, tucked bottom-right */
/* Corner tools: horizontal on mobile, above bottom toolbar */
#canvas-corner-tools {
bottom: 8px;
left: auto;
bottom: 60px;
right: 8px;
flex-direction: row;
padding: 4px 6px;
@ -2010,8 +2035,6 @@
</div>
<div id="toolbar">
<button id="toolbar-collapse" title="Minimize toolbar"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"><line x1="6" y1="18" x2="18" y2="18"/></svg></button>
<!-- 1. Note -->
<div class="toolbar-group">
<button class="toolbar-group-toggle" title="Note"><span class="tg-icon">📝</span><span class="tg-label">Note</span></button>
@ -2147,6 +2170,7 @@
<button id="new-booking" title="Booking">✈️ Booking</button>
</div>
</div>
<button id="toolbar-collapse" title="Minimize toolbar"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"><line x1="6" y1="18" x2="18" y2="18"/></svg></button>
</div>
<div id="toolbar-panel">
@ -2246,6 +2270,7 @@
<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">
@ -2998,6 +3023,8 @@
document.getElementById("canvas-loading")?.remove();
status.className = "offline";
statusText.textContent = "Offline (cached)";
connState = "offline";
renderPeopleBadge();
}
} catch (e) {
console.warn("[Canvas] Offline cache init failed:", e);
@ -3231,6 +3258,8 @@
const peopleBadgeText = document.getElementById("people-badge-text");
const peopleCount = document.getElementById("people-count");
const peopleList = document.getElementById("people-list");
const peopleConnStatus = document.getElementById("people-conn-status");
let connState = "connecting"; // "connected" | "offline" | "reconnecting" | "connecting"
const pingToast = document.getElementById("ping-toast");
const pingToastText = document.getElementById("ping-toast-text");
const pingToastGo = document.getElementById("ping-toast-go");
@ -3293,6 +3322,17 @@
dotCount++;
}
peopleCount.textContent = totalCount;
// Connection status indicator
if (connState === "connected") {
peopleConnStatus.classList.remove("visible");
peopleConnStatus.innerHTML = "";
} else {
const color = connState === "offline" ? "#f59e0b" : "#3b82f6";
const label = connState === "offline" ? "Offline" : "Reconnecting…";
const pulse = connState !== "offline" ? " pulse" : "";
peopleConnStatus.innerHTML = `<span class="conn-dot${pulse}" style="background:${color}"></span>${label}`;
peopleConnStatus.classList.add("visible");
}
}
function renderPeoplePanel() {
@ -3445,15 +3485,19 @@
sync.addEventListener("connected", () => {
status.className = "connected";
statusText.textContent = "Connected";
connState = "connected";
renderPeopleBadge();
});
sync.addEventListener("disconnected", () => {
if (navigator.onLine) {
status.className = "disconnected";
statusText.textContent = "Reconnecting...";
connState = "reconnecting";
} else {
status.className = "offline";
statusText.textContent = "Offline (changes saved locally)";
connState = "offline";
}
// Clear online peers on disconnect (they'll re-announce on reconnect)
onlinePeers.clear();
@ -6368,6 +6412,8 @@
console.log("[Canvas] Browser went online, reconnecting...");
status.className = "syncing";
statusText.textContent = "Reconnecting...";
connState = "reconnecting";
renderPeopleBadge();
sync.connect(wsUrl);
});
@ -6375,6 +6421,8 @@
console.log("[Canvas] Browser went offline");
status.className = "offline";
statusText.textContent = "Offline (changes saved locally)";
connState = "offline";
renderPeopleBadge();
});
// Handle offline-loaded event