const WIZARD_SESSION_PATTERN = /^[A-Za-z0-9_-]{43}$/u; const WIZARD_ROUTES = new Set(["/", "/assistant", "/local", "/supergrok-vps", "/claude-vps", "/local-model-vps", "/hosting"]); const fragmentSessionWindows = new WeakSet(); const pendingBrowserTransfer = globalThis.__relmioWizardSessionReady; if (pendingBrowserTransfer && typeof pendingBrowserTransfer.then === "function") { try { await pendingBrowserTransfer; } catch { /* The bootstrap fails closed. */ } try { delete globalThis.__relmioWizardSessionReady; } catch { globalThis.__relmioWizardSessionReady = null; } } function validWizardSession(value) { return typeof value === "string" && WIZARD_SESSION_PATTERN.test(value); } function cleanWizardLocation(location) { return `${location.pathname}${location.hash ?? ""}`; } function preserveFragmentSession(browserWindow, session) { if ( !validWizardSession(session) || !browserWindow || (typeof browserWindow !== "object" && typeof browserWindow !== "function") || typeof browserWindow.addEventListener !== "function" || fragmentSessionWindows.has(browserWindow) ) return; fragmentSessionWindows.add(browserWindow); const documentPath = browserWindow.location.pathname; // Navigation carries the private capability through history before reloading. // Back/Forward can restore that same-document entry with the old route's DOM. browserWindow.addEventListener("popstate", () => { if (browserWindow.location.pathname !== documentPath) { browserWindow.location.reload(); } }); browserWindow.addEventListener("pageshow", (event) => { if (event.persisted) browserWindow.location.reload(); }); browserWindow.addEventListener("hashchange", () => { browserWindow.history.replaceState( { relmioWizardSession: session }, "", cleanWizardLocation(browserWindow.location), ); }); } export function readWizardSession(browserWindow = window) { const { history, location } = browserWindow; const historySession = validWizardSession( history.state?.relmioWizardSession, ) ? history.state.relmioWizardSession : null; const session = historySession; const cleanLocation = cleanWizardLocation(location); history.replaceState( session ? { relmioWizardSession: session } : null, "", cleanLocation, ); preserveFragmentSession(browserWindow, session); return session; } export function bindWizardNavigation( link, path, session, browserWindow = window, ) { if (!link?.setAttribute || !link?.addEventListener) { throw new TypeError("A wizard navigation link is required."); } if (!WIZARD_ROUTES.has(path)) { throw new TypeError("The wizard navigation route is invalid."); } link.setAttribute("href", path); link.addEventListener("click", (event) => { const target = link.getAttribute?.("target") || link.target; const modified = event.button !== 0 || event.altKey || event.ctrlKey || event.metaKey || event.shiftKey; if ( event.defaultPrevented || modified || (target && target.toLowerCase() !== "_self") || !validWizardSession(session) ) { return; } event.preventDefault(); browserWindow.history.pushState( { relmioWizardSession: session }, "", path, ); browserWindow.location.reload(); }); } // The failure a wizard API reported, with the fields error help reads. `recovery` // is left out on purpose: that table is ChatGPT sign-in advice. export function wizardApiError(result, status, fallback = "The operation could not be completed.") { const error = new Error(typeof result?.error === "string" && result.error ? result.error : fallback); error.status = status; for (const key of ["code", "param"]) { if (typeof result?.[key] === "string" && result[key] && result[key].length <= 128) error[key] = result[key]; } for (const flag of ["remoteOutcomeUnknown", "retryBlocked"]) { if (result?.[flag] === true) error[flag] = true; } return error; } const copiedTimers = new WeakMap(); // Copy feedback: marks the button with data-copied for a moment, and the kit // shows a check. The accessible name never changes; the caller announces the // copy through its polite status line. export function flashCopied(button) { button.toggleAttribute("data-copied", true); clearTimeout(copiedTimers.get(button)); copiedTimers.set(button, setTimeout(() => button.removeAttribute("data-copied"), 1_800)); } // Runs a poll tick now, or once when the tab is shown again if it is hidden, // so a background tab sends no requests. export function whenVisible(tick, page = document) { if (!page.hidden) { tick(); return; } page.addEventListener("visibilitychange", function shown() { if (page.hidden) return; page.removeEventListener("visibilitychange", shown); tick(); }); }