accessify-widget 0.1.0 → 0.2.1

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.
Files changed (38) hide show
  1. package/README.md +57 -3
  2. package/dist/accessify.min.js +1 -1
  3. package/dist/accessify.min.js.map +1 -1
  4. package/dist/accessify.mjs +1 -1
  5. package/dist/{alt-text-CLxbmwG6.js → alt-text-CgzNGvdT.js} +2 -2
  6. package/dist/{alt-text-CLxbmwG6.js.map → alt-text-CgzNGvdT.js.map} +1 -1
  7. package/dist/contrast-CqsICAkU.js +139 -0
  8. package/dist/contrast-CqsICAkU.js.map +1 -0
  9. package/dist/{index-CUQfpnwR.js → index-qmiN2JAz.js} +1811 -736
  10. package/dist/index-qmiN2JAz.js.map +1 -0
  11. package/dist/{keyboard-nav-BdPyLaZt.js → keyboard-nav-DH4qBThF.js} +16 -12
  12. package/dist/keyboard-nav-DH4qBThF.js.map +1 -0
  13. package/dist/{page-structure-2X8mOSpC.js → page-structure-DTBqkrYs.js} +11 -7
  14. package/dist/page-structure-DTBqkrYs.js.map +1 -0
  15. package/dist/text-size-C6OFhCGi.js +186 -0
  16. package/dist/text-size-C6OFhCGi.js.map +1 -0
  17. package/dist/widget.js +1 -1
  18. package/dist/widget.js.map +1 -1
  19. package/package.json +1 -1
  20. package/dist/color-blind-0LFng55r.js +0 -108
  21. package/dist/color-blind-0LFng55r.js.map +0 -1
  22. package/dist/contrast-DCkE0NXZ.js +0 -64
  23. package/dist/contrast-DCkE0NXZ.js.map +0 -1
  24. package/dist/dyslexia-font-wONgIy2T.js +0 -77
  25. package/dist/dyslexia-font-wONgIy2T.js.map +0 -1
  26. package/dist/index-CUQfpnwR.js.map +0 -1
  27. package/dist/keyboard-nav-BdPyLaZt.js.map +0 -1
  28. package/dist/line-height-BT98qgEF.js +0 -54
  29. package/dist/line-height-BT98qgEF.js.map +0 -1
  30. package/dist/page-structure-2X8mOSpC.js.map +0 -1
  31. package/dist/saturation-D8ZXpWAN.js +0 -59
  32. package/dist/saturation-D8ZXpWAN.js.map +0 -1
  33. package/dist/spacing-DENai3JU.js +0 -106
  34. package/dist/spacing-DENai3JU.js.map +0 -1
  35. package/dist/text-align-BDRPqPvl.js +0 -51
  36. package/dist/text-align-BDRPqPvl.js.map +0 -1
  37. package/dist/text-size-B-uv436p.js +0 -69
  38. package/dist/text-size-B-uv436p.js.map +0 -1
@@ -1,3 +1,4 @@
1
+ import { t, g as getCurrentWidgetLang } from "./index-qmiN2JAz.js";
1
2
  function createKeyboardNavModule() {
2
3
  let enabled = false;
3
4
  const STYLE_ID = "accessify-keyboard-nav";
@@ -9,6 +10,9 @@ function createKeyboardNavModule() {
9
10
  let headings = [];
10
11
  let landmarks = [];
11
12
  let helpOverlayVisible = false;
13
+ function lang() {
14
+ return getCurrentWidgetLang();
15
+ }
12
16
  function findMainContent() {
13
17
  return document.querySelector("main") || document.getElementById("content") || document.getElementById("main-content") || document.querySelector('[role="main"]') || document.querySelector("h1");
14
18
  }
@@ -32,7 +36,7 @@ function createKeyboardNavModule() {
32
36
  const skipLink = document.createElement("a");
33
37
  skipLink.id = SKIP_LINK_ID;
34
38
  skipLink.href = `#${target.id}`;
35
- skipLink.textContent = "Skip to main content";
39
+ skipLink.textContent = t("keyboard.skipToContent", lang());
36
40
  skipLink.addEventListener("click", (e) => {
37
41
  e.preventDefault();
38
42
  target.setAttribute("tabindex", "-1");
@@ -100,7 +104,7 @@ function createKeyboardNavModule() {
100
104
  const overlay = document.createElement("div");
101
105
  overlay.id = OVERLAY_ID;
102
106
  overlay.setAttribute("role", "dialog");
103
- overlay.setAttribute("aria-label", "Keyboard shortcuts");
107
+ overlay.setAttribute("aria-label", t("keyboard.shortcuts", lang()));
104
108
  overlay.setAttribute("aria-modal", "true");
105
109
  overlay.innerHTML = `
106
110
  <div style="
@@ -113,41 +117,41 @@ function createKeyboardNavModule() {
113
117
  max-width: 480px; width: 90%; max-height: 80vh; overflow-y: auto;
114
118
  box-shadow: 0 8px 32px rgba(0,0,0,0.3); font-family: system-ui, sans-serif;
115
119
  " role="document">
116
- <h2 style="margin: 0 0 16px; font-size: 20px; color: #1a73e8;">Keyboard Shortcuts</h2>
120
+ <h2 style="margin: 0 0 16px; font-size: 20px; color: #1a73e8;">${t("keyboard.shortcuts", lang())}</h2>
117
121
  <table style="width: 100%; border-collapse: collapse; font-size: 14px;">
118
122
  <thead>
119
123
  <tr style="border-bottom: 2px solid #e0e0e0;">
120
- <th style="text-align: left; padding: 8px 12px;">Shortcut</th>
121
- <th style="text-align: left; padding: 8px 12px;">Action</th>
124
+ <th style="text-align: left; padding: 8px 12px;">${t("keyboard.shortcut", lang())}</th>
125
+ <th style="text-align: left; padding: 8px 12px;">${t("keyboard.action", lang())}</th>
122
126
  </tr>
123
127
  </thead>
124
128
  <tbody>
125
129
  <tr style="border-bottom: 1px solid #f0f0f0;">
126
130
  <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>
127
- <td style="padding: 8px 12px;">Navigate to next heading</td>
131
+ <td style="padding: 8px 12px;">${t("keyboard.nextHeading", lang())}</td>
128
132
  </tr>
129
133
  <tr style="border-bottom: 1px solid #f0f0f0;">
130
134
  <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>
131
- <td style="padding: 8px 12px;">Navigate to next landmark</td>
135
+ <td style="padding: 8px 12px;">${t("keyboard.nextLandmark", lang())}</td>
132
136
  </tr>
133
137
  <tr style="border-bottom: 1px solid #f0f0f0;">
134
138
  <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>
135
- <td style="padding: 8px 12px;">Toggle this help overlay</td>
139
+ <td style="padding: 8px 12px;">${t("keyboard.toggleHelp", lang())}</td>
136
140
  </tr>
137
141
  <tr style="border-bottom: 1px solid #f0f0f0;">
138
142
  <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>
139
- <td style="padding: 8px 12px;">Move to next focusable element</td>
143
+ <td style="padding: 8px 12px;">${t("keyboard.nextFocusable", lang())}</td>
140
144
  </tr>
141
145
  <tr>
142
146
  <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>
143
- <td style="padding: 8px 12px;">Close this overlay</td>
147
+ <td style="padding: 8px 12px;">${t("keyboard.closeOverlay", lang())}</td>
144
148
  </tr>
145
149
  </tbody>
146
150
  </table>
147
151
  <button style="
148
152
  margin-top: 20px; padding: 8px 24px; background: #1a73e8; color: #fff;
149
153
  border: none; border-radius: 6px; cursor: pointer; font-size: 14px;
150
- " id="accessify-keyboard-help-close">Close</button>
154
+ " id="accessify-keyboard-help-close">${t("widget.close", lang())}</button>
151
155
  </div>
152
156
  </div>
153
157
  `;
@@ -309,4 +313,4 @@ function createKeyboardNavModule() {
309
313
  export {
310
314
  createKeyboardNavModule as default
311
315
  };
312
- //# sourceMappingURL=keyboard-nav-BdPyLaZt.js.map
316
+ //# sourceMappingURL=keyboard-nav-DH4qBThF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keyboard-nav-DH4qBThF.js","sources":["../src/features/keyboard-nav.ts"],"sourcesContent":["import type { FeatureModule, FeatureState } from '../types';\nimport { getCurrentWidgetLang, t } from '../i18n/index';\n\nexport default function createKeyboardNavModule(): FeatureModule {\n let enabled = false;\n const STYLE_ID = 'accessify-keyboard-nav';\n const SKIP_LINK_ID = 'accessify-skip-link';\n const OVERLAY_ID = 'accessify-keyboard-help';\n const STORAGE_KEY = 'accessify-keyboard-nav';\n\n let headingIndex = -1;\n let landmarkIndex = -1;\n let headings: HTMLElement[] = [];\n let landmarks: HTMLElement[] = [];\n let helpOverlayVisible = false;\n\n function lang(): string {\n return getCurrentWidgetLang();\n }\n\n // --- Skip-to-content link ---\n\n function findMainContent(): HTMLElement | null {\n return (\n document.querySelector('main') ||\n document.getElementById('content') ||\n document.getElementById('main-content') ||\n document.querySelector('[role=\"main\"]') ||\n document.querySelector('h1')\n );\n }\n\n function hasSkipLink(): boolean {\n const links = document.querySelectorAll('a[href^=\"#\"]');\n for (const link of links) {\n const text = (link as HTMLElement).textContent?.toLowerCase() || '';\n if (text.includes('skip') || text.includes('main content') || text.includes('navigation')) {\n return true;\n }\n }\n return false;\n }\n\n function injectSkipLink() {\n if (hasSkipLink()) return;\n const target = findMainContent();\n if (!target) return;\n\n // Ensure target has an ID for the skip link to reference\n if (!target.id) {\n target.id = 'accessify-main-content';\n }\n\n const skipLink = document.createElement('a');\n skipLink.id = SKIP_LINK_ID;\n skipLink.href = `#${target.id}`;\n skipLink.textContent = t('keyboard.skipToContent', lang());\n skipLink.addEventListener('click', (e) => {\n e.preventDefault();\n target.setAttribute('tabindex', '-1');\n target.focus();\n target.scrollIntoView({ behavior: 'smooth', block: 'start' });\n });\n\n document.body.insertBefore(skipLink, document.body.firstChild);\n }\n\n function removeSkipLink() {\n const skipLink = document.getElementById(SKIP_LINK_ID);\n skipLink?.remove();\n }\n\n // --- Heading navigation (Alt+H) ---\n\n function collectHeadings() {\n headings = Array.from(\n document.querySelectorAll<HTMLElement>('h1, h2, h3, h4, h5, h6')\n ).filter((el) => {\n // Only visible headings\n const style = window.getComputedStyle(el);\n return style.display !== 'none' && style.visibility !== 'hidden';\n });\n }\n\n function navigateToNextHeading() {\n collectHeadings();\n if (headings.length === 0) return;\n headingIndex = (headingIndex + 1) % headings.length;\n const heading = headings[headingIndex];\n heading.setAttribute('tabindex', '-1');\n heading.focus();\n heading.scrollIntoView({ behavior: 'smooth', block: 'center' });\n }\n\n // --- Landmark navigation (Alt+L) ---\n\n function collectLandmarks() {\n const selectors = [\n 'header, [role=\"banner\"]',\n 'nav, [role=\"navigation\"]',\n 'main, [role=\"main\"]',\n 'aside, [role=\"complementary\"]',\n '[role=\"search\"]',\n '[role=\"form\"]',\n 'footer, [role=\"contentinfo\"]',\n '[role=\"region\"][aria-label]',\n ];\n landmarks = Array.from(\n document.querySelectorAll<HTMLElement>(selectors.join(', '))\n ).filter((el) => {\n const style = window.getComputedStyle(el);\n return style.display !== 'none' && style.visibility !== 'hidden';\n });\n }\n\n function navigateToNextLandmark() {\n collectLandmarks();\n if (landmarks.length === 0) return;\n landmarkIndex = (landmarkIndex + 1) % landmarks.length;\n const landmark = landmarks[landmarkIndex];\n landmark.setAttribute('tabindex', '-1');\n landmark.focus();\n landmark.scrollIntoView({ behavior: 'smooth', block: 'center' });\n }\n\n // --- Keyboard shortcut help overlay (Alt+K) ---\n\n function toggleHelpOverlay() {\n const existing = document.getElementById(OVERLAY_ID);\n if (existing) {\n existing.remove();\n helpOverlayVisible = false;\n return;\n }\n\n helpOverlayVisible = true;\n const overlay = document.createElement('div');\n overlay.id = OVERLAY_ID;\n overlay.setAttribute('role', 'dialog');\n overlay.setAttribute('aria-label', t('keyboard.shortcuts', lang()));\n overlay.setAttribute('aria-modal', 'true');\n\n overlay.innerHTML = `\n <div style=\"\n position: fixed; inset: 0; background: rgba(0,0,0,0.6);\n display: flex; align-items: center; justify-content: center;\n z-index: 2147483647;\n \">\n <div style=\"\n background: #fff; color: #222; border-radius: 12px; padding: 32px;\n max-width: 480px; width: 90%; max-height: 80vh; overflow-y: auto;\n box-shadow: 0 8px 32px rgba(0,0,0,0.3); font-family: system-ui, sans-serif;\n \" role=\"document\">\n <h2 style=\"margin: 0 0 16px; font-size: 20px; color: #1a73e8;\">${t('keyboard.shortcuts', lang())}</h2>\n <table style=\"width: 100%; border-collapse: collapse; font-size: 14px;\">\n <thead>\n <tr style=\"border-bottom: 2px solid #e0e0e0;\">\n <th style=\"text-align: left; padding: 8px 12px;\">${t('keyboard.shortcut', lang())}</th>\n <th style=\"text-align: left; padding: 8px 12px;\">${t('keyboard.action', lang())}</th>\n </tr>\n </thead>\n <tbody>\n <tr style=\"border-bottom: 1px solid #f0f0f0;\">\n <td style=\"padding: 8px 12px;\"><kbd style=\"background:#f5f5f5;padding:2px 8px;border-radius:4px;border:1px solid #ccc;font-family:monospace;\">Alt + H</kbd></td>\n <td style=\"padding: 8px 12px;\">${t('keyboard.nextHeading', lang())}</td>\n </tr>\n <tr style=\"border-bottom: 1px solid #f0f0f0;\">\n <td style=\"padding: 8px 12px;\"><kbd style=\"background:#f5f5f5;padding:2px 8px;border-radius:4px;border:1px solid #ccc;font-family:monospace;\">Alt + L</kbd></td>\n <td style=\"padding: 8px 12px;\">${t('keyboard.nextLandmark', lang())}</td>\n </tr>\n <tr style=\"border-bottom: 1px solid #f0f0f0;\">\n <td style=\"padding: 8px 12px;\"><kbd style=\"background:#f5f5f5;padding:2px 8px;border-radius:4px;border:1px solid #ccc;font-family:monospace;\">Alt + K</kbd></td>\n <td style=\"padding: 8px 12px;\">${t('keyboard.toggleHelp', lang())}</td>\n </tr>\n <tr style=\"border-bottom: 1px solid #f0f0f0;\">\n <td style=\"padding: 8px 12px;\"><kbd style=\"background:#f5f5f5;padding:2px 8px;border-radius:4px;border:1px solid #ccc;font-family:monospace;\">Tab</kbd></td>\n <td style=\"padding: 8px 12px;\">${t('keyboard.nextFocusable', lang())}</td>\n </tr>\n <tr>\n <td style=\"padding: 8px 12px;\"><kbd style=\"background:#f5f5f5;padding:2px 8px;border-radius:4px;border:1px solid #ccc;font-family:monospace;\">Escape</kbd></td>\n <td style=\"padding: 8px 12px;\">${t('keyboard.closeOverlay', lang())}</td>\n </tr>\n </tbody>\n </table>\n <button style=\"\n margin-top: 20px; padding: 8px 24px; background: #1a73e8; color: #fff;\n border: none; border-radius: 6px; cursor: pointer; font-size: 14px;\n \" id=\"accessify-keyboard-help-close\">${t('widget.close', lang())}</button>\n </div>\n </div>\n `;\n\n document.body.appendChild(overlay);\n\n // Focus management for the dialog\n const closeBtn = document.getElementById('accessify-keyboard-help-close');\n closeBtn?.focus();\n closeBtn?.addEventListener('click', () => {\n overlay.remove();\n helpOverlayVisible = false;\n });\n }\n\n // --- Tabindex injection for interactive elements ---\n\n function injectTabindex() {\n const selectors = [\n 'a[href]',\n 'button',\n 'input',\n 'select',\n 'textarea',\n '[role=\"button\"]',\n '[role=\"link\"]',\n '[role=\"checkbox\"]',\n '[role=\"radio\"]',\n '[role=\"tab\"]',\n '[role=\"menuitem\"]',\n '[role=\"switch\"]',\n '[contenteditable=\"true\"]',\n 'summary',\n ];\n\n const elements = document.querySelectorAll<HTMLElement>(selectors.join(', '));\n elements.forEach((el) => {\n // Only add tabindex if the element is not already focusable via native mechanism\n // and does not already have an explicit tabindex\n if (!el.hasAttribute('tabindex') && !isNativelyFocusable(el)) {\n el.setAttribute('tabindex', '0');\n el.dataset.accessifyTabindex = 'true';\n }\n });\n }\n\n function removeInjectedTabindex() {\n const elements = document.querySelectorAll<HTMLElement>('[data-accessify-tabindex=\"true\"]');\n elements.forEach((el) => {\n el.removeAttribute('tabindex');\n delete el.dataset.accessifyTabindex;\n });\n }\n\n function isNativelyFocusable(el: HTMLElement): boolean {\n const tag = el.tagName.toLowerCase();\n if (['a', 'button', 'input', 'select', 'textarea'].includes(tag)) {\n return true;\n }\n if (tag === 'a' && !(el as HTMLAnchorElement).href) {\n return false;\n }\n return false;\n }\n\n // --- Keyboard event handler ---\n\n function handleKeyDown(e: KeyboardEvent) {\n if (!enabled) return;\n\n // Close help overlay on Escape\n if (e.key === 'Escape' && helpOverlayVisible) {\n const overlay = document.getElementById(OVERLAY_ID);\n overlay?.remove();\n helpOverlayVisible = false;\n return;\n }\n\n if (!e.altKey) return;\n\n switch (e.key.toLowerCase()) {\n case 'h':\n e.preventDefault();\n navigateToNextHeading();\n break;\n case 'l':\n e.preventDefault();\n navigateToNextLandmark();\n break;\n case 'k':\n e.preventDefault();\n toggleHelpOverlay();\n break;\n }\n }\n\n // --- Styles for skip link ---\n\n function getStyles(): string {\n return `\n /* accessify keyboard navigation */\n #${SKIP_LINK_ID} {\n position: fixed;\n top: -100px;\n left: 50%;\n transform: translateX(-50%);\n background: #1a73e8;\n color: #fff;\n padding: 12px 24px;\n border-radius: 0 0 8px 8px;\n font-size: 16px;\n font-family: system-ui, sans-serif;\n text-decoration: none;\n z-index: 2147483647;\n transition: top 0.2s ease;\n box-shadow: 0 2px 8px rgba(0,0,0,0.3);\n }\n #${SKIP_LINK_ID}:focus {\n top: 0;\n outline: 3px solid #ffdd00;\n outline-offset: 2px;\n }\n `;\n }\n\n function injectStyles() {\n let styleEl = document.getElementById(STYLE_ID);\n if (!styleEl) {\n styleEl = document.createElement('style');\n styleEl.id = STYLE_ID;\n document.head.appendChild(styleEl);\n }\n styleEl.textContent = getStyles();\n }\n\n function removeStyles() {\n const styleEl = document.getElementById(STYLE_ID);\n styleEl?.remove();\n }\n\n // --- Module lifecycle ---\n\n function activate() {\n enabled = true;\n headingIndex = -1;\n landmarkIndex = -1;\n\n injectStyles();\n injectSkipLink();\n injectTabindex();\n document.addEventListener('keydown', handleKeyDown);\n localStorage.setItem(STORAGE_KEY, 'true');\n }\n\n function deactivate() {\n enabled = false;\n helpOverlayVisible = false;\n\n document.removeEventListener('keydown', handleKeyDown);\n removeSkipLink();\n removeInjectedTabindex();\n removeStyles();\n\n const overlay = document.getElementById(OVERLAY_ID);\n overlay?.remove();\n\n localStorage.removeItem(STORAGE_KEY);\n }\n\n return {\n id: 'keyboard-nav',\n name: () => 'Keyboard Navigation',\n description: 'Enhanced keyboard navigation with skip links, heading/landmark jumping, and shortcut help',\n icon: 'keyboard-nav',\n category: 'motor',\n activate,\n deactivate,\n getState: (): FeatureState => ({\n id: 'keyboard-nav',\n enabled,\n }),\n setState: (state: { enabled: boolean }) => {\n if (state.enabled) {\n activate();\n } else {\n deactivate();\n }\n },\n };\n}\n"],"names":[],"mappings":"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"}
@@ -1,7 +1,11 @@
1
+ import { t, g as getCurrentWidgetLang } from "./index-qmiN2JAz.js";
1
2
  function createPageStructureModule() {
2
3
  let enabled = false;
3
4
  let panelEl = null;
4
5
  const PANEL_ID = "accessify-page-structure";
6
+ function lang() {
7
+ return getCurrentWidgetLang();
8
+ }
5
9
  function collectHeadings() {
6
10
  const results = [];
7
11
  const headings = document.querySelectorAll("h1, h2, h3, h4, h5, h6");
@@ -42,7 +46,7 @@ function createPageStructureModule() {
42
46
  panelEl = document.createElement("div");
43
47
  panelEl.id = PANEL_ID;
44
48
  panelEl.setAttribute("role", "dialog");
45
- panelEl.setAttribute("aria-label", "Page Structure");
49
+ panelEl.setAttribute("aria-label", t("pageStructure.title", lang()));
46
50
  const headings = collectHeadings();
47
51
  const landmarks = collectLandmarks();
48
52
  const indent = {
@@ -91,11 +95,11 @@ function createPageStructureModule() {
91
95
  #${PANEL_ID} .ps-close:hover { background: #2a2a2e; }
92
96
  }
93
97
  </style>
94
- <button class="ps-close" aria-label="Close">&times;</button>
95
- <h3>Page Structure</h3>
98
+ <button class="ps-close" aria-label="${t("widget.close", lang())}">&times;</button>
99
+ <h3>${t("pageStructure.title", lang())}</h3>
96
100
  `;
97
101
  if (headings.length > 0) {
98
- html += '<div class="ps-section"><div class="ps-label">Headings</div>';
102
+ html += `<div class="ps-section"><div class="ps-label">${t("pageStructure.headings", lang())}</div>`;
99
103
  headings.forEach((h, i) => {
100
104
  html += `<button class="ps-item" data-type="heading" data-index="${i}" style="padding-left:${indent[h.tag] || "0"}">
101
105
  <span class="ps-tag">${h.tag}</span>${escapeHtml(h.text.slice(0, 80))}
@@ -104,7 +108,7 @@ function createPageStructureModule() {
104
108
  html += "</div>";
105
109
  }
106
110
  if (landmarks.length > 0) {
107
- html += '<div class="ps-section"><div class="ps-label">Landmarks</div>';
111
+ html += `<div class="ps-section"><div class="ps-label">${t("pageStructure.landmarks", lang())}</div>`;
108
112
  landmarks.forEach((lm, i) => {
109
113
  html += `<button class="ps-item" data-type="landmark" data-index="${i}">
110
114
  <span class="ps-tag">${escapeHtml(lm.role)}</span>${escapeHtml(lm.label.slice(0, 60))}
@@ -113,7 +117,7 @@ function createPageStructureModule() {
113
117
  html += "</div>";
114
118
  }
115
119
  if (headings.length === 0 && landmarks.length === 0) {
116
- html += '<p style="color:#888">No headings or landmarks found on this page.</p>';
120
+ html += `<p style="color:#888">${t("pageStructure.empty", lang())}</p>`;
117
121
  }
118
122
  panelEl.innerHTML = html;
119
123
  panelEl.addEventListener("click", (e) => {
@@ -163,4 +167,4 @@ function createPageStructureModule() {
163
167
  export {
164
168
  createPageStructureModule as default
165
169
  };
166
- //# sourceMappingURL=page-structure-2X8mOSpC.js.map
170
+ //# sourceMappingURL=page-structure-DTBqkrYs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-structure-DTBqkrYs.js","sources":["../src/features/page-structure.ts"],"sourcesContent":["import type { FeatureModule, FeatureState } from '../types';\nimport { getCurrentWidgetLang, t } from '../i18n/index';\n\nexport default function createPageStructureModule(): FeatureModule {\n let enabled = false;\n let panelEl: HTMLElement | null = null;\n const PANEL_ID = 'accessify-page-structure';\n\n function lang(): string {\n return getCurrentWidgetLang();\n }\n\n function collectHeadings(): Array<{ tag: string; text: string; el: HTMLElement }> {\n const results: Array<{ tag: string; text: string; el: HTMLElement }> = [];\n const headings = document.querySelectorAll<HTMLElement>('h1, h2, h3, h4, h5, h6');\n headings.forEach((el) => {\n if (el.closest('#accessify-root')) return;\n const text = el.textContent?.trim() || '';\n if (text) results.push({ tag: el.tagName.toLowerCase(), text, el });\n });\n return results;\n }\n\n function collectLandmarks(): Array<{ role: string; label: string; el: HTMLElement }> {\n const results: Array<{ role: string; label: string; el: HTMLElement }> = [];\n const selectors = [\n 'main', 'nav', 'aside', 'header', 'footer', 'section[aria-label]',\n 'section[aria-labelledby]', '[role=\"main\"]', '[role=\"navigation\"]',\n '[role=\"complementary\"]', '[role=\"banner\"]', '[role=\"contentinfo\"]',\n '[role=\"search\"]',\n ];\n const els = document.querySelectorAll<HTMLElement>(selectors.join(','));\n els.forEach((el) => {\n if (el.closest('#accessify-root')) return;\n const role = el.getAttribute('role') || el.tagName.toLowerCase();\n const label = el.getAttribute('aria-label') || el.getAttribute('aria-labelledby') || role;\n results.push({ role, label, el });\n });\n return results;\n }\n\n function buildPanel() {\n panelEl = document.createElement('div');\n panelEl.id = PANEL_ID;\n panelEl.setAttribute('role', 'dialog');\n panelEl.setAttribute('aria-label', t('pageStructure.title', lang()));\n\n const headings = collectHeadings();\n const landmarks = collectLandmarks();\n\n const indent: Record<string, string> = {\n h1: '0', h2: '12px', h3: '24px', h4: '36px', h5: '48px', h6: '60px',\n };\n\n let html = `\n <style>\n #${PANEL_ID} {\n position: fixed; top: 50%; left: 50%;\n transform: translate(-50%, -50%);\n z-index: 999998;\n background: #fff; color: #1a1a1a;\n border-radius: 12px; box-shadow: 0 8px 32px rgba(0,0,0,0.2);\n padding: 24px; width: 400px; max-height: 80vh;\n overflow-y: auto; font-family: system-ui, sans-serif;\n font-size: 14px; line-height: 1.5;\n }\n #${PANEL_ID} h3 { margin: 0 0 12px; font-size: 16px; }\n #${PANEL_ID} .ps-section { margin-bottom: 16px; }\n #${PANEL_ID} .ps-label { font-size: 11px; text-transform: uppercase; letter-spacing: 0.5px; color: #888; margin-bottom: 6px; }\n #${PANEL_ID} .ps-item {\n display: block; width: 100%; text-align: left;\n padding: 6px 8px; border: none; background: none;\n cursor: pointer; border-radius: 6px; font-size: 13px;\n color: #1a1a1a; font-family: inherit;\n }\n #${PANEL_ID} .ps-item:hover { background: #f0f0f2; }\n #${PANEL_ID} .ps-item:focus-visible { outline: 2px solid #0055CC; outline-offset: 1px; }\n #${PANEL_ID} .ps-tag { color: #888; font-size: 11px; margin-right: 6px; }\n #${PANEL_ID} .ps-close {\n position: absolute; top: 12px; right: 12px;\n background: none; border: none; cursor: pointer;\n font-size: 18px; color: #888; padding: 4px 8px; border-radius: 4px;\n }\n #${PANEL_ID} .ps-close:hover { background: #f0f0f2; }\n @media (prefers-color-scheme: dark) {\n #${PANEL_ID} { background: #1a1a1a; color: #f0f0f0; }\n #${PANEL_ID} .ps-item { color: #f0f0f0; }\n #${PANEL_ID} .ps-item:hover { background: #2a2a2e; }\n #${PANEL_ID} .ps-close { color: #aaa; }\n #${PANEL_ID} .ps-close:hover { background: #2a2a2e; }\n }\n </style>\n <button class=\"ps-close\" aria-label=\"${t('widget.close', lang())}\">&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 function activate() {\n enabled = true;\n buildPanel();\n }\n\n function deactivate() {\n enabled = false;\n panelEl?.remove();\n panelEl = null;\n }\n\n return {\n id: 'page-structure',\n name: () => 'Page Structure',\n description: 'View headings and landmarks for quick navigation',\n icon: 'page-structure',\n category: 'motor',\n activate,\n deactivate,\n getState: (): FeatureState => ({ id: 'page-structure', enabled }),\n };\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,186 @@
1
+ function createTextSizeModule() {
2
+ let currentSize = 100;
3
+ let observer = null;
4
+ let refreshQueued = false;
5
+ const STYLE_ID = "accessify-text-size-styles";
6
+ const STORAGE_KEY = "accessify-text-size";
7
+ const PROCESSED_ATTR = "data-accessify-text-size-processed";
8
+ const ORIGINAL_INLINE_ATTR = "data-accessify-original-inline-font-size";
9
+ const ORIGINAL_COMPUTED_ATTR = "data-accessify-original-computed-font-size";
10
+ const MIN_SIZE = 100;
11
+ const MAX_SIZE = 200;
12
+ const STEP = 10;
13
+ const SKIP_TAGS = /* @__PURE__ */ new Set([
14
+ "SCRIPT",
15
+ "STYLE",
16
+ "NOSCRIPT",
17
+ "IFRAME",
18
+ "SVG",
19
+ "PATH",
20
+ "G",
21
+ "DEFS",
22
+ "MASK",
23
+ "BR",
24
+ "META",
25
+ "LINK",
26
+ "HEAD"
27
+ ]);
28
+ function ensureStyleMarker(percent) {
29
+ let styleEl = document.getElementById(STYLE_ID);
30
+ if (!styleEl) {
31
+ styleEl = document.createElement("style");
32
+ styleEl.id = STYLE_ID;
33
+ document.head.appendChild(styleEl);
34
+ }
35
+ styleEl.textContent = `/* accessify text-size: ${percent}% */`;
36
+ }
37
+ function removeStyleMarker() {
38
+ document.getElementById(STYLE_ID)?.remove();
39
+ }
40
+ function hasOwnTextContent(el) {
41
+ return Array.from(el.childNodes).some((node) => {
42
+ return node.nodeType === Node.TEXT_NODE && Boolean(node.textContent?.trim());
43
+ });
44
+ }
45
+ function shouldScaleElement(el) {
46
+ if (el.id === "accessify-root" || el.closest("#accessify-root")) return false;
47
+ if (SKIP_TAGS.has(el.tagName)) return false;
48
+ const computed = window.getComputedStyle(el);
49
+ const fontSize = parseFloat(computed.fontSize);
50
+ if (!Number.isFinite(fontSize) || fontSize <= 0) return false;
51
+ if (computed.display === "none" || computed.visibility === "hidden") return false;
52
+ const interactiveTags = /* @__PURE__ */ new Set(["INPUT", "TEXTAREA", "SELECT", "BUTTON", "LABEL", "OPTION"]);
53
+ return interactiveTags.has(el.tagName) || hasOwnTextContent(el);
54
+ }
55
+ function getTargetElements() {
56
+ if (!document.body) return [];
57
+ const elements = [document.body, ...Array.from(document.body.querySelectorAll("*"))];
58
+ return elements.filter(shouldScaleElement);
59
+ }
60
+ function rememberOriginalSize(el) {
61
+ if (!el.hasAttribute(ORIGINAL_INLINE_ATTR)) {
62
+ el.setAttribute(ORIGINAL_INLINE_ATTR, el.style.fontSize || "");
63
+ }
64
+ if (!el.hasAttribute(ORIGINAL_COMPUTED_ATTR)) {
65
+ const computedSize = parseFloat(window.getComputedStyle(el).fontSize);
66
+ if (Number.isFinite(computedSize) && computedSize > 0) {
67
+ el.setAttribute(ORIGINAL_COMPUTED_ATTR, String(computedSize));
68
+ }
69
+ }
70
+ }
71
+ function scaleElement(el, scale) {
72
+ rememberOriginalSize(el);
73
+ const originalSize = parseFloat(el.getAttribute(ORIGINAL_COMPUTED_ATTR) || "");
74
+ if (!Number.isFinite(originalSize) || originalSize <= 0) return;
75
+ const nextSize = Math.round(originalSize * scale * 100) / 100;
76
+ el.style.setProperty("font-size", `${nextSize}px`, "important");
77
+ el.setAttribute(PROCESSED_ATTR, "true");
78
+ }
79
+ function restoreElement(el) {
80
+ const originalInline = el.getAttribute(ORIGINAL_INLINE_ATTR);
81
+ if (originalInline === null) return;
82
+ if (originalInline) {
83
+ el.style.fontSize = originalInline;
84
+ } else {
85
+ el.style.removeProperty("font-size");
86
+ }
87
+ el.removeAttribute(PROCESSED_ATTR);
88
+ el.removeAttribute(ORIGINAL_INLINE_ATTR);
89
+ el.removeAttribute(ORIGINAL_COMPUTED_ATTR);
90
+ }
91
+ function restoreProcessedElements() {
92
+ document.querySelectorAll(`[${PROCESSED_ATTR}]`).forEach((el) => {
93
+ restoreElement(el);
94
+ });
95
+ }
96
+ function queueRefresh() {
97
+ if (refreshQueued || currentSize === 100) return;
98
+ refreshQueued = true;
99
+ const flush = () => {
100
+ refreshQueued = false;
101
+ applySize(currentSize);
102
+ };
103
+ if (typeof window.requestAnimationFrame === "function") {
104
+ window.requestAnimationFrame(() => flush());
105
+ } else {
106
+ window.setTimeout(flush, 0);
107
+ }
108
+ }
109
+ function startObserver() {
110
+ if (observer || !document.body) return;
111
+ observer = new MutationObserver((mutations) => {
112
+ for (const mutation of mutations) {
113
+ if (mutation.type === "childList" && (mutation.addedNodes.length > 0 || mutation.removedNodes.length > 0)) {
114
+ queueRefresh();
115
+ return;
116
+ }
117
+ }
118
+ });
119
+ observer.observe(document.body, {
120
+ childList: true,
121
+ subtree: true
122
+ });
123
+ }
124
+ function stopObserver() {
125
+ observer?.disconnect();
126
+ observer = null;
127
+ refreshQueued = false;
128
+ }
129
+ function applySize(percent) {
130
+ if (percent === 100) {
131
+ stopObserver();
132
+ removeStyleMarker();
133
+ restoreProcessedElements();
134
+ return;
135
+ }
136
+ ensureStyleMarker(percent);
137
+ const scale = percent / 100;
138
+ const targets = new Set(getTargetElements());
139
+ document.querySelectorAll(`[${PROCESSED_ATTR}]`).forEach((el) => {
140
+ if (!targets.has(el)) {
141
+ restoreElement(el);
142
+ }
143
+ });
144
+ targets.forEach((el) => {
145
+ scaleElement(el, scale);
146
+ });
147
+ startObserver();
148
+ }
149
+ function clamp(value) {
150
+ return Math.min(MAX_SIZE, Math.max(MIN_SIZE, Math.round(value / STEP) * STEP));
151
+ }
152
+ function activate() {
153
+ const saved = localStorage.getItem(STORAGE_KEY);
154
+ currentSize = saved ? clamp(parseInt(saved, 10)) : 150;
155
+ applySize(currentSize);
156
+ localStorage.setItem(STORAGE_KEY, String(currentSize));
157
+ }
158
+ function deactivate() {
159
+ currentSize = 100;
160
+ applySize(100);
161
+ localStorage.removeItem(STORAGE_KEY);
162
+ }
163
+ return {
164
+ id: "text-size",
165
+ name: () => "Text Size",
166
+ description: "Adjust text size from 100% to 200%",
167
+ icon: "text-size",
168
+ category: "visual",
169
+ activate,
170
+ deactivate,
171
+ getState: () => ({
172
+ id: "text-size",
173
+ enabled: currentSize !== 100,
174
+ value: currentSize
175
+ }),
176
+ setState: (size) => {
177
+ currentSize = clamp(size);
178
+ applySize(currentSize);
179
+ localStorage.setItem(STORAGE_KEY, String(currentSize));
180
+ }
181
+ };
182
+ }
183
+ export {
184
+ createTextSizeModule as default
185
+ };
186
+ //# sourceMappingURL=text-size-C6OFhCGi.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text-size-C6OFhCGi.js","sources":["../src/features/text-size.ts"],"sourcesContent":["import type { FeatureModule, FeatureState } from '../types';\n\nexport default function createTextSizeModule(): FeatureModule {\n let currentSize = 100;\n let observer: MutationObserver | null = null;\n let refreshQueued = false;\n const STYLE_ID = 'accessify-text-size-styles';\n const STORAGE_KEY = 'accessify-text-size';\n const PROCESSED_ATTR = 'data-accessify-text-size-processed';\n const ORIGINAL_INLINE_ATTR = 'data-accessify-original-inline-font-size';\n const ORIGINAL_COMPUTED_ATTR = 'data-accessify-original-computed-font-size';\n const MIN_SIZE = 100;\n const MAX_SIZE = 200;\n const STEP = 10;\n const SKIP_TAGS = new Set([\n 'SCRIPT',\n 'STYLE',\n 'NOSCRIPT',\n 'IFRAME',\n 'SVG',\n 'PATH',\n 'G',\n 'DEFS',\n 'MASK',\n 'BR',\n 'META',\n 'LINK',\n 'HEAD',\n ]);\n\n function ensureStyleMarker(percent: number) {\n let styleEl = document.getElementById(STYLE_ID) as HTMLStyleElement | null;\n if (!styleEl) {\n styleEl = document.createElement('style');\n styleEl.id = STYLE_ID;\n document.head.appendChild(styleEl);\n }\n styleEl.textContent = `/* accessify text-size: ${percent}% */`;\n }\n\n function removeStyleMarker() {\n document.getElementById(STYLE_ID)?.remove();\n }\n\n function hasOwnTextContent(el: HTMLElement): boolean {\n return Array.from(el.childNodes).some((node) => {\n return node.nodeType === Node.TEXT_NODE && Boolean(node.textContent?.trim());\n });\n }\n\n function shouldScaleElement(el: HTMLElement): boolean {\n if (el.id === 'accessify-root' || el.closest('#accessify-root')) return false;\n if (SKIP_TAGS.has(el.tagName)) return false;\n\n const computed = window.getComputedStyle(el);\n const fontSize = parseFloat(computed.fontSize);\n if (!Number.isFinite(fontSize) || fontSize <= 0) return false;\n if (computed.display === 'none' || computed.visibility === 'hidden') return false;\n\n const interactiveTags = new Set(['INPUT', 'TEXTAREA', 'SELECT', 'BUTTON', 'LABEL', 'OPTION']);\n return interactiveTags.has(el.tagName) || hasOwnTextContent(el);\n }\n\n function getTargetElements(): HTMLElement[] {\n if (!document.body) return [];\n const elements = [document.body, ...Array.from(document.body.querySelectorAll<HTMLElement>('*'))];\n return elements.filter(shouldScaleElement);\n }\n\n function rememberOriginalSize(el: HTMLElement) {\n if (!el.hasAttribute(ORIGINAL_INLINE_ATTR)) {\n el.setAttribute(ORIGINAL_INLINE_ATTR, el.style.fontSize || '');\n }\n\n if (!el.hasAttribute(ORIGINAL_COMPUTED_ATTR)) {\n const computedSize = parseFloat(window.getComputedStyle(el).fontSize);\n if (Number.isFinite(computedSize) && computedSize > 0) {\n el.setAttribute(ORIGINAL_COMPUTED_ATTR, String(computedSize));\n }\n }\n }\n\n function scaleElement(el: HTMLElement, scale: number) {\n rememberOriginalSize(el);\n\n const originalSize = parseFloat(el.getAttribute(ORIGINAL_COMPUTED_ATTR) || '');\n if (!Number.isFinite(originalSize) || originalSize <= 0) return;\n\n const nextSize = Math.round(originalSize * scale * 100) / 100;\n el.style.setProperty('font-size', `${nextSize}px`, 'important');\n el.setAttribute(PROCESSED_ATTR, 'true');\n }\n\n function restoreElement(el: HTMLElement) {\n const originalInline = el.getAttribute(ORIGINAL_INLINE_ATTR);\n if (originalInline === null) return;\n\n if (originalInline) {\n el.style.fontSize = originalInline;\n } else {\n el.style.removeProperty('font-size');\n }\n\n el.removeAttribute(PROCESSED_ATTR);\n el.removeAttribute(ORIGINAL_INLINE_ATTR);\n el.removeAttribute(ORIGINAL_COMPUTED_ATTR);\n }\n\n function restoreProcessedElements() {\n document.querySelectorAll<HTMLElement>(`[${PROCESSED_ATTR}]`).forEach((el) => {\n restoreElement(el);\n });\n }\n\n function queueRefresh() {\n if (refreshQueued || currentSize === 100) return;\n\n refreshQueued = true;\n const flush = () => {\n refreshQueued = false;\n applySize(currentSize);\n };\n\n if (typeof window.requestAnimationFrame === 'function') {\n window.requestAnimationFrame(() => flush());\n } else {\n window.setTimeout(flush, 0);\n }\n }\n\n function startObserver() {\n if (observer || !document.body) return;\n\n observer = new MutationObserver((mutations) => {\n for (const mutation of mutations) {\n if (mutation.type === 'childList' && (mutation.addedNodes.length > 0 || mutation.removedNodes.length > 0)) {\n queueRefresh();\n return;\n }\n }\n });\n\n observer.observe(document.body, {\n childList: true,\n subtree: true,\n });\n }\n\n function stopObserver() {\n observer?.disconnect();\n observer = null;\n refreshQueued = false;\n }\n\n function applySize(percent: number) {\n if (percent === 100) {\n stopObserver();\n removeStyleMarker();\n restoreProcessedElements();\n return;\n }\n\n ensureStyleMarker(percent);\n\n const scale = percent / 100;\n const targets = new Set(getTargetElements());\n\n document.querySelectorAll<HTMLElement>(`[${PROCESSED_ATTR}]`).forEach((el) => {\n if (!targets.has(el)) {\n restoreElement(el);\n }\n });\n\n targets.forEach((el) => {\n scaleElement(el, scale);\n });\n\n startObserver();\n }\n\n function clamp(value: number): number {\n return Math.min(MAX_SIZE, Math.max(MIN_SIZE, Math.round(value / STEP) * STEP));\n }\n\n function activate() {\n const saved = localStorage.getItem(STORAGE_KEY);\n currentSize = saved ? clamp(parseInt(saved, 10)) : 150;\n applySize(currentSize);\n localStorage.setItem(STORAGE_KEY, String(currentSize));\n }\n\n function deactivate() {\n currentSize = 100;\n applySize(100);\n localStorage.removeItem(STORAGE_KEY);\n }\n\n return {\n id: 'text-size',\n name: () => 'Text Size',\n description: 'Adjust text size from 100% to 200%',\n icon: 'text-size',\n category: 'visual',\n activate,\n deactivate,\n getState: (): FeatureState => ({\n id: 'text-size',\n enabled: currentSize !== 100,\n value: currentSize,\n }),\n setState: (size: number) => {\n currentSize = clamp(size);\n applySize(currentSize);\n localStorage.setItem(STORAGE_KEY, String(currentSize));\n },\n };\n}\n"],"names":[],"mappings":"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"}