import { DEFAULT_SETTINGS, OPEN_IN_VALUES } from "./defaults.js"; export function normalizeLanguage(value) { if (typeof value !== "string") return ""; const trimmed = value.trim(); return [...trimmed].length <= 100 && !/[\r\n\x00-\x1f]/u.test(trimmed) ? trimmed : ""; } // Pure. Layers stored values over the defaults, dropping unknown keys and // falling back on wrong types or out-of-range values. A fresh or signed-out // profile returns {} from storage, which yields the defaults. export function mergeSettings(stored) { const merged = { ...DEFAULT_SETTINGS }; if (stored === null || typeof stored !== "object") { return merged; } merged.contextMenuMode = ["off", "simple", "advanced"].includes(stored.contextMenuMode) ? stored.contextMenuMode : stored.showContextMenu === false ? "off" : "simple"; if (typeof stored.showTranslate === "boolean") merged.showTranslate = stored.showTranslate; if (["ask", "preferred"].includes(stored.translationMode)) merged.translationMode = stored.translationMode; merged.translationLanguage = normalizeLanguage(stored.translationLanguage); if (typeof stored.temporaryChat === "boolean") { merged.temporaryChat = stored.temporaryChat; } if (typeof stored.webSearch === "boolean") { merged.webSearch = stored.webSearch; } if (OPEN_IN_VALUES.includes(stored.openIn)) { merged.openIn = stored.openIn; } if (typeof stored.promptTemplate === "string") { merged.promptTemplate = stored.promptTemplate; } return merged; } // storage.sync works with no Mozilla account (it behaves as local storage and // syncs later if the user signs in), so no capability probe is needed. Any // rejection falls back to defaults rather than blocking the user's action. // Firefox sanitises all non-quota backend errors to a single generic string, so // there is nothing useful to branch on. export async function load() { try { const stored = await browser.storage.sync.get([...Object.keys(DEFAULT_SETTINGS), "showContextMenu"]); return mergeSettings(stored); } catch { return mergeSettings(null); } } export async function save(partial) { const normalized = mergeSettings(partial); const patch = {}; if (partial && typeof partial === "object") { for (const key of Object.keys(DEFAULT_SETTINGS)) { if (Object.hasOwn(partial, key)) patch[key] = normalized[key]; } if (Object.hasOwn(partial, "showContextMenu")) patch.contextMenuMode = normalized.contextMenuMode; } await browser.storage.sync.set(patch); }