haltija 1.3.2 → 1.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -674,7 +674,7 @@ var injectorCode = `
674
674
  `;
675
675
 
676
676
  // src/version.ts
677
- var VERSION = "1.3.2";
677
+ var VERSION = "1.3.4";
678
678
 
679
679
  // src/embedded-assets.ts
680
680
  var APP_MD = `# Haltija App
@@ -1086,6 +1086,8 @@ Use ancestors:true to see parent elements when inspecting deep elements.
1086
1086
  | \`pierceFrames\` | boolean,null | Pierce same-origin iframes (default true) |
1087
1087
  | \`compact\` | boolean,null | Minimal output (default false) |
1088
1088
  | \`ancestors\` | boolean,null | Include ancestor path from root (default false) |
1089
+ | \`includeBox\` | boolean,null | Include each node's bounding box { x, y, w, h, visible } (default false) |
1090
+ | \`mode\` | string,null | 'actionable' returns a page action summary (url, title, headings, buttons, links, inputs, selects) instead of the DOM tree |
1089
1091
  | \`window\` | string,null | Target window ID |
1090
1092
 
1091
1093
  **Examples:**
@@ -2483,7 +2485,14 @@ Great for debugging - check for errors after actions fail.
2483
2485
  Run arbitrary JavaScript in the browser context. Returns the result.
2484
2486
 
2485
2487
  The code runs in the page's context with access to window, document, etc.
2486
- Return values are JSON-serialized. Promises are awaited.
2488
+ Return values are JSON-serialized.
2489
+
2490
+ Async code is supported: a returned Promise is awaited, and top-level \`await\`
2491
+ works. Multi-statement code needs an explicit \`return\` to produce a value.
2492
+
2493
+ document.title -> the title
2494
+ await fetch('/api').then(r => r.json()) -> the parsed body
2495
+ const r = await fetch('/api'); return r.status -> the status code
2487
2496
 
2488
2497
  **Parameters:**
2489
2498
 
@@ -3019,7 +3028,7 @@ var COMPONENT_JS = `(() => {
3019
3028
  });
3020
3029
 
3021
3030
  // src/version.ts
3022
- var VERSION = "1.3.2";
3031
+ var VERSION = "1.3.4";
3023
3032
 
3024
3033
  // src/text-selector.ts
3025
3034
  var TEXT_PSEUDO_RE = /:(?:text-is|has-text|text)\\(/;
@@ -3571,13 +3580,13 @@ var COMPONENT_JS = `(() => {
3571
3580
  }
3572
3581
  function resolveRefOrSelector(ref, selector) {
3573
3582
  if (ref) {
3574
- const result2 = refRegistry.resolveWithStatus(ref);
3575
- if (result2.element) {
3583
+ const result = refRegistry.resolveWithStatus(ref);
3584
+ if (result.element) {
3576
3585
  stats.refsResolved++;
3577
- return { element: result2.element, targetDesc: \`@\${ref}\` };
3586
+ return { element: result.element, targetDesc: \`@\${ref}\` };
3578
3587
  }
3579
3588
  stats.refsStale++;
3580
- const errorMsg = result2.status === "never_assigned" ? \`Ref @\${ref} was never assigned (highest ref is @\${refRegistry.getStats().highWaterMark})\` : result2.status === "removed_from_dom" ? \`Ref @\${ref} points to an element that was removed from the DOM (try refreshing /tree)\` : \`Ref @\${ref} is stale - element was garbage collected (try refreshing /tree)\`;
3589
+ const errorMsg = result.status === "never_assigned" ? \`Ref @\${ref} was never assigned (highest ref is @\${refRegistry.getStats().highWaterMark})\` : result.status === "removed_from_dom" ? \`Ref @\${ref} points to an element that was removed from the DOM (try refreshing /tree)\` : \`Ref @\${ref} is stale - element was garbage collected (try refreshing /tree)\`;
3581
3590
  return { element: null, targetDesc: \`@\${ref}\`, error: errorMsg };
3582
3591
  }
3583
3592
  if (selector) {
@@ -3588,13 +3597,13 @@ var COMPONENT_JS = `(() => {
3588
3597
  }
3589
3598
  function resolveRefOrSelectorAll(ref, selector) {
3590
3599
  if (ref) {
3591
- const result2 = refRegistry.resolveWithStatus(ref);
3592
- if (result2.element) {
3600
+ const result = refRegistry.resolveWithStatus(ref);
3601
+ if (result.element) {
3593
3602
  stats.refsResolved++;
3594
- return { elements: [result2.element], targetDesc: \`@\${ref}\` };
3603
+ return { elements: [result.element], targetDesc: \`@\${ref}\` };
3595
3604
  }
3596
3605
  stats.refsStale++;
3597
- const errorMsg = result2.status === "never_assigned" ? \`Ref @\${ref} was never assigned (highest ref is @\${refRegistry.getStats().highWaterMark})\` : result2.status === "removed_from_dom" ? \`Ref @\${ref} points to an element that was removed from the DOM (try refreshing /tree)\` : \`Ref @\${ref} is stale - element was garbage collected (try refreshing /tree)\`;
3606
+ const errorMsg = result.status === "never_assigned" ? \`Ref @\${ref} was never assigned (highest ref is @\${refRegistry.getStats().highWaterMark})\` : result.status === "removed_from_dom" ? \`Ref @\${ref} points to an element that was removed from the DOM (try refreshing /tree)\` : \`Ref @\${ref} is stale - element was garbage collected (try refreshing /tree)\`;
3598
3607
  return { elements: [], targetDesc: \`@\${ref}\`, error: errorMsg };
3599
3608
  }
3600
3609
  if (selector) {
@@ -4734,6 +4743,7 @@ var COMPONENT_JS = `(() => {
4734
4743
  displayStream = null;
4735
4744
  displayVideo = null;
4736
4745
  isActive = true;
4746
+ visibilityHandler = null;
4737
4747
  homeLeft = 0;
4738
4748
  homeBottom = 16;
4739
4749
  headless = false;
@@ -4925,6 +4935,16 @@ var COMPONENT_JS = `(() => {
4925
4935
  }
4926
4936
  this.interceptConsole();
4927
4937
  this.interceptDialogs();
4938
+ if (typeof document !== "undefined") {
4939
+ this.visibilityHandler = () => {
4940
+ if (document.visibilityState === "hidden") {
4941
+ this.deactivate();
4942
+ } else {
4943
+ this.activate();
4944
+ }
4945
+ };
4946
+ document.addEventListener("visibilitychange", this.visibilityHandler);
4947
+ }
4928
4948
  this.connect();
4929
4949
  }
4930
4950
  disconnectedCallback() {
@@ -4935,6 +4955,10 @@ var COMPONENT_JS = `(() => {
4935
4955
  this.clearEventWatchers();
4936
4956
  this.stopMutationWatch();
4937
4957
  this.stopScreenCapture();
4958
+ if (this.visibilityHandler && typeof document !== "undefined") {
4959
+ document.removeEventListener("visibilitychange", this.visibilityHandler);
4960
+ this.visibilityHandler = null;
4961
+ }
4938
4962
  }
4939
4963
  attributeChangedCallback(name, _old, value) {
4940
4964
  if (name === "server") {
@@ -5828,8 +5852,8 @@ var COMPONENT_JS = `(() => {
5828
5852
  headers: { "Content-Type": "application/json" },
5829
5853
  body: JSON.stringify({ action: "start", window: this.windowId })
5830
5854
  });
5831
- const result2 = await response.json();
5832
- if (result2.success) {
5855
+ const result = await response.json();
5856
+ if (result.success) {
5833
5857
  this.isRecording = true;
5834
5858
  this.serverManagedRecording = true;
5835
5859
  this.recordingStartTime = Date.now();
@@ -5841,7 +5865,7 @@ var COMPONENT_JS = `(() => {
5841
5865
  this.updateRecordingUI(true);
5842
5866
  console.log("[Haltija] Recording started (server-managed)");
5843
5867
  } else {
5844
- console.error("[Haltija] Failed to start recording:", result2.error);
5868
+ console.error("[Haltija] Failed to start recording:", result.error);
5845
5869
  }
5846
5870
  } catch (err) {
5847
5871
  console.error("[Haltija] Failed to start recording:", err);
@@ -5856,10 +5880,10 @@ var COMPONENT_JS = `(() => {
5856
5880
  headers: { "Content-Type": "application/json" },
5857
5881
  body: JSON.stringify({ action: "stop", window: this.windowId })
5858
5882
  });
5859
- const result2 = await response.json();
5860
- if (result2.success && result2.data) {
5861
- this.recordingEvents = result2.data.events || [];
5862
- this.recordingStartTime = result2.data.startTime || this.recordingStartTime;
5883
+ const result = await response.json();
5884
+ if (result.success && result.data) {
5885
+ this.recordingEvents = result.data.events || [];
5886
+ this.recordingStartTime = result.data.startTime || this.recordingStartTime;
5863
5887
  const recordingId = \`rec_\${this.recordingStartTime}_\${Math.random().toString(36).slice(2, 8)}\`;
5864
5888
  this.lastRecordingId = recordingId;
5865
5889
  console.log(\`[Haltija] Recording stopped (\${this.recordingEvents.length} events)\`);
@@ -6172,15 +6196,15 @@ var COMPONENT_JS = `(() => {
6172
6196
  headers: { "Content-Type": "application/json" },
6173
6197
  body: JSON.stringify({ description, agentId })
6174
6198
  });
6175
- const result2 = await response.json();
6176
- if (result2.error) {
6199
+ const result = await response.json();
6200
+ if (result.error) {
6177
6201
  if (successMsg) {
6178
- successMsg.textContent = \`\u26A0\uFE0F \${result2.error}\`;
6202
+ successMsg.textContent = \`\u26A0\uFE0F \${result.error}\`;
6179
6203
  successMsg.style.color = "#f59e0b";
6180
6204
  }
6181
6205
  } else {
6182
6206
  if (successMsg) {
6183
- successMsg.textContent = \`\u2713 Sent to \${agentName}\${result2.interrupted ? " (interrupted)" : ""}\`;
6207
+ successMsg.textContent = \`\u2713 Sent to \${agentName}\${result.interrupted ? " (interrupted)" : ""}\`;
6184
6208
  successMsg.style.color = "#22c55e";
6185
6209
  }
6186
6210
  setTimeout(() => this.closeTestModal(), 1500);
@@ -6711,13 +6735,13 @@ var COMPONENT_JS = `(() => {
6711
6735
  const haltija = window.haltija;
6712
6736
  if (haltija?.openAgentTab) {
6713
6737
  newAgentText.textContent = "Creating...";
6714
- const result2 = await haltija.openAgentTab();
6715
- if (result2?.shellId) {
6738
+ const result = await haltija.openAgentTab();
6739
+ if (result?.shellId) {
6716
6740
  await new Promise((r) => setTimeout(r, 300));
6717
6741
  if (type === "selection") {
6718
- this.sendSelectionToAgent(result2.shellId, result2.name || "agent");
6742
+ this.sendSelectionToAgent(result.shellId, result.name || "agent");
6719
6743
  } else if (type === "recording") {
6720
- this.sendRecordingToAgent(result2.shellId, result2.name || "agent");
6744
+ this.sendRecordingToAgent(result.shellId, result.name || "agent");
6721
6745
  }
6722
6746
  } else {
6723
6747
  console.error(\`\${LOG_PREFIX} Failed to create agent tab\`);
@@ -6786,8 +6810,8 @@ var COMPONENT_JS = `(() => {
6786
6810
  context: \`selection from \${window.location.host}\`
6787
6811
  })
6788
6812
  });
6789
- const result2 = await response.json();
6790
- if (result2.sent) {
6813
+ const result = await response.json();
6814
+ if (result.sent) {
6791
6815
  this.clearSelection();
6792
6816
  console.log(\`\${LOG_PREFIX} Selection sent to \${agentName}\`);
6793
6817
  }
@@ -8073,6 +8097,7 @@ var COMPONENT_JS = `(() => {
8073
8097
  this.state = "connected";
8074
8098
  this.show();
8075
8099
  const windowType = window.opener ? "popup" : window.parent !== window ? "iframe" : "tab";
8100
+ this.isActive = typeof document === "undefined" || document.visibilityState !== "hidden";
8076
8101
  this.send("system", "connected", {
8077
8102
  windowId: this.windowId,
8078
8103
  browserId: this.browserId,
@@ -8301,11 +8326,11 @@ var COMPONENT_JS = `(() => {
8301
8326
  this.respond(msg2.id, false, undefined, "Video capture requires the Haltija Desktop app. Run: npx haltija@latest -f");
8302
8327
  return;
8303
8328
  }
8304
- haltija.startVideoCapture({ maxDuration: payload2.maxDuration }).then((result2) => {
8305
- if (result2.success) {
8306
- this.respond(msg2.id, true, { recordingId: result2.recordingId });
8329
+ haltija.startVideoCapture({ maxDuration: payload2.maxDuration }).then((result) => {
8330
+ if (result.success) {
8331
+ this.respond(msg2.id, true, { recordingId: result.recordingId });
8307
8332
  } else {
8308
- this.respond(msg2.id, false, undefined, result2.error || "Failed to start video");
8333
+ this.respond(msg2.id, false, undefined, result.error || "Failed to start video");
8309
8334
  }
8310
8335
  }).catch((err) => {
8311
8336
  this.respond(msg2.id, false, undefined, \`Failed to start video: \${err.message}\`);
@@ -8315,11 +8340,11 @@ var COMPONENT_JS = `(() => {
8315
8340
  this.respond(msg2.id, false, undefined, "Video capture requires the Haltija Desktop app. Run: npx haltija@latest -f");
8316
8341
  return;
8317
8342
  }
8318
- haltija.stopVideoCapture().then((result2) => {
8319
- if (result2.success) {
8320
- this.respond(msg2.id, true, { path: result2.path, duration: result2.duration, size: result2.size, format: result2.format });
8343
+ haltija.stopVideoCapture().then((result) => {
8344
+ if (result.success) {
8345
+ this.respond(msg2.id, true, { path: result.path, duration: result.duration, size: result.size, format: result.format });
8321
8346
  } else {
8322
- this.respond(msg2.id, false, undefined, result2.error || "Failed to stop video");
8347
+ this.respond(msg2.id, false, undefined, result.error || "Failed to stop video");
8323
8348
  }
8324
8349
  }).catch((err) => {
8325
8350
  this.respond(msg2.id, false, undefined, \`Failed to stop video: \${err.message}\`);
@@ -8329,8 +8354,8 @@ var COMPONENT_JS = `(() => {
8329
8354
  this.respond(msg2.id, true, { recording: false });
8330
8355
  return;
8331
8356
  }
8332
- haltija.videoStatus().then((result2) => {
8333
- this.respond(msg2.id, true, result2);
8357
+ haltija.videoStatus().then((result) => {
8358
+ this.respond(msg2.id, true, result);
8334
8359
  }).catch(() => {
8335
8360
  this.respond(msg2.id, true, { recording: false });
8336
8361
  });
@@ -8347,8 +8372,8 @@ var COMPONENT_JS = `(() => {
8347
8372
  this.respond(msg2.id, false, undefined, notAvailable);
8348
8373
  return;
8349
8374
  }
8350
- haltija.networkWatch(payload2 || {}).then((result2) => {
8351
- this.respond(msg2.id, result2.success !== false, result2);
8375
+ haltija.networkWatch(payload2 || {}).then((result) => {
8376
+ this.respond(msg2.id, result.success !== false, result);
8352
8377
  }).catch((err) => {
8353
8378
  this.respond(msg2.id, false, undefined, err.message);
8354
8379
  });
@@ -8357,8 +8382,8 @@ var COMPONENT_JS = `(() => {
8357
8382
  this.respond(msg2.id, false, undefined, notAvailable);
8358
8383
  return;
8359
8384
  }
8360
- haltija.networkUnwatch().then((result2) => {
8361
- this.respond(msg2.id, true, result2);
8385
+ haltija.networkUnwatch().then((result) => {
8386
+ this.respond(msg2.id, true, result);
8362
8387
  }).catch((err) => {
8363
8388
  this.respond(msg2.id, false, undefined, err.message);
8364
8389
  });
@@ -8367,8 +8392,8 @@ var COMPONENT_JS = `(() => {
8367
8392
  this.respond(msg2.id, false, undefined, notAvailable);
8368
8393
  return;
8369
8394
  }
8370
- haltija.networkLog(payload2 || {}).then((result2) => {
8371
- this.respond(msg2.id, true, result2);
8395
+ haltija.networkLog(payload2 || {}).then((result) => {
8396
+ this.respond(msg2.id, true, result);
8372
8397
  }).catch((err) => {
8373
8398
  this.respond(msg2.id, false, undefined, err.message);
8374
8399
  });
@@ -8377,8 +8402,8 @@ var COMPONENT_JS = `(() => {
8377
8402
  this.respond(msg2.id, false, undefined, notAvailable);
8378
8403
  return;
8379
8404
  }
8380
- haltija.networkStats().then((result2) => {
8381
- this.respond(msg2.id, true, result2);
8405
+ haltija.networkStats().then((result) => {
8406
+ this.respond(msg2.id, true, result);
8382
8407
  }).catch((err) => {
8383
8408
  this.respond(msg2.id, false, undefined, err.message);
8384
8409
  });
@@ -8387,8 +8412,8 @@ var COMPONENT_JS = `(() => {
8387
8412
  this.respond(msg2.id, false, undefined, notAvailable);
8388
8413
  return;
8389
8414
  }
8390
- haltija.networkClear().then((result2) => {
8391
- this.respond(msg2.id, true, result2);
8415
+ haltija.networkClear().then((result) => {
8416
+ this.respond(msg2.id, true, result);
8392
8417
  }).catch((err) => {
8393
8418
  this.respond(msg2.id, false, undefined, err.message);
8394
8419
  });
@@ -8834,9 +8859,9 @@ var COMPONENT_JS = `(() => {
8834
8859
  try {
8835
8860
  const capturePromise = targetSelector ? haltija.captureElement(targetSelector) : haltija.capturePage();
8836
8861
  const timeoutPromise = new Promise((resolve) => setTimeout(() => resolve({ success: false, error: "Screenshot capture timed out after 10s" }), 1e4));
8837
- const result2 = await Promise.race([capturePromise, timeoutPromise]);
8838
- if (result2?.success && result2.data) {
8839
- const converted = await convertFormat(result2.data);
8862
+ const result = await Promise.race([capturePromise, timeoutPromise]);
8863
+ if (result?.success && result.data) {
8864
+ const converted = await convertFormat(result.data);
8840
8865
  this.respond(msg2.id, true, {
8841
8866
  image: converted.image,
8842
8867
  viewport,
@@ -8846,8 +8871,8 @@ var COMPONENT_JS = `(() => {
8846
8871
  source: "electron"
8847
8872
  });
8848
8873
  return;
8849
- } else if (result2 && !result2.success) {
8850
- this.respond(msg2.id, false, null, result2.error || "Electron capture failed");
8874
+ } else if (result && !result.success) {
8875
+ this.respond(msg2.id, false, null, result.error || "Electron capture failed");
8851
8876
  return;
8852
8877
  }
8853
8878
  } catch (err) {
@@ -8918,14 +8943,29 @@ var COMPONENT_JS = `(() => {
8918
8943
  this.respond(msg2.id, true);
8919
8944
  }
8920
8945
  }
8921
- async handleEvalMessage(msg) {
8946
+ async handleEvalMessage(msg2) {
8922
8947
  try {
8923
- const result = eval(msg.payload.code);
8948
+ const result = this.evalCode(msg2.payload.code);
8924
8949
  const resolved = result instanceof Promise ? await result : result;
8925
- this.respond(msg.id, true, resolved);
8950
+ this.respond(msg2.id, true, resolved);
8951
+ } catch (err) {
8952
+ this.respond(msg2.id, false, null, err.message);
8953
+ }
8954
+ }
8955
+ evalCode(code) {
8956
+ try {
8957
+ return eval(code);
8958
+ } catch (err) {
8959
+ if (!(err instanceof SyntaxError) || !/\\b(await|return)\\b/.test(code))
8960
+ throw err;
8961
+ }
8962
+ try {
8963
+ return eval(\`(async () => (\${code}))()\`);
8926
8964
  } catch (err) {
8927
- this.respond(msg.id, false, null, err.message);
8965
+ if (!(err instanceof SyntaxError))
8966
+ throw err;
8928
8967
  }
8968
+ return eval(\`(async () => { \${code} })()\`);
8929
8969
  }
8930
8970
  async handleFetchMessage(msg2) {
8931
8971
  const { url } = msg2.payload;
@@ -9003,14 +9043,14 @@ var COMPONENT_JS = `(() => {
9003
9043
  let el = null;
9004
9044
  let targetDesc = "";
9005
9045
  if (ref) {
9006
- const result2 = refRegistry.resolveWithStatus(ref);
9007
- el = result2.element;
9046
+ const result = refRegistry.resolveWithStatus(ref);
9047
+ el = result.element;
9008
9048
  targetDesc = \`@\${ref}\`;
9009
9049
  if (el) {
9010
9050
  stats.refsResolved++;
9011
9051
  } else {
9012
9052
  stats.refsStale++;
9013
- const errorMsg = result2.status === "never_assigned" ? \`Ref @\${ref} was never assigned (highest ref is @\${refRegistry.getStats().highWaterMark})\` : result2.status === "removed_from_dom" ? \`Ref @\${ref} points to an element that was removed from the DOM (try refreshing /tree)\` : \`Ref @\${ref} is stale - element was garbage collected (try refreshing /tree)\`;
9053
+ const errorMsg = result.status === "never_assigned" ? \`Ref @\${ref} was never assigned (highest ref is @\${refRegistry.getStats().highWaterMark})\` : result.status === "removed_from_dom" ? \`Ref @\${ref} points to an element that was removed from the DOM (try refreshing /tree)\` : \`Ref @\${ref} is stale - element was garbage collected (try refreshing /tree)\`;
9014
9054
  this.respond(responseId, false, null, errorMsg);
9015
9055
  return;
9016
9056
  }
@@ -9355,14 +9395,14 @@ var COMPONENT_JS = `(() => {
9355
9395
  let el = null;
9356
9396
  let targetDesc = "";
9357
9397
  if (payload2.ref) {
9358
- const result2 = refRegistry.resolveWithStatus(payload2.ref);
9359
- el = result2.element;
9398
+ const result = refRegistry.resolveWithStatus(payload2.ref);
9399
+ el = result.element;
9360
9400
  targetDesc = \`@\${payload2.ref}\`;
9361
9401
  if (el) {
9362
9402
  stats.refsResolved++;
9363
9403
  } else {
9364
9404
  stats.refsStale++;
9365
- const errorMsg = result2.status === "never_assigned" ? \`Ref @\${payload2.ref} was never assigned (highest ref is @\${refRegistry.getStats().highWaterMark})\` : result2.status === "removed_from_dom" ? \`Ref @\${payload2.ref} points to an element that was removed from the DOM (try refreshing /tree)\` : \`Ref @\${payload2.ref} is stale - element was garbage collected (try refreshing /tree)\`;
9405
+ const errorMsg = result.status === "never_assigned" ? \`Ref @\${payload2.ref} was never assigned (highest ref is @\${refRegistry.getStats().highWaterMark})\` : result.status === "removed_from_dom" ? \`Ref @\${payload2.ref} points to an element that was removed from the DOM (try refreshing /tree)\` : \`Ref @\${payload2.ref} is stale - element was garbage collected (try refreshing /tree)\`;
9366
9406
  this.respond(responseId, false, null, errorMsg);
9367
9407
  return;
9368
9408
  }
@@ -9431,15 +9471,15 @@ var COMPONENT_JS = `(() => {
9431
9471
  let target = null;
9432
9472
  let targetDesc = "";
9433
9473
  if (ref) {
9434
- const result2 = refRegistry.resolveWithStatus(ref);
9435
- target = result2.element;
9474
+ const result = refRegistry.resolveWithStatus(ref);
9475
+ target = result.element;
9436
9476
  targetDesc = \`@\${ref}\`;
9437
9477
  if (target) {
9438
9478
  stats.refsResolved++;
9439
9479
  target.focus();
9440
9480
  } else {
9441
9481
  stats.refsStale++;
9442
- const errorMsg = result2.status === "never_assigned" ? \`Ref @\${ref} was never assigned (highest ref is @\${refRegistry.getStats().highWaterMark})\` : result2.status === "removed_from_dom" ? \`Ref @\${ref} points to an element that was removed from the DOM (try refreshing /tree)\` : \`Ref @\${ref} is stale - element was garbage collected (try refreshing /tree)\`;
9482
+ const errorMsg = result.status === "never_assigned" ? \`Ref @\${ref} was never assigned (highest ref is @\${refRegistry.getStats().highWaterMark})\` : result.status === "removed_from_dom" ? \`Ref @\${ref} points to an element that was removed from the DOM (try refreshing /tree)\` : \`Ref @\${ref} is stale - element was garbage collected (try refreshing /tree)\`;
9443
9483
  this.respond(responseId, false, null, errorMsg);
9444
9484
  return;
9445
9485
  }
@@ -9831,9 +9871,9 @@ var COMPONENT_JS = `(() => {
9831
9871
  });
9832
9872
  } else if (action2 === "result") {
9833
9873
  if (this.selectionResult) {
9834
- const result2 = this.selectionResult;
9874
+ const result = this.selectionResult;
9835
9875
  this.clearSelection();
9836
- this.respond(msg2.id, true, result2);
9876
+ this.respond(msg2.id, true, result);
9837
9877
  } else {
9838
9878
  this.respond(msg2.id, false, null, "No selection available");
9839
9879
  }
@@ -11094,7 +11134,17 @@ ${isElectronApp ? "" : ` <script>
11094
11134
  document.getElementById('tab-' + tabId).classList.add('active');
11095
11135
  });
11096
11136
  });
11097
-
11137
+
11138
+ // Deep-link to a tab via ?tab=<name>. Lets tests (and links) land directly
11139
+ // on a tab instead of relying on a click landing right after navigation \u2014
11140
+ // which raced under headless CI.
11141
+ (function() {
11142
+ var wanted = new URLSearchParams(location.search).get('tab');
11143
+ if (!wanted) return;
11144
+ var btn = document.querySelector('.tab[data-tab="' + wanted + '"]');
11145
+ if (btn) btn.click();
11146
+ })();
11147
+
11098
11148
  // Copy button functionality
11099
11149
  function copyCode(btn) {
11100
11150
  var pre = btn.parentElement.querySelector('pre');
@@ -11373,6 +11423,8 @@ Use ancestors:true to see parent elements when inspecting deep elements.`,
11373
11423
  pierceFrames: L.boolean.describe("Pierce same-origin iframes (default true)").optional,
11374
11424
  compact: L.boolean.describe("Minimal output (default false)").optional,
11375
11425
  ancestors: L.boolean.describe("Include ancestor path from root (default false)").optional,
11426
+ includeBox: L.boolean.describe("Include each node's bounding box { x, y, w, h, visible } (default false)").optional,
11427
+ mode: L.string.describe("'actionable' returns a page action summary (url, title, headings, buttons, links, inputs, selects) instead of the DOM tree").optional,
11376
11428
  window: L.string.describe("Target window ID").optional
11377
11429
  }),
11378
11430
  examples: [
@@ -12360,7 +12412,14 @@ var eval_ = endpoint({
12360
12412
  description: `Run arbitrary JavaScript in the browser context. Returns the result.
12361
12413
 
12362
12414
  The code runs in the page's context with access to window, document, etc.
12363
- Return values are JSON-serialized. Promises are awaited.`,
12415
+ Return values are JSON-serialized.
12416
+
12417
+ Async code is supported: a returned Promise is awaited, and top-level \`await\`
12418
+ works. Multi-statement code needs an explicit \`return\` to produce a value.
12419
+
12420
+ document.title -> the title
12421
+ await fetch('/api').then(r => r.json()) -> the parsed body
12422
+ const r = await fetch('/api'); return r.status -> the status code`,
12364
12423
  category: "debug",
12365
12424
  input: L.object({
12366
12425
  code: L.string.describe("JavaScript code to execute"),
@@ -14063,7 +14122,10 @@ registerHandler(tree, async (body, ctx) => {
14063
14122
  pierceShadow: body.pierceShadow,
14064
14123
  pierceFrames: body.pierceFrames,
14065
14124
  visibleOnly: body.visibleOnly,
14066
- ancestors: body.ancestors
14125
+ interactiveOnly: body.interactiveOnly,
14126
+ ancestors: body.ancestors,
14127
+ includeBox: body.includeBox,
14128
+ mode: body.mode
14067
14129
  }, 5000, windowId);
14068
14130
  return Response.json(response, { headers: ctx.headers });
14069
14131
  });
@@ -18525,7 +18587,8 @@ var serverConfig = {
18525
18587
  label: existingWindow?.label,
18526
18588
  windowType: windowType || "tab"
18527
18589
  });
18528
- if (!focusedWindowId || focusedWindowId === windowId) {
18590
+ const isVisibleTab = (windowType || "tab") === "tab" && active !== false;
18591
+ if (!focusedWindowId || focusedWindowId === windowId || isVisibleTab) {
18529
18592
  focusedWindowId = windowId;
18530
18593
  }
18531
18594
  console.log(`${LOG_PREFIX} Window connected: ${windowId} (${url})`);
@@ -18569,6 +18632,9 @@ var serverConfig = {
18569
18632
  if (win) {
18570
18633
  win.active = active;
18571
18634
  win.lastSeen = Date.now();
18635
+ if (active && win.windowType === "tab") {
18636
+ focusedWindowId = windowId;
18637
+ }
18572
18638
  }
18573
18639
  }
18574
18640
  } catch {}