const API_KEY_STORAGE_KEY = "palico-api-key"; const DISPLAY_NAME_STORAGE_KEY = "palico-display-name"; export function getStoredApiKey() { return localStorage.getItem(API_KEY_STORAGE_KEY) || ""; } export function setStoredApiKey(value) { localStorage.setItem(API_KEY_STORAGE_KEY, value || ""); } export function getStoredDisplayName() { return localStorage.getItem(DISPLAY_NAME_STORAGE_KEY) || ""; } export function setStoredDisplayName(value) { localStorage.setItem(DISPLAY_NAME_STORAGE_KEY, value || ""); } export function createStatusManager(statusElement) { let hideTimeout; function setStatus(message, type = "info", autoHide = true) { if (!statusElement) return; statusElement.textContent = message; statusElement.className = `status status--${type}`; statusElement.hidden = false; if (autoHide) { clearTimeout(hideTimeout); hideTimeout = setTimeout(() => { statusElement.hidden = true; }, 5000); } } function clearStatus() { if (!statusElement) return; statusElement.hidden = true; statusElement.textContent = ""; clearTimeout(hideTimeout); } return { setStatus, clearStatus }; } export function escapeHtml(text = "") { return text .toString() .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'"); } export function formatBytes(bytes = 0) { if (!Number(bytes)) return "0 B"; const units = ["B", "KB", "MB", "GB", "TB"]; const index = Math.floor(Math.log(bytes) / Math.log(1024)); return `${(bytes / Math.pow(1024, index)).toFixed(1)} ${units[index]}`; } export function formatDate(value) { if (!value) return "?"; try { return new Date(value).toLocaleString(); } catch (_) { return value; } } export function createApiClient({ getApiKey, basePath = "" } = {}) { const normalizedBase = (basePath || "").replace(/\/$/, ""); return async function apiFetch(path, options = {}) { const url = path.startsWith("http") ? path : `${normalizedBase}${path.startsWith("/") ? "" : "/"}${path}`; const fetchOptions = { method: options.method || "GET", headers: new Headers(options.headers || {}), }; const apiKey = typeof getApiKey === "function" ? getApiKey() : null; if (apiKey) { fetchOptions.headers.set("Authorization", `Bearer ${apiKey}`); } if (options.body instanceof FormData) { fetchOptions.body = options.body; } else if (options.body) { if (!fetchOptions.headers.has("Content-Type")) { fetchOptions.headers.set("Content-Type", "application/json"); } fetchOptions.body = typeof options.body === "string" ? options.body : JSON.stringify(options.body); } const response = await fetch(url, fetchOptions); const contentType = response.headers.get("content-type") || ""; let payload = null; if (contentType.includes("application/json")) { payload = await response.json(); } else { payload = await response.text(); } if (!response.ok) { const errorMessage = payload?.error || payload?.message || response.statusText; throw new Error(errorMessage); } return payload; }; }