website

#astro#js#html#css

git clone https://git.pyrossh.dev/website

木 Personal website of pyrossh. Built with astrojs, shiki, vite.


assets/client.js
60e980e 1
// All client-side JS the site ships, in one plain vanilla file — no
60e980e 2
// bundler needed for two small, independent event listeners.
60e980e 3
ada6e53 4
// Wires up the theme button rendered as static markup in site-header.njk
ada6e53 5
// (no web component, no framework — a stateless click-to-toggle button
ada6e53 6
// doesn't need one). Loaded with `defer` so this runs after the DOM is
ada6e53 7
// parsed and #theme-change already exists.
ada6e53 8
document.getElementById("theme-change").addEventListener("click", () => {
ada6e53 9
  const root = document.documentElement;
ada6e53 10
  // data-theme is tri-state: absent means "follow the OS preference" (see
ada6e53 11
  // `color-scheme: light dark` in shared.css). Resolve against that OS
ada6e53 12
  // preference when no explicit value is set yet, so the first click
ada6e53 13
  // always flips to the opposite of what's *currently shown* rather than
ada6e53 14
  // assuming light.
ada6e53 15
  const currentTheme = root.getAttribute("data-theme");
ada6e53 16
  const isDark = currentTheme ? currentTheme === "dark" : matchMedia("(prefers-color-scheme: dark)").matches;
ada6e53 17
  const next = isDark ? "light" : "dark";
ada6e53 18
  root.setAttribute("data-theme", next);
ada6e53 19
  localStorage.setItem("theme", next);
ada6e53 20
});
60e980e 21
60e980e 22
// .file-explorer-tree lives outside #file-pane (see the comment on
60e980e 23
// .file-explorer in repos.css) so htmx swaps of #file-pane never re-render
60e980e 24
// it — clicking a file never rebuilds the tree. That's good for keeping
60e980e 25
// scroll position/expanded folders, but it also means the .active-file
60e980e 26
// class painted at page-load time never moves on its own. Move it here,
60e980e 27
// synchronously on click, instead of waiting on htmx's response.
60e980e 28
document.addEventListener("click", (e) => {
60e980e 29
  const link = e.target.closest(".file-explorer-tree a");
60e980e 30
  if (!link) return;
60e980e 31
  const tree = link.closest(".file-explorer-tree");
60e980e 32
  const current = tree.querySelector("a.active-file");
60e980e 33
  if (current) current.classList.remove("active-file");
60e980e 34
  link.classList.add("active-file");
60e980e 35
});
8be9c52 36
8be9c52 37
// Copies the git clone command shown in the repo header (see .clone-cmd in
8be9c52 38
// repos.css) and flips the button to a checkmark for a bit as feedback,
8be9c52 39
// instead of a tooltip/toast that would need its own positioning logic.
8be9c52 40
document.addEventListener("click", (e) => {
8be9c52 41
  const btn = e.target.closest(".copy-btn");
8be9c52 42
  if (!btn) return;
8be9c52 43
  navigator.clipboard.writeText(btn.dataset.clone).then(() => {
8be9c52 44
    clearTimeout(btn._copyResetTimer);
8be9c52 45
    btn.classList.add("copied");
8be9c52 46
    btn._copyResetTimer = setTimeout(() => btn.classList.remove("copied"), 1500);
8be9c52 47
  });
8be9c52 48
});