@checkflow/sdk 1.1.2 → 1.1.3
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/chunk-6EVTRC5X.mjs +59 -0
- package/dist/{highlighter-D_wZWHlS.d.mts → highlighter-Dx2zURb6.d.mts} +2 -0
- package/dist/{highlighter-D_wZWHlS.d.ts → highlighter-Dx2zURb6.d.ts} +2 -0
- package/dist/index.d.mts +10 -5
- package/dist/index.d.ts +10 -5
- package/dist/index.js +545 -176
- package/dist/index.mjs +514 -123
- package/dist/react.d.mts +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/react.js +554 -179
- package/dist/react.mjs +1 -1
- package/dist/screenshot-HXKGZNCZ.mjs +10 -0
- package/dist/vue.d.mts +1 -1
- package/dist/vue.d.ts +1 -1
- package/dist/vue.js +554 -179
- package/dist/vue.mjs +1 -1
- package/package.json +1 -1
- package/dist/chunk-CQ56DMFR.mjs +0 -83
- package/dist/screenshot-CUMBPE2T.mjs +0 -12
package/dist/index.js
CHANGED
|
@@ -134,6 +134,75 @@ function clearLogs() {
|
|
|
134
134
|
errorsCapture = [];
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
+
// src/network-interceptor.ts
|
|
138
|
+
var MAX_LOGS2 = 50;
|
|
139
|
+
var logs2 = [];
|
|
140
|
+
var installed2 = false;
|
|
141
|
+
function installNetworkInterceptor() {
|
|
142
|
+
if (installed2 || typeof window === "undefined") return;
|
|
143
|
+
installed2 = true;
|
|
144
|
+
const origFetch = window.fetch;
|
|
145
|
+
window.fetch = async function(...args) {
|
|
146
|
+
const start = Date.now();
|
|
147
|
+
const req = new Request(...args);
|
|
148
|
+
const entry = {
|
|
149
|
+
method: req.method,
|
|
150
|
+
url: req.url,
|
|
151
|
+
type: "fetch",
|
|
152
|
+
timestamp: start
|
|
153
|
+
};
|
|
154
|
+
try {
|
|
155
|
+
const res = await origFetch.apply(this, args);
|
|
156
|
+
entry.status = res.status;
|
|
157
|
+
entry.duration = Date.now() - start;
|
|
158
|
+
entry.response_type = res.headers.get("content-type") || void 0;
|
|
159
|
+
pushLog(entry);
|
|
160
|
+
return res;
|
|
161
|
+
} catch (err) {
|
|
162
|
+
entry.duration = Date.now() - start;
|
|
163
|
+
entry.error = err.message || "Network error";
|
|
164
|
+
pushLog(entry);
|
|
165
|
+
throw err;
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
const origOpen = XMLHttpRequest.prototype.open;
|
|
169
|
+
const origSend = XMLHttpRequest.prototype.send;
|
|
170
|
+
XMLHttpRequest.prototype.open = function(method, url, ...rest) {
|
|
171
|
+
this.__cf_method = method;
|
|
172
|
+
this.__cf_url = String(url);
|
|
173
|
+
return origOpen.apply(this, [method, url, ...rest]);
|
|
174
|
+
};
|
|
175
|
+
XMLHttpRequest.prototype.send = function(...args) {
|
|
176
|
+
const start = Date.now();
|
|
177
|
+
const xhr = this;
|
|
178
|
+
xhr.addEventListener("loadend", () => {
|
|
179
|
+
const entry = {
|
|
180
|
+
method: xhr.__cf_method || "GET",
|
|
181
|
+
url: xhr.__cf_url || "",
|
|
182
|
+
status: xhr.status,
|
|
183
|
+
duration: Date.now() - start,
|
|
184
|
+
type: "xhr",
|
|
185
|
+
timestamp: start,
|
|
186
|
+
response_type: xhr.getResponseHeader("content-type") || void 0
|
|
187
|
+
};
|
|
188
|
+
if (xhr.status === 0) entry.error = "Request failed";
|
|
189
|
+
pushLog(entry);
|
|
190
|
+
});
|
|
191
|
+
return origSend.apply(this, args);
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
function pushLog(entry) {
|
|
195
|
+
if (entry.url.includes("/sdk/feedback")) return;
|
|
196
|
+
logs2.push(entry);
|
|
197
|
+
if (logs2.length > MAX_LOGS2) logs2.shift();
|
|
198
|
+
}
|
|
199
|
+
function getNetworkLogs() {
|
|
200
|
+
return [...logs2];
|
|
201
|
+
}
|
|
202
|
+
function clearNetworkLogs() {
|
|
203
|
+
logs2 = [];
|
|
204
|
+
}
|
|
205
|
+
|
|
137
206
|
// src/widget.ts
|
|
138
207
|
var DEFAULT_CONFIG = {
|
|
139
208
|
position: "bottom-right",
|
|
@@ -145,78 +214,154 @@ var container = null;
|
|
|
145
214
|
var onSubmitCallback = null;
|
|
146
215
|
var screenshotDataUrl = null;
|
|
147
216
|
var annotationsData = [];
|
|
217
|
+
var isExpanded = false;
|
|
148
218
|
function mountWidget(config2 = {}, onSubmit, opts) {
|
|
149
219
|
if (container) return;
|
|
150
220
|
onSubmitCallback = onSubmit;
|
|
151
221
|
const cfg = { ...DEFAULT_CONFIG, ...config2 };
|
|
152
222
|
container = document.createElement("div");
|
|
153
223
|
container.id = "checkflow-widget";
|
|
154
|
-
|
|
224
|
+
injectStyles(cfg);
|
|
225
|
+
container.innerHTML = getTriggerHTML(cfg);
|
|
155
226
|
document.body.appendChild(container);
|
|
156
227
|
const btn = container.querySelector("#cf-trigger");
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
const
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
228
|
+
btn?.addEventListener("click", () => openModal(cfg, opts));
|
|
229
|
+
}
|
|
230
|
+
function openModal(cfg, opts) {
|
|
231
|
+
if (!container) return;
|
|
232
|
+
const existing = document.getElementById("cf-modal-backdrop");
|
|
233
|
+
if (existing) existing.remove();
|
|
234
|
+
isExpanded = false;
|
|
235
|
+
screenshotDataUrl = null;
|
|
236
|
+
annotationsData = [];
|
|
237
|
+
const backdrop = document.createElement("div");
|
|
238
|
+
backdrop.id = "cf-modal-backdrop";
|
|
239
|
+
backdrop.innerHTML = getModalHTML(cfg, false);
|
|
240
|
+
document.body.appendChild(backdrop);
|
|
241
|
+
backdrop.addEventListener("click", (e) => {
|
|
242
|
+
if (e.target === backdrop) closeModal();
|
|
171
243
|
});
|
|
172
|
-
|
|
244
|
+
bindModalEvents(cfg, opts);
|
|
245
|
+
}
|
|
246
|
+
function closeModal() {
|
|
247
|
+
const backdrop = document.getElementById("cf-modal-backdrop");
|
|
248
|
+
if (backdrop) backdrop.remove();
|
|
249
|
+
isExpanded = false;
|
|
250
|
+
screenshotDataUrl = null;
|
|
251
|
+
annotationsData = [];
|
|
252
|
+
}
|
|
253
|
+
function expandModal(cfg, opts) {
|
|
254
|
+
const backdrop = document.getElementById("cf-modal-backdrop");
|
|
255
|
+
if (!backdrop) return;
|
|
256
|
+
const name = backdrop.querySelector("#cf-name")?.value || "";
|
|
257
|
+
const email = backdrop.querySelector("#cf-email")?.value || "";
|
|
258
|
+
const desc = backdrop.querySelector("#cf-desc")?.value || "";
|
|
259
|
+
isExpanded = true;
|
|
260
|
+
backdrop.innerHTML = getModalHTML(cfg, true);
|
|
261
|
+
bindModalEvents(cfg, opts);
|
|
262
|
+
const nameEl = backdrop.querySelector("#cf-name");
|
|
263
|
+
const emailEl = backdrop.querySelector("#cf-email");
|
|
264
|
+
const descEl = backdrop.querySelector("#cf-desc");
|
|
265
|
+
if (nameEl) nameEl.value = name;
|
|
266
|
+
if (emailEl) emailEl.value = email;
|
|
267
|
+
if (descEl) descEl.value = desc;
|
|
268
|
+
}
|
|
269
|
+
function collapseModal(cfg, opts) {
|
|
270
|
+
const backdrop = document.getElementById("cf-modal-backdrop");
|
|
271
|
+
if (!backdrop) return;
|
|
272
|
+
const name = backdrop.querySelector("#cf-name")?.value || "";
|
|
273
|
+
const email = backdrop.querySelector("#cf-email")?.value || "";
|
|
274
|
+
const desc = backdrop.querySelector("#cf-desc")?.value || "";
|
|
275
|
+
isExpanded = false;
|
|
276
|
+
screenshotDataUrl = null;
|
|
277
|
+
annotationsData = [];
|
|
278
|
+
backdrop.innerHTML = getModalHTML(cfg, false);
|
|
279
|
+
bindModalEvents(cfg, opts);
|
|
280
|
+
const nameEl = backdrop.querySelector("#cf-name");
|
|
281
|
+
const emailEl = backdrop.querySelector("#cf-email");
|
|
282
|
+
const descEl = backdrop.querySelector("#cf-desc");
|
|
283
|
+
if (nameEl) nameEl.value = name;
|
|
284
|
+
if (emailEl) emailEl.value = email;
|
|
285
|
+
if (descEl) descEl.value = desc;
|
|
286
|
+
}
|
|
287
|
+
function bindModalEvents(cfg, opts) {
|
|
288
|
+
const backdrop = document.getElementById("cf-modal-backdrop");
|
|
289
|
+
if (!backdrop) return;
|
|
290
|
+
backdrop.querySelector("#cf-cancel")?.addEventListener("click", closeModal);
|
|
291
|
+
backdrop.querySelector("#cf-screenshot-btn")?.addEventListener("click", async () => {
|
|
173
292
|
if (!opts?.onScreenshot) return;
|
|
174
|
-
|
|
175
|
-
form.style.display = "none";
|
|
293
|
+
backdrop.style.display = "none";
|
|
176
294
|
try {
|
|
177
295
|
const data = await opts.onScreenshot();
|
|
178
296
|
if (data) {
|
|
179
297
|
screenshotDataUrl = data;
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
}
|
|
298
|
+
backdrop.style.display = "flex";
|
|
299
|
+
expandModal(cfg, opts);
|
|
300
|
+
return;
|
|
184
301
|
}
|
|
185
302
|
} catch {
|
|
186
303
|
}
|
|
187
|
-
|
|
188
|
-
|
|
304
|
+
backdrop.style.display = "flex";
|
|
305
|
+
});
|
|
306
|
+
backdrop.querySelector("#cf-remove-screenshot")?.addEventListener("click", () => {
|
|
307
|
+
collapseModal(cfg, opts);
|
|
189
308
|
});
|
|
190
|
-
|
|
309
|
+
backdrop.querySelector("#cf-highlight-btn")?.addEventListener("click", async () => {
|
|
191
310
|
if (!opts?.onHighlight) return;
|
|
192
|
-
|
|
311
|
+
backdrop.style.display = "none";
|
|
193
312
|
try {
|
|
194
313
|
const annotations = await opts.onHighlight();
|
|
195
314
|
if (annotations && annotations.length > 0) {
|
|
196
315
|
annotationsData = annotations;
|
|
197
|
-
highlightBtn.innerHTML = `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 20h9"/><path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"/></svg> ${annotations.length} highlighted`;
|
|
198
316
|
}
|
|
199
317
|
} catch {
|
|
200
318
|
}
|
|
201
|
-
|
|
319
|
+
backdrop.style.display = "flex";
|
|
320
|
+
const hlBtn = backdrop.querySelector("#cf-highlight-btn");
|
|
321
|
+
if (hlBtn && annotationsData.length > 0) {
|
|
322
|
+
hlBtn.textContent = `Surligner (${annotationsData.length})`;
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
backdrop.querySelector("#cf-mask-btn")?.addEventListener("click", async () => {
|
|
326
|
+
if (!opts?.onHighlight) return;
|
|
327
|
+
backdrop.style.display = "none";
|
|
328
|
+
try {
|
|
329
|
+
const annotations = await opts.onHighlight();
|
|
330
|
+
if (annotations && annotations.length > 0) {
|
|
331
|
+
annotationsData = [...annotationsData, ...annotations];
|
|
332
|
+
}
|
|
333
|
+
} catch {
|
|
334
|
+
}
|
|
335
|
+
backdrop.style.display = "flex";
|
|
202
336
|
});
|
|
203
|
-
|
|
204
|
-
const
|
|
205
|
-
const
|
|
206
|
-
const
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
337
|
+
backdrop.querySelector("#cf-submit")?.addEventListener("click", () => {
|
|
338
|
+
const desc = backdrop.querySelector("#cf-desc")?.value;
|
|
339
|
+
const name = backdrop.querySelector("#cf-name")?.value;
|
|
340
|
+
const email = backdrop.querySelector("#cf-email")?.value;
|
|
341
|
+
if (!desc?.trim()) {
|
|
342
|
+
const descEl = backdrop.querySelector("#cf-desc");
|
|
343
|
+
if (descEl) {
|
|
344
|
+
descEl.style.borderColor = "#e74c3c";
|
|
345
|
+
descEl.focus();
|
|
346
|
+
}
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
onSubmitCallback?.({
|
|
350
|
+
title: desc.trim().slice(0, 100),
|
|
351
|
+
description: desc.trim(),
|
|
352
|
+
type: "BUG",
|
|
353
|
+
priority: "MEDIUM",
|
|
354
|
+
screenshot: screenshotDataUrl || void 0,
|
|
355
|
+
annotations: annotationsData.length > 0 ? annotationsData : void 0,
|
|
356
|
+
reporter_name: name?.trim() || void 0,
|
|
357
|
+
reporter_email: email?.trim() || void 0
|
|
358
|
+
});
|
|
359
|
+
closeModal();
|
|
360
|
+
showToast("Rapport envoy\xE9 avec succ\xE8s !");
|
|
217
361
|
});
|
|
218
362
|
}
|
|
219
363
|
function unmountWidget() {
|
|
364
|
+
closeModal();
|
|
220
365
|
if (container) {
|
|
221
366
|
container.remove();
|
|
222
367
|
container = null;
|
|
@@ -225,10 +370,252 @@ function unmountWidget() {
|
|
|
225
370
|
function showToast(msg) {
|
|
226
371
|
const toast = document.createElement("div");
|
|
227
372
|
toast.textContent = msg;
|
|
228
|
-
toast.style.cssText =
|
|
373
|
+
toast.style.cssText = 'position:fixed;bottom:80px;right:20px;background:#10b981;color:#fff;padding:10px 20px;border-radius:10px;font-size:14px;z-index:100010;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;box-shadow:0 4px 16px rgba(0,0,0,.18);animation:cf-toast-in .3s ease;';
|
|
229
374
|
document.body.appendChild(toast);
|
|
375
|
+
setTimeout(() => {
|
|
376
|
+
toast.style.opacity = "0";
|
|
377
|
+
toast.style.transition = "opacity .3s";
|
|
378
|
+
}, 2500);
|
|
230
379
|
setTimeout(() => toast.remove(), 3e3);
|
|
231
380
|
}
|
|
381
|
+
function injectStyles(cfg) {
|
|
382
|
+
const style = document.createElement("style");
|
|
383
|
+
style.id = "cf-widget-styles";
|
|
384
|
+
style.textContent = `
|
|
385
|
+
@keyframes cf-toast-in { from { opacity:0; transform:translateY(10px); } to { opacity:1; transform:translateY(0); } }
|
|
386
|
+
@keyframes cf-modal-in { from { opacity:0; transform:scale(.96) translateY(8px); } to { opacity:1; transform:scale(1) translateY(0); } }
|
|
387
|
+
|
|
388
|
+
#cf-trigger {
|
|
389
|
+
position:fixed;
|
|
390
|
+
${getPositionCSS(cfg.position)}
|
|
391
|
+
z-index:100000;
|
|
392
|
+
background:${cfg.color};
|
|
393
|
+
color:#fff;
|
|
394
|
+
border:none;
|
|
395
|
+
border-radius:50px;
|
|
396
|
+
padding:11px 20px;
|
|
397
|
+
font-size:14px;
|
|
398
|
+
font-weight:500;
|
|
399
|
+
font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;
|
|
400
|
+
cursor:pointer;
|
|
401
|
+
box-shadow:0 4px 16px rgba(0,0,0,.18);
|
|
402
|
+
display:flex;
|
|
403
|
+
align-items:center;
|
|
404
|
+
gap:8px;
|
|
405
|
+
transition:transform .15s,box-shadow .15s;
|
|
406
|
+
}
|
|
407
|
+
#cf-trigger:hover { transform:scale(1.04); box-shadow:0 6px 24px rgba(0,0,0,.22); }
|
|
408
|
+
#cf-trigger svg { width:18px; height:18px; }
|
|
409
|
+
|
|
410
|
+
#cf-modal-backdrop {
|
|
411
|
+
position:fixed;
|
|
412
|
+
top:0;left:0;right:0;bottom:0;
|
|
413
|
+
background:rgba(0,0,0,.45);
|
|
414
|
+
z-index:100001;
|
|
415
|
+
display:flex;
|
|
416
|
+
align-items:center;
|
|
417
|
+
justify-content:center;
|
|
418
|
+
font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
.cf-modal {
|
|
422
|
+
background:#fff;
|
|
423
|
+
border-radius:16px;
|
|
424
|
+
box-shadow:0 24px 80px rgba(0,0,0,.25);
|
|
425
|
+
overflow:hidden;
|
|
426
|
+
animation:cf-modal-in .25s ease;
|
|
427
|
+
display:flex;
|
|
428
|
+
flex-direction:column;
|
|
429
|
+
max-height:90vh;
|
|
430
|
+
}
|
|
431
|
+
.cf-modal--compact { width:420px; }
|
|
432
|
+
.cf-modal--expanded { width:min(1100px,92vw); flex-direction:column; }
|
|
433
|
+
|
|
434
|
+
.cf-modal-header {
|
|
435
|
+
display:flex;
|
|
436
|
+
align-items:center;
|
|
437
|
+
justify-content:space-between;
|
|
438
|
+
padding:20px 24px 16px;
|
|
439
|
+
border-bottom:1px solid #f0f0f0;
|
|
440
|
+
}
|
|
441
|
+
.cf-modal-header h2 {
|
|
442
|
+
margin:0;
|
|
443
|
+
font-size:20px;
|
|
444
|
+
font-weight:700;
|
|
445
|
+
color:#1a1a2e;
|
|
446
|
+
}
|
|
447
|
+
.cf-modal-header .cf-logo {
|
|
448
|
+
width:28px;
|
|
449
|
+
height:28px;
|
|
450
|
+
color:${cfg.color};
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
.cf-modal-body { display:flex; flex:1; overflow:hidden; }
|
|
454
|
+
|
|
455
|
+
.cf-screenshot-panel {
|
|
456
|
+
flex:1;
|
|
457
|
+
min-width:0;
|
|
458
|
+
background:#1a1a2e;
|
|
459
|
+
display:flex;
|
|
460
|
+
flex-direction:column;
|
|
461
|
+
position:relative;
|
|
462
|
+
}
|
|
463
|
+
.cf-screenshot-panel img {
|
|
464
|
+
width:100%;
|
|
465
|
+
height:100%;
|
|
466
|
+
object-fit:contain;
|
|
467
|
+
max-height:60vh;
|
|
468
|
+
}
|
|
469
|
+
.cf-screenshot-tools {
|
|
470
|
+
position:absolute;
|
|
471
|
+
bottom:16px;
|
|
472
|
+
left:50%;
|
|
473
|
+
transform:translateX(-50%);
|
|
474
|
+
display:flex;
|
|
475
|
+
gap:8px;
|
|
476
|
+
}
|
|
477
|
+
.cf-screenshot-tools button {
|
|
478
|
+
padding:8px 18px;
|
|
479
|
+
border-radius:8px;
|
|
480
|
+
font-size:13px;
|
|
481
|
+
font-weight:500;
|
|
482
|
+
cursor:pointer;
|
|
483
|
+
font-family:inherit;
|
|
484
|
+
border:none;
|
|
485
|
+
transition:all .15s;
|
|
486
|
+
}
|
|
487
|
+
.cf-tool-highlight {
|
|
488
|
+
background:${cfg.color};
|
|
489
|
+
color:#fff;
|
|
490
|
+
}
|
|
491
|
+
.cf-tool-highlight:hover { opacity:.9; }
|
|
492
|
+
.cf-tool-mask {
|
|
493
|
+
background:#fff;
|
|
494
|
+
color:#1a1a2e;
|
|
495
|
+
border:1px solid #e0e0e0 !important;
|
|
496
|
+
}
|
|
497
|
+
.cf-tool-mask:hover { background:#f5f5f5; }
|
|
498
|
+
|
|
499
|
+
.cf-form-panel {
|
|
500
|
+
width:100%;
|
|
501
|
+
padding:20px 24px;
|
|
502
|
+
display:flex;
|
|
503
|
+
flex-direction:column;
|
|
504
|
+
gap:14px;
|
|
505
|
+
overflow-y:auto;
|
|
506
|
+
}
|
|
507
|
+
.cf-modal--expanded .cf-form-panel {
|
|
508
|
+
width:340px;
|
|
509
|
+
flex-shrink:0;
|
|
510
|
+
border-left:1px solid #f0f0f0;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
.cf-field label {
|
|
514
|
+
display:block;
|
|
515
|
+
font-size:13px;
|
|
516
|
+
font-weight:500;
|
|
517
|
+
color:#1a1a2e;
|
|
518
|
+
margin-bottom:6px;
|
|
519
|
+
}
|
|
520
|
+
.cf-field label span { font-weight:400; color:#999; }
|
|
521
|
+
.cf-field input, .cf-field textarea {
|
|
522
|
+
width:100%;
|
|
523
|
+
padding:10px 14px;
|
|
524
|
+
border:1px solid #e0e0e0;
|
|
525
|
+
border-radius:10px;
|
|
526
|
+
font-size:14px;
|
|
527
|
+
font-family:inherit;
|
|
528
|
+
box-sizing:border-box;
|
|
529
|
+
outline:none;
|
|
530
|
+
transition:border-color .15s,box-shadow .15s;
|
|
531
|
+
color:#1a1a2e;
|
|
532
|
+
background:#fff;
|
|
533
|
+
}
|
|
534
|
+
.cf-field input::placeholder, .cf-field textarea::placeholder { color:#bbb; }
|
|
535
|
+
.cf-field input:focus, .cf-field textarea:focus {
|
|
536
|
+
border-color:${cfg.color};
|
|
537
|
+
box-shadow:0 0 0 3px ${cfg.color}18;
|
|
538
|
+
}
|
|
539
|
+
.cf-field textarea { resize:vertical; min-height:100px; }
|
|
540
|
+
|
|
541
|
+
.cf-screenshot-action {
|
|
542
|
+
width:100%;
|
|
543
|
+
padding:12px;
|
|
544
|
+
border:1px solid #e0e0e0;
|
|
545
|
+
border-radius:10px;
|
|
546
|
+
background:#fff;
|
|
547
|
+
font-size:14px;
|
|
548
|
+
color:#1a1a2e;
|
|
549
|
+
cursor:pointer;
|
|
550
|
+
font-family:inherit;
|
|
551
|
+
transition:all .15s;
|
|
552
|
+
text-align:center;
|
|
553
|
+
}
|
|
554
|
+
.cf-screenshot-action:hover { background:#f8f8f8; border-color:#ccc; }
|
|
555
|
+
|
|
556
|
+
.cf-remove-screenshot {
|
|
557
|
+
width:100%;
|
|
558
|
+
padding:10px;
|
|
559
|
+
border:1px solid #e0e0e0;
|
|
560
|
+
border-radius:10px;
|
|
561
|
+
background:#fff;
|
|
562
|
+
font-size:13px;
|
|
563
|
+
color:#666;
|
|
564
|
+
cursor:pointer;
|
|
565
|
+
font-family:inherit;
|
|
566
|
+
transition:all .15s;
|
|
567
|
+
text-align:center;
|
|
568
|
+
}
|
|
569
|
+
.cf-remove-screenshot:hover { background:#fff0f0; color:#e74c3c; border-color:#e74c3c; }
|
|
570
|
+
|
|
571
|
+
.cf-submit-btn {
|
|
572
|
+
width:100%;
|
|
573
|
+
padding:13px;
|
|
574
|
+
border:none;
|
|
575
|
+
border-radius:10px;
|
|
576
|
+
background:${cfg.color};
|
|
577
|
+
color:#fff;
|
|
578
|
+
font-size:15px;
|
|
579
|
+
font-weight:600;
|
|
580
|
+
cursor:pointer;
|
|
581
|
+
font-family:inherit;
|
|
582
|
+
transition:all .15s;
|
|
583
|
+
}
|
|
584
|
+
.cf-submit-btn:hover { opacity:.92; transform:translateY(-1px); box-shadow:0 4px 12px ${cfg.color}40; }
|
|
585
|
+
.cf-submit-btn:disabled { opacity:.5; cursor:not-allowed; transform:none; }
|
|
586
|
+
|
|
587
|
+
.cf-cancel-btn {
|
|
588
|
+
width:100%;
|
|
589
|
+
padding:11px;
|
|
590
|
+
border:1px solid #e0e0e0;
|
|
591
|
+
border-radius:10px;
|
|
592
|
+
background:#fff;
|
|
593
|
+
color:#1a1a2e;
|
|
594
|
+
font-size:14px;
|
|
595
|
+
font-weight:500;
|
|
596
|
+
cursor:pointer;
|
|
597
|
+
font-family:inherit;
|
|
598
|
+
transition:all .15s;
|
|
599
|
+
text-align:center;
|
|
600
|
+
}
|
|
601
|
+
.cf-cancel-btn:hover { background:#f8f8f8; }
|
|
602
|
+
|
|
603
|
+
.cf-footer {
|
|
604
|
+
text-align:center;
|
|
605
|
+
padding:12px;
|
|
606
|
+
border-top:1px solid #f0f0f0;
|
|
607
|
+
font-size:11px;
|
|
608
|
+
color:#bbb;
|
|
609
|
+
}
|
|
610
|
+
.cf-footer a {
|
|
611
|
+
color:#999;
|
|
612
|
+
text-decoration:none;
|
|
613
|
+
font-weight:500;
|
|
614
|
+
}
|
|
615
|
+
.cf-footer a:hover { color:${cfg.color}; }
|
|
616
|
+
`;
|
|
617
|
+
document.head.appendChild(style);
|
|
618
|
+
}
|
|
232
619
|
function getPositionCSS(pos) {
|
|
233
620
|
switch (pos) {
|
|
234
621
|
case "bottom-left":
|
|
@@ -241,133 +628,118 @@ function getPositionCSS(pos) {
|
|
|
241
628
|
return "bottom:20px;right:20px;";
|
|
242
629
|
}
|
|
243
630
|
}
|
|
244
|
-
function
|
|
245
|
-
switch (pos) {
|
|
246
|
-
case "bottom-left":
|
|
247
|
-
return "bottom:70px;left:20px;";
|
|
248
|
-
case "top-right":
|
|
249
|
-
return "top:70px;right:20px;";
|
|
250
|
-
case "top-left":
|
|
251
|
-
return "top:70px;left:20px;";
|
|
252
|
-
default:
|
|
253
|
-
return "bottom:70px;right:20px;";
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
function getWidgetHTML(cfg) {
|
|
257
|
-
const posBtn = getPositionCSS(cfg.position);
|
|
258
|
-
const posForm = getFormPositionCSS(cfg.position);
|
|
631
|
+
function getTriggerHTML(cfg) {
|
|
259
632
|
return `
|
|
260
|
-
<
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
#cf-trigger svg{width:16px;height:16px}
|
|
264
|
-
#cf-form{position:fixed;${posForm}z-index:100000;background:#fff;border-radius:12px;box-shadow:0 8px 30px rgba(0,0,0,.12);width:360px;padding:16px;font-family:system-ui,-apple-system,sans-serif;display:none;flex-direction:column;gap:10px}
|
|
265
|
-
#cf-form h3{margin:0;font-size:15px;font-weight:600;color:#111}
|
|
266
|
-
#cf-form input,#cf-form textarea,#cf-form select{width:100%;padding:8px 10px;border:1px solid #e2e8f0;border-radius:8px;font-size:13px;font-family:inherit;box-sizing:border-box;outline:none;transition:border .15s}
|
|
267
|
-
#cf-form input:focus,#cf-form textarea:focus,#cf-form select:focus{border-color:${cfg.color}}
|
|
268
|
-
#cf-form textarea{resize:vertical;min-height:60px}
|
|
269
|
-
.cf-row{display:flex;gap:8px}
|
|
270
|
-
.cf-row select{flex:1}
|
|
271
|
-
.cf-tools{display:flex;gap:6px}
|
|
272
|
-
.cf-tool-btn{display:flex;align-items:center;gap:4px;padding:6px 10px;border:1px solid #e2e8f0;border-radius:6px;background:#f8fafc;color:#475569;font-size:11px;cursor:pointer;font-family:inherit;transition:all .15s}
|
|
273
|
-
.cf-tool-btn:hover{background:#f1f5f9;border-color:#cbd5e1}
|
|
274
|
-
.cf-tool-btn svg{width:14px;height:14px}
|
|
275
|
-
#cf-submit{background:${cfg.color};color:#fff;border:none;border-radius:8px;padding:9px;font-size:13px;font-weight:500;cursor:pointer;transition:opacity .15s}
|
|
276
|
-
#cf-submit:hover{opacity:.9}
|
|
277
|
-
#cf-close{position:absolute;top:10px;right:12px;background:none;border:none;cursor:pointer;font-size:18px;color:#94a3b8;line-height:1}
|
|
278
|
-
#cf-screenshot-preview{display:none;border:1px solid #e2e8f0;border-radius:8px;overflow:hidden;max-height:120px}
|
|
279
|
-
#cf-screenshot-preview img{width:100%;height:auto;display:block}
|
|
280
|
-
</style>
|
|
281
|
-
<button id="cf-trigger">
|
|
282
|
-
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
|
|
283
|
-
${cfg.text}
|
|
284
|
-
</button>
|
|
285
|
-
<div id="cf-form">
|
|
286
|
-
<button id="cf-close">×</button>
|
|
287
|
-
<h3>Report Feedback</h3>
|
|
288
|
-
<input id="cf-title" type="text" placeholder="Title *" />
|
|
289
|
-
<textarea id="cf-desc" placeholder="Description (optional)"></textarea>
|
|
290
|
-
<div class="cf-row">
|
|
291
|
-
<select id="cf-type">
|
|
292
|
-
<option value="BUG">Bug</option>
|
|
293
|
-
<option value="FEATURE">Feature</option>
|
|
294
|
-
<option value="IMPROVEMENT">Improvement</option>
|
|
295
|
-
<option value="QUESTION">Question</option>
|
|
296
|
-
</select>
|
|
297
|
-
<select id="cf-priority">
|
|
298
|
-
<option value="LOW">Low</option>
|
|
299
|
-
<option value="MEDIUM" selected>Medium</option>
|
|
300
|
-
<option value="HIGH">High</option>
|
|
301
|
-
<option value="CRITICAL">Critical</option>
|
|
302
|
-
</select>
|
|
303
|
-
</div>
|
|
304
|
-
<div class="cf-tools">
|
|
305
|
-
<button type="button" class="cf-tool-btn" id="cf-screenshot">
|
|
306
|
-
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><path d="m21 15-5-5L5 21"/></svg>
|
|
307
|
-
Screenshot
|
|
308
|
-
</button>
|
|
309
|
-
<button type="button" class="cf-tool-btn" id="cf-highlight">
|
|
310
|
-
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 20h9"/><path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"/></svg>
|
|
311
|
-
Highlight
|
|
633
|
+
<button id="cf-trigger">
|
|
634
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
|
|
635
|
+
${cfg.text}
|
|
312
636
|
</button>
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
637
|
+
`;
|
|
638
|
+
}
|
|
639
|
+
function getCheckflowLogo() {
|
|
640
|
+
return `<svg class="cf-logo" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M2 12C2 6.5 6.5 2 12 2a10 10 0 0 1 8 4"/><path d="M5 19.5C5.5 18 6 15 6 12c0-.7.12-1.37.34-2"/><path d="M17.29 21.02c.12-.6.43-2.3.5-3.02"/><path d="M12 10a2 2 0 0 0-2 2c0 1.02-.1 2.51-.26 4"/><path d="M8.65 22c.21-.66.45-1.32.57-2"/><path d="M14 13.12c0 2.38 0 6.38-1 8.88"/><path d="M2 16h.01"/><path d="M21.8 16c.2-2 .131-5.354 0-6"/><path d="M9 6.8a6 6 0 0 1 9 5.2v2"/></svg>`;
|
|
641
|
+
}
|
|
642
|
+
function getModalHTML(cfg, expanded) {
|
|
643
|
+
const modalClass = expanded ? "cf-modal cf-modal--expanded" : "cf-modal cf-modal--compact";
|
|
644
|
+
const formFields = `
|
|
645
|
+
<div class="cf-field">
|
|
646
|
+
<label>Nom <span>(obligatoire)</span></label>
|
|
647
|
+
<input id="cf-name" type="text" placeholder="Votre nom" />
|
|
648
|
+
</div>
|
|
649
|
+
<div class="cf-field">
|
|
650
|
+
<label>Email <span>(obligatoire)</span></label>
|
|
651
|
+
<input id="cf-email" type="email" placeholder="votre.email@exemple.com" />
|
|
652
|
+
</div>
|
|
653
|
+
<div class="cf-field">
|
|
654
|
+
<label>Description <span>(obligatoire)</span></label>
|
|
655
|
+
<textarea id="cf-desc" placeholder="Quel est le probl\xE8me ? Que vous attendiez-vous \xE0 voir ?"></textarea>
|
|
656
|
+
</div>
|
|
657
|
+
`;
|
|
658
|
+
if (expanded && screenshotDataUrl) {
|
|
659
|
+
return `
|
|
660
|
+
<div class="${modalClass}">
|
|
661
|
+
<div class="cf-modal-header">
|
|
662
|
+
<h2>Envoyer le rapport</h2>
|
|
663
|
+
${getCheckflowLogo()}
|
|
664
|
+
</div>
|
|
665
|
+
<div class="cf-modal-body">
|
|
666
|
+
<div class="cf-screenshot-panel">
|
|
667
|
+
<img src="${screenshotDataUrl}" alt="Capture d'\xE9cran" />
|
|
668
|
+
<div class="cf-screenshot-tools">
|
|
669
|
+
<button class="cf-tool-highlight" id="cf-highlight-btn">Surligner</button>
|
|
670
|
+
<button class="cf-tool-mask" id="cf-mask-btn">Masquer</button>
|
|
671
|
+
</div>
|
|
672
|
+
</div>
|
|
673
|
+
<div class="cf-form-panel">
|
|
674
|
+
${formFields}
|
|
675
|
+
<button class="cf-remove-screenshot" id="cf-remove-screenshot">Supprimer la capture d'\xE9cran</button>
|
|
676
|
+
<button class="cf-submit-btn" id="cf-submit">Envoyer le rapport</button>
|
|
677
|
+
<button class="cf-cancel-btn" id="cf-cancel">Annuler</button>
|
|
678
|
+
</div>
|
|
679
|
+
</div>
|
|
680
|
+
<div class="cf-footer">Powered by <a href="https://checkflow.space" target="_blank" rel="noopener">Checkflow</a></div>
|
|
681
|
+
</div>
|
|
682
|
+
`;
|
|
683
|
+
}
|
|
684
|
+
return `
|
|
685
|
+
<div class="${modalClass}">
|
|
686
|
+
<div class="cf-modal-header">
|
|
687
|
+
<h2>Envoyer le rapport</h2>
|
|
688
|
+
${getCheckflowLogo()}
|
|
689
|
+
</div>
|
|
690
|
+
<div class="cf-form-panel">
|
|
691
|
+
${formFields}
|
|
692
|
+
<button class="cf-screenshot-action" id="cf-screenshot-btn">Ajouter une capture d'\xE9cran</button>
|
|
693
|
+
<button class="cf-submit-btn" id="cf-submit">Envoyer le rapport</button>
|
|
694
|
+
<button class="cf-cancel-btn" id="cf-cancel">Annuler</button>
|
|
695
|
+
</div>
|
|
696
|
+
<div class="cf-footer">Powered by <a href="https://checkflow.space" target="_blank" rel="noopener">Checkflow</a></div>
|
|
697
|
+
</div>
|
|
698
|
+
`;
|
|
317
699
|
}
|
|
318
700
|
|
|
319
701
|
// src/screenshot.ts
|
|
320
702
|
var screenshotData = null;
|
|
321
703
|
async function captureScreenshot() {
|
|
322
704
|
try {
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
705
|
+
const stream = await navigator.mediaDevices.getDisplayMedia({
|
|
706
|
+
video: { displaySurface: "browser" },
|
|
707
|
+
preferCurrentTab: true
|
|
708
|
+
});
|
|
709
|
+
const track = stream.getVideoTracks()[0];
|
|
710
|
+
const imageCapture = new window.ImageCapture(track);
|
|
711
|
+
await new Promise((r) => setTimeout(r, 300));
|
|
712
|
+
let bitmap;
|
|
713
|
+
try {
|
|
714
|
+
bitmap = await imageCapture.grabFrame();
|
|
715
|
+
} catch {
|
|
716
|
+
const video = document.createElement("video");
|
|
717
|
+
video.srcObject = stream;
|
|
718
|
+
video.autoplay = true;
|
|
719
|
+
await new Promise((resolve) => {
|
|
720
|
+
video.onloadedmetadata = () => {
|
|
721
|
+
video.play();
|
|
722
|
+
resolve();
|
|
723
|
+
};
|
|
333
724
|
});
|
|
334
|
-
|
|
725
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
726
|
+
const canvas2 = document.createElement("canvas");
|
|
727
|
+
canvas2.width = video.videoWidth;
|
|
728
|
+
canvas2.height = video.videoHeight;
|
|
729
|
+
const ctx2 = canvas2.getContext("2d");
|
|
730
|
+
ctx2.drawImage(video, 0, 0);
|
|
731
|
+
track.stop();
|
|
732
|
+
screenshotData = canvas2.toDataURL("image/png", 0.92);
|
|
335
733
|
return screenshotData;
|
|
336
734
|
}
|
|
337
735
|
const canvas = document.createElement("canvas");
|
|
736
|
+
canvas.width = bitmap.width;
|
|
737
|
+
canvas.height = bitmap.height;
|
|
338
738
|
const ctx = canvas.getContext("2d");
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
344
|
-
ctx.fillStyle = "#333333";
|
|
345
|
-
ctx.font = "14px system-ui";
|
|
346
|
-
ctx.fillText(`Page: ${window.location.href}`, 10, 30);
|
|
347
|
-
ctx.fillText(`Viewport: ${window.innerWidth}x${window.innerHeight}`, 10, 50);
|
|
348
|
-
ctx.fillText(`Screenshot captured at ${(/* @__PURE__ */ new Date()).toISOString()}`, 10, 70);
|
|
349
|
-
const svgData = `
|
|
350
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="${window.innerWidth}" height="${window.innerHeight}">
|
|
351
|
-
<foreignObject width="100%" height="100%">
|
|
352
|
-
<div xmlns="http://www.w3.org/1999/xhtml">
|
|
353
|
-
${document.documentElement.outerHTML}
|
|
354
|
-
</div>
|
|
355
|
-
</foreignObject>
|
|
356
|
-
</svg>`;
|
|
357
|
-
try {
|
|
358
|
-
const blob = new Blob([svgData], { type: "image/svg+xml;charset=utf-8" });
|
|
359
|
-
const url = URL.createObjectURL(blob);
|
|
360
|
-
const img = new Image();
|
|
361
|
-
await new Promise((resolve, reject) => {
|
|
362
|
-
img.onload = () => resolve();
|
|
363
|
-
img.onerror = () => reject();
|
|
364
|
-
img.src = url;
|
|
365
|
-
});
|
|
366
|
-
ctx.drawImage(img, 0, 0);
|
|
367
|
-
URL.revokeObjectURL(url);
|
|
368
|
-
} catch {
|
|
369
|
-
}
|
|
370
|
-
screenshotData = canvas.toDataURL("image/png", 0.8);
|
|
739
|
+
ctx.drawImage(bitmap, 0, 0);
|
|
740
|
+
bitmap.close();
|
|
741
|
+
track.stop();
|
|
742
|
+
screenshotData = canvas.toDataURL("image/png", 0.92);
|
|
371
743
|
return screenshotData;
|
|
372
744
|
} catch {
|
|
373
745
|
return null;
|
|
@@ -376,19 +748,6 @@ async function captureScreenshot() {
|
|
|
376
748
|
function clearScreenshot() {
|
|
377
749
|
screenshotData = null;
|
|
378
750
|
}
|
|
379
|
-
function loadHtml2Canvas() {
|
|
380
|
-
return new Promise((resolve, reject) => {
|
|
381
|
-
if (typeof window.html2canvas === "function") {
|
|
382
|
-
resolve();
|
|
383
|
-
return;
|
|
384
|
-
}
|
|
385
|
-
const script = document.createElement("script");
|
|
386
|
-
script.src = "https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js";
|
|
387
|
-
script.onload = () => resolve();
|
|
388
|
-
script.onerror = () => reject(new Error("Failed to load html2canvas"));
|
|
389
|
-
document.head.appendChild(script);
|
|
390
|
-
});
|
|
391
|
-
}
|
|
392
751
|
|
|
393
752
|
// src/highlighter.ts
|
|
394
753
|
var overlay = null;
|
|
@@ -527,10 +886,7 @@ function init(cfg) {
|
|
|
527
886
|
};
|
|
528
887
|
if (!config.enabled) return;
|
|
529
888
|
installInterceptors();
|
|
530
|
-
|
|
531
|
-
loadHtml2Canvas().catch(() => {
|
|
532
|
-
});
|
|
533
|
-
}
|
|
889
|
+
installNetworkInterceptor();
|
|
534
890
|
if (typeof window !== "undefined" && config.widget?.showOnInit !== false) {
|
|
535
891
|
mountWidget(
|
|
536
892
|
config.widget,
|
|
@@ -541,7 +897,9 @@ function init(cfg) {
|
|
|
541
897
|
type: data.type,
|
|
542
898
|
priority: data.priority,
|
|
543
899
|
screenshot_data: data.screenshot,
|
|
544
|
-
annotations: data.annotations
|
|
900
|
+
annotations: data.annotations,
|
|
901
|
+
reporter_name: data.reporter_name,
|
|
902
|
+
reporter_email: data.reporter_email
|
|
545
903
|
});
|
|
546
904
|
},
|
|
547
905
|
{
|
|
@@ -559,6 +917,7 @@ async function sendFeedback(data) {
|
|
|
559
917
|
const perf = typeof window !== "undefined" ? collectPerformance() : void 0;
|
|
560
918
|
const consoleLogs = getConsoleLogs();
|
|
561
919
|
const jsErrors = getJavascriptErrors();
|
|
920
|
+
const networkLogs = getNetworkLogs();
|
|
562
921
|
const payload = {
|
|
563
922
|
...data,
|
|
564
923
|
...context,
|
|
@@ -566,6 +925,7 @@ async function sendFeedback(data) {
|
|
|
566
925
|
performance_metrics: perf,
|
|
567
926
|
console_logs: consoleLogs.length > 0 ? consoleLogs : void 0,
|
|
568
927
|
javascript_errors: jsErrors.length > 0 ? jsErrors : void 0,
|
|
928
|
+
network_logs: networkLogs.length > 0 ? networkLogs : void 0,
|
|
569
929
|
sdk_version: SDK_VERSION
|
|
570
930
|
};
|
|
571
931
|
if (data.screenshot_data) {
|
|
@@ -574,6 +934,12 @@ async function sendFeedback(data) {
|
|
|
574
934
|
if (data.annotations && data.annotations.length > 0) {
|
|
575
935
|
payload.annotations = data.annotations;
|
|
576
936
|
}
|
|
937
|
+
if (data.reporter_name) {
|
|
938
|
+
payload.reporter_name = data.reporter_name;
|
|
939
|
+
}
|
|
940
|
+
if (data.reporter_email) {
|
|
941
|
+
payload.reporter_email = data.reporter_email;
|
|
942
|
+
}
|
|
577
943
|
try {
|
|
578
944
|
const res = await fetch(`${config.endpoint}/sdk/feedback`, {
|
|
579
945
|
method: "POST",
|
|
@@ -603,7 +969,9 @@ function showWidget() {
|
|
|
603
969
|
type: data.type,
|
|
604
970
|
priority: data.priority,
|
|
605
971
|
screenshot_data: data.screenshot,
|
|
606
|
-
annotations: data.annotations
|
|
972
|
+
annotations: data.annotations,
|
|
973
|
+
reporter_name: data.reporter_name,
|
|
974
|
+
reporter_email: data.reporter_email
|
|
607
975
|
});
|
|
608
976
|
},
|
|
609
977
|
{
|
|
@@ -618,6 +986,7 @@ function hideWidget() {
|
|
|
618
986
|
function destroy() {
|
|
619
987
|
unmountWidget();
|
|
620
988
|
clearLogs();
|
|
989
|
+
clearNetworkLogs();
|
|
621
990
|
clearScreenshot();
|
|
622
991
|
config = null;
|
|
623
992
|
}
|