Squiggle Ops
PWA gateway • Dashboard • Launcher
Core Control Dashboard
Persist: on
0
Weapons Console
Haptics: local
⚡ Blast
🛡️ Shield
∅ Nullify
🔥 Ignition
⤴ Charge
⤵ Drain
⟲ Reset
🔒 Toggle Persist
Notes: Save to Home Screen for an app-like experience. Some PWA features (service worker) require HTTPS.
‘+escapeHtml(l)+’
‘).join(”); } function escapeHtml(s){ return (s+”).replace(/[&”]/g, c => ({‘&’:’&’,”:’>’,’”‘:’"’}[c])); } function render(){ const e = Math.max(0,Math.min(100, Math.round(state.energy))); energyBar.style.width = e + ‘%’; energyValue.textContent = Math.round(state.energy); energyPerc.textContent = e + ‘%’; coreVal.textContent = Math.round(state.core); netVal.textContent = Math.round(state.net); persistStatus.textContent = ‘Persist: ‘ + (state.persist ? ‘on’ : ‘off’); } // Load from localStorage function loadState(){ try{ const raw = localStorage.getItem(STORAGE_KEY); if(raw){ const s = JSON.parse(raw); state = Object.assign(state, s); } }catch(e){ console.warn(‘load failed’,e) } } function saveState(){ try{ if(state.persist) localStorage.setItem(STORAGE_KEY, JSON.stringify(state)); else localStorage.removeItem(STORAGE_KEY); }catch(e){ console.warn(‘save failed’,e) } } const persistStateDebounced = debounce(saveState, 300); // Helpers function debounce(fn, ms){ let t; return (…a)=>{ clearTimeout(t); t=setTimeout(()=>fn(…a), ms); };} // Controls: weapon behavior document.getElementById(‘blastBtn’).addEventListener(‘click’, ()=>{ state.energy = Math.max(0, state.energy – 12); state.core = Math.max(0, state.core – 6); log(‘Blast fired — energy -12’); flash(‘#ff7a18’); render(); persistStateDebounced(); }); document.getElementById(‘shieldBtn’).addEventListener(‘click’, ()=>{ state.energy = Math.max(0, state.energy – 6); state.net = Math.min(100, state.net + 4); log(‘Shield activated — energy -6, net +4’); flash(‘#1bd1a3’); render(); persistStateDebounced(); }); document.getElementById(‘nullBtn’).addEventListener(‘click’, ()=>{ state.energy = Math.max(0, state.energy – 10); state.core = Math.max(0, state.core – 10); log(‘Nullify pulse — systems stabilized’); flash(‘#9aa6b2’); render(); persistStateDebounced(); }); document.getElementById(‘igniteBtn’).addEventListener(‘click’, ()=>{ state.energy = Math.min(100, state.energy + 14); state.core = Math.min(100, state.core + 8); log(‘Ignition sequence — energy +14’); flash(‘#ff4d6d’); render(); persistStateDebounced(); }); document.getElementById(‘chargeBtn’).addEventListener(‘click’, ()=>{ state.energy = Math.min(100, state.energy + 8); state.net = Math.min(100, state.net + 3); log(‘Manual charge +8’); render(); persistStateDebounced(); }); document.getElementById(‘drainBtn’).addEventListener(‘click’, ()=>{ state.energy = Math.max(0, state.energy – 8); state.net = Math.max(0, state.net – 4); log(‘Manual drain -8’); render(); persistStateDebounced(); }); document.getElementById(‘resetBtn’).addEventListener(‘click’, ()=>{ state.energy = 48; state.core = 52; state.net = 46; log(‘Systems reset’); render(); persistStateDebounced(); }); document.getElementById(‘togglePersist’).addEventListener(‘click’, ()=>{ state.persist = !state.persist; log(‘Persist ‘ + (state.persist ? ‘enabled’:’disabled’)); render(); persistStateDebounced(); }); // Export / Import document.getElementById(‘exportState’).addEventListener(‘click’, ()=>{ const data = JSON.stringify(state, null, 2); const blob = new Blob([data], {type:’application/json’}); const url = URL.createObjectURL(blob); const a = document.createElement(‘a’); a.href = url; a.download = ‘squiggle_state.json’; document.body.appendChild(a); a.click(); a.remove(); URL.revokeObjectURL(url); log(‘State exported’); }); document.getElementById(‘importState’).addEventListener(‘click’, ()=>{ const input = document.createElement(‘input’); input.type=’file’; input.accept=’application/json’; input.onchange = e=>{ const f = e.target.files[0]; if(!f) return; const r = new FileReader(); r.onload = ev=>{ try{ const s = JSON.parse(ev.target.result); state = Object.assign(state, s); log(‘State imported’); render(); persistStateDebounced(); }catch(err){ log(‘Import failed’); } }; r.readAsText(f); }; input.click(); }); document.getElementById(‘clearState’).addEventListener(‘click’, ()=>{ if(confirm(‘Clear all local state?’)) { state = {energy:48,core:52,net:46,persist:true,logs:[]}; saveState(); render(); log(‘State cleared’); } }); // Visual flash function flash(color){ const el = document.querySelector(‘.logo’); const prev = el.style.boxShadow; el.style.boxShadow = ‘0 0 30px 6px ‘+color; setTimeout(()=> el.style.boxShadow = prev, 220); // small vibration on supported devices if(navigator.vibrate) navigator.vibrate(40); } // Protocol launcher (attempt) document.getElementById(‘openProtocol’).addEventListener(‘click’, ()=>{ openProtocol(‘squiggle://ausar.merr.amon.ops’); }); function openProtocol(url){ // Try: iframe method (works for many mobile scenarios) log(‘Attempting to open protocol: ‘ + url); const start = Date.now(); // create invisible iframe const ifr = document.createElement(‘iframe’); ifr.style.display=’none’; document.body.appendChild(ifr); try { ifr.src = url; } catch(e){ // fallback to location change window.location = url; } setTimeout(()=>{ try{ ifr.remove(); } catch(e){} // If still on page after a short time, assume not installed if(Date.now() – start { // grow energy slowly, capped at 100 if(state.energy { self.skipWaiting(); e.waitUntil(caches.open(CACHE_NAME).then(c=>c.addAll(assets)).catch(()=>{})); }); self.addEventListener(‘activate’, e => { e.waitUntil(self.clients.claim()); }); self.addEventListener(‘fetch’, e => { e.respondWith(caches.match(e.request).then(r=>r || fetch(e.request)).catch(()=>fetch(e.request))); }); `; const blob = new Blob([swCode], {type:’application/javascript’}); const swUrl = URL.createObjectURL(blob); navigator.serviceWorker.register(swUrl).then(reg => { log(‘Service worker attempted to register (may require HTTPS).’); }).catch(err=>{ console.warn(‘SW register failed’,err); log(‘Service worker registration failed (HTTPS required).’); }); }catch(e){ console.warn(‘sw fail’,e); log(‘Service worker not available’); } } // Load & start loadState(); render(); renderLogs(); startAutoCharge(); // Small UX polish: ensure localStorage toggle text is correct persistStatus.addEventListener(‘click’, ()=>{ state.persist = !state.persist; render(); persistStateDebounced(); log(‘Persist toggled via pill’); }); // Save on page hide window.addEventListener(‘pagehide’, saveState); window.addEventListener(‘beforeunload’, saveState); // initial log log(‘Dashboard ready’); })();