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/dist/accessify.min.js +1 -1
- package/dist/accessify.min.js.map +1 -1
- package/dist/accessify.mjs +1 -1
- package/dist/{animation-stop-_chC8bg1.js → animation-stop-DrDe9Q9n.js} +87 -2
- package/dist/animation-stop-DrDe9Q9n.js.map +1 -0
- package/dist/{contrast-BsIJv7ad.js → contrast-CqsOs6Uo.js} +38 -1
- package/dist/contrast-CqsOs6Uo.js.map +1 -0
- package/dist/{index-Q9OckvPM.js → index-kzWIIKQd.js} +58 -12
- package/dist/{index-Q9OckvPM.js.map → index-kzWIIKQd.js.map} +1 -1
- package/dist/{keyboard-nav-8iqatTex.js → keyboard-nav-DLIviKXx.js} +2 -2
- package/dist/{keyboard-nav-8iqatTex.js.map → keyboard-nav-DLIviKXx.js.map} +1 -1
- package/dist/loader.min.js +1 -1
- package/dist/{page-structure-DCn8Y3Y9.js → page-structure-Bo1-XXRs.js} +2 -2
- package/dist/{page-structure-DCn8Y3Y9.js.map → page-structure-Bo1-XXRs.js.map} +1 -1
- package/dist/{text-simplify-CK2GFhq2.js → text-simplify-B0hIi6bW.js} +38 -3
- package/dist/text-simplify-B0hIi6bW.js.map +1 -0
- package/dist/widget.js +1 -1
- package/dist/widget.js.map +1 -1
- package/package.json +1 -1
- package/dist/animation-stop-_chC8bg1.js.map +0 -1
- package/dist/contrast-BsIJv7ad.js.map +0 -1
- package/dist/text-simplify-CK2GFhq2.js.map +0 -1
package/dist/accessify.mjs
CHANGED
|
@@ -9,6 +9,42 @@ function createAnimationStopModule() {
|
|
|
9
9
|
let originalAnimate = null;
|
|
10
10
|
let pausedVideos = [];
|
|
11
11
|
let gifOriginals = /* @__PURE__ */ new Map();
|
|
12
|
+
const VISUAL_ONLY_PROPS = /* @__PURE__ */ new Set([
|
|
13
|
+
"transform",
|
|
14
|
+
"scale",
|
|
15
|
+
"rotate",
|
|
16
|
+
"translate",
|
|
17
|
+
"filter",
|
|
18
|
+
"backdropFilter",
|
|
19
|
+
"backdrop-filter",
|
|
20
|
+
"webkitTransform",
|
|
21
|
+
"webkitFilter",
|
|
22
|
+
"-webkit-transform",
|
|
23
|
+
"-webkit-filter",
|
|
24
|
+
"offset",
|
|
25
|
+
"easing",
|
|
26
|
+
"composite"
|
|
27
|
+
]);
|
|
28
|
+
function isVisualOnlyKeyframes(keyframes) {
|
|
29
|
+
if (!keyframes) return false;
|
|
30
|
+
try {
|
|
31
|
+
if (Array.isArray(keyframes)) {
|
|
32
|
+
for (const kf of keyframes) {
|
|
33
|
+
if (!kf || typeof kf !== "object") return false;
|
|
34
|
+
for (const key of Object.keys(kf)) {
|
|
35
|
+
if (!VISUAL_ONLY_PROPS.has(key)) return false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
for (const key of Object.keys(keyframes)) {
|
|
41
|
+
if (!VISUAL_ONLY_PROPS.has(key)) return false;
|
|
42
|
+
}
|
|
43
|
+
return true;
|
|
44
|
+
} catch {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
12
48
|
function patchAnimate() {
|
|
13
49
|
if (originalAnimate) return;
|
|
14
50
|
originalAnimate = Element.prototype.animate;
|
|
@@ -16,6 +52,19 @@ function createAnimationStopModule() {
|
|
|
16
52
|
if (!enabled) {
|
|
17
53
|
return originalAnimate.call(this, keyframes, options);
|
|
18
54
|
}
|
|
55
|
+
try {
|
|
56
|
+
let node = this;
|
|
57
|
+
while (node) {
|
|
58
|
+
if (node.id === "accessify-root") {
|
|
59
|
+
return originalAnimate.call(this, keyframes, options);
|
|
60
|
+
}
|
|
61
|
+
const parent = node.parentNode || node.host;
|
|
62
|
+
if (parent === node) break;
|
|
63
|
+
node = parent;
|
|
64
|
+
}
|
|
65
|
+
} catch {
|
|
66
|
+
}
|
|
67
|
+
const visualOnly = isVisualOnlyKeyframes(keyframes);
|
|
19
68
|
let opts;
|
|
20
69
|
if (typeof options === "number") {
|
|
21
70
|
opts = { duration: 1e-3, fill: "forwards" };
|
|
@@ -32,7 +81,11 @@ function createAnimationStopModule() {
|
|
|
32
81
|
}
|
|
33
82
|
const anim = originalAnimate.call(this, keyframes, opts);
|
|
34
83
|
try {
|
|
35
|
-
|
|
84
|
+
if (visualOnly) {
|
|
85
|
+
anim.cancel();
|
|
86
|
+
} else {
|
|
87
|
+
anim.finish();
|
|
88
|
+
}
|
|
36
89
|
} catch {
|
|
37
90
|
}
|
|
38
91
|
return anim;
|
|
@@ -60,17 +113,49 @@ function createAnimationStopModule() {
|
|
|
60
113
|
animation-timeline: auto !important;
|
|
61
114
|
scroll-timeline: none !important;
|
|
62
115
|
}
|
|
116
|
+
/* Neutralize interaction transforms (hover zoom, click scale, focus
|
|
117
|
+
lift). Without this, CSS like \`.card:hover { transform: scale(1.05) }\`
|
|
118
|
+
still visibly changes the element — just instantly. Users who
|
|
119
|
+
disable animations expect NO motion including the end-state. */
|
|
120
|
+
*:not(#accessify-root):not(#accessify-root *):hover,
|
|
121
|
+
*:not(#accessify-root):not(#accessify-root *):focus,
|
|
122
|
+
*:not(#accessify-root):not(#accessify-root *):focus-within,
|
|
123
|
+
*:not(#accessify-root):not(#accessify-root *):focus-visible,
|
|
124
|
+
*:not(#accessify-root):not(#accessify-root *):active {
|
|
125
|
+
transform: none !important;
|
|
126
|
+
scale: 1 !important;
|
|
127
|
+
rotate: 0deg !important;
|
|
128
|
+
translate: 0 0 !important;
|
|
129
|
+
filter: none !important;
|
|
130
|
+
-webkit-filter: none !important;
|
|
131
|
+
}
|
|
63
132
|
`;
|
|
64
133
|
document.head.appendChild(style);
|
|
65
134
|
}
|
|
66
135
|
function removeStyles() {
|
|
67
136
|
document.getElementById(STYLE_ID)?.remove();
|
|
68
137
|
}
|
|
138
|
+
function isWidgetAnimation(anim) {
|
|
139
|
+
try {
|
|
140
|
+
const target = anim.effect?.target;
|
|
141
|
+
if (!target) return false;
|
|
142
|
+
let node = target;
|
|
143
|
+
while (node) {
|
|
144
|
+
if (node.id === "accessify-root") return true;
|
|
145
|
+
const parent = node.parentNode || node.host;
|
|
146
|
+
if (parent === node) break;
|
|
147
|
+
node = parent;
|
|
148
|
+
}
|
|
149
|
+
} catch {
|
|
150
|
+
}
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
69
153
|
function finishAllAnimations() {
|
|
70
154
|
try {
|
|
71
155
|
const animations = document.getAnimations?.();
|
|
72
156
|
if (!animations) return;
|
|
73
157
|
for (const anim of animations) {
|
|
158
|
+
if (isWidgetAnimation(anim)) continue;
|
|
74
159
|
try {
|
|
75
160
|
anim.finish();
|
|
76
161
|
} catch {
|
|
@@ -402,4 +487,4 @@ function createAnimationStopModule() {
|
|
|
402
487
|
export {
|
|
403
488
|
createAnimationStopModule as default
|
|
404
489
|
};
|
|
405
|
-
//# sourceMappingURL=animation-stop-
|
|
490
|
+
//# sourceMappingURL=animation-stop-DrDe9Q9n.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"animation-stop-DrDe9Q9n.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: 1 !important;\n rotate: 0deg !important;\n translate: 0 0 !important;\n filter: none !important;\n -webkit-filter: 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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"}
|
|
@@ -122,6 +122,41 @@ function buildCSS(mode) {
|
|
|
122
122
|
` : "";
|
|
123
123
|
return base + textColor + links + forms + focus + borders + images + highExtras;
|
|
124
124
|
}
|
|
125
|
+
const BACKUP_ATTR = "data-a11y-bg";
|
|
126
|
+
function stripInlineBackgrounds() {
|
|
127
|
+
const els = document.querySelectorAll("body *:not(#accessify-root):not(#accessify-root *)");
|
|
128
|
+
for (const el of els) {
|
|
129
|
+
const html = el;
|
|
130
|
+
if (html.tagName === "IMG" || html.tagName === "VIDEO" || html.tagName === "PICTURE" || html.tagName === "CANVAS" || html.tagName === "SVG") continue;
|
|
131
|
+
if (html.tagName === "INPUT" || html.tagName === "TEXTAREA" || html.tagName === "SELECT") continue;
|
|
132
|
+
const inlineBg = html.style.background || html.style.backgroundColor || html.style.backgroundImage;
|
|
133
|
+
if (!inlineBg) continue;
|
|
134
|
+
if (!html.hasAttribute(BACKUP_ATTR)) {
|
|
135
|
+
html.setAttribute(BACKUP_ATTR, JSON.stringify({
|
|
136
|
+
bg: html.style.background,
|
|
137
|
+
bgColor: html.style.backgroundColor,
|
|
138
|
+
bgImage: html.style.backgroundImage
|
|
139
|
+
}));
|
|
140
|
+
}
|
|
141
|
+
html.style.setProperty("background", "transparent", "important");
|
|
142
|
+
html.style.setProperty("background-color", "transparent", "important");
|
|
143
|
+
html.style.setProperty("background-image", "none", "important");
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
function restoreInlineBackgrounds() {
|
|
147
|
+
const els = document.querySelectorAll(`[${BACKUP_ATTR}]`);
|
|
148
|
+
for (const el of els) {
|
|
149
|
+
const html = el;
|
|
150
|
+
try {
|
|
151
|
+
const saved = JSON.parse(html.getAttribute(BACKUP_ATTR) || "{}");
|
|
152
|
+
html.style.background = saved.bg || "";
|
|
153
|
+
html.style.backgroundColor = saved.bgColor || "";
|
|
154
|
+
html.style.backgroundImage = saved.bgImage || "";
|
|
155
|
+
} catch {
|
|
156
|
+
}
|
|
157
|
+
html.removeAttribute(BACKUP_ATTR);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
125
160
|
function createContrastModule() {
|
|
126
161
|
let currentMode = "off";
|
|
127
162
|
const STYLE_ID = "accessify-contrast";
|
|
@@ -130,6 +165,7 @@ function createContrastModule() {
|
|
|
130
165
|
let styleEl = document.getElementById(STYLE_ID);
|
|
131
166
|
if (mode === "off") {
|
|
132
167
|
styleEl?.remove();
|
|
168
|
+
restoreInlineBackgrounds();
|
|
133
169
|
return;
|
|
134
170
|
}
|
|
135
171
|
if (!styleEl) {
|
|
@@ -138,6 +174,7 @@ function createContrastModule() {
|
|
|
138
174
|
document.head.appendChild(styleEl);
|
|
139
175
|
}
|
|
140
176
|
styleEl.textContent = buildCSS(mode);
|
|
177
|
+
stripInlineBackgrounds();
|
|
141
178
|
}
|
|
142
179
|
function activate() {
|
|
143
180
|
const saved = localStorage.getItem(STORAGE_KEY);
|
|
@@ -172,4 +209,4 @@ function createContrastModule() {
|
|
|
172
209
|
export {
|
|
173
210
|
createContrastModule as default
|
|
174
211
|
};
|
|
175
|
-
//# sourceMappingURL=contrast-
|
|
212
|
+
//# sourceMappingURL=contrast-CqsOs6Uo.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contrast-CqsOs6Uo.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\n// ---------------------------------------------------------------------------\n// Inline-style override — Framer/Webflow/etc set background via inline style\n// which beats stylesheet !important. We must strip them via JS and restore on\n// deactivate.\n// ---------------------------------------------------------------------------\n\nconst BACKUP_ATTR = 'data-a11y-bg';\n\n/** Strip inline background from all elements (except widget + media) */\nfunction stripInlineBackgrounds() {\n const els = document.querySelectorAll('body *:not(#accessify-root):not(#accessify-root *)');\n for (const el of els) {\n const html = el as HTMLElement;\n if (html.tagName === 'IMG' || html.tagName === 'VIDEO' || html.tagName === 'PICTURE' ||\n html.tagName === 'CANVAS' || html.tagName === 'SVG') continue;\n // Skip form controls — they get their own bg in Layer 4\n if (html.tagName === 'INPUT' || html.tagName === 'TEXTAREA' || html.tagName === 'SELECT') continue;\n\n const inlineBg = html.style.background || html.style.backgroundColor || html.style.backgroundImage;\n if (!inlineBg) continue;\n\n // Back up original values so we can restore later\n if (!html.hasAttribute(BACKUP_ATTR)) {\n html.setAttribute(BACKUP_ATTR, JSON.stringify({\n bg: html.style.background,\n bgColor: html.style.backgroundColor,\n bgImage: html.style.backgroundImage,\n }));\n }\n html.style.setProperty('background', 'transparent', 'important');\n html.style.setProperty('background-color', 'transparent', 'important');\n html.style.setProperty('background-image', 'none', 'important');\n }\n}\n\n/** Restore original inline backgrounds */\nfunction restoreInlineBackgrounds() {\n const els = document.querySelectorAll(`[${BACKUP_ATTR}]`);\n for (const el of els) {\n const html = el as HTMLElement;\n try {\n const saved = JSON.parse(html.getAttribute(BACKUP_ATTR) || '{}');\n html.style.background = saved.bg || '';\n html.style.backgroundColor = saved.bgColor || '';\n html.style.backgroundImage = saved.bgImage || '';\n } catch { /* ignore parse errors */ }\n html.removeAttribute(BACKUP_ATTR);\n }\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 restoreInlineBackgrounds();\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 // Strip inline backgrounds AFTER CSS is applied (handles Framer/Webflow)\n stripInlineBackgrounds();\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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"}
|
|
@@ -6618,18 +6618,18 @@ function FeatureGrid($$anchor, $$props) {
|
|
|
6618
6618
|
const AI_FEATURES = /* @__PURE__ */ new Set(["text-simplify", "alt-text"]);
|
|
6619
6619
|
const loadedModules = /* @__PURE__ */ new Map();
|
|
6620
6620
|
const FEATURE_LOADERS = {
|
|
6621
|
-
contrast: () => import("./contrast-
|
|
6621
|
+
contrast: () => import("./contrast-CqsOs6Uo.js"),
|
|
6622
6622
|
"text-size": () => import("./text-size-m_mHNPWo.js"),
|
|
6623
|
-
"keyboard-nav": () => import("./keyboard-nav-
|
|
6623
|
+
"keyboard-nav": () => import("./keyboard-nav-DLIviKXx.js"),
|
|
6624
6624
|
"link-highlight": () => import("./link-highlight-DBGm067Y.js"),
|
|
6625
6625
|
"reading-guide": () => import("./reading-guide-VT8NciIL.js"),
|
|
6626
6626
|
"reading-mask": () => import("./reading-mask-BABChuCz.js"),
|
|
6627
|
-
"animation-stop": () => import("./animation-stop-
|
|
6627
|
+
"animation-stop": () => import("./animation-stop-DrDe9Q9n.js"),
|
|
6628
6628
|
"hide-images": () => import("./hide-images-B_LeCBcd.js"),
|
|
6629
6629
|
"big-cursor": () => import("./big-cursor-B2UKu9dQ.js"),
|
|
6630
|
-
"page-structure": () => import("./page-structure-
|
|
6630
|
+
"page-structure": () => import("./page-structure-Bo1-XXRs.js"),
|
|
6631
6631
|
tts: () => import("./tts-CjszLRnb.js"),
|
|
6632
|
-
"text-simplify": () => import("./text-simplify-
|
|
6632
|
+
"text-simplify": () => import("./text-simplify-B0hIi6bW.js"),
|
|
6633
6633
|
"alt-text": () => Promise.resolve().then(() => altText)
|
|
6634
6634
|
};
|
|
6635
6635
|
let contrastMode = /* @__PURE__ */ state(proxy(readStoredContrastMode()));
|
|
@@ -7699,7 +7699,7 @@ function createWidgetStyles(config2) {
|
|
|
7699
7699
|
const pos = config2.position || "bottom-right";
|
|
7700
7700
|
const isRight = pos.includes("right");
|
|
7701
7701
|
const isBottom = pos.includes("bottom");
|
|
7702
|
-
const z = config2.zIndex ||
|
|
7702
|
+
const z = config2.zIndex || 2147483647;
|
|
7703
7703
|
const panelRadius = isRight ? "20px 0 0 20px" : "0 20px 20px 0";
|
|
7704
7704
|
const hiddenTransform = isRight ? "translateX(24px)" : "translateX(-24px)";
|
|
7705
7705
|
const c = config2;
|
|
@@ -8213,6 +8213,7 @@ async function setCachedAltText(src, altText2, langCode) {
|
|
|
8213
8213
|
});
|
|
8214
8214
|
}
|
|
8215
8215
|
const DEFAULT_API_BASE = "https://accessify-api.accessify.workers.dev";
|
|
8216
|
+
let currentAltSiteMode = "manual";
|
|
8216
8217
|
async function fetchServerAltTexts(siteKey, proxyUrl, lang) {
|
|
8217
8218
|
const map = /* @__PURE__ */ new Map();
|
|
8218
8219
|
try {
|
|
@@ -8224,6 +8225,7 @@ async function fetchServerAltTexts(siteKey, proxyUrl, lang) {
|
|
|
8224
8225
|
});
|
|
8225
8226
|
if (!res.ok) return map;
|
|
8226
8227
|
const data = await res.json();
|
|
8228
|
+
currentAltSiteMode = data.siteMode === "auto" ? "auto" : "manual";
|
|
8227
8229
|
if (data.blocks) {
|
|
8228
8230
|
for (const block2 of data.blocks) {
|
|
8229
8231
|
if (block2.selector && block2.result) map.set(block2.selector, block2.result);
|
|
@@ -8593,6 +8595,12 @@ function createAltTextModule(aiService, initialLang = "de", serverConfig) {
|
|
|
8593
8595
|
}
|
|
8594
8596
|
async function generateBgAlts() {
|
|
8595
8597
|
if (!aiService || !enabled) return;
|
|
8598
|
+
if (currentAltSiteMode !== "auto") {
|
|
8599
|
+
if (window.__ACCESSIFY_DEBUG) {
|
|
8600
|
+
console.info("[Accessify] alt-text: siteMode=manual — skipping live generation for background images");
|
|
8601
|
+
}
|
|
8602
|
+
return;
|
|
8603
|
+
}
|
|
8596
8604
|
const bgElements = scanForBackgroundImages();
|
|
8597
8605
|
for (const el of bgElements) {
|
|
8598
8606
|
if (!enabled) break;
|
|
@@ -8646,6 +8654,15 @@ function createAltTextModule(aiService, initialLang = "de", serverConfig) {
|
|
|
8646
8654
|
autoGenerating = false;
|
|
8647
8655
|
return;
|
|
8648
8656
|
}
|
|
8657
|
+
if (currentAltSiteMode !== "auto") {
|
|
8658
|
+
if (window.__ACCESSIFY_DEBUG) {
|
|
8659
|
+
console.info(
|
|
8660
|
+
`[Accessify] alt-text: siteMode=manual — ${uncached.length} image(s) left untouched. Trigger a crawl from the dashboard or enable auto mode.`
|
|
8661
|
+
);
|
|
8662
|
+
}
|
|
8663
|
+
autoGenerating = false;
|
|
8664
|
+
return;
|
|
8665
|
+
}
|
|
8649
8666
|
for (let i = 0; i < uncached.length; i += CONCURRENCY) {
|
|
8650
8667
|
if (!enabled) break;
|
|
8651
8668
|
const batch = uncached.slice(i, i + CONCURRENCY);
|
|
@@ -8676,6 +8693,7 @@ function createAltTextModule(aiService, initialLang = "de", serverConfig) {
|
|
|
8676
8693
|
applyAltText(img, cached);
|
|
8677
8694
|
return;
|
|
8678
8695
|
}
|
|
8696
|
+
if (currentAltSiteMode !== "auto") return;
|
|
8679
8697
|
try {
|
|
8680
8698
|
const ctx = gatherImageContext(img);
|
|
8681
8699
|
const alt = await aiService.generateAltText(src, ctx, lang());
|
|
@@ -8798,14 +8816,20 @@ const REMOVED_FEATURES = /* @__PURE__ */ new Set([
|
|
|
8798
8816
|
"saturation",
|
|
8799
8817
|
"text-align",
|
|
8800
8818
|
"line-height",
|
|
8801
|
-
"page-structure",
|
|
8802
8819
|
"focus-highlight",
|
|
8803
8820
|
"auto-scan"
|
|
8804
8821
|
]);
|
|
8822
|
+
function detectLang() {
|
|
8823
|
+
const htmlLang = document.documentElement.lang?.trim();
|
|
8824
|
+
if (htmlLang) return htmlLang.split("-")[0].toLowerCase();
|
|
8825
|
+
const nav = (navigator.language || navigator.userLanguage || "").trim();
|
|
8826
|
+
if (nav) return nav.split("-")[0].toLowerCase();
|
|
8827
|
+
return "en";
|
|
8828
|
+
}
|
|
8805
8829
|
const DEFAULT_CONFIG = {
|
|
8806
8830
|
position: "bottom-right",
|
|
8807
8831
|
theme: "auto",
|
|
8808
|
-
lang:
|
|
8832
|
+
lang: detectLang(),
|
|
8809
8833
|
features: [
|
|
8810
8834
|
"contrast",
|
|
8811
8835
|
"text-size",
|
|
@@ -8818,9 +8842,10 @@ const DEFAULT_CONFIG = {
|
|
|
8818
8842
|
"animation-stop",
|
|
8819
8843
|
"tts",
|
|
8820
8844
|
"text-simplify",
|
|
8821
|
-
"alt-text"
|
|
8845
|
+
"alt-text",
|
|
8846
|
+
"page-structure"
|
|
8822
8847
|
],
|
|
8823
|
-
zIndex:
|
|
8848
|
+
zIndex: 2147483647,
|
|
8824
8849
|
compact: false
|
|
8825
8850
|
};
|
|
8826
8851
|
let widgetInstance = null;
|
|
@@ -8880,7 +8905,7 @@ async function init(userConfig = {}) {
|
|
|
8880
8905
|
const pos = config.position || "bottom-right";
|
|
8881
8906
|
const isRight = pos.includes("right");
|
|
8882
8907
|
const isBottom = pos.includes("bottom");
|
|
8883
|
-
const z = config.zIndex ||
|
|
8908
|
+
const z = config.zIndex || 2147483647;
|
|
8884
8909
|
containerEl.style.cssText = [
|
|
8885
8910
|
"position:fixed!important",
|
|
8886
8911
|
isBottom ? "bottom:20px!important" : "top:20px!important",
|
|
@@ -8934,7 +8959,28 @@ async function init(userConfig = {}) {
|
|
|
8934
8959
|
sheet.replaceSync(createWidgetStyles(config));
|
|
8935
8960
|
}
|
|
8936
8961
|
}));
|
|
8962
|
+
const ALLOWED_PREVIEW_ORIGINS = [
|
|
8963
|
+
"https://accessify-dashboard.pages.dev",
|
|
8964
|
+
"https://accessify.dev",
|
|
8965
|
+
"https://www.accessify.dev",
|
|
8966
|
+
"http://localhost:4321",
|
|
8967
|
+
"http://localhost:5173",
|
|
8968
|
+
"http://localhost:8787",
|
|
8969
|
+
"http://127.0.0.1:4321",
|
|
8970
|
+
"http://127.0.0.1:5173"
|
|
8971
|
+
];
|
|
8972
|
+
function isAllowedPreviewOrigin(origin) {
|
|
8973
|
+
if (ALLOWED_PREVIEW_ORIGINS.includes(origin)) return true;
|
|
8974
|
+
try {
|
|
8975
|
+
const u = new URL(origin);
|
|
8976
|
+
if (u.hostname.endsWith(".accessify.dev")) return true;
|
|
8977
|
+
if (u.hostname.endsWith(".accessify-dashboard.pages.dev")) return true;
|
|
8978
|
+
} catch {
|
|
8979
|
+
}
|
|
8980
|
+
return false;
|
|
8981
|
+
}
|
|
8937
8982
|
window.addEventListener("message", (e) => {
|
|
8983
|
+
if (!isAllowedPreviewOrigin(e.origin)) return;
|
|
8938
8984
|
if (e.data?.type === "accessify:preview-config") {
|
|
8939
8985
|
const preview = e.data.config || {};
|
|
8940
8986
|
Object.assign(config, preview);
|
|
@@ -8991,4 +9037,4 @@ export {
|
|
|
8991
9037
|
init as i,
|
|
8992
9038
|
t
|
|
8993
9039
|
};
|
|
8994
|
-
//# sourceMappingURL=index-
|
|
9040
|
+
//# sourceMappingURL=index-kzWIIKQd.js.map
|