1// Kadoink AI voice FAB (logged-out, bottom-left). A big funky mic button that 2// invites anonymous visitors to "talk to Kadoink AI". Tapping enters a demo 3// "listening" state â the disc turns punch-red and pulses (.is-live in CSS), a 4// live count-up timer sticker runs, and the wiggly "AI" tag rides the corner â 5// then a pop bubble nudges the visitor to sign up free (the real voice command 6// lives behind sign-in via the command box, so there's no mic capture here). 7// CSP-safe: external same-origin script, no inline JS. Mirrors kadoink_voice.js. 8(function () { 9 var CAP_MS = 8000; // safety cap â auto-finish the demo so the timer can't run forever 10 11 function boot() { 12 var fab = document.querySelector('[data-ai-fab]'); 13 if (!fab) return; 14 var btn = fab.querySelector('[data-ai-fab-btn]'); 15 var timeEl = fab.querySelector('[data-ai-fab-time]'); 16 var pop = fab.querySelector('[data-ai-fab-pop]'); 17 if (!btn) return; 18 19 // Progressive enhancement: only surface the mic once it's wired. 20 fab.hidden = false; 21 22 var live = false, startedAt = 0, tick = 0, cap = 0; 23 24 function pad(n) { return (n < 10 ? '0' : '') + n; } 25 function fmt(sec) { 26 return pad(Math.floor(sec / 60)) + ':' + pad(sec % 60); // mm:ss, matches the design's "00:00" 27 } 28 function render() { 29 if (timeEl) timeEl.textContent = fmt(Math.floor((Date.now() - startedAt) / 1000)); 30 } 31 function start() { 32 live = true; 33 if (pop) pop.hidden = true; 34 startedAt = Date.now(); 35 render(); 36 fab.classList.add('is-live'); 37 btn.setAttribute('aria-pressed', 'true'); 38 btn.title = 'Listening⦠tap to finish'; 39 tick = window.setInterval(render, 250); 40 cap = window.setTimeout(stop, CAP_MS); 41 } 42 function stop() { 43 live = false; 44 fab.classList.remove('is-live'); 45 window.clearInterval(tick); tick = 0; 46 window.clearTimeout(cap); cap = 0; 47 btn.setAttribute('aria-pressed', 'false'); 48 btn.title = 'Talk to Kadoink'; 49 if (timeEl) timeEl.textContent = '00:00'; 50 if (pop) pop.hidden = false; // show the sign-up nudge 51 } 52 53 btn.addEventListener('click', function () { 54 if (live) stop(); else start(); 55 }); 56 // Dismiss the nudge bubble on an outside click. 57 document.addEventListener('click', function (e) { 58 if (pop && !pop.hidden && !fab.contains(e.target)) pop.hidden = true; 59 }); 60 } 61 62 if (document.readyState !== 'loading') boot(); 63 else document.addEventListener('DOMContentLoaded', boot); 64})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.