// What only a script can add to the page; without it the page still draws, // saves and switches palettes by link. // - Copy: the SVG, in the palette on screen, onto the clipboard. // - Palettes swap in place (the address keeps the choice, so a live // refresh and a reload render the same one). // - The example prompts fill the box; a drawing in progress counts up. const board = () => document.getElementById("board"); async function swapIn(url) { const response = await fetch(url, { headers: { accept: "text/html" } }); const fresh = new DOMParser().parseFromString(await response.text(), "text/html"); for (const id of ["board", "palettes"]) { const next = fresh.getElementById(id); const here = document.getElementById(id); if (next && here) here.replaceWith(next); } wake(); } document.addEventListener("click", async (event) => { const swatch = event.target.closest("[data-palette]"); if (swatch) { event.preventDefault(); const url = new URL(location.href); url.searchParams.set("palette", swatch.dataset.palette); history.replaceState(null, "", url); try { localStorage.setItem("glyph.palette", swatch.dataset.palette); } catch {} await swapIn(url); return; } const copy = event.target.closest(".js-copy"); if (copy) { const said = copy.textContent; try { const svg = fetch(copy.dataset.href).then((r) => r.text()); // Safari keeps the click's permission only for a ClipboardItem given a promise. if (window.ClipboardItem) await navigator.clipboard.write([ new ClipboardItem({ "text/plain": svg.then((t) => new Blob([t], { type: "text/plain" })) }), ]); else await navigator.clipboard.writeText(await svg); copy.textContent = "Copied"; } catch { copy.textContent = "Couldn't copy"; } setTimeout(() => (copy.textContent = said), 1600); return; } const example = event.target.closest("[data-example]"); if (example) { const box = document.getElementById("prompt"); if (box) { box.value = example.dataset.example; box.focus(); } } }); // Enter draws; shift+enter is a new line. document.addEventListener("keydown", (event) => { if (event.target.id === "prompt" && event.key === "Enter" && !event.shiftKey) { event.preventDefault(); event.target.form.requestSubmit(); } }); function wake() { for (const el of document.querySelectorAll(".js-copy, .examples")) el.hidden = false; } // A drawing in progress says how long it has been at it. setInterval(() => { for (const el of document.querySelectorAll("[data-since]")) { const s = Math.max(0, Math.round((Date.now() - Date.parse(el.dataset.since)) / 1000)); el.textContent = `Claude is drawing… ${s}s`; } }, 1000); // live.js swaps the board when a drawing changes: wake what it brought. new MutationObserver(wake).observe(document.body, { childList: true, subtree: true }); // The palette last picked, when the address names none. try { const kept = localStorage.getItem("glyph.palette"); const url = new URL(location.href); if (kept && !url.searchParams.has("palette") && board()) { url.searchParams.set("palette", kept); history.replaceState(null, "", url); swapIn(url); } } catch {} wake();