@skippr/live-agent-sdk 0.103.0 → 0.105.0

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/README.md CHANGED
@@ -56,7 +56,6 @@ The widget opens with a picker of your active modules — the user chooses which
56
56
 
57
57
  ```tsx
58
58
  import { LiveAgent } from '@skippr/live-agent-sdk';
59
- import '@skippr/live-agent-sdk/styles';
60
59
 
61
60
  function App() {
62
61
  return <LiveAgent appKey="pk_live_your_key" />;
@@ -5089,6 +5089,21 @@ var ROLE_BY_TAG = {
5089
5089
  embed: "embed",
5090
5090
  object: "object"
5091
5091
  };
5092
+ var REACT_PROPS_KEY_PREFIX = "__reactProps$";
5093
+ var POINTER_HANDLER_PROPS = ["onClick", "onMouseDown", "onMouseUp", "onPointerDown"];
5094
+ function hasFrameworkPointerHandler(element) {
5095
+ try {
5096
+ const propsKey = Object.keys(element).find((key) => key.startsWith(REACT_PROPS_KEY_PREFIX));
5097
+ if (!propsKey)
5098
+ return false;
5099
+ const props = element[propsKey];
5100
+ if (!props || typeof props !== "object")
5101
+ return false;
5102
+ return POINTER_HANDLER_PROPS.some((handler) => typeof props[handler] === "function");
5103
+ } catch {
5104
+ return false;
5105
+ }
5106
+ }
5092
5107
  function inferRole(element) {
5093
5108
  const explicitRole = element.getAttribute("role");
5094
5109
  if (explicitRole)
@@ -5246,6 +5261,56 @@ function isCursorClickable(element, rect) {
5246
5261
  return false;
5247
5262
  return true;
5248
5263
  }
5264
+ var KEYSTROKE_TRAP_MAX_EDGE_PX = 8;
5265
+ function isKeystrokeTrap(rect) {
5266
+ return rect.width < KEYSTROKE_TRAP_MAX_EDGE_PX || rect.height < KEYSTROKE_TRAP_MAX_EDGE_PX;
5267
+ }
5268
+ var CONTAINED_CONTROL_SELECTOR = "input, select, textarea, button, a[href]";
5269
+ function isLayoutContainer(element) {
5270
+ if (element.querySelectorAll(CONTAINED_CONTROL_SELECTOR).length > 1)
5271
+ return true;
5272
+ return element.querySelector("h1, h2, h3, h4, h5, h6") !== null;
5273
+ }
5274
+ var MENU_MIN_OPTION_ROWS = 2;
5275
+ function optionRowsOf(element) {
5276
+ const children = Array.from(element.children);
5277
+ if (children.length < MENU_MIN_OPTION_ROWS)
5278
+ return [];
5279
+ const rects = children.map((child) => child.getBoundingClientRect());
5280
+ const everyChildIsALabelledLeaf = children.every((child, index2) => child.children.length === 0 && normalizeText(child.textContent).length > 0 && !isVisuallyEmpty(rects[index2]));
5281
+ return everyChildIsALabelledLeaf && areStackedVertically(rects) ? children : [];
5282
+ }
5283
+ function isVisuallyEmpty(rect) {
5284
+ return rect.width <= 0 || rect.height <= 0;
5285
+ }
5286
+ function areStackedVertically(rects) {
5287
+ for (let index2 = 1;index2 < rects.length; index2++) {
5288
+ const previous = rects[index2 - 1];
5289
+ const current = rects[index2];
5290
+ if (current.top < previous.bottom)
5291
+ return false;
5292
+ }
5293
+ return true;
5294
+ }
5295
+ var WIDGET_NESTING_SEARCH_DEPTH = 4;
5296
+ function isInsideHandlerOwningWidget(element) {
5297
+ let ancestor = element.parentElement;
5298
+ for (let depth = 0;depth < WIDGET_NESTING_SEARCH_DEPTH && ancestor; depth++) {
5299
+ if (hasFrameworkPointerHandler(ancestor) && !isLayoutContainer(ancestor))
5300
+ return true;
5301
+ ancestor = ancestor.parentElement;
5302
+ }
5303
+ return false;
5304
+ }
5305
+ function isFrameworkClickable(element, rect) {
5306
+ if (!intersectsViewport(rect, window.innerWidth, window.innerHeight))
5307
+ return false;
5308
+ if (!hasFrameworkPointerHandler(element))
5309
+ return false;
5310
+ if (isLayoutContainer(element))
5311
+ return false;
5312
+ return !isInsideHandlerOwningWidget(element);
5313
+ }
5249
5314
  function isBlindRegion(role) {
5250
5315
  return role === "video" || role === "iframe" || role === "canvas" || role === "audio" || role === "embed" || role === "object";
5251
5316
  }
@@ -5570,6 +5635,19 @@ function collectTableRowAsSingleEntry(row, depth, rect, entries) {
5570
5635
  }
5571
5636
  return true;
5572
5637
  }
5638
+ function collectOptionRows(rows, depth, entries) {
5639
+ for (const row of rows) {
5640
+ const rect = row.getBoundingClientRect();
5641
+ entries.push({
5642
+ depth,
5643
+ role: "option",
5644
+ name: normalizeText(row.textContent).slice(0, NAME_MAX_CHARS),
5645
+ ref: ensureStableRef(row),
5646
+ attrs: [],
5647
+ area: rect.width * rect.height
5648
+ });
5649
+ }
5650
+ }
5573
5651
  function walkAndCollect(element, depth, entries, suppressTextLeaves = false) {
5574
5652
  if (shouldSkipSubtree(element))
5575
5653
  return;
@@ -5580,7 +5658,18 @@ function walkAndCollect(element, depth, entries, suppressTextLeaves = false) {
5580
5658
  if (collectTableRowAsSingleEntry(element, depth, rect, entries))
5581
5659
  return;
5582
5660
  }
5583
- const role = inferRole(element) ?? (isCursorClickable(element, rect) ? "button" : null);
5661
+ if (element.tagName === "INPUT" && isKeystrokeTrap(rect))
5662
+ return;
5663
+ const markupRole = inferRole(element);
5664
+ const isFrameworkControl = markupRole === null && isFrameworkClickable(element, rect);
5665
+ if (isFrameworkControl && isVisible(element, rect)) {
5666
+ const optionRows = optionRowsOf(element);
5667
+ if (optionRows.length > 0) {
5668
+ collectOptionRows(optionRows, depth, entries);
5669
+ return;
5670
+ }
5671
+ }
5672
+ const role = markupRole ?? (isCursorClickable(element, rect) || isFrameworkControl ? "button" : null);
5584
5673
  const isVisibleAndEmittable = role !== null && isVisible(element, rect) && shouldEmitElement(element, role);
5585
5674
  let childDepth = depth;
5586
5675
  let childSuppressTextLeaves = suppressTextLeaves;
@@ -7219,6 +7308,7 @@ function performSelect(element, value) {
7219
7308
  element.click();
7220
7309
  }
7221
7310
  }
7311
+ var NATIVELY_ACTIVATABLE_SELECTOR = "a[href], button, input, select, textarea, label, summary";
7222
7312
  var INTERACTIVE_SELECTOR = 'button, a[href], [role="button"], [role="link"], input, select, [onclick]';
7223
7313
  function resolveClickTarget(element) {
7224
7314
  if (element.matches(INTERACTIVE_SELECTOR))
@@ -7230,23 +7320,36 @@ function resolveClickTarget(element) {
7230
7320
  const onlyDescendant = descendants.length === 1 ? descendants[0] : null;
7231
7321
  return onlyDescendant ?? element;
7232
7322
  }
7323
+ function deepestElementAt(fallback, clientX, clientY) {
7324
+ const topmost = document.elementFromPoint(clientX, clientY);
7325
+ if (!(topmost instanceof HTMLElement))
7326
+ return fallback;
7327
+ if (!fallback.contains(topmost))
7328
+ return fallback;
7329
+ return isActionBlocked(topmost) ? fallback : topmost;
7330
+ }
7233
7331
  function performClick(element) {
7234
7332
  const rect = element.getBoundingClientRect();
7235
7333
  const clientX = rect.left + rect.width / 2;
7236
7334
  const clientY = rect.top + rect.height / 2;
7237
7335
  const base = { bubbles: true, cancelable: true, composed: true, clientX, clientY, button: 0 };
7238
- if (typeof element.focus === "function") {
7336
+ const hitTarget = deepestElementAt(element, clientX, clientY);
7337
+ if (typeof hitTarget.focus === "function") {
7239
7338
  try {
7240
- element.focus({ preventScroll: true });
7339
+ hitTarget.focus({ preventScroll: true });
7241
7340
  } catch {}
7242
7341
  }
7243
7342
  try {
7244
- element.dispatchEvent(new PointerEvent("pointerdown", { ...base, pointerId: 1, isPrimary: true }));
7245
- element.dispatchEvent(new MouseEvent("mousedown", base));
7246
- element.dispatchEvent(new PointerEvent("pointerup", { ...base, pointerId: 1, isPrimary: true }));
7247
- element.dispatchEvent(new MouseEvent("mouseup", base));
7343
+ hitTarget.dispatchEvent(new PointerEvent("pointerdown", { ...base, pointerId: 1, isPrimary: true }));
7344
+ hitTarget.dispatchEvent(new MouseEvent("mousedown", base));
7345
+ hitTarget.dispatchEvent(new PointerEvent("pointerup", { ...base, pointerId: 1, isPrimary: true }));
7346
+ hitTarget.dispatchEvent(new MouseEvent("mouseup", base));
7347
+ if (hitTarget.matches(NATIVELY_ACTIVATABLE_SELECTOR)) {
7348
+ hitTarget.click();
7349
+ } else {
7350
+ hitTarget.dispatchEvent(new MouseEvent("click", base));
7351
+ }
7248
7352
  } catch {}
7249
- element.click();
7250
7353
  }
7251
7354
  function performHover(element) {
7252
7355
  const rect = element.getBoundingClientRect();