accessify-widget 0.3.19 → 0.3.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import { t, g as getCurrentWidgetLang } from "./index-ZJae2VzU.js";
1
+ import { t, g as getCurrentWidgetLang } from "./index-bqpA9Puf.js";
2
2
  function createKeyboardNavModule() {
3
3
  let enabled = false;
4
4
  const STYLE_ID = "accessify-keyboard-nav";
@@ -375,4 +375,4 @@ function createKeyboardNavModule() {
375
375
  export {
376
376
  createKeyboardNavModule as default
377
377
  };
378
- //# sourceMappingURL=keyboard-nav-BdSCmx4b.js.map
378
+ //# sourceMappingURL=keyboard-nav-CPnRSBAa.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"keyboard-nav-BdSCmx4b.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
+ {"version":3,"file":"keyboard-nav-CPnRSBAa.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 +1 @@
1
- !function(){"use strict";var e=document.currentScript,t=[],n=function(e){n._loaded&&window.__accessify_exec?window.__accessify_exec.apply(null,arguments):t.push(Array.prototype.slice.call(arguments))};function o(){var o=document.createElement("script");o.src=function(){if(e&&e.src)try{var t=new URL(e.src);return t.origin+t.pathname.replace(/\/[^\/]*$/,"")}catch(e){}return"https://cdn.jsdelivr.net/npm/accessify-widget/dist"}()+"/accessify.min.js?v=0.3.19",o.async=!0,o.crossOrigin="anonymous",o.onload=function(){n._loaded=!0;var o=function(){if(!e)return{};var t=e.dataset,n={position:t.position,theme:t.theme,lang:t.lang,openRouterKey:t.openrouterKey,siteKey:t.siteKey,proxyUrl:t.proxyUrl,zIndex:t.zIndex?parseInt(t.zIndex,10):void 0,compact:"true"===t.compact};return n.siteKey&&!n.proxyUrl&&(n.proxyUrl="https://accessify-api.accessify.workers.dev"),n}();window.__accessify_dataConfig=o,o.siteKey&&!t.some(function(e){return"init"===e[0]})&&n("init",o)},o.onerror=function(){console.warn("[Accessify] Failed to load widget.")},(document.head||document.documentElement).appendChild(o)}n.q=t,n._loaded=!1,window.Accessify=n,window.A11yPlus=n,"complete"===document.readyState||"interactive"===document.readyState?setTimeout(o,1):document.addEventListener("DOMContentLoaded",o)}();
1
+ !function(){"use strict";var e=document.currentScript,t=[],n=function(e){n._loaded&&window.__accessify_exec?window.__accessify_exec.apply(null,arguments):t.push(Array.prototype.slice.call(arguments))};function o(){var o=document.createElement("script");o.src=function(){if(e&&e.src)try{var t=new URL(e.src);return t.origin+t.pathname.replace(/\/[^\/]*$/,"")}catch(e){}return"https://cdn.jsdelivr.net/npm/accessify-widget/dist"}()+"/accessify.min.js?v=0.3.21",o.async=!0,o.crossOrigin="anonymous",o.onload=function(){n._loaded=!0;var o=function(){if(!e)return{};var t=e.dataset,n={position:t.position,theme:t.theme,lang:t.lang,openRouterKey:t.openrouterKey,siteKey:t.siteKey,proxyUrl:t.proxyUrl,zIndex:t.zIndex?parseInt(t.zIndex,10):void 0,compact:"true"===t.compact};return n.siteKey&&!n.proxyUrl&&(n.proxyUrl="https://accessify-api.accessify.workers.dev"),n}();window.__accessify_dataConfig=o,o.siteKey&&!t.some(function(e){return"init"===e[0]})&&n("init",o)},o.onerror=function(){console.warn("[Accessify] Failed to load widget.")},(document.head||document.documentElement).appendChild(o)}n.q=t,n._loaded=!1,window.Accessify=n,window.A11yPlus=n,"complete"===document.readyState||"interactive"===document.readyState?setTimeout(o,1):document.addEventListener("DOMContentLoaded",o)}();
@@ -1,4 +1,4 @@
1
- import { t, g as getCurrentWidgetLang } from "./index-ZJae2VzU.js";
1
+ import { t, g as getCurrentWidgetLang } from "./index-bqpA9Puf.js";
2
2
  function createPageStructureModule() {
3
3
  let enabled = false;
4
4
  let panelEl = null;
@@ -240,4 +240,4 @@ function createPageStructureModule() {
240
240
  export {
241
241
  createPageStructureModule as default
242
242
  };
243
- //# sourceMappingURL=page-structure-D6bdtWiD.js.map
243
+ //# sourceMappingURL=page-structure-Bp15mzNy.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"page-structure-D6bdtWiD.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())}\">&times;</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, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\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":";AAGA,SAAwB,4BAA2C;AACjE,MAAI,UAAU;AACd,MAAI,UAA8B;AAClC,QAAM,WAAW;AACjB,MAAI,mBAA4C;AAChD,MAAI,gBAAsD;AAC1D,MAAI,oBAAqD;AACzD,MAAI,uBAA2D;AAC/D,QAAM,uBAAuB,MAAM,gBAAA;AACnC,MAAI,kBAAkB;AAEtB,WAAS,OAAe;AACtB,WAAO,qBAAA;AAAA,EACT;AAEA,WAAS,kBAAyE;AAChF,UAAM,UAAiE,CAAA;AACvE,UAAM,WAAW,SAAS,iBAA8B,wBAAwB;AAChF,aAAS,QAAQ,CAAC,OAAO;AACvB,UAAI,GAAG,QAAQ,iBAAiB,EAAG;AACnC,YAAM,OAAO,GAAG,aAAa,KAAA,KAAU;AACvC,UAAI,KAAM,SAAQ,KAAK,EAAE,KAAK,GAAG,QAAQ,YAAA,GAAe,MAAM,GAAA,CAAI;AAAA,IACpE,CAAC;AACD,WAAO;AAAA,EACT;AAEA,WAAS,mBAA4E;AACnF,UAAM,UAAmE,CAAA;AACzE,UAAM,YAAY;AAAA,MAChB;AAAA,MAAQ;AAAA,MAAO;AAAA,MAAS;AAAA,MAAU;AAAA,MAAU;AAAA,MAC5C;AAAA,MAA4B;AAAA,MAAiB;AAAA,MAC7C;AAAA,MAA0B;AAAA,MAAmB;AAAA,MAC7C;AAAA,IAAA;AAEF,UAAM,MAAM,SAAS,iBAA8B,UAAU,KAAK,GAAG,CAAC;AACtE,QAAI,QAAQ,CAAC,OAAO;AAClB,UAAI,GAAG,QAAQ,iBAAiB,EAAG;AACnC,YAAM,OAAO,GAAG,aAAa,MAAM,KAAK,GAAG,QAAQ,YAAA;AACnD,YAAM,QAAQ,GAAG,aAAa,YAAY,KAAK,GAAG,aAAa,iBAAiB,KAAK;AACrF,cAAQ,KAAK,EAAE,MAAM,OAAO,IAAI;AAAA,IAClC,CAAC;AACD,WAAO;AAAA,EACT;AAEA,WAAS,aAAa;AACpB,cAAU,SAAS,cAAc,KAAK;AACtC,YAAQ,KAAK;AACb,YAAQ,aAAa,QAAQ,QAAQ;AACrC,YAAQ,aAAa,cAAc,EAAE,uBAAuB,KAAA,CAAM,CAAC;AAEnE,UAAM,WAAW,gBAAA;AACjB,UAAM,YAAY,iBAAA;AAElB,UAAM,SAAiC;AAAA,MACrC,IAAI;AAAA,MAAK,IAAI;AAAA,MAAQ,IAAI;AAAA,MAAQ,IAAI;AAAA,MAAQ,IAAI;AAAA,MAAQ,IAAI;AAAA,IAAA;AAG/D,QAAI,OAAO;AAAA;AAAA,WAEJ,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,WAUR,QAAQ;AAAA,WACR,QAAQ;AAAA,WACR,QAAQ;AAAA,WACR,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,WAMR,QAAQ;AAAA,WACR,QAAQ;AAAA,WACR,QAAQ;AAAA,WACR,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA,WAKR,QAAQ;AAAA;AAAA,aAEN,QAAQ;AAAA,aACR,QAAQ;AAAA,aACR,QAAQ;AAAA,aACR,QAAQ;AAAA,aACR,QAAQ;AAAA;AAAA;AAAA,6CAGwB,EAAE,gBAAgB,KAAA,CAAM,CAAC;AAAA,YAC1D,EAAE,uBAAuB,KAAA,CAAM,CAAC;AAAA;AAGxC,QAAI,SAAS,SAAS,GAAG;AACvB,cAAQ,iDAAiD,EAAE,0BAA0B,KAAA,CAAM,CAAC;AAC5F,eAAS,QAAQ,CAAC,GAAG,MAAM;AACzB,gBAAQ,2DAA2D,CAAC,yBAAyB,OAAO,EAAE,GAAG,KAAK,GAAG;AAAA,iCACxF,EAAE,GAAG,UAAU,WAAW,EAAE,KAAK,MAAM,GAAG,EAAE,CAAC,CAAC;AAAA;AAAA,MAEzE,CAAC;AACD,cAAQ;AAAA,IACV;AAEA,QAAI,UAAU,SAAS,GAAG;AACxB,cAAQ,iDAAiD,EAAE,2BAA2B,KAAA,CAAM,CAAC;AAC7F,gBAAU,QAAQ,CAAC,IAAI,MAAM;AAC3B,gBAAQ,4DAA4D,CAAC;AAAA,iCAC5C,WAAW,GAAG,IAAI,CAAC,UAAU,WAAW,GAAG,MAAM,MAAM,GAAG,EAAE,CAAC,CAAC;AAAA;AAAA,MAEzF,CAAC;AACD,cAAQ;AAAA,IACV;AAEA,QAAI,SAAS,WAAW,KAAK,UAAU,WAAW,GAAG;AACnD,cAAQ,yBAAyB,EAAE,uBAAuB,KAAA,CAAM,CAAC;AAAA,IACnE;AAEA,YAAQ,YAAY;AAGpB,YAAQ,iBAAiB,SAAS,CAAC,MAAM;AACvC,YAAM,MAAO,EAAE,OAAuB,QAAqB,UAAU;AACrE,UAAI,KAAK;AACP,cAAM,OAAO,IAAI,QAAQ;AACzB,cAAM,MAAM,SAAS,IAAI,QAAQ,SAAS,KAAK,EAAE;AACjD,YAAI;AACJ,YAAI,SAAS,UAAW,UAAS,SAAS,GAAG,GAAG;AAAA,iBACvC,SAAS,WAAY,UAAS,UAAU,GAAG,GAAG;AACvD,YAAI,QAAQ;AACV,iBAAO,eAAe,EAAE,UAAU,UAAU,OAAO,UAAU;AAC7D,iBAAO,MAAM,EAAE,eAAe,KAAA,CAAM;AAAA,QACtC;AAAA,MACF;AACA,UAAK,EAAE,OAAuB,QAAQ,WAAW,GAAG;AAClD,mBAAA;AAAA,MACF;AAAA,IACF,CAAC;AAED,aAAS,KAAK,YAAY,OAAO;AACjC,UAAM,WAAW,QAAQ,cAA2B,qBAAqB;AACzE,cAAU,MAAA;AAAA,EACZ;AAEA,WAAS,WAAW,KAAqB;AACvC,WAAO,IAAI,QAAQ,MAAM,OAAO,EAAE,QAAQ,MAAM,MAAM,EAAE,QAAQ,MAAM,MAAM;AAAA,EAC9E;AAGA,WAAS,cAAsB;AAC7B,UAAM,WAAW,SAAS,iBAAiB,wBAAwB;AACnE,QAAI,OAAO;AACX,aAAS,QAAQ,CAAC,OAAO;AAAE,cAAQ,GAAG,aAAa,KAAA,EAAO,MAAM,GAAG,EAAE,KAAK;AAAA,IAAI,CAAC;AAC/E,WAAO;AAAA,EACT;AAEA,WAAS,kBAAkB;AACzB,QAAI,CAAC,WAAW,CAAC,QAAS;AAC1B,QAAI,4BAA4B,aAAa;AAC7C,oBAAgB,WAAW,MAAM;AAC/B,YAAM,UAAU,YAAA;AAChB,UAAI,YAAY,iBAAiB;AAC/B,0BAAkB;AAClB,iBAAS,OAAA;AACT,mBAAA;AAAA,MACF;AAAA,IACF,GAAG,GAAG;AAAA,EACR;AAGA,WAAS,oBAAoB;AAE3B,QAAI,CAAC,mBAAmB;AACtB,0BAAoB,QAAQ;AAC5B,cAAQ,YAAY,YAAa,MAA4C;AAC3E,0BAAmB,MAAM,MAAM,IAAI;AACnC,wBAAA;AAAA,MACF;AAAA,IACF;AACA,QAAI,CAAC,sBAAsB;AACzB,6BAAuB,QAAQ;AAC/B,cAAQ,eAAe,YAAa,MAA+C;AACjF,6BAAsB,MAAM,MAAM,IAAI;AACtC,wBAAA;AAAA,MACF;AAAA,IACF;AACA,WAAO,iBAAiB,YAAY,oBAAoB;AAAA,EAC1D;AAEA,WAAS,uBAAuB;AAC9B,QAAI,mBAAmB;AACrB,cAAQ,YAAY;AACpB,0BAAoB;AAAA,IACtB;AACA,QAAI,sBAAsB;AACxB,cAAQ,eAAe;AACvB,6BAAuB;AAAA,IACzB;AACA,WAAO,oBAAoB,YAAY,oBAAoB;AAAA,EAC7D;AAGA,WAAS,gBAAgB;AACvB,QAAI,iBAAkB;AACtB,uBAAmB,IAAI,iBAAiB,MAAM,iBAAiB;AAC/D,UAAM,SAAS,SAAS,cAAc,MAAM,KAAK,SAAS;AAC1D,qBAAiB,QAAQ,QAAQ,EAAE,WAAW,MAAM,SAAS,MAAM;AAAA,EACrE;AAEA,WAAS,mBAAmB;AAC1B,sBAAkB,WAAA;AAClB,uBAAmB;AACnB,QAAI,eAAe;AAAE,mBAAa,aAAa;AAAG,sBAAgB;AAAA,IAAM;AAAA,EAC1E;AAEA,WAAS,WAAW;AAClB,cAAU;AACV,sBAAkB,YAAA;AAClB,eAAA;AACA,kBAAA;AACA,sBAAA;AAAA,EACF;AAEA,WAAS,aAAa;AACpB,cAAU;AACV,qBAAA;AACA,yBAAA;AACA,aAAS,OAAA;AACT,cAAU;AAAA,EACZ;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,kBAAkB,QAAA;AAAA,EAAQ;AAEnE;"}
1
+ {"version":3,"file":"page-structure-Bp15mzNy.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())}\">&times;</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, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\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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"}
@@ -540,15 +540,8 @@ function createTextSimplifyModule(aiService, lang = "de") {
540
540
  document.head.appendChild(style);
541
541
  }
542
542
  }
543
- function showDone(count, fromCache) {
544
- if (overlayEl) {
545
- const cacheNote = fromCache > 0 ? lang.startsWith("de") ? ` (${fromCache} aus Cache)` : ` (${fromCache} from cache)` : "";
546
- const skippedNote = skippedBlocks > 0 ? lang.startsWith("de") ? `, ${skippedBlocks} übersprungen` : `, ${skippedBlocks} skipped` : "";
547
- const label = lang.startsWith("de") ? `${count} Textabschnitte vereinfacht${cacheNote}${skippedNote}` : `${count} text sections simplified${cacheNote}${skippedNote}`;
548
- overlayEl.innerHTML = `<span style="color:#22c55e">&#x2713;</span> ${label}`;
549
- overlayEl.style.color = "#22c55e";
550
- setTimeout(() => removeProgress(), 3e3);
551
- }
543
+ function showDone(_count, _fromCache) {
544
+ removeProgress();
552
545
  }
553
546
  function showError(msg) {
554
547
  if (overlayEl) {
@@ -591,7 +584,7 @@ function createTextSimplifyModule(aiService, lang = "de") {
591
584
  }
592
585
  }
593
586
  if (remaining.length === 0) {
594
- showDone(fromCache, fromCache);
587
+ showDone();
595
588
  return;
596
589
  }
597
590
  }
@@ -619,7 +612,7 @@ function createTextSimplifyModule(aiService, lang = "de") {
619
612
  }
620
613
  remaining = stillRemaining;
621
614
  if (remaining.length === 0) {
622
- showDone(fromCache, fromCache);
615
+ showDone();
623
616
  return;
624
617
  }
625
618
  }
@@ -629,7 +622,7 @@ function createTextSimplifyModule(aiService, lang = "de") {
629
622
  console.info(`[Accessify] ${staleBlocks.size} stale block(s) skipped — no AI service to regenerate`);
630
623
  }
631
624
  if (fromCache > 0) {
632
- showDone(fromCache, fromCache);
625
+ showDone();
633
626
  } else {
634
627
  showProgress(0, 1);
635
628
  showError(lang.startsWith("de") ? "Kein API-Key konfiguriert" : "No API key configured");
@@ -719,7 +712,7 @@ function createTextSimplifyModule(aiService, lang = "de") {
719
712
  clearLoading(el);
720
713
  }
721
714
  if (!abortController?.signal.aborted) {
722
- showDone(totalSimplified, fromCache);
715
+ showDone();
723
716
  }
724
717
  }
725
718
  function restorePage() {
@@ -765,16 +758,53 @@ function createTextSimplifyModule(aiService, lang = "de") {
765
758
  function savePreferences() {
766
759
  localStorage.setItem(STORAGE_KEY, JSON.stringify({ level }));
767
760
  }
761
+ let lastUrl = "";
762
+ let navigationCleanup = null;
763
+ function setupNavigationWatcher() {
764
+ lastUrl = location.href;
765
+ const onUrlChange = () => {
766
+ if (!enabled || location.href === lastUrl) return;
767
+ lastUrl = location.href;
768
+ if (window.__ACCESSIFY_DEBUG) {
769
+ console.log("[Accessify] URL changed, re-simplifying:", lastUrl);
770
+ }
771
+ abortController?.abort();
772
+ restorePage();
773
+ removeProgress();
774
+ setTimeout(() => {
775
+ if (enabled) simplifyPage();
776
+ }, 500);
777
+ };
778
+ window.addEventListener("popstate", onUrlChange);
779
+ const origPush = history.pushState.bind(history);
780
+ const origReplace = history.replaceState.bind(history);
781
+ history.pushState = function(...args) {
782
+ origPush(...args);
783
+ onUrlChange();
784
+ };
785
+ history.replaceState = function(...args) {
786
+ origReplace(...args);
787
+ onUrlChange();
788
+ };
789
+ navigationCleanup = () => {
790
+ window.removeEventListener("popstate", onUrlChange);
791
+ history.pushState = origPush;
792
+ history.replaceState = origReplace;
793
+ };
794
+ }
768
795
  function activate() {
769
796
  if (enabled) return;
770
797
  enabled = true;
771
798
  loadPreferences();
799
+ setupNavigationWatcher();
772
800
  simplifyPage();
773
801
  }
774
802
  function deactivate() {
775
803
  enabled = false;
776
804
  abortController?.abort();
777
805
  abortController = null;
806
+ navigationCleanup?.();
807
+ navigationCleanup = null;
778
808
  restorePage();
779
809
  removeProgress();
780
810
  }
@@ -802,4 +832,4 @@ function createTextSimplifyModule(aiService, lang = "de") {
802
832
  export {
803
833
  createTextSimplifyModule as default
804
834
  };
805
- //# sourceMappingURL=text-simplify-CU8icsDG.js.map
835
+ //# sourceMappingURL=text-simplify-DPTSSdUB.js.map