UNPKG

state-mirror

Version:

Real-time cross-tab/device state synchronization library with plugin support.

317 lines (301 loc) 9.03 kB
'use strict'; class StateMirrorDevTools { constructor(instance, config = {}) { this.overlay = null; this.isVisible = false; this.instance = instance; this.config = { position: 'top-right', theme: 'light', autoHide: false, showPatches: true, showPlugins: true, ...config }; } /** * Initialize the DevTools overlay */ init() { this.createOverlay(); this.setupEventListeners(); this.updateDisplay(); } /** * Show the DevTools overlay */ show() { if (this.overlay) { this.overlay.style.display = 'block'; this.isVisible = true; } } /** * Hide the DevTools overlay */ hide() { if (this.overlay) { this.overlay.style.display = 'none'; this.isVisible = false; } } /** * Toggle the DevTools overlay */ toggle() { if (this.isVisible) { this.hide(); } else { this.show(); } } /** * Update the display with current state */ updateDisplay() { if (!this.overlay) return; const content = this.generateContent(); const contentElement = this.overlay.querySelector('.state-mirror-content'); if (contentElement) { contentElement.innerHTML = content; } } createOverlay() { // Remove existing overlay if any const existing = document.querySelector('.state-mirror-devtools'); if (existing) { existing.remove(); } // Create new overlay this.overlay = document.createElement('div'); this.overlay.className = 'state-mirror-devtools'; this.overlay.innerHTML = ` <div class="state-mirror-header"> <span class="state-mirror-title">StateMirror DevTools</span> <button class="state-mirror-toggle">−</button> <button class="state-mirror-close">×</button> </div> <div class="state-mirror-content"> ${this.generateContent()} </div> `; // Apply styles this.applyStyles(); // Add to document document.body.appendChild(this.overlay); // Set initial visibility if (this.config.autoHide) { this.hide(); } } generateContent() { const status = this.instance.isConnected ? 'Connected' : 'Disconnected'; const watching = this.instance.isWatching ? 'Watching' : 'Not Watching'; let content = ` <div class="state-mirror-section"> <h3>Status</h3> <div class="state-mirror-status"> <div class="status-item"> <span class="label">Connection:</span> <span class="value ${this.instance.isConnected ? 'connected' : 'disconnected'}">${status}</span> </div> <div class="status-item"> <span class="label">Watching:</span> <span class="value">${watching}</span> </div> <div class="status-item"> <span class="label">Instance ID:</span> <span class="value">${this.instance.id}</span> </div> </div> </div> `; if (this.config.showPatches) { content += ` <div class="state-mirror-section"> <h3>Recent Activity</h3> <div class="state-mirror-activity"> <div class="activity-item"> <span class="label">Last Update:</span> <span class="value">${new Date().toLocaleTimeString()}</span> </div> </div> </div> `; } if (this.config.showPlugins) { content += ` <div class="state-mirror-section"> <h3>Plugins</h3> <div class="state-mirror-plugins"> <div class="plugin-item"> <span class="label">Active Plugins:</span> <span class="value">0</span> </div> </div> </div> `; } return content; } applyStyles() { if (!this.overlay) return; const position = this.config.position; const theme = this.config.theme; const styles = ` .state-mirror-devtools { position: fixed; z-index: 9999; background: ${theme === 'dark' ? '#2d3748' : '#ffffff'}; border: 1px solid ${theme === 'dark' ? '#4a5568' : '#e2e8f0'}; border-radius: 8px; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; font-size: 12px; min-width: 300px; max-width: 400px; ${this.getPositionStyles(position)} } .state-mirror-header { display: flex; align-items: center; justify-content: space-between; padding: 8px 12px; background: ${theme === 'dark' ? '#1a202c' : '#f7fafc'}; border-bottom: 1px solid ${theme === 'dark' ? '#4a5568' : '#e2e8f0'}; border-radius: 8px 8px 0 0; } .state-mirror-title { font-weight: 600; color: ${theme === 'dark' ? '#e2e8f0' : '#2d3748'}; } .state-mirror-toggle, .state-mirror-close { background: none; border: none; color: ${theme === 'dark' ? '#a0aec0' : '#718096'}; cursor: pointer; font-size: 14px; padding: 2px 6px; border-radius: 4px; } .state-mirror-toggle:hover, .state-mirror-close:hover { background: ${theme === 'dark' ? '#4a5568' : '#edf2f7'}; } .state-mirror-content { padding: 12px; color: ${theme === 'dark' ? '#e2e8f0' : '#2d3748'}; } .state-mirror-section { margin-bottom: 16px; } .state-mirror-section h3 { margin: 0 0 8px 0; font-size: 14px; font-weight: 600; color: ${theme === 'dark' ? '#e2e8f0' : '#2d3748'}; } .state-mirror-status, .state-mirror-activity, .state-mirror-plugins { display: flex; flex-direction: column; gap: 4px; } .status-item, .activity-item, .plugin-item { display: flex; justify-content: space-between; align-items: center; } .label { color: ${theme === 'dark' ? '#a0aec0' : '#718096'}; } .value { font-weight: 500; } .value.connected { color: #48bb78; } .value.disconnected { color: #f56565; } `; const styleElement = document.createElement('style'); styleElement.textContent = styles; document.head.appendChild(styleElement); } getPositionStyles(position) { const pos = position || 'top-right'; switch (pos) { case 'top-left': return 'top: 20px; left: 20px;'; case 'top-right': return 'top: 20px; right: 20px;'; case 'bottom-left': return 'bottom: 20px; left: 20px;'; case 'bottom-right': return 'bottom: 20px; right: 20px;'; default: return 'top: 20px; right: 20px;'; } } setupEventListeners() { if (!this.overlay) return; const toggleBtn = this.overlay.querySelector('.state-mirror-toggle'); const closeBtn = this.overlay.querySelector('.state-mirror-close'); if (toggleBtn) { toggleBtn.addEventListener('click', () => { this.toggle(); }); } if (closeBtn) { closeBtn.addEventListener('click', () => { this.hide(); }); } // Listen to instance events this.instance.on('update', () => { this.updateDisplay(); }); this.instance.on('connect', () => { this.updateDisplay(); }); this.instance.on('disconnect', () => { this.updateDisplay(); }); } /** * Destroy the DevTools */ destroy() { if (this.overlay) { this.overlay.remove(); this.overlay = null; } } } /** * Enable DevTools for a StateMirror instance */ function enableDevTools(instance, config) { const devTools = new StateMirrorDevTools(instance, config); devTools.init(); return devTools; } /** * Create DevTools with custom configuration */ function createDevTools(instance, config) { return new StateMirrorDevTools(instance, config); } exports.StateMirrorDevTools = StateMirrorDevTools; exports.createDevTools = createDevTools; exports.enableDevTools = enableDevTools; //# sourceMappingURL=index.js.map