kuaifan a3d5bdf93c feat(ai-assistant): 重构页面操作为统一执行器(Web DOM floor + Electron CDP 双后端)
页面操作(doo page → /ws → 浮窗 operation-module → action-executor)原用手写
iView-only 选择器 + 朴素 value= 赋值,在 React+Radix 同源 iframe 业务表单上几乎
填不进。重构为「复用 Playwright 注入脚本的描述层 + 按环境选后端的执行层」:

描述层(共用):
- 新增 aria/aria-bundle.js,vendoring Playwright ariaSnapshot/roleUtils/domUtils
  子集预构建为 ESM(Apache-2.0 NOTICE + 固定上游 commit),产出 YAML 快照与活的
  ref→Element Map。
- page-context-collector 改用 buildSnapshot 采集,operation-module 经 setRefMap
  以 Map 直接持有元素取代 selector 反查。

执行层(按环境选后端,失败回退):
- web-backend:原生 prototype value setter + beforeinput/input/change/blur 序列
  + 回读校验(不符报 value_not_applied,绝不假报成功);自定义下拉/日期 open→click。
- electron-backend + electron/lib/page-input.js:经 webContents.debugger 走 CDP
  Input 域产生 isTrusted=true 可信输入(insertText/dispatchMouseEvent/KeyEvent),
  任一步失败回退 Web 后端。
- 复杂控件(动态明细表/成员选择器/文件上传)如实返回 unsupported_widget。

协议与链路(doo page、active-context 失效守卫、导航类 action)保持不变。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 02:03:59 +00:00

2564 lines
82 KiB
JavaScript
Vendored

/*
* Vendored subset of Playwright injected ariaSnapshot (Apache-2.0).
* Source: github.com/microsoft/playwright @ aa3c7b5 (packages/injected/src/{ariaSnapshot,roleUtils,domUtils}.ts + packages/isomorphic/*).
* Built via esbuild from esm-entry.ts. Do not edit by hand; regenerate to update.
* See dootask/docs/ai-page-operation-unified-executor.md.
*/
// packages/isomorphic/ariaSnapshot.ts
function ariaNodesEqual(a, b) {
if (a.role !== b.role || a.name !== b.name)
return false;
if (!ariaPropsEqual(a, b) || hasPointerCursor(a) !== hasPointerCursor(b))
return false;
const aKeys = Object.keys(a.props);
const bKeys = Object.keys(b.props);
return aKeys.length === bKeys.length && aKeys.every((k) => a.props[k] === b.props[k]);
}
function hasPointerCursor(ariaNode) {
return ariaNode.box.cursor === "pointer";
}
function ariaPropsEqual(a, b) {
return a.active === b.active && a.checked === b.checked && a.disabled === b.disabled && a.expanded === b.expanded && a.invalid === b.invalid && a.selected === b.selected && a.level === b.level && a.pressed === b.pressed;
}
// packages/isomorphic/stringUtils.ts
var normalizedWhitespaceCache;
function normalizeWhiteSpace(text) {
let result = normalizedWhitespaceCache?.get(text);
if (result === void 0) {
result = text.replace(/[\u200b\u00ad]/g, "").trim().replace(/\s+/g, " ");
normalizedWhitespaceCache?.set(text, result);
}
return result;
}
function escapeRegExp(s) {
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
function longestCommonSubstring(s1, s2) {
const n = s1.length;
const m = s2.length;
let maxLen = 0;
let endingIndex = 0;
const dp = Array(n + 1).fill(null).map(() => Array(m + 1).fill(0));
for (let i = 1; i <= n; i++) {
for (let j = 1; j <= m; j++) {
if (s1[i - 1] === s2[j - 1]) {
dp[i][j] = dp[i - 1][j - 1] + 1;
if (dp[i][j] > maxLen) {
maxLen = dp[i][j];
endingIndex = i;
}
}
}
}
return s1.slice(endingIndex - maxLen, endingIndex);
}
var ansiRegex = new RegExp("([\\u001B\\u009B][[\\]()#?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)|(?:(?:\\d{0,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~])))", "g");
// packages/isomorphic/yaml.ts
function yamlEscapeKeyIfNeeded(str) {
if (!yamlStringNeedsQuotes(str))
return str;
return `'` + str.replace(/'/g, `''`) + `'`;
}
function yamlEscapeValueIfNeeded(str) {
if (!yamlStringNeedsQuotes(str))
return str;
return '"' + str.replace(/[\\"\x00-\x1f\x7f-\x9f]/g, (c) => {
switch (c) {
case "\\":
return "\\\\";
case '"':
return '\\"';
case "\b":
return "\\b";
case "\f":
return "\\f";
case "\n":
return "\\n";
case "\r":
return "\\r";
case " ":
return "\\t";
default:
const code = c.charCodeAt(0);
return "\\x" + code.toString(16).padStart(2, "0");
}
}) + '"';
}
function yamlStringNeedsQuotes(str) {
if (str.length === 0)
return true;
if (/^\s|\s$/.test(str))
return true;
if (/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f-\x9f]/.test(str))
return true;
if (/^-/.test(str))
return true;
if (/[\n:](\s|$)/.test(str))
return true;
if (/\s#/.test(str))
return true;
if (/[\n\r]/.test(str))
return true;
if (/^[&*\],?!>|@"'#%]/.test(str))
return true;
if (/[{}`]/.test(str))
return true;
if (/^\[/.test(str))
return true;
if (!isNaN(Number(str)) || ["y", "n", "yes", "no", "true", "false", "on", "off", "null"].includes(str.toLowerCase()))
return true;
return false;
}
// packages/injected/src/domUtils.ts
var globalOptions = {};
function parentElementOrShadowHost(element) {
if (element.parentElement)
return element.parentElement;
if (!element.parentNode)
return;
if (element.parentNode.nodeType === 11 && element.parentNode.host)
return element.parentNode.host;
}
function enclosingShadowRootOrDocument(element) {
let node = element;
while (node.parentNode)
node = node.parentNode;
if (node.nodeType === 11 || node.nodeType === 9)
return node;
}
function enclosingShadowHost(element) {
while (element.parentElement)
element = element.parentElement;
return parentElementOrShadowHost(element);
}
function closestCrossShadow(element, css, scope) {
while (element) {
const closest = element.closest(css);
if (scope && closest !== scope && closest?.contains(scope))
return;
if (closest)
return closest;
element = enclosingShadowHost(element);
}
}
function getElementComputedStyle(element, pseudo) {
const cache = pseudo === "::before" ? cacheStyleBefore : pseudo === "::after" ? cacheStyleAfter : cacheStyle;
if (cache && cache.has(element))
return cache.get(element);
const style = element.ownerDocument && element.ownerDocument.defaultView ? element.ownerDocument.defaultView.getComputedStyle(element, pseudo) : void 0;
cache?.set(element, style);
return style;
}
function isElementStyleVisibilityVisible(element, style) {
style = style ?? getElementComputedStyle(element);
if (!style)
return true;
if (Element.prototype.checkVisibility && globalOptions.browserNameForWorkarounds !== "webkit") {
if (!element.checkVisibility())
return false;
} else {
const detailsOrSummary = element.closest("details,summary");
if (detailsOrSummary !== element && detailsOrSummary?.nodeName === "DETAILS" && !detailsOrSummary.open)
return false;
}
if (style.visibility !== "visible")
return false;
return true;
}
function computeBox(element) {
const style = getElementComputedStyle(element);
if (!style)
return { visible: true, inline: false };
const cursor = style.cursor;
if (style.display === "contents") {
for (let child = element.firstChild; child; child = child.nextSibling) {
if (child.nodeType === 1 && isElementVisible(child))
return { visible: true, inline: false, cursor };
if (child.nodeType === 3 && isVisibleTextNode(child))
return { visible: true, inline: true, cursor };
}
return { visible: false, inline: false, cursor };
}
if (!isElementStyleVisibilityVisible(element, style))
return { cursor, visible: false, inline: false };
const rect = element.getBoundingClientRect();
return { cursor, visible: rect.width > 0 && rect.height > 0, inline: style.display === "inline" };
}
function isElementVisible(element) {
return computeBox(element).visible;
}
function isVisibleTextNode(node) {
const range = node.ownerDocument.createRange();
range.selectNode(node);
const rect = range.getBoundingClientRect();
return rect.width > 0 && rect.height > 0;
}
function elementSafeTagName(element) {
const tagName = element.tagName;
if (typeof tagName === "string")
return tagName.toUpperCase();
if (element instanceof HTMLFormElement)
return "FORM";
return element.tagName.toUpperCase();
}
var cacheStyle;
var cacheStyleBefore;
var cacheStyleAfter;
var cachesCounter = 0;
function beginDOMCaches() {
++cachesCounter;
cacheStyle ??= /* @__PURE__ */ new Map();
cacheStyleBefore ??= /* @__PURE__ */ new Map();
cacheStyleAfter ??= /* @__PURE__ */ new Map();
}
function endDOMCaches() {
if (!--cachesCounter) {
cacheStyle = void 0;
cacheStyleBefore = void 0;
cacheStyleAfter = void 0;
}
}
// packages/isomorphic/cssTokenizer.ts
var between = function(num, first, last) {
return num >= first && num <= last;
};
function digit(code) {
return between(code, 48, 57);
}
function hexdigit(code) {
return digit(code) || between(code, 65, 70) || between(code, 97, 102);
}
function uppercaseletter(code) {
return between(code, 65, 90);
}
function lowercaseletter(code) {
return between(code, 97, 122);
}
function letter(code) {
return uppercaseletter(code) || lowercaseletter(code);
}
function nonascii(code) {
return code >= 128;
}
function namestartchar(code) {
return letter(code) || nonascii(code) || code === 95;
}
function namechar(code) {
return namestartchar(code) || digit(code) || code === 45;
}
function nonprintable(code) {
return between(code, 0, 8) || code === 11 || between(code, 14, 31) || code === 127;
}
function newline(code) {
return code === 10;
}
function whitespace(code) {
return newline(code) || code === 9 || code === 32;
}
var maximumallowedcodepoint = 1114111;
var InvalidCharacterError = class extends Error {
constructor(message) {
super(message);
this.name = "InvalidCharacterError";
}
};
function preprocess(str) {
const codepoints = [];
for (let i = 0; i < str.length; i++) {
let code = str.charCodeAt(i);
if (code === 13 && str.charCodeAt(i + 1) === 10) {
code = 10;
i++;
}
if (code === 13 || code === 12)
code = 10;
if (code === 0)
code = 65533;
if (between(code, 55296, 56319) && between(str.charCodeAt(i + 1), 56320, 57343)) {
const lead = code - 55296;
const trail = str.charCodeAt(i + 1) - 56320;
code = Math.pow(2, 16) + lead * Math.pow(2, 10) + trail;
i++;
}
codepoints.push(code);
}
return codepoints;
}
function stringFromCode(code) {
if (code <= 65535)
return String.fromCharCode(code);
code -= Math.pow(2, 16);
const lead = Math.floor(code / Math.pow(2, 10)) + 55296;
const trail = code % Math.pow(2, 10) + 56320;
return String.fromCharCode(lead) + String.fromCharCode(trail);
}
function tokenize(str1) {
const str = preprocess(str1);
let i = -1;
const tokens = [];
let code;
let line = 0;
let column = 0;
let lastLineLength = 0;
const incrLineno = function() {
line += 1;
lastLineLength = column;
column = 0;
};
const locStart = { line, column };
const codepoint = function(i2) {
if (i2 >= str.length)
return -1;
return str[i2];
};
const next = function(num) {
if (num === void 0)
num = 1;
if (num > 3)
throw "Spec Error: no more than three codepoints of lookahead.";
return codepoint(i + num);
};
const consume = function(num) {
if (num === void 0)
num = 1;
i += num;
code = codepoint(i);
if (newline(code))
incrLineno();
else
column += num;
return true;
};
const reconsume = function() {
i -= 1;
if (newline(code)) {
line -= 1;
column = lastLineLength;
} else {
column -= 1;
}
locStart.line = line;
locStart.column = column;
return true;
};
const eof = function(codepoint2) {
if (codepoint2 === void 0)
codepoint2 = code;
return codepoint2 === -1;
};
const donothing = function() {
};
const parseerror = function() {
};
const consumeAToken = function() {
consumeComments();
consume();
if (whitespace(code)) {
while (whitespace(next()))
consume();
return new WhitespaceToken();
} else if (code === 34) {
return consumeAStringToken();
} else if (code === 35) {
if (namechar(next()) || areAValidEscape(next(1), next(2))) {
const token = new HashToken("");
if (wouldStartAnIdentifier(next(1), next(2), next(3)))
token.type = "id";
token.value = consumeAName();
return token;
} else {
return new DelimToken(code);
}
} else if (code === 36) {
if (next() === 61) {
consume();
return new SuffixMatchToken();
} else {
return new DelimToken(code);
}
} else if (code === 39) {
return consumeAStringToken();
} else if (code === 40) {
return new OpenParenToken();
} else if (code === 41) {
return new CloseParenToken();
} else if (code === 42) {
if (next() === 61) {
consume();
return new SubstringMatchToken();
} else {
return new DelimToken(code);
}
} else if (code === 43) {
if (startsWithANumber()) {
reconsume();
return consumeANumericToken();
} else {
return new DelimToken(code);
}
} else if (code === 44) {
return new CommaToken();
} else if (code === 45) {
if (startsWithANumber()) {
reconsume();
return consumeANumericToken();
} else if (next(1) === 45 && next(2) === 62) {
consume(2);
return new CDCToken();
} else if (startsWithAnIdentifier()) {
reconsume();
return consumeAnIdentlikeToken();
} else {
return new DelimToken(code);
}
} else if (code === 46) {
if (startsWithANumber()) {
reconsume();
return consumeANumericToken();
} else {
return new DelimToken(code);
}
} else if (code === 58) {
return new ColonToken();
} else if (code === 59) {
return new SemicolonToken();
} else if (code === 60) {
if (next(1) === 33 && next(2) === 45 && next(3) === 45) {
consume(3);
return new CDOToken();
} else {
return new DelimToken(code);
}
} else if (code === 64) {
if (wouldStartAnIdentifier(next(1), next(2), next(3)))
return new AtKeywordToken(consumeAName());
else
return new DelimToken(code);
} else if (code === 91) {
return new OpenSquareToken();
} else if (code === 92) {
if (startsWithAValidEscape()) {
reconsume();
return consumeAnIdentlikeToken();
} else {
parseerror();
return new DelimToken(code);
}
} else if (code === 93) {
return new CloseSquareToken();
} else if (code === 94) {
if (next() === 61) {
consume();
return new PrefixMatchToken();
} else {
return new DelimToken(code);
}
} else if (code === 123) {
return new OpenCurlyToken();
} else if (code === 124) {
if (next() === 61) {
consume();
return new DashMatchToken();
} else if (next() === 124) {
consume();
return new ColumnToken();
} else {
return new DelimToken(code);
}
} else if (code === 125) {
return new CloseCurlyToken();
} else if (code === 126) {
if (next() === 61) {
consume();
return new IncludeMatchToken();
} else {
return new DelimToken(code);
}
} else if (digit(code)) {
reconsume();
return consumeANumericToken();
} else if (namestartchar(code)) {
reconsume();
return consumeAnIdentlikeToken();
} else if (eof()) {
return new EOFToken();
} else {
return new DelimToken(code);
}
};
const consumeComments = function() {
while (next(1) === 47 && next(2) === 42) {
consume(2);
while (true) {
consume();
if (code === 42 && next() === 47) {
consume();
break;
} else if (eof()) {
parseerror();
return;
}
}
}
};
const consumeANumericToken = function() {
const num = consumeANumber();
if (wouldStartAnIdentifier(next(1), next(2), next(3))) {
const token = new DimensionToken();
token.value = num.value;
token.repr = num.repr;
token.type = num.type;
token.unit = consumeAName();
return token;
} else if (next() === 37) {
consume();
const token = new PercentageToken();
token.value = num.value;
token.repr = num.repr;
return token;
} else {
const token = new NumberToken();
token.value = num.value;
token.repr = num.repr;
token.type = num.type;
return token;
}
};
const consumeAnIdentlikeToken = function() {
const str2 = consumeAName();
if (str2.toLowerCase() === "url" && next() === 40) {
consume();
while (whitespace(next(1)) && whitespace(next(2)))
consume();
if (next() === 34 || next() === 39)
return new FunctionToken(str2);
else if (whitespace(next()) && (next(2) === 34 || next(2) === 39))
return new FunctionToken(str2);
else
return consumeAURLToken();
} else if (next() === 40) {
consume();
return new FunctionToken(str2);
} else {
return new IdentToken(str2);
}
};
const consumeAStringToken = function(endingCodePoint) {
if (endingCodePoint === void 0)
endingCodePoint = code;
let string = "";
while (consume()) {
if (code === endingCodePoint || eof()) {
return new StringToken(string);
} else if (newline(code)) {
parseerror();
reconsume();
return new BadStringToken();
} else if (code === 92) {
if (eof(next()))
donothing();
else if (newline(next()))
consume();
else
string += stringFromCode(consumeEscape());
} else {
string += stringFromCode(code);
}
}
throw new Error("Internal error");
};
const consumeAURLToken = function() {
const token = new URLToken("");
while (whitespace(next()))
consume();
if (eof(next()))
return token;
while (consume()) {
if (code === 41 || eof()) {
return token;
} else if (whitespace(code)) {
while (whitespace(next()))
consume();
if (next() === 41 || eof(next())) {
consume();
return token;
} else {
consumeTheRemnantsOfABadURL();
return new BadURLToken();
}
} else if (code === 34 || code === 39 || code === 40 || nonprintable(code)) {
parseerror();
consumeTheRemnantsOfABadURL();
return new BadURLToken();
} else if (code === 92) {
if (startsWithAValidEscape()) {
token.value += stringFromCode(consumeEscape());
} else {
parseerror();
consumeTheRemnantsOfABadURL();
return new BadURLToken();
}
} else {
token.value += stringFromCode(code);
}
}
throw new Error("Internal error");
};
const consumeEscape = function() {
consume();
if (hexdigit(code)) {
const digits = [code];
for (let total = 0; total < 5; total++) {
if (hexdigit(next())) {
consume();
digits.push(code);
} else {
break;
}
}
if (whitespace(next()))
consume();
let value = parseInt(digits.map(function(x) {
return String.fromCharCode(x);
}).join(""), 16);
if (value > maximumallowedcodepoint)
value = 65533;
return value;
} else if (eof()) {
return 65533;
} else {
return code;
}
};
const areAValidEscape = function(c1, c2) {
if (c1 !== 92)
return false;
if (newline(c2))
return false;
return true;
};
const startsWithAValidEscape = function() {
return areAValidEscape(code, next());
};
const wouldStartAnIdentifier = function(c1, c2, c3) {
if (c1 === 45)
return namestartchar(c2) || c2 === 45 || areAValidEscape(c2, c3);
else if (namestartchar(c1))
return true;
else if (c1 === 92)
return areAValidEscape(c1, c2);
else
return false;
};
const startsWithAnIdentifier = function() {
return wouldStartAnIdentifier(code, next(1), next(2));
};
const wouldStartANumber = function(c1, c2, c3) {
if (c1 === 43 || c1 === 45) {
if (digit(c2))
return true;
if (c2 === 46 && digit(c3))
return true;
return false;
} else if (c1 === 46) {
if (digit(c2))
return true;
return false;
} else if (digit(c1)) {
return true;
} else {
return false;
}
};
const startsWithANumber = function() {
return wouldStartANumber(code, next(1), next(2));
};
const consumeAName = function() {
let result = "";
while (consume()) {
if (namechar(code)) {
result += stringFromCode(code);
} else if (startsWithAValidEscape()) {
result += stringFromCode(consumeEscape());
} else {
reconsume();
return result;
}
}
throw new Error("Internal parse error");
};
const consumeANumber = function() {
let repr = "";
let type = "integer";
if (next() === 43 || next() === 45) {
consume();
repr += stringFromCode(code);
}
while (digit(next())) {
consume();
repr += stringFromCode(code);
}
if (next(1) === 46 && digit(next(2))) {
consume();
repr += stringFromCode(code);
consume();
repr += stringFromCode(code);
type = "number";
while (digit(next())) {
consume();
repr += stringFromCode(code);
}
}
const c1 = next(1);
const c2 = next(2);
const c3 = next(3);
if ((c1 === 69 || c1 === 101) && digit(c2)) {
consume();
repr += stringFromCode(code);
consume();
repr += stringFromCode(code);
type = "number";
while (digit(next())) {
consume();
repr += stringFromCode(code);
}
} else if ((c1 === 69 || c1 === 101) && (c2 === 43 || c2 === 45) && digit(c3)) {
consume();
repr += stringFromCode(code);
consume();
repr += stringFromCode(code);
consume();
repr += stringFromCode(code);
type = "number";
while (digit(next())) {
consume();
repr += stringFromCode(code);
}
}
const value = convertAStringToANumber(repr);
return { type, value, repr };
};
const convertAStringToANumber = function(string) {
return +string;
};
const consumeTheRemnantsOfABadURL = function() {
while (consume()) {
if (code === 41 || eof()) {
return;
} else if (startsWithAValidEscape()) {
consumeEscape();
donothing();
} else {
donothing();
}
}
};
let iterationCount = 0;
while (!eof(next())) {
tokens.push(consumeAToken());
iterationCount++;
if (iterationCount > str.length * 2)
throw new Error("I'm infinite-looping!");
}
return tokens;
}
var CSSParserToken = class {
tokenType = "";
value;
toJSON() {
return { token: this.tokenType };
}
toString() {
return this.tokenType;
}
toSource() {
return "" + this;
}
};
var BadStringToken = class extends CSSParserToken {
tokenType = "BADSTRING";
};
var BadURLToken = class extends CSSParserToken {
tokenType = "BADURL";
};
var WhitespaceToken = class extends CSSParserToken {
tokenType = "WHITESPACE";
toString() {
return "WS";
}
toSource() {
return " ";
}
};
var CDOToken = class extends CSSParserToken {
tokenType = "CDO";
toSource() {
return "<!--";
}
};
var CDCToken = class extends CSSParserToken {
tokenType = "CDC";
toSource() {
return "-->";
}
};
var ColonToken = class extends CSSParserToken {
tokenType = ":";
};
var SemicolonToken = class extends CSSParserToken {
tokenType = ";";
};
var CommaToken = class extends CSSParserToken {
tokenType = ",";
};
var GroupingToken = class extends CSSParserToken {
value = "";
mirror = "";
};
var OpenCurlyToken = class extends GroupingToken {
tokenType = "{";
constructor() {
super();
this.value = "{";
this.mirror = "}";
}
};
var CloseCurlyToken = class extends GroupingToken {
tokenType = "}";
constructor() {
super();
this.value = "}";
this.mirror = "{";
}
};
var OpenSquareToken = class extends GroupingToken {
tokenType = "[";
constructor() {
super();
this.value = "[";
this.mirror = "]";
}
};
var CloseSquareToken = class extends GroupingToken {
tokenType = "]";
constructor() {
super();
this.value = "]";
this.mirror = "[";
}
};
var OpenParenToken = class extends GroupingToken {
tokenType = "(";
constructor() {
super();
this.value = "(";
this.mirror = ")";
}
};
var CloseParenToken = class extends GroupingToken {
tokenType = ")";
constructor() {
super();
this.value = ")";
this.mirror = "(";
}
};
var IncludeMatchToken = class extends CSSParserToken {
tokenType = "~=";
};
var DashMatchToken = class extends CSSParserToken {
tokenType = "|=";
};
var PrefixMatchToken = class extends CSSParserToken {
tokenType = "^=";
};
var SuffixMatchToken = class extends CSSParserToken {
tokenType = "$=";
};
var SubstringMatchToken = class extends CSSParserToken {
tokenType = "*=";
};
var ColumnToken = class extends CSSParserToken {
tokenType = "||";
};
var EOFToken = class extends CSSParserToken {
tokenType = "EOF";
toSource() {
return "";
}
};
var DelimToken = class extends CSSParserToken {
tokenType = "DELIM";
value = "";
constructor(code) {
super();
this.value = stringFromCode(code);
}
toString() {
return "DELIM(" + this.value + ")";
}
toJSON() {
const json = this.constructor.prototype.constructor.prototype.toJSON.call(this);
json.value = this.value;
return json;
}
toSource() {
if (this.value === "\\")
return "\\\n";
else
return this.value;
}
};
var StringValuedToken = class extends CSSParserToken {
value = "";
ASCIIMatch(str) {
return this.value.toLowerCase() === str.toLowerCase();
}
toJSON() {
const json = this.constructor.prototype.constructor.prototype.toJSON.call(this);
json.value = this.value;
return json;
}
};
var IdentToken = class extends StringValuedToken {
constructor(val) {
super();
this.value = val;
}
tokenType = "IDENT";
toString() {
return "IDENT(" + this.value + ")";
}
toSource() {
return escapeIdent(this.value);
}
};
var FunctionToken = class extends StringValuedToken {
tokenType = "FUNCTION";
mirror;
constructor(val) {
super();
this.value = val;
this.mirror = ")";
}
toString() {
return "FUNCTION(" + this.value + ")";
}
toSource() {
return escapeIdent(this.value) + "(";
}
};
var AtKeywordToken = class extends StringValuedToken {
tokenType = "AT-KEYWORD";
constructor(val) {
super();
this.value = val;
}
toString() {
return "AT(" + this.value + ")";
}
toSource() {
return "@" + escapeIdent(this.value);
}
};
var HashToken = class extends StringValuedToken {
tokenType = "HASH";
type;
constructor(val) {
super();
this.value = val;
this.type = "unrestricted";
}
toString() {
return "HASH(" + this.value + ")";
}
toJSON() {
const json = this.constructor.prototype.constructor.prototype.toJSON.call(this);
json.value = this.value;
json.type = this.type;
return json;
}
toSource() {
if (this.type === "id")
return "#" + escapeIdent(this.value);
else
return "#" + escapeHash(this.value);
}
};
var StringToken = class extends StringValuedToken {
tokenType = "STRING";
constructor(val) {
super();
this.value = val;
}
toString() {
return '"' + escapeString(this.value) + '"';
}
};
var URLToken = class extends StringValuedToken {
tokenType = "URL";
constructor(val) {
super();
this.value = val;
}
toString() {
return "URL(" + this.value + ")";
}
toSource() {
return 'url("' + escapeString(this.value) + '")';
}
};
var NumberToken = class extends CSSParserToken {
tokenType = "NUMBER";
type;
repr;
constructor() {
super();
this.type = "integer";
this.repr = "";
}
toString() {
if (this.type === "integer")
return "INT(" + this.value + ")";
return "NUMBER(" + this.value + ")";
}
toJSON() {
const json = super.toJSON();
json.value = this.value;
json.type = this.type;
json.repr = this.repr;
return json;
}
toSource() {
return this.repr;
}
};
var PercentageToken = class extends CSSParserToken {
tokenType = "PERCENTAGE";
repr;
constructor() {
super();
this.repr = "";
}
toString() {
return "PERCENTAGE(" + this.value + ")";
}
toJSON() {
const json = this.constructor.prototype.constructor.prototype.toJSON.call(this);
json.value = this.value;
json.repr = this.repr;
return json;
}
toSource() {
return this.repr + "%";
}
};
var DimensionToken = class extends CSSParserToken {
tokenType = "DIMENSION";
type;
repr;
unit;
constructor() {
super();
this.type = "integer";
this.repr = "";
this.unit = "";
}
toString() {
return "DIM(" + this.value + "," + this.unit + ")";
}
toJSON() {
const json = this.constructor.prototype.constructor.prototype.toJSON.call(this);
json.value = this.value;
json.type = this.type;
json.repr = this.repr;
json.unit = this.unit;
return json;
}
toSource() {
const source = this.repr;
let unit = escapeIdent(this.unit);
if (unit[0].toLowerCase() === "e" && (unit[1] === "-" || between(unit.charCodeAt(1), 48, 57))) {
unit = "\\65 " + unit.slice(1, unit.length);
}
return source + unit;
}
};
function escapeIdent(string) {
string = "" + string;
let result = "";
const firstcode = string.charCodeAt(0);
for (let i = 0; i < string.length; i++) {
const code = string.charCodeAt(i);
if (code === 0)
throw new InvalidCharacterError("Invalid character: the input contains U+0000.");
if (between(code, 1, 31) || code === 127 || i === 0 && between(code, 48, 57) || i === 1 && between(code, 48, 57) && firstcode === 45)
result += "\\" + code.toString(16) + " ";
else if (code >= 128 || code === 45 || code === 95 || between(code, 48, 57) || between(code, 65, 90) || between(code, 97, 122))
result += string[i];
else
result += "\\" + string[i];
}
return result;
}
function escapeHash(string) {
string = "" + string;
let result = "";
for (let i = 0; i < string.length; i++) {
const code = string.charCodeAt(i);
if (code === 0)
throw new InvalidCharacterError("Invalid character: the input contains U+0000.");
if (code >= 128 || code === 45 || code === 95 || between(code, 48, 57) || between(code, 65, 90) || between(code, 97, 122))
result += string[i];
else
result += "\\" + code.toString(16) + " ";
}
return result;
}
function escapeString(string) {
string = "" + string;
let result = "";
for (let i = 0; i < string.length; i++) {
const code = string.charCodeAt(i);
if (code === 0)
throw new InvalidCharacterError("Invalid character: the input contains U+0000.");
if (between(code, 1, 31) || code === 127)
result += "\\" + code.toString(16) + " ";
else if (code === 34 || code === 92)
result += "\\" + string[i];
else
result += string[i];
}
return result;
}
// packages/injected/src/roleUtils.ts
function hasExplicitAccessibleName(e) {
return e.hasAttribute("aria-label") || e.hasAttribute("aria-labelledby");
}
var kAncestorPreventingLandmark = "article:not([role]), aside:not([role]), main:not([role]), nav:not([role]), section:not([role]), [role=article], [role=complementary], [role=main], [role=navigation], [role=region]";
var kGlobalAriaAttributes = [
["aria-atomic", void 0],
["aria-busy", void 0],
["aria-controls", void 0],
["aria-current", void 0],
["aria-describedby", void 0],
["aria-details", void 0],
// Global use deprecated in ARIA 1.2
// ['aria-disabled', undefined],
["aria-dropeffect", void 0],
// Global use deprecated in ARIA 1.2
// ['aria-errormessage', undefined],
["aria-flowto", void 0],
["aria-grabbed", void 0],
// Global use deprecated in ARIA 1.2
// ['aria-haspopup', undefined],
["aria-hidden", void 0],
// Global use deprecated in ARIA 1.2
// ['aria-invalid', undefined],
["aria-keyshortcuts", void 0],
["aria-label", ["caption", "code", "deletion", "emphasis", "generic", "insertion", "paragraph", "presentation", "strong", "subscript", "superscript"]],
["aria-labelledby", ["caption", "code", "deletion", "emphasis", "generic", "insertion", "paragraph", "presentation", "strong", "subscript", "superscript"]],
["aria-live", void 0],
["aria-owns", void 0],
["aria-relevant", void 0],
["aria-roledescription", ["generic"]]
];
function hasGlobalAriaAttribute(element, forRole) {
return kGlobalAriaAttributes.some(([attr, prohibited]) => {
return !prohibited?.includes(forRole || "") && element.hasAttribute(attr);
});
}
function hasTabIndex(element) {
return !Number.isNaN(Number(String(element.getAttribute("tabindex"))));
}
function isFocusable(element) {
return !isNativelyDisabled(element) && (isNativelyFocusable(element) || hasTabIndex(element));
}
function isNativelyFocusable(element) {
const tagName = elementSafeTagName(element);
if (["BUTTON", "DETAILS", "SELECT", "TEXTAREA"].includes(tagName))
return true;
if (tagName === "A" || tagName === "AREA")
return element.hasAttribute("href");
if (tagName === "INPUT")
return !element.hidden;
return false;
}
var kImplicitRoleByTagName = {
"A": (e) => {
return e.hasAttribute("href") ? "link" : null;
},
"AREA": (e) => {
return e.hasAttribute("href") ? "link" : null;
},
"ARTICLE": () => "article",
"ASIDE": () => "complementary",
"BLOCKQUOTE": () => "blockquote",
"BUTTON": () => "button",
"CAPTION": () => "caption",
"CODE": () => "code",
"DATALIST": () => "listbox",
"DD": () => "definition",
"DEL": () => "deletion",
"DETAILS": () => "group",
"DFN": () => "term",
"DIALOG": () => "dialog",
"DT": () => "term",
"EM": () => "emphasis",
"FIELDSET": () => "group",
"FIGURE": () => "figure",
"FOOTER": (e) => closestCrossShadow(e, kAncestorPreventingLandmark) ? null : "contentinfo",
"FORM": (e) => hasExplicitAccessibleName(e) ? "form" : null,
"H1": () => "heading",
"H2": () => "heading",
"H3": () => "heading",
"H4": () => "heading",
"H5": () => "heading",
"H6": () => "heading",
"HEADER": (e) => closestCrossShadow(e, kAncestorPreventingLandmark) ? null : "banner",
"HR": () => "separator",
"HTML": () => "document",
"IMG": (e) => e.getAttribute("alt") === "" && !e.getAttribute("title") && !hasGlobalAriaAttribute(e) && !hasTabIndex(e) ? "presentation" : "img",
"INPUT": (e) => {
const type = e.type.toLowerCase();
if (type === "search")
return e.hasAttribute("list") ? "combobox" : "searchbox";
if (["email", "tel", "text", "url", ""].includes(type)) {
const list = getIdRefs(e, e.getAttribute("list"))[0];
return list && elementSafeTagName(list) === "DATALIST" ? "combobox" : "textbox";
}
if (type === "hidden")
return null;
if (type === "file")
return "button";
return inputTypeToRole[type] || "textbox";
},
"INS": () => "insertion",
"LI": () => "listitem",
"MAIN": () => "main",
"MARK": () => "mark",
"MATH": () => "math",
"MENU": () => "list",
"METER": () => "meter",
"NAV": () => "navigation",
"OL": () => "list",
"OPTGROUP": () => "group",
"OPTION": () => "option",
"OUTPUT": () => "status",
"P": () => "paragraph",
"PROGRESS": () => "progressbar",
"SEARCH": () => "search",
"SECTION": (e) => hasExplicitAccessibleName(e) ? "region" : null,
"SELECT": (e) => e.hasAttribute("multiple") || e.size > 1 ? "listbox" : "combobox",
"STRONG": () => "strong",
"SUB": () => "subscript",
"SUP": () => "superscript",
// For <svg> we default to Chrome behavior:
// - Chrome reports 'img'.
// - Firefox reports 'diagram' that is not in official ARIA spec yet.
// - Safari reports 'no role', but still computes accessible name.
"SVG": () => "img",
"TABLE": () => "table",
"TBODY": () => "rowgroup",
"TD": (e) => {
const table = closestCrossShadow(e, "table");
const role = table ? getExplicitAriaRole(table) : "";
return role === "grid" || role === "treegrid" ? "gridcell" : "cell";
},
"TEXTAREA": () => "textbox",
"TFOOT": () => "rowgroup",
"TH": (e) => {
const scope = e.getAttribute("scope");
if (scope === "col" || scope === "colgroup")
return "columnheader";
if (scope === "row" || scope === "rowgroup")
return "rowheader";
const nextSibling = e.nextElementSibling;
const prevSibling = e.previousElementSibling;
const row = !!e.parentElement && elementSafeTagName(e.parentElement) === "TR" ? e.parentElement : void 0;
if (!nextSibling && !prevSibling) {
if (row) {
const table = closestCrossShadow(row, "table");
if (table && table.rows.length <= 1)
return null;
}
return "columnheader";
}
if (isHeaderCell(nextSibling) && isHeaderCell(prevSibling))
return "columnheader";
if (isNonEmptyDataCell(nextSibling) || isNonEmptyDataCell(prevSibling))
return "rowheader";
return "columnheader";
},
"THEAD": () => "rowgroup",
"TIME": () => "time",
"TR": () => "row",
"UL": () => "list"
};
function isHeaderCell(element) {
return !!element && elementSafeTagName(element) === "TH";
}
function isNonEmptyDataCell(element) {
if (!element || elementSafeTagName(element) !== "TD")
return false;
return !!(element.textContent?.trim() || element.children.length > 0);
}
var kPresentationInheritanceParents = {
"DD": ["DL", "DIV"],
"DIV": ["DL"],
"DT": ["DL", "DIV"],
"LI": ["OL", "UL"],
"TBODY": ["TABLE"],
"TD": ["TR"],
"TFOOT": ["TABLE"],
"TH": ["TR"],
"THEAD": ["TABLE"],
"TR": ["THEAD", "TBODY", "TFOOT", "TABLE"]
};
function getImplicitAriaRole(element) {
const implicitRole = kImplicitRoleByTagName[elementSafeTagName(element)]?.(element) || "";
if (!implicitRole)
return null;
let ancestor = element;
while (ancestor) {
const parent = parentElementOrShadowHost(ancestor);
const parents = kPresentationInheritanceParents[elementSafeTagName(ancestor)];
if (!parents || !parent || !parents.includes(elementSafeTagName(parent)))
break;
const parentExplicitRole = getExplicitAriaRole(parent);
if ((parentExplicitRole === "none" || parentExplicitRole === "presentation") && !hasPresentationConflictResolution(parent, parentExplicitRole))
return parentExplicitRole;
ancestor = parent;
}
return implicitRole;
}
var validRoles = [
"alert",
"alertdialog",
"application",
"article",
"banner",
"blockquote",
"button",
"caption",
"cell",
"checkbox",
"code",
"columnheader",
"combobox",
"complementary",
"contentinfo",
"definition",
"deletion",
"dialog",
"directory",
"document",
"emphasis",
"feed",
"figure",
"form",
"generic",
"grid",
"gridcell",
"group",
"heading",
"img",
"insertion",
"link",
"list",
"listbox",
"listitem",
"log",
"main",
"mark",
"marquee",
"math",
"meter",
"menu",
"menubar",
"menuitem",
"menuitemcheckbox",
"menuitemradio",
"navigation",
"none",
"note",
"option",
"paragraph",
"presentation",
"progressbar",
"radio",
"radiogroup",
"region",
"row",
"rowgroup",
"rowheader",
"scrollbar",
"search",
"searchbox",
"separator",
"slider",
"spinbutton",
"status",
"strong",
"subscript",
"superscript",
"switch",
"tab",
"table",
"tablist",
"tabpanel",
"term",
"textbox",
"time",
"timer",
"toolbar",
"tooltip",
"tree",
"treegrid",
"treeitem"
];
function getExplicitAriaRole(element) {
const roles = (element.getAttribute("role") || "").split(" ").map((role) => role.trim());
return roles.find((role) => validRoles.includes(role)) || null;
}
function hasPresentationConflictResolution(element, role) {
return hasGlobalAriaAttribute(element, role) || isFocusable(element);
}
function getAriaRole(element) {
const explicitRole = getExplicitAriaRole(element);
if (!explicitRole)
return getImplicitAriaRole(element);
if (explicitRole === "none" || explicitRole === "presentation") {
const implicitRole = getImplicitAriaRole(element);
if (hasPresentationConflictResolution(element, implicitRole))
return implicitRole;
}
return explicitRole;
}
function getAriaBoolean(attr) {
return attr === null ? void 0 : attr.toLowerCase() === "true";
}
function isElementIgnoredForAria(element) {
return ["STYLE", "SCRIPT", "NOSCRIPT", "TEMPLATE"].includes(elementSafeTagName(element));
}
function isElementHiddenForAria(element) {
if (isElementIgnoredForAria(element))
return true;
const style = getElementComputedStyle(element);
const isSlot = element.nodeName === "SLOT";
if (style?.display === "contents" && !isSlot) {
for (let child = element.firstChild; child; child = child.nextSibling) {
if (child.nodeType === 1 && !isElementHiddenForAria(child))
return false;
if (child.nodeType === 3 && isVisibleTextNode(child))
return false;
}
return true;
}
const isOptionInsideSelect = element.nodeName === "OPTION" && !!element.closest("select");
if (!isOptionInsideSelect && !isSlot && !isElementStyleVisibilityVisible(element, style))
return true;
return belongsToDisplayNoneOrAriaHiddenOrNonSlotted(element);
}
function belongsToDisplayNoneOrAriaHiddenOrNonSlotted(element) {
let hidden = cacheIsHidden?.get(element);
if (hidden === void 0) {
hidden = false;
if (element.parentElement && element.parentElement.shadowRoot && !element.assignedSlot)
hidden = true;
if (!hidden) {
const style = getElementComputedStyle(element);
hidden = !style || style.display === "none" || getAriaBoolean(element.getAttribute("aria-hidden")) === true;
}
if (!hidden) {
const parent = parentElementOrShadowHost(element);
if (parent)
hidden = belongsToDisplayNoneOrAriaHiddenOrNonSlotted(parent);
}
cacheIsHidden?.set(element, hidden);
}
return hidden;
}
function getIdRefs(element, ref) {
if (!ref)
return [];
const root = enclosingShadowRootOrDocument(element);
if (!root)
return [];
try {
const ids = ref.split(" ").filter((id) => !!id);
const result = [];
for (const id of ids) {
const firstElement = root.querySelector("#" + CSS.escape(id));
if (firstElement && !result.includes(firstElement))
result.push(firstElement);
}
return result;
} catch (e) {
return [];
}
}
function trimFlatString(s) {
return s.trim();
}
function asFlatString(s) {
return s.split("\xA0").map((chunk) => chunk.replace(/\r\n/g, "\n").replace(/[\u200b\u00ad]/g, "").replace(/\s\s*/g, " ")).join("\xA0").trim();
}
function queryInAriaOwned(element, selector) {
const result = [...element.querySelectorAll(selector)];
for (const owned of getIdRefs(element, element.getAttribute("aria-owns"))) {
if (owned.matches(selector))
result.push(owned);
result.push(...owned.querySelectorAll(selector));
}
return result;
}
function getCSSContent(element, pseudo) {
const cache = pseudo === "::before" ? cachePseudoContentBefore : pseudo === "::after" ? cachePseudoContentAfter : cachePseudoContent;
if (cache?.has(element))
return cache?.get(element);
const style = getElementComputedStyle(element, pseudo);
let content;
if (style) {
const contentValue = style.content;
if (contentValue && contentValue !== "none" && contentValue !== "normal") {
if (style.display !== "none" && style.visibility !== "hidden") {
content = parseCSSContentPropertyAsString(element, contentValue, !!pseudo);
}
}
}
if (pseudo && content !== void 0) {
const display = style?.display || "inline";
if (display !== "inline")
content = " " + content + " ";
}
if (cache)
cache.set(element, content);
return content;
}
function parseCSSContentPropertyAsString(element, content, isPseudo) {
if (!content || content === "none" || content === "normal") {
return;
}
try {
let tokens = tokenize(content).filter((token) => !(token instanceof WhitespaceToken));
const delimIndex = tokens.findIndex((token) => token instanceof DelimToken && token.value === "/");
if (delimIndex !== -1) {
tokens = tokens.slice(delimIndex + 1);
} else if (!isPseudo) {
return;
}
const accumulated = [];
let index = 0;
while (index < tokens.length) {
if (tokens[index] instanceof StringToken) {
accumulated.push(tokens[index].value);
index++;
} else if (index + 2 < tokens.length && tokens[index] instanceof FunctionToken && tokens[index].value === "attr" && tokens[index + 1] instanceof IdentToken && tokens[index + 2] instanceof CloseParenToken) {
const attrName = tokens[index + 1].value;
accumulated.push(element.getAttribute(attrName) || "");
index += 3;
} else {
return;
}
}
return accumulated.join("");
} catch {
}
}
function getAriaLabelledByElements(element) {
const ref = element.getAttribute("aria-labelledby");
if (ref === null)
return null;
const refs = getIdRefs(element, ref);
return refs.length ? refs : null;
}
function allowsNameFromContent(role, targetDescendant) {
const alwaysAllowsNameFromContent = ["button", "cell", "checkbox", "columnheader", "gridcell", "heading", "link", "menuitem", "menuitemcheckbox", "menuitemradio", "option", "radio", "row", "rowheader", "switch", "tab", "tooltip", "treeitem"].includes(role);
const descendantAllowsNameFromContent = targetDescendant && ["", "caption", "code", "contentinfo", "definition", "deletion", "emphasis", "insertion", "list", "listitem", "mark", "none", "paragraph", "presentation", "region", "row", "rowgroup", "section", "strong", "subscript", "superscript", "table", "term", "time"].includes(role);
return alwaysAllowsNameFromContent || descendantAllowsNameFromContent;
}
function getElementAccessibleName(element, includeHidden) {
const cache = includeHidden ? cacheAccessibleNameHidden : cacheAccessibleName;
let accessibleName = cache?.get(element);
if (accessibleName === void 0) {
accessibleName = "";
const elementProhibitsNaming = ["caption", "code", "definition", "deletion", "emphasis", "generic", "insertion", "mark", "paragraph", "presentation", "strong", "subscript", "suggestion", "superscript", "term", "time"].includes(getAriaRole(element) || "");
if (!elementProhibitsNaming) {
accessibleName = asFlatString(getTextAlternativeInternal(element, {
includeHidden,
visitedElements: /* @__PURE__ */ new Set(),
embeddedInTargetElement: "self"
}));
}
cache?.set(element, accessibleName);
}
return accessibleName;
}
var kAriaInvalidRoles = [
"application",
"checkbox",
"columnheader",
"combobox",
"gridcell",
"listbox",
"radiogroup",
"rowheader",
"searchbox",
"slider",
"spinbutton",
"switch",
"textbox",
"tree"
];
function getAriaInvalid(element) {
const ariaInvalid = element.getAttribute("aria-invalid");
if (!ariaInvalid || ariaInvalid.trim() === "" || ariaInvalid.toLocaleLowerCase() === "false")
return "false";
if (ariaInvalid === "true" || ariaInvalid === "grammar" || ariaInvalid === "spelling")
return ariaInvalid;
return "true";
}
function getTextAlternativeInternal(element, options) {
if (options.visitedElements.has(element))
return "";
const childOptions = {
...options,
embeddedInTargetElement: options.embeddedInTargetElement === "self" ? "descendant" : options.embeddedInTargetElement
};
if (!options.includeHidden) {
const isEmbeddedInHiddenReferenceTraversal = !!options.embeddedInLabelledBy?.hidden || !!options.embeddedInDescribedBy?.hidden || !!options.embeddedInNativeTextAlternative?.hidden || !!options.embeddedInLabel?.hidden;
if (isElementIgnoredForAria(element) || !isEmbeddedInHiddenReferenceTraversal && isElementHiddenForAria(element)) {
options.visitedElements.add(element);
return "";
}
}
const labelledBy = getAriaLabelledByElements(element);
if (!options.embeddedInLabelledBy) {
const accessibleName = (labelledBy || []).map((ref) => getTextAlternativeInternal(ref, {
...options,
embeddedInLabelledBy: { element: ref, hidden: isElementHiddenForAria(ref) },
embeddedInDescribedBy: void 0,
embeddedInTargetElement: void 0,
embeddedInLabel: void 0,
embeddedInNativeTextAlternative: void 0
})).join(" ");
if (accessibleName)
return accessibleName;
}
const role = getAriaRole(element) || "";
const tagName = elementSafeTagName(element);
if (!!options.embeddedInLabel || !!options.embeddedInLabelledBy || options.embeddedInTargetElement === "descendant") {
const isOwnLabel = [...element.labels || []].includes(element);
const isOwnLabelledBy = (labelledBy || []).includes(element);
if (!isOwnLabel && !isOwnLabelledBy) {
if (role === "textbox") {
options.visitedElements.add(element);
if (tagName === "INPUT" || tagName === "TEXTAREA")
return element.value;
return element.textContent || "";
}
if (["combobox", "listbox"].includes(role)) {
options.visitedElements.add(element);
let selectedOptions;
if (tagName === "SELECT") {
selectedOptions = [...element.selectedOptions];
if (!selectedOptions.length && element.options.length)
selectedOptions.push(element.options[0]);
} else {
const listbox = role === "combobox" ? queryInAriaOwned(element, "*").find((e) => getAriaRole(e) === "listbox") : element;
selectedOptions = listbox ? queryInAriaOwned(listbox, '[aria-selected="true"]').filter((e) => getAriaRole(e) === "option") : [];
}
if (!selectedOptions.length && tagName === "INPUT") {
return element.value;
}
return selectedOptions.map((option) => getTextAlternativeInternal(option, childOptions)).join(" ");
}
if (["progressbar", "scrollbar", "slider", "spinbutton", "meter"].includes(role)) {
options.visitedElements.add(element);
if (element.hasAttribute("aria-valuetext"))
return element.getAttribute("aria-valuetext") || "";
if (element.hasAttribute("aria-valuenow"))
return element.getAttribute("aria-valuenow") || "";
return element.getAttribute("value") || "";
}
if (["menu"].includes(role)) {
options.visitedElements.add(element);
return "";
}
}
}
const ariaLabel = element.getAttribute("aria-label") || "";
if (trimFlatString(ariaLabel)) {
options.visitedElements.add(element);
return ariaLabel;
}
if (!["presentation", "none"].includes(role)) {
if (tagName === "INPUT" && ["button", "submit", "reset"].includes(element.type)) {
options.visitedElements.add(element);
const value = element.value || "";
if (trimFlatString(value))
return value;
if (element.type === "submit")
return "Submit";
if (element.type === "reset")
return "Reset";
const title = element.getAttribute("title") || "";
return title;
}
if (tagName === "INPUT" && element.type === "file") {
options.visitedElements.add(element);
const labels = element.labels || [];
if (labels.length && !options.embeddedInLabelledBy)
return getAccessibleNameFromAssociatedLabels(labels, options);
return "Choose File";
}
if (tagName === "INPUT" && element.type === "image") {
options.visitedElements.add(element);
const labels = element.labels || [];
if (labels.length && !options.embeddedInLabelledBy)
return getAccessibleNameFromAssociatedLabels(labels, options);
const alt = element.getAttribute("alt") || "";
if (trimFlatString(alt))
return alt;
const title = element.getAttribute("title") || "";
if (trimFlatString(title))
return title;
return "Submit";
}
if (!labelledBy && tagName === "BUTTON") {
options.visitedElements.add(element);
const labels = element.labels || [];
if (labels.length)
return getAccessibleNameFromAssociatedLabels(labels, options);
}
if (!labelledBy && tagName === "OUTPUT") {
options.visitedElements.add(element);
const labels = element.labels || [];
if (labels.length)
return getAccessibleNameFromAssociatedLabels(labels, options);
return element.getAttribute("title") || "";
}
if (!labelledBy && (tagName === "TEXTAREA" || tagName === "SELECT" || tagName === "INPUT")) {
options.visitedElements.add(element);
const labels = element.labels || [];
if (labels.length)
return getAccessibleNameFromAssociatedLabels(labels, options);
const usePlaceholder = tagName === "INPUT" && ["text", "password", "search", "tel", "email", "url"].includes(element.type) || tagName === "TEXTAREA";
const placeholder = element.getAttribute("placeholder") || "";
const title = element.getAttribute("title") || "";
if (!usePlaceholder || title)
return title;
return placeholder;
}
if (!labelledBy && tagName === "FIELDSET") {
options.visitedElements.add(element);
for (let child = element.firstElementChild; child; child = child.nextElementSibling) {
if (elementSafeTagName(child) === "LEGEND") {
return getTextAlternativeInternal(child, {
...childOptions,
embeddedInNativeTextAlternative: { element: child, hidden: isElementHiddenForAria(child) }
});
}
}
const title = element.getAttribute("title") || "";
return title;
}
if (!labelledBy && tagName === "FIGURE") {
options.visitedElements.add(element);
for (let child = element.firstElementChild; child; child = child.nextElementSibling) {
if (elementSafeTagName(child) === "FIGCAPTION") {
return getTextAlternativeInternal(child, {
...childOptions,
embeddedInNativeTextAlternative: { element: child, hidden: isElementHiddenForAria(child) }
});
}
}
const title = element.getAttribute("title") || "";
return title;
}
if (tagName === "IMG") {
options.visitedElements.add(element);
const alt = element.getAttribute("alt") || "";
if (trimFlatString(alt))
return alt;
const title = element.getAttribute("title") || "";
return title;
}
if (tagName === "TABLE") {
options.visitedElements.add(element);
for (let child = element.firstElementChild; child; child = child.nextElementSibling) {
if (elementSafeTagName(child) === "CAPTION") {
return getTextAlternativeInternal(child, {
...childOptions,
embeddedInNativeTextAlternative: { element: child, hidden: isElementHiddenForAria(child) }
});
}
}
const summary = element.getAttribute("summary") || "";
if (summary)
return summary;
}
if (tagName === "AREA") {
options.visitedElements.add(element);
const alt = element.getAttribute("alt") || "";
if (trimFlatString(alt))
return alt;
const title = element.getAttribute("title") || "";
return title;
}
if (tagName === "SVG" || element.ownerSVGElement) {
options.visitedElements.add(element);
for (let child = element.firstElementChild; child; child = child.nextElementSibling) {
if (elementSafeTagName(child) === "TITLE" && child.ownerSVGElement) {
return getTextAlternativeInternal(child, {
...childOptions,
embeddedInLabelledBy: { element: child, hidden: isElementHiddenForAria(child) }
});
}
}
}
if (element.ownerSVGElement && tagName === "A") {
const title = element.getAttribute("xlink:title") || "";
if (trimFlatString(title)) {
options.visitedElements.add(element);
return title;
}
}
}
const shouldNameFromContentForSummary = tagName === "SUMMARY" && !["presentation", "none"].includes(role);
if (allowsNameFromContent(role, options.embeddedInTargetElement === "descendant") || shouldNameFromContentForSummary || !!options.embeddedInLabelledBy || !!options.embeddedInDescribedBy || !!options.embeddedInLabel || !!options.embeddedInNativeTextAlternative) {
options.visitedElements.add(element);
const accessibleName = innerAccumulatedElementText(element, childOptions);
const maybeTrimmedAccessibleName = options.embeddedInTargetElement === "self" ? trimFlatString(accessibleName) : accessibleName;
if (maybeTrimmedAccessibleName)
return accessibleName;
}
if (!["presentation", "none"].includes(role) || tagName === "IFRAME") {
options.visitedElements.add(element);
const title = element.getAttribute("title") || "";
if (trimFlatString(title))
return title;
}
options.visitedElements.add(element);
return "";
}
function innerAccumulatedElementText(element, options) {
const tokens = [];
const visit = (node, skipSlotted) => {
if (skipSlotted && node.assignedSlot)
return;
if (node.nodeType === 1) {
const display = getElementComputedStyle(node)?.display || "inline";
let token = getTextAlternativeInternal(node, options);
if (display !== "inline" || node.nodeName === "BR")
token = " " + token + " ";
tokens.push(token);
} else if (node.nodeType === 3) {
tokens.push(node.textContent || "");
}
};
tokens.push(getCSSContent(element, "::before") || "");
const content = getCSSContent(element);
if (content !== void 0) {
tokens.push(content);
} else {
const assignedNodes = element.nodeName === "SLOT" ? element.assignedNodes() : [];
if (assignedNodes.length) {
for (const child of assignedNodes)
visit(child, false);
} else {
for (let child = element.firstChild; child; child = child.nextSibling)
visit(child, true);
if (element.shadowRoot) {
for (let child = element.shadowRoot.firstChild; child; child = child.nextSibling)
visit(child, true);
}
for (const owned of getIdRefs(element, element.getAttribute("aria-owns")))
visit(owned, true);
}
}
tokens.push(getCSSContent(element, "::after") || "");
return tokens.join("");
}
var kAriaSelectedRoles = ["gridcell", "option", "row", "tab", "rowheader", "columnheader", "treeitem"];
function getAriaSelected(element) {
if (elementSafeTagName(element) === "OPTION")
return element.selected;
if (kAriaSelectedRoles.includes(getAriaRole(element) || ""))
return getAriaBoolean(element.getAttribute("aria-selected")) === true;
return false;
}
var kAriaCheckedRoles = ["checkbox", "menuitemcheckbox", "option", "radio", "switch", "menuitemradio", "treeitem"];
function getAriaChecked(element) {
const result = getChecked(element, true);
return result === "error" ? false : result;
}
function getChecked(element, allowMixed) {
const tagName = elementSafeTagName(element);
if (allowMixed && tagName === "INPUT" && element.indeterminate)
return "mixed";
if (tagName === "INPUT" && ["checkbox", "radio"].includes(element.type))
return element.checked;
if (kAriaCheckedRoles.includes(getAriaRole(element) || "")) {
const checked = element.getAttribute("aria-checked");
if (checked === "true")
return true;
if (allowMixed && checked === "mixed")
return "mixed";
return false;
}
return "error";
}
var kAriaPressedRoles = ["button"];
function getAriaPressed(element) {
if (kAriaPressedRoles.includes(getAriaRole(element) || "")) {
const pressed = element.getAttribute("aria-pressed");
if (pressed === "true")
return true;
if (pressed === "mixed")
return "mixed";
}
return false;
}
var kAriaExpandedRoles = ["application", "button", "checkbox", "combobox", "gridcell", "link", "listbox", "menuitem", "row", "rowheader", "tab", "treeitem", "columnheader", "menuitemcheckbox", "menuitemradio", "rowheader", "switch"];
function getAriaExpanded(element) {
if (elementSafeTagName(element) === "DETAILS")
return element.open;
if (kAriaExpandedRoles.includes(getAriaRole(element) || "")) {
const expanded = element.getAttribute("aria-expanded");
if (expanded === null)
return void 0;
if (expanded === "true")
return true;
return false;
}
return void 0;
}
var kAriaLevelRoles = ["heading", "listitem", "row", "treeitem"];
function getAriaLevel(element) {
const native = { "H1": 1, "H2": 2, "H3": 3, "H4": 4, "H5": 5, "H6": 6 }[elementSafeTagName(element)];
if (native)
return native;
if (kAriaLevelRoles.includes(getAriaRole(element) || "")) {
const attr = element.getAttribute("aria-level");
const value = attr === null ? Number.NaN : Number(attr);
if (Number.isInteger(value) && value >= 1)
return value;
}
return 0;
}
var kAriaDisabledRoles = ["application", "button", "composite", "gridcell", "group", "input", "link", "menuitem", "scrollbar", "separator", "tab", "checkbox", "columnheader", "combobox", "grid", "listbox", "menu", "menubar", "menuitemcheckbox", "menuitemradio", "option", "radio", "radiogroup", "row", "rowheader", "searchbox", "select", "slider", "spinbutton", "switch", "tablist", "textbox", "toolbar", "tree", "treegrid", "treeitem"];
function getAriaDisabled(element) {
return isNativelyDisabled(element) || hasExplicitAriaDisabled(element);
}
function isNativelyDisabled(element) {
const isNativeFormControl = ["BUTTON", "INPUT", "SELECT", "TEXTAREA", "OPTION", "OPTGROUP"].includes(elementSafeTagName(element));
return isNativeFormControl && (element.hasAttribute("disabled") || belongsToDisabledOptGroup(element) || belongsToDisabledFieldSet(element));
}
function belongsToDisabledOptGroup(element) {
return elementSafeTagName(element) === "OPTION" && !!element.closest("OPTGROUP[DISABLED]");
}
function belongsToDisabledFieldSet(element) {
const fieldSetElement = element?.closest("FIELDSET[DISABLED]");
if (!fieldSetElement)
return false;
const legendElement = fieldSetElement.querySelector(":scope > LEGEND");
return !legendElement || !legendElement.contains(element);
}
function hasExplicitAriaDisabled(element, isAncestor = false) {
if (!element)
return false;
if (isAncestor || kAriaDisabledRoles.includes(getAriaRole(element) || "")) {
const attribute = (element.getAttribute("aria-disabled") || "").toLowerCase();
if (attribute === "true")
return true;
if (attribute === "false")
return false;
return hasExplicitAriaDisabled(parentElementOrShadowHost(element), true);
}
return false;
}
function getAccessibleNameFromAssociatedLabels(labels, options) {
return [...labels].map((label) => getTextAlternativeInternal(label, {
...options,
embeddedInLabel: { element: label, hidden: isElementHiddenForAria(label) },
embeddedInNativeTextAlternative: void 0,
embeddedInLabelledBy: void 0,
embeddedInDescribedBy: void 0,
embeddedInTargetElement: void 0
})).filter((accessibleName) => !!accessibleName).join(" ");
}
function receivesPointerEvents(element) {
const cache = cachePointerEvents;
let e = element;
let result;
const parents = [];
for (; e; e = parentElementOrShadowHost(e)) {
const cached = cache.get(e);
if (cached !== void 0) {
result = cached;
break;
}
parents.push(e);
const style = getElementComputedStyle(e);
if (!style) {
result = true;
break;
}
const value = style.pointerEvents;
if (value) {
result = value !== "none";
break;
}
}
if (result === void 0)
result = true;
for (const parent of parents)
cache.set(parent, result);
return result;
}
var cacheAccessibleName;
var cacheAccessibleNameHidden;
var cacheAccessibleDescription;
var cacheAccessibleDescriptionHidden;
var cacheAccessibleErrorMessage;
var cacheIsHidden;
var cachePseudoContent;
var cachePseudoContentBefore;
var cachePseudoContentAfter;
var cachePointerEvents;
var cachesCounter2 = 0;
function beginAriaCaches() {
beginDOMCaches();
++cachesCounter2;
cacheAccessibleName ??= /* @__PURE__ */ new Map();
cacheAccessibleNameHidden ??= /* @__PURE__ */ new Map();
cacheAccessibleDescription ??= /* @__PURE__ */ new Map();
cacheAccessibleDescriptionHidden ??= /* @__PURE__ */ new Map();
cacheAccessibleErrorMessage ??= /* @__PURE__ */ new Map();
cacheIsHidden ??= /* @__PURE__ */ new Map();
cachePseudoContent ??= /* @__PURE__ */ new Map();
cachePseudoContentBefore ??= /* @__PURE__ */ new Map();
cachePseudoContentAfter ??= /* @__PURE__ */ new Map();
cachePointerEvents ??= /* @__PURE__ */ new Map();
}
function endAriaCaches() {
if (!--cachesCounter2) {
cacheAccessibleName = void 0;
cacheAccessibleNameHidden = void 0;
cacheAccessibleDescription = void 0;
cacheAccessibleDescriptionHidden = void 0;
cacheAccessibleErrorMessage = void 0;
cacheIsHidden = void 0;
cachePseudoContent = void 0;
cachePseudoContentBefore = void 0;
cachePseudoContentAfter = void 0;
cachePointerEvents = void 0;
}
endDOMCaches();
}
var inputTypeToRole = {
"button": "button",
"checkbox": "checkbox",
"image": "button",
"number": "spinbutton",
"radio": "radio",
"range": "slider",
"reset": "button",
"submit": "button"
};
// packages/injected/src/ariaSnapshot.ts
var lastRef = 0;
function toInternalOptions(options) {
const renderBoxes = options.boxes;
if (options.mode === "ai") {
return {
visibility: "ariaOrVisible",
refs: "interactable",
refPrefix: options.refPrefix,
includeGenericRole: true,
renderActive: !options.doNotRenderActive,
renderCursorPointer: true,
renderBoxes
};
}
if (options.mode === "autoexpect") {
return { visibility: "ariaAndVisible", refs: "none", renderBoxes };
}
if (options.mode === "codegen") {
return { visibility: "aria", refs: "none", renderStringsAsRegex: true, renderBoxes };
}
return { visibility: "aria", refs: "none", renderBoxes };
}
function generateAriaTree(rootElement, publicOptions) {
const options = toInternalOptions(publicOptions);
const visited = /* @__PURE__ */ new Set();
const snapshot = {
root: { role: "fragment", name: "", children: [], props: {}, box: computeBox(rootElement), receivesPointerEvents: true },
elements: /* @__PURE__ */ new Map(),
refs: /* @__PURE__ */ new Map(),
iframeRefs: []
};
setAriaNodeElement(snapshot.root, rootElement);
const visit = (ariaNode, node, parentElementVisible) => {
if (visited.has(node))
return;
visited.add(node);
if (node.nodeType === Node.TEXT_NODE && node.nodeValue) {
if (!parentElementVisible)
return;
const text = node.nodeValue;
if (ariaNode.role !== "textbox" && text)
ariaNode.children.push(node.nodeValue || "");
return;
}
if (node.nodeType !== Node.ELEMENT_NODE)
return;
const element = node;
const isElementVisibleForAria = !isElementHiddenForAria(element);
let visible = isElementVisibleForAria;
if (options.visibility === "ariaOrVisible")
visible = isElementVisibleForAria || isElementVisible(element);
if (options.visibility === "ariaAndVisible")
visible = isElementVisibleForAria && isElementVisible(element);
if (options.visibility === "aria" && !visible)
return;
const ariaChildren = [];
if (element.hasAttribute("aria-owns")) {
const ids = element.getAttribute("aria-owns").split(/\s+/);
for (const id of ids) {
const ownedElement = rootElement.ownerDocument.getElementById(id);
if (ownedElement)
ariaChildren.push(ownedElement);
}
}
const childAriaNode = visible ? toAriaNode(element, options) : null;
if (childAriaNode) {
if (childAriaNode.ref) {
snapshot.elements.set(childAriaNode.ref, element);
snapshot.refs.set(element, childAriaNode.ref);
if (childAriaNode.role === "iframe")
snapshot.iframeRefs.push(childAriaNode.ref);
}
ariaNode.children.push(childAriaNode);
}
processElement(childAriaNode || ariaNode, element, ariaChildren, visible);
};
function processElement(ariaNode, element, ariaChildren, parentElementVisible) {
const display = getElementComputedStyle(element)?.display || "inline";
const treatAsBlock = display !== "inline" || element.nodeName === "BR" ? " " : "";
if (treatAsBlock)
ariaNode.children.push(treatAsBlock);
ariaNode.children.push(getCSSContent(element, "::before") || "");
const assignedNodes = element.nodeName === "SLOT" ? element.assignedNodes() : [];
if (assignedNodes.length) {
for (const child of assignedNodes)
visit(ariaNode, child, parentElementVisible);
} else {
for (let child = element.firstChild; child; child = child.nextSibling) {
if (!child.assignedSlot)
visit(ariaNode, child, parentElementVisible);
}
if (element.shadowRoot) {
for (let child = element.shadowRoot.firstChild; child; child = child.nextSibling)
visit(ariaNode, child, parentElementVisible);
}
}
for (const child of ariaChildren)
visit(ariaNode, child, parentElementVisible);
ariaNode.children.push(getCSSContent(element, "::after") || "");
if (treatAsBlock)
ariaNode.children.push(treatAsBlock);
if (ariaNode.children.length === 1 && ariaNode.name === ariaNode.children[0])
ariaNode.children = [];
if (ariaNode.role === "link" && element.hasAttribute("href")) {
const href = element.getAttribute("href");
ariaNode.props["url"] = href;
}
if (ariaNode.role === "textbox" && element.hasAttribute("placeholder") && element.getAttribute("placeholder") !== ariaNode.name) {
const placeholder = element.getAttribute("placeholder");
ariaNode.props["placeholder"] = placeholder;
}
}
beginAriaCaches();
try {
visit(snapshot.root, rootElement, true);
} finally {
endAriaCaches();
}
normalizeStringChildren(snapshot.root);
normalizeGenericRoles(snapshot.root);
return snapshot;
}
function computeAriaRef(ariaNode, options) {
if (options.refs === "none")
return;
if (options.refs === "interactable" && (!ariaNode.box.visible || !ariaNode.receivesPointerEvents))
return;
const element = ariaNodeElement(ariaNode);
let ariaRef = element._ariaRef;
if (!ariaRef || ariaRef.role !== ariaNode.role || ariaRef.name !== ariaNode.name) {
ariaRef = { role: ariaNode.role, name: ariaNode.name, ref: (options.refPrefix ?? "") + "e" + ++lastRef };
element._ariaRef = ariaRef;
}
ariaNode.ref = ariaRef.ref;
}
function toAriaNode(element, options) {
const active = element.ownerDocument.activeElement === element;
if (element.nodeName === "IFRAME") {
const ariaNode = {
role: "iframe",
name: "",
children: [],
props: {},
box: computeBox(element),
receivesPointerEvents: true,
active
};
setAriaNodeElement(ariaNode, element);
computeAriaRef(ariaNode, options);
return ariaNode;
}
const defaultRole = options.includeGenericRole ? "generic" : null;
const role = getAriaRole(element) ?? defaultRole;
if (!role || role === "presentation" || role === "none")
return null;
const name = normalizeWhiteSpace(getElementAccessibleName(element, false) || "");
const receivesPointerEvents2 = receivesPointerEvents(element);
const box = computeBox(element);
if (role === "generic" && box.inline && element.childNodes.length === 1 && element.childNodes[0].nodeType === Node.TEXT_NODE)
return null;
const result = {
role,
name,
children: [],
props: {},
box,
receivesPointerEvents: receivesPointerEvents2,
active
};
setAriaNodeElement(result, element);
computeAriaRef(result, options);
if (kAriaCheckedRoles.includes(role))
result.checked = getAriaChecked(element);
if (kAriaDisabledRoles.includes(role))
result.disabled = getAriaDisabled(element);
if (kAriaExpandedRoles.includes(role))
result.expanded = getAriaExpanded(element);
if (kAriaInvalidRoles.includes(role)) {
const invalid = getAriaInvalid(element);
result.invalid = invalid === "false" ? false : invalid === "true" ? true : invalid;
}
if (kAriaLevelRoles.includes(role))
result.level = getAriaLevel(element);
if (kAriaPressedRoles.includes(role))
result.pressed = getAriaPressed(element);
if (kAriaSelectedRoles.includes(role))
result.selected = getAriaSelected(element);
if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement) {
if (element.type !== "checkbox" && element.type !== "radio" && element.type !== "file")
result.children = [element.value];
}
return result;
}
function normalizeGenericRoles(node) {
const normalizeChildren = (node2) => {
const result = [];
for (const child of node2.children || []) {
if (typeof child === "string") {
result.push(child);
continue;
}
const normalized = normalizeChildren(child);
result.push(...normalized);
}
const removeSelf = node2.role === "generic" && !node2.name && result.length <= 1 && result.every((c) => typeof c !== "string" && !!c.ref);
if (removeSelf)
return result;
node2.children = result;
return [node2];
};
normalizeChildren(node);
}
function normalizeStringChildren(rootA11yNode) {
const flushChildren = (buffer, normalizedChildren) => {
if (!buffer.length)
return;
const text = normalizeWhiteSpace(buffer.join(""));
if (text)
normalizedChildren.push(text);
buffer.length = 0;
};
const visit = (ariaNode) => {
const normalizedChildren = [];
const buffer = [];
for (const child of ariaNode.children || []) {
if (typeof child === "string") {
buffer.push(child);
} else {
flushChildren(buffer, normalizedChildren);
visit(child);
normalizedChildren.push(child);
}
}
flushChildren(buffer, normalizedChildren);
ariaNode.children = normalizedChildren.length ? normalizedChildren : [];
if (ariaNode.children.length === 1 && ariaNode.children[0] === ariaNode.name)
ariaNode.children = [];
};
visit(rootA11yNode);
}
function buildByRefMap(root, map = /* @__PURE__ */ new Map()) {
if (root?.ref)
map.set(root.ref, root);
for (const child of root?.children || []) {
if (typeof child !== "string")
buildByRefMap(child, map);
}
return map;
}
function compareSnapshots(ariaSnapshot, previousSnapshot) {
const previousByRef = buildByRefMap(previousSnapshot?.root);
const result = /* @__PURE__ */ new Map();
const visit = (ariaNode, previousNode) => {
let same = ariaNode.children.length === previousNode?.children.length && ariaNodesEqual(ariaNode, previousNode);
let canBeSkipped = same;
for (let childIndex = 0; childIndex < ariaNode.children.length; childIndex++) {
const child = ariaNode.children[childIndex];
const previousChild = previousNode?.children[childIndex];
if (typeof child === "string") {
same &&= child === previousChild;
canBeSkipped &&= child === previousChild;
} else {
let previous = typeof previousChild !== "string" ? previousChild : void 0;
if (child.ref)
previous = previousByRef.get(child.ref);
const sameChild = visit(child, previous);
if (!previous || !sameChild && !child.ref || previous !== previousChild)
canBeSkipped = false;
same &&= sameChild && previous === previousChild;
}
}
result.set(ariaNode, same ? "same" : canBeSkipped ? "skip" : "changed");
return same;
};
visit(ariaSnapshot.root, previousByRef.get(previousSnapshot?.root?.ref));
return result;
}
function filterSnapshotDiff(nodes, statusMap) {
const result = [];
const visit = (ariaNode) => {
const status = statusMap.get(ariaNode);
if (status === "same") {
} else if (status === "skip") {
for (const child of ariaNode.children) {
if (typeof child !== "string")
visit(child);
}
} else {
result.push(ariaNode);
}
};
for (const node of nodes) {
if (typeof node === "string")
result.push(node);
else
visit(node);
}
return result;
}
function indent(depth) {
return " ".repeat(depth);
}
function renderAriaTree(ariaSnapshot, publicOptions, previousSnapshot) {
const options = toInternalOptions(publicOptions);
const lines = [];
const iframeDepths = {};
const includeText = options.renderStringsAsRegex ? textContributesInfo : () => true;
const renderString = options.renderStringsAsRegex ? convertToBestGuessRegex : (str) => str;
let nodesToRender = ariaSnapshot.root.role === "fragment" ? ariaSnapshot.root.children : [ariaSnapshot.root];
const statusMap = compareSnapshots(ariaSnapshot, previousSnapshot);
if (previousSnapshot)
nodesToRender = filterSnapshotDiff(nodesToRender, statusMap);
const visitText = (text, depth) => {
if (publicOptions.depth && depth > publicOptions.depth)
return;
const escaped = yamlEscapeValueIfNeeded(renderString(text));
if (escaped)
lines.push(indent(depth) + "- text: " + escaped);
};
const createKey = (ariaNode, renderCursorPointer) => {
let key = ariaNode.role;
if (ariaNode.name && ariaNode.name.length <= 900) {
const name = renderString(ariaNode.name);
if (name) {
const stringifiedName = name.startsWith("/") && name.endsWith("/") ? name : JSON.stringify(name);
key += " " + stringifiedName;
}
}
if (ariaNode.checked === "mixed")
key += ` [checked=mixed]`;
if (ariaNode.checked === true)
key += ` [checked]`;
if (ariaNode.disabled)
key += ` [disabled]`;
if (ariaNode.expanded)
key += ` [expanded]`;
if (ariaNode.active && options.renderActive)
key += ` [active]`;
if (ariaNode.invalid === "grammar" || ariaNode.invalid === "spelling")
key += ` [invalid=${ariaNode.invalid}]`;
if (ariaNode.invalid === true)
key += ` [invalid]`;
if (ariaNode.level)
key += ` [level=${ariaNode.level}]`;
if (ariaNode.pressed === "mixed")
key += ` [pressed=mixed]`;
if (ariaNode.pressed === true)
key += ` [pressed]`;
if (ariaNode.selected === true)
key += ` [selected]`;
if (ariaNode.ref) {
key += ` [ref=${ariaNode.ref}]`;
if (renderCursorPointer && hasPointerCursor(ariaNode))
key += " [cursor=pointer]";
}
if (options.renderBoxes) {
const element = ariaNodeElement(ariaNode);
if (element) {
const r = element.getBoundingClientRect();
key += ` [box=${Math.round(r.x)},${Math.round(r.y)},${Math.round(r.width)},${Math.round(r.height)}]`;
}
}
return key;
};
const getSingleInlinedTextChild = (ariaNode) => {
return ariaNode?.children.length === 1 && typeof ariaNode.children[0] === "string" && !Object.keys(ariaNode.props).length ? ariaNode.children[0] : void 0;
};
const visit = (ariaNode, depth, renderCursorPointer) => {
if (publicOptions.depth && depth > publicOptions.depth)
return;
if (ariaNode.role === "iframe" && ariaNode.ref)
iframeDepths[ariaNode.ref] = depth;
if (statusMap.get(ariaNode) === "same" && ariaNode.ref) {
lines.push(indent(depth) + `- ref=${ariaNode.ref} [unchanged]`);
return;
}
const isDiffRoot = !!previousSnapshot && !depth;
const escapedKey = indent(depth) + "- " + (isDiffRoot ? "<changed> " : "") + yamlEscapeKeyIfNeeded(createKey(ariaNode, renderCursorPointer));
const singleInlinedTextChild = getSingleInlinedTextChild(ariaNode);
const isAtDepthLimit = !!publicOptions.depth && depth === publicOptions.depth;
const hasNoChildren = !singleInlinedTextChild && (!ariaNode.children.length || isAtDepthLimit);
if (hasNoChildren && !Object.keys(ariaNode.props).length) {
lines.push(escapedKey);
} else if (singleInlinedTextChild !== void 0) {
const shouldInclude = includeText(ariaNode, singleInlinedTextChild);
if (shouldInclude)
lines.push(escapedKey + ": " + yamlEscapeValueIfNeeded(renderString(singleInlinedTextChild)));
else
lines.push(escapedKey);
} else {
lines.push(escapedKey + ":");
for (const [name, value] of Object.entries(ariaNode.props))
lines.push(indent(depth + 1) + "- /" + name + ": " + yamlEscapeValueIfNeeded(value));
const inCursorPointer = !!ariaNode.ref && renderCursorPointer && hasPointerCursor(ariaNode);
for (const child of ariaNode.children) {
if (typeof child === "string")
visitText(includeText(ariaNode, child) ? child : "", depth + 1);
else
visit(child, depth + 1, renderCursorPointer && !inCursorPointer);
}
}
};
for (const nodeToRender of nodesToRender) {
if (typeof nodeToRender === "string")
visitText(nodeToRender, 0);
else
visit(nodeToRender, 0, !!options.renderCursorPointer);
}
return { text: lines.join("\n"), iframeDepths };
}
function convertToBestGuessRegex(text) {
const dynamicContent = [
// 550e8400-e29b-41d4-a716-446655440000
{ regex: /\b[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\b/, replacement: "[0-9a-fA-F-]+" },
// 2mb
{ regex: /\b[\d,.]+[bkmBKM]+\b/, replacement: "[\\d,.]+[bkmBKM]+" },
// 2ms, 20s
{ regex: /\b\d+[hmsp]+\b/, replacement: "\\d+[hmsp]+" },
{ regex: /\b[\d,.]+[hmsp]+\b/, replacement: "[\\d,.]+[hmsp]+" },
// Do not replace single digits with regex by default.
// 2+ digits: [Issue 22, 22.3, 2.33, 2,333]
{ regex: /\b\d+,\d+\b/, replacement: "\\d+,\\d+" },
{ regex: /\b\d+\.\d{2,}\b/, replacement: "\\d+\\.\\d+" },
{ regex: /\b\d{2,}\.\d+\b/, replacement: "\\d+\\.\\d+" },
{ regex: /\b\d{2,}\b/, replacement: "\\d+" }
];
let pattern = "";
let lastIndex = 0;
const combinedRegex = new RegExp(dynamicContent.map((r) => "(" + r.regex.source + ")").join("|"), "g");
text.replace(combinedRegex, (match, ...args) => {
const offset = args[args.length - 2];
const groups = args.slice(0, -2);
pattern += escapeRegExp(text.slice(lastIndex, offset));
for (let i = 0; i < groups.length; i++) {
if (groups[i]) {
const { replacement } = dynamicContent[i];
pattern += replacement;
break;
}
}
lastIndex = offset + match.length;
return match;
});
if (!pattern)
return text;
pattern += escapeRegExp(text.slice(lastIndex));
return String(new RegExp(pattern));
}
function textContributesInfo(node, text) {
if (!text.length)
return false;
if (!node.name)
return true;
const substr = text.length <= 200 && node.name.length <= 200 ? longestCommonSubstring(text, node.name) : "";
let filtered = text;
while (substr && filtered.includes(substr))
filtered = filtered.replace(substr, "");
return filtered.trim().length / text.length > 0.1;
}
var elementSymbol = /* @__PURE__ */ Symbol("element");
function ariaNodeElement(ariaNode) {
return ariaNode[elementSymbol];
}
function setAriaNodeElement(ariaNode, element) {
ariaNode[elementSymbol] = element;
}
// esm-entry.ts
function buildSnapshot(root, options) {
const o = Object.assign({ mode: "ai" }, options || {});
const tree = generateAriaTree(root || document.body, o);
const rendered = renderAriaTree(tree, o);
return {
text: rendered.text,
elements: tree.elements,
// Map<string ref, Element>
refs: tree.refs
// Map<Element, string ref>
};
}
export {
buildSnapshot,
generateAriaTree,
renderAriaTree
};