(function (root, factory) { if (typeof module === 'object' && module.exports) module.exports = factory; else { root.createSignOutReview = factory; root.createSignOutReview.mount = boundary => { const document = root.document; const review = factory({ sheet: document.getElementById('sign-out-review-sheet'), heading: document.getElementById('sign-out-review-heading'), summary: document.getElementById('sign-out-review-summary'), warning: document.getElementById('sign-out-review-warning'), cancelButton: document.getElementById('cancel-sign-out'), confirmButton: document.getElementById('confirm-sign-out'), escapeTarget: document, historyTarget: root, history: root.history, backgroundTargets: Array.from(document.querySelectorAll('body > :not(#sign-out-review-sheet)')), getActiveElement: () => document.activeElement, localStorage: root.localStorage, sessionStorage: root.sessionStorage, privateDatabases: root.stackchainPrivateDatabases, inspectPrivateDatabases: root.inspectStackchainPrivateDatabases, onConfirm: mode => mode === 'all' ? boundary.signOutAllDevices({ reviewed: true }) : boundary.signOut(), }); review.start(); return review; }; } })(typeof globalThis !== 'undefined' ? globalThis : this, function createSignOutReview(options) { let mode = 'current'; let launcher = null; let open = false; let historyEntry = false; function ownedItemCount(storage) { if (!storage) return 0; let count = 0; for (let index = 0; index < storage.length; index += 1) { if (storage.key(index)?.startsWith('stackchain.')) count += 1; } return count; } async function show(nextMode, source) { mode = nextMode; launcher = source; const itemCount = ownedItemCount(options.localStorage) + (options.sessionStorage === options.localStorage ? 0 : ownedItemCount(options.sessionStorage)); let inventory = { recordCount: 0, unavailable: true }; try { inventory = await options.inspectPrivateDatabases?.(options.privateDatabases) || inventory; } catch (_error) { /* Unknown private work must use the guarded confirmation path. */ } const recordCount = Math.max(0, Number(inventory.recordCount) || 0); options.heading.textContent = mode === 'all' ? 'Review sign out on every device' : 'Review sign out'; options.summary.textContent = inventory.unavailable ? `This device has ${itemCount} private browser item${itemCount === 1 ? '' : 's'}; private work status is unknown.` : `This device has ${itemCount} private browser item${itemCount === 1 ? '' : 's'} and ${recordCount} private work record${recordCount === 1 ? '' : 's'}.`; const atRisk = itemCount > 0 || recordCount > 0 || inventory.unavailable; if (mode === 'all') { options.warning.textContent = atRisk ? 'Every device will need to sign in again. Private drafts or queued work on this device may not be synced and will be erased.' : 'Every device will need to sign in again. This device will be cleared after its session is revoked.'; } else { options.warning.textContent = atRisk ? 'Private drafts or queued work may not be synced. Signing out erases them from this device.' : 'Signing out clears this device after the session is revoked.'; } options.confirmButton.textContent = atRisk ? 'Sign out and erase private work' : (mode === 'all' ? 'Sign out all devices' : 'Sign out'); options.sheet.hidden = false; (options.backgroundTargets || []).forEach(target => { target.inert = true; }); open = true; options.history?.pushState?.({ stackchainSignOutReview: true }, ''); historyEntry = Boolean(options.history?.back); options.cancelButton.focus(); } function close({ restoreFocus = true } = {}) { if (!open) return; options.sheet.hidden = true; (options.backgroundTargets || []).forEach(target => { target.inert = false; }); open = false; if (restoreFocus) launcher?.focus?.(); } function dismiss() { if (historyEntry) { historyEntry = false; options.history.back(); } else close(); } async function confirm() { options.confirmButton.disabled = true; try { await options.onConfirm(mode); close({ restoreFocus: false }); } catch (_error) { options.warning.textContent = 'Sign out could not finish clearing private work. Close other Stackchain tabs, then retry.'; } finally { options.confirmButton.disabled = false; } } function start() { options.launcher?.addEventListener('click', event => show('current', event.currentTarget || options.launcher)); options.allLauncher?.addEventListener('click', event => show('all', event.currentTarget || options.allLauncher)); options.cancelButton?.addEventListener('click', dismiss); options.confirmButton?.addEventListener('click', confirm); options.escapeTarget?.addEventListener('keydown', event => { if (!open) return; if (event.key === 'Escape') { event.preventDefault(); dismiss(); return; } if (event.key !== 'Tab') return; const focusable = Array.from(options.sheet.querySelectorAll('button:not([disabled])')); const first = focusable[0]; const last = focusable[focusable.length - 1]; const active = options.getActiveElement?.(); if (!event.shiftKey && active === last) { event.preventDefault(); first?.focus(); } if (event.shiftKey && active === first) { event.preventDefault(); last?.focus(); } }); options.historyTarget?.addEventListener('popstate', () => { historyEntry = false; close(); }); } return { start, show, close }; });