/** Keep one context address unambiguous inside the in-memory lookup table. */ function zoomKey(cmapSlug, contextKey) { return `${cmapSlug}\u0000${contextKey}`; } /** * Represent wiki and user settings for the CMap workspace. * The backend is authoritative; this object only caches the active session state. */ export class CmapSettingsRepository { constructor(api) { if (typeof api !== "function") throw new TypeError("A wiki API function is required"); this.api = api; this.loaded = false; this.startCmapSlug = ""; this.pageGuidesVisible = true; this.zoomLevels = new Map(); } async load() { const result = await this.api("/api/cmap-settings"); this.startCmapSlug = typeof result.startCmapSlug === "string" ? result.startCmapSlug : ""; this.pageGuidesVisible = result.pageGuidesVisible !== false; this.zoomLevels.clear(); for (const entry of (Array.isArray(result.zooms) ? result.zooms : [])) { const zoom = Number(entry.zoomPercent); if (entry.cmapSlug && entry.contextKey && zoom >= 25 && zoom <= 300) { this.zoomLevels.set(zoomKey(entry.cmapSlug, entry.contextKey), zoom); } } this.loaded = true; return this; } zoom(cmapSlug, contextKey) { return this.zoomLevels.get(zoomKey(cmapSlug, contextKey)) || 100; } async setStartCmap(slug) { const result = await this.api("/api/cmap-settings/start", { method: "PUT", body: JSON.stringify({ startCmapSlug: slug || "" }) }); this.startCmapSlug = result.startCmapSlug || ""; return this.startCmapSlug; } async setPageGuidesVisible(visible) { const result = await this.api("/api/cmap-settings/page-guides", { method: "PUT", body: JSON.stringify({ pageGuidesVisible: Boolean(visible) }) }); this.pageGuidesVisible = result.pageGuidesVisible !== false; return this.pageGuidesVisible; } async setZoom(cmapSlug, contextKey, zoomPercent) { if (!cmapSlug) return zoomPercent; const result = await this.api("/api/cmap-settings/zoom", { method: "PUT", body: JSON.stringify({ cmapSlug, contextKey, zoomPercent }) }); const storedZoom = Number(result.zoomPercent); this.zoomLevels.set(zoomKey(cmapSlug, contextKey), storedZoom); return storedZoom; } }