@nuanu-ai/agentbrowse 0.2.21 → 0.2.23
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 +52 -9
- package/dist/commands/act.d.ts.map +1 -1
- package/dist/commands/act.js +100 -73
- package/dist/commands/action-acceptance.d.ts +4 -1
- package/dist/commands/action-acceptance.d.ts.map +1 -1
- package/dist/commands/action-acceptance.js +127 -37
- package/dist/commands/action-executor-helpers.d.ts.map +1 -1
- package/dist/commands/action-executor-helpers.js +12 -4
- package/dist/commands/action-fallbacks.d.ts.map +1 -1
- package/dist/commands/action-result-resolution.d.ts.map +1 -1
- package/dist/commands/action-result-resolution.js +1 -1
- package/dist/commands/action-value-projection.d.ts.map +1 -1
- package/dist/commands/close.d.ts +12 -1
- package/dist/commands/close.d.ts.map +1 -1
- package/dist/commands/close.js +19 -21
- package/dist/commands/datepicker-action-executor.d.ts.map +1 -1
- package/dist/commands/datepicker-action-executor.js +1 -1
- package/dist/commands/descriptor-validation.d.ts.map +1 -1
- package/dist/commands/descriptor-validation.js +13 -172
- package/dist/commands/extract-scoped-dialog-text.d.ts.map +1 -1
- package/dist/commands/extract-scoped-dialog-text.js +1 -4
- package/dist/commands/extract-snapshot-sanitizer.d.ts.map +1 -1
- package/dist/commands/extract-stagehand-executor.d.ts.map +1 -1
- package/dist/commands/extract.d.ts.map +1 -1
- package/dist/commands/extract.js +23 -4
- package/dist/commands/launch.d.ts +22 -1
- package/dist/commands/launch.d.ts.map +1 -1
- package/dist/commands/launch.js +122 -59
- package/dist/commands/observe-accessibility.d.ts +22 -0
- package/dist/commands/observe-accessibility.d.ts.map +1 -0
- package/dist/commands/observe-accessibility.js +497 -0
- package/dist/commands/observe-display-label.d.ts +4 -0
- package/dist/commands/observe-display-label.d.ts.map +1 -0
- package/dist/commands/observe-display-label.js +26 -0
- package/dist/commands/observe-dom-label-contract.d.ts +2 -0
- package/dist/commands/observe-dom-label-contract.d.ts.map +1 -0
- package/dist/commands/observe-dom-label-contract.js +521 -0
- package/dist/commands/observe-fallback-semantics.d.ts +6 -0
- package/dist/commands/observe-fallback-semantics.d.ts.map +1 -0
- package/dist/commands/observe-fallback-semantics.js +86 -0
- package/dist/commands/observe-inventory.d.ts +23 -18
- package/dist/commands/observe-inventory.d.ts.map +1 -1
- package/dist/commands/observe-inventory.js +172 -719
- package/dist/commands/observe-label-policy.d.ts +8 -0
- package/dist/commands/observe-label-policy.d.ts.map +1 -0
- package/dist/commands/observe-label-policy.js +21 -0
- package/dist/commands/observe-page-state.d.ts +1 -1
- package/dist/commands/observe-page-state.d.ts.map +1 -1
- package/dist/commands/observe-persistence.d.ts.map +1 -1
- package/dist/commands/observe-persistence.js +10 -3
- package/dist/commands/observe-projection.d.ts +2 -1
- package/dist/commands/observe-projection.d.ts.map +1 -1
- package/dist/commands/observe-projection.js +5 -2
- package/dist/commands/observe-semantics.d.ts.map +1 -1
- package/dist/commands/observe-semantics.js +18 -1
- package/dist/commands/observe-signals.d.ts +48 -0
- package/dist/commands/observe-signals.d.ts.map +1 -0
- package/dist/commands/observe-signals.js +461 -0
- package/dist/commands/observe-stagehand.d.ts.map +1 -1
- package/dist/commands/observe-stagehand.js +5 -18
- package/dist/commands/observe-surfaces.d.ts.map +1 -1
- package/dist/commands/observe-surfaces.js +5 -4
- package/dist/commands/observe.d.ts +0 -6
- package/dist/commands/observe.d.ts.map +1 -1
- package/dist/commands/observe.js +30 -6
- package/dist/commands/observe.test-harness.d.ts +0 -6
- package/dist/commands/observe.test-harness.d.ts.map +1 -1
- package/dist/commands/screenshot.d.ts.map +1 -1
- package/dist/commands/select-action-executor.d.ts.map +1 -1
- package/dist/commands/select-action-executor.js +1 -1
- package/dist/commands/semantic-observe.d.ts.map +1 -1
- package/dist/commands/semantic-observe.js +1 -4
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/text-input-action-executor.d.ts.map +1 -1
- package/dist/commands/text-input-action-executor.js +1 -1
- package/dist/control-semantics.d.ts.map +1 -1
- package/dist/control-semantics.js +2 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +30 -9
- package/dist/owned-process.d.ts +4 -0
- package/dist/owned-process.d.ts.map +1 -0
- package/dist/owned-process.js +57 -0
- package/dist/playwright-runtime.d.ts.map +1 -1
- package/dist/playwright-runtime.js +3 -4
- package/dist/runtime-state.d.ts +3 -0
- package/dist/runtime-state.d.ts.map +1 -1
- package/dist/runtime-state.js +3 -0
- package/dist/secrets/catalog-applicability.d.ts.map +1 -1
- package/dist/secrets/form-matcher.d.ts.map +1 -1
- package/dist/secrets/form-matcher.js +2 -1
- package/dist/secrets/mock-agentpay-backend.d.ts +1 -1
- package/dist/secrets/mock-agentpay-backend.d.ts.map +1 -1
- package/dist/secrets/mock-agentpay-backend.js +2 -4
- package/dist/secrets/mock-agentpay-cabinet.d.ts.map +1 -1
- package/dist/secrets/mock-agentpay-cabinet.js +6 -1
- package/dist/secrets/protected-field-values.d.ts.map +1 -1
- package/dist/secrets/protected-field-values.js +1 -1
- package/dist/secrets/protected-fill.d.ts.map +1 -1
- package/dist/secrets/protected-fill.js +16 -4
- package/dist/session.d.ts +13 -0
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +52 -7
- package/dist/solver/captcha-detector.d.ts.map +1 -1
- package/dist/solver/types.d.ts +2 -0
- package/dist/solver/types.d.ts.map +1 -1
- package/package.json +3 -3
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { inferAcceptancePolicyFromFacts, inferAllowedActionsFromFacts, inferAvailabilityFromFacts, inferControlFamilyFromFacts, } from '../control-semantics.js';
|
|
2
2
|
import { LOCATOR_DOM_SIGNATURE_SCRIPT, normalizePageSignature, readLocatorDomSignature, } from './descriptor-validation.js';
|
|
3
|
+
import { OBSERVE_DOM_LABEL_CONTRACT_HELPER_SCRIPT } from './observe-dom-label-contract.js';
|
|
3
4
|
import { TRANSPARENT_ACTIONABLE_CONTROL_HELPER_SCRIPT } from './user-actionable.js';
|
|
4
5
|
const DOM_TARGET_COLLECTION_LIMIT = 640;
|
|
5
6
|
const DOM_TARGET_OUTPUT_LIMIT = 480;
|
|
6
|
-
const DOM_SIGNAL_COLLECTION_LIMIT = 24;
|
|
7
7
|
export function inferStructuredCellVariantFromEvidence(evidence) {
|
|
8
8
|
const role = (evidence.role ?? '').toLowerCase();
|
|
9
9
|
const surfaceKind = (evidence.surfaceKind ?? '').toLowerCase();
|
|
@@ -14,8 +14,7 @@ export function inferStructuredCellVariantFromEvidence(evidence) {
|
|
|
14
14
|
const hasSeatMetadata = Boolean(evidence.hasSeatAttribute || evidence.hasSeatRowAttribute || evidence.hasSeatColumnAttribute);
|
|
15
15
|
const seatIdentityLike = /(?:\bseat\s*\d{1,3}[a-z]?\b|место\s*\d{1,3}[a-z]?\b|\b\d{1,3}[a-z]\b|\b[a-z]\d{1,3}\b)/i.test(normalizedLabel);
|
|
16
16
|
const seatClassLike = /seat|cabin|fare|row/.test(className);
|
|
17
|
-
const compactDateCellLabel = /^(?:\d{1,2}|january|february|march|april|may|june|july|august|september|october|november|december|январ|феврал|март|апрел|ма[йя]|июн|июл|август|сентябр|октябр|ноябр|декабр)$/i.test(normalizedLabel) ||
|
|
18
|
-
/^(?:\d{1,2}[./-]\d{1,2}(?:[./-]\d{2,4})?)$/.test(normalizedLabel);
|
|
17
|
+
const compactDateCellLabel = /^(?:\d{1,2}|january|february|march|april|may|june|july|august|september|october|november|december|январ|феврал|март|апрел|ма[йя]|июн|июл|август|сентябр|октябр|ноябр|декабр)$/i.test(normalizedLabel) || /^(?:\d{1,2}[./-]\d{1,2}(?:[./-]\d{2,4})?)$/.test(normalizedLabel);
|
|
19
18
|
const dateLike = surfaceKind === 'datepicker' ||
|
|
20
19
|
((role === 'gridcell' || structuredSurface || hasDateMetadata) && compactDateCellLabel);
|
|
21
20
|
if (dateLike) {
|
|
@@ -48,7 +47,8 @@ function normalizeInheritedPageSignature(pageSignature) {
|
|
|
48
47
|
: undefined;
|
|
49
48
|
}
|
|
50
49
|
function applyInheritedDomTargetMetadata(target, options) {
|
|
51
|
-
const normalizedFramePath = normalizeInheritedFramePath(options?.framePath) ??
|
|
50
|
+
const normalizedFramePath = normalizeInheritedFramePath(options?.framePath) ??
|
|
51
|
+
normalizeInheritedFramePath(target.framePath);
|
|
52
52
|
const normalizedFrameUrl = normalizeInheritedFrameUrl(options?.frameUrl) ?? target.frameUrl;
|
|
53
53
|
const normalizedPageSignature = normalizeInheritedPageSignature(options?.pageSignature) ?? target.pageSignature;
|
|
54
54
|
return {
|
|
@@ -84,19 +84,11 @@ function enrichObservedTargetSemantics(target) {
|
|
|
84
84
|
...target,
|
|
85
85
|
capability,
|
|
86
86
|
availability,
|
|
87
|
+
controlFamily,
|
|
87
88
|
allowedActions,
|
|
88
89
|
acceptancePolicy,
|
|
89
90
|
};
|
|
90
91
|
}
|
|
91
|
-
function applyInheritedSignalMetadata(signal, options) {
|
|
92
|
-
const normalizedFramePath = normalizeInheritedFramePath(options?.framePath) ?? normalizeInheritedFramePath(signal.framePath);
|
|
93
|
-
const normalizedFrameUrl = normalizeInheritedFrameUrl(options?.frameUrl) ?? signal.frameUrl;
|
|
94
|
-
return {
|
|
95
|
-
...signal,
|
|
96
|
-
framePath: normalizedFramePath,
|
|
97
|
-
frameUrl: normalizedFrameUrl,
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
92
|
function hasStagehandDomFacts(domFacts) {
|
|
101
93
|
return Boolean(domFacts &&
|
|
102
94
|
(domFacts.kind ||
|
|
@@ -163,23 +155,11 @@ export async function readStagehandLocatorSnapshot(locator) {
|
|
|
163
155
|
return best;
|
|
164
156
|
}
|
|
165
157
|
const STAGEHAND_DOM_FACTS_SCRIPT = String.raw `
|
|
158
|
+
${OBSERVE_DOM_LABEL_CONTRACT_HELPER_SCRIPT}
|
|
166
159
|
if (!(element instanceof HTMLElement)) {
|
|
167
160
|
return null;
|
|
168
161
|
}
|
|
169
162
|
|
|
170
|
-
const normalizeText = (value) => (value ?? '').replace(/\s+/g, ' ').trim();
|
|
171
|
-
const isVisible = (candidate) => {
|
|
172
|
-
if (!(candidate instanceof HTMLElement)) {
|
|
173
|
-
return false;
|
|
174
|
-
}
|
|
175
|
-
const style = candidate.ownerDocument?.defaultView?.getComputedStyle(candidate);
|
|
176
|
-
if (!style || style.display === 'none' || style.visibility === 'hidden') {
|
|
177
|
-
return false;
|
|
178
|
-
}
|
|
179
|
-
const rect = candidate.getBoundingClientRect();
|
|
180
|
-
return rect.width > 0 && rect.height > 0;
|
|
181
|
-
};
|
|
182
|
-
|
|
183
163
|
const inputLike = element;
|
|
184
164
|
const disabledProperty =
|
|
185
165
|
typeof inputLike.disabled === 'boolean' ? inputLike.disabled : false;
|
|
@@ -209,47 +189,10 @@ const STAGEHAND_DOM_FACTS_SCRIPT = String.raw `
|
|
|
209
189
|
const states = {};
|
|
210
190
|
const directValue =
|
|
211
191
|
'value' in inputLike && typeof inputLike.value === 'string'
|
|
212
|
-
?
|
|
192
|
+
? observedNormalizeDescriptorText(inputLike.value)
|
|
213
193
|
: undefined;
|
|
214
|
-
const directText =
|
|
215
|
-
|
|
216
|
-
directValue ||
|
|
217
|
-
(element.tagName.toLowerCase() === 'input' || element.tagName.toLowerCase() === 'textarea'
|
|
218
|
-
? undefined
|
|
219
|
-
: directText || undefined);
|
|
220
|
-
|
|
221
|
-
const popupBacked =
|
|
222
|
-
element.getAttribute('role') === 'combobox' ||
|
|
223
|
-
element.hasAttribute('aria-haspopup') ||
|
|
224
|
-
element.hasAttribute('aria-controls') ||
|
|
225
|
-
readonly;
|
|
226
|
-
if (!currentValue && popupBacked && element.parentElement) {
|
|
227
|
-
const siblingValues = Array.from(element.parentElement.children)
|
|
228
|
-
.filter((candidate) => candidate !== element)
|
|
229
|
-
.filter((candidate) => isVisible(candidate))
|
|
230
|
-
.filter((candidate) => {
|
|
231
|
-
const tag = candidate.tagName.toLowerCase();
|
|
232
|
-
if (tag === 'label' || tag === 'legend') {
|
|
233
|
-
return false;
|
|
234
|
-
}
|
|
235
|
-
const role = candidate.getAttribute('role') || '';
|
|
236
|
-
return (
|
|
237
|
-
role === 'listbox' ||
|
|
238
|
-
role === 'button' ||
|
|
239
|
-
role === 'option' ||
|
|
240
|
-
candidate.hasAttribute('aria-selected') ||
|
|
241
|
-
candidate.hasAttribute('aria-current')
|
|
242
|
-
);
|
|
243
|
-
})
|
|
244
|
-
.map((candidate) => normalizeText(candidate.innerText || candidate.textContent || ''))
|
|
245
|
-
.filter(
|
|
246
|
-
(value, index, all) =>
|
|
247
|
-
Boolean(value) && value.length <= 40 && all.indexOf(value) === index
|
|
248
|
-
);
|
|
249
|
-
if (siblingValues.length === 1) {
|
|
250
|
-
currentValue = siblingValues[0];
|
|
251
|
-
}
|
|
252
|
-
}
|
|
194
|
+
const directText = observedNormalizeDescriptorText(element.innerText || element.textContent || '');
|
|
195
|
+
const currentValue = observedPopupCurrentValueOf(element);
|
|
253
196
|
|
|
254
197
|
if (expanded !== undefined) states.expanded = expanded;
|
|
255
198
|
if (selected !== undefined) states.selected = selected;
|
|
@@ -407,9 +350,10 @@ async function collectDomTargetsFromDocument(context, options) {
|
|
|
407
350
|
return undefined;
|
|
408
351
|
};
|
|
409
352
|
${TRANSPARENT_ACTIONABLE_CONTROL_HELPER_SCRIPT}
|
|
353
|
+
${OBSERVE_DOM_LABEL_CONTRACT_HELPER_SCRIPT}
|
|
410
354
|
${INFER_STRUCTURED_CELL_VARIANT_HELPER_SCRIPT}
|
|
411
355
|
|
|
412
|
-
const normalizeDescriptorText = (value) => (value
|
|
356
|
+
const normalizeDescriptorText = (value) => observedNormalizeDescriptorText(value);
|
|
413
357
|
|
|
414
358
|
const isVisible = (element) => {
|
|
415
359
|
const style = window.getComputedStyle(element);
|
|
@@ -636,38 +580,12 @@ async function collectDomTargetsFromDocument(context, options) {
|
|
|
636
580
|
return values.length > 0 ? values.join(' ') : undefined;
|
|
637
581
|
};
|
|
638
582
|
|
|
639
|
-
const
|
|
640
|
-
/(?:window\.[a-z0-9_$]+|apiary(?:sleepingqueue|markerportal)|document\.getelementbyid\(|addeventlistener\(|push\(\[|["']widgets["']\s*:|["']meta["']\s*:)/i;
|
|
641
|
-
|
|
642
|
-
const looksLikeMachineDescriptorText = (value) => {
|
|
643
|
-
const normalized = normalizeDescriptorText(value || '');
|
|
644
|
-
if (!normalized) {
|
|
645
|
-
return false;
|
|
646
|
-
}
|
|
647
|
-
|
|
648
|
-
if (MACHINE_DESCRIPTOR_TEXT_RE.test(normalized)) {
|
|
649
|
-
return true;
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
if (normalized.length < 160) {
|
|
653
|
-
return false;
|
|
654
|
-
}
|
|
655
|
-
|
|
656
|
-
const syntaxCount =
|
|
657
|
-
(normalized.match(/[{}[\]();=]/g) || []).length +
|
|
658
|
-
(normalized.match(/["']/g) || []).length;
|
|
659
|
-
return syntaxCount >= 24 && syntaxCount / normalized.length >= 0.08;
|
|
660
|
-
};
|
|
661
|
-
|
|
662
|
-
const safeVisibleDescriptorTextOf = (element) => {
|
|
583
|
+
const visibleDescriptorTextOf = (element) => {
|
|
663
584
|
const text = normalizeDescriptorText(element?.innerText || '');
|
|
664
|
-
|
|
665
|
-
return undefined;
|
|
666
|
-
}
|
|
667
|
-
return text;
|
|
585
|
+
return text || undefined;
|
|
668
586
|
};
|
|
669
587
|
|
|
670
|
-
const
|
|
588
|
+
const visibleChildDescriptorTextOf = (element) => {
|
|
671
589
|
if (!isHTMLElementNode(element)) {
|
|
672
590
|
return undefined;
|
|
673
591
|
}
|
|
@@ -676,7 +594,7 @@ async function collectDomTargetsFromDocument(context, options) {
|
|
|
676
594
|
const seen = new Set();
|
|
677
595
|
const pushValue = (value) => {
|
|
678
596
|
const normalized = normalizeDescriptorText(value || '');
|
|
679
|
-
if (!normalized ||
|
|
597
|
+
if (!normalized || seen.has(normalized)) {
|
|
680
598
|
return;
|
|
681
599
|
}
|
|
682
600
|
seen.add(normalized);
|
|
@@ -712,18 +630,9 @@ async function collectDomTargetsFromDocument(context, options) {
|
|
|
712
630
|
return buttonLikeText;
|
|
713
631
|
}
|
|
714
632
|
const altValue = imageAltTextOf(element);
|
|
715
|
-
const visibleText =
|
|
716
|
-
const childText = container ?
|
|
717
|
-
const
|
|
718
|
-
const textContentFallback =
|
|
719
|
-
!container &&
|
|
720
|
-
rawTextContent &&
|
|
721
|
-
rawTextContent !== visibleText &&
|
|
722
|
-
rawTextContent !== childText &&
|
|
723
|
-
!looksLikeMachineDescriptorText(rawTextContent)
|
|
724
|
-
? rawTextContent
|
|
725
|
-
: undefined;
|
|
726
|
-
const textValue = visibleText || childText || textContentFallback;
|
|
633
|
+
const visibleText = visibleDescriptorTextOf(element);
|
|
634
|
+
const childText = container ? visibleChildDescriptorTextOf(element) : undefined;
|
|
635
|
+
const textValue = visibleText || childText;
|
|
727
636
|
|
|
728
637
|
if (!textValue && !altValue) {
|
|
729
638
|
return undefined;
|
|
@@ -737,139 +646,13 @@ async function collectDomTargetsFromDocument(context, options) {
|
|
|
737
646
|
return (altValue + ' ' + textValue).trim();
|
|
738
647
|
};
|
|
739
648
|
|
|
740
|
-
const isMeaningfulLabel = (value) =>
|
|
741
|
-
const normalized = (value || '').replace(/\s+/g, ' ').trim();
|
|
742
|
-
return Boolean(normalized) && normalized !== '[object Object]';
|
|
743
|
-
};
|
|
744
|
-
|
|
745
|
-
const inputTypeOf = (element) => {
|
|
746
|
-
if (!isHTMLInputNode(element)) {
|
|
747
|
-
return '';
|
|
748
|
-
}
|
|
749
|
-
return (element.getAttribute('type') || 'text').trim().toLowerCase();
|
|
750
|
-
};
|
|
751
|
-
|
|
752
|
-
const isButtonLikeInput = (element) => {
|
|
753
|
-
return isHTMLInputNode(element) && ['button', 'submit', 'reset'].includes(inputTypeOf(element));
|
|
754
|
-
};
|
|
755
|
-
|
|
756
|
-
const LOOSE_ACTION_TEXT_RE =
|
|
757
|
-
/^(?:pay|buy|continue|submit|search|book|reserve|checkout|next|done|оплат|куп|продолж|дальше|поиск|заброни)/i;
|
|
758
|
-
|
|
759
|
-
const tokenizeSemanticText = (value) => {
|
|
760
|
-
const normalized = (value || '')
|
|
761
|
-
.replace(/([a-z])([A-Z])/g, '$1 $2')
|
|
762
|
-
.replace(/[^a-zA-Z0-9\u0400-\u04FF]+/g, ' ')
|
|
763
|
-
.toLowerCase()
|
|
764
|
-
.trim();
|
|
765
|
-
if (!normalized) return [];
|
|
766
|
-
return normalized
|
|
767
|
-
.split(/\s+/)
|
|
768
|
-
.filter((token) => token.length >= 2 && token !== 'input' && token !== 'field');
|
|
769
|
-
};
|
|
770
|
-
|
|
771
|
-
const fieldSemanticTokensOf = (element) => {
|
|
772
|
-
const tokens = new Set();
|
|
773
|
-
const pushValue = (value) => {
|
|
774
|
-
for (const token of tokenizeSemanticText(value)) {
|
|
775
|
-
tokens.add(token);
|
|
776
|
-
}
|
|
777
|
-
};
|
|
778
|
-
|
|
779
|
-
const inputType = inputTypeOf(element);
|
|
780
|
-
const autocomplete = (element.getAttribute('autocomplete') || '').trim().toLowerCase();
|
|
781
|
-
|
|
782
|
-
if (isButtonLikeInput(element)) {
|
|
783
|
-
return tokens;
|
|
784
|
-
}
|
|
785
|
-
|
|
786
|
-
pushValue(element.getAttribute('placeholder'));
|
|
787
|
-
pushValue(element.getAttribute('name'));
|
|
788
|
-
pushValue(element.getAttribute('id'));
|
|
789
|
-
pushValue(autocomplete);
|
|
790
|
-
pushValue(inputType);
|
|
791
|
-
|
|
792
|
-
if (inputType === 'email' || autocomplete.includes('email')) {
|
|
793
|
-
pushValue('email mail e-mail');
|
|
794
|
-
}
|
|
795
|
-
if (
|
|
796
|
-
inputType === 'tel' ||
|
|
797
|
-
autocomplete.startsWith('tel') ||
|
|
798
|
-
autocomplete.includes('phone')
|
|
799
|
-
) {
|
|
800
|
-
pushValue('phone telephone mobile tel номер телефон');
|
|
801
|
-
}
|
|
802
|
-
if (inputType === 'password' || autocomplete.includes('password')) {
|
|
803
|
-
pushValue('password pass пароль');
|
|
804
|
-
}
|
|
805
|
-
if (inputType === 'search' || autocomplete.includes('search')) {
|
|
806
|
-
pushValue('search find поиск найти');
|
|
807
|
-
}
|
|
808
|
-
if (
|
|
809
|
-
inputType === 'date' ||
|
|
810
|
-
autocomplete.includes('bday') ||
|
|
811
|
-
autocomplete.includes('birth') ||
|
|
812
|
-
autocomplete.includes('dob')
|
|
813
|
-
) {
|
|
814
|
-
pushValue('date birth birthday dob дата рождения');
|
|
815
|
-
}
|
|
816
|
-
|
|
817
|
-
return tokens;
|
|
818
|
-
};
|
|
819
|
-
|
|
820
|
-
const isLooseFieldLabelCompatible = (element, candidateText) => {
|
|
821
|
-
const normalizedCandidate = normalizeDescriptorText(candidateText || '');
|
|
822
|
-
if (!normalizedCandidate) return false;
|
|
823
|
-
if (LOOSE_ACTION_TEXT_RE.test(normalizedCandidate)) {
|
|
824
|
-
return false;
|
|
825
|
-
}
|
|
826
|
-
|
|
827
|
-
const semanticTokens = fieldSemanticTokensOf(element);
|
|
828
|
-
if (semanticTokens.size === 0) {
|
|
829
|
-
return true;
|
|
830
|
-
}
|
|
831
|
-
|
|
832
|
-
const candidateTokens = tokenizeSemanticText(normalizedCandidate);
|
|
833
|
-
if (candidateTokens.length === 0) {
|
|
834
|
-
return false;
|
|
835
|
-
}
|
|
836
|
-
|
|
837
|
-
return candidateTokens.some((token) => semanticTokens.has(token));
|
|
838
|
-
};
|
|
839
|
-
|
|
840
|
-
const isFieldLikeControl = (element) => {
|
|
841
|
-
const tag = element.tagName.toLowerCase();
|
|
842
|
-
const explicitRole = (element.getAttribute('role') || '').trim().toLowerCase();
|
|
843
|
-
if (tag === 'input') {
|
|
844
|
-
return !isButtonLikeInput(element);
|
|
845
|
-
}
|
|
846
|
-
return (
|
|
847
|
-
tag === 'textarea' ||
|
|
848
|
-
tag === 'select' ||
|
|
849
|
-
explicitRole === 'textbox' ||
|
|
850
|
-
explicitRole === 'combobox' ||
|
|
851
|
-
explicitRole === 'searchbox' ||
|
|
852
|
-
explicitRole === 'spinbutton'
|
|
853
|
-
);
|
|
854
|
-
};
|
|
855
|
-
|
|
856
|
-
const precedesElementInDocument = (candidate, element) => {
|
|
857
|
-
return Boolean(candidate.compareDocumentPosition(element) & Node.DOCUMENT_POSITION_FOLLOWING);
|
|
858
|
-
};
|
|
649
|
+
const isMeaningfulLabel = (value) => observedIsMeaningfulLabel(value);
|
|
859
650
|
|
|
860
|
-
const
|
|
861
|
-
const labelledBy = element.getAttribute('aria-labelledby')?.trim();
|
|
862
|
-
if (!labelledBy) return undefined;
|
|
651
|
+
const inputTypeOf = (element) => observedInputTypeOf(element);
|
|
863
652
|
|
|
864
|
-
|
|
865
|
-
.split(/\s+/)
|
|
866
|
-
.map((id) => textOf(document.getElementById(id)))
|
|
867
|
-
.filter(Boolean)
|
|
868
|
-
.join(' ')
|
|
869
|
-
.trim();
|
|
653
|
+
const isButtonLikeInput = (element) => observedIsButtonLikeInput(element);
|
|
870
654
|
|
|
871
|
-
|
|
872
|
-
};
|
|
655
|
+
const popupCurrentValueOf = (element) => observedPopupCurrentValueOf(element);
|
|
873
656
|
|
|
874
657
|
const describedByTextOf = (element) => {
|
|
875
658
|
const describedBy = element.getAttribute('aria-describedby')?.trim();
|
|
@@ -885,105 +668,9 @@ async function collectDomTargetsFromDocument(context, options) {
|
|
|
885
668
|
return isMeaningfulLabel(text) ? text : undefined;
|
|
886
669
|
};
|
|
887
670
|
|
|
888
|
-
const
|
|
889
|
-
const restrictLooseCandidatesToPreceding = isFieldLikeControl(element);
|
|
890
|
-
const anchors = [
|
|
891
|
-
composedParentElement(element),
|
|
892
|
-
composedParentElement(composedParentElement(element)),
|
|
893
|
-
composedParentElement(composedParentElement(composedParentElement(element))),
|
|
894
|
-
].filter(Boolean);
|
|
895
|
-
|
|
896
|
-
for (const anchor of anchors) {
|
|
897
|
-
if (!isHTMLElementNode(anchor)) continue;
|
|
898
|
-
|
|
899
|
-
const explicitLabels = Array.from(
|
|
900
|
-
anchor.querySelectorAll('label, legend, [class*="label"], [data-testid*="label"]')
|
|
901
|
-
).filter((candidate) => {
|
|
902
|
-
return (
|
|
903
|
-
isHTMLElementNode(candidate) &&
|
|
904
|
-
candidate !== element &&
|
|
905
|
-
!candidate.contains(element) &&
|
|
906
|
-
!element.contains(candidate)
|
|
907
|
-
);
|
|
908
|
-
});
|
|
909
|
-
|
|
910
|
-
for (const candidate of explicitLabels) {
|
|
911
|
-
if (
|
|
912
|
-
restrictLooseCandidatesToPreceding &&
|
|
913
|
-
!candidate.matches('label, legend') &&
|
|
914
|
-
!precedesElementInDocument(candidate, element)
|
|
915
|
-
) {
|
|
916
|
-
continue;
|
|
917
|
-
}
|
|
918
|
-
const candidateText = textOf(candidate);
|
|
919
|
-
if (isMeaningfulLabel(candidateText)) {
|
|
920
|
-
return candidateText;
|
|
921
|
-
}
|
|
922
|
-
}
|
|
923
|
-
|
|
924
|
-
for (const child of Array.from(anchor.children).slice(0, 8)) {
|
|
925
|
-
if (!isHTMLElementNode(child)) continue;
|
|
926
|
-
if (child === element || child.contains(element) || element.contains(child)) continue;
|
|
927
|
-
if (restrictLooseCandidatesToPreceding && !precedesElementInDocument(child, element)) {
|
|
928
|
-
continue;
|
|
929
|
-
}
|
|
930
|
-
|
|
931
|
-
const candidateText = textOf(child);
|
|
932
|
-
if (isMeaningfulLabel(candidateText)) {
|
|
933
|
-
return candidateText;
|
|
934
|
-
}
|
|
935
|
-
}
|
|
936
|
-
}
|
|
937
|
-
|
|
938
|
-
return undefined;
|
|
939
|
-
};
|
|
940
|
-
|
|
941
|
-
const labelOf = (element) => {
|
|
942
|
-
const ariaLabel = element.getAttribute('aria-label')?.trim();
|
|
943
|
-
if (isMeaningfulLabel(ariaLabel)) return ariaLabel;
|
|
944
|
-
|
|
945
|
-
const customLabel = element.getAttribute('label')?.trim();
|
|
946
|
-
if (isMeaningfulLabel(customLabel)) return customLabel;
|
|
947
|
-
|
|
948
|
-
const ariaLabelledbyText = ariaLabelledbyTextOf(element);
|
|
949
|
-
if (isMeaningfulLabel(ariaLabelledbyText)) return ariaLabelledbyText;
|
|
950
|
-
|
|
951
|
-
const labels = element.labels;
|
|
952
|
-
if (labels && labels.length > 0) {
|
|
953
|
-
const labelText = textOf(labels[0]);
|
|
954
|
-
if (isMeaningfulLabel(labelText)) return labelText;
|
|
955
|
-
}
|
|
956
|
-
|
|
957
|
-
const title = element.getAttribute('title')?.trim();
|
|
958
|
-
if (isMeaningfulLabel(title)) return title;
|
|
959
|
-
|
|
960
|
-
const skipNearestFieldLabel = isButtonLikeInput(element);
|
|
961
|
-
const preferFieldLabelBeforeVisibleText = isFieldLikeControl(element);
|
|
962
|
-
const text = textOf(element);
|
|
963
|
-
if (!preferFieldLabelBeforeVisibleText && isMeaningfulLabel(text)) return text;
|
|
964
|
-
|
|
965
|
-
const placeholder = element.getAttribute('placeholder')?.trim();
|
|
966
|
-
const nearestFieldLabel = nearestFieldLabelOf(element);
|
|
967
|
-
if (
|
|
968
|
-
!skipNearestFieldLabel &&
|
|
969
|
-
isMeaningfulLabel(nearestFieldLabel) &&
|
|
970
|
-
(!preferFieldLabelBeforeVisibleText ||
|
|
971
|
-
isLooseFieldLabelCompatible(element, nearestFieldLabel))
|
|
972
|
-
) {
|
|
973
|
-
return nearestFieldLabel;
|
|
974
|
-
}
|
|
975
|
-
|
|
976
|
-
if (isMeaningfulLabel(placeholder)) return placeholder;
|
|
977
|
-
|
|
978
|
-
if (!skipNearestFieldLabel && isMeaningfulLabel(nearestFieldLabel)) return nearestFieldLabel;
|
|
979
|
-
|
|
980
|
-
if (isMeaningfulLabel(text)) return text;
|
|
671
|
+
const explicitLabelOf = (element) => observedExplicitLabelOf(element);
|
|
981
672
|
|
|
982
|
-
|
|
983
|
-
if (isMeaningfulLabel(syntheticLabel)) return syntheticLabel;
|
|
984
|
-
|
|
985
|
-
return undefined;
|
|
986
|
-
};
|
|
673
|
+
const looseFieldLabelOf = (element) => observedLooseFieldLabelOf(element);
|
|
987
674
|
|
|
988
675
|
const VALIDATION_TEXT_RE =
|
|
989
676
|
/(?:required|invalid|incorrect|too\s+(?:short|long)|must|error|format|please\s+(?:enter|select|choose|fill)|невер|ошиб|обязател|заполн|введите|укажите|выберите|долж|нужно|формат|цифр|символ)/i;
|
|
@@ -1023,58 +710,7 @@ async function collectDomTargetsFromDocument(context, options) {
|
|
|
1023
710
|
return undefined;
|
|
1024
711
|
};
|
|
1025
712
|
|
|
1026
|
-
const
|
|
1027
|
-
const ariaLabel = element.getAttribute('aria-label')?.trim();
|
|
1028
|
-
if (isMeaningfulLabel(ariaLabel)) return ariaLabel;
|
|
1029
|
-
|
|
1030
|
-
const customLabel = element.getAttribute('label')?.trim();
|
|
1031
|
-
if (isMeaningfulLabel(customLabel)) return customLabel;
|
|
1032
|
-
|
|
1033
|
-
const title = element.getAttribute('title')?.trim();
|
|
1034
|
-
if (isMeaningfulLabel(title)) return title;
|
|
1035
|
-
|
|
1036
|
-
const ariaLabelledbyText = ariaLabelledbyTextOf(element);
|
|
1037
|
-
if (isMeaningfulLabel(ariaLabelledbyText)) return ariaLabelledbyText;
|
|
1038
|
-
|
|
1039
|
-
const labels = element.labels;
|
|
1040
|
-
if (labels && labels.length > 0) {
|
|
1041
|
-
const labelText = textOf(labels[0]);
|
|
1042
|
-
if (isMeaningfulLabel(labelText)) return labelText;
|
|
1043
|
-
}
|
|
1044
|
-
|
|
1045
|
-
const text = textOf(element);
|
|
1046
|
-
if (isMeaningfulLabel(text)) return text;
|
|
1047
|
-
|
|
1048
|
-
const skipNearestFieldLabel = isButtonLikeInput(element);
|
|
1049
|
-
const nearestFieldLabel = nearestFieldLabelOf(element);
|
|
1050
|
-
if (!skipNearestFieldLabel && isMeaningfulLabel(nearestFieldLabel)) return nearestFieldLabel;
|
|
1051
|
-
|
|
1052
|
-
return syntheticLabelOf(element);
|
|
1053
|
-
};
|
|
1054
|
-
|
|
1055
|
-
const inferRole = (element) => {
|
|
1056
|
-
const explicitRole = element.getAttribute('role')?.trim();
|
|
1057
|
-
if (explicitRole) return explicitRole;
|
|
1058
|
-
|
|
1059
|
-
const labelBackedChoice = labelBackedChoiceControlOf(element);
|
|
1060
|
-
if (isHTMLInputNode(labelBackedChoice)) {
|
|
1061
|
-
const inputType = (labelBackedChoice.type || '').toLowerCase();
|
|
1062
|
-
if (inputType === 'radio') return 'radio';
|
|
1063
|
-
if (inputType === 'checkbox') return 'checkbox';
|
|
1064
|
-
}
|
|
1065
|
-
|
|
1066
|
-
const tag = element.tagName.toLowerCase();
|
|
1067
|
-
if (tag === 'button') return 'button';
|
|
1068
|
-
if (tag === 'a' && element.getAttribute('href')) return 'link';
|
|
1069
|
-
if (tag === 'select') return 'combobox';
|
|
1070
|
-
if (tag === 'textarea') return 'textbox';
|
|
1071
|
-
if (tag === 'input') {
|
|
1072
|
-
const inputType = (element.getAttribute('type') || 'text').toLowerCase();
|
|
1073
|
-
if (['button', 'submit', 'reset'].includes(inputType)) return 'button';
|
|
1074
|
-
return 'textbox';
|
|
1075
|
-
}
|
|
1076
|
-
return undefined;
|
|
1077
|
-
};
|
|
713
|
+
const inferRole = (element) => observedInferRole(element) || undefined;
|
|
1078
714
|
|
|
1079
715
|
const kindOf = (element) => {
|
|
1080
716
|
const tag = element.tagName.toLowerCase();
|
|
@@ -1299,22 +935,6 @@ async function collectDomTargetsFromDocument(context, options) {
|
|
|
1299
935
|
};
|
|
1300
936
|
};
|
|
1301
937
|
|
|
1302
|
-
const landmarkLabelOf = (container) => {
|
|
1303
|
-
if (!container) return undefined;
|
|
1304
|
-
|
|
1305
|
-
const ariaLabel = container.getAttribute?.('aria-label')?.trim();
|
|
1306
|
-
if (ariaLabel) return ariaLabel;
|
|
1307
|
-
|
|
1308
|
-
const heading = container.querySelector?.(headingSelector);
|
|
1309
|
-
const headingText = textOf(heading);
|
|
1310
|
-
if (headingText) return headingText;
|
|
1311
|
-
|
|
1312
|
-
const text = textOf(container, { container: true });
|
|
1313
|
-
if (text && text.length <= 140) return text;
|
|
1314
|
-
if (text) return text.slice(0, 140);
|
|
1315
|
-
return undefined;
|
|
1316
|
-
};
|
|
1317
|
-
|
|
1318
938
|
const isStructuredContainer = (element) => {
|
|
1319
939
|
if (!isHTMLElementNode(element)) return false;
|
|
1320
940
|
|
|
@@ -1389,7 +1009,7 @@ async function collectDomTargetsFromDocument(context, options) {
|
|
|
1389
1009
|
const cursorClick = style.cursor === 'pointer';
|
|
1390
1010
|
if (!explicitClick && !cursorClick) return false;
|
|
1391
1011
|
|
|
1392
|
-
const descriptorText =
|
|
1012
|
+
const descriptorText = explicitLabelOf(element) || textOf(element) || syntheticLabelOf(element);
|
|
1393
1013
|
if (!descriptorText) return false;
|
|
1394
1014
|
|
|
1395
1015
|
const interactiveDescendants = visibleInteractiveDescendantCountOf(element);
|
|
@@ -1458,12 +1078,6 @@ async function collectDomTargetsFromDocument(context, options) {
|
|
|
1458
1078
|
return undefined;
|
|
1459
1079
|
};
|
|
1460
1080
|
|
|
1461
|
-
const containerTextOf = (container) => {
|
|
1462
|
-
const text = textOf(container, { container: true });
|
|
1463
|
-
if (!text) return undefined;
|
|
1464
|
-
return text.length <= 240 ? text : text.slice(0, 240);
|
|
1465
|
-
};
|
|
1466
|
-
|
|
1467
1081
|
const groupOf = (element) => {
|
|
1468
1082
|
if (element.matches?.(collectionSelector)) {
|
|
1469
1083
|
return element;
|
|
@@ -1532,70 +1146,6 @@ async function collectDomTargetsFromDocument(context, options) {
|
|
|
1532
1146
|
|
|
1533
1147
|
const normalizeText = (value) => (value || '').replace(/\s+/g, ' ').trim().toLowerCase();
|
|
1534
1148
|
|
|
1535
|
-
const contextualHintOf = (element) => {
|
|
1536
|
-
const excluded = new Set(
|
|
1537
|
-
[labelOf(element), textOf(element)]
|
|
1538
|
-
.map((value) => normalizeText(value))
|
|
1539
|
-
.filter(Boolean)
|
|
1540
|
-
);
|
|
1541
|
-
|
|
1542
|
-
const seen = new Set();
|
|
1543
|
-
const pushCandidate = (candidates, value) => {
|
|
1544
|
-
const text = (value || '').replace(/\s+/g, ' ').trim();
|
|
1545
|
-
const normalized = normalizeText(text);
|
|
1546
|
-
if (!normalized || excluded.has(normalized) || seen.has(normalized)) {
|
|
1547
|
-
return;
|
|
1548
|
-
}
|
|
1549
|
-
if (text.length < 4 || text.length > 120) {
|
|
1550
|
-
return;
|
|
1551
|
-
}
|
|
1552
|
-
seen.add(normalized);
|
|
1553
|
-
candidates.push(text);
|
|
1554
|
-
};
|
|
1555
|
-
|
|
1556
|
-
const describeNode = (node, candidates) => {
|
|
1557
|
-
if (!isHTMLElementNode(node)) return;
|
|
1558
|
-
|
|
1559
|
-
const heading = node.querySelector?.(headingSelector);
|
|
1560
|
-
if (isHTMLElementNode(heading) && heading !== element && !heading.contains(element)) {
|
|
1561
|
-
pushCandidate(candidates, textOf(heading));
|
|
1562
|
-
}
|
|
1563
|
-
|
|
1564
|
-
for (const child of Array.from(node.children).slice(0, 8)) {
|
|
1565
|
-
if (!isHTMLElementNode(child)) continue;
|
|
1566
|
-
if (child === element || child.contains(element) || element.contains(child)) continue;
|
|
1567
|
-
pushCandidate(candidates, textOf(child));
|
|
1568
|
-
}
|
|
1569
|
-
|
|
1570
|
-
pushCandidate(candidates, landmarkLabelOf(node));
|
|
1571
|
-
pushCandidate(candidates, textOf(node, { container: true }));
|
|
1572
|
-
};
|
|
1573
|
-
|
|
1574
|
-
const anchors = [
|
|
1575
|
-
itemOf(element),
|
|
1576
|
-
groupOf(element),
|
|
1577
|
-
containerOf(element),
|
|
1578
|
-
composedParentElement(element),
|
|
1579
|
-
composedParentElement(composedParentElement(element)),
|
|
1580
|
-
].filter(Boolean);
|
|
1581
|
-
|
|
1582
|
-
for (const anchor of anchors) {
|
|
1583
|
-
const candidates = [];
|
|
1584
|
-
pushCandidate(candidates, describedByTextOf(element));
|
|
1585
|
-
describeNode(anchor, candidates);
|
|
1586
|
-
if (candidates.length > 0) {
|
|
1587
|
-
return candidates[0];
|
|
1588
|
-
}
|
|
1589
|
-
}
|
|
1590
|
-
|
|
1591
|
-
const describedBy = describedByTextOf(element);
|
|
1592
|
-
if (describedBy) {
|
|
1593
|
-
return describedBy;
|
|
1594
|
-
}
|
|
1595
|
-
|
|
1596
|
-
return undefined;
|
|
1597
|
-
};
|
|
1598
|
-
|
|
1599
1149
|
const VALIDATION_CLASS_RE = /\b(?:error|invalid|warning|danger|alert|failed)\b/i;
|
|
1600
1150
|
const validationFieldSelectors =
|
|
1601
1151
|
'input, textarea, select, [role="textbox"], [contenteditable="true"], [aria-invalid="true"]';
|
|
@@ -1825,7 +1375,7 @@ async function collectDomTargetsFromDocument(context, options) {
|
|
|
1825
1375
|
const role = inferRole(element) || '';
|
|
1826
1376
|
const className = (element.getAttribute('class') || '').toLowerCase();
|
|
1827
1377
|
const surfaceKind = inferSurfaceKind(surface);
|
|
1828
|
-
const label =
|
|
1378
|
+
const label = explicitLabelOf(element) || textOf(element) || syntheticLabelOf(element) || '';
|
|
1829
1379
|
const normalizedLabel = label.replace(/\s+/g, ' ').trim();
|
|
1830
1380
|
const explicitDateCellMetadata =
|
|
1831
1381
|
element.hasAttribute('data-day') ||
|
|
@@ -1909,6 +1459,49 @@ async function collectDomTargetsFromDocument(context, options) {
|
|
|
1909
1459
|
return undefined;
|
|
1910
1460
|
};
|
|
1911
1461
|
|
|
1462
|
+
const siblingModalBackdropOf = (surface) => {
|
|
1463
|
+
let current = surface;
|
|
1464
|
+
let depth = 0;
|
|
1465
|
+
while (current && depth < 16) {
|
|
1466
|
+
const parent = composedParentElement(current);
|
|
1467
|
+
if (isHTMLElementNode(parent)) {
|
|
1468
|
+
const siblings = Array.from(parent.children).filter(
|
|
1469
|
+
(candidate) => candidate !== current && isHTMLElementNode(candidate)
|
|
1470
|
+
);
|
|
1471
|
+
for (const sibling of siblings) {
|
|
1472
|
+
if (!isVisible(sibling)) {
|
|
1473
|
+
continue;
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1476
|
+
const style = window.getComputedStyle(sibling);
|
|
1477
|
+
const position = (style.position || '').toLowerCase();
|
|
1478
|
+
const rect = sibling.getBoundingClientRect();
|
|
1479
|
+
const viewportArea = Math.max(window.innerWidth * window.innerHeight, 1);
|
|
1480
|
+
const coverage = (rect.width * rect.height) / viewportArea;
|
|
1481
|
+
const backgroundVisible =
|
|
1482
|
+
style.backgroundColor &&
|
|
1483
|
+
style.backgroundColor !== 'transparent' &&
|
|
1484
|
+
style.backgroundColor !== 'rgba(0, 0, 0, 0)';
|
|
1485
|
+
const opacity = parseFloat(style.opacity || '1');
|
|
1486
|
+
|
|
1487
|
+
if (
|
|
1488
|
+
position === 'fixed' &&
|
|
1489
|
+
coverage > 0.45 &&
|
|
1490
|
+
backgroundVisible &&
|
|
1491
|
+
opacity >= 0.05
|
|
1492
|
+
) {
|
|
1493
|
+
return sibling;
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
current = parent;
|
|
1499
|
+
depth += 1;
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1502
|
+
return undefined;
|
|
1503
|
+
};
|
|
1504
|
+
|
|
1912
1505
|
const surfacePositionTraitsOf = (surface) => {
|
|
1913
1506
|
if (!isHTMLElementNode(surface) || !isVisible(surface)) return undefined;
|
|
1914
1507
|
|
|
@@ -1949,7 +1542,7 @@ async function collectDomTargetsFromDocument(context, options) {
|
|
|
1949
1542
|
hasCardChrome &&
|
|
1950
1543
|
coverage <= 0.35 &&
|
|
1951
1544
|
interactiveCount >= 1 &&
|
|
1952
|
-
modalBackdropAncestorOf(surface)
|
|
1545
|
+
(modalBackdropAncestorOf(surface) || siblingModalBackdropOf(surface))
|
|
1953
1546
|
) {
|
|
1954
1547
|
return { kind: 'floating-panel', priority: 90 };
|
|
1955
1548
|
}
|
|
@@ -1980,15 +1573,97 @@ async function collectDomTargetsFromDocument(context, options) {
|
|
|
1980
1573
|
return role || surface.tagName.toLowerCase();
|
|
1981
1574
|
};
|
|
1982
1575
|
|
|
1576
|
+
const surfaceFallbackLabelOf = (surface, surfaceKind) => {
|
|
1577
|
+
if (!isHTMLElementNode(surface)) {
|
|
1578
|
+
return undefined;
|
|
1579
|
+
}
|
|
1580
|
+
|
|
1581
|
+
const kind = (surfaceKind || '').toLowerCase();
|
|
1582
|
+
const overlayLike =
|
|
1583
|
+
kind === 'dialog' ||
|
|
1584
|
+
kind === 'floating-panel' ||
|
|
1585
|
+
kind === 'sticky-panel' ||
|
|
1586
|
+
kind === 'listbox' ||
|
|
1587
|
+
kind === 'menu' ||
|
|
1588
|
+
kind === 'grid' ||
|
|
1589
|
+
kind === 'tabpanel' ||
|
|
1590
|
+
kind === 'popover' ||
|
|
1591
|
+
kind === 'dropdown' ||
|
|
1592
|
+
kind === 'datepicker' ||
|
|
1593
|
+
kind === 'form';
|
|
1594
|
+
if (!overlayLike) {
|
|
1595
|
+
return undefined;
|
|
1596
|
+
}
|
|
1597
|
+
|
|
1598
|
+
const directLabel = normalizeDescriptorText(
|
|
1599
|
+
surface.getAttribute('aria-label') || surface.getAttribute('title') || ''
|
|
1600
|
+
);
|
|
1601
|
+
if (directLabel) {
|
|
1602
|
+
return directLabel;
|
|
1603
|
+
}
|
|
1604
|
+
|
|
1605
|
+
const heading = surface.querySelector(
|
|
1606
|
+
'h1, h2, h3, h4, h5, h6, [role="heading"], legend, strong'
|
|
1607
|
+
);
|
|
1608
|
+
if (!isHTMLElementNode(heading) || !isVisible(heading)) {
|
|
1609
|
+
return undefined;
|
|
1610
|
+
}
|
|
1611
|
+
|
|
1612
|
+
return normalizeDescriptorText(heading.innerText || heading.textContent || '') || undefined;
|
|
1613
|
+
};
|
|
1614
|
+
|
|
1615
|
+
const contextLabelOf = (element) => {
|
|
1616
|
+
if (!isHTMLElementNode(element)) {
|
|
1617
|
+
return undefined;
|
|
1618
|
+
}
|
|
1619
|
+
|
|
1620
|
+
const ariaLabel = normalizeDescriptorText(element.getAttribute('aria-label') || '');
|
|
1621
|
+
if (ariaLabel) {
|
|
1622
|
+
return ariaLabel;
|
|
1623
|
+
}
|
|
1624
|
+
|
|
1625
|
+
const ariaLabelledby = element.getAttribute('aria-labelledby')?.trim();
|
|
1626
|
+
if (ariaLabelledby) {
|
|
1627
|
+
const labelledByText = normalizeDescriptorText(
|
|
1628
|
+
ariaLabelledby
|
|
1629
|
+
.split(/\s+/)
|
|
1630
|
+
.map((id) => textOf(document.getElementById(id)))
|
|
1631
|
+
.filter(Boolean)
|
|
1632
|
+
.join(' ')
|
|
1633
|
+
);
|
|
1634
|
+
if (labelledByText) {
|
|
1635
|
+
return labelledByText;
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1638
|
+
|
|
1639
|
+
const heading = element.querySelector(headingSelector);
|
|
1640
|
+
if (isHTMLElementNode(heading) && heading !== element && !heading.contains(element)) {
|
|
1641
|
+
const headingText = normalizeDescriptorText(heading.innerText || heading.textContent || '');
|
|
1642
|
+
if (headingText) {
|
|
1643
|
+
return headingText;
|
|
1644
|
+
}
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1647
|
+
const text = textOf(element, { container: true });
|
|
1648
|
+
if (!text) {
|
|
1649
|
+
return undefined;
|
|
1650
|
+
}
|
|
1651
|
+
return text.length <= 140 ? text : text.slice(0, 140);
|
|
1652
|
+
};
|
|
1653
|
+
|
|
1983
1654
|
const contextNodeOf = (element) => {
|
|
1984
1655
|
if (!element) return undefined;
|
|
1985
1656
|
|
|
1986
1657
|
const kind = element.getAttribute?.('role')?.trim() || element.tagName?.toLowerCase?.();
|
|
1987
|
-
const label =
|
|
1988
|
-
const
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1658
|
+
const label = contextLabelOf(element);
|
|
1659
|
+
const selector = buildSelector(element);
|
|
1660
|
+
if (!kind && !label && !selector) return undefined;
|
|
1661
|
+
return {
|
|
1662
|
+
kind: kind || undefined,
|
|
1663
|
+
label,
|
|
1664
|
+
text: undefined,
|
|
1665
|
+
selector,
|
|
1666
|
+
};
|
|
1992
1667
|
};
|
|
1993
1668
|
|
|
1994
1669
|
const pageSignature =
|
|
@@ -2328,7 +2003,6 @@ async function collectDomTargetsFromDocument(context, options) {
|
|
|
2328
2003
|
variant: 'seat-cell',
|
|
2329
2004
|
row,
|
|
2330
2005
|
column,
|
|
2331
|
-
zone: landmarkLabelOf(ownerSurface) || landmarkLabelOf(surface) || undefined,
|
|
2332
2006
|
cellLabel: label,
|
|
2333
2007
|
},
|
|
2334
2008
|
surface: ownerSurface,
|
|
@@ -2442,7 +2116,7 @@ async function collectDomTargetsFromDocument(context, options) {
|
|
|
2442
2116
|
|
|
2443
2117
|
let current = composedParentElement(element);
|
|
2444
2118
|
let depth = 0;
|
|
2445
|
-
while (current && depth <
|
|
2119
|
+
while (current && depth < 16) {
|
|
2446
2120
|
pushCandidate(current, depth + 2);
|
|
2447
2121
|
current = composedParentElement(current);
|
|
2448
2122
|
depth += 1;
|
|
@@ -2518,9 +2192,13 @@ async function collectDomTargetsFromDocument(context, options) {
|
|
|
2518
2192
|
(isHTMLElementNode(overlaySurface) ? overlaySurface : localSurface || selfSurface);
|
|
2519
2193
|
const surfaceSelectors = surfaceSelectorsOf(element, localSurface || selfSurface);
|
|
2520
2194
|
const structure = visualSeatGrid?.structure || inferStructuredCell(element, surface);
|
|
2521
|
-
const
|
|
2195
|
+
const fallbackLabel = explicitLabelOf(element) || looseFieldLabelOf(element);
|
|
2196
|
+
const currentValue = popupCurrentValueOf(element);
|
|
2522
2197
|
const role = inferRole(element);
|
|
2523
2198
|
const surfaceKind = visualSeatGrid?.surfaceKind || surfaceKindOf(surface);
|
|
2199
|
+
const fallbackSurfaceLabel =
|
|
2200
|
+
(visualSeatGrid?.hintText ? 'Seat map' : undefined) ||
|
|
2201
|
+
surfaceFallbackLabelOf(surface, surfaceKind);
|
|
2524
2202
|
const form = composedClosest(element, 'form');
|
|
2525
2203
|
const testIdAttribute = element.hasAttribute('data-testid')
|
|
2526
2204
|
? 'data-testid'
|
|
@@ -2529,9 +2207,11 @@ async function collectDomTargetsFromDocument(context, options) {
|
|
|
2529
2207
|
: undefined;
|
|
2530
2208
|
return {
|
|
2531
2209
|
kind: inferredKind,
|
|
2532
|
-
label:
|
|
2210
|
+
label: undefined,
|
|
2211
|
+
fallbackLabel: visualSeatGrid?.label || fallbackLabel,
|
|
2533
2212
|
interactionHint: visualSeatGrid?.interactionHint || (genericClickable ? 'click' : undefined),
|
|
2534
2213
|
role,
|
|
2214
|
+
currentValue: currentValue || undefined,
|
|
2535
2215
|
text: textOf(element),
|
|
2536
2216
|
placeholder: element.getAttribute('placeholder')?.trim() || undefined,
|
|
2537
2217
|
inputName: element.getAttribute('name')?.trim() || undefined,
|
|
@@ -2554,7 +2234,8 @@ async function collectDomTargetsFromDocument(context, options) {
|
|
|
2554
2234
|
? { ...(stateOf(element) || {}), ...(visualSeatGrid?.states || {}) }
|
|
2555
2235
|
: undefined,
|
|
2556
2236
|
surfaceKind,
|
|
2557
|
-
surfaceLabel:
|
|
2237
|
+
surfaceLabel: undefined,
|
|
2238
|
+
fallbackSurfaceLabel,
|
|
2558
2239
|
surfaceSelector: surface ? buildSelector(surface) : undefined,
|
|
2559
2240
|
surfaceSelectors,
|
|
2560
2241
|
surfacePriority: surfacePriorityOf(surface),
|
|
@@ -2576,7 +2257,8 @@ async function collectDomTargetsFromDocument(context, options) {
|
|
|
2576
2257
|
lane: laneOf(rect),
|
|
2577
2258
|
band: bandOf(rect),
|
|
2578
2259
|
},
|
|
2579
|
-
hintText:
|
|
2260
|
+
hintText: undefined,
|
|
2261
|
+
fallbackHintText: visualSeatGrid?.hintText,
|
|
2580
2262
|
visual: visualOf(element),
|
|
2581
2263
|
},
|
|
2582
2264
|
};
|
|
@@ -2727,188 +2409,6 @@ async function collectDomTargetsFromDocument(context, options) {
|
|
|
2727
2409
|
pageSignature: options?.pageSignature,
|
|
2728
2410
|
})));
|
|
2729
2411
|
}
|
|
2730
|
-
async function collectPageSignalsFromDocument(context, options) {
|
|
2731
|
-
const inheritedFramePath = JSON.stringify(options?.framePath ?? []);
|
|
2732
|
-
const inheritedFrameUrl = JSON.stringify(options?.frameUrl ?? '');
|
|
2733
|
-
const observedSignals = await context.evaluate(String.raw `(() => {
|
|
2734
|
-
const inheritedFramePath = ${inheritedFramePath};
|
|
2735
|
-
const inheritedFrameUrl = ${inheritedFrameUrl};
|
|
2736
|
-
const limit = ${DOM_SIGNAL_COLLECTION_LIMIT};
|
|
2737
|
-
const interactiveSelector =
|
|
2738
|
-
'button, a[href], input:not([type="hidden"]), textarea, select, [role="button"], [role="link"], [role="textbox"], [role="combobox"], [role="option"], [role="gridcell"], [contenteditable="true"], [tabindex]:not([tabindex="-1"])';
|
|
2739
|
-
const explicitSelector = [
|
|
2740
|
-
'[role="alert"]',
|
|
2741
|
-
'[role="status"]',
|
|
2742
|
-
'[aria-live="assertive"]',
|
|
2743
|
-
'[aria-live="polite"]',
|
|
2744
|
-
'[role="dialog"]',
|
|
2745
|
-
'[aria-modal="true"]',
|
|
2746
|
-
'[class*="toast"]',
|
|
2747
|
-
'[class*="snackbar"]',
|
|
2748
|
-
'[class*="banner"]',
|
|
2749
|
-
'[class*="notice"]',
|
|
2750
|
-
'[class*="success"]',
|
|
2751
|
-
'[class*="error"]',
|
|
2752
|
-
'[class*="warning"]',
|
|
2753
|
-
'[data-testid*="toast"]',
|
|
2754
|
-
'[data-testid*="banner"]',
|
|
2755
|
-
'[data-testid*="alert"]',
|
|
2756
|
-
'[data-testid*="success"]',
|
|
2757
|
-
'[data-testid*="error"]',
|
|
2758
|
-
'[aria-busy="true"]',
|
|
2759
|
-
'[role="progressbar"]',
|
|
2760
|
-
'button[disabled]',
|
|
2761
|
-
].join(', ');
|
|
2762
|
-
const candidateSelector = 'h1, h2, h3, p, section, article, div, [role="region"]';
|
|
2763
|
-
const outcomeTextRe =
|
|
2764
|
-
/(?:thanks|thank you|success(?:ful|fully)?|receipt|order|confirmed|complete(?:d)?|declin(?:e|ed)|fail(?:ed|ure)|error|unable|try again|verification|verify|challenge|captcha|processing|pending|approved|denied)/i;
|
|
2765
|
-
|
|
2766
|
-
const normalizeText = (value) => (value ?? '').replace(/\s+/g, ' ').trim();
|
|
2767
|
-
const sampleText = (value, maxLength) => {
|
|
2768
|
-
const normalized = normalizeText(value);
|
|
2769
|
-
if (!normalized) {
|
|
2770
|
-
return '';
|
|
2771
|
-
}
|
|
2772
|
-
if (normalized.length <= maxLength) {
|
|
2773
|
-
return normalized;
|
|
2774
|
-
}
|
|
2775
|
-
return normalized.slice(0, maxLength - 1).trimEnd() + '…';
|
|
2776
|
-
};
|
|
2777
|
-
const isVisible = (element) => {
|
|
2778
|
-
if (!(element instanceof HTMLElement)) {
|
|
2779
|
-
return false;
|
|
2780
|
-
}
|
|
2781
|
-
const style = element.ownerDocument?.defaultView?.getComputedStyle(element);
|
|
2782
|
-
if (!style || style.display === 'none' || style.visibility === 'hidden') {
|
|
2783
|
-
return false;
|
|
2784
|
-
}
|
|
2785
|
-
const rect = element.getBoundingClientRect();
|
|
2786
|
-
return rect.width > 0 && rect.height > 0;
|
|
2787
|
-
};
|
|
2788
|
-
const textOf = (element) => {
|
|
2789
|
-
if (!(element instanceof HTMLElement)) {
|
|
2790
|
-
return '';
|
|
2791
|
-
}
|
|
2792
|
-
return sampleText(element.innerText || element.textContent || '', 240);
|
|
2793
|
-
};
|
|
2794
|
-
const hasVisibleInteractiveDescendant = (element) => {
|
|
2795
|
-
if (!(element instanceof HTMLElement)) {
|
|
2796
|
-
return false;
|
|
2797
|
-
}
|
|
2798
|
-
return Array.from(element.querySelectorAll(interactiveSelector)).some(
|
|
2799
|
-
(candidate) => candidate !== element && candidate instanceof HTMLElement && isVisible(candidate)
|
|
2800
|
-
);
|
|
2801
|
-
};
|
|
2802
|
-
const hasNestedOutcomeCandidate = (element) => {
|
|
2803
|
-
if (!(element instanceof HTMLElement)) {
|
|
2804
|
-
return false;
|
|
2805
|
-
}
|
|
2806
|
-
return Array.from(
|
|
2807
|
-
element.querySelectorAll(
|
|
2808
|
-
'h1, h2, h3, p, [role="alert"], [role="status"], [aria-live="assertive"], [aria-live="polite"]'
|
|
2809
|
-
)
|
|
2810
|
-
).some((candidate) => {
|
|
2811
|
-
if (!(candidate instanceof HTMLElement) || candidate === element || !isVisible(candidate)) {
|
|
2812
|
-
return false;
|
|
2813
|
-
}
|
|
2814
|
-
return outcomeTextRe.test(normalizeText(candidate.innerText || candidate.textContent || ''));
|
|
2815
|
-
});
|
|
2816
|
-
};
|
|
2817
|
-
const signalKindOf = (element, text) => {
|
|
2818
|
-
const role = element.getAttribute('role') || '';
|
|
2819
|
-
const ariaLive = element.getAttribute('aria-live') || '';
|
|
2820
|
-
const classBlob =
|
|
2821
|
-
((element.getAttribute('class') || '') + ' ' + Object.values(element.dataset || {}).join(' ')).toLowerCase();
|
|
2822
|
-
|
|
2823
|
-
if (role === 'dialog' || element.getAttribute('aria-modal') === 'true') {
|
|
2824
|
-
return 'dialog';
|
|
2825
|
-
}
|
|
2826
|
-
if (role === 'alert' || ariaLive === 'assertive') {
|
|
2827
|
-
return 'alert';
|
|
2828
|
-
}
|
|
2829
|
-
if (
|
|
2830
|
-
role === 'status' ||
|
|
2831
|
-
ariaLive === 'polite' ||
|
|
2832
|
-
element.hasAttribute('aria-busy') ||
|
|
2833
|
-
element.matches('button[disabled], [role="progressbar"]')
|
|
2834
|
-
) {
|
|
2835
|
-
return 'status';
|
|
2836
|
-
}
|
|
2837
|
-
if (/toast|snackbar|banner|notice|warning|error|success/.test(classBlob)) {
|
|
2838
|
-
return 'notice';
|
|
2839
|
-
}
|
|
2840
|
-
return 'notice';
|
|
2841
|
-
};
|
|
2842
|
-
|
|
2843
|
-
const seen = new Set();
|
|
2844
|
-
const signals = [];
|
|
2845
|
-
const pushSignal = (kind, text) => {
|
|
2846
|
-
const normalized = sampleText(text, 240);
|
|
2847
|
-
if (!normalized) {
|
|
2848
|
-
return;
|
|
2849
|
-
}
|
|
2850
|
-
const key = kind + '|' + normalized.toLowerCase();
|
|
2851
|
-
if (seen.has(key)) {
|
|
2852
|
-
return;
|
|
2853
|
-
}
|
|
2854
|
-
seen.add(key);
|
|
2855
|
-
signals.push({
|
|
2856
|
-
kind,
|
|
2857
|
-
text: normalized,
|
|
2858
|
-
framePath: inheritedFramePath.length > 0 ? inheritedFramePath : undefined,
|
|
2859
|
-
frameUrl: inheritedFrameUrl || undefined,
|
|
2860
|
-
source: 'dom',
|
|
2861
|
-
});
|
|
2862
|
-
if (signals.length > limit) {
|
|
2863
|
-
signals.length = limit;
|
|
2864
|
-
}
|
|
2865
|
-
};
|
|
2866
|
-
|
|
2867
|
-
for (const element of Array.from(document.querySelectorAll(explicitSelector))) {
|
|
2868
|
-
if (!(element instanceof HTMLElement) || !isVisible(element)) {
|
|
2869
|
-
continue;
|
|
2870
|
-
}
|
|
2871
|
-
const text = textOf(element);
|
|
2872
|
-
if (!text) {
|
|
2873
|
-
continue;
|
|
2874
|
-
}
|
|
2875
|
-
pushSignal(signalKindOf(element, text), text);
|
|
2876
|
-
if (signals.length >= limit) {
|
|
2877
|
-
return signals;
|
|
2878
|
-
}
|
|
2879
|
-
}
|
|
2880
|
-
|
|
2881
|
-
for (const element of Array.from(document.querySelectorAll(candidateSelector))) {
|
|
2882
|
-
if (!(element instanceof HTMLElement) || !isVisible(element)) {
|
|
2883
|
-
continue;
|
|
2884
|
-
}
|
|
2885
|
-
const text = textOf(element);
|
|
2886
|
-
if (text.length < 12 || text.length > 240 || !outcomeTextRe.test(text)) {
|
|
2887
|
-
continue;
|
|
2888
|
-
}
|
|
2889
|
-
if (hasVisibleInteractiveDescendant(element) || hasNestedOutcomeCandidate(element)) {
|
|
2890
|
-
continue;
|
|
2891
|
-
}
|
|
2892
|
-
pushSignal(signalKindOf(element, text), text);
|
|
2893
|
-
if (signals.length >= limit) {
|
|
2894
|
-
break;
|
|
2895
|
-
}
|
|
2896
|
-
}
|
|
2897
|
-
|
|
2898
|
-
return signals;
|
|
2899
|
-
})()`);
|
|
2900
|
-
if (!Array.isArray(observedSignals)) {
|
|
2901
|
-
return [];
|
|
2902
|
-
}
|
|
2903
|
-
return observedSignals.filter((signal) => Boolean(signal &&
|
|
2904
|
-
typeof signal === 'object' &&
|
|
2905
|
-
typeof signal.kind === 'string' &&
|
|
2906
|
-
typeof signal.text === 'string' &&
|
|
2907
|
-
signal.text.length > 0)).map((signal) => applyInheritedSignalMetadata(signal, {
|
|
2908
|
-
framePath: options?.framePath,
|
|
2909
|
-
frameUrl: options?.frameUrl,
|
|
2910
|
-
}));
|
|
2911
|
-
}
|
|
2912
2412
|
const FRAME_HOST_DESCRIPTOR_SCRIPT = String.raw `
|
|
2913
2413
|
const ownerWindowOf = (node) => node?.ownerDocument?.defaultView || window;
|
|
2914
2414
|
const isHTMLElementNode = (value) => {
|
|
@@ -3111,7 +2611,7 @@ const FRAME_HOST_DESCRIPTOR_SCRIPT = String.raw `
|
|
|
3111
2611
|
const selector = isSelectorUniqueFor(element, structuralSelector) ? structuralSelector : null;
|
|
3112
2612
|
return descriptorOf(selector);
|
|
3113
2613
|
`;
|
|
3114
|
-
async function readFrameHostDescriptor(frame) {
|
|
2614
|
+
export async function readFrameHostDescriptor(frame) {
|
|
3115
2615
|
const frameElement = await frame.frameElement().catch(() => null);
|
|
3116
2616
|
if (!frameElement) {
|
|
3117
2617
|
return null;
|
|
@@ -3178,55 +2678,8 @@ export async function collectDomTargets(page, options) {
|
|
|
3178
2678
|
await walk(page.mainFrame());
|
|
3179
2679
|
return collected;
|
|
3180
2680
|
}
|
|
3181
|
-
export async function collectPageSignals(page) {
|
|
3182
|
-
if (typeof page.mainFrame !== 'function') {
|
|
3183
|
-
return collectPageSignalsFromDocument(page);
|
|
3184
|
-
}
|
|
3185
|
-
const collected = [];
|
|
3186
|
-
const seen = new Set();
|
|
3187
|
-
const pushSignals = (signals) => {
|
|
3188
|
-
for (const signal of signals) {
|
|
3189
|
-
const key = [signal.kind, signal.text.toLowerCase(), signal.framePath?.join('>') ?? 'top'].join('|');
|
|
3190
|
-
if (seen.has(key)) {
|
|
3191
|
-
continue;
|
|
3192
|
-
}
|
|
3193
|
-
seen.add(key);
|
|
3194
|
-
collected.push(signal);
|
|
3195
|
-
if (collected.length >= DOM_SIGNAL_COLLECTION_LIMIT) {
|
|
3196
|
-
break;
|
|
3197
|
-
}
|
|
3198
|
-
}
|
|
3199
|
-
};
|
|
3200
|
-
const walk = async (frame, framePath) => {
|
|
3201
|
-
if (collected.length >= DOM_SIGNAL_COLLECTION_LIMIT) {
|
|
3202
|
-
return;
|
|
3203
|
-
}
|
|
3204
|
-
const frameUrl = frame.url().trim() || undefined;
|
|
3205
|
-
const signals = await collectPageSignalsFromDocument(frame, {
|
|
3206
|
-
framePath,
|
|
3207
|
-
frameUrl,
|
|
3208
|
-
}).catch(() => []);
|
|
3209
|
-
pushSignals(signals);
|
|
3210
|
-
if (collected.length >= DOM_SIGNAL_COLLECTION_LIMIT) {
|
|
3211
|
-
return;
|
|
3212
|
-
}
|
|
3213
|
-
for (const childFrame of frame.childFrames().slice(0, 20)) {
|
|
3214
|
-
if (collected.length >= DOM_SIGNAL_COLLECTION_LIMIT) {
|
|
3215
|
-
break;
|
|
3216
|
-
}
|
|
3217
|
-
const frameHost = await readFrameHostDescriptor(childFrame);
|
|
3218
|
-
if (!frameHost?.selector || !frameHost.userVisible) {
|
|
3219
|
-
continue;
|
|
3220
|
-
}
|
|
3221
|
-
await walk(childFrame, [...(framePath ?? []), frameHost.selector]);
|
|
3222
|
-
}
|
|
3223
|
-
};
|
|
3224
|
-
await walk(page.mainFrame());
|
|
3225
|
-
return collected;
|
|
3226
|
-
}
|
|
3227
2681
|
export const __testDomTargetCollection = {
|
|
3228
2682
|
collectDomTargetsFromDocument,
|
|
3229
|
-
collectPageSignalsFromDocument,
|
|
3230
2683
|
inferStructuredCellVariantFromEvidence,
|
|
3231
2684
|
locatorDomSignatureScript: LOCATOR_DOM_SIGNATURE_SCRIPT,
|
|
3232
2685
|
};
|