accessify-widget 0.2.3 → 0.2.5
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-C6ToNlBr.js +505 -0
- package/dist/animation-stop-C6ToNlBr.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-CsJDqdBW.js → index-eprjFXa3.js} +37 -10
- package/dist/{index-CsJDqdBW.js.map → index-eprjFXa3.js.map} +1 -1
- package/dist/{keyboard-nav-CkAYxUc1.js → keyboard-nav-CAWn30Tw.js} +64 -2
- package/dist/keyboard-nav-CAWn30Tw.js.map +1 -0
- package/dist/{page-structure-yWkBKmwo.js → page-structure-DDjJeVCc.js} +75 -2
- package/dist/page-structure-DDjJeVCc.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 +2 -2
- 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-CkAYxUc1.js.map +0 -1
- package/dist/page-structure-yWkBKmwo.js.map +0 -1
- package/dist/text-simplify-C9gzE3t0.js.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t, g as getCurrentWidgetLang } from "./index-
|
|
1
|
+
import { t, g as getCurrentWidgetLang } from "./index-eprjFXa3.js";
|
|
2
2
|
function createKeyboardNavModule() {
|
|
3
3
|
let enabled = false;
|
|
4
4
|
const STYLE_ID = "accessify-keyboard-nav";
|
|
@@ -10,6 +10,11 @@ function createKeyboardNavModule() {
|
|
|
10
10
|
let headings = [];
|
|
11
11
|
let landmarks = [];
|
|
12
12
|
let helpOverlayVisible = false;
|
|
13
|
+
let mutationObserver = null;
|
|
14
|
+
let debounceTimer = null;
|
|
15
|
+
let originalPushState = null;
|
|
16
|
+
let originalReplaceState = null;
|
|
17
|
+
const boundPopstateHandler = () => scheduleDynamicRescan();
|
|
13
18
|
function lang() {
|
|
14
19
|
return getCurrentWidgetLang();
|
|
15
20
|
}
|
|
@@ -268,6 +273,59 @@ function createKeyboardNavModule() {
|
|
|
268
273
|
const styleEl = document.getElementById(STYLE_ID);
|
|
269
274
|
styleEl?.remove();
|
|
270
275
|
}
|
|
276
|
+
function scheduleDynamicRescan() {
|
|
277
|
+
if (!enabled) return;
|
|
278
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
279
|
+
debounceTimer = setTimeout(() => {
|
|
280
|
+
collectHeadings();
|
|
281
|
+
collectLandmarks();
|
|
282
|
+
headingIndex = -1;
|
|
283
|
+
landmarkIndex = -1;
|
|
284
|
+
injectTabindex();
|
|
285
|
+
}, 300);
|
|
286
|
+
}
|
|
287
|
+
function setupDynamicObserver() {
|
|
288
|
+
if (mutationObserver) return;
|
|
289
|
+
mutationObserver = new MutationObserver(() => scheduleDynamicRescan());
|
|
290
|
+
const target = document.querySelector("main") || document.body;
|
|
291
|
+
mutationObserver.observe(target, { childList: true, subtree: true });
|
|
292
|
+
}
|
|
293
|
+
function teardownDynamicObserver() {
|
|
294
|
+
mutationObserver?.disconnect();
|
|
295
|
+
mutationObserver = null;
|
|
296
|
+
if (debounceTimer) {
|
|
297
|
+
clearTimeout(debounceTimer);
|
|
298
|
+
debounceTimer = null;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
function setupSPAListeners() {
|
|
302
|
+
if (!originalPushState) {
|
|
303
|
+
originalPushState = history.pushState;
|
|
304
|
+
history.pushState = function(...args) {
|
|
305
|
+
originalPushState.apply(this, args);
|
|
306
|
+
scheduleDynamicRescan();
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
if (!originalReplaceState) {
|
|
310
|
+
originalReplaceState = history.replaceState;
|
|
311
|
+
history.replaceState = function(...args) {
|
|
312
|
+
originalReplaceState.apply(this, args);
|
|
313
|
+
scheduleDynamicRescan();
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
window.addEventListener("popstate", boundPopstateHandler);
|
|
317
|
+
}
|
|
318
|
+
function teardownSPAListeners() {
|
|
319
|
+
if (originalPushState) {
|
|
320
|
+
history.pushState = originalPushState;
|
|
321
|
+
originalPushState = null;
|
|
322
|
+
}
|
|
323
|
+
if (originalReplaceState) {
|
|
324
|
+
history.replaceState = originalReplaceState;
|
|
325
|
+
originalReplaceState = null;
|
|
326
|
+
}
|
|
327
|
+
window.removeEventListener("popstate", boundPopstateHandler);
|
|
328
|
+
}
|
|
271
329
|
function activate() {
|
|
272
330
|
enabled = true;
|
|
273
331
|
headingIndex = -1;
|
|
@@ -276,11 +334,15 @@ function createKeyboardNavModule() {
|
|
|
276
334
|
injectSkipLink();
|
|
277
335
|
injectTabindex();
|
|
278
336
|
document.addEventListener("keydown", handleKeyDown);
|
|
337
|
+
setupDynamicObserver();
|
|
338
|
+
setupSPAListeners();
|
|
279
339
|
localStorage.setItem(STORAGE_KEY, "true");
|
|
280
340
|
}
|
|
281
341
|
function deactivate() {
|
|
282
342
|
enabled = false;
|
|
283
343
|
helpOverlayVisible = false;
|
|
344
|
+
teardownDynamicObserver();
|
|
345
|
+
teardownSPAListeners();
|
|
284
346
|
document.removeEventListener("keydown", handleKeyDown);
|
|
285
347
|
removeSkipLink();
|
|
286
348
|
removeInjectedTabindex();
|
|
@@ -313,4 +375,4 @@ function createKeyboardNavModule() {
|
|
|
313
375
|
export {
|
|
314
376
|
createKeyboardNavModule as default
|
|
315
377
|
};
|
|
316
|
-
//# sourceMappingURL=keyboard-nav-
|
|
378
|
+
//# sourceMappingURL=keyboard-nav-CAWn30Tw.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keyboard-nav-CAWn30Tw.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 let mutationObserver: MutationObserver | null = null;\n let debounceTimer: ReturnType<typeof setTimeout> | null = null;\n let originalPushState: typeof history.pushState | null = null;\n let originalReplaceState: typeof history.replaceState | null = null;\n const boundPopstateHandler = () => scheduleDynamicRescan();\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 // --- Dynamic re-scan for SPA navigation + lazy content ---\n\n function scheduleDynamicRescan() {\n if (!enabled) return;\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n // Re-collect headings and landmarks, reset indices\n collectHeadings();\n collectLandmarks();\n headingIndex = -1;\n landmarkIndex = -1;\n // Re-inject tabindex for newly added interactive elements\n injectTabindex();\n }, 300);\n }\n\n function setupDynamicObserver() {\n if (mutationObserver) return;\n mutationObserver = new MutationObserver(() => scheduleDynamicRescan());\n const target = document.querySelector('main') || document.body;\n mutationObserver.observe(target, { childList: true, subtree: true });\n }\n\n function teardownDynamicObserver() {\n mutationObserver?.disconnect();\n mutationObserver = null;\n if (debounceTimer) { clearTimeout(debounceTimer); debounceTimer = null; }\n }\n\n function setupSPAListeners() {\n if (!originalPushState) {\n originalPushState = history.pushState;\n history.pushState = function (...args: Parameters<typeof history.pushState>) {\n originalPushState!.apply(this, args);\n scheduleDynamicRescan();\n };\n }\n if (!originalReplaceState) {\n originalReplaceState = history.replaceState;\n history.replaceState = function (...args: Parameters<typeof history.replaceState>) {\n originalReplaceState!.apply(this, args);\n scheduleDynamicRescan();\n };\n }\n window.addEventListener('popstate', boundPopstateHandler);\n }\n\n function teardownSPAListeners() {\n if (originalPushState) {\n history.pushState = originalPushState;\n originalPushState = null;\n }\n if (originalReplaceState) {\n history.replaceState = originalReplaceState;\n originalReplaceState = null;\n }\n window.removeEventListener('popstate', boundPopstateHandler);\n }\n\n // --- 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 setupDynamicObserver();\n setupSPAListeners();\n localStorage.setItem(STORAGE_KEY, 'true');\n }\n\n function deactivate() {\n enabled = false;\n helpOverlayVisible = false;\n\n teardownDynamicObserver();\n teardownSPAListeners();\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,8 +1,14 @@
|
|
|
1
|
-
import { t, g as getCurrentWidgetLang } from "./index-
|
|
1
|
+
import { t, g as getCurrentWidgetLang } from "./index-eprjFXa3.js";
|
|
2
2
|
function createPageStructureModule() {
|
|
3
3
|
let enabled = false;
|
|
4
4
|
let panelEl = null;
|
|
5
5
|
const PANEL_ID = "accessify-page-structure";
|
|
6
|
+
let mutationObserver = null;
|
|
7
|
+
let debounceTimer = null;
|
|
8
|
+
let originalPushState = null;
|
|
9
|
+
let originalReplaceState = null;
|
|
10
|
+
const boundPopstateHandler = () => scheduleRefresh();
|
|
11
|
+
let lastContentHash = "";
|
|
6
12
|
function lang() {
|
|
7
13
|
return getCurrentWidgetLang();
|
|
8
14
|
}
|
|
@@ -144,12 +150,79 @@ function createPageStructureModule() {
|
|
|
144
150
|
function escapeHtml(str) {
|
|
145
151
|
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
146
152
|
}
|
|
153
|
+
function contentHash() {
|
|
154
|
+
const headings = document.querySelectorAll("h1, h2, h3, h4, h5, h6");
|
|
155
|
+
let hash = "";
|
|
156
|
+
headings.forEach((el) => {
|
|
157
|
+
hash += el.textContent?.trim().slice(0, 20) || "";
|
|
158
|
+
});
|
|
159
|
+
return hash;
|
|
160
|
+
}
|
|
161
|
+
function scheduleRefresh() {
|
|
162
|
+
if (!enabled || !panelEl) return;
|
|
163
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
164
|
+
debounceTimer = setTimeout(() => {
|
|
165
|
+
const newHash = contentHash();
|
|
166
|
+
if (newHash !== lastContentHash) {
|
|
167
|
+
lastContentHash = newHash;
|
|
168
|
+
panelEl?.remove();
|
|
169
|
+
buildPanel();
|
|
170
|
+
}
|
|
171
|
+
}, 300);
|
|
172
|
+
}
|
|
173
|
+
function setupSPAListeners() {
|
|
174
|
+
if (!originalPushState) {
|
|
175
|
+
originalPushState = history.pushState;
|
|
176
|
+
history.pushState = function(...args) {
|
|
177
|
+
originalPushState.apply(this, args);
|
|
178
|
+
scheduleRefresh();
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
if (!originalReplaceState) {
|
|
182
|
+
originalReplaceState = history.replaceState;
|
|
183
|
+
history.replaceState = function(...args) {
|
|
184
|
+
originalReplaceState.apply(this, args);
|
|
185
|
+
scheduleRefresh();
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
window.addEventListener("popstate", boundPopstateHandler);
|
|
189
|
+
}
|
|
190
|
+
function teardownSPAListeners() {
|
|
191
|
+
if (originalPushState) {
|
|
192
|
+
history.pushState = originalPushState;
|
|
193
|
+
originalPushState = null;
|
|
194
|
+
}
|
|
195
|
+
if (originalReplaceState) {
|
|
196
|
+
history.replaceState = originalReplaceState;
|
|
197
|
+
originalReplaceState = null;
|
|
198
|
+
}
|
|
199
|
+
window.removeEventListener("popstate", boundPopstateHandler);
|
|
200
|
+
}
|
|
201
|
+
function setupObserver() {
|
|
202
|
+
if (mutationObserver) return;
|
|
203
|
+
mutationObserver = new MutationObserver(() => scheduleRefresh());
|
|
204
|
+
const target = document.querySelector("main") || document.body;
|
|
205
|
+
mutationObserver.observe(target, { childList: true, subtree: true });
|
|
206
|
+
}
|
|
207
|
+
function teardownObserver() {
|
|
208
|
+
mutationObserver?.disconnect();
|
|
209
|
+
mutationObserver = null;
|
|
210
|
+
if (debounceTimer) {
|
|
211
|
+
clearTimeout(debounceTimer);
|
|
212
|
+
debounceTimer = null;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
147
215
|
function activate() {
|
|
148
216
|
enabled = true;
|
|
217
|
+
lastContentHash = contentHash();
|
|
149
218
|
buildPanel();
|
|
219
|
+
setupObserver();
|
|
220
|
+
setupSPAListeners();
|
|
150
221
|
}
|
|
151
222
|
function deactivate() {
|
|
152
223
|
enabled = false;
|
|
224
|
+
teardownObserver();
|
|
225
|
+
teardownSPAListeners();
|
|
153
226
|
panelEl?.remove();
|
|
154
227
|
panelEl = null;
|
|
155
228
|
}
|
|
@@ -167,4 +240,4 @@ function createPageStructureModule() {
|
|
|
167
240
|
export {
|
|
168
241
|
createPageStructureModule as default
|
|
169
242
|
};
|
|
170
|
-
//# sourceMappingURL=page-structure-
|
|
243
|
+
//# sourceMappingURL=page-structure-DDjJeVCc.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"page-structure-DDjJeVCc.js","sources":["../src/features/page-structure.ts"],"sourcesContent":["import type { FeatureModule, FeatureState } from '../types';\nimport { getCurrentWidgetLang, t } from '../i18n/index';\n\nexport default function createPageStructureModule(): FeatureModule {\n let enabled = false;\n let panelEl: HTMLElement | null = null;\n const PANEL_ID = 'accessify-page-structure';\n let mutationObserver: MutationObserver | null = null;\n let debounceTimer: ReturnType<typeof setTimeout> | null = null;\n let originalPushState: typeof history.pushState | null = null;\n let originalReplaceState: typeof history.replaceState | null = null;\n const boundPopstateHandler = () => scheduleRefresh();\n let lastContentHash = '';\n\n function lang(): string {\n return getCurrentWidgetLang();\n }\n\n function collectHeadings(): Array<{ tag: string; text: string; el: HTMLElement }> {\n const results: Array<{ tag: string; text: string; el: HTMLElement }> = [];\n const headings = document.querySelectorAll<HTMLElement>('h1, h2, h3, h4, h5, h6');\n headings.forEach((el) => {\n if (el.closest('#accessify-root')) return;\n const text = el.textContent?.trim() || '';\n if (text) results.push({ tag: el.tagName.toLowerCase(), text, el });\n });\n return results;\n }\n\n function collectLandmarks(): Array<{ role: string; label: string; el: HTMLElement }> {\n const results: Array<{ role: string; label: string; el: HTMLElement }> = [];\n const selectors = [\n 'main', 'nav', 'aside', 'header', 'footer', 'section[aria-label]',\n 'section[aria-labelledby]', '[role=\"main\"]', '[role=\"navigation\"]',\n '[role=\"complementary\"]', '[role=\"banner\"]', '[role=\"contentinfo\"]',\n '[role=\"search\"]',\n ];\n const els = document.querySelectorAll<HTMLElement>(selectors.join(','));\n els.forEach((el) => {\n if (el.closest('#accessify-root')) return;\n const role = el.getAttribute('role') || el.tagName.toLowerCase();\n const label = el.getAttribute('aria-label') || el.getAttribute('aria-labelledby') || role;\n results.push({ role, label, el });\n });\n return results;\n }\n\n function buildPanel() {\n panelEl = document.createElement('div');\n panelEl.id = PANEL_ID;\n panelEl.setAttribute('role', 'dialog');\n panelEl.setAttribute('aria-label', t('pageStructure.title', lang()));\n\n const headings = collectHeadings();\n const landmarks = collectLandmarks();\n\n const indent: Record<string, string> = {\n h1: '0', h2: '12px', h3: '24px', h4: '36px', h5: '48px', h6: '60px',\n };\n\n let html = `\n <style>\n #${PANEL_ID} {\n position: fixed; top: 50%; left: 50%;\n transform: translate(-50%, -50%);\n z-index: 999998;\n background: #fff; color: #1a1a1a;\n border-radius: 12px; box-shadow: 0 8px 32px rgba(0,0,0,0.2);\n padding: 24px; width: 400px; max-height: 80vh;\n overflow-y: auto; font-family: system-ui, sans-serif;\n font-size: 14px; line-height: 1.5;\n }\n #${PANEL_ID} h3 { margin: 0 0 12px; font-size: 16px; }\n #${PANEL_ID} .ps-section { margin-bottom: 16px; }\n #${PANEL_ID} .ps-label { font-size: 11px; text-transform: uppercase; letter-spacing: 0.5px; color: #888; margin-bottom: 6px; }\n #${PANEL_ID} .ps-item {\n display: block; width: 100%; text-align: left;\n padding: 6px 8px; border: none; background: none;\n cursor: pointer; border-radius: 6px; font-size: 13px;\n color: #1a1a1a; font-family: inherit;\n }\n #${PANEL_ID} .ps-item:hover { 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 // --- Content hash for change detection ---\n function contentHash(): string {\n const headings = document.querySelectorAll('h1, h2, h3, h4, h5, h6');\n let hash = '';\n headings.forEach((el) => { hash += el.textContent?.trim().slice(0, 20) || ''; });\n return hash;\n }\n\n function scheduleRefresh() {\n if (!enabled || !panelEl) return;\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n const newHash = contentHash();\n if (newHash !== lastContentHash) {\n lastContentHash = newHash;\n panelEl?.remove();\n buildPanel();\n }\n }, 300);\n }\n\n // --- SPA navigation detection ---\n function setupSPAListeners() {\n // Override pushState/replaceState to detect SPA navigation\n if (!originalPushState) {\n originalPushState = history.pushState;\n history.pushState = function (...args: Parameters<typeof history.pushState>) {\n originalPushState!.apply(this, args);\n scheduleRefresh();\n };\n }\n if (!originalReplaceState) {\n originalReplaceState = history.replaceState;\n history.replaceState = function (...args: Parameters<typeof history.replaceState>) {\n originalReplaceState!.apply(this, args);\n scheduleRefresh();\n };\n }\n window.addEventListener('popstate', boundPopstateHandler);\n }\n\n function teardownSPAListeners() {\n if (originalPushState) {\n history.pushState = originalPushState;\n originalPushState = null;\n }\n if (originalReplaceState) {\n history.replaceState = originalReplaceState;\n originalReplaceState = null;\n }\n window.removeEventListener('popstate', boundPopstateHandler);\n }\n\n // --- MutationObserver for dynamic content ---\n function setupObserver() {\n if (mutationObserver) return;\n mutationObserver = new MutationObserver(() => scheduleRefresh());\n const target = document.querySelector('main') || document.body;\n mutationObserver.observe(target, { childList: true, subtree: true });\n }\n\n function teardownObserver() {\n mutationObserver?.disconnect();\n mutationObserver = null;\n if (debounceTimer) { clearTimeout(debounceTimer); debounceTimer = null; }\n }\n\n function activate() {\n enabled = true;\n lastContentHash = contentHash();\n buildPanel();\n setupObserver();\n setupSPAListeners();\n }\n\n function deactivate() {\n enabled = false;\n teardownObserver();\n teardownSPAListeners();\n panelEl?.remove();\n panelEl = null;\n }\n\n return {\n id: 'page-structure',\n name: () => 'Page Structure',\n description: 'View headings and landmarks for quick navigation',\n icon: 'page-structure',\n category: 'motor',\n activate,\n deactivate,\n getState: (): FeatureState => ({ id: 'page-structure', enabled }),\n };\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -92,6 +92,38 @@ function createTextSimplifyModule(aiService, lang = "de") {
|
|
|
92
92
|
}
|
|
93
93
|
return hash.toString(36);
|
|
94
94
|
}
|
|
95
|
+
function charDiff(a, b) {
|
|
96
|
+
const na = a.replace(/\s+/g, " ").trim();
|
|
97
|
+
const nb = b.replace(/\s+/g, " ").trim();
|
|
98
|
+
if (na === nb) return 0;
|
|
99
|
+
if (na.length > 500 || nb.length > 500) {
|
|
100
|
+
const lenDiff = Math.abs(na.length - nb.length);
|
|
101
|
+
if (lenDiff > 3) return lenDiff;
|
|
102
|
+
let diffs = lenDiff;
|
|
103
|
+
const minLen = Math.min(na.length, nb.length);
|
|
104
|
+
for (let i = 0; i < minLen; i++) {
|
|
105
|
+
if (na[i] !== nb[i]) diffs++;
|
|
106
|
+
if (diffs > 3) return diffs;
|
|
107
|
+
}
|
|
108
|
+
return diffs;
|
|
109
|
+
}
|
|
110
|
+
const m = na.length;
|
|
111
|
+
const n = nb.length;
|
|
112
|
+
if (Math.abs(m - n) > 3) return Math.abs(m - n);
|
|
113
|
+
const dp = Array.from({ length: n + 1 }, (_, i) => i);
|
|
114
|
+
for (let i = 1; i <= m; i++) {
|
|
115
|
+
let prev = dp[0];
|
|
116
|
+
dp[0] = i;
|
|
117
|
+
for (let j = 1; j <= n; j++) {
|
|
118
|
+
const temp = dp[j];
|
|
119
|
+
dp[j] = na[i - 1] === nb[j - 1] ? prev : 1 + Math.min(prev, dp[j], dp[j - 1]);
|
|
120
|
+
prev = temp;
|
|
121
|
+
}
|
|
122
|
+
if (Math.min(...dp) > 3) return 4;
|
|
123
|
+
}
|
|
124
|
+
return dp[n];
|
|
125
|
+
}
|
|
126
|
+
const STALENESS_THRESHOLD = 3;
|
|
95
127
|
function getLeafTextNodes(root) {
|
|
96
128
|
const textNodes = [];
|
|
97
129
|
const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, null);
|
|
@@ -285,7 +317,7 @@ function createTextSimplifyModule(aiService, lang = "de") {
|
|
|
285
317
|
const { safe, patches } = checkLayoutSafety(el);
|
|
286
318
|
if (!safe) {
|
|
287
319
|
el.innerHTML = originalHtml;
|
|
288
|
-
el.removeAttribute("data-accessify-replacing");
|
|
320
|
+
setTimeout(() => el.removeAttribute("data-accessify-replacing"), 0);
|
|
289
321
|
console.warn(
|
|
290
322
|
"[Accessify] Block skipped — layout clipping detected after replacement:",
|
|
291
323
|
el.textContent?.slice(0, 60)
|
|
@@ -293,7 +325,6 @@ function createTextSimplifyModule(aiService, lang = "de") {
|
|
|
293
325
|
skippedBlocks++;
|
|
294
326
|
return false;
|
|
295
327
|
}
|
|
296
|
-
el.removeAttribute("data-accessify-replacing");
|
|
297
328
|
el.setAttribute(SIMPLIFIED_ATTR, "true");
|
|
298
329
|
el.setAttribute(ORIGINAL_ATTR, originalHtml);
|
|
299
330
|
if (blockHash) {
|
|
@@ -301,6 +332,7 @@ function createTextSimplifyModule(aiService, lang = "de") {
|
|
|
301
332
|
}
|
|
302
333
|
savedParagraphs.push({ el, originalHtml, ancestorPatches: patches });
|
|
303
334
|
resizeObserver?.observe(el);
|
|
335
|
+
setTimeout(() => el.removeAttribute("data-accessify-replacing"), 0);
|
|
304
336
|
return true;
|
|
305
337
|
}
|
|
306
338
|
function revertSingleBlock(saved) {
|
|
@@ -312,43 +344,84 @@ function createTextSimplifyModule(aiService, lang = "de") {
|
|
|
312
344
|
resizeObserver?.unobserve(saved.el);
|
|
313
345
|
savedParagraphs = savedParagraphs.filter((sp) => sp.el !== saved.el);
|
|
314
346
|
}
|
|
347
|
+
const manifestCache = /* @__PURE__ */ new Map();
|
|
315
348
|
async function fetchManifest(siteKey, proxyUrl) {
|
|
349
|
+
const cacheKey = `${siteKey}:${level}`;
|
|
350
|
+
if (manifestCache.has(cacheKey)) return manifestCache.get(cacheKey);
|
|
316
351
|
const base = proxyUrl || "https://accessify-api.accessify.workers.dev";
|
|
317
352
|
const pageUrl = encodeURIComponent(window.location.origin + window.location.pathname);
|
|
318
353
|
const url = `${base}/v1/manifest?siteKey=${siteKey}&url=${pageUrl}&feature=simplify&variant=${level}`;
|
|
319
354
|
try {
|
|
320
|
-
const res = await fetch(url);
|
|
321
|
-
if (!res.ok)
|
|
355
|
+
const res = await fetch(url, { cache: "no-cache" });
|
|
356
|
+
if (!res.ok) {
|
|
357
|
+
manifestCache.set(cacheKey, null);
|
|
358
|
+
return null;
|
|
359
|
+
}
|
|
322
360
|
const data = await res.json();
|
|
323
|
-
|
|
324
|
-
|
|
361
|
+
const result = data.blocks?.length ? data : null;
|
|
362
|
+
manifestCache.set(cacheKey, result);
|
|
363
|
+
return result;
|
|
325
364
|
} catch {
|
|
365
|
+
manifestCache.set(cacheKey, null);
|
|
326
366
|
return null;
|
|
327
367
|
}
|
|
328
368
|
}
|
|
329
369
|
function applyManifestBlocks(manifest, elements) {
|
|
330
370
|
const applied = [];
|
|
331
371
|
const remaining = [];
|
|
372
|
+
const stale = [];
|
|
332
373
|
const byHash = /* @__PURE__ */ new Map();
|
|
374
|
+
const bySelector = /* @__PURE__ */ new Map();
|
|
333
375
|
for (const block of manifest.blocks) {
|
|
334
376
|
byHash.set(block.blockHash, block);
|
|
377
|
+
if (block.selector) {
|
|
378
|
+
bySelector.set(block.selector, block);
|
|
379
|
+
}
|
|
335
380
|
}
|
|
336
381
|
for (const el of elements) {
|
|
337
382
|
const text = el.textContent?.trim() || "";
|
|
338
383
|
const elHash = hashText(text);
|
|
339
|
-
const
|
|
340
|
-
if (
|
|
341
|
-
const success = safeReplace(el,
|
|
384
|
+
const exactMatch = byHash.get(elHash);
|
|
385
|
+
if (exactMatch?.result) {
|
|
386
|
+
const success = safeReplace(el, exactMatch.result, elHash);
|
|
342
387
|
if (success) {
|
|
343
388
|
applied.push(el);
|
|
344
389
|
} else {
|
|
345
390
|
remaining.push(el);
|
|
346
391
|
}
|
|
347
|
-
|
|
348
|
-
|
|
392
|
+
continue;
|
|
393
|
+
}
|
|
394
|
+
let fuzzyMatch;
|
|
395
|
+
for (const [selector, block] of bySelector) {
|
|
396
|
+
try {
|
|
397
|
+
if (el.matches(selector) || el.closest(selector) === el) {
|
|
398
|
+
fuzzyMatch = block;
|
|
399
|
+
break;
|
|
400
|
+
}
|
|
401
|
+
} catch {
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
if (fuzzyMatch?.result && fuzzyMatch.originalText) {
|
|
405
|
+
const diff = charDiff(text, fuzzyMatch.originalText);
|
|
406
|
+
if (diff <= STALENESS_THRESHOLD) {
|
|
407
|
+
const success = safeReplace(el, fuzzyMatch.result, elHash);
|
|
408
|
+
if (success) {
|
|
409
|
+
applied.push(el);
|
|
410
|
+
} else {
|
|
411
|
+
remaining.push(el);
|
|
412
|
+
}
|
|
413
|
+
} else {
|
|
414
|
+
console.info(
|
|
415
|
+
`[Accessify] Stale block (diff=${diff}):`,
|
|
416
|
+
text.slice(0, 50) + "…"
|
|
417
|
+
);
|
|
418
|
+
stale.push(el);
|
|
419
|
+
}
|
|
420
|
+
continue;
|
|
349
421
|
}
|
|
422
|
+
remaining.push(el);
|
|
350
423
|
}
|
|
351
|
-
return { applied, remaining };
|
|
424
|
+
return { applied, remaining, stale };
|
|
352
425
|
}
|
|
353
426
|
function persistToManifest(siteKey, blockHash, originalText, simplifiedText, proxyUrl) {
|
|
354
427
|
const base = proxyUrl || "https://accessify-api.accessify.workers.dev";
|
|
@@ -445,13 +518,15 @@ function createTextSimplifyModule(aiService, lang = "de") {
|
|
|
445
518
|
const { siteKey, proxyUrl } = getConfig();
|
|
446
519
|
let fromCache = 0;
|
|
447
520
|
let remaining = elements;
|
|
521
|
+
const staleBlocks = /* @__PURE__ */ new Set();
|
|
448
522
|
if (siteKey) {
|
|
449
523
|
showProgress(0, elements.length);
|
|
450
524
|
const manifest = await fetchManifest(siteKey, proxyUrl);
|
|
451
525
|
if (manifest?.blocks?.length) {
|
|
452
526
|
const result = applyManifestBlocks(manifest, elements);
|
|
453
527
|
fromCache = result.applied.length;
|
|
454
|
-
|
|
528
|
+
for (const el of result.stale) staleBlocks.add(el);
|
|
529
|
+
remaining = [...result.stale, ...result.remaining];
|
|
455
530
|
for (const block of manifest.blocks) {
|
|
456
531
|
if (block.result) {
|
|
457
532
|
setClientCached(clientCacheKey(block.blockHash), block.result);
|
|
@@ -491,6 +566,10 @@ function createTextSimplifyModule(aiService, lang = "de") {
|
|
|
491
566
|
}
|
|
492
567
|
}
|
|
493
568
|
if (!aiService && remaining.length > 0) {
|
|
569
|
+
if (staleBlocks.size > 0) {
|
|
570
|
+
skippedBlocks += staleBlocks.size;
|
|
571
|
+
console.info(`[Accessify] ${staleBlocks.size} stale block(s) skipped — no AI service to regenerate`);
|
|
572
|
+
}
|
|
494
573
|
if (fromCache > 0) {
|
|
495
574
|
showDone(fromCache, fromCache);
|
|
496
575
|
} else {
|
|
@@ -505,6 +584,7 @@ function createTextSimplifyModule(aiService, lang = "de") {
|
|
|
505
584
|
const totalElements = elements.length;
|
|
506
585
|
let completed = fromCache;
|
|
507
586
|
let totalSimplified = fromCache;
|
|
587
|
+
const isStale = (el) => staleBlocks.has(el);
|
|
508
588
|
showProgress(completed, totalElements, skippedBlocks);
|
|
509
589
|
for (const el of remaining) {
|
|
510
590
|
if (abortController.signal.aborted) break;
|
|
@@ -530,6 +610,15 @@ function createTextSimplifyModule(aiService, lang = "de") {
|
|
|
530
610
|
}
|
|
531
611
|
}
|
|
532
612
|
} catch (err) {
|
|
613
|
+
if (err?.message?.includes("401") || err?.message?.includes("403")) {
|
|
614
|
+
console.warn("[Accessify] AI auth failed, stopping live simplification");
|
|
615
|
+
for (const r of remaining) {
|
|
616
|
+
if (isStale(r) && !r.hasAttribute(SIMPLIFIED_ATTR)) skippedBlocks++;
|
|
617
|
+
}
|
|
618
|
+
clearLoading(el);
|
|
619
|
+
completed++;
|
|
620
|
+
break;
|
|
621
|
+
}
|
|
533
622
|
if (err?.message?.includes("429")) {
|
|
534
623
|
await new Promise((r) => setTimeout(r, 8e3));
|
|
535
624
|
try {
|
|
@@ -546,19 +635,31 @@ function createTextSimplifyModule(aiService, lang = "de") {
|
|
|
546
635
|
}
|
|
547
636
|
}
|
|
548
637
|
} catch {
|
|
638
|
+
if (isStale(el)) {
|
|
639
|
+
skippedBlocks++;
|
|
640
|
+
console.warn("[Accessify] Stale block skipped — AI retry failed:", text.slice(0, 50));
|
|
641
|
+
}
|
|
549
642
|
}
|
|
550
643
|
} else {
|
|
551
|
-
|
|
644
|
+
if (isStale(el)) {
|
|
645
|
+
skippedBlocks++;
|
|
646
|
+
console.warn("[Accessify] Stale block skipped — AI error:", text.slice(0, 50));
|
|
647
|
+
} else {
|
|
648
|
+
console.warn("[Accessify] Failed to simplify:", err);
|
|
649
|
+
}
|
|
552
650
|
}
|
|
553
651
|
} finally {
|
|
554
652
|
clearLoading(el);
|
|
555
653
|
completed++;
|
|
556
654
|
if (!abortController?.signal.aborted) {
|
|
557
655
|
showProgress(completed, totalElements, skippedBlocks);
|
|
558
|
-
await new Promise((r) => setTimeout(r,
|
|
656
|
+
await new Promise((r) => setTimeout(r, 600));
|
|
559
657
|
}
|
|
560
658
|
}
|
|
561
659
|
}
|
|
660
|
+
for (const el of remaining) {
|
|
661
|
+
clearLoading(el);
|
|
662
|
+
}
|
|
562
663
|
if (!abortController?.signal.aborted) {
|
|
563
664
|
showDone(totalSimplified, fromCache);
|
|
564
665
|
}
|
|
@@ -629,4 +730,4 @@ function createTextSimplifyModule(aiService, lang = "de") {
|
|
|
629
730
|
export {
|
|
630
731
|
createTextSimplifyModule as default
|
|
631
732
|
};
|
|
632
|
-
//# sourceMappingURL=text-simplify-
|
|
733
|
+
//# sourceMappingURL=text-simplify-B1v6Muvn.js.map
|