PageSourceSearch

https://www.kadoink.com/static/kadoink_ai_fab.js

js kadoink.com collected 2026-10-02 00:30:50 UTC 2,535 bytes, 64 lines download raw bytes

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.