quick-bug-reporter-react 1.0.5 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +213 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +37 -1
- package/dist/index.d.ts +37 -1
- package/dist/index.js +212 -8
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -555,6 +555,31 @@ var ScreenshotCapturer = class {
|
|
|
555
555
|
|
|
556
556
|
// src/core/types.ts
|
|
557
557
|
var DEFAULT_MAX_RECORDING_MS = 2 * 60 * 1e3;
|
|
558
|
+
function formatConsoleLogs(logs) {
|
|
559
|
+
if (logs.length === 0) {
|
|
560
|
+
return "No console output captured.";
|
|
561
|
+
}
|
|
562
|
+
return logs.map((entry) => {
|
|
563
|
+
const tag = entry.level.toUpperCase().padEnd(5);
|
|
564
|
+
const args = entry.args.join(" ");
|
|
565
|
+
return `[${entry.timestamp}] ${tag} ${args}`;
|
|
566
|
+
}).join("\n");
|
|
567
|
+
}
|
|
568
|
+
function formatJsErrors(errors) {
|
|
569
|
+
if (errors.length === 0) {
|
|
570
|
+
return "No JavaScript errors captured.";
|
|
571
|
+
}
|
|
572
|
+
return errors.map((entry) => {
|
|
573
|
+
const lines = [`[${entry.timestamp}] ${entry.type}: ${entry.message}`];
|
|
574
|
+
if (entry.source) {
|
|
575
|
+
lines.push(` at ${entry.source}${entry.lineno ? `:${entry.lineno}` : ""}${entry.colno ? `:${entry.colno}` : ""}`);
|
|
576
|
+
}
|
|
577
|
+
if (entry.stack) {
|
|
578
|
+
lines.push(entry.stack.split("\n").map((l) => ` ${l}`).join("\n"));
|
|
579
|
+
}
|
|
580
|
+
return lines.join("\n");
|
|
581
|
+
}).join("\n\n");
|
|
582
|
+
}
|
|
558
583
|
function formatNetworkLogs(logs) {
|
|
559
584
|
if (logs.length === 0) {
|
|
560
585
|
return "No network requests captured.";
|
|
@@ -859,6 +884,8 @@ var BugReporter = class {
|
|
|
859
884
|
videoBlob: artifacts.videoBlob,
|
|
860
885
|
screenshotBlob: options.screenshotBlob ?? artifacts.screenshotBlob,
|
|
861
886
|
networkLogs: artifacts.networkLogs,
|
|
887
|
+
consoleLogs: options.consoleLogs ?? [],
|
|
888
|
+
jsErrors: options.jsErrors ?? [],
|
|
862
889
|
captureMode: artifacts.captureMode,
|
|
863
890
|
pageUrl: typeof window !== "undefined" ? window.location.href : "",
|
|
864
891
|
userAgent: typeof navigator !== "undefined" ? navigator.userAgent : "",
|
|
@@ -898,6 +925,124 @@ var BugReporter = class {
|
|
|
898
925
|
}
|
|
899
926
|
};
|
|
900
927
|
|
|
928
|
+
// src/core/ConsoleCapture.ts
|
|
929
|
+
var MAX_CONSOLE_ENTRIES = 200;
|
|
930
|
+
var MAX_ERROR_ENTRIES = 50;
|
|
931
|
+
var MAX_ARG_LENGTH = 1e3;
|
|
932
|
+
function serializeArg(arg) {
|
|
933
|
+
if (typeof arg === "string") {
|
|
934
|
+
return arg.length > MAX_ARG_LENGTH ? arg.slice(0, MAX_ARG_LENGTH) + "\u2026" : arg;
|
|
935
|
+
}
|
|
936
|
+
try {
|
|
937
|
+
const json = JSON.stringify(arg);
|
|
938
|
+
return json.length > MAX_ARG_LENGTH ? json.slice(0, MAX_ARG_LENGTH) + "\u2026" : json;
|
|
939
|
+
} catch {
|
|
940
|
+
return String(arg);
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
var ConsoleCapture = class {
|
|
944
|
+
constructor() {
|
|
945
|
+
this.consoleLogs = [];
|
|
946
|
+
this.errors = [];
|
|
947
|
+
this.originals = {};
|
|
948
|
+
this.errorHandler = null;
|
|
949
|
+
this.rejectionHandler = null;
|
|
950
|
+
this.active = false;
|
|
951
|
+
}
|
|
952
|
+
start() {
|
|
953
|
+
if (this.active || typeof window === "undefined") {
|
|
954
|
+
return;
|
|
955
|
+
}
|
|
956
|
+
this.active = true;
|
|
957
|
+
this.consoleLogs = [];
|
|
958
|
+
this.errors = [];
|
|
959
|
+
const levels = ["log", "info", "warn", "error"];
|
|
960
|
+
for (const level of levels) {
|
|
961
|
+
const original = console[level];
|
|
962
|
+
this.originals[level] = original;
|
|
963
|
+
const capture = this;
|
|
964
|
+
console[level] = (...args) => {
|
|
965
|
+
try {
|
|
966
|
+
capture.consoleLogs.push({
|
|
967
|
+
level,
|
|
968
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
969
|
+
args: args.map(serializeArg)
|
|
970
|
+
});
|
|
971
|
+
if (capture.consoleLogs.length > MAX_CONSOLE_ENTRIES) {
|
|
972
|
+
capture.consoleLogs.shift();
|
|
973
|
+
}
|
|
974
|
+
} catch {
|
|
975
|
+
}
|
|
976
|
+
original.apply(console, args);
|
|
977
|
+
};
|
|
978
|
+
}
|
|
979
|
+
this.errorHandler = (event) => {
|
|
980
|
+
try {
|
|
981
|
+
this.errors.push({
|
|
982
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
983
|
+
message: event.message || "Unknown error",
|
|
984
|
+
source: event.filename || void 0,
|
|
985
|
+
lineno: event.lineno || void 0,
|
|
986
|
+
colno: event.colno || void 0,
|
|
987
|
+
stack: event.error?.stack || void 0,
|
|
988
|
+
type: "error"
|
|
989
|
+
});
|
|
990
|
+
if (this.errors.length > MAX_ERROR_ENTRIES) {
|
|
991
|
+
this.errors.shift();
|
|
992
|
+
}
|
|
993
|
+
} catch {
|
|
994
|
+
}
|
|
995
|
+
};
|
|
996
|
+
window.addEventListener("error", this.errorHandler);
|
|
997
|
+
this.rejectionHandler = (event) => {
|
|
998
|
+
try {
|
|
999
|
+
const reason = event.reason;
|
|
1000
|
+
this.errors.push({
|
|
1001
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1002
|
+
message: reason instanceof Error ? reason.message : String(reason),
|
|
1003
|
+
stack: reason instanceof Error ? reason.stack || void 0 : void 0,
|
|
1004
|
+
type: "unhandledrejection"
|
|
1005
|
+
});
|
|
1006
|
+
if (this.errors.length > MAX_ERROR_ENTRIES) {
|
|
1007
|
+
this.errors.shift();
|
|
1008
|
+
}
|
|
1009
|
+
} catch {
|
|
1010
|
+
}
|
|
1011
|
+
};
|
|
1012
|
+
window.addEventListener("unhandledrejection", this.rejectionHandler);
|
|
1013
|
+
}
|
|
1014
|
+
snapshot() {
|
|
1015
|
+
return {
|
|
1016
|
+
consoleLogs: [...this.consoleLogs],
|
|
1017
|
+
jsErrors: [...this.errors]
|
|
1018
|
+
};
|
|
1019
|
+
}
|
|
1020
|
+
stop() {
|
|
1021
|
+
if (!this.active) {
|
|
1022
|
+
return;
|
|
1023
|
+
}
|
|
1024
|
+
for (const [level, original] of Object.entries(this.originals)) {
|
|
1025
|
+
if (original) {
|
|
1026
|
+
console[level] = original;
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
this.originals = {};
|
|
1030
|
+
if (this.errorHandler) {
|
|
1031
|
+
window.removeEventListener("error", this.errorHandler);
|
|
1032
|
+
this.errorHandler = null;
|
|
1033
|
+
}
|
|
1034
|
+
if (this.rejectionHandler) {
|
|
1035
|
+
window.removeEventListener("unhandledrejection", this.rejectionHandler);
|
|
1036
|
+
this.rejectionHandler = null;
|
|
1037
|
+
}
|
|
1038
|
+
this.active = false;
|
|
1039
|
+
}
|
|
1040
|
+
clear() {
|
|
1041
|
+
this.consoleLogs = [];
|
|
1042
|
+
this.errors = [];
|
|
1043
|
+
}
|
|
1044
|
+
};
|
|
1045
|
+
|
|
901
1046
|
// src/integrations/linear.ts
|
|
902
1047
|
var DEFAULT_GRAPHQL_ENDPOINT = "https://api.linear.app/graphql";
|
|
903
1048
|
var noop = () => {
|
|
@@ -979,12 +1124,22 @@ var LinearIntegration = class {
|
|
|
979
1124
|
const description = buildCleanDescription(payload, { screenshotUrl, recordingUrl });
|
|
980
1125
|
const issue = await this.createIssue(payload.title, description);
|
|
981
1126
|
progress("Attaching logs\u2026");
|
|
1127
|
+
const comments = [];
|
|
982
1128
|
const logsComment = "### Network Logs\n```text\n" + formatNetworkLogs(payload.networkLogs) + "\n```";
|
|
1129
|
+
comments.push(this.addComment(issue.id, logsComment));
|
|
1130
|
+
if (payload.jsErrors.length > 0 || payload.consoleLogs.length > 0) {
|
|
1131
|
+
const parts = [];
|
|
1132
|
+
if (payload.jsErrors.length > 0) {
|
|
1133
|
+
parts.push("### JavaScript Errors\n```text\n" + formatJsErrors(payload.jsErrors) + "\n```");
|
|
1134
|
+
}
|
|
1135
|
+
if (payload.consoleLogs.length > 0) {
|
|
1136
|
+
parts.push("### Console Output\n```text\n" + formatConsoleLogs(payload.consoleLogs) + "\n```");
|
|
1137
|
+
}
|
|
1138
|
+
comments.push(this.addComment(issue.id, parts.join("\n\n")));
|
|
1139
|
+
}
|
|
983
1140
|
const metadataComment = "### Client Metadata\n```json\n" + JSON.stringify(payload.metadata, null, 2) + "\n```";
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
this.addComment(issue.id, metadataComment)
|
|
987
|
-
]);
|
|
1141
|
+
comments.push(this.addComment(issue.id, metadataComment));
|
|
1142
|
+
await Promise.all(comments);
|
|
988
1143
|
progress("Done!");
|
|
989
1144
|
return {
|
|
990
1145
|
provider: this.provider,
|
|
@@ -1317,6 +1472,17 @@ var JiraIntegration = class {
|
|
|
1317
1472
|
}
|
|
1318
1473
|
const logsBlob = new Blob([formatNetworkLogs(payload.networkLogs)], { type: "text/plain" });
|
|
1319
1474
|
uploads.push(this.uploadAttachment(issue.key, logsBlob, "network-logs.txt", "text/plain"));
|
|
1475
|
+
if (payload.consoleLogs.length > 0 || payload.jsErrors.length > 0) {
|
|
1476
|
+
const consoleParts = [];
|
|
1477
|
+
if (payload.jsErrors.length > 0) {
|
|
1478
|
+
consoleParts.push("=== JavaScript Errors ===\n" + formatJsErrors(payload.jsErrors));
|
|
1479
|
+
}
|
|
1480
|
+
if (payload.consoleLogs.length > 0) {
|
|
1481
|
+
consoleParts.push("=== Console Output ===\n" + formatConsoleLogs(payload.consoleLogs));
|
|
1482
|
+
}
|
|
1483
|
+
const consoleBlob = new Blob([consoleParts.join("\n\n")], { type: "text/plain" });
|
|
1484
|
+
uploads.push(this.uploadAttachment(issue.key, consoleBlob, "console-logs.txt", "text/plain"));
|
|
1485
|
+
}
|
|
1320
1486
|
const metadataBlob = new Blob([JSON.stringify(payload.metadata, null, 2)], { type: "application/json" });
|
|
1321
1487
|
uploads.push(this.uploadAttachment(issue.key, metadataBlob, "client-metadata.json", "application/json"));
|
|
1322
1488
|
await Promise.all(uploads);
|
|
@@ -1617,6 +1783,16 @@ function BugReporterProvider({
|
|
|
1617
1783
|
imageHeight: 0
|
|
1618
1784
|
});
|
|
1619
1785
|
const reporterRef = react.useRef(null);
|
|
1786
|
+
const consoleCaptureRef = react.useRef(null);
|
|
1787
|
+
react.useEffect(() => {
|
|
1788
|
+
const capture = new ConsoleCapture();
|
|
1789
|
+
capture.start();
|
|
1790
|
+
consoleCaptureRef.current = capture;
|
|
1791
|
+
return () => {
|
|
1792
|
+
capture.stop();
|
|
1793
|
+
consoleCaptureRef.current = null;
|
|
1794
|
+
};
|
|
1795
|
+
}, []);
|
|
1620
1796
|
const availableProviders = react.useMemo(() => {
|
|
1621
1797
|
return ["linear", "jira"].filter((provider) => Boolean(integrations[provider]));
|
|
1622
1798
|
}, [integrations]);
|
|
@@ -1740,6 +1916,18 @@ function BugReporterProvider({
|
|
|
1740
1916
|
window.clearInterval(interval);
|
|
1741
1917
|
};
|
|
1742
1918
|
}, [isRecording]);
|
|
1919
|
+
react.useEffect(() => {
|
|
1920
|
+
if (!isRecording) {
|
|
1921
|
+
return;
|
|
1922
|
+
}
|
|
1923
|
+
const onBeforeUnload = (event) => {
|
|
1924
|
+
event.preventDefault();
|
|
1925
|
+
};
|
|
1926
|
+
window.addEventListener("beforeunload", onBeforeUnload);
|
|
1927
|
+
return () => {
|
|
1928
|
+
window.removeEventListener("beforeunload", onBeforeUnload);
|
|
1929
|
+
};
|
|
1930
|
+
}, [isRecording]);
|
|
1743
1931
|
react.useEffect(() => {
|
|
1744
1932
|
return () => {
|
|
1745
1933
|
setScreenshotPreviewUrl((current) => {
|
|
@@ -1963,10 +2151,16 @@ function BugReporterProvider({
|
|
|
1963
2151
|
highlights: screenshotAnnotation.highlights
|
|
1964
2152
|
} : void 0
|
|
1965
2153
|
};
|
|
2154
|
+
const { consoleLogs, jsErrors } = consoleCaptureRef.current?.snapshot() ?? {
|
|
2155
|
+
consoleLogs: [],
|
|
2156
|
+
jsErrors: []
|
|
2157
|
+
};
|
|
1966
2158
|
try {
|
|
1967
2159
|
const result = await reporter.submit(title, description, {
|
|
1968
2160
|
screenshotBlob: screenshotBlobForSubmit,
|
|
1969
2161
|
metadata,
|
|
2162
|
+
consoleLogs,
|
|
2163
|
+
jsErrors,
|
|
1970
2164
|
onProgress: setSubmissionProgress
|
|
1971
2165
|
});
|
|
1972
2166
|
setSuccess(`Submitted to ${getProviderLabel(result.provider)} (${result.issueKey}).`);
|
|
@@ -2189,7 +2383,8 @@ function FloatingBugButton() {
|
|
|
2189
2383
|
"div",
|
|
2190
2384
|
{
|
|
2191
2385
|
ref: containerRef,
|
|
2192
|
-
|
|
2386
|
+
style: { position: "fixed", bottom: "1rem", right: "1rem", zIndex: 1100 },
|
|
2387
|
+
className: "flex flex-col items-end",
|
|
2193
2388
|
"data-bug-reporter-ui": "true",
|
|
2194
2389
|
children: isRecording ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
2195
2390
|
/* @__PURE__ */ jsxRuntime.jsxs(Button, { className: "h-11 gap-2 rounded-full px-4 shadow-lg shadow-black/20", type: "button", variant: "destructive", onClick: () => void handleStopRecording(), children: [
|
|
@@ -2312,6 +2507,7 @@ function DialogOverlay({
|
|
|
2312
2507
|
radixUi.Dialog.Overlay,
|
|
2313
2508
|
{
|
|
2314
2509
|
"data-slot": "dialog-overlay",
|
|
2510
|
+
"data-bug-reporter-ui": "true",
|
|
2315
2511
|
className: cn("data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 bg-black/60 duration-100 supports-backdrop-filter:backdrop-blur-sm fixed inset-0 isolate z-50", className),
|
|
2316
2512
|
...props
|
|
2317
2513
|
}
|
|
@@ -2329,8 +2525,16 @@ function DialogContent({
|
|
|
2329
2525
|
radixUi.Dialog.Content,
|
|
2330
2526
|
{
|
|
2331
2527
|
"data-slot": "dialog-content",
|
|
2528
|
+
"data-bug-reporter-ui": "true",
|
|
2529
|
+
style: {
|
|
2530
|
+
position: "fixed",
|
|
2531
|
+
top: "50%",
|
|
2532
|
+
left: "50%",
|
|
2533
|
+
transform: "translate(-50%, -50%)",
|
|
2534
|
+
translate: "none"
|
|
2535
|
+
},
|
|
2332
2536
|
className: cn(
|
|
2333
|
-
"bg-white text-gray-900 data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 ring-gray-900/5 grid max-w-[calc(100%-2rem)] gap-6 rounded-4xl p-6 text-sm ring-1 shadow-xl duration-100 sm:max-w-md
|
|
2537
|
+
"bg-white text-gray-900 data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 ring-gray-900/5 grid max-w-[calc(100%-2rem)] gap-6 rounded-4xl p-6 text-sm ring-1 shadow-xl duration-100 sm:max-w-md z-50 w-full outline-none",
|
|
2334
2538
|
className
|
|
2335
2539
|
),
|
|
2336
2540
|
...props,
|
|
@@ -3030,6 +3234,7 @@ exports.BugReporter = BugReporter;
|
|
|
3030
3234
|
exports.BugReporterModal = BugReporterModal;
|
|
3031
3235
|
exports.BugReporterProvider = BugReporterProvider;
|
|
3032
3236
|
exports.BugSession = BugSession;
|
|
3237
|
+
exports.ConsoleCapture = ConsoleCapture;
|
|
3033
3238
|
exports.DEFAULT_MAX_RECORDING_MS = DEFAULT_MAX_RECORDING_MS;
|
|
3034
3239
|
exports.FloatingBugButton = FloatingBugButton;
|
|
3035
3240
|
exports.JiraIntegration = JiraIntegration;
|
|
@@ -3038,6 +3243,8 @@ exports.NetworkLogger = NetworkLogger;
|
|
|
3038
3243
|
exports.ScreenRecorder = ScreenRecorder;
|
|
3039
3244
|
exports.ScreenshotCapturer = ScreenshotCapturer;
|
|
3040
3245
|
exports.collectClientEnvironmentMetadata = collectClientEnvironmentMetadata;
|
|
3246
|
+
exports.formatConsoleLogs = formatConsoleLogs;
|
|
3247
|
+
exports.formatJsErrors = formatJsErrors;
|
|
3041
3248
|
exports.formatNetworkLogs = formatNetworkLogs;
|
|
3042
3249
|
exports.toErrorMessage = toErrorMessage;
|
|
3043
3250
|
exports.useBugReporter = useBugReporter;
|