33 lines
1.1 KiB
TypeScript
33 lines
1.1 KiB
TypeScript
/**
|
|
* Artifact download helpers (release polish, 2026-10-07): the tiny
|
|
* save-a-blob seam shared by the demo + figure cards, plus the filename
|
|
* slug. The slug is pure (test-pinned); downloadFile touches the DOM and
|
|
* only runs on click.
|
|
*/
|
|
|
|
/** Slugify a demo title into a filename stem — lowercase, accents folded,
|
|
* runs of non-alphanumerics collapsed to "-", trimmed, capped at 64
|
|
* chars. Empty/fully-symbolic titles fall back to "demo". */
|
|
export function slugFilename(title: string): string {
|
|
const slug = title
|
|
.toLowerCase()
|
|
.normalize("NFKD")
|
|
.replace(/[\u0300-\u036f]/g, "")
|
|
.replace(/[^\p{L}\p{N}]+/gu, "-")
|
|
.replace(/^-+|-+$/g, "")
|
|
.slice(0, 64);
|
|
return slug || "demo";
|
|
}
|
|
|
|
/** Save text as a downloaded file. The blob URL is revoked right after the
|
|
* click — Chromium keeps the download alive past the revoke. */
|
|
export function downloadFile(name: string, text: string, mime: string): void {
|
|
const blob = new Blob([text], { type: mime });
|
|
const url = URL.createObjectURL(blob);
|
|
const a = document.createElement("a");
|
|
a.href = url;
|
|
a.download = name;
|
|
a.click();
|
|
URL.revokeObjectURL(url);
|
|
}
|