// MacroPad PWA Application (Execute-only) class MacroPadApp { constructor() { this.macros = {}; this.tabs = []; this.currentTab = 'All'; this.ws = null; this.init(); } async init() { await this.loadTabs(); await this.loadMacros(); this.setupWebSocket(); this.setupEventListeners(); this.checkInstallPrompt(); } // API Methods async loadTabs() { try { const response = await fetch('/api/tabs'); const data = await response.json(); this.tabs = data.tabs || []; this.renderTabs(); } catch (error) { console.error('Error loading tabs:', error); this.showToast('Error loading tabs', 'error'); } } async loadMacros() { try { const url = this.currentTab === 'All' ? '/api/macros' : `/api/macros/${encodeURIComponent(this.currentTab)}`; const response = await fetch(url); const data = await response.json(); this.macros = data.macros || {}; this.renderMacros(); } catch (error) { console.error('Error loading macros:', error); this.showToast('Error loading macros', 'error'); } } async executeMacro(macroId) { try { const card = document.querySelector(`[data-macro-id="${macroId}"]`); if (card) card.classList.add('executing'); const response = await fetch('/api/execute', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ macro_id: macroId }) }); if (!response.ok) throw new Error('Execution failed'); setTimeout(() => { if (card) card.classList.remove('executing'); }, 300); } catch (error) { console.error('Error executing macro:', error); this.showToast('Error executing macro', 'error'); } } // WebSocket setupWebSocket() { const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; const wsUrl = `${protocol}//${window.location.host}/ws`; try { this.ws = new WebSocket(wsUrl); this.ws.onopen = () => { this.updateConnectionStatus(true); }; this.ws.onclose = () => { this.updateConnectionStatus(false); setTimeout(() => this.setupWebSocket(), 3000); }; this.ws.onmessage = (event) => { const data = JSON.parse(event.data); this.handleWebSocketMessage(data); }; this.ws.onerror = () => { this.updateConnectionStatus(false); }; } catch (error) { console.error('WebSocket error:', error); } } handleWebSocketMessage(data) { switch (data.type) { case 'macro_created': case 'macro_updated': case 'macro_deleted': this.loadTabs(); this.loadMacros(); break; case 'executed': const card = document.querySelector(`[data-macro-id="${data.macro_id}"]`); if (card) { card.classList.add('executing'); setTimeout(() => card.classList.remove('executing'), 300); } break; } } updateConnectionStatus(connected) { const dot = document.querySelector('.status-dot'); const text = document.querySelector('.connection-status span'); if (dot) { dot.classList.toggle('connected', connected); } if (text) { text.textContent = connected ? 'Connected' : 'Disconnected'; } } // Rendering renderTabs() { const container = document.getElementById('tabs-container'); if (!container) return; container.innerHTML = this.tabs.map(tab => ` `).join(''); } renderMacros() { const container = document.getElementById('macro-grid'); if (!container) return; const macroEntries = Object.entries(this.macros); if (macroEntries.length === 0) { container.innerHTML = `
No macros found
Create macros in the desktop app