auto-webmcp 0.3.9 → 0.3.11

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.
@@ -631,6 +631,19 @@ function resolveNativeControlFallbackKey(control) {
631
631
  }
632
632
  return null;
633
633
  }
634
+ function resolveAriaElementKey(el) {
635
+ if (el.dataset["webmcpName"])
636
+ return sanitizeName(el.dataset["webmcpName"]);
637
+ if (el.id && !AUTO_GENERATED_ID_RE.test(el.id))
638
+ return sanitizeName(el.id);
639
+ const label = el.getAttribute("aria-label");
640
+ if (label)
641
+ return sanitizeName(label);
642
+ const placeholder = el.getAttribute("placeholder");
643
+ if (placeholder)
644
+ return sanitizeName(placeholder);
645
+ return null;
646
+ }
634
647
  function collectAriaControls(form) {
635
648
  const selector = ARIA_ROLES_TO_SCAN.map((r) => `[role="${r}"]`).join(", ");
636
649
  const rawResults = [];
@@ -889,6 +902,22 @@ function buildSchemaFromInputs(inputs) {
889
902
  const processedRadioGroups = /* @__PURE__ */ new Set();
890
903
  const processedCheckboxGroups = /* @__PURE__ */ new Set();
891
904
  for (const control of inputs) {
905
+ if (!(control instanceof HTMLInputElement) && !(control instanceof HTMLTextAreaElement) && !(control instanceof HTMLSelectElement)) {
906
+ const fieldKey2 = resolveAriaElementKey(control);
907
+ if (!fieldKey2)
908
+ continue;
909
+ if (!isControlVisible(control))
910
+ continue;
911
+ const prop = { type: "string" };
912
+ prop.title = control.getAttribute("aria-label") ?? fieldKey2;
913
+ const desc2 = control.getAttribute("aria-description") ?? control.getAttribute("aria-describedby") ? null : null;
914
+ if (desc2)
915
+ prop.description = desc2;
916
+ properties[fieldKey2] = prop;
917
+ fieldElements.set(fieldKey2, control);
918
+ required.push(fieldKey2);
919
+ continue;
920
+ }
892
921
  const rawName = control.name;
893
922
  const fieldKey = (rawName ? sanitizeName(rawName) : null) || resolveNativeControlFallbackKey(control);
894
923
  if (!fieldKey)
@@ -1284,10 +1313,32 @@ function fillAriaField(el, value) {
1284
1313
  }
1285
1314
  const htmlEl = el;
1286
1315
  if (htmlEl.isContentEditable) {
1287
- htmlEl.textContent = String(value ?? "");
1316
+ htmlEl.focus();
1317
+ const range = document.createRange();
1318
+ range.selectNodeContents(htmlEl);
1319
+ const sel = window.getSelection();
1320
+ sel?.removeAllRanges();
1321
+ sel?.addRange(range);
1322
+ let handled = false;
1323
+ try {
1324
+ const dt = new DataTransfer();
1325
+ dt.setData("text/plain", String(value ?? ""));
1326
+ const ev = new ClipboardEvent("paste", {
1327
+ bubbles: true,
1328
+ cancelable: true,
1329
+ composed: true,
1330
+ clipboardData: dt
1331
+ });
1332
+ handled = !htmlEl.dispatchEvent(ev);
1333
+ } catch {
1334
+ }
1335
+ if (!handled) {
1336
+ document.execCommand("insertText", false, String(value ?? ""));
1337
+ }
1338
+ } else {
1339
+ el.dispatchEvent(new Event("input", { bubbles: true }));
1340
+ el.dispatchEvent(new Event("change", { bubbles: true }));
1288
1341
  }
1289
- el.dispatchEvent(new Event("input", { bubbles: true }));
1290
- el.dispatchEvent(new Event("change", { bubbles: true }));
1291
1342
  }
1292
1343
  function serializeFormData(form, params, fieldEls) {
1293
1344
  const result = {};
@@ -1560,10 +1611,10 @@ async function scanOrphanInputs(config) {
1560
1611
  return;
1561
1612
  const SUBMIT_BTN_SELECTOR = '[type="submit"]:not([disabled]), button[data-variant="primary"]:not([disabled])';
1562
1613
  const SUBMIT_BTN_GROUPING_SELECTOR = '[type="submit"], button[data-variant="primary"]';
1563
- const SUBMIT_TEXT_RE = /subscribe|submit|sign[\s-]?up|send|join|go|search/i;
1614
+ const SUBMIT_TEXT_RE = /subscribe|submit|sign[\s-]?up|send|join|go|search|post|tweet|publish/i;
1564
1615
  const orphanInputs = Array.from(
1565
1616
  document.querySelectorAll(
1566
- "input:not(form input), textarea:not(form textarea), select:not(form select)"
1617
+ 'input:not(form input), textarea:not(form textarea), select:not(form select), [role="textbox"]:not(form [role="textbox"]):not(input):not(textarea), [role="searchbox"]:not(form [role="searchbox"]):not(input):not(textarea), [contenteditable="true"]:not(form [contenteditable="true"]):not(input):not(textarea)'
1567
1618
  )
1568
1619
  ).filter((el) => {
1569
1620
  if (el instanceof HTMLInputElement && ORPHAN_EXCLUDED_TYPES.has(el.type.toLowerCase())) {
@@ -1619,6 +1670,15 @@ async function scanOrphanInputs(config) {
1619
1670
  if (submitBtn)
1620
1671
  console.log(`[auto-webmcp] orphan: using disabled submit button as reference: "${submitBtn.textContent?.trim()}"`);
1621
1672
  }
1673
+ if (!submitBtn) {
1674
+ const containerBtns = Array.from(container.querySelectorAll("button")).filter((b) => {
1675
+ const r = b.getBoundingClientRect();
1676
+ return r.width > 0 && r.height > 0 && !b.disabled && SUBMIT_TEXT_RE.test(b.textContent ?? "");
1677
+ });
1678
+ submitBtn = containerBtns[containerBtns.length - 1] ?? null;
1679
+ if (submitBtn)
1680
+ console.log(`[auto-webmcp] orphan: using text-matched button in container: "${submitBtn.textContent?.trim()}"`);
1681
+ }
1622
1682
  if (!submitBtn) {
1623
1683
  const pageBtns = Array.from(document.querySelectorAll("button")).filter(
1624
1684
  (b) => {
@@ -1638,10 +1698,10 @@ async function scanOrphanInputs(config) {
1638
1698
  const AUTO_ID_RE = /^_r_[0-9a-z]+_$/i;
1639
1699
  for (const el of inputs) {
1640
1700
  const id = el.id && !AUTO_ID_RE.test(el.id) ? el.id : null;
1641
- const key = el.name || el.dataset["webmcpName"] || id || el.getAttribute("aria-label") || (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement ? el.placeholder || null : null) || null;
1701
+ const key = el.name || el.getAttribute("name") || el.dataset["webmcpName"] || id || el.getAttribute("aria-label") || el.getAttribute("placeholder") || null;
1642
1702
  const safeKey = key ? key.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "").slice(0, 64) : null;
1643
1703
  const matched = !!(safeKey && schemaProps[safeKey]);
1644
- console.log(`[auto-webmcp] orphan: field (name="${el.name}" id="${el.id}") rawKey="${key}" safeKey="${safeKey}" matched=${matched}`);
1704
+ console.log(`[auto-webmcp] orphan: field (name="${el.name ?? ""}" id="${el.id}") rawKey="${key}" safeKey="${safeKey}" matched=${matched}`);
1645
1705
  if (matched) {
1646
1706
  inputPairs.push({ key: safeKey, el });
1647
1707
  }