Files
stork/static/stork-common.js
ilia b91ba3490c
CI / secret-scan (pull_request) Successful in 32s
CI / python-ci (pull_request) Successful in 50s
Add shareable multi-board links with paper UI and voice notes.
Boards use /b/<uid> with no invite password; restore renamable columns,
recordings, and seeded locale notes, with first column locked.
2026-08-07 14:47:28 -04:00

97 lines
2.8 KiB
JavaScript

/** Shared helpers for Stork v1 + v2. */
(function (global) {
function formatError(detail) {
if (detail == null || detail === "") return "Something went wrong";
if (typeof detail === "string") {
try {
const parsed = JSON.parse(detail);
return formatError(parsed);
} catch {
return detail;
}
}
if (Array.isArray(detail)) {
return detail
.map((item) => {
if (!item || typeof item !== "object") return String(item);
const loc = Array.isArray(item.loc) ? item.loc.filter((x) => x !== "body").join(".") : "";
const msg = item.msg || "Invalid value";
if (loc === "spelling" || (item.loc && item.loc.includes("spelling"))) {
if (String(msg).toLowerCase().includes("at least 1")) return "Enter a name spelling";
return `Spelling: ${msg}`;
}
return loc ? `${loc}: ${msg}` : msg;
})
.join(". ");
}
if (typeof detail === "object" && detail.msg) return String(detail.msg);
return String(detail);
}
function showError(el, detail) {
if (!el) return;
el.textContent = formatError(detail);
el.classList.remove("hidden");
}
function clearError(el) {
if (!el) return;
el.textContent = "";
el.classList.add("hidden");
}
const THEME_KEY = "stork_theme";
function systemTheme() {
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
}
function currentTheme() {
return localStorage.getItem(THEME_KEY) || "system";
}
function resolvedTheme() {
const t = currentTheme();
return t === "system" ? systemTheme() : t;
}
function applyTheme() {
const resolved = resolvedTheme();
document.documentElement.setAttribute("data-theme", resolved);
document.documentElement.style.colorScheme = resolved;
document.querySelectorAll("[data-theme-toggle]").forEach((btn) => {
const mode = btn.getAttribute("data-theme-toggle");
const active = currentTheme() === mode;
btn.setAttribute("aria-pressed", active ? "true" : "false");
btn.classList.toggle("active", active);
});
}
function setTheme(mode) {
if (mode === "system") localStorage.removeItem(THEME_KEY);
else localStorage.setItem(THEME_KEY, mode);
applyTheme();
}
function initThemeControls() {
applyTheme();
document.querySelectorAll("[data-theme-toggle]").forEach((btn) => {
btn.addEventListener("click", () => setTheme(btn.getAttribute("data-theme-toggle")));
});
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", () => {
if (currentTheme() === "system") applyTheme();
});
}
global.StorkUI = {
formatError,
showError,
clearError,
applyTheme,
setTheme,
initThemeControls,
currentTheme,
resolvedTheme,
};
})(window);