accessify-widget 0.2.3 → 0.2.4

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.
@@ -1,4 +1,4 @@
1
- import { d, i } from "./index-CsJDqdBW.js";
1
+ import { d, i } from "./index-D8e7_na0.js";
2
2
  export {
3
3
  d as destroy,
4
4
  i as init
@@ -0,0 +1,394 @@
1
+ function createAnimationStopModule() {
2
+ let enabled = false;
3
+ const STYLE_ID = "accessify-animation-stop";
4
+ const STORAGE_KEY = "accessify-animation-stop";
5
+ let pausedVideos = [];
6
+ let pausedAnimations = [];
7
+ let originalAnimate = null;
8
+ let originalRAF = null;
9
+ let originalMatchMedia = null;
10
+ let mutationObserver = null;
11
+ let rafLoopTracker = /* @__PURE__ */ new Map();
12
+ let blockedRAFs = /* @__PURE__ */ new Set();
13
+ let gifOriginals = /* @__PURE__ */ new Map();
14
+ function getStyles() {
15
+ return `
16
+ /* accessify animation stop - WCAG 2.3.1 compliance */
17
+ *, *::before, *::after {
18
+ animation-duration: 0.01s !important;
19
+ animation-delay: 0s !important;
20
+ animation-iteration-count: 1 !important;
21
+ animation-play-state: paused !important;
22
+ transition-duration: 0.01s !important;
23
+ transition-delay: 0s !important;
24
+ scroll-behavior: auto !important;
25
+ }
26
+ marquee { -moz-binding: none; }
27
+ `;
28
+ }
29
+ function injectStyles() {
30
+ let styleEl = document.getElementById(STYLE_ID);
31
+ if (!styleEl) {
32
+ styleEl = document.createElement("style");
33
+ styleEl.id = STYLE_ID;
34
+ document.head.appendChild(styleEl);
35
+ }
36
+ styleEl.textContent = getStyles();
37
+ }
38
+ function removeStyles() {
39
+ document.getElementById(STYLE_ID)?.remove();
40
+ }
41
+ function pauseAllWebAnimations() {
42
+ try {
43
+ const animations = document.getAnimations();
44
+ for (const anim of animations) {
45
+ if (anim.playState === "running" || anim.playState === "pending") {
46
+ anim.pause();
47
+ pausedAnimations.push(anim);
48
+ }
49
+ }
50
+ } catch {
51
+ }
52
+ }
53
+ function overrideElementAnimate() {
54
+ if (originalAnimate) return;
55
+ originalAnimate = Element.prototype.animate;
56
+ Element.prototype.animate = function(keyframes, options) {
57
+ const anim = originalAnimate.call(this, keyframes, options);
58
+ if (enabled) {
59
+ anim.pause();
60
+ pausedAnimations.push(anim);
61
+ }
62
+ return anim;
63
+ };
64
+ }
65
+ function restoreElementAnimate() {
66
+ if (originalAnimate) {
67
+ Element.prototype.animate = originalAnimate;
68
+ originalAnimate = null;
69
+ }
70
+ }
71
+ function resumePausedAnimations() {
72
+ for (const anim of pausedAnimations) {
73
+ try {
74
+ anim.play();
75
+ } catch {
76
+ }
77
+ }
78
+ pausedAnimations = [];
79
+ }
80
+ function overrideRAF() {
81
+ if (originalRAF) return;
82
+ originalRAF = window.requestAnimationFrame;
83
+ const origCancel = window.cancelAnimationFrame;
84
+ window.requestAnimationFrame = function(callback) {
85
+ if (!enabled) return originalRAF.call(window, callback);
86
+ const key = callback.toString().slice(0, 200);
87
+ const now = performance.now();
88
+ const entry = rafLoopTracker.get(key);
89
+ if (entry) {
90
+ entry.count++;
91
+ if (now - entry.lastTime < 100 && entry.count > 3) {
92
+ const id = originalRAF.call(window, () => {
93
+ });
94
+ blockedRAFs.add(id);
95
+ return id;
96
+ }
97
+ entry.lastTime = now;
98
+ } else {
99
+ rafLoopTracker.set(key, { count: 1, lastTime: now });
100
+ }
101
+ return originalRAF.call(window, callback);
102
+ };
103
+ window.cancelAnimationFrame = function(id) {
104
+ blockedRAFs.delete(id);
105
+ return origCancel.call(window, id);
106
+ };
107
+ }
108
+ function restoreRAF() {
109
+ if (originalRAF) {
110
+ window.requestAnimationFrame = originalRAF;
111
+ originalRAF = null;
112
+ }
113
+ rafLoopTracker.clear();
114
+ blockedRAFs.clear();
115
+ }
116
+ function setupMutationObserver() {
117
+ if (mutationObserver) return;
118
+ mutationObserver = new MutationObserver((mutations) => {
119
+ if (!enabled) return;
120
+ for (const mutation of mutations) {
121
+ for (const node of mutation.addedNodes) {
122
+ if (!(node instanceof HTMLElement)) continue;
123
+ try {
124
+ const anims = node.getAnimations({ subtree: true });
125
+ for (const anim of anims) {
126
+ if (anim.playState === "running" || anim.playState === "pending") {
127
+ anim.pause();
128
+ pausedAnimations.push(anim);
129
+ }
130
+ }
131
+ } catch {
132
+ }
133
+ if (node.tagName === "VIDEO") {
134
+ const video = node;
135
+ if (!video.paused) {
136
+ video.pause();
137
+ pausedVideos.push(video);
138
+ }
139
+ }
140
+ node.querySelectorAll?.("video")?.forEach((video) => {
141
+ if (!video.paused) {
142
+ video.pause();
143
+ pausedVideos.push(video);
144
+ }
145
+ });
146
+ if (node.tagName === "IMG") {
147
+ freezeGif(node);
148
+ }
149
+ node.querySelectorAll?.("img")?.forEach((img) => freezeGif(img));
150
+ if (node.hasAttribute?.("data-framer-appear-id")) {
151
+ forceFramerAppearVisibleSingle(node);
152
+ }
153
+ node.querySelectorAll?.("[data-framer-appear-id]")?.forEach((el) => {
154
+ forceFramerAppearVisibleSingle(el);
155
+ });
156
+ }
157
+ }
158
+ });
159
+ mutationObserver.observe(document.body, {
160
+ childList: true,
161
+ subtree: true
162
+ });
163
+ }
164
+ function disconnectMutationObserver() {
165
+ mutationObserver?.disconnect();
166
+ mutationObserver = null;
167
+ }
168
+ function emulateReducedMotion() {
169
+ if (originalMatchMedia) return;
170
+ originalMatchMedia = window.matchMedia;
171
+ window.matchMedia = function(query) {
172
+ const mql = originalMatchMedia.call(window, query);
173
+ if (query.includes("prefers-reduced-motion")) {
174
+ const proxy = Object.create(mql);
175
+ Object.defineProperty(proxy, "matches", { get: () => enabled, configurable: true });
176
+ Object.defineProperty(proxy, "media", { get: () => query, configurable: true });
177
+ return proxy;
178
+ }
179
+ return mql;
180
+ };
181
+ try {
182
+ const mql = originalMatchMedia.call(window, "(prefers-reduced-motion: reduce)");
183
+ mql.dispatchEvent(new MediaQueryListEvent("change", { matches: true, media: mql.media }));
184
+ } catch {
185
+ }
186
+ }
187
+ function restoreMatchMedia() {
188
+ if (originalMatchMedia) {
189
+ window.matchMedia = originalMatchMedia;
190
+ originalMatchMedia = null;
191
+ try {
192
+ const mql = window.matchMedia("(prefers-reduced-motion: reduce)");
193
+ mql.dispatchEvent(new MediaQueryListEvent("change", { matches: mql.matches, media: mql.media }));
194
+ } catch {
195
+ }
196
+ }
197
+ }
198
+ function freezeGif(img) {
199
+ const src = img.src || img.getAttribute("src") || "";
200
+ if (!src.toLowerCase().includes(".gif")) return;
201
+ if (gifOriginals.has(img)) return;
202
+ const freeze = () => {
203
+ try {
204
+ const canvas = document.createElement("canvas");
205
+ canvas.width = img.naturalWidth || img.width;
206
+ canvas.height = img.naturalHeight || img.height;
207
+ if (canvas.width === 0 || canvas.height === 0) return;
208
+ const ctx = canvas.getContext("2d");
209
+ if (!ctx) return;
210
+ ctx.drawImage(img, 0, 0);
211
+ gifOriginals.set(img, src);
212
+ img.src = canvas.toDataURL("image/png");
213
+ } catch {
214
+ }
215
+ };
216
+ if (img.complete && img.naturalWidth > 0) {
217
+ freeze();
218
+ } else {
219
+ img.addEventListener("load", freeze, { once: true });
220
+ }
221
+ }
222
+ function freezeAllGifs() {
223
+ document.querySelectorAll("img").forEach(freezeGif);
224
+ }
225
+ function restoreGifs() {
226
+ for (const [img, originalSrc] of gifOriginals) {
227
+ try {
228
+ img.src = originalSrc;
229
+ } catch {
230
+ }
231
+ }
232
+ gifOriginals.clear();
233
+ }
234
+ const FRAMER_FIXED_ATTR = "data-accessify-framer-fixed";
235
+ let framerFixedElements = [];
236
+ function forceFramerAppearVisible() {
237
+ const appearEls = document.querySelectorAll("[data-framer-appear-id]");
238
+ for (const el of appearEls) {
239
+ if (el.hasAttribute(FRAMER_FIXED_ATTR)) continue;
240
+ const style = el.getAttribute("style") || "";
241
+ if (style.includes("opacity:0.001") || style.includes("opacity: 0.001")) {
242
+ framerFixedElements.push({ el, origStyle: style });
243
+ el.style.opacity = "1";
244
+ el.style.transform = "none";
245
+ el.style.willChange = "auto";
246
+ el.setAttribute(FRAMER_FIXED_ATTR, "true");
247
+ }
248
+ }
249
+ const allWillChange = document.querySelectorAll('[style*="will-change"]');
250
+ for (const el of allWillChange) {
251
+ if (el.hasAttribute(FRAMER_FIXED_ATTR)) continue;
252
+ const computed = getComputedStyle(el);
253
+ const opacity = parseFloat(computed.opacity);
254
+ if (opacity < 0.05 && computed.willChange.includes("transform")) {
255
+ framerFixedElements.push({ el, origStyle: el.getAttribute("style") || "" });
256
+ el.style.opacity = "1";
257
+ el.style.transform = "none";
258
+ el.style.willChange = "auto";
259
+ el.setAttribute(FRAMER_FIXED_ATTR, "true");
260
+ }
261
+ }
262
+ }
263
+ function restoreFramerAppear() {
264
+ for (const { el, origStyle } of framerFixedElements) {
265
+ try {
266
+ el.setAttribute("style", origStyle);
267
+ el.removeAttribute(FRAMER_FIXED_ATTR);
268
+ } catch {
269
+ }
270
+ }
271
+ framerFixedElements = [];
272
+ }
273
+ function forceFramerAppearVisibleSingle(el) {
274
+ if (el.hasAttribute(FRAMER_FIXED_ATTR)) return;
275
+ const style = el.getAttribute("style") || "";
276
+ if (style.includes("opacity:0.001") || style.includes("opacity: 0.001")) {
277
+ framerFixedElements.push({ el, origStyle: style });
278
+ el.style.opacity = "1";
279
+ el.style.transform = "none";
280
+ el.style.willChange = "auto";
281
+ el.setAttribute(FRAMER_FIXED_ATTR, "true");
282
+ }
283
+ }
284
+ function stopMarquees() {
285
+ document.querySelectorAll("marquee").forEach((el) => {
286
+ try {
287
+ el.stop();
288
+ } catch {
289
+ }
290
+ });
291
+ }
292
+ function startMarquees() {
293
+ document.querySelectorAll("marquee").forEach((el) => {
294
+ try {
295
+ el.start();
296
+ } catch {
297
+ }
298
+ });
299
+ }
300
+ function pauseSVGAnimations() {
301
+ document.querySelectorAll("svg").forEach((svg) => {
302
+ try {
303
+ if (typeof svg.pauseAnimations === "function") {
304
+ svg.pauseAnimations();
305
+ }
306
+ } catch {
307
+ }
308
+ });
309
+ }
310
+ function resumeSVGAnimations() {
311
+ document.querySelectorAll("svg").forEach((svg) => {
312
+ try {
313
+ if (typeof svg.unpauseAnimations === "function") {
314
+ svg.unpauseAnimations();
315
+ }
316
+ } catch {
317
+ }
318
+ });
319
+ }
320
+ function pauseAutoplayVideos() {
321
+ const videos = document.querySelectorAll("video[autoplay], video");
322
+ pausedVideos = [];
323
+ videos.forEach((video) => {
324
+ if (!video.paused) {
325
+ video.pause();
326
+ pausedVideos.push(video);
327
+ }
328
+ });
329
+ }
330
+ function resumePausedVideos() {
331
+ pausedVideos.forEach((video) => {
332
+ try {
333
+ video.play();
334
+ } catch {
335
+ }
336
+ });
337
+ pausedVideos = [];
338
+ }
339
+ function activate() {
340
+ if (enabled) return;
341
+ enabled = true;
342
+ injectStyles();
343
+ pauseAllWebAnimations();
344
+ overrideElementAnimate();
345
+ overrideRAF();
346
+ setupMutationObserver();
347
+ emulateReducedMotion();
348
+ freezeAllGifs();
349
+ forceFramerAppearVisible();
350
+ stopMarquees();
351
+ pauseSVGAnimations();
352
+ pauseAutoplayVideos();
353
+ localStorage.setItem(STORAGE_KEY, "true");
354
+ }
355
+ function deactivate() {
356
+ enabled = false;
357
+ resumePausedVideos();
358
+ resumeSVGAnimations();
359
+ startMarquees();
360
+ restoreFramerAppear();
361
+ restoreGifs();
362
+ restoreMatchMedia();
363
+ disconnectMutationObserver();
364
+ restoreRAF();
365
+ restoreElementAnimate();
366
+ resumePausedAnimations();
367
+ removeStyles();
368
+ localStorage.removeItem(STORAGE_KEY);
369
+ }
370
+ return {
371
+ id: "animation-stop",
372
+ name: () => "Stop Animations",
373
+ description: "Pause all animations, transitions, and auto-playing videos (WCAG 2.3.1)",
374
+ icon: "animation-stop",
375
+ category: "visual",
376
+ activate,
377
+ deactivate,
378
+ getState: () => ({
379
+ id: "animation-stop",
380
+ enabled
381
+ }),
382
+ setState: (state) => {
383
+ if (state.enabled) {
384
+ activate();
385
+ } else {
386
+ deactivate();
387
+ }
388
+ }
389
+ };
390
+ }
391
+ export {
392
+ createAnimationStopModule as default
393
+ };
394
+ //# sourceMappingURL=animation-stop-CA4Sz28J.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"animation-stop-CA4Sz28J.js","sources":["../src/features/animation-stop.ts"],"sourcesContent":["import type { FeatureModule, FeatureState } from '../types';\n\nexport default function createAnimationStopModule(): FeatureModule {\n let enabled = false;\n const STYLE_ID = 'accessify-animation-stop';\n const STORAGE_KEY = 'accessify-animation-stop';\n\n // --- State for clean restore ---\n let pausedVideos: HTMLVideoElement[] = [];\n let pausedAnimations: Animation[] = [];\n let originalAnimate: typeof Element.prototype.animate | null = null;\n let originalRAF: typeof window.requestAnimationFrame | null = null;\n let originalMatchMedia: typeof window.matchMedia | null = null;\n let mutationObserver: MutationObserver | null = null;\n let rafLoopTracker = new Map<string, { count: number; lastTime: number }>();\n let blockedRAFs = new Set<number>();\n let gifOriginals = new Map<HTMLImageElement, string>();\n let reducedMotionListeners: Array<{ mql: MediaQueryList; handler: () => void }> = [];\n\n // -------------------------------------------------------------------------\n // Layer 0: CSS overrides (existing — catches pure CSS animations)\n // -------------------------------------------------------------------------\n\n function getStyles(): string {\n return `\n /* accessify animation stop - WCAG 2.3.1 compliance */\n *, *::before, *::after {\n animation-duration: 0.01s !important;\n animation-delay: 0s !important;\n animation-iteration-count: 1 !important;\n animation-play-state: paused !important;\n transition-duration: 0.01s !important;\n transition-delay: 0s !important;\n scroll-behavior: auto !important;\n }\n marquee { -moz-binding: none; }\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 document.getElementById(STYLE_ID)?.remove();\n }\n\n // -------------------------------------------------------------------------\n // Layer 1: Web Animations API — pause all running + intercept new\n // -------------------------------------------------------------------------\n\n function pauseAllWebAnimations() {\n try {\n const animations = document.getAnimations();\n for (const anim of animations) {\n if (anim.playState === 'running' || anim.playState === 'pending') {\n anim.pause();\n pausedAnimations.push(anim);\n }\n }\n } catch { /* getAnimations not supported */ }\n }\n\n function overrideElementAnimate() {\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 const anim = originalAnimate!.call(this, keyframes, options);\n if (enabled) {\n anim.pause();\n pausedAnimations.push(anim);\n }\n return anim;\n };\n }\n\n function restoreElementAnimate() {\n if (originalAnimate) {\n Element.prototype.animate = originalAnimate;\n originalAnimate = null;\n }\n }\n\n function resumePausedAnimations() {\n for (const anim of pausedAnimations) {\n try { anim.play(); } catch { /* animation may be gone */ }\n }\n pausedAnimations = [];\n }\n\n // -------------------------------------------------------------------------\n // Layer 2: requestAnimationFrame loop detection\n // -------------------------------------------------------------------------\n\n function overrideRAF() {\n if (originalRAF) return;\n originalRAF = window.requestAnimationFrame;\n const origCancel = window.cancelAnimationFrame;\n\n window.requestAnimationFrame = function (callback: FrameRequestCallback): number {\n if (!enabled) return originalRAF!.call(window, callback);\n\n // Fingerprint callback to detect animation loops\n const key = callback.toString().slice(0, 200);\n const now = performance.now();\n const entry = rafLoopTracker.get(key);\n\n if (entry) {\n entry.count++;\n if (now - entry.lastTime < 100 && entry.count > 3) {\n // This callback is re-registering rapidly — animation loop detected\n const id = originalRAF!.call(window, () => {}); // no-op\n blockedRAFs.add(id);\n return id;\n }\n entry.lastTime = now;\n } else {\n rafLoopTracker.set(key, { count: 1, lastTime: now });\n }\n\n // Allow through but wrap to track re-registration\n return originalRAF!.call(window, callback);\n };\n\n window.cancelAnimationFrame = function (id: number) {\n blockedRAFs.delete(id);\n return origCancel.call(window, id);\n };\n }\n\n function restoreRAF() {\n if (originalRAF) {\n window.requestAnimationFrame = originalRAF;\n originalRAF = null;\n }\n rafLoopTracker.clear();\n blockedRAFs.clear();\n }\n\n // -------------------------------------------------------------------------\n // Layer 3: MutationObserver — catch dynamically added animated elements\n // -------------------------------------------------------------------------\n\n function setupMutationObserver() {\n if (mutationObserver) return;\n\n mutationObserver = new MutationObserver((mutations) => {\n if (!enabled) return;\n for (const mutation of mutations) {\n for (const node of mutation.addedNodes) {\n if (!(node instanceof HTMLElement)) continue;\n // Pause animations on newly added elements\n try {\n const anims = node.getAnimations({ subtree: true });\n for (const anim of anims) {\n if (anim.playState === 'running' || anim.playState === 'pending') {\n anim.pause();\n pausedAnimations.push(anim);\n }\n }\n } catch { /* not supported */ }\n\n // Pause new videos\n if (node.tagName === 'VIDEO') {\n const video = node as HTMLVideoElement;\n if (!video.paused) {\n video.pause();\n pausedVideos.push(video);\n }\n }\n node.querySelectorAll?.('video')?.forEach((video) => {\n if (!video.paused) {\n video.pause();\n pausedVideos.push(video);\n }\n });\n\n // Freeze new GIFs\n if (node.tagName === 'IMG') {\n freezeGif(node as HTMLImageElement);\n }\n node.querySelectorAll?.('img')?.forEach((img) => freezeGif(img));\n\n // Fix Framer appear elements added dynamically\n if (node.hasAttribute?.('data-framer-appear-id')) {\n forceFramerAppearVisibleSingle(node);\n }\n node.querySelectorAll?.('[data-framer-appear-id]')?.forEach((el) => {\n forceFramerAppearVisibleSingle(el as HTMLElement);\n });\n }\n }\n });\n\n mutationObserver.observe(document.body, {\n childList: true,\n subtree: true,\n });\n }\n\n function disconnectMutationObserver() {\n mutationObserver?.disconnect();\n mutationObserver = null;\n }\n\n // -------------------------------------------------------------------------\n // Layer 4: prefers-reduced-motion emulation\n // -------------------------------------------------------------------------\n\n function emulateReducedMotion() {\n if (originalMatchMedia) return;\n originalMatchMedia = window.matchMedia;\n\n window.matchMedia = function (query: string): MediaQueryList {\n const mql = originalMatchMedia!.call(window, query);\n\n if (query.includes('prefers-reduced-motion')) {\n // Return a proxy that always reports matches: true\n const proxy = Object.create(mql);\n Object.defineProperty(proxy, 'matches', { get: () => enabled, configurable: true });\n Object.defineProperty(proxy, 'media', { get: () => query, configurable: true });\n return proxy;\n }\n return mql;\n };\n\n // Trigger change event on existing reduced-motion media queries\n // so frameworks (Framer Motion, GSAP) react immediately\n try {\n const mql = originalMatchMedia.call(window, '(prefers-reduced-motion: reduce)');\n mql.dispatchEvent(new MediaQueryListEvent('change', { matches: true, media: mql.media }));\n } catch { /* older browsers */ }\n }\n\n function restoreMatchMedia() {\n if (originalMatchMedia) {\n window.matchMedia = originalMatchMedia;\n originalMatchMedia = null;\n\n // Dispatch change event to signal motion is allowed again\n try {\n const mql = window.matchMedia('(prefers-reduced-motion: reduce)');\n mql.dispatchEvent(new MediaQueryListEvent('change', { matches: mql.matches, media: mql.media }));\n } catch { /* older browsers */ }\n }\n }\n\n // -------------------------------------------------------------------------\n // Layer 5: GIF freeze — replace with static first frame\n // -------------------------------------------------------------------------\n\n function freezeGif(img: HTMLImageElement) {\n const src = img.src || img.getAttribute('src') || '';\n if (!src.toLowerCase().includes('.gif')) return;\n if (gifOriginals.has(img)) return;\n\n const freeze = () => {\n try {\n const canvas = document.createElement('canvas');\n canvas.width = img.naturalWidth || img.width;\n canvas.height = img.naturalHeight || img.height;\n if (canvas.width === 0 || canvas.height === 0) return;\n const ctx = canvas.getContext('2d');\n if (!ctx) return;\n ctx.drawImage(img, 0, 0);\n gifOriginals.set(img, src);\n img.src = canvas.toDataURL('image/png');\n } catch { /* cross-origin, skip */ }\n };\n\n if (img.complete && img.naturalWidth > 0) {\n freeze();\n } else {\n img.addEventListener('load', freeze, { once: true });\n }\n }\n\n function freezeAllGifs() {\n document.querySelectorAll<HTMLImageElement>('img').forEach(freezeGif);\n }\n\n function restoreGifs() {\n for (const [img, originalSrc] of gifOriginals) {\n try { img.src = originalSrc; } catch { /* element may be gone */ }\n }\n gifOriginals.clear();\n }\n\n // -------------------------------------------------------------------------\n // Layer 6: Framer appear animations — force visible\n // Framer renders elements with opacity:0.001 + will-change:transform and\n // animates them in via JS. When we stop animations, they stay invisible.\n // Fix: force all Framer appear elements to their final visible state.\n // -------------------------------------------------------------------------\n\n const FRAMER_FIXED_ATTR = 'data-accessify-framer-fixed';\n let framerFixedElements: Array<{ el: HTMLElement; origStyle: string }> = [];\n\n function forceFramerAppearVisible() {\n // Framer appear elements\n const appearEls = document.querySelectorAll<HTMLElement>('[data-framer-appear-id]');\n for (const el of appearEls) {\n if (el.hasAttribute(FRAMER_FIXED_ATTR)) continue;\n const style = el.getAttribute('style') || '';\n // Only fix elements that are in their initial hidden state\n if (style.includes('opacity:0.001') || style.includes('opacity: 0.001')) {\n framerFixedElements.push({ el, origStyle: style });\n el.style.opacity = '1';\n el.style.transform = 'none';\n el.style.willChange = 'auto';\n el.setAttribute(FRAMER_FIXED_ATTR, 'true');\n }\n }\n\n // Generic: any element with near-zero opacity that has will-change:transform\n // (common pattern in animation libraries for scroll-reveal)\n const allWillChange = document.querySelectorAll<HTMLElement>('[style*=\"will-change\"]');\n for (const el of allWillChange) {\n if (el.hasAttribute(FRAMER_FIXED_ATTR)) continue;\n const computed = getComputedStyle(el);\n const opacity = parseFloat(computed.opacity);\n if (opacity < 0.05 && computed.willChange.includes('transform')) {\n framerFixedElements.push({ el, origStyle: el.getAttribute('style') || '' });\n el.style.opacity = '1';\n el.style.transform = 'none';\n el.style.willChange = 'auto';\n el.setAttribute(FRAMER_FIXED_ATTR, 'true');\n }\n }\n }\n\n function restoreFramerAppear() {\n for (const { el, origStyle } of framerFixedElements) {\n try {\n el.setAttribute('style', origStyle);\n el.removeAttribute(FRAMER_FIXED_ATTR);\n } catch { /* element may be gone */ }\n }\n framerFixedElements = [];\n }\n\n function forceFramerAppearVisibleSingle(el: HTMLElement) {\n if (el.hasAttribute(FRAMER_FIXED_ATTR)) return;\n const style = el.getAttribute('style') || '';\n if (style.includes('opacity:0.001') || style.includes('opacity: 0.001')) {\n framerFixedElements.push({ el, origStyle: style });\n el.style.opacity = '1';\n el.style.transform = 'none';\n el.style.willChange = 'auto';\n el.setAttribute(FRAMER_FIXED_ATTR, 'true');\n }\n }\n\n // -------------------------------------------------------------------------\n // Layer 7: Marquee & SVG SMIL animations\n // -------------------------------------------------------------------------\n\n function stopMarquees() {\n document.querySelectorAll('marquee').forEach((el) => {\n try { (el as any).stop(); } catch { /* not supported */ }\n });\n }\n\n function startMarquees() {\n document.querySelectorAll('marquee').forEach((el) => {\n try { (el as any).start(); } catch { /* not supported */ }\n });\n }\n\n function pauseSVGAnimations() {\n document.querySelectorAll('svg').forEach((svg) => {\n try {\n if (typeof (svg as any).pauseAnimations === 'function') {\n (svg as any).pauseAnimations();\n }\n } catch { /* not supported */ }\n });\n }\n\n function resumeSVGAnimations() {\n document.querySelectorAll('svg').forEach((svg) => {\n try {\n if (typeof (svg as any).unpauseAnimations === 'function') {\n (svg as any).unpauseAnimations();\n }\n } catch { /* not supported */ }\n });\n }\n\n // -------------------------------------------------------------------------\n // Video control (existing)\n // -------------------------------------------------------------------------\n\n function pauseAutoplayVideos() {\n const videos = document.querySelectorAll<HTMLVideoElement>('video[autoplay], video');\n pausedVideos = [];\n videos.forEach((video) => {\n if (!video.paused) {\n video.pause();\n pausedVideos.push(video);\n }\n });\n }\n\n function resumePausedVideos() {\n pausedVideos.forEach((video) => {\n try { video.play(); } catch { /* removed from DOM */ }\n });\n pausedVideos = [];\n }\n\n // -------------------------------------------------------------------------\n // Lifecycle\n // -------------------------------------------------------------------------\n\n function activate() {\n if (enabled) return;\n enabled = true;\n\n // Layer 0: CSS overrides\n injectStyles();\n\n // Layer 1: Web Animations API\n pauseAllWebAnimations();\n overrideElementAnimate();\n\n // Layer 2: RAF loop detection\n overrideRAF();\n\n // Layer 3: MutationObserver for dynamic content\n setupMutationObserver();\n\n // Layer 4: prefers-reduced-motion emulation\n emulateReducedMotion();\n\n // Layer 5: GIF freeze\n freezeAllGifs();\n\n // Layer 6: Framer appear — force visible\n forceFramerAppearVisible();\n\n // Layer 7: Marquee & SVG SMIL\n stopMarquees();\n pauseSVGAnimations();\n\n // Videos\n pauseAutoplayVideos();\n\n localStorage.setItem(STORAGE_KEY, 'true');\n }\n\n function deactivate() {\n enabled = false;\n\n // Reverse order for clean teardown\n resumePausedVideos();\n resumeSVGAnimations();\n startMarquees();\n restoreFramerAppear();\n restoreGifs();\n restoreMatchMedia();\n disconnectMutationObserver();\n restoreRAF();\n restoreElementAnimate();\n resumePausedAnimations();\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 => ({\n id: 'animation-stop',\n enabled,\n }),\n setState: (state: { enabled: boolean }) => {\n if (state.enabled) {\n activate();\n } else {\n deactivate();\n }\n },\n };\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,105 @@
1
+ function createHideImagesModule() {
2
+ let enabled = false;
3
+ const STYLE_ID = "accessify-hide-images";
4
+ let bgImageOriginals = /* @__PURE__ */ new Map();
5
+ let mutationObserver = null;
6
+ const CSS = `
7
+ /* Standard image elements */
8
+ img, picture, svg:not([aria-hidden="true"]), video, canvas,
9
+ [role="img"], figure {
10
+ opacity: 0 !important;
11
+ pointer-events: none !important;
12
+ }
13
+
14
+ /* CSS background-images (inline styles) */
15
+ [style*="background-image"] {
16
+ background-image: none !important;
17
+ }
18
+
19
+ /* Lazy-load images that haven't loaded yet */
20
+ img[data-src], img[data-lazy-src], img[data-original],
21
+ img[loading="lazy"] {
22
+ opacity: 0 !important;
23
+ pointer-events: none !important;
24
+ }
25
+ `;
26
+ function hideBackgroundImages() {
27
+ const all = document.querySelectorAll("*");
28
+ for (const el of all) {
29
+ if (el.closest("#accessify-root")) continue;
30
+ const computed = getComputedStyle(el);
31
+ if (computed.backgroundImage && computed.backgroundImage !== "none") {
32
+ bgImageOriginals.set(el, el.style.backgroundImage);
33
+ el.style.setProperty("background-image", "none", "important");
34
+ }
35
+ }
36
+ }
37
+ function restoreBackgroundImages() {
38
+ for (const [el, original] of bgImageOriginals) {
39
+ try {
40
+ el.style.backgroundImage = original;
41
+ } catch {
42
+ }
43
+ }
44
+ bgImageOriginals.clear();
45
+ }
46
+ function setupMutationObserver() {
47
+ if (mutationObserver) return;
48
+ mutationObserver = new MutationObserver((mutations) => {
49
+ if (!enabled) return;
50
+ for (const mutation of mutations) {
51
+ for (const node of mutation.addedNodes) {
52
+ if (!(node instanceof HTMLElement)) continue;
53
+ if (node.closest("#accessify-root")) continue;
54
+ const computed = getComputedStyle(node);
55
+ if (computed.backgroundImage && computed.backgroundImage !== "none") {
56
+ bgImageOriginals.set(node, node.style.backgroundImage);
57
+ node.style.setProperty("background-image", "none", "important");
58
+ }
59
+ node.querySelectorAll("*").forEach((child) => {
60
+ if (child.closest("#accessify-root")) return;
61
+ const cs = getComputedStyle(child);
62
+ if (cs.backgroundImage && cs.backgroundImage !== "none") {
63
+ bgImageOriginals.set(child, child.style.backgroundImage);
64
+ child.style.setProperty("background-image", "none", "important");
65
+ }
66
+ });
67
+ }
68
+ }
69
+ });
70
+ mutationObserver.observe(document.body, { childList: true, subtree: true });
71
+ }
72
+ function activate() {
73
+ enabled = true;
74
+ let styleEl = document.getElementById(STYLE_ID);
75
+ if (!styleEl) {
76
+ styleEl = document.createElement("style");
77
+ styleEl.id = STYLE_ID;
78
+ document.head.appendChild(styleEl);
79
+ }
80
+ styleEl.textContent = CSS;
81
+ hideBackgroundImages();
82
+ setupMutationObserver();
83
+ }
84
+ function deactivate() {
85
+ enabled = false;
86
+ mutationObserver?.disconnect();
87
+ mutationObserver = null;
88
+ restoreBackgroundImages();
89
+ document.getElementById(STYLE_ID)?.remove();
90
+ }
91
+ return {
92
+ id: "hide-images",
93
+ name: () => "Hide Images",
94
+ description: "Remove images for distraction-free reading",
95
+ icon: "hide-images",
96
+ category: "visual",
97
+ activate,
98
+ deactivate,
99
+ getState: () => ({ id: "hide-images", enabled })
100
+ };
101
+ }
102
+ export {
103
+ createHideImagesModule as default
104
+ };
105
+ //# sourceMappingURL=hide-images-B_LeCBcd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hide-images-B_LeCBcd.js","sources":["../src/features/hide-images.ts"],"sourcesContent":["import type { FeatureModule, FeatureState } from '../types';\n\nexport default function createHideImagesModule(): FeatureModule {\n let enabled = false;\n const STYLE_ID = 'accessify-hide-images';\n let bgImageOriginals = new Map<HTMLElement, string>();\n let mutationObserver: MutationObserver | null = null;\n\n const CSS = `\n /* Standard image elements */\n img, picture, svg:not([aria-hidden=\"true\"]), video, canvas,\n [role=\"img\"], figure {\n opacity: 0 !important;\n pointer-events: none !important;\n }\n\n /* CSS background-images (inline styles) */\n [style*=\"background-image\"] {\n background-image: none !important;\n }\n\n /* Lazy-load images that haven't loaded yet */\n img[data-src], img[data-lazy-src], img[data-original],\n img[loading=\"lazy\"] {\n opacity: 0 !important;\n pointer-events: none !important;\n }\n `;\n\n function hideBackgroundImages() {\n // Find elements with computed background-image (covers CSS classes, not just inline)\n const all = document.querySelectorAll<HTMLElement>('*');\n for (const el of all) {\n if (el.closest('#accessify-root')) continue;\n const computed = getComputedStyle(el);\n if (computed.backgroundImage && computed.backgroundImage !== 'none') {\n bgImageOriginals.set(el, el.style.backgroundImage);\n el.style.setProperty('background-image', 'none', 'important');\n }\n }\n }\n\n function restoreBackgroundImages() {\n for (const [el, original] of bgImageOriginals) {\n try {\n el.style.backgroundImage = original;\n } catch { /* element may be gone */ }\n }\n bgImageOriginals.clear();\n }\n\n function setupMutationObserver() {\n if (mutationObserver) return;\n mutationObserver = new MutationObserver((mutations) => {\n if (!enabled) return;\n for (const mutation of mutations) {\n for (const node of mutation.addedNodes) {\n if (!(node instanceof HTMLElement)) continue;\n if (node.closest('#accessify-root')) continue;\n // Hide background images on newly added elements\n const computed = getComputedStyle(node);\n if (computed.backgroundImage && computed.backgroundImage !== 'none') {\n bgImageOriginals.set(node, node.style.backgroundImage);\n node.style.setProperty('background-image', 'none', 'important');\n }\n // Also check children\n node.querySelectorAll<HTMLElement>('*').forEach((child) => {\n if (child.closest('#accessify-root')) return;\n const cs = getComputedStyle(child);\n if (cs.backgroundImage && cs.backgroundImage !== 'none') {\n bgImageOriginals.set(child, child.style.backgroundImage);\n child.style.setProperty('background-image', 'none', 'important');\n }\n });\n }\n }\n });\n mutationObserver.observe(document.body, { childList: true, subtree: true });\n }\n\n function activate() {\n enabled = true;\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 = CSS;\n hideBackgroundImages();\n setupMutationObserver();\n }\n\n function deactivate() {\n enabled = false;\n mutationObserver?.disconnect();\n mutationObserver = null;\n restoreBackgroundImages();\n document.getElementById(STYLE_ID)?.remove();\n }\n\n return {\n id: 'hide-images',\n name: () => 'Hide Images',\n description: 'Remove images for distraction-free reading',\n icon: 'hide-images',\n category: 'visual',\n activate,\n deactivate,\n getState: (): FeatureState => ({ id: 'hide-images', enabled }),\n };\n}\n"],"names":[],"mappings":"AAEA,SAAwB,yBAAwC;AAC9D,MAAI,UAAU;AACd,QAAM,WAAW;AACjB,MAAI,uCAAuB,IAAA;AAC3B,MAAI,mBAA4C;AAEhD,QAAM,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAqBZ,WAAS,uBAAuB;AAE9B,UAAM,MAAM,SAAS,iBAA8B,GAAG;AACtD,eAAW,MAAM,KAAK;AACpB,UAAI,GAAG,QAAQ,iBAAiB,EAAG;AACnC,YAAM,WAAW,iBAAiB,EAAE;AACpC,UAAI,SAAS,mBAAmB,SAAS,oBAAoB,QAAQ;AACnE,yBAAiB,IAAI,IAAI,GAAG,MAAM,eAAe;AACjD,WAAG,MAAM,YAAY,oBAAoB,QAAQ,WAAW;AAAA,MAC9D;AAAA,IACF;AAAA,EACF;AAEA,WAAS,0BAA0B;AACjC,eAAW,CAAC,IAAI,QAAQ,KAAK,kBAAkB;AAC7C,UAAI;AACF,WAAG,MAAM,kBAAkB;AAAA,MAC7B,QAAQ;AAAA,MAA4B;AAAA,IACtC;AACA,qBAAiB,MAAA;AAAA,EACnB;AAEA,WAAS,wBAAwB;AAC/B,QAAI,iBAAkB;AACtB,uBAAmB,IAAI,iBAAiB,CAAC,cAAc;AACrD,UAAI,CAAC,QAAS;AACd,iBAAW,YAAY,WAAW;AAChC,mBAAW,QAAQ,SAAS,YAAY;AACtC,cAAI,EAAE,gBAAgB,aAAc;AACpC,cAAI,KAAK,QAAQ,iBAAiB,EAAG;AAErC,gBAAM,WAAW,iBAAiB,IAAI;AACtC,cAAI,SAAS,mBAAmB,SAAS,oBAAoB,QAAQ;AACnE,6BAAiB,IAAI,MAAM,KAAK,MAAM,eAAe;AACrD,iBAAK,MAAM,YAAY,oBAAoB,QAAQ,WAAW;AAAA,UAChE;AAEA,eAAK,iBAA8B,GAAG,EAAE,QAAQ,CAAC,UAAU;AACzD,gBAAI,MAAM,QAAQ,iBAAiB,EAAG;AACtC,kBAAM,KAAK,iBAAiB,KAAK;AACjC,gBAAI,GAAG,mBAAmB,GAAG,oBAAoB,QAAQ;AACvD,+BAAiB,IAAI,OAAO,MAAM,MAAM,eAAe;AACvD,oBAAM,MAAM,YAAY,oBAAoB,QAAQ,WAAW;AAAA,YACjE;AAAA,UACF,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF,CAAC;AACD,qBAAiB,QAAQ,SAAS,MAAM,EAAE,WAAW,MAAM,SAAS,MAAM;AAAA,EAC5E;AAEA,WAAS,WAAW;AAClB,cAAU;AACV,QAAI,UAAU,SAAS,eAAe,QAAQ;AAC9C,QAAI,CAAC,SAAS;AACZ,gBAAU,SAAS,cAAc,OAAO;AACxC,cAAQ,KAAK;AACb,eAAS,KAAK,YAAY,OAAO;AAAA,IACnC;AACA,YAAQ,cAAc;AACtB,yBAAA;AACA,0BAAA;AAAA,EACF;AAEA,WAAS,aAAa;AACpB,cAAU;AACV,sBAAkB,WAAA;AAClB,uBAAmB;AACnB,4BAAA;AACA,aAAS,eAAe,QAAQ,GAAG,OAAA;AAAA,EACrC;AAEA,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,MAAM,MAAM;AAAA,IACZ,aAAa;AAAA,IACb,MAAM;AAAA,IACN,UAAU;AAAA,IACV;AAAA,IACA;AAAA,IACA,UAAU,OAAqB,EAAE,IAAI,eAAe,QAAA;AAAA,EAAQ;AAEhE;"}
@@ -6310,16 +6310,16 @@ function FeatureGrid($$anchor, $$props) {
6310
6310
  const FEATURE_LOADERS = {
6311
6311
  contrast: () => import("./contrast-CqsICAkU.js"),
6312
6312
  "text-size": () => import("./text-size-C6OFhCGi.js"),
6313
- "keyboard-nav": () => import("./keyboard-nav-CkAYxUc1.js"),
6313
+ "keyboard-nav": () => import("./keyboard-nav-kiIcwX2D.js"),
6314
6314
  "link-highlight": () => import("./link-highlight-DBGm067Y.js"),
6315
6315
  "reading-guide": () => import("./reading-guide-VT8NciIL.js"),
6316
6316
  "reading-mask": () => import("./reading-mask-BABChuCz.js"),
6317
- "animation-stop": () => import("./animation-stop-DXebPS8D.js"),
6318
- "hide-images": () => import("./hide-images-DJwmsV2C.js"),
6317
+ "animation-stop": () => import("./animation-stop-CA4Sz28J.js"),
6318
+ "hide-images": () => import("./hide-images-B_LeCBcd.js"),
6319
6319
  "big-cursor": () => import("./big-cursor-B2UKu9dQ.js"),
6320
- "page-structure": () => import("./page-structure-yWkBKmwo.js"),
6320
+ "page-structure": () => import("./page-structure-CqY3jueN.js"),
6321
6321
  tts: () => import("./tts-CjszLRnb.js"),
6322
- "text-simplify": () => import("./text-simplify-C9gzE3t0.js"),
6322
+ "text-simplify": () => import("./text-simplify-B1v6Muvn.js"),
6323
6323
  "alt-text": () => Promise.resolve().then(() => altText)
6324
6324
  };
6325
6325
  let contrastMode = /* @__PURE__ */ state(proxy(readStoredContrastMode()));
@@ -7567,7 +7567,8 @@ async function fetchServerAltTexts(siteKey, proxyUrl, lang) {
7567
7567
  const base = proxyUrl || DEFAULT_API_BASE;
7568
7568
  const pageUrl = window.location.origin + window.location.pathname;
7569
7569
  const res = await fetch(`${base}/v1/manifest?siteKey=${encodeURIComponent(siteKey)}&url=${encodeURIComponent(pageUrl)}&feature=alt_text&variant=${encodeURIComponent(lang)}`, {
7570
- headers: { "Accept": "application/json" }
7570
+ headers: { "Accept": "application/json" },
7571
+ cache: "no-cache"
7571
7572
  });
7572
7573
  if (!res.ok) return map;
7573
7574
  const data = await res.json();
@@ -7647,6 +7648,7 @@ async function autoApplyCachedAltTexts(widgetConfig) {
7647
7648
  if (cached) {
7648
7649
  img.setAttribute("alt", cached);
7649
7650
  img.setAttribute("title", cached);
7651
+ img.setAttribute("data-accessify-alt", "auto");
7650
7652
  applied++;
7651
7653
  }
7652
7654
  });
@@ -7662,6 +7664,7 @@ async function autoApplyCachedAltTexts(widgetConfig) {
7662
7664
  if (c) {
7663
7665
  img.setAttribute("alt", c);
7664
7666
  img.setAttribute("title", c);
7667
+ img.setAttribute("data-accessify-alt", "auto");
7665
7668
  }
7666
7669
  }
7667
7670
  }
@@ -7960,6 +7963,15 @@ function createAltTextModule(aiService, initialLang = "de", serverConfig) {
7960
7963
  });
7961
7964
  return missing;
7962
7965
  }
7966
+ function scanAutoApplied() {
7967
+ const images = document.querySelectorAll('img[data-accessify-alt="auto"]');
7968
+ const applied = [];
7969
+ images.forEach((img) => {
7970
+ if (img.closest("#accessify-root")) return;
7971
+ if (!processedImages.has(img)) applied.push(img);
7972
+ });
7973
+ return applied;
7974
+ }
7963
7975
  function applyAltText(img, altText2) {
7964
7976
  img.setAttribute("alt", altText2);
7965
7977
  img.setAttribute("title", altText2);
@@ -8127,8 +8139,23 @@ function createAltTextModule(aiService, initialLang = "de", serverConfig) {
8127
8139
  if (enabled) return;
8128
8140
  enabled = true;
8129
8141
  injectStyles();
8130
- missingAltImages = scanForMissingAlt();
8131
- missingAltImages.forEach((img) => img.classList.add(HIGHLIGHT_CLASS));
8142
+ const alreadyApplied = scanAutoApplied();
8143
+ for (const img of alreadyApplied) {
8144
+ const alt = img.getAttribute("alt");
8145
+ processedImages.set(img, { generatedAlt: alt });
8146
+ missingAltImages.push(img);
8147
+ img.classList.add(HIGHLIGHT_CLASS, "accessify-alt-done");
8148
+ addInfoButton(img);
8149
+ img.removeEventListener("mouseenter", onMouseEnter);
8150
+ img.removeEventListener("mouseleave", onMouseLeave);
8151
+ img.addEventListener("mouseenter", onMouseEnter);
8152
+ img.addEventListener("mouseleave", onMouseLeave);
8153
+ }
8154
+ const missing = scanForMissingAlt();
8155
+ for (const img of missing) {
8156
+ missingAltImages.push(img);
8157
+ img.classList.add(HIGHLIGHT_CLASS);
8158
+ }
8132
8159
  updateBadge();
8133
8160
  generateAll();
8134
8161
  document.querySelectorAll("img").forEach((img) => {
@@ -8162,7 +8189,7 @@ function createAltTextModule(aiService, initialLang = "de", serverConfig) {
8162
8189
  missingAltImages = [];
8163
8190
  removeStyles();
8164
8191
  }
8165
- autoApplyCachedAltTexts().catch(() => {
8192
+ autoApplyCachedAltTexts({ siteKey, proxyUrl, lang: initialLang }).catch(() => {
8166
8193
  });
8167
8194
  return {
8168
8195
  id: "alt-text",
@@ -8373,4 +8400,4 @@ export {
8373
8400
  init as i,
8374
8401
  t
8375
8402
  };
8376
- //# sourceMappingURL=index-CsJDqdBW.js.map
8403
+ //# sourceMappingURL=index-D8e7_na0.js.map