/** * — shows connection status in the shell header. * * States: * - online: hidden (no visual noise) * - syncing: brief spinner (shown during init) * - offline: orange dot + "Offline" * - error: red dot + tooltip with error info */ import type { RuntimeStatus } from '../local-first/runtime'; export class RStackOfflineIndicator extends HTMLElement { #shadow: ShadowRoot; #status: RuntimeStatus = 'idle'; #unsub: (() => void) | null = null; constructor() { super(); this.#shadow = this.attachShadow({ mode: 'open' }); } connectedCallback() { this.#render(); // Connect to runtime when available const tryConnect = () => { const runtime = (window as any).__rspaceOfflineRuntime; if (runtime) { this.#status = runtime.status; this.#render(); this.#unsub = runtime.onStatusChange((s: RuntimeStatus) => { this.#status = s; this.#render(); }); } else { // Runtime not ready yet, try again shortly setTimeout(tryConnect, 500); } }; tryConnect(); } disconnectedCallback() { this.#unsub?.(); this.#unsub = null; } #render() { const hidden = this.#status === 'online' || this.#status === 'idle'; const isOffline = this.#status === 'offline'; const isError = this.#status === 'error'; const isSyncing = this.#status === 'initializing'; const dotColor = isError ? '#ef4444' : isOffline ? '#f59e0b' : '#3b82f6'; const label = isError ? 'Sync Error' : isOffline ? 'Offline' : isSyncing ? 'Syncing' : ''; const tooltip = isError ? 'Unable to connect to sync server. Changes saved locally.' : isOffline ? 'Working offline. Changes will sync when reconnected.' : isSyncing ? 'Connecting to sync server...' : ''; this.#shadow.innerHTML = `
${label}
`; } static define() { if (!customElements.get('rstack-offline-indicator')) { customElements.define('rstack-offline-indicator', RStackOfflineIndicator); } } }