@canaryai/cli 0.2.8 → 0.2.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +77 -92
  2. package/dist/chunk-C2PGZRYK.js +167 -0
  3. package/dist/chunk-C2PGZRYK.js.map +1 -0
  4. package/dist/{chunk-K2OB72B6.js → chunk-LC7ZVXPH.js} +2 -2
  5. package/dist/{chunk-6WWHXWCS.js → chunk-QLFSJG5O.js} +33 -5
  6. package/dist/chunk-QLFSJG5O.js.map +1 -0
  7. package/dist/{chunk-FK3EZADZ.js → chunk-XGO62PO2.js} +1829 -868
  8. package/dist/chunk-XGO62PO2.js.map +1 -0
  9. package/dist/{debug-workflow-55G4Y6YT.js → debug-workflow-I3F36JBL.js} +57 -36
  10. package/dist/debug-workflow-I3F36JBL.js.map +1 -0
  11. package/dist/{docs-RPFT7ZJB.js → docs-REHST3YB.js} +2 -2
  12. package/dist/{feature-flag-2FDSKOVX.js → feature-flag-3HB5NTMY.js} +3 -2
  13. package/dist/{feature-flag-2FDSKOVX.js.map → feature-flag-3HB5NTMY.js.map} +1 -1
  14. package/dist/index.js +22 -9
  15. package/dist/index.js.map +1 -1
  16. package/dist/{issues-6ZDNDSD6.js → issues-YU57CHXS.js} +3 -2
  17. package/dist/{issues-6ZDNDSD6.js.map → issues-YU57CHXS.js.map} +1 -1
  18. package/dist/{knobs-MZRTYS3P.js → knobs-QJ4IBLCT.js} +3 -2
  19. package/dist/{knobs-MZRTYS3P.js.map → knobs-QJ4IBLCT.js.map} +1 -1
  20. package/dist/{local-browser-X7J27IGS.js → local-browser-MKTJ36KY.js} +3 -3
  21. package/dist/{mcp-4JVLADZL.js → mcp-ZOKM2AUE.js} +49 -238
  22. package/dist/mcp-ZOKM2AUE.js.map +1 -0
  23. package/dist/{record-4OX7HXWQ.js → record-TNDBT3NY.js} +130 -28
  24. package/dist/record-TNDBT3NY.js.map +1 -0
  25. package/dist/session-RNLKFS2Z.js +751 -0
  26. package/dist/session-RNLKFS2Z.js.map +1 -0
  27. package/dist/skill-CZ7SHI3P.js +156 -0
  28. package/dist/skill-CZ7SHI3P.js.map +1 -0
  29. package/dist/{src-I4EXB5OD.js → src-2WSMYBMJ.js} +18 -2
  30. package/package.json +1 -1
  31. package/dist/chunk-6WWHXWCS.js.map +0 -1
  32. package/dist/chunk-FK3EZADZ.js.map +0 -1
  33. package/dist/debug-workflow-55G4Y6YT.js.map +0 -1
  34. package/dist/mcp-4JVLADZL.js.map +0 -1
  35. package/dist/record-4OX7HXWQ.js.map +0 -1
  36. /package/dist/{chunk-K2OB72B6.js.map → chunk-LC7ZVXPH.js.map} +0 -0
  37. /package/dist/{docs-RPFT7ZJB.js.map → docs-REHST3YB.js.map} +0 -0
  38. /package/dist/{local-browser-X7J27IGS.js.map → local-browser-MKTJ36KY.js.map} +0 -0
  39. /package/dist/{src-I4EXB5OD.js.map → src-2WSMYBMJ.js.map} +0 -0
@@ -4383,7 +4383,7 @@ var require_src = __commonJS({
4383
4383
  var require_encoder = __commonJS({
4384
4384
  "../../node_modules/.bun/jpeg-js@0.4.4/node_modules/jpeg-js/lib/encoder.js"(exports2, module2) {
4385
4385
  "use strict";
4386
- var btoa = btoa || function(buf) {
4386
+ var btoa2 = btoa2 || function(buf) {
4387
4387
  return Buffer.from(buf).toString("base64");
4388
4388
  };
4389
4389
  function JPEGEncoder(quality2) {
@@ -5404,7 +5404,7 @@ var require_encoder = __commonJS({
5404
5404
  writeWord(65497);
5405
5405
  if (typeof module2 === "undefined") return new Uint8Array(byteout);
5406
5406
  return Buffer.from(byteout);
5407
- var jpegDataUri = "data:image/jpeg;base64," + btoa(byteout.join(""));
5407
+ var jpegDataUri = "data:image/jpeg;base64," + btoa2(byteout.join(""));
5408
5408
  byteout = [];
5409
5409
  var duration = (/* @__PURE__ */ new Date()).getTime() - time_start;
5410
5410
  return jpegDataUri;
@@ -37998,6 +37998,201 @@ var CdpScreencastManager = class {
37998
37998
  }
37999
37999
  };
38000
38000
 
38001
+ // ../browser-core/src/indexeddb-helpers.ts
38002
+ var MAX_TOTAL_SIZE_BYTES = 10 * 1024 * 1024;
38003
+ async function extractIndexedDB(page) {
38004
+ try {
38005
+ const data = await page.evaluate(async () => {
38006
+ if (!indexedDB.databases) return null;
38007
+ const dbInfos = await indexedDB.databases();
38008
+ if (!dbInfos || dbInfos.length === 0) return null;
38009
+ function serializeValue(value) {
38010
+ if (value instanceof ArrayBuffer) {
38011
+ return {
38012
+ __type: "ArrayBuffer",
38013
+ __data: btoa(String.fromCharCode(...new Uint8Array(value)))
38014
+ };
38015
+ }
38016
+ if (ArrayBuffer.isView(value)) {
38017
+ const buf = value.buffer instanceof ArrayBuffer ? value.buffer : value.buffer;
38018
+ return {
38019
+ __type: "TypedArray",
38020
+ __arrayType: value.constructor.name,
38021
+ __data: btoa(String.fromCharCode(...new Uint8Array(buf, value.byteOffset, value.byteLength)))
38022
+ };
38023
+ }
38024
+ if (value instanceof Blob) {
38025
+ return { __type: "Blob", __skipped: true };
38026
+ }
38027
+ if (value instanceof Date) {
38028
+ return { __type: "Date", __data: value.toISOString() };
38029
+ }
38030
+ if (Array.isArray(value)) {
38031
+ return value.map(serializeValue);
38032
+ }
38033
+ if (value !== null && typeof value === "object") {
38034
+ const result = {};
38035
+ for (const [k, v] of Object.entries(value)) {
38036
+ result[k] = serializeValue(v);
38037
+ }
38038
+ return result;
38039
+ }
38040
+ return value;
38041
+ }
38042
+ const databases = [];
38043
+ for (const dbInfo of dbInfos) {
38044
+ if (!dbInfo.name) continue;
38045
+ try {
38046
+ const db = await new Promise((resolve2, reject2) => {
38047
+ const request2 = indexedDB.open(dbInfo.name, dbInfo.version);
38048
+ request2.onsuccess = () => resolve2(request2.result);
38049
+ request2.onerror = () => reject2(request2.error);
38050
+ request2.onupgradeneeded = () => {
38051
+ request2.transaction?.abort();
38052
+ reject2(new Error("version_mismatch"));
38053
+ };
38054
+ });
38055
+ const objectStores = [];
38056
+ for (const storeName of Array.from(db.objectStoreNames)) {
38057
+ try {
38058
+ const tx = db.transaction(storeName, "readonly");
38059
+ const store = tx.objectStore(storeName);
38060
+ const entries = await new Promise(
38061
+ (resolve2, reject2) => {
38062
+ const items = [];
38063
+ const cursorReq = store.openCursor();
38064
+ cursorReq.onsuccess = () => {
38065
+ const cursor = cursorReq.result;
38066
+ if (cursor) {
38067
+ items.push({
38068
+ key: cursor.key,
38069
+ value: serializeValue(cursor.value)
38070
+ });
38071
+ cursor.continue();
38072
+ } else {
38073
+ resolve2(items);
38074
+ }
38075
+ };
38076
+ cursorReq.onerror = () => reject2(cursorReq.error);
38077
+ }
38078
+ );
38079
+ objectStores.push({
38080
+ name: storeName,
38081
+ keyPath: store.keyPath,
38082
+ autoIncrement: store.autoIncrement,
38083
+ entries
38084
+ });
38085
+ } catch {
38086
+ }
38087
+ }
38088
+ db.close();
38089
+ databases.push({
38090
+ name: dbInfo.name,
38091
+ version: dbInfo.version ?? 1,
38092
+ objectStores
38093
+ });
38094
+ } catch {
38095
+ }
38096
+ }
38097
+ return databases.length > 0 ? { databases } : null;
38098
+ });
38099
+ if (!data) return null;
38100
+ const serialized = JSON.stringify(data);
38101
+ if (serialized.length > MAX_TOTAL_SIZE_BYTES) {
38102
+ return null;
38103
+ }
38104
+ return data;
38105
+ } catch {
38106
+ return null;
38107
+ }
38108
+ }
38109
+ async function restoreIndexedDB(page, data) {
38110
+ if (!data.databases || data.databases.length === 0) return;
38111
+ await page.evaluate(async (databases) => {
38112
+ function deserializeValue(value) {
38113
+ if (value !== null && typeof value === "object" && !Array.isArray(value)) {
38114
+ const obj = value;
38115
+ if (obj.__type === "ArrayBuffer" && typeof obj.__data === "string") {
38116
+ const binary = atob(obj.__data);
38117
+ const bytes = new Uint8Array(binary.length);
38118
+ for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
38119
+ return bytes.buffer;
38120
+ }
38121
+ if (obj.__type === "TypedArray" && typeof obj.__data === "string") {
38122
+ const binary = atob(obj.__data);
38123
+ const bytes = new Uint8Array(binary.length);
38124
+ for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
38125
+ return bytes.buffer;
38126
+ }
38127
+ if (obj.__type === "Date" && typeof obj.__data === "string") {
38128
+ return new Date(obj.__data);
38129
+ }
38130
+ if (obj.__type === "Blob" && obj.__skipped) {
38131
+ return null;
38132
+ }
38133
+ const result = {};
38134
+ for (const [k, v] of Object.entries(obj)) {
38135
+ result[k] = deserializeValue(v);
38136
+ }
38137
+ return result;
38138
+ }
38139
+ if (Array.isArray(value)) {
38140
+ return value.map(deserializeValue);
38141
+ }
38142
+ return value;
38143
+ }
38144
+ for (const dbData of databases) {
38145
+ try {
38146
+ await new Promise((resolve2, reject2) => {
38147
+ const req = indexedDB.deleteDatabase(dbData.name);
38148
+ req.onsuccess = () => resolve2();
38149
+ req.onerror = () => reject2(req.error);
38150
+ req.onblocked = () => resolve2();
38151
+ });
38152
+ const db = await new Promise((resolve2, reject2) => {
38153
+ const request2 = indexedDB.open(dbData.name, dbData.version);
38154
+ request2.onupgradeneeded = () => {
38155
+ const db2 = request2.result;
38156
+ for (const storeData of dbData.objectStores) {
38157
+ if (!db2.objectStoreNames.contains(storeData.name)) {
38158
+ db2.createObjectStore(storeData.name, {
38159
+ keyPath: storeData.keyPath ?? void 0,
38160
+ autoIncrement: storeData.autoIncrement
38161
+ });
38162
+ }
38163
+ }
38164
+ };
38165
+ request2.onsuccess = () => resolve2(request2.result);
38166
+ request2.onerror = () => reject2(request2.error);
38167
+ });
38168
+ for (const storeData of dbData.objectStores) {
38169
+ if (storeData.entries.length === 0) continue;
38170
+ if (!db.objectStoreNames.contains(storeData.name)) continue;
38171
+ try {
38172
+ const tx = db.transaction(storeData.name, "readwrite");
38173
+ const store = tx.objectStore(storeData.name);
38174
+ for (const entry of storeData.entries) {
38175
+ const value = deserializeValue(entry.value);
38176
+ if (storeData.keyPath) {
38177
+ store.put(value);
38178
+ } else {
38179
+ store.put(value, entry.key);
38180
+ }
38181
+ }
38182
+ await new Promise((resolve2, reject2) => {
38183
+ tx.oncomplete = () => resolve2();
38184
+ tx.onerror = () => reject2(tx.error);
38185
+ });
38186
+ } catch {
38187
+ }
38188
+ }
38189
+ db.close();
38190
+ } catch {
38191
+ }
38192
+ }
38193
+ }, data.databases);
38194
+ }
38195
+
38001
38196
  // ../browser-core/src/snapshot-analyzer.ts
38002
38197
  var logger = consoleLogger;
38003
38198
  function setSnapshotAnalyzerLogger(l) {
@@ -38343,7 +38538,7 @@ function normalizeIconLabelText(role, text) {
38343
38538
  return ICON_ONLY_ROLES.has(role) ? "(icon)" : "";
38344
38539
  }
38345
38540
 
38346
- // ../browser-core/src/snapshot-formatter.ts
38541
+ // ../browser-core/src/snapshot-formatter-shared.ts
38347
38542
  var MAX_SEARCH_MATCHES = 10;
38348
38543
  var INTERACTIVE_LEAF_ROLES = /* @__PURE__ */ new Set([
38349
38544
  "button",
@@ -38378,134 +38573,6 @@ var STRUCTURAL_ROLES = /* @__PURE__ */ new Set([
38378
38573
  "section",
38379
38574
  "iframe"
38380
38575
  ]);
38381
- var VISUAL_HEADING_MAX_LENGTH = 60;
38382
- function hasInteractiveDescendants(element) {
38383
- if (element.ref && (INTERACTIVE_ROLES.has(element.role) || isClickableByAttribute(element))) return true;
38384
- for (const child of element.children) {
38385
- if (hasInteractiveDescendants(child)) return true;
38386
- }
38387
- return false;
38388
- }
38389
- function isVisualHeading(element, siblings, siblingIndex) {
38390
- if (element.role !== "generic" && element.role !== "paragraph") return false;
38391
- if (isClickableByAttribute(element)) return false;
38392
- const text = getVisualHeadingText(element);
38393
- if (!text || text.length < 3 || text.length > VISUAL_HEADING_MAX_LENGTH) return false;
38394
- if (hasInteractiveDescendants(element)) return false;
38395
- let interactiveCount = 0;
38396
- for (let i = siblingIndex + 1; i < siblings.length; i++) {
38397
- const sib = siblings[i];
38398
- if (sib.role === "heading" || STRUCTURAL_ROLES.has(sib.role)) break;
38399
- if (sib.role === "generic" || sib.role === "paragraph") {
38400
- if (!isClickableByAttribute(sib) && !hasInteractiveDescendants(sib)) {
38401
- const sibText = getVisualHeadingText(sib);
38402
- if (sibText && sibText.length >= 3 && sibText.length <= VISUAL_HEADING_MAX_LENGTH) {
38403
- break;
38404
- }
38405
- }
38406
- }
38407
- if (hasInteractiveDescendants(sib) || isClickableByAttribute(sib)) {
38408
- interactiveCount++;
38409
- }
38410
- }
38411
- return interactiveCount >= 3;
38412
- }
38413
- function getVisualHeadingText(element) {
38414
- const direct = element.text;
38415
- if (direct) return direct;
38416
- for (const child of element.children) {
38417
- if (TEXT_CARRYING_ROLES.has(child.role) && child.text) return child.text;
38418
- }
38419
- return void 0;
38420
- }
38421
- function findNextSectionBoundary(siblings, startIndex) {
38422
- for (let i = startIndex; i < siblings.length; i++) {
38423
- const sib = siblings[i];
38424
- if (sib.role === "heading" || STRUCTURAL_ROLES.has(sib.role)) return i;
38425
- if (isVisualHeading(sib, siblings, i)) return i;
38426
- }
38427
- return siblings.length;
38428
- }
38429
- function isFieldLabelCandidate(element) {
38430
- if (element.role !== "generic" && element.role !== "paragraph" && element.role !== "text" && element.role !== "label" && element.role !== "strong" && element.role !== "emphasis") {
38431
- return false;
38432
- }
38433
- if (isClickableByAttribute(element)) return false;
38434
- if (hasInteractiveDescendants(element)) return false;
38435
- const text = getFieldLabelText(element);
38436
- if (!text || text.length < 1 || text.length > VISUAL_HEADING_MAX_LENGTH) return false;
38437
- return true;
38438
- }
38439
- var FIELD_LABEL_TEXT_ROLES = /* @__PURE__ */ new Set([
38440
- "generic",
38441
- "paragraph",
38442
- "text",
38443
- "strong",
38444
- "emphasis",
38445
- "label",
38446
- "heading"
38447
- ]);
38448
- function getFieldLabelText(element) {
38449
- if (element.text) return element.text.trim();
38450
- if (element.inputValue && !INTERACTIVE_ROLES.has(element.role)) return element.inputValue.trim();
38451
- for (const child of element.children) {
38452
- if (FIELD_LABEL_TEXT_ROLES.has(child.role)) {
38453
- if (child.text) return child.text.trim();
38454
- if (child.inputValue && !INTERACTIVE_ROLES.has(child.role)) return child.inputValue.trim();
38455
- }
38456
- }
38457
- return void 0;
38458
- }
38459
- function findFieldLabelFromSiblings(siblings, interactiveIndex) {
38460
- for (let i = interactiveIndex - 1; i >= 0; i--) {
38461
- const sib = siblings[i];
38462
- if (INTERACTIVE_ROLES.has(sib.role) || isClickableByAttribute(sib) || STRUCTURAL_ROLES.has(sib.role) || sib.role === "heading") {
38463
- break;
38464
- }
38465
- if (isFieldLabelCandidate(sib)) {
38466
- return getFieldLabelText(sib);
38467
- }
38468
- if (sib.children.length >= 1 && sib.children.length <= 4) {
38469
- const containerLabel = resolveContainerFieldLabel(sib);
38470
- if (containerLabel) return containerLabel;
38471
- }
38472
- if (sib.children.length > 4) break;
38473
- }
38474
- return void 0;
38475
- }
38476
- function findFieldLabelInContext(_element, siblings, siblingIndex) {
38477
- return findFieldLabelFromSiblings(siblings, siblingIndex);
38478
- }
38479
- function resolveContainerFieldLabel(container) {
38480
- const kids = container.children;
38481
- if (kids.length < 2 || kids.length > 4) return void 0;
38482
- const firstChild = kids[0];
38483
- if (isFieldLabelCandidate(firstChild)) {
38484
- return getFieldLabelText(firstChild);
38485
- }
38486
- return void 0;
38487
- }
38488
- function shouldDropInheritedFieldLabel(element, inheritedFieldLabel) {
38489
- if (!inheritedFieldLabel) return false;
38490
- if (INTERACTIVE_LEAF_ROLES.has(element.role)) return false;
38491
- if (!isClickableByAttribute(element)) return false;
38492
- const directText = getElementText(element);
38493
- if (directText) {
38494
- return directText.toLowerCase() !== inheritedFieldLabel.toLowerCase();
38495
- }
38496
- const childTexts = [];
38497
- for (const child of element.children) {
38498
- if (!INTERACTIVE_LEAF_ROLES.has(child.role)) {
38499
- const text = getElementText(child);
38500
- if (text && TEXT_CARRYING_ROLES.has(child.role)) {
38501
- childTexts.push(text);
38502
- }
38503
- }
38504
- }
38505
- const composedText = childTexts.join(" ");
38506
- if (!composedText) return false;
38507
- return composedText.toLowerCase() !== inheritedFieldLabel.toLowerCase();
38508
- }
38509
38576
  var TEXT_CARRYING_ROLES = /* @__PURE__ */ new Set([
38510
38577
  "generic",
38511
38578
  "paragraph",
@@ -38516,12 +38583,11 @@ var TEXT_CARRYING_ROLES = /* @__PURE__ */ new Set([
38516
38583
  "blockquote",
38517
38584
  "caption"
38518
38585
  ]);
38586
+ var VISUAL_HEADING_MAX_LENGTH = 60;
38519
38587
  var STATUS_WORDS = /* @__PURE__ */ new Set(["favorable", "unfavorable", "success", "error", "warning", "pending"]);
38520
38588
  var BLANK_PATTERNS = [
38521
38589
  /\s*\(Blank\)\s*/gi,
38522
- // "(Blank)" in any case
38523
38590
  /\s*\(blank\)\s*/gi
38524
- // Redundant but explicit
38525
38591
  ];
38526
38592
  var MENU_TRIGGER_PATTERNS = [
38527
38593
  /^Open menu for /i,
@@ -38536,21 +38602,77 @@ var SECTION_BOUNDARY_ROLES = /* @__PURE__ */ new Set([
38536
38602
  "article",
38537
38603
  "section",
38538
38604
  "complementary"
38539
- // sidebars
38540
38605
  ]);
38606
+ var FRAMEWORK_NOISE_RE = /^(ng-binding|ng-scope|ng-pristine|ng-dirty|ng-valid|ng-invalid|ng-untouched|ng-touched|v-enter|v-leave)$/;
38541
38607
  function normalizeSearchText(text) {
38542
38608
  return text.toLowerCase().replace(/[-_.,;:!?'"()\[\]{}\/\\@#$%^&*+=<>~`|]/g, "").replace(/\s+/g, " ").trim();
38543
38609
  }
38544
- function isMenuTriggerNoise(element) {
38545
- if (isActiveElement(element)) return false;
38546
- if (element.role !== "button") return false;
38547
- const text = element.text || "";
38548
- if (!text.trim()) return false;
38549
- for (const pattern of MENU_TRIGGER_PATTERNS) {
38550
- if (pattern.test(text)) return true;
38610
+ function isActiveElement(element) {
38611
+ return element.attributes["active"] !== void 0 || element.rawLine.includes("[active]");
38612
+ }
38613
+ function isDisabledElement(element) {
38614
+ return element.attributes["disabled"] !== void 0 || element.rawLine.includes("[disabled]");
38615
+ }
38616
+ function isClickableByAttribute(element) {
38617
+ return element.attributes["cursor"] === "pointer";
38618
+ }
38619
+ function isActionableClickableGeneric(element) {
38620
+ return isClickableByAttribute(element) && !INTERACTIVE_ROLES.has(element.role);
38621
+ }
38622
+ function isSearchInteractiveElement(element) {
38623
+ return INTERACTIVE_LEAF_ROLES.has(element.role) || isActionableClickableGeneric(element);
38624
+ }
38625
+ function getElementText(el) {
38626
+ if (el.text && el.inputValue && FRAMEWORK_NOISE_RE.test(el.text.trim())) {
38627
+ return el.inputValue;
38628
+ }
38629
+ return el.text || el.inputValue;
38630
+ }
38631
+ function composeLabel(element) {
38632
+ const directText = getElementText(element);
38633
+ if (directText) return directText;
38634
+ const textParts = [];
38635
+ function collectText(el, depth) {
38636
+ if (depth > 3) return;
38637
+ const text = getElementText(el);
38638
+ if (text && TEXT_CARRYING_ROLES.has(el.role)) {
38639
+ textParts.push(text);
38640
+ }
38641
+ for (const child of el.children) {
38642
+ if (!INTERACTIVE_LEAF_ROLES.has(child.role)) {
38643
+ collectText(child, depth + 1);
38644
+ }
38645
+ }
38646
+ }
38647
+ for (const child of element.children) {
38648
+ if (!INTERACTIVE_LEAF_ROLES.has(child.role)) {
38649
+ collectText(child, 0);
38650
+ }
38651
+ }
38652
+ return textParts.join(" | ") || element.role;
38653
+ }
38654
+ function getDisplayText(element, options) {
38655
+ const isClickable = isActionableClickableGeneric(element);
38656
+ const rawTextSource = options?.overrideLabel ?? (isClickable ? composeLabel(element) : element.text ?? "");
38657
+ return normalizeIconLabelText(element.role, rawTextSource);
38658
+ }
38659
+ function hasInteractiveDescendants(element) {
38660
+ if (element.ref && (INTERACTIVE_ROLES.has(element.role) || isClickableByAttribute(element))) return true;
38661
+ for (const child of element.children) {
38662
+ if (hasInteractiveDescendants(child)) return true;
38663
+ }
38664
+ return false;
38665
+ }
38666
+ function subtreeContainsActive(element) {
38667
+ if (isActiveElement(element)) return true;
38668
+ for (const child of element.children) {
38669
+ if (subtreeContainsActive(child)) return true;
38551
38670
  }
38552
38671
  return false;
38553
38672
  }
38673
+ function cleanLabel(text) {
38674
+ return text.replace(/\s+/g, " ").replace(/…+/g, "...").trim();
38675
+ }
38554
38676
  function isBlankValue(value) {
38555
38677
  if (!value) return true;
38556
38678
  const trimmed = value.trim().toLowerCase();
@@ -38590,160 +38712,15 @@ function extractDataValue(text) {
38590
38712
  }
38591
38713
  return { label: cleanLabel(cleaned), status };
38592
38714
  }
38593
- function findRelevanceScope(elements, activeElementRef) {
38594
- if (!activeElementRef) return null;
38595
- const path5 = [];
38596
- function findPath(el) {
38597
- if (el.ref) {
38598
- path5.push({ ref: el.ref, role: el.role });
38599
- }
38600
- if (el.ref === activeElementRef) {
38601
- return true;
38602
- }
38603
- for (const child of el.children) {
38604
- if (findPath(child)) return true;
38605
- }
38606
- if (el.ref) path5.pop();
38607
- return false;
38608
- }
38609
- for (const el of elements) {
38610
- if (findPath(el)) break;
38611
- }
38612
- for (let i = path5.length - 1; i >= 0; i--) {
38613
- if (SECTION_BOUNDARY_ROLES.has(path5[i].role)) {
38614
- return path5[i].ref;
38615
- }
38616
- }
38617
- return null;
38618
- }
38619
- function searchElements(elements, searchTerms, scopeRef = null) {
38620
- const allMatches = [];
38621
- const normalizedTerms = searchTerms.map((t) => normalizeSearchText(t)).filter(Boolean);
38622
- if (normalizedTerms.length === 0) return { matches: [], totalFound: 0 };
38623
- function searchElement(el, inScope) {
38624
- const nowInScope = inScope || el.ref === scopeRef || scopeRef === null;
38625
- if (el.ref && nowInScope) {
38626
- const searchText = [el.text, el.inputValue].filter(Boolean).join(" ");
38627
- const normalizedSearchText = normalizeSearchText(searchText);
38628
- for (const term of normalizedTerms) {
38629
- if (normalizedSearchText.includes(term)) {
38630
- const normalizedLabel = normalizeIconLabelText(el.role, el.text || "");
38631
- allMatches.push({
38632
- ref: el.ref,
38633
- role: el.role,
38634
- label: normalizedLabel || el.role,
38635
- term,
38636
- isInteractive: INTERACTIVE_LEAF_ROLES.has(el.role)
38637
- });
38638
- break;
38639
- }
38640
- }
38641
- }
38642
- for (const child of el.children) {
38643
- searchElement(child, nowInScope);
38644
- }
38645
- }
38646
- for (const el of elements) {
38647
- searchElement(el, scopeRef === null);
38648
- }
38649
- const totalFound = allMatches.length;
38650
- allMatches.sort((a, b) => {
38651
- if (a.isInteractive && !b.isInteractive) return -1;
38652
- if (!a.isInteractive && b.isInteractive) return 1;
38653
- return 0;
38654
- });
38655
- return {
38656
- matches: allMatches.slice(0, MAX_SEARCH_MATCHES),
38657
- totalFound
38658
- };
38659
- }
38660
- function findElementInSections(sections, targetRef, ancestors = []) {
38661
- for (const section of sections) {
38662
- const newAncestors = [...ancestors, section];
38663
- const path5 = newAncestors.map((s) => s.heading);
38664
- if (section.elements.some((el) => el.ref === targetRef)) {
38665
- return { section, ancestors: newAncestors, path: path5 };
38666
- }
38667
- const found = findElementInSections(section.subsections, targetRef, newAncestors);
38668
- if (found) return found;
38669
- }
38670
- return null;
38671
- }
38672
- function populateSearchContext(matches, sections) {
38673
- for (const match of matches) {
38674
- const result = findElementInSections(sections, match.ref);
38675
- if (result) {
38676
- const { section, ancestors, path: path5 } = result;
38677
- const inFocusedDialog = ancestors.some((s) => s.isFocused);
38678
- const inBackground = section.isBackground || ancestors.some((s) => s.isBackground);
38679
- if (inFocusedDialog) {
38680
- match.context = "focused";
38681
- } else if (inBackground) {
38682
- match.context = "background";
38683
- } else {
38684
- match.context = "foreground";
38685
- }
38686
- const SKIP_HEADINGS = ["generic", "main", "iframe"];
38687
- match.sectionPath = path5.filter((p) => !SKIP_HEADINGS.includes(p.toLowerCase())).slice(-3).join(" > ");
38688
- }
38689
- }
38690
- }
38691
- function sortMatchesByContext(matches) {
38692
- const contextOrder = {
38693
- focused: 0,
38694
- foreground: 1,
38695
- background: 2
38696
- };
38697
- return [...matches].sort((a, b) => {
38698
- const aContext = contextOrder[a.context ?? "foreground"] ?? 1;
38699
- const bContext = contextOrder[b.context ?? "foreground"] ?? 1;
38700
- if (aContext !== bContext) return aContext - bContext;
38701
- if (a.isInteractive && !b.isInteractive) return -1;
38702
- if (!a.isInteractive && b.isInteractive) return 1;
38703
- return 0;
38704
- });
38705
- }
38706
- function cleanLabel(text) {
38707
- return text.replace(/\s+/g, " ").replace(/…+/g, "...").trim();
38708
- }
38709
- function isActiveElement(element) {
38710
- return element.attributes["active"] !== void 0 || element.rawLine.includes("[active]");
38711
- }
38712
- function isDisabledElement(element) {
38713
- return element.attributes["disabled"] !== void 0 || element.rawLine.includes("[disabled]");
38714
- }
38715
- function isClickableByAttribute(element) {
38716
- return element.attributes["cursor"] === "pointer";
38717
- }
38718
- var FRAMEWORK_NOISE_RE = /^(ng-binding|ng-scope|ng-pristine|ng-dirty|ng-valid|ng-invalid|ng-untouched|ng-touched|v-enter|v-leave)$/;
38719
- function getElementText(el) {
38720
- if (el.text && el.inputValue && FRAMEWORK_NOISE_RE.test(el.text.trim())) {
38721
- return el.inputValue;
38722
- }
38723
- return el.text || el.inputValue;
38724
- }
38725
- function composeLabel(element) {
38726
- const directText = getElementText(element);
38727
- if (directText) return directText;
38728
- const textParts = [];
38729
- function collectText(el, depth) {
38730
- if (depth > 3) return;
38731
- const text = getElementText(el);
38732
- if (text && TEXT_CARRYING_ROLES.has(el.role)) {
38733
- textParts.push(text);
38734
- }
38735
- for (const child of el.children) {
38736
- if (!INTERACTIVE_LEAF_ROLES.has(child.role)) {
38737
- collectText(child, depth + 1);
38738
- }
38739
- }
38740
- }
38741
- for (const child of element.children) {
38742
- if (!INTERACTIVE_LEAF_ROLES.has(child.role)) {
38743
- collectText(child, 0);
38744
- }
38715
+ function isMenuTriggerNoise(element) {
38716
+ if (isActiveElement(element)) return false;
38717
+ if (element.role !== "button") return false;
38718
+ const text = element.text || "";
38719
+ if (!text.trim()) return false;
38720
+ for (const pattern of MENU_TRIGGER_PATTERNS) {
38721
+ if (pattern.test(text)) return true;
38745
38722
  }
38746
- return textParts.join(" | ") || element.role;
38723
+ return false;
38747
38724
  }
38748
38725
  function countElementsWithRefs(element) {
38749
38726
  let count = element.ref ? 1 : 0;
@@ -38752,74 +38729,23 @@ function countElementsWithRefs(element) {
38752
38729
  }
38753
38730
  return count;
38754
38731
  }
38755
- function isExpandedElement(element) {
38756
- return element.attributes["expanded"] === "true" || element.attributes["aria-expanded"] === "true";
38757
- }
38758
- function collectExpandedChildren(element, result) {
38759
- for (const child of element.children) {
38760
- if (STRUCTURAL_ROLES.has(child.role)) continue;
38761
- if (INTERACTIVE_LEAF_ROLES.has(child.role) && child.ref) {
38762
- result.push(formatElement(child));
38763
- continue;
38764
- }
38765
- if (child.role === "listitem" && child.ref) {
38766
- const text = child.text || composeLabel(child);
38767
- if (text && text !== "listitem") {
38768
- result.push(formatElement(child, { overrideLabel: text }));
38769
- continue;
38770
- }
38771
- }
38772
- if (isClickableByAttribute(child) && child.ref) {
38773
- result.push(formatElement(child));
38732
+ function collectComboboxOptions(element) {
38733
+ const options = [];
38734
+ function walk(el) {
38735
+ if (el.role === "option" && el.text?.trim()) {
38736
+ options.push(el.text.trim());
38774
38737
  }
38775
- collectExpandedChildren(child, result);
38776
- }
38777
- }
38778
- function hasActionableChildren(element) {
38779
- const children2 = [];
38780
- collectExpandedChildren(element, children2);
38781
- return children2.length > 0;
38782
- }
38783
- function isExpandedWithActionableChildren(element) {
38784
- if (!isExpandedElement(element)) return false;
38785
- if (element.role === "combobox") return false;
38786
- return hasActionableChildren(element);
38787
- }
38788
- function composeTriggerLabel(element) {
38789
- const ariaLabel = element.attributes["aria-label"];
38790
- if (ariaLabel) return ariaLabel;
38791
- const fullText = element.text || "";
38792
- if (fullText) {
38793
- let collectLeafTexts2 = function(el) {
38794
- for (const child of el.children) {
38795
- const text = child.text?.trim();
38796
- if (text) {
38797
- leafTexts.push(text);
38798
- }
38799
- collectLeafTexts2(child);
38800
- }
38801
- };
38802
- var collectLeafTexts = collectLeafTexts2;
38803
- const leafTexts = [];
38804
- collectLeafTexts2(element);
38805
- if (leafTexts.length > 0) {
38806
- let stripped = fullText;
38807
- for (const lt of leafTexts) {
38808
- stripped = stripped.replace(lt, "");
38809
- }
38810
- stripped = stripped.replace(/\s+/g, " ").trim();
38811
- if (stripped) return stripped;
38738
+ for (const child of el.children) {
38739
+ walk(child);
38812
38740
  }
38813
38741
  }
38814
- return element.role;
38815
- }
38816
- function subtreeContainsActive(element) {
38817
- if (isActiveElement(element)) return true;
38818
38742
  for (const child of element.children) {
38819
- if (subtreeContainsActive(child)) return true;
38743
+ walk(child);
38820
38744
  }
38821
- return false;
38745
+ return options.length > 0 ? options : void 0;
38822
38746
  }
38747
+
38748
+ // ../browser-core/src/snapshot-formatter-grid.ts
38823
38749
  function findDescendantByRole(el, role) {
38824
38750
  if (el.role === role) return el;
38825
38751
  for (const child of el.children) {
@@ -38864,6 +38790,176 @@ function isWidgetTable(element) {
38864
38790
  }
38865
38791
  return totalCells > 0 && interactiveCells >= totalCells * 0.5;
38866
38792
  }
38793
+ function gridCellPriority(el) {
38794
+ if (el.isActive) return 5;
38795
+ if (FORM_INPUT_ROLES.has(el.role)) return 4;
38796
+ if (el.isClickableGeneric) return 3;
38797
+ if (el.role !== "button") return 2;
38798
+ return 1;
38799
+ }
38800
+ function extractCellText(el) {
38801
+ const findFirstParagraph = (node) => {
38802
+ if (node.role === "paragraph" && node.text) {
38803
+ return node.text.trim();
38804
+ }
38805
+ for (const child of node.children) {
38806
+ if (child.role === "generic" || child.role === "paragraph") {
38807
+ const found = findFirstParagraph(child);
38808
+ if (found) return found;
38809
+ }
38810
+ }
38811
+ return null;
38812
+ };
38813
+ const paragraphText = findFirstParagraph(el);
38814
+ if (paragraphText) return paragraphText;
38815
+ const text = el.text?.trim();
38816
+ if (text) return text;
38817
+ return null;
38818
+ }
38819
+ function extractGridCell(cellEl) {
38820
+ const interactiveElements = [];
38821
+ const findInteractive = (el) => {
38822
+ if (isClickableByAttribute(el) && el.ref && el.role === "generic") {
38823
+ const childTexts = [];
38824
+ for (const child of el.children) {
38825
+ if (child.role === "generic") {
38826
+ const childText = child.text || child.inputValue;
38827
+ if (childText) childTexts.push(childText);
38828
+ }
38829
+ }
38830
+ interactiveElements.push({
38831
+ ref: el.ref,
38832
+ column: el.attributes["label"] || childTexts[0] || "",
38833
+ value: childTexts.join(" - ") || el.text || el.inputValue || "",
38834
+ role: el.role,
38835
+ isClickableGeneric: true,
38836
+ isActive: isActiveElement(el)
38837
+ });
38838
+ }
38839
+ if (INTERACTIVE_LEAF_ROLES.has(el.role) && el.ref) {
38840
+ const value2 = el.role === "checkbox" ? el.attributes["checked"] === "true" ? "checked" : "unchecked" : el.inputValue || el.text || "";
38841
+ interactiveElements.push({
38842
+ ref: el.ref,
38843
+ column: el.attributes["label"] || el.text || "",
38844
+ value: value2,
38845
+ role: el.role,
38846
+ isClickableGeneric: false,
38847
+ isActive: isActiveElement(el)
38848
+ });
38849
+ }
38850
+ for (const child of el.children) findInteractive(child);
38851
+ };
38852
+ findInteractive(cellEl);
38853
+ if (interactiveElements.length === 0) {
38854
+ const textValue = extractCellText(cellEl);
38855
+ if (textValue) {
38856
+ return {
38857
+ ref: cellEl.ref,
38858
+ column: cellEl.text || "",
38859
+ value: textValue,
38860
+ hasValue: true
38861
+ };
38862
+ }
38863
+ return null;
38864
+ }
38865
+ const found = interactiveElements.reduce(
38866
+ (best, current) => gridCellPriority(current) > gridCellPriority(best) ? current : best
38867
+ );
38868
+ const column = stripBlankPatterns(found.column);
38869
+ const value = isBlankValue(found.value) ? "" : stripBlankPatterns(found.value).trim();
38870
+ const gridcellLabel = cellEl.text ? stripBlankPatterns(cellEl.text).trim() : void 0;
38871
+ return {
38872
+ ref: found.ref,
38873
+ column: column.trim(),
38874
+ value,
38875
+ hasValue: value !== "",
38876
+ gridcellLabel: gridcellLabel || void 0,
38877
+ role: found.role
38878
+ };
38879
+ }
38880
+ function reconcileCellColumn(cell, knownColumns) {
38881
+ if (knownColumns.has(cell.column)) return cell;
38882
+ if (cell.gridcellLabel && knownColumns.has(cell.gridcellLabel)) {
38883
+ const newValue = cell.column || cell.value;
38884
+ const cleanValue = isBlankValue(newValue) ? "" : newValue.trim();
38885
+ return {
38886
+ ...cell,
38887
+ column: cell.gridcellLabel,
38888
+ value: cleanValue,
38889
+ hasValue: cleanValue !== ""
38890
+ };
38891
+ }
38892
+ let bestMatch = "";
38893
+ for (const col of knownColumns) {
38894
+ if (cell.column.startsWith(col) && col.length > bestMatch.length && (cell.column.length === col.length || cell.column[col.length] === " ")) {
38895
+ bestMatch = col;
38896
+ }
38897
+ }
38898
+ if (bestMatch) {
38899
+ const remainder = cell.column.slice(bestMatch.length).trim();
38900
+ const newValue = isBlankValue(remainder) ? "" : remainder;
38901
+ return {
38902
+ ...cell,
38903
+ column: bestMatch,
38904
+ value: newValue || cell.value,
38905
+ hasValue: (newValue || cell.value) !== ""
38906
+ };
38907
+ }
38908
+ return cell;
38909
+ }
38910
+ function reconcileGridCells(grid) {
38911
+ if (grid.columns.length === 0) return grid;
38912
+ const knownColumns = new Set(grid.columns.map((c2) => c2.name));
38913
+ return {
38914
+ ...grid,
38915
+ rows: grid.rows.map((row) => {
38916
+ const reconciledCells = row.cells.map((cell) => {
38917
+ const reconciled = reconcileCellColumn(cell, knownColumns);
38918
+ if (knownColumns.has(reconciled.column)) return reconciled;
38919
+ if (grid.columnByPosition && cell.positionInRow !== void 0 && grid.headerChildCount !== void 0 && row.totalChildren === grid.headerChildCount) {
38920
+ const positionalColumn = grid.columnByPosition.get(cell.positionInRow);
38921
+ if (positionalColumn) {
38922
+ const newValue = cell.column || cell.value;
38923
+ const cleanValue = isBlankValue(newValue) ? "" : newValue.trim();
38924
+ return {
38925
+ ...cell,
38926
+ column: positionalColumn,
38927
+ value: cleanValue,
38928
+ hasValue: cleanValue !== ""
38929
+ };
38930
+ }
38931
+ }
38932
+ return reconciled;
38933
+ });
38934
+ if (grid.headerCellPositions && row.cellPositions) {
38935
+ const claimedColumns = new Set(
38936
+ reconciledCells.filter((c2) => knownColumns.has(c2.column)).map((c2) => c2.column)
38937
+ );
38938
+ for (let idx = 0; idx < reconciledCells.length; idx++) {
38939
+ const cell = reconciledCells[idx];
38940
+ if (knownColumns.has(cell.column) || cell.positionInRow === void 0) continue;
38941
+ const cellOrdinal = row.cellPositions.indexOf(cell.positionInRow);
38942
+ if (cellOrdinal >= 0 && cellOrdinal < grid.headerCellPositions.length) {
38943
+ const headerPos = grid.headerCellPositions[cellOrdinal];
38944
+ const positionalColumn = grid.columnByPosition?.get(headerPos);
38945
+ if (positionalColumn && !claimedColumns.has(positionalColumn)) {
38946
+ const newValue = cell.column || cell.value;
38947
+ const cleanValue = isBlankValue(newValue) ? "" : newValue.trim();
38948
+ reconciledCells[idx] = {
38949
+ ...cell,
38950
+ column: positionalColumn,
38951
+ value: cleanValue,
38952
+ hasValue: cleanValue !== ""
38953
+ };
38954
+ claimedColumns.add(positionalColumn);
38955
+ }
38956
+ }
38957
+ }
38958
+ }
38959
+ return { ...row, cells: reconciledCells };
38960
+ })
38961
+ };
38962
+ }
38867
38963
  function detectGrid(element) {
38868
38964
  if (element.role !== "grid" && element.role !== "treegrid" && element.role !== "table") {
38869
38965
  return null;
@@ -38874,18 +38970,28 @@ function detectGrid(element) {
38874
38970
  let headerRowRef = null;
38875
38971
  const columnByPosition = /* @__PURE__ */ new Map();
38876
38972
  let headerChildCount;
38973
+ let headerCellPositions;
38974
+ const isRowLike = (el) => {
38975
+ if (el.role === "row") return true;
38976
+ const cellCount = el.children.filter(
38977
+ (c2) => c2.role === "cell" || c2.role === "gridcell"
38978
+ ).length;
38979
+ return cellCount >= 2;
38980
+ };
38877
38981
  const processRow = (rowEl, index) => {
38878
- if (rowEl.role !== "row") return null;
38982
+ if (!isRowLike(rowEl)) return null;
38879
38983
  const hasColumnHeaders = rowEl.children.some((c2) => c2.role === "columnheader");
38880
38984
  if (hasColumnHeaders) {
38881
38985
  headerRowRef = rowEl.ref;
38882
38986
  const columnHeaders = rowEl.children.filter((c2) => c2.role === "columnheader");
38883
- const hasChildful = columnHeaders.some((c2) => c2.children.length > 0);
38987
+ const childful = columnHeaders.filter((c2) => c2.children.length > 0);
38988
+ const textBearingChildless = columnHeaders.filter((c2) => c2.children.length === 0 && c2.text);
38989
+ const skipChildlessHeaders = childful.length > 0 && textBearingChildless.length <= childful.length;
38884
38990
  const columnsBefore = columns.length;
38885
38991
  for (let i = 0; i < rowEl.children.length; i++) {
38886
38992
  const child = rowEl.children[i];
38887
38993
  if (child.role === "columnheader") {
38888
- if (hasChildful && child.children.length === 0) continue;
38994
+ if (skipChildlessHeaders && child.children.length === 0) continue;
38889
38995
  const primaryLabel = findPrimaryColumnLabel(child);
38890
38996
  const rawName = (primaryLabel ? getElementText(primaryLabel) : void 0) || child.text || "";
38891
38997
  const cleanName = rawName.replace(/,?\s*sorted in \w+ order/i, "").trim();
@@ -38904,6 +39010,7 @@ function detectGrid(element) {
38904
39010
  }
38905
39011
  if (columns.length > columnsBefore) {
38906
39012
  headerChildCount = rowEl.children.length;
39013
+ headerCellPositions = Array.from(columnByPosition.keys()).sort((a, b) => a - b);
38907
39014
  }
38908
39015
  return null;
38909
39016
  }
@@ -38945,6 +39052,7 @@ function detectGrid(element) {
38945
39052
  }
38946
39053
  if (columns.length > columnsBefore) {
38947
39054
  headerChildCount = rowEl.children.length;
39055
+ headerCellPositions = Array.from(columnByPosition.keys()).sort((a, b) => a - b);
38948
39056
  }
38949
39057
  return null;
38950
39058
  }
@@ -38970,7 +39078,20 @@ function detectGrid(element) {
38970
39078
  findButtons(cellEl);
38971
39079
  }
38972
39080
  }
38973
- const row = { ref: rowEl.ref, index, isSelected, cells, totalChildren: rowEl.children.length };
39081
+ const cellPositions = [];
39082
+ for (let i = 0; i < rowEl.children.length; i++) {
39083
+ if (rowEl.children[i].role === "gridcell" || rowEl.children[i].role === "cell") {
39084
+ cellPositions.push(i);
39085
+ }
39086
+ }
39087
+ const row = {
39088
+ ref: rowEl.ref,
39089
+ index,
39090
+ isSelected,
39091
+ cells,
39092
+ totalChildren: rowEl.children.length,
39093
+ cellPositions
39094
+ };
38974
39095
  if (cells.length === 0 && rowEl.children.some((c2) => c2.role === "rowheader")) {
38975
39096
  const labels = [];
38976
39097
  const buttons = [];
@@ -38998,7 +39119,7 @@ function detectGrid(element) {
38998
39119
  return row;
38999
39120
  };
39000
39121
  const findRows = (el) => {
39001
- if (el.role === "row") {
39122
+ if (isRowLike(el)) {
39002
39123
  const row = processRow(el, rows.length);
39003
39124
  if (row) rows.push(row);
39004
39125
  } else {
@@ -39016,177 +39137,414 @@ function detectGrid(element) {
39016
39137
  totalRows: rows.length,
39017
39138
  containsActive: subtreeContainsActive(element),
39018
39139
  columnByPosition: columnByPosition.size > 0 ? columnByPosition : void 0,
39019
- headerChildCount
39140
+ headerChildCount,
39141
+ headerCellPositions
39020
39142
  };
39021
39143
  return reconcileGridCells(rawGrid);
39022
39144
  }
39023
- function gridCellPriority(el) {
39024
- if (el.isActive) return 5;
39025
- if (FORM_INPUT_ROLES.has(el.role)) return 4;
39026
- if (el.isClickableGeneric) return 3;
39027
- if (el.role !== "button") return 2;
39028
- return 1;
39145
+ function gridContainsRef(grid, targetRef) {
39146
+ for (const column of grid.columns) {
39147
+ if (column.ref === targetRef) return true;
39148
+ }
39149
+ for (const row of grid.rows) {
39150
+ if (row.ref === targetRef) return true;
39151
+ if (row.cells.some((cell) => cell.ref === targetRef)) return true;
39152
+ if (row.rowActions?.some((action) => action.ref === targetRef)) return true;
39153
+ if (row.expandedContent?.buttons.some((button) => button.ref === targetRef)) return true;
39154
+ }
39155
+ return false;
39029
39156
  }
39030
- function extractCellText(el) {
39031
- const findFirstParagraph = (node) => {
39032
- if (node.role === "paragraph" && node.text) {
39033
- return node.text.trim();
39157
+ function countRenderedGridItems(grid) {
39158
+ const knownColumns = grid.columns.length > 0 ? new Set(grid.columns.map((c2) => c2.name)) : void 0;
39159
+ let count = 0;
39160
+ for (const row of grid.rows) {
39161
+ count += row.cells.filter((c2) => c2.hasValue && (!knownColumns || knownColumns.has(c2.column))).length;
39162
+ count += row.rowActions?.length ?? 0;
39163
+ count += row.expandedContent?.buttons.length ?? 0;
39164
+ }
39165
+ return count;
39166
+ }
39167
+ function formatGridOutput(grid, indent, expanded = false) {
39168
+ const indentStr = " ".repeat(indent);
39169
+ const lines = [];
39170
+ const colInfo = grid.columns.length > 0 ? `${grid.columns.length} columns` : "unknown columns";
39171
+ lines.push(`${indentStr}GRID [${grid.ref}]: ${grid.totalRows} rows, ${colInfo}`);
39172
+ const MAX_DISPLAY_COLUMNS = 10;
39173
+ if (grid.columns.length > 0) {
39174
+ const displayColumns = grid.columns.slice(0, MAX_DISPLAY_COLUMNS);
39175
+ const colsWithRefs = displayColumns.map((c2) => `${c2.name} [${c2.ref}]`).join(", ");
39176
+ const moreColsNote = grid.columns.length > MAX_DISPLAY_COLUMNS ? ` (+${grid.columns.length - MAX_DISPLAY_COLUMNS} more)` : "";
39177
+ lines.push(`${indentStr} Columns: ${colsWithRefs}${moreColsNote}`);
39178
+ }
39179
+ lines.push("");
39180
+ const knownColumns = grid.columns.length > 0 ? new Set(grid.columns.map((c2) => c2.name)) : void 0;
39181
+ const MAX_GRID_ROWS = 30;
39182
+ const displayRows = expanded ? grid.rows : grid.rows.slice(0, MAX_GRID_ROWS);
39183
+ for (const row of displayRows) {
39184
+ lines.push(formatGridRow(row, indent + 1, knownColumns));
39185
+ }
39186
+ if (!expanded && grid.rows.length > MAX_GRID_ROWS) {
39187
+ lines.push(`${indentStr} ... and ${grid.rows.length - MAX_GRID_ROWS} more rows (use expand="${grid.ref}" to see all)`);
39188
+ }
39189
+ return lines.join("\n");
39190
+ }
39191
+ function formatGridRow(row, indent, knownColumns) {
39192
+ const indentStr = " ".repeat(indent);
39193
+ if (row.expandedContent) {
39194
+ const parts = [];
39195
+ if (row.expandedContent.labels.length > 0) {
39196
+ parts.push(row.expandedContent.labels.join(", "));
39034
39197
  }
39035
- for (const child of node.children) {
39036
- if (child.role === "generic" || child.role === "paragraph") {
39037
- const found = findFirstParagraph(child);
39038
- if (found) return found;
39198
+ for (const btn of row.expandedContent.buttons) {
39199
+ parts.push(`${btn.label} [${btn.ref}]`);
39200
+ }
39201
+ return `${indentStr} \u21B3 Expanded [${row.ref}]: ${parts.join(" | ")}`;
39202
+ }
39203
+ const marker = row.isSelected ? "ACTIVE \u2192 Row" : "Row";
39204
+ const actionsPrefix = row.rowActions?.length ? row.rowActions.map((a) => `[\u25B6 ${a.ref}]`).join(" ") + " " : "";
39205
+ const cellsWithValue = row.cells.filter(
39206
+ (c2) => c2.hasValue && (!knownColumns || knownColumns.has(c2.column))
39207
+ );
39208
+ if (cellsWithValue.length > 0) {
39209
+ const cellStrs = cellsWithValue.map((c2) => {
39210
+ if (c2.role === "checkbox") {
39211
+ const icon = c2.value === "checked" ? "\u2611" : "\u2610";
39212
+ return `${icon} [${c2.ref}]`;
39213
+ }
39214
+ if (c2.column === c2.value) {
39215
+ return `${c2.column} [${c2.ref}]`;
39039
39216
  }
39217
+ return `${c2.column} [${c2.ref}]: ${c2.value}`;
39218
+ });
39219
+ return `${indentStr}${actionsPrefix}${marker} [${row.ref}]: ${cellStrs.join(" | ")}`;
39220
+ }
39221
+ return `${indentStr}${actionsPrefix}${marker} [${row.ref}]: (empty row)`;
39222
+ }
39223
+
39224
+ // ../browser-core/src/snapshot-formatter-search.ts
39225
+ function findRelevanceScope(elements, activeElementRef) {
39226
+ if (!activeElementRef) return null;
39227
+ const path5 = [];
39228
+ function findPath(el) {
39229
+ if (el.ref) {
39230
+ path5.push({ ref: el.ref, role: el.role });
39040
39231
  }
39041
- return null;
39042
- };
39043
- const paragraphText = findFirstParagraph(el);
39044
- if (paragraphText) return paragraphText;
39045
- const text = el.text?.trim();
39046
- if (text) return text;
39232
+ if (el.ref === activeElementRef) {
39233
+ return true;
39234
+ }
39235
+ for (const child of el.children) {
39236
+ if (findPath(child)) return true;
39237
+ }
39238
+ if (el.ref) path5.pop();
39239
+ return false;
39240
+ }
39241
+ for (const el of elements) {
39242
+ if (findPath(el)) break;
39243
+ }
39244
+ for (let i = path5.length - 1; i >= 0; i--) {
39245
+ if (SECTION_BOUNDARY_ROLES.has(path5[i].role)) {
39246
+ return path5[i].ref;
39247
+ }
39248
+ }
39047
39249
  return null;
39048
39250
  }
39049
- function extractGridCell(cellEl) {
39050
- const interactiveElements = [];
39051
- const findInteractive = (el) => {
39052
- if (isClickableByAttribute(el) && el.ref && el.role === "generic") {
39053
- const childTexts = [];
39054
- for (const child of el.children) {
39055
- if (child.role === "generic") {
39056
- const childText = child.text || child.inputValue;
39057
- if (childText) childTexts.push(childText);
39251
+ function searchElements(elements, searchTerms, scopeRef = null) {
39252
+ const allMatches = [];
39253
+ const normalizedTerms = searchTerms.map((t) => normalizeSearchText(t)).filter(Boolean);
39254
+ if (normalizedTerms.length === 0) return { matches: [], totalFound: 0 };
39255
+ function searchElement(el, inScope) {
39256
+ const nowInScope = inScope || el.ref === scopeRef || scopeRef === null;
39257
+ if (el.ref && nowInScope) {
39258
+ const searchLabel = getDisplayText(el);
39259
+ const searchText = [searchLabel, el.inputValue].filter(Boolean).join(" ");
39260
+ const normalizedSearchText = normalizeSearchText(searchText);
39261
+ for (const term of normalizedTerms) {
39262
+ if (normalizedSearchText.includes(term)) {
39263
+ allMatches.push({
39264
+ ref: el.ref,
39265
+ role: el.role,
39266
+ label: searchLabel || el.role,
39267
+ term,
39268
+ isInteractive: isSearchInteractiveElement(el)
39269
+ });
39270
+ break;
39058
39271
  }
39059
39272
  }
39060
- interactiveElements.push({
39061
- ref: el.ref,
39062
- column: el.attributes["label"] || childTexts[0] || "",
39063
- value: childTexts.join(" - ") || el.text || el.inputValue || "",
39064
- role: el.role,
39065
- isClickableGeneric: true,
39066
- isActive: isActiveElement(el)
39067
- });
39068
39273
  }
39069
- if (INTERACTIVE_LEAF_ROLES.has(el.role) && el.ref) {
39070
- const value2 = el.role === "checkbox" ? el.attributes["checked"] === "true" ? "checked" : "unchecked" : el.inputValue || el.text || "";
39071
- interactiveElements.push({
39072
- ref: el.ref,
39073
- column: el.attributes["label"] || el.text || "",
39074
- value: value2,
39075
- role: el.role,
39076
- isClickableGeneric: false,
39077
- isActive: isActiveElement(el)
39078
- });
39274
+ for (const child of el.children) {
39275
+ searchElement(child, nowInScope);
39079
39276
  }
39080
- for (const child of el.children) findInteractive(child);
39277
+ }
39278
+ for (const el of elements) {
39279
+ searchElement(el, scopeRef === null);
39280
+ }
39281
+ const totalFound = allMatches.length;
39282
+ allMatches.sort((a, b) => {
39283
+ if (a.isInteractive && !b.isInteractive) return -1;
39284
+ if (!a.isInteractive && b.isInteractive) return 1;
39285
+ return 0;
39286
+ });
39287
+ return {
39288
+ matches: allMatches.slice(0, MAX_SEARCH_MATCHES),
39289
+ totalFound
39081
39290
  };
39082
- findInteractive(cellEl);
39083
- if (interactiveElements.length === 0) {
39084
- const textValue = extractCellText(cellEl);
39085
- if (textValue) {
39086
- return {
39087
- ref: cellEl.ref,
39088
- column: cellEl.text || "",
39089
- value: textValue,
39090
- hasValue: true
39091
- };
39291
+ }
39292
+ function findElementInSections(sections, targetRef, ancestors = []) {
39293
+ for (const section of sections) {
39294
+ const newAncestors = [...ancestors, section];
39295
+ const path5 = newAncestors.map((s) => s.heading);
39296
+ if (section.elements.some((el) => el.ref === targetRef)) {
39297
+ return { section, ancestors: newAncestors, path: path5 };
39092
39298
  }
39093
- return null;
39299
+ if (section.gridInfo && gridContainsRef(section.gridInfo, targetRef)) {
39300
+ return { section, ancestors: newAncestors, path: path5 };
39301
+ }
39302
+ const found = findElementInSections(section.subsections, targetRef, newAncestors);
39303
+ if (found) return found;
39094
39304
  }
39095
- const found = interactiveElements.reduce(
39096
- (best, current) => gridCellPriority(current) > gridCellPriority(best) ? current : best
39097
- );
39098
- const column = stripBlankPatterns(found.column);
39099
- const value = isBlankValue(found.value) ? "" : stripBlankPatterns(found.value).trim();
39100
- const gridcellLabel = cellEl.text ? stripBlankPatterns(cellEl.text).trim() : void 0;
39101
- return {
39102
- ref: found.ref,
39103
- column: column.trim(),
39104
- value,
39105
- hasValue: value !== "",
39106
- gridcellLabel: gridcellLabel || void 0,
39107
- role: found.role
39305
+ return null;
39306
+ }
39307
+ function populateSearchContext(matches, sections) {
39308
+ for (const match of matches) {
39309
+ const result = findElementInSections(sections, match.ref);
39310
+ if (result) {
39311
+ const { section, ancestors, path: path5 } = result;
39312
+ const inFocusedDialog = ancestors.some((s) => s.isFocused);
39313
+ const inBackground = section.isBackground || ancestors.some((s) => s.isBackground);
39314
+ if (inFocusedDialog) {
39315
+ match.context = "focused";
39316
+ } else if (inBackground) {
39317
+ match.context = "background";
39318
+ } else {
39319
+ match.context = "foreground";
39320
+ }
39321
+ const SKIP_HEADINGS = ["generic", "main", "iframe"];
39322
+ match.sectionPath = path5.filter((p) => !SKIP_HEADINGS.includes(p.toLowerCase())).slice(-3).join(" > ");
39323
+ }
39324
+ }
39325
+ }
39326
+ function sortMatchesByContext(matches) {
39327
+ const contextOrder = {
39328
+ focused: 0,
39329
+ foreground: 1,
39330
+ background: 2
39108
39331
  };
39332
+ return [...matches].sort((a, b) => {
39333
+ const aContext = contextOrder[a.context ?? "foreground"] ?? 1;
39334
+ const bContext = contextOrder[b.context ?? "foreground"] ?? 1;
39335
+ if (aContext !== bContext) return aContext - bContext;
39336
+ if (a.isInteractive && !b.isInteractive) return -1;
39337
+ if (!a.isInteractive && b.isInteractive) return 1;
39338
+ return 0;
39339
+ });
39109
39340
  }
39110
- function reconcileCellColumn(cell, knownColumns) {
39111
- if (knownColumns.has(cell.column)) return cell;
39112
- if (cell.gridcellLabel && knownColumns.has(cell.gridcellLabel)) {
39113
- const newValue = cell.column || cell.value;
39114
- const cleanValue = isBlankValue(newValue) ? "" : newValue.trim();
39115
- return {
39116
- ...cell,
39117
- column: cell.gridcellLabel,
39118
- value: cleanValue,
39119
- hasValue: cleanValue !== ""
39120
- };
39341
+
39342
+ // ../browser-core/src/snapshot-formatter-sections.ts
39343
+ var FIELD_LABEL_TEXT_ROLES = /* @__PURE__ */ new Set([
39344
+ "generic",
39345
+ "paragraph",
39346
+ "text",
39347
+ "strong",
39348
+ "emphasis",
39349
+ "label",
39350
+ "heading"
39351
+ ]);
39352
+ var MIN_ELEMENTS_TO_COLLAPSE = 20;
39353
+ var DIALOG_ROLES = /* @__PURE__ */ new Set(["dialog", "alertdialog"]);
39354
+ function isVisualHeading(element, siblings, siblingIndex) {
39355
+ if (element.role !== "generic" && element.role !== "paragraph") return false;
39356
+ if (isClickableByAttribute(element)) return false;
39357
+ const text = getVisualHeadingText(element);
39358
+ if (!text || text.length < 3 || text.length > VISUAL_HEADING_MAX_LENGTH) return false;
39359
+ if (hasInteractiveDescendants(element)) return false;
39360
+ let interactiveCount = 0;
39361
+ for (let i = siblingIndex + 1; i < siblings.length; i++) {
39362
+ const sib = siblings[i];
39363
+ if (sib.role === "heading" || STRUCTURAL_ROLES.has(sib.role)) break;
39364
+ if (sib.role === "generic" || sib.role === "paragraph") {
39365
+ if (!isClickableByAttribute(sib) && !hasInteractiveDescendants(sib)) {
39366
+ const sibText = getVisualHeadingText(sib);
39367
+ if (sibText && sibText.length >= 3 && sibText.length <= VISUAL_HEADING_MAX_LENGTH) {
39368
+ break;
39369
+ }
39370
+ }
39371
+ }
39372
+ if (hasInteractiveDescendants(sib) || isClickableByAttribute(sib)) {
39373
+ interactiveCount++;
39374
+ }
39121
39375
  }
39122
- let bestMatch = "";
39123
- for (const col of knownColumns) {
39124
- if (cell.column.startsWith(col) && col.length > bestMatch.length && // Ensure there's a space separator after the column name (not a partial word match)
39125
- (cell.column.length === col.length || cell.column[col.length] === " ")) {
39126
- bestMatch = col;
39376
+ return interactiveCount >= 3;
39377
+ }
39378
+ function getVisualHeadingText(element) {
39379
+ const direct = element.text;
39380
+ if (direct) return direct;
39381
+ for (const child of element.children) {
39382
+ if (TEXT_CARRYING_ROLES.has(child.role) && child.text) return child.text;
39383
+ }
39384
+ return void 0;
39385
+ }
39386
+ function findNextSectionBoundary(siblings, startIndex) {
39387
+ for (let i = startIndex; i < siblings.length; i++) {
39388
+ const sib = siblings[i];
39389
+ if (sib.role === "heading" || STRUCTURAL_ROLES.has(sib.role)) return i;
39390
+ if (isVisualHeading(sib, siblings, i)) return i;
39391
+ }
39392
+ return siblings.length;
39393
+ }
39394
+ function isFieldLabelCandidate(element) {
39395
+ if (element.role !== "generic" && element.role !== "paragraph" && element.role !== "text" && element.role !== "label" && element.role !== "strong" && element.role !== "emphasis") {
39396
+ return false;
39397
+ }
39398
+ if (isClickableByAttribute(element)) return false;
39399
+ if (hasInteractiveDescendants(element)) return false;
39400
+ const text = getFieldLabelText(element);
39401
+ if (!text || text.length < 1 || text.length > VISUAL_HEADING_MAX_LENGTH) return false;
39402
+ return true;
39403
+ }
39404
+ function getFieldLabelText(element) {
39405
+ if (element.text) return element.text.trim();
39406
+ if (element.inputValue && !INTERACTIVE_ROLES.has(element.role)) return element.inputValue.trim();
39407
+ for (const child of element.children) {
39408
+ if (FIELD_LABEL_TEXT_ROLES.has(child.role)) {
39409
+ if (child.text) return child.text.trim();
39410
+ if (child.inputValue && !INTERACTIVE_ROLES.has(child.role)) return child.inputValue.trim();
39127
39411
  }
39128
39412
  }
39129
- if (bestMatch) {
39130
- const remainder = cell.column.slice(bestMatch.length).trim();
39131
- const newValue = isBlankValue(remainder) ? "" : remainder;
39132
- return {
39133
- ...cell,
39134
- column: bestMatch,
39135
- value: newValue || cell.value,
39136
- hasValue: (newValue || cell.value) !== ""
39137
- };
39413
+ return void 0;
39414
+ }
39415
+ function resolveContainerFieldLabel(container) {
39416
+ const kids = container.children;
39417
+ if (kids.length < 2 || kids.length > 4) return void 0;
39418
+ const firstChild = kids[0];
39419
+ if (isFieldLabelCandidate(firstChild)) {
39420
+ return getFieldLabelText(firstChild);
39138
39421
  }
39139
- return cell;
39422
+ return void 0;
39140
39423
  }
39141
- function reconcileGridCells(grid) {
39142
- if (grid.columns.length === 0) return grid;
39143
- const knownColumns = new Set(grid.columns.map((c2) => c2.name));
39144
- return {
39145
- ...grid,
39146
- rows: grid.rows.map((row) => ({
39147
- ...row,
39148
- cells: row.cells.map((cell) => {
39149
- const reconciled = reconcileCellColumn(cell, knownColumns);
39150
- if (knownColumns.has(reconciled.column)) return reconciled;
39151
- if (grid.columnByPosition && cell.positionInRow !== void 0 && grid.headerChildCount !== void 0 && row.totalChildren === grid.headerChildCount) {
39152
- const positionalColumn = grid.columnByPosition.get(cell.positionInRow);
39153
- if (positionalColumn) {
39154
- const newValue = cell.column || cell.value;
39155
- const cleanValue = isBlankValue(newValue) ? "" : newValue.trim();
39156
- return {
39157
- ...cell,
39158
- column: positionalColumn,
39159
- value: cleanValue,
39160
- hasValue: cleanValue !== ""
39161
- };
39424
+ function findFieldLabelFromSiblings(siblings, interactiveIndex) {
39425
+ for (let i = interactiveIndex - 1; i >= 0; i--) {
39426
+ const sib = siblings[i];
39427
+ if (INTERACTIVE_ROLES.has(sib.role) || STRUCTURAL_ROLES.has(sib.role) || sib.role === "heading") {
39428
+ break;
39429
+ }
39430
+ if (isClickableByAttribute(sib)) {
39431
+ if (sib.role === "generic" || sib.role === "paragraph" || sib.role === "label") {
39432
+ for (let j = i - 1; j >= 0; j--) {
39433
+ const prev = siblings[j];
39434
+ if (INTERACTIVE_ROLES.has(prev.role) || isClickableByAttribute(prev) || STRUCTURAL_ROLES.has(prev.role) || prev.role === "heading") {
39435
+ break;
39162
39436
  }
39437
+ if (isFieldLabelCandidate(prev)) {
39438
+ return getFieldLabelText(prev);
39439
+ }
39440
+ if (prev.children.length >= 1 && prev.children.length <= 4) {
39441
+ const containerLabel = resolveContainerFieldLabel(prev);
39442
+ if (containerLabel) return containerLabel;
39443
+ }
39444
+ if (prev.children.length > 4) break;
39163
39445
  }
39164
- return reconciled;
39165
- })
39166
- }))
39167
- };
39446
+ }
39447
+ break;
39448
+ }
39449
+ if (isFieldLabelCandidate(sib)) {
39450
+ return getFieldLabelText(sib);
39451
+ }
39452
+ if (sib.children.length >= 1 && sib.children.length <= 4) {
39453
+ const containerLabel = resolveContainerFieldLabel(sib);
39454
+ if (containerLabel) return containerLabel;
39455
+ }
39456
+ if (sib.children.length > 4) break;
39457
+ }
39458
+ return void 0;
39168
39459
  }
39169
- function findActiveElement(elements) {
39170
- let deepestActive = null;
39171
- function findDeepest(element) {
39172
- for (const child of element.children) {
39173
- findDeepest(child);
39460
+ function findFieldLabelInContext(_element, siblings, siblingIndex) {
39461
+ return findFieldLabelFromSiblings(siblings, siblingIndex);
39462
+ }
39463
+ function shouldDropInheritedFieldLabel(element, inheritedFieldLabel) {
39464
+ if (!inheritedFieldLabel) return false;
39465
+ if (INTERACTIVE_LEAF_ROLES.has(element.role)) return false;
39466
+ if (!isClickableByAttribute(element)) return false;
39467
+ const directText = getElementText(element);
39468
+ if (directText) {
39469
+ return directText.toLowerCase() !== inheritedFieldLabel.toLowerCase();
39470
+ }
39471
+ const childTexts = [];
39472
+ for (const child of element.children) {
39473
+ if (!INTERACTIVE_LEAF_ROLES.has(child.role)) {
39474
+ const text = getElementText(child);
39475
+ if (text && TEXT_CARRYING_ROLES.has(child.role)) {
39476
+ childTexts.push(text);
39477
+ }
39174
39478
  }
39175
- if (isActiveElement(element)) {
39176
- if (!deepestActive) {
39177
- deepestActive = element;
39479
+ }
39480
+ const composedText = childTexts.join(" ");
39481
+ if (!composedText) return false;
39482
+ return composedText.toLowerCase() !== inheritedFieldLabel.toLowerCase();
39483
+ }
39484
+ function isExpandedElement(element) {
39485
+ return element.attributes["expanded"] === "true" || element.attributes["aria-expanded"] === "true";
39486
+ }
39487
+ function collectExpandedChildren(element, result) {
39488
+ for (const child of element.children) {
39489
+ if (STRUCTURAL_ROLES.has(child.role)) continue;
39490
+ if (INTERACTIVE_LEAF_ROLES.has(child.role) && child.ref) {
39491
+ result.push(formatElement(child));
39492
+ continue;
39493
+ }
39494
+ if (child.role === "listitem" && child.ref) {
39495
+ const text = child.text || child.children.map((c2) => getElementText(c2)).filter((value) => Boolean(value)).join(" | ") || getDisplayText(child);
39496
+ if (text && text !== "listitem") {
39497
+ result.push(formatElement(child, { overrideLabel: text }));
39498
+ continue;
39178
39499
  }
39179
39500
  }
39501
+ if (isClickableByAttribute(child) && child.ref) {
39502
+ result.push(formatElement(child));
39503
+ }
39504
+ collectExpandedChildren(child, result);
39180
39505
  }
39181
- for (const element of elements) {
39182
- findDeepest(element);
39506
+ }
39507
+ function hasActionableChildren(element) {
39508
+ const children2 = [];
39509
+ collectExpandedChildren(element, children2);
39510
+ return children2.length > 0;
39511
+ }
39512
+ function isExpandedWithActionableChildren(element) {
39513
+ if (!isExpandedElement(element)) return false;
39514
+ if (element.role === "combobox") return false;
39515
+ return hasActionableChildren(element);
39516
+ }
39517
+ function composeTriggerLabel(element) {
39518
+ const ariaLabel = element.attributes["aria-label"];
39519
+ if (ariaLabel) return ariaLabel;
39520
+ const fullText = element.text || "";
39521
+ if (fullText) {
39522
+ let collectLeafTexts2 = function(el) {
39523
+ for (const child of el.children) {
39524
+ const text = child.text?.trim();
39525
+ if (text) {
39526
+ leafTexts.push(text);
39527
+ }
39528
+ collectLeafTexts2(child);
39529
+ }
39530
+ };
39531
+ var collectLeafTexts = collectLeafTexts2;
39532
+ const leafTexts = [];
39533
+ collectLeafTexts2(element);
39534
+ if (leafTexts.length > 0) {
39535
+ let stripped = fullText;
39536
+ for (const lt of leafTexts) {
39537
+ stripped = stripped.replace(lt, "");
39538
+ }
39539
+ stripped = stripped.replace(/\s+/g, " ").trim();
39540
+ if (stripped) return stripped;
39541
+ }
39183
39542
  }
39184
- return deepestActive;
39543
+ return element.role;
39185
39544
  }
39186
39545
  function formatElement(element, options) {
39187
- const isClickable = isClickableByAttribute(element) && !INTERACTIVE_ROLES.has(element.role);
39188
- const rawTextSource = options?.overrideLabel ?? (isClickable ? composeLabel(element) : element.text ?? "");
39189
- const textSource = normalizeIconLabelText(element.role, rawTextSource);
39546
+ const isClickable = isActionableClickableGeneric(element);
39547
+ const textSource = getDisplayText(element, { overrideLabel: options?.overrideLabel });
39190
39548
  const extracted = extractDataValue(textSource);
39191
39549
  const normalizedAttributes = Object.entries(element.attributes).filter(
39192
39550
  ([key]) => key !== "ref" && key !== "cursor" && key !== "aria-controls" && key !== "aria-owns" && key !== "options" && key !== "field"
@@ -39220,105 +39578,141 @@ function formatElement(element, options) {
39220
39578
  attributes: normalizedAttributes
39221
39579
  };
39222
39580
  }
39223
- function collectComboboxOptions(element) {
39224
- const options = [];
39225
- function walk(el) {
39226
- if (el.role === "option" && el.text?.trim()) {
39227
- options.push(el.text.trim());
39228
- }
39229
- for (const child of el.children) {
39230
- walk(child);
39231
- }
39232
- }
39233
- for (const child of element.children) {
39234
- walk(child);
39235
- }
39236
- return options.length > 0 ? options : void 0;
39237
- }
39238
- var MIN_ELEMENTS_TO_COLLAPSE = 20;
39239
39581
  function shouldCollapse(section) {
39240
39582
  if (section.containsActive) return false;
39241
39583
  if (section.hasSearchMatch) return false;
39242
39584
  if (section.isBackground) return true;
39243
39585
  if (section.role === "dialog" || section.role === "alertdialog") return false;
39244
- if (section.role === "navigation" || section.role === "menubar" || section.role === "menu") {
39245
- return false;
39246
- }
39586
+ if (section.role === "navigation" || section.role === "menubar" || section.role === "menu") return false;
39247
39587
  if (section.role === "banner") return false;
39248
39588
  if (section.role === "form") return false;
39249
39589
  if (section.elementCount >= MIN_ELEMENTS_TO_COLLAPSE) return true;
39250
39590
  return false;
39251
39591
  }
39252
- function buildSectionTree(elements) {
39253
- const sections = [];
39254
- for (const element of elements) {
39255
- const section = buildSection(element, 0);
39256
- if (section) {
39257
- sections.push(section);
39592
+ function deduplicateFieldLabels(elements) {
39593
+ const byFieldLabel = /* @__PURE__ */ new Map();
39594
+ for (const el of elements) {
39595
+ if (!el.fieldLabel) continue;
39596
+ const existing = byFieldLabel.get(el.fieldLabel);
39597
+ if (existing) {
39598
+ existing.push(el);
39599
+ } else {
39600
+ byFieldLabel.set(el.fieldLabel, [el]);
39258
39601
  }
39259
39602
  }
39260
- markBackgroundSections(sections);
39261
- return sections;
39262
- }
39263
- function markBackgroundSections(sections) {
39264
- const hasActiveSibling = sections.some((s) => s.containsActive);
39265
- if (hasActiveSibling) {
39266
- for (const section of sections) {
39267
- if (!section.containsActive) {
39268
- section.isBackground = true;
39269
- section.collapsed = shouldCollapse(section);
39603
+ for (const [, group] of byFieldLabel) {
39604
+ if (group.length < 2) continue;
39605
+ for (let i = 0; i < group.length; i++) {
39606
+ const el = group[i];
39607
+ if (el.name && el.name !== el.fieldLabel) {
39608
+ el.fieldLabel = `${el.fieldLabel}: ${el.name}`;
39609
+ } else {
39610
+ el.fieldLabel = `${el.fieldLabel} (${i + 1})`;
39270
39611
  }
39271
39612
  }
39272
39613
  }
39273
- for (const section of sections) {
39274
- markBackgroundSections(section.subsections);
39275
- }
39276
39614
  }
39277
- function markMatchingSections(sections, matchedRefs) {
39278
- for (const section of sections) {
39279
- const hasDirectMatch = section.elements.some((el) => matchedRefs.has(el.ref));
39280
- markMatchingSections(section.subsections, matchedRefs);
39281
- const hasSubsectionMatch = section.subsections.some((s) => s.hasSearchMatch);
39282
- if (hasDirectMatch || hasSubsectionMatch) {
39283
- section.hasSearchMatch = true;
39284
- section.collapsed = false;
39285
- }
39615
+ function buildGridSection(element) {
39616
+ const gridInfo = detectGrid(element);
39617
+ if (!gridInfo) {
39618
+ throw new Error("buildGridSection requires a grid element");
39286
39619
  }
39620
+ return {
39621
+ ref: element.ref || "",
39622
+ role: element.role,
39623
+ heading: "Grid",
39624
+ level: 0,
39625
+ elements: [],
39626
+ subsections: [],
39627
+ containsActive: gridInfo.containsActive,
39628
+ collapsed: false,
39629
+ isBackground: false,
39630
+ isFocused: false,
39631
+ elementCount: gridInfo.totalRows * Math.max(gridInfo.columns.length, 1),
39632
+ gridInfo
39633
+ };
39287
39634
  }
39288
- var DIALOG_ROLES = /* @__PURE__ */ new Set(["dialog", "alertdialog"]);
39289
- function sortSectionsByFocus(sections) {
39290
- const dialogStack = [];
39291
- function processLevel(levelSections) {
39292
- for (const section of levelSections) {
39293
- const result = processLevel(section.subsections);
39294
- section.subsections = result;
39635
+ function collectInteractiveElements(element, elements, subsections, depth, inheritedFieldLabel) {
39636
+ if ((INTERACTIVE_LEAF_ROLES.has(element.role) || isClickableByAttribute(element)) && element.ref) {
39637
+ const effectiveFieldLabel = shouldDropInheritedFieldLabel(element, inheritedFieldLabel) ? void 0 : inheritedFieldLabel;
39638
+ if (INTERACTIVE_LEAF_ROLES.has(element.role) && isExpandedWithActionableChildren(element)) {
39639
+ elements.push(formatElement(element, { overrideLabel: composeTriggerLabel(element), fieldLabel: effectiveFieldLabel }));
39640
+ collectExpandedChildren(element, elements);
39641
+ return;
39295
39642
  }
39296
- const dialogs = levelSections.filter((s) => DIALOG_ROLES.has(s.role));
39297
- const nonDialogs = levelSections.filter((s) => !DIALOG_ROLES.has(s.role));
39298
- const focusedDialog = dialogs.find((d) => d.containsActive);
39299
- if (focusedDialog) {
39300
- focusedDialog.isFocused = true;
39301
- dialogStack.unshift(focusedDialog.heading);
39643
+ if (!isMenuTriggerNoise(element)) {
39644
+ elements.push(formatElement(element, { fieldLabel: effectiveFieldLabel }));
39302
39645
  }
39303
- for (const dialog of dialogs) {
39304
- if (!dialog.containsActive) {
39305
- dialogStack.push(dialog.heading);
39646
+ if (!INTERACTIVE_LEAF_ROLES.has(element.role) && isClickableByAttribute(element)) {
39647
+ for (const child of element.children) {
39648
+ collectInteractiveElements(child, elements, subsections, depth + 1, effectiveFieldLabel);
39306
39649
  }
39307
39650
  }
39308
- const sortedDialogs = dialogs.sort((a, b) => {
39309
- if (a.containsActive && !b.containsActive) return -1;
39310
- if (!a.containsActive && b.containsActive) return 1;
39311
- return 0;
39312
- });
39313
- return [...sortedDialogs, ...nonDialogs];
39651
+ return;
39652
+ }
39653
+ if (element.role === "heading" || STRUCTURAL_ROLES.has(element.role) || element.role === "grid" || element.role === "treegrid" || element.role === "table" && !isWidgetTable(element)) {
39654
+ const section = buildSection(element, depth);
39655
+ if (section) {
39656
+ subsections.push(section);
39657
+ }
39658
+ return;
39659
+ }
39660
+ const kids = element.children;
39661
+ for (let ki = 0; ki < kids.length; ki++) {
39662
+ const child = kids[ki];
39663
+ if (isVisualHeading(child, kids, ki)) {
39664
+ const headingText = getVisualHeadingText(child);
39665
+ const sectionElements = [];
39666
+ const sectionSubsections = [];
39667
+ const nextBoundary = findNextSectionBoundary(kids, ki + 1);
39668
+ for (let si = ki + 1; si < nextBoundary; si++) {
39669
+ collectInteractiveElements(kids[si], sectionElements, sectionSubsections, depth + 2);
39670
+ }
39671
+ if (sectionElements.length > 0 || sectionSubsections.length > 0) {
39672
+ const totalElements = sectionElements.length + sectionSubsections.reduce((sum, s) => sum + s.elementCount, 0);
39673
+ subsections.push({
39674
+ heading: headingText ?? "",
39675
+ ref: child.ref,
39676
+ role: "heading",
39677
+ level: 4,
39678
+ elements: sectionElements,
39679
+ subsections: sectionSubsections,
39680
+ collapsed: false,
39681
+ containsActive: false,
39682
+ isBackground: false,
39683
+ isFocused: false,
39684
+ elementCount: totalElements
39685
+ });
39686
+ ki = nextBoundary - 1;
39687
+ } else {
39688
+ collectInteractiveElements(child, elements, subsections, depth + 1);
39689
+ }
39690
+ } else if ((INTERACTIVE_LEAF_ROLES.has(child.role) || isClickableByAttribute(child)) && child.ref) {
39691
+ const rawFieldLabel = findFieldLabelInContext(child, kids, ki) ?? inheritedFieldLabel;
39692
+ const fieldLabel = shouldDropInheritedFieldLabel(child, rawFieldLabel) ? void 0 : rawFieldLabel;
39693
+ if (!isMenuTriggerNoise(child)) {
39694
+ if (INTERACTIVE_LEAF_ROLES.has(child.role) && isExpandedWithActionableChildren(child)) {
39695
+ elements.push(formatElement(child, { overrideLabel: composeTriggerLabel(child), fieldLabel }));
39696
+ collectExpandedChildren(child, elements);
39697
+ } else {
39698
+ elements.push(formatElement(child, { fieldLabel }));
39699
+ }
39700
+ }
39701
+ if (!INTERACTIVE_LEAF_ROLES.has(child.role) && isClickableByAttribute(child)) {
39702
+ for (const grandchild of child.children) {
39703
+ collectInteractiveElements(grandchild, elements, subsections, depth + 1, fieldLabel);
39704
+ }
39705
+ }
39706
+ } else {
39707
+ const containerLabel = resolveContainerFieldLabel(child) ?? (child.role === "table" ? findFieldLabelFromSiblings(kids, ki) : void 0) ?? inheritedFieldLabel;
39708
+ collectInteractiveElements(child, elements, subsections, depth + 1, containerLabel);
39709
+ }
39314
39710
  }
39315
- const sorted = processLevel(sections);
39316
- return { sorted, dialogStack };
39317
39711
  }
39318
39712
  function buildSection(element, depth) {
39319
39713
  const gridInfo = detectGrid(element);
39320
39714
  if (gridInfo) {
39321
- return buildGridSection(element, gridInfo);
39715
+ return buildGridSection(element);
39322
39716
  }
39323
39717
  const isHeading = element.role === "heading";
39324
39718
  const isStructural = STRUCTURAL_ROLES.has(element.role);
@@ -39356,7 +39750,6 @@ function buildSection(element, depth) {
39356
39750
  ref: child.ref,
39357
39751
  role: "heading",
39358
39752
  level: 4,
39359
- // Visual headings rendered at h6 level (4 + 2 = 6)
39360
39753
  elements: sectionElements,
39361
39754
  subsections: sectionSubsections,
39362
39755
  collapsed: false,
@@ -39400,192 +39793,138 @@ function buildSection(element, depth) {
39400
39793
  const section = {
39401
39794
  ref: element.ref || "",
39402
39795
  role: element.role,
39403
- heading: element.text || (isClickableByAttribute(element) ? composeLabel(element) : element.role),
39796
+ heading: element.text || (isClickableByAttribute(element) ? getDisplayText(element) : element.role),
39404
39797
  level,
39405
39798
  elements: interactiveElements,
39406
39799
  subsections,
39407
39800
  containsActive,
39408
39801
  collapsed: false,
39409
39802
  isBackground: false,
39410
- // Will be set by markBackgroundSections if needed
39411
39803
  isFocused: false,
39412
- // Will be set by sortSectionsByFocus if needed
39413
39804
  elementCount
39414
39805
  };
39806
+ deduplicateFieldLabels(interactiveElements);
39415
39807
  section.collapsed = shouldCollapse(section);
39416
39808
  return section;
39417
39809
  }
39418
- function buildGridSection(element, gridInfo) {
39419
- return {
39420
- ref: element.ref || "",
39421
- role: element.role,
39422
- heading: "Grid",
39423
- level: 0,
39424
- elements: [],
39425
- // Grid cells formatted via gridInfo instead
39426
- subsections: [],
39427
- containsActive: gridInfo.containsActive,
39428
- collapsed: false,
39429
- // Grids use their own summarization
39430
- isBackground: false,
39431
- isFocused: false,
39432
- elementCount: gridInfo.totalRows * Math.max(gridInfo.columns.length, 1),
39433
- gridInfo
39434
- };
39435
- }
39436
- function collectInteractiveElements(element, elements, subsections, depth, inheritedFieldLabel) {
39437
- if ((INTERACTIVE_LEAF_ROLES.has(element.role) || isClickableByAttribute(element)) && element.ref) {
39438
- const effectiveFieldLabel = shouldDropInheritedFieldLabel(element, inheritedFieldLabel) ? void 0 : inheritedFieldLabel;
39439
- if (INTERACTIVE_LEAF_ROLES.has(element.role) && isExpandedWithActionableChildren(element)) {
39440
- elements.push(formatElement(element, { overrideLabel: composeTriggerLabel(element), fieldLabel: effectiveFieldLabel }));
39441
- collectExpandedChildren(element, elements);
39442
- return;
39443
- }
39444
- if (!isMenuTriggerNoise(element)) {
39445
- elements.push(formatElement(element, { fieldLabel: effectiveFieldLabel }));
39446
- }
39447
- if (!INTERACTIVE_LEAF_ROLES.has(element.role) && isClickableByAttribute(element)) {
39448
- for (const child of element.children) {
39449
- collectInteractiveElements(child, elements, subsections, depth + 1, effectiveFieldLabel);
39810
+ function markBackgroundSections(sections) {
39811
+ const hasActiveSibling = sections.some((s) => s.containsActive);
39812
+ if (hasActiveSibling) {
39813
+ for (const section of sections) {
39814
+ if (!section.containsActive) {
39815
+ section.isBackground = true;
39816
+ section.collapsed = shouldCollapse(section);
39450
39817
  }
39451
39818
  }
39452
- return;
39453
39819
  }
39454
- if (element.role === "heading" || STRUCTURAL_ROLES.has(element.role) || element.role === "grid" || element.role === "treegrid" || element.role === "table" && !isWidgetTable(element)) {
39455
- const section = buildSection(element, depth);
39820
+ for (const section of sections) {
39821
+ markBackgroundSections(section.subsections);
39822
+ }
39823
+ }
39824
+ function buildSectionTree(elements) {
39825
+ const sections = [];
39826
+ for (const element of elements) {
39827
+ const section = buildSection(element, 0);
39456
39828
  if (section) {
39457
- subsections.push(section);
39829
+ sections.push(section);
39458
39830
  }
39459
- return;
39460
39831
  }
39461
- const kids = element.children;
39462
- for (let ki = 0; ki < kids.length; ki++) {
39463
- const child = kids[ki];
39464
- if (isVisualHeading(child, kids, ki)) {
39465
- const headingText = getVisualHeadingText(child);
39466
- const sectionElements = [];
39467
- const sectionSubsections = [];
39468
- const nextBoundary = findNextSectionBoundary(kids, ki + 1);
39469
- for (let si = ki + 1; si < nextBoundary; si++) {
39470
- collectInteractiveElements(kids[si], sectionElements, sectionSubsections, depth + 2);
39471
- }
39472
- if (sectionElements.length > 0 || sectionSubsections.length > 0) {
39473
- const totalElements = sectionElements.length + sectionSubsections.reduce((sum, s) => sum + s.elementCount, 0);
39474
- subsections.push({
39475
- heading: headingText ?? "",
39476
- ref: child.ref,
39477
- role: "heading",
39478
- level: 4,
39479
- elements: sectionElements,
39480
- subsections: sectionSubsections,
39481
- collapsed: false,
39482
- containsActive: false,
39483
- isBackground: false,
39484
- isFocused: false,
39485
- elementCount: totalElements
39486
- });
39487
- ki = nextBoundary - 1;
39488
- } else {
39489
- collectInteractiveElements(child, elements, subsections, depth + 1);
39490
- }
39491
- } else if ((INTERACTIVE_LEAF_ROLES.has(child.role) || isClickableByAttribute(child)) && child.ref) {
39492
- const rawFieldLabel = findFieldLabelInContext(child, kids, ki) ?? inheritedFieldLabel;
39493
- const fieldLabel = shouldDropInheritedFieldLabel(child, rawFieldLabel) ? void 0 : rawFieldLabel;
39494
- if (!isMenuTriggerNoise(child)) {
39495
- if (INTERACTIVE_LEAF_ROLES.has(child.role) && isExpandedWithActionableChildren(child)) {
39496
- elements.push(formatElement(child, { overrideLabel: composeTriggerLabel(child), fieldLabel }));
39497
- collectExpandedChildren(child, elements);
39498
- } else {
39499
- elements.push(formatElement(child, { fieldLabel }));
39500
- }
39501
- }
39502
- if (!INTERACTIVE_LEAF_ROLES.has(child.role) && isClickableByAttribute(child)) {
39503
- for (const grandchild of child.children) {
39504
- collectInteractiveElements(grandchild, elements, subsections, depth + 1, fieldLabel);
39505
- }
39506
- }
39507
- } else {
39508
- const containerLabel = resolveContainerFieldLabel(child) ?? (child.role === "table" ? findFieldLabelFromSiblings(kids, ki) : void 0) ?? inheritedFieldLabel;
39509
- collectInteractiveElements(child, elements, subsections, depth + 1, containerLabel);
39832
+ markBackgroundSections(sections);
39833
+ return sections;
39834
+ }
39835
+ function markMatchingSections(sections, matchedRefs) {
39836
+ for (const section of sections) {
39837
+ const hasDirectMatch = section.elements.some((el) => matchedRefs.has(el.ref)) || (section.gridInfo ? Array.from(matchedRefs).some((ref) => gridContainsRef(section.gridInfo, ref)) : false);
39838
+ markMatchingSections(section.subsections, matchedRefs);
39839
+ const hasSubsectionMatch = section.subsections.some((s) => s.hasSearchMatch);
39840
+ if (hasDirectMatch || hasSubsectionMatch) {
39841
+ section.hasSearchMatch = true;
39842
+ section.collapsed = false;
39510
39843
  }
39511
39844
  }
39512
39845
  }
39513
- function formatSectionOutput(section, indent, showCollapsed) {
39514
- if (section.gridInfo) {
39515
- return formatGridOutput(section.gridInfo, indent);
39516
- }
39517
- const indentStr = " ".repeat(indent);
39518
- const lines = [];
39519
- const headingPrefix = section.level > 0 ? "#".repeat(Math.min(section.level + 2, 6)) + " " : "";
39520
- const refPart = section.ref ? ` [${section.ref}]` : "";
39521
- const focusedPart = section.isFocused ? " [FOCUSED]" : "";
39522
- const backgroundPart = section.isBackground ? " [BACKGROUND]" : "";
39523
- const activePart = section.containsActive && !section.isFocused ? " \u2190 ACTIVE" : "";
39524
- const headerLine = `${indentStr}${headingPrefix}${section.heading}${refPart}${focusedPart}${backgroundPart}${activePart}`;
39525
- lines.push(headerLine);
39526
- if (section.collapsed && !showCollapsed) {
39527
- if (section.isBackground) {
39528
- if (section.ref) {
39529
- lines.push(`${indentStr} (${section.elementCount} elements - use expand="${section.ref}" to see content)`);
39530
- } else {
39531
- lines.push(`${indentStr} (${section.elementCount} elements)`);
39532
- }
39533
- return lines.join("\n");
39534
- }
39535
- const MAX_DIRECT_ELEMENTS = 4;
39536
- const previewElements = section.elements.slice(0, MAX_DIRECT_ELEMENTS);
39537
- const remainingDirectCount = section.elements.length - previewElements.length;
39538
- for (const el of previewElements) {
39539
- lines.push(formatElementLine(el, indent + 1));
39540
- }
39541
- if (remainingDirectCount > 0) {
39542
- lines.push(`${indentStr} ... +${remainingDirectCount} more elements`);
39846
+ function sortSectionsByFocus(sections) {
39847
+ const dialogStack = [];
39848
+ function processLevel(levelSections) {
39849
+ for (const section of levelSections) {
39850
+ section.subsections = processLevel(section.subsections);
39543
39851
  }
39544
- for (const subsection of section.subsections) {
39545
- lines.push(`${indentStr} ${formatSubsectionSummary(subsection)}`);
39852
+ const dialogs = levelSections.filter((s) => DIALOG_ROLES.has(s.role));
39853
+ const nonDialogs = levelSections.filter((s) => !DIALOG_ROLES.has(s.role));
39854
+ const focusedDialog = dialogs.find((d) => d.containsActive);
39855
+ if (focusedDialog) {
39856
+ focusedDialog.isFocused = true;
39857
+ dialogStack.unshift(focusedDialog.heading);
39546
39858
  }
39547
- if (section.ref && (section.elements.length > MAX_DIRECT_ELEMENTS || section.subsections.length > 0)) {
39548
- lines.push(`${indentStr} (use expand="${section.ref}" to see all)`);
39859
+ for (const dialog of dialogs) {
39860
+ if (!dialog.containsActive) {
39861
+ dialogStack.push(dialog.heading);
39862
+ }
39549
39863
  }
39550
- return lines.join("\n");
39551
- }
39552
- for (const el of section.elements) {
39553
- lines.push(formatElementLine(el, indent + 1));
39864
+ const sortedDialogs = dialogs.sort((a, b) => {
39865
+ if (a.containsActive && !b.containsActive) return -1;
39866
+ if (!a.containsActive && b.containsActive) return 1;
39867
+ return 0;
39868
+ });
39869
+ return [...sortedDialogs, ...nonDialogs];
39554
39870
  }
39555
- for (const subsection of section.subsections) {
39556
- const subsectionOutput = formatSectionOutput(subsection, indent + 1, showCollapsed);
39557
- if (subsectionOutput) {
39558
- lines.push("");
39559
- lines.push(subsectionOutput);
39871
+ const sorted = processLevel(sections);
39872
+ return { sorted, dialogStack };
39873
+ }
39874
+ function expandSectionByRef(sections, targetRef) {
39875
+ for (const section of sections) {
39876
+ if (section.ref === targetRef) {
39877
+ section.collapsed = false;
39878
+ return true;
39879
+ }
39880
+ if (expandSectionByRef(section.subsections, targetRef)) {
39881
+ return true;
39560
39882
  }
39561
39883
  }
39562
- return lines.join("\n");
39884
+ return false;
39563
39885
  }
39564
- function collectAllElementsFlat(section) {
39565
- const elements = [...section.elements];
39566
- for (const subsection of section.subsections) {
39567
- elements.push(...collectAllElementsFlat(subsection));
39886
+ function findSectionByRef(sections, targetRef) {
39887
+ for (const section of sections) {
39888
+ if (section.ref === targetRef) {
39889
+ return section;
39890
+ }
39891
+ const found = findSectionByRef(section.subsections, targetRef);
39892
+ if (found) {
39893
+ return found;
39894
+ }
39568
39895
  }
39569
- return elements;
39896
+ return null;
39570
39897
  }
39571
- function formatSubsectionSummary(subsection) {
39572
- const refPart = subsection.ref ? ` [${subsection.ref}]` : "";
39573
- if (subsection.gridInfo) {
39574
- const colInfo = subsection.gridInfo.columns.length > 0 ? `${subsection.gridInfo.columns.length} columns` : "unknown columns";
39575
- return `GRID${refPart}: ${subsection.gridInfo.totalRows} rows, ${colInfo}`;
39898
+
39899
+ // ../browser-core/src/snapshot-formatter-render.ts
39900
+ var COLOR_NAMES = [
39901
+ "red",
39902
+ "green",
39903
+ "blue",
39904
+ "yellow",
39905
+ "magenta",
39906
+ "cyan",
39907
+ "orange",
39908
+ "purple",
39909
+ "teal",
39910
+ "pink"
39911
+ ];
39912
+ function findActiveElement(elements) {
39913
+ let deepestActive = null;
39914
+ function findDeepest(element) {
39915
+ for (const child of element.children) {
39916
+ findDeepest(child);
39917
+ }
39918
+ if (element.attributes["active"] !== void 0 || element.rawLine.includes("[active]")) {
39919
+ if (!deepestActive) {
39920
+ deepestActive = element;
39921
+ }
39922
+ }
39576
39923
  }
39577
- const allElements = collectAllElementsFlat(subsection);
39578
- const elementNames = allElements.map((el) => el.label || el.name || el.role).filter(Boolean);
39579
- const heading = subsection.heading || subsection.role;
39580
- if (elementNames.length === 0) {
39581
- return `${heading}${refPart}`;
39924
+ for (const element of elements) {
39925
+ findDeepest(element);
39582
39926
  }
39583
- const MAX_PREVIEW_NAMES = 3;
39584
- const previewNames = elementNames.slice(0, MAX_PREVIEW_NAMES);
39585
- const remainingCount = elementNames.length - previewNames.length;
39586
- const namesList = previewNames.join(", ");
39587
- const moreNote = remainingCount > 0 ? `... +${remainingCount} more` : "";
39588
- return `${heading}${refPart}: ${namesList}${moreNote}`;
39927
+ return deepestActive;
39589
39928
  }
39590
39929
  function formatElementLine(el, indent) {
39591
39930
  const indentStr = " ".repeat(indent);
@@ -39637,23 +39976,102 @@ function formatElementLine(el, indent) {
39637
39976
  }
39638
39977
  return indentStr + parts.join(" ");
39639
39978
  }
39640
- var COLOR_NAMES = [
39641
- "red",
39642
- "green",
39643
- "blue",
39644
- "yellow",
39645
- "magenta",
39646
- "cyan",
39647
- "orange",
39648
- "purple",
39649
- "teal",
39650
- "pink"
39651
- ];
39979
+ function collectAllElementsFlat(section) {
39980
+ const elements = [...section.elements];
39981
+ for (const subsection of section.subsections) {
39982
+ elements.push(...collectAllElementsFlat(subsection));
39983
+ }
39984
+ return elements;
39985
+ }
39986
+ function formatSubsectionSummary(subsection) {
39987
+ const refPart = subsection.ref ? ` [${subsection.ref}]` : "";
39988
+ if (subsection.gridInfo) {
39989
+ const colInfo = subsection.gridInfo.columns.length > 0 ? `${subsection.gridInfo.columns.length} columns` : "unknown columns";
39990
+ return `GRID${refPart}: ${subsection.gridInfo.totalRows} rows, ${colInfo}`;
39991
+ }
39992
+ const allElements = collectAllElementsFlat(subsection);
39993
+ const elementNames = allElements.map((el) => el.label || el.name || el.role).filter(Boolean);
39994
+ const heading = subsection.heading || subsection.role;
39995
+ if (elementNames.length === 0) {
39996
+ return `${heading}${refPart}`;
39997
+ }
39998
+ const MAX_PREVIEW_NAMES = 3;
39999
+ const previewNames = elementNames.slice(0, MAX_PREVIEW_NAMES);
40000
+ const remainingCount = elementNames.length - previewNames.length;
40001
+ const namesList = previewNames.join(", ");
40002
+ const moreNote = remainingCount > 0 ? `... +${remainingCount} more` : "";
40003
+ return `${heading}${refPart}: ${namesList}${moreNote}`;
40004
+ }
40005
+ function formatSectionOutput(section, indent, showCollapsed) {
40006
+ if (section.gridInfo) {
40007
+ return formatGridOutput(section.gridInfo, indent);
40008
+ }
40009
+ const indentStr = " ".repeat(indent);
40010
+ const lines = [];
40011
+ const headingPrefix = section.level > 0 ? "#".repeat(Math.min(section.level + 2, 6)) + " " : "";
40012
+ const refPart = section.ref ? ` [${section.ref}]` : "";
40013
+ const focusedPart = section.isFocused ? " [FOCUSED]" : "";
40014
+ const backgroundPart = section.isBackground ? " [BACKGROUND]" : "";
40015
+ const activePart = section.containsActive && !section.isFocused ? " \u2190 ACTIVE" : "";
40016
+ lines.push(`${indentStr}${headingPrefix}${section.heading}${refPart}${focusedPart}${backgroundPart}${activePart}`);
40017
+ if (section.collapsed && !showCollapsed) {
40018
+ if (section.isBackground) {
40019
+ if (section.ref) {
40020
+ lines.push(`${indentStr} (${section.elementCount} elements - use expand="${section.ref}" to see content)`);
40021
+ } else {
40022
+ lines.push(`${indentStr} (${section.elementCount} elements)`);
40023
+ }
40024
+ return lines.join("\n");
40025
+ }
40026
+ const MAX_DIRECT_ELEMENTS = 4;
40027
+ const previewElements = section.elements.slice(0, MAX_DIRECT_ELEMENTS);
40028
+ const remainingDirectCount = section.elements.length - previewElements.length;
40029
+ for (const el of previewElements) {
40030
+ lines.push(formatElementLine(el, indent + 1));
40031
+ }
40032
+ if (remainingDirectCount > 0) {
40033
+ lines.push(`${indentStr} ... +${remainingDirectCount} more elements`);
40034
+ }
40035
+ for (const subsection of section.subsections) {
40036
+ lines.push(`${indentStr} ${formatSubsectionSummary(subsection)}`);
40037
+ }
40038
+ if (section.ref && (section.elements.length > MAX_DIRECT_ELEMENTS || section.subsections.length > 0)) {
40039
+ lines.push(`${indentStr} (use expand="${section.ref}" to see all)`);
40040
+ }
40041
+ return lines.join("\n");
40042
+ }
40043
+ for (const el of section.elements) {
40044
+ lines.push(formatElementLine(el, indent + 1));
40045
+ }
40046
+ for (const subsection of section.subsections) {
40047
+ const subsectionOutput = formatSectionOutput(subsection, indent + 1, showCollapsed);
40048
+ if (subsectionOutput) {
40049
+ lines.push("");
40050
+ lines.push(subsectionOutput);
40051
+ }
40052
+ }
40053
+ return lines.join("\n");
40054
+ }
40055
+ function countRenderedElements(sections) {
40056
+ let count = 0;
40057
+ for (const section of sections) {
40058
+ count += section.elements.length;
40059
+ if (section.gridInfo) {
40060
+ count += countRenderedGridItems(section.gridInfo);
40061
+ }
40062
+ count += countRenderedElements(section.subsections);
40063
+ }
40064
+ return count;
40065
+ }
40066
+ function countTotalSections(sections) {
40067
+ let count = sections.length;
40068
+ for (const section of sections) {
40069
+ count += countTotalSections(section.subsections);
40070
+ }
40071
+ return count;
40072
+ }
39652
40073
  function generateFrontmatter(elementCount, sectionCount, activeElement, dialogStack, searchMatches, totalMatchCount, snapshotFilePath, probeResult, currentTime, capturedToasts) {
39653
- const activeLabel = activeElement ? normalizeIconLabelText(
39654
- activeElement.role,
39655
- INTERACTIVE_LEAF_ROLES.has(activeElement.role) && isExpandedWithActionableChildren(activeElement) ? composeTriggerLabel(activeElement) : activeElement.text || ""
39656
- ) : "";
40074
+ const activeLabel = activeElement ? (FORM_INPUT_ROLES.has(activeElement.role) || activeElement.role === "button" || activeElement.role === "link") && (activeElement.attributes["expanded"] === "true" || activeElement.attributes["aria-expanded"] === "true") ? getDisplayText(activeElement, { overrideLabel: composeTriggerLabel(activeElement) }) : getDisplayText(activeElement) : "";
39657
40075
  const activeDesc = activeElement ? `${activeElement.ref} ${activeElement.role} "${activeLabel}"` : "None";
39658
40076
  const dialogStackLine = dialogStack.length > 0 ? `
39659
40077
  DIALOG STACK: ${dialogStack.join(" \u2192 ")}` : "";
@@ -39692,9 +40110,8 @@ ${toastLines.join("\n")}`;
39692
40110
  SEARCH RESULTS (${matchCountText}${sortNote}):
39693
40111
  ${matchLines.join("\n")}`;
39694
40112
  }
39695
- const INPUT_ROLES = /* @__PURE__ */ new Set(["textbox", "combobox", "searchbox", "spinbutton"]);
39696
40113
  let typeActionHint = "";
39697
- if (activeElement && INPUT_ROLES.has(activeElement.role)) {
40114
+ if (activeElement && FORM_INPUT_ROLES.has(activeElement.role)) {
39698
40115
  typeActionHint = `
39699
40116
  - Type in active field: browser_type ref=${activeElement.ref} text="..."`;
39700
40117
  }
@@ -39729,21 +40146,6 @@ ACTIONS:
39729
40146
  - Use bash or python_execute on the FULL TREE FILE for detailed element analysis
39730
40147
  ---`;
39731
40148
  }
39732
- function countTotalElements(sections) {
39733
- let count = 0;
39734
- for (const section of sections) {
39735
- count += section.elements.length;
39736
- count += countTotalElements(section.subsections);
39737
- }
39738
- return count;
39739
- }
39740
- function countTotalSections(sections) {
39741
- let count = sections.length;
39742
- for (const section of sections) {
39743
- count += countTotalSections(section.subsections);
39744
- }
39745
- return count;
39746
- }
39747
40149
  function formatSemanticSnapshot(yaml, options) {
39748
40150
  if (!yaml || yaml.trim() === "") {
39749
40151
  return "---\nSEMANTIC SNAPSHOT - 0 elements\nPage appears empty or not loaded.\n---";
@@ -39775,10 +40177,9 @@ function formatSemanticSnapshot(yaml, options) {
39775
40177
  }
39776
40178
  }
39777
40179
  if (options?.probeResult?.ref) {
39778
- const probeRefs = /* @__PURE__ */ new Set([options.probeResult.ref]);
39779
- markMatchingSections(sortedSections, probeRefs);
40180
+ markMatchingSections(sortedSections, /* @__PURE__ */ new Set([options.probeResult.ref]));
39780
40181
  }
39781
- const elementCount = countTotalElements(sortedSections);
40182
+ const elementCount = countRenderedElements(sortedSections);
39782
40183
  const sectionCount = countTotalSections(sortedSections);
39783
40184
  const lines = [];
39784
40185
  lines.push(
@@ -39788,7 +40189,6 @@ function formatSemanticSnapshot(yaml, options) {
39788
40189
  activeElement,
39789
40190
  dialogStack,
39790
40191
  sortedMatches,
39791
- // Use re-sorted matches with context
39792
40192
  options?.totalMatchCount,
39793
40193
  options?.snapshotFilePath,
39794
40194
  options?.probeResult,
@@ -39818,7 +40218,7 @@ function expandSection(yaml, sectionRef) {
39818
40218
  const sections = buildSectionTree(elements);
39819
40219
  const { sorted: sortedSections, dialogStack } = sortSectionsByFocus(sections);
39820
40220
  expandSectionByRef(sortedSections, sectionRef);
39821
- const elementCount = countTotalElements(sortedSections);
40221
+ const elementCount = countRenderedElements(sortedSections);
39822
40222
  const sectionCount = countTotalSections(sortedSections);
39823
40223
  const lines = [];
39824
40224
  lines.push(generateFrontmatter(elementCount, sectionCount, activeElement, dialogStack));
@@ -39832,29 +40232,26 @@ function expandSection(yaml, sectionRef) {
39832
40232
  }
39833
40233
  return lines.join("\n").trim();
39834
40234
  }
39835
- function expandSectionByRef(sections, targetRef) {
39836
- for (const section of sections) {
39837
- if (section.ref === targetRef) {
39838
- section.collapsed = false;
39839
- return true;
39840
- }
39841
- if (expandSectionByRef(section.subsections, targetRef)) {
39842
- return true;
39843
- }
40235
+ function formatExpandedSectionOutput(section) {
40236
+ const lines = [];
40237
+ const subsectionInfo = section.subsections.length > 0 ? `, ${section.subsections.length} subsections` : "";
40238
+ lines.push(`---`);
40239
+ lines.push(`EXPANDED SECTION: ${section.heading} [${section.ref}]`);
40240
+ lines.push(`Contains ${section.elementCount} elements${subsectionInfo}`);
40241
+ lines.push(`---`);
40242
+ lines.push("");
40243
+ if (section.gridInfo) {
40244
+ lines.push(formatGridOutput(section.gridInfo, 0, true));
40245
+ return lines.join("\n").trim();
39844
40246
  }
39845
- return false;
39846
- }
39847
- function findSectionByRef(sections, targetRef) {
39848
- for (const section of sections) {
39849
- if (section.ref === targetRef) {
39850
- return section;
39851
- }
39852
- const found = findSectionByRef(section.subsections, targetRef);
39853
- if (found) {
39854
- return found;
39855
- }
40247
+ for (const el of section.elements) {
40248
+ lines.push(formatElementLine(el, 0));
39856
40249
  }
39857
- return null;
40250
+ for (const subsection of section.subsections) {
40251
+ lines.push("");
40252
+ lines.push(formatSectionOutput(subsection, 0, false));
40253
+ }
40254
+ return lines.join("\n").trim();
39858
40255
  }
39859
40256
  function expandSectionOnly(yaml, sectionRef) {
39860
40257
  if (!yaml || yaml.trim() === "") {
@@ -39881,79 +40278,133 @@ Section with ref="${sectionRef}" not found in snapshot.
39881
40278
  targetSection.collapsed = false;
39882
40279
  return formatExpandedSectionOutput(targetSection);
39883
40280
  }
39884
- function formatExpandedSectionOutput(section) {
39885
- const lines = [];
39886
- const subsectionInfo = section.subsections.length > 0 ? `, ${section.subsections.length} subsections` : "";
39887
- lines.push(`---`);
39888
- lines.push(`EXPANDED SECTION: ${section.heading} [${section.ref}]`);
39889
- lines.push(`Contains ${section.elementCount} elements${subsectionInfo}`);
39890
- lines.push(`---`);
39891
- lines.push("");
39892
- for (const el of section.elements) {
39893
- lines.push(formatElementLine(el, 0));
39894
- }
39895
- for (const subsection of section.subsections) {
39896
- lines.push("");
39897
- lines.push(formatSectionOutput(subsection, 0, false));
40281
+
40282
+ // ../browser-core/src/table-extractor.ts
40283
+ function extractTablesFromSnapshot(yamlOrResponse, options) {
40284
+ const yaml = yamlOrResponse.includes("Page Snapshot:") ? extractSnapshotYaml(yamlOrResponse) : yamlOrResponse;
40285
+ if (!yaml) return null;
40286
+ const elements = parseSnapshot(yaml);
40287
+ if (elements.length === 0) return null;
40288
+ const grids = [];
40289
+ const findGrids = (els) => {
40290
+ for (const el of els) {
40291
+ const grid = detectGrid(el);
40292
+ if (grid && grid.rows.length > 0) {
40293
+ grids.push({ grid, element: el });
40294
+ }
40295
+ if (!grid) {
40296
+ findGrids(el.children);
40297
+ }
40298
+ }
40299
+ };
40300
+ findGrids(elements);
40301
+ if (grids.length === 0) return null;
40302
+ let target;
40303
+ if (options?.ref) {
40304
+ const match = grids.find((g) => g.grid.ref === options.ref);
40305
+ if (!match) return null;
40306
+ target = match;
40307
+ } else {
40308
+ target = grids.reduce(
40309
+ (best, current) => current.grid.rows.length > best.grid.rows.length ? current : best
40310
+ );
39898
40311
  }
39899
- return lines.join("\n").trim();
40312
+ const { columns, rows } = gridInfoToRows(target.grid);
40313
+ if (columns.length === 0) return null;
40314
+ const pagination = detectPaginationInfo(elements, target.grid.ref);
40315
+ return {
40316
+ columns,
40317
+ rows,
40318
+ gridRef: target.grid.ref,
40319
+ pagination: pagination ?? void 0
40320
+ };
39900
40321
  }
39901
- function formatGridOutput(grid, indent) {
39902
- const indentStr = " ".repeat(indent);
39903
- const lines = [];
39904
- const colInfo = grid.columns.length > 0 ? `${grid.columns.length} columns` : "unknown columns";
39905
- lines.push(`${indentStr}GRID [${grid.ref}]: ${grid.totalRows} rows, ${colInfo}`);
39906
- const MAX_DISPLAY_COLUMNS = 10;
39907
- if (grid.columns.length > 0) {
39908
- const displayColumns = grid.columns.slice(0, MAX_DISPLAY_COLUMNS);
39909
- const colsWithRefs = displayColumns.map((c2) => `${c2.name} [${c2.ref}]`).join(", ");
39910
- const moreColsNote = grid.columns.length > MAX_DISPLAY_COLUMNS ? ` (+${grid.columns.length - MAX_DISPLAY_COLUMNS} more)` : "";
39911
- lines.push(`${indentStr} Columns: ${colsWithRefs}${moreColsNote}`);
40322
+ function gridInfoToRows(grid) {
40323
+ const columns = grid.columns.map((c2) => c2.name);
40324
+ if (columns.length === 0) return { columns: [], rows: [] };
40325
+ const columnIndex = /* @__PURE__ */ new Map();
40326
+ for (let i = 0; i < columns.length; i++) {
40327
+ columnIndex.set(columns[i], i);
39912
40328
  }
39913
- lines.push("");
39914
- const knownColumns = grid.columns.length > 0 ? new Set(grid.columns.map((c2) => c2.name)) : void 0;
39915
- const MAX_GRID_ROWS = 30;
39916
- const displayRows = grid.rows.slice(0, MAX_GRID_ROWS);
39917
- for (const row of displayRows) {
39918
- lines.push(formatGridRow(row, indent + 1, knownColumns));
40329
+ const rows = [];
40330
+ for (const row of grid.rows) {
40331
+ const values = new Array(columns.length).fill("");
40332
+ for (const cell of row.cells) {
40333
+ const idx = columnIndex.get(cell.column);
40334
+ if (idx !== void 0) {
40335
+ values[idx] = cleanCellValue(cell.value, cell.gridcellLabel);
40336
+ }
40337
+ }
40338
+ rows.push(values);
39919
40339
  }
39920
- if (grid.rows.length > MAX_GRID_ROWS) {
39921
- lines.push(`${indentStr} ... and ${grid.rows.length - MAX_GRID_ROWS} more rows (use expand="${grid.ref}" to see all)`);
40340
+ return { columns, rows };
40341
+ }
40342
+ var SVG_ARTIFACT_PATTERN = /^Styled\(svg\)$/i;
40343
+ function cleanCellValue(value, gridcellLabel) {
40344
+ if (!value || SVG_ARTIFACT_PATTERN.test(value)) {
40345
+ if (gridcellLabel) {
40346
+ return gridcellLabel.replace(/\s*Styled\(svg\)\s*/gi, "").trim();
40347
+ }
40348
+ return "";
39922
40349
  }
39923
- return lines.join("\n");
40350
+ return value;
39924
40351
  }
39925
- function formatGridRow(row, indent, knownColumns) {
39926
- const indentStr = " ".repeat(indent);
39927
- if (row.expandedContent) {
39928
- const parts = [];
39929
- if (row.expandedContent.labels.length > 0) {
39930
- parts.push(row.expandedContent.labels.join(", "));
40352
+ function formatCSVWithFrontmatter(columns, rows, _options) {
40353
+ const lines = [];
40354
+ lines.push(columns.map(escapeCSVField).join(","));
40355
+ for (const row of rows) {
40356
+ lines.push(row.map(escapeCSVField).join(","));
40357
+ }
40358
+ return lines.join("\n") + "\n";
40359
+ }
40360
+ function appendRowsToCSV(existingCSV, newRows, columns, _options) {
40361
+ const existingLines = existingCSV.split("\n").filter((l) => l.trim() !== "");
40362
+ const dataLines = [];
40363
+ let headerLine = "";
40364
+ for (const line of existingLines) {
40365
+ if (line.startsWith("#")) continue;
40366
+ if (!headerLine) {
40367
+ headerLine = line;
40368
+ continue;
39931
40369
  }
39932
- for (const btn of row.expandedContent.buttons) {
39933
- parts.push(`${btn.label} [${btn.ref}]`);
40370
+ dataLines.push(line);
40371
+ }
40372
+ const existingRowSet = new Set(dataLines);
40373
+ const newRowStrings = newRows.map((row) => row.map(escapeCSVField).join(","));
40374
+ const addedRows = [];
40375
+ for (const rowStr of newRowStrings) {
40376
+ if (!existingRowSet.has(rowStr)) {
40377
+ addedRows.push(rowStr);
40378
+ existingRowSet.add(rowStr);
39934
40379
  }
39935
- return `${indentStr} \u21B3 Expanded [${row.ref}]: ${parts.join(" | ")}`;
39936
40380
  }
39937
- const marker = row.isSelected ? "ACTIVE \u2192 Row" : "Row";
39938
- const actionsPrefix = row.rowActions?.length ? row.rowActions.map((a) => `[\u25B6 ${a.ref}]`).join(" ") + " " : "";
39939
- const cellsWithValue = row.cells.filter(
39940
- (c2) => c2.hasValue && (!knownColumns || knownColumns.has(c2.column))
39941
- );
39942
- if (cellsWithValue.length > 0) {
39943
- const cellStrs = cellsWithValue.map((c2) => {
39944
- if (c2.role === "checkbox") {
39945
- const icon = c2.value === "checked" ? "\u2611" : "\u2610";
39946
- return `${icon} [${c2.ref}]`;
39947
- }
39948
- if (c2.column === c2.value) {
39949
- return `${c2.column} [${c2.ref}]`;
40381
+ const allDataLines = [...dataLines, ...addedRows];
40382
+ const result = [headerLine || columns.map(escapeCSVField).join(","), ...allDataLines];
40383
+ return result.join("\n") + "\n";
40384
+ }
40385
+ function detectPaginationInfo(elements, _gridRef) {
40386
+ const paginationPattern = /(\d+)\s*[-–]\s*(\d+)\s+of\s+([\d,]+)/i;
40387
+ const searchText = (els) => {
40388
+ for (const el of els) {
40389
+ const text = el.text || "";
40390
+ const match = text.match(paginationPattern);
40391
+ if (match) {
40392
+ const total = parseInt(match[3].replace(/,/g, ""), 10);
40393
+ return { showing: `${match[1]}-${match[2]}`, total };
39950
40394
  }
39951
- return `${c2.column} [${c2.ref}]: ${c2.value}`;
39952
- });
39953
- return `${indentStr}${actionsPrefix}${marker} [${row.ref}]: ${cellStrs.join(" | ")}`;
39954
- } else {
39955
- return `${indentStr}${actionsPrefix}${marker} [${row.ref}]: (empty row)`;
40395
+ const childResult = searchText(el.children);
40396
+ if (childResult) return childResult;
40397
+ }
40398
+ return null;
40399
+ };
40400
+ return searchText(elements);
40401
+ }
40402
+ function escapeCSVField(field) {
40403
+ if (!field) return "";
40404
+ if (field.includes(",") || field.includes('"') || field.includes("\n")) {
40405
+ return `"${field.replace(/"/g, '""')}"`;
39956
40406
  }
40407
+ return field;
39957
40408
  }
39958
40409
 
39959
40410
  // ../browser-core/src/snapshot-diff.ts
@@ -41898,6 +42349,136 @@ function getBrowserToolDefinitionsWithLifecycle() {
41898
42349
  ];
41899
42350
  }
41900
42351
 
42352
+ // ../browser-core/src/tools/dispatcher.ts
42353
+ async function dispatchBrowserTool(executor, toolName, args) {
42354
+ switch (toolName) {
42355
+ case "browser_navigate":
42356
+ return executor.navigate(args.url);
42357
+ case "browser_navigate_back":
42358
+ return executor.navigateBack();
42359
+ case "browser_snapshot":
42360
+ return executor.snapshot({
42361
+ mode: args.mode,
42362
+ expand: args.expand,
42363
+ search: args.search,
42364
+ showCoordinateGrid: args.showCoordinateGrid,
42365
+ probeAt: args.probeAt
42366
+ });
42367
+ case "browser_screenshot":
42368
+ return executor.screenshot({
42369
+ fullPage: args.fullPage,
42370
+ element: args.element,
42371
+ ref: args.ref,
42372
+ label: args.label,
42373
+ returnImage: args.returnImage
42374
+ });
42375
+ case "browser_evaluate":
42376
+ return executor.evaluate({
42377
+ expression: args.function,
42378
+ element: args.element,
42379
+ ref: args.ref
42380
+ });
42381
+ case "browser_console_messages":
42382
+ return executor.consoleMessages({
42383
+ onlyErrors: args.onlyErrors
42384
+ });
42385
+ case "browser_network_requests":
42386
+ return executor.networkRequests();
42387
+ case "browser_click":
42388
+ return executor.click({
42389
+ ref: args.ref,
42390
+ x: args.x,
42391
+ y: args.y,
42392
+ element: args.element,
42393
+ doubleClick: args.doubleClick,
42394
+ button: args.button,
42395
+ modifiers: args.modifiers
42396
+ });
42397
+ case "browser_hover":
42398
+ return executor.hover({
42399
+ ref: args.ref,
42400
+ element: args.element
42401
+ });
42402
+ case "browser_drag":
42403
+ return executor.drag({
42404
+ startRef: args.startRef,
42405
+ endRef: args.endRef,
42406
+ startElement: args.startElement,
42407
+ endElement: args.endElement,
42408
+ startX: args.startX,
42409
+ startY: args.startY,
42410
+ endX: args.endX,
42411
+ endY: args.endY
42412
+ });
42413
+ case "browser_type":
42414
+ return executor.type({
42415
+ ref: args.ref,
42416
+ text: args.text,
42417
+ element: args.element,
42418
+ submit: args.submit,
42419
+ delay: args.delay
42420
+ });
42421
+ case "browser_press_key":
42422
+ return executor.pressKey(args.key);
42423
+ case "browser_fill_form":
42424
+ return executor.fillForm(
42425
+ args.fields
42426
+ );
42427
+ case "browser_select_option":
42428
+ return executor.selectOption({
42429
+ ref: args.ref,
42430
+ value: args.value,
42431
+ element: args.element
42432
+ });
42433
+ case "browser_file_upload":
42434
+ return executor.fileUpload(args.paths);
42435
+ case "browser_scroll":
42436
+ return executor.scroll({
42437
+ direction: args.direction,
42438
+ amount: args.amount,
42439
+ withinRef: args.withinRef,
42440
+ toRef: args.toRef,
42441
+ x: args.x,
42442
+ y: args.y
42443
+ });
42444
+ case "browser_handle_dialog":
42445
+ return executor.handleDialog({
42446
+ action: args.action,
42447
+ promptText: args.promptText
42448
+ });
42449
+ case "browser_dismiss_overlay":
42450
+ return executor.dismissOverlay({
42451
+ preferredStrategy: args.preferredStrategy
42452
+ });
42453
+ case "browser_wait_for":
42454
+ return executor.waitFor({
42455
+ timeSec: args.time,
42456
+ text: args.text,
42457
+ textGone: args.textGone,
42458
+ selector: args.selector,
42459
+ state: args.state,
42460
+ timeout: args.timeout
42461
+ });
42462
+ case "browser_close":
42463
+ return executor.close();
42464
+ case "browser_resize":
42465
+ return executor.resize(args.width, args.height);
42466
+ case "browser_tabs":
42467
+ return executor.tabs({
42468
+ action: args.action,
42469
+ index: args.index
42470
+ });
42471
+ case "browser_list_downloads":
42472
+ return executor.listDownloads();
42473
+ case "browser_read_download":
42474
+ return executor.readDownload(args.downloadId);
42475
+ case "browser_pdf_read":
42476
+ return executor.fetchPdfText(args.url);
42477
+ default:
42478
+ throw new Error(`Unknown browser tool: ${toolName}`);
42479
+ }
42480
+ }
42481
+
41901
42482
  // ../browser-core/src/playwright-client.ts
41902
42483
  import {
41903
42484
  chromium as playwrightChromium
@@ -43018,6 +43599,29 @@ function appendAttributeIfMissing(line, key, value) {
43018
43599
  const baseLine = hasTrailingColon ? line.replace(/:\s*$/, "") : line;
43019
43600
  return `${baseLine} [${key}=${value}]${hasTrailingColon ? ":" : ""}`;
43020
43601
  }
43602
+ function findLabelFromYamlSiblings(elements, targetRef) {
43603
+ const findParent = (els, parent2) => {
43604
+ for (const el of els) {
43605
+ if (el.ref === targetRef) return parent2;
43606
+ const found = findParent(el.children, el);
43607
+ if (found) return found;
43608
+ }
43609
+ return null;
43610
+ };
43611
+ const parent = findParent(elements, null);
43612
+ if (!parent) return void 0;
43613
+ const targetIdx = parent.children.findIndex((c2) => c2.ref === targetRef);
43614
+ if (targetIdx < 0) return void 0;
43615
+ for (let i = targetIdx - 1; i >= 0; i--) {
43616
+ const sib = parent.children[i];
43617
+ const sibText = sib.text?.trim();
43618
+ if (sibText && sibText.length > 0 && sibText.length < 80 && /[a-zA-Z0-9]/.test(sibText)) {
43619
+ if (FORM_FIELD_ROLES.has(sib.role)) continue;
43620
+ return sibText;
43621
+ }
43622
+ }
43623
+ return void 0;
43624
+ }
43021
43625
  async function augmentUnlabeledElements(page, yaml, logger2) {
43022
43626
  try {
43023
43627
  const elements = parseSnapshot(yaml);
@@ -43323,6 +43927,9 @@ async function augmentGridCellValues(page, yaml, logger2) {
43323
43927
  }
43324
43928
  }
43325
43929
  var FORM_FIELD_ROLES = /* @__PURE__ */ new Set(["textbox", "combobox", "searchbox"]);
43930
+ function splitCamelCase(name) {
43931
+ return name.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/[_-]/g, " ").replace(/\s+/g, " ").trim();
43932
+ }
43326
43933
  async function augmentFormFieldLabels(page, yaml, logger2) {
43327
43934
  try {
43328
43935
  const elements = parseSnapshot(yaml);
@@ -43340,16 +43947,30 @@ async function augmentFormFieldLabels(page, yaml, logger2) {
43340
43947
  walkForFields(el);
43341
43948
  }
43342
43949
  if (formFieldRefs.length === 0) return yaml;
43343
- logger2.debug("[DirectPlaywright] Augmenting form field labels", {
43950
+ const glyphFields = formFieldRefs.filter((f) => f.text && !/[a-zA-Z0-9]/.test(f.text));
43951
+ const displayNameIdx = yaml.indexOf("Display Name");
43952
+ const yamlAroundDisplayName = displayNameIdx >= 0 ? yaml.slice(Math.max(0, displayNameIdx - 50), displayNameIdx + 200) : "NOT FOUND";
43953
+ logger2.warn("[DirectPlaywright] augmentFormFieldLabels called", {
43344
43954
  count: formFieldRefs.length,
43345
- refs: formFieldRefs.slice(0, 10).map((f) => f.ref)
43955
+ glyphCount: glyphFields.length,
43956
+ allTexts: formFieldRefs.map((f) => `${f.ref}:${JSON.stringify(f.text)}`),
43957
+ hasDisplayName: yaml.includes("Display Name"),
43958
+ yamlHasGlyph: yaml.includes("\u268A"),
43959
+ yamlAroundDisplayName
43346
43960
  });
43347
43961
  let augmented = yaml;
43348
43962
  for (const { ref, role, text } of formFieldRefs) {
43349
43963
  try {
43964
+ const isGlyphField = text && !/[a-zA-Z0-9]/.test(text);
43965
+ if (isGlyphField) {
43966
+ logger2.info("[DirectPlaywright] Glyph-labeled field entering augmentation", { ref, role, text });
43967
+ }
43350
43968
  const locator = page.locator(`aria-ref=${ref}`);
43351
43969
  const count = await locator.count();
43352
- if (count === 0) continue;
43970
+ if (count === 0) {
43971
+ if (isGlyphField) logger2.info("[DirectPlaywright] Glyph field: locator count=0, skipping", { ref });
43972
+ continue;
43973
+ }
43353
43974
  const label = await locator.first().evaluate((el) => {
43354
43975
  if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement || el instanceof HTMLSelectElement) {
43355
43976
  const labels = el.labels;
@@ -43358,6 +43979,30 @@ async function augmentFormFieldLabels(page, yaml, logger2) {
43358
43979
  if (labelText) return labelText;
43359
43980
  }
43360
43981
  }
43982
+ {
43983
+ const precedingLabels = [];
43984
+ let prevEl = el.previousElementSibling;
43985
+ while (prevEl) {
43986
+ if (prevEl.tagName === "LABEL") {
43987
+ precedingLabels.unshift(prevEl);
43988
+ } else {
43989
+ break;
43990
+ }
43991
+ prevEl = prevEl.previousElementSibling;
43992
+ }
43993
+ if (precedingLabels.length > 1) {
43994
+ const selected = precedingLabels.find((l) => {
43995
+ const cls = l.className.toLowerCase();
43996
+ return cls.includes("selected") || cls.includes("active") || cls.includes("current") || cls.includes("checked");
43997
+ });
43998
+ if (selected?.textContent?.trim()) return selected.textContent.trim();
43999
+ const first = precedingLabels[0];
44000
+ if (first?.textContent?.trim()) return first.textContent.trim();
44001
+ } else if (precedingLabels.length === 1) {
44002
+ const labelText = precedingLabels[0].textContent?.trim();
44003
+ if (labelText) return labelText;
44004
+ }
44005
+ }
43361
44006
  const labelledBy = el.getAttribute("aria-labelledby");
43362
44007
  if (labelledBy) {
43363
44008
  const labelEl = document.getElementById(labelledBy);
@@ -43365,31 +44010,101 @@ async function augmentFormFieldLabels(page, yaml, logger2) {
43365
44010
  }
43366
44011
  const ariaLabel = el.getAttribute("aria-label");
43367
44012
  const placeholder = el.getAttribute("placeholder");
43368
- if (ariaLabel && ariaLabel !== placeholder) return ariaLabel;
43369
- let sibling = el.previousElementSibling;
44013
+ if (ariaLabel && ariaLabel !== placeholder && /[a-zA-Z0-9]/.test(ariaLabel)) return ariaLabel;
43370
44014
  let target = el;
43371
- for (let i = 0; i < 3 && target; i++) {
43372
- sibling = target.previousElementSibling;
43373
- if (sibling) {
44015
+ for (let depth = 0; depth < 3 && target; depth++) {
44016
+ let sibling = target.previousElementSibling;
44017
+ for (let sibCount = 0; sibling && sibCount < 3; sibCount++) {
43374
44018
  const sibText = sibling.textContent?.trim();
43375
44019
  if (sibText && sibText.length > 0 && sibText.length < 80 && !sibling.querySelector("input, textarea, select, button")) {
43376
44020
  return sibText;
43377
44021
  }
44022
+ if (sibling.querySelector("input, textarea, select, button")) break;
44023
+ sibling = sibling.previousElementSibling;
43378
44024
  }
43379
44025
  target = target.parentElement;
43380
44026
  }
43381
44027
  return void 0;
43382
44028
  });
44029
+ if (isGlyphField) {
44030
+ logger2.info("[DirectPlaywright] Glyph field: DOM evaluate result", { ref, label, text });
44031
+ }
43383
44032
  if (label && label !== text) {
43384
44033
  augmented = updateElementLineByRef(
43385
44034
  augmented,
43386
44035
  ref,
43387
44036
  (line) => injectElementLabel(line, role, label)
43388
44037
  );
44038
+ if (isGlyphField) {
44039
+ logger2.info("[DirectPlaywright] Glyph field: injected DOM label", { ref, label });
44040
+ }
44041
+ }
44042
+ const hasGlyphOnlyLabel = text && !/[a-zA-Z0-9]/.test(text);
44043
+ if (hasGlyphOnlyLabel && (!label || label === text || !/[a-zA-Z0-9]/.test(label))) {
44044
+ const yamlLabel = findLabelFromYamlSiblings(elements, ref);
44045
+ logger2.info("[DirectPlaywright] YAML sibling fallback for glyph field", {
44046
+ ref,
44047
+ text,
44048
+ label,
44049
+ yamlLabel
44050
+ });
44051
+ if (yamlLabel) {
44052
+ augmented = updateElementLineByRef(
44053
+ augmented,
44054
+ ref,
44055
+ (line) => injectElementLabel(line, role, yamlLabel)
44056
+ );
44057
+ }
43389
44058
  }
43390
44059
  } catch {
43391
44060
  }
43392
44061
  }
44062
+ try {
44063
+ const postElements = parseSnapshot(augmented);
44064
+ const labeledFields = [];
44065
+ const walkForLabeled = (el) => {
44066
+ if (FORM_FIELD_ROLES.has(el.role) && el.ref && el.text) {
44067
+ labeledFields.push({ ref: el.ref, role: el.role, label: el.text });
44068
+ }
44069
+ for (const child of el.children) walkForLabeled(child);
44070
+ };
44071
+ for (const el of postElements) walkForLabeled(el);
44072
+ const byLabel = /* @__PURE__ */ new Map();
44073
+ for (const f of labeledFields) {
44074
+ const existing = byLabel.get(f.label);
44075
+ if (existing) {
44076
+ existing.push({ ref: f.ref, role: f.role });
44077
+ } else {
44078
+ byLabel.set(f.label, [{ ref: f.ref, role: f.role }]);
44079
+ }
44080
+ }
44081
+ for (const [label, fields] of byLabel) {
44082
+ if (fields.length < 2) continue;
44083
+ for (const field of fields) {
44084
+ try {
44085
+ const locator = page.locator(`aria-ref=${field.ref}`);
44086
+ const count = await locator.count();
44087
+ if (count === 0) continue;
44088
+ const disambiguation = await locator.first().evaluate((el) => {
44089
+ const placeholder = el.getAttribute("placeholder") || void 0;
44090
+ const ariaLabel = el.getAttribute("aria-label") || void 0;
44091
+ const name = el.getAttribute("name") || void 0;
44092
+ return { placeholder, ariaLabel, name };
44093
+ });
44094
+ const disambig = disambiguation.placeholder || disambiguation.ariaLabel || (disambiguation.name ? splitCamelCase(disambiguation.name) : void 0);
44095
+ if (disambig && disambig !== label) {
44096
+ augmented = updateElementLineByRef(
44097
+ augmented,
44098
+ field.ref,
44099
+ (line) => injectElementLabel(line, field.role, disambig)
44100
+ );
44101
+ }
44102
+ } catch {
44103
+ }
44104
+ }
44105
+ }
44106
+ } catch {
44107
+ }
43393
44108
  return augmented;
43394
44109
  } catch (err) {
43395
44110
  logger2.debug("[DirectPlaywright] Form field label augmentation failed", {
@@ -43594,6 +44309,148 @@ async function augmentHiddenSelectOptions(page, yaml, logger2) {
43594
44309
  return yaml;
43595
44310
  }
43596
44311
  }
44312
+ async function enrichModalDialogElements(page, logger2) {
44313
+ try {
44314
+ const enrichedCount = await page.evaluate(() => {
44315
+ let count = 0;
44316
+ const MAX_ENRICHMENTS = 3;
44317
+ function shouldSkip(el) {
44318
+ if (el.getAttribute("role") === "dialog" || el.getAttribute("role") === "alertdialog") return true;
44319
+ if (el.getAttribute("data-enriched-dialog") === "true") return true;
44320
+ return false;
44321
+ }
44322
+ function isVisible(el) {
44323
+ const style = window.getComputedStyle(el);
44324
+ if (style.display === "none" || style.visibility === "hidden") return false;
44325
+ if (el instanceof HTMLElement && el.offsetWidth === 0 && el.offsetHeight === 0) return false;
44326
+ return true;
44327
+ }
44328
+ function enrichAsDialog(el) {
44329
+ if (count >= MAX_ENRICHMENTS) return;
44330
+ if (shouldSkip(el)) return;
44331
+ if (!isVisible(el)) return;
44332
+ el.setAttribute("role", "dialog");
44333
+ const heading = el.querySelector("h1, h2, h3, h4, h5, h6");
44334
+ if (heading?.textContent?.trim()) {
44335
+ el.setAttribute("aria-label", heading.textContent.trim());
44336
+ }
44337
+ el.setAttribute("data-enriched-dialog", "true");
44338
+ count++;
44339
+ }
44340
+ function hasInteractiveContent(el) {
44341
+ return el.querySelector('input, textarea, select, button, a[href], [role="button"], [role="link"], [role="textbox"], [role="combobox"]') !== null;
44342
+ }
44343
+ const CLASS_PATTERNS = [
44344
+ /\bmodal(?![-_]?(backdrop|mask|overlay|fade|bg|background))\b/i,
44345
+ /\bdialog\b/i,
44346
+ /\bpopup\b/i,
44347
+ /\blightbox\b/i,
44348
+ /\bdrawer\b/i
44349
+ ];
44350
+ const FRAMEWORK_SELECTORS = [
44351
+ ".cdk-overlay-pane",
44352
+ ".mat-dialog-container",
44353
+ ".mat-mdc-dialog-container",
44354
+ ".ui-dialog",
44355
+ ".modal-dialog",
44356
+ ".ant-modal-content",
44357
+ ".el-dialog",
44358
+ ".v-dialog",
44359
+ ".p-dialog"
44360
+ ];
44361
+ for (const selector of FRAMEWORK_SELECTORS) {
44362
+ if (count >= MAX_ENRICHMENTS) break;
44363
+ const els = document.querySelectorAll(selector);
44364
+ for (const el of els) {
44365
+ if (count >= MAX_ENRICHMENTS) break;
44366
+ if (hasInteractiveContent(el)) {
44367
+ enrichAsDialog(el);
44368
+ }
44369
+ }
44370
+ }
44371
+ if (count < MAX_ENRICHMENTS) {
44372
+ const allElements = document.querySelectorAll("*");
44373
+ for (const el of allElements) {
44374
+ if (count >= MAX_ENRICHMENTS) break;
44375
+ const className = el.className;
44376
+ if (typeof className !== "string") continue;
44377
+ if (CLASS_PATTERNS.some((pattern) => pattern.test(className))) {
44378
+ if (hasInteractiveContent(el)) {
44379
+ enrichAsDialog(el);
44380
+ }
44381
+ }
44382
+ }
44383
+ }
44384
+ if (count < MAX_ENRICHMENTS) {
44385
+ const allFixed = document.querySelectorAll("*");
44386
+ for (const el of allFixed) {
44387
+ if (count >= MAX_ENRICHMENTS) break;
44388
+ if (!(el instanceof HTMLElement)) continue;
44389
+ const style = window.getComputedStyle(el);
44390
+ if (style.position !== "fixed" && style.position !== "absolute") continue;
44391
+ const rect = el.getBoundingClientRect();
44392
+ const viewportW = window.innerWidth;
44393
+ const viewportH = window.innerHeight;
44394
+ const coversViewport = rect.width >= viewportW * 0.8 && rect.height >= viewportH * 0.8;
44395
+ if (!coversViewport) continue;
44396
+ const bg = style.backgroundColor;
44397
+ const opacity = parseFloat(style.opacity);
44398
+ const isSemiTransparent = bg.includes("rgba") && !bg.includes("rgba(0, 0, 0, 0)") && bg.match(/,\s*([\d.]+)\)/) && parseFloat(bg.match(/,\s*([\d.]+)\)/)?.[1] || "1") < 1 || opacity < 1 && opacity > 0;
44399
+ if (!isSemiTransparent) continue;
44400
+ const nextSib = el.nextElementSibling;
44401
+ if (nextSib && nextSib instanceof HTMLElement && isVisible(nextSib) && hasInteractiveContent(nextSib)) {
44402
+ enrichAsDialog(nextSib);
44403
+ continue;
44404
+ }
44405
+ if (el.parentElement) {
44406
+ const backdropZ = parseInt(style.zIndex) || 0;
44407
+ for (const sibling of el.parentElement.children) {
44408
+ if (sibling === el || !(sibling instanceof HTMLElement)) continue;
44409
+ const sibStyle = window.getComputedStyle(sibling);
44410
+ const sibZ = parseInt(sibStyle.zIndex) || 0;
44411
+ if (sibZ > backdropZ && isVisible(sibling) && hasInteractiveContent(sibling)) {
44412
+ enrichAsDialog(sibling);
44413
+ break;
44414
+ }
44415
+ }
44416
+ }
44417
+ }
44418
+ }
44419
+ if (count < MAX_ENRICHMENTS) {
44420
+ const allElements = document.querySelectorAll("*");
44421
+ for (const el of allElements) {
44422
+ if (count >= MAX_ENRICHMENTS) break;
44423
+ if (!(el instanceof HTMLElement)) continue;
44424
+ if (el.getAttribute("data-enriched-dialog") === "true") continue;
44425
+ if (el.getAttribute("role") === "dialog" || el.getAttribute("role") === "alertdialog") continue;
44426
+ const style = window.getComputedStyle(el);
44427
+ if (style.position !== "fixed" && style.position !== "absolute") continue;
44428
+ const zIndex = parseInt(style.zIndex) || 0;
44429
+ if (zIndex <= 100) continue;
44430
+ if (!isVisible(el)) continue;
44431
+ const rect = el.getBoundingClientRect();
44432
+ if (rect.width < 200 || rect.height < 200) continue;
44433
+ const viewportW = window.innerWidth;
44434
+ const viewportH = window.innerHeight;
44435
+ if (rect.width >= viewportW * 0.95 && rect.height >= viewportH * 0.95) continue;
44436
+ if (hasInteractiveContent(el)) {
44437
+ enrichAsDialog(el);
44438
+ }
44439
+ }
44440
+ }
44441
+ return count;
44442
+ });
44443
+ if (enrichedCount > 0) {
44444
+ logger2.debug("[DirectPlaywright] Enriched modal dialog elements", {
44445
+ count: enrichedCount
44446
+ });
44447
+ }
44448
+ } catch (err) {
44449
+ logger2.debug("[DirectPlaywright] Modal dialog enrichment failed", {
44450
+ error: errorMessage2(err)
44451
+ });
44452
+ }
44453
+ }
43597
44454
  async function enrichInteractiveSVGElements(page, logger2) {
43598
44455
  try {
43599
44456
  const enrichedCount = await page.evaluate(() => {
@@ -43989,7 +44846,8 @@ var PlaywrightClient = class _PlaywrightClient {
43989
44846
  extensions,
43990
44847
  highlightInteractions,
43991
44848
  cursorOverlay,
43992
- extraHTTPHeaders
44849
+ extraHTTPHeaders,
44850
+ httpCredentials
43993
44851
  } = options;
43994
44852
  const resolvedBrowserMode = browserMode ?? "headless";
43995
44853
  const resolvedViewport = viewport ?? DEFAULT_VIEWPORT;
@@ -44002,7 +44860,8 @@ var PlaywrightClient = class _PlaywrightClient {
44002
44860
  extensions,
44003
44861
  highlightInteractions,
44004
44862
  cursorOverlay,
44005
- extraHTTPHeaders
44863
+ extraHTTPHeaders,
44864
+ httpCredentials
44006
44865
  };
44007
44866
  this.storageStatePath = storageStatePath;
44008
44867
  this.pageClosedIntentionally = false;
@@ -44075,6 +44934,9 @@ var PlaywrightClient = class _PlaywrightClient {
44075
44934
  if (extraHTTPHeaders && Object.keys(extraHTTPHeaders).length > 0) {
44076
44935
  contextOptions.extraHTTPHeaders = extraHTTPHeaders;
44077
44936
  }
44937
+ if (httpCredentials) {
44938
+ contextOptions.httpCredentials = httpCredentials;
44939
+ }
44078
44940
  this.logger.info("[DirectPlaywright] Using pooled browser via lease", {
44079
44941
  leaseId: this.browserLease.leaseId,
44080
44942
  browserId: this.browserLease.browserId
@@ -44102,7 +44964,8 @@ var PlaywrightClient = class _PlaywrightClient {
44102
44964
  recordVideo,
44103
44965
  stealth,
44104
44966
  extensions,
44105
- extraHTTPHeaders
44967
+ extraHTTPHeaders,
44968
+ httpCredentials
44106
44969
  }),
44107
44970
  {
44108
44971
  logger: this.logger,
@@ -44121,6 +44984,9 @@ var PlaywrightClient = class _PlaywrightClient {
44121
44984
  const initialPageId = this._assignPageId(this.page);
44122
44985
  this._pageCreationTimes.set(initialPageId, now);
44123
44986
  this._tabFocusLog.push({ pageIndex: initialPageId, timestamp: now });
44987
+ if (storageStatePath) {
44988
+ await this.restoreIndexedDBFromStorageState(storageStatePath);
44989
+ }
44124
44990
  this.logger.info("[DirectPlaywright] Browser connected successfully", {
44125
44991
  browserMode: resolvedBrowserMode,
44126
44992
  stealthEnabled: this.stealthEnabled,
@@ -44200,6 +45066,13 @@ var PlaywrightClient = class _PlaywrightClient {
44200
45066
  headerNames: Object.keys(mergedHeaders)
44201
45067
  });
44202
45068
  }
45069
+ const resolvedHttpCredentials = options.httpCredentials ?? this.lastConnectOptions?.httpCredentials;
45070
+ if (resolvedHttpCredentials) {
45071
+ contextOptions.httpCredentials = resolvedHttpCredentials;
45072
+ if (this.lastConnectOptions) {
45073
+ this.lastConnectOptions.httpCredentials = resolvedHttpCredentials;
45074
+ }
45075
+ }
44203
45076
  if (nextStorageStatePath) {
44204
45077
  try {
44205
45078
  await fs4.access(nextStorageStatePath);
@@ -44251,6 +45124,9 @@ var PlaywrightClient = class _PlaywrightClient {
44251
45124
  const swapPageId = this._assignPageId(this.page);
44252
45125
  this._pageCreationTimes.set(swapPageId, swapNow);
44253
45126
  this._tabFocusLog.push({ pageIndex: swapPageId, timestamp: swapNow });
45127
+ if (nextStorageStatePath) {
45128
+ await this.restoreIndexedDBFromStorageState(nextStorageStatePath);
45129
+ }
44254
45130
  if (wasScreencasting && this.page && this.screencastHandler) {
44255
45131
  await this.startScreencast(this.screencastHandler);
44256
45132
  }
@@ -44269,7 +45145,8 @@ var PlaywrightClient = class _PlaywrightClient {
44269
45145
  recordVideo,
44270
45146
  stealth,
44271
45147
  extensions,
44272
- extraHTTPHeaders
45148
+ extraHTTPHeaders,
45149
+ httpCredentials
44273
45150
  } = options;
44274
45151
  const resolvedViewport = viewport ?? DEFAULT_VIEWPORT;
44275
45152
  const useStealthMode = stealth === true || typeof stealth === "object" && stealth.enabled;
@@ -44300,6 +45177,10 @@ var PlaywrightClient = class _PlaywrightClient {
44300
45177
  headerNames: Object.keys(extraHTTPHeaders)
44301
45178
  });
44302
45179
  }
45180
+ if (httpCredentials) {
45181
+ contextOptions.httpCredentials = httpCredentials;
45182
+ this.logger.info("[DirectPlaywright] HTTP Basic Authentication configured");
45183
+ }
44303
45184
  if (storageStatePath) {
44304
45185
  try {
44305
45186
  await fs4.access(storageStatePath);
@@ -44365,6 +45246,52 @@ var PlaywrightClient = class _PlaywrightClient {
44365
45246
  );
44366
45247
  return { browser, context };
44367
45248
  }
45249
+ /**
45250
+ * Read the storage state file, check for IndexedDB data, and restore it.
45251
+ * IndexedDB is origin-scoped, so we navigate to the appropriate origin
45252
+ * before writing data, then navigate back.
45253
+ */
45254
+ async restoreIndexedDBFromStorageState(storageStatePath) {
45255
+ try {
45256
+ const raw = await fs4.readFile(storageStatePath, "utf-8");
45257
+ const parsed = JSON.parse(raw);
45258
+ if (!parsed.indexedDB || !parsed.indexedDB.databases || parsed.indexedDB.databases.length === 0) {
45259
+ return;
45260
+ }
45261
+ const page = this.page;
45262
+ if (!page || page.isClosed()) return;
45263
+ let targetOrigin = null;
45264
+ if (parsed.origins && parsed.origins.length > 0) {
45265
+ targetOrigin = parsed.origins[0].origin;
45266
+ }
45267
+ if (!targetOrigin) {
45268
+ const cookies = parsed.cookies;
45269
+ if (cookies && cookies.length > 0) {
45270
+ const domain = cookies[0].domain.replace(/^\./, "");
45271
+ targetOrigin = `https://${domain}`;
45272
+ }
45273
+ }
45274
+ if (!targetOrigin) {
45275
+ this.logger.debug("[DirectPlaywright] No origin found for IndexedDB restoration, skipping");
45276
+ return;
45277
+ }
45278
+ this.logger.debug("[DirectPlaywright] Restoring IndexedDB data", {
45279
+ targetOrigin,
45280
+ databaseCount: parsed.indexedDB.databases.length,
45281
+ databaseNames: parsed.indexedDB.databases.map((db) => db.name)
45282
+ });
45283
+ await page.goto(targetOrigin, { waitUntil: "commit", timeout: 15e3 });
45284
+ await restoreIndexedDB(page, parsed.indexedDB);
45285
+ this.logger.info("[DirectPlaywright] IndexedDB data restored successfully", {
45286
+ databaseCount: parsed.indexedDB.databases.length
45287
+ });
45288
+ } catch (err) {
45289
+ this.logger.warn("[DirectPlaywright] Failed to restore IndexedDB from storage state", {
45290
+ error: err instanceof Error ? err.message : String(err),
45291
+ storageStatePath
45292
+ });
45293
+ }
45294
+ }
44368
45295
  /**
44369
45296
  * Resolve a working Chromium executable path, handling version mismatches
44370
45297
  * between the Playwright version bundled in this package and the browser
@@ -45586,7 +46513,25 @@ Use coordinate-based clicking (x, y) to interact with elements visible in the sc
45586
46513
  this.logger.debug("[DirectPlaywright] Getting storage state", {
45587
46514
  method: opts?.method ?? "playwright"
45588
46515
  });
45589
- return await context.storageState();
46516
+ const playwrightState = await context.storageState();
46517
+ try {
46518
+ const page = await this.getPage();
46519
+ const indexedDB2 = await extractIndexedDB(page);
46520
+ if (indexedDB2) {
46521
+ this.logger.info("[DirectPlaywright] Extracted IndexedDB data", {
46522
+ databaseCount: indexedDB2.databases.length,
46523
+ databaseNames: indexedDB2.databases.map((db) => db.name)
46524
+ });
46525
+ return { ...playwrightState, indexedDB: indexedDB2 };
46526
+ } else {
46527
+ this.logger.debug("[DirectPlaywright] No IndexedDB databases found on current page");
46528
+ }
46529
+ } catch (err) {
46530
+ this.logger.warn("[DirectPlaywright] Failed to extract IndexedDB, saving without it", {
46531
+ error: err instanceof Error ? err.message : String(err)
46532
+ });
46533
+ }
46534
+ return playwrightState;
45590
46535
  }
45591
46536
  async getCurrentUrl(_opts) {
45592
46537
  return (await this.getPage()).url();
@@ -45833,6 +46778,7 @@ Use coordinate-based clicking (x, y) to interact with elements visible in the sc
45833
46778
  }
45834
46779
  await this.enrichInteractiveSVGElements(page);
45835
46780
  await this.enrichHiddenClickableElements(page);
46781
+ await this.enrichModalDialogElements(page);
45836
46782
  let snapshot;
45837
46783
  const maxRetries = 3;
45838
46784
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
@@ -45961,6 +46907,13 @@ Use coordinate-based clicking (x, y) to interact with elements visible in the sc
45961
46907
  async enrichHiddenClickableElements(page) {
45962
46908
  await enrichHiddenClickableElements(page, this.logger);
45963
46909
  }
46910
+ /**
46911
+ * Enrich positioned overlays that look like modals but lack `role="dialog"`.
46912
+ * Injects `role="dialog"` and `aria-label` so the agent recognizes them.
46913
+ */
46914
+ async enrichModalDialogElements(page) {
46915
+ await enrichModalDialogElements(page, this.logger);
46916
+ }
45964
46917
  /**
45965
46918
  * Resolve a ref to a Playwright locator using the internal aria-ref selector.
45966
46919
  * This is the same mechanism MCP uses to resolve refs.
@@ -46993,6 +47946,8 @@ export {
46993
47946
  CursorOverlay,
46994
47947
  setCdpScreencastLogger,
46995
47948
  CdpScreencastManager,
47949
+ extractIndexedDB,
47950
+ restoreIndexedDB,
46996
47951
  setSnapshotAnalyzerLogger,
46997
47952
  extractSnapshotYaml,
46998
47953
  parseSnapshot,
@@ -47007,22 +47962,27 @@ export {
47007
47962
  normalizeIconLabelText,
47008
47963
  normalizeSearchText,
47009
47964
  extractDataValue,
47965
+ extractCellText,
47966
+ extractGridCell,
47967
+ reconcileCellColumn,
47968
+ detectGrid,
47969
+ formatGridOutput,
47970
+ formatGridRow,
47010
47971
  findRelevanceScope,
47011
47972
  searchElements,
47012
47973
  populateSearchContext,
47013
47974
  sortMatchesByContext,
47014
- detectGrid,
47015
- extractCellText,
47016
- extractGridCell,
47017
- reconcileCellColumn,
47018
- findActiveElement,
47019
47975
  buildSectionTree,
47020
47976
  markMatchingSections,
47977
+ findActiveElement,
47021
47978
  formatSemanticSnapshot,
47022
47979
  expandSection,
47023
47980
  expandSectionOnly,
47024
- formatGridOutput,
47025
- formatGridRow,
47981
+ extractTablesFromSnapshot,
47982
+ gridInfoToRows,
47983
+ formatCSVWithFrontmatter,
47984
+ appendRowsToCSV,
47985
+ detectPaginationInfo,
47026
47986
  captureSnapshotState,
47027
47987
  compareSnapshots,
47028
47988
  hasDiffChanges,
@@ -47042,6 +48002,7 @@ export {
47042
48002
  getBrowserToolDefinitions,
47043
48003
  BROWSER_LIFECYCLE_TOOLS,
47044
48004
  getBrowserToolDefinitionsWithLifecycle,
48005
+ dispatchBrowserTool,
47045
48006
  extractSemanticHint,
47046
48007
  PlaywrightClient
47047
48008
  };
@@ -47324,4 +48285,4 @@ playwright-extra/dist/index.esm.js:
47324
48285
  * @license MIT
47325
48286
  *)
47326
48287
  */
47327
- //# sourceMappingURL=chunk-FK3EZADZ.js.map
48288
+ //# sourceMappingURL=chunk-XGO62PO2.js.map