PageSourceSearch

https://www.iyush.dev/site.js

js iyush.dev collected 2026-10-02 01:52:23 UTC 23,527 bytes, 567 lines download raw bytes

1/* ============================================================
2   iyush.dev — shared behavior
3   Theme engine (light/dark + dice themes), floating command shell,
4   live telemetry (npm + GitHub), scroll reveals, homepage boot.
5   Linked (deferred) by every page.
6   ============================================================ */
7(function () {
8  "use strict";
9
10  var reduce = window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
11
12  /* ---------- Storage that never crashes ---------- */
13  var mem = {};
14  function storeGet(key) {
15    try { return window.localStorage.getItem(key); } catch (e) { return mem[key] || null; }
16  }
17  function storeSet(key, val) {
18    mem[key] = val;
19    try { window.localStorage.setItem(key, val); } catch (e) { /* memory only */ }
20  }
21
22  /* ---------- Analytics (Google Analytics 4 / gtag) ---------- */
23  // 👉 Paste your GA4 Measurement ID below (format: "G-XXXXXXXXXX") to switch analytics ON.
24  //    Empty string = disabled (safe no-op). Auto-skipped on localhost so your own
25  //    dev traffic never pollutes the numbers. This is the ONLY place you set it.
26  var GA_ID = "G-GKC1N1X7KY";
27  function initAnalytics() {
28    if (!GA_ID || GA_ID.indexOf("G-") !== 0) return;
29    var h = location.hostname;
30    if (!h || h === "localhost" || h === "127.0.0.1" || h.slice(-6) === ".local") return;
31    var s = document.createElement("script");
32    s.async = true;
33    s.src = "https://www.googletagmanager.com/gtag/js?id=" + encodeURIComponent(GA_ID);
34    document.head.appendChild(s);
35    window.dataLayer = window.dataLayer || [];
36    window.gtag = function () { window.dataLayer.push(arguments); };
37    window.gtag("js", new Date());
38    window.gtag("config", GA_ID);
39  }
40  // Fire a custom event (no-op until GA is configured).
41  function track(name, params) {
42    try { if (window.gtag) window.gtag("event", name, params || {}); } catch (e) { /* ignore */ }
43  }
44
45  /* ---------- Themes ---------- */
46  // mode drives the light/dark toggle behavior + the OS-default logic.
47  var THEMES = {
48    dark:      { label: "dark",      mode: "dark",  icon: "☾" },
49    light:     { label: "light",     mode: "light", icon: "☀" },
50    onepiece:  { label: "one piece", mode: "dark",  icon: "⚓" },
51    matrix:    { label: "matrix",    mode: "dark",  icon: "◆" },
52    synthwave: { label: "synthwave", mode: "dark",  icon: "◆" },
53    dracula:   { label: "dracula",   mode: "dark",  icon: "◆" },
54    solarized: { label: "solarized", mode: "light", icon: "☀" }
55  };
56  function currentTheme() {
57    var t = document.documentElement.getAttribute("data-theme");
58    return THEMES[t] ? t : "dark";
59  }
60  function updateToggle(theme) {
61    var btn = document.getElementById("themeToggle");
62    if (!btn) return;
63    var t = THEMES[theme] || THEMES.dark;
64    btn.setAttribute("aria-pressed", t.mode === "light" ? "true" : "false");
65    btn.setAttribute("aria-label", "Switch light/dark theme (current: " + t.label + ")");
66    var icon = btn.querySelector(".tt-icon");
67    var label = btn.querySelector(".tt-label");
68    if (icon) icon.textContent = t.icon;
69    if (label) label.textContent = t.label;
70  }
71  function applyTheme(theme) {
72    if (!THEMES[theme]) theme = "dark";
73    document.documentElement.setAttribute("data-theme", theme);
74    storeSet("theme", theme);
75    updateToggle(theme);
76  }
77  function toggleTheme() {
78    // Always resolve to one of the two base modes.
79    var mode = (THEMES[currentTheme()] || THEMES.dark).mode;
80    applyTheme(mode === "dark" ? "light" : "dark");
81  }
82  function rollTheme() {
83    var cur = currentTheme();
84    var keys = Object.keys(THEMES).filter(function (k) { return k !== cur; });
85    var pick = keys[Math.floor(Math.random() * keys.length)];
86    applyTheme(pick);
87    toast("🎲 rolled: " + THEMES[pick].label);
88    track("theme_change", { theme: pick, via: "dice" });
89    return pick;
90  }
91
92  /* ---------- Toast (theme roll announcements) ---------- */
93  var toastEl = null, toastTimer = null;
94  function toast(msg) {
95    if (!toastEl) return;
96    toastEl.textContent = msg;
97    toastEl.hidden = false;
98    void toastEl.offsetWidth; // reflow so the transition runs
99    toastEl.classList.add("show");
100    if (toastTimer) clearTimeout(toastTimer);
101    toastTimer = setTimeout(function () {
102      toastEl.classList.remove("show");
103      setTimeout(function () { toastEl.hidden = true; }, reduce ? 0 : 260);
104    }, reduce ? 1200 : 1700);
105  }
106
107  function injectThemeControls() {
108    var toggle = document.getElementById("themeToggle");
109    if (toggle && toggle.parentNode && !document.getElementById("diceRoll")) {
110      var d = document.createElement("button");
111      d.id = "diceRoll";
112      d.type = "button";
113      d.className = "theme-toggle dice";
114      d.setAttribute("aria-label", "Roll a random theme");
115      d.title = "Roll a random theme";
116      d.innerHTML = '<span class="tt-icon" aria-hidden="true">🎲</span>';
117      d.addEventListener("click", rollTheme);
118      toggle.parentNode.insertBefore(d, toggle);
119    }
120    if (!document.getElementById("themeToast")) {
121      toastEl = document.createElement("div");
122      toastEl.id = "themeToast";
123      toastEl.className = "theme-toast";
124      toastEl.setAttribute("role", "status");
125      toastEl.setAttribute("aria-live", "polite");
126      toastEl.hidden = true;
127      document.body.appendChild(toastEl);
128    } else {
129      toastEl = document.getElementById("themeToast");
130    }
131  }
132
133  // Add the "games" nav link on every page (keeps the shell DRY).
134  function injectNav() {
135    var nav = document.querySelector(".sb-nav");
136    if (!nav || nav.querySelector('a[href="games.html"]')) return;
137    var a = document.createElement("a");
138    a.href = "games.html";
139    a.textContent = "games";
140    if (/(^|\/)games\.html$/.test(location.pathname)) a.setAttribute("aria-current", "page");
141    var ext = nav.querySelector(".ext");
142    nav.insertBefore(a, ext || null);
143  }
144
145  /* ---------- Live telemetry (graceful — silent on failure) ---------- */
146  var npmWeekly = null; // owthorize weekly downloads, once fetched
147
148  function fmtNum(n) {
149    if (n == null) return "—";
150    if (n >= 1000) return (n / 1000).toFixed(n >= 10000 ? 0 : 1).replace(/\.0$/, "") + "k";
151    return String(n);
152  }
153
154  function renderNpm() {
155    if (npmWeekly == null) return;
156    var card = document.querySelector('a.service[href*="owthorize"]');
157    if (!card) return;
158    var m = card.querySelector(".live-metric");
159    if (!m) {
160      m = document.createElement("span");
161      m.className = "live-metric";
162      var link = card.querySelector(".service__link");
163      if (link) card.insertBefore(m, link); else card.appendChild(m);
164    }
165    m.innerHTML = '<span class="ld" aria-hidden="true"></span><span><b>' +
166      fmtNum(npmWeekly) + "</b> installs / week</span>";
167  }
168
169  function fetchNpm() {
170    try {
171      fetch("https://api.npmjs.org/downloads/point/last-week/owthorize")
172        .then(function (r) { return r.ok ? r.json() : null; })
173        .then(function (d) {
174          if (d && typeof d.downloads === "number") { npmWeekly = d.downloads; renderNpm(); }
175        })
176        .catch(function () {});
177    } catch (e) { /* ignore */ }
178  }
179
180  var ghState = null; // { days: [counts...], lastPushMs }
181
182  function timeAgo(ms) {
183    if (!ms) return "";
184    var s = Math.max(1, Math.floor((Date.now() - ms) / 1000));
185    if (s < 3600) return Math.floor(s / 60) + "m ago";
186    if (s < 86400) return Math.floor(s / 3600) + "h ago";
187    return Math.floor(s / 86400) + "d ago";
188  }
189
190  function renderGh() {
191    if (!ghState) return;
192    var inner = document.querySelector(".footer__inner");
193    if (!inner) return;
194    var sig = document.getElementById("ghSignal");
195    if (!sig) {
196      sig = document.createElement("span");
197      sig.id = "ghSignal";
198      sig.className = "gh-signal";
199      sig.title = "GitHub push activity — last 14 days";
200      inner.insertBefore(sig, inner.firstChild);
201    }
202    var max = Math.max(1, Math.max.apply(null, ghState.days));
203    var bars = ghState.days.map(function (c) {
204      var h = c === 0 ? 2 : Math.round(3 + (c / max) * 13);
205      var op = c === 0 ? 0.25 : 0.85;
206      return '<i style="height:' + h + "px;opacity:" + op + '"></i>';
207    }).join("");
208    sig.innerHTML = '<span class="gh-label">gh activity</span>' +
209      '<span class="gh-bars" aria-hidden="true">' + bars + "</span>" +
210      (ghState.lastPushMs ? '<span class="gh-last">push ' + timeAgo(ghState.lastPushMs) + "</span>" : "");
211  }
212
213  function fetchGitHub() {
214    try {
215      fetch("https://api.github.com/users/Spyyy004/events/public?per_page=100")
216        .then(function (r) { return r.ok ? r.json() : null; })
217        .then(function (events) {
218          if (!Array.isArray(events)) return;
219          var days = [], i;
220          for (i = 0; i < 14; i++) days.push(0);
221          var now = Date.now(), lastPush = 0;
222          events.forEach(function (e) {
223            var t = Date.parse(e.created_at);
224            if (isNaN(t)) return;
225            if (e.type === "PushEvent") {
226              if (t > lastPush) lastPush = t;
227              var idx = 13 - Math.floor((now - t) / 86400000);
228              if (idx >= 0 && idx < 14) {
229                var commits = (e.payload && e.payload.commits && e.payload.commits.length) || 1;
230                days[idx] += commits;
231              }
232            }
233          });
234          ghState = { days: days, lastPushMs: lastPush };
235          renderGh();
236        })
237        .catch(function () {});
238    } catch (e) { /* ignore */ }
239  }
240
241  /* ---------- Command bar / shell ---------- */
242  var ROUTES = { home: "index.html", projects: "projects.html", blog: "blog.html", games: "games.html" };
243  var OPEN = {
244    github: "https://github.com/Spyyy004",
245    linkedin: "https://linkedin.com/in/ayush-pawar004",
246    email: "mailto:[email protected]"
247  };
248  var STACK = ["javascript", "typescript", "node", "react", "python", "postgres", "ml", "dns"];
249
250  function esc(s) {
251    return String(s).replace(/[&<>"']/g, function (c) {
252      return { "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" }[c];
253    });
254  }
255  function yearsShipping() {
256    try { return Math.max(1, new Date().getFullYear() - 2022); } catch (e) { return 3; }
257  }
258
259  function resolveRoute(s) {
260    var t = s.replace(/^cd\b\s*/, "").replace(/^~\/?/, "").replace(/^\//, "").replace(/\/+$/, "").trim();
261    if (t === "" || t === "~" || t === "home" || t === "index") return { label: "home", url: ROUTES.home };
262    if (t === "projects" || t === "project" || t === "services") return { label: "projects", url: ROUTES.projects };
263    if (t === "blog" || t === "blogs" || t === "posts") return { label: "blog", url: ROUTES.blog };
264    if (t === "games" || t === "game" || t === "arcade" || t === "play") return { label: "games", url: ROUTES.games };
265    return null;
266  }
267
268  function initCommandBar() {
269    var bar = document.getElementById("cmdbar");
270    var input = document.getElementById("cmdInput");
271    var pop = document.getElementById("cmdPop");
272    if (!bar || !input || !pop) return;
273
274    var history = [], hIndex = -1, popTimer = null;
275
276    function showPop(html) {
277      if (popTimer) { clearTimeout(popTimer); popTimer = null; }
278      pop.innerHTML = html;
279      pop.hidden = false;
280    }
281    function autoPop(html) { showPop(html); popTimer = setTimeout(hidePop, 2800); }
282    function hidePop() { pop.hidden = true; pop.innerHTML = ""; }
283    function flash(msg, isErr) {
284      autoPop('<span class="pop-line' + (isErr ? " pop-err" : "") + '">' + msg + "</span>");
285    }
286
287    function showHelp() {
288      showPop(
289        '<div class="pop-h">available commands</div>' +
290        '<ul class="pop-list">' +
291          "<li><b>cd projects</b> · <b>cd blog</b> · <b>cd home</b><span>navigate</span></li>" +
292          "<li><b>ls</b><span>list routes</span></li>" +
293          "<li><b>neofetch</b><span>system card</span></li>" +
294          "<li><b>top</b><span>projects as processes</span></li>" +
295          "<li><b>stack</b><span>loaded modules</span></li>" +
296          "<li><b>theme</b> · light · dark · &lt;name&gt;<span>switch appearance</span></li>" +
297          "<li><b>roll</b><span>random theme 🎲</span></li>" +
298          "<li><b>open</b> github · linkedin · email<span>external links</span></li>" +
299          "<li><b>whoami</b> · <b>date</b> · <b>clear</b><span>&nbsp;</span></li>" +
300        "</ul>" +
301        '<div class="pop-tip">press <kbd>/</kbd> focus · <kbd>↑</kbd><kbd>↓</kbd> history · <kbd>esc</kbd> dismiss</div>'
302      );
303    }
304    function showLs() {
305      showPop(
306        '<div class="pop-h">routes</div><ul class="pop-list">' +
307          "<li><b>~/</b><span>home</span></li>" +
308          "<li><b>~/projects</b><span>all services</span></li>" +
309          "<li><b>~/blog</b><span>writing</span></li>" +
310          "<li><b>~/games</b><span>the arcade</span></li>" +
311        "</ul>"
312      );
313    }
314    function showNeofetch() {
315      var yrs = yearsShipping();
316      var npm = npmWeekly != null ? (fmtNum(npmWeekly) + "/wk on npm") : "on npm";
317      var L = function (k, v) { return '<span class="k">' + k + "</span>  " + v; };
318      showPop(
319        '<pre class="pop-pre">' +
320          '<span class="accent">[email protected]</span>\n' +
321          "---------------\n" +
322          L("host    ", "full-stack engineer") + "\n" +
323          L("uptime  ", yrs + " yrs (shipping since 2022)") + "\n" +
324          L("shipped ", "6 products · 1 acquired") + "\n" +
325          L("stack   ", "js · ts · node · react · python") + "\n" +
326          L("live    ", '<span class="live">owthorize</span> — ' + npm) + "\n" +
327          L("contact ", "[email protected]") + "\n" +
328          L("theme   ", currentTheme() + '  <span class="k">(try: roll)</span>') +
329        "</pre>"
330      );
331    }
332    function showTop() {
333      var pad = function (s, n) { s = String(s); while (s.length < n) s += " "; return s; };
334      var row = function (pid, statusCls, status, name, stat) {
335        return pad(pid, 5) + '<span class="' + statusCls + '">' + pad(status, 10) + "</span>" +
336          pad(name, 18) + '<span class="k">' + stat + "</span>";
337      };
338      var owStat = npmWeekly != null ? (fmtNum(npmWeekly) + "/wk") : "live on npm";
339      showPop(
340        '<pre class="pop-pre">' +
341          '<span class="k">tasks: 3 total · 1 live · 1 acquired · 1 shipped</span>\n\n' +
342          '<span class="k">' + pad("PID", 5) + pad("STATUS", 10) + pad("PROJECT", 18) + "STAT</span>\n" +
343          row("0", "live", "live", "owthorize", owStat) + "\n" +
344          row("1", "acquired", "acquired", "snoopsignal", "sold · $3k") + "\n" +
345          row("2", "archived", "shipped", "sqlpremierleague", "700+ users") +
346        "</pre>"
347      );
348    }
349    function showStack() {
350      showPop(
351        '<div class="pop-h">loaded modules · lsmod</div>' +
352        '<pre class="pop-pre">' + STACK.join("   ") + "</pre>"
353      );
354    }
355
356    function navigate(url) {
357      var go = function () { window.location.assign(url); };
358      if (reduce) go(); else setTimeout(go, 220);
359    }
360
361    function run(raw) {
362      var cmd = (raw || "").trim().replace(/\s+/g, " ");
363      if (!cmd) return;
364      history.push(cmd); hIndex = history.length;
365      var lc = cmd.toLowerCase().replace(/^[>$]\s*/, "");
366      track("command", { command: lc.split(" ")[0] || "unknown" });
367
368      if (lc === "clear") { input.value = ""; updateIdle(); hidePop(); return; }
369      if (lc === "help" || lc === "?" || lc === "man") { showHelp(); return; }
370      if (lc === "ls" || lc === "dir") { showLs(); return; }
371      if (lc === "neofetch" || lc === "about") { showNeofetch(); return; }
372      if (lc === "top" || lc === "htop" || lc === "ps") { showTop(); return; }
373      if (lc === "stack" || lc === "lsmod" || lc === "skills") { showStack(); return; }
374      if (lc === "whoami") { flash("ayush pawar — full-stack engineer who ships fast."); return; }
375      if (lc === "date" || lc === "uptime") {
376        var now = new Date();
377        flash("<b>" + esc(now.toString().replace(/\s\(.*\)$/, "")) + "</b> · uptime " + yearsShipping() + "y");
378        return;
379      }
380      if (lc === "roll" || lc === "dice" || lc === "theme random" || lc === "shuffle") {
381        var picked = rollTheme(); flash("🎲 rolled: <b>" + esc(THEMES[picked].label) + "</b>"); return;
382      }
383      if (lc === "theme") { toggleTheme(); flash("theme &rarr; <b>" + currentTheme() + "</b>"); return; }
384      if (lc.indexOf("theme ") === 0) {
385        var name = lc.slice(6).trim();
386        if (THEMES[name]) { applyTheme(name); flash("theme &rarr; <b>" + esc(name) + "</b>"); }
387        else { flash("unknown theme: <b>" + esc(name) + "</b> — try 'roll' or theme light/dark", true); }
388        return;
389      }
390      if (lc === "sudo hire-me" || lc === "sudo hire me") {
391        flash("permission granted ✓ — opening mail…");
392        window.location.href = OPEN.email; return;
393      }
394      if (lc.indexOf("sudo ") === 0) { flash("nice try — you're not in the sudoers file. 😉"); return; }
395
396      if (lc.indexOf("open ") === 0) {
397        var key = lc.slice(5).trim().replace(/^~\//, "");
398        if (key === "mail") key = "email";
399        if (OPEN[key]) {
400          flash("opening <b>" + esc(key) + "</b>…");
401          if (key === "email") window.location.href = OPEN[key];
402          else window.open(OPEN[key], "_blank", "noopener");
403        } else { flash("can't open '" + esc(key) + "' — try github, linkedin, or email", true); }
404        return;
405      }
406
407      var route = resolveRoute(lc);
408      if (route) {
409        input.value = ""; updateIdle();
410        flash("cd &rarr; <b>~/" + (route.label === "home" ? "" : route.label) + "</b>");
411        navigate(route.url); return;
412      }
413
414      flash("command not found: <b>" + esc(cmd) + "</b> — try 'help'", true);
415    }
416
417    function updateIdle() {
418      bar.classList.toggle("idle", document.activeElement !== input && !input.value);
419    }
420
421    input.addEventListener("focus", function () { updateIdle(); hidePop(); });
422    input.addEventListener("blur", updateIdle);
423    input.addEventListener("input", updateIdle);
424    input.addEventListener("keydown", function (e) {
425      if (e.key === "Enter") { e.preventDefault(); run(input.value); }
426      else if (e.key === "Escape") { hidePop(); input.blur(); }
427      else if (e.key === "ArrowUp") {
428        if (history.length) { e.preventDefault(); hIndex = Math.max(0, hIndex - 1); input.value = history[hIndex] || ""; updateIdle(); }
429      } else if (e.key === "ArrowDown") {
430        if (history.length) {
431          e.preventDefault(); hIndex = Math.min(history.length, hIndex + 1);
432          input.value = history[hIndex] || ""; updateIdle();
433        }
434      }
435    });
436
437    document.addEventListener("keydown", function (e) {
438      var t = e.target;
439      var typing = t && (t.tagName === "INPUT" || t.tagName === "TEXTAREA" || t.tagName === "SELECT" || t.isContentEditable);
440      if (e.key === "/" && !typing) { e.preventDefault(); input.focus(); }
441      else if (e.key === "Escape" && document.activeElement === input) { hidePop(); input.blur(); }
442    });
443
444    updateIdle();
445  }
446
447  /* ---------- Scroll reveals ---------- */
448  function initReveals() {
449    var els = document.querySelectorAll(".service, .reveal");
450    if (!els.length) return;
451    if (reduce || !("IntersectionObserver" in window)) {
452      els.forEach(function (el) { el.classList.add("in"); });
453      return;
454    }
455    var io = new IntersectionObserver(function (entries) {
456      entries.forEach(function (e) {
457        if (e.isIntersecting) {
458          var i = parseInt(e.target.style.getPropertyValue("--i"), 10) || 0;
459          e.target.style.animationDelay = (i * 0.08) + "s";
460          e.target.classList.add("in");
461          io.unobserve(e.target);
462        }
463      });
464    }, { threshold: 0, rootMargin: "0px 0px -12% 0px" }); // threshold 0 so elements taller than the viewport still reveal
465    els.forEach(function (el) { io.observe(el); });
466  }
467
468  /* ---------- Homepage boot sequence ---------- */
469  function typeStatus() {
470    var el = document.getElementById("sbStatus");
471    if (!el || reduce) return;
472    var rest = "online", i = 0;
473    el.textContent = "status: ";
474    (function tick() {
475      if (i <= rest.length) { el.textContent = "status: " + rest.slice(0, i); i++; setTimeout(tick, 55); }
476    })();
477  }
478  function typeRole() {
479    var el = document.querySelector(".role");
480    if (!el) return;
481    var text = el.getAttribute("data-type") || el.textContent.trim();
482    if (reduce) { el.style.opacity = 1; return; }
483    el.textContent = "";
484    var caret = document.createElement("span");
485    caret.className = "typed-caret";
486    el.appendChild(caret);
487    var i = 0;
488    (function tick() {
489      if (i < text.length) {
490        caret.insertAdjacentText("beforebegin", text.charAt(i));
491        i++;
492        setTimeout(tick, 22 + Math.random() * 26);
493      } else {
494        setTimeout(function () { if (caret.parentNode) caret.parentNode.removeChild(caret); }, 1400);
495      }
496    })();
497  }
498  function counts() {
499    document.querySelectorAll(".metric__num").forEach(function (el) {
500      var target = parseInt(el.getAttribute("data-count"), 10);
501      var vEl = el.querySelector(".v");
502      if (reduce || isNaN(target)) { if (vEl) vEl.textContent = target; return; }
503      var start = null, dur = 900;
504      function step(ts) {
505        if (!start) start = ts;
506        var p = Math.min((ts - start) / dur, 1);
507        var eased = 1 - Math.pow(1 - p, 3);
508        vEl.textContent = Math.round(eased * target);
509        if (p < 1) requestAnimationFrame(step);
510        else vEl.textContent = target;
511      }
512      requestAnimationFrame(step);
513    });
514  }
515  function initHomeBoot() {
516    var hero = document.getElementById("hero");
517    if (!hero) return;
518    window.addEventListener("load", function () {
519      typeStatus();
520      hero.classList.add("in");
521      if (reduce) { typeRole(); counts(); return; }
522      setTimeout(typeRole, 950);
523      setTimeout(counts, 1150);
524    });
525  }
526
527  /* ---------- DevTools easter egg ---------- */
528  function consoleGreeting() {
529    try {
530      console.log("%c [email protected] ",
531        "background:#3FB950;color:#0A0C10;font-weight:700;padding:2px 6px;border-radius:4px");
532      console.log("%cpsst — this site has a shell. press \"/\" then try: %chelp%c · %cneofetch%c · %ctop%c · %croll",
533        "color:#8B95A5", "color:#3FB950", "color:#8B95A5",
534        "color:#3FB950", "color:#8B95A5", "color:#3FB950", "color:#8B95A5", "color:#D29922");
535    } catch (e) { /* ignore */ }
536  }
537
538  /* ---------- Wire up ---------- */
539  function init() {
540    initAnalytics();
541    updateToggle(currentTheme());
542    injectThemeControls();
543    injectNav();
544    var btn = document.getElementById("themeToggle");
545    if (btn) btn.addEventListener("click", toggleTheme);
546
547    // Follow OS theme changes only while the user hasn't chosen explicitly.
548    if (window.matchMedia) {
549      try {
550        var mq = window.matchMedia("(prefers-color-scheme: light)");
551        var onChange = function (e) { if (!storeGet("theme")) applyTheme(e.matches ? "light" : "dark"); };
552        if (mq.addEventListener) mq.addEventListener("change", onChange);
553        else if (mq.addListener) mq.addListener(onChange);
554      } catch (e) { /* ignore */ }
555    }
556
557    initCommandBar();
558    initReveals();
559    initHomeBoot();
560    fetchNpm();
561    fetchGitHub();
562    consoleGreeting();
563  }
564
565  if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", init);
566  else init();
567})();

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.