veo-sdk 0.6.2 → 0.6.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/builder-5F2EFT4X.mjs +5 -0
- package/dist/{builder-ENKEIVF2.mjs.map → builder-5F2EFT4X.mjs.map} +1 -1
- package/dist/builder.cjs +396 -103
- package/dist/builder.cjs.map +1 -1
- package/dist/builder.d.cts +76 -6
- package/dist/builder.d.ts +76 -6
- package/dist/builder.mjs +4 -4
- package/dist/{chunk-L3QQJLDJ.mjs → chunk-72YH2GXC.mjs} +3 -3
- package/dist/{chunk-L3QQJLDJ.mjs.map → chunk-72YH2GXC.mjs.map} +1 -1
- package/dist/{chunk-ZILDR4YE.mjs → chunk-CMPOHTUM.mjs} +112 -80
- package/dist/chunk-CMPOHTUM.mjs.map +1 -0
- package/dist/{chunk-4QZDNJJE.mjs → chunk-YEKCBVNJ.mjs} +284 -15
- package/dist/chunk-YEKCBVNJ.mjs.map +1 -0
- package/dist/index.cjs +653 -26
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +10 -1
- package/dist/index.d.ts +10 -1
- package/dist/index.mjs +308 -7
- package/dist/index.mjs.map +1 -1
- package/dist/veo-builder.js +54 -19
- package/dist/veo-builder.js.map +1 -1
- package/dist/veo-guides.js +6 -6
- package/dist/veo-guides.js.map +1 -1
- package/dist/veo.js +2 -2
- package/dist/veo.js.map +1 -1
- package/package.json +1 -1
- package/dist/builder-ENKEIVF2.mjs +0 -5
- package/dist/chunk-4QZDNJJE.mjs.map +0 -1
- package/dist/chunk-ZILDR4YE.mjs.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -251,7 +251,8 @@ var init_constants = __esm({
|
|
|
251
251
|
throttleMs: 100,
|
|
252
252
|
maxTextLength: 200,
|
|
253
253
|
captureElementMetadata: true,
|
|
254
|
-
captureAncestors: 5
|
|
254
|
+
captureAncestors: 5,
|
|
255
|
+
captureClickQuality: true
|
|
255
256
|
};
|
|
256
257
|
MAX_ANCESTORS = 10;
|
|
257
258
|
ALWAYS_BLOCK_SELECTORS = [
|
|
@@ -440,13 +441,14 @@ var init_activation_rules = __esm({
|
|
|
440
441
|
});
|
|
441
442
|
|
|
442
443
|
// src/plugins/guides/constants.ts
|
|
443
|
-
var GUIDE_Z_INDEX, DEFAULT_ANCHOR_WAIT_MS, SAFE_URL_PROTOCOLS, GUIDE_HOST_ATTR, FREQUENCY_CACHE_KEY_PREFIX, DEFAULT_TRACKER_FLUSH_INTERVAL_MS, DEFAULT_TRACKER_BATCH_SIZE, DEFAULT_TRACKER_MAX_RETRIES, FREQUENCY_CACHE_MAX_ENTRIES, FREQUENCY_CACHE_TTL_MS, WALKTHROUGH_STATE_KEY_PREFIX, WALKTHROUGH_ABANDONMENT_TIMEOUT_MS, WALKTHROUGH_STEP_SELECTOR_TIMEOUT_MS;
|
|
444
|
+
var GUIDE_Z_INDEX, DEFAULT_ANCHOR_WAIT_MS, SAFE_URL_PROTOCOLS, GUIDE_HOST_ATTR, BADGE_MARKER_HOST_ATTR, FREQUENCY_CACHE_KEY_PREFIX, DEFAULT_TRACKER_FLUSH_INTERVAL_MS, DEFAULT_TRACKER_BATCH_SIZE, DEFAULT_TRACKER_MAX_RETRIES, FREQUENCY_CACHE_MAX_ENTRIES, FREQUENCY_CACHE_TTL_MS, WALKTHROUGH_STATE_KEY_PREFIX, WALKTHROUGH_ABANDONMENT_TIMEOUT_MS, WALKTHROUGH_STEP_SELECTOR_TIMEOUT_MS;
|
|
444
445
|
var init_constants2 = __esm({
|
|
445
446
|
"src/plugins/guides/constants.ts"() {
|
|
446
447
|
GUIDE_Z_INDEX = 2147483640;
|
|
447
448
|
DEFAULT_ANCHOR_WAIT_MS = 5e3;
|
|
448
449
|
SAFE_URL_PROTOCOLS = ["http:", "https:"];
|
|
449
450
|
GUIDE_HOST_ATTR = "data-veo-guide";
|
|
451
|
+
BADGE_MARKER_HOST_ATTR = "data-veo-badge";
|
|
450
452
|
FREQUENCY_CACHE_KEY_PREFIX = "veo:freq:";
|
|
451
453
|
DEFAULT_TRACKER_FLUSH_INTERVAL_MS = 3e3;
|
|
452
454
|
DEFAULT_TRACKER_BATCH_SIZE = 5;
|
|
@@ -2056,6 +2058,25 @@ var init_base_renderer = __esm({
|
|
|
2056
2058
|
this.shadow = shadow;
|
|
2057
2059
|
return { host, shadow, root };
|
|
2058
2060
|
}
|
|
2061
|
+
/**
|
|
2062
|
+
* Host SUELTO con los mismos estilos (shadow DOM), sin agregarlo al DOM ni
|
|
2063
|
+
* registrarlo como `this.host`. Para piezas que deben vivir dentro de la app
|
|
2064
|
+
* (el marcador de un badge, junto a su ancla) mientras la tarjeta se queda
|
|
2065
|
+
* en `body`: si la tarjeta viajara dentro del ancla, un ancestro con
|
|
2066
|
+
* `transform`/`overflow` la recortaría y su z-index quedaría atrapado.
|
|
2067
|
+
*/
|
|
2068
|
+
createDetachedHost(attr = BADGE_MARKER_HOST_ATTR) {
|
|
2069
|
+
const host = document.createElement("div");
|
|
2070
|
+
host.setAttribute(attr, "");
|
|
2071
|
+
const shadow = host.attachShadow({ mode: "open" });
|
|
2072
|
+
const style = document.createElement("style");
|
|
2073
|
+
style.textContent = GUIDE_STYLES;
|
|
2074
|
+
shadow.appendChild(style);
|
|
2075
|
+
const root = document.createElement("div");
|
|
2076
|
+
shadow.appendChild(root);
|
|
2077
|
+
this.registerCleanup(() => host.remove());
|
|
2078
|
+
return { host, root };
|
|
2079
|
+
}
|
|
2059
2080
|
/**
|
|
2060
2081
|
* Aplica la tematización por datos del step (`step.style`) como variables
|
|
2061
2082
|
* CSS sobre el host. Llamar tras `createHost()`. No-op si no hay host o
|
|
@@ -3592,6 +3613,18 @@ var init_snooze = __esm({
|
|
|
3592
3613
|
};
|
|
3593
3614
|
}
|
|
3594
3615
|
});
|
|
3616
|
+
|
|
3617
|
+
// src/plugins/guides/z-index.ts
|
|
3618
|
+
function clampZIndex(value, fallback) {
|
|
3619
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return fallback;
|
|
3620
|
+
return Math.max(-MAX_Z_INDEX, Math.min(MAX_Z_INDEX, Math.round(value)));
|
|
3621
|
+
}
|
|
3622
|
+
var MAX_Z_INDEX;
|
|
3623
|
+
var init_z_index = __esm({
|
|
3624
|
+
"src/plugins/guides/z-index.ts"() {
|
|
3625
|
+
MAX_Z_INDEX = 2147483647;
|
|
3626
|
+
}
|
|
3627
|
+
});
|
|
3595
3628
|
function num(v, fallback) {
|
|
3596
3629
|
return typeof v === "number" && Number.isFinite(v) ? v : fallback;
|
|
3597
3630
|
}
|
|
@@ -3741,7 +3774,7 @@ function canNest(anchor) {
|
|
|
3741
3774
|
}
|
|
3742
3775
|
function attachMarker(anchor, host, marker) {
|
|
3743
3776
|
host.style.position = "absolute";
|
|
3744
|
-
host.style.zIndex = String(
|
|
3777
|
+
host.style.zIndex = String(clampZIndex(marker.zIndex, DEFAULT_MARKER_Z));
|
|
3745
3778
|
host.style.margin = "0";
|
|
3746
3779
|
const position = marker.position ?? "top-right";
|
|
3747
3780
|
const offset6 = { x: num(marker.offsetX, 0), y: num(marker.offsetY, 0) };
|
|
@@ -3816,6 +3849,7 @@ function attachMarker(anchor, host, marker) {
|
|
|
3816
3849
|
var INFO_GLYPH, QUESTION_GLYPH, STAR_GLYPH, CIRCLE, TARGET_GLYPH, BADGE_ICONS, DEFAULT_MARKER_SIZE, DEFAULT_MARKER_Z, SIDE_GAP, NO_CHILDREN;
|
|
3817
3850
|
var init_badge_marker = __esm({
|
|
3818
3851
|
"src/plugins/guides/renderers/badge-marker.ts"() {
|
|
3852
|
+
init_z_index();
|
|
3819
3853
|
INFO_GLYPH = "M11.25 11.25l.041-.02a.75.75 0 011.063.852l-.708 2.836a.75.75 0 001.063.853l.041-.021";
|
|
3820
3854
|
QUESTION_GLYPH = "M9.879 7.519c1.171-1.025 3.071-1.025 4.242 0 1.172 1.025 1.172 2.687 0 3.712-.203.179-.43.326-.67.442-.745.361-1.45.999-1.45 1.827v.75";
|
|
3821
3855
|
STAR_GLYPH = "M11.48 3.499a.562.562 0 011.04 0l2.125 5.111a.563.563 0 00.475.345l5.518.442c.499.04.701.663.321.988l-4.204 3.602a.563.563 0 00-.182.557l1.285 5.385a.562.562 0 01-.84.61l-4.725-2.885a.563.563 0 00-.586 0L6.982 20.54a.562.562 0 01-.84-.61l1.285-5.386a.562.562 0 00-.182-.557l-4.204-3.602a.563.563 0 01.321-.988l5.518-.442a.563.563 0 00.475-.345L11.48 3.5z";
|
|
@@ -3942,7 +3976,7 @@ function liftTarget(anchor, zIndex) {
|
|
|
3942
3976
|
const prevPosition = el2.style.position;
|
|
3943
3977
|
const prevZ = el2.style.zIndex;
|
|
3944
3978
|
if (getComputedStyle(el2).position === "static") el2.style.position = "relative";
|
|
3945
|
-
el2.style.zIndex = String(zIndex);
|
|
3979
|
+
el2.style.zIndex = String(clampZIndex(zIndex, 0));
|
|
3946
3980
|
return () => {
|
|
3947
3981
|
el2.style.position = prevPosition;
|
|
3948
3982
|
el2.style.zIndex = prevZ;
|
|
@@ -4033,6 +4067,7 @@ var init_composite_renderer = __esm({
|
|
|
4033
4067
|
init_scroll_lock();
|
|
4034
4068
|
init_snooze();
|
|
4035
4069
|
init_wait_for_element();
|
|
4070
|
+
init_z_index();
|
|
4036
4071
|
init_badge_marker();
|
|
4037
4072
|
init_base_renderer();
|
|
4038
4073
|
init_floating_arrow();
|
|
@@ -4080,7 +4115,9 @@ var init_composite_renderer = __esm({
|
|
|
4080
4115
|
const { host, root } = this.createHost();
|
|
4081
4116
|
const doc = root.ownerDocument ?? document;
|
|
4082
4117
|
const design = step.design ?? {};
|
|
4083
|
-
if (typeof design.zIndex === "number")
|
|
4118
|
+
if (typeof design.zIndex === "number") {
|
|
4119
|
+
host.style.zIndex = String(clampZIndex(design.zIndex, 0));
|
|
4120
|
+
}
|
|
4084
4121
|
const card = doc.createElement("div");
|
|
4085
4122
|
card.className = "veo-card";
|
|
4086
4123
|
const themeMode = themeModeFor(context.guide.theme, design);
|
|
@@ -4189,7 +4226,7 @@ var init_composite_renderer = __esm({
|
|
|
4189
4226
|
const bg = doc.createElement("div");
|
|
4190
4227
|
bg.className = "veo-composite-overlay veo-composite-overlay--dim";
|
|
4191
4228
|
if (typeof design.backdropColor === "string") bg.style.background = design.backdropColor;
|
|
4192
|
-
const bz =
|
|
4229
|
+
const bz = clampZIndex(design.backdropZIndex, 2e5);
|
|
4193
4230
|
bg.style.zIndex = String(bz);
|
|
4194
4231
|
root.appendChild(bg);
|
|
4195
4232
|
this.registerCleanup(lockScroll(doc));
|
|
@@ -4211,7 +4248,7 @@ var init_composite_renderer = __esm({
|
|
|
4211
4248
|
overlay.style.background = design.backdropColor;
|
|
4212
4249
|
}
|
|
4213
4250
|
if (typeof design.backdropZIndex === "number") {
|
|
4214
|
-
overlay.style.zIndex = String(design.backdropZIndex);
|
|
4251
|
+
overlay.style.zIndex = String(clampZIndex(design.backdropZIndex, 0));
|
|
4215
4252
|
}
|
|
4216
4253
|
overlay.addEventListener("click", (e) => {
|
|
4217
4254
|
if (e.target === overlay) {
|
|
@@ -4532,6 +4569,7 @@ var init_composite_renderer = __esm({
|
|
|
4532
4569
|
renderBadgeMode(step, card, anchor, anchorSelector, host, root, doc, context, idx, design) {
|
|
4533
4570
|
const trigger = step.badge?.trigger === "click" ? "click" : "hover";
|
|
4534
4571
|
const placement = step.badge?.placement === "top" || step.badge?.placement === "left" || step.badge?.placement === "right" ? step.badge.placement : "bottom";
|
|
4572
|
+
const { host: markerHost, root: markerRoot } = this.createDetachedHost();
|
|
4535
4573
|
const badge = doc.createElement("div");
|
|
4536
4574
|
badge.className = "veo-composite-badge";
|
|
4537
4575
|
badge.setAttribute("role", "button");
|
|
@@ -4542,7 +4580,8 @@ var init_composite_renderer = __esm({
|
|
|
4542
4580
|
const marker = step.badge?.marker ?? null;
|
|
4543
4581
|
if (marker) badge.appendChild(buildMarkerElement(doc, marker));
|
|
4544
4582
|
else this.buildContent(badge, step.badge?.blocks ?? [], doc, context, idx, [], host);
|
|
4545
|
-
|
|
4583
|
+
markerRoot.appendChild(badge);
|
|
4584
|
+
host.style.display = "block";
|
|
4546
4585
|
if (design.fullWidth === true) {
|
|
4547
4586
|
card.style.width = "auto";
|
|
4548
4587
|
card.style.maxWidth = "320px";
|
|
@@ -4557,13 +4596,13 @@ var init_composite_renderer = __esm({
|
|
|
4557
4596
|
wrapper.appendChild(card);
|
|
4558
4597
|
root.appendChild(wrapper);
|
|
4559
4598
|
if (marker) {
|
|
4560
|
-
|
|
4561
|
-
this.registerCleanup(attachMarker(anchor,
|
|
4599
|
+
markerHost.style.display = "inline-block";
|
|
4600
|
+
this.registerCleanup(attachMarker(anchor, markerHost, marker));
|
|
4562
4601
|
} else {
|
|
4563
|
-
|
|
4602
|
+
markerHost.style.display = "inline-block";
|
|
4564
4603
|
const pos = inlinePosOf(step.inlinePosition);
|
|
4565
|
-
insertHost(anchor,
|
|
4566
|
-
this.registerCleanup(keepHostAttached(
|
|
4604
|
+
insertHost(anchor, markerHost, pos);
|
|
4605
|
+
this.registerCleanup(keepHostAttached(markerHost, anchorSelector, pos));
|
|
4567
4606
|
}
|
|
4568
4607
|
let open = false;
|
|
4569
4608
|
let stopFloat = null;
|
|
@@ -5690,14 +5729,19 @@ function injectBuilderChrome(opts) {
|
|
|
5690
5729
|
let previewOn = false;
|
|
5691
5730
|
let hiddenOn = false;
|
|
5692
5731
|
let heightCb = null;
|
|
5693
|
-
|
|
5732
|
+
let tagging = false;
|
|
5733
|
+
const height = () => CHROME_TOPBAR_H + (collapsed || tagging ? 0 : CHROME_STEPS_H);
|
|
5694
5734
|
const applyHeight = () => {
|
|
5695
5735
|
const h = previewOn || hiddenOn ? 0 : height();
|
|
5696
5736
|
html.style.marginTop = dock === "top" ? `${h}px` : prevMarginTop;
|
|
5697
5737
|
html.style.marginBottom = dock === "bottom" ? `${h}px` : prevMarginBottom;
|
|
5698
5738
|
root.style.setProperty("--veo-chrome-h", `${height()}px`);
|
|
5699
5739
|
root.classList.toggle("is-bottom", dock === "bottom");
|
|
5700
|
-
stepsBar.hidden = collapsed;
|
|
5740
|
+
stepsBar.hidden = collapsed || tagging;
|
|
5741
|
+
togglePill.hidden = tagging;
|
|
5742
|
+
preview.hidden = tagging;
|
|
5743
|
+
saveGroup.hidden = tagging;
|
|
5744
|
+
showPanel.title = tagging ? "Mostrar el panel" : "Mostrar el editor";
|
|
5701
5745
|
const open = dock === "top" ? "chevronUp" : "chevronDown";
|
|
5702
5746
|
const closedIcon = dock === "top" ? "chevronDown" : "chevronUp";
|
|
5703
5747
|
togglePill.innerHTML = `<span>${collapsed ? "Mostrar pasos" : "Ocultar pasos"}</span>${svg(
|
|
@@ -5750,8 +5794,13 @@ function injectBuilderChrome(opts) {
|
|
|
5750
5794
|
const render = () => {
|
|
5751
5795
|
const s = current;
|
|
5752
5796
|
if (!s) return;
|
|
5797
|
+
const nextTagging = s.variant === "tagging";
|
|
5798
|
+
if (nextTagging !== tagging) {
|
|
5799
|
+
tagging = nextTagging;
|
|
5800
|
+
applyHeight();
|
|
5801
|
+
}
|
|
5753
5802
|
const date = shortDate(s.createdAt);
|
|
5754
|
-
nameEl.textContent = `${s.guideName.trim() || "Sin nombre"}${date ? ` \xB7 ${date}` : ""}`;
|
|
5803
|
+
nameEl.textContent = tagging ? s.guideName.trim() || "Modo tagging" : `${s.guideName.trim() || "Sin nombre"}${date ? ` \xB7 ${date}` : ""}`;
|
|
5755
5804
|
if (s.status) {
|
|
5756
5805
|
statusEl.hidden = false;
|
|
5757
5806
|
statusEl.textContent = s.status.label;
|
|
@@ -5871,6 +5920,8 @@ var init_builder_chrome = __esm({
|
|
|
5871
5920
|
font-size: 14px; color: #1c1917;
|
|
5872
5921
|
}
|
|
5873
5922
|
.veo-chrome[hidden] { display: none; }
|
|
5923
|
+
/* Modo tagging oculta controles por atributo: gana a los display de sus clases. */
|
|
5924
|
+
.veo-chrome [hidden], .veo-steps-toggle[hidden] { display: none !important; }
|
|
5874
5925
|
.veo-chrome.is-bottom { top: auto; bottom: 0; flex-direction: column-reverse; }
|
|
5875
5926
|
.veo-chrome.is-bottom .veo-tb-menu { top: auto; bottom: calc(100% + 8px); }
|
|
5876
5927
|
.veo-chrome.is-bottom .veo-steps { border-bottom: 0; border-top: 1px solid #d6cfc8; }
|
|
@@ -6503,6 +6554,65 @@ var init_design_manipulator = __esm({
|
|
|
6503
6554
|
}
|
|
6504
6555
|
});
|
|
6505
6556
|
|
|
6557
|
+
// src/plugins/builder/interactive-elements.ts
|
|
6558
|
+
function isInteractiveElement(element) {
|
|
6559
|
+
try {
|
|
6560
|
+
return element.matches(INTERACTIVE_SELECTOR);
|
|
6561
|
+
} catch {
|
|
6562
|
+
return false;
|
|
6563
|
+
}
|
|
6564
|
+
}
|
|
6565
|
+
function closestInteractive(element) {
|
|
6566
|
+
try {
|
|
6567
|
+
return element.closest(INTERACTIVE_SELECTOR);
|
|
6568
|
+
} catch {
|
|
6569
|
+
return null;
|
|
6570
|
+
}
|
|
6571
|
+
}
|
|
6572
|
+
function elementLabelText(element, max = 120) {
|
|
6573
|
+
const clean = (raw) => (raw ?? "").replace(EMOJI_RE, "").replace(/\s+/g, " ").trim();
|
|
6574
|
+
const text = clean(element.textContent);
|
|
6575
|
+
if (text) return text.slice(0, max);
|
|
6576
|
+
const fallback = clean(element.getAttribute("aria-label")) || clean(element.getAttribute("title"));
|
|
6577
|
+
return fallback.slice(0, max);
|
|
6578
|
+
}
|
|
6579
|
+
var INTERACTIVE_ROLES, INTERACTIVE_SELECTOR, EMOJI_RE;
|
|
6580
|
+
var init_interactive_elements = __esm({
|
|
6581
|
+
"src/plugins/builder/interactive-elements.ts"() {
|
|
6582
|
+
INTERACTIVE_ROLES = [
|
|
6583
|
+
"button",
|
|
6584
|
+
"link",
|
|
6585
|
+
"tab",
|
|
6586
|
+
"menuitem",
|
|
6587
|
+
"menuitemcheckbox",
|
|
6588
|
+
"menuitemradio",
|
|
6589
|
+
"checkbox",
|
|
6590
|
+
"radio",
|
|
6591
|
+
"switch",
|
|
6592
|
+
"option",
|
|
6593
|
+
"combobox",
|
|
6594
|
+
"textbox",
|
|
6595
|
+
"searchbox",
|
|
6596
|
+
"slider",
|
|
6597
|
+
"spinbutton"
|
|
6598
|
+
];
|
|
6599
|
+
INTERACTIVE_SELECTOR = [
|
|
6600
|
+
"button",
|
|
6601
|
+
"a[href]",
|
|
6602
|
+
'input:not([type="hidden"])',
|
|
6603
|
+
"select",
|
|
6604
|
+
"textarea",
|
|
6605
|
+
"form",
|
|
6606
|
+
"summary",
|
|
6607
|
+
'[contenteditable="true"]',
|
|
6608
|
+
"[onclick]",
|
|
6609
|
+
"[data-veo-tag]",
|
|
6610
|
+
...INTERACTIVE_ROLES.map((r) => `[role="${r}"]`)
|
|
6611
|
+
].join(", ");
|
|
6612
|
+
EMOJI_RE = /\p{Extended_Pictographic}|\p{Emoji_Modifier}|\u{FE0F}|\u{200D}|\u{20E3}/gu;
|
|
6613
|
+
}
|
|
6614
|
+
});
|
|
6615
|
+
|
|
6506
6616
|
// src/plugins/builder/element-picker.ts
|
|
6507
6617
|
function startElementPicker(options) {
|
|
6508
6618
|
if (!hasDocument()) {
|
|
@@ -6521,6 +6631,7 @@ var init_element_picker = __esm({
|
|
|
6521
6631
|
"src/plugins/builder/element-picker.ts"() {
|
|
6522
6632
|
init_safe_env();
|
|
6523
6633
|
init_selector_builder();
|
|
6634
|
+
init_interactive_elements();
|
|
6524
6635
|
PICKER_Z = 2147483646;
|
|
6525
6636
|
PICKER_HOST_ATTR = "data-veo-picker";
|
|
6526
6637
|
DEFAULT_MAX_ANCESTORS = 5;
|
|
@@ -6590,21 +6701,24 @@ var init_element_picker = __esm({
|
|
|
6590
6701
|
this.label = null;
|
|
6591
6702
|
this.current = null;
|
|
6592
6703
|
this.maxAncestors = DEFAULT_MAX_ANCESTORS;
|
|
6704
|
+
this.only = null;
|
|
6593
6705
|
this.onPick = null;
|
|
6594
6706
|
this.onCancel = null;
|
|
6595
6707
|
this.onMove = (event) => {
|
|
6596
|
-
|
|
6597
|
-
|
|
6708
|
+
if (!(event.target instanceof Element) || this.isOwn(event.target)) return;
|
|
6709
|
+
const target = this.resolveTarget(event.target);
|
|
6598
6710
|
this.current = target;
|
|
6599
|
-
this.draw(target);
|
|
6711
|
+
if (target) this.draw(target);
|
|
6712
|
+
else this.hideBox();
|
|
6600
6713
|
};
|
|
6601
6714
|
this.onClick = (event) => {
|
|
6602
6715
|
if (event.altKey) return;
|
|
6603
|
-
|
|
6604
|
-
if (!target || this.isOwn(target)) return;
|
|
6716
|
+
if (this.isOwn(event.target)) return;
|
|
6605
6717
|
event.preventDefault();
|
|
6606
6718
|
event.stopPropagation();
|
|
6607
6719
|
event.stopImmediatePropagation();
|
|
6720
|
+
const target = this.current ?? this.resolveTarget(event.target);
|
|
6721
|
+
if (!target) return;
|
|
6608
6722
|
const picked = this.describe(target);
|
|
6609
6723
|
const cb = this.onPick;
|
|
6610
6724
|
this.stop();
|
|
@@ -6631,6 +6745,7 @@ var init_element_picker = __esm({
|
|
|
6631
6745
|
start(options) {
|
|
6632
6746
|
this.stop();
|
|
6633
6747
|
this.maxAncestors = options.maxAncestors ?? DEFAULT_MAX_ANCESTORS;
|
|
6748
|
+
this.only = options.only ?? null;
|
|
6634
6749
|
this.onPick = options.onPick;
|
|
6635
6750
|
this.onCancel = options.onCancel ?? null;
|
|
6636
6751
|
const host = document.createElement("div");
|
|
@@ -6693,9 +6808,19 @@ var init_element_picker = __esm({
|
|
|
6693
6808
|
this.box = null;
|
|
6694
6809
|
this.label = null;
|
|
6695
6810
|
this.current = null;
|
|
6811
|
+
this.only = null;
|
|
6696
6812
|
this.onPick = null;
|
|
6697
6813
|
this.onCancel = null;
|
|
6698
6814
|
}
|
|
6815
|
+
/** Elemento que se elegiría para el nodo bajo el cursor (o null si no hay). */
|
|
6816
|
+
resolveTarget(target) {
|
|
6817
|
+
if (!(target instanceof Element) || this.isOwn(target)) return null;
|
|
6818
|
+
return this.only === "interactive" ? closestInteractive(target) : target;
|
|
6819
|
+
}
|
|
6820
|
+
hideBox() {
|
|
6821
|
+
if (this.box) this.box.style.display = "none";
|
|
6822
|
+
if (this.label) this.label.style.display = "none";
|
|
6823
|
+
}
|
|
6699
6824
|
/** ¿Es un nodo de nuestro propio overlay? (no debe ser seleccionable). */
|
|
6700
6825
|
isOwn(target) {
|
|
6701
6826
|
return target instanceof Node && this.host !== null && this.host.contains(target);
|
|
@@ -6732,7 +6857,7 @@ var init_element_picker = __esm({
|
|
|
6732
6857
|
tag: element.tagName.toLowerCase(),
|
|
6733
6858
|
id: element.id || null,
|
|
6734
6859
|
classes: filterHumanClasses(element),
|
|
6735
|
-
text: (element
|
|
6860
|
+
text: elementLabelText(element),
|
|
6736
6861
|
rect: {
|
|
6737
6862
|
x: Math.round(rect.left),
|
|
6738
6863
|
y: Math.round(rect.top),
|
|
@@ -7728,7 +7853,7 @@ function initBuilderMode() {
|
|
|
7728
7853
|
if (h && hoverCard && !previewMode) renderCardChrome(h, previewStepIndex);
|
|
7729
7854
|
}
|
|
7730
7855
|
panel?.setTitle(
|
|
7731
|
-
cmd.state.section === "activation" ? "Ajustes de activaci\xF3n" : cmd.state.section === "badge" ? "Ajustes de badge" : cmd.state.section === "target" ? "Ajustes del elemento objetivo" : cmd.state.section === "row" ? "Editar fila" : cmd.state.blockLabel ? `Editor \xB7 ${cmd.state.blockLabel}` : cmd.state.steps[cmd.state.activeStep]?.label ?? "Paso"
|
|
7856
|
+
cmd.state.panelTitle ?? (cmd.state.section === "activation" ? "Ajustes de activaci\xF3n" : cmd.state.section === "badge" ? "Ajustes de badge" : cmd.state.section === "target" ? "Ajustes del elemento objetivo" : cmd.state.section === "row" ? "Editar fila" : cmd.state.blockLabel ? `Editor \xB7 ${cmd.state.blockLabel}` : cmd.state.steps[cmd.state.activeStep]?.label ?? "Paso")
|
|
7732
7857
|
);
|
|
7733
7858
|
break;
|
|
7734
7859
|
case "panel-visibility":
|
|
@@ -7766,7 +7891,8 @@ function initBuilderMode() {
|
|
|
7766
7891
|
post({ type: "pick-cancelled" });
|
|
7767
7892
|
},
|
|
7768
7893
|
...cmd.maxAncestors !== void 0 ? { maxAncestors: cmd.maxAncestors } : {},
|
|
7769
|
-
...cmd.hint !== void 0 ? { hint: cmd.hint } : {}
|
|
7894
|
+
...cmd.hint !== void 0 ? { hint: cmd.hint } : {},
|
|
7895
|
+
...cmd.only !== void 0 ? { only: cmd.only } : {}
|
|
7770
7896
|
});
|
|
7771
7897
|
break;
|
|
7772
7898
|
}
|
|
@@ -7775,6 +7901,15 @@ function initBuilderMode() {
|
|
|
7775
7901
|
picker = null;
|
|
7776
7902
|
chrome?.setHidden(false);
|
|
7777
7903
|
break;
|
|
7904
|
+
case "scan-page":
|
|
7905
|
+
post({ type: "page-scan", payload: scanPage(cmd.selectors) });
|
|
7906
|
+
break;
|
|
7907
|
+
case "highlight":
|
|
7908
|
+
setHighlight(cmd.selector);
|
|
7909
|
+
break;
|
|
7910
|
+
case "show-confirmation":
|
|
7911
|
+
showConfirmation(cmd.payload);
|
|
7912
|
+
break;
|
|
7778
7913
|
case "preview":
|
|
7779
7914
|
if (!cmd.guide) break;
|
|
7780
7915
|
if (manipulator?.isDragging()) pendingPreview = cmd;
|
|
@@ -7859,10 +7994,154 @@ function initBuilderMode() {
|
|
|
7859
7994
|
post({ type: "closed" });
|
|
7860
7995
|
panel?.teardown();
|
|
7861
7996
|
panel = null;
|
|
7997
|
+
highlightBox?.remove();
|
|
7998
|
+
highlightBox = null;
|
|
7999
|
+
confirmation?.remove();
|
|
8000
|
+
confirmation = null;
|
|
8001
|
+
if (confirmationKey) document.removeEventListener("keydown", confirmationKey, true);
|
|
8002
|
+
confirmationKey = null;
|
|
7862
8003
|
chrome?.teardown();
|
|
7863
8004
|
chrome = null;
|
|
7864
8005
|
clearBuilderToken();
|
|
7865
8006
|
};
|
|
8007
|
+
const scanPage = (selectors) => {
|
|
8008
|
+
const present = selectors.filter((sel) => {
|
|
8009
|
+
try {
|
|
8010
|
+
return document.querySelector(sel) !== null;
|
|
8011
|
+
} catch {
|
|
8012
|
+
return false;
|
|
8013
|
+
}
|
|
8014
|
+
});
|
|
8015
|
+
const taggedEls = /* @__PURE__ */ new Set();
|
|
8016
|
+
for (const sel of present) {
|
|
8017
|
+
try {
|
|
8018
|
+
for (const e of Array.from(document.querySelectorAll(sel))) taggedEls.add(e);
|
|
8019
|
+
} catch {
|
|
8020
|
+
}
|
|
8021
|
+
}
|
|
8022
|
+
const seen = /* @__PURE__ */ new Set();
|
|
8023
|
+
const candidates = [];
|
|
8024
|
+
const nodes = document.querySelectorAll(INTERACTIVE_SELECTOR);
|
|
8025
|
+
for (const element of Array.from(nodes)) {
|
|
8026
|
+
if (candidates.length >= 200) break;
|
|
8027
|
+
if (element.closest("[data-veo-builder-chrome],[data-veo-builder-panel],[data-veo-picker]")) {
|
|
8028
|
+
continue;
|
|
8029
|
+
}
|
|
8030
|
+
if (element.getClientRects().length === 0) continue;
|
|
8031
|
+
let tagged = false;
|
|
8032
|
+
for (let n = element; n; n = n.parentElement) {
|
|
8033
|
+
if (taggedEls.has(n)) {
|
|
8034
|
+
tagged = true;
|
|
8035
|
+
break;
|
|
8036
|
+
}
|
|
8037
|
+
}
|
|
8038
|
+
if (tagged) continue;
|
|
8039
|
+
const selector = buildSelectorPath(element, 5);
|
|
8040
|
+
if (!selector || seen.has(selector)) continue;
|
|
8041
|
+
seen.add(selector);
|
|
8042
|
+
candidates.push({
|
|
8043
|
+
selector,
|
|
8044
|
+
shortSelector: buildSelectorPath(element, 0),
|
|
8045
|
+
tag: element.tagName.toLowerCase(),
|
|
8046
|
+
id: element.id || null,
|
|
8047
|
+
classes: filterHumanClasses(element),
|
|
8048
|
+
text: elementLabelText(element)
|
|
8049
|
+
});
|
|
8050
|
+
}
|
|
8051
|
+
const { pathname, hash } = window.location;
|
|
8052
|
+
const routePath = /^#\/./.test(hash) ? `${pathname}${hash}` : pathname;
|
|
8053
|
+
return { pagePath: pathname, routePath, candidates, present };
|
|
8054
|
+
};
|
|
8055
|
+
let confirmation = null;
|
|
8056
|
+
let confirmationKey = null;
|
|
8057
|
+
const closeConfirmation = () => {
|
|
8058
|
+
if (!confirmation) return;
|
|
8059
|
+
confirmation.remove();
|
|
8060
|
+
confirmation = null;
|
|
8061
|
+
if (confirmationKey) document.removeEventListener("keydown", confirmationKey, true);
|
|
8062
|
+
confirmationKey = null;
|
|
8063
|
+
panel?.setVisible(true);
|
|
8064
|
+
post({ type: "confirmation-closed" });
|
|
8065
|
+
};
|
|
8066
|
+
const showConfirmation = (card) => {
|
|
8067
|
+
if (confirmation) {
|
|
8068
|
+
confirmation.remove();
|
|
8069
|
+
confirmation = null;
|
|
8070
|
+
}
|
|
8071
|
+
panel?.setVisible(false);
|
|
8072
|
+
const host = document.createElement("div");
|
|
8073
|
+
host.setAttribute("data-veo-builder-chrome", "");
|
|
8074
|
+
host.setAttribute("data-veo-confirmation", "");
|
|
8075
|
+
const shadow = host.attachShadow({ mode: "open" });
|
|
8076
|
+
const style = document.createElement("style");
|
|
8077
|
+
style.textContent = CONFIRMATION_STYLES;
|
|
8078
|
+
const backdrop = document.createElement("div");
|
|
8079
|
+
backdrop.className = "veo-confirm-backdrop";
|
|
8080
|
+
const dialog = document.createElement("div");
|
|
8081
|
+
dialog.className = "veo-confirm";
|
|
8082
|
+
dialog.setAttribute("role", "dialog");
|
|
8083
|
+
dialog.setAttribute("aria-modal", "true");
|
|
8084
|
+
dialog.setAttribute("aria-label", card.title);
|
|
8085
|
+
const badge = document.createElement("div");
|
|
8086
|
+
badge.className = "veo-confirm-check";
|
|
8087
|
+
badge.innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12.5l4.5 4.5L19 7.5"/></svg>';
|
|
8088
|
+
const title = document.createElement("h2");
|
|
8089
|
+
title.className = "veo-confirm-title";
|
|
8090
|
+
title.textContent = card.title;
|
|
8091
|
+
const message = document.createElement("p");
|
|
8092
|
+
message.className = "veo-confirm-msg";
|
|
8093
|
+
message.textContent = card.message;
|
|
8094
|
+
const rows = document.createElement("dl");
|
|
8095
|
+
rows.className = "veo-confirm-rows";
|
|
8096
|
+
for (const r of card.rows) {
|
|
8097
|
+
const row = document.createElement("div");
|
|
8098
|
+
row.className = "veo-confirm-row";
|
|
8099
|
+
const dt = document.createElement("dt");
|
|
8100
|
+
dt.textContent = r.label;
|
|
8101
|
+
const dd = document.createElement("dd");
|
|
8102
|
+
dd.textContent = r.value;
|
|
8103
|
+
row.append(dt, dd);
|
|
8104
|
+
rows.appendChild(row);
|
|
8105
|
+
}
|
|
8106
|
+
const button = document.createElement("button");
|
|
8107
|
+
button.type = "button";
|
|
8108
|
+
button.className = "veo-confirm-btn";
|
|
8109
|
+
button.textContent = card.buttonLabel ?? "Listo";
|
|
8110
|
+
button.addEventListener("click", closeConfirmation);
|
|
8111
|
+
dialog.append(badge, title, message);
|
|
8112
|
+
if (card.rows.length > 0) dialog.appendChild(rows);
|
|
8113
|
+
dialog.appendChild(button);
|
|
8114
|
+
shadow.append(style, backdrop, dialog);
|
|
8115
|
+
document.body.appendChild(host);
|
|
8116
|
+
confirmation = host;
|
|
8117
|
+
confirmationKey = (e) => {
|
|
8118
|
+
if (e.key !== "Escape") return;
|
|
8119
|
+
e.preventDefault();
|
|
8120
|
+
e.stopPropagation();
|
|
8121
|
+
closeConfirmation();
|
|
8122
|
+
};
|
|
8123
|
+
document.addEventListener("keydown", confirmationKey, true);
|
|
8124
|
+
button.focus();
|
|
8125
|
+
};
|
|
8126
|
+
let highlightBox = null;
|
|
8127
|
+
const setHighlight = (selector) => {
|
|
8128
|
+
highlightBox?.remove();
|
|
8129
|
+
highlightBox = null;
|
|
8130
|
+
if (!selector) return;
|
|
8131
|
+
let target = null;
|
|
8132
|
+
try {
|
|
8133
|
+
target = document.querySelector(selector);
|
|
8134
|
+
} catch {
|
|
8135
|
+
return;
|
|
8136
|
+
}
|
|
8137
|
+
if (!target) return;
|
|
8138
|
+
const r = target.getBoundingClientRect();
|
|
8139
|
+
const box = document.createElement("div");
|
|
8140
|
+
box.setAttribute("data-veo-builder-chrome", "");
|
|
8141
|
+
box.style.cssText = `position:fixed;left:${r.left - 3}px;top:${r.top - 3}px;width:${r.width + 6}px;height:${r.height + 6}px;border:2px solid #FF5B35;border-radius:6px;background:rgba(255,91,53,0.10);pointer-events:none;z-index:2147483646;`;
|
|
8142
|
+
document.body.appendChild(box);
|
|
8143
|
+
highlightBox = box;
|
|
8144
|
+
};
|
|
7866
8145
|
if (ctx.panel) {
|
|
7867
8146
|
chrome = injectBuilderChrome({
|
|
7868
8147
|
appLabel: window.location.hostname,
|
|
@@ -7990,10 +8269,12 @@ function previewProgressFetcher() {
|
|
|
7990
8269
|
return data.items ?? [];
|
|
7991
8270
|
};
|
|
7992
8271
|
}
|
|
8272
|
+
var CONFIRMATION_STYLES;
|
|
7993
8273
|
var init_builder_mode = __esm({
|
|
7994
8274
|
"src/plugins/builder/builder-mode.ts"() {
|
|
7995
8275
|
init_builder_token();
|
|
7996
8276
|
init_safe_env();
|
|
8277
|
+
init_selector_builder();
|
|
7997
8278
|
init_activation_devices();
|
|
7998
8279
|
init_activation_rules();
|
|
7999
8280
|
init_block_builder();
|
|
@@ -8008,6 +8289,42 @@ var init_builder_mode = __esm({
|
|
|
8008
8289
|
init_builder_panel();
|
|
8009
8290
|
init_design_manipulator();
|
|
8010
8291
|
init_element_picker();
|
|
8292
|
+
init_interactive_elements();
|
|
8293
|
+
CONFIRMATION_STYLES = `
|
|
8294
|
+
:host { all: initial; }
|
|
8295
|
+
.veo-confirm-backdrop {
|
|
8296
|
+
position: fixed; inset: 0; z-index: 2147483645;
|
|
8297
|
+
background: rgba(20, 16, 14, 0.45);
|
|
8298
|
+
}
|
|
8299
|
+
.veo-confirm {
|
|
8300
|
+
position: fixed; left: 50%; top: 50%; transform: translate(-50%, -50%);
|
|
8301
|
+
z-index: 2147483646; box-sizing: border-box;
|
|
8302
|
+
width: min(432px, calc(100vw - 32px)); padding: 28px 24px 24px;
|
|
8303
|
+
border-radius: 16px; background: #fff; color: #1d1917;
|
|
8304
|
+
box-shadow: 0 24px 64px rgba(0,0,0,0.28);
|
|
8305
|
+
font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
|
|
8306
|
+
text-align: center;
|
|
8307
|
+
}
|
|
8308
|
+
.veo-confirm-check {
|
|
8309
|
+
width: 64px; height: 64px; margin: 0 auto; border-radius: 50%;
|
|
8310
|
+
background: #e6f6df; color: #2f9a4e; display: flex; align-items: center; justify-content: center;
|
|
8311
|
+
}
|
|
8312
|
+
.veo-confirm-check svg { width: 30px; height: 30px; }
|
|
8313
|
+
.veo-confirm-title { margin: 18px 0 0; font-size: 17px; font-weight: 600; }
|
|
8314
|
+
.veo-confirm-msg { margin: 8px 0 0; font-size: 13px; color: #6b6461; }
|
|
8315
|
+
.veo-confirm-rows {
|
|
8316
|
+
margin: 18px 0 0; padding: 12px 14px; border-radius: 12px; background: #f6f2ef; text-align: left;
|
|
8317
|
+
}
|
|
8318
|
+
.veo-confirm-row { display: flex; justify-content: space-between; gap: 16px; font-size: 13px; padding: 4px 0; }
|
|
8319
|
+
.veo-confirm-row dt { color: #6b6461; margin: 0; }
|
|
8320
|
+
.veo-confirm-row dd { margin: 0; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
8321
|
+
.veo-confirm-btn {
|
|
8322
|
+
display: block; width: 100%; margin-top: 18px; height: 44px; border: 0; border-radius: 10px;
|
|
8323
|
+
background: #b74d00; color: #fff; font: inherit; font-size: 15px; font-weight: 600; cursor: pointer;
|
|
8324
|
+
}
|
|
8325
|
+
.veo-confirm-btn:hover { background: #953c00; }
|
|
8326
|
+
.veo-confirm-btn:focus-visible { outline: 3px solid rgba(183,77,0,0.35); outline-offset: 2px; }
|
|
8327
|
+
`;
|
|
8011
8328
|
}
|
|
8012
8329
|
});
|
|
8013
8330
|
|
|
@@ -8140,11 +8457,15 @@ var init_builder_session = __esm({
|
|
|
8140
8457
|
// src/plugins/builder/index.ts
|
|
8141
8458
|
var builder_exports = {};
|
|
8142
8459
|
__export(builder_exports, {
|
|
8460
|
+
INTERACTIVE_SELECTOR: () => INTERACTIVE_SELECTOR,
|
|
8143
8461
|
VEO_BLOCK_MIME: () => VEO_BLOCK_MIME,
|
|
8144
8462
|
attachBlockDropTarget: () => attachBlockDropTarget,
|
|
8145
8463
|
attachDesignManipulator: () => attachDesignManipulator,
|
|
8464
|
+
closestInteractive: () => closestInteractive,
|
|
8146
8465
|
createBuilderSession: () => createBuilderSession,
|
|
8466
|
+
elementLabelText: () => elementLabelText,
|
|
8147
8467
|
initBuilderMode: () => initBuilderMode,
|
|
8468
|
+
isInteractiveElement: () => isInteractiveElement,
|
|
8148
8469
|
startElementPicker: () => startElementPicker,
|
|
8149
8470
|
stopElementPicker: () => stopElementPicker
|
|
8150
8471
|
});
|
|
@@ -8155,6 +8476,7 @@ var init_builder = __esm({
|
|
|
8155
8476
|
init_builder_session();
|
|
8156
8477
|
init_design_manipulator();
|
|
8157
8478
|
init_element_picker();
|
|
8479
|
+
init_interactive_elements();
|
|
8158
8480
|
}
|
|
8159
8481
|
});
|
|
8160
8482
|
|
|
@@ -8624,7 +8946,7 @@ var init_detect_theme = __esm({
|
|
|
8624
8946
|
});
|
|
8625
8947
|
|
|
8626
8948
|
// src/contract/v1.ts
|
|
8627
|
-
var SDK_VERSION = "0.
|
|
8949
|
+
var SDK_VERSION = "0.6.4";
|
|
8628
8950
|
var DEFAULT_FLUSH_INTERVAL_MS = 5e3;
|
|
8629
8951
|
var DEFAULT_FLUSH_BATCH_SIZE = 20;
|
|
8630
8952
|
var DEFAULT_API_URL = "https://veo-backend-production-8c78.up.railway.app";
|
|
@@ -8694,6 +9016,298 @@ function definePlugin(plugin) {
|
|
|
8694
9016
|
|
|
8695
9017
|
// src/plugins/autocapture/dom-listener.ts
|
|
8696
9018
|
init_safe_env();
|
|
9019
|
+
|
|
9020
|
+
// src/plugins/autocapture/click-quality.ts
|
|
9021
|
+
init_safe_env();
|
|
9022
|
+
init_selector_builder();
|
|
9023
|
+
var NO_DEAD_TAGS = /* @__PURE__ */ new Set(["input", "textarea", "select", "option", "label"]);
|
|
9024
|
+
var MAX_ERROR_MESSAGE = 200;
|
|
9025
|
+
var HISTORY_MS = 3e3;
|
|
9026
|
+
var DEFAULTS = {
|
|
9027
|
+
reactionWindowMs: 1e3,
|
|
9028
|
+
errorWindowMs: 2e3,
|
|
9029
|
+
rageClicks: 3,
|
|
9030
|
+
rageWindowMs: 1e3,
|
|
9031
|
+
rageClicksSlow: 5,
|
|
9032
|
+
rageWindowSlowMs: 3e3,
|
|
9033
|
+
maxPerElement: 50
|
|
9034
|
+
};
|
|
9035
|
+
function describeError(value) {
|
|
9036
|
+
let text;
|
|
9037
|
+
if (value instanceof Error) text = `${value.name}: ${value.message}`;
|
|
9038
|
+
else if (typeof value === "string") text = value;
|
|
9039
|
+
else {
|
|
9040
|
+
try {
|
|
9041
|
+
text = JSON.stringify(value) ?? String(value);
|
|
9042
|
+
} catch {
|
|
9043
|
+
text = String(value);
|
|
9044
|
+
}
|
|
9045
|
+
}
|
|
9046
|
+
return text.slice(0, MAX_ERROR_MESSAGE);
|
|
9047
|
+
}
|
|
9048
|
+
var ClickQualityTracker = class {
|
|
9049
|
+
constructor(emit, options = {}) {
|
|
9050
|
+
this.emit = emit;
|
|
9051
|
+
this.installed = false;
|
|
9052
|
+
this.cleanups = [];
|
|
9053
|
+
// Sensores globales (contadores: cada observación compara su foto inicial).
|
|
9054
|
+
this.netCount = 0;
|
|
9055
|
+
this.navCount = 0;
|
|
9056
|
+
this.focusCount = 0;
|
|
9057
|
+
this.unloading = false;
|
|
9058
|
+
this.errors = [];
|
|
9059
|
+
this.history = /* @__PURE__ */ new Map();
|
|
9060
|
+
this.bursts = /* @__PURE__ */ new Map();
|
|
9061
|
+
this.emitted = /* @__PURE__ */ new Map();
|
|
9062
|
+
this.opts = { ...DEFAULTS, ...options };
|
|
9063
|
+
}
|
|
9064
|
+
install() {
|
|
9065
|
+
if (this.installed || !hasWindow()) return;
|
|
9066
|
+
this.installed = true;
|
|
9067
|
+
const on = (type, handler, capture = true) => {
|
|
9068
|
+
window.addEventListener(type, handler, capture);
|
|
9069
|
+
this.cleanups.push(() => window.removeEventListener(type, handler, capture));
|
|
9070
|
+
};
|
|
9071
|
+
on("error", (e) => {
|
|
9072
|
+
const err = e;
|
|
9073
|
+
this.pushError("error", err.error ?? err.message ?? "error");
|
|
9074
|
+
});
|
|
9075
|
+
on("unhandledrejection", (e) => {
|
|
9076
|
+
this.pushError("unhandledrejection", e.reason ?? "rejection");
|
|
9077
|
+
});
|
|
9078
|
+
on("popstate", () => this.navCount++);
|
|
9079
|
+
on("hashchange", () => this.navCount++);
|
|
9080
|
+
on("focusin", () => this.focusCount++);
|
|
9081
|
+
on("pagehide", () => {
|
|
9082
|
+
this.unloading = true;
|
|
9083
|
+
});
|
|
9084
|
+
on("beforeunload", () => {
|
|
9085
|
+
this.unloading = true;
|
|
9086
|
+
});
|
|
9087
|
+
this.patchHistory();
|
|
9088
|
+
this.patchConsole();
|
|
9089
|
+
this.patchFetch();
|
|
9090
|
+
this.patchXhr();
|
|
9091
|
+
}
|
|
9092
|
+
destroy() {
|
|
9093
|
+
for (const c of this.cleanups) c();
|
|
9094
|
+
this.cleanups = [];
|
|
9095
|
+
for (const b of this.bursts.values()) window.clearTimeout(b.timer);
|
|
9096
|
+
this.bursts.clear();
|
|
9097
|
+
this.history.clear();
|
|
9098
|
+
this.installed = false;
|
|
9099
|
+
}
|
|
9100
|
+
/** Llamar por cada clic real sobre un elemento interactivo, ANTES del throttle. */
|
|
9101
|
+
onClick(element, event) {
|
|
9102
|
+
if (!this.installed) return;
|
|
9103
|
+
const now = Date.now();
|
|
9104
|
+
const obs = { at: now, reacted: false };
|
|
9105
|
+
this.remember(element, obs);
|
|
9106
|
+
this.observeReaction(element, obs, event);
|
|
9107
|
+
this.observeErrors(element, now);
|
|
9108
|
+
this.trackRage(element, now);
|
|
9109
|
+
}
|
|
9110
|
+
// ---------------------------------------------------------------- dead ----
|
|
9111
|
+
observeReaction(element, obs, event) {
|
|
9112
|
+
const netAtClick = this.netCount;
|
|
9113
|
+
const navAtClick = this.navCount;
|
|
9114
|
+
const focusAtClick = this.focusCount;
|
|
9115
|
+
const hrefAtClick = window.location.href;
|
|
9116
|
+
const skipDead = this.isDeadExempt(element, event);
|
|
9117
|
+
let observer = null;
|
|
9118
|
+
if (typeof MutationObserver !== "undefined") {
|
|
9119
|
+
observer = new MutationObserver(() => {
|
|
9120
|
+
obs.reacted = true;
|
|
9121
|
+
observer?.disconnect();
|
|
9122
|
+
observer = null;
|
|
9123
|
+
});
|
|
9124
|
+
observer.observe(document.documentElement, {
|
|
9125
|
+
childList: true,
|
|
9126
|
+
attributes: true,
|
|
9127
|
+
characterData: true,
|
|
9128
|
+
subtree: true
|
|
9129
|
+
});
|
|
9130
|
+
}
|
|
9131
|
+
window.setTimeout(() => {
|
|
9132
|
+
observer?.disconnect();
|
|
9133
|
+
const reacted = obs.reacted || this.unloading || this.netCount !== netAtClick || this.navCount !== navAtClick || this.focusCount !== focusAtClick || window.location.href !== hrefAtClick;
|
|
9134
|
+
obs.reacted = reacted;
|
|
9135
|
+
if (!reacted && !skipDead) this.report("$dead_click", element, {});
|
|
9136
|
+
}, this.opts.reactionWindowMs);
|
|
9137
|
+
}
|
|
9138
|
+
isDeadExempt(element, event) {
|
|
9139
|
+
if (event && (event.ctrlKey || event.metaKey || event.shiftKey || event.altKey)) return true;
|
|
9140
|
+
if (event && typeof event.button === "number" && event.button !== 0) return true;
|
|
9141
|
+
const tag = element.tagName.toLowerCase();
|
|
9142
|
+
if (NO_DEAD_TAGS.has(tag)) return true;
|
|
9143
|
+
if (element.closest('[contenteditable=""],[contenteditable="true"]')) return true;
|
|
9144
|
+
const anchor = element.closest("a");
|
|
9145
|
+
if (anchor) {
|
|
9146
|
+
if (anchor.target === "_blank" || anchor.hasAttribute("download")) return true;
|
|
9147
|
+
const href = anchor.getAttribute("href") ?? "";
|
|
9148
|
+
if (/^(mailto|tel|sms):/i.test(href)) return true;
|
|
9149
|
+
}
|
|
9150
|
+
return false;
|
|
9151
|
+
}
|
|
9152
|
+
// --------------------------------------------------------------- error ----
|
|
9153
|
+
observeErrors(element, clickedAt) {
|
|
9154
|
+
const deadline = clickedAt + this.opts.errorWindowMs;
|
|
9155
|
+
const check = () => {
|
|
9156
|
+
const err = this.errors.find((e) => e.at >= clickedAt && e.at <= deadline);
|
|
9157
|
+
if (err) {
|
|
9158
|
+
this.report("$click_error", element, {
|
|
9159
|
+
$error_source: err.source,
|
|
9160
|
+
$error_message: err.message
|
|
9161
|
+
});
|
|
9162
|
+
return;
|
|
9163
|
+
}
|
|
9164
|
+
if (Date.now() < deadline) window.setTimeout(check, 250);
|
|
9165
|
+
};
|
|
9166
|
+
window.setTimeout(check, 250);
|
|
9167
|
+
}
|
|
9168
|
+
pushError(source, value) {
|
|
9169
|
+
const message = describeError(value);
|
|
9170
|
+
if (message.startsWith("[veo]")) return;
|
|
9171
|
+
this.errors.push({ at: Date.now(), source, message });
|
|
9172
|
+
const cutoff = Date.now() - this.opts.errorWindowMs - 1e3;
|
|
9173
|
+
while (this.errors.length > 0 && this.errors[0].at < cutoff) {
|
|
9174
|
+
this.errors.shift();
|
|
9175
|
+
}
|
|
9176
|
+
if (this.errors.length > 50) this.errors.splice(0, this.errors.length - 50);
|
|
9177
|
+
}
|
|
9178
|
+
// ---------------------------------------------------------------- rage ----
|
|
9179
|
+
remember(element, obs) {
|
|
9180
|
+
const list = this.history.get(element) ?? [];
|
|
9181
|
+
const cutoff = obs.at - HISTORY_MS;
|
|
9182
|
+
const kept = list.filter((o) => o.at >= cutoff);
|
|
9183
|
+
kept.push(obs);
|
|
9184
|
+
this.history.set(element, kept);
|
|
9185
|
+
if (this.history.size > 100) {
|
|
9186
|
+
for (const [el2, obsList] of this.history) {
|
|
9187
|
+
if (obsList.every((o) => o.at < cutoff)) this.history.delete(el2);
|
|
9188
|
+
}
|
|
9189
|
+
}
|
|
9190
|
+
}
|
|
9191
|
+
trackRage(element, now) {
|
|
9192
|
+
const list = this.history.get(element) ?? [];
|
|
9193
|
+
const fast = list.filter((o) => o.at >= now - this.opts.rageWindowMs).length;
|
|
9194
|
+
const slow = list.filter((o) => o.at >= now - this.opts.rageWindowSlowMs).length;
|
|
9195
|
+
const existing = this.bursts.get(element);
|
|
9196
|
+
const isRage = fast >= this.opts.rageClicks || slow >= this.opts.rageClicksSlow;
|
|
9197
|
+
if (!existing && !isRage) return;
|
|
9198
|
+
const burst = existing ?? {
|
|
9199
|
+
startedAt: (list.find((o) => o.at >= now - this.opts.rageWindowSlowMs) ?? { at: now }).at,
|
|
9200
|
+
count: 0,
|
|
9201
|
+
timer: 0
|
|
9202
|
+
};
|
|
9203
|
+
burst.count = list.filter((o) => o.at >= burst.startedAt).length;
|
|
9204
|
+
window.clearTimeout(burst.timer);
|
|
9205
|
+
burst.timer = window.setTimeout(() => {
|
|
9206
|
+
this.bursts.delete(element);
|
|
9207
|
+
const clicks = (this.history.get(element) ?? []).filter((o) => o.at >= burst.startedAt);
|
|
9208
|
+
this.report("$rage_click", element, {
|
|
9209
|
+
$click_count: Math.max(clicks.length, burst.count),
|
|
9210
|
+
$reacted_clicks: clicks.filter((o) => o.reacted).length
|
|
9211
|
+
});
|
|
9212
|
+
}, this.opts.reactionWindowMs + 50);
|
|
9213
|
+
this.bursts.set(element, burst);
|
|
9214
|
+
}
|
|
9215
|
+
// ---------------------------------------------------------------- emit ----
|
|
9216
|
+
report(kind, element, extra) {
|
|
9217
|
+
const key = buildSelectorPath(element, 5);
|
|
9218
|
+
const counts = this.emitted.get(key) ?? { $dead_click: 0, $rage_click: 0, $click_error: 0 };
|
|
9219
|
+
if (counts[kind] >= this.opts.maxPerElement) return;
|
|
9220
|
+
counts[kind]++;
|
|
9221
|
+
this.emitted.set(key, counts);
|
|
9222
|
+
try {
|
|
9223
|
+
this.emit(kind, element, extra);
|
|
9224
|
+
} catch {
|
|
9225
|
+
}
|
|
9226
|
+
}
|
|
9227
|
+
// ------------------------------------------------------------- patches ----
|
|
9228
|
+
patchHistory() {
|
|
9229
|
+
const h = window.history;
|
|
9230
|
+
if (!h) return;
|
|
9231
|
+
const origPush = h.pushState;
|
|
9232
|
+
const origReplace = h.replaceState;
|
|
9233
|
+
const self = this;
|
|
9234
|
+
h.pushState = function(...args) {
|
|
9235
|
+
self.navCount++;
|
|
9236
|
+
return origPush.apply(this, args);
|
|
9237
|
+
};
|
|
9238
|
+
h.replaceState = function(...args) {
|
|
9239
|
+
self.navCount++;
|
|
9240
|
+
return origReplace.apply(this, args);
|
|
9241
|
+
};
|
|
9242
|
+
this.cleanups.push(() => {
|
|
9243
|
+
h.pushState = origPush;
|
|
9244
|
+
h.replaceState = origReplace;
|
|
9245
|
+
});
|
|
9246
|
+
}
|
|
9247
|
+
patchConsole() {
|
|
9248
|
+
const orig = console.error;
|
|
9249
|
+
const self = this;
|
|
9250
|
+
console.error = function(...args) {
|
|
9251
|
+
try {
|
|
9252
|
+
self.pushError("console", args.map(describeError).join(" "));
|
|
9253
|
+
} catch {
|
|
9254
|
+
}
|
|
9255
|
+
return orig.apply(this, args);
|
|
9256
|
+
};
|
|
9257
|
+
this.cleanups.push(() => {
|
|
9258
|
+
console.error = orig;
|
|
9259
|
+
});
|
|
9260
|
+
}
|
|
9261
|
+
/**
|
|
9262
|
+
* `fetch`: cuenta la petición (reacción) y registra respuestas ≥500 o
|
|
9263
|
+
* rechazos como error. Se observa con `.then(ok, err)` sobre la promesa
|
|
9264
|
+
* original: eso marca el rechazo como manejado para el navegador, así que
|
|
9265
|
+
* un fetch fallido sin catch de la app no dispara `unhandledrejection` —
|
|
9266
|
+
* lo registramos nosotros como error de red.
|
|
9267
|
+
*/
|
|
9268
|
+
patchFetch() {
|
|
9269
|
+
if (typeof window.fetch !== "function") return;
|
|
9270
|
+
const orig = window.fetch;
|
|
9271
|
+
const self = this;
|
|
9272
|
+
window.fetch = function(...args) {
|
|
9273
|
+
self.netCount++;
|
|
9274
|
+
const p = orig.apply(this, args);
|
|
9275
|
+
if (p && typeof p.then === "function") {
|
|
9276
|
+
p.then(
|
|
9277
|
+
(res) => {
|
|
9278
|
+
if (res && res.status >= 500) self.pushError("fetch", `${res.status} ${res.url}`);
|
|
9279
|
+
},
|
|
9280
|
+
(err) => self.pushError("fetch", err)
|
|
9281
|
+
);
|
|
9282
|
+
}
|
|
9283
|
+
return p;
|
|
9284
|
+
};
|
|
9285
|
+
this.cleanups.push(() => {
|
|
9286
|
+
window.fetch = orig;
|
|
9287
|
+
});
|
|
9288
|
+
}
|
|
9289
|
+
patchXhr() {
|
|
9290
|
+
const proto = typeof XMLHttpRequest !== "undefined" ? XMLHttpRequest.prototype : null;
|
|
9291
|
+
if (!proto) return;
|
|
9292
|
+
const origSend = proto.send;
|
|
9293
|
+
const self = this;
|
|
9294
|
+
proto.send = function(...args) {
|
|
9295
|
+
self.netCount++;
|
|
9296
|
+
try {
|
|
9297
|
+
this.addEventListener("loadend", () => {
|
|
9298
|
+
if (this.status >= 500) self.pushError("xhr", `${this.status} ${this.responseURL}`);
|
|
9299
|
+
});
|
|
9300
|
+
} catch {
|
|
9301
|
+
}
|
|
9302
|
+
return origSend.apply(this, args);
|
|
9303
|
+
};
|
|
9304
|
+
this.cleanups.push(() => {
|
|
9305
|
+
proto.send = origSend;
|
|
9306
|
+
});
|
|
9307
|
+
}
|
|
9308
|
+
};
|
|
9309
|
+
|
|
9310
|
+
// src/plugins/autocapture/dom-listener.ts
|
|
8697
9311
|
init_constants();
|
|
8698
9312
|
|
|
8699
9313
|
// src/plugins/autocapture/element-serializer.ts
|
|
@@ -8804,6 +9418,7 @@ var DomListener = class {
|
|
|
8804
9418
|
constructor(client, config) {
|
|
8805
9419
|
this.client = client;
|
|
8806
9420
|
this.cleanups = [];
|
|
9421
|
+
this.quality = null;
|
|
8807
9422
|
const d = DEFAULT_AUTOCAPTURE_CONFIG;
|
|
8808
9423
|
this.config = {
|
|
8809
9424
|
enabled: config.enabled ?? d.enabled,
|
|
@@ -8816,12 +9431,20 @@ var DomListener = class {
|
|
|
8816
9431
|
throttleMs: config.throttleMs ?? d.throttleMs,
|
|
8817
9432
|
maxTextLength: config.maxTextLength ?? d.maxTextLength,
|
|
8818
9433
|
captureElementMetadata: config.captureElementMetadata ?? d.captureElementMetadata,
|
|
8819
|
-
captureAncestors: Math.min(config.captureAncestors ?? d.captureAncestors, MAX_ANCESTORS)
|
|
9434
|
+
captureAncestors: Math.min(config.captureAncestors ?? d.captureAncestors, MAX_ANCESTORS),
|
|
9435
|
+
captureClickQuality: config.captureClickQuality ?? d.captureClickQuality
|
|
8820
9436
|
};
|
|
8821
9437
|
this.throttle = new ElementThrottle(this.config.throttleMs);
|
|
8822
9438
|
}
|
|
8823
9439
|
install() {
|
|
8824
9440
|
if (!hasDocument() || !this.config.enabled) return;
|
|
9441
|
+
if (this.config.captureClicks && this.config.captureClickQuality) {
|
|
9442
|
+
this.quality = new ClickQualityTracker((kind, element, extra) => {
|
|
9443
|
+
const payload = buildAutocapturePayload("click", element, this.config);
|
|
9444
|
+
this.client.track(kind, { ...payload, ...extra });
|
|
9445
|
+
});
|
|
9446
|
+
this.quality.install();
|
|
9447
|
+
}
|
|
8825
9448
|
if (this.config.captureClicks) this.attach("click", (e) => this.handleClick(e));
|
|
8826
9449
|
if (this.config.captureSubmits) this.attach("submit", (e) => this.handleSubmit(e), true);
|
|
8827
9450
|
if (this.config.captureChanges) this.attach("change", (e) => this.handleChange(e), true);
|
|
@@ -8829,6 +9452,8 @@ var DomListener = class {
|
|
|
8829
9452
|
destroy() {
|
|
8830
9453
|
for (const cleanup of this.cleanups) cleanup();
|
|
8831
9454
|
this.cleanups = [];
|
|
9455
|
+
this.quality?.destroy();
|
|
9456
|
+
this.quality = null;
|
|
8832
9457
|
}
|
|
8833
9458
|
attach(eventName, handler, useCapture = false) {
|
|
8834
9459
|
const listener = (e) => {
|
|
@@ -8847,6 +9472,7 @@ var DomListener = class {
|
|
|
8847
9472
|
const meaningful = this.findMeaningfulAncestor(target);
|
|
8848
9473
|
if (!meaningful) return;
|
|
8849
9474
|
if (isElementBlocked(meaningful, this.config.blockSelectors)) return;
|
|
9475
|
+
this.quality?.onClick(meaningful, event);
|
|
8850
9476
|
if (!this.throttle.shouldFire(meaningful)) return;
|
|
8851
9477
|
this.emit("click", meaningful);
|
|
8852
9478
|
}
|
|
@@ -9352,6 +9978,7 @@ function stableStringify(value) {
|
|
|
9352
9978
|
var Client = class {
|
|
9353
9979
|
constructor(config) {
|
|
9354
9980
|
this.config = config;
|
|
9981
|
+
this.version = SDK_VERSION;
|
|
9355
9982
|
this.plugins = [];
|
|
9356
9983
|
this.currentOrganizationId = null;
|
|
9357
9984
|
this.listeners = {
|