@checkflow/sdk 1.1.1 → 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/README.md +63 -219
- package/dist/chunk-6EVTRC5X.mjs +59 -0
- package/dist/chunk-CD33QAA6.mjs +131 -0
- package/dist/highlighter-Dx2zURb6.d.mts +73 -0
- package/dist/highlighter-Dx2zURb6.d.ts +73 -0
- package/dist/highlighter-W4XDALRE.mjs +8 -0
- package/dist/index.d.mts +46 -0
- package/dist/index.d.ts +43 -20
- package/dist/index.js +974 -17219
- package/dist/index.mjs +802 -0
- package/dist/react.d.mts +28 -0
- package/dist/react.d.ts +28 -0
- package/dist/react.js +1118 -0
- package/dist/react.mjs +51 -0
- package/dist/screenshot-HXKGZNCZ.mjs +10 -0
- package/dist/vue.d.mts +26 -0
- package/dist/vue.d.ts +26 -0
- package/dist/vue.js +1119 -0
- package/dist/vue.mjs +53 -0
- package/package.json +38 -53
- package/dist/analytics-tracker.d.ts +0 -112
- package/dist/annotation/editor.d.ts +0 -72
- package/dist/annotation/index.d.ts +0 -9
- package/dist/annotation/styles.d.ts +0 -6
- package/dist/annotation/toolbar.d.ts +0 -32
- package/dist/annotation/types.d.ts +0 -85
- package/dist/api-client.d.ts +0 -76
- package/dist/checkflow.css +0 -1
- package/dist/checkflow.d.ts +0 -112
- package/dist/context-capture.d.ts +0 -42
- package/dist/error-capture.d.ts +0 -60
- package/dist/index.esm.js +0 -17210
- package/dist/index.esm.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/optimized-recorder.d.ts +0 -94
- package/dist/privacy/detector.d.ts +0 -56
- package/dist/privacy/index.d.ts +0 -8
- package/dist/privacy/masker.d.ts +0 -43
- package/dist/privacy/types.d.ts +0 -54
- package/dist/react/index.d.ts +0 -77
- package/dist/session-recording-rrweb.d.ts +0 -100
- package/dist/session-recording.d.ts +0 -74
- package/dist/types.d.ts +0 -299
- package/dist/vue/index.d.ts +0 -55
- package/dist/widget/Widget.d.ts +0 -98
- package/dist/widget/index.d.ts +0 -2
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,802 @@
|
|
|
1
|
+
import {
|
|
2
|
+
captureScreenshot,
|
|
3
|
+
clearScreenshot
|
|
4
|
+
} from "./chunk-6EVTRC5X.mjs";
|
|
5
|
+
import {
|
|
6
|
+
startHighlighting
|
|
7
|
+
} from "./chunk-CD33QAA6.mjs";
|
|
8
|
+
|
|
9
|
+
// src/collector.ts
|
|
10
|
+
function collectContext() {
|
|
11
|
+
const ua = navigator.userAgent;
|
|
12
|
+
return {
|
|
13
|
+
url: window.location.href,
|
|
14
|
+
viewport: {
|
|
15
|
+
width: window.innerWidth,
|
|
16
|
+
height: window.innerHeight,
|
|
17
|
+
device: window.innerWidth < 768 ? "mobile" : window.innerWidth < 1024 ? "tablet" : "desktop"
|
|
18
|
+
},
|
|
19
|
+
user_agent: ua,
|
|
20
|
+
browser: detectBrowser(ua),
|
|
21
|
+
os: detectOS(ua),
|
|
22
|
+
locale: navigator.language,
|
|
23
|
+
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function collectPerformance() {
|
|
27
|
+
try {
|
|
28
|
+
const entries = performance.getEntriesByType("paint");
|
|
29
|
+
const metrics = {};
|
|
30
|
+
entries.forEach((e) => {
|
|
31
|
+
if (e.name === "first-contentful-paint") metrics.fcp = Math.round(e.startTime);
|
|
32
|
+
});
|
|
33
|
+
const nav = performance.getEntriesByType("navigation")[0];
|
|
34
|
+
if (nav) {
|
|
35
|
+
metrics.dom_load = Math.round(nav.domContentLoadedEventEnd - nav.startTime);
|
|
36
|
+
metrics.load = Math.round(nav.loadEventEnd - nav.startTime);
|
|
37
|
+
}
|
|
38
|
+
return Object.keys(metrics).length > 0 ? metrics : void 0;
|
|
39
|
+
} catch {
|
|
40
|
+
return void 0;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function detectBrowser(ua) {
|
|
44
|
+
if (ua.includes("Firefox/")) return "Firefox";
|
|
45
|
+
if (ua.includes("Edg/")) return "Edge";
|
|
46
|
+
if (ua.includes("Chrome/")) return "Chrome";
|
|
47
|
+
if (ua.includes("Safari/")) return "Safari";
|
|
48
|
+
return "Unknown";
|
|
49
|
+
}
|
|
50
|
+
function detectOS(ua) {
|
|
51
|
+
if (ua.includes("Windows")) return "Windows";
|
|
52
|
+
if (ua.includes("Mac OS")) return "macOS";
|
|
53
|
+
if (ua.includes("Linux")) return "Linux";
|
|
54
|
+
if (ua.includes("Android")) return "Android";
|
|
55
|
+
if (ua.includes("iPhone") || ua.includes("iPad")) return "iOS";
|
|
56
|
+
return "Unknown";
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// src/console-interceptor.ts
|
|
60
|
+
var MAX_LOGS = 50;
|
|
61
|
+
var logs = [];
|
|
62
|
+
var errorsCapture = [];
|
|
63
|
+
var installed = false;
|
|
64
|
+
function installInterceptors() {
|
|
65
|
+
if (installed) return;
|
|
66
|
+
installed = true;
|
|
67
|
+
const origConsole = {
|
|
68
|
+
log: console.log,
|
|
69
|
+
warn: console.warn,
|
|
70
|
+
error: console.error
|
|
71
|
+
};
|
|
72
|
+
["log", "warn", "error"].forEach((level) => {
|
|
73
|
+
console[level] = (...args) => {
|
|
74
|
+
logs.push({
|
|
75
|
+
level,
|
|
76
|
+
message: args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" "),
|
|
77
|
+
timestamp: Date.now()
|
|
78
|
+
});
|
|
79
|
+
if (logs.length > MAX_LOGS) logs.shift();
|
|
80
|
+
origConsole[level](...args);
|
|
81
|
+
};
|
|
82
|
+
});
|
|
83
|
+
window.addEventListener("error", (event) => {
|
|
84
|
+
errorsCapture.push({
|
|
85
|
+
message: event.message,
|
|
86
|
+
filename: event.filename,
|
|
87
|
+
lineno: event.lineno,
|
|
88
|
+
colno: event.colno,
|
|
89
|
+
timestamp: Date.now()
|
|
90
|
+
});
|
|
91
|
+
if (errorsCapture.length > MAX_LOGS) errorsCapture.shift();
|
|
92
|
+
});
|
|
93
|
+
window.addEventListener("unhandledrejection", (event) => {
|
|
94
|
+
errorsCapture.push({
|
|
95
|
+
message: String(event.reason),
|
|
96
|
+
type: "unhandledrejection",
|
|
97
|
+
timestamp: Date.now()
|
|
98
|
+
});
|
|
99
|
+
if (errorsCapture.length > MAX_LOGS) errorsCapture.shift();
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
function getConsoleLogs() {
|
|
103
|
+
return [...logs];
|
|
104
|
+
}
|
|
105
|
+
function getJavascriptErrors() {
|
|
106
|
+
return [...errorsCapture];
|
|
107
|
+
}
|
|
108
|
+
function clearLogs() {
|
|
109
|
+
logs = [];
|
|
110
|
+
errorsCapture = [];
|
|
111
|
+
}
|
|
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
|
+
|
|
182
|
+
// src/widget.ts
|
|
183
|
+
var DEFAULT_CONFIG = {
|
|
184
|
+
position: "bottom-right",
|
|
185
|
+
color: "#1e3a5f",
|
|
186
|
+
text: "Report Bug",
|
|
187
|
+
showOnInit: true
|
|
188
|
+
};
|
|
189
|
+
var container = null;
|
|
190
|
+
var onSubmitCallback = null;
|
|
191
|
+
var screenshotDataUrl = null;
|
|
192
|
+
var annotationsData = [];
|
|
193
|
+
var isExpanded = false;
|
|
194
|
+
function mountWidget(config2 = {}, onSubmit, opts) {
|
|
195
|
+
if (container) return;
|
|
196
|
+
onSubmitCallback = onSubmit;
|
|
197
|
+
const cfg = { ...DEFAULT_CONFIG, ...config2 };
|
|
198
|
+
container = document.createElement("div");
|
|
199
|
+
container.id = "checkflow-widget";
|
|
200
|
+
injectStyles(cfg);
|
|
201
|
+
container.innerHTML = getTriggerHTML(cfg);
|
|
202
|
+
document.body.appendChild(container);
|
|
203
|
+
const btn = container.querySelector("#cf-trigger");
|
|
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();
|
|
219
|
+
});
|
|
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 () => {
|
|
268
|
+
if (!opts?.onScreenshot) return;
|
|
269
|
+
backdrop.style.display = "none";
|
|
270
|
+
try {
|
|
271
|
+
const data = await opts.onScreenshot();
|
|
272
|
+
if (data) {
|
|
273
|
+
screenshotDataUrl = data;
|
|
274
|
+
backdrop.style.display = "flex";
|
|
275
|
+
expandModal(cfg, opts);
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
} catch {
|
|
279
|
+
}
|
|
280
|
+
backdrop.style.display = "flex";
|
|
281
|
+
});
|
|
282
|
+
backdrop.querySelector("#cf-remove-screenshot")?.addEventListener("click", () => {
|
|
283
|
+
collapseModal(cfg, opts);
|
|
284
|
+
});
|
|
285
|
+
backdrop.querySelector("#cf-highlight-btn")?.addEventListener("click", async () => {
|
|
286
|
+
if (!opts?.onHighlight) return;
|
|
287
|
+
backdrop.style.display = "none";
|
|
288
|
+
try {
|
|
289
|
+
const annotations = await opts.onHighlight();
|
|
290
|
+
if (annotations && annotations.length > 0) {
|
|
291
|
+
annotationsData = annotations;
|
|
292
|
+
}
|
|
293
|
+
} catch {
|
|
294
|
+
}
|
|
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";
|
|
312
|
+
});
|
|
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 !");
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
function unmountWidget() {
|
|
340
|
+
closeModal();
|
|
341
|
+
if (container) {
|
|
342
|
+
container.remove();
|
|
343
|
+
container = null;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
function showToast(msg) {
|
|
347
|
+
const toast = document.createElement("div");
|
|
348
|
+
toast.textContent = msg;
|
|
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;';
|
|
350
|
+
document.body.appendChild(toast);
|
|
351
|
+
setTimeout(() => {
|
|
352
|
+
toast.style.opacity = "0";
|
|
353
|
+
toast.style.transition = "opacity .3s";
|
|
354
|
+
}, 2500);
|
|
355
|
+
setTimeout(() => toast.remove(), 3e3);
|
|
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
|
+
}
|
|
595
|
+
function getPositionCSS(pos) {
|
|
596
|
+
switch (pos) {
|
|
597
|
+
case "bottom-left":
|
|
598
|
+
return "bottom:20px;left:20px;";
|
|
599
|
+
case "top-right":
|
|
600
|
+
return "top:20px;right:20px;";
|
|
601
|
+
case "top-left":
|
|
602
|
+
return "top:20px;left:20px;";
|
|
603
|
+
default:
|
|
604
|
+
return "bottom:20px;right:20px;";
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
function getTriggerHTML(cfg) {
|
|
608
|
+
return `
|
|
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}
|
|
612
|
+
</button>
|
|
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
|
+
`;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
// src/index.ts
|
|
678
|
+
var SDK_VERSION = "1.1.0";
|
|
679
|
+
var DEFAULT_ENDPOINT = "https://api.checkflow.space/api/v1";
|
|
680
|
+
var config = null;
|
|
681
|
+
function init(cfg) {
|
|
682
|
+
config = {
|
|
683
|
+
enabled: true,
|
|
684
|
+
endpoint: DEFAULT_ENDPOINT,
|
|
685
|
+
environment: "production",
|
|
686
|
+
...cfg
|
|
687
|
+
};
|
|
688
|
+
if (!config.enabled) return;
|
|
689
|
+
installInterceptors();
|
|
690
|
+
installNetworkInterceptor();
|
|
691
|
+
if (typeof window !== "undefined" && config.widget?.showOnInit !== false) {
|
|
692
|
+
mountWidget(
|
|
693
|
+
config.widget,
|
|
694
|
+
(data) => {
|
|
695
|
+
sendFeedback({
|
|
696
|
+
title: data.title,
|
|
697
|
+
description: data.description,
|
|
698
|
+
type: data.type,
|
|
699
|
+
priority: data.priority,
|
|
700
|
+
screenshot_data: data.screenshot,
|
|
701
|
+
annotations: data.annotations,
|
|
702
|
+
reporter_name: data.reporter_name,
|
|
703
|
+
reporter_email: data.reporter_email
|
|
704
|
+
});
|
|
705
|
+
},
|
|
706
|
+
{
|
|
707
|
+
onScreenshot: () => captureScreenshot(),
|
|
708
|
+
onHighlight: () => startHighlighting()
|
|
709
|
+
}
|
|
710
|
+
);
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
async function sendFeedback(data) {
|
|
714
|
+
if (!config) {
|
|
715
|
+
return { success: false, error: { message: "SDK not initialized. Call init() first.", code: "NOT_INITIALIZED" } };
|
|
716
|
+
}
|
|
717
|
+
const context = typeof window !== "undefined" ? collectContext() : {};
|
|
718
|
+
const perf = typeof window !== "undefined" ? collectPerformance() : void 0;
|
|
719
|
+
const consoleLogs = getConsoleLogs();
|
|
720
|
+
const jsErrors = getJavascriptErrors();
|
|
721
|
+
const networkLogs = getNetworkLogs();
|
|
722
|
+
const payload = {
|
|
723
|
+
...data,
|
|
724
|
+
...context,
|
|
725
|
+
environment: config.environment,
|
|
726
|
+
performance_metrics: perf,
|
|
727
|
+
console_logs: consoleLogs.length > 0 ? consoleLogs : void 0,
|
|
728
|
+
javascript_errors: jsErrors.length > 0 ? jsErrors : void 0,
|
|
729
|
+
network_logs: networkLogs.length > 0 ? networkLogs : void 0,
|
|
730
|
+
sdk_version: SDK_VERSION
|
|
731
|
+
};
|
|
732
|
+
if (data.screenshot_data) {
|
|
733
|
+
payload.screenshot_data = data.screenshot_data;
|
|
734
|
+
}
|
|
735
|
+
if (data.annotations && data.annotations.length > 0) {
|
|
736
|
+
payload.annotations = data.annotations;
|
|
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
|
+
}
|
|
744
|
+
try {
|
|
745
|
+
const res = await fetch(`${config.endpoint}/sdk/feedback`, {
|
|
746
|
+
method: "POST",
|
|
747
|
+
headers: {
|
|
748
|
+
"Content-Type": "application/json",
|
|
749
|
+
"X-API-Key": config.apiKey
|
|
750
|
+
},
|
|
751
|
+
body: JSON.stringify(payload)
|
|
752
|
+
});
|
|
753
|
+
const json = await res.json();
|
|
754
|
+
if (!res.ok) {
|
|
755
|
+
return { success: false, error: json.error || { message: "Request failed", code: "REQUEST_FAILED" } };
|
|
756
|
+
}
|
|
757
|
+
return { success: true, data: json.data };
|
|
758
|
+
} catch (err) {
|
|
759
|
+
return { success: false, error: { message: err.message, code: "NETWORK_ERROR" } };
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
function showWidget() {
|
|
763
|
+
if (!config) return;
|
|
764
|
+
mountWidget(
|
|
765
|
+
config.widget,
|
|
766
|
+
(data) => {
|
|
767
|
+
sendFeedback({
|
|
768
|
+
title: data.title,
|
|
769
|
+
description: data.description,
|
|
770
|
+
type: data.type,
|
|
771
|
+
priority: data.priority,
|
|
772
|
+
screenshot_data: data.screenshot,
|
|
773
|
+
annotations: data.annotations,
|
|
774
|
+
reporter_name: data.reporter_name,
|
|
775
|
+
reporter_email: data.reporter_email
|
|
776
|
+
});
|
|
777
|
+
},
|
|
778
|
+
{
|
|
779
|
+
onScreenshot: () => captureScreenshot(),
|
|
780
|
+
onHighlight: () => startHighlighting()
|
|
781
|
+
}
|
|
782
|
+
);
|
|
783
|
+
}
|
|
784
|
+
function hideWidget() {
|
|
785
|
+
unmountWidget();
|
|
786
|
+
}
|
|
787
|
+
function destroy() {
|
|
788
|
+
unmountWidget();
|
|
789
|
+
clearLogs();
|
|
790
|
+
clearNetworkLogs();
|
|
791
|
+
clearScreenshot();
|
|
792
|
+
config = null;
|
|
793
|
+
}
|
|
794
|
+
export {
|
|
795
|
+
captureScreenshot,
|
|
796
|
+
destroy,
|
|
797
|
+
hideWidget,
|
|
798
|
+
init,
|
|
799
|
+
sendFeedback,
|
|
800
|
+
showWidget,
|
|
801
|
+
startHighlighting
|
|
802
|
+
};
|