@quanta-intellect/vessel-browser 0.1.35 → 0.1.45

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.
@@ -3187,12 +3187,33 @@ function vesselExtractContent() {
3187
3187
  function resolveElementSelector(index) {
3188
3188
  return elementSelectors[index] || null;
3189
3189
  }
3190
+ function resolveElementIndexBySelector(selector) {
3191
+ if (!selector || typeof selector !== "string") return null;
3192
+ let el = null;
3193
+ try {
3194
+ if (selector.includes(" >>> ")) {
3195
+ el = resolveShadowSelector(selector);
3196
+ } else {
3197
+ el = document.querySelector(selector);
3198
+ }
3199
+ } catch {
3200
+ return null;
3201
+ }
3202
+ if (!el) return null;
3203
+ const existing = indexedElements.get(el);
3204
+ return typeof existing === "number" ? existing : null;
3205
+ }
3190
3206
  function interactByIndex(index, action, value) {
3191
3207
  const el = indexedElementRefs[index];
3192
- if (!el || !(el instanceof HTMLElement)) {
3208
+ if (!el || !(el instanceof HTMLElement) || !document.contains(el)) {
3193
3209
  return "Error[stale-index]: Element not found — the page may have changed. Call read_page to refresh.";
3194
3210
  }
3195
3211
  if (action === "click") {
3212
+ el.scrollIntoView({ behavior: "instant", block: "center", inline: "center" });
3213
+ const rect = el.getBoundingClientRect();
3214
+ if (rect.width <= 0 || rect.height <= 0) {
3215
+ return "Error[hidden]: Element has no visible area. It may be inside a collapsed, lazy-loaded, or virtual-scroll section. Scroll toward it, then call read_page to refresh visible elements.";
3216
+ }
3196
3217
  el.focus();
3197
3218
  el.click();
3198
3219
  if (el instanceof HTMLInputElement) {
@@ -3214,9 +3235,12 @@ function interactByIndex(index, action, value) {
3214
3235
  }
3215
3236
  return `${ariaChecked === "true" ? "Selected" : "Clicked"}: ${label}`;
3216
3237
  }
3217
- return "Clicked: " + (el.getAttribute("aria-label") || el.textContent?.trim().slice(0, 60) || el.tagName.toLowerCase());
3238
+ const anchor = el instanceof HTMLAnchorElement ? el : el.closest("a[href]");
3239
+ const href = anchor instanceof HTMLAnchorElement ? anchor.href : null;
3240
+ return "Clicked: " + (el.getAttribute("aria-label") || el.textContent?.trim().slice(0, 60) || el.tagName.toLowerCase()) + (href ? "\nhref: " + href : "");
3218
3241
  }
3219
3242
  if (action === "focus") {
3243
+ el.scrollIntoView({ behavior: "instant", block: "center", inline: "center" });
3220
3244
  el.focus();
3221
3245
  return "Focused: " + (el.getAttribute("aria-label") || el.textContent?.trim().slice(0, 60) || el.tagName.toLowerCase());
3222
3246
  }
@@ -3241,6 +3265,7 @@ function interactByIndex(index, action, value) {
3241
3265
  electron.contextBridge.exposeInMainWorld("__vessel", {
3242
3266
  extractContent: vesselExtractContent,
3243
3267
  getElementSelector: resolveElementSelector,
3268
+ getElementIndexBySelector: resolveElementIndexBySelector,
3244
3269
  interactByIndex,
3245
3270
  resolveShadowSelector,
3246
3271
  notifyHighlightSelection: (text) => {