1// Pastel rainbow link colors 2const pastelColors = [ 3 "#e8a0bf", 4 "#f0b8a8", 5 "#f2cc8f", 6 "#b5d99c", 7 "#a8d8ea", 8 "#b8b8f0", 9 "#d4a5e5", 10 "#f5a8a8", 11 "#a8e0c8", 12 "#c4b8e8", 13 "#e8c8a0", 14 "#a0c8e8", 15]; 16 17// Shared with typer.js (separate script scope) for per-glyph pastel colors. 18window.pastelColors = pastelColors; 19 20const LETTER = /[A-Za-z]/; 21 22function colorizeLinks() { 23 for (const link of document.querySelectorAll("main a")) { 24 const color = pastelColors[Math.floor(Math.random() * pastelColors.length)]; 25 link.style.color = color; 26 } 27} 28 29function colorizeRandomLetters(el, accentRatio = 0.28) { 30 const text = el.dataset.original || el.textContent; 31 const letterPositions = []; 32 33 el.dataset.original = text; 34 for (const [index, ch] of [...text].entries()) { 35 if (LETTER.test(ch)) { 36 letterPositions.push(index); 37 } 38 } 39 40 if (!letterPositions.length) { 41 return; 42 } 43 44 const accentCount = Math.max( 45 1, 46 Math.floor(letterPositions.length * accentRatio) 47 ); 48 const accentPositions = new Set(); 49 50 while (accentPositions.size < Math.min(accentCount, letterPositions.length)) { 51 const randomIndex = Math.floor(Math.random() * letterPositions.length); 52 accentPositions.add(letterPositions[randomIndex]); 53 } 54 55 const fragment = document.createDocumentFragment(); 56 for (const [index, ch] of [...text].entries()) { 57 if (!LETTER.test(ch)) { 58 fragment.appendChild(document.createTextNode(ch)); 59 return; 60 } 61 62 const span = document.createElement("span"); 63 span.textContent = ch; 64 65 if (accentPositions.has(index)) { 66 span.style.color = 67 pastelColors[Math.floor(Math.random() * pastelColors.length)]; 68 } 69 70 fragment.appendChild(span); 71 } 72 73 el.replaceChildren(fragment); 74} 75 76// Social icon highlights per section 77const socialHighlights = { 78 about: { linkedin: "#0A66C2", x: null }, 79 microblog: { scholar: "#4285F4" },
80 news: { github: null, pytorch: "#EE4C2C" }, 81 research: { scholar: "#4285F4" }, 82}; 83 84// GitHub & X brand is black/white â just use site text color 85Object.defineProperty(socialHighlights.news, "github", { 86 get() { 87 return window 88 .getComputedStyle(document.documentElement) 89 .getPropertyValue("--text") 90 .trim(); 91 }, 92}); 93Object.defineProperty(socialHighlights.about, "x", { 94 get() { 95 return window 96 .getComputedStyle(document.documentElement) 97 .getPropertyValue("--text") 98 .trim(); 99 }, 100}); 101 102function highlightSocials(sectionId) { 103 const map = socialHighlights[sectionId]; 104 for (const a of document.querySelectorAll(".social-links a[data-social]")) { 105 a.style.color = map?.[a.dataset.social] || ""; 106 } 107} 108 109// Rainbow footer text 110const footerCity = document.getElementById("footer-city"); 111const footerEmail = document.getElementById("footer-email"); 112 113function rainbowText(el) { 114 const text = el.dataset.original || el.textContent; 115 el.dataset.original = text; 116 el.innerHTML = [...text] 117 .map((ch, i) => 118 ch === " " 119 ? " " 120 : `<span class="rainbow-ch" style="color:${pastelColors[i % pastelColors.length]};animation-delay:${i * 30}ms">${ch}</span>` 121 ) 122 .join(""); 123} 124 125function resetText(el) { 126 if (el.dataset.original) { 127 el.innerHTML = [...el.dataset.original] 128 .map((ch) => (ch === " " ? " " : `<span>${ch}</span>`)) 129 .join(""); 130 } 131} 132 133function updateFooterRainbow(sectionId) { 134 if (sectionId === "gallery") { 135 rainbowText(footerCity); 136 } else { 137 resetText(footerCity); 138 } 139 if (sectionId === "research") { 140 rainbowText(footerEmail); 141 } else { 142 resetText(footerEmail); 143 } 144}
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.