Source
This app’s code and revision history are public. Download a copy or explore the files below.
Public
The app’s code and revision history are public.
Run it yourself
Take the whole tree and run it on your own machine. The download needs no account. Cloning needs a sign-in.
Download .zip
bounded clone cf614aedcf4cdabfa9c45095
A copy you download is not bounded. The guarantees this app runs under - declared egress, proven invariants, writes that fail closed - live in the runtime serving this page, not in the files. What you get is auditable source you can read and run, with no runtime guarantees attached.
src/ui.tsx
// Small shared pieces on the kit: dialog (a sheet on phones), confirm, menu, toast, copy, share.
import { useEffect, useId, useRef, useState, type ReactNode } from "react";
const FOCUSABLE = "a[href], button:not(:disabled), input:not(:disabled), select:not(:disabled), textarea:not(:disabled), [tabindex]:not([tabindex='-1'])";
/** A dialog that animates out before it unmounts. footer may be a function that gets close(). */
export function Dialog({ title, onClose, children, footer }: {
title: string; onClose: () => void; children: ReactNode; footer?: ReactNode | ((close: () => void) => ReactNode);
}) {
const ref = useRef<HTMLDivElement>(null);
const titleId = useId();
const [leaving, setLeaving] = useState(false);
const close = () => setLeaving(true);
useEffect(() => {
const prev = document.activeElement as HTMLElement | null;
const onKey = (e: KeyboardEvent) => {
// A confirm can open over another dialog: only the top one answers keys.
const dialogs = document.querySelectorAll(".dialog");
if (!ref.current || dialogs[dialogs.length - 1] !== ref.current) return;
if (e.key === "Escape") { e.preventDefault(); setLeaving(true); }
if (e.key !== "Tab") return;
const items = [...ref.current.querySelectorAll<HTMLElement>(FOCUSABLE)];
if (!items.length) return;
const first = items[0], last = items[items.length - 1];
if (e.shiftKey && document.activeElement === first) { e.preventDefault(); last.focus(); }
else if (!e.shiftKey && document.activeElement === last) { e.preventDefault(); first.focus(); }
};
document.addEventListener("keydown", onKey);
document.body.style.overflow = "hidden";
(ref.current?.querySelector<HTMLElement>("[autofocus]") || ref.current)?.focus();
return () => {
document.removeEventListener("keydown", onKey);
if (!document.querySelector(".dialog")) document.body.style.overflow = "";
prev?.focus?.();
};
}, []);
return (
<div className={`dialog-backdrop${leaving ? " leaving" : ""}`} onMouseDown={(e) => { if (e.target === e.currentTarget) close(); }}
onAnimationEnd={(e) => { if (leaving && e.target === ref.current) onClose(); }}>
<div className="dialog" role="dialog" aria-modal="true" aria-labelledby={titleId} ref={ref} tabIndex={-1}>
<div className="dialog-head">
<h2 className="dialog-title" id={titleId}>{title}</h2>
<button className="btn btn-quiet btn-icon btn-sm" type="button" onClick={close} aria-label="Close">
<svg width="18" height="18" viewBox="0 0 20 20" aria-hidden="true"><path d="M5 5l10 10M15 5 5 15" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" /></svg>
</button>
</div>
<div className="dialog-body">{children}</div>
{footer && <div className="dialog-foot">{typeof footer === "function" ? footer(close) : footer}</div>}
</div>
</div>
);
}
interface Ask { title: string; body: string; action: string; danger?: boolean; resolve: (ok: boolean) => void }
let pushAsk: ((a: Ask) => void) | null = null;
/** Asks before something that cannot be undone or that changes what people see. Resolves true on the action. */
export function confirmAction(a: Omit<Ask, "resolve">): Promise<boolean> {
return new Promise((resolve) => (pushAsk ? pushAsk({ ...a, resolve }) : resolve(window.confirm(a.body))));
}
function ConfirmHost() {
const [ask, setAsk] = useState<Ask | null>(null);
const answer = useRef(false);
useEffect(() => { pushAsk = (a) => { answer.current = false; setAsk(a); }; return () => { pushAsk = null; }; }, []);
if (!ask) return null;
return (
<Dialog title={ask.title} onClose={() => { ask.resolve(answer.current); setAsk(null); }}
footer={(close) => <>
<button className="btn btn-line" type="button" onClick={close}>Cancel</button>
<button className={`btn${ask.danger ? " btn-danger-solid" : ""}`} type="button" autoFocus
onClick={() => { answer.current = true; close(); }}>{ask.action}</button>
</>}>
<p className="muted">{ask.body}</p>
</Dialog>
);
}
/** A menu under a "More" button. Picking an item closes it. */
export function Menu({ label, children }: { label: string; children: ReactNode }) {
const [open, setOpen] = useState(false);
const [leaving, setLeaving] = useState(false);
const ref = useRef<HTMLDivElement>(null);
const button = useRef<HTMLButtonElement>(null);
const shut = () => { if (open) setLeaving(true); };
useEffect(() => {
if (!open || leaving) return;
ref.current?.querySelector<HTMLElement>("[role=menuitem]:not(:disabled)")?.focus();
const onDown = (e: PointerEvent) => { if (!ref.current?.contains(e.target as Node)) setLeaving(true); };
const onKey = (e: KeyboardEvent) => {
if (e.key === "Escape") { setLeaving(true); button.current?.focus(); return; }
if (e.key !== "ArrowDown" && e.key !== "ArrowUp") return;
e.preventDefault();
const items = [...(ref.current?.querySelectorAll<HTMLElement>("[role=menuitem]:not(:disabled)") ?? [])];
const i = items.indexOf(document.activeElement as HTMLElement);
items[(i + (e.key === "ArrowDown" ? 1 : items.length - 1)) % items.length]?.focus();
};
document.addEventListener("pointerdown", onDown);
document.addEventListener("keydown", onKey);
return () => { document.removeEventListener("pointerdown", onDown); document.removeEventListener("keydown", onKey); };
}, [open, leaving]);
return (
<div className="dropdown" ref={ref}>
<button ref={button} className="btn btn-line btn-icon" type="button" aria-haspopup="menu" aria-expanded={open && !leaving} aria-label={label} title={label}
onClick={() => (open ? shut() : setOpen(true))}>
<svg width="18" height="18" viewBox="0 0 20 20" aria-hidden="true"><circle cx="4.5" cy="10" r="1.6" fill="currentColor" /><circle cx="10" cy="10" r="1.6" fill="currentColor" /><circle cx="15.5" cy="10" r="1.6" fill="currentColor" /></svg>
</button>
{open && (
<div className={`dropdown-menu dropdown-menu-end${leaving ? " leaving" : ""}`} role="menu" aria-label={label}
onClick={(e) => { if ((e.target as Element).closest("[role=menuitem]")) setLeaving(true); }}
onAnimationEnd={() => { if (leaving) { setOpen(false); setLeaving(false); } }}>
{children}
</div>
)}
</div>
);
}
let pushToast: ((text: string) => void) | null = null;
export function toast(text: string) { pushToast?.(text); }
function Toaster() {
const [t, setT] = useState<{ text: string; n: number; leaving: boolean } | null>(null);
useEffect(() => {
let timer: number | undefined;
pushToast = (text) => {
setT((prev) => ({ text, n: (prev?.n ?? 0) + 1, leaving: false }));
window.clearTimeout(timer);
timer = window.setTimeout(() => setT((prev) => prev && { ...prev, leaving: true }), 2600);
};
return () => { pushToast = null; window.clearTimeout(timer); };
}, []);
return (
<div aria-live="polite">
{t && <div key={t.n} className={`toast${t.leaving ? " leaving" : ""}`} onAnimationEnd={() => { if (t.leaving) setT(null); }}>{t.text}</div>}
</div>
);
}
/** Toasts and confirm dialogs, mounted once. */
export function Overlays() {
return <><Toaster /><ConfirmHost /></>;
}
export async function copy(text: string, done = "Copied") {
try {
await navigator.clipboard.writeText(text);
toast(done);
} catch {
window.prompt("Copy this:", text);
}
}
export async function share(url: string, title: string) {
if (navigator.share && matchMedia("(pointer: coarse)").matches) {
try { await navigator.share({ title, url }); return; } catch { /* cancelled */ }
}
await copy(url, "Link copied");
}
/** True while the media query matches, for layouts that differ in structure (not just style) on phones. */
export function useMedia(query: string): boolean {
const [on, setOn] = useState(() => matchMedia(query).matches);
useEffect(() => {
const m = matchMedia(query);
const update = () => setOn(m.matches);
m.addEventListener("change", update);
return () => m.removeEventListener("change", update);
}, [query]);
return on;
}
Change history
Quality pass: conditional questions, 0 to 10 scale, QR code, prefill links, results tabs, API docs
Bounded source sync
Bounded source sync
Bounded source sync
Bounded source sync
Bounded source sync