accessify-widget 0.3.48 → 0.3.50

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "accessify-widget",
3
- "version": "0.3.48",
3
+ "version": "0.3.50",
4
4
  "description": "Accessify-Widget accessibility widget for any website",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/maddesv1-ctrl/accessify-widget",
@@ -1 +0,0 @@
1
- {"version":3,"file":"animation-stop-_chC8bg1.js","sources":["../src/features/animation-stop.ts"],"sourcesContent":["import type { FeatureModule, FeatureState } from '../types';\n\n/**\n * Animation Stop – V4 (Clean 3-Layer Approach)\n *\n * Layer 1: CSS injection — kill all CSS animations/transitions via 0.001ms duration\n * Layer 2: Web Animations API — finish() all running WAAPI animations (Framer Motion etc.)\n * Layer 3: JS library detection — GSAP globalTimeline, inline-style freeze for rAF-based libs\n * Layer 4: Media — pause videos, freeze GIFs, stop SVG SMIL, stop marquees\n *\n * + MutationObserver for dynamic content\n * + Periodic re-scan (every 500ms) for frameworks that create new animations\n *\n * DOES NOT:\n * - Monkey-patch IntersectionObserver (breaks lazy loading)\n * - Override requestAnimationFrame (breaks entire page)\n * - Override Element.prototype.animate() (too invasive)\n * - Override matchMedia (too invasive)\n * - Set opacity:1 !important via CSS (breaks modals/hidden elements)\n * - Use animation:none or transition:none (breaks JS events)\n */\nexport default function createAnimationStopModule(): FeatureModule {\n let enabled = false;\n const STYLE_ID = 'a11y-stop-animations';\n const STORAGE_KEY = 'accessify-animation-stop';\n\n // --- State ---\n let mutationObserver: MutationObserver | null = null;\n let scanInterval: ReturnType<typeof setInterval> | null = null;\n let scrollHandler: (() => void) | null = null;\n let originalVideoPlay: typeof HTMLVideoElement.prototype.play | null = null;\n let originalAnimate: typeof Element.prototype.animate | null = null;\n let pausedVideos: HTMLVideoElement[] = [];\n let gifOriginals = new Map<HTMLImageElement, string>();\n\n // =========================================================================\n // Layer 0: Element.prototype.animate() intercept\n //\n // This is THE critical piece for Framer Motion. Framer uses WAAPI\n // (Element.prototype.animate()) for ALL visual animations — hover,\n // appear, scroll, transitions. CSS rules do NOT affect WAAPI.\n // We intercept at creation time and force duration to 0.001ms\n // so every animation completes instantly.\n // =========================================================================\n\n function patchAnimate() {\n if (originalAnimate) return;\n originalAnimate = Element.prototype.animate;\n\n Element.prototype.animate = function (\n this: Element,\n keyframes: Keyframe[] | PropertyIndexedKeyframes | null,\n options?: number | KeyframeAnimationOptions,\n ): Animation {\n if (!enabled) {\n return originalAnimate!.call(this, keyframes, options);\n }\n\n // Force instant completion: duration 0.001ms, no delay, 1 iteration, fill forwards\n let opts: KeyframeAnimationOptions;\n if (typeof options === 'number') {\n opts = { duration: 0.001, fill: 'forwards' as FillMode };\n } else {\n opts = {\n ...(options || {}),\n duration: 0.001,\n delay: 0,\n iterations: 1,\n fill: 'forwards' as FillMode,\n };\n // Remove iterationStart, endDelay etc. that could interfere\n delete (opts as any).iterationStart;\n delete (opts as any).endDelay;\n }\n\n const anim = originalAnimate!.call(this, keyframes, opts);\n // Immediately finish so element lands in end-state\n try { anim.finish(); } catch { /* ignore */ }\n return anim;\n };\n }\n\n function restoreAnimate() {\n if (originalAnimate) {\n Element.prototype.animate = originalAnimate;\n originalAnimate = null;\n }\n }\n\n // =========================================================================\n // Layer 1: CSS Injection\n // =========================================================================\n\n function injectStyles() {\n if (document.getElementById(STYLE_ID)) return;\n const style = document.createElement('style');\n style.id = STYLE_ID;\n style.textContent = `\n *, *::before, *::after {\n animation-duration: 0.001ms !important;\n animation-iteration-count: 1 !important;\n animation-delay: 0s !important;\n animation-fill-mode: forwards !important;\n transition-duration: 0.001ms !important;\n transition-delay: 0s !important;\n scroll-behavior: auto !important;\n animation-timeline: auto !important;\n scroll-timeline: none !important;\n }\n `;\n document.head.appendChild(style);\n }\n\n function removeStyles() {\n document.getElementById(STYLE_ID)?.remove();\n }\n\n // =========================================================================\n // Layer 2: Web Animations API — finish all WAAPI animations\n // =========================================================================\n\n function finishAllAnimations() {\n try {\n const animations = document.getAnimations?.();\n if (!animations) return;\n for (const anim of animations) {\n try {\n anim.finish();\n } catch {\n try { anim.cancel(); } catch { /* ignore */ }\n }\n }\n } catch { /* getAnimations not supported */ }\n }\n\n // =========================================================================\n // Layer 3: JS animation library detection + inline style freeze\n // =========================================================================\n\n function patchGSAP() {\n const g = (window as any).gsap;\n if (g?.globalTimeline) {\n try {\n g.globalTimeline.timeScale(0);\n (window as any)._a11yGsapPatched = true;\n } catch { /* ignore */ }\n }\n }\n\n function restoreGSAP() {\n const g = (window as any).gsap;\n if ((window as any)._a11yGsapPatched && g?.globalTimeline) {\n try {\n g.globalTimeline.timeScale(1);\n delete (window as any)._a11yGsapPatched;\n } catch { /* ignore */ }\n }\n }\n\n /**\n * Force hidden-by-animation elements visible.\n * Only touches INLINE styles — never sets CSS !important on opacity etc.\n * which would break modals and intentionally hidden elements.\n */\n function forceHiddenElementsVisible() {\n // Only process elements near the viewport to avoid layout chaos on\n // Framer/scroll-animated pages where dozens of off-screen elements\n // are hidden via opacity:0 / translateY as part of scroll reveals.\n const viewH = window.innerHeight;\n const margin = viewH * 1.5; // 1.5x viewport ahead\n\n const all = document.querySelectorAll<HTMLElement>('*');\n for (const el of all) {\n if (el.tagName === 'SCRIPT' || el.tagName === 'STYLE' || el.tagName === 'NOSCRIPT' || el.tagName === 'META' || el.tagName === 'LINK') continue;\n if (el.closest('#accessify-root') || el.closest('accessify-widget')) continue;\n if (el.dataset.a11yOrigStyle) continue; // Already processed\n\n const computed = window.getComputedStyle(el);\n if (computed.display === 'none') continue; // Intentionally hidden\n\n // Skip elements far below the current scroll position —\n // they will be handled when the user scrolls to them (periodic re-scan).\n const rect = el.getBoundingClientRect();\n if (rect.top > viewH + margin) continue; // Too far below viewport\n // Also skip if entirely above viewport (already scrolled past)\n if (rect.bottom < -margin) continue;\n\n const inline = el.style;\n let needsFix = false;\n const originals: Record<string, string> = {};\n\n // Inline opacity 0 or near-0 (computed, not stylesheet)\n const opacityVal = parseFloat(computed.opacity);\n if (opacityVal < 0.1 && inline.opacity !== '') {\n originals.opacity = inline.opacity;\n el.style.opacity = '1';\n needsFix = true;\n } else if (opacityVal < 0.1 && el.hasAttribute('data-framer-appear-id')) {\n // Framer appear elements: opacity set via WAAPI or framework\n originals.opacity = inline.opacity || '';\n el.style.opacity = '1';\n needsFix = true;\n }\n\n // Inline transform with translate that moves element off-screen\n if (inline.transform && /translate[XY]\\([^0]/.test(inline.transform)) {\n originals.transform = inline.transform;\n el.style.transform = 'none';\n needsFix = true;\n }\n\n // Inline visibility hidden\n if (inline.visibility === 'hidden') {\n originals.visibility = inline.visibility;\n el.style.visibility = 'visible';\n needsFix = true;\n }\n\n // Inline clip-path that hides element\n if (inline.clipPath && inline.clipPath !== 'none') {\n originals.clipPath = inline.clipPath;\n el.style.clipPath = 'none';\n needsFix = true;\n }\n\n if (needsFix) {\n el.dataset.a11yOrigStyle = JSON.stringify(originals);\n }\n }\n }\n\n function restoreHiddenElements() {\n document.querySelectorAll<HTMLElement>('[data-a11y-orig-style]').forEach(el => {\n try {\n const orig: Record<string, string> = JSON.parse(el.dataset.a11yOrigStyle || '{}');\n for (const [prop, val] of Object.entries(orig)) {\n if (val === '') {\n el.style.removeProperty(prop);\n } else {\n (el.style as any)[prop] = val;\n }\n }\n } catch { /* malformed JSON */ }\n delete el.dataset.a11yOrigStyle;\n });\n }\n\n /**\n * Freeze inline-animated styles so scroll-handlers can't update them.\n * Takes a snapshot of current inline transforms and enforces them on scroll.\n */\n function freezeScrollAnimations() {\n document.querySelectorAll<HTMLElement>('[style]').forEach(el => {\n if (el.closest('#accessify-root') || el.closest('accessify-widget')) return;\n if (el.dataset.a11yFrozenStyle) return;\n const s = el.style;\n if (s.transform || s.opacity) {\n el.dataset.a11yFrozenStyle = JSON.stringify({\n transform: s.transform || '',\n opacity: s.opacity || '',\n });\n }\n });\n\n if (!scrollHandler) {\n scrollHandler = () => {\n if (!enabled) return;\n document.querySelectorAll<HTMLElement>('[data-a11y-frozen-style]').forEach(el => {\n try {\n const frozen = JSON.parse(el.dataset.a11yFrozenStyle || '{}');\n if (frozen.transform && el.style.transform !== frozen.transform) {\n el.style.transform = frozen.transform;\n }\n } catch { /* ignore */ }\n });\n };\n window.addEventListener('scroll', scrollHandler, { passive: true, capture: true });\n }\n }\n\n function unfreezeScrollAnimations() {\n if (scrollHandler) {\n window.removeEventListener('scroll', scrollHandler, true);\n scrollHandler = null;\n }\n document.querySelectorAll<HTMLElement>('[data-a11y-frozen-style]').forEach(el => {\n delete el.dataset.a11yFrozenStyle;\n });\n }\n\n // =========================================================================\n // Layer 4: Media\n // =========================================================================\n\n function pauseAllVideos() {\n document.querySelectorAll<HTMLVideoElement>('video').forEach(v => {\n if (!v.paused) {\n v.dataset.a11yPaused = 'true';\n v.pause();\n pausedVideos.push(v);\n }\n });\n }\n\n function interceptVideoPlay() {\n if (originalVideoPlay) return;\n originalVideoPlay = HTMLVideoElement.prototype.play;\n HTMLVideoElement.prototype.play = function () {\n if (enabled) return Promise.resolve();\n return originalVideoPlay!.call(this);\n };\n }\n\n function restoreVideoPlay() {\n if (originalVideoPlay) {\n HTMLVideoElement.prototype.play = originalVideoPlay;\n originalVideoPlay = null;\n }\n }\n\n function resumeAllVideos() {\n restoreVideoPlay();\n document.querySelectorAll<HTMLVideoElement>('video[data-a11y-paused]').forEach(v => {\n try { v.play(); } catch { /* gone */ }\n delete v.dataset.a11yPaused;\n });\n pausedVideos = [];\n }\n\n function freezeGif(img: HTMLImageElement) {\n const src = (img.currentSrc || img.src || '').toLowerCase();\n if (!src.match(/\\.gif(\\?|$)/i)) return;\n if (gifOriginals.has(img)) return;\n\n const doFreeze = () => {\n try {\n const w = img.naturalWidth || img.width;\n const h = img.naturalHeight || img.height;\n if (w === 0 || h === 0) return;\n const c = document.createElement('canvas');\n c.width = w; c.height = h;\n const ctx = c.getContext('2d');\n if (!ctx) return;\n ctx.drawImage(img, 0, 0);\n gifOriginals.set(img, img.src);\n img.src = c.toDataURL('image/png');\n } catch { /* CORS */ }\n };\n\n if (img.complete && img.naturalWidth > 0) doFreeze();\n else img.addEventListener('load', doFreeze, { once: true });\n }\n\n function freezeAllGifs() {\n document.querySelectorAll<HTMLImageElement>('img').forEach(freezeGif);\n }\n\n function restoreAllGifs() {\n for (const [img, src] of gifOriginals) {\n try { img.src = src; } catch { /* gone */ }\n }\n gifOriginals.clear();\n }\n\n function pauseSVG() {\n document.querySelectorAll('svg').forEach(s => {\n try { (s as any).pauseAnimations?.(); } catch { /* ignore */ }\n });\n }\n\n function resumeSVG() {\n document.querySelectorAll('svg').forEach(s => {\n try { (s as any).unpauseAnimations?.(); } catch { /* ignore */ }\n });\n }\n\n function stopMarquees() {\n document.querySelectorAll('marquee').forEach(m => {\n try { (m as any).stop?.(); } catch { /* ignore */ }\n });\n }\n\n function startMarquees() {\n document.querySelectorAll('marquee').forEach(m => {\n try { (m as any).start?.(); } catch { /* ignore */ }\n });\n }\n\n // =========================================================================\n // MutationObserver — dynamic content\n // =========================================================================\n\n function setupObserver() {\n if (mutationObserver) return;\n mutationObserver = new MutationObserver(mutations => {\n if (!enabled) return;\n let hasNewContent = false;\n for (const m of mutations) {\n if (m.addedNodes.length > 0) { hasNewContent = true; break; }\n }\n if (hasNewContent) {\n finishAllAnimations();\n forceHiddenElementsVisible();\n pauseAllVideos();\n freezeAllGifs();\n pauseSVG();\n }\n });\n mutationObserver.observe(document.body, { childList: true, subtree: true });\n }\n\n function teardownObserver() {\n mutationObserver?.disconnect();\n mutationObserver = null;\n }\n\n // =========================================================================\n // Periodic re-scan — frameworks create new animations constantly\n // =========================================================================\n\n function startPeriodicScan() {\n if (scanInterval) return;\n scanInterval = setInterval(() => {\n if (!enabled) return;\n finishAllAnimations();\n }, 500);\n }\n\n function stopPeriodicScan() {\n if (scanInterval) {\n clearInterval(scanInterval);\n scanInterval = null;\n }\n }\n\n // =========================================================================\n // Lifecycle\n // =========================================================================\n\n function activate() {\n if (enabled) return;\n enabled = true;\n\n // Layer 0: Intercept Element.animate() — catches ALL new WAAPI animations instantly\n patchAnimate();\n\n // Layer 1: CSS\n injectStyles();\n\n // Layer 2: Kill all currently running WAAPI animations\n finishAllAnimations();\n\n // Layer 3: JS libs + inline style fixes\n patchGSAP();\n forceHiddenElementsVisible();\n freezeScrollAnimations();\n\n // Layer 4: Media\n pauseAllVideos();\n interceptVideoPlay();\n freezeAllGifs();\n pauseSVG();\n stopMarquees();\n\n // Observers\n setupObserver();\n startPeriodicScan();\n\n // Delayed re-scans for async rendering frameworks\n for (const delay of [50, 200, 500, 1000, 2000, 4000]) {\n setTimeout(() => {\n if (!enabled) return;\n finishAllAnimations();\n forceHiddenElementsVisible();\n freezeScrollAnimations();\n pauseAllVideos();\n freezeAllGifs();\n }, delay);\n }\n\n localStorage.setItem(STORAGE_KEY, 'true');\n }\n\n function deactivate() {\n enabled = false;\n\n stopPeriodicScan();\n teardownObserver();\n restoreAnimate();\n unfreezeScrollAnimations();\n restoreGSAP();\n restoreHiddenElements();\n startMarquees();\n resumeSVG();\n restoreAllGifs();\n resumeAllVideos();\n removeStyles();\n\n localStorage.removeItem(STORAGE_KEY);\n }\n\n return {\n id: 'animation-stop',\n name: () => 'Stop Animations',\n description: 'Pause all animations, transitions, and auto-playing videos (WCAG 2.3.1)',\n icon: 'animation-stop',\n category: 'visual',\n activate,\n deactivate,\n getState: (): FeatureState => ({ id: 'animation-stop', enabled }),\n setState: (state: { enabled: boolean }) => {\n if (state.enabled) activate();\n else deactivate();\n },\n };\n}\n"],"names":[],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"contrast-BsIJv7ad.js","sources":["../src/features/contrast.ts"],"sourcesContent":["// ---------------------------------------------------------------------------\n// Accessify – Contrast Modes (Accessibility Feature)\n// ---------------------------------------------------------------------------\n//\n// Three modes: light, dark, high — each as a clearly defined token set.\n//\n// Design principles:\n// 1. CONTENT elements (text, headings, links, buttons, form controls) get\n// explicit color overrides — these are what users need to read.\n// 2. STRUCTURAL elements (wrapper divs, sections, nav) get background only\n// via `html` and `body` — not individually. This prevents breaking\n// Framer/CSS-Grid/Flex layouts where divs use fit-content, overflow:hidden,\n// or fixed heights.\n// 3. The widget itself (inside #accessify-root / Shadow DOM) is NEVER touched.\n// 4. Focus indicators are defined in ALL modes, not just high contrast.\n// 5. border-color is NOT globally overridden — only on elements that have\n// visible borders for content separation (tables, fieldsets, hr).\n//\n// Selector strategy:\n// - `:where()` for zero-specificity (site styles can still win if needed,\n// but `!important` ensures accessibility overrides take priority).\n// - `body :where(...)` scopes to page content.\n// - `body *:not(#accessify-root):not(#accessify-root *)` used only where\n// we need broad text color coverage.\n// - Framer-safe: no background-color on generic div/section/article — only\n// on html/body. Content readability comes from text color, not from\n// painting every container.\n//\n// ---------------------------------------------------------------------------\n\nimport type { FeatureModule, FeatureState } from '../types';\n\ntype ContrastMode = 'off' | 'light' | 'dark' | 'high';\n\n// ---------------------------------------------------------------------------\n// Token definitions — each mode is a self-contained color system\n// ---------------------------------------------------------------------------\n\ninterface ContrastTokens {\n colorScheme: string;\n pageBg: string;\n text: string;\n textStrong: string; // headings, bold\n link: string;\n linkVisited: string;\n focusRing: string;\n borderContent: string; // for tables, hr, fieldset — NOT every div\n inputBg: string;\n inputBorder: string;\n imgFilter: string; // empty string = no filter\n}\n\nconst TOKENS: Record<Exclude<ContrastMode, 'off'>, ContrastTokens> = {\n light: {\n colorScheme: 'light',\n pageBg: '#fbf7ef',\n text: '#1a1a1a',\n textStrong: '#0d0d0d',\n link: '#0047b3',\n linkVisited: '#3d2b85',\n focusRing: '#0047b3',\n borderContent:'#888078',\n inputBg: '#ffffff',\n inputBorder: '#6f675d',\n imgFilter: '',\n },\n dark: {\n colorScheme: 'dark',\n pageBg: '#101418',\n text: '#e8e4dc',\n textStrong: '#ffffff',\n link: '#7cc8f5',\n linkVisited: '#b8a0e8',\n focusRing: '#7cc8f5',\n borderContent:'#3a4a58',\n inputBg: '#1a2028',\n inputBorder: '#4a5a68',\n imgFilter: 'brightness(0.90) contrast(1.06)',\n },\n high: {\n colorScheme: 'dark',\n pageBg: '#000000',\n text: '#ffffff',\n textStrong: '#ffffff',\n link: '#ffff00',\n linkVisited: '#ffff00',\n focusRing: '#ffff00',\n borderContent:'#ffffff',\n inputBg: '#000000',\n inputBorder: '#ffffff',\n imgFilter: 'contrast(1.25) brightness(0.95)',\n },\n};\n\n// ---------------------------------------------------------------------------\n// CSS generation — separated into logical layers\n// ---------------------------------------------------------------------------\n\n/** Elements to exclude from page-level contrast (the widget itself) */\nconst EX = ':not(#accessify-root):not(#accessify-root *)';\n\nfunction buildCSS(mode: Exclude<ContrastMode, 'off'>): string {\n const t = TOKENS[mode];\n const isHigh = mode === 'high';\n\n // ── Layer 1: Page background + strip all container backgrounds ──\n // Strategy: set pageBg ONLY on html/body, then make all other elements\n // transparent. This prevents opaque containers from hiding images on\n // Shopify/Framer sites (overlay divs, z-index stacking, etc.).\n // Form controls get their own bg back in Layer 4.\n const base = `\n html {\n color-scheme: ${t.colorScheme} !important;\n }\n html, body {\n background-color: ${t.pageBg} !important;\n }\n body *${EX} {\n background-color: transparent !important;\n background-image: none !important;\n }\n `;\n\n // ── Layer 2: Text color on ALL content elements ──\n // Using body * ensures text inside any container is readable, without\n // painting the container's background (which would break layouts).\n const textColor = `\n body *${EX} {\n color: ${t.text} !important;\n }\n body :where(h1, h2, h3, h4, h5, h6, strong, b, th)${EX} {\n color: ${t.textStrong} !important;\n }\n `;\n\n // ── Layer 3: Links — distinct color + underline for visibility ──\n const links = `\n body :where(a)${EX} {\n color: ${t.link} !important;\n text-decoration: underline !important;\n text-decoration-color: ${t.link} !important;\n ${isHigh ? 'text-decoration-thickness: 2px !important; text-underline-offset: 3px !important;' : ''}\n }\n body :where(a:visited)${EX} {\n color: ${t.linkVisited} !important;\n }\n body :where(a:hover, a:focus)${EX} {\n opacity: 0.85 !important;\n }\n `;\n\n // ── Layer 4: Form controls — explicit bg + border for readability ──\n // These get background because they ARE content, not structural wrappers.\n const forms = `\n body :where(input, textarea, select)${EX} {\n background-color: ${t.inputBg} !important;\n color: ${t.text} !important;\n border: 1px solid ${t.inputBorder} !important;\n box-shadow: none !important;\n }\n body :where(button, [role=\"button\"])${EX} {\n color: ${t.text} !important;\n border-color: ${t.inputBorder} !important;\n ${isHigh ? `background-color: ${t.pageBg} !important; border: 2px solid ${t.inputBorder} !important;` : ''}\n }\n `;\n\n // ── Layer 5: Focus indicators — in ALL modes, stronger in high ──\n const focus = `\n body :where(a, button, input, textarea, select, [tabindex])${EX}:focus-visible {\n outline: ${isHigh ? '3px' : '2px'} solid ${t.focusRing} !important;\n outline-offset: 2px !important;\n }\n `;\n\n // ── Layer 6: Content borders — only on elements that visually separate ──\n // NOT on generic divs — that destroys Framer card layouts.\n const borders = `\n body :where(table, th, td, hr, fieldset)${EX} {\n border-color: ${t.borderContent} !important;\n }\n `;\n\n // ── Layer 7: Images — gentle filter, skip decorative SVG icons ──\n const images = t.imgFilter ? `\n body :where(img, video, picture)${EX} {\n filter: ${t.imgFilter} !important;\n }\n ` : '';\n\n // ── Layer 8: High contrast extras ──\n const highExtras = isHigh ? `\n body :where(::placeholder)${EX} {\n color: #aaaaaa !important;\n opacity: 1 !important;\n }\n body :where(::selection) {\n background: #ffff00 !important;\n color: #000000 !important;\n }\n body :where(mark)${EX} {\n background: #ffff00 !important;\n color: #000000 !important;\n }\n ` : '';\n\n return base + textColor + links + forms + focus + borders + images + highExtras;\n}\n\n// ---------------------------------------------------------------------------\n// Module\n// ---------------------------------------------------------------------------\n\nexport default function createContrastModule(): FeatureModule {\n let currentMode: ContrastMode = 'off';\n const STYLE_ID = 'accessify-contrast';\n const STORAGE_KEY = 'accessify-contrast-mode';\n\n function applyStyles(mode: ContrastMode) {\n let styleEl = document.getElementById(STYLE_ID);\n if (mode === 'off') {\n styleEl?.remove();\n return;\n }\n if (!styleEl) {\n styleEl = document.createElement('style');\n styleEl.id = STYLE_ID;\n document.head.appendChild(styleEl);\n }\n styleEl.textContent = buildCSS(mode);\n }\n\n function activate() {\n const saved = localStorage.getItem(STORAGE_KEY) as ContrastMode;\n currentMode = saved === 'light' || saved === 'dark' || saved === 'high' ? saved : 'dark';\n applyStyles(currentMode);\n }\n\n function deactivate() {\n currentMode = 'off';\n applyStyles('off');\n localStorage.removeItem(STORAGE_KEY);\n }\n\n return {\n id: 'contrast',\n name: () => 'Contrast',\n description: 'Increase contrast for better readability',\n icon: 'contrast',\n category: 'visual',\n activate,\n deactivate,\n getState: (): FeatureState => ({ id: 'contrast', enabled: currentMode !== 'off', value: currentMode }),\n setState: (mode: ContrastMode) => {\n currentMode = mode === 'light' || mode === 'dark' || mode === 'high' ? mode : 'off';\n applyStyles(currentMode);\n if (currentMode === 'off') {\n localStorage.removeItem(STORAGE_KEY);\n } else {\n localStorage.setItem(STORAGE_KEY, currentMode);\n }\n },\n };\n}\n"],"names":[],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"text-simplify-CK2GFhq2.js","sources":["../src/features/text-simplify.ts"],"sourcesContent":["// ---------------------------------------------------------------------------\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';\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\ninterface RuntimeManifest {\n url: string;\n siteKey: string;\n feature: string;\n variant: string;\n blocks: ManifestBlock[];\n generatedAt: string;\n contentHash: string;\n}\n\nexport default function createTextSimplifyModule(\n aiService?: AIService,\n lang: string = 'de',\n): FeatureModule {\n let enabled = false;\n let level: SimplifyLevel = '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\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 // 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 const FAQ_CONTAINER_SELECTORS = 'details, [role=\"region\"][aria-labelledby], .faq-item, .accordion-item, .accordion__item';\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 = 'p, li, td, th, blockquote, figcaption, .hero-subtitle, h1, h2, h3, h4, h5, h6, summary, dt, dd';\n const elements: HTMLElement[] = [];\n const seen = new Set<HTMLElement>();\n\n document.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 const text = htmlEl.textContent?.trim() || '';\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 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 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 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 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 async function fetchManifest(siteKey: string, proxyUrl?: string): Promise<RuntimeManifest | null> {\n const cacheKey = `${siteKey}:${level}`;\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 try {\n const res = await fetch(url, { cache: 'no-cache' });\n if (!res.ok) { manifestCache.set(cacheKey, null); return null; }\n const data = await res.json() as RuntimeManifest;\n const result = data.blocks?.length ? data : null;\n manifestCache.set(cacheKey, result);\n return result;\n } catch {\n manifestCache.set(cacheKey, null);\n return null;\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 for (const el of elements) {\n const text = 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 } else {\n remaining.push(el);\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 } else {\n remaining.push(el);\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 }\n continue;\n }\n\n // No match at all — goes to live AI or stays unsimplified\n remaining.push(el);\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 } else {\n console.warn(`[Accessify] Persist failed: ${res.status}`);\n }\n }).catch((err) => {\n console.warn('[Accessify] Failed to persist live simplification:', err);\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 overlayEl.innerHTML = `<span style=\"color:#ef4444\">&#x2715;</span> ${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 abortController = new AbortController();\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\n const DEBUG = !!(window as any).__ACCESSIFY_DEBUG;\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);\n\n if (DEBUG) console.log(`[Accessify] Manifest: ${manifest?.blocks?.length ?? 0} blocks for ${elements.length} elements`);\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\n const result = applyManifestBlocks(manifest, elements);\n fromCache = result.applied.length;\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 if (remaining.length === 0) {\n // All blocks were in the manifest — done instantly\n showDone(fromCache, fromCache);\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 const text = 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 (cached) {\n const success = safeReplace(el, cached, blockHash);\n if (success) {\n fromCache++;\n } else {\n stillRemaining.push(el);\n }\n } else {\n stillRemaining.push(el);\n }\n }\n remaining = stillRemaining;\n\n if (remaining.length === 0) {\n showDone(fromCache, fromCache);\n return;\n }\n }\n\n // -- Phase 2: Live fallback for remaining blocks --------------------------\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 (abortController.signal.aborted) break;\n\n const text = 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\n try {\n const simplified = await aiService!.simplifyText(text, level, lang);\n if (abortController?.signal.aborted) 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 }\n }\n } catch (err: any) {\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 try {\n const retry = await aiService!.simplifyText(text, level, lang);\n if (abortController?.signal.aborted) 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 }\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 (!abortController?.signal.aborted) {\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 }\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 (!abortController?.signal.aborted) {\n showDone(totalSimplified, fromCache);\n // Show AI disclaimer on the page\n if (totalSimplified > 0 || fromCache > 0) {\n showDisclaimer();\n }\n }\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 for (const { el, originalHtml, savedTextNodes, ancestorPatches } of savedParagraphs) {\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 }\n } else {\n el.innerHTML = originalHtml;\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 // Revert any ancestor patches (max-height, overflow changes)\n revertPatches(ancestorPatches);\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 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":"AAqDA,SAAwB,yBACtB,WACA,OAAe,MACA;AACf,MAAI,UAAU;AACd,MAAI,QAAuB;AAC3B,MAAI,kBAAoC,CAAA;AACxC,MAAI,kBAA0C;AAC9C,MAAI,YAAmC;AACvC,MAAI,gBAAgB;AAEpB,MAAI,iBAAwC;AAC5C,MAAI,mBAA4C;AAGhD,QAAM,2CAA2B,IAAA;AAEjC,QAAM,cAAc;AACpB,QAAM,kBAAkB;AACxB,QAAM,gBAAgB;AACtB,QAAM,kBAAkB;AAMxB,QAAM,kBAAkB;AACxB,QAAM,qBAAqB;AAC3B,QAAM,uBAAuB;AAE7B,WAAS,kBAAwC;AAC/C,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,YAAM,MAAM,UAAU,KAAK,iBAAiB,oBAAoB;AAChE,UAAI,kBAAkB,MAAM;AAC1B,cAAM,KAAK,IAAI;AACf,YAAI,CAAC,GAAG,iBAAiB,SAAS,kBAAkB,GAAG;AACrD,aAAG,kBAAkB,kBAAkB;AAAA,QACzC;AAAA,MACF;AACA,UAAI,YAAY,MAAM,QAAQ,IAAI,MAAM;AACxC,UAAI,UAAU,MAAM,OAAO,IAAI,KAAK;AAAA,IACtC,CAAC;AAAA,EACH;AAEA,iBAAe,gBAAgB,KAAqC;AAClE,QAAI;AACF,YAAM,KAAK,MAAM,gBAAA;AACjB,aAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,cAAM,KAAK,GAAG,YAAY,oBAAoB,UAAU;AACxD,cAAM,QAAQ,GAAG,YAAY,kBAAkB;AAC/C,cAAM,MAAM,MAAM,IAAI,GAAG;AACzB,YAAI,YAAY,MAAM,QAAQ,IAAI,UAAU,IAAI;AAChD,YAAI,UAAU,MAAM,QAAQ,IAAI;AAAA,MAClC,CAAC;AAAA,IACH,QAAQ;AAAE,aAAO;AAAA,IAAM;AAAA,EACzB;AAEA,iBAAe,gBAAgB,KAAa,OAA8B;AACxE,QAAI;AACF,YAAM,KAAK,MAAM,gBAAA;AACjB,YAAM,KAAK,GAAG,YAAY,oBAAoB,WAAW;AACzD,SAAG,YAAY,kBAAkB,EAAE,IAAI,OAAO,GAAG;AAAA,IACnD,QAAQ;AAAA,IAAe;AAAA,EACzB;AAYA,iBAAe,0BAAyC;AACtD,QAAI;AACF,YAAM,KAAK,MAAM,gBAAA;AACjB,YAAM,KAAK,GAAG,YAAY,oBAAoB,WAAW;AACzD,YAAM,QAAQ,GAAG,YAAY,kBAAkB;AAC/C,YAAM,UAAU,OAAO,SAAS,SAAS,OAAO,SAAS;AACzD,YAAM,SAAS,GAAG,OAAO,aAAa,KAAK;AAI3C,YAAM,MAAM,MAAM,WAAA;AAClB,UAAI,YAAY,MAAM;AACpB,cAAM,SAAS,IAAI;AACnB,YAAI,CAAC,OAAQ;AACb,YAAI,OAAO,OAAO,QAAQ,YAAY,OAAO,IAAI,WAAW,MAAM,GAAG;AACnE,iBAAO,OAAA;AAAA,QACT;AACA,eAAO,SAAA;AAAA,MACT;AAAA,IACF,QAAQ;AAAA,IAAe;AAAA,EACzB;AAEA,WAAS,eAAe,WAA2B;AACjD,UAAM,UAAU,OAAO,SAAS,SAAS,OAAO,SAAS;AACzD,WAAO,GAAG,OAAO,aAAa,KAAK,IAAI,SAAS;AAAA,EAClD;AAMA,WAAS,YAAqD;AAC5D,UAAM,SAAS,SAAS,cAAc,uBAAuB;AAC7D,UAAM,UAAU,QAAQ,aAAa,eAAe,KACjD,OAAe,WAAW,QAAQ,WAAW;AAChD,UAAM,WAAW,QAAQ,aAAa,gBAAgB,KACnD,OAAe,WAAW,QAAQ,YAAY;AACjD,WAAO,EAAE,SAAS,WAAW,QAAW,UAAU,YAAY,OAAA;AAAA,EAChE;AAUA,QAAM,0BAA0B;AAKhC,QAAM,sBAAsB;AAE5B,WAAS,kBAAiC;AACxC,UAAM,YAAY;AAClB,UAAM,WAA0B,CAAA;AAChC,UAAM,2BAAW,IAAA;AAEjB,aAAS,iBAAiB,SAAS,EAAE,QAAQ,CAAC,OAAO;AACnD,YAAM,SAAS;AACf,UAAI,OAAO,QAAQ,iBAAiB,EAAG;AAGvC,UAAI,OAAO,iBAAiB,QAAQ,OAAO,MAAM,aAAa,SAAS;AACrE,cAAM,mBAAmB,OAAO,QAAQ,yBAAyB,KAAK,OAAO,QAAQ,oBAAoB;AACzG,YAAI,CAAC,iBAAkB;AAAA,MACzB;AACA,YAAM,OAAO,OAAO,aAAa,KAAA,KAAU;AAC3C,UAAI,KAAK,SAAS,GAAI;AAEtB,UAAI,KAAK,SAAS,MAAM,oBAAoB,KAAK,IAAI,EAAG;AACxD,UAAI,KAAK,IAAI,MAAM,EAAG;AAKtB,YAAM,iBAAiB,OAAO,QAAQ,uBAAuB;AAC7D,UAAI,gBAAgB;AAIlB,cAAM,cAAc,OAAO,iBAAiB,wBAAwB;AACpE,YAAI,YAAY,SAAS,GAAG;AAE1B,cAAK,OAAe,mBAAmB;AACrC,oBAAQ,IAAI,6DAA6D,OAAO,OAAO;AAAA,UACzF;AACA;AAAA,QACF;AAAA,MACF;AAEA,UAAI,YAAY;AAChB,iBAAW,KAAK,MAAM;AACpB,YAAI,EAAE,SAAS,MAAM,GAAG;AAAE,sBAAY;AAAM;AAAA,QAAO;AAAA,MACrD;AACA,UAAI,UAAW;AACf,WAAK,IAAI,MAAM;AACf,eAAS,KAAK,MAAM;AAAA,IACtB,CAAC;AAED,WAAO;AAAA,EACT;AAMA,WAAS,SAAS,MAAsB;AACtC,UAAM,aAAa,KAAK,QAAQ,QAAQ,GAAG,EAAE,KAAA,EAAO,YAAA;AACpD,QAAI,OAAO;AACX,aAAS,IAAI,GAAG,IAAI,WAAW,QAAQ,KAAK;AAC1C,cAAS,QAAQ,KAAK,OAAO,WAAW,WAAW,CAAC,IAAK;AAAA,IAC3D;AACA,WAAO,KAAK,SAAS,EAAE;AAAA,EACzB;AAWA,WAAS,SAAS,GAAW,GAAmB;AAC9C,UAAM,KAAK,EAAE,QAAQ,QAAQ,GAAG,EAAE,KAAA;AAClC,UAAM,KAAK,EAAE,QAAQ,QAAQ,GAAG,EAAE,KAAA;AAGlC,QAAI,OAAO,GAAI,QAAO;AAGtB,QAAI,GAAG,SAAS,OAAO,GAAG,SAAS,KAAK;AACtC,YAAM,UAAU,KAAK,IAAI,GAAG,SAAS,GAAG,MAAM;AAC9C,UAAI,UAAU,EAAG,QAAO;AAExB,UAAI,QAAQ;AACZ,YAAM,SAAS,KAAK,IAAI,GAAG,QAAQ,GAAG,MAAM;AAC5C,eAAS,IAAI,GAAG,IAAI,QAAQ,KAAK;AAC/B,YAAI,GAAG,CAAC,MAAM,GAAG,CAAC,EAAG;AACrB,YAAI,QAAQ,EAAG,QAAO;AAAA,MACxB;AACA,aAAO;AAAA,IACT;AAGA,UAAM,IAAI,GAAG;AACb,UAAM,IAAI,GAAG;AAEb,QAAI,KAAK,IAAI,IAAI,CAAC,IAAI,EAAG,QAAO,KAAK,IAAI,IAAI,CAAC;AAE9C,UAAM,KAAe,MAAM,KAAK,EAAE,QAAQ,IAAI,KAAK,CAAC,GAAG,MAAM,CAAC;AAC9D,aAAS,IAAI,GAAG,KAAK,GAAG,KAAK;AAC3B,UAAI,OAAO,GAAG,CAAC;AACf,SAAG,CAAC,IAAI;AACR,eAAS,IAAI,GAAG,KAAK,GAAG,KAAK;AAC3B,cAAM,OAAO,GAAG,CAAC;AACjB,WAAG,CAAC,IAAI,GAAG,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,IAC1B,OACA,IAAI,KAAK,IAAI,MAAM,GAAG,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC;AACvC,eAAO;AAAA,MACT;AAEA,UAAI,KAAK,IAAI,GAAG,EAAE,IAAI,EAAG,QAAO;AAAA,IAClC;AACA,WAAO,GAAG,CAAC;AAAA,EACb;AAEA,QAAM,sBAAsB;AAM5B,WAAS,iBAAiB,MAAoB;AAC5C,UAAM,YAAoB,CAAA;AAC1B,UAAM,SAAS,SAAS,iBAAiB,MAAM,WAAW,WAAW,IAAI;AACzE,QAAI;AACJ,WAAQ,OAAO,OAAO,YAA4B;AAChD,YAAM,UAAU,KAAK,WAAW,KAAA;AAChC,UAAI,WAAW,QAAQ,SAAS,GAAG;AACjC,kBAAU,KAAK,IAAI;AAAA,MACrB;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAWA,WAAS,qBAAqB,IAAiB,gBAA8B;AAC3E,UAAM,YAAY,iBAAiB,EAAE;AACrC,QAAI,UAAU,WAAW,EAAG;AAE5B,QAAI,UAAU,WAAW,GAAG;AAE1B,gBAAU,CAAC,EAAE,YAAY;AACzB;AAAA,IACF;AAIA,UAAM,YAAY,eAAe,MAAM,mBAAmB,KAAK,CAAC,cAAc;AAG9E,UAAM,mBAAmB,UAAU,OAAO,CAAC,KAAK,MAAM,OAAO,EAAE,WAAW,KAAA,EAAO,UAAU,IAAI,CAAC;AAEhG,QAAI,qBAAqB,GAAG;AAE1B,gBAAU,CAAC,EAAE,YAAY;AACzB,eAAS,IAAI,GAAG,IAAI,UAAU,QAAQ,KAAK;AACzC,kBAAU,CAAC,EAAE,YAAY;AAAA,MAC3B;AACA;AAAA,IACF;AAGA,QAAI,gBAAgB;AACpB,aAAS,IAAI,GAAG,IAAI,UAAU,QAAQ,KAAK;AACzC,YAAM,cAAc,UAAU,CAAC,EAAE,WAAW,KAAA,EAAO,UAAU,KAAK;AAClE,YAAM,gBAAgB,KAAK,IAAI,GAAG,KAAK,MAAM,aAAa,UAAU,MAAM,CAAC;AAE3E,YAAM,QAAQ,UAAU,MAAM,eAAe,gBAAgB,aAAa,EAAE,KAAK,EAAE;AACnF,gBAAU,CAAC,EAAE,YAAY,SAAS;AAClC,uBAAiB;AAAA,IACnB;AAGA,QAAI,gBAAgB,UAAU,QAAQ;AACpC,YAAM,WAAW,UAAU,UAAU,SAAS,CAAC;AAC/C,eAAS,aAAa,SAAS,aAAa,MAAM,UAAU,MAAM,aAAa,EAAE,KAAK,EAAE;AAAA,IAC1F;AAAA,EACF;AAMA,WAAS,eAAe,IAAiE;AACvF,WAAO,EAAE,cAAc,GAAG,cAAc,cAAc,GAAG,aAAA;AAAA,EAC3D;AAMA,WAAS,qBAAqB,IAAqC;AACjE,QAAI,UAA8B;AAClC,WAAO,WAAW,YAAY,SAAS,MAAM;AAC3C,YAAM,QAAQ,iBAAiB,OAAO;AACtC,YAAM,WAAW,MAAM,WAAW,MAAM,MAAM;AAC9C,YAAM,oBAAoB,SAAS,SAAS,QAAQ,KAAK,SAAS,SAAS,MAAM;AACjF,YAAM,iBAAiB,MAAM,cAAc,UACzC,MAAM,WAAW,UAAU,MAAM,WAAW,MAAM,CAAC,MAAM,OAAO,SAAS,GAAG;AAE9E,UAAI,qBAAqB,gBAAgB;AACvC,eAAO;AAAA,MACT;AACA,gBAAU,QAAQ;AAAA,IACpB;AACA,WAAO;AAAA,EACT;AAMA,WAAS,kBAAkB,IAA8D;AACvF,UAAM,UAA2B,CAAA;AAGjC,UAAM,QAAQ,eAAe,EAAE;AAC/B,UAAM,uBAAuB,MAAM,eAAe,MAAM,eAAe;AAEvE,QAAI,CAAC,sBAAsB;AAEzB,YAAMA,gBAAe,qBAAqB,EAAE;AAC5C,UAAI,CAACA,cAAc,QAAO,EAAE,MAAM,MAAM,SAAS,GAAC;AAElD,YAAM,gBAAgB,eAAeA,aAAY;AACjD,UAAI,cAAc,gBAAgB,cAAc,eAAe,GAAG;AAChE,eAAO,EAAE,MAAM,MAAM,SAAS,CAAA,EAAC;AAAA,MACjC;AAGA,aAAO,mBAAmBA,eAAc,OAAO;AAAA,IACjD;AAGA,UAAM,eAAe,qBAAqB,EAAE;AAC5C,QAAI,CAAC,cAAc;AAEjB,aAAO,EAAE,MAAM,MAAM,SAAS,CAAA,EAAC;AAAA,IACjC;AAEA,WAAO,mBAAmB,cAAc,OAAO;AAAA,EACjD;AAEA,WAAS,mBACP,UACA,SAC6C;AAC7C,UAAM,QAAQ,iBAAiB,QAAQ;AAGvC,QAAI,MAAM,cAAc,QAAQ;AAC9B,cAAQ,KAAK;AAAA,QACX,IAAI;AAAA,QACJ,UAAU;AAAA,QACV,eAAe,SAAS,MAAM;AAAA,MAAA,CAC/B;AACD,eAAS,MAAM,YAAY;AAAA,IAC7B;AAGA,QAAI,MAAM,SAAS,SAAS,QAAQ,KAAK,MAAM,UAAU,SAAS,QAAQ,GAAG;AAC3E,cAAQ,KAAK;AAAA,QACX,IAAI;AAAA,QACJ,UAAU;AAAA,QACV,eAAe,SAAS,MAAM;AAAA,MAAA,CAC/B;AACD,eAAS,MAAM,WAAW;AAAA,IAC5B;AAGA,UAAM,aAAa,eAAe,QAAQ;AAC1C,QAAI,WAAW,gBAAgB,WAAW,eAAe,GAAG;AAC1D,aAAO,EAAE,MAAM,MAAM,QAAA;AAAA,IACvB;AAGA,kBAAc,OAAO;AACrB,WAAO,EAAE,MAAM,OAAO,SAAS,CAAA,EAAC;AAAA,EAClC;AAEA,WAAS,cAAc,SAA0B;AAC/C,eAAW,SAAS,SAAS;AAC1B,YAAM,GAAG,MAAc,MAAM,QAAQ,IAAI,MAAM;AAAA,IAClD;AAAA,EACF;AAMA,WAAS,sBAAsB;AAC7B,QAAI,eAAgB;AAEpB,qBAAiB,IAAI,eAAe,CAAC,YAAY;AAC/C,iBAAW,SAAS,SAAS;AAC3B,cAAM,KAAK,MAAM;AACjB,YAAI,CAAC,GAAG,aAAa,eAAe,EAAG;AAEvC,cAAM,QAAQ,gBAAgB,KAAK,CAAC,OAAO,GAAG,OAAO,EAAE;AACvD,YAAI,CAAC,MAAO;AAGZ,cAAM,eAAe,qBAAqB,EAAE;AAC5C,YAAI,cAAc;AAChB,gBAAM,OAAO,eAAe,YAAY;AACxC,cAAI,KAAK,eAAe,KAAK,eAAe,GAAG;AAE7C,oBAAQ;AAAA,cACN;AAAA,cACA;AAAA,YAAA;AAEF,8BAAkB,KAAK;AACvB;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AAEA,WAAS,wBAAwB;AAC/B,QAAI,iBAAkB;AAEtB,uBAAmB,IAAI,iBAAiB,CAAC,cAAc;AACrD,iBAAW,YAAY,WAAW;AAEhC,mBAAW,WAAW,SAAS,cAAc;AAC3C,cAAI,EAAE,mBAAmB,aAAc;AACvC,gBAAM,MAAM,gBAAgB,UAAU,CAAC,OAAO,GAAG,OAAO,WAAW,QAAQ,SAAS,GAAG,EAAE,CAAC;AAC1F,cAAI,QAAQ,IAAI;AACd,kBAAM,QAAQ,gBAAgB,GAAG;AAEjC,4BAAgB,UAAU,MAAM,EAAE;AAClC,kBAAM,GAAG,gBAAgB,eAAe;AACxC,kBAAM,GAAG,gBAAgB,aAAa;AACtC,kBAAM,GAAG,gBAAgB,eAAe;AACxC,0BAAc,MAAM,eAAe;AACnC,4BAAgB,OAAO,KAAK,CAAC;AAAA,UAC/B;AAAA,QACF;AAGA,YAAI,SAAS,SAAS,mBAAmB,SAAS,SAAS,aAAa;AACtE,gBAAM,SAAS,SAAS,kBAAkB,cACtC,SAAS,SACT,SAAS,OAAO;AACpB,cAAI,CAAC,OAAQ;AAEb,gBAAM,eAAe,OAAO,QAAQ,IAAI,eAAe,GAAG;AAC1D,cAAI,cAAc;AAChB,kBAAM,QAAQ,gBAAgB,KAAK,CAAC,OAAO,GAAG,OAAO,YAAY;AACjE,gBAAI,SAAS,CAAC,aAAa,aAAa,0BAA0B,GAAG;AAEnE,sBAAQ;AAAA,gBACN;AAAA,gBACA;AAAA,cAAA;AAEF,8BAAgB,UAAU,MAAM,EAAE;AAClC,oBAAM,GAAG,gBAAgB,eAAe;AACxC,oBAAM,GAAG,gBAAgB,aAAa;AACtC,oBAAM,GAAG,gBAAgB,eAAe;AACxC,4BAAc,MAAM,eAAe;AACnC,gCAAkB,gBAAgB,OAAO,CAAC,OAAO,GAAG,OAAO,YAAY;AAAA,YACzE;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF,CAAC;AAED,qBAAiB,QAAQ,SAAS,MAAM;AAAA,MACtC,WAAW;AAAA,MACX,SAAS;AAAA,MACT,eAAe;AAAA,IAAA,CAChB;AAAA,EACH;AAEA,WAAS,sBAAsB;AAC7B,QAAI,gBAAgB;AAClB,qBAAe,WAAA;AACf,uBAAiB;AAAA,IACnB;AACA,QAAI,kBAAkB;AACpB,uBAAiB,WAAA;AACjB,yBAAmB;AAAA,IACrB;AACA,yBAAqB,MAAA;AAAA,EACvB;AAMA,WAAS,YACP,IACA,gBACA,WACS;AAET,UAAM,eAAe,GAAG;AACxB,UAAM,kBAAkB,iBAAiB,EAAE;AAC3C,UAAM,iBAAkC,gBAAgB,IAAI,CAAA,OAAM;AAAA,MAChE,MAAM;AAAA,MACN,UAAU,EAAE;AAAA,IAAA,EACZ;AAGF,OAAG,aAAa,4BAA4B,MAAM;AAIlD,yBAAqB,IAAI,cAAc;AAGvC,UAAM,EAAE,MAAM,YAAY,kBAAkB,EAAE;AAE9C,QAAI,CAAC,MAAM;AAET,iBAAW,SAAS,gBAAgB;AAClC,YAAI,MAAM,KAAK,WAAY,OAAM,KAAK,OAAO,MAAM;AAAA,MACrD;AACA,iBAAW,MAAM,GAAG,gBAAgB,0BAA0B,GAAG,CAAC;AAClE,cAAQ;AAAA,QACN;AAAA,QACA,GAAG,aAAa,MAAM,GAAG,EAAE;AAAA,MAAA;AAE7B;AACA,aAAO;AAAA,IACT;AAGA,OAAG,aAAa,iBAAiB,MAAM;AACvC,OAAG,aAAa,eAAe,YAAY;AAC3C,QAAI,WAAW;AACb,SAAG,aAAa,iBAAiB,SAAS;AAAA,IAC5C;AAEA,oBAAgB,KAAK,EAAE,IAAI,cAAc,gBAAgB,iBAAiB,SAAS;AAGnF,oBAAgB,QAAQ,EAAE;AAG1B,eAAW,MAAM,GAAG,gBAAgB,0BAA0B,GAAG,CAAC;AAElE,WAAO;AAAA,EACT;AAKA,WAAS,kBAAkB,OAAuB;AAEhD,QAAI,MAAM,eAAe,SAAS,GAAG;AACnC,iBAAW,MAAM,MAAM,gBAAgB;AACrC,YAAI,GAAG,KAAK,WAAY,IAAG,KAAK,OAAO,GAAG;AAAA,MAC5C;AAAA,IACF,OAAO;AAEL,YAAM,GAAG,YAAY,MAAM;AAAA,IAC7B;AACA,UAAM,GAAG,gBAAgB,eAAe;AACxC,UAAM,GAAG,gBAAgB,aAAa;AACtC,UAAM,GAAG,gBAAgB,eAAe;AACxC,kBAAc,MAAM,eAAe;AACnC,oBAAgB,UAAU,MAAM,EAAE;AAClC,sBAAkB,gBAAgB,OAAO,CAAC,OAAO,GAAG,OAAO,MAAM,EAAE;AAAA,EACrE;AAOA,QAAM,oCAAoB,IAAA;AAE1B,iBAAe,cAAc,SAAiB,UAAoD;AAChG,UAAM,WAAW,GAAG,OAAO,IAAI,KAAK;AACpC,QAAI,cAAc,IAAI,QAAQ,EAAG,QAAO,cAAc,IAAI,QAAQ;AAElE,UAAM,OAAO,YAAY;AACzB,UAAM,UAAU,mBAAmB,OAAO,SAAS,SAAS,OAAO,SAAS,QAAQ;AACpF,UAAM,MAAM,GAAG,IAAI,wBAAwB,OAAO,QAAQ,OAAO,6BAA6B,KAAK;AAEnG,QAAI;AACF,YAAM,MAAM,MAAM,MAAM,KAAK,EAAE,OAAO,YAAY;AAClD,UAAI,CAAC,IAAI,IAAI;AAAE,sBAAc,IAAI,UAAU,IAAI;AAAG,eAAO;AAAA,MAAM;AAC/D,YAAM,OAAO,MAAM,IAAI,KAAA;AACvB,YAAM,SAAS,KAAK,QAAQ,SAAS,OAAO;AAC5C,oBAAc,IAAI,UAAU,MAAM;AAClC,aAAO;AAAA,IACT,QAAQ;AACN,oBAAc,IAAI,UAAU,IAAI;AAChC,aAAO;AAAA,IACT;AAAA,EACF;AAMA,WAAS,oBACP,UACA,UAC4E;AAC5E,UAAM,UAAyB,CAAA;AAC/B,UAAM,YAA2B,CAAA;AACjC,UAAM,QAAuB,CAAA;AAG7B,UAAM,6BAAa,IAAA;AACnB,UAAM,iCAAiB,IAAA;AACvB,eAAW,SAAS,SAAS,QAAQ;AACnC,aAAO,IAAI,MAAM,WAAW,KAAK;AACjC,UAAI,MAAM,UAAU;AAClB,mBAAW,IAAI,MAAM,UAAU,KAAK;AAAA,MACtC;AAAA,IACF;AAEA,eAAW,MAAM,UAAU;AACzB,YAAM,OAAO,GAAG,aAAa,KAAA,KAAU;AACvC,YAAM,SAAS,SAAS,IAAI;AAG5B,YAAM,aAAa,OAAO,IAAI,MAAM;AACpC,UAAI,YAAY,QAAQ;AACtB,cAAM,UAAU,YAAY,IAAI,WAAW,QAAQ,MAAM;AACzD,YAAI,SAAS;AACX,kBAAQ,KAAK,EAAE;AAAA,QACjB,OAAO;AACL,oBAAU,KAAK,EAAE;AAAA,QACnB;AACA;AAAA,MACF;AAIA,UAAI;AACJ,iBAAW,CAAC,UAAU,KAAK,KAAK,YAAY;AAC1C,YAAI;AACF,cAAI,GAAG,QAAQ,QAAQ,KAAK,GAAG,QAAQ,QAAQ,MAAM,IAAI;AACvD,yBAAa;AACb;AAAA,UACF;AAAA,QACF,QAAQ;AAAA,QAA+B;AAAA,MACzC;AAEA,UAAI,YAAY,UAAU,WAAW,cAAc;AACjD,cAAM,OAAO,SAAS,MAAM,WAAW,YAAY;AACnD,YAAI,QAAQ,qBAAqB;AAE/B,gBAAM,UAAU,YAAY,IAAI,WAAW,QAAQ,MAAM;AACzD,cAAI,SAAS;AACX,oBAAQ,KAAK,EAAE;AAAA,UACjB,OAAO;AACL,sBAAU,KAAK,EAAE;AAAA,UACnB;AAAA,QACF,OAAO;AAEL,kBAAQ;AAAA,YACN,iCAAiC,IAAI;AAAA,YACrC,KAAK,MAAM,GAAG,EAAE,IAAI;AAAA,UAAA;AAEtB,gBAAM,KAAK,EAAE;AAAA,QACf;AACA;AAAA,MACF;AAGA,gBAAU,KAAK,EAAE;AAAA,IACnB;AAEA,WAAO,EAAE,SAAS,WAAW,MAAA;AAAA,EAC/B;AAOA,WAAS,cAAc,IAAyB;AAC9C,UAAM,MAAM,GAAG,QAAQ,YAAA;AAEvB,QAAI,GAAG,GAAI,QAAO,IAAI,IAAI,OAAO,GAAG,EAAE,CAAC;AAEvC,UAAM,SAAS,GAAG;AAClB,QAAI,CAAC,OAAQ,QAAO;AACpB,UAAM,WAAW,MAAM,KAAK,OAAO,QAAQ,EAAE,OAAO,CAAA,MAAK,EAAE,YAAY,GAAG,OAAO;AACjF,QAAI,SAAS,WAAW,EAAG,QAAO;AAClC,UAAM,MAAM,SAAS,QAAQ,EAAE,IAAI;AACnC,WAAO,GAAG,GAAG,gBAAgB,GAAG;AAAA,EAClC;AAGA,QAAM,eAKD,CAAA;AACL,MAAI,oBAA0D;AAC9D,QAAM,sCAAsB,IAAA;AAE5B,WAAS,kBACP,SACA,WACA,cACA,gBACA,UACA,UACM;AAEN,QAAI,gBAAgB,IAAI,SAAS,EAAG;AACpC,oBAAgB,IAAI,SAAS;AAE7B,iBAAa,KAAK,EAAE,WAAW,cAAc,QAAQ,gBAAgB,UAAU;AAG/E,QAAI,gCAAgC,iBAAiB;AACrD,wBAAoB,WAAW,MAAM,kBAAkB,SAAS,QAAQ,GAAG,GAAI;AAAA,EACjF;AAEA,WAAS,kBAAkB,SAAiB,UAAyB;AACnE,QAAI,aAAa,WAAW,EAAG;AAC/B,UAAM,OAAO,YAAY;AACzB,UAAM,UAAU,OAAO,SAAS,SAAS,OAAO,SAAS;AACzD,UAAM,SAAS,aAAa,OAAO,CAAC;AAEpC,UAAM,GAAG,IAAI,qBAAqB;AAAA,MAChC,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,cAAc;AAAA,MAAA;AAAA,MAEhB,MAAM,KAAK,UAAU;AAAA,QACnB;AAAA,QACA,KAAK;AAAA,QACL,SAAS;AAAA,QACT,SAAS;AAAA,QACT;AAAA,MAAA,CACD;AAAA,IAAA,CACF,EAAE,KAAK,CAAC,QAAQ;AACf,UAAI,IAAI,IAAI;AACV,YAAK,OAAe,mBAAmB;AACrC,kBAAQ,IAAI,yBAAyB,OAAO,MAAM,kBAAkB;AAAA,QACtE;AAAA,MACF,OAAO;AACL,gBAAQ,KAAK,+BAA+B,IAAI,MAAM,EAAE;AAAA,MAC1D;AAAA,IACF,CAAC,EAAE,MAAM,CAAC,QAAQ;AAChB,cAAQ,KAAK,sDAAsD,GAAG;AAAA,IACxE,CAAC;AAAA,EACH;AAMA,WAAS,YAAY,IAAiB;AACpC,OAAG,MAAM,UAAU;AACnB,OAAG,MAAM,aAAa;AAAA,EACxB;AAEA,WAAS,aAAa,IAAiB;AACrC,OAAG,MAAM,UAAU;AACnB,OAAG,MAAM,aAAa;AAAA,EACxB;AAMA,WAAS,aAAa,UAAkB,QAAgB,WAAmB,GAAG;AAAA,EAE9E;AAEA,WAAS,SAAS,QAAgB,YAAoB;AAEpD,mBAAA;AAAA,EACF;AAEA,WAAS,UAAU,KAAa;AAC9B,QAAI,WAAW;AACb,gBAAU,YAAY,+CAA+C,GAAG;AACxE,gBAAU,MAAM,QAAQ;AACxB,iBAAW,MAAM,eAAA,GAAkB,GAAI;AAAA,IACzC;AAAA,EACF;AAEA,WAAS,iBAAiB;AACxB,eAAW,OAAA;AACX,gBAAY;AACZ,aAAS,eAAe,0BAA0B,GAAG,OAAA;AAAA,EACvD;AAKA,QAAM,gBAAgB;AAEtB,WAAS,iBAAiB;AACxB,QAAI,SAAS,eAAe,aAAa,EAAG;AAC5C,UAAM,OAAO,KAAK,WAAW,IAAI,IAC7B,0BACA;AAEJ,UAAM,SAAS,SAAS,cAAc,KAAK;AAC3C,WAAO,KAAK;AACZ,WAAO,aAAa,QAAQ,QAAQ;AACpC,WAAO,aAAa,aAAa,QAAQ;AACzC,WAAO,cAAc;AACrB,WAAO,OAAO,OAAO,OAAO;AAAA,MAC1B,UAAU;AAAA,MACV,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,SAAS;AAAA,MACT,YAAY;AAAA,MACZ,OAAO;AAAA,MACP,UAAU;AAAA,MACV,YAAY;AAAA,MACZ,cAAc;AAAA,MACd,eAAe;AAAA,IAAA,CAChB;AACD,aAAS,KAAK,YAAY,MAAM;AAAA,EAClC;AAEA,WAAS,mBAAmB;AAC1B,aAAS,eAAe,aAAa,GAAG,OAAA;AAAA,EAC1C;AAMA,iBAAe,eAAe;AAC5B,UAAM,WAAW,gBAAA;AACjB,QAAI,SAAS,WAAW,EAAG;AAE3B,sBAAkB,IAAI,gBAAA;AACtB,sBAAkB,CAAA;AAClB,oBAAgB;AAGhB,wBAAA;AACA,0BAAA;AAEA,UAAM,EAAE,SAAS,SAAA,IAAa,UAAA;AAC9B,QAAI,YAAY;AAChB,QAAI,YAAY;AAEhB,UAAM,kCAAkB,IAAA;AAExB,UAAM,QAAQ,CAAC,CAAE,OAAe;AAGhC,QAAI,SAAS;AACX,mBAAa,GAAG,SAAS,MAAM;AAC/B,YAAM,WAAW,MAAM,cAAc,SAAS,QAAQ;AAEtD,UAAI,MAAO,SAAQ,IAAI,yBAAyB,UAAU,QAAQ,UAAU,CAAC,eAAe,SAAS,MAAM,WAAW;AAEtH,UAAI,UAAU,QAAQ,QAAQ;AAG5B,cAAM,wBAAA;AAEN,cAAM,SAAS,oBAAoB,UAAU,QAAQ;AACrD,oBAAY,OAAO,QAAQ;AAC3B,YAAI,MAAO,SAAQ,IAAI,6BAA6B,SAAS,YAAY,OAAO,MAAM,MAAM,gBAAgB,OAAO,UAAU,MAAM,EAAE;AAErI,mBAAW,MAAM,OAAO,MAAO,aAAY,IAAI,EAAE;AACjD,oBAAY,CAAC,GAAG,OAAO,OAAO,GAAG,OAAO,SAAS;AAGjD,mBAAW,SAAS,SAAS,QAAQ;AACnC,cAAI,MAAM,QAAQ;AAChB,4BAAgB,eAAe,MAAM,SAAS,GAAG,MAAM,MAAM;AAAA,UAC/D;AAAA,QACF;AAEA,YAAI,UAAU,WAAW,GAAG;AAE1B,mBAA6B;AAC7B;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAGA;AACE,YAAM,iBAAgC,CAAA;AACtC,iBAAW,MAAM,WAAW;AAC1B,cAAM,OAAO,GAAG,aAAa,KAAA,KAAU;AACvC,YAAI,KAAK,SAAS,IAAI;AAAE,yBAAe,KAAK,EAAE;AAAG;AAAA,QAAU;AAC3D,cAAM,YAAY,SAAS,IAAI;AAC/B,cAAM,SAAS,MAAM,gBAAgB,eAAe,SAAS,CAAC;AAC9D,YAAI,QAAQ;AACV,gBAAM,UAAU,YAAY,IAAI,QAAQ,SAAS;AACjD,cAAI,SAAS;AACX;AAAA,UACF,OAAO;AACL,2BAAe,KAAK,EAAE;AAAA,UACxB;AAAA,QACF,OAAO;AACL,yBAAe,KAAK,EAAE;AAAA,QACxB;AAAA,MACF;AACA,kBAAY;AAEZ,UAAI,UAAU,WAAW,GAAG;AAC1B,iBAA6B;AAC7B;AAAA,MACF;AAAA,IACF;AAKA,QAAI,CAAC,aAAa,UAAU,SAAS,GAAG;AAEtC,UAAI,YAAY,OAAO,GAAG;AACxB,yBAAiB,YAAY;AAC7B,gBAAQ,KAAK,eAAe,YAAY,IAAI,uDAAuD;AAAA,MACrG;AACA,UAAI,YAAY,GAAG;AACjB,iBAA6B;AAAA,MAC/B,OAAO;AAEL,kBAAU,KAAK,WAAW,IAAI,IAC1B,8BACA,uBAAuB;AAAA,MAC7B;AACA;AAAA,IACF;AAGA,eAAW,MAAM,WAAW;AAC1B,kBAAY,EAAE;AAAA,IAChB;AAEsB,aAAS;AAE/B,QAAI,kBAAkB;AACtB,UAAM,UAAU,CAAC,OAAoB,YAAY,IAAI,EAAE;AAIvD,eAAW,MAAM,WAAW;AAC1B,UAAI,gBAAgB,OAAO,QAAS;AAEpC,YAAM,OAAO,GAAG,aAAa,KAAA,KAAU;AACvC,UAAI,KAAK,SAAS,IAAI;AACpB,qBAAa,EAAE;AAGf;AAAA,MACF;AAGA,UAAI,KAAK,SAAS,MAAM,oBAAoB,KAAK,IAAI,GAAG;AACtD,qBAAa,EAAE;AAEf;AAEA;AAAA,MACF;AAEA,YAAM,YAAY,SAAS,IAAI;AAE/B,UAAI;AACF,cAAM,aAAa,MAAM,UAAW,aAAa,MAAM,OAAO,IAAI;AAClE,YAAI,iBAAiB,OAAO,QAAS;AAErC,YAAI,cAAc,eAAe,MAAM;AACrC,gBAAM,UAAU,YAAY,IAAI,YAAY,SAAS;AACrD,cAAI,SAAS;AACX;AAGA,4BAAgB,eAAe,SAAS,GAAG,UAAU;AAGrD,gBAAI,SAAS;AACX,gCAAkB,SAAS,WAAW,MAAM,YAAY,cAAc,EAAE,GAAG,QAAQ;AAAA,YACrF;AAAA,UACF;AAAA,QACF;AAAA,MACF,SAAS,KAAU;AAEjB,YAAI,KAAK,SAAS,SAAS,KAAK,KAAK,KAAK,SAAS,SAAS,KAAK,GAAG;AAClE,kBAAQ,KAAK,0DAA0D;AAEvE,qBAAW,KAAK,WAAW;AACzB,gBAAI,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,eAAe,EAAG;AAAA,UACtD;AACA,uBAAa,EAAE;AAEf;AAAA,QACF;AACA,YAAI,KAAK,SAAS,SAAS,KAAK,GAAG;AAEjC,gBAAM,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,GAAI,CAAC;AAC5C,cAAI;AACF,kBAAM,QAAQ,MAAM,UAAW,aAAa,MAAM,OAAO,IAAI;AAC7D,gBAAI,iBAAiB,OAAO,QAAS;AACrC,gBAAI,SAAS,UAAU,MAAM;AAC3B,oBAAM,UAAU,YAAY,IAAI,OAAO,SAAS;AAChD,kBAAI,SAAS;AACX;AACA,gCAAgB,eAAe,SAAS,GAAG,KAAK;AAChD,oBAAI,SAAS;AACX,oCAAkB,SAAS,WAAW,MAAM,OAAO,cAAc,EAAE,GAAG,QAAQ;AAAA,gBAChF;AAAA,cACF;AAAA,YACF;AAAA,UACF,QAAQ;AAEN,gBAAI,QAAQ,EAAE,GAAG;AACf;AACA,sBAAQ,KAAK,sDAAsD,KAAK,MAAM,GAAG,EAAE,CAAC;AAAA,YACtF;AAAA,UACF;AAAA,QACF,OAAO;AAEL,cAAI,QAAQ,EAAE,GAAG;AACf;AACA,oBAAQ,KAAK,+CAA+C,KAAK,MAAM,GAAG,EAAE,CAAC;AAAA,UAC/E,OAAO;AACL,oBAAQ,KAAK,mCAAmC,GAAG;AAAA,UACrD;AAAA,QACF;AAAA,MACF,UAAA;AACE,qBAAa,EAAE;AAEf,YAAI,CAAC,iBAAiB,OAAO,SAAS;AAGpC,gBAAM,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,GAAG,CAAC;AAAA,QAC7C;AAAA,MACF;AAAA,IACF;AAGA,eAAW,MAAM,WAAW;AAC1B,mBAAa,EAAE;AAAA,IACjB;AAEA,QAAI,CAAC,iBAAiB,OAAO,SAAS;AACpC,eAAmC;AAEnC,UAAI,kBAAkB,KAAK,YAAY,GAAG;AACxC,uBAAA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAMA,WAAS,cAAc;AAErB,wBAAA;AAEA,eAAW,EAAE,IAAI,cAAc,gBAAgB,gBAAA,KAAqB,iBAAiB;AAEnF,UAAI,eAAe,SAAS,GAAG;AAC7B,YAAI,cAAc;AAClB,mBAAW,MAAM,gBAAgB;AAC/B,cAAI,GAAG,KAAK,YAAY;AACtB,eAAG,KAAK,OAAO,GAAG;AAAA,UACpB,OAAO;AACL,0BAAc;AAAA,UAChB;AAAA,QACF;AAEA,YAAI,CAAC,aAAa;AAChB,aAAG,YAAY;AAAA,QACjB;AAAA,MACF,OAAO;AACL,WAAG,YAAY;AAAA,MACjB;AAEA,SAAG,gBAAgB,aAAa;AAChC,SAAG,gBAAgB,eAAe;AAClC,SAAG,gBAAgB,eAAe;AAClC,SAAG,gBAAgB,0BAA0B;AAC7C,mBAAa,EAAE;AAGf,oBAAc,eAAe;AAAA,IAC/B;AACA,sBAAkB,CAAA;AAClB,oBAAgB;AAAA,EAClB;AAMA,WAAS,kBAAkB;AACzB,QAAI;AACF,YAAM,QAAQ,aAAa,QAAQ,WAAW;AAC9C,UAAI,OAAO;AACT,cAAM,SAAS,KAAK,MAAM,KAAK;AAC/B,YAAI,OAAO,UAAU,cAAc,OAAO,UAAU,WAAW;AAC7D,kBAAQ,OAAO;AAAA,QACjB;AAAA,MACF;AAAA,IACF,QAAQ;AAAA,IAAqB;AAAA,EAC/B;AAEA,WAAS,kBAAkB;AACzB,iBAAa,QAAQ,aAAa,KAAK,UAAU,EAAE,MAAA,CAAO,CAAC;AAAA,EAC7D;AAUA,MAAI,UAAU;AACd,MAAI,oBAAyC;AAE7C,WAAS,yBAAyB;AAChC,cAAU,SAAS;AAEnB,UAAM,cAAc,MAAM;AACxB,UAAI,CAAC,WAAW,SAAS,SAAS,QAAS;AAC3C,gBAAU,SAAS;AACnB,UAAK,OAAe,mBAAmB;AACrC,gBAAQ,IAAI,4CAA4C,OAAO;AAAA,MACjE;AAEA,uBAAiB,MAAA;AACjB,kBAAA;AACA,qBAAA;AAEA,iBAAW,MAAM;AAAE,YAAI,QAAS,cAAA;AAAA,MAAgB,GAAG,GAAG;AAAA,IACxD;AAGA,WAAO,iBAAiB,YAAY,WAAW;AAG/C,UAAM,WAAW,QAAQ,UAAU,KAAK,OAAO;AAC/C,UAAM,cAAc,QAAQ,aAAa,KAAK,OAAO;AACrD,YAAQ,YAAY,YAAY,MAAW;AACzC,eAAS,GAAG,IAAI;AAChB,kBAAA;AAAA,IACF;AACA,YAAQ,eAAe,YAAY,MAAW;AAC5C,kBAAY,GAAG,IAAI;AACnB,kBAAA;AAAA,IACF;AAEA,wBAAoB,MAAM;AACxB,aAAO,oBAAoB,YAAY,WAAW;AAClD,cAAQ,YAAY;AACpB,cAAQ,eAAe;AAAA,IACzB;AAAA,EACF;AAEA,WAAS,WAAW;AAClB,QAAI,QAAS;AACb,cAAU;AACV,oBAAA;AACA,2BAAA;AACA,iBAAA;AAAA,EACF;AAEA,WAAS,aAAa;AACpB,cAAU;AACV,qBAAiB,MAAA;AACjB,sBAAkB;AAClB,wBAAA;AACA,wBAAoB;AACpB,gBAAA;AACA,mBAAA;AACA,qBAAA;AAAA,EACF;AAEA,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,MAAM,MAAO,KAAK,WAAW,IAAI,IAAI,sBAAsB;AAAA,IAC3D,aAAa,KAAK,WAAW,IAAI,IAC7B,uCACA;AAAA,IACJ,MAAM;AAAA,IACN,UAAU;AAAA,IACV;AAAA,IACA;AAAA,IACA,UAAU,OAAqB;AAAA,MAC7B,IAAI;AAAA,MACJ;AAAA,MACA,OAAO,EAAE,MAAA;AAAA,IAAM;AAAA,IAEjB,UAAU,CAAC,aAAyC;AAClD,UAAI,SAAS,UAAU,SAAS,UAAU,cAAc,SAAS,UAAU,YAAY;AACrF,gBAAQ,SAAS;AACjB,wBAAA;AAAA,MACF;AAAA,IACF;AAAA,EAAA;AAEJ;"}