@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.mjs
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
captureScreenshot,
|
|
3
|
-
clearScreenshot
|
|
4
|
-
|
|
5
|
-
} from "./chunk-CQ56DMFR.mjs";
|
|
3
|
+
clearScreenshot
|
|
4
|
+
} from "./chunk-6EVTRC5X.mjs";
|
|
6
5
|
import {
|
|
7
6
|
startHighlighting
|
|
8
7
|
} from "./chunk-CD33QAA6.mjs";
|
|
@@ -111,6 +110,75 @@ function clearLogs() {
|
|
|
111
110
|
errorsCapture = [];
|
|
112
111
|
}
|
|
113
112
|
|
|
113
|
+
// src/network-interceptor.ts
|
|
114
|
+
var MAX_LOGS2 = 50;
|
|
115
|
+
var logs2 = [];
|
|
116
|
+
var installed2 = false;
|
|
117
|
+
function installNetworkInterceptor() {
|
|
118
|
+
if (installed2 || typeof window === "undefined") return;
|
|
119
|
+
installed2 = true;
|
|
120
|
+
const origFetch = window.fetch;
|
|
121
|
+
window.fetch = async function(...args) {
|
|
122
|
+
const start = Date.now();
|
|
123
|
+
const req = new Request(...args);
|
|
124
|
+
const entry = {
|
|
125
|
+
method: req.method,
|
|
126
|
+
url: req.url,
|
|
127
|
+
type: "fetch",
|
|
128
|
+
timestamp: start
|
|
129
|
+
};
|
|
130
|
+
try {
|
|
131
|
+
const res = await origFetch.apply(this, args);
|
|
132
|
+
entry.status = res.status;
|
|
133
|
+
entry.duration = Date.now() - start;
|
|
134
|
+
entry.response_type = res.headers.get("content-type") || void 0;
|
|
135
|
+
pushLog(entry);
|
|
136
|
+
return res;
|
|
137
|
+
} catch (err) {
|
|
138
|
+
entry.duration = Date.now() - start;
|
|
139
|
+
entry.error = err.message || "Network error";
|
|
140
|
+
pushLog(entry);
|
|
141
|
+
throw err;
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
const origOpen = XMLHttpRequest.prototype.open;
|
|
145
|
+
const origSend = XMLHttpRequest.prototype.send;
|
|
146
|
+
XMLHttpRequest.prototype.open = function(method, url, ...rest) {
|
|
147
|
+
this.__cf_method = method;
|
|
148
|
+
this.__cf_url = String(url);
|
|
149
|
+
return origOpen.apply(this, [method, url, ...rest]);
|
|
150
|
+
};
|
|
151
|
+
XMLHttpRequest.prototype.send = function(...args) {
|
|
152
|
+
const start = Date.now();
|
|
153
|
+
const xhr = this;
|
|
154
|
+
xhr.addEventListener("loadend", () => {
|
|
155
|
+
const entry = {
|
|
156
|
+
method: xhr.__cf_method || "GET",
|
|
157
|
+
url: xhr.__cf_url || "",
|
|
158
|
+
status: xhr.status,
|
|
159
|
+
duration: Date.now() - start,
|
|
160
|
+
type: "xhr",
|
|
161
|
+
timestamp: start,
|
|
162
|
+
response_type: xhr.getResponseHeader("content-type") || void 0
|
|
163
|
+
};
|
|
164
|
+
if (xhr.status === 0) entry.error = "Request failed";
|
|
165
|
+
pushLog(entry);
|
|
166
|
+
});
|
|
167
|
+
return origSend.apply(this, args);
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
function pushLog(entry) {
|
|
171
|
+
if (entry.url.includes("/sdk/feedback")) return;
|
|
172
|
+
logs2.push(entry);
|
|
173
|
+
if (logs2.length > MAX_LOGS2) logs2.shift();
|
|
174
|
+
}
|
|
175
|
+
function getNetworkLogs() {
|
|
176
|
+
return [...logs2];
|
|
177
|
+
}
|
|
178
|
+
function clearNetworkLogs() {
|
|
179
|
+
logs2 = [];
|
|
180
|
+
}
|
|
181
|
+
|
|
114
182
|
// src/widget.ts
|
|
115
183
|
var DEFAULT_CONFIG = {
|
|
116
184
|
position: "bottom-right",
|
|
@@ -122,78 +190,154 @@ var container = null;
|
|
|
122
190
|
var onSubmitCallback = null;
|
|
123
191
|
var screenshotDataUrl = null;
|
|
124
192
|
var annotationsData = [];
|
|
193
|
+
var isExpanded = false;
|
|
125
194
|
function mountWidget(config2 = {}, onSubmit, opts) {
|
|
126
195
|
if (container) return;
|
|
127
196
|
onSubmitCallback = onSubmit;
|
|
128
197
|
const cfg = { ...DEFAULT_CONFIG, ...config2 };
|
|
129
198
|
container = document.createElement("div");
|
|
130
199
|
container.id = "checkflow-widget";
|
|
131
|
-
|
|
200
|
+
injectStyles(cfg);
|
|
201
|
+
container.innerHTML = getTriggerHTML(cfg);
|
|
132
202
|
document.body.appendChild(container);
|
|
133
203
|
const btn = container.querySelector("#cf-trigger");
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
204
|
+
btn?.addEventListener("click", () => openModal(cfg, opts));
|
|
205
|
+
}
|
|
206
|
+
function openModal(cfg, opts) {
|
|
207
|
+
if (!container) return;
|
|
208
|
+
const existing = document.getElementById("cf-modal-backdrop");
|
|
209
|
+
if (existing) existing.remove();
|
|
210
|
+
isExpanded = false;
|
|
211
|
+
screenshotDataUrl = null;
|
|
212
|
+
annotationsData = [];
|
|
213
|
+
const backdrop = document.createElement("div");
|
|
214
|
+
backdrop.id = "cf-modal-backdrop";
|
|
215
|
+
backdrop.innerHTML = getModalHTML(cfg, false);
|
|
216
|
+
document.body.appendChild(backdrop);
|
|
217
|
+
backdrop.addEventListener("click", (e) => {
|
|
218
|
+
if (e.target === backdrop) closeModal();
|
|
148
219
|
});
|
|
149
|
-
|
|
220
|
+
bindModalEvents(cfg, opts);
|
|
221
|
+
}
|
|
222
|
+
function closeModal() {
|
|
223
|
+
const backdrop = document.getElementById("cf-modal-backdrop");
|
|
224
|
+
if (backdrop) backdrop.remove();
|
|
225
|
+
isExpanded = false;
|
|
226
|
+
screenshotDataUrl = null;
|
|
227
|
+
annotationsData = [];
|
|
228
|
+
}
|
|
229
|
+
function expandModal(cfg, opts) {
|
|
230
|
+
const backdrop = document.getElementById("cf-modal-backdrop");
|
|
231
|
+
if (!backdrop) return;
|
|
232
|
+
const name = backdrop.querySelector("#cf-name")?.value || "";
|
|
233
|
+
const email = backdrop.querySelector("#cf-email")?.value || "";
|
|
234
|
+
const desc = backdrop.querySelector("#cf-desc")?.value || "";
|
|
235
|
+
isExpanded = true;
|
|
236
|
+
backdrop.innerHTML = getModalHTML(cfg, true);
|
|
237
|
+
bindModalEvents(cfg, opts);
|
|
238
|
+
const nameEl = backdrop.querySelector("#cf-name");
|
|
239
|
+
const emailEl = backdrop.querySelector("#cf-email");
|
|
240
|
+
const descEl = backdrop.querySelector("#cf-desc");
|
|
241
|
+
if (nameEl) nameEl.value = name;
|
|
242
|
+
if (emailEl) emailEl.value = email;
|
|
243
|
+
if (descEl) descEl.value = desc;
|
|
244
|
+
}
|
|
245
|
+
function collapseModal(cfg, opts) {
|
|
246
|
+
const backdrop = document.getElementById("cf-modal-backdrop");
|
|
247
|
+
if (!backdrop) return;
|
|
248
|
+
const name = backdrop.querySelector("#cf-name")?.value || "";
|
|
249
|
+
const email = backdrop.querySelector("#cf-email")?.value || "";
|
|
250
|
+
const desc = backdrop.querySelector("#cf-desc")?.value || "";
|
|
251
|
+
isExpanded = false;
|
|
252
|
+
screenshotDataUrl = null;
|
|
253
|
+
annotationsData = [];
|
|
254
|
+
backdrop.innerHTML = getModalHTML(cfg, false);
|
|
255
|
+
bindModalEvents(cfg, opts);
|
|
256
|
+
const nameEl = backdrop.querySelector("#cf-name");
|
|
257
|
+
const emailEl = backdrop.querySelector("#cf-email");
|
|
258
|
+
const descEl = backdrop.querySelector("#cf-desc");
|
|
259
|
+
if (nameEl) nameEl.value = name;
|
|
260
|
+
if (emailEl) emailEl.value = email;
|
|
261
|
+
if (descEl) descEl.value = desc;
|
|
262
|
+
}
|
|
263
|
+
function bindModalEvents(cfg, opts) {
|
|
264
|
+
const backdrop = document.getElementById("cf-modal-backdrop");
|
|
265
|
+
if (!backdrop) return;
|
|
266
|
+
backdrop.querySelector("#cf-cancel")?.addEventListener("click", closeModal);
|
|
267
|
+
backdrop.querySelector("#cf-screenshot-btn")?.addEventListener("click", async () => {
|
|
150
268
|
if (!opts?.onScreenshot) return;
|
|
151
|
-
|
|
152
|
-
form.style.display = "none";
|
|
269
|
+
backdrop.style.display = "none";
|
|
153
270
|
try {
|
|
154
271
|
const data = await opts.onScreenshot();
|
|
155
272
|
if (data) {
|
|
156
273
|
screenshotDataUrl = data;
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
}
|
|
274
|
+
backdrop.style.display = "flex";
|
|
275
|
+
expandModal(cfg, opts);
|
|
276
|
+
return;
|
|
161
277
|
}
|
|
162
278
|
} catch {
|
|
163
279
|
}
|
|
164
|
-
|
|
165
|
-
|
|
280
|
+
backdrop.style.display = "flex";
|
|
281
|
+
});
|
|
282
|
+
backdrop.querySelector("#cf-remove-screenshot")?.addEventListener("click", () => {
|
|
283
|
+
collapseModal(cfg, opts);
|
|
166
284
|
});
|
|
167
|
-
|
|
285
|
+
backdrop.querySelector("#cf-highlight-btn")?.addEventListener("click", async () => {
|
|
168
286
|
if (!opts?.onHighlight) return;
|
|
169
|
-
|
|
287
|
+
backdrop.style.display = "none";
|
|
170
288
|
try {
|
|
171
289
|
const annotations = await opts.onHighlight();
|
|
172
290
|
if (annotations && annotations.length > 0) {
|
|
173
291
|
annotationsData = annotations;
|
|
174
|
-
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`;
|
|
175
292
|
}
|
|
176
293
|
} catch {
|
|
177
294
|
}
|
|
178
|
-
|
|
295
|
+
backdrop.style.display = "flex";
|
|
296
|
+
const hlBtn = backdrop.querySelector("#cf-highlight-btn");
|
|
297
|
+
if (hlBtn && annotationsData.length > 0) {
|
|
298
|
+
hlBtn.textContent = `Surligner (${annotationsData.length})`;
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
backdrop.querySelector("#cf-mask-btn")?.addEventListener("click", async () => {
|
|
302
|
+
if (!opts?.onHighlight) return;
|
|
303
|
+
backdrop.style.display = "none";
|
|
304
|
+
try {
|
|
305
|
+
const annotations = await opts.onHighlight();
|
|
306
|
+
if (annotations && annotations.length > 0) {
|
|
307
|
+
annotationsData = [...annotationsData, ...annotations];
|
|
308
|
+
}
|
|
309
|
+
} catch {
|
|
310
|
+
}
|
|
311
|
+
backdrop.style.display = "flex";
|
|
179
312
|
});
|
|
180
|
-
|
|
181
|
-
const
|
|
182
|
-
const
|
|
183
|
-
const
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
313
|
+
backdrop.querySelector("#cf-submit")?.addEventListener("click", () => {
|
|
314
|
+
const desc = backdrop.querySelector("#cf-desc")?.value;
|
|
315
|
+
const name = backdrop.querySelector("#cf-name")?.value;
|
|
316
|
+
const email = backdrop.querySelector("#cf-email")?.value;
|
|
317
|
+
if (!desc?.trim()) {
|
|
318
|
+
const descEl = backdrop.querySelector("#cf-desc");
|
|
319
|
+
if (descEl) {
|
|
320
|
+
descEl.style.borderColor = "#e74c3c";
|
|
321
|
+
descEl.focus();
|
|
322
|
+
}
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
onSubmitCallback?.({
|
|
326
|
+
title: desc.trim().slice(0, 100),
|
|
327
|
+
description: desc.trim(),
|
|
328
|
+
type: "BUG",
|
|
329
|
+
priority: "MEDIUM",
|
|
330
|
+
screenshot: screenshotDataUrl || void 0,
|
|
331
|
+
annotations: annotationsData.length > 0 ? annotationsData : void 0,
|
|
332
|
+
reporter_name: name?.trim() || void 0,
|
|
333
|
+
reporter_email: email?.trim() || void 0
|
|
334
|
+
});
|
|
335
|
+
closeModal();
|
|
336
|
+
showToast("Rapport envoy\xE9 avec succ\xE8s !");
|
|
194
337
|
});
|
|
195
338
|
}
|
|
196
339
|
function unmountWidget() {
|
|
340
|
+
closeModal();
|
|
197
341
|
if (container) {
|
|
198
342
|
container.remove();
|
|
199
343
|
container = null;
|
|
@@ -202,10 +346,252 @@ function unmountWidget() {
|
|
|
202
346
|
function showToast(msg) {
|
|
203
347
|
const toast = document.createElement("div");
|
|
204
348
|
toast.textContent = msg;
|
|
205
|
-
toast.style.cssText =
|
|
349
|
+
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;';
|
|
206
350
|
document.body.appendChild(toast);
|
|
351
|
+
setTimeout(() => {
|
|
352
|
+
toast.style.opacity = "0";
|
|
353
|
+
toast.style.transition = "opacity .3s";
|
|
354
|
+
}, 2500);
|
|
207
355
|
setTimeout(() => toast.remove(), 3e3);
|
|
208
356
|
}
|
|
357
|
+
function injectStyles(cfg) {
|
|
358
|
+
const style = document.createElement("style");
|
|
359
|
+
style.id = "cf-widget-styles";
|
|
360
|
+
style.textContent = `
|
|
361
|
+
@keyframes cf-toast-in { from { opacity:0; transform:translateY(10px); } to { opacity:1; transform:translateY(0); } }
|
|
362
|
+
@keyframes cf-modal-in { from { opacity:0; transform:scale(.96) translateY(8px); } to { opacity:1; transform:scale(1) translateY(0); } }
|
|
363
|
+
|
|
364
|
+
#cf-trigger {
|
|
365
|
+
position:fixed;
|
|
366
|
+
${getPositionCSS(cfg.position)}
|
|
367
|
+
z-index:100000;
|
|
368
|
+
background:${cfg.color};
|
|
369
|
+
color:#fff;
|
|
370
|
+
border:none;
|
|
371
|
+
border-radius:50px;
|
|
372
|
+
padding:11px 20px;
|
|
373
|
+
font-size:14px;
|
|
374
|
+
font-weight:500;
|
|
375
|
+
font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;
|
|
376
|
+
cursor:pointer;
|
|
377
|
+
box-shadow:0 4px 16px rgba(0,0,0,.18);
|
|
378
|
+
display:flex;
|
|
379
|
+
align-items:center;
|
|
380
|
+
gap:8px;
|
|
381
|
+
transition:transform .15s,box-shadow .15s;
|
|
382
|
+
}
|
|
383
|
+
#cf-trigger:hover { transform:scale(1.04); box-shadow:0 6px 24px rgba(0,0,0,.22); }
|
|
384
|
+
#cf-trigger svg { width:18px; height:18px; }
|
|
385
|
+
|
|
386
|
+
#cf-modal-backdrop {
|
|
387
|
+
position:fixed;
|
|
388
|
+
top:0;left:0;right:0;bottom:0;
|
|
389
|
+
background:rgba(0,0,0,.45);
|
|
390
|
+
z-index:100001;
|
|
391
|
+
display:flex;
|
|
392
|
+
align-items:center;
|
|
393
|
+
justify-content:center;
|
|
394
|
+
font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
.cf-modal {
|
|
398
|
+
background:#fff;
|
|
399
|
+
border-radius:16px;
|
|
400
|
+
box-shadow:0 24px 80px rgba(0,0,0,.25);
|
|
401
|
+
overflow:hidden;
|
|
402
|
+
animation:cf-modal-in .25s ease;
|
|
403
|
+
display:flex;
|
|
404
|
+
flex-direction:column;
|
|
405
|
+
max-height:90vh;
|
|
406
|
+
}
|
|
407
|
+
.cf-modal--compact { width:420px; }
|
|
408
|
+
.cf-modal--expanded { width:min(1100px,92vw); flex-direction:column; }
|
|
409
|
+
|
|
410
|
+
.cf-modal-header {
|
|
411
|
+
display:flex;
|
|
412
|
+
align-items:center;
|
|
413
|
+
justify-content:space-between;
|
|
414
|
+
padding:20px 24px 16px;
|
|
415
|
+
border-bottom:1px solid #f0f0f0;
|
|
416
|
+
}
|
|
417
|
+
.cf-modal-header h2 {
|
|
418
|
+
margin:0;
|
|
419
|
+
font-size:20px;
|
|
420
|
+
font-weight:700;
|
|
421
|
+
color:#1a1a2e;
|
|
422
|
+
}
|
|
423
|
+
.cf-modal-header .cf-logo {
|
|
424
|
+
width:28px;
|
|
425
|
+
height:28px;
|
|
426
|
+
color:${cfg.color};
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
.cf-modal-body { display:flex; flex:1; overflow:hidden; }
|
|
430
|
+
|
|
431
|
+
.cf-screenshot-panel {
|
|
432
|
+
flex:1;
|
|
433
|
+
min-width:0;
|
|
434
|
+
background:#1a1a2e;
|
|
435
|
+
display:flex;
|
|
436
|
+
flex-direction:column;
|
|
437
|
+
position:relative;
|
|
438
|
+
}
|
|
439
|
+
.cf-screenshot-panel img {
|
|
440
|
+
width:100%;
|
|
441
|
+
height:100%;
|
|
442
|
+
object-fit:contain;
|
|
443
|
+
max-height:60vh;
|
|
444
|
+
}
|
|
445
|
+
.cf-screenshot-tools {
|
|
446
|
+
position:absolute;
|
|
447
|
+
bottom:16px;
|
|
448
|
+
left:50%;
|
|
449
|
+
transform:translateX(-50%);
|
|
450
|
+
display:flex;
|
|
451
|
+
gap:8px;
|
|
452
|
+
}
|
|
453
|
+
.cf-screenshot-tools button {
|
|
454
|
+
padding:8px 18px;
|
|
455
|
+
border-radius:8px;
|
|
456
|
+
font-size:13px;
|
|
457
|
+
font-weight:500;
|
|
458
|
+
cursor:pointer;
|
|
459
|
+
font-family:inherit;
|
|
460
|
+
border:none;
|
|
461
|
+
transition:all .15s;
|
|
462
|
+
}
|
|
463
|
+
.cf-tool-highlight {
|
|
464
|
+
background:${cfg.color};
|
|
465
|
+
color:#fff;
|
|
466
|
+
}
|
|
467
|
+
.cf-tool-highlight:hover { opacity:.9; }
|
|
468
|
+
.cf-tool-mask {
|
|
469
|
+
background:#fff;
|
|
470
|
+
color:#1a1a2e;
|
|
471
|
+
border:1px solid #e0e0e0 !important;
|
|
472
|
+
}
|
|
473
|
+
.cf-tool-mask:hover { background:#f5f5f5; }
|
|
474
|
+
|
|
475
|
+
.cf-form-panel {
|
|
476
|
+
width:100%;
|
|
477
|
+
padding:20px 24px;
|
|
478
|
+
display:flex;
|
|
479
|
+
flex-direction:column;
|
|
480
|
+
gap:14px;
|
|
481
|
+
overflow-y:auto;
|
|
482
|
+
}
|
|
483
|
+
.cf-modal--expanded .cf-form-panel {
|
|
484
|
+
width:340px;
|
|
485
|
+
flex-shrink:0;
|
|
486
|
+
border-left:1px solid #f0f0f0;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
.cf-field label {
|
|
490
|
+
display:block;
|
|
491
|
+
font-size:13px;
|
|
492
|
+
font-weight:500;
|
|
493
|
+
color:#1a1a2e;
|
|
494
|
+
margin-bottom:6px;
|
|
495
|
+
}
|
|
496
|
+
.cf-field label span { font-weight:400; color:#999; }
|
|
497
|
+
.cf-field input, .cf-field textarea {
|
|
498
|
+
width:100%;
|
|
499
|
+
padding:10px 14px;
|
|
500
|
+
border:1px solid #e0e0e0;
|
|
501
|
+
border-radius:10px;
|
|
502
|
+
font-size:14px;
|
|
503
|
+
font-family:inherit;
|
|
504
|
+
box-sizing:border-box;
|
|
505
|
+
outline:none;
|
|
506
|
+
transition:border-color .15s,box-shadow .15s;
|
|
507
|
+
color:#1a1a2e;
|
|
508
|
+
background:#fff;
|
|
509
|
+
}
|
|
510
|
+
.cf-field input::placeholder, .cf-field textarea::placeholder { color:#bbb; }
|
|
511
|
+
.cf-field input:focus, .cf-field textarea:focus {
|
|
512
|
+
border-color:${cfg.color};
|
|
513
|
+
box-shadow:0 0 0 3px ${cfg.color}18;
|
|
514
|
+
}
|
|
515
|
+
.cf-field textarea { resize:vertical; min-height:100px; }
|
|
516
|
+
|
|
517
|
+
.cf-screenshot-action {
|
|
518
|
+
width:100%;
|
|
519
|
+
padding:12px;
|
|
520
|
+
border:1px solid #e0e0e0;
|
|
521
|
+
border-radius:10px;
|
|
522
|
+
background:#fff;
|
|
523
|
+
font-size:14px;
|
|
524
|
+
color:#1a1a2e;
|
|
525
|
+
cursor:pointer;
|
|
526
|
+
font-family:inherit;
|
|
527
|
+
transition:all .15s;
|
|
528
|
+
text-align:center;
|
|
529
|
+
}
|
|
530
|
+
.cf-screenshot-action:hover { background:#f8f8f8; border-color:#ccc; }
|
|
531
|
+
|
|
532
|
+
.cf-remove-screenshot {
|
|
533
|
+
width:100%;
|
|
534
|
+
padding:10px;
|
|
535
|
+
border:1px solid #e0e0e0;
|
|
536
|
+
border-radius:10px;
|
|
537
|
+
background:#fff;
|
|
538
|
+
font-size:13px;
|
|
539
|
+
color:#666;
|
|
540
|
+
cursor:pointer;
|
|
541
|
+
font-family:inherit;
|
|
542
|
+
transition:all .15s;
|
|
543
|
+
text-align:center;
|
|
544
|
+
}
|
|
545
|
+
.cf-remove-screenshot:hover { background:#fff0f0; color:#e74c3c; border-color:#e74c3c; }
|
|
546
|
+
|
|
547
|
+
.cf-submit-btn {
|
|
548
|
+
width:100%;
|
|
549
|
+
padding:13px;
|
|
550
|
+
border:none;
|
|
551
|
+
border-radius:10px;
|
|
552
|
+
background:${cfg.color};
|
|
553
|
+
color:#fff;
|
|
554
|
+
font-size:15px;
|
|
555
|
+
font-weight:600;
|
|
556
|
+
cursor:pointer;
|
|
557
|
+
font-family:inherit;
|
|
558
|
+
transition:all .15s;
|
|
559
|
+
}
|
|
560
|
+
.cf-submit-btn:hover { opacity:.92; transform:translateY(-1px); box-shadow:0 4px 12px ${cfg.color}40; }
|
|
561
|
+
.cf-submit-btn:disabled { opacity:.5; cursor:not-allowed; transform:none; }
|
|
562
|
+
|
|
563
|
+
.cf-cancel-btn {
|
|
564
|
+
width:100%;
|
|
565
|
+
padding:11px;
|
|
566
|
+
border:1px solid #e0e0e0;
|
|
567
|
+
border-radius:10px;
|
|
568
|
+
background:#fff;
|
|
569
|
+
color:#1a1a2e;
|
|
570
|
+
font-size:14px;
|
|
571
|
+
font-weight:500;
|
|
572
|
+
cursor:pointer;
|
|
573
|
+
font-family:inherit;
|
|
574
|
+
transition:all .15s;
|
|
575
|
+
text-align:center;
|
|
576
|
+
}
|
|
577
|
+
.cf-cancel-btn:hover { background:#f8f8f8; }
|
|
578
|
+
|
|
579
|
+
.cf-footer {
|
|
580
|
+
text-align:center;
|
|
581
|
+
padding:12px;
|
|
582
|
+
border-top:1px solid #f0f0f0;
|
|
583
|
+
font-size:11px;
|
|
584
|
+
color:#bbb;
|
|
585
|
+
}
|
|
586
|
+
.cf-footer a {
|
|
587
|
+
color:#999;
|
|
588
|
+
text-decoration:none;
|
|
589
|
+
font-weight:500;
|
|
590
|
+
}
|
|
591
|
+
.cf-footer a:hover { color:${cfg.color}; }
|
|
592
|
+
`;
|
|
593
|
+
document.head.appendChild(style);
|
|
594
|
+
}
|
|
209
595
|
function getPositionCSS(pos) {
|
|
210
596
|
switch (pos) {
|
|
211
597
|
case "bottom-left":
|
|
@@ -218,79 +604,74 @@ function getPositionCSS(pos) {
|
|
|
218
604
|
return "bottom:20px;right:20px;";
|
|
219
605
|
}
|
|
220
606
|
}
|
|
221
|
-
function
|
|
222
|
-
switch (pos) {
|
|
223
|
-
case "bottom-left":
|
|
224
|
-
return "bottom:70px;left:20px;";
|
|
225
|
-
case "top-right":
|
|
226
|
-
return "top:70px;right:20px;";
|
|
227
|
-
case "top-left":
|
|
228
|
-
return "top:70px;left:20px;";
|
|
229
|
-
default:
|
|
230
|
-
return "bottom:70px;right:20px;";
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
function getWidgetHTML(cfg) {
|
|
234
|
-
const posBtn = getPositionCSS(cfg.position);
|
|
235
|
-
const posForm = getFormPositionCSS(cfg.position);
|
|
607
|
+
function getTriggerHTML(cfg) {
|
|
236
608
|
return `
|
|
237
|
-
<
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
#cf-trigger svg{width:16px;height:16px}
|
|
241
|
-
#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}
|
|
242
|
-
#cf-form h3{margin:0;font-size:15px;font-weight:600;color:#111}
|
|
243
|
-
#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}
|
|
244
|
-
#cf-form input:focus,#cf-form textarea:focus,#cf-form select:focus{border-color:${cfg.color}}
|
|
245
|
-
#cf-form textarea{resize:vertical;min-height:60px}
|
|
246
|
-
.cf-row{display:flex;gap:8px}
|
|
247
|
-
.cf-row select{flex:1}
|
|
248
|
-
.cf-tools{display:flex;gap:6px}
|
|
249
|
-
.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}
|
|
250
|
-
.cf-tool-btn:hover{background:#f1f5f9;border-color:#cbd5e1}
|
|
251
|
-
.cf-tool-btn svg{width:14px;height:14px}
|
|
252
|
-
#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}
|
|
253
|
-
#cf-submit:hover{opacity:.9}
|
|
254
|
-
#cf-close{position:absolute;top:10px;right:12px;background:none;border:none;cursor:pointer;font-size:18px;color:#94a3b8;line-height:1}
|
|
255
|
-
#cf-screenshot-preview{display:none;border:1px solid #e2e8f0;border-radius:8px;overflow:hidden;max-height:120px}
|
|
256
|
-
#cf-screenshot-preview img{width:100%;height:auto;display:block}
|
|
257
|
-
</style>
|
|
258
|
-
<button id="cf-trigger">
|
|
259
|
-
<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>
|
|
260
|
-
${cfg.text}
|
|
261
|
-
</button>
|
|
262
|
-
<div id="cf-form">
|
|
263
|
-
<button id="cf-close">×</button>
|
|
264
|
-
<h3>Report Feedback</h3>
|
|
265
|
-
<input id="cf-title" type="text" placeholder="Title *" />
|
|
266
|
-
<textarea id="cf-desc" placeholder="Description (optional)"></textarea>
|
|
267
|
-
<div class="cf-row">
|
|
268
|
-
<select id="cf-type">
|
|
269
|
-
<option value="BUG">Bug</option>
|
|
270
|
-
<option value="FEATURE">Feature</option>
|
|
271
|
-
<option value="IMPROVEMENT">Improvement</option>
|
|
272
|
-
<option value="QUESTION">Question</option>
|
|
273
|
-
</select>
|
|
274
|
-
<select id="cf-priority">
|
|
275
|
-
<option value="LOW">Low</option>
|
|
276
|
-
<option value="MEDIUM" selected>Medium</option>
|
|
277
|
-
<option value="HIGH">High</option>
|
|
278
|
-
<option value="CRITICAL">Critical</option>
|
|
279
|
-
</select>
|
|
280
|
-
</div>
|
|
281
|
-
<div class="cf-tools">
|
|
282
|
-
<button type="button" class="cf-tool-btn" id="cf-screenshot">
|
|
283
|
-
<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>
|
|
284
|
-
Screenshot
|
|
285
|
-
</button>
|
|
286
|
-
<button type="button" class="cf-tool-btn" id="cf-highlight">
|
|
287
|
-
<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>
|
|
288
|
-
Highlight
|
|
609
|
+
<button id="cf-trigger">
|
|
610
|
+
<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>
|
|
611
|
+
${cfg.text}
|
|
289
612
|
</button>
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
613
|
+
`;
|
|
614
|
+
}
|
|
615
|
+
function getCheckflowLogo() {
|
|
616
|
+
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>`;
|
|
617
|
+
}
|
|
618
|
+
function getModalHTML(cfg, expanded) {
|
|
619
|
+
const modalClass = expanded ? "cf-modal cf-modal--expanded" : "cf-modal cf-modal--compact";
|
|
620
|
+
const formFields = `
|
|
621
|
+
<div class="cf-field">
|
|
622
|
+
<label>Nom <span>(obligatoire)</span></label>
|
|
623
|
+
<input id="cf-name" type="text" placeholder="Votre nom" />
|
|
624
|
+
</div>
|
|
625
|
+
<div class="cf-field">
|
|
626
|
+
<label>Email <span>(obligatoire)</span></label>
|
|
627
|
+
<input id="cf-email" type="email" placeholder="votre.email@exemple.com" />
|
|
628
|
+
</div>
|
|
629
|
+
<div class="cf-field">
|
|
630
|
+
<label>Description <span>(obligatoire)</span></label>
|
|
631
|
+
<textarea id="cf-desc" placeholder="Quel est le probl\xE8me ? Que vous attendiez-vous \xE0 voir ?"></textarea>
|
|
632
|
+
</div>
|
|
633
|
+
`;
|
|
634
|
+
if (expanded && screenshotDataUrl) {
|
|
635
|
+
return `
|
|
636
|
+
<div class="${modalClass}">
|
|
637
|
+
<div class="cf-modal-header">
|
|
638
|
+
<h2>Envoyer le rapport</h2>
|
|
639
|
+
${getCheckflowLogo()}
|
|
640
|
+
</div>
|
|
641
|
+
<div class="cf-modal-body">
|
|
642
|
+
<div class="cf-screenshot-panel">
|
|
643
|
+
<img src="${screenshotDataUrl}" alt="Capture d'\xE9cran" />
|
|
644
|
+
<div class="cf-screenshot-tools">
|
|
645
|
+
<button class="cf-tool-highlight" id="cf-highlight-btn">Surligner</button>
|
|
646
|
+
<button class="cf-tool-mask" id="cf-mask-btn">Masquer</button>
|
|
647
|
+
</div>
|
|
648
|
+
</div>
|
|
649
|
+
<div class="cf-form-panel">
|
|
650
|
+
${formFields}
|
|
651
|
+
<button class="cf-remove-screenshot" id="cf-remove-screenshot">Supprimer la capture d'\xE9cran</button>
|
|
652
|
+
<button class="cf-submit-btn" id="cf-submit">Envoyer le rapport</button>
|
|
653
|
+
<button class="cf-cancel-btn" id="cf-cancel">Annuler</button>
|
|
654
|
+
</div>
|
|
655
|
+
</div>
|
|
656
|
+
<div class="cf-footer">Powered by <a href="https://checkflow.space" target="_blank" rel="noopener">Checkflow</a></div>
|
|
657
|
+
</div>
|
|
658
|
+
`;
|
|
659
|
+
}
|
|
660
|
+
return `
|
|
661
|
+
<div class="${modalClass}">
|
|
662
|
+
<div class="cf-modal-header">
|
|
663
|
+
<h2>Envoyer le rapport</h2>
|
|
664
|
+
${getCheckflowLogo()}
|
|
665
|
+
</div>
|
|
666
|
+
<div class="cf-form-panel">
|
|
667
|
+
${formFields}
|
|
668
|
+
<button class="cf-screenshot-action" id="cf-screenshot-btn">Ajouter une capture d'\xE9cran</button>
|
|
669
|
+
<button class="cf-submit-btn" id="cf-submit">Envoyer le rapport</button>
|
|
670
|
+
<button class="cf-cancel-btn" id="cf-cancel">Annuler</button>
|
|
671
|
+
</div>
|
|
672
|
+
<div class="cf-footer">Powered by <a href="https://checkflow.space" target="_blank" rel="noopener">Checkflow</a></div>
|
|
673
|
+
</div>
|
|
674
|
+
`;
|
|
294
675
|
}
|
|
295
676
|
|
|
296
677
|
// src/index.ts
|
|
@@ -306,10 +687,7 @@ function init(cfg) {
|
|
|
306
687
|
};
|
|
307
688
|
if (!config.enabled) return;
|
|
308
689
|
installInterceptors();
|
|
309
|
-
|
|
310
|
-
loadHtml2Canvas().catch(() => {
|
|
311
|
-
});
|
|
312
|
-
}
|
|
690
|
+
installNetworkInterceptor();
|
|
313
691
|
if (typeof window !== "undefined" && config.widget?.showOnInit !== false) {
|
|
314
692
|
mountWidget(
|
|
315
693
|
config.widget,
|
|
@@ -320,7 +698,9 @@ function init(cfg) {
|
|
|
320
698
|
type: data.type,
|
|
321
699
|
priority: data.priority,
|
|
322
700
|
screenshot_data: data.screenshot,
|
|
323
|
-
annotations: data.annotations
|
|
701
|
+
annotations: data.annotations,
|
|
702
|
+
reporter_name: data.reporter_name,
|
|
703
|
+
reporter_email: data.reporter_email
|
|
324
704
|
});
|
|
325
705
|
},
|
|
326
706
|
{
|
|
@@ -338,6 +718,7 @@ async function sendFeedback(data) {
|
|
|
338
718
|
const perf = typeof window !== "undefined" ? collectPerformance() : void 0;
|
|
339
719
|
const consoleLogs = getConsoleLogs();
|
|
340
720
|
const jsErrors = getJavascriptErrors();
|
|
721
|
+
const networkLogs = getNetworkLogs();
|
|
341
722
|
const payload = {
|
|
342
723
|
...data,
|
|
343
724
|
...context,
|
|
@@ -345,6 +726,7 @@ async function sendFeedback(data) {
|
|
|
345
726
|
performance_metrics: perf,
|
|
346
727
|
console_logs: consoleLogs.length > 0 ? consoleLogs : void 0,
|
|
347
728
|
javascript_errors: jsErrors.length > 0 ? jsErrors : void 0,
|
|
729
|
+
network_logs: networkLogs.length > 0 ? networkLogs : void 0,
|
|
348
730
|
sdk_version: SDK_VERSION
|
|
349
731
|
};
|
|
350
732
|
if (data.screenshot_data) {
|
|
@@ -353,6 +735,12 @@ async function sendFeedback(data) {
|
|
|
353
735
|
if (data.annotations && data.annotations.length > 0) {
|
|
354
736
|
payload.annotations = data.annotations;
|
|
355
737
|
}
|
|
738
|
+
if (data.reporter_name) {
|
|
739
|
+
payload.reporter_name = data.reporter_name;
|
|
740
|
+
}
|
|
741
|
+
if (data.reporter_email) {
|
|
742
|
+
payload.reporter_email = data.reporter_email;
|
|
743
|
+
}
|
|
356
744
|
try {
|
|
357
745
|
const res = await fetch(`${config.endpoint}/sdk/feedback`, {
|
|
358
746
|
method: "POST",
|
|
@@ -382,7 +770,9 @@ function showWidget() {
|
|
|
382
770
|
type: data.type,
|
|
383
771
|
priority: data.priority,
|
|
384
772
|
screenshot_data: data.screenshot,
|
|
385
|
-
annotations: data.annotations
|
|
773
|
+
annotations: data.annotations,
|
|
774
|
+
reporter_name: data.reporter_name,
|
|
775
|
+
reporter_email: data.reporter_email
|
|
386
776
|
});
|
|
387
777
|
},
|
|
388
778
|
{
|
|
@@ -397,6 +787,7 @@ function hideWidget() {
|
|
|
397
787
|
function destroy() {
|
|
398
788
|
unmountWidget();
|
|
399
789
|
clearLogs();
|
|
790
|
+
clearNetworkLogs();
|
|
400
791
|
clearScreenshot();
|
|
401
792
|
config = null;
|
|
402
793
|
}
|