pike/glyph Public Public: anyone can read this; nobody can change it.

forked from apps/glyph

workbenchthis machine
Files draw
1
// What only a script can add to the page; without it the page still draws,
2
// saves and switches palettes by link.
3
//   - Copy: the SVG, in the palette on screen, onto the clipboard.
4
//   - Palettes swap in place (the address keeps the choice, so a live
5
//     refresh and a reload render the same one).
6
//   - The example prompts fill the box; a drawing in progress counts up.
7
8
const board = () => document.getElementById("board");
9
10
async function swapIn(url) {
11
  const response = await fetch(url, { headers: { accept: "text/html" } });
12
  const fresh = new DOMParser().parseFromString(await response.text(), "text/html");
13
  for (const id of ["board", "palettes"]) {
14
    const next = fresh.getElementById(id);
15
    const here = document.getElementById(id);
16
    if (next && here) here.replaceWith(next);
17
  }
18
  wake();
19
}
20
21
document.addEventListener("click", async (event) => {
22
  const swatch = event.target.closest("[data-palette]");
23
  if (swatch) {
24
    event.preventDefault();
25
    const url = new URL(location.href);
26
    url.searchParams.set("palette", swatch.dataset.palette);
27
    history.replaceState(null, "", url);
28
    try {
29
      localStorage.setItem("glyph.palette", swatch.dataset.palette);
30
    } catch {}
31
    await swapIn(url);
32
    return;
33
  }
34
  const copy = event.target.closest(".js-copy");
35
  if (copy) {
36
    const said = copy.textContent;
37
    try {
38
      const svg = fetch(copy.dataset.href).then((r) => r.text());
39
      // Safari keeps the click's permission only for a ClipboardItem given a promise.
40
      if (window.ClipboardItem)
41
        await navigator.clipboard.write([
42
          new ClipboardItem({ "text/plain": svg.then((t) => new Blob([t], { type: "text/plain" })) }),
43
        ]);
44
      else await navigator.clipboard.writeText(await svg);
45
      copy.textContent = "Copied";
46
    } catch {
47
      copy.textContent = "Couldn't copy";
48
    }
49
    setTimeout(() => (copy.textContent = said), 1600);
50
    return;
51
  }
52
  const example = event.target.closest("[data-example]");
53
  if (example) {
54
    const box = document.getElementById("prompt");
55
    if (box) {
56
      box.value = example.dataset.example;
57
      box.focus();
58
    }
59
  }
60
});
61
62
// Enter draws; shift+enter is a new line.
63
document.addEventListener("keydown", (event) => {
64
  if (event.target.id === "prompt" && event.key === "Enter" && !event.shiftKey) {
65
    event.preventDefault();
66
    event.target.form.requestSubmit();
67
  }
68
});
69
70
function wake() {
71
  for (const el of document.querySelectorAll(".js-copy, .examples")) el.hidden = false;
72
}
73
74
// A drawing in progress says how long it has been at it.
75
setInterval(() => {
76
  for (const el of document.querySelectorAll("[data-since]")) {
77
    const s = Math.max(0, Math.round((Date.now() - Date.parse(el.dataset.since)) / 1000));
78
    el.textContent = `Claude is drawing… ${s}s`;
79
  }
80
}, 1000);
81
82
// live.js swaps the board when a drawing changes: wake what it brought.
83
new MutationObserver(wake).observe(document.body, { childList: true, subtree: true });
84
85
// The palette last picked, when the address names none.
86
try {
87
  const kept = localStorage.getItem("glyph.palette");
88
  const url = new URL(location.href);
89
  if (kept && !url.searchParams.has("palette") && board()) {
90
    url.searchParams.set("palette", kept);
91
    history.replaceState(null, "", url);
92
    swapIn(url);
93
  }
94
} catch {}
95
wake();
↑↓ navigate ↵ open esc close
↑↓ navigate ↵ open esc close

Keyboard shortcuts