@diegotsi/flint-react 1.0.1 → 1.0.2
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/index.cjs +443 -31
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -5
- package/dist/index.d.ts +14 -5
- package/dist/index.js +432 -20
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -32,7 +32,7 @@ var index_exports = {};
|
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
FlintModal: () => FlintModal,
|
|
34
34
|
FlintWidget: () => FlintWidget,
|
|
35
|
-
flint: () =>
|
|
35
|
+
flint: () => flint
|
|
36
36
|
});
|
|
37
37
|
module.exports = __toCommonJS(index_exports);
|
|
38
38
|
|
|
@@ -40,8 +40,437 @@ module.exports = __toCommonJS(index_exports);
|
|
|
40
40
|
var import_react2 = require("react");
|
|
41
41
|
var import_react_i18next = require("react-i18next");
|
|
42
42
|
|
|
43
|
-
//
|
|
44
|
-
var
|
|
43
|
+
// ../core/dist/index.js
|
|
44
|
+
var import_fflate = require("fflate");
|
|
45
|
+
async function fetchWithRetry(url, init, retries = 3, baseDelay = 1e3) {
|
|
46
|
+
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
47
|
+
try {
|
|
48
|
+
const res = await fetch(url, init);
|
|
49
|
+
if (res.ok || attempt === retries) return res;
|
|
50
|
+
if (res.status >= 400 && res.status < 500 && res.status !== 429) return res;
|
|
51
|
+
} catch (err) {
|
|
52
|
+
if (attempt === retries) throw err;
|
|
53
|
+
}
|
|
54
|
+
await new Promise((r) => setTimeout(r, baseDelay * 2 ** attempt));
|
|
55
|
+
}
|
|
56
|
+
throw new Error("Max retries exceeded");
|
|
57
|
+
}
|
|
58
|
+
async function submitReport(serverUrl, projectKey, payload, screenshot) {
|
|
59
|
+
const url = `${serverUrl.replace(/\/$/, "")}/api/v1/bug-reports`;
|
|
60
|
+
let body;
|
|
61
|
+
const headers = {
|
|
62
|
+
"x-project-key": projectKey
|
|
63
|
+
};
|
|
64
|
+
if (screenshot) {
|
|
65
|
+
const form = new FormData();
|
|
66
|
+
form.append("reporterId", payload.reporterId);
|
|
67
|
+
form.append("reporterName", payload.reporterName);
|
|
68
|
+
if (payload.reporterEmail) form.append("reporterEmail", payload.reporterEmail);
|
|
69
|
+
form.append("description", payload.description);
|
|
70
|
+
if (payload.expectedBehavior) form.append("expectedBehavior", payload.expectedBehavior);
|
|
71
|
+
if (payload.stepsToReproduce) form.append("stepsToReproduce", JSON.stringify(payload.stepsToReproduce));
|
|
72
|
+
if (payload.externalReplayUrl) form.append("externalReplayUrl", payload.externalReplayUrl);
|
|
73
|
+
if (payload.additionalContext) form.append("additionalContext", payload.additionalContext);
|
|
74
|
+
form.append("severity", payload.severity);
|
|
75
|
+
if (payload.url) form.append("url", payload.url);
|
|
76
|
+
if (payload.meta) form.append("meta", JSON.stringify(payload.meta));
|
|
77
|
+
form.append("screenshot", screenshot);
|
|
78
|
+
body = form;
|
|
79
|
+
} else {
|
|
80
|
+
body = JSON.stringify(payload);
|
|
81
|
+
headers["Content-Type"] = "application/json";
|
|
82
|
+
}
|
|
83
|
+
const res = await fetchWithRetry(url, { method: "POST", headers, body });
|
|
84
|
+
if (!res.ok) {
|
|
85
|
+
const err = await res.json().catch(() => ({ error: "Unknown error" }));
|
|
86
|
+
throw new Error(err.error ?? `HTTP ${res.status}`);
|
|
87
|
+
}
|
|
88
|
+
return res.json();
|
|
89
|
+
}
|
|
90
|
+
async function submitReplay(serverUrl, projectKey, reportId, events) {
|
|
91
|
+
const json = JSON.stringify(events);
|
|
92
|
+
const encoded = new TextEncoder().encode(json);
|
|
93
|
+
const compressed = (0, import_fflate.gzipSync)(encoded);
|
|
94
|
+
const url = `${serverUrl.replace(/\/$/, "")}/api/v1/bug-reports/${reportId}/replay`;
|
|
95
|
+
await fetch(url, {
|
|
96
|
+
method: "POST",
|
|
97
|
+
headers: {
|
|
98
|
+
"x-project-key": projectKey,
|
|
99
|
+
"Content-Type": "application/octet-stream"
|
|
100
|
+
},
|
|
101
|
+
body: compressed.buffer
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
var MAX_ENTRIES = 50;
|
|
105
|
+
function createConsoleCollector() {
|
|
106
|
+
const entries = [];
|
|
107
|
+
let active = false;
|
|
108
|
+
const originals = {
|
|
109
|
+
log: console.log.bind(console),
|
|
110
|
+
warn: console.warn.bind(console),
|
|
111
|
+
error: console.error.bind(console)
|
|
112
|
+
};
|
|
113
|
+
let origOnerror = null;
|
|
114
|
+
let origUnhandled = null;
|
|
115
|
+
function push(level, args) {
|
|
116
|
+
let str;
|
|
117
|
+
try {
|
|
118
|
+
str = args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ");
|
|
119
|
+
} catch {
|
|
120
|
+
str = String(args[0]);
|
|
121
|
+
}
|
|
122
|
+
if (str.length > 500) str = str.slice(0, 500) + "\u2026";
|
|
123
|
+
entries.push({ level, args: str, timestamp: Date.now() });
|
|
124
|
+
if (entries.length > MAX_ENTRIES) entries.shift();
|
|
125
|
+
}
|
|
126
|
+
return {
|
|
127
|
+
start() {
|
|
128
|
+
if (active) return;
|
|
129
|
+
active = true;
|
|
130
|
+
console.log = (...args) => {
|
|
131
|
+
push("log", args);
|
|
132
|
+
originals.log(...args);
|
|
133
|
+
};
|
|
134
|
+
console.warn = (...args) => {
|
|
135
|
+
push("warn", args);
|
|
136
|
+
originals.warn(...args);
|
|
137
|
+
};
|
|
138
|
+
console.error = (...args) => {
|
|
139
|
+
push("error", args);
|
|
140
|
+
originals.error(...args);
|
|
141
|
+
};
|
|
142
|
+
origOnerror = window.onerror;
|
|
143
|
+
window.onerror = (msg, src, line, col, err) => {
|
|
144
|
+
push("error", [err?.message ?? String(msg), `${src}:${line}:${col}`]);
|
|
145
|
+
if (typeof origOnerror === "function") return origOnerror(msg, src, line, col, err);
|
|
146
|
+
return false;
|
|
147
|
+
};
|
|
148
|
+
origUnhandled = window.onunhandledrejection;
|
|
149
|
+
window.onunhandledrejection = (event) => {
|
|
150
|
+
const reason = event.reason instanceof Error ? event.reason.message : JSON.stringify(event.reason);
|
|
151
|
+
push("error", ["UnhandledRejection:", reason]);
|
|
152
|
+
if (typeof origUnhandled === "function") origUnhandled.call(window, event);
|
|
153
|
+
};
|
|
154
|
+
},
|
|
155
|
+
stop() {
|
|
156
|
+
if (!active) return;
|
|
157
|
+
active = false;
|
|
158
|
+
console.log = originals.log;
|
|
159
|
+
console.warn = originals.warn;
|
|
160
|
+
console.error = originals.error;
|
|
161
|
+
window.onerror = origOnerror;
|
|
162
|
+
window.onunhandledrejection = origUnhandled;
|
|
163
|
+
},
|
|
164
|
+
getEntries() {
|
|
165
|
+
return [...entries];
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
function collectEnvironment() {
|
|
170
|
+
const ua = navigator.userAgent;
|
|
171
|
+
let browser = "Unknown";
|
|
172
|
+
const chromeM = ua.match(/Chrome\/(\d+)/);
|
|
173
|
+
const firefoxM = ua.match(/Firefox\/(\d+)/);
|
|
174
|
+
const edgeM = ua.match(/Edg\/(\d+)/);
|
|
175
|
+
const safariM = ua.match(/Version\/(\d+)/);
|
|
176
|
+
const operaM = ua.match(/OPR\/(\d+)/);
|
|
177
|
+
if (operaM) {
|
|
178
|
+
browser = `Opera ${operaM[1]}`;
|
|
179
|
+
} else if (edgeM) {
|
|
180
|
+
browser = `Edge ${edgeM[1]}`;
|
|
181
|
+
} else if (chromeM && !/Edg|OPR/.test(ua)) {
|
|
182
|
+
browser = `Chrome ${chromeM[1]}`;
|
|
183
|
+
} else if (firefoxM) {
|
|
184
|
+
browser = `Firefox ${firefoxM[1]}`;
|
|
185
|
+
} else if (safariM && /Safari\//.test(ua)) {
|
|
186
|
+
browser = `Safari ${safariM[1]}`;
|
|
187
|
+
}
|
|
188
|
+
let os = "Unknown";
|
|
189
|
+
const macM = ua.match(/Mac OS X (\d+[._]\d+)/);
|
|
190
|
+
const winM = ua.match(/Windows NT (\d+\.\d+)/);
|
|
191
|
+
const androidM = ua.match(/Android (\d+)/);
|
|
192
|
+
const iosM = ua.match(/iPhone OS (\d+[._]\d+)/);
|
|
193
|
+
if (macM) {
|
|
194
|
+
os = `macOS ${macM[1].replace("_", ".")}`;
|
|
195
|
+
} else if (winM) {
|
|
196
|
+
const winMap = {
|
|
197
|
+
"10.0": "10/11",
|
|
198
|
+
"6.3": "8.1",
|
|
199
|
+
"6.2": "8",
|
|
200
|
+
"6.1": "7"
|
|
201
|
+
};
|
|
202
|
+
os = `Windows ${winMap[winM[1]] ?? winM[1]}`;
|
|
203
|
+
} else if (androidM) {
|
|
204
|
+
os = `Android ${androidM[1]}`;
|
|
205
|
+
} else if (iosM) {
|
|
206
|
+
os = `iOS ${iosM[1].replace(/_/g, ".")}`;
|
|
207
|
+
} else if (/Linux/.test(ua)) {
|
|
208
|
+
os = "Linux";
|
|
209
|
+
}
|
|
210
|
+
return {
|
|
211
|
+
browser,
|
|
212
|
+
os,
|
|
213
|
+
viewport: `${window.innerWidth}x${window.innerHeight}`,
|
|
214
|
+
screen: `${screen.width}x${screen.height}`,
|
|
215
|
+
language: navigator.language,
|
|
216
|
+
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
217
|
+
online: navigator.onLine
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
function createFrustrationCollector(opts) {
|
|
221
|
+
const threshold = opts?.rageClickThreshold ?? 3;
|
|
222
|
+
const window2 = opts?.rageClickWindow ?? 500;
|
|
223
|
+
const errorThreshold = opts?.errorLoopThreshold ?? 3;
|
|
224
|
+
const errorWindow = opts?.errorLoopWindow ?? 3e4;
|
|
225
|
+
const deadClicksEnabled = opts?.enableDeadClicks ?? true;
|
|
226
|
+
const events = [];
|
|
227
|
+
const listeners2 = /* @__PURE__ */ new Set();
|
|
228
|
+
const clickHistory = [];
|
|
229
|
+
const errorCounts = /* @__PURE__ */ new Map();
|
|
230
|
+
let clickHandler = null;
|
|
231
|
+
let origConsoleError = null;
|
|
232
|
+
function emit2(event) {
|
|
233
|
+
events.push(event);
|
|
234
|
+
if (events.length > 50) events.shift();
|
|
235
|
+
for (const cb of listeners2) cb(event);
|
|
236
|
+
}
|
|
237
|
+
function getCSSSelector(el) {
|
|
238
|
+
if (el.id) return `#${el.id}`;
|
|
239
|
+
const tag = el.tagName.toLowerCase();
|
|
240
|
+
const cls = [...el.classList].slice(0, 3).join(".");
|
|
241
|
+
if (cls) return `${tag}.${cls}`;
|
|
242
|
+
return tag;
|
|
243
|
+
}
|
|
244
|
+
function isInteractive(el) {
|
|
245
|
+
const tag = el.tagName.toLowerCase();
|
|
246
|
+
if (["a", "button", "input", "select", "textarea", "label", "summary"].includes(tag)) return true;
|
|
247
|
+
if (el.getAttribute("role") === "button" || el.getAttribute("tabindex")) return true;
|
|
248
|
+
if (el.onclick || el.getAttribute("onclick")) return true;
|
|
249
|
+
if (el.closest("a, button, [role=button], [onclick]")) return true;
|
|
250
|
+
try {
|
|
251
|
+
if (getComputedStyle(el).cursor === "pointer") return true;
|
|
252
|
+
} catch {
|
|
253
|
+
}
|
|
254
|
+
return false;
|
|
255
|
+
}
|
|
256
|
+
function handleClick(e) {
|
|
257
|
+
const target = e.target;
|
|
258
|
+
if (!target) return;
|
|
259
|
+
const now = Date.now();
|
|
260
|
+
clickHistory.push({ target, time: now });
|
|
261
|
+
while (clickHistory.length > 0 && now - clickHistory[0].time > 1e3) {
|
|
262
|
+
clickHistory.shift();
|
|
263
|
+
}
|
|
264
|
+
const recentOnSame = clickHistory.filter((c) => c.target === target && now - c.time < window2);
|
|
265
|
+
if (recentOnSame.length >= threshold) {
|
|
266
|
+
emit2({
|
|
267
|
+
type: "rage_click",
|
|
268
|
+
timestamp: now,
|
|
269
|
+
target: getCSSSelector(target),
|
|
270
|
+
details: `${recentOnSame.length} clicks in ${now - recentOnSame[0].time}ms`,
|
|
271
|
+
url: globalThis.location?.href ?? ""
|
|
272
|
+
});
|
|
273
|
+
clickHistory.length = 0;
|
|
274
|
+
}
|
|
275
|
+
if (deadClicksEnabled && !isInteractive(target)) {
|
|
276
|
+
emit2({
|
|
277
|
+
type: "dead_click",
|
|
278
|
+
timestamp: now,
|
|
279
|
+
target: getCSSSelector(target),
|
|
280
|
+
details: `Click on non-interactive <${target.tagName.toLowerCase()}>`,
|
|
281
|
+
url: globalThis.location?.href ?? ""
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
function patchConsoleError() {
|
|
286
|
+
origConsoleError = console.error;
|
|
287
|
+
console.error = (...args) => {
|
|
288
|
+
origConsoleError?.apply(console, args);
|
|
289
|
+
const key = String(args[0]).slice(0, 100);
|
|
290
|
+
const now = Date.now();
|
|
291
|
+
const existing = errorCounts.get(key);
|
|
292
|
+
if (existing && now - existing.firstSeen < errorWindow) {
|
|
293
|
+
existing.count++;
|
|
294
|
+
if (existing.count >= errorThreshold) {
|
|
295
|
+
emit2({
|
|
296
|
+
type: "error_loop",
|
|
297
|
+
timestamp: now,
|
|
298
|
+
target: "console",
|
|
299
|
+
details: `Same error ${existing.count}x in ${Math.round((now - existing.firstSeen) / 1e3)}s: ${key}`,
|
|
300
|
+
url: globalThis.location?.href ?? ""
|
|
301
|
+
});
|
|
302
|
+
errorCounts.delete(key);
|
|
303
|
+
}
|
|
304
|
+
} else {
|
|
305
|
+
errorCounts.set(key, { count: 1, firstSeen: now });
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
return {
|
|
310
|
+
start() {
|
|
311
|
+
clickHandler = handleClick;
|
|
312
|
+
document.addEventListener("click", clickHandler, true);
|
|
313
|
+
patchConsoleError();
|
|
314
|
+
},
|
|
315
|
+
stop() {
|
|
316
|
+
if (clickHandler) document.removeEventListener("click", clickHandler, true);
|
|
317
|
+
if (origConsoleError) console.error = origConsoleError;
|
|
318
|
+
clickHistory.length = 0;
|
|
319
|
+
errorCounts.clear();
|
|
320
|
+
},
|
|
321
|
+
getEvents() {
|
|
322
|
+
return [...events];
|
|
323
|
+
},
|
|
324
|
+
onFrustration(callback) {
|
|
325
|
+
listeners2.add(callback);
|
|
326
|
+
return () => listeners2.delete(callback);
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
var MAX_ENTRIES2 = 50;
|
|
331
|
+
var BLOCKED_HOSTS = /* @__PURE__ */ new Set([
|
|
332
|
+
"browser-intake-datadoghq.com",
|
|
333
|
+
"rum.browser-intake-datadoghq.com",
|
|
334
|
+
"logs.browser-intake-datadoghq.com",
|
|
335
|
+
"session-replay.browser-intake-datadoghq.com"
|
|
336
|
+
]);
|
|
337
|
+
function isBlockedUrl(url, extra) {
|
|
338
|
+
try {
|
|
339
|
+
const host = new URL(url, location.href).hostname;
|
|
340
|
+
const all = [...BLOCKED_HOSTS, ...extra];
|
|
341
|
+
return all.some((b) => host === b || host.endsWith("." + b));
|
|
342
|
+
} catch {
|
|
343
|
+
return false;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
function truncateUrl(url) {
|
|
347
|
+
try {
|
|
348
|
+
const u = new URL(url, location.href);
|
|
349
|
+
const base = `${u.origin}${u.pathname}`;
|
|
350
|
+
return base.length > 200 ? base.slice(0, 200) + "\u2026" : base;
|
|
351
|
+
} catch {
|
|
352
|
+
return url.length > 200 ? url.slice(0, 200) + "\u2026" : url;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
function createNetworkCollector(extraBlockedHosts = []) {
|
|
356
|
+
const entries = [];
|
|
357
|
+
const blocked = new Set(extraBlockedHosts);
|
|
358
|
+
let origFetch = null;
|
|
359
|
+
let origXHROpen = null;
|
|
360
|
+
let active = false;
|
|
361
|
+
function push(entry) {
|
|
362
|
+
entries.push(entry);
|
|
363
|
+
if (entries.length > MAX_ENTRIES2) entries.shift();
|
|
364
|
+
}
|
|
365
|
+
return {
|
|
366
|
+
start() {
|
|
367
|
+
if (active) return;
|
|
368
|
+
active = true;
|
|
369
|
+
origFetch = window.fetch;
|
|
370
|
+
window.fetch = async (input, init) => {
|
|
371
|
+
const method = (init?.method ?? "GET").toUpperCase();
|
|
372
|
+
const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
|
|
373
|
+
const startTime = Date.now();
|
|
374
|
+
const res = await origFetch.call(window, input, init);
|
|
375
|
+
if (res.status >= 400 && !isBlockedUrl(url, blocked)) {
|
|
376
|
+
push({
|
|
377
|
+
method,
|
|
378
|
+
url: truncateUrl(url),
|
|
379
|
+
status: res.status,
|
|
380
|
+
duration: Date.now() - startTime,
|
|
381
|
+
timestamp: startTime
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
return res;
|
|
385
|
+
};
|
|
386
|
+
origXHROpen = XMLHttpRequest.prototype.open;
|
|
387
|
+
XMLHttpRequest.prototype.open = function(method, url, async, username, password) {
|
|
388
|
+
const startTime = Date.now();
|
|
389
|
+
const urlStr = typeof url === "string" ? url : url.href;
|
|
390
|
+
this.addEventListener("load", () => {
|
|
391
|
+
if (this.status >= 400 && !isBlockedUrl(urlStr, blocked)) {
|
|
392
|
+
push({
|
|
393
|
+
method: method.toUpperCase(),
|
|
394
|
+
url: truncateUrl(urlStr),
|
|
395
|
+
status: this.status,
|
|
396
|
+
duration: Date.now() - startTime,
|
|
397
|
+
timestamp: startTime
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
});
|
|
401
|
+
return origXHROpen.apply(this, [method, url, async ?? true, username, password]);
|
|
402
|
+
};
|
|
403
|
+
},
|
|
404
|
+
stop() {
|
|
405
|
+
if (!active) return;
|
|
406
|
+
active = false;
|
|
407
|
+
if (origFetch) window.fetch = origFetch;
|
|
408
|
+
if (origXHROpen) XMLHttpRequest.prototype.open = origXHROpen;
|
|
409
|
+
},
|
|
410
|
+
getEntries() {
|
|
411
|
+
return [...entries];
|
|
412
|
+
}
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
var state = { user: void 0, sessionReplay: void 0 };
|
|
416
|
+
var listeners = /* @__PURE__ */ new Set();
|
|
417
|
+
function emit() {
|
|
418
|
+
for (const l of listeners) l();
|
|
419
|
+
}
|
|
420
|
+
function subscribe(listener) {
|
|
421
|
+
listeners.add(listener);
|
|
422
|
+
return () => listeners.delete(listener);
|
|
423
|
+
}
|
|
424
|
+
function getSnapshot() {
|
|
425
|
+
return state;
|
|
426
|
+
}
|
|
427
|
+
var flint = {
|
|
428
|
+
setUser(user) {
|
|
429
|
+
state = { ...state, user: user ?? void 0 };
|
|
430
|
+
emit();
|
|
431
|
+
},
|
|
432
|
+
setSessionReplay(url) {
|
|
433
|
+
state = { ...state, sessionReplay: url ?? void 0 };
|
|
434
|
+
emit();
|
|
435
|
+
}
|
|
436
|
+
};
|
|
437
|
+
var light = {
|
|
438
|
+
background: "rgba(255,255,255,0.90)",
|
|
439
|
+
backgroundSecondary: "rgba(249,250,251,0.75)",
|
|
440
|
+
accent: "#2563eb",
|
|
441
|
+
accentHover: "#1d4ed8",
|
|
442
|
+
text: "#111827",
|
|
443
|
+
textMuted: "#6b7280",
|
|
444
|
+
border: "rgba(255,255,255,0.9)",
|
|
445
|
+
shadow: "0 32px 80px rgba(0,0,0,0.18), 0 8px 32px rgba(0,0,0,0.1), 0 0 0 1px rgba(0,0,0,0.04)",
|
|
446
|
+
buttonText: "#ffffff",
|
|
447
|
+
backdropFilter: "blur(32px) saturate(1.8)"
|
|
448
|
+
};
|
|
449
|
+
var dark = {
|
|
450
|
+
background: "rgba(15,20,35,0.88)",
|
|
451
|
+
backgroundSecondary: "rgba(5,8,18,0.65)",
|
|
452
|
+
accent: "#4d8aff",
|
|
453
|
+
accentHover: "#3b6fdb",
|
|
454
|
+
text: "#dde3ef",
|
|
455
|
+
textMuted: "#6b7a93",
|
|
456
|
+
border: "rgba(255,255,255,0.08)",
|
|
457
|
+
shadow: "0 24px 60px rgba(0,0,0,0.6), 0 0 0 1px rgba(255,255,255,0.04)",
|
|
458
|
+
buttonText: "#ffffff",
|
|
459
|
+
backdropFilter: "blur(32px) saturate(1.6)"
|
|
460
|
+
};
|
|
461
|
+
function resolveTheme(theme) {
|
|
462
|
+
if (theme === "dark") return dark;
|
|
463
|
+
if (theme === "light") return light;
|
|
464
|
+
const override = theme;
|
|
465
|
+
return {
|
|
466
|
+
...light,
|
|
467
|
+
background: override.background ?? light.background,
|
|
468
|
+
accent: override.accent ?? light.accent,
|
|
469
|
+
accentHover: override.accent ?? light.accentHover,
|
|
470
|
+
text: override.text ?? light.text,
|
|
471
|
+
border: override.border ?? light.border
|
|
472
|
+
};
|
|
473
|
+
}
|
|
45
474
|
|
|
46
475
|
// src/ScreenAnnotator.tsx
|
|
47
476
|
var import_modern_screenshot = require("modern-screenshot");
|
|
@@ -185,9 +614,6 @@ function ScreenAnnotator({ zIndex, onCapture, onCancel }) {
|
|
|
185
614
|
);
|
|
186
615
|
}
|
|
187
616
|
|
|
188
|
-
// src/theme.ts
|
|
189
|
-
var import_core2 = require("@flint/core");
|
|
190
|
-
|
|
191
617
|
// src/FlintModal.tsx
|
|
192
618
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
193
619
|
var SEVERITIES = ["P1", "P2", "P3", "P4"];
|
|
@@ -254,7 +680,7 @@ function FlintModal({
|
|
|
254
680
|
onError
|
|
255
681
|
}) {
|
|
256
682
|
const { t } = (0, import_react_i18next.useTranslation)();
|
|
257
|
-
const colors =
|
|
683
|
+
const colors = resolveTheme(theme);
|
|
258
684
|
const isDark = theme === "dark";
|
|
259
685
|
const [severity, setSeverity] = (0, import_react2.useState)("P2");
|
|
260
686
|
const [description, setDescription] = (0, import_react2.useState)("");
|
|
@@ -331,13 +757,13 @@ function FlintModal({
|
|
|
331
757
|
}
|
|
332
758
|
}
|
|
333
759
|
try {
|
|
334
|
-
const res = await
|
|
760
|
+
const res = await submitReport(serverUrl, projectKey, payload, !isText ? screenshot ?? void 0 : void 0);
|
|
335
761
|
setResult(res);
|
|
336
762
|
setStatus("success");
|
|
337
763
|
onSuccess?.(res);
|
|
338
764
|
const events = getReplayEvents();
|
|
339
765
|
if (events.length > 0) {
|
|
340
|
-
|
|
766
|
+
submitReplay(serverUrl, projectKey, res.id, events).catch(() => {
|
|
341
767
|
});
|
|
342
768
|
}
|
|
343
769
|
} catch (err) {
|
|
@@ -1180,18 +1606,6 @@ function CheckIcon({ size = 20 }) {
|
|
|
1180
1606
|
var import_react4 = require("react");
|
|
1181
1607
|
var import_react_i18next3 = require("react-i18next");
|
|
1182
1608
|
|
|
1183
|
-
// src/collectors/console.ts
|
|
1184
|
-
var import_core3 = require("@flint/core");
|
|
1185
|
-
|
|
1186
|
-
// src/collectors/environment.ts
|
|
1187
|
-
var import_core4 = require("@flint/core");
|
|
1188
|
-
|
|
1189
|
-
// src/collectors/frustration.ts
|
|
1190
|
-
var import_core5 = require("@flint/core");
|
|
1191
|
-
|
|
1192
|
-
// src/collectors/network.ts
|
|
1193
|
-
var import_core6 = require("@flint/core");
|
|
1194
|
-
|
|
1195
1609
|
// src/i18n/index.ts
|
|
1196
1610
|
var import_i18next = require("i18next");
|
|
1197
1611
|
var import_react_i18next2 = require("react-i18next");
|
|
@@ -1242,11 +1656,9 @@ widgetI18n.use(import_react_i18next2.initReactI18next).init({
|
|
|
1242
1656
|
var i18n_default = widgetI18n;
|
|
1243
1657
|
|
|
1244
1658
|
// src/store.ts
|
|
1245
|
-
var import_core7 = require("@flint/core");
|
|
1246
1659
|
var import_react3 = require("react");
|
|
1247
|
-
var import_core8 = require("@flint/core");
|
|
1248
1660
|
function useFlintStore() {
|
|
1249
|
-
return (0, import_react3.useSyncExternalStore)(
|
|
1661
|
+
return (0, import_react3.useSyncExternalStore)(subscribe, getSnapshot);
|
|
1250
1662
|
}
|
|
1251
1663
|
|
|
1252
1664
|
// src/FlintWidget.tsx
|
|
@@ -1308,7 +1720,7 @@ function WidgetContent({
|
|
|
1308
1720
|
const [open, setOpen] = (0, import_react4.useState)(false);
|
|
1309
1721
|
const [hovered, setHovered] = (0, import_react4.useState)(false);
|
|
1310
1722
|
const pendingSelection = (0, import_react4.useRef)("");
|
|
1311
|
-
const colors =
|
|
1723
|
+
const colors = resolveTheme(theme);
|
|
1312
1724
|
const [selectionTooltip, setSelectionTooltip] = (0, import_react4.useState)(null);
|
|
1313
1725
|
const tooltipRef = (0, import_react4.useRef)(null);
|
|
1314
1726
|
const triggerRef = (0, import_react4.useRef)(null);
|
|
@@ -1356,7 +1768,7 @@ function WidgetContent({
|
|
|
1356
1768
|
const replayEvents = (0, import_react4.useRef)([]);
|
|
1357
1769
|
const stopReplay = (0, import_react4.useRef)(null);
|
|
1358
1770
|
if (enableConsole && !consoleCollector.current) {
|
|
1359
|
-
consoleCollector.current =
|
|
1771
|
+
consoleCollector.current = createConsoleCollector();
|
|
1360
1772
|
consoleCollector.current.start();
|
|
1361
1773
|
}
|
|
1362
1774
|
if (enableNetwork && !networkCollector.current) {
|
|
@@ -1367,12 +1779,12 @@ function WidgetContent({
|
|
|
1367
1779
|
return "";
|
|
1368
1780
|
}
|
|
1369
1781
|
})();
|
|
1370
|
-
networkCollector.current =
|
|
1782
|
+
networkCollector.current = createNetworkCollector(flintHost ? [flintHost] : []);
|
|
1371
1783
|
networkCollector.current.start();
|
|
1372
1784
|
}
|
|
1373
1785
|
const frustrationCollector = (0, import_react4.useRef)(null);
|
|
1374
1786
|
if (enableFrustration && !frustrationCollector.current) {
|
|
1375
|
-
frustrationCollector.current =
|
|
1787
|
+
frustrationCollector.current = createFrustrationCollector();
|
|
1376
1788
|
frustrationCollector.current.start();
|
|
1377
1789
|
}
|
|
1378
1790
|
(0, import_react4.useEffect)(() => {
|
|
@@ -1404,7 +1816,7 @@ function WidgetContent({
|
|
|
1404
1816
|
if (!enableFrustration || !autoReportFrustration || !frustrationCollector.current) return;
|
|
1405
1817
|
const unsubscribe = frustrationCollector.current.onFrustration(async (event) => {
|
|
1406
1818
|
const user2 = resolvedUser;
|
|
1407
|
-
await
|
|
1819
|
+
await submitReport(serverUrl, projectKey, {
|
|
1408
1820
|
reporterId: user2?.id ?? "anonymous",
|
|
1409
1821
|
reporterName: user2?.name ?? "Anonymous",
|
|
1410
1822
|
reporterEmail: user2?.email,
|
|
@@ -1412,7 +1824,7 @@ function WidgetContent({
|
|
|
1412
1824
|
severity: event.type === "error_loop" ? "P1" : event.type === "rage_click" ? "P2" : "P3",
|
|
1413
1825
|
url: event.url,
|
|
1414
1826
|
meta: {
|
|
1415
|
-
environment:
|
|
1827
|
+
environment: collectEnvironment(),
|
|
1416
1828
|
consoleLogs: consoleCollector.current?.getEntries() ?? [],
|
|
1417
1829
|
networkErrors: networkCollector.current?.getEntries() ?? [],
|
|
1418
1830
|
frustrationEvent: event
|
|
@@ -1518,7 +1930,7 @@ function WidgetContent({
|
|
|
1518
1930
|
onClose?.();
|
|
1519
1931
|
pendingSelection.current = "";
|
|
1520
1932
|
},
|
|
1521
|
-
getEnvironment:
|
|
1933
|
+
getEnvironment: collectEnvironment,
|
|
1522
1934
|
getConsoleLogs: () => consoleCollector.current?.getEntries() ?? [],
|
|
1523
1935
|
getNetworkErrors: () => networkCollector.current?.getEntries() ?? [],
|
|
1524
1936
|
getReplayEvents: () => [...replayEvents.current],
|