@qaiddev/thumbs-embed 1.3.0 → 1.4.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/dist/bootstrap.d.ts +15 -0
- package/dist/chunks/annotate-BNibaDg3.js +367 -0
- package/dist/chunks/annotate-BNibaDg3.js.map +1 -0
- package/dist/chunks/bootstrap-U4yiPm5-.js +1099 -0
- package/dist/chunks/bootstrap-U4yiPm5-.js.map +1 -0
- package/dist/chunks/modal-BX5wqy4s.js +119 -0
- package/dist/chunks/modal-BX5wqy4s.js.map +1 -0
- package/dist/chunks/network-capture-DMbMwwwR.js +63 -0
- package/dist/chunks/network-capture-DMbMwwwR.js.map +1 -0
- package/dist/chunks/recording--IwOtRL2.js +256 -0
- package/dist/chunks/recording--IwOtRL2.js.map +1 -0
- package/dist/chunks/screenshot-BkOCLuS2.js +33 -0
- package/dist/chunks/screenshot-BkOCLuS2.js.map +1 -0
- package/dist/chunks/screenshot-dom-CME7tYRE.js +63 -0
- package/dist/chunks/screenshot-dom-CME7tYRE.js.map +1 -0
- package/dist/chunks/targeting-DwdDr9Cm.js +295 -0
- package/dist/chunks/targeting-DwdDr9Cm.js.map +1 -0
- package/dist/chunks/video-BOYqf2Im.js +209 -0
- package/dist/chunks/video-BOYqf2Im.js.map +1 -0
- package/dist/dom.d.ts +36 -0
- package/dist/embed.d.ts +53 -68
- package/dist/index.d.ts +10 -12
- package/dist/index.js +22 -0
- package/dist/index.js.map +1 -0
- package/dist/loader.d.ts +18 -0
- package/dist/loader.js +7 -0
- package/dist/loader.js.map +1 -0
- package/dist/modal.d.ts +49 -0
- package/dist/qaid.umd.cjs +23 -23
- package/dist/qaid.umd.cjs.map +1 -1
- package/dist/recording.d.ts +74 -0
- package/dist/targeting.d.ts +72 -0
- package/dist/types.d.ts +7 -1
- package/dist/video-capture.d.ts +7 -0
- package/dist/video-orientation.d.ts +2 -0
- package/dist/video-redaction.d.ts +58 -0
- package/package.json +9 -5
- package/dist/qaid.js +0 -2013
- package/dist/qaid.js.map +0 -1
|
@@ -0,0 +1,1099 @@
|
|
|
1
|
+
import { i as nt, T as st, a as rt, R as dt } from "./annotate-BNibaDg3.js";
|
|
2
|
+
const lt = "button{cursor:pointer}.qaid-buttons{display:flex;gap:.5rem}.qaid-buttons.qaid-vertical{flex-direction:column}.qaid-buttons.qaid-auto-container{position:fixed;z-index:50}.qaid-buttons.qaid-auto-container.qaid-bottom-right{bottom:1rem;right:1rem}.qaid-buttons.qaid-auto-container.qaid-bottom-left{bottom:1rem;left:1rem}.qaid-buttons.qaid-auto-container.qaid-top-right{top:1rem;right:1rem}.qaid-buttons.qaid-auto-container.qaid-top-left{top:1rem;left:1rem}.qaid-buttons.qaid-incognito{opacity:0;transition:opacity .2s ease-in-out}.qaid-buttons.qaid-incognito:hover{opacity:1}.qaid-buttons.qaid-dismissed{display:none!important}.qaid-dismiss-btn{width:20px;height:20px;padding:0;border:none;border-radius:50%;background:#0006;color:#fff;display:flex;align-items:center;justify-content:center;align-self:center;opacity:0;transition:opacity .15s,background .15s;cursor:pointer;pointer-events:auto;-webkit-appearance:none;appearance:none}.qaid-dismiss-btn:hover{background:#0009}.qaid-buttons:hover .qaid-dismiss-btn{opacity:1}button.qaid-btn-structural{display:inline-flex;align-items:center;justify-content:center;cursor:pointer;-webkit-appearance:none;appearance:none}.qaid-icon{width:24px;height:24px}.qaid-btn-structural:not(:has(svg)),.qaid-btn:not(:has(svg)){font-size:var(--qaid-icon-size, 24px);line-height:1}.qaid-emoji-icon{font-size:var(--qaid-icon-size, 24px);line-height:1}.qaid-buttons,.qaid-buttons *,.qaid-modal-container,.qaid-modal-container *{cursor:pointer!important}.qaid-tooltip-wrapper{position:relative}.qaid-tooltip-text{position:fixed;padding:.5rem .75rem;background:#1f2937;color:#fff;font-size:1rem;font-weight:600;border-radius:.5rem;white-space:nowrap;opacity:0;pointer-events:none;transition:opacity .15s;z-index:99999}.qaid-tooltip-text.qaid-tooltip-visible{opacity:1}.qaid-targeting-overlay{position:fixed;inset:0;z-index:40;pointer-events:none}.qaid-capture-layer{position:fixed;inset:0;pointer-events:none;z-index:9999}@keyframes qaid-slideDown{0%{transform:translateY(-100%)}to{transform:translateY(0)}}.qaid-vignette{position:fixed;inset:0;pointer-events:none;background:radial-gradient(ellipse at center,transparent 30%,rgba(0,0,0,.3) 70%,rgba(0,0,0,.6) 100%);z-index:41}.qaid-crosshair-h,.qaid-crosshair-v{position:fixed;pointer-events:none;z-index:42}.qaid-crosshair-h{left:0;right:0;height:1px;background:color-mix(in srgb,var(--qaid-negative) 60%,transparent)}.qaid-crosshair-v{top:0;bottom:0;width:1px;background:color-mix(in srgb,var(--qaid-negative) 60%,transparent)}.qaid-type-up .qaid-crosshair-h,.qaid-type-up .qaid-crosshair-v{background:color-mix(in srgb,var(--qaid-positive) 60%,transparent)}.qaid-highlight-box{position:fixed;top:0;left:0;pointer-events:none;z-index:41;display:none;border:3px solid color-mix(in srgb,var(--qaid-negative) 80%,transparent);border-radius:2px;background:color-mix(in srgb,var(--qaid-negative) 8%,transparent);will-change:transform,width,height}.qaid-type-up .qaid-highlight-box{border-color:color-mix(in srgb,var(--qaid-positive) 80%,transparent);background:color-mix(in srgb,var(--qaid-positive) 8%,transparent)}.qaid-scope{position:fixed;width:80px;height:80px;pointer-events:none;z-index:43;transform:translate(-50%,-50%)}.qaid-scope-ring{position:absolute;inset:10px;border:2px solid color-mix(in srgb,var(--qaid-negative) 80%,transparent);border-radius:50%}.qaid-type-up .qaid-scope-ring{border-color:color-mix(in srgb,var(--qaid-positive) 80%,transparent)}.qaid-scope-ring-inner{position:absolute;inset:20px;border:1px solid color-mix(in srgb,var(--qaid-negative) 50%,transparent);border-radius:50%}.qaid-type-up .qaid-scope-ring-inner{border-color:color-mix(in srgb,var(--qaid-positive) 50%,transparent)}.qaid-scope-dot{position:absolute;top:50%;left:50%;width:4px;height:4px;background:var(--qaid-negative);border-radius:50%;transform:translate(-50%,-50%)}.qaid-type-up .qaid-scope-dot{background:var(--qaid-positive)}.qaid-selected-marker{position:fixed;border:3px solid var(--qaid-marker, #6366f1);border-radius:50%;pointer-events:none;z-index:44;animation:qaid-markerPulse 1.5s ease-in-out infinite}@keyframes qaid-markerPulse{0%,to{opacity:1;transform:scale(1)}50%{opacity:.7;transform:scale(1.05)}}.qaid-backdrop{position:fixed;inset:0;z-index:45;background:#0000004d}.qaid-modal-container{position:fixed;z-index:50;display:flex;flex-direction:column;align-items:flex-start;max-height:calc(100vh - 32px);font-family:var(--qaid-font-family, system-ui, -apple-system, sans-serif);font-size:var(--qaid-font-size, 16px)}.qaid-modal-container.qaid-above{flex-direction:column-reverse}.qaid-modal-arrow{width:0;height:0;border-left:12px solid transparent;border-right:12px solid transparent;position:relative;align-self:flex-start}.qaid-modal-container.qaid-below .qaid-modal-arrow{border-bottom:12px solid light-dark(#ffffff,#1f2937)}.qaid-modal-container.qaid-above .qaid-modal-arrow{border-top:12px solid light-dark(#ffffff,#1f2937)}.qaid-modal-box{color-scheme:inherit;background:light-dark(#ffffff,#1f2937);border-radius:1rem;padding:1.5rem;box-shadow:0 25px 50px -12px light-dark(rgba(0,0,0,.25),rgba(0,0,0,.5));width:var(--qaid-modal-width, 400px);max-width:calc(100vw - 32px);max-height:calc(100vh - 60px);overflow-y:auto}.qaid-modal-header{display:flex;align-items:flex-start;gap:.75rem;margin-bottom:1rem}button.qaid-type-toggle{border:none;display:flex;align-items:center;justify-content:center;cursor:pointer;transition:all .2s;flex-shrink:0;-webkit-appearance:none;appearance:none}button.qaid-type-toggle:hover{transform:scale(1.1)}button.qaid-type-toggle:not(.qaid-type-toggle-custom){width:2.5rem;height:2.5rem;border-radius:50%}button.qaid-type-toggle:not(.qaid-type-toggle-custom) svg{width:1.25rem;height:1.25rem}button.qaid-type-toggle.qaid-type-up{background:var(--qaid-positive);color:#fff}button.qaid-type-toggle.qaid-type-down{background:var(--qaid-negative);color:#fff}.qaid-modal-header-text{flex:1;min-width:0}.qaid-modal-title{font-size:1.125rem;font-weight:700;margin:0 0 .25rem;color:light-dark(#1f2937,#f9fafb)}.qaid-modal-subtitle{color:light-dark(#6b7280,#9ca3af);margin:0;font-size:.875rem}.qaid-textarea{width:100%;height:6rem;padding:.75rem;border:1px solid light-dark(#d1d5db,#374151);border-radius:.5rem;font-family:inherit;font-size:1rem;resize:vertical;margin-bottom:1rem;box-sizing:border-box;background:light-dark(#ffffff,#111827);color:light-dark(#1f2937,#f9fafb)}.qaid-textarea:focus{outline:none;border-color:var(--qaid-marker);box-shadow:0 0 0 3px color-mix(in srgb,var(--qaid-marker) 20%,transparent)}.qaid-btn-row{display:flex;gap:.5rem;justify-content:flex-end}button.qaid-btn-submit{padding:.5rem 1rem;background:var(--qaid-marker);color:var(--qaid-marker-text, white);border:none;border-radius:.5rem;font-size:.875rem;font-weight:500;cursor:pointer;transition:background-color .2s,filter .2s;-webkit-appearance:none;appearance:none}button.qaid-btn-submit:hover{filter:brightness(.85)}button.qaid-btn-submit:focus{outline:none;box-shadow:0 0 0 3px color-mix(in srgb,var(--qaid-marker) 30%,transparent)}.qaid-bottom-sheet{position:fixed;bottom:0;left:0;right:0;z-index:50;animation:qaid-slideUpSheet .3s ease-out;font-family:var(--qaid-font-family, system-ui, -apple-system, sans-serif);font-size:var(--qaid-font-size, 16px)}.qaid-bottom-sheet-content{background:light-dark(#ffffff,#1f2937);border-radius:1rem 1rem 0 0;padding:1.5rem;padding-bottom:max(1.5rem,env(safe-area-inset-bottom))}.qaid-bottom-sheet-handle{width:36px;height:4px;background:light-dark(rgba(0,0,0,.2),rgba(255,255,255,.2));border-radius:2px;margin:0 auto 1rem}@keyframes qaid-slideUpSheet{0%{transform:translateY(100%)}to{transform:translateY(0)}}button.qaid-btn-record:hover{background:#dc2626;color:#fff}.qaid-recording-indicator{position:fixed;top:12px;left:50%;transform:translate(-50%);display:flex;align-items:center;gap:.5rem;padding:.5rem 1rem;background:light-dark(#1f2937,#374151);color:#fff;border-radius:9999px;font-family:var(--qaid-font-family, system-ui, -apple-system, sans-serif);font-size:.875rem;font-weight:500;box-shadow:0 4px 12px #0000004d;z-index:99999;animation:qaid-slideDown .2s ease-out}.qaid-recording-dot{width:10px;height:10px;background:#dc2626;border-radius:50%;animation:qaid-dotPulse 1.5s ease-in-out infinite}@keyframes qaid-dotPulse{0%,to{opacity:1}50%{opacity:.3}}.qaid-recording-time{font-variant-numeric:tabular-nums;min-width:2.5rem;text-align:center}button.qaid-recording-stop{padding:.25rem .75rem;background:#dc2626;color:#fff;border:none;border-radius:9999px;font-size:.75rem;font-weight:600;cursor:pointer;transition:background-color .2s;-webkit-appearance:none;appearance:none}button.qaid-recording-stop:hover{background:#b91c1c}.qaid-video-preview{position:fixed;inset:0 0 auto;height:100vh;height:100dvh;display:flex;align-items:center;justify-content:center;padding:16px;padding-top:max(16px,env(safe-area-inset-top));padding-bottom:max(16px,env(safe-area-inset-bottom));box-sizing:border-box;background:#0009;z-index:99998;animation:qaid-fadeIn .2s ease-out}@keyframes qaid-fadeIn{0%{opacity:0}to{opacity:1}}.qaid-video-preview-box{color-scheme:inherit;background:light-dark(#ffffff,#1f2937);border-radius:1rem;padding:1.5rem;box-shadow:0 25px 50px -12px #00000080;width:560px;max-width:100%;max-height:100%;overflow-y:auto;font-family:var(--qaid-font-family, system-ui, -apple-system, sans-serif);font-size:var(--qaid-font-size, 16px)}.qaid-video-preview-box h3{font-size:1.125rem;font-weight:700;margin:0 0 1rem;color:light-dark(#1f2937,#f9fafb)}.qaid-video-preview-box video{width:100%;max-height:50vh;max-height:50dvh;object-fit:contain;border-radius:.5rem;background:#000;margin-bottom:1rem}.qaid-video-preview-box textarea{width:100%;height:4rem;padding:.75rem;border:1px solid light-dark(#d1d5db,#374151);border-radius:.5rem;font-family:inherit;font-size:.875rem;resize:vertical;margin-bottom:1rem;box-sizing:border-box;background:light-dark(#ffffff,#111827);color:light-dark(#1f2937,#f9fafb)}.qaid-video-preview-box textarea:focus{outline:none;border-color:var(--qaid-marker);box-shadow:0 0 0 3px color-mix(in srgb,var(--qaid-marker) 20%,transparent)}.qaid-video-preview-actions{display:flex;gap:.5rem;justify-content:flex-end}button.qaid-video-btn{padding:.5rem 1rem;border:none;border-radius:.5rem;font-size:.875rem;font-weight:500;cursor:pointer;transition:background-color .2s,filter .2s;-webkit-appearance:none;appearance:none}button.qaid-video-btn-cancel{background:light-dark(#f3f4f6,#374151);color:light-dark(#374151,#d1d5db)}button.qaid-video-btn-cancel:hover{background:light-dark(#e5e7eb,#4b5563)}button.qaid-video-btn-rerecord{background:light-dark(#fef3c7,#78350f);color:light-dark(#92400e,#fde68a)}button.qaid-video-btn-rerecord:hover{filter:brightness(.9)}button.qaid-video-btn-send{background:var(--qaid-marker);color:var(--qaid-marker-text, white)}button.qaid-video-btn-send:hover{filter:brightness(.85)}button.qaid-video-btn-send:disabled{opacity:.5;cursor:not-allowed}.qaid-video-sending{display:flex;align-items:center;gap:.5rem;font-size:.875rem;color:light-dark(#6b7280,#9ca3af)}.qaid-annotate{position:fixed;inset:0;z-index:2147483000;display:flex;flex-direction:column;gap:.75rem;padding:16px;padding-top:max(16px,env(safe-area-inset-top));padding-bottom:max(16px,env(safe-area-inset-bottom));box-sizing:border-box;background:#000000d9;font-family:var(--qaid-font-family, system-ui, -apple-system, sans-serif);font-size:var(--qaid-font-size, 16px);animation:qaid-fadeIn .2s ease-out}.qaid-annotate-title{margin:0;font-size:1rem;font-weight:700;text-align:center;color:#f9fafb}.qaid-annotate-desc{margin:0;font-size:.8125rem;line-height:1.4;text-align:center;color:#f9fafbbf}.qaid-annotate-stage{flex:1 1 auto;min-height:0;display:flex;align-items:center;justify-content:center;overflow:auto}.qaid-annotate-canvas{max-width:100%;max-height:100%;object-fit:contain;background:light-dark(#ffffff,#111827);border-radius:.5rem;box-shadow:0 10px 30px #00000080;touch-action:none;cursor:crosshair}.qaid-annotate-toolbar{display:flex;flex-wrap:wrap;gap:.75rem;align-items:center;justify-content:center;padding:.5rem .75rem;background:light-dark(#ffffff,#1f2937);border-radius:.75rem;box-shadow:0 10px 30px light-dark(rgba(0,0,0,.25),rgba(0,0,0,.5))}.qaid-annotate-tools,.qaid-annotate-actions{display:flex;gap:.375rem;align-items:center}button.qaid-annotate-tool,button.qaid-annotate-action{display:inline-flex;align-items:center;justify-content:center;gap:.375rem;min-width:44px;min-height:44px;padding:0 .75rem;border:1px solid light-dark(#d1d5db,#374151);border-radius:.5rem;background:light-dark(#f9fafb,#111827);color:light-dark(#1f2937,#f9fafb);font-family:inherit;font-size:.8125rem;font-weight:600;cursor:pointer;-webkit-appearance:none;appearance:none;transition:background-color .15s,color .15s,border-color .15s,filter .15s}button.qaid-annotate-tool{padding:0}button.qaid-annotate-tool svg,button.qaid-annotate-action svg{width:20px;height:20px}button.qaid-annotate-tool:hover,button.qaid-annotate-action:hover{background:light-dark(#eef2ff,#312e81)}button.qaid-annotate-tool[aria-pressed=true],button.qaid-annotate-done{background:var(--qaid-marker, #6366f1);color:var(--qaid-marker-text, #fff);border-color:var(--qaid-marker, #6366f1)}button.qaid-annotate-done:hover{filter:brightness(.9);background:var(--qaid-marker, #6366f1)}button.qaid-annotate-action:disabled{opacity:.45;cursor:not-allowed}@media(forced-colors:active){button.qaid-annotate-tool[aria-pressed=true],button.qaid-annotate-done{border:2px solid Highlight}.qaid-annotate-canvas{border:1px solid CanvasText}}button:focus-visible,textarea:focus-visible,a:focus-visible,[tabindex]:focus-visible,[role=button]:focus-visible{outline:2px solid var(--qaid-marker, #6366f1);outline-offset:2px}.qaid-textarea:focus-visible,button.qaid-btn-submit:focus-visible,.qaid-video-preview-box textarea:focus-visible{outline:2px solid var(--qaid-marker, #6366f1);outline-offset:2px}.qaid-buttons.qaid-incognito:focus-within{opacity:1}.qaid-buttons:focus-within .qaid-dismiss-btn,.qaid-dismiss-btn:focus-visible{opacity:1}.qaid-dismiss-btn{min-width:24px;min-height:24px}button.qaid-recording-stop{min-height:24px}@media(prefers-reduced-motion:reduce){*,*:before,*:after{animation:none!important;transition:none!important;scroll-behavior:auto!important}}@media(forced-colors:active){button:focus-visible,textarea:focus-visible,a:focus-visible,[tabindex]:focus-visible,[role=button]:focus-visible,.qaid-textarea:focus,button.qaid-btn-submit:focus,.qaid-video-preview-box textarea:focus{outline:2px solid CanvasText;outline-offset:2px}.qaid-selected-marker,.qaid-highlight-box{border-color:Highlight}}", ct = "body.qaid-targeting,body.qaid-targeting *{cursor:none!important}";
|
|
3
|
+
let b = 0;
|
|
4
|
+
const ut = "button.qaid-btn{width:var(--qaid-btn-size);height:var(--qaid-btn-size);border-radius:50%;border:none;background:#f3f4f6;color:#374151;display:flex;align-items:center;justify-content:center;cursor:pointer;box-shadow:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -1px rgba(0,0,0,.06);transition:background-color .2s,color .2s,transform .2s;-webkit-appearance:none;appearance:none;--qaid-hover-up-bg:var(--qaid-positive);--qaid-hover-up-color:#fff;--qaid-hover-down-bg:var(--qaid-negative);--qaid-hover-down-color:#fff}button.qaid-btn:hover{transform:scale(1.05)}button.qaid-btn-up:hover{background:var(--qaid-hover-up-bg);color:var(--qaid-hover-up-color)}button.qaid-btn-down:hover{background:var(--qaid-hover-down-bg);color:var(--qaid-hover-down-color)}button.qaid-btn svg{width:var(--qaid-icon-size);height:var(--qaid-icon-size)}", ht = {
|
|
5
|
+
small: 36,
|
|
6
|
+
medium: 48,
|
|
7
|
+
large: 64
|
|
8
|
+
}, pt = {
|
|
9
|
+
small: 18,
|
|
10
|
+
medium: 24,
|
|
11
|
+
large: 32
|
|
12
|
+
};
|
|
13
|
+
function ft(e, t, i) {
|
|
14
|
+
const [o, s, a] = [e, t, i].map((n) => (n = n / 255, n <= 0.03928 ? n / 12.92 : Math.pow((n + 0.055) / 1.055, 2.4)));
|
|
15
|
+
return 0.2126 * o + 0.7152 * s + 0.0722 * a;
|
|
16
|
+
}
|
|
17
|
+
function bt(e) {
|
|
18
|
+
if (e.startsWith("#")) {
|
|
19
|
+
const i = e.slice(1), o = i.length === 3 ? i.split("").map((a) => a + a).join("") : i, s = parseInt(o, 16);
|
|
20
|
+
return {
|
|
21
|
+
r: s >> 16 & 255,
|
|
22
|
+
g: s >> 8 & 255,
|
|
23
|
+
b: s & 255
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
const t = e.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/);
|
|
27
|
+
return t ? {
|
|
28
|
+
r: parseInt(t[1]),
|
|
29
|
+
g: parseInt(t[2]),
|
|
30
|
+
b: parseInt(t[3])
|
|
31
|
+
} : null;
|
|
32
|
+
}
|
|
33
|
+
function mt(e) {
|
|
34
|
+
const t = bt(e);
|
|
35
|
+
return t && ft(t.r, t.g, t.b) > 0.4 ? "black" : "white";
|
|
36
|
+
}
|
|
37
|
+
function gt(e = {}) {
|
|
38
|
+
const {
|
|
39
|
+
positiveColor: t = "rgb(0, 200, 83)",
|
|
40
|
+
negativeColor: i = "rgb(255, 0, 0)",
|
|
41
|
+
markerColor: o = "#6366f1",
|
|
42
|
+
buttonSize: s = "medium",
|
|
43
|
+
modalWidth: a = 400,
|
|
44
|
+
backdropOpacity: n = 0.3,
|
|
45
|
+
fontFamily: l = "system-ui, -apple-system, sans-serif",
|
|
46
|
+
fontSize: c = 16
|
|
47
|
+
} = e, d = ht[s], r = pt[s], h = mt(o);
|
|
48
|
+
return {
|
|
49
|
+
"--qaid-positive": t,
|
|
50
|
+
"--qaid-negative": i,
|
|
51
|
+
"--qaid-marker": o,
|
|
52
|
+
"--qaid-marker-text": h,
|
|
53
|
+
"--qaid-btn-size": `${d}px`,
|
|
54
|
+
"--qaid-icon-size": `${r}px`,
|
|
55
|
+
"--qaid-modal-width": `${a}px`,
|
|
56
|
+
"--qaid-backdrop-opacity": String(n),
|
|
57
|
+
"--qaid-font-family": l,
|
|
58
|
+
"--qaid-font-size": `${c}px`
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function vt(e, t) {
|
|
62
|
+
for (const [i, o] of Object.entries(t))
|
|
63
|
+
e.style.setProperty(i, o);
|
|
64
|
+
}
|
|
65
|
+
function M() {
|
|
66
|
+
return lt + ut;
|
|
67
|
+
}
|
|
68
|
+
function yt() {
|
|
69
|
+
if (b++, b > 1) return;
|
|
70
|
+
const e = document.createElement("style");
|
|
71
|
+
e.id = "qaid-styles", e.textContent = ct, document.head.appendChild(e);
|
|
72
|
+
}
|
|
73
|
+
function wt() {
|
|
74
|
+
b <= 0 || (b--, b === 0 && document.getElementById("qaid-styles").remove());
|
|
75
|
+
}
|
|
76
|
+
const B = "data-qaid-a11y-live", qt = "position:absolute;width:1px;height:1px;margin:-1px;padding:0;border:0;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;", xt = [
|
|
77
|
+
"a[href]",
|
|
78
|
+
"button",
|
|
79
|
+
"input",
|
|
80
|
+
"textarea",
|
|
81
|
+
"select",
|
|
82
|
+
"[tabindex]"
|
|
83
|
+
].join(",");
|
|
84
|
+
function kt(e) {
|
|
85
|
+
return e.ownerDocument || document;
|
|
86
|
+
}
|
|
87
|
+
function St(e, t) {
|
|
88
|
+
const i = t ? "assertive" : "polite", o = e.querySelector(
|
|
89
|
+
`[${B}="${i}"]`
|
|
90
|
+
);
|
|
91
|
+
if (o) return o;
|
|
92
|
+
const a = kt(e).createElement("div");
|
|
93
|
+
return a.setAttribute(B, i), a.setAttribute("role", t ? "alert" : "status"), a.setAttribute("aria-live", t ? "assertive" : "polite"), a.setAttribute("aria-atomic", "true"), a.style.cssText = qt, e.appendChild(a), a;
|
|
94
|
+
}
|
|
95
|
+
function Ct(e, t, i = {}) {
|
|
96
|
+
const o = St(e, !!i.assertive);
|
|
97
|
+
o.textContent = "", o.textContent = t;
|
|
98
|
+
}
|
|
99
|
+
function At(e) {
|
|
100
|
+
return e.hasAttribute("disabled") ? !0 : e.disabled === !0;
|
|
101
|
+
}
|
|
102
|
+
function Tt(e) {
|
|
103
|
+
let t = e;
|
|
104
|
+
for (; t; ) {
|
|
105
|
+
if (t.hasAttribute("hidden")) return !0;
|
|
106
|
+
const i = t.style;
|
|
107
|
+
if (i && (i.display === "none" || i.visibility === "hidden" || i.visibility === "collapse"))
|
|
108
|
+
return !0;
|
|
109
|
+
t = t.parentElement;
|
|
110
|
+
}
|
|
111
|
+
return !1;
|
|
112
|
+
}
|
|
113
|
+
function O(e) {
|
|
114
|
+
return Array.from(
|
|
115
|
+
e.querySelectorAll(xt)
|
|
116
|
+
).filter((i) => !(i.getAttribute("tabindex") === "-1" || i instanceof HTMLInputElement && i.type === "hidden" || i instanceof HTMLAnchorElement && !i.getAttribute("href") || At(i) || Tt(i)));
|
|
117
|
+
}
|
|
118
|
+
function U(e) {
|
|
119
|
+
let t = e.activeElement;
|
|
120
|
+
for (; t && t.shadowRoot && t.shadowRoot.activeElement; )
|
|
121
|
+
t = t.shadowRoot.activeElement;
|
|
122
|
+
return t instanceof HTMLElement ? t : null;
|
|
123
|
+
}
|
|
124
|
+
function Dt(e) {
|
|
125
|
+
const t = e.ownerDocument || document;
|
|
126
|
+
let i = !1;
|
|
127
|
+
const o = (a) => {
|
|
128
|
+
if (a.key !== "Tab") return;
|
|
129
|
+
const n = O(e);
|
|
130
|
+
if (n.length === 0) {
|
|
131
|
+
a.preventDefault(), e.focus();
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
const l = n[0], c = n[n.length - 1], d = U(t), r = d ? n.indexOf(d) !== -1 : !1;
|
|
135
|
+
a.shiftKey ? (!r || d === l) && (a.preventDefault(), c.focus()) : (!r || d === c) && (a.preventDefault(), l.focus());
|
|
136
|
+
};
|
|
137
|
+
e.addEventListener("keydown", o);
|
|
138
|
+
const s = O(e);
|
|
139
|
+
return s.length > 0 ? s[0].focus() : (e.hasAttribute("tabindex") || (e.setAttribute("tabindex", "-1"), i = !0), e.focus()), {
|
|
140
|
+
release() {
|
|
141
|
+
e.removeEventListener("keydown", o), i && (e.removeAttribute("tabindex"), i = !1);
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
function It() {
|
|
146
|
+
return U(document);
|
|
147
|
+
}
|
|
148
|
+
function Et(e) {
|
|
149
|
+
if (!(!e || typeof e.focus != "function"))
|
|
150
|
+
try {
|
|
151
|
+
e.focus();
|
|
152
|
+
} catch {
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
function Ht(e, t = {}) {
|
|
156
|
+
e.setAttribute("role", "dialog"), e.setAttribute("aria-modal", "true"), t.labelledbyId && e.setAttribute("aria-labelledby", t.labelledbyId), t.describedbyId && e.setAttribute("aria-describedby", t.describedbyId), t.label && e.setAttribute("aria-label", t.label);
|
|
157
|
+
}
|
|
158
|
+
function zt(e, t) {
|
|
159
|
+
let i = e;
|
|
160
|
+
for (; i && i !== t; ) {
|
|
161
|
+
const o = i.parentNode;
|
|
162
|
+
if (o === t) return i;
|
|
163
|
+
if (o && o.host) {
|
|
164
|
+
i = o.host;
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
if (!o) return null;
|
|
168
|
+
i = o;
|
|
169
|
+
}
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
function Lt(e) {
|
|
173
|
+
const i = (e.ownerDocument || document).body;
|
|
174
|
+
if (!i) return () => {
|
|
175
|
+
};
|
|
176
|
+
const o = zt(e, i), s = [];
|
|
177
|
+
return Array.from(i.children).forEach((a) => {
|
|
178
|
+
if (!(a instanceof HTMLElement) || o && a === o) return;
|
|
179
|
+
const n = a.inert === !0, l = a.getAttribute("aria-hidden");
|
|
180
|
+
n && l === "true" || (a.inert = !0, a.setAttribute("aria-hidden", "true"), s.push({ el: a, prevInert: n, prevAriaHidden: l }));
|
|
181
|
+
}), function() {
|
|
182
|
+
for (; s.length; ) {
|
|
183
|
+
const n = s.pop();
|
|
184
|
+
n.el.inert = n.prevInert, n.prevAriaHidden === null ? n.el.removeAttribute("aria-hidden") : n.el.setAttribute("aria-hidden", n.prevAriaHidden);
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
const V = {
|
|
189
|
+
width: 400,
|
|
190
|
+
height: 280,
|
|
191
|
+
arrowHeight: 12,
|
|
192
|
+
gap: 8,
|
|
193
|
+
viewportPadding: 16
|
|
194
|
+
};
|
|
195
|
+
function Mt(e, t, i, o, s) {
|
|
196
|
+
const a = e.y, n = t - (e.y + e.height);
|
|
197
|
+
if (n >= i + o)
|
|
198
|
+
return {
|
|
199
|
+
top: e.y + e.height + o,
|
|
200
|
+
position: "below"
|
|
201
|
+
};
|
|
202
|
+
if (a >= i + o)
|
|
203
|
+
return {
|
|
204
|
+
top: e.y - i - o,
|
|
205
|
+
position: "above"
|
|
206
|
+
};
|
|
207
|
+
const l = n > a ? "below" : "above";
|
|
208
|
+
let c;
|
|
209
|
+
return l === "below" ? c = Math.min(
|
|
210
|
+
e.y + e.height + o,
|
|
211
|
+
t - i - s
|
|
212
|
+
) : c = Math.max(s, e.y - i - o), { top: c, position: l };
|
|
213
|
+
}
|
|
214
|
+
function Bt(e, t, i, o) {
|
|
215
|
+
let a = e.x + e.width / 2 - i / 2;
|
|
216
|
+
return a = Math.max(o, Math.min(a, t - i - o)), a;
|
|
217
|
+
}
|
|
218
|
+
function Ot(e, t, i, o = 24, s = 24) {
|
|
219
|
+
const n = e.clickX - t - s / 2;
|
|
220
|
+
return Math.max(o, Math.min(n, i - o - s / 2));
|
|
221
|
+
}
|
|
222
|
+
function Rt(e, t, i, o = V) {
|
|
223
|
+
const s = o.height + o.arrowHeight, a = Mt(
|
|
224
|
+
e,
|
|
225
|
+
i,
|
|
226
|
+
s,
|
|
227
|
+
o.gap,
|
|
228
|
+
o.viewportPadding
|
|
229
|
+
), n = Bt(
|
|
230
|
+
e,
|
|
231
|
+
t,
|
|
232
|
+
o.width,
|
|
233
|
+
o.viewportPadding
|
|
234
|
+
);
|
|
235
|
+
return {
|
|
236
|
+
top: a.top,
|
|
237
|
+
left: n,
|
|
238
|
+
position: a.position
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
function Pt(e, t, i, o = {}) {
|
|
242
|
+
const s = o.gap ?? 8;
|
|
243
|
+
let a = e.bottom + s;
|
|
244
|
+
a + t.height > i.height - s && (a = e.top - t.height - s);
|
|
245
|
+
let n = e.left;
|
|
246
|
+
return n < s ? n = s : n + t.width > i.width - s && (n = i.width - t.width - s), a < s ? a = s : a + t.height > i.height - s && (a = i.height - t.height - s), { top: a, left: n };
|
|
247
|
+
}
|
|
248
|
+
function Zt(e, t, i, o = V) {
|
|
249
|
+
const s = Rt(e, t, i, o), a = Ot(
|
|
250
|
+
e,
|
|
251
|
+
s.left,
|
|
252
|
+
o.width
|
|
253
|
+
);
|
|
254
|
+
return {
|
|
255
|
+
modal: s,
|
|
256
|
+
arrow: { left: a }
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
const Ft = 20;
|
|
260
|
+
function Ut(e) {
|
|
261
|
+
const t = [], i = console.error, o = console.warn, s = console.log, a = (n, l) => {
|
|
262
|
+
const c = {
|
|
263
|
+
message: l.map((d) => String(d)).join(" "),
|
|
264
|
+
timestamp: Date.now(),
|
|
265
|
+
level: n
|
|
266
|
+
};
|
|
267
|
+
t.length >= Ft && t.shift(), t.push(c), e && e(c);
|
|
268
|
+
};
|
|
269
|
+
return console.error = function(...n) {
|
|
270
|
+
a("error", n), i.apply(console, n);
|
|
271
|
+
}, console.warn = function(...n) {
|
|
272
|
+
a("warn", n), o.apply(console, n);
|
|
273
|
+
}, console.log = function(...n) {
|
|
274
|
+
a("log", n), s.apply(console, n);
|
|
275
|
+
}, {
|
|
276
|
+
errors: t,
|
|
277
|
+
restore: () => {
|
|
278
|
+
console.error = i, console.warn = o, console.log = s;
|
|
279
|
+
}
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
const Vt = "https://unpkg.com/@qaiddev/quests-embed@1/dist/qaid-quests.js", Kt = (e) => (
|
|
283
|
+
// The URL is a runtime value, not a static specifier — keep Vite from
|
|
284
|
+
// trying to analyze/bundle it.
|
|
285
|
+
import(
|
|
286
|
+
/* @vite-ignore */
|
|
287
|
+
e
|
|
288
|
+
)
|
|
289
|
+
);
|
|
290
|
+
let _t = Kt, f = null, w = null;
|
|
291
|
+
function $t(e) {
|
|
292
|
+
return f && w === e || (w = e, f = Promise.resolve(_t(e)).then((t) => {
|
|
293
|
+
const i = t;
|
|
294
|
+
if (!i || typeof i.QaidQuests != "function")
|
|
295
|
+
throw new Error("quests module has no QaidQuests export");
|
|
296
|
+
return i;
|
|
297
|
+
}).catch((t) => {
|
|
298
|
+
throw f = null, w = null, t;
|
|
299
|
+
})), f;
|
|
300
|
+
}
|
|
301
|
+
async function Nt(e) {
|
|
302
|
+
const t = await $t(e.moduleUrl), i = e.base.replace(/\/+$/, ""), o = e.feedbackId != null ? { feedbackId: e.feedbackId } : void 0;
|
|
303
|
+
return new t.QaidQuests({
|
|
304
|
+
endpoint: `${i}/responses`,
|
|
305
|
+
configUrl: `${i}/${encodeURIComponent(e.questId)}/definition`,
|
|
306
|
+
apiKey: e.apiKey || void 0,
|
|
307
|
+
metadata: o,
|
|
308
|
+
onClose: e.onClose
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
const R = "qaid_visitor_id", P = "qaid_hide_feedback";
|
|
312
|
+
function jt() {
|
|
313
|
+
return typeof window.matchMedia == "function" && window.matchMedia("(pointer: coarse)").matches;
|
|
314
|
+
}
|
|
315
|
+
function q(e) {
|
|
316
|
+
return e ? `${P}_${e}` : P;
|
|
317
|
+
}
|
|
318
|
+
function Qt(e) {
|
|
319
|
+
try {
|
|
320
|
+
return localStorage.getItem(q(e)) === "1";
|
|
321
|
+
} catch {
|
|
322
|
+
return !1;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
function F(e, t = !0) {
|
|
326
|
+
try {
|
|
327
|
+
t ? localStorage.setItem(q(e), "1") : localStorage.removeItem(q(e));
|
|
328
|
+
} catch {
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
function Wt() {
|
|
332
|
+
try {
|
|
333
|
+
let e = localStorage.getItem(R);
|
|
334
|
+
return e || (e = crypto.randomUUID(), localStorage.setItem(R, e)), e;
|
|
335
|
+
} catch {
|
|
336
|
+
return crypto.randomUUID();
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
class Yt {
|
|
340
|
+
config;
|
|
341
|
+
state = "IDLE";
|
|
342
|
+
feedbackData = {
|
|
343
|
+
feedbackType: null,
|
|
344
|
+
elementSelector: null,
|
|
345
|
+
elementText: null,
|
|
346
|
+
consoleErrors: []
|
|
347
|
+
};
|
|
348
|
+
selectedBounds = {
|
|
349
|
+
x: 0,
|
|
350
|
+
y: 0,
|
|
351
|
+
width: 0,
|
|
352
|
+
height: 0,
|
|
353
|
+
clickX: 0,
|
|
354
|
+
clickY: 0,
|
|
355
|
+
visible: !1
|
|
356
|
+
};
|
|
357
|
+
feedbackId = null;
|
|
358
|
+
// A quest launched from a button (in place of the message box), if any.
|
|
359
|
+
activeQuest = null;
|
|
360
|
+
isMobile = !1;
|
|
361
|
+
visitorId;
|
|
362
|
+
// Console capture
|
|
363
|
+
consoleCapture = null;
|
|
364
|
+
// Video recording — the whole subsystem lives in a lazily-loaded chunk
|
|
365
|
+
// (recording.ts); a thumbs-only visitor never downloads it.
|
|
366
|
+
recording = null;
|
|
367
|
+
// Element targeting — also a lazily-loaded chunk (targeting.ts).
|
|
368
|
+
targeting = null;
|
|
369
|
+
targetingPrewarmed = !1;
|
|
370
|
+
// Preload-on-intent: warm the lazily-split feature chunks before first use.
|
|
371
|
+
prewarmHandle = null;
|
|
372
|
+
prewarmIsTimeout = !1;
|
|
373
|
+
videoPrewarmed = !1;
|
|
374
|
+
screenshotPrewarmed = !1;
|
|
375
|
+
// Shadow DOM
|
|
376
|
+
shadowHost = null;
|
|
377
|
+
shadowRoot = null;
|
|
378
|
+
// Overlay shadow DOM (always on document.body for full-page coverage)
|
|
379
|
+
overlayShadowHost = null;
|
|
380
|
+
overlayShadowRoot = null;
|
|
381
|
+
// DOM elements (inside shadow root)
|
|
382
|
+
buttonsContainer = null;
|
|
383
|
+
isUserProvidedContainer = !1;
|
|
384
|
+
// The message modal lives in a lazily-loaded chunk (modal.ts).
|
|
385
|
+
modal = null;
|
|
386
|
+
modalPrewarmed = !1;
|
|
387
|
+
dismissBtn = null;
|
|
388
|
+
// Per-instance CSS variables
|
|
389
|
+
cssVars = {};
|
|
390
|
+
activeThumbBtn = null;
|
|
391
|
+
// Tracks whether the pending activation came from the keyboard (Enter/Space)
|
|
392
|
+
// rather than a pointer, so targeting can avoid elementFromPoint(0,0).
|
|
393
|
+
keyboardActivation = !1;
|
|
394
|
+
// Accessibility: dialog focus management
|
|
395
|
+
dialogTrigger = null;
|
|
396
|
+
dialogTrap = null;
|
|
397
|
+
dialogRestoreInert = null;
|
|
398
|
+
// Unique id suffix for aria-labelledby/describedby references
|
|
399
|
+
uid = Math.random().toString(36).slice(2, 9);
|
|
400
|
+
// Bound event handlers
|
|
401
|
+
boundKeyDown;
|
|
402
|
+
boundResize;
|
|
403
|
+
// Start with buttons slid off-screen (localStorage dismiss, no animation)
|
|
404
|
+
_startDismissed = !1;
|
|
405
|
+
// DOM persistence (survives framework client-side navigation)
|
|
406
|
+
destroyed = !1;
|
|
407
|
+
domObserver = null;
|
|
408
|
+
boundBeforeSwap = null;
|
|
409
|
+
constructor(t) {
|
|
410
|
+
this.config = {
|
|
411
|
+
endpoint: t.endpoint,
|
|
412
|
+
apiKey: t.apiKey ?? "",
|
|
413
|
+
container: t.container ?? "",
|
|
414
|
+
buttonClass: t.buttonClass ?? "",
|
|
415
|
+
direction: t.direction ?? "horizontal",
|
|
416
|
+
position: t.position ?? "bottom-right",
|
|
417
|
+
offset: {
|
|
418
|
+
x: t.offset?.x ?? 16,
|
|
419
|
+
y: t.offset?.y ?? 16
|
|
420
|
+
},
|
|
421
|
+
zIndex: t.zIndex ?? 50,
|
|
422
|
+
skipTargeting: t.skipTargeting ?? !1,
|
|
423
|
+
colors: {
|
|
424
|
+
positive: t.colors?.positive ?? "rgb(0, 200, 83)",
|
|
425
|
+
negative: t.colors?.negative ?? "rgb(255, 0, 0)",
|
|
426
|
+
marker: t.colors?.marker ?? "#6366f1"
|
|
427
|
+
},
|
|
428
|
+
buttonSize: t.buttonSize ?? "medium",
|
|
429
|
+
text: {
|
|
430
|
+
tooltip: t.text?.tooltip ?? "",
|
|
431
|
+
modalTitle: t.text?.modalTitle ?? "Thank you for your feedback!",
|
|
432
|
+
modalSubtitle: t.text?.modalSubtitle ?? "Would you like to add a message to help us understand your feedback better?",
|
|
433
|
+
placeholder: t.text?.placeholder ?? "Optional: Tell us more about your experience...",
|
|
434
|
+
submitButton: t.text?.submitButton ?? "Submit",
|
|
435
|
+
skipButton: t.text?.skipButton ?? "Skip",
|
|
436
|
+
positiveLabel: t.text?.positiveLabel ?? "Send positive feedback",
|
|
437
|
+
negativeLabel: t.text?.negativeLabel ?? "Send negative feedback",
|
|
438
|
+
recordLabel: t.text?.recordLabel ?? "Record a screen recording",
|
|
439
|
+
dismissLabel: t.text?.dismissLabel ?? "Hide Feedback"
|
|
440
|
+
},
|
|
441
|
+
modalWidth: t.modalWidth ?? 400,
|
|
442
|
+
backdropOpacity: t.backdropOpacity ?? 0.3,
|
|
443
|
+
fontFamily: t.fontFamily ?? "system-ui, -apple-system, sans-serif",
|
|
444
|
+
fontSize: t.fontSize ?? 16,
|
|
445
|
+
captureScreenshot: t.captureScreenshot ?? !1,
|
|
446
|
+
annotate: t.annotate ?? !0,
|
|
447
|
+
screenshotMethod: t.screenshotMethod ?? "permission",
|
|
448
|
+
screenshotOptions: {
|
|
449
|
+
quality: t.screenshotOptions?.quality ?? 0.8,
|
|
450
|
+
maxWidth: t.screenshotOptions?.maxWidth ?? 1280,
|
|
451
|
+
maxHeight: t.screenshotOptions?.maxHeight ?? 800
|
|
452
|
+
},
|
|
453
|
+
incognito: t.incognito ?? !1,
|
|
454
|
+
hideDismiss: t.hideDismiss ?? !1,
|
|
455
|
+
positiveIcon: t.positiveIcon ?? "",
|
|
456
|
+
negativeIcon: t.negativeIcon ?? "",
|
|
457
|
+
hideThumbs: t.hideThumbs ?? !1,
|
|
458
|
+
css: t.css ?? "",
|
|
459
|
+
captureVideo: t.captureVideo ?? !1,
|
|
460
|
+
videoOptions: {
|
|
461
|
+
maxDuration: t.videoOptions?.maxDuration ?? 15,
|
|
462
|
+
redaction: t.videoOptions?.redaction ?? !1
|
|
463
|
+
},
|
|
464
|
+
recordIcon: t.recordIcon ?? "",
|
|
465
|
+
quests: {
|
|
466
|
+
base: t.quests?.base ?? "",
|
|
467
|
+
up: t.quests?.up ?? "",
|
|
468
|
+
down: t.quests?.down ?? "",
|
|
469
|
+
video: t.quests?.video ?? "",
|
|
470
|
+
// Quest service reuses the feedback API key unless overridden.
|
|
471
|
+
apiKey: t.quests?.apiKey ?? t.apiKey ?? "",
|
|
472
|
+
moduleUrl: t.quests?.moduleUrl ?? Vt
|
|
473
|
+
}
|
|
474
|
+
}, this.boundKeyDown = this.handleKeyDown.bind(this), this.boundResize = this.handleResize.bind(this), this.visitorId = Wt(), this.init();
|
|
475
|
+
}
|
|
476
|
+
applyVars(t) {
|
|
477
|
+
vt(t, this.cssVars);
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* Announce a message via the shared visually-hidden live regions.
|
|
481
|
+
* Prefer the overlay shadow root (which hosts every transient surface and
|
|
482
|
+
* is never inerted by its own dialogs) so announcements are not suppressed
|
|
483
|
+
* while a dialog aria-hides the main button host.
|
|
484
|
+
*/
|
|
485
|
+
announceMsg(t, i = !1) {
|
|
486
|
+
const o = this.overlayShadowRoot ?? this.shadowRoot;
|
|
487
|
+
o && Ct(o, t, { assertive: i });
|
|
488
|
+
}
|
|
489
|
+
/**
|
|
490
|
+
* Turn a transient surface into an accessible modal dialog: save the
|
|
491
|
+
* invoking control, apply dialog semantics, trap focus, and inert the
|
|
492
|
+
* background. Paired with closeDialogA11y() on every close path.
|
|
493
|
+
*/
|
|
494
|
+
openDialogA11y(t, i) {
|
|
495
|
+
this.dialogTrigger = It(), Ht(t, i), this.dialogTrap = Dt(t), this.dialogRestoreInert = Lt(t);
|
|
496
|
+
}
|
|
497
|
+
closeDialogA11y() {
|
|
498
|
+
this.dialogTrap?.release(), this.dialogTrap = null, this.dialogRestoreInert && (this.dialogRestoreInert(), this.dialogRestoreInert = null), Et(this.dialogTrigger), this.dialogTrigger = null;
|
|
499
|
+
}
|
|
500
|
+
clearActiveThumb() {
|
|
501
|
+
this.activeThumbBtn && (this.activeThumbBtn.setAttribute("aria-pressed", "false"), this.activeThumbBtn = null);
|
|
502
|
+
}
|
|
503
|
+
init() {
|
|
504
|
+
yt(), this.cssVars = gt({
|
|
505
|
+
positiveColor: this.config.colors.positive,
|
|
506
|
+
negativeColor: this.config.colors.negative,
|
|
507
|
+
markerColor: this.config.colors.marker,
|
|
508
|
+
buttonSize: this.config.buttonSize,
|
|
509
|
+
modalWidth: this.config.modalWidth,
|
|
510
|
+
backdropOpacity: this.config.backdropOpacity,
|
|
511
|
+
fontFamily: this.config.fontFamily,
|
|
512
|
+
fontSize: this.config.fontSize
|
|
513
|
+
}), !this.config.hideDismiss && Qt(this.config.apiKey) && (this._startDismissed = !0), this.checkMobile(), window.addEventListener("resize", this.boundResize), this.createEmbed(), this.consoleCapture = Ut((t) => {
|
|
514
|
+
this.feedbackData.consoleErrors = this.consoleCapture?.errors ?? [];
|
|
515
|
+
}), this.feedbackData.consoleErrors = this.consoleCapture.errors, this.observeDom(), (this.config.captureVideo && this.videoSupported() || this.config.captureScreenshot) && this.schedulePrewarm(() => {
|
|
516
|
+
this.config.captureVideo && this.videoSupported() && this.prewarmVideo(), this.config.captureScreenshot && this.prewarmScreenshot();
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
/**
|
|
520
|
+
* Best-effort preload of a lazily-split feature chunk so its first use is
|
|
521
|
+
* instant. Warming fetches + compiles (and defines) the module; the feature
|
|
522
|
+
* modules have no load-time side effects, so this is safe. Errors are
|
|
523
|
+
* swallowed — a failed preload just falls back to an on-demand load.
|
|
524
|
+
*/
|
|
525
|
+
prewarmVideo() {
|
|
526
|
+
this.videoPrewarmed || (this.videoPrewarmed = !0, import("./recording--IwOtRL2.js").catch(() => {
|
|
527
|
+
}), import("./video-BOYqf2Im.js").catch(() => {
|
|
528
|
+
}));
|
|
529
|
+
}
|
|
530
|
+
prewarmScreenshot() {
|
|
531
|
+
this.screenshotPrewarmed || (this.screenshotPrewarmed = !0, (this.shouldCaptureViaDom() ? import("./screenshot-dom-CME7tYRE.js") : import("./screenshot-BkOCLuS2.js")).catch(() => {
|
|
532
|
+
}), this.config.annotate && import("./annotate-BNibaDg3.js").then((t) => t.f).catch(() => {
|
|
533
|
+
}));
|
|
534
|
+
}
|
|
535
|
+
/** Run fn when the main thread is idle; cancelled by destroy(). */
|
|
536
|
+
schedulePrewarm(t) {
|
|
537
|
+
const i = window, o = () => {
|
|
538
|
+
this.destroyed || t();
|
|
539
|
+
};
|
|
540
|
+
typeof i.requestIdleCallback == "function" ? (this.prewarmIsTimeout = !1, this.prewarmHandle = i.requestIdleCallback(o, { timeout: 2e3 })) : (this.prewarmIsTimeout = !0, this.prewarmHandle = window.setTimeout(o, 1200));
|
|
541
|
+
}
|
|
542
|
+
/**
|
|
543
|
+
* Watch for the shadow hosts being removed from the DOM by framework
|
|
544
|
+
* client-side navigation (e.g. Astro View Transitions swapping <body>
|
|
545
|
+
* contents, or any SPA router that replaces DOM subtrees). If the host
|
|
546
|
+
* is disconnected and destroy() wasn't called, re-append it.
|
|
547
|
+
*
|
|
548
|
+
* Also hooks into Astro's `astro:before-swap` when available, which
|
|
549
|
+
* lets us carry elements into the new document before the swap happens
|
|
550
|
+
* (avoids a flash of the widget disappearing and reappearing).
|
|
551
|
+
*/
|
|
552
|
+
observeDom() {
|
|
553
|
+
this.boundBeforeSwap = (t) => {
|
|
554
|
+
const i = t.newDocument;
|
|
555
|
+
!i || this.destroyed || (this.shadowHost && i.body.appendChild(this.shadowHost), this.overlayShadowHost && i.body.appendChild(this.overlayShadowHost));
|
|
556
|
+
}, document.addEventListener("astro:before-swap", this.boundBeforeSwap), this.domObserver = new MutationObserver(() => {
|
|
557
|
+
this.destroyed || (this.shadowHost && !this.shadowHost.isConnected && document.body.appendChild(this.shadowHost), this.overlayShadowHost && !this.overlayShadowHost.isConnected && document.body.appendChild(this.overlayShadowHost));
|
|
558
|
+
}), this.domObserver.observe(document.body, { childList: !0 });
|
|
559
|
+
}
|
|
560
|
+
checkMobile() {
|
|
561
|
+
this.isMobile = nt();
|
|
562
|
+
}
|
|
563
|
+
handleResize() {
|
|
564
|
+
this.checkMobile();
|
|
565
|
+
}
|
|
566
|
+
createEmbed() {
|
|
567
|
+
this.shadowHost = document.createElement("div"), this.shadowHost.setAttribute("data-qaid-embed", ""), this.shadowHost.style.position = "static", this.shadowHost.style.display = "contents";
|
|
568
|
+
let t = null;
|
|
569
|
+
this.config.container && (t = document.querySelector(this.config.container)), t ? (t.appendChild(this.shadowHost), this.isUserProvidedContainer = !0, this.config.hideDismiss = !0) : (this.shadowHost.style.position = "fixed", this.shadowHost.style.display = "block", this.shadowHost.style.inset = "0", this.shadowHost.style.pointerEvents = "none", this.shadowHost.style.zIndex = String(this.config.zIndex), document.body.appendChild(this.shadowHost)), this.shadowRoot = this.shadowHost.attachShadow({ mode: "open" });
|
|
570
|
+
const i = document.createElement("style");
|
|
571
|
+
if (i.textContent = M(), this.shadowRoot.appendChild(i), this.config.css) {
|
|
572
|
+
const d = document.createElement("style");
|
|
573
|
+
d.textContent = this.config.css, this.shadowRoot.appendChild(d);
|
|
574
|
+
}
|
|
575
|
+
const o = this.config.direction === "vertical" ? "qaid-vertical" : "";
|
|
576
|
+
if (this.isUserProvidedContainer)
|
|
577
|
+
this.buttonsContainer = document.createElement("div"), this.buttonsContainer.className = `qaid-buttons qaid-${this.config.position} ${o}`.trim(), this.config.incognito && this.buttonsContainer.classList.add("qaid-incognito");
|
|
578
|
+
else {
|
|
579
|
+
this.buttonsContainer = document.createElement("div"), this.buttonsContainer.className = `qaid-buttons qaid-auto-container qaid-${this.config.position} ${o}${this.config.incognito ? " qaid-incognito" : ""}`.trim(), this.buttonsContainer.style.pointerEvents = "auto";
|
|
580
|
+
const { x: d, y: r } = this.config.offset;
|
|
581
|
+
this.config.position.includes("right") ? this.buttonsContainer.style.right = `${d}px` : this.buttonsContainer.style.left = `${d}px`, this.config.position.includes("bottom") ? this.buttonsContainer.style.bottom = `${r}px` : this.buttonsContainer.style.top = `${r}px`;
|
|
582
|
+
}
|
|
583
|
+
this.applyVars(this.buttonsContainer), this.shadowRoot.appendChild(this.buttonsContainer), this.buttonsContainer.addEventListener("keydown", (d) => {
|
|
584
|
+
(d.key === "Enter" || d.key === " " || d.key === "Spacebar") && (this.keyboardActivation = !0);
|
|
585
|
+
}), this.buttonsContainer.addEventListener("mousedown", () => {
|
|
586
|
+
this.keyboardActivation = !1;
|
|
587
|
+
}), this.buttonsContainer.addEventListener("pointerdown", () => {
|
|
588
|
+
this.keyboardActivation = !1;
|
|
589
|
+
});
|
|
590
|
+
const s = !!this.config.buttonClass, a = s ? `qaid-btn-structural ${this.config.buttonClass}` : "qaid-btn", l = this.config.text.tooltip || "Feedback for us?", c = document.createElement("div");
|
|
591
|
+
if (c.className = "qaid-tooltip-text", c.textContent = l, this.applyVars(c), this.shadowRoot.appendChild(c), this.tooltipElement = c, !this.config.hideThumbs) {
|
|
592
|
+
const d = document.createElement("div");
|
|
593
|
+
d.className = "qaid-tooltip-wrapper";
|
|
594
|
+
const r = document.createElement("button");
|
|
595
|
+
r.type = "button", r.className = s ? `${a} qaid-btn-up` : "qaid-btn qaid-btn-up", r.setAttribute("aria-label", this.config.text.positiveLabel), r.innerHTML = this.config.positiveIcon || st, r.addEventListener("click", (p) => this.handleThumbClick("up", p.currentTarget, p)), r.addEventListener("mouseenter", () => {
|
|
596
|
+
this.prewarmTargeting(), this.showTooltip(r);
|
|
597
|
+
}), r.addEventListener("mouseleave", () => this.hideTooltip()), d.appendChild(r);
|
|
598
|
+
const h = document.createElement("div");
|
|
599
|
+
h.className = "qaid-tooltip-wrapper";
|
|
600
|
+
const u = document.createElement("button");
|
|
601
|
+
u.type = "button", u.className = s ? `${a} qaid-btn-down` : "qaid-btn qaid-btn-down", u.setAttribute("aria-label", this.config.text.negativeLabel), u.innerHTML = this.config.negativeIcon || rt, u.addEventListener("click", (p) => this.handleThumbClick("down", p.currentTarget, p)), u.addEventListener("mouseenter", () => {
|
|
602
|
+
this.prewarmTargeting(), this.showTooltip(u);
|
|
603
|
+
}), u.addEventListener("mouseleave", () => this.hideTooltip()), h.appendChild(u), this.buttonsContainer.appendChild(d), this.buttonsContainer.appendChild(h);
|
|
604
|
+
}
|
|
605
|
+
if (this.config.captureVideo && this.videoSupported()) {
|
|
606
|
+
const d = document.createElement("div");
|
|
607
|
+
d.className = "qaid-tooltip-wrapper";
|
|
608
|
+
const r = document.createElement("button");
|
|
609
|
+
r.type = "button", r.className = s ? `${a} qaid-btn-record` : "qaid-btn qaid-btn-record", r.setAttribute("aria-label", this.config.text.recordLabel), r.innerHTML = this.config.recordIcon || dt, r.addEventListener("click", () => {
|
|
610
|
+
this.ensureRecording().then(
|
|
611
|
+
(u) => this.config.videoOptions.redaction ? u.startPicking() : u.startRecording()
|
|
612
|
+
);
|
|
613
|
+
});
|
|
614
|
+
const h = () => this.prewarmVideo();
|
|
615
|
+
r.addEventListener("mouseenter", () => {
|
|
616
|
+
h(), this.showTooltip(r);
|
|
617
|
+
}), r.addEventListener("focus", h), r.addEventListener("touchstart", h, { passive: !0 }), r.addEventListener("mouseleave", () => this.hideTooltip()), d.appendChild(r), this.buttonsContainer.appendChild(d);
|
|
618
|
+
}
|
|
619
|
+
this.config.hideDismiss || (this.dismissBtn = document.createElement("button"), this.dismissBtn.type = "button", this.dismissBtn.className = "qaid-dismiss-btn", this.dismissBtn.setAttribute("aria-label", this.config.text.dismissLabel), this.dismissBtn.title = this.config.text.dismissLabel, this.dismissBtn.innerHTML = '<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M2 2l8 8M10 2l-8 8" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>', this.dismissBtn.addEventListener("click", (d) => {
|
|
620
|
+
d.stopPropagation(), this.handleDismiss();
|
|
621
|
+
}), this.buttonsContainer.insertBefore(this.dismissBtn, this.buttonsContainer.firstChild), this._startDismissed && this.buttonsContainer.classList.add("qaid-dismissed"));
|
|
622
|
+
}
|
|
623
|
+
/**
|
|
624
|
+
* Lazily create a separate overlay shadow host on document.body.
|
|
625
|
+
* This host contains all full-page elements (targeting overlay, marker,
|
|
626
|
+
* backdrop, modal, recording indicator, video preview) so they escape
|
|
627
|
+
* clip-path / transform containing blocks in user containers.
|
|
628
|
+
*/
|
|
629
|
+
ensureOverlayHost() {
|
|
630
|
+
if (this.overlayShadowRoot) return this.overlayShadowRoot;
|
|
631
|
+
this.overlayShadowHost = document.createElement("div"), this.overlayShadowHost.setAttribute("data-qaid-embed-overlay", ""), this.overlayShadowHost.style.position = "fixed", this.overlayShadowHost.style.inset = "0", this.overlayShadowHost.style.pointerEvents = "none", this.overlayShadowHost.style.zIndex = String(this.config.zIndex), document.body.appendChild(this.overlayShadowHost), this.overlayShadowRoot = this.overlayShadowHost.attachShadow({ mode: "open" });
|
|
632
|
+
const t = document.createElement("style");
|
|
633
|
+
if (t.textContent = M(), this.overlayShadowRoot.appendChild(t), this.config.css) {
|
|
634
|
+
const i = document.createElement("style");
|
|
635
|
+
i.textContent = this.config.css, this.overlayShadowRoot.appendChild(i);
|
|
636
|
+
}
|
|
637
|
+
return this.overlayShadowRoot;
|
|
638
|
+
}
|
|
639
|
+
tooltipElement = null;
|
|
640
|
+
showTooltip(t) {
|
|
641
|
+
const i = this.tooltipElement;
|
|
642
|
+
i.style.visibility = "hidden", i.classList.add("qaid-tooltip-visible");
|
|
643
|
+
const o = t.getBoundingClientRect(), s = i.getBoundingClientRect(), { top: a, left: n } = Pt(
|
|
644
|
+
o,
|
|
645
|
+
s,
|
|
646
|
+
{ width: window.innerWidth, height: window.innerHeight }
|
|
647
|
+
);
|
|
648
|
+
i.style.top = `${a}px`, i.style.left = `${n}px`, i.style.visibility = "visible";
|
|
649
|
+
}
|
|
650
|
+
hideTooltip() {
|
|
651
|
+
this.tooltipElement.classList.remove("qaid-tooltip-visible");
|
|
652
|
+
}
|
|
653
|
+
handleDismiss() {
|
|
654
|
+
this.buttonsContainer.classList.add("qaid-dismissed"), F(this.config.apiKey, !0);
|
|
655
|
+
}
|
|
656
|
+
handleThumbClick(t, i, o) {
|
|
657
|
+
if (this.buttonsContainer?.classList.contains("qaid-incognito") && (this.buttonsContainer.classList.remove("qaid-incognito"), F(this.config.apiKey, !1)), this.prewarmModal(), this.config.skipTargeting)
|
|
658
|
+
this.submitDirectFeedback(t, i);
|
|
659
|
+
else {
|
|
660
|
+
this.activeThumbBtn = i, i.setAttribute("aria-pressed", "true");
|
|
661
|
+
const s = this.keyboardActivation;
|
|
662
|
+
this.keyboardActivation = !1, this.ensureTargeting().then((a) => {
|
|
663
|
+
this.destroyed || (s ? a.startKeyboard(t) : a.startPointer(t, o));
|
|
664
|
+
});
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
submitDirectFeedback(t, i) {
|
|
668
|
+
this.feedbackData.feedbackType = t, this.feedbackData.elementSelector = null, this.feedbackData.elementText = null;
|
|
669
|
+
const o = i.getBoundingClientRect();
|
|
670
|
+
this.selectedBounds = {
|
|
671
|
+
x: o.left,
|
|
672
|
+
y: o.top,
|
|
673
|
+
width: o.width,
|
|
674
|
+
height: o.height,
|
|
675
|
+
clickX: o.left + o.width / 2,
|
|
676
|
+
clickY: o.top + o.height / 2,
|
|
677
|
+
visible: !1
|
|
678
|
+
}, this.submitFeedback();
|
|
679
|
+
}
|
|
680
|
+
handleKeyDown(t) {
|
|
681
|
+
if (t.key === "Escape") {
|
|
682
|
+
if (this.recording?.handleEscape()) return;
|
|
683
|
+
this.state === "TARGETING" ? this.targeting?.cancel() : this.state === "MODAL_OPEN" && this.modal?.close();
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
/** Whether to capture the screenshot with the DOM/canvas method (html2canvas)
|
|
687
|
+
* instead of the permission-based Screen Capture API. Explicit "dom" wins;
|
|
688
|
+
* otherwise DOM is used on touch devices to avoid the getDisplayMedia prompt. */
|
|
689
|
+
shouldCaptureViaDom() {
|
|
690
|
+
return this.config.screenshotMethod === "dom" || jt();
|
|
691
|
+
}
|
|
692
|
+
/**
|
|
693
|
+
* Open the full-screen annotation editor over the captured screenshot,
|
|
694
|
+
* reusing the overlay shadow host and the shared dialog a11y helpers.
|
|
695
|
+
* Resolves with the composited WebP data URL, or null when the user skips
|
|
696
|
+
* (caller keeps the original). Pointer events on the overlay host are
|
|
697
|
+
* enabled while the editor is open and restored on close.
|
|
698
|
+
*/
|
|
699
|
+
async openAnnotationEditor(t) {
|
|
700
|
+
const i = this.ensureOverlayHost(), o = this.overlayShadowHost, s = o.style.pointerEvents;
|
|
701
|
+
o.style.pointerEvents = "auto";
|
|
702
|
+
try {
|
|
703
|
+
const { openAnnotationEditor: a } = await import("./annotate-BNibaDg3.js").then((n) => n.f);
|
|
704
|
+
return await a({
|
|
705
|
+
dataUrl: t,
|
|
706
|
+
root: i,
|
|
707
|
+
quality: this.config.screenshotOptions.quality,
|
|
708
|
+
color: this.config.colors.marker,
|
|
709
|
+
applyVars: (n) => this.applyVars(n),
|
|
710
|
+
announce: (n, l) => this.announceMsg(n, l),
|
|
711
|
+
openDialog: (n, l) => this.openDialogA11y(n, l),
|
|
712
|
+
closeDialog: () => this.closeDialogA11y()
|
|
713
|
+
});
|
|
714
|
+
} finally {
|
|
715
|
+
o.style.pointerEvents = s;
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
async submitFeedback() {
|
|
719
|
+
let t = null;
|
|
720
|
+
if (this.config.captureScreenshot) {
|
|
721
|
+
if (this.shouldCaptureViaDom()) {
|
|
722
|
+
const { captureDomScreenshot: n } = await import("./screenshot-dom-CME7tYRE.js");
|
|
723
|
+
t = await n(this.config.screenshotOptions);
|
|
724
|
+
} else {
|
|
725
|
+
const { captureScreenshot: n } = await import("./screenshot-BkOCLuS2.js");
|
|
726
|
+
t = await n(this.config.screenshotOptions);
|
|
727
|
+
}
|
|
728
|
+
t && this.announceMsg("Screenshot captured");
|
|
729
|
+
}
|
|
730
|
+
t && this.config.annotate && (t = await this.openAnnotationEditor(t) ?? t);
|
|
731
|
+
const i = this.feedbackData.elementSelector ? {
|
|
732
|
+
x: this.selectedBounds.x,
|
|
733
|
+
y: this.selectedBounds.y,
|
|
734
|
+
width: this.selectedBounds.width,
|
|
735
|
+
height: this.selectedBounds.height
|
|
736
|
+
} : null, o = {
|
|
737
|
+
feedbackType: this.feedbackData.feedbackType,
|
|
738
|
+
pageUrl: window.location.href,
|
|
739
|
+
apiKey: this.config.apiKey || void 0,
|
|
740
|
+
elementSelector: this.feedbackData.elementSelector,
|
|
741
|
+
elementText: this.feedbackData.elementText,
|
|
742
|
+
consoleErrors: [...this.feedbackData.consoleErrors],
|
|
743
|
+
screenWidth: window.innerWidth,
|
|
744
|
+
screenHeight: window.innerHeight,
|
|
745
|
+
clickX: this.selectedBounds.clickX,
|
|
746
|
+
clickY: this.selectedBounds.clickY,
|
|
747
|
+
scrollX: window.scrollX,
|
|
748
|
+
scrollY: window.scrollY,
|
|
749
|
+
screenshot: t,
|
|
750
|
+
visitorId: this.visitorId,
|
|
751
|
+
// Include element bounds and user agent for server-side screenshot fallback
|
|
752
|
+
elementBounds: i,
|
|
753
|
+
userAgent: navigator.userAgent
|
|
754
|
+
};
|
|
755
|
+
try {
|
|
756
|
+
const n = await fetch(this.config.endpoint, {
|
|
757
|
+
method: "POST",
|
|
758
|
+
headers: { "Content-Type": "application/json" },
|
|
759
|
+
body: JSON.stringify(o)
|
|
760
|
+
});
|
|
761
|
+
if (n.ok) {
|
|
762
|
+
const l = await n.json();
|
|
763
|
+
this.feedbackId = l.id, this.announceMsg("Feedback sent");
|
|
764
|
+
}
|
|
765
|
+
} catch (n) {
|
|
766
|
+
console.error("Failed to submit feedback:", n), this.announceMsg("Failed to send feedback", !0);
|
|
767
|
+
}
|
|
768
|
+
const s = this.feedbackData.feedbackType;
|
|
769
|
+
if (s && await this.tryLaunchQuest(s, this.feedbackId)) {
|
|
770
|
+
this.resetFeedbackUi();
|
|
771
|
+
return;
|
|
772
|
+
}
|
|
773
|
+
this.state = "MODAL_OPEN";
|
|
774
|
+
const a = await this.ensureModal();
|
|
775
|
+
this.destroyed || (a.open(), this.overlayShadowHost && (this.overlayShadowHost.style.pointerEvents = "auto"));
|
|
776
|
+
}
|
|
777
|
+
/**
|
|
778
|
+
* Quest id linked to `type`, or "" when quest launching is disabled
|
|
779
|
+
* (no `base`) or this button has no quest configured.
|
|
780
|
+
*/
|
|
781
|
+
questIdFor(t) {
|
|
782
|
+
const i = this.config.quests;
|
|
783
|
+
return i.base && i[t] || "";
|
|
784
|
+
}
|
|
785
|
+
/**
|
|
786
|
+
* Launch the quest linked to `type`, if any. Resolves `true` when a quest
|
|
787
|
+
* was configured and the widget launched; `false` when no quest is
|
|
788
|
+
* configured or the widget failed to load (caller falls back to its
|
|
789
|
+
* normal UI). The created feedback record id is passed through so the
|
|
790
|
+
* quest response can be joined back to it server-side.
|
|
791
|
+
*/
|
|
792
|
+
async tryLaunchQuest(t, i) {
|
|
793
|
+
const o = this.questIdFor(t);
|
|
794
|
+
if (!o) return !1;
|
|
795
|
+
try {
|
|
796
|
+
return this.activeQuest?.destroy(), this.activeQuest = await Nt({
|
|
797
|
+
questId: o,
|
|
798
|
+
base: this.config.quests.base,
|
|
799
|
+
apiKey: this.config.quests.apiKey || void 0,
|
|
800
|
+
moduleUrl: this.config.quests.moduleUrl,
|
|
801
|
+
feedbackId: i,
|
|
802
|
+
onClose: () => {
|
|
803
|
+
this.activeQuest = null;
|
|
804
|
+
}
|
|
805
|
+
}), !0;
|
|
806
|
+
} catch (s) {
|
|
807
|
+
return console.error("Failed to launch quest:", s), this.activeQuest = null, !1;
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
/**
|
|
811
|
+
* Reset the thumbs targeting/marker UI back to idle without opening or
|
|
812
|
+
* closing the message modal. Shared by closeModal() and the quest-launch
|
|
813
|
+
* path (which bypasses the modal entirely).
|
|
814
|
+
*/
|
|
815
|
+
resetFeedbackUi() {
|
|
816
|
+
this.targeting?.hideMarker(), this.overlayShadowHost && (this.overlayShadowHost.style.pointerEvents = "none"), this.state = "IDLE", this.feedbackId = null, this.feedbackData.feedbackType = null, this.feedbackData.elementSelector = null, this.feedbackData.elementText = null, this.selectedBounds.visible = !1;
|
|
817
|
+
}
|
|
818
|
+
/**
|
|
819
|
+
* Lazily load and instantiate the modal controller. The message modal lives
|
|
820
|
+
* in a separate chunk, fetched the first time it opens (and pre-warmed while
|
|
821
|
+
* the user targets an element — see prewarmModal).
|
|
822
|
+
*/
|
|
823
|
+
async ensureModal() {
|
|
824
|
+
if (!this.modal) {
|
|
825
|
+
const { ModalController: t } = await import("./modal-BX5wqy4s.js");
|
|
826
|
+
this.modal = new t(this.makeModalHost());
|
|
827
|
+
}
|
|
828
|
+
return this.modal;
|
|
829
|
+
}
|
|
830
|
+
/** Narrow view of the embed the modal controller talks back through. */
|
|
831
|
+
makeModalHost() {
|
|
832
|
+
const t = this;
|
|
833
|
+
return {
|
|
834
|
+
get config() {
|
|
835
|
+
return t.config;
|
|
836
|
+
},
|
|
837
|
+
get uid() {
|
|
838
|
+
return t.uid;
|
|
839
|
+
},
|
|
840
|
+
get isMobile() {
|
|
841
|
+
return t.isMobile;
|
|
842
|
+
},
|
|
843
|
+
get state() {
|
|
844
|
+
return t.state;
|
|
845
|
+
},
|
|
846
|
+
get boundKeyDown() {
|
|
847
|
+
return t.boundKeyDown;
|
|
848
|
+
},
|
|
849
|
+
get feedbackData() {
|
|
850
|
+
return t.feedbackData;
|
|
851
|
+
},
|
|
852
|
+
get selectedBounds() {
|
|
853
|
+
return t.selectedBounds;
|
|
854
|
+
},
|
|
855
|
+
get feedbackId() {
|
|
856
|
+
return t.feedbackId;
|
|
857
|
+
},
|
|
858
|
+
setFeedbackId: (i) => {
|
|
859
|
+
t.feedbackId = i;
|
|
860
|
+
},
|
|
861
|
+
ensureOverlayHost: () => t.ensureOverlayHost(),
|
|
862
|
+
applyVars: (i) => t.applyVars(i),
|
|
863
|
+
announceMsg: (i, o) => t.announceMsg(i, o),
|
|
864
|
+
openDialogA11y: (i, o) => t.openDialogA11y(i, o),
|
|
865
|
+
closeDialogA11y: () => t.closeDialogA11y(),
|
|
866
|
+
resetFeedbackUi: () => t.resetFeedbackUi()
|
|
867
|
+
};
|
|
868
|
+
}
|
|
869
|
+
/** Warm the modal chunk while the user is targeting, so it opens instantly. */
|
|
870
|
+
prewarmModal() {
|
|
871
|
+
this.modalPrewarmed || (this.modalPrewarmed = !0, import("./modal-BX5wqy4s.js").catch(() => {
|
|
872
|
+
}));
|
|
873
|
+
}
|
|
874
|
+
/**
|
|
875
|
+
* Lazily load and instantiate the targeting controller. The subsystem (with
|
|
876
|
+
* element-selector) lives in a separate chunk, fetched the first time the
|
|
877
|
+
* user targets — pre-warmed on thumb-button hover (see prewarmTargeting).
|
|
878
|
+
*/
|
|
879
|
+
async ensureTargeting() {
|
|
880
|
+
if (!this.targeting) {
|
|
881
|
+
const { TargetingController: t } = await import("./targeting-DwdDr9Cm.js");
|
|
882
|
+
this.targeting = new t(this.makeTargetingHost());
|
|
883
|
+
}
|
|
884
|
+
return this.targeting;
|
|
885
|
+
}
|
|
886
|
+
/** Narrow view of the embed the targeting controller talks back through. */
|
|
887
|
+
makeTargetingHost() {
|
|
888
|
+
const t = this;
|
|
889
|
+
return {
|
|
890
|
+
get config() {
|
|
891
|
+
return t.config;
|
|
892
|
+
},
|
|
893
|
+
get cssVars() {
|
|
894
|
+
return t.cssVars;
|
|
895
|
+
},
|
|
896
|
+
get state() {
|
|
897
|
+
return t.state;
|
|
898
|
+
},
|
|
899
|
+
get shadowHost() {
|
|
900
|
+
return t.shadowHost;
|
|
901
|
+
},
|
|
902
|
+
get overlayShadowHost() {
|
|
903
|
+
return t.overlayShadowHost;
|
|
904
|
+
},
|
|
905
|
+
get boundKeyDown() {
|
|
906
|
+
return t.boundKeyDown;
|
|
907
|
+
},
|
|
908
|
+
get feedbackData() {
|
|
909
|
+
return t.feedbackData;
|
|
910
|
+
},
|
|
911
|
+
get selectedBounds() {
|
|
912
|
+
return t.selectedBounds;
|
|
913
|
+
},
|
|
914
|
+
setState: (i) => {
|
|
915
|
+
t.state = i;
|
|
916
|
+
},
|
|
917
|
+
ensureOverlayHost: () => t.ensureOverlayHost(),
|
|
918
|
+
applyVars: (i) => t.applyVars(i),
|
|
919
|
+
announceMsg: (i, o) => t.announceMsg(i, o),
|
|
920
|
+
clearActiveThumb: () => t.clearActiveThumb(),
|
|
921
|
+
submitFeedback: () => t.submitFeedback()
|
|
922
|
+
};
|
|
923
|
+
}
|
|
924
|
+
/** Warm the targeting chunk on thumb-button hover, before the click. */
|
|
925
|
+
prewarmTargeting() {
|
|
926
|
+
this.targetingPrewarmed || this.config.skipTargeting || (this.targetingPrewarmed = !0, import("./targeting-DwdDr9Cm.js").catch(() => {
|
|
927
|
+
}));
|
|
928
|
+
}
|
|
929
|
+
// ==================== Video Recording ====================
|
|
930
|
+
/**
|
|
931
|
+
* Cheap synchronous capability check so the record button can render without
|
|
932
|
+
* pulling in the (lazily-loaded) video subsystem. Mirrors
|
|
933
|
+
* isVideoRecordingSupported() in video-capture.ts.
|
|
934
|
+
*/
|
|
935
|
+
videoSupported() {
|
|
936
|
+
return typeof navigator < "u" && !!navigator.mediaDevices && typeof navigator.mediaDevices.getDisplayMedia == "function" && typeof MediaRecorder < "u";
|
|
937
|
+
}
|
|
938
|
+
/**
|
|
939
|
+
* Lazily load and instantiate the recording controller. The whole recording
|
|
940
|
+
* subsystem lives in a separate chunk, fetched only the first time the record
|
|
941
|
+
* button is used (and pre-warmed on hover — see prewarmVideo).
|
|
942
|
+
*/
|
|
943
|
+
async ensureRecording() {
|
|
944
|
+
if (!this.recording) {
|
|
945
|
+
const { RecordingController: t } = await import("./recording--IwOtRL2.js");
|
|
946
|
+
this.recording = new t(this.makeRecordingHost());
|
|
947
|
+
}
|
|
948
|
+
return this.recording;
|
|
949
|
+
}
|
|
950
|
+
/** Narrow view of the embed the recording controller talks back through. */
|
|
951
|
+
makeRecordingHost() {
|
|
952
|
+
const t = this;
|
|
953
|
+
return {
|
|
954
|
+
get config() {
|
|
955
|
+
return t.config;
|
|
956
|
+
},
|
|
957
|
+
get visitorId() {
|
|
958
|
+
return t.visitorId;
|
|
959
|
+
},
|
|
960
|
+
get uid() {
|
|
961
|
+
return t.uid;
|
|
962
|
+
},
|
|
963
|
+
get overlayShadowHost() {
|
|
964
|
+
return t.overlayShadowHost;
|
|
965
|
+
},
|
|
966
|
+
get consoleCapture() {
|
|
967
|
+
return t.consoleCapture;
|
|
968
|
+
},
|
|
969
|
+
get boundKeyDown() {
|
|
970
|
+
return t.boundKeyDown;
|
|
971
|
+
},
|
|
972
|
+
get state() {
|
|
973
|
+
return t.state;
|
|
974
|
+
},
|
|
975
|
+
setState: (i) => {
|
|
976
|
+
t.state = i;
|
|
977
|
+
},
|
|
978
|
+
ensureOverlayHost: () => t.ensureOverlayHost(),
|
|
979
|
+
applyVars: (i) => t.applyVars(i),
|
|
980
|
+
announceMsg: (i, o) => t.announceMsg(i, o),
|
|
981
|
+
openDialogA11y: (i, o) => t.openDialogA11y(i, o),
|
|
982
|
+
closeDialogA11y: () => t.closeDialogA11y(),
|
|
983
|
+
setButtonsDisabled: (i) => t.setButtonsDisabled(i),
|
|
984
|
+
tryLaunchQuest: (i, o) => t.tryLaunchQuest(i, o)
|
|
985
|
+
};
|
|
986
|
+
}
|
|
987
|
+
setButtonsDisabled(t) {
|
|
988
|
+
if (!this.buttonsContainer) return;
|
|
989
|
+
this.buttonsContainer.querySelectorAll("button.qaid-btn, button.qaid-btn-structural").forEach((o) => {
|
|
990
|
+
t ? o.classList.contains("qaid-btn-record") || (o.disabled = !0, o.style.opacity = "0.5") : (o.disabled = !1, o.style.opacity = "");
|
|
991
|
+
});
|
|
992
|
+
}
|
|
993
|
+
/**
|
|
994
|
+
* Destroy the embed and clean up all resources
|
|
995
|
+
*/
|
|
996
|
+
destroy() {
|
|
997
|
+
if (this.destroyed = !0, this.domObserver && (this.domObserver.disconnect(), this.domObserver = null), this.boundBeforeSwap && (document.removeEventListener("astro:before-swap", this.boundBeforeSwap), this.boundBeforeSwap = null), this.targeting?.destroy(), this.targeting = null, this.clearActiveThumb(), this.closeDialogA11y(), this.prewarmHandle !== null) {
|
|
998
|
+
const t = window;
|
|
999
|
+
this.prewarmIsTimeout ? clearTimeout(this.prewarmHandle) : t.cancelIdleCallback?.(this.prewarmHandle), this.prewarmHandle = null;
|
|
1000
|
+
}
|
|
1001
|
+
this.recording?.destroy(), this.recording = null, this.modal?.destroy(), this.modal = null, this.activeQuest?.destroy(), this.activeQuest = null, this.consoleCapture && (this.consoleCapture.restore(), this.consoleCapture = null), window.removeEventListener("resize", this.boundResize), document.removeEventListener("keydown", this.boundKeyDown), document.body.classList.remove("qaid-targeting", "qaid-type-up"), document.body.style.removeProperty("--qaid-positive"), document.body.style.removeProperty("--qaid-negative"), this.shadowHost && (this.shadowHost.remove(), this.shadowHost = null, this.shadowRoot = null), this.overlayShadowHost && (this.overlayShadowHost.remove(), this.overlayShadowHost = null, this.overlayShadowRoot = null), this.buttonsContainer = null, this.dismissBtn = null, this.tooltipElement = null, wt();
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
function K(e) {
|
|
1005
|
+
return document.querySelector(e)?.textContent?.trim() ?? "";
|
|
1006
|
+
}
|
|
1007
|
+
function Xt() {
|
|
1008
|
+
const e = document.querySelector(
|
|
1009
|
+
'script[type="application/json"][data-feedback-config]'
|
|
1010
|
+
);
|
|
1011
|
+
if (!e) return null;
|
|
1012
|
+
const t = e.textContent?.trim();
|
|
1013
|
+
if (!t) return null;
|
|
1014
|
+
try {
|
|
1015
|
+
const i = JSON.parse(t);
|
|
1016
|
+
return i.cssSelector && !i.css && (i.css = K(i.cssSelector), delete i.cssSelector), i;
|
|
1017
|
+
} catch {
|
|
1018
|
+
return null;
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
function Gt(e) {
|
|
1022
|
+
const t = e.getAttribute("data-endpoint");
|
|
1023
|
+
if (!t) return null;
|
|
1024
|
+
const i = e.getAttribute("data-position"), o = e.getAttribute("data-zindex"), s = e.getAttribute("data-positive-color"), a = e.getAttribute("data-negative-color"), n = e.getAttribute("data-marker-color"), l = e.getAttribute("data-container"), c = e.getAttribute("data-button-class"), d = e.getAttribute("data-skip-targeting"), r = e.getAttribute("data-incognito"), h = e.getAttribute("data-button-size"), u = e.getAttribute("data-offset-x"), p = e.getAttribute("data-offset-y"), x = e.getAttribute("data-modal-width"), k = e.getAttribute("data-backdrop-opacity"), _ = e.getAttribute("data-font-family"), S = e.getAttribute("data-font-size"), C = e.getAttribute("data-tooltip"), A = e.getAttribute("data-modal-title"), T = e.getAttribute("data-modal-subtitle"), D = e.getAttribute("data-placeholder"), I = e.getAttribute("data-submit-button"), E = e.getAttribute("data-skip-button"), $ = e.getAttribute("data-positive-icon"), N = e.getAttribute("data-negative-icon"), j = e.getAttribute("data-api-key"), Q = e.getAttribute("data-capture-screenshot"), W = e.getAttribute("data-annotate"), m = e.getAttribute("data-screenshot-quality"), g = e.getAttribute("data-screenshot-max-width"), v = e.getAttribute("data-screenshot-max-height"), Y = e.getAttribute("data-capture-video"), H = e.getAttribute("data-video-redaction"), X = e.getAttribute("data-hide-thumbs"), G = e.getAttribute("data-hide-dismiss"), y = e.getAttribute("data-video-max-duration"), J = e.getAttribute("data-screenshot-method"), Z = e.getAttribute("data-direction"), z = e.getAttribute("data-css-selector"), L = e.getAttribute("data-quest-base"), tt = e.getAttribute("data-quest-up"), et = e.getAttribute("data-quest-down"), it = e.getAttribute("data-quest-video"), ot = e.getAttribute("data-quest-api-key"), at = e.getAttribute("data-quest-module-url");
|
|
1025
|
+
return {
|
|
1026
|
+
endpoint: t,
|
|
1027
|
+
css: z ? K(z) : void 0,
|
|
1028
|
+
apiKey: j ?? void 0,
|
|
1029
|
+
captureScreenshot: Q === "true" ? !0 : void 0,
|
|
1030
|
+
// Annotation is on by default; only an explicit "false" disables it.
|
|
1031
|
+
annotate: W === "false" ? !1 : void 0,
|
|
1032
|
+
screenshotOptions: m || g || v ? {
|
|
1033
|
+
quality: m ? parseFloat(m) : void 0,
|
|
1034
|
+
maxWidth: g ? parseInt(g, 10) : void 0,
|
|
1035
|
+
maxHeight: v ? parseInt(v, 10) : void 0
|
|
1036
|
+
} : void 0,
|
|
1037
|
+
container: l ?? void 0,
|
|
1038
|
+
buttonClass: c ?? void 0,
|
|
1039
|
+
direction: Z ?? void 0,
|
|
1040
|
+
position: i ?? void 0,
|
|
1041
|
+
zIndex: o ? parseInt(o, 10) : void 0,
|
|
1042
|
+
skipTargeting: d === "true" ? !0 : void 0,
|
|
1043
|
+
incognito: r === "true" ? !0 : void 0,
|
|
1044
|
+
buttonSize: h ?? void 0,
|
|
1045
|
+
offset: u || p ? {
|
|
1046
|
+
x: u ? parseInt(u, 10) : void 0,
|
|
1047
|
+
y: p ? parseInt(p, 10) : void 0
|
|
1048
|
+
} : void 0,
|
|
1049
|
+
modalWidth: x ? parseInt(x, 10) : void 0,
|
|
1050
|
+
backdropOpacity: k ? parseFloat(k) : void 0,
|
|
1051
|
+
fontFamily: _ ?? void 0,
|
|
1052
|
+
fontSize: S ? parseInt(S, 10) : void 0,
|
|
1053
|
+
colors: {
|
|
1054
|
+
positive: s ?? void 0,
|
|
1055
|
+
negative: a ?? void 0,
|
|
1056
|
+
marker: n ?? void 0
|
|
1057
|
+
},
|
|
1058
|
+
text: C || A || T || D || I || E ? {
|
|
1059
|
+
tooltip: C ?? void 0,
|
|
1060
|
+
modalTitle: A ?? void 0,
|
|
1061
|
+
modalSubtitle: T ?? void 0,
|
|
1062
|
+
placeholder: D ?? void 0,
|
|
1063
|
+
submitButton: I ?? void 0,
|
|
1064
|
+
skipButton: E ?? void 0
|
|
1065
|
+
} : void 0,
|
|
1066
|
+
positiveIcon: $ ?? void 0,
|
|
1067
|
+
negativeIcon: N ?? void 0,
|
|
1068
|
+
screenshotMethod: J ?? void 0,
|
|
1069
|
+
captureVideo: Y === "true" ? !0 : void 0,
|
|
1070
|
+
hideThumbs: X === "true" ? !0 : void 0,
|
|
1071
|
+
hideDismiss: G === "true" ? !0 : void 0,
|
|
1072
|
+
videoOptions: y || H ? {
|
|
1073
|
+
maxDuration: y ? parseInt(y, 10) : void 0,
|
|
1074
|
+
redaction: H === "true" ? !0 : void 0
|
|
1075
|
+
} : void 0,
|
|
1076
|
+
quests: L ? {
|
|
1077
|
+
base: L,
|
|
1078
|
+
up: tt ?? void 0,
|
|
1079
|
+
down: et ?? void 0,
|
|
1080
|
+
video: it ?? void 0,
|
|
1081
|
+
apiKey: ot ?? void 0,
|
|
1082
|
+
moduleUrl: at ?? void 0
|
|
1083
|
+
} : void 0
|
|
1084
|
+
};
|
|
1085
|
+
}
|
|
1086
|
+
function te() {
|
|
1087
|
+
if (typeof document > "u") return;
|
|
1088
|
+
const e = () => {
|
|
1089
|
+
const t = document.currentScript ?? document.querySelector("script[data-endpoint]"), i = Xt(), o = t ? Gt(t) : null, s = i ?? o;
|
|
1090
|
+
s?.endpoint && new Yt(s);
|
|
1091
|
+
};
|
|
1092
|
+
document.readyState === "loading" ? document.addEventListener("DOMContentLoaded", e) : e();
|
|
1093
|
+
}
|
|
1094
|
+
export {
|
|
1095
|
+
Yt as Q,
|
|
1096
|
+
te as a,
|
|
1097
|
+
Zt as c
|
|
1098
|
+
};
|
|
1099
|
+
//# sourceMappingURL=bootstrap-U4yiPm5-.js.map
|