@a11y-oracle/core-engine 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +473 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +28 -0
- package/dist/lib/a11y-orchestrator.d.ts +112 -0
- package/dist/lib/a11y-orchestrator.d.ts.map +1 -0
- package/dist/lib/a11y-orchestrator.js +170 -0
- package/dist/lib/role-map.d.ts +41 -0
- package/dist/lib/role-map.d.ts.map +1 -0
- package/dist/lib/role-map.js +128 -0
- package/dist/lib/speech-engine.d.ts +149 -0
- package/dist/lib/speech-engine.d.ts.map +1 -0
- package/dist/lib/speech-engine.js +219 -0
- package/dist/lib/state-map.d.ts +82 -0
- package/dist/lib/state-map.d.ts.map +1 -0
- package/dist/lib/state-map.js +86 -0
- package/dist/lib/types.d.ts +162 -0
- package/dist/lib/types.d.ts.map +1 -0
- package/dist/lib/types.js +10 -0
- package/package.json +52 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module a11y-orchestrator
|
|
3
|
+
*
|
|
4
|
+
* Coordinates the three sub-engines (Speech, Keyboard, Focus) into a
|
|
5
|
+
* single `pressKey()` → unified-state workflow.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { A11yOrchestrator } from '@a11y-oracle/core-engine';
|
|
10
|
+
*
|
|
11
|
+
* const oracle = new A11yOrchestrator(cdpSession);
|
|
12
|
+
* await oracle.enable();
|
|
13
|
+
*
|
|
14
|
+
* const state = await oracle.pressKey('Tab');
|
|
15
|
+
* console.log(state.speech); // "Products, button, collapsed"
|
|
16
|
+
* console.log(state.focusedElement?.tag); // "BUTTON"
|
|
17
|
+
* console.log(state.focusIndicator.meetsWCAG_AA); // true
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
import { SpeechEngine } from './speech-engine.js';
|
|
21
|
+
import { KeyboardEngine } from '@a11y-oracle/keyboard-engine';
|
|
22
|
+
import { FocusAnalyzer } from '@a11y-oracle/focus-analyzer';
|
|
23
|
+
/**
|
|
24
|
+
* Orchestrates speech, keyboard dispatch, and focus analysis into a
|
|
25
|
+
* unified accessibility testing API.
|
|
26
|
+
*
|
|
27
|
+
* A single `pressKey()` call dispatches a keystroke, waits for focus
|
|
28
|
+
* to settle, then collects speech output, focused element info, and
|
|
29
|
+
* focus indicator analysis in parallel.
|
|
30
|
+
*
|
|
31
|
+
* ## Sub-engines
|
|
32
|
+
*
|
|
33
|
+
* | Engine | Responsibility |
|
|
34
|
+
* |--------|---------------|
|
|
35
|
+
* | {@link SpeechEngine} | AXTree → speech string |
|
|
36
|
+
* | {@link KeyboardEngine} | CDP key dispatch + `document.activeElement` |
|
|
37
|
+
* | {@link FocusAnalyzer} | CSS focus indicator + tab order + trap detection |
|
|
38
|
+
*/
|
|
39
|
+
export class A11yOrchestrator {
|
|
40
|
+
speech;
|
|
41
|
+
keyboard;
|
|
42
|
+
focusAnalyzer;
|
|
43
|
+
options;
|
|
44
|
+
/**
|
|
45
|
+
* @param cdp - CDP session for sending protocol commands.
|
|
46
|
+
* @param options - Optional configuration for speech output and focus settling.
|
|
47
|
+
*/
|
|
48
|
+
constructor(cdp, options = {}) {
|
|
49
|
+
this.speech = new SpeechEngine(cdp, options);
|
|
50
|
+
this.keyboard = new KeyboardEngine(cdp);
|
|
51
|
+
this.focusAnalyzer = new FocusAnalyzer(cdp);
|
|
52
|
+
this.options = {
|
|
53
|
+
includeLandmarks: options.includeLandmarks ?? true,
|
|
54
|
+
includeDescription: options.includeDescription ?? false,
|
|
55
|
+
focusSettleMs: options.focusSettleMs ?? 50,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Enable the CDP Accessibility domain.
|
|
60
|
+
*
|
|
61
|
+
* Must be called before any other method.
|
|
62
|
+
*/
|
|
63
|
+
async enable() {
|
|
64
|
+
await this.speech.enable();
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Disable the CDP Accessibility domain.
|
|
68
|
+
*
|
|
69
|
+
* Call this when done to free browser resources.
|
|
70
|
+
*/
|
|
71
|
+
async disable() {
|
|
72
|
+
await this.speech.disable();
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Dispatch a key press and return the unified accessibility state.
|
|
76
|
+
*
|
|
77
|
+
* 1. Dispatches `keyDown` + `keyUp` via CDP `Input.dispatchKeyEvent`.
|
|
78
|
+
* 2. Waits {@link A11yOrchestratorOptions.focusSettleMs} for transitions.
|
|
79
|
+
* 3. Collects speech, focused element, and focus indicator **in parallel**.
|
|
80
|
+
*
|
|
81
|
+
* @param key - Key name (e.g. `'Tab'`, `'Enter'`, `'ArrowDown'`).
|
|
82
|
+
* @param modifiers - Optional modifier keys.
|
|
83
|
+
* @returns Unified accessibility state snapshot.
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* ```typescript
|
|
87
|
+
* const state = await orchestrator.pressKey('Tab');
|
|
88
|
+
* expect(state.speech).toBe('Products, button, collapsed');
|
|
89
|
+
* expect(state.focusIndicator.meetsWCAG_AA).toBe(true);
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
async pressKey(key, modifiers) {
|
|
93
|
+
await this.keyboard.press(key, modifiers);
|
|
94
|
+
// Wait for focus transitions and CSS animations to settle
|
|
95
|
+
if (this.options.focusSettleMs > 0) {
|
|
96
|
+
await new Promise((resolve) => setTimeout(resolve, this.options.focusSettleMs));
|
|
97
|
+
}
|
|
98
|
+
return this.getState();
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Get the current unified accessibility state without pressing a key.
|
|
102
|
+
*
|
|
103
|
+
* Collects speech, focused element, and focus indicator in parallel.
|
|
104
|
+
*
|
|
105
|
+
* @returns Unified accessibility state snapshot.
|
|
106
|
+
*/
|
|
107
|
+
async getState() {
|
|
108
|
+
const [speechResult, focusedElementInfo, focusIndicator] = await Promise.all([
|
|
109
|
+
this.speech.getSpeech(),
|
|
110
|
+
this.keyboard.getFocusedElement(),
|
|
111
|
+
this.focusAnalyzer.getFocusIndicator(),
|
|
112
|
+
]);
|
|
113
|
+
const focusedElement = focusedElementInfo
|
|
114
|
+
? this.mapFocusedElement(focusedElementInfo)
|
|
115
|
+
: null;
|
|
116
|
+
const a11yFocusIndicator = {
|
|
117
|
+
isVisible: focusIndicator.isVisible,
|
|
118
|
+
contrastRatio: focusIndicator.contrastRatio,
|
|
119
|
+
meetsWCAG_AA: focusIndicator.meetsWCAG_AA,
|
|
120
|
+
};
|
|
121
|
+
return {
|
|
122
|
+
speech: speechResult?.speech ?? '',
|
|
123
|
+
speechResult,
|
|
124
|
+
focusedElement,
|
|
125
|
+
focusIndicator: a11yFocusIndicator,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Extract all tabbable elements in DOM tab order.
|
|
130
|
+
*
|
|
131
|
+
* @returns Report with sorted tab order entries and total count.
|
|
132
|
+
*/
|
|
133
|
+
async traverseTabOrder() {
|
|
134
|
+
const entries = await this.focusAnalyzer.getTabOrder();
|
|
135
|
+
return {
|
|
136
|
+
entries,
|
|
137
|
+
totalCount: entries.length,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Detect whether a container traps keyboard focus.
|
|
142
|
+
*
|
|
143
|
+
* Focuses the first tabbable element in the container, then
|
|
144
|
+
* presses Tab repeatedly. If focus never escapes after `maxTabs`
|
|
145
|
+
* presses, the container is a keyboard trap (WCAG 2.1.2 failure).
|
|
146
|
+
*
|
|
147
|
+
* @param selector - CSS selector for the container to test.
|
|
148
|
+
* @param maxTabs - Maximum Tab presses before declaring a trap. Default 50.
|
|
149
|
+
* @returns Traversal result indicating whether focus is trapped.
|
|
150
|
+
*/
|
|
151
|
+
async traverseSubTree(selector, maxTabs) {
|
|
152
|
+
return this.focusAnalyzer.detectKeyboardTrap(selector, maxTabs);
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Map a raw `FocusedElementInfo` from keyboard-engine to the
|
|
156
|
+
* orchestrator's `A11yFocusedElement` shape.
|
|
157
|
+
*/
|
|
158
|
+
mapFocusedElement(info) {
|
|
159
|
+
return {
|
|
160
|
+
tag: info.tag,
|
|
161
|
+
id: info.id,
|
|
162
|
+
className: info.className,
|
|
163
|
+
textContent: info.textContent,
|
|
164
|
+
role: info.role,
|
|
165
|
+
ariaLabel: info.ariaLabel,
|
|
166
|
+
tabIndex: info.tabIndex,
|
|
167
|
+
rect: info.rect,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module role-map
|
|
3
|
+
*
|
|
4
|
+
* Maps Chrome DevTools Protocol AXTree role values to human-readable
|
|
5
|
+
* speech role strings.
|
|
6
|
+
*
|
|
7
|
+
* CDP already maps HTML elements to their implicit ARIA roles (e.g.,
|
|
8
|
+
* `<button>` → `"button"`, `<nav>` → `"navigation"`). This module
|
|
9
|
+
* translates those internal role names to the strings that appear in
|
|
10
|
+
* the final speech output.
|
|
11
|
+
*
|
|
12
|
+
* Unknown roles pass through as-is, ensuring forward compatibility
|
|
13
|
+
* with new ARIA roles.
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Maps CDP AXTree `role.value` strings to speech output role strings.
|
|
17
|
+
*
|
|
18
|
+
* Keys are the role values returned by `Accessibility.getFullAXTree()`.
|
|
19
|
+
* Values are the corresponding human-readable strings used in speech
|
|
20
|
+
* output.
|
|
21
|
+
*
|
|
22
|
+
* Roles that map to an empty string (`''`) are silent — they produce
|
|
23
|
+
* no role announcement in the speech output.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```typescript
|
|
27
|
+
* ROLE_TO_SPEECH['button'] // → 'button'
|
|
28
|
+
* ROLE_TO_SPEECH['navigation'] // → 'navigation'
|
|
29
|
+
* ROLE_TO_SPEECH['generic'] // → '' (silent)
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export declare const ROLE_TO_SPEECH: Record<string, string>;
|
|
33
|
+
/**
|
|
34
|
+
* Set of roles classified as "landmark" roles.
|
|
35
|
+
*
|
|
36
|
+
* When a node has a landmark role, the word `"landmark"` is appended
|
|
37
|
+
* to its speech output. For example, a `<nav>` element produces
|
|
38
|
+
* `"Main, navigation landmark"` rather than just `"Main, navigation"`.
|
|
39
|
+
*/
|
|
40
|
+
export declare const LANDMARK_ROLES: Set<string>;
|
|
41
|
+
//# sourceMappingURL=role-map.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"role-map.d.ts","sourceRoot":"","sources":["../../src/lib/role-map.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAkFjD,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,aASzB,CAAC"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module role-map
|
|
3
|
+
*
|
|
4
|
+
* Maps Chrome DevTools Protocol AXTree role values to human-readable
|
|
5
|
+
* speech role strings.
|
|
6
|
+
*
|
|
7
|
+
* CDP already maps HTML elements to their implicit ARIA roles (e.g.,
|
|
8
|
+
* `<button>` → `"button"`, `<nav>` → `"navigation"`). This module
|
|
9
|
+
* translates those internal role names to the strings that appear in
|
|
10
|
+
* the final speech output.
|
|
11
|
+
*
|
|
12
|
+
* Unknown roles pass through as-is, ensuring forward compatibility
|
|
13
|
+
* with new ARIA roles.
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Maps CDP AXTree `role.value` strings to speech output role strings.
|
|
17
|
+
*
|
|
18
|
+
* Keys are the role values returned by `Accessibility.getFullAXTree()`.
|
|
19
|
+
* Values are the corresponding human-readable strings used in speech
|
|
20
|
+
* output.
|
|
21
|
+
*
|
|
22
|
+
* Roles that map to an empty string (`''`) are silent — they produce
|
|
23
|
+
* no role announcement in the speech output.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```typescript
|
|
27
|
+
* ROLE_TO_SPEECH['button'] // → 'button'
|
|
28
|
+
* ROLE_TO_SPEECH['navigation'] // → 'navigation'
|
|
29
|
+
* ROLE_TO_SPEECH['generic'] // → '' (silent)
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export const ROLE_TO_SPEECH = {
|
|
33
|
+
// ─── Interactive widget roles ───
|
|
34
|
+
button: 'button',
|
|
35
|
+
link: 'link',
|
|
36
|
+
checkbox: 'checkbox',
|
|
37
|
+
radio: 'radio button',
|
|
38
|
+
textbox: 'edit text',
|
|
39
|
+
combobox: 'combo box',
|
|
40
|
+
slider: 'slider',
|
|
41
|
+
switch: 'switch',
|
|
42
|
+
tab: 'tab',
|
|
43
|
+
menuitem: 'menu item',
|
|
44
|
+
menuitemcheckbox: 'menu item checkbox',
|
|
45
|
+
menuitemradio: 'menu item radio',
|
|
46
|
+
option: 'option',
|
|
47
|
+
searchbox: 'search text',
|
|
48
|
+
spinbutton: 'spin button',
|
|
49
|
+
// ─── Landmark roles ───
|
|
50
|
+
navigation: 'navigation',
|
|
51
|
+
main: 'main',
|
|
52
|
+
banner: 'banner',
|
|
53
|
+
contentinfo: 'content info',
|
|
54
|
+
complementary: 'complementary',
|
|
55
|
+
search: 'search',
|
|
56
|
+
region: 'region',
|
|
57
|
+
form: 'form',
|
|
58
|
+
// ─── Document structure roles ───
|
|
59
|
+
heading: 'heading',
|
|
60
|
+
list: 'list',
|
|
61
|
+
listitem: 'list item',
|
|
62
|
+
img: 'image',
|
|
63
|
+
figure: 'figure',
|
|
64
|
+
table: 'table',
|
|
65
|
+
row: 'row',
|
|
66
|
+
cell: 'cell',
|
|
67
|
+
columnheader: 'column header',
|
|
68
|
+
rowheader: 'row header',
|
|
69
|
+
grid: 'grid',
|
|
70
|
+
gridcell: 'grid cell',
|
|
71
|
+
tree: 'tree',
|
|
72
|
+
treeitem: 'tree item',
|
|
73
|
+
tablist: 'tab list',
|
|
74
|
+
tabpanel: 'tab panel',
|
|
75
|
+
menu: 'menu',
|
|
76
|
+
menubar: 'menu bar',
|
|
77
|
+
toolbar: 'toolbar',
|
|
78
|
+
dialog: 'dialog',
|
|
79
|
+
alertdialog: 'alert dialog',
|
|
80
|
+
alert: 'alert',
|
|
81
|
+
status: 'status',
|
|
82
|
+
progressbar: 'progress bar',
|
|
83
|
+
separator: 'separator',
|
|
84
|
+
group: 'group',
|
|
85
|
+
article: 'article',
|
|
86
|
+
definition: 'definition',
|
|
87
|
+
term: 'term',
|
|
88
|
+
note: 'note',
|
|
89
|
+
log: 'log',
|
|
90
|
+
marquee: 'marquee',
|
|
91
|
+
timer: 'timer',
|
|
92
|
+
tooltip: 'tooltip',
|
|
93
|
+
feed: 'feed',
|
|
94
|
+
math: 'math',
|
|
95
|
+
directory: 'directory',
|
|
96
|
+
document: 'document',
|
|
97
|
+
application: 'application',
|
|
98
|
+
// ─── Silent roles (no spoken role) ───
|
|
99
|
+
generic: '',
|
|
100
|
+
none: '',
|
|
101
|
+
presentation: '',
|
|
102
|
+
StaticText: '',
|
|
103
|
+
InlineTextBox: '',
|
|
104
|
+
LineBreak: '',
|
|
105
|
+
RootWebArea: '',
|
|
106
|
+
WebArea: '',
|
|
107
|
+
paragraph: '',
|
|
108
|
+
DescriptionListDetail: '',
|
|
109
|
+
DescriptionListTerm: '',
|
|
110
|
+
DescriptionList: '',
|
|
111
|
+
};
|
|
112
|
+
/**
|
|
113
|
+
* Set of roles classified as "landmark" roles.
|
|
114
|
+
*
|
|
115
|
+
* When a node has a landmark role, the word `"landmark"` is appended
|
|
116
|
+
* to its speech output. For example, a `<nav>` element produces
|
|
117
|
+
* `"Main, navigation landmark"` rather than just `"Main, navigation"`.
|
|
118
|
+
*/
|
|
119
|
+
export const LANDMARK_ROLES = new Set([
|
|
120
|
+
'navigation',
|
|
121
|
+
'main',
|
|
122
|
+
'banner',
|
|
123
|
+
'contentinfo',
|
|
124
|
+
'complementary',
|
|
125
|
+
'search',
|
|
126
|
+
'region',
|
|
127
|
+
'form',
|
|
128
|
+
]);
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module speech-engine
|
|
3
|
+
*
|
|
4
|
+
* The central class of A11y-Oracle. {@link SpeechEngine} connects to the
|
|
5
|
+
* browser's Accessibility Tree via the Chrome DevTools Protocol and generates
|
|
6
|
+
* standardized speech output following the format:
|
|
7
|
+
*
|
|
8
|
+
* ```
|
|
9
|
+
* [Computed Name], [Role], [State/Properties]
|
|
10
|
+
* ```
|
|
11
|
+
*
|
|
12
|
+
* Chrome's CDP already computes accessible names per the W3C AccName spec,
|
|
13
|
+
* so the engine's job is to:
|
|
14
|
+
* 1. Fetch the AXTree via `Accessibility.getFullAXTree()`
|
|
15
|
+
* 2. Find the currently focused node
|
|
16
|
+
* 3. Map the node's role and properties to human-readable strings
|
|
17
|
+
* 4. Assemble the final speech string
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* import { SpeechEngine } from '@a11y-oracle/core-engine';
|
|
22
|
+
*
|
|
23
|
+
* const engine = new SpeechEngine(cdpSession);
|
|
24
|
+
* await engine.enable();
|
|
25
|
+
*
|
|
26
|
+
* const result = await engine.getSpeech();
|
|
27
|
+
* console.log(result?.speech); // "Products, button, collapsed"
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
import type { Protocol } from 'devtools-protocol';
|
|
31
|
+
import type { CDPSessionLike, SpeechEngineOptions, SpeechResult } from './types.js';
|
|
32
|
+
/**
|
|
33
|
+
* Generates standardized speech output from the browser's Accessibility Tree.
|
|
34
|
+
*
|
|
35
|
+
* The engine operates through a {@link CDPSessionLike} interface, making it
|
|
36
|
+
* framework-agnostic. It works with Playwright's CDP sessions, raw WebSocket
|
|
37
|
+
* connections, or any other CDP-compatible client.
|
|
38
|
+
*
|
|
39
|
+
* ## Speech Output Format
|
|
40
|
+
*
|
|
41
|
+
* Every element produces a string in this format:
|
|
42
|
+
* ```
|
|
43
|
+
* [Computed Name], [Role], [State/Properties]
|
|
44
|
+
* ```
|
|
45
|
+
*
|
|
46
|
+
* Parts are omitted if they are empty. For example:
|
|
47
|
+
* - `"Products, button, collapsed"` — name + role + state
|
|
48
|
+
* - `"Main, navigation landmark"` — name + role (landmark)
|
|
49
|
+
* - `"Home, link"` — name + role (no states)
|
|
50
|
+
*
|
|
51
|
+
* ## Landmark Roles
|
|
52
|
+
*
|
|
53
|
+
* Landmark roles (navigation, main, banner, etc.) automatically append
|
|
54
|
+
* the word "landmark" to their role string unless
|
|
55
|
+
* {@link SpeechEngineOptions.includeLandmarks} is set to `false`.
|
|
56
|
+
*/
|
|
57
|
+
export declare class SpeechEngine {
|
|
58
|
+
private cdp;
|
|
59
|
+
private options;
|
|
60
|
+
/**
|
|
61
|
+
* Create a new SpeechEngine instance.
|
|
62
|
+
*
|
|
63
|
+
* @param cdp - A CDP session-like object for sending protocol commands.
|
|
64
|
+
* @param options - Optional configuration for speech output behavior.
|
|
65
|
+
*/
|
|
66
|
+
constructor(cdp: CDPSessionLike, options?: SpeechEngineOptions);
|
|
67
|
+
/**
|
|
68
|
+
* Enable the CDP Accessibility domain.
|
|
69
|
+
*
|
|
70
|
+
* Must be called before any other method. Enables the browser to
|
|
71
|
+
* start tracking and reporting accessibility tree data.
|
|
72
|
+
*/
|
|
73
|
+
enable(): Promise<void>;
|
|
74
|
+
/**
|
|
75
|
+
* Disable the CDP Accessibility domain.
|
|
76
|
+
*
|
|
77
|
+
* Call this when you're done using the engine to free browser resources.
|
|
78
|
+
*/
|
|
79
|
+
disable(): Promise<void>;
|
|
80
|
+
/**
|
|
81
|
+
* Get the speech output for the currently focused element.
|
|
82
|
+
*
|
|
83
|
+
* Fetches the full AXTree, locates the node with `focused: true`,
|
|
84
|
+
* and computes its speech string.
|
|
85
|
+
*
|
|
86
|
+
* @returns The {@link SpeechResult} for the focused element, or `null`
|
|
87
|
+
* if no element has focus or the focused element is ignored.
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* ```typescript
|
|
91
|
+
* // After pressing Tab to focus a button
|
|
92
|
+
* const result = await engine.getSpeech();
|
|
93
|
+
* console.log(result?.speech); // "Products, button, collapsed"
|
|
94
|
+
* console.log(result?.name); // "Products"
|
|
95
|
+
* console.log(result?.role); // "button"
|
|
96
|
+
* console.log(result?.states); // ["collapsed"]
|
|
97
|
+
* ```
|
|
98
|
+
*/
|
|
99
|
+
getSpeech(): Promise<SpeechResult | null>;
|
|
100
|
+
/**
|
|
101
|
+
* Get speech output for ALL non-ignored, non-silent nodes in the tree.
|
|
102
|
+
*
|
|
103
|
+
* Useful for asserting on landmarks, headings, or other structural
|
|
104
|
+
* elements that may not have focus.
|
|
105
|
+
*
|
|
106
|
+
* @returns An array of {@link SpeechResult} objects for every visible
|
|
107
|
+
* node that produces speech output.
|
|
108
|
+
*
|
|
109
|
+
* @example
|
|
110
|
+
* ```typescript
|
|
111
|
+
* const all = await engine.getFullTreeSpeech();
|
|
112
|
+
* const nav = all.find(r => r.speech === 'Main, navigation landmark');
|
|
113
|
+
* expect(nav).toBeDefined();
|
|
114
|
+
* ```
|
|
115
|
+
*/
|
|
116
|
+
getFullTreeSpeech(): Promise<SpeechResult[]>;
|
|
117
|
+
/**
|
|
118
|
+
* Find the most specific focused node in the flat AXTree node list.
|
|
119
|
+
*
|
|
120
|
+
* CDP returns the AXTree as a flat array with the document node first
|
|
121
|
+
* and more specific nodes later. Multiple nodes can report `focused: true`
|
|
122
|
+
* (e.g., both the RootWebArea and the actual focused element). This method
|
|
123
|
+
* returns the **last** focused node, which is the most specific (deepest)
|
|
124
|
+
* element that actually has user focus.
|
|
125
|
+
*
|
|
126
|
+
* @param nodes - The flat array of AXNodes from `getFullAXTree()`.
|
|
127
|
+
* @returns The most specific focused node, or `null` if no node is focused.
|
|
128
|
+
*/
|
|
129
|
+
findFocusedNode(nodes: Protocol.Accessibility.AXNode[]): Protocol.Accessibility.AXNode | null;
|
|
130
|
+
/**
|
|
131
|
+
* Compute the speech string for a single AXNode.
|
|
132
|
+
*
|
|
133
|
+
* Follows the output format: `[Computed Name], [Role], [State/Properties]`.
|
|
134
|
+
* Parts are omitted when empty. Ignored nodes and nodes with no
|
|
135
|
+
* meaningful content return `null`.
|
|
136
|
+
*
|
|
137
|
+
* @param node - A CDP AXNode from the accessibility tree.
|
|
138
|
+
* @returns A {@link SpeechResult} with the computed speech, or `null`
|
|
139
|
+
* if the node should be silent (ignored, no role, no name).
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* ```typescript
|
|
143
|
+
* const result = engine.computeSpeech(axNode);
|
|
144
|
+
* // { speech: "Products, button, collapsed", name: "Products", ... }
|
|
145
|
+
* ```
|
|
146
|
+
*/
|
|
147
|
+
computeSpeech(node: Protocol.Accessibility.AXNode): SpeechResult | null;
|
|
148
|
+
}
|
|
149
|
+
//# sourceMappingURL=speech-engine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"speech-engine.d.ts","sourceRoot":"","sources":["../../src/lib/speech-engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAKpF;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,GAAG,CAAiB;IAC5B,OAAO,CAAC,OAAO,CAAgC;IAE/C;;;;;OAKG;gBACS,GAAG,EAAE,cAAc,EAAE,OAAO,GAAE,mBAAwB;IAQlE;;;;;OAKG;IACG,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAI7B;;;;OAIG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAI9B;;;;;;;;;;;;;;;;;;OAkBG;IACG,SAAS,IAAI,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;IAO/C;;;;;;;;;;;;;;;OAeG;IACG,iBAAiB,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;IAOlD;;;;;;;;;;;OAWG;IACH,eAAe,CACb,KAAK,EAAE,QAAQ,CAAC,aAAa,CAAC,MAAM,EAAE,GACrC,QAAQ,CAAC,aAAa,CAAC,MAAM,GAAG,IAAI;IAWvC;;;;;;;;;;;;;;;;OAgBG;IACH,aAAa,CAAC,IAAI,EAAE,QAAQ,CAAC,aAAa,CAAC,MAAM,GAAG,YAAY,GAAG,IAAI;CA6CxE"}
|