accessify-widget 0.3.100 → 0.3.102
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/accessify.min.js +1 -1
- package/dist/accessify.min.js.map +1 -1
- package/dist/accessify.mjs +1 -1
- package/dist/{index-BCnhsRbl.js → index-CyZv4-2d.js} +4 -4
- package/dist/{index-BCnhsRbl.js.map → index-CyZv4-2d.js.map} +1 -1
- package/dist/{keyboard-nav-yfjbY6i4.js → keyboard-nav-BYl36wkS.js} +2 -2
- package/dist/{keyboard-nav-yfjbY6i4.js.map → keyboard-nav-BYl36wkS.js.map} +1 -1
- package/dist/{page-structure-sIAqugcR.js → page-structure-DkNwkCP-.js} +2 -2
- package/dist/{page-structure-sIAqugcR.js.map → page-structure-DkNwkCP-.js.map} +1 -1
- package/dist/{text-simplify-QxPVLWca.js → text-simplify-YuRRlyGQ.js} +117 -1
- package/dist/text-simplify-YuRRlyGQ.js.map +1 -0
- package/dist/widget.js +1 -1
- package/dist/widget.js.map +1 -1
- package/package.json +1 -1
- package/dist/text-simplify-QxPVLWca.js.map +0 -1
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"text-simplify-QxPVLWca.js","sources":["../src/features/text-simplify-helpers.ts","../src/features/text-simplify.ts"],"sourcesContent":["// ---------------------------------------------------------------------------\n// Pure helpers for the Text Simplifier matching / safety / stale logic.\n//\n// These are stateless utilities so they live outside the feature factory and\n// can be unit-tested in isolation. They back three fixes that landed in the\n// matching/safety/stale batch:\n//\n// 1. Token similarity — detects \"same block, changed text\" when a manifest\n// block's CSS selector fails to match the live DOM (legacy b-xxx\n// selectors, Framer-specific selectors, …).\n// 2. Content anchors — ancestors of manifest-applied elements form the\n// content region; anything outside all anchors is treated as\n// boilerplate rather than content.\n// 3. Obvious boilerplate — nav/menu landmarks that should always be\n// skipped, independent of anchors.\n// ---------------------------------------------------------------------------\n\n/** Extract content tokens (lowercased, ≥3 chars). */\nexport function tokenize(s: string): Set<string> {\n const tokens = new Set<string>();\n const parts = s\n .toLowerCase()\n .replace(/[^\\p{L}\\p{N}\\s]/gu, ' ')\n .split(/\\s+/);\n for (const t of parts) {\n if (t.length >= 3) tokens.add(t);\n }\n return tokens;\n}\n\n/** Jaccard similarity between two token sets. 1.0 = identical, 0.0 = disjoint. */\nexport function tokenSimilarity(a: Set<string>, b: Set<string>): number {\n if (a.size === 0 || b.size === 0) return 0;\n let inter = 0;\n for (const t of a) if (b.has(t)) inter++;\n const uni = a.size + b.size - inter;\n return uni === 0 ? 0 : inter / uni;\n}\n\n/** Minimum token-Jaccard for two blocks to be considered \"same block, stale\". */\nexport const STALE_SIMILARITY_THRESHOLD = 0.4;\n\n/** How many ancestor levels up to consider when anchoring content regions. */\nexport const CONTENT_ANCHOR_DEPTH = 4;\n\n/** Check whether an element is a whole-page ancestor we must never anchor on.\n * Anchoring <body> or <html> would flag every element on the page as\n * \"near content\" — which defeats the whole boilerplate filter. */\nfunction isWholePageAncestor(a: HTMLElement): boolean {\n const tag = a.tagName;\n return tag === 'BODY' || tag === 'HTML';\n}\n\n/** Build the set of ancestor containers of manifest-applied elements. */\nexport function buildContentAnchors(applied: HTMLElement[]): Set<HTMLElement> {\n const anchors = new Set<HTMLElement>();\n for (const el of applied) {\n let a: HTMLElement | null = el;\n for (let i = 0; i < CONTENT_ANCHOR_DEPTH && a; i++) {\n if (isWholePageAncestor(a)) break;\n anchors.add(a);\n a = a.parentElement;\n }\n }\n return anchors;\n}\n\n/** True if `el` sits within (or near) any element that has a manifest match. */\nexport function isNearContent(el: HTMLElement, anchors: Set<HTMLElement>): boolean {\n let a: HTMLElement | null = el;\n for (let i = 0; i < CONTENT_ANCHOR_DEPTH && a; i++) {\n if (isWholePageAncestor(a)) return false;\n if (anchors.has(a)) return true;\n a = a.parentElement;\n }\n return false;\n}\n\n/** True if `el` is inside obvious structural boilerplate (nav/menu).\n * Footer/aside intentionally NOT included: Framer & similar frameworks\n * misuse these tags for real content, so the content-anchor heuristic\n * must be authoritative there. */\nexport function isObviousBoilerplate(el: HTMLElement): boolean {\n return !!el.closest(\n 'nav, [role=\"navigation\"], [role=\"menu\"], [role=\"menubar\"], [role=\"menuitem\"]',\n );\n}\n","// ---------------------------------------------------------------------------\n// Accessify – Text Simplification Feature (Safe Replace Engine)\n// ---------------------------------------------------------------------------\n// Phase 1: Manifest-based precomputed simplification\n// 1. Fetch manifest from /v1/manifest for current page\n// 2. Apply cached variants instantly via leaf-node replacement\n// 3. Live-fallback only for blocks not in manifest\n// Phase 2: Layout-safe replacement with guards + observers\n// ---------------------------------------------------------------------------\n\nimport type { FeatureModule, FeatureState, AIService } from '../types';\nimport {\n tokenize,\n tokenSimilarity,\n STALE_SIMILARITY_THRESHOLD,\n buildContentAnchors,\n isNearContent,\n isObviousBoilerplate,\n} from './text-simplify-helpers';\n\ntype SimplifyLevel = 'einfache' | 'leichte';\n\ninterface TextSimplifyState {\n level: SimplifyLevel;\n}\n\ninterface SavedTextNode {\n node: Text;\n original: string;\n}\n\ninterface SavedParagraph {\n el: HTMLElement;\n originalHtml: string;\n savedTextNodes: SavedTextNode[];\n ancestorPatches: AncestorPatch[];\n}\n\ninterface AncestorPatch {\n el: HTMLElement;\n property: string;\n originalValue: string;\n}\n\ninterface ManifestBlock {\n selector: string;\n blockHash: string;\n result: string;\n originalText?: string; // For staleness check on dynamic pages\n}\n\ntype ResimplifyMode = 'manual' | 'auto';\n\ninterface RuntimeManifest {\n url: string;\n siteKey: string;\n feature: string;\n variant: string;\n blocks: ManifestBlock[];\n generatedAt: string;\n contentHash: string;\n siteMode?: ResimplifyMode; // undefined = treated as manual (safe default)\n}\n\nexport default function createTextSimplifyModule(\n aiService?: AIService,\n lang: string = 'de',\n options?: { siteKey?: string; proxyUrl?: string; simplificationLevel?: SimplifyLevel } | SimplifyLevel,\n): FeatureModule {\n let enabled = false;\n const defaultLevel = typeof options === 'string' ? options\n : (options as any)?.simplificationLevel || undefined;\n let level: SimplifyLevel = defaultLevel || 'einfache';\n let savedParagraphs: SavedParagraph[] = [];\n let abortController: AbortController | null = null;\n let overlayEl: HTMLDivElement | null = null;\n let skippedBlocks = 0;\n\n let resizeObserver: ResizeObserver | null = null;\n let mutationObserver: MutationObserver | null = null;\n // Current site's re-simplify mode. Set from manifest response on every run.\n // 'manual' (default) = never call live AI for unknown blocks.\n // 'auto' = live AI fallback + D1 auto-persist is allowed.\n let currentSiteMode: ResimplifyMode = 'manual';\n\n // Map from element to pre-replacement dimensions for resize revert checks\n const preReplaceDimensions = new Map<HTMLElement, { scrollHeight: number; clientHeight: number }>();\n\n const STORAGE_KEY = 'accessify-text-simplify';\n const SIMPLIFIED_ATTR = 'data-accessify-simplified';\n const ORIGINAL_ATTR = 'data-accessify-original';\n const BLOCK_HASH_ATTR = 'data-accessify-block-hash';\n\n // -------------------------------------------------------------------------\n // DIAGNOSTIC LOGGER — temporary, remove after root-cause analysis\n // Activate with: window.__ACCESSIFY_DIAG = true (console, before toggle)\n // Read results: window.__accessifyDiag\n // -------------------------------------------------------------------------\n\n interface DiagBlock {\n hash: string;\n text: string; // first 60 chars\n tag: string;\n manifestMatch: 'exact' | 'fuzzy' | 'similarity' | 'none';\n idbHit: boolean;\n liveAI: boolean;\n persistOk: boolean | null; // null = not attempted\n replaced: boolean;\n skipReason: string | null;\n stale: boolean;\n source: 'manifest' | 'idb' | 'ai' | 'skipped';\n }\n\n interface DiagRun {\n ts: string;\n route: string;\n level: SimplifyLevel;\n siteMode: ResimplifyMode;\n siteKey: string | null;\n manifestLoaded: boolean;\n manifestBlocks: number;\n elementsFound: number;\n phase1Applied: number;\n phase1Stale: number;\n phase15IdbHits: number;\n phase2AICalls: number;\n phase2AIErrors: number;\n totalReplaced: number;\n totalSkipped: number;\n blocks: DiagBlock[];\n restoreLog: DiagRestore[];\n }\n\n interface DiagRestore {\n ts: string;\n hash: string;\n text: string;\n textNodeRestore: boolean;\n innerHtmlFallback: boolean;\n reason: string;\n }\n\n let diagRun: DiagRun | null = null;\n const diagHistory: DiagRun[] = [];\n const DIAG = () => !!(window as any).__ACCESSIFY_DIAG;\n\n function diagInit(route: string, siteKey: string | null) {\n diagRun = {\n ts: new Date().toISOString(),\n route,\n level,\n siteMode: currentSiteMode,\n siteKey,\n manifestLoaded: false,\n manifestBlocks: 0,\n elementsFound: 0,\n phase1Applied: 0,\n phase1Stale: 0,\n phase15IdbHits: 0,\n phase2AICalls: 0,\n phase2AIErrors: 0,\n totalReplaced: 0,\n totalSkipped: 0,\n blocks: [],\n restoreLog: [],\n };\n }\n\n function diagBlock(partial: Partial<DiagBlock> & { hash: string }): DiagBlock {\n const entry: DiagBlock = {\n hash: partial.hash,\n text: partial.text || '',\n tag: partial.tag || '',\n manifestMatch: partial.manifestMatch || 'none',\n idbHit: partial.idbHit || false,\n liveAI: partial.liveAI || false,\n persistOk: partial.persistOk ?? null,\n replaced: partial.replaced || false,\n skipReason: partial.skipReason || null,\n stale: partial.stale || false,\n source: partial.source || 'skipped',\n };\n diagRun?.blocks.push(entry);\n return entry;\n }\n\n function diagFinish() {\n if (!diagRun) return;\n diagRun.totalReplaced = diagRun.blocks.filter(b => b.replaced).length;\n diagRun.totalSkipped = diagRun.blocks.filter(b => !b.replaced).length;\n diagHistory.push(diagRun);\n // Expose on window for console access\n (window as any).__accessifyDiag = diagHistory;\n if (DIAG()) {\n console.groupCollapsed(\n `%c[Accessify DIAG] Run complete: ${diagRun.route}`,\n 'color: #38bdf8; font-weight: bold',\n );\n console.table({\n route: diagRun.route,\n level: diagRun.level,\n siteMode: diagRun.siteMode,\n manifestLoaded: diagRun.manifestLoaded,\n manifestBlocks: diagRun.manifestBlocks,\n elementsFound: diagRun.elementsFound,\n 'Phase 1 (manifest)': diagRun.phase1Applied,\n 'Phase 1 stale': diagRun.phase1Stale,\n 'Phase 1.5 (IDB)': diagRun.phase15IdbHits,\n 'Phase 2 (AI)': diagRun.phase2AICalls,\n 'Phase 2 errors': diagRun.phase2AIErrors,\n totalReplaced: diagRun.totalReplaced,\n totalSkipped: diagRun.totalSkipped,\n });\n console.table(diagRun.blocks.map(b => ({\n hash: b.hash,\n text: b.text,\n tag: b.tag,\n source: b.source,\n manifest: b.manifestMatch,\n idb: b.idbHit,\n ai: b.liveAI,\n persist: b.persistOk,\n replaced: b.replaced,\n skip: b.skipReason,\n stale: b.stale,\n })));\n console.groupEnd();\n }\n }\n\n // -------------------------------------------------------------------------\n // Client-side IndexedDB cache for instant reload\n // -------------------------------------------------------------------------\n\n const CLIENT_CACHE_DB = 'accessify-simplify-cache';\n const CLIENT_CACHE_STORE = 'blocks';\n const CLIENT_CACHE_VERSION = 1;\n\n function openClientCache(): Promise<IDBDatabase> {\n return new Promise((resolve, reject) => {\n const req = indexedDB.open(CLIENT_CACHE_DB, CLIENT_CACHE_VERSION);\n req.onupgradeneeded = () => {\n const db = req.result;\n if (!db.objectStoreNames.contains(CLIENT_CACHE_STORE)) {\n db.createObjectStore(CLIENT_CACHE_STORE);\n }\n };\n req.onsuccess = () => resolve(req.result);\n req.onerror = () => reject(req.error);\n });\n }\n\n async function getClientCached(key: string): Promise<string | null> {\n try {\n const db = await openClientCache();\n return new Promise((resolve) => {\n const tx = db.transaction(CLIENT_CACHE_STORE, 'readonly');\n const store = tx.objectStore(CLIENT_CACHE_STORE);\n const req = store.get(key);\n req.onsuccess = () => resolve(req.result ?? null);\n req.onerror = () => resolve(null);\n });\n } catch { return null; }\n }\n\n async function setClientCached(key: string, value: string): Promise<void> {\n try {\n const db = await openClientCache();\n const tx = db.transaction(CLIENT_CACHE_STORE, 'readwrite');\n tx.objectStore(CLIENT_CACHE_STORE).put(value, key);\n } catch { /* ignore */ }\n }\n\n async function deleteClientCached(key: string): Promise<void> {\n try {\n const db = await openClientCache();\n const tx = db.transaction(CLIENT_CACHE_STORE, 'readwrite');\n tx.objectStore(CLIENT_CACHE_STORE).delete(key);\n } catch { /* ignore */ }\n }\n\n /** Clear ALL client-cached entries for the current page+level.\n * Used when a new server manifest is available to prevent stale hallucinations. */\n async function clearClientCacheForPage(): Promise<void> {\n try {\n const db = await openClientCache();\n const tx = db.transaction(CLIENT_CACHE_STORE, 'readwrite');\n const store = tx.objectStore(CLIENT_CACHE_STORE);\n const pageUrl = window.location.origin + window.location.pathname;\n const prefix = `${pageUrl}:simplify:${level}:`;\n\n // IDBObjectStore doesn't support prefix queries natively,\n // so we iterate all keys and delete matching ones\n const req = store.openCursor();\n req.onsuccess = () => {\n const cursor = req.result;\n if (!cursor) return;\n if (typeof cursor.key === 'string' && cursor.key.startsWith(prefix)) {\n cursor.delete();\n }\n cursor.continue();\n };\n } catch { /* ignore */ }\n }\n\n function clientCacheKey(blockHash: string): string {\n const pageUrl = window.location.origin + window.location.pathname;\n return `${pageUrl}:simplify:${level}:${blockHash}`;\n }\n\n // -------------------------------------------------------------------------\n // Resolve config from the script tag or global config\n // -------------------------------------------------------------------------\n\n function getConfig(): { siteKey?: string; proxyUrl?: string } {\n const script = document.querySelector('script[data-site-key]');\n const siteKey = script?.getAttribute('data-site-key') ||\n (window as any).Accessify?.config?.siteKey || '';\n const proxyUrl = script?.getAttribute('data-proxy-url') ||\n (window as any).Accessify?.config?.proxyUrl || '';\n return { siteKey: siteKey || undefined, proxyUrl: proxyUrl || undefined };\n }\n\n // -------------------------------------------------------------------------\n // Find text-heavy elements on the page\n // -------------------------------------------------------------------------\n\n /**\n * FAQ/Accordion safety: these containers must NOT be treated as a single\n * simplify block. Instead, we descend into their children (question vs answer).\n */\n // FAQ / Accordion / Disclosure containers — must NEVER be simplified as one block.\n // Covers: native <details>, ARIA patterns, common class-name conventions,\n // component libraries (Radix, Headless UI, MUI, Bootstrap), and generic\n // collapsible widgets. Matched via .closest() so only the *container* is detected.\n const FAQ_CONTAINER_SELECTORS = [\n 'details',\n '[role=\"region\"][aria-labelledby]',\n '[aria-expanded]',\n '[data-state=\"open\"]',\n '[data-state=\"closed\"]',\n '[class*=\"accordion\" i]',\n '[class*=\"faq\" i]',\n '[class*=\"disclosure\" i]',\n '[class*=\"expandable\" i]',\n '[class*=\"collapsible\" i]',\n '[class*=\"collapse\" i]',\n ].join(', ');\n const FAQ_SKIP_SELECTORS = 'summary, [role=\"button\"], .accordion-header, .accordion__header, .faq-question';\n\n /** Lines that are photo credits, attributions, or metadata — not actual content.\n * These should NOT be simplified (risk of hallucination on short context). */\n const CREDIT_LINE_PATTERN = /^(Bild|Foto|Photo|Image|Fotocredit|Credit|Text|Autor|Author|Quelle|Source|Copyright|©|\\(c\\))\\s*[:·\\-–—]/i;\n\n function getTextElements(): HTMLElement[] {\n const selectors = 'h1, h2, h3, h4, h5, h6, p, li, td, th, blockquote, figcaption, .hero-subtitle, summary, dt, dd';\n const elements: HTMLElement[] = [];\n const seen = new Set<HTMLElement>();\n\n // Scan the entire page — the manifest acts as the real filter.\n // Only blocks whose hash matches a manifest entry get replaced;\n // everything else (nav, footer, sidebar) is skipped automatically.\n // This avoids brittle content-root heuristics that break on Framer,\n // Webflow, Shopify, and other non-standard DOM structures.\n document.body.querySelectorAll(selectors).forEach((el) => {\n const htmlEl = el as HTMLElement;\n if (htmlEl.closest('#accessify-root')) return;\n // Skip truly hidden elements, but allow Framer scroll-animated ones\n // (they have offsetParent===null due to opacity:0 / will-change but ARE real content)\n if (htmlEl.offsetParent === null && htmlEl.style.position !== 'fixed') {\n const isFramerAnimated = htmlEl.closest('[data-framer-appear-id]') || htmlEl.closest('[data-framer-name]');\n if (!isFramerAnimated) return;\n }\n // Freeze the original text on first encounter — this ensures we always\n // hash the real page text, never text that was already replaced by us.\n // This prevents the \"simplified text stored as original\" corruption loop.\n if (!htmlEl.dataset.accessifyOriginal) {\n htmlEl.dataset.accessifyOriginal = htmlEl.textContent?.trim() || '';\n }\n const text = htmlEl.dataset.accessifyOriginal;\n if (text.length < 20) return;\n // Skip short photo credits / attribution lines (high hallucination risk)\n if (text.length < 60 && CREDIT_LINE_PATTERN.test(text)) return;\n if (seen.has(htmlEl)) return;\n\n // FAQ safety: skip elements whose parent is an FAQ container if the\n // element itself is a question/toggle (those are handled separately)\n // This prevents question+answer from being merged into one block.\n const inFaqContainer = htmlEl.closest(FAQ_CONTAINER_SELECTORS);\n if (inFaqContainer) {\n // Inside an FAQ container: only collect leaf text elements,\n // not the container itself. Skip if this element contains\n // both question and answer content (too risky to simplify as one).\n const childBlocks = htmlEl.querySelectorAll('p, li, dd, dt, summary');\n if (childBlocks.length > 1) {\n // This element wraps multiple sub-blocks — skip it, let children be collected\n if ((window as any).__ACCESSIFY_DEBUG) {\n console.log('[Accessify] FAQ: skipping wrapper, will collect children:', htmlEl.tagName);\n }\n return;\n }\n }\n\n let dominated = false;\n for (const s of seen) {\n if (s.contains(htmlEl)) { dominated = true; break; }\n }\n if (dominated) return;\n seen.add(htmlEl);\n elements.push(htmlEl);\n });\n\n return elements;\n }\n\n // -------------------------------------------------------------------------\n // Block hash — DJB2, same as server side\n // -------------------------------------------------------------------------\n\n function hashText(text: string): string {\n const normalized = text.replace(/\\s+/g, ' ').trim().toLowerCase();\n let hash = 5381;\n for (let i = 0; i < normalized.length; i++) {\n hash = ((hash << 5) + hash + normalized.charCodeAt(i)) & 0x7fffffff;\n }\n return hash.toString(36);\n }\n\n // -------------------------------------------------------------------------\n // Staleness check — compare current page text with DB original\n // -------------------------------------------------------------------------\n\n /**\n * Fast character-diff count between two normalized strings.\n * Uses Levenshtein distance for short strings, falls back to\n * a simple diff-count for longer texts (performance).\n */\n function charDiff(a: string, b: string): number {\n const na = a.replace(/\\s+/g, ' ').trim();\n const nb = b.replace(/\\s+/g, ' ').trim();\n\n // Fast path: identical\n if (na === nb) return 0;\n\n // For very long strings, use a quick heuristic (length diff + sample comparison)\n if (na.length > 500 || nb.length > 500) {\n const lenDiff = Math.abs(na.length - nb.length);\n if (lenDiff > 3) return lenDiff;\n // Compare char-by-char up to the shorter length\n let diffs = lenDiff;\n const minLen = Math.min(na.length, nb.length);\n for (let i = 0; i < minLen; i++) {\n if (na[i] !== nb[i]) diffs++;\n if (diffs > 3) return diffs; // early exit\n }\n return diffs;\n }\n\n // Levenshtein distance for shorter strings (accurate)\n const m = na.length;\n const n = nb.length;\n // Early exit if length diff alone exceeds threshold\n if (Math.abs(m - n) > 3) return Math.abs(m - n);\n\n const dp: number[] = Array.from({ length: n + 1 }, (_, i) => i);\n for (let i = 1; i <= m; i++) {\n let prev = dp[0];\n dp[0] = i;\n for (let j = 1; j <= n; j++) {\n const temp = dp[j];\n dp[j] = na[i - 1] === nb[j - 1]\n ? prev\n : 1 + Math.min(prev, dp[j], dp[j - 1]);\n prev = temp;\n }\n // Early exit if minimum possible distance exceeds threshold\n if (Math.min(...dp) > 3) return 4;\n }\n return dp[n];\n }\n\n const STALENESS_THRESHOLD = 3; // max char-diff to still accept cached version\n\n // -------------------------------------------------------------------------\n // Leaf text node replacement — preserves inline markup\n // -------------------------------------------------------------------------\n\n function getLeafTextNodes(root: Node): Text[] {\n const textNodes: Text[] = [];\n const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, null);\n let node: Text | null;\n while ((node = walker.nextNode() as Text | null)) {\n const trimmed = node.nodeValue?.trim();\n if (trimmed && trimmed.length > 0) {\n textNodes.push(node);\n }\n }\n return textNodes;\n }\n\n /**\n * Replace leaf text nodes within an element with simplified text.\n * The simplified text is distributed across existing text nodes proportionally,\n * preserving the surrounding inline markup structure.\n *\n * Strategy: if there is only one meaningful text node, replace it directly.\n * If there are multiple text nodes, split the simplified text into sentences\n * and distribute them proportionally across the existing text nodes.\n */\n function replaceLeafTextNodes(el: HTMLElement, simplifiedText: string): void {\n const textNodes = getLeafTextNodes(el);\n if (textNodes.length === 0) return;\n\n if (textNodes.length === 1) {\n // Single text node — straightforward replacement\n textNodes[0].nodeValue = simplifiedText;\n return;\n }\n\n // Multiple text nodes: distribute simplified text proportionally\n // Split simplified text into segments by sentence boundaries\n const sentences = simplifiedText.match(/[^.!?]+[.!?]*\\s*/g) || [simplifiedText];\n\n // Calculate original character weight per text node\n const totalOriginalLen = textNodes.reduce((sum, n) => sum + (n.nodeValue?.trim().length || 0), 0);\n\n if (totalOriginalLen === 0) {\n // Fallback: put all simplified text in first node, clear others\n textNodes[0].nodeValue = simplifiedText;\n for (let i = 1; i < textNodes.length; i++) {\n textNodes[i].nodeValue = '';\n }\n return;\n }\n\n // Distribute sentences across text nodes based on character weight\n let sentenceIndex = 0;\n for (let i = 0; i < textNodes.length; i++) {\n const nodeWeight = (textNodes[i].nodeValue?.trim().length || 0) / totalOriginalLen;\n const sentenceCount = Math.max(1, Math.round(nodeWeight * sentences.length));\n\n const chunk = sentences.slice(sentenceIndex, sentenceIndex + sentenceCount).join('');\n textNodes[i].nodeValue = chunk || '';\n sentenceIndex += sentenceCount;\n }\n\n // If there are leftover sentences, append to the last non-empty node\n if (sentenceIndex < sentences.length) {\n const lastNode = textNodes[textNodes.length - 1];\n lastNode.nodeValue = (lastNode.nodeValue || '') + sentences.slice(sentenceIndex).join('');\n }\n }\n\n // -------------------------------------------------------------------------\n // Layout Guard — detect overflow/clipping after replacement\n // -------------------------------------------------------------------------\n\n function measureElement(el: HTMLElement): { scrollHeight: number; clientHeight: number } {\n return { scrollHeight: el.scrollHeight, clientHeight: el.clientHeight };\n }\n\n /**\n * Check if an element or its ancestors have overflow:hidden with fixed height\n * that would clip expanded content.\n */\n function findClippingAncestor(el: HTMLElement): HTMLElement | null {\n let current: HTMLElement | null = el;\n while (current && current !== document.body) {\n const style = getComputedStyle(current);\n const overflow = style.overflow + ' ' + style.overflowY;\n const hasHiddenOverflow = overflow.includes('hidden') || overflow.includes('clip');\n const hasFixedHeight = style.maxHeight !== 'none' || (\n style.height !== 'auto' && style.height !== '' && !style.height.includes('%')\n );\n if (hasHiddenOverflow && hasFixedHeight) {\n return current;\n }\n current = current.parentElement;\n }\n return null;\n }\n\n /**\n * Check layout safety after a replacement. Returns true if safe, false if clipping detected.\n * Attempts minimal ancestor patches to resolve clipping before giving up.\n */\n function checkLayoutSafety(el: HTMLElement): { safe: boolean; patches: AncestorPatch[] } {\n const patches: AncestorPatch[] = [];\n\n // Check the element itself for overflow\n const after = measureElement(el);\n const isElementOverflowing = after.scrollHeight > after.clientHeight + 2; // 2px tolerance\n\n if (!isElementOverflowing) {\n // Also check for clipping ancestors\n const clipAncestor = findClippingAncestor(el);\n if (!clipAncestor) return { safe: true, patches: [] };\n\n const ancestorAfter = measureElement(clipAncestor);\n if (ancestorAfter.scrollHeight <= ancestorAfter.clientHeight + 2) {\n return { safe: true, patches: [] };\n }\n\n // Try patching the ancestor\n return tryAncestorPatches(clipAncestor, patches);\n }\n\n // Element itself is overflowing — check if a parent is clipping it\n const clipAncestor = findClippingAncestor(el);\n if (!clipAncestor) {\n // No clipping ancestor — overflow is fine (content just grows naturally)\n return { safe: true, patches: [] };\n }\n\n return tryAncestorPatches(clipAncestor, patches);\n }\n\n function tryAncestorPatches(\n ancestor: HTMLElement,\n patches: AncestorPatch[],\n ): { safe: boolean; patches: AncestorPatch[] } {\n const style = getComputedStyle(ancestor);\n\n // Try removing max-height\n if (style.maxHeight !== 'none') {\n patches.push({\n el: ancestor,\n property: 'maxHeight',\n originalValue: ancestor.style.maxHeight,\n });\n ancestor.style.maxHeight = 'none';\n }\n\n // Try setting overflow to visible\n if (style.overflow.includes('hidden') || style.overflowY.includes('hidden')) {\n patches.push({\n el: ancestor,\n property: 'overflow',\n originalValue: ancestor.style.overflow,\n });\n ancestor.style.overflow = 'visible';\n }\n\n // Re-check after patches\n const afterPatch = measureElement(ancestor);\n if (afterPatch.scrollHeight <= afterPatch.clientHeight + 2) {\n return { safe: true, patches };\n }\n\n // Patches didn't fix it — revert them\n revertPatches(patches);\n return { safe: false, patches: [] };\n }\n\n function revertPatches(patches: AncestorPatch[]) {\n for (const patch of patches) {\n (patch.el.style as any)[patch.property] = patch.originalValue;\n }\n }\n\n // -------------------------------------------------------------------------\n // Observers — ResizeObserver + MutationObserver\n // -------------------------------------------------------------------------\n\n function setupResizeObserver() {\n if (resizeObserver) return;\n\n resizeObserver = new ResizeObserver((entries) => {\n for (const entry of entries) {\n const el = entry.target as HTMLElement;\n if (!el.hasAttribute(SIMPLIFIED_ATTR)) continue;\n\n const saved = savedParagraphs.find((sp) => sp.el === el);\n if (!saved) continue;\n\n // Check if a layout change caused clipping\n const clipAncestor = findClippingAncestor(el);\n if (clipAncestor) {\n const dims = measureElement(clipAncestor);\n if (dims.scrollHeight > dims.clientHeight + 2) {\n // External layout change caused clipping — revert this block\n console.warn(\n '[Accessify] ResizeObserver detected clipping after external layout change, reverting block:',\n el,\n );\n revertSingleBlock(saved);\n skippedBlocks++;\n }\n }\n }\n });\n }\n\n function setupMutationObserver() {\n if (mutationObserver) return;\n\n mutationObserver = new MutationObserver((mutations) => {\n for (const mutation of mutations) {\n // Check if any removed nodes were tracked simplified blocks\n for (const removed of mutation.removedNodes) {\n if (!(removed instanceof HTMLElement)) continue;\n const idx = savedParagraphs.findIndex((sp) => sp.el === removed || removed.contains(sp.el));\n if (idx !== -1) {\n const saved = savedParagraphs[idx];\n // Element was removed from DOM by the page itself — clean up tracking\n resizeObserver?.unobserve(saved.el);\n saved.el.removeAttribute(SIMPLIFIED_ATTR);\n saved.el.removeAttribute(ORIGINAL_ATTR);\n saved.el.removeAttribute(BLOCK_HASH_ATTR);\n if (saved.el.dataset.accessifyOrigTransform) {\n saved.el.style.textTransform = saved.el.dataset.accessifyOrigTransform;\n delete saved.el.dataset.accessifyOrigTransform;\n }\n revertPatches(saved.ancestorPatches);\n savedParagraphs.splice(idx, 1);\n }\n }\n\n // Check if a simplified element's content was changed externally\n if (mutation.type === 'characterData' || mutation.type === 'childList') {\n const target = mutation.target instanceof HTMLElement\n ? mutation.target\n : mutation.target.parentElement;\n if (!target) continue;\n\n const simplifiedEl = target.closest(`[${SIMPLIFIED_ATTR}]`) as HTMLElement | null;\n if (simplifiedEl) {\n const saved = savedParagraphs.find((sp) => sp.el === simplifiedEl);\n if (saved && !simplifiedEl.hasAttribute('data-accessify-replacing')) {\n // External mutation on a simplified block — clean up\n console.warn(\n '[Accessify] External mutation detected on simplified block, cleaning up tracking:',\n simplifiedEl,\n );\n resizeObserver?.unobserve(saved.el);\n saved.el.removeAttribute(SIMPLIFIED_ATTR);\n saved.el.removeAttribute(ORIGINAL_ATTR);\n saved.el.removeAttribute(BLOCK_HASH_ATTR);\n if (saved.el.dataset.accessifyOrigTransform) {\n saved.el.style.textTransform = saved.el.dataset.accessifyOrigTransform;\n delete saved.el.dataset.accessifyOrigTransform;\n }\n revertPatches(saved.ancestorPatches);\n savedParagraphs = savedParagraphs.filter((sp) => sp.el !== simplifiedEl);\n }\n }\n }\n }\n });\n\n mutationObserver.observe(document.body, {\n childList: true,\n subtree: true,\n characterData: true,\n });\n }\n\n function disconnectObservers() {\n if (resizeObserver) {\n resizeObserver.disconnect();\n resizeObserver = null;\n }\n if (mutationObserver) {\n mutationObserver.disconnect();\n mutationObserver = null;\n }\n preReplaceDimensions.clear();\n }\n\n // -------------------------------------------------------------------------\n // Safe replacement — leaf nodes + layout guard\n // -------------------------------------------------------------------------\n\n function safeReplace(\n el: HTMLElement,\n simplifiedText: string,\n blockHash?: string,\n ): boolean {\n // Save original — both innerHTML (fallback) and individual text nodes (preferred)\n const originalHtml = el.innerHTML;\n const textNodesBefore = getLeafTextNodes(el);\n const savedTextNodes: SavedTextNode[] = textNodesBefore.map(n => ({\n node: n,\n original: n.data,\n }));\n\n // Guard flag to prevent MutationObserver from treating our own replacement as external\n el.setAttribute('data-accessify-replacing', 'true');\n\n // Perform leaf text node replacement (preserves inline markup)\n // ONLY sets textNode.nodeValue — never modifies DOM structure\n replaceLeafTextNodes(el, simplifiedText);\n\n // Check layout safety\n const { safe, patches } = checkLayoutSafety(el);\n\n if (!safe) {\n // Revert — restore via text node .data (React/Framer-safe)\n for (const saved of savedTextNodes) {\n if (saved.node.parentNode) saved.node.data = saved.original;\n }\n setTimeout(() => el.removeAttribute('data-accessify-replacing'), 0);\n console.warn(\n '[Accessify] Block skipped — layout clipping detected after replacement:',\n el.textContent?.slice(0, 60),\n );\n skippedBlocks++;\n return false;\n }\n\n // Replacement is safe — finalize\n el.setAttribute(SIMPLIFIED_ATTR, 'true');\n el.setAttribute(ORIGINAL_ATTR, originalHtml);\n if (blockHash) {\n el.setAttribute(BLOCK_HASH_ATTR, blockHash);\n }\n\n // Reset CSS text-transform so simplified text displays as-authored\n // (many sites apply uppercase/capitalize to headings/buttons)\n // Use setProperty with !important to override site CSS that uses !important\n const computed = window.getComputedStyle(el);\n if (computed.textTransform && computed.textTransform !== 'none') {\n el.dataset.accessifyOrigTransform = computed.textTransform;\n el.style.setProperty('text-transform', 'none', 'important');\n }\n\n savedParagraphs.push({ el, originalHtml, savedTextNodes, ancestorPatches: patches });\n\n // Observe for future layout changes\n resizeObserver?.observe(el);\n\n // Remove guard flag AFTER MutationObserver has processed current mutations\n setTimeout(() => el.removeAttribute('data-accessify-replacing'), 0);\n\n return true;\n }\n\n /**\n * Revert a single block — used by ResizeObserver when external layout changes cause clipping.\n */\n function revertSingleBlock(saved: SavedParagraph) {\n // Prefer text-node restore (React/Framer-safe: no DOM structure change)\n if (saved.savedTextNodes.length > 0) {\n for (const sn of saved.savedTextNodes) {\n if (sn.node.parentNode) sn.node.data = sn.original;\n }\n } else {\n // Fallback to innerHTML if text nodes are gone (framework re-rendered)\n saved.el.innerHTML = saved.originalHtml;\n }\n saved.el.removeAttribute(SIMPLIFIED_ATTR);\n saved.el.removeAttribute(ORIGINAL_ATTR);\n saved.el.removeAttribute(BLOCK_HASH_ATTR);\n // Restore original text-transform if we overrode it\n if (saved.el.dataset.accessifyOrigTransform) {\n saved.el.style.textTransform = saved.el.dataset.accessifyOrigTransform;\n delete saved.el.dataset.accessifyOrigTransform;\n }\n revertPatches(saved.ancestorPatches);\n resizeObserver?.unobserve(saved.el);\n savedParagraphs = savedParagraphs.filter((sp) => sp.el !== saved.el);\n }\n\n // -------------------------------------------------------------------------\n // Fetch manifest from API\n // -------------------------------------------------------------------------\n\n // In-memory manifest cache to avoid refetching on toggle\n const manifestCache = new Map<string, RuntimeManifest | null>();\n\n /** Hard upper bound for manifest fetch. Beyond this the run continues with\n * no manifest instead of hanging the toggle flow. */\n const MANIFEST_FETCH_TIMEOUT_MS = 8000;\n\n async function fetchManifest(\n siteKey: string,\n proxyUrl?: string,\n externalSignal?: AbortSignal,\n ): Promise<RuntimeManifest | null> {\n const cacheKey = `${siteKey}:${level}:${window.location.pathname}`;\n if (manifestCache.has(cacheKey)) return manifestCache.get(cacheKey)!;\n\n const base = proxyUrl || 'https://accessify-api.accessify.workers.dev';\n const pageUrl = encodeURIComponent(window.location.origin + window.location.pathname);\n const url = `${base}/v1/manifest?siteKey=${siteKey}&url=${pageUrl}&feature=simplify&variant=${level}`;\n\n // Combine timeout + optional external abort signal so a slow/hung Worker\n // cannot freeze the simplify flow and toggle-off stays responsive.\n const timeoutController = new AbortController();\n const timeoutId = setTimeout(() => timeoutController.abort(), MANIFEST_FETCH_TIMEOUT_MS);\n const externalAbortHandler = () => timeoutController.abort();\n if (externalSignal) {\n if (externalSignal.aborted) timeoutController.abort();\n else externalSignal.addEventListener('abort', externalAbortHandler);\n }\n\n try {\n const res = await fetch(url, { cache: 'no-cache', signal: timeoutController.signal });\n if (!res.ok) { manifestCache.set(cacheKey, null); return null; }\n const data = await res.json() as RuntimeManifest & { miss?: boolean };\n // Always update mode from server — this is the authoritative signal that\n // tells the widget whether live AI fallback is permitted for this site.\n currentSiteMode = data.siteMode === 'auto' ? 'auto' : 'manual';\n const result = data.blocks?.length ? data : null;\n manifestCache.set(cacheKey, result);\n return result;\n } catch {\n // Timeouts, aborts, network errors — all fall through to \"no manifest\".\n // We DO NOT cache aborts/timeouts so the next run can retry.\n return null;\n } finally {\n clearTimeout(timeoutId);\n if (externalSignal) externalSignal.removeEventListener('abort', externalAbortHandler);\n }\n }\n\n // -------------------------------------------------------------------------\n // Apply manifest blocks to the page (safe replacement)\n // -------------------------------------------------------------------------\n\n function applyManifestBlocks(\n manifest: RuntimeManifest,\n elements: HTMLElement[],\n ): { applied: HTMLElement[]; remaining: HTMLElement[]; stale: HTMLElement[] } {\n const applied: HTMLElement[] = [];\n const remaining: HTMLElement[] = [];\n const stale: HTMLElement[] = [];\n\n // Build lookups by block hash AND by CSS selector for fuzzy matching\n const byHash = new Map<string, ManifestBlock>();\n const bySelector = new Map<string, ManifestBlock>();\n for (const block of manifest.blocks) {\n byHash.set(block.blockHash, block);\n if (block.selector) {\n bySelector.set(block.selector, block);\n }\n }\n\n // Track which manifest blocks have been consumed (applied or claimed as stale).\n // Needed for the Phase C similarity fallback so we don't assign one block to\n // two elements.\n const claimedBlockHashes = new Set<string>();\n\n for (const el of elements) {\n const text = el.dataset.accessifyOriginal || el.textContent?.trim() || '';\n const elHash = hashText(text);\n\n // --- Phase A: Exact hash match (text unchanged) ---\n const exactMatch = byHash.get(elHash);\n if (exactMatch?.result) {\n const success = safeReplace(el, exactMatch.result, elHash);\n if (success) {\n applied.push(el);\n claimedBlockHashes.add(exactMatch.blockHash);\n // --- DIAG ---\n if (DIAG()) diagBlock({ hash: elHash, text: text.slice(0, 60), tag: el.tagName, manifestMatch: 'exact', replaced: true, source: 'manifest' });\n } else {\n remaining.push(el);\n if (DIAG()) diagBlock({ hash: elHash, text: text.slice(0, 60), tag: el.tagName, manifestMatch: 'exact', replaced: false, skipReason: 'layout-unsafe', source: 'skipped' });\n }\n continue;\n }\n\n // --- Phase B: Selector-based fuzzy match (text may have changed slightly) ---\n // Try to find this element in the manifest by CSS selector\n let fuzzyMatch: ManifestBlock | undefined;\n for (const [selector, block] of bySelector) {\n try {\n if (el.matches(selector) || el.closest(selector) === el) {\n fuzzyMatch = block;\n break;\n }\n } catch { /* invalid selector, skip */ }\n }\n\n if (fuzzyMatch?.result && fuzzyMatch.originalText) {\n const diff = charDiff(text, fuzzyMatch.originalText);\n if (diff <= STALENESS_THRESHOLD) {\n // Minor change (typo fix, date update) — cached simplification still valid\n const success = safeReplace(el, fuzzyMatch.result, elHash);\n if (success) {\n applied.push(el);\n claimedBlockHashes.add(fuzzyMatch.blockHash);\n if (DIAG()) diagBlock({ hash: elHash, text: text.slice(0, 60), tag: el.tagName, manifestMatch: 'fuzzy', replaced: true, source: 'manifest' });\n } else {\n remaining.push(el);\n if (DIAG()) diagBlock({ hash: elHash, text: text.slice(0, 60), tag: el.tagName, manifestMatch: 'fuzzy', replaced: false, skipReason: 'layout-unsafe', source: 'skipped' });\n }\n } else {\n // Text changed significantly — simplification is stale, needs AI refresh\n console.info(\n `[Accessify] Stale block (diff=${diff}):`,\n text.slice(0, 50) + '…',\n );\n stale.push(el);\n claimedBlockHashes.add(fuzzyMatch.blockHash);\n if (DIAG()) diagBlock({ hash: elHash, text: text.slice(0, 60), tag: el.tagName, manifestMatch: 'fuzzy', stale: true, skipReason: `stale(diff=${diff})`, source: 'skipped' });\n }\n continue;\n }\n\n // No match in Phase A or B — defer to Phase C post-pass (token similarity).\n remaining.push(el);\n }\n\n // --- Phase C: Token-similarity stale detection ---\n // For each remaining element, check if it's the same block as an unclaimed\n // manifest block (high token-Jaccard overlap) whose originalText has\n // drifted beyond the char-diff threshold. This covers two important cases:\n // 1. Legacy b-xxx selectors from pre-Batch-1 auto-persist that never\n // matched valid CSS and so always fell through Phase B.\n // 2. Framer/Webflow-generated selectors that only worked on the crawler's\n // DOM snapshot and not on the rendered page.\n // Without this pass, any block whose content changes meaningfully AND whose\n // selector fails to match becomes permanently invisible to the widget.\n const unclaimedBlocks = manifest.blocks.filter(b =>\n b.originalText && b.result && !claimedBlockHashes.has(b.blockHash),\n );\n if (unclaimedBlocks.length > 0 && remaining.length > 0) {\n const blockTokens = unclaimedBlocks.map(b => ({\n block: b,\n tokens: tokenize(b.originalText!),\n }));\n\n interface Candidate { el: HTMLElement; block: ManifestBlock; sim: number; }\n const candidates: Candidate[] = [];\n for (const el of remaining) {\n const text = el.dataset.accessifyOriginal || el.textContent?.trim() || '';\n if (text.length < 20) continue;\n const elTokens = tokenize(text);\n if (elTokens.size < 3) continue; // too short to match meaningfully\n let best: Candidate | null = null;\n for (const { block, tokens } of blockTokens) {\n const sim = tokenSimilarity(elTokens, tokens);\n if (sim >= STALE_SIMILARITY_THRESHOLD && (!best || sim > best.sim)) {\n best = { el, block, sim };\n }\n }\n if (best) candidates.push(best);\n }\n\n // Greedy assignment: highest similarity first, one block per element.\n candidates.sort((a, b) => b.sim - a.sim);\n const assignedBlocks = new Set<string>();\n const assignedEls = new Set<HTMLElement>();\n for (const c of candidates) {\n if (assignedBlocks.has(c.block.blockHash)) continue;\n if (assignedEls.has(c.el)) continue;\n assignedBlocks.add(c.block.blockHash);\n assignedEls.add(c.el);\n stale.push(c.el);\n claimedBlockHashes.add(c.block.blockHash);\n const elText = c.el.dataset.accessifyOriginal || c.el.textContent?.trim() || '';\n console.info(\n `[Accessify] Stale block (similarity=${c.sim.toFixed(2)}):`,\n elText.slice(0, 50) + '…',\n );\n if (DIAG()) diagBlock({\n hash: hashText(elText),\n text: elText.slice(0, 60),\n tag: c.el.tagName,\n manifestMatch: 'similarity',\n stale: true,\n skipReason: `stale(sim=${c.sim.toFixed(2)})`,\n source: 'skipped',\n });\n }\n\n // Rebuild remaining without the newly-claimed stale elements.\n if (assignedEls.size > 0) {\n const stillRemaining = remaining.filter(el => !assignedEls.has(el));\n remaining.length = 0;\n remaining.push(...stillRemaining);\n }\n }\n\n return { applied, remaining, stale };\n }\n\n // -------------------------------------------------------------------------\n // Live fallback auto-persist — write back to D1+KV\n // -------------------------------------------------------------------------\n\n /** Build a minimal CSS selector for a DOM element so the server can store it */\n function buildSelector(el: HTMLElement): string {\n const tag = el.tagName.toLowerCase();\n // If element has an id, use that (most specific)\n if (el.id) return `#${CSS.escape(el.id)}`;\n // Otherwise use nth-of-type within parent\n const parent = el.parentElement;\n if (!parent) return tag;\n const siblings = Array.from(parent.children).filter(c => c.tagName === el.tagName);\n if (siblings.length === 1) return tag;\n const idx = siblings.indexOf(el) + 1;\n return `${tag}:nth-of-type(${idx})`;\n }\n\n /** Batch buffer: collect blocks, flush once after simplification completes */\n const persistQueue: Array<{\n blockHash: string;\n originalText: string;\n result: string;\n selector: string;\n }> = [];\n let persistFlushTimer: ReturnType<typeof setTimeout> | null = null;\n const persistedHashes = new Set<string>(); // deduplicate across toggle cycles\n\n function persistToManifest(\n siteKey: string,\n blockHash: string,\n originalText: string,\n simplifiedText: string,\n selector: string,\n proxyUrl?: string,\n ): void {\n // Skip if already persisted this session\n if (persistedHashes.has(blockHash)) return;\n persistedHashes.add(blockHash);\n\n persistQueue.push({ blockHash, originalText, result: simplifiedText, selector });\n\n // Debounce: flush 2s after last persist call (batches rapid-fire calls)\n if (persistFlushTimer) clearTimeout(persistFlushTimer);\n persistFlushTimer = setTimeout(() => flushPersistQueue(siteKey, proxyUrl), 2000);\n }\n\n function flushPersistQueue(siteKey: string, proxyUrl?: string): void {\n if (persistQueue.length === 0) return;\n const base = proxyUrl || 'https://accessify-api.accessify.workers.dev';\n const pageUrl = window.location.origin + window.location.pathname;\n const blocks = persistQueue.splice(0);\n\n fetch(`${base}/v1/cache/persist`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-Site-Key': siteKey,\n },\n body: JSON.stringify({\n siteKey,\n url: pageUrl,\n feature: 'simplify',\n variant: level,\n blocks,\n }),\n }).then((res) => {\n if (res.ok) {\n if ((window as any).__ACCESSIFY_DEBUG) {\n console.log(`[Accessify] Persisted ${blocks.length} blocks to D1+KV`);\n }\n // --- DIAG: mark persisted blocks ---\n if (DIAG() && diagRun) {\n for (const b of blocks) {\n const entry = diagRun.blocks.find(d => d.hash === b.blockHash);\n if (entry) entry.persistOk = true;\n }\n console.log(\n `%c[Accessify DIAG] Persist OK: ${blocks.length} blocks → ${pageUrl}`,\n 'color: #22c55e',\n );\n }\n } else {\n console.warn(`[Accessify] Persist failed: ${res.status}`);\n // --- DIAG: mark persist failure ---\n if (DIAG() && diagRun) {\n for (const b of blocks) {\n const entry = diagRun.blocks.find(d => d.hash === b.blockHash);\n if (entry) entry.persistOk = false;\n }\n console.warn(\n `%c[Accessify DIAG] Persist FAILED (${res.status}): ${blocks.length} blocks`,\n 'color: #ef4444',\n );\n }\n }\n }).catch((err) => {\n console.warn('[Accessify] Failed to persist live simplification:', err);\n // --- DIAG: mark persist error ---\n if (DIAG() && diagRun) {\n for (const b of blocks) {\n const entry = diagRun.blocks.find(d => d.hash === b.blockHash);\n if (entry) entry.persistOk = false;\n }\n }\n });\n }\n\n // -------------------------------------------------------------------------\n // Loading overlay on individual elements\n // -------------------------------------------------------------------------\n\n function markLoading(el: HTMLElement) {\n el.style.opacity = '0.5';\n el.style.transition = 'opacity 0.3s ease';\n }\n\n function clearLoading(el: HTMLElement) {\n el.style.opacity = '';\n el.style.transition = '';\n }\n\n // -------------------------------------------------------------------------\n // Global progress indicator\n // -------------------------------------------------------------------------\n\n function showProgress(_current: number, _total: number, _skipped: number = 0) {\n // Progress overlay disabled — text appears silently as it's ready\n }\n\n function showDone(_count: number, _fromCache: number) {\n // Silently remove progress overlay — no \"done\" banner needed\n removeProgress();\n }\n\n function showError(msg: string) {\n if (overlayEl) {\n // Build via textContent so any provider/network error text is never\n // interpreted as HTML — prevents script injection via error messages.\n overlayEl.textContent = '';\n const icon = document.createElement('span');\n icon.style.color = '#ef4444';\n icon.textContent = '\\u2715 ';\n overlayEl.appendChild(icon);\n overlayEl.appendChild(document.createTextNode(String(msg ?? '')));\n overlayEl.style.color = '#ef4444';\n setTimeout(() => removeProgress(), 5000);\n }\n }\n\n function removeProgress() {\n overlayEl?.remove();\n overlayEl = null;\n document.getElementById('accessify-autospin-style')?.remove();\n }\n\n // -------------------------------------------------------------------------\n // AI Disclaimer Banner — shown on the actual page (outside shadow DOM)\n // -------------------------------------------------------------------------\n const DISCLAIMER_ID = 'accessify-ai-disclaimer';\n\n function showDisclaimer() {\n if (document.getElementById(DISCLAIMER_ID)) return;\n const text = lang.startsWith('de')\n ? 'KI-vereinfachter Text'\n : 'AI-simplified text';\n\n const banner = document.createElement('div');\n banner.id = DISCLAIMER_ID;\n banner.setAttribute('role', 'status');\n banner.setAttribute('aria-live', 'polite');\n banner.textContent = text;\n Object.assign(banner.style, {\n position: 'fixed',\n bottom: '12px',\n left: '12px',\n zIndex: '2147483640',\n padding: '6px 12px',\n background: 'rgba(30,41,59,0.85)',\n color: '#94a3b8',\n fontSize: '11px',\n fontFamily: '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\n borderRadius: '6px',\n pointerEvents: 'none',\n });\n document.body.appendChild(banner);\n }\n\n function removeDisclaimer() {\n document.getElementById(DISCLAIMER_ID)?.remove();\n }\n\n // -------------------------------------------------------------------------\n // Core: simplify page — manifest first, live fallback for misses\n // -------------------------------------------------------------------------\n\n async function simplifyPage() {\n const elements = getTextElements();\n if (elements.length === 0) return;\n\n // Abort any still-running previous run, then start a new one.\n // We hold a LOCAL reference (runController/runSignal) for this run so\n // post-await checks stay correct even if `abortController` is nulled\n // mid-flight by deactivate(). isAborted() also OR's against `!enabled`\n // so a deactivate that races past abort() still short-circuits the run.\n abortController?.abort();\n const runController = new AbortController();\n abortController = runController;\n const runSignal = runController.signal;\n const isAborted = () => runSignal.aborted || !enabled;\n\n savedParagraphs = [];\n skippedBlocks = 0;\n\n // Set up observers\n setupResizeObserver();\n setupMutationObserver();\n\n const { siteKey, proxyUrl } = getConfig();\n let fromCache = 0;\n let remaining = elements;\n // Track stale blocks — these MUST be re-generated by AI; if AI fails, skip them\n const staleBlocks = new Set<HTMLElement>();\n // Track manifest-applied elements — used by the Safety Gate to build the\n // content-anchor set that distinguishes real content from boilerplate.\n let appliedFromManifest: HTMLElement[] = [];\n\n const DEBUG = !!(window as any).__ACCESSIFY_DEBUG;\n\n // --- DIAG: init run ---\n if (DIAG()) diagInit(window.location.pathname, siteKey || null);\n if (diagRun) diagRun.elementsFound = elements.length;\n\n // -- Phase 1: Try manifest (instant, no AI calls) -------------------------\n if (siteKey) {\n showProgress(0, elements.length);\n const manifest = await fetchManifest(siteKey, proxyUrl, runSignal);\n\n if (DEBUG) console.log(`[Accessify] Manifest: ${manifest?.blocks?.length ?? 0} blocks for ${elements.length} elements`);\n // --- DIAG: manifest result ---\n if (diagRun) {\n diagRun.manifestLoaded = !!manifest?.blocks?.length;\n diagRun.manifestBlocks = manifest?.blocks?.length ?? 0;\n diagRun.siteMode = currentSiteMode;\n }\n\n if (isAborted()) { if (DIAG()) diagFinish(); return; }\n\n if (manifest?.blocks?.length) {\n // Server manifest is authoritative — clear old client cache to prevent\n // stale hallucinations from previous live AI runs overriding server data\n await clearClientCacheForPage();\n if (isAborted()) { if (DIAG()) diagFinish(); return; }\n\n const result = applyManifestBlocks(manifest, elements);\n fromCache = result.applied.length;\n appliedFromManifest = result.applied;\n if (DEBUG) console.log(`[Accessify] Manifest hit: ${fromCache}, stale: ${result.stale.length}, remaining: ${result.remaining.length}`);\n // Stale blocks get prepended to remaining so AI handles them\n for (const el of result.stale) staleBlocks.add(el);\n remaining = [...result.stale, ...result.remaining];\n\n // Populate client cache with (correct) manifest results\n for (const block of manifest.blocks) {\n if (block.result) {\n setClientCached(clientCacheKey(block.blockHash), block.result);\n }\n }\n\n // --- DIAG: Phase 1 counters ---\n if (diagRun) {\n diagRun.phase1Applied = result.applied.length;\n diagRun.phase1Stale = result.stale.length;\n }\n\n if (remaining.length === 0) {\n // All blocks were in the manifest — done instantly\n showDone(fromCache, fromCache);\n if (DIAG()) diagFinish();\n return;\n }\n }\n }\n\n // -- Phase 1.5: Check client-side IndexedDB cache --------------------------\n {\n const stillRemaining: HTMLElement[] = [];\n for (const el of remaining) {\n if (isAborted()) return;\n const text = el.dataset.accessifyOriginal || el.textContent?.trim() || '';\n if (text.length < 20) { stillRemaining.push(el); continue; }\n const blockHash = hashText(text);\n const cached = await getClientCached(clientCacheKey(blockHash));\n if (isAborted()) return;\n if (cached) {\n const success = safeReplace(el, cached, blockHash);\n if (success) {\n fromCache++;\n // --- DIAG ---\n if (DIAG()) {\n diagBlock({ hash: blockHash, text: text.slice(0, 60), tag: el.tagName, idbHit: true, replaced: true, source: 'idb' });\n if (diagRun) diagRun.phase15IdbHits++;\n }\n } else {\n stillRemaining.push(el);\n if (DIAG()) diagBlock({ hash: blockHash, text: text.slice(0, 60), tag: el.tagName, idbHit: true, replaced: false, skipReason: 'layout-unsafe', source: 'skipped' });\n }\n } else {\n stillRemaining.push(el);\n // (no diag entry yet — will be created in Phase 2 or safety gate)\n }\n }\n remaining = stillRemaining;\n\n if (remaining.length === 0) {\n showDone(fromCache, fromCache);\n if (DIAG()) diagFinish();\n return;\n }\n }\n\n // -- Phase 2: Live fallback for remaining blocks --------------------------\n // SAFETY GATE: Decide which of the still-unmatched elements should go to\n // live AI versus be skipped as boilerplate.\n //\n // The old rule was \"manifest loaded + element not in manifest → skip as\n // nav/footer/sidebar\". That collapsed two different populations into one:\n // (a) truly structural boilerplate (nav, menu, legal footer) that the\n // crawler correctly excluded — should stay skipped\n // (b) content that drifted away from the manifest (new paragraph,\n // rewritten block, Framer layout change) — should go to AI\n // The content-anchor heuristic separates them: ancestors of applied\n // manifest blocks form a \"content region\" set. Remaining elements that\n // sit inside that set are near real content and treated as candidates\n // for live AI. Elements outside any content region plus elements inside\n // obvious navigation landmarks stay skipped.\n //\n // Without a manifest, the old behaviour applies:\n // manual mode → skip all live AI\n // auto mode → live AI for everything (first visit before any crawl)\n const manifestWasLoaded = fromCache > 0 || (siteKey && staleBlocks.size > 0);\n\n if (manifestWasLoaded && remaining.length > 0) {\n const contentAnchors = buildContentAnchors(appliedFromManifest);\n const skipAsBoilerplate: HTMLElement[] = [];\n const keepForAI: HTMLElement[] = [];\n\n for (const el of remaining) {\n // Stale blocks always go to AI — their content is known to have drifted.\n if (staleBlocks.has(el)) { keepForAI.push(el); continue; }\n // Obvious navigation landmarks always skip, even if near content.\n if (isObviousBoilerplate(el)) { skipAsBoilerplate.push(el); continue; }\n // Content-anchor decision: inside applied-ancestor region?\n // auto mode → keep (likely new/changed content, let AI resolve)\n // manual mode → still skip (manifest is authoritative)\n if (currentSiteMode === 'auto' && isNearContent(el, contentAnchors)) {\n keepForAI.push(el);\n } else {\n skipAsBoilerplate.push(el);\n }\n }\n\n if (skipAsBoilerplate.length > 0 && DEBUG) {\n console.info(\n `[Accessify] Safety Gate: ${skipAsBoilerplate.length} boilerplate skipped, ` +\n `${keepForAI.length} queued for AI (stale + near-content).`,\n );\n }\n\n // --- DIAG: log skipped boilerplate ---\n if (DIAG()) {\n for (const el of skipAsBoilerplate) {\n const t = el.dataset.accessifyOriginal || el.textContent?.trim() || '';\n const reason = isObviousBoilerplate(el)\n ? 'structural-boilerplate(nav/menu)'\n : currentSiteMode === 'auto'\n ? 'no-content-anchor'\n : `manual-mode(siteMode=${currentSiteMode})`;\n diagBlock({ hash: hashText(t), text: t.slice(0, 60), tag: el.tagName, skipReason: reason, source: 'skipped' });\n }\n }\n\n // Clear loading on skipped blocks\n for (const el of skipAsBoilerplate) clearLoading(el);\n remaining = keepForAI;\n\n if (remaining.length === 0) {\n // All manifest blocks applied, no stale or near-content leftovers — done\n showDone(fromCache, fromCache);\n showDisclaimer();\n if (DIAG()) diagFinish();\n return;\n }\n } else if (currentSiteMode !== 'auto' && remaining.length > 0) {\n // No manifest — manual mode: skip all live AI\n if (DEBUG) {\n console.info(\n `[Accessify] siteMode=${currentSiteMode} — skipping live AI for ${remaining.length} uncached block(s). ` +\n `Site owner: trigger a crawl from the dashboard or switch to 'auto' mode.`,\n );\n }\n // --- DIAG: log all blocks skipped by manual mode ---\n if (DIAG()) {\n for (const el of remaining) {\n const t = el.dataset.accessifyOriginal || el.textContent?.trim() || '';\n diagBlock({ hash: hashText(t), text: t.slice(0, 60), tag: el.tagName, skipReason: `manual-mode(siteMode=${currentSiteMode})`, source: 'skipped' });\n }\n }\n if (staleBlocks.size > 0) skippedBlocks += staleBlocks.size;\n // Clear loading state on blocks we won't touch\n for (const el of remaining) clearLoading(el);\n if (fromCache > 0) {\n showDone(fromCache, fromCache);\n showDisclaimer();\n } else {\n removeProgress();\n }\n if (DIAG()) diagFinish();\n return;\n }\n\n // If no AI service available: stale blocks must be skipped (never show outdated text),\n // non-stale remaining blocks also can't be simplified → show appropriate message\n if (!aiService && remaining.length > 0) {\n // Skip all stale blocks — outdated simplifications must NOT be shown\n if (staleBlocks.size > 0) {\n skippedBlocks += staleBlocks.size;\n console.info(`[Accessify] ${staleBlocks.size} stale block(s) skipped — no AI service to regenerate`);\n }\n if (fromCache > 0) {\n showDone(fromCache, fromCache);\n } else {\n showProgress(0, 1);\n showError(lang.startsWith('de')\n ? 'Kein API-Key konfiguriert'\n : 'No API key configured');\n }\n return;\n }\n\n // Mark remaining elements as loading\n for (const el of remaining) {\n markLoading(el);\n }\n\n const totalElements = elements.length;\n let completed = fromCache;\n let totalSimplified = fromCache;\n const isStale = (el: HTMLElement) => staleBlocks.has(el);\n\n showProgress(completed, totalElements, skippedBlocks);\n\n for (const el of remaining) {\n if (isAborted()) break;\n\n const text = el.dataset.accessifyOriginal || el.textContent?.trim() || '';\n if (text.length < 20) {\n clearLoading(el);\n completed++;\n showProgress(completed, totalElements, skippedBlocks);\n continue;\n }\n\n // Skip short credit/attribution lines in live AI (hallucination risk)\n if (text.length < 60 && CREDIT_LINE_PATTERN.test(text)) {\n clearLoading(el);\n completed++;\n skippedBlocks++;\n showProgress(completed, totalElements, skippedBlocks);\n continue;\n }\n\n const blockHash = hashText(text);\n // --- DIAG: track this AI block ---\n let diagEntry: DiagBlock | null = null;\n if (DIAG()) {\n diagEntry = diagBlock({ hash: blockHash, text: text.slice(0, 60), tag: el.tagName, liveAI: true, stale: isStale(el), source: 'ai' });\n if (diagRun) diagRun.phase2AICalls++;\n }\n\n try {\n const simplified = await aiService!.simplifyText(text, level, lang);\n if (isAborted()) return;\n\n if (simplified && simplified !== text) {\n const success = safeReplace(el, simplified, blockHash);\n if (success) {\n totalSimplified++;\n\n // Cache client-side for instant reload\n setClientCached(clientCacheKey(blockHash), simplified);\n\n // Auto-persist live result back to manifest (non-blocking)\n if (siteKey) {\n persistToManifest(siteKey, blockHash, text, simplified, buildSelector(el), proxyUrl);\n }\n if (diagEntry) diagEntry.replaced = true;\n } else {\n if (diagEntry) { diagEntry.replaced = false; diagEntry.skipReason = 'layout-unsafe'; }\n }\n } else {\n if (diagEntry) { diagEntry.replaced = false; diagEntry.skipReason = simplified ? 'ai-returned-identical' : 'ai-returned-empty'; }\n }\n } catch (err: any) {\n if (diagEntry) { diagEntry.skipReason = `error:${err?.message?.slice(0, 50) || 'unknown'}`; diagEntry.replaced = false; }\n if (diagRun) diagRun.phase2AIErrors++;\n // Abort immediately on auth errors — no point retrying\n if (err?.message?.includes('401') || err?.message?.includes('403')) {\n console.warn('[Accessify] AI auth failed, stopping live simplification');\n // Stale blocks that haven't been processed yet must be counted as skipped\n for (const r of remaining) {\n if (isStale(r) && !r.hasAttribute(SIMPLIFIED_ATTR)) skippedBlocks++;\n }\n clearLoading(el);\n completed++;\n break;\n }\n if (err?.message?.includes('429')) {\n // Rate limited — retry once after delay\n await new Promise((r) => setTimeout(r, 8000));\n if (isAborted()) return;\n try {\n const retry = await aiService!.simplifyText(text, level, lang);\n if (isAborted()) return;\n if (retry && retry !== text) {\n const success = safeReplace(el, retry, blockHash);\n if (success) {\n totalSimplified++;\n setClientCached(clientCacheKey(blockHash), retry);\n if (siteKey) {\n persistToManifest(siteKey, blockHash, text, retry, buildSelector(el), proxyUrl);\n }\n if (diagEntry) { diagEntry.replaced = true; diagEntry.skipReason = null; }\n }\n }\n } catch {\n // Stale block that failed AI regeneration → skip it entirely\n if (isStale(el)) {\n skippedBlocks++;\n console.warn('[Accessify] Stale block skipped — AI retry failed:', text.slice(0, 50));\n }\n }\n } else {\n // Any other AI error on a stale block → skip (don't show outdated text)\n if (isStale(el)) {\n skippedBlocks++;\n console.warn('[Accessify] Stale block skipped — AI error:', text.slice(0, 50));\n } else {\n console.warn('[Accessify] Failed to simplify:', err);\n }\n }\n } finally {\n clearLoading(el);\n completed++;\n if (!isAborted()) {\n showProgress(completed, totalElements, skippedBlocks);\n // Throttle: small delay between requests to respect free-tier rate limits\n await new Promise((r) => setTimeout(r, 600));\n }\n }\n // After the throttle sleep, check abort again before continuing the loop.\n if (isAborted()) break;\n }\n\n // Clean up any remaining loading indicators (e.g. after auth-error break)\n for (const el of remaining) {\n clearLoading(el);\n }\n\n if (!isAborted()) {\n showDone(totalSimplified, fromCache);\n // Show AI disclaimer on the page\n if (totalSimplified > 0 || fromCache > 0) {\n showDisclaimer();\n }\n }\n // --- DIAG: finalize run ---\n if (DIAG()) diagFinish();\n }\n\n // -------------------------------------------------------------------------\n // Restore original text — clean teardown\n // -------------------------------------------------------------------------\n\n function restorePage() {\n // Disconnect observers first to avoid triggering them during restore\n disconnectObservers();\n\n const restoreReason = abortController?.signal.aborted ? 'abort(navigation/toggle)' : 'toggle-off';\n\n // Defensive sweep: if a late write slipped past every abort guard (e.g. a\n // racing AI response that resolved between our check and safeReplace), the\n // element would carry SIMPLIFIED_ATTR but not appear in savedParagraphs.\n // Scan the DOM once and fold any such orphan into the restore list before\n // the main loop runs.\n const trackedEls = new Set(savedParagraphs.map((s) => s.el));\n document.querySelectorAll(`[${SIMPLIFIED_ATTR}]`).forEach((node) => {\n const el = node as HTMLElement;\n if (trackedEls.has(el)) return;\n // Best-effort: use the saved original text if we have it. We prefer\n // textContent over innerHTML here because orphans have no savedTextNodes\n // to work with and innerHTML would need the pre-replace markup we never\n // stored for this element.\n const originalText = el.dataset.accessifyOriginal;\n if (originalText && el.textContent !== originalText) {\n el.textContent = originalText;\n }\n el.removeAttribute(SIMPLIFIED_ATTR);\n el.removeAttribute(ORIGINAL_ATTR);\n el.removeAttribute(BLOCK_HASH_ATTR);\n el.removeAttribute('data-accessify-replacing');\n clearLoading(el);\n if (el.dataset.accessifyOrigTransform) {\n el.style.textTransform = el.dataset.accessifyOrigTransform;\n delete el.dataset.accessifyOrigTransform;\n }\n if (DIAG() && diagRun) {\n diagRun.restoreLog.push({\n ts: new Date().toISOString(),\n hash: el.getAttribute(BLOCK_HASH_ATTR) || 'orphan',\n text: originalText?.slice(0, 60) || '',\n textNodeRestore: false,\n innerHtmlFallback: false,\n reason: `${restoreReason}+orphan-sweep`,\n });\n }\n });\n\n for (const { el, originalHtml, savedTextNodes, ancestorPatches } of savedParagraphs) {\n const blockHash = el.getAttribute(BLOCK_HASH_ATTR) || 'unknown';\n let textNodeRestore = false;\n let innerHtmlFallback = false;\n\n // Prefer text-node restore (React/Framer-safe: no DOM structure change)\n if (savedTextNodes.length > 0) {\n let allRestored = true;\n for (const sn of savedTextNodes) {\n if (sn.node.parentNode) {\n sn.node.data = sn.original; // NUR .data setzen!\n } else {\n allRestored = false;\n }\n }\n // Fallback to innerHTML only if text nodes were removed by framework re-render\n if (!allRestored) {\n el.innerHTML = originalHtml;\n innerHtmlFallback = true;\n } else {\n textNodeRestore = true;\n }\n } else {\n el.innerHTML = originalHtml;\n innerHtmlFallback = true;\n }\n\n // --- DIAG: log restore ---\n if (DIAG() && diagRun) {\n diagRun.restoreLog.push({\n ts: new Date().toISOString(),\n hash: blockHash,\n text: (el.textContent?.trim() || '').slice(0, 60),\n textNodeRestore,\n innerHtmlFallback,\n reason: innerHtmlFallback ? `${restoreReason}+innerHTML-fallback(text-nodes-detached)` : restoreReason,\n });\n }\n\n el.removeAttribute(ORIGINAL_ATTR);\n el.removeAttribute(SIMPLIFIED_ATTR);\n el.removeAttribute(BLOCK_HASH_ATTR);\n el.removeAttribute('data-accessify-replacing');\n clearLoading(el);\n\n // Restore original text-transform if we overrode it\n if (el.dataset.accessifyOrigTransform) {\n el.style.textTransform = el.dataset.accessifyOrigTransform;\n delete el.dataset.accessifyOrigTransform;\n }\n\n // Revert any ancestor patches (max-height, overflow changes)\n revertPatches(ancestorPatches);\n }\n\n // --- DIAG: log restore summary ---\n if (DIAG() && diagRun) {\n const rl = diagRun.restoreLog;\n console.log(\n `%c[Accessify DIAG] Restore: ${rl.length} blocks, ${rl.filter(r => r.textNodeRestore).length} text-node, ${rl.filter(r => r.innerHtmlFallback).length} innerHTML-fallback`,\n 'color: #f59e0b; font-weight: bold',\n );\n if (rl.some(r => r.innerHtmlFallback)) {\n console.warn('[Accessify DIAG] innerHTML fallbacks detected — framework may have detached text nodes:');\n console.table(rl.filter(r => r.innerHtmlFallback));\n }\n }\n\n savedParagraphs = [];\n skippedBlocks = 0;\n }\n\n // -------------------------------------------------------------------------\n // Preferences\n // -------------------------------------------------------------------------\n\n function loadPreferences() {\n try {\n const saved = localStorage.getItem(STORAGE_KEY);\n if (saved) {\n const parsed = JSON.parse(saved);\n if (parsed.level === 'einfache' || parsed.level === 'leichte') {\n level = parsed.level;\n }\n }\n } catch { /* use defaults */ }\n }\n\n function savePreferences() {\n localStorage.setItem(STORAGE_KEY, JSON.stringify({ level }));\n }\n\n // -------------------------------------------------------------------------\n // Module lifecycle\n // -------------------------------------------------------------------------\n\n // -------------------------------------------------------------------------\n // SPA navigation detection — re-simplify on URL change\n // -------------------------------------------------------------------------\n\n let lastUrl = '';\n let navigationCleanup: (() => void) | null = null;\n\n function setupNavigationWatcher() {\n lastUrl = location.href;\n\n const onUrlChange = () => {\n if (!enabled || location.href === lastUrl) return;\n lastUrl = location.href;\n if ((window as any).__ACCESSIFY_DEBUG) {\n console.log('[Accessify] URL changed, re-simplifying:', lastUrl);\n }\n // Abort current run, restore old page, re-simplify new content\n abortController?.abort();\n restorePage();\n removeProgress();\n // Small delay for new DOM to settle\n setTimeout(() => { if (enabled) simplifyPage(); }, 500);\n };\n\n // popstate fires on back/forward\n window.addEventListener('popstate', onUrlChange);\n\n // Intercept pushState / replaceState for SPA routers\n const origPush = history.pushState.bind(history);\n const origReplace = history.replaceState.bind(history);\n history.pushState = function(...args: any) {\n origPush(...args);\n onUrlChange();\n };\n history.replaceState = function(...args: any) {\n origReplace(...args);\n onUrlChange();\n };\n\n navigationCleanup = () => {\n window.removeEventListener('popstate', onUrlChange);\n history.pushState = origPush;\n history.replaceState = origReplace;\n };\n }\n\n function activate() {\n if (enabled) return;\n enabled = true;\n loadPreferences();\n setupNavigationWatcher();\n simplifyPage();\n }\n\n function deactivate() {\n // ORDER MATTERS: flip `enabled` first so any in-flight isAborted() checks\n // inside simplifyPage short-circuit even if abort() has not yet propagated\n // through the microtask queue. Only THEN abort + null the controller.\n enabled = false;\n abortController?.abort();\n abortController = null;\n navigationCleanup?.();\n navigationCleanup = null;\n restorePage();\n removeProgress();\n removeDisclaimer();\n }\n\n return {\n id: 'text-simplify',\n name: () => (lang.startsWith('de') ? 'Text vereinfachen' : 'Simplify Text'),\n description: lang.startsWith('de')\n ? 'Text in Einfache Sprache umwandeln'\n : 'Simplify text for easier understanding',\n icon: 'simplify-text',\n category: 'ai',\n activate,\n deactivate,\n getState: (): FeatureState => ({\n id: 'text-simplify',\n enabled,\n value: { level } as TextSimplifyState,\n }),\n setState: (newState: Partial<TextSimplifyState>) => {\n if (newState.level && (newState.level === 'einfache' || newState.level === 'leichte')) {\n level = newState.level;\n savePreferences();\n }\n },\n };\n}\n"],"names":["clipAncestor"],"mappings":"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"}
|