@obinexusltd/obix-driver-accessibility-tree 0.1.0 → 0.1.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 (50) hide show
  1. package/README.md +380 -0
  2. package/dist/accessibility-tree-mirror.d.ts +26 -0
  3. package/dist/accessibility-tree-mirror.d.ts.map +1 -0
  4. package/dist/accessibility-tree-mirror.js +173 -0
  5. package/dist/accessibility-tree-mirror.js.map +1 -0
  6. package/dist/aria-widget-compliance.d.ts +42 -0
  7. package/dist/aria-widget-compliance.d.ts.map +1 -0
  8. package/dist/aria-widget-compliance.js +291 -0
  9. package/dist/aria-widget-compliance.js.map +1 -0
  10. package/dist/axe-core-integration.d.ts +30 -0
  11. package/dist/axe-core-integration.d.ts.map +1 -0
  12. package/dist/axe-core-integration.js +85 -0
  13. package/dist/axe-core-integration.js.map +1 -0
  14. package/dist/focus-management.d.ts +26 -0
  15. package/dist/focus-management.d.ts.map +1 -0
  16. package/dist/focus-management.js +138 -0
  17. package/dist/focus-management.js.map +1 -0
  18. package/dist/framework-integration.d.ts +45 -0
  19. package/dist/framework-integration.d.ts.map +1 -0
  20. package/dist/framework-integration.js +110 -0
  21. package/dist/framework-integration.js.map +1 -0
  22. package/dist/index.d.ts +12 -32
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +154 -15
  25. package/dist/index.js.map +1 -1
  26. package/dist/keyboard-navigation.d.ts +29 -0
  27. package/dist/keyboard-navigation.d.ts.map +1 -0
  28. package/dist/keyboard-navigation.js +182 -0
  29. package/dist/keyboard-navigation.js.map +1 -0
  30. package/dist/live-region-manager.d.ts +25 -0
  31. package/dist/live-region-manager.d.ts.map +1 -0
  32. package/dist/live-region-manager.js +123 -0
  33. package/dist/live-region-manager.js.map +1 -0
  34. package/dist/screen-reader-bridge.d.ts +25 -0
  35. package/dist/screen-reader-bridge.d.ts.map +1 -0
  36. package/dist/screen-reader-bridge.js +71 -0
  37. package/dist/screen-reader-bridge.js.map +1 -0
  38. package/dist/semantic-html-enhancer.d.ts +36 -0
  39. package/dist/semantic-html-enhancer.d.ts.map +1 -0
  40. package/dist/semantic-html-enhancer.js +140 -0
  41. package/dist/semantic-html-enhancer.js.map +1 -0
  42. package/dist/state-properties-manager.d.ts +19 -0
  43. package/dist/state-properties-manager.d.ts.map +1 -0
  44. package/dist/state-properties-manager.js +94 -0
  45. package/dist/state-properties-manager.js.map +1 -0
  46. package/dist/types.d.ts +49 -0
  47. package/dist/types.d.ts.map +1 -0
  48. package/dist/types.js +5 -0
  49. package/dist/types.js.map +1 -0
  50. package/package.json +9 -1
package/README.md ADDED
@@ -0,0 +1,380 @@
1
+ # @obinexusltd/obix-driver-accessibility-tree
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@obinexusltd/obix-driver-accessibility-tree)](https://www.npmjs.com/package/@obinexusltd/obix-driver-accessibility-tree)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+
6
+ A full-featured accessibility driver for the [OBIX SDK](https://github.com/OBINexusComputing/obix-sdk). Solves 10 core browser accessibility problems: live region management, screen reader detection, accessibility tree mirroring, ARIA widget compliance, semantic HTML enhancement, keyboard navigation, ARIA state management, focus trapping, axe-core validation, and framework-level interaction mode detection.
7
+
8
+ ## Installation
9
+
10
+ ```bash
11
+ npm install @obinexusltd/obix-driver-accessibility-tree
12
+
13
+ # Optional: enable axe-core validation
14
+ npm install axe-core
15
+ ```
16
+
17
+ ## Quick Start
18
+
19
+ ```typescript
20
+ import { createAccessibilityTreeDriver } from '@obinexusltd/obix-driver-accessibility-tree';
21
+
22
+ const driver = createAccessibilityTreeDriver({
23
+ rootElement: document.getElementById('app')!,
24
+ liveRegionDefaults: { level: 'polite', atomic: true },
25
+ screenReaderHints: true,
26
+ });
27
+
28
+ await driver.initialize();
29
+
30
+ // Announce a message to screen readers
31
+ driver.announce('Your changes have been saved.', 'polite');
32
+
33
+ // Trap focus inside a dialog
34
+ const trap = driver.focusManager.createFocusTrap(dialogElement);
35
+ trap.activate();
36
+
37
+ // Clean up
38
+ await driver.destroy();
39
+ ```
40
+
41
+ ## API Reference
42
+
43
+ ### Driver Lifecycle
44
+
45
+ ```typescript
46
+ const driver = createAccessibilityTreeDriver(config?: AccessibilityTreeDriverConfig);
47
+
48
+ await driver.initialize(); // Lazy-initialize all sub-features
49
+ await driver.destroy(); // Tear down all features and release resources
50
+ ```
51
+
52
+ **Config options:**
53
+
54
+ | Field | Type | Default | Description |
55
+ |-------|------|---------|-------------|
56
+ | `rootElement` | `HTMLElement` | `document.body` | Root element to observe |
57
+ | `liveRegionDefaults.level` | `'off' \| 'polite' \| 'assertive'` | `'polite'` | Default aria-live level |
58
+ | `liveRegionDefaults.atomic` | `boolean` | `false` | Default aria-atomic value |
59
+ | `liveRegionDefaults.relevant` | `string` | `'additions text'` | Default aria-relevant value |
60
+ | `liveRegionDefaults.label` | `string` | `undefined` | Default aria-label for regions |
61
+ | `screenReaderHints` | `boolean` | `false` | Enable screen reader detection heuristics |
62
+
63
+ ---
64
+
65
+ ### Live Region Manager
66
+
67
+ Manages `aria-live` regions for dynamic content announcements. Creates visually hidden elements that are accessible to screen readers.
68
+
69
+ ```typescript
70
+ // Access after initialize()
71
+ const { liveRegions } = driver;
72
+
73
+ // Create a named live region
74
+ const region = liveRegions.createRegion('status', { level: 'polite', atomic: true });
75
+
76
+ // Announce via a specific region
77
+ region.announce('Item deleted.');
78
+
79
+ // Announce globally (uses the default region)
80
+ liveRegions.announceGlobal('Page loaded.', 'assertive');
81
+
82
+ // Inspect active regions
83
+ const active = liveRegions.getActiveRegions();
84
+ ```
85
+
86
+ ---
87
+
88
+ ### Screen Reader Bridge
89
+
90
+ Browser-safe screen reader detection using media queries (no intrusive heuristics). Creates dedicated regions for polite, assertive, and route-change announcements.
91
+
92
+ ```typescript
93
+ const { screenReaderBridge } = driver;
94
+
95
+ // Detect screen reader signals
96
+ const result = screenReaderBridge.detect();
97
+ result.likely; // boolean — screen reader probably active
98
+ result.signals; // string[] — detected signals (forced-colors, prefers-contrast, etc.)
99
+
100
+ // Make announcements
101
+ screenReaderBridge.announcePolite('Form submitted successfully.');
102
+ screenReaderBridge.announceAssertive('Error: required field missing.');
103
+ screenReaderBridge.announceRouteChange('/dashboard');
104
+ ```
105
+
106
+ ---
107
+
108
+ ### Accessibility Tree Mirror
109
+
110
+ Real-time DOM-to-accessibility-tree synchronization via `MutationObserver`. Maps HTML semantics to ARIA roles and diffs the tree on every mutation.
111
+
112
+ ```typescript
113
+ const { treeMirror } = driver;
114
+
115
+ // Start observing
116
+ treeMirror.observe(document.getElementById('app')!);
117
+
118
+ // Snapshot the current tree
119
+ const tree = treeMirror.snapshot();
120
+
121
+ // Diff since last snapshot
122
+ const changes = treeMirror.diff();
123
+
124
+ // Serialize to JSON
125
+ const json = treeMirror.serialize();
126
+
127
+ // React to tree changes
128
+ treeMirror.onTreeChange((changes) => {
129
+ console.log('tree updated', changes);
130
+ });
131
+ ```
132
+
133
+ ---
134
+
135
+ ### ARIA Widget Compliance
136
+
137
+ W3C ARIA Authoring Practices pattern validation and auto-configuration. Applies correct ARIA attributes for common widget patterns.
138
+
139
+ **Supported patterns:**
140
+
141
+ | Pattern | Widget Type |
142
+ |---------|-------------|
143
+ | `accordion` | Expandable sections |
144
+ | `menu-bar` | Horizontal menu |
145
+ | `slider` | Range input |
146
+ | `tabs` | Tab panel group |
147
+ | `dialog` | Modal dialog |
148
+ | `listbox` | Selection list |
149
+ | `tree` | Hierarchical list |
150
+ | `combobox` | Autocomplete input |
151
+
152
+ ```typescript
153
+ const { widgetCompliance } = driver;
154
+
155
+ // Apply a pattern to an element
156
+ widgetCompliance.applyPattern(element, 'tabs');
157
+
158
+ // Validate an element against its declared pattern
159
+ const result = widgetCompliance.validate(element);
160
+ result.valid; // boolean
161
+ result.errors; // string[]
162
+ result.warnings; // string[]
163
+ ```
164
+
165
+ ---
166
+
167
+ ### Semantic HTML Enhancer
168
+
169
+ Auto-injects missing ARIA attributes based on built-in rules. Detects and fixes common accessibility issues such as clickable `div` elements without a role, missing `alt` text, and unlabeled inputs.
170
+
171
+ ```typescript
172
+ const { semanticEnhancer } = driver;
173
+
174
+ // One-shot scan of the root element
175
+ const report = semanticEnhancer.scan();
176
+ report.fixed; // number — attributes auto-applied
177
+ report.skipped; // number — elements that needed manual review
178
+
179
+ // Continuously observe and fix new content
180
+ semanticEnhancer.observe(document.body);
181
+
182
+ // Add a custom rule
183
+ semanticEnhancer.addRule({
184
+ selector: '[data-tooltip]',
185
+ apply: (el) => { el.setAttribute('aria-describedby', el.dataset.tooltip!); },
186
+ });
187
+
188
+ // Inspect the built-in rule set
189
+ const rules = semanticEnhancer.getBuiltinRules();
190
+ ```
191
+
192
+ ---
193
+
194
+ ### Keyboard Navigation Controller
195
+
196
+ Implements ARIA keyboard navigation patterns with arrow-key movement, Home/End support, and optional type-ahead.
197
+
198
+ **Supported patterns:** `roving-tabindex`, `activedescendant`, `grid`
199
+
200
+ ```typescript
201
+ const { keyboardNav } = driver;
202
+
203
+ // Create a named navigation context
204
+ const nav = keyboardNav.createNavigation('my-listbox', {
205
+ pattern: 'roving-tabindex',
206
+ orientation: 'vertical',
207
+ wrap: true,
208
+ typeAhead: true,
209
+ });
210
+
211
+ nav.mount(containerElement);
212
+
213
+ // Programmatic movement
214
+ nav.moveTo(itemElement);
215
+ nav.moveNext();
216
+ nav.movePrev();
217
+ nav.moveFirst();
218
+ nav.moveLast();
219
+
220
+ const current = nav.getCurrentItem();
221
+
222
+ nav.unmount();
223
+ ```
224
+
225
+ ---
226
+
227
+ ### ARIA State Manager
228
+
229
+ Reactive ARIA state management with batch update support. Tracks element-to-state mappings and emits change events.
230
+
231
+ ```typescript
232
+ const { stateManager } = driver;
233
+
234
+ // Set ARIA state on an element
235
+ stateManager.setState(buttonEl, { expanded: true, disabled: false });
236
+
237
+ // Read current state
238
+ const state = stateManager.getState(buttonEl);
239
+
240
+ // Read all tracked states
241
+ const all = stateManager.getAllStates();
242
+
243
+ // Subscribe to changes
244
+ const unsubscribe = stateManager.onStateChange((element, newState) => {
245
+ console.log('state changed', element, newState);
246
+ });
247
+
248
+ unsubscribe(); // remove listener
249
+ ```
250
+
251
+ ---
252
+
253
+ ### Focus Management
254
+
255
+ Programmatic focus control with a save/restore stack and focus trap support. Escape key automatically deactivates traps.
256
+
257
+ ```typescript
258
+ const { focusManager } = driver;
259
+
260
+ // Move focus to an element
261
+ focusManager.moveFocus(targetElement);
262
+
263
+ // Save the current focus position
264
+ focusManager.saveFocus();
265
+
266
+ // Restore last saved focus
267
+ focusManager.restoreFocus();
268
+
269
+ // Create and activate a focus trap (e.g. inside a modal)
270
+ const trap = focusManager.createFocusTrap(dialogElement);
271
+ trap.activate(); // Tab/Shift+Tab cycle inside dialogElement, Escape deactivates
272
+ trap.deactivate(); // Release trap and return focus to trigger
273
+ ```
274
+
275
+ ---
276
+
277
+ ### axe-core Integration
278
+
279
+ Optional runtime ARIA validation using axe-core (peer dependency). Lazy-loads axe-core on first use; runs 18 default ARIA-specific rules.
280
+
281
+ ```typescript
282
+ // Requires: npm install axe-core
283
+ const { axeIntegration } = driver;
284
+
285
+ // Check if axe-core is available
286
+ const available = await axeIntegration.isAvailable();
287
+
288
+ // Run on the whole document
289
+ const results = await axeIntegration.run();
290
+ results.violations; // AxeViolation[]
291
+ results.passes; // AxePass[]
292
+ results.incomplete; // AxeIncomplete[]
293
+
294
+ // Run on a specific element
295
+ const nodeResults = await axeIntegration.runOnNode(formElement);
296
+ ```
297
+
298
+ ---
299
+
300
+ ### Framework Integration
301
+
302
+ Unified facade for framework-level accessibility. Detects interaction mode, manages navigation landmarks, and provides an accessibility summary.
303
+
304
+ ```typescript
305
+ const { framework } = driver;
306
+
307
+ // Detect how the user is currently interacting
308
+ const mode = framework.detectInteractionMode();
309
+ // 'keyboard' | 'pointer' | 'touch' | 'screen-reader'
310
+
311
+ // Register a navigation pathway
312
+ framework.registerPathway('main-nav', {
313
+ landmark: 'navigation',
314
+ element: navElement,
315
+ label: 'Main Navigation',
316
+ });
317
+
318
+ // Jump to a landmark
319
+ framework.navigateToLandmark('main');
320
+
321
+ // Get an accessibility health summary
322
+ const summary = framework.getAccessibilitySummary();
323
+ summary.interactionMode; // current mode
324
+ summary.landmarks; // registered landmark count
325
+ summary.liveRegions; // active region count
326
+ ```
327
+
328
+ ---
329
+
330
+ ### Sub-Module Accessors
331
+
332
+ Direct access to underlying sub-modules after `initialize()`:
333
+
334
+ ```typescript
335
+ driver.liveRegions // LiveRegionManagerAPI
336
+ driver.screenReaderBridge // ScreenReaderBridgeAPI
337
+ driver.treeMirror // TreeMirrorAPI
338
+ driver.widgetCompliance // WidgetComplianceAPI
339
+ driver.semanticEnhancer // SemanticEnhancerAPI
340
+ driver.keyboardNav // KeyboardNavigationAPI
341
+ driver.stateManager // AriaStateManagerAPI
342
+ driver.focusManager // FocusManagerAPI
343
+ driver.axeIntegration // AxeIntegrationAPI
344
+ driver.framework // FrameworkIntegrationAPI
345
+ ```
346
+
347
+ ---
348
+
349
+ ## Architecture
350
+
351
+ | Module | Responsibility |
352
+ |--------|---------------|
353
+ | `live-region-manager` | aria-live region creation and announcements |
354
+ | `screen-reader-bridge` | Screen reader detection via media queries |
355
+ | `accessibility-tree-mirror` | MutationObserver-based DOM-to-ARIA tree sync |
356
+ | `aria-widget-compliance` | W3C ARIA pattern validation and auto-config |
357
+ | `semantic-html-enhancer` | Auto-inject missing ARIA from built-in rules |
358
+ | `keyboard-navigation` | Roving tabindex / activedescendant / grid patterns |
359
+ | `state-properties-manager` | Reactive ARIA state with change events |
360
+ | `focus-management` | Focus trap, save/restore, programmatic movement |
361
+ | `axe-core-integration` | Optional runtime validation via axe-core |
362
+ | `framework-integration` | Interaction mode, landmarks, accessibility summary |
363
+ | `types` | All shared TypeScript interfaces |
364
+
365
+ ---
366
+
367
+ ## Environment Support
368
+
369
+ | Environment | Support |
370
+ |-------------|---------|
371
+ | Browser | Full — MutationObserver, media queries, focus APIs |
372
+ | Node.js / SSR | Safe import — no `window`/`document` access at load time |
373
+ | Deno | Graceful no-op — `isBrowser: false` |
374
+ | jsdom (test) | Full — all features work in JSDOM (Vitest) |
375
+
376
+ ---
377
+
378
+ ## License
379
+
380
+ MIT — OBINexus <okpalan@protonmail.com>
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Feature 4: Accessibility Tree Mirror
3
+ * Real-time DOM-to-Accessibility Tree synchronization with MutationObserver
4
+ */
5
+ import type { AccessibilityNode, Disposable } from "./types.js";
6
+ export interface TreeMirrorConfig {
7
+ rootElement: Element;
8
+ observe?: boolean;
9
+ subtreeFilter?: (element: Element) => boolean;
10
+ }
11
+ export interface TreeDiff {
12
+ type: "added" | "removed" | "changed";
13
+ path: string;
14
+ oldNode?: AccessibilityNode;
15
+ newNode?: AccessibilityNode;
16
+ }
17
+ export interface TreeMirrorAPI extends Disposable {
18
+ observe(): void;
19
+ disconnect(): void;
20
+ snapshot(): AccessibilityNode;
21
+ diff(previous: AccessibilityNode, current: AccessibilityNode): TreeDiff[];
22
+ serialize(): string;
23
+ onTreeChange(handler: (diffs: TreeDiff[]) => void): () => void;
24
+ }
25
+ export declare function createAccessibilityTreeMirror(config: TreeMirrorConfig): TreeMirrorAPI;
26
+ //# sourceMappingURL=accessibility-tree-mirror.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"accessibility-tree-mirror.d.ts","sourceRoot":"","sources":["../src/accessibility-tree-mirror.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AA2ChE,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,OAAO,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC;CAC/C;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,OAAO,GAAG,SAAS,GAAG,SAAS,CAAC;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,iBAAiB,CAAC;IAC5B,OAAO,CAAC,EAAE,iBAAiB,CAAC;CAC7B;AAED,MAAM,WAAW,aAAc,SAAQ,UAAU;IAC/C,OAAO,IAAI,IAAI,CAAC;IAChB,UAAU,IAAI,IAAI,CAAC;IACnB,QAAQ,IAAI,iBAAiB,CAAC;IAC9B,IAAI,CAAC,QAAQ,EAAE,iBAAiB,EAAE,OAAO,EAAE,iBAAiB,GAAG,QAAQ,EAAE,CAAC;IAC1E,SAAS,IAAI,MAAM,CAAC;IACpB,YAAY,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,QAAQ,EAAE,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;CAChE;AA6ED,wBAAgB,6BAA6B,CAC3C,MAAM,EAAE,gBAAgB,GACvB,aAAa,CA4Ef"}
@@ -0,0 +1,173 @@
1
+ /**
2
+ * Feature 4: Accessibility Tree Mirror
3
+ * Real-time DOM-to-Accessibility Tree synchronization with MutationObserver
4
+ */
5
+ /** Implied ARIA role mapping from HTML tag names */
6
+ const IMPLIED_ROLES = {
7
+ a: "link",
8
+ article: "article",
9
+ aside: "complementary",
10
+ button: "button",
11
+ details: "group",
12
+ dialog: "dialog",
13
+ footer: "contentinfo",
14
+ form: "form",
15
+ h1: "heading",
16
+ h2: "heading",
17
+ h3: "heading",
18
+ h4: "heading",
19
+ h5: "heading",
20
+ h6: "heading",
21
+ header: "banner",
22
+ hr: "separator",
23
+ img: "img",
24
+ input: "textbox",
25
+ li: "listitem",
26
+ main: "main",
27
+ menu: "menu",
28
+ nav: "navigation",
29
+ ol: "list",
30
+ option: "option",
31
+ progress: "progressbar",
32
+ section: "region",
33
+ select: "combobox",
34
+ summary: "button",
35
+ table: "table",
36
+ tbody: "rowgroup",
37
+ td: "cell",
38
+ textarea: "textbox",
39
+ tfoot: "rowgroup",
40
+ th: "columnheader",
41
+ thead: "rowgroup",
42
+ tr: "row",
43
+ ul: "list",
44
+ };
45
+ function buildNode(element, filter) {
46
+ const tag = element.tagName?.toLowerCase() ?? "";
47
+ const explicitRole = element.getAttribute("role");
48
+ const role = explicitRole || IMPLIED_ROLES[tag] || "generic";
49
+ const label = element.getAttribute("aria-label") ||
50
+ element.getAttribute("aria-labelledby") ||
51
+ element.title ||
52
+ undefined;
53
+ const description = element.getAttribute("aria-describedby") || undefined;
54
+ // Collect aria-* attributes
55
+ const attributes = {};
56
+ if (element.attributes) {
57
+ for (let i = 0; i < element.attributes.length; i++) {
58
+ const attr = element.attributes[i];
59
+ if (attr.name.startsWith("aria-") || attr.name === "role" || attr.name === "tabindex") {
60
+ attributes[attr.name] = attr.value;
61
+ }
62
+ }
63
+ }
64
+ const children = [];
65
+ for (let i = 0; i < element.children.length; i++) {
66
+ const child = element.children[i];
67
+ if (filter && !filter(child))
68
+ continue;
69
+ children.push(buildNode(child, filter));
70
+ }
71
+ return {
72
+ role,
73
+ label,
74
+ description,
75
+ attributes: Object.keys(attributes).length > 0 ? attributes : undefined,
76
+ children: children.length > 0 ? children : undefined,
77
+ };
78
+ }
79
+ function diffNodes(prev, curr, path, result) {
80
+ if (!prev && curr) {
81
+ result.push({ type: "added", path, newNode: curr });
82
+ return;
83
+ }
84
+ if (prev && !curr) {
85
+ result.push({ type: "removed", path, oldNode: prev });
86
+ return;
87
+ }
88
+ if (!prev || !curr)
89
+ return;
90
+ // Check if node itself changed
91
+ if (prev.role !== curr.role ||
92
+ prev.label !== curr.label ||
93
+ prev.description !== curr.description ||
94
+ JSON.stringify(prev.attributes) !== JSON.stringify(curr.attributes)) {
95
+ result.push({ type: "changed", path, oldNode: prev, newNode: curr });
96
+ }
97
+ // Diff children
98
+ const prevChildren = prev.children ?? [];
99
+ const currChildren = curr.children ?? [];
100
+ const maxLen = Math.max(prevChildren.length, currChildren.length);
101
+ for (let i = 0; i < maxLen; i++) {
102
+ diffNodes(prevChildren[i], currChildren[i], `${path}/${i}`, result);
103
+ }
104
+ }
105
+ export function createAccessibilityTreeMirror(config) {
106
+ let observer = null;
107
+ let cachedSnapshot = null;
108
+ const changeHandlers = new Set();
109
+ function takeSnapshot() {
110
+ return buildNode(config.rootElement, config.subtreeFilter);
111
+ }
112
+ function handleMutations() {
113
+ const prev = cachedSnapshot;
114
+ cachedSnapshot = takeSnapshot();
115
+ if (prev && changeHandlers.size > 0) {
116
+ const diffs = diffNodes_wrapper(prev, cachedSnapshot);
117
+ if (diffs.length > 0) {
118
+ for (const handler of changeHandlers) {
119
+ handler(diffs);
120
+ }
121
+ }
122
+ }
123
+ }
124
+ function diffNodes_wrapper(prev, curr) {
125
+ const result = [];
126
+ diffNodes(prev, curr, "", result);
127
+ return result;
128
+ }
129
+ return {
130
+ observe() {
131
+ if (observer)
132
+ return;
133
+ cachedSnapshot = takeSnapshot();
134
+ observer = new MutationObserver(handleMutations);
135
+ observer.observe(config.rootElement, {
136
+ attributes: true,
137
+ attributeFilter: ["role", "tabindex", "aria-label", "aria-labelledby",
138
+ "aria-describedby", "aria-expanded", "aria-selected", "aria-hidden",
139
+ "aria-controls", "aria-live", "aria-atomic", "aria-relevant",
140
+ "aria-activedescendant", "aria-checked", "aria-disabled",
141
+ "aria-pressed", "aria-valuenow", "aria-valuemin", "aria-valuemax"],
142
+ childList: true,
143
+ subtree: true,
144
+ });
145
+ },
146
+ disconnect() {
147
+ if (observer) {
148
+ observer.disconnect();
149
+ observer = null;
150
+ }
151
+ },
152
+ snapshot() {
153
+ cachedSnapshot = takeSnapshot();
154
+ return cachedSnapshot;
155
+ },
156
+ diff(previous, current) {
157
+ return diffNodes_wrapper(previous, current);
158
+ },
159
+ serialize() {
160
+ return JSON.stringify(this.snapshot(), null, 2);
161
+ },
162
+ onTreeChange(handler) {
163
+ changeHandlers.add(handler);
164
+ return () => { changeHandlers.delete(handler); };
165
+ },
166
+ destroy() {
167
+ this.disconnect();
168
+ cachedSnapshot = null;
169
+ changeHandlers.clear();
170
+ },
171
+ };
172
+ }
173
+ //# sourceMappingURL=accessibility-tree-mirror.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"accessibility-tree-mirror.js","sourceRoot":"","sources":["../src/accessibility-tree-mirror.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Feature 2: ARIA Widget Compliance Engine
3
+ * W3C ARIA Authoring Practices design patterns with validation
4
+ */
5
+ import type { Disposable } from "./types.js";
6
+ import type { AriaStateManagerAPI } from "./state-properties-manager.js";
7
+ import type { FocusManagerAPI } from "./focus-management.js";
8
+ import type { KeyboardNavigationAPI } from "./keyboard-navigation.js";
9
+ export type WidgetPattern = "accordion" | "menu-bar" | "slider" | "tabs" | "dialog" | "listbox" | "tree" | "combobox";
10
+ export interface WidgetConfig {
11
+ container: Element;
12
+ pattern: WidgetPattern;
13
+ options?: Record<string, unknown>;
14
+ }
15
+ export interface WidgetHandle extends Disposable {
16
+ activate(): void;
17
+ deactivate(): void;
18
+ getPattern(): WidgetPattern;
19
+ validate(): WidgetValidationResult;
20
+ }
21
+ export interface WidgetValidationResult {
22
+ valid: boolean;
23
+ errors: Array<{
24
+ element: Element;
25
+ message: string;
26
+ severity: "error" | "warning";
27
+ }>;
28
+ }
29
+ export interface WidgetComplianceDeps {
30
+ stateManager: AriaStateManagerAPI;
31
+ focusManager: FocusManagerAPI;
32
+ keyboardNav: KeyboardNavigationAPI;
33
+ }
34
+ export interface WidgetComplianceAPI extends Disposable {
35
+ createWidget(id: string, config: WidgetConfig): WidgetHandle;
36
+ getWidget(id: string): WidgetHandle | undefined;
37
+ destroyWidget(id: string): void;
38
+ validateAll(): Map<string, WidgetValidationResult>;
39
+ getSupportedPatterns(): WidgetPattern[];
40
+ }
41
+ export declare function createWidgetComplianceEngine(deps: WidgetComplianceDeps): WidgetComplianceAPI;
42
+ //# sourceMappingURL=aria-widget-compliance.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aria-widget-compliance.d.ts","sourceRoot":"","sources":["../src/aria-widget-compliance.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AACzE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AAEtE,MAAM,MAAM,aAAa,GACrB,WAAW,GACX,UAAU,GACV,QAAQ,GACR,MAAM,GACN,QAAQ,GACR,SAAS,GACT,MAAM,GACN,UAAU,CAAC;AAEf,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,aAAa,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,YAAa,SAAQ,UAAU;IAC9C,QAAQ,IAAI,IAAI,CAAC;IACjB,UAAU,IAAI,IAAI,CAAC;IACnB,UAAU,IAAI,aAAa,CAAC;IAC5B,QAAQ,IAAI,sBAAsB,CAAC;CACpC;AAED,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,KAAK,CAAC;QACZ,OAAO,EAAE,OAAO,CAAC;QACjB,OAAO,EAAE,MAAM,CAAC;QAChB,QAAQ,EAAE,OAAO,GAAG,SAAS,CAAC;KAC/B,CAAC,CAAC;CACJ;AAED,MAAM,WAAW,oBAAoB;IACnC,YAAY,EAAE,mBAAmB,CAAC;IAClC,YAAY,EAAE,eAAe,CAAC;IAC9B,WAAW,EAAE,qBAAqB,CAAC;CACpC;AAED,MAAM,WAAW,mBAAoB,SAAQ,UAAU;IACrD,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,YAAY,CAAC;IAC7D,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAAC;IAChD,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,WAAW,IAAI,GAAG,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAC;IACnD,oBAAoB,IAAI,aAAa,EAAE,CAAC;CACzC;AAmQD,wBAAgB,4BAA4B,CAC1C,IAAI,EAAE,oBAAoB,GACzB,mBAAmB,CAiErB"}