shared.ts
Raw
1export function humanFileSize(size: number) {
2 if (size < 1024) return `${size} B`;
3 const i = Math.min(4, Math.floor(Math.log(size) / Math.log(1024)));
4 return `${(size / 1024 ** i).toFixed(2)} ${["B", "KiB", "MiB", "GiB", "TiB"][i]}`;
5}
6
7// TextDecoder-based UTF-8 check that works in both Bun and the browser.
8export function isValidUTF8(buf: ArrayBuffer | ArrayBufferView): boolean {
9 try {
10 new TextDecoder("utf-8", { fatal: true }).decode(buf);
11 return true;
12 } catch (_e) {
13 return false;
14 }
15}
16
17const MODE_STORAGE_KEY = "zbin-mode";
18
19// Enables the (HTML-disabled) client-side radio now that JS is running, applies
20// the saved client/server preference (defaulting to client since JS is here),
21// and persists changes. Shared by the index (encrypt_mode) and show (decrypt_mode) pages.
22export function setupModeRadios(name: string) {
23 const radios = document.querySelectorAll<HTMLInputElement>(
24 `input[name="${name}"]`,
25 );
26 if (radios.length === 0) return;
27 const saved = localStorage.getItem(MODE_STORAGE_KEY) ?? "client";
28 for (const radio of radios) {
29 radio.disabled = false;
30 radio.checked = radio.value === saved;
31 radio.addEventListener("change", () => {
32 if (radio.checked) localStorage.setItem(MODE_STORAGE_KEY, radio.value);
33 });
34 }
35}
36
37export function getMode(name: string): string {
38 const checked = document.querySelector<HTMLInputElement>(
39 `input[name="${name}"]:checked`,
40 );
41 return checked?.value ?? "server";
42}
43