The LMGTFY part: a link with ?q= types the question into the box, then asks. Everything after the submit is the server's: Datastar morphs what it streams. Only the home page has the box; the code and rules pages do not.
The box is a textarea that grows with the question. field-sizing: content
does it where it is supported; elsewhere the height follows the text here.
13 const native = CSS.supports("field-sizing", "content"); 14 function grow() { 15 if (native) return; 16 box.style.height = "auto"; 17 box.style.height = `${box.scrollHeight + box.offsetHeight - box.clientHeight}px`; 18 } 19 box.addEventListener("input", grow); 20 form.addEventListener("submit", () => setTimeout(grow, 0)); 21 // A question is one line: Enter asks (Shift+Enter too), and a pasted line 22 // break becomes a space. Not while an IME is composing: Enter confirms there. 23 box.addEventListener("keydown", (event) => { 24 if (event.key !== "Enter" || event.isComposing) return; 25 event.preventDefault(); 26 form.requestSubmit(); 27 }); 28 box.addEventListener("paste", (event) => { 29 const text = event.clipboardData?.getData("text"); 30 if (text === undefined || !/[\r\n]/.test(text)) return; 31 event.preventDefault(); 32 box.setRangeText(text.replace(/\s*[\r\n]+\s*/g, " "), box.selectionStart, box.selectionEnd, "end"); 33 box.dispatchEvent(new Event("input", { bubbles: true })); 34 }); 35 const wait = (ms) => new Promise((done) => setTimeout(done, calm ? 0 : ms));
Datastar loads as a module, after this script. When it binds the box it writes its (empty) signal into it, so anything typed before then is lost. This listener is in place before the module runs, so it cannot miss the event.
40 const bound = new Promise((done) => document.addEventListener("datastar-ready", done, { once: true }));
42 async function type() { 43 await bound; 44 box.value = ""; 45 await wait(700); 46 for (const ch of question) { 47 box.value += ch; 48 // Datastar's bind listens for input: the signal follows the box. 49 box.dispatchEvent(new Event("input", { bubbles: true })); 50 await wait(45 + Math.random() * 70); 51 } 52 await wait(500); 53 form.requestSubmit(); 54 } 55 if (question) type();
What this page asked last, so the toast for it is not shown back to it.
/live: the archive tells every open browser how many are open and where,
and sends a toast when someone asks a question or clones the code. The
socket is a SharedWorker's (/live.js), one for all this browser's tabs of
this build; a browser without SharedWorker holds its own, per tab. Either
reconnects, waiting longer each time, if the socket drops.
66(() => { 67 const online = document.getElementById("online"); 68 const toasts = document.getElementById("toasts"); 69 if (!online || !toasts) return; 70 const mine = document.querySelector('meta[name="lmjtfy-build"]')?.content ?? ""; 71 function heard(text) { 72 let live; 73 try { live = JSON.parse(text); } catch { return; } 74 if (typeof live.online === "number") { 75 online.textContent = `${live.online} online`; 76 online.hidden = false; 77 } 78 if (live.closed) online.hidden = true; 79 if (typeof live.toast === "string") toast(live.toast); 80 if (typeof live.patch === "string") patch(live.patch); 81 if (typeof live.build === "string") built(live.build); 82 } 83 function shared() { 84 const worker = new SharedWorker(`/live.js?build=${encodeURIComponent(mine)}`, { name: `lmjtfy-live-${mine}` }); 85 worker.port.addEventListener("message", (event) => heard(event.data)); 86 worker.port.start(); 87 addEventListener("pagehide", () => worker.port.postMessage("bye"), { once: true }); 88 } 89 let wait = 1000; 90 function connect() { 91 // The page's build, so the archive tells only an earlier page what a 92 // deploy changed. 93 const socket = new WebSocket(`${location.protocol === "https:" ? "wss" : "ws"}://${location.host}/live?build=${encodeURIComponent(mine)}`); 94 socket.addEventListener("open", () => { wait = 1000; }); 95 socket.addEventListener("message", (event) => heard(event.data)); 96 socket.addEventListener("close", () => { 97 online.hidden = true; 98 setTimeout(connect, wait); 99 wait = Math.min(wait * 2, 60000); 100 }); 101 } 102 function start() { 103 if (typeof SharedWorker === "function") { 104 try { shared(); return; } catch { /* a browser that refuses it: hold our own */ } 105 } 106 connect(); 107 } 108 // Back from the back-forward cache: the worker forgot this tab when it 109 // said goodbye, so it joins again. 110 addEventListener("pageshow", (event) => { if (event.persisted && typeof SharedWorker === "function") shared(); }); 111 // The site was deployed since this page was rendered: a toast that stays 112 // until it is used or dismissed, once. 113 let offered = false; 114 function built(build) { 115 if (!mine || build === mine || offered) return; 116 offered = true; 117 const shown = document.createElement("div"); 118 shown.className = "toast stays"; 119 shown.innerHTML = 'A new version of the site is live. <button type="button" class="reload">Reload</button> <button type="button" class="ghost dismiss" aria-label="Dismiss">×</button>'; 120 shown.querySelector(".reload").addEventListener("click", () => location.reload()); 121 shown.querySelector(".dismiss").addEventListener("click", () => shown.remove()); 122 toasts.append(shown); 123 } 124 // Each element sent replaces this page's element with the same id, if it 125 // has one: the feeds on the home page, and whatever else is sent this way. 126 // One with `data-prepend` puts its children at the top of the element it 127 // names instead, each taking the place of its own id wherever that was, 128 // so what this page has scrolled in further down stays. 129 function patch(html) { 130 const holder = document.createElement("template"); 131 holder.innerHTML = html; 132 for (const fresh of [...holder.content.children]) { 133 const into = fresh.dataset.prepend && document.getElementById(fresh.dataset.prepend); 134 if (into) { 135 for (const line of [...fresh.children].reverse()) { 136 if (line.id) document.getElementById(line.id)?.remove(); 137 into.prepend(line); 138 } 139 } else if (fresh.id) { 140 document.getElementById(fresh.id)?.replaceWith(fresh); 141 } 142 } 143 } 144 // The markup is the server's, escaped there (view/toast.rs). 145 function toast(html) { 146 const holder = document.createElement("div"); 147 holder.innerHTML = html; 148 const shown = holder.firstElementChild; 149 if (!shown || (shown.dataset.q && shown.dataset.q === window.lmjtfyAsked)) return; 150 toasts.append(shown); 151 // The oldest passing toasts go first; one that stays is never pushed out. 152 const passing = [...toasts.children].filter((toast) => !toast.classList.contains("stays")); 153 while (passing.length > 3) passing.shift().remove(); 154 setTimeout(() => { shown.classList.add("gone"); setTimeout(() => shown.remove(), 400); }, 7000); 155 } 156 start(); 157})();
The copy buttons, on every page: data-copy names the element whose link
or text is copied.
161(() => { 162 document.addEventListener("click", (event) => { 163 const button = event.target.closest("[data-copy]"); 164 if (!button) return; 165 const link = document.getElementById(button.dataset.copy); 166 navigator.clipboard.writeText(link.href ?? link.textContent).then( 167 () => { button.textContent = "Copied"; }, 168 () => { getSelection().selectAllChildren(link); button.textContent = "Selected, press Ctrl+C"; } 169 ); 170 }); 171})();
The dot fields behind the page, after typesafe.ai: soft blobs put through an 8x8 ordered dither, one image pixel per dot, tiled. Drawn once into CSS variables rather than into elements, because the transcript is replaced wholesale and a canvas inside it would be wiped with it.
177(() => { 178 const BAYER = [ 179 [0, 32, 8, 40, 2, 34, 10, 42], [48, 16, 56, 24, 50, 18, 58, 26], 180 [12, 44, 4, 36, 14, 46, 6, 38], [60, 28, 52, 20, 62, 30, 54, 22], 181 [3, 35, 11, 43, 1, 33, 9, 41], [51, 19, 59, 27, 49, 17, 57, 25], 182 [15, 47, 7, 39, 13, 45, 5, 37], [63, 31, 55, 23, 61, 29, 53, 21], 183 ]; 184 const W = 400, H = 300; 185 // A fixed seed: the same field on every load. 186 const random = (seed) => () => ((seed = (seed * 1664525 + 1013904223) >>> 0) / 4294967296); 187 188 function field(seed, count, strength, [r, g, b]) { 189 const next = random(seed); 190 const blobs = Array.from({ length: count }, () => ({ 191 x: next() * W, y: next() * H, size: 16 + next() * 30, weight: (0.5 + next() * 0.5) * strength, 192 })); 193 const canvas = document.createElement("canvas"); 194 canvas.width = W; canvas.height = H; 195 const context = canvas.getContext("2d"); 196 const image = context.createImageData(W, H); 197 for (let y = 0; y < H; y++) { 198 for (let x = 0; x < W; x++) { 199 let value = 0; 200 for (const blob of blobs) { 201 // Distances wrap, so the tile has no seam. 202 const dx = Math.min(Math.abs(x - blob.x), W - Math.abs(x - blob.x)); 203 const dy = Math.min(Math.abs(y - blob.y), H - Math.abs(y - blob.y)); 204 value += blob.weight * Math.exp(-(dx * dx + dy * dy) / (2 * blob.size * blob.size)); 205 } 206 if (value > (BAYER[y % 8][x % 8] + 0.5) / 64) { 207 const at = (y * W + x) * 4; 208 image.data[at] = r; image.data[at + 1] = g; image.data[at + 2] = b; image.data[at + 3] = 255; 209 } 210 } 211 } 212 context.putImageData(image, 0, 0); 213 return `url(${canvas.toDataURL()})`; 214 } 215 216 const root = document.documentElement.style; 217 root.setProperty("--dither-paper", field(7, 4, 0.55, [196, 196, 196])); 218 root.setProperty("--dither-pink", field(23, 5, 0.9, [30, 30, 30])); 219})();
A diagram or picture in a document opens full size on a click: mermaid's drawings and the rules' SVG are too small to read inline. Escape, the close button or a click on the backdrop closes it.
224(() => { 225 let dialog; 226 function open(figure) { 227 if (!dialog) { 228 dialog = document.createElement("dialog"); 229 dialog.className = "zoom"; 230 dialog.innerHTML = '<button type="button" class="ghost close">Close</button><div class="zoomed"></div>'; 231 dialog.addEventListener("click", (event) => { 232 if (event.target === dialog || event.target.closest(".close")) dialog.close(); 233 }); 234 document.body.append(dialog); 235 } 236 const zoomed = dialog.querySelector(".zoomed"); 237 const copy = figure.cloneNode(true); 238 copy.removeAttribute("style"); 239 copy.removeAttribute("width"); 240 copy.removeAttribute("height"); 241 zoomed.replaceChildren(copy); 242 dialog.showModal(); 243 } 244 document.addEventListener("click", (event) => { 245 const figure = event.target.closest(".md pre.mermaid svg, .md img"); 246 if (figure && !event.target.closest("dialog")) open(figure); 247 }); 248})();
What only the page knows, told to /seen (the archive keeps it as a
read event): how long this page was in view, how far down it was seen,
and the screen. It is said each time the page is put away, as the time in
view since it last said, so a tab left open and come back to adds up. A
link followed off the site is said as it is followed (out).
255(() => { 256 // The code host has no `/seen`, and its pages have no `#online` (nav). 257 if (!navigator.sendBeacon || !document.getElementById("online")) return; 258 let since = document.visibilityState === "visible" ? performance.now() : null; 259 let seen = 0; 260 let deepest = 0; 261 // The home page scrolls the window; a code page scrolls its pane. 262 const measure = () => { 263 const pane = document.querySelector("main.pane"); 264 const top = pane ? pane.scrollTop : scrollY; 265 const tall = pane ? pane.scrollHeight : document.documentElement.scrollHeight; 266 const high = pane ? pane.clientHeight : innerHeight; 267 if (tall > 0) deepest = Math.max(deepest, Math.min(1, (top + high) / tall)); 268 }; 269 const say = (more) => navigator.sendBeacon("/seen", JSON.stringify({ 270 path: location.pathname, query: location.search, ms: 0, scroll: deepest, 271 screen: `${screen.width}x${screen.height}`, viewport: `${innerWidth}x${innerHeight}`, ...more, 272 })); 273 addEventListener("scroll", measure, { passive: true, capture: true }); 274 document.addEventListener("visibilitychange", () => { 275 if (document.visibilityState === "visible") { since = performance.now(); return; } 276 if (since === null) return; 277 measure(); 278 seen = performance.now() - since; 279 since = null; 280 // A glance of under a second is not a reading. 281 if (seen >= 1000) say({ ms: Math.round(seen) }); 282 }); 283 document.addEventListener("click", (event) => { 284 const link = event.target.closest && event.target.closest("a[href]"); 285 if (link && link.host && link.host !== location.host && /^https?:$/.test(link.protocol)) say({ out: link.href }); 286 }, { capture: true }); 287 measure(); 288})();