@firstlook-uat/sdk 0.4.0 โ 0.4.1
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/firstlook.es.js +191 -101
- package/dist/firstlook.es.js.map +1 -1
- package/dist/firstlook.umd.js +50 -3
- package/dist/firstlook.umd.js.map +1 -1
- package/package.json +1 -1
package/dist/firstlook.es.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
const
|
|
1
|
+
const I = [
|
|
2
2
|
'input[type="password"]',
|
|
3
3
|
"[data-sensitive]",
|
|
4
4
|
"[data-mask]",
|
|
5
5
|
".uat-mask"
|
|
6
6
|
];
|
|
7
|
-
function
|
|
7
|
+
function M(a) {
|
|
8
8
|
var t, e, s, i, n, o, l, c, h, d;
|
|
9
9
|
if (!a.endpoint)
|
|
10
10
|
throw new Error("[FirstLook] 'endpoint' is required. Set your Supabase ingest-session URL.");
|
|
@@ -23,7 +23,7 @@ function q(a) {
|
|
|
23
23
|
},
|
|
24
24
|
security: {
|
|
25
25
|
watermark: ((n = a.security) == null ? void 0 : n.watermark) ?? !0,
|
|
26
|
-
maskSelectors: ((o = a.security) == null ? void 0 : o.maskSelectors) ??
|
|
26
|
+
maskSelectors: ((o = a.security) == null ? void 0 : o.maskSelectors) ?? I
|
|
27
27
|
},
|
|
28
28
|
recording: {
|
|
29
29
|
domSnapshot: ((l = a.recording) == null ? void 0 : l.domSnapshot) ?? !0,
|
|
@@ -33,7 +33,7 @@ function q(a) {
|
|
|
33
33
|
}
|
|
34
34
|
};
|
|
35
35
|
}
|
|
36
|
-
function
|
|
36
|
+
function y() {
|
|
37
37
|
return {
|
|
38
38
|
userAgent: navigator.userAgent,
|
|
39
39
|
platform: navigator.platform,
|
|
@@ -44,7 +44,7 @@ function w() {
|
|
|
44
44
|
touchSupport: "ontouchstart" in window || navigator.maxTouchPoints > 0
|
|
45
45
|
};
|
|
46
46
|
}
|
|
47
|
-
class
|
|
47
|
+
class E {
|
|
48
48
|
constructor() {
|
|
49
49
|
this.listeners = /* @__PURE__ */ new Map();
|
|
50
50
|
}
|
|
@@ -75,7 +75,7 @@ class C {
|
|
|
75
75
|
this.listeners.clear();
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
|
-
class
|
|
78
|
+
class R {
|
|
79
79
|
constructor(t) {
|
|
80
80
|
this.events = t, this.quests = [], this.results = [], this.currentIndex = -1, this.sessionStartTime = 0, this.blocked = !1, this.pendingFeedbacks = [];
|
|
81
81
|
}
|
|
@@ -116,15 +116,16 @@ class I {
|
|
|
116
116
|
logs: t,
|
|
117
117
|
comment: e == null ? void 0 : e.comment,
|
|
118
118
|
concern: (e == null ? void 0 : e.concern) ?? !1,
|
|
119
|
-
feedbacks: this.drainFeedbacks()
|
|
119
|
+
feedbacks: this.drainFeedbacks(),
|
|
120
|
+
checklist: e == null ? void 0 : e.checklist
|
|
120
121
|
};
|
|
121
122
|
return this.results.push(i), this.events.emit({ type: "quest:completed", questId: s.id }), this.advance(), i;
|
|
122
123
|
}
|
|
123
|
-
failCurrentQuest(t, e, s) {
|
|
124
|
-
const
|
|
125
|
-
if (!
|
|
126
|
-
const
|
|
127
|
-
questId:
|
|
124
|
+
failCurrentQuest(t, e, s, i) {
|
|
125
|
+
const n = this.getCurrentQuest();
|
|
126
|
+
if (!n) return null;
|
|
127
|
+
const o = {
|
|
128
|
+
questId: n.id,
|
|
128
129
|
status: "FAILED",
|
|
129
130
|
timestamp: Date.now(),
|
|
130
131
|
relativeTime: Date.now() - this.sessionStartTime,
|
|
@@ -132,9 +133,10 @@ class I {
|
|
|
132
133
|
logs: t,
|
|
133
134
|
comment: e,
|
|
134
135
|
concern: !1,
|
|
135
|
-
feedbacks: this.drainFeedbacks()
|
|
136
|
+
feedbacks: this.drainFeedbacks(),
|
|
137
|
+
checklist: i
|
|
136
138
|
};
|
|
137
|
-
return this.results.push(
|
|
139
|
+
return this.results.push(o), this.events.emit({ type: "quest:failed", questId: n.id }), n.blocking ? (this.blocked = !0, this.events.emit({ type: "quest:blocked", questId: n.id })) : this.advance(), o;
|
|
138
140
|
}
|
|
139
141
|
addFeedback(t) {
|
|
140
142
|
this.pendingFeedbacks.push(t);
|
|
@@ -160,16 +162,34 @@ function r(a, t, e) {
|
|
|
160
162
|
typeof i == "string" ? s.appendChild(document.createTextNode(i)) : s.appendChild(i);
|
|
161
163
|
return s;
|
|
162
164
|
}
|
|
163
|
-
function
|
|
165
|
+
function L() {
|
|
164
166
|
return `fl_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
|
|
165
167
|
}
|
|
166
|
-
function
|
|
168
|
+
function A(a) {
|
|
167
169
|
const t = Math.floor(a / 60), e = a % 60;
|
|
168
170
|
return `${t.toString().padStart(2, "0")}:${e.toString().padStart(2, "0")}`;
|
|
169
171
|
}
|
|
170
|
-
|
|
172
|
+
function D(a) {
|
|
173
|
+
const t = a.split(`
|
|
174
|
+
`), e = [], s = [];
|
|
175
|
+
let i = !1;
|
|
176
|
+
for (const n of t) {
|
|
177
|
+
const o = n.trim(), l = o.match(/^(?:(\d+)[\.\)]\s+|[-*]\s+)(.+)$/);
|
|
178
|
+
l ? (i = !0, s.push({
|
|
179
|
+
index: s.length,
|
|
180
|
+
text: l[2],
|
|
181
|
+
checked: !1
|
|
182
|
+
})) : !i && o && e.push(o);
|
|
183
|
+
}
|
|
184
|
+
return {
|
|
185
|
+
preamble: e.join(`
|
|
186
|
+
`),
|
|
187
|
+
items: s
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
class N {
|
|
171
191
|
constructor(t, e) {
|
|
172
|
-
this.shadowRoot = t, this.callbacks = e, this.minimized = !1, this.timerInterval = null, this.startTime = 0, this.root = document.createElement("div"), this.root.className = "fl-quest-overlay", this.root.style.pointerEvents = "auto";
|
|
192
|
+
this.shadowRoot = t, this.callbacks = e, this.minimized = !1, this.timerInterval = null, this.startTime = 0, this.checklist = [], this.root = document.createElement("div"), this.root.className = "fl-quest-overlay", this.root.style.pointerEvents = "auto";
|
|
173
193
|
for (const s of ["click", "mousedown", "mouseup", "pointerdown", "pointerup", "touchstart", "touchend"])
|
|
174
194
|
this.root.addEventListener(s, (i) => i.stopPropagation());
|
|
175
195
|
this.shadowRoot.appendChild(this.root);
|
|
@@ -186,37 +206,54 @@ class E {
|
|
|
186
206
|
this.root.appendChild(n);
|
|
187
207
|
const o = r("div", { className: "fl-quest-content" }), l = r("div", { className: "fl-quest-progress" });
|
|
188
208
|
for (const f of e) {
|
|
189
|
-
const
|
|
190
|
-
l.appendChild(r("div", { className: `fl-quest-progress-dot ${
|
|
209
|
+
const p = f === "COMPLETED" ? "fl-completed" : f === "FAILED" ? "fl-failed" : f === "ACTIVE" ? "fl-active" : "";
|
|
210
|
+
l.appendChild(r("div", { className: `fl-quest-progress-dot ${p}` }));
|
|
191
211
|
}
|
|
192
|
-
const c =
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
212
|
+
const c = D(t.description);
|
|
213
|
+
this.checklist = c.items;
|
|
214
|
+
const h = r("div", { className: "fl-quest-body" });
|
|
215
|
+
if (h.appendChild(l), c.items.length > 0) {
|
|
216
|
+
const f = r("div", { className: "fl-checklist-counter" }), p = () => {
|
|
217
|
+
const b = this.checklist.filter((k) => k.checked).length;
|
|
218
|
+
f.textContent = `โ ${b}/${this.checklist.length} ็ขบ่ชๆธใฟ`;
|
|
219
|
+
};
|
|
220
|
+
p(), h.appendChild(f), c.preamble && h.appendChild(r("p", { className: "fl-quest-description" }, [c.preamble]));
|
|
221
|
+
const m = r("ul", { className: "fl-checklist" });
|
|
222
|
+
for (const b of this.checklist) {
|
|
223
|
+
const k = r("li", { className: "fl-checklist-item" }), v = document.createElement("input");
|
|
224
|
+
v.type = "checkbox", v.className = "fl-checklist-checkbox", v.checked = b.checked;
|
|
225
|
+
const T = r("span", { className: "fl-checklist-text" }, [b.text]);
|
|
226
|
+
v.onchange = () => {
|
|
227
|
+
b.checked = v.checked, k.classList.toggle("fl-checked", b.checked), p();
|
|
228
|
+
}, k.onclick = (q) => {
|
|
229
|
+
q.target !== v && (v.checked = !v.checked, b.checked = v.checked, k.classList.toggle("fl-checked", b.checked), p());
|
|
230
|
+
}, k.appendChild(v), k.appendChild(T), m.appendChild(k);
|
|
231
|
+
}
|
|
232
|
+
h.appendChild(m);
|
|
233
|
+
} else
|
|
234
|
+
h.appendChild(r("p", { className: "fl-quest-description" }, [t.description]));
|
|
235
|
+
h.appendChild(r("div", { className: "fl-quest-memo-row" }, [
|
|
236
|
+
this.createButton("fl-btn fl-btn-memo", "๐ ใกใขใๆฎใ", () => this.renderFeedbackModal(t.title))
|
|
237
|
+
])), h.appendChild(r("div", { className: "fl-quest-actions" }, [
|
|
238
|
+
this.createButton("fl-btn fl-btn-ok", "โ OK", () => this.renderFeedbackModal(t.title, "ok")),
|
|
239
|
+
this.createButton("fl-btn fl-btn-concern", "โ ๆฐใซใชใ", () => this.renderFeedbackModal(t.title, "concern")),
|
|
240
|
+
this.createButton("fl-btn fl-btn-ng", "โ NG", this.callbacks.onNg)
|
|
241
|
+
])), o.appendChild(h), s && o.appendChild(
|
|
205
242
|
r("div", { className: "fl-voice-indicator" }, [
|
|
206
243
|
r("span", { className: "fl-voice-dot" }),
|
|
207
244
|
"Recording..."
|
|
208
245
|
])
|
|
209
246
|
);
|
|
210
|
-
const
|
|
211
|
-
s &&
|
|
247
|
+
const d = r("div", { className: "fl-status-bar" });
|
|
248
|
+
s && d.appendChild(
|
|
212
249
|
r("span", { className: "fl-status-recording" }, [
|
|
213
250
|
r("span", { className: "fl-voice-dot" }),
|
|
214
251
|
"REC"
|
|
215
252
|
])
|
|
216
253
|
);
|
|
217
|
-
const
|
|
218
|
-
|
|
219
|
-
this.minimized && (this.minimized = !1, this.root.className = "fl-quest-overlay", this.root.innerHTML = "", this.root.appendChild(n), this.root.appendChild(o), this.startTimer(
|
|
254
|
+
const g = r("span", { className: "fl-status-timer" }, ["00:00"]);
|
|
255
|
+
d.appendChild(g), o.appendChild(d), this.root.appendChild(o), this.startTimer(g), this.root.onclick = () => {
|
|
256
|
+
this.minimized && (this.minimized = !1, this.root.className = "fl-quest-overlay", this.root.innerHTML = "", this.root.appendChild(n), this.root.appendChild(o), this.startTimer(g));
|
|
220
257
|
};
|
|
221
258
|
}
|
|
222
259
|
renderFeedbackModal(t, e = "memo") {
|
|
@@ -249,11 +286,11 @@ class E {
|
|
|
249
286
|
memo: "ๆฐใฅใใใใจใใกใข...๐"
|
|
250
287
|
}, d = document.createElement("textarea");
|
|
251
288
|
d.className = "fl-feedback-textarea fl-feedback-textarea-modal", d.placeholder = h[e], d.rows = 3, c.appendChild(d);
|
|
252
|
-
const
|
|
253
|
-
this.createButton("fl-btn fl-btn-skip",
|
|
289
|
+
const g = "้ไฟก", f = e === "ok" ? "ในใญใใ" : "ใญใฃใณใปใซ", p = r("div", { className: "fl-quest-actions fl-feedback-modal-actions" }, [
|
|
290
|
+
this.createButton("fl-btn fl-btn-skip", f, () => {
|
|
254
291
|
e === "ng" ? this.callbacks.onNgWithFeedback("") : e === "ok" && this.callbacks.onOkWithFeedback(""), s.remove();
|
|
255
292
|
}),
|
|
256
|
-
this.createButton("fl-btn fl-btn-finish",
|
|
293
|
+
this.createButton("fl-btn fl-btn-finish", g, () => {
|
|
257
294
|
const m = d.value.trim();
|
|
258
295
|
if (e === "ng")
|
|
259
296
|
this.callbacks.onNgWithFeedback(m);
|
|
@@ -304,6 +341,9 @@ class E {
|
|
|
304
341
|
]);
|
|
305
342
|
this.root.appendChild(e);
|
|
306
343
|
}
|
|
344
|
+
getChecklist() {
|
|
345
|
+
return this.checklist.length > 0 ? [...this.checklist] : void 0;
|
|
346
|
+
}
|
|
307
347
|
destroy() {
|
|
308
348
|
this.stopTimer(), this.root.remove();
|
|
309
349
|
}
|
|
@@ -330,15 +370,15 @@ class E {
|
|
|
330
370
|
startTimer(t) {
|
|
331
371
|
this.stopTimer(), this.startTime || (this.startTime = Date.now()), this.timerInterval = setInterval(() => {
|
|
332
372
|
const e = Math.floor((Date.now() - this.startTime) / 1e3);
|
|
333
|
-
t.textContent =
|
|
373
|
+
t.textContent = A(e);
|
|
334
374
|
}, 1e3);
|
|
335
375
|
}
|
|
336
376
|
stopTimer() {
|
|
337
377
|
this.timerInterval && (clearInterval(this.timerInterval), this.timerInterval = null);
|
|
338
378
|
}
|
|
339
379
|
}
|
|
340
|
-
const
|
|
341
|
-
class
|
|
380
|
+
const F = 100, P = 2 * 1024 * 1024;
|
|
381
|
+
class z {
|
|
342
382
|
constructor(t, e, s) {
|
|
343
383
|
this.config = t, this.events = e, this.maskSelector = s, this.actionLogs = [], this.snapshots = [], this.snapshotTimer = null, this.startTime = 0, this.running = !1, this.handleClick = this.onClick.bind(this), this.handleScroll = this.throttle(this.onScroll.bind(this), 500), this.handleInput = this.onInput.bind(this);
|
|
344
384
|
}
|
|
@@ -405,8 +445,8 @@ class D {
|
|
|
405
445
|
const s = t.querySelector("#firstlook-sdk-root");
|
|
406
446
|
s == null || s.remove();
|
|
407
447
|
const i = t.outerHTML;
|
|
408
|
-
if (i.length >
|
|
409
|
-
this.snapshots.length >=
|
|
448
|
+
if (i.length > P) return;
|
|
449
|
+
this.snapshots.length >= F && this.snapshots.shift(), this.snapshots.push({
|
|
410
450
|
type: "dom-snapshot",
|
|
411
451
|
timestamp: Date.now() - this.startTime,
|
|
412
452
|
data: i
|
|
@@ -427,7 +467,7 @@ function x(a) {
|
|
|
427
467
|
const t = a.tagName.toLowerCase(), e = a.id ? `#${a.id}` : "", s = a.className && typeof a.className == "string" ? `.${a.className.trim().split(/\s+/).slice(0, 2).join(".")}` : "", i = ((n = a.textContent) == null ? void 0 : n.trim().slice(0, 30)) || "";
|
|
428
468
|
return `${t}${e}${s}${i ? ` "${i}"` : ""}`;
|
|
429
469
|
}
|
|
430
|
-
class
|
|
470
|
+
class O {
|
|
431
471
|
constructor(t) {
|
|
432
472
|
this.events = t, this.mediaRecorder = null, this.stream = null, this.chunks = [], this._recording = !1;
|
|
433
473
|
}
|
|
@@ -486,7 +526,7 @@ class N {
|
|
|
486
526
|
return "";
|
|
487
527
|
}
|
|
488
528
|
}
|
|
489
|
-
class
|
|
529
|
+
class Q {
|
|
490
530
|
constructor(t, e) {
|
|
491
531
|
this.shadowRoot = t, this.config = e, this.container = null, this.refreshInterval = null, this.cachedIp = null;
|
|
492
532
|
}
|
|
@@ -516,7 +556,7 @@ class F {
|
|
|
516
556
|
}
|
|
517
557
|
}
|
|
518
558
|
}
|
|
519
|
-
class
|
|
559
|
+
class B {
|
|
520
560
|
constructor(t) {
|
|
521
561
|
this.selectors = t, this.observer = null, this.maskedElements = /* @__PURE__ */ new Set();
|
|
522
562
|
}
|
|
@@ -554,20 +594,20 @@ class P {
|
|
|
554
594
|
}
|
|
555
595
|
}
|
|
556
596
|
}
|
|
557
|
-
const
|
|
597
|
+
const $ = "firstlook_uat", U = 1, u = {
|
|
558
598
|
sessions: "sessions",
|
|
559
599
|
recordings: "recordings",
|
|
560
600
|
annotations: "annotations",
|
|
561
601
|
uploadQueue: "upload_queue"
|
|
562
602
|
};
|
|
563
|
-
class
|
|
603
|
+
class j {
|
|
564
604
|
constructor() {
|
|
565
605
|
this.db = null;
|
|
566
606
|
}
|
|
567
607
|
async open() {
|
|
568
608
|
if (!this.db)
|
|
569
609
|
return new Promise((t, e) => {
|
|
570
|
-
const s = indexedDB.open(
|
|
610
|
+
const s = indexedDB.open($, U);
|
|
571
611
|
s.onupgradeneeded = () => {
|
|
572
612
|
const i = s.result;
|
|
573
613
|
i.objectStoreNames.contains(u.sessions) || i.createObjectStore(u.sessions, { keyPath: "sessionId" }), i.objectStoreNames.contains(u.recordings) || i.createObjectStore(u.recordings, { autoIncrement: !0 }).createIndex("sessionId", "sessionId", { unique: !1 }), i.objectStoreNames.contains(u.annotations) || i.createObjectStore(u.annotations, { autoIncrement: !0 }).createIndex("sessionId", "sessionId", { unique: !1 }), i.objectStoreNames.contains(u.uploadQueue) || i.createObjectStore(u.uploadQueue, { autoIncrement: !0 });
|
|
@@ -677,10 +717,10 @@ class Q {
|
|
|
677
717
|
});
|
|
678
718
|
}
|
|
679
719
|
}
|
|
680
|
-
const
|
|
681
|
-
class
|
|
720
|
+
const S = ["#e17055", "#6c5ce7", "#00b894", "#fdcb6e", "#ffffff"];
|
|
721
|
+
class K {
|
|
682
722
|
constructor(t) {
|
|
683
|
-
this.shadowRoot = t, this.overlay = null, this.canvas = null, this.ctx = null, this.drawing = !1, this.currentPath = [], this.paths = [], this.selectedColor =
|
|
723
|
+
this.shadowRoot = t, this.overlay = null, this.canvas = null, this.ctx = null, this.drawing = !1, this.currentPath = [], this.paths = [], this.selectedColor = S[0], this.screenshotDataUrl = "";
|
|
684
724
|
}
|
|
685
725
|
async open() {
|
|
686
726
|
return this.screenshotDataUrl = await this.captureScreenshot(), new Promise((t) => {
|
|
@@ -692,7 +732,7 @@ class B {
|
|
|
692
732
|
type: "text",
|
|
693
733
|
placeholder: "Add a comment..."
|
|
694
734
|
}), i = r("div", { className: "fl-color-picker" });
|
|
695
|
-
for (const c of
|
|
735
|
+
for (const c of S) {
|
|
696
736
|
const h = r("div", { className: `fl-color-swatch ${c === this.selectedColor ? "fl-selected" : ""}` });
|
|
697
737
|
h.style.background = c, h.onclick = () => {
|
|
698
738
|
this.selectedColor = c, i.querySelectorAll(".fl-color-swatch").forEach((d) => d.classList.remove("fl-selected")), h.classList.add("fl-selected");
|
|
@@ -742,13 +782,13 @@ class B {
|
|
|
742
782
|
<foreignObject width="100%" height="100%">
|
|
743
783
|
${l}
|
|
744
784
|
</foreignObject>
|
|
745
|
-
</svg>`, h = new Blob([c], { type: "image/svg+xml;charset=utf-8" }), d = URL.createObjectURL(h),
|
|
746
|
-
|
|
747
|
-
const
|
|
748
|
-
return
|
|
785
|
+
</svg>`, h = new Blob([c], { type: "image/svg+xml;charset=utf-8" }), d = URL.createObjectURL(h), g = document.createElement("canvas");
|
|
786
|
+
g.width = t * window.devicePixelRatio, g.height = e * window.devicePixelRatio;
|
|
787
|
+
const f = g.getContext("2d");
|
|
788
|
+
return f.scale(window.devicePixelRatio, window.devicePixelRatio), new Promise((p) => {
|
|
749
789
|
const m = new Image();
|
|
750
790
|
m.onload = () => {
|
|
751
|
-
|
|
791
|
+
f.drawImage(m, 0, 0, t, e), URL.revokeObjectURL(d), p(g.toDataURL("image/png"));
|
|
752
792
|
}, m.onerror = () => {
|
|
753
793
|
URL.revokeObjectURL(d), p(this.captureScreenshotFallback(t, e));
|
|
754
794
|
}, m.src = d;
|
|
@@ -796,11 +836,11 @@ class B {
|
|
|
796
836
|
return ((t = this.canvas) == null ? void 0 : t.toDataURL("image/png")) || this.screenshotDataUrl;
|
|
797
837
|
}
|
|
798
838
|
}
|
|
799
|
-
class
|
|
839
|
+
class H {
|
|
800
840
|
constructor(t, e) {
|
|
801
841
|
this.shadowRoot = t, this.events = e, this.annotations = [], this.active = !1, this.onKeydown = (s) => {
|
|
802
842
|
s.ctrlKey && s.shiftKey && s.key === "R" && (s.preventDefault(), this.trigger());
|
|
803
|
-
}, this.annotationCanvas = new
|
|
843
|
+
}, this.annotationCanvas = new K(t);
|
|
804
844
|
}
|
|
805
845
|
start() {
|
|
806
846
|
document.addEventListener("keydown", this.onKeydown);
|
|
@@ -823,7 +863,7 @@ class U {
|
|
|
823
863
|
}
|
|
824
864
|
}
|
|
825
865
|
}
|
|
826
|
-
class
|
|
866
|
+
class _ {
|
|
827
867
|
constructor(t, e) {
|
|
828
868
|
this.requiredTaps = t, this.onActivate = e, this.tapCount = 0, this.tapTimer = null, this.handler = null;
|
|
829
869
|
}
|
|
@@ -840,8 +880,8 @@ class j {
|
|
|
840
880
|
this.tapCount = 0, this.tapTimer && (clearTimeout(this.tapTimer), this.tapTimer = null);
|
|
841
881
|
}
|
|
842
882
|
}
|
|
843
|
-
const
|
|
844
|
-
class
|
|
883
|
+
const w = "firstlook:uat-active";
|
|
884
|
+
class C {
|
|
845
885
|
constructor(t) {
|
|
846
886
|
this.onActivate = t, this.handlers = [];
|
|
847
887
|
}
|
|
@@ -866,7 +906,7 @@ class S {
|
|
|
866
906
|
}
|
|
867
907
|
static clearPersisted() {
|
|
868
908
|
try {
|
|
869
|
-
sessionStorage.removeItem(
|
|
909
|
+
sessionStorage.removeItem(w);
|
|
870
910
|
} catch {
|
|
871
911
|
}
|
|
872
912
|
}
|
|
@@ -884,19 +924,19 @@ class S {
|
|
|
884
924
|
}
|
|
885
925
|
persist() {
|
|
886
926
|
try {
|
|
887
|
-
sessionStorage.setItem(
|
|
927
|
+
sessionStorage.setItem(w, "1");
|
|
888
928
|
} catch {
|
|
889
929
|
}
|
|
890
930
|
}
|
|
891
931
|
hasPersisted() {
|
|
892
932
|
try {
|
|
893
|
-
return sessionStorage.getItem(
|
|
933
|
+
return sessionStorage.getItem(w) === "1";
|
|
894
934
|
} catch {
|
|
895
935
|
return !1;
|
|
896
936
|
}
|
|
897
937
|
}
|
|
898
938
|
}
|
|
899
|
-
class
|
|
939
|
+
class W {
|
|
900
940
|
constructor(t) {
|
|
901
941
|
this.onActivate = t, this.handler = null;
|
|
902
942
|
}
|
|
@@ -909,7 +949,7 @@ class $ {
|
|
|
909
949
|
this.handler && (window.removeEventListener("keydown", this.handler), this.handler = null);
|
|
910
950
|
}
|
|
911
951
|
}
|
|
912
|
-
class
|
|
952
|
+
class V {
|
|
913
953
|
constructor(t, e) {
|
|
914
954
|
this.shadowRoot = t, this.callbacks = e, this.root = null, this.outsideClickHandler = null;
|
|
915
955
|
}
|
|
@@ -946,7 +986,7 @@ class K {
|
|
|
946
986
|
return this.root !== null;
|
|
947
987
|
}
|
|
948
988
|
}
|
|
949
|
-
const
|
|
989
|
+
const X = (
|
|
950
990
|
/* css */
|
|
951
991
|
`
|
|
952
992
|
:host {
|
|
@@ -1157,6 +1197,51 @@ const H = (
|
|
|
1157
1197
|
50% { opacity: 0.3; }
|
|
1158
1198
|
}
|
|
1159
1199
|
|
|
1200
|
+
/* === Checklist === */
|
|
1201
|
+
.fl-checklist-counter {
|
|
1202
|
+
font-size: 13px;
|
|
1203
|
+
font-weight: 600;
|
|
1204
|
+
color: #6c5ce7;
|
|
1205
|
+
margin-bottom: 8px;
|
|
1206
|
+
}
|
|
1207
|
+
.fl-checklist {
|
|
1208
|
+
list-style: none;
|
|
1209
|
+
padding: 0;
|
|
1210
|
+
margin: 0 0 12px;
|
|
1211
|
+
max-height: 240px;
|
|
1212
|
+
overflow-y: auto;
|
|
1213
|
+
-webkit-overflow-scrolling: touch;
|
|
1214
|
+
}
|
|
1215
|
+
.fl-checklist-item {
|
|
1216
|
+
display: flex;
|
|
1217
|
+
align-items: flex-start;
|
|
1218
|
+
gap: 8px;
|
|
1219
|
+
padding: 8px 4px;
|
|
1220
|
+
border-bottom: 1px solid rgba(0,0,0,.06);
|
|
1221
|
+
font-size: 13px;
|
|
1222
|
+
color: #4a4a5a;
|
|
1223
|
+
cursor: pointer;
|
|
1224
|
+
min-height: 40px;
|
|
1225
|
+
transition: background .2s;
|
|
1226
|
+
}
|
|
1227
|
+
.fl-checklist-item:last-child { border-bottom: none; }
|
|
1228
|
+
.fl-checklist-item:active { background: rgba(108,92,231,.05); }
|
|
1229
|
+
.fl-checklist-item.fl-checked { color: #aaa; }
|
|
1230
|
+
.fl-checklist-item.fl-checked .fl-checklist-text { text-decoration: line-through; }
|
|
1231
|
+
.fl-checklist-checkbox {
|
|
1232
|
+
margin-top: 2px;
|
|
1233
|
+
width: 18px;
|
|
1234
|
+
height: 18px;
|
|
1235
|
+
accent-color: #6c5ce7;
|
|
1236
|
+
flex-shrink: 0;
|
|
1237
|
+
cursor: pointer;
|
|
1238
|
+
}
|
|
1239
|
+
.fl-checklist-text {
|
|
1240
|
+
flex: 1;
|
|
1241
|
+
line-height: 1.4;
|
|
1242
|
+
word-break: break-word;
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1160
1245
|
/* === Annotation overlay === */
|
|
1161
1246
|
.fl-annotation-overlay {
|
|
1162
1247
|
position: fixed;
|
|
@@ -1402,10 +1487,10 @@ const H = (
|
|
|
1402
1487
|
}
|
|
1403
1488
|
.fl-feedback-modal-actions { padding: 0 16px 16px; }
|
|
1404
1489
|
`
|
|
1405
|
-
),
|
|
1406
|
-
class
|
|
1490
|
+
), Y = 5;
|
|
1491
|
+
class G {
|
|
1407
1492
|
constructor() {
|
|
1408
|
-
this.config = null, this.events = new
|
|
1493
|
+
this.config = null, this.events = new E(), this.state = "idle", this.hostElement = null, this.shadowRoot = null, this.questManager = null, this.questOverlay = null, this.sessionRecorder = null, this.voiceRecorder = null, this.watermark = null, this.fieldMasker = null, this.storage = null, this.issueReporter = null, this.debugMenu = null, this.tapTrigger = null, this.deepLinkTrigger = null, this.keyboardTrigger = null, this.sessionId = null, this.sessionStartTime = 0, this.maxDurationTimer = null, this.backupTimer = null, this.isFlushing = !1;
|
|
1409
1494
|
}
|
|
1410
1495
|
// ----------------------------------------------------------------
|
|
1411
1496
|
// Public API
|
|
@@ -1419,7 +1504,7 @@ class W {
|
|
|
1419
1504
|
console.warn("[FirstLook] SDK already initialized.");
|
|
1420
1505
|
return;
|
|
1421
1506
|
}
|
|
1422
|
-
this.config =
|
|
1507
|
+
this.config = M(t), this.storage = new j(), await this.storage.open(), this.createShadowHost(), this.setupTriggers(), this.fieldMasker = new B(this.config.security.maskSelectors), this.state = "initialized", this.events.emit({ type: "sdk:initialized" }), this.flushUploadQueue(!0);
|
|
1423
1508
|
}
|
|
1424
1509
|
/**
|
|
1425
1510
|
* Manually activate the SDK UI (bypassing triggers).
|
|
@@ -1438,11 +1523,11 @@ class W {
|
|
|
1438
1523
|
var s;
|
|
1439
1524
|
if (this.state !== "active")
|
|
1440
1525
|
throw new Error("[FirstLook] Cannot start session: SDK not active. Call activate() first.");
|
|
1441
|
-
(s = this.debugMenu) == null || s.hide(), this.sessionId =
|
|
1526
|
+
(s = this.debugMenu) == null || s.hide(), this.sessionId = L(), this.sessionStartTime = Date.now(), this.questManager = new R(this.events), this.questManager.loadQuests(t), this.sessionRecorder = new z(
|
|
1442
1527
|
this.config,
|
|
1443
1528
|
this.events,
|
|
1444
1529
|
this.fieldMasker.getCombinedSelector()
|
|
1445
|
-
), this.voiceRecorder = new
|
|
1530
|
+
), this.voiceRecorder = new O(this.events), this.issueReporter = new H(this.shadowRoot, this.events), this.issueReporter.start(), this.sessionRecorder.start(), this.fieldMasker.start(), this.config.recording.voice && await this.voiceRecorder.start(), this.config.security.watermark && (this.watermark = new Q(this.shadowRoot, this.config), this.watermark.show()), this.questOverlay = new N(this.shadowRoot, {
|
|
1446
1531
|
onOkWithFeedback: (i) => this.handleQuestOk(i),
|
|
1447
1532
|
onConcern: (i) => this.handleConcern(i),
|
|
1448
1533
|
onNg: () => this.handleQuestNg(),
|
|
@@ -1475,7 +1560,7 @@ class W {
|
|
|
1475
1560
|
* End the current session, persist data, and trigger upload.
|
|
1476
1561
|
*/
|
|
1477
1562
|
async endSession() {
|
|
1478
|
-
var i, n, o, l, c, h, d,
|
|
1563
|
+
var i, n, o, l, c, h, d, g, f, p, m;
|
|
1479
1564
|
if (this.state !== "recording" || !this.sessionId) return null;
|
|
1480
1565
|
this.maxDurationTimer && (clearTimeout(this.maxDurationTimer), this.maxDurationTimer = null), this.backupTimer && (clearInterval(this.backupTimer), this.backupTimer = null), (i = this.sessionRecorder) == null || i.stop(), await ((n = this.voiceRecorder) == null ? void 0 : n.stopAsync()), (o = this.issueReporter) == null || o.stop(), (l = this.fieldMasker) == null || l.stop(), (c = this.watermark) == null || c.hide();
|
|
1481
1566
|
const t = this.questManager.getResults();
|
|
@@ -1489,13 +1574,13 @@ class W {
|
|
|
1489
1574
|
delete b.voiceMemoBlob;
|
|
1490
1575
|
}
|
|
1491
1576
|
await Promise.allSettled(
|
|
1492
|
-
b.feedbacks.map(async (
|
|
1493
|
-
if (
|
|
1577
|
+
b.feedbacks.map(async (k) => {
|
|
1578
|
+
if (k.voiceMemoBlob) {
|
|
1494
1579
|
try {
|
|
1495
|
-
|
|
1580
|
+
k.voiceMemoBase64 = await this.blobToBase64(k.voiceMemoBlob);
|
|
1496
1581
|
} catch {
|
|
1497
1582
|
}
|
|
1498
|
-
delete
|
|
1583
|
+
delete k.voiceMemoBlob;
|
|
1499
1584
|
}
|
|
1500
1585
|
})
|
|
1501
1586
|
);
|
|
@@ -1506,7 +1591,7 @@ class W {
|
|
|
1506
1591
|
projectId: this.config.projectId,
|
|
1507
1592
|
userId: this.config.userId,
|
|
1508
1593
|
role: this.config.role,
|
|
1509
|
-
deviceInfo:
|
|
1594
|
+
deviceInfo: y(),
|
|
1510
1595
|
startedAt: new Date(this.sessionStartTime).toISOString(),
|
|
1511
1596
|
endedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1512
1597
|
duration: Math.floor((Date.now() - this.sessionStartTime) / 1e3),
|
|
@@ -1514,9 +1599,9 @@ class W {
|
|
|
1514
1599
|
recordings: ((h = this.sessionRecorder) == null ? void 0 : h.getSnapshots()) ?? []
|
|
1515
1600
|
};
|
|
1516
1601
|
await ((d = this.storage) == null ? void 0 : d.saveSession(e));
|
|
1517
|
-
const s = ((
|
|
1602
|
+
const s = ((g = this.issueReporter) == null ? void 0 : g.getAnnotations()) ?? [];
|
|
1518
1603
|
for (const b of s)
|
|
1519
|
-
await ((
|
|
1604
|
+
await ((f = this.storage) == null ? void 0 : f.saveAnnotation(this.sessionId, b));
|
|
1520
1605
|
return await ((p = this.storage) == null ? void 0 : p.enqueueUpload({ session: e, annotations: s })), (m = this.questOverlay) == null || m.destroy(), this.questOverlay = null, this.state = "finished", this.events.emit({ type: "session:ended", sessionId: this.sessionId }), this.flushUploadQueue(!1), e;
|
|
1521
1606
|
}
|
|
1522
1607
|
/**
|
|
@@ -1535,9 +1620,9 @@ class W {
|
|
|
1535
1620
|
* Completely destroy the SDK instance and clean up all resources.
|
|
1536
1621
|
*/
|
|
1537
1622
|
destroy() {
|
|
1538
|
-
var t, e, s, i, n, o, l, c, h, d,
|
|
1539
|
-
this.maxDurationTimer && (clearTimeout(this.maxDurationTimer), this.maxDurationTimer = null), this.backupTimer && (clearInterval(this.backupTimer), this.backupTimer = null), (t = this.tapTrigger) == null || t.stop(), (e = this.deepLinkTrigger) == null || e.stop(), (s = this.keyboardTrigger) == null || s.stop(),
|
|
1540
|
-
}), (o = this.issueReporter) == null || o.stop(), (l = this.fieldMasker) == null || l.stop(), (c = this.watermark) == null || c.hide(), (h = this.questOverlay) == null || h.destroy(), (d = this.debugMenu) == null || d.hide(), (
|
|
1623
|
+
var t, e, s, i, n, o, l, c, h, d, g, f;
|
|
1624
|
+
this.maxDurationTimer && (clearTimeout(this.maxDurationTimer), this.maxDurationTimer = null), this.backupTimer && (clearInterval(this.backupTimer), this.backupTimer = null), (t = this.tapTrigger) == null || t.stop(), (e = this.deepLinkTrigger) == null || e.stop(), (s = this.keyboardTrigger) == null || s.stop(), C.clearPersisted(), (i = this.sessionRecorder) == null || i.stop(), (n = this.voiceRecorder) == null || n.stopAsync().catch(() => {
|
|
1625
|
+
}), (o = this.issueReporter) == null || o.stop(), (l = this.fieldMasker) == null || l.stop(), (c = this.watermark) == null || c.hide(), (h = this.questOverlay) == null || h.destroy(), (d = this.debugMenu) == null || d.hide(), (g = this.hostElement) == null || g.remove(), (f = this.storage) == null || f.close(), this.events.removeAll(), this.config = null, this.questManager = null, this.questOverlay = null, this.sessionRecorder = null, this.voiceRecorder = null, this.issueReporter = null, this.fieldMasker = null, this.debugMenu = null, this.tapTrigger = null, this.deepLinkTrigger = null, this.keyboardTrigger = null, this.storage = null, this.hostElement = null, this.shadowRoot = null, this.sessionId = null, this.state = "idle";
|
|
1541
1626
|
}
|
|
1542
1627
|
// ----------------------------------------------------------------
|
|
1543
1628
|
// Private methods
|
|
@@ -1546,15 +1631,15 @@ class W {
|
|
|
1546
1631
|
var e;
|
|
1547
1632
|
(e = document.getElementById("firstlook-sdk-root")) == null || e.remove(), this.hostElement = document.createElement("div"), this.hostElement.id = "firstlook-sdk-root", this.hostElement.style.cssText = "position:fixed;inset:0;z-index:2147483647;pointer-events:none;", document.body.appendChild(this.hostElement), this.shadowRoot = this.hostElement.attachShadow({ mode: "closed" });
|
|
1548
1633
|
const t = document.createElement("style");
|
|
1549
|
-
t.textContent =
|
|
1634
|
+
t.textContent = X, this.shadowRoot.appendChild(t);
|
|
1550
1635
|
}
|
|
1551
1636
|
setupTriggers() {
|
|
1552
1637
|
const t = this.config.triggers;
|
|
1553
|
-
this.tapTrigger = new
|
|
1638
|
+
this.tapTrigger = new _(t.tapCount, () => this.onActivate()), this.tapTrigger.start(), t.deepLink && (this.deepLinkTrigger = new C(() => this.onActivate()), this.deepLinkTrigger.start()), t.keyboard && (this.keyboardTrigger = new W(() => this.onActivate()), this.keyboardTrigger.start()), t.customCheck && t.customCheck() && this.onActivate();
|
|
1554
1639
|
}
|
|
1555
1640
|
onActivate() {
|
|
1556
1641
|
var t, e, s;
|
|
1557
|
-
this.state === "initialized" && (this.state = "active", (t = this.tapTrigger) == null || t.stop(), (e = this.deepLinkTrigger) == null || e.stop(), (s = this.keyboardTrigger) == null || s.stop(), this.events.emit({ type: "sdk:activated" }), this.debugMenu = new
|
|
1642
|
+
this.state === "initialized" && (this.state = "active", (t = this.tapTrigger) == null || t.stop(), (e = this.deepLinkTrigger) == null || e.stop(), (s = this.keyboardTrigger) == null || s.stop(), this.events.emit({ type: "sdk:activated" }), this.debugMenu = new V(this.shadowRoot, {
|
|
1558
1643
|
onStartSession: () => {
|
|
1559
1644
|
this.events.emit({ type: "sdk:activated" });
|
|
1560
1645
|
},
|
|
@@ -1568,18 +1653,22 @@ class W {
|
|
|
1568
1653
|
}), this.debugMenu.show());
|
|
1569
1654
|
}
|
|
1570
1655
|
handleQuestOk(t) {
|
|
1656
|
+
var i;
|
|
1571
1657
|
if (!this.questManager || !this.sessionRecorder) return;
|
|
1572
|
-
const e = this.sessionRecorder.getActionLogs();
|
|
1658
|
+
const e = this.sessionRecorder.getActionLogs(), s = (i = this.questOverlay) == null ? void 0 : i.getChecklist();
|
|
1573
1659
|
this.questManager.completeCurrentQuest(e, {
|
|
1574
|
-
comment: t || void 0
|
|
1660
|
+
comment: t || void 0,
|
|
1661
|
+
checklist: s
|
|
1575
1662
|
}), this.renderCurrentQuest();
|
|
1576
1663
|
}
|
|
1577
1664
|
handleConcern(t) {
|
|
1665
|
+
var i;
|
|
1578
1666
|
if (!this.questManager || !this.sessionRecorder) return;
|
|
1579
|
-
const e = this.sessionRecorder.getActionLogs();
|
|
1667
|
+
const e = this.sessionRecorder.getActionLogs(), s = (i = this.questOverlay) == null ? void 0 : i.getChecklist();
|
|
1580
1668
|
this.questManager.completeCurrentQuest(e, {
|
|
1581
1669
|
comment: t,
|
|
1582
|
-
concern: !0
|
|
1670
|
+
concern: !0,
|
|
1671
|
+
checklist: s
|
|
1583
1672
|
}), this.renderCurrentQuest();
|
|
1584
1673
|
}
|
|
1585
1674
|
handleQuestNg() {
|
|
@@ -1588,9 +1677,10 @@ class W {
|
|
|
1588
1677
|
t && this.questOverlay.renderFeedbackModal(t.title, "ng");
|
|
1589
1678
|
}
|
|
1590
1679
|
handleNgFeedbackSubmit(t) {
|
|
1680
|
+
var i;
|
|
1591
1681
|
if (!this.questManager || !this.sessionRecorder) return;
|
|
1592
|
-
const e = this.sessionRecorder.getActionLogs();
|
|
1593
|
-
this.questManager.failCurrentQuest(e, t || void 0), this.renderCurrentQuest();
|
|
1682
|
+
const e = this.sessionRecorder.getActionLogs(), s = (i = this.questOverlay) == null ? void 0 : i.getChecklist();
|
|
1683
|
+
this.questManager.failCurrentQuest(e, t || void 0, void 0, s), this.renderCurrentQuest();
|
|
1594
1684
|
}
|
|
1595
1685
|
handleMemo(t) {
|
|
1596
1686
|
if (!this.questManager || !t) return;
|
|
@@ -1632,7 +1722,7 @@ class W {
|
|
|
1632
1722
|
}
|
|
1633
1723
|
const e = await this.storage.getPendingUploads();
|
|
1634
1724
|
for (const s of e) {
|
|
1635
|
-
if (s.attempts >=
|
|
1725
|
+
if (s.attempts >= Y) {
|
|
1636
1726
|
await this.storage.removeFromQueue(s.key);
|
|
1637
1727
|
continue;
|
|
1638
1728
|
}
|
|
@@ -1668,7 +1758,7 @@ class W {
|
|
|
1668
1758
|
projectId: this.config.projectId,
|
|
1669
1759
|
userId: this.config.userId,
|
|
1670
1760
|
role: this.config.role,
|
|
1671
|
-
deviceInfo:
|
|
1761
|
+
deviceInfo: y(),
|
|
1672
1762
|
startedAt: new Date(this.sessionStartTime).toISOString(),
|
|
1673
1763
|
duration: Math.floor((Date.now() - this.sessionStartTime) / 1e3),
|
|
1674
1764
|
quests: ((t = this.questManager) == null ? void 0 : t.getResults()) ?? [],
|
|
@@ -1686,6 +1776,6 @@ class W {
|
|
|
1686
1776
|
}
|
|
1687
1777
|
}
|
|
1688
1778
|
export {
|
|
1689
|
-
|
|
1779
|
+
G as FirstLookSDK
|
|
1690
1780
|
};
|
|
1691
1781
|
//# sourceMappingURL=firstlook.es.js.map
|