accessify-widget 0.3.81 → 0.3.82
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/{alt-text-By2PvE1U.js → alt-text-Dg2ted6-.js} +5 -3
- package/dist/alt-text-Dg2ted6-.js.map +1 -0
- package/dist/{animation-stop-C2Ced0LV.js → animation-stop-C4OcBKkR.js} +32 -10
- package/dist/animation-stop-C4OcBKkR.js.map +1 -0
- package/dist/{index-B8IGMa5t.js → index-Bdke9nRb.js} +14 -9
- package/dist/{index-B8IGMa5t.js.map → index-Bdke9nRb.js.map} +1 -1
- package/dist/{keyboard-nav-BZXsqIA2.js → keyboard-nav-AFyqDbnd.js} +2 -2
- package/dist/{keyboard-nav-BZXsqIA2.js.map → keyboard-nav-AFyqDbnd.js.map} +1 -1
- package/dist/{page-structure-BKyxP7oX.js → page-structure-BpCqEHJ6.js} +7 -3
- package/dist/page-structure-BpCqEHJ6.js.map +1 -0
- package/dist/{text-simplify-H4aHr88a.js → text-simplify-7XhQbbXL.js} +5 -3
- package/dist/{text-simplify-H4aHr88a.js.map → text-simplify-7XhQbbXL.js.map} +1 -1
- package/dist/{tts-zrXtEd07.js → tts-64TqRR7s.js} +9 -6
- package/dist/tts-64TqRR7s.js.map +1 -0
- package/dist/widget.js +1 -1
- package/dist/widget.js.map +1 -1
- package/package.json +10 -10
- package/LICENSE +0 -21
- package/dist/alt-text-By2PvE1U.js.map +0 -1
- package/dist/animation-stop-C2Ced0LV.js.map +0 -1
- package/dist/page-structure-BKyxP7oX.js.map +0 -1
- package/dist/tts-zrXtEd07.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "accessify-widget",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.82",
|
|
4
4
|
"description": "Accessify-Widget accessibility widget for any website",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://github.com/maddesv1-ctrl/accessify-widget",
|
|
@@ -36,6 +36,14 @@
|
|
|
36
36
|
"dist/",
|
|
37
37
|
"README.md"
|
|
38
38
|
],
|
|
39
|
+
"scripts": {
|
|
40
|
+
"dev": "vite build --watch",
|
|
41
|
+
"build": "vite build && BUILD_WIDGET=true vite build && node scripts/build-loader.js",
|
|
42
|
+
"test": "vitest run",
|
|
43
|
+
"test:watch": "vitest",
|
|
44
|
+
"test:e2e": "playwright test",
|
|
45
|
+
"lint": "echo 'no linter configured'"
|
|
46
|
+
},
|
|
39
47
|
"devDependencies": {
|
|
40
48
|
"@axe-core/playwright": "^4.9.0",
|
|
41
49
|
"@playwright/test": "^1.45.0",
|
|
@@ -49,13 +57,5 @@
|
|
|
49
57
|
},
|
|
50
58
|
"dependencies": {
|
|
51
59
|
"axe-core": "^4.9.0"
|
|
52
|
-
},
|
|
53
|
-
"scripts": {
|
|
54
|
-
"dev": "vite build --watch",
|
|
55
|
-
"build": "vite build && BUILD_WIDGET=true vite build && node scripts/build-loader.js",
|
|
56
|
-
"test": "vitest run",
|
|
57
|
-
"test:watch": "vitest",
|
|
58
|
-
"test:e2e": "playwright test",
|
|
59
|
-
"lint": "echo 'no linter configured'"
|
|
60
60
|
}
|
|
61
|
-
}
|
|
61
|
+
}
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 Accessify Contributors
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"alt-text-By2PvE1U.js","sources":["../src/features/alt-text.ts"],"sourcesContent":["// ---------------------------------------------------------------------------\n// Accessify – Bildbeschreibung (Image Description) Feature\n// ---------------------------------------------------------------------------\n//\n// A) AUTO-APPLY (runs on widget init, no feature activation needed):\n// Loads alt texts from server manifest + IndexedDB cache and silently\n// applies them to images (alt + title). Ensures all visitors get\n// alt texts immediately.\n//\n// B) GENERATE (when user activates the feature toggle):\n// Scans for images without alt text → generates via AI → injects\n// alt + title into DOM. Title attribute provides native hover tooltip\n// on every image, consistently.\n//\n// Framer compatibility:\n// - Uses currentSrc || src (srcset-aware)\n// - Treats empty alt=\"\" on large images as \"missing\" (Framer sets alt=\"\" on all)\n// - Delayed re-scans for lazy-loaded images\n// - MutationObserver for dynamically added images\n// ---------------------------------------------------------------------------\n\nimport type { FeatureModule, FeatureState, AIService } from '../types';\nimport { RateLimitError } from '../services/ai-service';\nimport { getCurrentWidgetLang } from '../i18n/index';\n\n// ---------------------------------------------------------------------------\n// IndexedDB cache\n// ---------------------------------------------------------------------------\n\nconst IDB_NAME = 'accessify-alt-text-cache';\nconst IDB_STORE = 'alt-texts';\nconst IDB_VERSION = 2;\n\ninterface CachedAltText {\n key: string; src: string; altText: string; lang: string; createdAt: number;\n}\n\nfunction hashSrc(src: string): string {\n let hash = 5381;\n const n = src.toLowerCase().trim();\n for (let i = 0; i < n.length; i++) { hash = ((hash << 5) + hash) + n.charCodeAt(i); hash |= 0; }\n return Math.abs(hash).toString(36);\n}\n\n/** Normalize image URL: strip query params for cache matching (Framer adds ?width=&height=) */\nfunction normalizeImageUrl(url: string): string {\n try {\n const u = new URL(url);\n u.searchParams.delete('width');\n u.searchParams.delete('height');\n u.searchParams.delete('scale-down-to');\n return u.href;\n } catch {\n return url;\n }\n}\n\n/** Get the best available src for an image (Framer srcset aware) */\nfunction getImageSrc(img: HTMLImageElement): string {\n return img.currentSrc || img.src;\n}\n\nfunction openCache(): Promise<IDBDatabase | null> {\n return new Promise((resolve) => {\n try {\n const req = indexedDB.open(IDB_NAME, IDB_VERSION);\n req.onupgradeneeded = () => {\n const db = req.result;\n if (!db.objectStoreNames.contains(IDB_STORE)) db.createObjectStore(IDB_STORE, { keyPath: 'key' });\n if (!db.objectStoreNames.contains('alt-text-reports')) db.createObjectStore('alt-text-reports', { keyPath: 'key' });\n };\n req.onsuccess = () => resolve(req.result);\n req.onerror = () => resolve(null);\n } catch { resolve(null); }\n });\n}\n\nasync function getAllCachedAltTexts(): Promise<Map<string, string>> {\n const db = await openCache();\n const map = new Map<string, string>();\n if (!db) return map;\n return new Promise((resolve) => {\n try {\n const tx = db.transaction(IDB_STORE, 'readonly');\n const req = tx.objectStore(IDB_STORE).getAll();\n req.onsuccess = () => {\n for (const r of req.result as CachedAltText[]) { map.set(r.src, r.altText); }\n resolve(map);\n };\n req.onerror = () => resolve(map);\n } catch { resolve(map); }\n });\n}\n\nasync function getCachedAltText(src: string): Promise<string | null> {\n const db = await openCache();\n if (!db) return null;\n return new Promise((resolve) => {\n try {\n const tx = db.transaction(IDB_STORE, 'readonly');\n const store = tx.objectStore(IDB_STORE);\n const req1 = store.get(hashSrc(src));\n req1.onsuccess = () => {\n if ((req1.result as CachedAltText | undefined)?.altText) {\n resolve(req1.result.altText);\n return;\n }\n const norm = normalizeImageUrl(src);\n if (norm !== src) {\n const req2 = store.get(hashSrc(norm));\n req2.onsuccess = () => resolve((req2.result as CachedAltText | undefined)?.altText || null);\n req2.onerror = () => resolve(null);\n } else {\n resolve(null);\n }\n };\n req1.onerror = () => resolve(null);\n } catch { resolve(null); }\n });\n}\n\nasync function setCachedAltText(src: string, altText: string, langCode: string): Promise<void> {\n const db = await openCache();\n if (!db) return;\n return new Promise((resolve) => {\n try {\n const tx = db.transaction(IDB_STORE, 'readwrite');\n tx.objectStore(IDB_STORE).put({ key: hashSrc(src), src, altText, lang: langCode, createdAt: Date.now() } as CachedAltText);\n tx.oncomplete = () => resolve();\n tx.onerror = () => resolve();\n } catch { resolve(); }\n });\n}\n\n// ---------------------------------------------------------------------------\n// Server-side alt-text cache (cross-browser, cross-visitor)\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_API_BASE = 'https://accessify-api.accessify.workers.dev';\n\n// Module-wide mode tracking, set on every manifest fetch. 'manual' = default,\n// never run live AI for unknown images. 'auto' = live fallback + auto-persist OK.\ntype ResimplifyMode = 'manual' | 'auto';\nlet currentAltSiteMode: ResimplifyMode = 'manual';\n\nasync function fetchServerAltTexts(siteKey: string, proxyUrl: string, lang: string): Promise<Map<string, string>> {\n const map = new Map<string, string>();\n try {\n const base = proxyUrl || DEFAULT_API_BASE;\n const pageUrl = window.location.origin + window.location.pathname;\n const res = await fetch(`${base}/v1/manifest?siteKey=${encodeURIComponent(siteKey)}&url=${encodeURIComponent(pageUrl)}&feature=alt_text&variant=${encodeURIComponent(lang)}`, {\n headers: { 'Accept': 'application/json' },\n cache: 'no-cache',\n });\n if (!res.ok) return map;\n const data = await res.json() as { blocks?: Array<{ selector: string; result: string }>; siteMode?: ResimplifyMode };\n // Update authoritative mode from server on every fetch\n currentAltSiteMode = data.siteMode === 'auto' ? 'auto' : 'manual';\n if (data.blocks) {\n for (const block of data.blocks) {\n if (block.selector && block.result) map.set(block.selector, block.result);\n }\n }\n } catch { /* fall back to IndexedDB */ }\n return map;\n}\n\nfunction persistAltTextToServer(siteKey: string, proxyUrl: string, imageUrl: string, altText: string, lang: string): void {\n try {\n const base = proxyUrl || DEFAULT_API_BASE;\n const pageUrl = window.location.origin + window.location.pathname;\n fetch(`${base}/v1/cache/persist-alt-text`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ siteKey, pageUrl, imageUrl, altText, lang }),\n }).catch(() => {});\n } catch { /* fire-and-forget */ }\n}\n\n// ---------------------------------------------------------------------------\n// Auto-apply: runs on widget init, no feature activation needed\n// ---------------------------------------------------------------------------\n\nlet autoApplied = false;\n\n/** Silently apply cached/manifest alt texts to all images on the page */\nexport async function autoApplyCachedAltTexts(\n widgetConfig?: { siteKey?: string; proxyUrl?: string; lang?: string },\n): Promise<number> {\n if (autoApplied) return 0;\n autoApplied = true;\n\n const cache = new Map<string, string>();\n const siteKey = widgetConfig?.siteKey;\n const proxyUrl = widgetConfig?.proxyUrl || '';\n const pageLang = widgetConfig?.lang?.split('-')[0] || document.documentElement.lang?.split('-')[0] || 'en';\n\n // 1. Server manifest (freshest, cross-browser)\n if (siteKey) {\n const serverCache = await fetchServerAltTexts(siteKey, proxyUrl, pageLang);\n for (const [url, alt] of serverCache) {\n // Store BOTH original URL and normalized URL (without width/height params)\n // so lookups by currentSrc (any size) will match\n cache.set(url, alt);\n const norm = normalizeImageUrl(url);\n if (norm !== url) cache.set(norm, alt);\n setCachedAltText(url, alt, pageLang).catch(() => {});\n }\n }\n\n // 2. IndexedDB (fills gaps)\n const localCache = await getAllCachedAltTexts();\n for (const [url, alt] of localCache) {\n if (!cache.has(url)) cache.set(url, alt);\n }\n\n if (cache.size === 0) return 0;\n\n function applyToImg(img: HTMLImageElement): boolean {\n if (img.closest('#accessify-root') || img.closest('accessify-widget')) return false;\n const src = getImageSrc(img);\n const norm = normalizeImageUrl(src);\n const imgSrcNorm = normalizeImageUrl(img.src);\n const cached = cache.get(src) || cache.get(norm) || cache.get(img.src) || cache.get(imgSrcNorm);\n if (!cached) return false;\n\n const alt = img.getAttribute('alt');\n if (alt === null || alt.trim() === '') {\n img.setAttribute('alt', cached);\n img.setAttribute('data-accessify-alt', 'auto');\n }\n // Always set title for native hover tooltip\n img.setAttribute('title', cached);\n return true;\n }\n\n let applied = 0;\n document.querySelectorAll<HTMLImageElement>('img').forEach((img) => {\n if (applyToImg(img)) applied++;\n });\n\n // Watch for late-loading images (Framer SPA, lazy load)\n const observer = new MutationObserver((mutations) => {\n for (const m of mutations) {\n for (const node of m.addedNodes) {\n const imgs = node instanceof HTMLImageElement ? [node] :\n node instanceof HTMLElement ? Array.from(node.querySelectorAll<HTMLImageElement>('img')) : [];\n for (const img of imgs) applyToImg(img);\n }\n }\n });\n observer.observe(document.body, { childList: true, subtree: true });\n setTimeout(() => observer.disconnect(), 30000);\n\n return applied;\n}\n\n// ---------------------------------------------------------------------------\n// Feature Module\n// ---------------------------------------------------------------------------\n\nexport default function createAltTextModule(\n aiService?: AIService,\n initialLang: string = 'de',\n serverConfig?: { siteKey?: string; proxyUrl?: string },\n): FeatureModule {\n const siteKey = serverConfig?.siteKey || '';\n const proxyUrl = serverConfig?.proxyUrl || '';\n let enabled = false;\n let domObserver: MutationObserver | null = null;\n let autoGenerating = false;\n\n // Tracked images\n let missingAltImages: HTMLImageElement[] = [];\n const processedImages = new Map<HTMLImageElement, { generatedAlt: string }>();\n\n function lang(): string { return getCurrentWidgetLang() || initialLang; }\n function isDE(): boolean { return lang().startsWith('de'); }\n\n // -------------------------------------------------------------------------\n // Helpers\n // -------------------------------------------------------------------------\n\n function isGenericAlt(alt: string): boolean {\n if (!alt.trim()) return true;\n if (/^(image|img|photo|bild|foto|untitled|placeholder)/i.test(alt)) return true;\n if (/^IMG_\\d+/i.test(alt)) return true;\n if (/\\.(jpg|jpeg|png|gif|webp|svg|avif)$/i.test(alt)) return true;\n return false;\n }\n\n function isDecorativeImage(img: HTMLImageElement): boolean {\n const role = img.getAttribute('role');\n if (role === 'presentation' || role === 'none') return true;\n if (img.complete && img.naturalWidth > 0 && (img.naturalWidth < 50 || img.naturalHeight < 50)) return true;\n return false;\n }\n\n function isValidImage(img: HTMLImageElement): boolean {\n if (img.closest('#accessify-root') || img.closest('accessify-widget')) return false;\n // Skip tiny images (icons, tracking pixels, avatars)\n if (img.complete && img.naturalWidth > 0 && (img.naturalWidth < 50 || img.naturalHeight < 50)) return false;\n // Skip images not rendered on-screen (display:none, visibility:hidden, etc.)\n const rect = img.getBoundingClientRect();\n if (rect.width < 40 || rect.height < 40) return false;\n // Skip images hidden via CSS (opacity:0, visibility:hidden)\n const style = getComputedStyle(img);\n if (style.display === 'none' || style.visibility === 'hidden' || style.opacity === '0') return false;\n // Skip images with role=\"presentation\" or aria-hidden\n if (img.getAttribute('role') === 'presentation' || img.getAttribute('aria-hidden') === 'true') return false;\n return true;\n }\n\n function needsAltText(img: HTMLImageElement): boolean {\n if (!isValidImage(img)) return false;\n const alt = img.getAttribute('alt');\n if (alt === null) return true;\n if (isGenericAlt(alt)) return true;\n if (alt === '' && !isDecorativeImage(img)) return true;\n return false;\n }\n\n function scanForMissingAlt(): HTMLImageElement[] {\n const missing: HTMLImageElement[] = [];\n document.querySelectorAll<HTMLImageElement>('img').forEach((img) => {\n if (needsAltText(img)) missing.push(img);\n });\n document.querySelectorAll<HTMLElement>('picture').forEach((picture) => {\n if (picture.closest('#accessify-root') || picture.closest('accessify-widget')) return;\n const img = picture.querySelector('img');\n if (img && !missing.includes(img) && needsAltText(img)) {\n missing.push(img);\n }\n });\n return missing;\n }\n\n function gatherImageContext(img: HTMLImageElement): string {\n const parts: string[] = [];\n try {\n const url = new URL(getImageSrc(img), window.location.href);\n const filename = url.pathname.split('/').pop();\n if (filename) parts.push(`Filename: ${filename}`);\n } catch {}\n const figure = img.closest('figure');\n if (figure) {\n const caption = figure.querySelector('figcaption');\n if (caption?.textContent?.trim()) parts.push(`Caption: ${caption.textContent.trim()}`);\n }\n const container = img.parentElement?.parentElement || img.parentElement;\n if (container) {\n const heading = container.querySelector('h1, h2, h3, h4, h5, h6');\n if (heading?.textContent?.trim()) parts.push(`Nearby heading: ${heading.textContent.trim()}`);\n }\n return parts.join('. ');\n }\n\n // -------------------------------------------------------------------------\n // Apply + Generate (title = native hover tooltip on every image)\n // -------------------------------------------------------------------------\n\n function applyAltText(img: HTMLImageElement, altText: string) {\n img.setAttribute('alt', altText);\n img.setAttribute('title', altText);\n processedImages.set(img, { generatedAlt: altText });\n if (enabled) addInfoBadge(img, altText);\n }\n\n /** Set title on every visible image that has alt text but no title yet */\n function applyTitlesToAllImages() {\n document.querySelectorAll<HTMLImageElement>('img').forEach((img) => {\n if (!isValidImage(img)) return;\n // Skip if already has a title\n if (img.getAttribute('title')) return;\n const alt = img.getAttribute('alt');\n if (alt && alt.trim()) {\n img.setAttribute('title', alt);\n img.setAttribute('data-accessify-title', 'auto');\n }\n });\n }\n\n // -------------------------------------------------------------------------\n // Info badge — small \"i\" icon on each image with alt text\n // -------------------------------------------------------------------------\n // Badges live in a single overlay container on document.body (position:fixed\n // would need scroll tracking, so we use position:absolute on the body).\n // This keeps them completely outside any overflow:hidden zoom containers.\n // -------------------------------------------------------------------------\n\n const BADGE_ATTR = 'data-accessify-badge';\n const WRAPPER_ATTR = 'data-accessify-badge-wrap';\n const OVERLAY_ID = 'accessify-badge-overlay';\n const BADGE_STYLE_ID = 'accessify-badge-styles';\n let badgePositionRAF: number | null = null;\n let trackedBadges: Array<{ target: HTMLElement; badge: HTMLElement; tooltip: HTMLElement }> = [];\n let outsideClickHandler: ((e: Event) => void) | null = null;\n\n function getOrCreateOverlay(): HTMLElement {\n let overlay = document.getElementById(OVERLAY_ID);\n if (!overlay) {\n overlay = document.createElement('div');\n overlay.id = OVERLAY_ID;\n Object.assign(overlay.style, {\n position: 'absolute',\n top: '0',\n left: '0',\n width: '0',\n height: '0',\n overflow: 'visible',\n pointerEvents: 'none',\n zIndex: '10000',\n });\n document.body.appendChild(overlay);\n }\n // Inject badge hover + coarse pointer styles once\n if (!document.getElementById(BADGE_STYLE_ID)) {\n const style = document.createElement('style');\n style.id = BADGE_STYLE_ID;\n style.textContent = `\n [${BADGE_ATTR}=\"badge\"]:hover + [${BADGE_ATTR}=\"tooltip\"] { display: block !important; }\n [${BADGE_ATTR}=\"badge\"]:hover { background: rgba(0,0,0,0.85) !important; }\n @media (pointer: coarse) {\n [${BADGE_ATTR}=\"badge\"] { width: 36px !important; height: 36px !important; line-height: 36px !important; font-size: 16px !important; }\n }\n `;\n document.head.appendChild(style);\n }\n return overlay;\n }\n\n function positionBadge(target: HTMLElement, badge: HTMLElement, tooltip: HTMLElement) {\n const rect = target.getBoundingClientRect();\n // Skip if element is not visible / too small / off-screen\n if (rect.width < 40 || rect.height < 40 ||\n rect.bottom < 0 || rect.top > window.innerHeight + 200 ||\n rect.right < 0 || rect.left > window.innerWidth + 200) {\n badge.style.display = 'none';\n tooltip.style.display = 'none';\n return;\n }\n const scrollX = window.scrollX;\n const scrollY = window.scrollY;\n const top = rect.top + scrollY + 6;\n const left = rect.left + scrollX + 6;\n badge.style.display = '';\n badge.style.top = `${top}px`;\n badge.style.left = `${left}px`;\n // Tooltip positioned below badge\n tooltip.style.top = `${top + 26}px`;\n tooltip.style.left = `${left}px`;\n }\n\n function updateAllBadgePositions() {\n for (const { target, badge, tooltip } of trackedBadges) {\n if (!document.body.contains(target)) continue;\n positionBadge(target, badge, tooltip);\n }\n }\n\n function startBadgeTracking() {\n if (badgePositionRAF !== null) return;\n const tick = () => {\n updateAllBadgePositions();\n badgePositionRAF = requestAnimationFrame(tick);\n };\n badgePositionRAF = requestAnimationFrame(tick);\n\n // Outside-click handler to close all open tooltips\n if (!outsideClickHandler) {\n outsideClickHandler = (e: Event) => {\n const target = e.target as HTMLElement;\n if (target.getAttribute(BADGE_ATTR) === 'badge') return;\n for (const entry of trackedBadges) {\n entry.tooltip.style.display = 'none';\n entry.badge.style.background = 'rgba(0,0,0,0.6)';\n }\n };\n document.addEventListener('click', outsideClickHandler, { passive: true });\n }\n }\n\n function stopBadgeTracking() {\n if (badgePositionRAF !== null) {\n cancelAnimationFrame(badgePositionRAF);\n badgePositionRAF = null;\n }\n if (outsideClickHandler) {\n document.removeEventListener('click', outsideClickHandler);\n outsideClickHandler = null;\n }\n }\n\n function addInfoBadge(target: HTMLElement, altText: string) {\n if (target.getAttribute(BADGE_ATTR)) return;\n target.setAttribute(BADGE_ATTR, '1');\n\n const overlay = getOrCreateOverlay();\n\n // Badge \"i\" circle\n const badge = document.createElement('span');\n Object.assign(badge.style, {\n position: 'absolute',\n width: '22px',\n height: '22px',\n borderRadius: '50%',\n background: 'rgba(0,0,0,0.6)',\n color: '#fff',\n fontFamily: 'sans-serif',\n fontWeight: 'bold',\n fontSize: '13px',\n lineHeight: '22px',\n textAlign: 'center',\n cursor: 'default',\n pointerEvents: 'auto',\n boxShadow: '0 1px 4px rgba(0,0,0,0.4)',\n transition: 'background 0.15s',\n });\n badge.textContent = 'i';\n badge.setAttribute('aria-hidden', 'true');\n badge.setAttribute('translate', 'no');\n badge.classList.add('notranslate');\n badge.setAttribute(BADGE_ATTR, 'badge');\n\n // Tooltip\n const tooltip = document.createElement('span');\n Object.assign(tooltip.style, {\n display: 'none',\n position: 'absolute',\n minWidth: '150px',\n maxWidth: '280px',\n padding: '6px 10px',\n background: 'rgba(0,0,0,0.88)',\n color: '#fff',\n fontFamily: 'sans-serif',\n fontSize: '12px',\n lineHeight: '1.4',\n borderRadius: '6px',\n pointerEvents: 'none',\n wordWrap: 'break-word',\n boxShadow: '0 2px 8px rgba(0,0,0,0.3)',\n zIndex: '1',\n });\n tooltip.textContent = altText;\n tooltip.setAttribute('translate', 'no');\n tooltip.classList.add('notranslate');\n tooltip.setAttribute(BADGE_ATTR, 'tooltip');\n\n badge.addEventListener('click', () => {\n const isOpen = tooltip.style.display === 'block';\n // Close all other open tooltips first\n for (const entry of trackedBadges) {\n entry.tooltip.style.display = 'none';\n entry.badge.style.background = 'rgba(0,0,0,0.6)';\n }\n if (!isOpen) {\n badge.style.background = 'rgba(0,0,0,0.85)';\n tooltip.style.display = 'block';\n }\n });\n\n overlay.appendChild(badge);\n overlay.appendChild(tooltip);\n\n // Initial position\n positionBadge(target, badge, tooltip);\n\n // Track for continuous position updates\n trackedBadges.push({ target, badge, tooltip });\n }\n\n function removeAllBadges() {\n stopBadgeTracking();\n trackedBadges = [];\n // Remove overlay and badge styles\n document.getElementById(OVERLAY_ID)?.remove();\n document.getElementById(BADGE_STYLE_ID)?.remove();\n // Clear badge markers\n document.querySelectorAll(`[${BADGE_ATTR}]`).forEach(el => el.removeAttribute(BADGE_ATTR));\n // Legacy: unwrap any leftover wrappers from old version\n document.querySelectorAll<HTMLElement>(`[${WRAPPER_ATTR}]`).forEach(wrapper => {\n const img = wrapper.querySelector('img');\n if (img && wrapper.parentElement) {\n wrapper.parentElement.insertBefore(img, wrapper);\n wrapper.remove();\n }\n });\n }\n\n /** Add info badges to ALL visible images */\n function addBadgesToAllImages() {\n document.querySelectorAll<HTMLImageElement>('img').forEach((img) => {\n if (!isValidImage(img)) return;\n const alt = img.getAttribute('alt') || img.getAttribute('title');\n if (alt && alt.trim()) {\n addInfoBadge(img, alt.trim());\n }\n });\n // Also for background images\n document.querySelectorAll<HTMLElement>('[data-accessify-bg-alt]').forEach((el) => {\n const alt = el.getAttribute('aria-label');\n if (alt && alt.trim()) {\n addInfoBadge(el, alt.trim());\n }\n });\n // Start tracking positions (handles scroll, resize, zoom)\n if (trackedBadges.length > 0) startBadgeTracking();\n }\n\n function removeTitles() {\n document.querySelectorAll<HTMLImageElement>('img[data-accessify-title=\"auto\"]').forEach((img) => {\n img.removeAttribute('title');\n img.removeAttribute('data-accessify-title');\n });\n // Clean up background image descriptions\n document.querySelectorAll<HTMLElement>('[data-accessify-bg-alt]').forEach((el) => {\n el.removeAttribute('role');\n el.removeAttribute('aria-label');\n el.removeAttribute('title');\n el.removeAttribute('data-accessify-bg-alt');\n });\n // Remove all info badges\n removeAllBadges();\n }\n\n // -------------------------------------------------------------------------\n // Background images (CSS background-image)\n // -------------------------------------------------------------------------\n\n /** Find significant elements with CSS background-image and no accessibility info */\n function scanForBackgroundImages(): HTMLElement[] {\n const found: HTMLElement[] = [];\n // Scan ALL elements — not just specific class patterns — for background-image\n const allElements = document.querySelectorAll<HTMLElement>('*');\n for (const el of allElements) {\n if (el.closest('#accessify-root') || el.closest('accessify-widget')) continue;\n if (el.getAttribute('data-accessify-bg-alt')) continue;\n // Skip tiny or invisible elements quickly via offsetHeight (avoids getComputedStyle)\n if (el.offsetWidth < 200 || el.offsetHeight < 150) continue;\n const style = getComputedStyle(el);\n const bg = style.backgroundImage;\n if (!bg || bg === 'none') continue;\n // Must have a url() reference (not just gradients)\n const urlMatch = bg.match(/url\\([\"']?([^\"')]+)[\"']?\\)/);\n if (!urlMatch) continue;\n // Skip SVG data URIs and tiny inline images\n if (urlMatch[1].startsWith('data:image/svg')) continue;\n // Skip if already has role=\"img\" with aria-label\n if (el.getAttribute('role') === 'img' && el.getAttribute('aria-label')) continue;\n found.push(el);\n }\n return found;\n }\n\n function getBackgroundImageUrl(el: HTMLElement): string | null {\n const bg = getComputedStyle(el).backgroundImage;\n const match = bg?.match(/url\\([\"']?([^\"')]+)[\"']?\\)/);\n return match ? match[1] : null;\n }\n\n function applyBgAlt(el: HTMLElement, altText: string) {\n el.setAttribute('role', 'img');\n el.setAttribute('aria-label', altText);\n el.setAttribute('title', altText);\n el.setAttribute('data-accessify-bg-alt', 'auto');\n if (enabled) addInfoBadge(el, altText);\n }\n\n async function generateBgAlts() {\n if (!aiService || !enabled) return;\n if (currentAltSiteMode !== 'auto') {\n if ((window as any).__ACCESSIFY_DEBUG) {\n console.info('[Accessify] alt-text: siteMode=manual — skipping live generation for background images');\n }\n return;\n }\n const bgElements = scanForBackgroundImages();\n for (const el of bgElements) {\n if (!enabled) break;\n const src = getBackgroundImageUrl(el);\n if (!src) continue;\n // Check cache first\n const cached = await getCachedAltText(src);\n if (cached) {\n applyBgAlt(el, cached);\n continue;\n }\n // Generate via AI\n try {\n const ctx = gatherBgContext(el);\n const alt = await aiService.generateAltText(src, ctx, lang());\n if (alt && enabled) {\n applyBgAlt(el, alt);\n setCachedAltText(src, alt, lang()).catch(() => {});\n if (siteKey) persistAltTextToServer(siteKey, proxyUrl, src, alt, lang());\n }\n } catch (err) {\n console.warn('[Accessify] Bg alt-text generation failed:', src, err);\n }\n }\n }\n\n function gatherBgContext(el: HTMLElement): string {\n const parts: string[] = [];\n // Tag and class hints\n parts.push(`Element: <${el.tagName.toLowerCase()}>`);\n if (el.className) parts.push(`Class: ${el.className}`);\n // Text inside\n const text = el.textContent?.trim().slice(0, 100);\n if (text) parts.push(`Overlay text: ${text}`);\n // Nearby heading\n const heading = el.querySelector('h1, h2, h3');\n if (heading?.textContent?.trim()) parts.push(`Heading: ${heading.textContent.trim()}`);\n return parts.join('. ');\n }\n\n async function generateAll() {\n if (autoGenerating || !enabled || !aiService) return;\n autoGenerating = true;\n const CONCURRENCY = 3;\n\n // Phase 1: apply from IndexedDB cache\n const uncached: HTMLImageElement[] = [];\n for (const img of missingAltImages) {\n if (!enabled) { autoGenerating = false; return; }\n if (processedImages.has(img)) continue;\n const src = getImageSrc(img);\n const cached = await getCachedAltText(src);\n if (cached) {\n applyAltText(img, cached);\n } else {\n uncached.push(img);\n }\n }\n\n if (!enabled) { autoGenerating = false; return; }\n if (!uncached.length) { autoGenerating = false; return; }\n\n // SAFETY GATE: only proceed with live AI if site is in 'auto' mode.\n // In 'manual' (default), leave images without generated alt text — the\n // owner must trigger a crawl or switch modes from the dashboard.\n if (currentAltSiteMode !== 'auto') {\n if ((window as any).__ACCESSIFY_DEBUG) {\n console.info(\n `[Accessify] alt-text: siteMode=manual — ${uncached.length} image(s) left untouched. ` +\n `Trigger a crawl from the dashboard or enable auto mode.`,\n );\n }\n autoGenerating = false;\n return;\n }\n\n // Phase 2: AI generation in parallel batches\n for (let i = 0; i < uncached.length; i += CONCURRENCY) {\n if (!enabled) { autoGenerating = false; return; }\n const batch = uncached.slice(i, i + CONCURRENCY);\n await Promise.all(batch.map(async (img) => {\n if (!enabled) return;\n try {\n const src = getImageSrc(img);\n const ctx = gatherImageContext(img);\n const alt = await aiService!.generateAltText(src, ctx, lang());\n if (alt && enabled) {\n applyAltText(img, alt);\n setCachedAltText(src, alt, lang()).catch(() => {});\n if (siteKey) persistAltTextToServer(siteKey, proxyUrl, src, alt, lang());\n }\n } catch (err) {\n console.warn('[Accessify] Alt-text generation failed:', getImageSrc(img), err);\n }\n }));\n }\n\n autoGenerating = false;\n }\n\n async function generateSingle(img: HTMLImageElement) {\n if (!aiService || !enabled) return;\n const src = getImageSrc(img);\n const cached = await getCachedAltText(src);\n if (cached) { applyAltText(img, cached); return; }\n\n // Mode gate — only live-generate when site is in 'auto' mode\n if (currentAltSiteMode !== 'auto') return;\n\n try {\n const ctx = gatherImageContext(img);\n const alt = await aiService.generateAltText(src, ctx, lang());\n if (alt && enabled) {\n applyAltText(img, alt);\n setCachedAltText(src, alt, lang()).catch(() => {});\n if (siteKey) persistAltTextToServer(siteKey, proxyUrl, src, alt, lang());\n }\n } catch (err) {\n console.warn('[Accessify] Alt-text generation failed:', src, err);\n }\n }\n\n // -------------------------------------------------------------------------\n // Dynamic image registration\n // -------------------------------------------------------------------------\n\n function tryRegisterImage(img: HTMLImageElement) {\n if (!enabled) return;\n if (!isValidImage(img)) return;\n if (missingAltImages.includes(img)) return;\n\n function addIfValid() {\n if (!enabled || missingAltImages.includes(img)) return;\n if (img.naturalWidth < 20 || img.naturalHeight < 20) return;\n\n if (needsAltText(img)) {\n missingAltImages.push(img);\n generateSingle(img);\n } else {\n // Image has alt but maybe no title/badge yet — add both\n const alt = img.getAttribute('alt');\n if (alt && alt.trim()) {\n if (!img.getAttribute('title')) {\n img.setAttribute('title', alt);\n img.setAttribute('data-accessify-title', 'auto');\n }\n addInfoBadge(img, alt.trim());\n }\n }\n }\n\n if (img.complete && img.naturalWidth > 0) addIfValid();\n else img.addEventListener('load', addIfValid, { once: true });\n }\n\n // -------------------------------------------------------------------------\n // Lifecycle\n // -------------------------------------------------------------------------\n\n function activate() {\n if (enabled) return;\n enabled = true;\n\n // Recognize images that were auto-applied from cache/manifest\n document.querySelectorAll<HTMLImageElement>('img[data-accessify-alt=\"auto\"]').forEach((img) => {\n if (img.closest('#accessify-root')) return;\n if (!processedImages.has(img)) {\n processedImages.set(img, { generatedAlt: img.getAttribute('alt') || '' });\n missingAltImages.push(img);\n }\n });\n\n // Scan for truly missing alt texts\n const missing = scanForMissingAlt();\n for (const img of missing) {\n if (!missingAltImages.includes(img)) {\n missingAltImages.push(img);\n }\n }\n\n // Start AI generation in background (img + background images)\n generateAll();\n generateBgAlts();\n\n // Set title on ALL images (consistent hover tooltip everywhere)\n applyTitlesToAllImages();\n\n // Add info badges to show alt text is available\n addBadgesToAllImages();\n\n // Register not-yet-loaded images\n document.querySelectorAll<HTMLImageElement>('img').forEach((img) => {\n if (!img.complete) tryRegisterImage(img);\n });\n\n // Delayed re-scans for late-loading images and AI-generated alt texts\n for (const delay of [2000, 5000, 10000]) {\n setTimeout(() => {\n if (enabled) {\n document.querySelectorAll<HTMLImageElement>('img').forEach(tryRegisterImage);\n applyTitlesToAllImages();\n addBadgesToAllImages();\n }\n }, delay);\n }\n\n // Watch for new images\n domObserver = new MutationObserver((mutations) => {\n for (const m of mutations) {\n for (const node of m.addedNodes) {\n if (node instanceof HTMLImageElement) tryRegisterImage(node);\n else if (node instanceof HTMLElement) {\n node.querySelectorAll<HTMLImageElement>('img').forEach(tryRegisterImage);\n }\n }\n }\n });\n domObserver.observe(document.body, { childList: true, subtree: true });\n }\n\n function deactivate() {\n enabled = false;\n autoGenerating = false;\n domObserver?.disconnect();\n domObserver = null;\n\n // Remove auto-added titles (but keep alt texts — those are useful)\n removeTitles();\n missingAltImages = [];\n }\n\n // Run auto-apply immediately when the module is created\n autoApplyCachedAltTexts({ siteKey, proxyUrl, lang: initialLang }).catch(() => {});\n\n return {\n id: 'alt-text',\n name: () => (isDE() ? 'Bildbeschreibung' : 'Image Description'),\n description: isDE() ? 'Bildbeschreibungen per Hover anzeigen und fehlende automatisch erzeugen' : 'Show image descriptions on hover and auto-generate missing ones',\n icon: 'alt-text',\n category: 'ai',\n activate,\n deactivate,\n getState: (): FeatureState => ({\n id: 'alt-text',\n enabled,\n value: {\n missingCount: missingAltImages.filter((img) => !processedImages.has(img)).length,\n processedCount: processedImages.size,\n },\n }),\n };\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"animation-stop-C2Ced0LV.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 /**\n * Detect animations whose keyframes only modify transform-like or filter\n * properties. These are the signature of interaction animations (hover\n * zoom, click scale, focus rings) that Framer Motion and similar libs\n * create on-the-fly. We must CANCEL these instead of FINISHING them,\n * otherwise the element lands in the visible end-state (e.g. scale(1.05)).\n */\n const VISUAL_ONLY_PROPS = new Set([\n 'transform', 'scale', 'rotate', 'translate',\n 'filter', 'backdropFilter', 'backdrop-filter',\n 'webkitTransform', 'webkitFilter', '-webkit-transform', '-webkit-filter',\n 'offset', 'easing', 'composite',\n ]);\n function isVisualOnlyKeyframes(\n keyframes: Keyframe[] | PropertyIndexedKeyframes | null,\n ): boolean {\n if (!keyframes) return false;\n try {\n if (Array.isArray(keyframes)) {\n for (const kf of keyframes) {\n if (!kf || typeof kf !== 'object') return false;\n for (const key of Object.keys(kf)) {\n if (!VISUAL_ONLY_PROPS.has(key)) return false;\n }\n }\n return true;\n }\n // PropertyIndexedKeyframes\n for (const key of Object.keys(keyframes)) {\n if (!VISUAL_ONLY_PROPS.has(key)) return false;\n }\n return true;\n } catch {\n return false;\n }\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 // Never interfere with the widget's own animations. The widget lives\n // in shadow DOM under #accessify-root — walk up through shadow roots\n // and skip if we find the accessify host.\n try {\n let node: Node | null = this;\n while (node) {\n if ((node as Element).id === 'accessify-root') {\n return originalAnimate!.call(this, keyframes, options);\n }\n const parent = (node as any).parentNode || (node as any).host;\n if (parent === node) break;\n node = parent;\n }\n } catch { /* ignore */ }\n\n const visualOnly = isVisualOnlyKeyframes(keyframes);\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 try {\n if (visualOnly) {\n anim.cancel();\n } else {\n anim.finish();\n }\n } 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 /* Neutralize interaction transforms (hover zoom, click scale, focus\n lift). Without this, CSS like \\`.card:hover { transform: scale(1.05) }\\`\n still visibly changes the element — just instantly. Users who\n disable animations expect NO motion including the end-state. */\n *:not(#accessify-root):not(#accessify-root *):hover,\n *:not(#accessify-root):not(#accessify-root *):focus,\n *:not(#accessify-root):not(#accessify-root *):focus-within,\n *:not(#accessify-root):not(#accessify-root *):focus-visible,\n *:not(#accessify-root):not(#accessify-root *):active {\n transform: none !important;\n scale: none !important;\n rotate: none !important;\n translate: 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 isWidgetAnimation(anim: Animation): boolean {\n try {\n const target = (anim.effect as any)?.target as Element | null;\n if (!target) return false;\n let node: Node | null = target;\n while (node) {\n if ((node as Element).id === 'accessify-root') return true;\n const parent = (node as any).parentNode || (node as any).host;\n if (parent === node) break;\n node = parent;\n }\n } catch { /* ignore */ }\n return false;\n }\n\n function finishAllAnimations() {\n try {\n const animations = document.getAnimations?.();\n if (!animations) return;\n for (const anim of animations) {\n if (isWidgetAnimation(anim)) continue;\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":"page-structure-BKyxP7oX.js","sources":["../src/features/page-structure.ts"],"sourcesContent":["import type { FeatureModule, FeatureState } from '../types';\nimport { getCurrentWidgetLang, t } from '../i18n/index';\n\nexport default function createPageStructureModule(): FeatureModule {\n let enabled = false;\n let panelEl: HTMLElement | null = null;\n const PANEL_ID = 'accessify-page-structure';\n let mutationObserver: MutationObserver | null = null;\n let debounceTimer: ReturnType<typeof setTimeout> | null = null;\n let originalPushState: typeof history.pushState | null = null;\n let originalReplaceState: typeof history.replaceState | null = null;\n const boundPopstateHandler = () => scheduleRefresh();\n let lastContentHash = '';\n\n function lang(): string {\n return getCurrentWidgetLang();\n }\n\n function collectHeadings(): Array<{ tag: string; text: string; el: HTMLElement }> {\n const results: Array<{ tag: string; text: string; el: HTMLElement }> = [];\n const headings = document.querySelectorAll<HTMLElement>('h1, h2, h3, h4, h5, h6');\n headings.forEach((el) => {\n if (el.closest('#accessify-root')) return;\n const text = el.textContent?.trim() || '';\n if (text) results.push({ tag: el.tagName.toLowerCase(), text, el });\n });\n return results;\n }\n\n function collectLandmarks(): Array<{ role: string; label: string; el: HTMLElement }> {\n const results: Array<{ role: string; label: string; el: HTMLElement }> = [];\n const selectors = [\n 'main', 'nav', 'aside', 'header', 'footer', 'section[aria-label]',\n 'section[aria-labelledby]', '[role=\"main\"]', '[role=\"navigation\"]',\n '[role=\"complementary\"]', '[role=\"banner\"]', '[role=\"contentinfo\"]',\n '[role=\"search\"]',\n ];\n const els = document.querySelectorAll<HTMLElement>(selectors.join(','));\n els.forEach((el) => {\n if (el.closest('#accessify-root')) return;\n const role = el.getAttribute('role') || el.tagName.toLowerCase();\n const label = el.getAttribute('aria-label') || el.getAttribute('aria-labelledby') || role;\n results.push({ role, label, el });\n });\n return results;\n }\n\n function buildPanel() {\n panelEl = document.createElement('div');\n panelEl.id = PANEL_ID;\n panelEl.setAttribute('role', 'dialog');\n panelEl.setAttribute('aria-label', t('pageStructure.title', lang()));\n\n const headings = collectHeadings();\n const landmarks = collectLandmarks();\n\n const indent: Record<string, string> = {\n h1: '0', h2: '12px', h3: '24px', h4: '36px', h5: '48px', h6: '60px',\n };\n\n let html = `\n <style>\n #${PANEL_ID} {\n position: fixed; top: 50%; left: 50%;\n transform: translate(-50%, -50%);\n z-index: 999998;\n background: #fff; color: #1a1a1a;\n border-radius: 12px; box-shadow: 0 8px 32px rgba(0,0,0,0.2);\n padding: 24px; width: 400px; max-height: 80vh;\n overflow-y: auto; font-family: system-ui, sans-serif;\n font-size: 14px; line-height: 1.5;\n }\n #${PANEL_ID} h3 { margin: 0 0 12px; font-size: 16px; }\n #${PANEL_ID} .ps-section { margin-bottom: 16px; }\n #${PANEL_ID} .ps-label { font-size: 11px; text-transform: uppercase; letter-spacing: 0.5px; color: #888; margin-bottom: 6px; }\n #${PANEL_ID} .ps-item {\n display: block; width: 100%; text-align: left;\n padding: 6px 8px; border: none; background: none;\n cursor: pointer; border-radius: 6px; font-size: 13px;\n color: #1a1a1a; font-family: inherit;\n }\n #${PANEL_ID} .ps-item:hover, #${PANEL_ID} .ps-item:active { background: #f0f0f2; }\n #${PANEL_ID} .ps-item:focus-visible { outline: 2px solid #0055CC; outline-offset: 1px; }\n #${PANEL_ID} .ps-tag { color: #888; font-size: 11px; margin-right: 6px; }\n #${PANEL_ID} .ps-close {\n position: absolute; top: 12px; right: 12px;\n background: none; border: none; cursor: pointer;\n font-size: 18px; color: #888; padding: 4px 8px; border-radius: 4px;\n }\n #${PANEL_ID} .ps-close:hover, #${PANEL_ID} .ps-close:active { background: #f0f0f2; }\n @media (prefers-color-scheme: dark) {\n #${PANEL_ID} { background: #1a1a1a; color: #f0f0f0; }\n #${PANEL_ID} .ps-item { color: #f0f0f0; }\n #${PANEL_ID} .ps-item:hover, #${PANEL_ID} .ps-item:active { background: #2a2a2e; }\n #${PANEL_ID} .ps-close { color: #aaa; }\n #${PANEL_ID} .ps-close:hover, #${PANEL_ID} .ps-close:active { background: #2a2a2e; }\n }\n @media (max-width: 640px) {\n #${PANEL_ID} { width: calc(100vw - 32px); max-width: 400px; }\n }\n </style>\n <button class=\"ps-close\" aria-label=\"${t('widget.close', lang())}\">×</button>\n <h3>${t('pageStructure.title', lang())}</h3>\n `;\n\n if (headings.length > 0) {\n html += `<div class=\"ps-section\"><div class=\"ps-label\">${t('pageStructure.headings', lang())}</div>`;\n headings.forEach((h, i) => {\n html += `<button class=\"ps-item\" data-type=\"heading\" data-index=\"${i}\" style=\"padding-left:${indent[h.tag] || '0'}\">\n <span class=\"ps-tag\">${h.tag}</span>${escapeHtml(h.text.slice(0, 80))}\n </button>`;\n });\n html += '</div>';\n }\n\n if (landmarks.length > 0) {\n html += `<div class=\"ps-section\"><div class=\"ps-label\">${t('pageStructure.landmarks', lang())}</div>`;\n landmarks.forEach((lm, i) => {\n html += `<button class=\"ps-item\" data-type=\"landmark\" data-index=\"${i}\">\n <span class=\"ps-tag\">${escapeHtml(lm.role)}</span>${escapeHtml(lm.label.slice(0, 60))}\n </button>`;\n });\n html += '</div>';\n }\n\n if (headings.length === 0 && landmarks.length === 0) {\n html += `<p style=\"color:#888\">${t('pageStructure.empty', lang())}</p>`;\n }\n\n panelEl.innerHTML = html;\n\n // Event delegation\n panelEl.addEventListener('click', (e) => {\n const btn = (e.target as HTMLElement).closest<HTMLElement>('.ps-item');\n if (btn) {\n const type = btn.dataset.type;\n const idx = parseInt(btn.dataset.index || '0', 10);\n let target: HTMLElement | undefined;\n if (type === 'heading') target = headings[idx]?.el;\n else if (type === 'landmark') target = landmarks[idx]?.el;\n if (target) {\n target.scrollIntoView({ behavior: 'smooth', block: 'center' });\n target.focus({ preventScroll: true });\n }\n }\n if ((e.target as HTMLElement).closest('.ps-close')) {\n deactivate();\n }\n });\n\n document.body.appendChild(panelEl);\n const firstBtn = panelEl.querySelector<HTMLElement>('.ps-item, .ps-close');\n firstBtn?.focus();\n }\n\n function escapeHtml(str: string): string {\n return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');\n }\n\n // --- Content hash for change detection ---\n function contentHash(): string {\n const headings = document.querySelectorAll('h1, h2, h3, h4, h5, h6');\n let hash = '';\n headings.forEach((el) => { hash += el.textContent?.trim().slice(0, 20) || ''; });\n return hash;\n }\n\n function scheduleRefresh() {\n if (!enabled || !panelEl) return;\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n const newHash = contentHash();\n if (newHash !== lastContentHash) {\n lastContentHash = newHash;\n panelEl?.remove();\n buildPanel();\n }\n }, 300);\n }\n\n // --- SPA navigation detection ---\n function setupSPAListeners() {\n // Override pushState/replaceState to detect SPA navigation\n if (!originalPushState) {\n originalPushState = history.pushState;\n history.pushState = function (...args: Parameters<typeof history.pushState>) {\n originalPushState!.apply(this, args);\n scheduleRefresh();\n };\n }\n if (!originalReplaceState) {\n originalReplaceState = history.replaceState;\n history.replaceState = function (...args: Parameters<typeof history.replaceState>) {\n originalReplaceState!.apply(this, args);\n scheduleRefresh();\n };\n }\n window.addEventListener('popstate', boundPopstateHandler);\n }\n\n function teardownSPAListeners() {\n if (originalPushState) {\n history.pushState = originalPushState;\n originalPushState = null;\n }\n if (originalReplaceState) {\n history.replaceState = originalReplaceState;\n originalReplaceState = null;\n }\n window.removeEventListener('popstate', boundPopstateHandler);\n }\n\n // --- MutationObserver for dynamic content ---\n function setupObserver() {\n if (mutationObserver) return;\n mutationObserver = new MutationObserver(() => scheduleRefresh());\n const target = document.querySelector('main') || document.body;\n mutationObserver.observe(target, { childList: true, subtree: true });\n }\n\n function teardownObserver() {\n mutationObserver?.disconnect();\n mutationObserver = null;\n if (debounceTimer) { clearTimeout(debounceTimer); debounceTimer = null; }\n }\n\n function activate() {\n enabled = true;\n lastContentHash = contentHash();\n buildPanel();\n setupObserver();\n setupSPAListeners();\n }\n\n function deactivate() {\n enabled = false;\n teardownObserver();\n teardownSPAListeners();\n panelEl?.remove();\n panelEl = null;\n }\n\n return {\n id: 'page-structure',\n name: () => 'Page Structure',\n description: 'View headings and landmarks for quick navigation',\n icon: 'page-structure',\n category: 'motor',\n activate,\n deactivate,\n getState: (): FeatureState => ({ id: 'page-structure', enabled }),\n };\n}\n"],"names":[],"mappings":"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"}
|
package/dist/tts-zrXtEd07.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"tts-zrXtEd07.js","sources":["../src/features/tts.ts"],"sourcesContent":["import type { FeatureModule, FeatureState } from '../types';\n\ninterface TTSOptions {\n speed: number;\n}\n\nconst DEFAULT_OPTIONS: TTSOptions = {\n speed: 1.0,\n};\n\nexport default function createTTSModule(): FeatureModule {\n let enabled = false;\n let options: TTSOptions = { ...DEFAULT_OPTIONS };\n let controlBar: HTMLDivElement | null = null;\n let highlightedEl: HTMLElement | null = null;\n let isPlaying = false;\n let isPaused = false;\n let clickToReadActive = true;\n let currentUtterances: SpeechSynthesisUtterance[] = [];\n let currentUtteranceIndex = 0;\n let selectedVoice: SpeechSynthesisVoice | null = null;\n let detectedLang = 'en';\n\n const STORAGE_KEY = 'accessify-tts';\n const CONTROL_BAR_ID = 'accessify-tts-controls';\n const STYLE_ID = 'accessify-tts-styles';\n const HIGHLIGHT_CLASS = 'accessify-tts-highlight';\n\n // ---------------------------------------------------------------------------\n // Language detection\n // ---------------------------------------------------------------------------\n\n function detectPageLanguage(): string {\n const htmlLang = document.documentElement.lang;\n if (htmlLang) {\n return htmlLang.toLowerCase();\n }\n // Fallback: check <meta> content-language\n const meta = document.querySelector<HTMLMetaElement>(\n 'meta[http-equiv=\"content-language\"]'\n );\n if (meta?.content) {\n return meta.content.toLowerCase();\n }\n return 'en';\n }\n\n // ---------------------------------------------------------------------------\n // Voice selection\n // ---------------------------------------------------------------------------\n\n function selectBestVoice(lang: string): SpeechSynthesisVoice | null {\n const voices = speechSynthesis.getVoices();\n if (voices.length === 0) return null;\n\n const langBase = lang.split('-')[0]; // e.g. 'de' from 'de-DE'\n\n // For German, strongly prioritize native German voices\n if (langBase === 'de') {\n // Prefer high-quality (non-local) de-DE voices first\n const deDENetwork = voices.find(\n (v) => v.lang.toLowerCase().startsWith('de-de') && v.localService === false\n );\n if (deDENetwork) return deDENetwork;\n\n // Then any de-DE voice\n const deDE = voices.find(\n (v) => v.lang.toLowerCase().startsWith('de-de')\n );\n if (deDE) return deDE;\n\n // Then any German voice\n const deAny = voices.find((v) =>\n v.lang.toLowerCase().startsWith('de')\n );\n if (deAny) return deAny;\n }\n\n // Exact match (e.g. 'en-US' matches 'en-US')\n const exact = voices.find(\n (v) => v.lang.toLowerCase() === lang\n );\n if (exact) return exact;\n\n // Base language match (e.g. 'en' matches 'en-US', 'en-GB')\n const baseMatch = voices.find(\n (v) => v.lang.toLowerCase().startsWith(langBase)\n );\n if (baseMatch) return baseMatch;\n\n // Ultimate fallback: first available voice\n return voices[0] || null;\n }\n\n function refreshVoice() {\n selectedVoice = selectBestVoice(detectedLang);\n }\n\n /** Wait for voices to be available (Chrome loads them async). */\n function waitForVoices(timeoutMs = 3000): Promise<void> {\n return new Promise((resolve) => {\n const voices = speechSynthesis.getVoices();\n if (voices.length > 0) { resolve(); return; }\n let resolved = false;\n const handler = () => {\n if (resolved) return;\n resolved = true;\n resolve();\n };\n speechSynthesis.addEventListener('voiceschanged', handler, { once: true });\n setTimeout(handler, timeoutMs); // don't block forever\n });\n }\n\n // ---------------------------------------------------------------------------\n // Sentence splitting (avoids Chrome's ~15s synthesis cutoff)\n // ---------------------------------------------------------------------------\n\n function splitIntoSentences(text: string): string[] {\n // Split on sentence-ending punctuation followed by whitespace or end\n const raw = text.match(/[^.!?]*[.!?]+[\\s]?|[^.!?]+$/g);\n if (!raw) return [text];\n\n const sentences: string[] = [];\n let buffer = '';\n\n for (const segment of raw) {\n const trimmed = segment.trim();\n if (!trimmed) continue;\n\n buffer += (buffer ? ' ' : '') + trimmed;\n\n // Flush buffer if it's reasonably long (> 80 chars) or is a complete sentence\n if (buffer.length > 80 || /[.!?]$/.test(buffer)) {\n sentences.push(buffer);\n buffer = '';\n }\n }\n\n if (buffer.trim()) {\n sentences.push(buffer.trim());\n }\n\n return sentences.length > 0 ? sentences : [text];\n }\n\n // ---------------------------------------------------------------------------\n // Speech engine\n // ---------------------------------------------------------------------------\n\n // All pending watchdog + advance timeouts. Cleared on every stopSpeech\n // so a stalled timeout can't resurrect speech after the user stops.\n const activeTimeouts = new Set<ReturnType<typeof setTimeout>>();\n function trackTimeout(cb: () => void, ms: number): ReturnType<typeof setTimeout> {\n const id = setTimeout(() => {\n activeTimeouts.delete(id);\n cb();\n }, ms);\n activeTimeouts.add(id);\n return id;\n }\n function clearAllTimeouts() {\n for (const id of activeTimeouts) clearTimeout(id);\n activeTimeouts.clear();\n }\n\n function stopSpeech() {\n clearAllTimeouts();\n speechSynthesis.cancel();\n isPlaying = false;\n isPaused = false;\n currentUtterances = [];\n currentUtteranceIndex = 0;\n removeHighlight();\n updateControlBarState();\n }\n\n function speakText(text: string, sourceEl?: HTMLElement) {\n stopSpeech();\n\n if (!text.trim()) return;\n\n if (sourceEl) {\n applyHighlight(sourceEl);\n }\n\n const sentences = splitIntoSentences(text).slice(0, 50); // cap at 50 sentences\n currentUtterances = sentences.map((sentence) => {\n const utterance = new SpeechSynthesisUtterance(sentence);\n utterance.rate = options.speed;\n utterance.lang = detectedLang;\n if (selectedVoice) {\n utterance.voice = selectedVoice;\n }\n return utterance;\n });\n\n currentUtteranceIndex = 0;\n isPlaying = true;\n isPaused = false;\n updateControlBarState();\n\n speakNextUtterance();\n }\n\n function speakNextUtterance() {\n if (currentUtteranceIndex >= currentUtterances.length) {\n isPlaying = false;\n isPaused = false;\n removeHighlight();\n updateControlBarState();\n return;\n }\n\n const utterance = currentUtterances[currentUtteranceIndex];\n\n // Safety timeout: Chrome can stall on long utterances (>15s).\n // If onend/onerror don't fire within 20s, force-advance.\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\n let settled = false;\n\n function settle() {\n if (settled) return;\n settled = true;\n if (timeoutId) {\n clearTimeout(timeoutId);\n activeTimeouts.delete(timeoutId);\n timeoutId = null;\n }\n }\n\n timeoutId = trackTimeout(() => {\n if (settled || !isPlaying) return;\n settle();\n speechSynthesis.cancel();\n currentUtteranceIndex++;\n if (currentUtteranceIndex < currentUtterances.length && isPlaying) {\n trackTimeout(() => { if (isPlaying) speakNextUtterance(); }, 100);\n } else {\n isPlaying = false;\n isPaused = false;\n removeHighlight();\n updateControlBarState();\n }\n }, 20_000);\n\n utterance.onend = () => {\n if (!isPlaying) return;\n settle();\n currentUtteranceIndex++;\n if (currentUtteranceIndex < currentUtterances.length && isPlaying) {\n trackTimeout(() => { if (isPlaying) speakNextUtterance(); }, 50);\n } else {\n isPlaying = false;\n isPaused = false;\n removeHighlight();\n updateControlBarState();\n }\n };\n\n utterance.onerror = (e) => {\n if (!isPlaying) return;\n settle();\n if (e.error !== 'interrupted' && e.error !== 'canceled') {\n console.warn('[Accessify TTS] Speech error:', e.error);\n // Skip to next utterance on real errors instead of stopping entirely\n currentUtteranceIndex++;\n if (currentUtteranceIndex < currentUtterances.length && isPlaying) {\n trackTimeout(() => { if (isPlaying) speakNextUtterance(); }, 100);\n return;\n }\n }\n isPlaying = false;\n isPaused = false;\n removeHighlight();\n updateControlBarState();\n };\n\n speechSynthesis.speak(utterance);\n }\n\n function togglePause() {\n if (!isPlaying) return;\n\n if (isPaused) {\n speechSynthesis.resume();\n isPaused = false;\n } else {\n speechSynthesis.pause();\n isPaused = true;\n }\n updateControlBarState();\n }\n\n // ---------------------------------------------------------------------------\n // Highlight management\n // ---------------------------------------------------------------------------\n\n function applyHighlight(el: HTMLElement) {\n removeHighlight();\n el.classList.add(HIGHLIGHT_CLASS);\n highlightedEl = el;\n }\n\n function removeHighlight() {\n if (highlightedEl) {\n highlightedEl.classList.remove(HIGHLIGHT_CLASS);\n highlightedEl = null;\n }\n // Safety: remove from any stale elements\n document\n .querySelectorAll(`.${HIGHLIGHT_CLASS}`)\n .forEach((el) => el.classList.remove(HIGHLIGHT_CLASS));\n }\n\n // ---------------------------------------------------------------------------\n // Click-to-read handler\n // ---------------------------------------------------------------------------\n\n function handleClick(e: MouseEvent) {\n if (!enabled || !clickToReadActive) return;\n\n const target = e.target as HTMLElement;\n if (!target) return;\n\n // Ignore clicks on the control bar itself\n if (controlBar?.contains(target)) return;\n\n // Ignore clicks inside the Accessify widget (Shadow DOM host)\n const widgetRoot = document.getElementById('accessify-root');\n if (widgetRoot && (widgetRoot === target || widgetRoot.contains(target))) return;\n\n // Don't block clicks on interactive elements (links, buttons, inputs)\n const tag = target.tagName;\n if (tag === 'A' || tag === 'BUTTON' || tag === 'INPUT' || tag === 'SELECT' || tag === 'TEXTAREA' ||\n target.closest('a, button, input, select, textarea, [role=\"button\"]')) {\n return;\n }\n\n e.preventDefault();\n\n // Walk up to find the most meaningful text container\n const textEl = findTextElement(target);\n if (!textEl) return;\n\n const text = extractTextContent(textEl);\n if (text.trim()) {\n speakText(text, textEl);\n }\n }\n\n function findTextElement(el: HTMLElement): HTMLElement | null {\n // If the element itself has substantial text, use it\n const directText = extractTextContent(el);\n if (directText.trim().length > 0) return el;\n\n // Walk up to find a parent with text\n let current: HTMLElement | null = el;\n while (current && current !== document.body) {\n const text = extractTextContent(current);\n if (text.trim().length > 0) return current;\n current = current.parentElement;\n }\n return null;\n }\n\n function extractTextContent(el: HTMLElement): string {\n // Use innerText rather than textContent to get rendered text with spacing\n return (el.innerText || el.textContent || '').trim();\n }\n\n // ---------------------------------------------------------------------------\n // Control bar UI\n // ---------------------------------------------------------------------------\n\n function createControlBar(): HTMLDivElement {\n const bar = document.createElement('div');\n bar.id = CONTROL_BAR_ID;\n bar.setAttribute('role', 'toolbar');\n bar.setAttribute('aria-label', 'Text-to-Speech controls');\n\n Object.assign(bar.style, {\n position: 'fixed',\n top: '0',\n left: '50%',\n transform: 'translateX(-50%)',\n zIndex: '2147483646',\n display: 'flex',\n alignItems: 'center',\n gap: '6px',\n padding: '6px 14px',\n background: '#1a1a2e',\n color: '#f0f0f0',\n borderRadius: '0 0 10px 10px',\n boxShadow: '0 4px 16px rgba(0,0,0,0.3)',\n fontFamily: 'system-ui, -apple-system, sans-serif',\n fontSize: '13px',\n lineHeight: '1',\n userSelect: 'none',\n transition: 'opacity 0.2s ease',\n });\n\n // Play/Pause button\n const playPauseBtn = document.createElement('button');\n playPauseBtn.className = 'accessify-tts-btn accessify-tts-play';\n playPauseBtn.setAttribute('aria-label', 'Play / Pause');\n playPauseBtn.title = 'Play / Pause';\n playPauseBtn.innerHTML = iconPlay();\n playPauseBtn.addEventListener('click', () => {\n if (isPlaying) {\n togglePause();\n }\n // If nothing is playing, the user should click an element to start\n });\n\n // Stop button\n const stopBtn = document.createElement('button');\n stopBtn.className = 'accessify-tts-btn accessify-tts-stop';\n stopBtn.setAttribute('aria-label', 'Stop');\n stopBtn.title = 'Stop';\n stopBtn.innerHTML = iconStop();\n stopBtn.addEventListener('click', () => {\n stopSpeech();\n });\n\n // Speed control\n const speedContainer = document.createElement('div');\n Object.assign(speedContainer.style, {\n display: 'flex',\n alignItems: 'center',\n gap: '4px',\n marginLeft: '4px',\n });\n\n const speedLabel = document.createElement('label');\n speedLabel.textContent = 'Speed';\n speedLabel.setAttribute('for', 'accessify-tts-speed');\n Object.assign(speedLabel.style, {\n fontSize: '11px',\n opacity: '0.8',\n whiteSpace: 'nowrap',\n });\n\n const speedSlider = document.createElement('input');\n speedSlider.type = 'range';\n speedSlider.id = 'accessify-tts-speed';\n speedSlider.min = '0.5';\n speedSlider.max = '2';\n speedSlider.step = '0.1';\n speedSlider.value = String(options.speed);\n speedSlider.setAttribute('aria-label', 'Speech speed');\n Object.assign(speedSlider.style, {\n width: '70px',\n accentColor: '#4ea8de',\n cursor: 'pointer',\n });\n\n const speedValue = document.createElement('span');\n speedValue.className = 'accessify-tts-speed-val';\n speedValue.textContent = `${options.speed.toFixed(1)}x`;\n Object.assign(speedValue.style, {\n fontSize: '11px',\n minWidth: '30px',\n textAlign: 'center',\n opacity: '0.8',\n });\n\n speedSlider.addEventListener('input', () => {\n const newSpeed = parseFloat(speedSlider.value);\n options.speed = newSpeed;\n speedValue.textContent = `${newSpeed.toFixed(1)}x`;\n savePreferences();\n\n // Update rate on any currently queued utterances\n for (const u of currentUtterances) {\n u.rate = newSpeed;\n }\n });\n\n speedContainer.appendChild(speedLabel);\n speedContainer.appendChild(speedSlider);\n speedContainer.appendChild(speedValue);\n\n // Separator\n const sep = document.createElement('div');\n Object.assign(sep.style, {\n width: '1px',\n height: '18px',\n background: 'rgba(255,255,255,0.2)',\n margin: '0 4px',\n });\n\n // Hint text\n const hint = document.createElement('span');\n hint.className = 'accessify-tts-hint';\n hint.textContent = 'Click any text to read it aloud';\n Object.assign(hint.style, {\n fontSize: '11px',\n opacity: '0.6',\n whiteSpace: 'nowrap',\n });\n\n // Screen reader note (visually hidden, available to AT)\n const srNote = document.createElement('span');\n Object.assign(srNote.style, {\n position: 'absolute',\n width: '1px',\n height: '1px',\n padding: '0',\n margin: '-1px',\n overflow: 'hidden',\n clip: 'rect(0,0,0,0)',\n whiteSpace: 'nowrap',\n border: '0',\n });\n srNote.textContent =\n 'For full screen reader support, use NVDA, JAWS, or VoiceOver';\n\n bar.appendChild(playPauseBtn);\n bar.appendChild(stopBtn);\n bar.appendChild(sep);\n bar.appendChild(speedContainer);\n bar.appendChild(sep.cloneNode(true));\n bar.appendChild(hint);\n bar.appendChild(srNote);\n\n return bar;\n }\n\n function updateControlBarState() {\n if (!controlBar) return;\n\n const playBtn = controlBar.querySelector<HTMLButtonElement>(\n '.accessify-tts-play'\n );\n const hintEl = controlBar.querySelector<HTMLSpanElement>(\n '.accessify-tts-hint'\n );\n\n if (playBtn) {\n if (isPlaying && !isPaused) {\n playBtn.innerHTML = iconPause();\n playBtn.setAttribute('aria-label', 'Pause');\n playBtn.title = 'Pause';\n } else if (isPlaying && isPaused) {\n playBtn.innerHTML = iconPlay();\n playBtn.setAttribute('aria-label', 'Resume');\n playBtn.title = 'Resume';\n } else {\n playBtn.innerHTML = iconPlay();\n playBtn.setAttribute('aria-label', 'Play / Pause');\n playBtn.title = 'Play / Pause';\n }\n }\n\n if (hintEl) {\n if (isPlaying && !isPaused) {\n hintEl.textContent = 'Speaking...';\n } else if (isPaused) {\n hintEl.textContent = 'Paused';\n } else {\n hintEl.textContent = 'Click any text to read it aloud';\n }\n }\n }\n\n // ---------------------------------------------------------------------------\n // Inline SVG icons for control buttons\n // ---------------------------------------------------------------------------\n\n function iconPlay(): string {\n return '<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\" stroke=\"none\" aria-hidden=\"true\"><polygon points=\"6,3 20,12 6,21\"/></svg>';\n }\n\n function iconPause(): string {\n return '<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\" stroke=\"none\" aria-hidden=\"true\"><rect x=\"5\" y=\"3\" width=\"4\" height=\"18\"/><rect x=\"15\" y=\"3\" width=\"4\" height=\"18\"/></svg>';\n }\n\n function iconStop(): string {\n return '<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\" stroke=\"none\" aria-hidden=\"true\"><rect x=\"4\" y=\"4\" width=\"16\" height=\"16\" rx=\"2\"/></svg>';\n }\n\n // ---------------------------------------------------------------------------\n // Styles (injected into <head>, not Shadow DOM)\n // ---------------------------------------------------------------------------\n\n function getStyles(): string {\n return `\n /* accessify TTS styles */\n .${HIGHLIGHT_CLASS} {\n background-color: rgba(78, 168, 222, 0.15) !important;\n outline: 2px solid rgba(78, 168, 222, 0.5) !important;\n outline-offset: 2px !important;\n border-radius: 3px !important;\n transition: background-color 0.2s ease, outline-color 0.2s ease !important;\n }\n\n #${CONTROL_BAR_ID} .accessify-tts-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n padding: 0;\n margin: 0;\n border: 1px solid rgba(255,255,255,0.15);\n border-radius: 6px;\n background: rgba(255,255,255,0.08);\n color: #f0f0f0;\n cursor: pointer;\n transition: background 0.15s ease, border-color 0.15s ease;\n line-height: 1;\n }\n\n #${CONTROL_BAR_ID} .accessify-tts-btn:hover {\n background: rgba(255,255,255,0.18);\n border-color: rgba(255,255,255,0.3);\n }\n\n #${CONTROL_BAR_ID} .accessify-tts-btn:focus-visible {\n outline: 2px solid #4ea8de;\n outline-offset: 1px;\n }\n\n #${CONTROL_BAR_ID} .accessify-tts-btn:active {\n background: rgba(255,255,255,0.25);\n }\n `;\n }\n\n function injectStyles() {\n let styleEl = document.getElementById(STYLE_ID);\n if (!styleEl) {\n styleEl = document.createElement('style');\n styleEl.id = STYLE_ID;\n document.head.appendChild(styleEl);\n }\n styleEl.textContent = getStyles();\n }\n\n function removeStyles() {\n const styleEl = document.getElementById(STYLE_ID);\n styleEl?.remove();\n }\n\n // ---------------------------------------------------------------------------\n // Preferences persistence\n // ---------------------------------------------------------------------------\n\n function loadPreferences() {\n const saved = localStorage.getItem(STORAGE_KEY);\n if (saved) {\n try {\n const parsed = JSON.parse(saved);\n options.speed = Math.min(2, Math.max(0.5, parsed.speed ?? DEFAULT_OPTIONS.speed));\n } catch {\n options = { ...DEFAULT_OPTIONS };\n }\n }\n }\n\n function savePreferences() {\n localStorage.setItem(STORAGE_KEY, JSON.stringify({ speed: options.speed }));\n }\n\n // ---------------------------------------------------------------------------\n // Module lifecycle\n // ---------------------------------------------------------------------------\n\n function activate() {\n if (enabled) return;\n enabled = true;\n\n loadPreferences();\n detectedLang = detectPageLanguage();\n\n // Load voices async — don't block activation\n waitForVoices().then(() => refreshVoice());\n\n // Inject styles and control bar\n injectStyles();\n controlBar = createControlBar();\n document.documentElement.appendChild(controlBar);\n\n // Enable click-to-read\n clickToReadActive = true;\n document.addEventListener('click', handleClick, true);\n }\n\n function deactivate() {\n enabled = false;\n\n // Stop all speech\n stopSpeech();\n\n // Remove click handler\n document.removeEventListener('click', handleClick, true);\n clickToReadActive = false;\n\n // Remove control bar\n if (controlBar) {\n controlBar.remove();\n controlBar = null;\n }\n\n // Remove highlights\n removeHighlight();\n\n // Remove styles\n removeStyles();\n\n selectedVoice = null;\n }\n\n // ---------------------------------------------------------------------------\n // FeatureModule interface\n // ---------------------------------------------------------------------------\n\n return {\n id: 'tts',\n name: () => 'Text-to-Speech',\n description: 'Click any text to hear it read aloud with adjustable speed',\n icon: 'tts',\n category: 'cognitive',\n activate,\n deactivate,\n getState: (): FeatureState => ({\n id: 'tts',\n enabled,\n value: { speed: options.speed },\n }),\n setState: (newState: Partial<TTSOptions>) => {\n if (newState.speed !== undefined) {\n options.speed = Math.min(2, Math.max(0.5, newState.speed));\n savePreferences();\n\n // Update the slider in the control bar if present\n const slider = document.getElementById(\n 'accessify-tts-speed'\n ) as HTMLInputElement | null;\n if (slider) {\n slider.value = String(options.speed);\n }\n const valDisplay = controlBar?.querySelector<HTMLSpanElement>(\n '.accessify-tts-speed-val'\n );\n if (valDisplay) {\n valDisplay.textContent = `${options.speed.toFixed(1)}x`;\n }\n }\n },\n };\n}\n"],"names":[],"mappings":"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"}
|