accessify-widget 0.2.2 → 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.
- 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-CA4Sz28J.js +394 -0
- package/dist/animation-stop-CA4Sz28J.js.map +1 -0
- package/dist/hide-images-B_LeCBcd.js +105 -0
- package/dist/hide-images-B_LeCBcd.js.map +1 -0
- package/dist/{index-i-tNypvI.js → index-D8e7_na0.js} +62 -15
- package/dist/{index-i-tNypvI.js.map → index-D8e7_na0.js.map} +1 -1
- package/dist/{keyboard-nav-C9zLmfrJ.js → keyboard-nav-kiIcwX2D.js} +64 -2
- package/dist/keyboard-nav-kiIcwX2D.js.map +1 -0
- package/dist/{page-structure-GqhCQsl3.js → page-structure-CqY3jueN.js} +75 -2
- package/dist/page-structure-CqY3jueN.js.map +1 -0
- package/dist/{text-simplify-C9gzE3t0.js → text-simplify-B1v6Muvn.js} +117 -16
- package/dist/text-simplify-B1v6Muvn.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/animation-stop-DXebPS8D.js +0 -88
- package/dist/animation-stop-DXebPS8D.js.map +0 -1
- package/dist/hide-images-DJwmsV2C.js +0 -39
- package/dist/hide-images-DJwmsV2C.js.map +0 -1
- package/dist/keyboard-nav-C9zLmfrJ.js.map +0 -1
- package/dist/page-structure-GqhCQsl3.js.map +0 -1
- package/dist/text-simplify-C9gzE3t0.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "accessify-widget",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
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": "eslint src/ --ext .ts,.svelte"
|
|
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,88 +0,0 @@
|
|
|
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
|
-
function getStyles() {
|
|
7
|
-
return `
|
|
8
|
-
/* accessify animation stop - WCAG 2.3.1 compliance */
|
|
9
|
-
*, *::before, *::after {
|
|
10
|
-
animation-duration: 0.01s !important;
|
|
11
|
-
animation-delay: 0s !important;
|
|
12
|
-
animation-iteration-count: 1 !important;
|
|
13
|
-
animation-play-state: paused !important;
|
|
14
|
-
transition-duration: 0.01s !important;
|
|
15
|
-
transition-delay: 0s !important;
|
|
16
|
-
scroll-behavior: auto !important;
|
|
17
|
-
}
|
|
18
|
-
`;
|
|
19
|
-
}
|
|
20
|
-
function pauseAutoplayVideos() {
|
|
21
|
-
const videos = document.querySelectorAll("video[autoplay], video");
|
|
22
|
-
pausedVideos = [];
|
|
23
|
-
videos.forEach((video) => {
|
|
24
|
-
if (!video.paused) {
|
|
25
|
-
video.pause();
|
|
26
|
-
pausedVideos.push(video);
|
|
27
|
-
}
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
function resumePausedVideos() {
|
|
31
|
-
pausedVideos.forEach((video) => {
|
|
32
|
-
try {
|
|
33
|
-
video.play();
|
|
34
|
-
} catch {
|
|
35
|
-
}
|
|
36
|
-
});
|
|
37
|
-
pausedVideos = [];
|
|
38
|
-
}
|
|
39
|
-
function injectStyles() {
|
|
40
|
-
let styleEl = document.getElementById(STYLE_ID);
|
|
41
|
-
if (!styleEl) {
|
|
42
|
-
styleEl = document.createElement("style");
|
|
43
|
-
styleEl.id = STYLE_ID;
|
|
44
|
-
document.head.appendChild(styleEl);
|
|
45
|
-
}
|
|
46
|
-
styleEl.textContent = getStyles();
|
|
47
|
-
}
|
|
48
|
-
function removeStyles() {
|
|
49
|
-
const styleEl = document.getElementById(STYLE_ID);
|
|
50
|
-
styleEl?.remove();
|
|
51
|
-
}
|
|
52
|
-
function activate() {
|
|
53
|
-
enabled = true;
|
|
54
|
-
injectStyles();
|
|
55
|
-
pauseAutoplayVideos();
|
|
56
|
-
localStorage.setItem(STORAGE_KEY, "true");
|
|
57
|
-
}
|
|
58
|
-
function deactivate() {
|
|
59
|
-
enabled = false;
|
|
60
|
-
removeStyles();
|
|
61
|
-
resumePausedVideos();
|
|
62
|
-
localStorage.removeItem(STORAGE_KEY);
|
|
63
|
-
}
|
|
64
|
-
return {
|
|
65
|
-
id: "animation-stop",
|
|
66
|
-
name: () => "Stop Animations",
|
|
67
|
-
description: "Pause all animations, transitions, and auto-playing videos (WCAG 2.3.1)",
|
|
68
|
-
icon: "animation-stop",
|
|
69
|
-
category: "visual",
|
|
70
|
-
activate,
|
|
71
|
-
deactivate,
|
|
72
|
-
getState: () => ({
|
|
73
|
-
id: "animation-stop",
|
|
74
|
-
enabled
|
|
75
|
-
}),
|
|
76
|
-
setState: (state) => {
|
|
77
|
-
if (state.enabled) {
|
|
78
|
-
activate();
|
|
79
|
-
} else {
|
|
80
|
-
deactivate();
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
export {
|
|
86
|
-
createAnimationStopModule as default
|
|
87
|
-
};
|
|
88
|
-
//# sourceMappingURL=animation-stop-DXebPS8D.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"animation-stop-DXebPS8D.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 let pausedVideos: HTMLVideoElement[] = [];\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 `;\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 {\n video.play();\n } catch {\n // Video may have been removed from DOM\n }\n });\n pausedVideos = [];\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 function activate() {\n enabled = true;\n injectStyles();\n pauseAutoplayVideos();\n localStorage.setItem(STORAGE_KEY, 'true');\n }\n\n function deactivate() {\n enabled = false;\n removeStyles();\n resumePausedVideos();\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":"AAEA,SAAwB,4BAA2C;AACjE,MAAI,UAAU;AACd,QAAM,WAAW;AACjB,QAAM,cAAc;AACpB,MAAI,eAAmC,CAAA;AAEvC,WAAS,YAAoB;AAC3B,WAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYT;AAEA,WAAS,sBAAsB;AAC7B,UAAM,SAAS,SAAS,iBAAmC,wBAAwB;AACnF,mBAAe,CAAA;AACf,WAAO,QAAQ,CAAC,UAAU;AACxB,UAAI,CAAC,MAAM,QAAQ;AACjB,cAAM,MAAA;AACN,qBAAa,KAAK,KAAK;AAAA,MACzB;AAAA,IACF,CAAC;AAAA,EACH;AAEA,WAAS,qBAAqB;AAC5B,iBAAa,QAAQ,CAAC,UAAU;AAC9B,UAAI;AACF,cAAM,KAAA;AAAA,MACR,QAAQ;AAAA,MAER;AAAA,IACF,CAAC;AACD,mBAAe,CAAA;AAAA,EACjB;AAEA,WAAS,eAAe;AACtB,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,UAAA;AAAA,EACxB;AAEA,WAAS,eAAe;AACtB,UAAM,UAAU,SAAS,eAAe,QAAQ;AAChD,aAAS,OAAA;AAAA,EACX;AAEA,WAAS,WAAW;AAClB,cAAU;AACV,iBAAA;AACA,wBAAA;AACA,iBAAa,QAAQ,aAAa,MAAM;AAAA,EAC1C;AAEA,WAAS,aAAa;AACpB,cAAU;AACV,iBAAA;AACA,uBAAA;AACA,iBAAa,WAAW,WAAW;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;AAAA,MAC7B,IAAI;AAAA,MACJ;AAAA,IAAA;AAAA,IAEF,UAAU,CAAC,UAAgC;AACzC,UAAI,MAAM,SAAS;AACjB,iBAAA;AAAA,MACF,OAAO;AACL,mBAAA;AAAA,MACF;AAAA,IACF;AAAA,EAAA;AAEJ;"}
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
function createHideImagesModule() {
|
|
2
|
-
let enabled = false;
|
|
3
|
-
const STYLE_ID = "accessify-hide-images";
|
|
4
|
-
const CSS = `
|
|
5
|
-
img, picture, svg:not([aria-hidden="true"]), video, canvas,
|
|
6
|
-
[role="img"], figure {
|
|
7
|
-
opacity: 0 !important;
|
|
8
|
-
pointer-events: none !important;
|
|
9
|
-
}
|
|
10
|
-
`;
|
|
11
|
-
function activate() {
|
|
12
|
-
enabled = true;
|
|
13
|
-
let styleEl = document.getElementById(STYLE_ID);
|
|
14
|
-
if (!styleEl) {
|
|
15
|
-
styleEl = document.createElement("style");
|
|
16
|
-
styleEl.id = STYLE_ID;
|
|
17
|
-
document.head.appendChild(styleEl);
|
|
18
|
-
}
|
|
19
|
-
styleEl.textContent = CSS;
|
|
20
|
-
}
|
|
21
|
-
function deactivate() {
|
|
22
|
-
enabled = false;
|
|
23
|
-
document.getElementById(STYLE_ID)?.remove();
|
|
24
|
-
}
|
|
25
|
-
return {
|
|
26
|
-
id: "hide-images",
|
|
27
|
-
name: () => "Hide Images",
|
|
28
|
-
description: "Remove images for distraction-free reading",
|
|
29
|
-
icon: "hide-images",
|
|
30
|
-
category: "visual",
|
|
31
|
-
activate,
|
|
32
|
-
deactivate,
|
|
33
|
-
getState: () => ({ id: "hide-images", enabled })
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
export {
|
|
37
|
-
createHideImagesModule as default
|
|
38
|
-
};
|
|
39
|
-
//# sourceMappingURL=hide-images-DJwmsV2C.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"hide-images-DJwmsV2C.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\n const CSS = `\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\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 }\n\n function deactivate() {\n enabled = false;\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;AAEjB,QAAM,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQZ,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;AAAA,EACxB;AAEA,WAAS,aAAa;AACpB,cAAU;AACV,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;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"keyboard-nav-C9zLmfrJ.js","sources":["../src/features/keyboard-nav.ts"],"sourcesContent":["import type { FeatureModule, FeatureState } from '../types';\nimport { getCurrentWidgetLang, t } from '../i18n/index';\n\nexport default function createKeyboardNavModule(): FeatureModule {\n let enabled = false;\n const STYLE_ID = 'accessify-keyboard-nav';\n const SKIP_LINK_ID = 'accessify-skip-link';\n const OVERLAY_ID = 'accessify-keyboard-help';\n const STORAGE_KEY = 'accessify-keyboard-nav';\n\n let headingIndex = -1;\n let landmarkIndex = -1;\n let headings: HTMLElement[] = [];\n let landmarks: HTMLElement[] = [];\n let helpOverlayVisible = false;\n\n function lang(): string {\n return getCurrentWidgetLang();\n }\n\n // --- Skip-to-content link ---\n\n function findMainContent(): HTMLElement | null {\n return (\n document.querySelector('main') ||\n document.getElementById('content') ||\n document.getElementById('main-content') ||\n document.querySelector('[role=\"main\"]') ||\n document.querySelector('h1')\n );\n }\n\n function hasSkipLink(): boolean {\n const links = document.querySelectorAll('a[href^=\"#\"]');\n for (const link of links) {\n const text = (link as HTMLElement).textContent?.toLowerCase() || '';\n if (text.includes('skip') || text.includes('main content') || text.includes('navigation')) {\n return true;\n }\n }\n return false;\n }\n\n function injectSkipLink() {\n if (hasSkipLink()) return;\n const target = findMainContent();\n if (!target) return;\n\n // Ensure target has an ID for the skip link to reference\n if (!target.id) {\n target.id = 'accessify-main-content';\n }\n\n const skipLink = document.createElement('a');\n skipLink.id = SKIP_LINK_ID;\n skipLink.href = `#${target.id}`;\n skipLink.textContent = t('keyboard.skipToContent', lang());\n skipLink.addEventListener('click', (e) => {\n e.preventDefault();\n target.setAttribute('tabindex', '-1');\n target.focus();\n target.scrollIntoView({ behavior: 'smooth', block: 'start' });\n });\n\n document.body.insertBefore(skipLink, document.body.firstChild);\n }\n\n function removeSkipLink() {\n const skipLink = document.getElementById(SKIP_LINK_ID);\n skipLink?.remove();\n }\n\n // --- Heading navigation (Alt+H) ---\n\n function collectHeadings() {\n headings = Array.from(\n document.querySelectorAll<HTMLElement>('h1, h2, h3, h4, h5, h6')\n ).filter((el) => {\n // Only visible headings\n const style = window.getComputedStyle(el);\n return style.display !== 'none' && style.visibility !== 'hidden';\n });\n }\n\n function navigateToNextHeading() {\n collectHeadings();\n if (headings.length === 0) return;\n headingIndex = (headingIndex + 1) % headings.length;\n const heading = headings[headingIndex];\n heading.setAttribute('tabindex', '-1');\n heading.focus();\n heading.scrollIntoView({ behavior: 'smooth', block: 'center' });\n }\n\n // --- Landmark navigation (Alt+L) ---\n\n function collectLandmarks() {\n const selectors = [\n 'header, [role=\"banner\"]',\n 'nav, [role=\"navigation\"]',\n 'main, [role=\"main\"]',\n 'aside, [role=\"complementary\"]',\n '[role=\"search\"]',\n '[role=\"form\"]',\n 'footer, [role=\"contentinfo\"]',\n '[role=\"region\"][aria-label]',\n ];\n landmarks = Array.from(\n document.querySelectorAll<HTMLElement>(selectors.join(', '))\n ).filter((el) => {\n const style = window.getComputedStyle(el);\n return style.display !== 'none' && style.visibility !== 'hidden';\n });\n }\n\n function navigateToNextLandmark() {\n collectLandmarks();\n if (landmarks.length === 0) return;\n landmarkIndex = (landmarkIndex + 1) % landmarks.length;\n const landmark = landmarks[landmarkIndex];\n landmark.setAttribute('tabindex', '-1');\n landmark.focus();\n landmark.scrollIntoView({ behavior: 'smooth', block: 'center' });\n }\n\n // --- Keyboard shortcut help overlay (Alt+K) ---\n\n function toggleHelpOverlay() {\n const existing = document.getElementById(OVERLAY_ID);\n if (existing) {\n existing.remove();\n helpOverlayVisible = false;\n return;\n }\n\n helpOverlayVisible = true;\n const overlay = document.createElement('div');\n overlay.id = OVERLAY_ID;\n overlay.setAttribute('role', 'dialog');\n overlay.setAttribute('aria-label', t('keyboard.shortcuts', lang()));\n overlay.setAttribute('aria-modal', 'true');\n\n overlay.innerHTML = `\n <div style=\"\n position: fixed; inset: 0; background: rgba(0,0,0,0.6);\n display: flex; align-items: center; justify-content: center;\n z-index: 2147483647;\n \">\n <div style=\"\n background: #fff; color: #222; border-radius: 12px; padding: 32px;\n max-width: 480px; width: 90%; max-height: 80vh; overflow-y: auto;\n box-shadow: 0 8px 32px rgba(0,0,0,0.3); font-family: system-ui, sans-serif;\n \" role=\"document\">\n <h2 style=\"margin: 0 0 16px; font-size: 20px; color: #1a73e8;\">${t('keyboard.shortcuts', lang())}</h2>\n <table style=\"width: 100%; border-collapse: collapse; font-size: 14px;\">\n <thead>\n <tr style=\"border-bottom: 2px solid #e0e0e0;\">\n <th style=\"text-align: left; padding: 8px 12px;\">${t('keyboard.shortcut', lang())}</th>\n <th style=\"text-align: left; padding: 8px 12px;\">${t('keyboard.action', lang())}</th>\n </tr>\n </thead>\n <tbody>\n <tr style=\"border-bottom: 1px solid #f0f0f0;\">\n <td style=\"padding: 8px 12px;\"><kbd style=\"background:#f5f5f5;padding:2px 8px;border-radius:4px;border:1px solid #ccc;font-family:monospace;\">Alt + H</kbd></td>\n <td style=\"padding: 8px 12px;\">${t('keyboard.nextHeading', lang())}</td>\n </tr>\n <tr style=\"border-bottom: 1px solid #f0f0f0;\">\n <td style=\"padding: 8px 12px;\"><kbd style=\"background:#f5f5f5;padding:2px 8px;border-radius:4px;border:1px solid #ccc;font-family:monospace;\">Alt + L</kbd></td>\n <td style=\"padding: 8px 12px;\">${t('keyboard.nextLandmark', lang())}</td>\n </tr>\n <tr style=\"border-bottom: 1px solid #f0f0f0;\">\n <td style=\"padding: 8px 12px;\"><kbd style=\"background:#f5f5f5;padding:2px 8px;border-radius:4px;border:1px solid #ccc;font-family:monospace;\">Alt + K</kbd></td>\n <td style=\"padding: 8px 12px;\">${t('keyboard.toggleHelp', lang())}</td>\n </tr>\n <tr style=\"border-bottom: 1px solid #f0f0f0;\">\n <td style=\"padding: 8px 12px;\"><kbd style=\"background:#f5f5f5;padding:2px 8px;border-radius:4px;border:1px solid #ccc;font-family:monospace;\">Tab</kbd></td>\n <td style=\"padding: 8px 12px;\">${t('keyboard.nextFocusable', lang())}</td>\n </tr>\n <tr>\n <td style=\"padding: 8px 12px;\"><kbd style=\"background:#f5f5f5;padding:2px 8px;border-radius:4px;border:1px solid #ccc;font-family:monospace;\">Escape</kbd></td>\n <td style=\"padding: 8px 12px;\">${t('keyboard.closeOverlay', lang())}</td>\n </tr>\n </tbody>\n </table>\n <button style=\"\n margin-top: 20px; padding: 8px 24px; background: #1a73e8; color: #fff;\n border: none; border-radius: 6px; cursor: pointer; font-size: 14px;\n \" id=\"accessify-keyboard-help-close\">${t('widget.close', lang())}</button>\n </div>\n </div>\n `;\n\n document.body.appendChild(overlay);\n\n // Focus management for the dialog\n const closeBtn = document.getElementById('accessify-keyboard-help-close');\n closeBtn?.focus();\n closeBtn?.addEventListener('click', () => {\n overlay.remove();\n helpOverlayVisible = false;\n });\n }\n\n // --- Tabindex injection for interactive elements ---\n\n function injectTabindex() {\n const selectors = [\n 'a[href]',\n 'button',\n 'input',\n 'select',\n 'textarea',\n '[role=\"button\"]',\n '[role=\"link\"]',\n '[role=\"checkbox\"]',\n '[role=\"radio\"]',\n '[role=\"tab\"]',\n '[role=\"menuitem\"]',\n '[role=\"switch\"]',\n '[contenteditable=\"true\"]',\n 'summary',\n ];\n\n const elements = document.querySelectorAll<HTMLElement>(selectors.join(', '));\n elements.forEach((el) => {\n // Only add tabindex if the element is not already focusable via native mechanism\n // and does not already have an explicit tabindex\n if (!el.hasAttribute('tabindex') && !isNativelyFocusable(el)) {\n el.setAttribute('tabindex', '0');\n el.dataset.accessifyTabindex = 'true';\n }\n });\n }\n\n function removeInjectedTabindex() {\n const elements = document.querySelectorAll<HTMLElement>('[data-accessify-tabindex=\"true\"]');\n elements.forEach((el) => {\n el.removeAttribute('tabindex');\n delete el.dataset.accessifyTabindex;\n });\n }\n\n function isNativelyFocusable(el: HTMLElement): boolean {\n const tag = el.tagName.toLowerCase();\n if (['a', 'button', 'input', 'select', 'textarea'].includes(tag)) {\n return true;\n }\n if (tag === 'a' && !(el as HTMLAnchorElement).href) {\n return false;\n }\n return false;\n }\n\n // --- Keyboard event handler ---\n\n function handleKeyDown(e: KeyboardEvent) {\n if (!enabled) return;\n\n // Close help overlay on Escape\n if (e.key === 'Escape' && helpOverlayVisible) {\n const overlay = document.getElementById(OVERLAY_ID);\n overlay?.remove();\n helpOverlayVisible = false;\n return;\n }\n\n if (!e.altKey) return;\n\n switch (e.key.toLowerCase()) {\n case 'h':\n e.preventDefault();\n navigateToNextHeading();\n break;\n case 'l':\n e.preventDefault();\n navigateToNextLandmark();\n break;\n case 'k':\n e.preventDefault();\n toggleHelpOverlay();\n break;\n }\n }\n\n // --- Styles for skip link ---\n\n function getStyles(): string {\n return `\n /* accessify keyboard navigation */\n #${SKIP_LINK_ID} {\n position: fixed;\n top: -100px;\n left: 50%;\n transform: translateX(-50%);\n background: #1a73e8;\n color: #fff;\n padding: 12px 24px;\n border-radius: 0 0 8px 8px;\n font-size: 16px;\n font-family: system-ui, sans-serif;\n text-decoration: none;\n z-index: 2147483647;\n transition: top 0.2s ease;\n box-shadow: 0 2px 8px rgba(0,0,0,0.3);\n }\n #${SKIP_LINK_ID}:focus {\n top: 0;\n outline: 3px solid #ffdd00;\n outline-offset: 2px;\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 // --- Module lifecycle ---\n\n function activate() {\n enabled = true;\n headingIndex = -1;\n landmarkIndex = -1;\n\n injectStyles();\n injectSkipLink();\n injectTabindex();\n document.addEventListener('keydown', handleKeyDown);\n localStorage.setItem(STORAGE_KEY, 'true');\n }\n\n function deactivate() {\n enabled = false;\n helpOverlayVisible = false;\n\n document.removeEventListener('keydown', handleKeyDown);\n removeSkipLink();\n removeInjectedTabindex();\n removeStyles();\n\n const overlay = document.getElementById(OVERLAY_ID);\n overlay?.remove();\n\n localStorage.removeItem(STORAGE_KEY);\n }\n\n return {\n id: 'keyboard-nav',\n name: () => 'Keyboard Navigation',\n description: 'Enhanced keyboard navigation with skip links, heading/landmark jumping, and shortcut help',\n icon: 'keyboard-nav',\n category: 'motor',\n activate,\n deactivate,\n getState: (): FeatureState => ({\n id: 'keyboard-nav',\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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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"page-structure-GqhCQsl3.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\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 { 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 { 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 { background: #2a2a2e; }\n #${PANEL_ID} .ps-close { color: #aaa; }\n #${PANEL_ID} .ps-close:hover { background: #2a2a2e; }\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 function activate() {\n enabled = true;\n buildPanel();\n }\n\n function deactivate() {\n enabled = false;\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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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"text-simplify-C9gzE3t0.js","sources":["../src/features/text-simplify.ts"],"sourcesContent":["// ---------------------------------------------------------------------------\n// Accessify – Text Simplification Feature (Safe Replace Engine)\n// ---------------------------------------------------------------------------\n// Phase 1: Manifest-based precomputed simplification\n// 1. Fetch manifest from /v1/manifest for current page\n// 2. Apply cached variants instantly via leaf-node replacement\n// 3. Live-fallback only for blocks not in manifest\n// Phase 2: Layout-safe replacement with guards + observers\n// ---------------------------------------------------------------------------\n\nimport type { FeatureModule, FeatureState, AIService } from '../types';\n\ntype SimplifyLevel = 'einfache' | 'leichte';\n\ninterface TextSimplifyState {\n level: SimplifyLevel;\n}\n\ninterface SavedParagraph {\n el: HTMLElement;\n originalHtml: string;\n ancestorPatches: AncestorPatch[];\n}\n\ninterface AncestorPatch {\n el: HTMLElement;\n property: string;\n originalValue: string;\n}\n\ninterface ManifestBlock {\n selector: string;\n blockHash: string;\n result: string;\n}\n\ninterface RuntimeManifest {\n url: string;\n siteKey: string;\n feature: string;\n variant: string;\n blocks: ManifestBlock[];\n generatedAt: string;\n contentHash: string;\n}\n\nexport default function createTextSimplifyModule(\n aiService?: AIService,\n lang: string = 'de',\n): FeatureModule {\n let enabled = false;\n let level: SimplifyLevel = 'einfache';\n let savedParagraphs: SavedParagraph[] = [];\n let abortController: AbortController | null = null;\n let overlayEl: HTMLDivElement | null = null;\n let skippedBlocks = 0;\n\n let resizeObserver: ResizeObserver | null = null;\n let mutationObserver: MutationObserver | null = null;\n\n // Map from element to pre-replacement dimensions for resize revert checks\n const preReplaceDimensions = new Map<HTMLElement, { scrollHeight: number; clientHeight: number }>();\n\n const STORAGE_KEY = 'accessify-text-simplify';\n const SIMPLIFIED_ATTR = 'data-accessify-simplified';\n const ORIGINAL_ATTR = 'data-accessify-original';\n const BLOCK_HASH_ATTR = 'data-accessify-block-hash';\n\n // -------------------------------------------------------------------------\n // Client-side IndexedDB cache for instant reload\n // -------------------------------------------------------------------------\n\n const CLIENT_CACHE_DB = 'accessify-simplify-cache';\n const CLIENT_CACHE_STORE = 'blocks';\n const CLIENT_CACHE_VERSION = 1;\n\n function openClientCache(): Promise<IDBDatabase> {\n return new Promise((resolve, reject) => {\n const req = indexedDB.open(CLIENT_CACHE_DB, CLIENT_CACHE_VERSION);\n req.onupgradeneeded = () => {\n const db = req.result;\n if (!db.objectStoreNames.contains(CLIENT_CACHE_STORE)) {\n db.createObjectStore(CLIENT_CACHE_STORE);\n }\n };\n req.onsuccess = () => resolve(req.result);\n req.onerror = () => reject(req.error);\n });\n }\n\n async function getClientCached(key: string): Promise<string | null> {\n try {\n const db = await openClientCache();\n return new Promise((resolve) => {\n const tx = db.transaction(CLIENT_CACHE_STORE, 'readonly');\n const store = tx.objectStore(CLIENT_CACHE_STORE);\n const req = store.get(key);\n req.onsuccess = () => resolve(req.result ?? null);\n req.onerror = () => resolve(null);\n });\n } catch { return null; }\n }\n\n async function setClientCached(key: string, value: string): Promise<void> {\n try {\n const db = await openClientCache();\n const tx = db.transaction(CLIENT_CACHE_STORE, 'readwrite');\n tx.objectStore(CLIENT_CACHE_STORE).put(value, key);\n } catch { /* ignore */ }\n }\n\n function clientCacheKey(blockHash: string): string {\n const pageUrl = window.location.origin + window.location.pathname;\n return `${pageUrl}:simplify:${level}:${blockHash}`;\n }\n\n // -------------------------------------------------------------------------\n // Resolve config from the script tag or global config\n // -------------------------------------------------------------------------\n\n function getConfig(): { siteKey?: string; proxyUrl?: string } {\n const script = document.querySelector('script[data-site-key]');\n const siteKey = script?.getAttribute('data-site-key') ||\n (window as any).Accessify?.config?.siteKey || '';\n const proxyUrl = script?.getAttribute('data-proxy-url') ||\n (window as any).Accessify?.config?.proxyUrl || '';\n return { siteKey: siteKey || undefined, proxyUrl: proxyUrl || undefined };\n }\n\n // -------------------------------------------------------------------------\n // Find text-heavy elements on the page\n // -------------------------------------------------------------------------\n\n function getTextElements(): HTMLElement[] {\n const selectors = 'p, li, td, th, blockquote, figcaption, .hero-subtitle, h1, h2, h3, h4, h5, h6';\n const elements: HTMLElement[] = [];\n const seen = new Set<HTMLElement>();\n\n document.querySelectorAll(selectors).forEach((el) => {\n const htmlEl = el as HTMLElement;\n if (htmlEl.closest('#accessify-root')) return;\n if (htmlEl.offsetParent === null && htmlEl.style.display !== 'fixed') return;\n const text = htmlEl.textContent?.trim() || '';\n if (text.length < 20) return;\n if (seen.has(htmlEl)) return;\n let dominated = false;\n for (const s of seen) {\n if (s.contains(htmlEl)) { dominated = true; break; }\n }\n if (dominated) return;\n seen.add(htmlEl);\n elements.push(htmlEl);\n });\n\n return elements;\n }\n\n // -------------------------------------------------------------------------\n // Block hash — DJB2, same as server side\n // -------------------------------------------------------------------------\n\n function hashText(text: string): string {\n const normalized = text.replace(/\\s+/g, ' ').trim().toLowerCase();\n let hash = 5381;\n for (let i = 0; i < normalized.length; i++) {\n hash = ((hash << 5) + hash + normalized.charCodeAt(i)) & 0x7fffffff;\n }\n return hash.toString(36);\n }\n\n // -------------------------------------------------------------------------\n // Leaf text node replacement — preserves inline markup\n // -------------------------------------------------------------------------\n\n function getLeafTextNodes(root: Node): Text[] {\n const textNodes: Text[] = [];\n const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, null);\n let node: Text | null;\n while ((node = walker.nextNode() as Text | null)) {\n const trimmed = node.nodeValue?.trim();\n if (trimmed && trimmed.length > 0) {\n textNodes.push(node);\n }\n }\n return textNodes;\n }\n\n /**\n * Replace leaf text nodes within an element with simplified text.\n * The simplified text is distributed across existing text nodes proportionally,\n * preserving the surrounding inline markup structure.\n *\n * Strategy: if there is only one meaningful text node, replace it directly.\n * If there are multiple text nodes, split the simplified text into sentences\n * and distribute them proportionally across the existing text nodes.\n */\n function replaceLeafTextNodes(el: HTMLElement, simplifiedText: string): void {\n const textNodes = getLeafTextNodes(el);\n if (textNodes.length === 0) return;\n\n if (textNodes.length === 1) {\n // Single text node — straightforward replacement\n textNodes[0].nodeValue = simplifiedText;\n return;\n }\n\n // Multiple text nodes: distribute simplified text proportionally\n // Split simplified text into segments by sentence boundaries\n const sentences = simplifiedText.match(/[^.!?]+[.!?]*\\s*/g) || [simplifiedText];\n\n // Calculate original character weight per text node\n const totalOriginalLen = textNodes.reduce((sum, n) => sum + (n.nodeValue?.trim().length || 0), 0);\n\n if (totalOriginalLen === 0) {\n // Fallback: put all simplified text in first node, clear others\n textNodes[0].nodeValue = simplifiedText;\n for (let i = 1; i < textNodes.length; i++) {\n textNodes[i].nodeValue = '';\n }\n return;\n }\n\n // Distribute sentences across text nodes based on character weight\n let sentenceIndex = 0;\n for (let i = 0; i < textNodes.length; i++) {\n const nodeWeight = (textNodes[i].nodeValue?.trim().length || 0) / totalOriginalLen;\n const sentenceCount = Math.max(1, Math.round(nodeWeight * sentences.length));\n\n const chunk = sentences.slice(sentenceIndex, sentenceIndex + sentenceCount).join('');\n textNodes[i].nodeValue = chunk || '';\n sentenceIndex += sentenceCount;\n }\n\n // If there are leftover sentences, append to the last non-empty node\n if (sentenceIndex < sentences.length) {\n const lastNode = textNodes[textNodes.length - 1];\n lastNode.nodeValue = (lastNode.nodeValue || '') + sentences.slice(sentenceIndex).join('');\n }\n }\n\n // -------------------------------------------------------------------------\n // Layout Guard — detect overflow/clipping after replacement\n // -------------------------------------------------------------------------\n\n function measureElement(el: HTMLElement): { scrollHeight: number; clientHeight: number } {\n return { scrollHeight: el.scrollHeight, clientHeight: el.clientHeight };\n }\n\n /**\n * Check if an element or its ancestors have overflow:hidden with fixed height\n * that would clip expanded content.\n */\n function findClippingAncestor(el: HTMLElement): HTMLElement | null {\n let current: HTMLElement | null = el;\n while (current && current !== document.body) {\n const style = getComputedStyle(current);\n const overflow = style.overflow + ' ' + style.overflowY;\n const hasHiddenOverflow = overflow.includes('hidden') || overflow.includes('clip');\n const hasFixedHeight = style.maxHeight !== 'none' || (\n style.height !== 'auto' && style.height !== '' && !style.height.includes('%')\n );\n if (hasHiddenOverflow && hasFixedHeight) {\n return current;\n }\n current = current.parentElement;\n }\n return null;\n }\n\n /**\n * Check layout safety after a replacement. Returns true if safe, false if clipping detected.\n * Attempts minimal ancestor patches to resolve clipping before giving up.\n */\n function checkLayoutSafety(el: HTMLElement): { safe: boolean; patches: AncestorPatch[] } {\n const patches: AncestorPatch[] = [];\n\n // Check the element itself for overflow\n const after = measureElement(el);\n const isElementOverflowing = after.scrollHeight > after.clientHeight + 2; // 2px tolerance\n\n if (!isElementOverflowing) {\n // Also check for clipping ancestors\n const clipAncestor = findClippingAncestor(el);\n if (!clipAncestor) return { safe: true, patches: [] };\n\n const ancestorAfter = measureElement(clipAncestor);\n if (ancestorAfter.scrollHeight <= ancestorAfter.clientHeight + 2) {\n return { safe: true, patches: [] };\n }\n\n // Try patching the ancestor\n return tryAncestorPatches(clipAncestor, patches);\n }\n\n // Element itself is overflowing — check if a parent is clipping it\n const clipAncestor = findClippingAncestor(el);\n if (!clipAncestor) {\n // No clipping ancestor — overflow is fine (content just grows naturally)\n return { safe: true, patches: [] };\n }\n\n return tryAncestorPatches(clipAncestor, patches);\n }\n\n function tryAncestorPatches(\n ancestor: HTMLElement,\n patches: AncestorPatch[],\n ): { safe: boolean; patches: AncestorPatch[] } {\n const style = getComputedStyle(ancestor);\n\n // Try removing max-height\n if (style.maxHeight !== 'none') {\n patches.push({\n el: ancestor,\n property: 'maxHeight',\n originalValue: ancestor.style.maxHeight,\n });\n ancestor.style.maxHeight = 'none';\n }\n\n // Try setting overflow to visible\n if (style.overflow.includes('hidden') || style.overflowY.includes('hidden')) {\n patches.push({\n el: ancestor,\n property: 'overflow',\n originalValue: ancestor.style.overflow,\n });\n ancestor.style.overflow = 'visible';\n }\n\n // Re-check after patches\n const afterPatch = measureElement(ancestor);\n if (afterPatch.scrollHeight <= afterPatch.clientHeight + 2) {\n return { safe: true, patches };\n }\n\n // Patches didn't fix it — revert them\n revertPatches(patches);\n return { safe: false, patches: [] };\n }\n\n function revertPatches(patches: AncestorPatch[]) {\n for (const patch of patches) {\n (patch.el.style as any)[patch.property] = patch.originalValue;\n }\n }\n\n // -------------------------------------------------------------------------\n // Observers — ResizeObserver + MutationObserver\n // -------------------------------------------------------------------------\n\n function setupResizeObserver() {\n if (resizeObserver) return;\n\n resizeObserver = new ResizeObserver((entries) => {\n for (const entry of entries) {\n const el = entry.target as HTMLElement;\n if (!el.hasAttribute(SIMPLIFIED_ATTR)) continue;\n\n const saved = savedParagraphs.find((sp) => sp.el === el);\n if (!saved) continue;\n\n // Check if a layout change caused clipping\n const clipAncestor = findClippingAncestor(el);\n if (clipAncestor) {\n const dims = measureElement(clipAncestor);\n if (dims.scrollHeight > dims.clientHeight + 2) {\n // External layout change caused clipping — revert this block\n console.warn(\n '[Accessify] ResizeObserver detected clipping after external layout change, reverting block:',\n el,\n );\n revertSingleBlock(saved);\n skippedBlocks++;\n }\n }\n }\n });\n }\n\n function setupMutationObserver() {\n if (mutationObserver) return;\n\n mutationObserver = new MutationObserver((mutations) => {\n for (const mutation of mutations) {\n // Check if any removed nodes were tracked simplified blocks\n for (const removed of mutation.removedNodes) {\n if (!(removed instanceof HTMLElement)) continue;\n const idx = savedParagraphs.findIndex((sp) => sp.el === removed || removed.contains(sp.el));\n if (idx !== -1) {\n const saved = savedParagraphs[idx];\n // Element was removed from DOM by the page itself — clean up tracking\n resizeObserver?.unobserve(saved.el);\n saved.el.removeAttribute(SIMPLIFIED_ATTR);\n saved.el.removeAttribute(ORIGINAL_ATTR);\n saved.el.removeAttribute(BLOCK_HASH_ATTR);\n revertPatches(saved.ancestorPatches);\n savedParagraphs.splice(idx, 1);\n }\n }\n\n // Check if a simplified element's content was changed externally\n if (mutation.type === 'characterData' || mutation.type === 'childList') {\n const target = mutation.target instanceof HTMLElement\n ? mutation.target\n : mutation.target.parentElement;\n if (!target) continue;\n\n const simplifiedEl = target.closest(`[${SIMPLIFIED_ATTR}]`) as HTMLElement | null;\n if (simplifiedEl) {\n const saved = savedParagraphs.find((sp) => sp.el === simplifiedEl);\n if (saved && !simplifiedEl.hasAttribute('data-accessify-replacing')) {\n // External mutation on a simplified block — clean up\n console.warn(\n '[Accessify] External mutation detected on simplified block, cleaning up tracking:',\n simplifiedEl,\n );\n resizeObserver?.unobserve(saved.el);\n saved.el.removeAttribute(SIMPLIFIED_ATTR);\n saved.el.removeAttribute(ORIGINAL_ATTR);\n saved.el.removeAttribute(BLOCK_HASH_ATTR);\n revertPatches(saved.ancestorPatches);\n savedParagraphs = savedParagraphs.filter((sp) => sp.el !== simplifiedEl);\n }\n }\n }\n }\n });\n\n mutationObserver.observe(document.body, {\n childList: true,\n subtree: true,\n characterData: true,\n });\n }\n\n function disconnectObservers() {\n if (resizeObserver) {\n resizeObserver.disconnect();\n resizeObserver = null;\n }\n if (mutationObserver) {\n mutationObserver.disconnect();\n mutationObserver = null;\n }\n preReplaceDimensions.clear();\n }\n\n // -------------------------------------------------------------------------\n // Safe replacement — leaf nodes + layout guard\n // -------------------------------------------------------------------------\n\n function safeReplace(\n el: HTMLElement,\n simplifiedText: string,\n blockHash?: string,\n ): boolean {\n // Save original\n const originalHtml = el.innerHTML;\n\n // Guard flag to prevent MutationObserver from treating our own replacement as external\n el.setAttribute('data-accessify-replacing', 'true');\n\n // Perform leaf text node replacement (preserves inline markup)\n replaceLeafTextNodes(el, simplifiedText);\n\n // Check layout safety\n const { safe, patches } = checkLayoutSafety(el);\n\n if (!safe) {\n // Revert — restore original HTML\n el.innerHTML = originalHtml;\n el.removeAttribute('data-accessify-replacing');\n console.warn(\n '[Accessify] Block skipped — layout clipping detected after replacement:',\n el.textContent?.slice(0, 60),\n );\n skippedBlocks++;\n return false;\n }\n\n // Replacement is safe — finalize\n el.removeAttribute('data-accessify-replacing');\n el.setAttribute(SIMPLIFIED_ATTR, 'true');\n el.setAttribute(ORIGINAL_ATTR, originalHtml);\n if (blockHash) {\n el.setAttribute(BLOCK_HASH_ATTR, blockHash);\n }\n\n savedParagraphs.push({ el, originalHtml, ancestorPatches: patches });\n\n // Observe for future layout changes\n resizeObserver?.observe(el);\n\n return true;\n }\n\n /**\n * Revert a single block — used by ResizeObserver when external layout changes cause clipping.\n */\n function revertSingleBlock(saved: SavedParagraph) {\n saved.el.innerHTML = saved.originalHtml;\n saved.el.removeAttribute(SIMPLIFIED_ATTR);\n saved.el.removeAttribute(ORIGINAL_ATTR);\n saved.el.removeAttribute(BLOCK_HASH_ATTR);\n revertPatches(saved.ancestorPatches);\n resizeObserver?.unobserve(saved.el);\n savedParagraphs = savedParagraphs.filter((sp) => sp.el !== saved.el);\n }\n\n // -------------------------------------------------------------------------\n // Fetch manifest from API\n // -------------------------------------------------------------------------\n\n async function fetchManifest(siteKey: string, proxyUrl?: string): Promise<RuntimeManifest | null> {\n const base = proxyUrl || 'https://accessify-api.accessify.workers.dev';\n const pageUrl = encodeURIComponent(window.location.origin + window.location.pathname);\n const url = `${base}/v1/manifest?siteKey=${siteKey}&url=${pageUrl}&feature=simplify&variant=${level}`;\n\n try {\n const res = await fetch(url);\n if (!res.ok) return null;\n const data = await res.json() as RuntimeManifest;\n if (!data.blocks?.length) return null;\n return data;\n } catch {\n return null;\n }\n }\n\n // -------------------------------------------------------------------------\n // Apply manifest blocks to the page (safe replacement)\n // -------------------------------------------------------------------------\n\n function applyManifestBlocks(\n manifest: RuntimeManifest,\n elements: HTMLElement[],\n ): { applied: HTMLElement[]; remaining: HTMLElement[] } {\n const applied: HTMLElement[] = [];\n const remaining: HTMLElement[] = [];\n\n // Build a lookup by block hash\n const byHash = new Map<string, ManifestBlock>();\n for (const block of manifest.blocks) {\n byHash.set(block.blockHash, block);\n }\n\n for (const el of elements) {\n const text = el.textContent?.trim() || '';\n const elHash = hashText(text);\n const cached = byHash.get(elHash);\n\n if (cached?.result) {\n const success = safeReplace(el, cached.result, elHash);\n if (success) {\n applied.push(el);\n } else {\n // Layout guard rejected it — don't send to live fallback either\n remaining.push(el);\n }\n } else {\n remaining.push(el);\n }\n }\n\n return { applied, remaining };\n }\n\n // -------------------------------------------------------------------------\n // Live fallback auto-persist — write back to D1+KV\n // -------------------------------------------------------------------------\n\n function persistToManifest(\n siteKey: string,\n blockHash: string,\n originalText: string,\n simplifiedText: string,\n proxyUrl?: string,\n ): void {\n const base = proxyUrl || 'https://accessify-api.accessify.workers.dev';\n const pageUrl = window.location.origin + window.location.pathname;\n\n // Fire-and-forget POST — non-blocking write-back to D1\n fetch(`${base}/v1/cache/persist`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n siteKey,\n url: pageUrl,\n feature: 'simplify',\n variant: level,\n blockHash,\n originalText,\n result: simplifiedText,\n }),\n }).catch((err) => {\n console.warn('[Accessify] Failed to persist live simplification:', err);\n });\n }\n\n // -------------------------------------------------------------------------\n // Loading overlay on individual elements\n // -------------------------------------------------------------------------\n\n function markLoading(el: HTMLElement) {\n el.style.opacity = '0.5';\n el.style.transition = 'opacity 0.3s ease';\n }\n\n function clearLoading(el: HTMLElement) {\n el.style.opacity = '';\n el.style.transition = '';\n }\n\n // -------------------------------------------------------------------------\n // Global progress indicator\n // -------------------------------------------------------------------------\n\n function showProgress(current: number, total: number, skipped: number = 0) {\n if (!overlayEl) {\n overlayEl = document.createElement('div');\n overlayEl.setAttribute('role', 'status');\n overlayEl.setAttribute('aria-live', 'polite');\n Object.assign(overlayEl.style, {\n position: 'fixed',\n bottom: '80px',\n right: '24px',\n zIndex: '2147483645',\n padding: '10px 18px',\n background: '#1a1a2e',\n color: '#4ea8de',\n borderRadius: '10px',\n boxShadow: '0 4px 20px rgba(0,0,0,0.3)',\n fontFamily: 'system-ui, -apple-system, sans-serif',\n fontSize: '13px',\n fontWeight: '600',\n display: 'flex',\n alignItems: 'center',\n gap: '8px',\n });\n document.body.appendChild(overlayEl);\n }\n\n const pct = Math.round((current / total) * 100);\n const skippedNote = skipped > 0\n ? (lang.startsWith('de') ? ` (${skipped} übersprungen)` : ` (${skipped} skipped)`)\n : '';\n const label = lang.startsWith('de')\n ? `Wird vereinfacht... ${current}/${total} (${pct}%)${skippedNote}`\n : `Simplifying... ${current}/${total} (${pct}%)${skippedNote}`;\n overlayEl.innerHTML = `<span style=\"display:inline-block;width:14px;height:14px;border:2px solid rgba(78,168,222,0.3);border-top-color:#4ea8de;border-radius:50%;animation:accessify-autospin 0.6s linear infinite\"></span> ${label}`;\n\n if (!document.getElementById('accessify-autospin-style')) {\n const style = document.createElement('style');\n style.id = 'accessify-autospin-style';\n style.textContent = '@keyframes accessify-autospin { to { transform: rotate(360deg); } }';\n document.head.appendChild(style);\n }\n }\n\n function showDone(count: number, fromCache: number) {\n if (overlayEl) {\n const cacheNote = fromCache > 0\n ? (lang.startsWith('de') ? ` (${fromCache} aus Cache)` : ` (${fromCache} from cache)`)\n : '';\n const skippedNote = skippedBlocks > 0\n ? (lang.startsWith('de') ? `, ${skippedBlocks} übersprungen` : `, ${skippedBlocks} skipped`)\n : '';\n const label = lang.startsWith('de')\n ? `${count} Textabschnitte vereinfacht${cacheNote}${skippedNote}`\n : `${count} text sections simplified${cacheNote}${skippedNote}`;\n overlayEl.innerHTML = `<span style=\"color:#22c55e\">✓</span> ${label}`;\n overlayEl.style.color = '#22c55e';\n setTimeout(() => removeProgress(), 3000);\n }\n }\n\n function showError(msg: string) {\n if (overlayEl) {\n overlayEl.innerHTML = `<span style=\"color:#ef4444\">✕</span> ${msg}`;\n overlayEl.style.color = '#ef4444';\n setTimeout(() => removeProgress(), 5000);\n }\n }\n\n function removeProgress() {\n overlayEl?.remove();\n overlayEl = null;\n document.getElementById('accessify-autospin-style')?.remove();\n }\n\n // -------------------------------------------------------------------------\n // Core: simplify page — manifest first, live fallback for misses\n // -------------------------------------------------------------------------\n\n async function simplifyPage() {\n const elements = getTextElements();\n if (elements.length === 0) return;\n\n abortController = new AbortController();\n savedParagraphs = [];\n skippedBlocks = 0;\n\n // Set up observers\n setupResizeObserver();\n setupMutationObserver();\n\n const { siteKey, proxyUrl } = getConfig();\n let fromCache = 0;\n let remaining = elements;\n\n // -- Phase 1: Try manifest (instant, no AI calls) -------------------------\n if (siteKey) {\n showProgress(0, elements.length);\n const manifest = await fetchManifest(siteKey, proxyUrl);\n\n if (manifest?.blocks?.length) {\n const result = applyManifestBlocks(manifest, elements);\n fromCache = result.applied.length;\n remaining = result.remaining;\n\n // Populate client cache with manifest results\n for (const block of manifest.blocks) {\n if (block.result) {\n setClientCached(clientCacheKey(block.blockHash), block.result);\n }\n }\n\n if (remaining.length === 0) {\n // All blocks were in the manifest — done instantly\n showDone(fromCache, fromCache);\n return;\n }\n }\n }\n\n // -- Phase 1.5: Check client-side IndexedDB cache --------------------------\n {\n const stillRemaining: HTMLElement[] = [];\n for (const el of remaining) {\n const text = el.textContent?.trim() || '';\n if (text.length < 20) { stillRemaining.push(el); continue; }\n const blockHash = hashText(text);\n const cached = await getClientCached(clientCacheKey(blockHash));\n if (cached) {\n const success = safeReplace(el, cached, blockHash);\n if (success) {\n fromCache++;\n } else {\n stillRemaining.push(el);\n }\n } else {\n stillRemaining.push(el);\n }\n }\n remaining = stillRemaining;\n\n if (remaining.length === 0) {\n showDone(fromCache, fromCache);\n return;\n }\n }\n\n // -- Phase 2: Live fallback for remaining blocks --------------------------\n if (!aiService && remaining.length > 0) {\n if (fromCache > 0) {\n showDone(fromCache, fromCache);\n } else {\n showProgress(0, 1);\n showError(lang.startsWith('de')\n ? 'Kein API-Key konfiguriert'\n : 'No API key configured');\n }\n return;\n }\n\n // Mark remaining elements as loading\n for (const el of remaining) {\n markLoading(el);\n }\n\n const totalElements = elements.length;\n let completed = fromCache;\n let totalSimplified = fromCache;\n\n showProgress(completed, totalElements, skippedBlocks);\n\n for (const el of remaining) {\n if (abortController.signal.aborted) break;\n\n const text = el.textContent?.trim() || '';\n if (text.length < 20) {\n clearLoading(el);\n completed++;\n showProgress(completed, totalElements, skippedBlocks);\n continue;\n }\n\n const blockHash = hashText(text);\n\n try {\n const simplified = await aiService!.simplifyText(text, level, lang);\n if (abortController?.signal.aborted) return;\n\n if (simplified && simplified !== text) {\n const success = safeReplace(el, simplified, blockHash);\n if (success) {\n totalSimplified++;\n\n // Cache client-side for instant reload\n setClientCached(clientCacheKey(blockHash), simplified);\n\n // Auto-persist live result back to manifest (non-blocking)\n if (siteKey) {\n persistToManifest(siteKey, blockHash, text, simplified, proxyUrl);\n }\n }\n }\n } catch (err: any) {\n if (err?.message?.includes('429')) {\n await new Promise((r) => setTimeout(r, 8000));\n try {\n const retry = await aiService!.simplifyText(text, level, lang);\n if (abortController?.signal.aborted) return;\n if (retry && retry !== text) {\n const success = safeReplace(el, retry, blockHash);\n if (success) {\n totalSimplified++;\n setClientCached(clientCacheKey(blockHash), retry);\n if (siteKey) {\n persistToManifest(siteKey, blockHash, text, retry, proxyUrl);\n }\n }\n }\n } catch { /* skip */ }\n } else {\n console.warn('[Accessify] Failed to simplify:', err);\n }\n } finally {\n clearLoading(el);\n completed++;\n if (!abortController?.signal.aborted) {\n showProgress(completed, totalElements, skippedBlocks);\n // Throttle: small delay between requests to respect free-tier rate limits\n await new Promise((r) => setTimeout(r, 1200));\n }\n }\n }\n\n if (!abortController?.signal.aborted) {\n showDone(totalSimplified, fromCache);\n }\n }\n\n // -------------------------------------------------------------------------\n // Restore original text — clean teardown\n // -------------------------------------------------------------------------\n\n function restorePage() {\n // Disconnect observers first to avoid triggering them during restore\n disconnectObservers();\n\n for (const { el, originalHtml, ancestorPatches } of savedParagraphs) {\n el.innerHTML = originalHtml;\n el.removeAttribute(ORIGINAL_ATTR);\n el.removeAttribute(SIMPLIFIED_ATTR);\n el.removeAttribute(BLOCK_HASH_ATTR);\n el.removeAttribute('data-accessify-replacing');\n clearLoading(el);\n\n // Revert any ancestor patches (max-height, overflow changes)\n revertPatches(ancestorPatches);\n }\n savedParagraphs = [];\n skippedBlocks = 0;\n }\n\n // -------------------------------------------------------------------------\n // Preferences\n // -------------------------------------------------------------------------\n\n function loadPreferences() {\n try {\n const saved = localStorage.getItem(STORAGE_KEY);\n if (saved) {\n const parsed = JSON.parse(saved);\n if (parsed.level === 'einfache' || parsed.level === 'leichte') {\n level = parsed.level;\n }\n }\n } catch { /* use defaults */ }\n }\n\n function savePreferences() {\n localStorage.setItem(STORAGE_KEY, JSON.stringify({ level }));\n }\n\n // -------------------------------------------------------------------------\n // Module lifecycle\n // -------------------------------------------------------------------------\n\n function activate() {\n if (enabled) return;\n enabled = true;\n loadPreferences();\n simplifyPage();\n }\n\n function deactivate() {\n enabled = false;\n abortController?.abort();\n abortController = null;\n restorePage();\n removeProgress();\n }\n\n return {\n id: 'text-simplify',\n name: () => (lang.startsWith('de') ? 'Text vereinfachen' : 'Simplify Text'),\n description: lang.startsWith('de')\n ? 'Text in Einfache Sprache umwandeln'\n : 'Simplify text for easier understanding',\n icon: 'simplify-text',\n category: 'ai',\n activate,\n deactivate,\n getState: (): FeatureState => ({\n id: 'text-simplify',\n enabled,\n value: { level } as TextSimplifyState,\n }),\n setState: (newState: Partial<TextSimplifyState>) => {\n if (newState.level && (newState.level === 'einfache' || newState.level === 'leichte')) {\n level = newState.level;\n savePreferences();\n }\n },\n };\n}\n"],"names":["clipAncestor"],"mappings":"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"}
|