quick-bug-reporter-react 1.3.2 → 1.5.0
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 +1 -0
- package/dist/index.cjs +329 -50
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +13 -2
- package/dist/index.d.ts +13 -2
- package/dist/index.js +329 -50
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +11 -9
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { NetworkLogger, BugSessionArtifacts, RecordingStopReason, ReportCaptureMode, BugReporterIntegration, BugClientMetadata, ConsoleLogEntry, CapturedJsError, SubmitProgressCallback, BugSubmitResult, BugTrackerProvider, BugReporterIntegrations, ScreenshotHighlightRegion } from '@quick-bug-reporter/core';
|
|
1
|
+
import { NetworkLogger, BugSessionArtifacts, RecordingStopReason, ReportCaptureMode, NetworkLogEntry, BugReporterIntegration, BugClientMetadata, ConsoleLogEntry, CapturedJsError, SubmitProgressCallback, BugSubmitResult, BugTrackerProvider, BugReporterIntegrations, ScreenshotHighlightRegion } from '@quick-bug-reporter/core';
|
|
2
2
|
export { BugClientMetadata, BugReportPayload, BugReporterIntegration, BugReporterIntegrations, BugSessionArtifacts, BugSubmitResult, BugTrackerProvider, CapturedJsError, CloudIntegration, CloudIntegrationOptions, ConsoleCapture, ConsoleLogEntry, DEFAULT_MAX_RECORDING_MS, JiraIntegration, JiraIntegrationOptions, LinearIntegration, LinearIntegrationOptions, NetworkLogEntry, NetworkLogger, RecordingStopReason, ReportCaptureMode, ScreenshotHighlightRegion, SubmitProgressCallback, formatConsoleLogs, formatJsErrors, formatNetworkLogs, toErrorMessage } from '@quick-bug-reporter/core';
|
|
3
3
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
4
4
|
import { ReactNode } from 'react';
|
|
@@ -62,6 +62,7 @@ declare class BugSession {
|
|
|
62
62
|
private autoStopTimeout;
|
|
63
63
|
private stopInFlight;
|
|
64
64
|
private lastArtifacts;
|
|
65
|
+
private screenshotLogsPendingSubmit;
|
|
65
66
|
constructor(options?: BugSessionOptions);
|
|
66
67
|
start(): Promise<void>;
|
|
67
68
|
captureScreenshot(region?: CaptureRegion): Promise<BugSessionArtifacts>;
|
|
@@ -71,6 +72,7 @@ declare class BugSession {
|
|
|
71
72
|
getMaxDurationMs(): number;
|
|
72
73
|
getLastArtifacts(): BugSessionArtifacts | null;
|
|
73
74
|
getLastCaptureMode(): ReportCaptureMode | null;
|
|
75
|
+
finalizeNetworkLogsForSubmit(captureMode: ReportCaptureMode): NetworkLogEntry[];
|
|
74
76
|
resetArtifacts(): void;
|
|
75
77
|
dispose(): Promise<void>;
|
|
76
78
|
private stopInternal;
|
|
@@ -90,6 +92,10 @@ type BugReporterSubmitOptions = {
|
|
|
90
92
|
consoleLogs?: ConsoleLogEntry[];
|
|
91
93
|
jsErrors?: CapturedJsError[];
|
|
92
94
|
onProgress?: SubmitProgressCallback;
|
|
95
|
+
stepsToReproduce?: string;
|
|
96
|
+
expectedResult?: string;
|
|
97
|
+
actualResult?: string;
|
|
98
|
+
additionalContext?: string;
|
|
93
99
|
};
|
|
94
100
|
declare class BugReporter {
|
|
95
101
|
private integration;
|
|
@@ -151,7 +157,12 @@ type BugReporterContextValue = {
|
|
|
151
157
|
screenshotHighlightCount: number;
|
|
152
158
|
updateScreenshotAnnotation: (annotation: ScreenshotAnnotationState) => void;
|
|
153
159
|
clearDraft: () => void;
|
|
154
|
-
submitReport: (title: string,
|
|
160
|
+
submitReport: (title: string, structuredFields: {
|
|
161
|
+
stepsToReproduce: string;
|
|
162
|
+
expectedResult: string;
|
|
163
|
+
actualResult: string;
|
|
164
|
+
additionalContext: string;
|
|
165
|
+
}) => Promise<BugSubmitResult | null>;
|
|
155
166
|
resetMessages: () => void;
|
|
156
167
|
};
|
|
157
168
|
declare function BugReporterProvider({ children, integrations, defaultProvider, maxDurationMs, }: BugReporterProviderProps): react_jsx_runtime.JSX.Element;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { NetworkLogger, BugSessionArtifacts, RecordingStopReason, ReportCaptureMode, BugReporterIntegration, BugClientMetadata, ConsoleLogEntry, CapturedJsError, SubmitProgressCallback, BugSubmitResult, BugTrackerProvider, BugReporterIntegrations, ScreenshotHighlightRegion } from '@quick-bug-reporter/core';
|
|
1
|
+
import { NetworkLogger, BugSessionArtifacts, RecordingStopReason, ReportCaptureMode, NetworkLogEntry, BugReporterIntegration, BugClientMetadata, ConsoleLogEntry, CapturedJsError, SubmitProgressCallback, BugSubmitResult, BugTrackerProvider, BugReporterIntegrations, ScreenshotHighlightRegion } from '@quick-bug-reporter/core';
|
|
2
2
|
export { BugClientMetadata, BugReportPayload, BugReporterIntegration, BugReporterIntegrations, BugSessionArtifacts, BugSubmitResult, BugTrackerProvider, CapturedJsError, CloudIntegration, CloudIntegrationOptions, ConsoleCapture, ConsoleLogEntry, DEFAULT_MAX_RECORDING_MS, JiraIntegration, JiraIntegrationOptions, LinearIntegration, LinearIntegrationOptions, NetworkLogEntry, NetworkLogger, RecordingStopReason, ReportCaptureMode, ScreenshotHighlightRegion, SubmitProgressCallback, formatConsoleLogs, formatJsErrors, formatNetworkLogs, toErrorMessage } from '@quick-bug-reporter/core';
|
|
3
3
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
4
4
|
import { ReactNode } from 'react';
|
|
@@ -62,6 +62,7 @@ declare class BugSession {
|
|
|
62
62
|
private autoStopTimeout;
|
|
63
63
|
private stopInFlight;
|
|
64
64
|
private lastArtifacts;
|
|
65
|
+
private screenshotLogsPendingSubmit;
|
|
65
66
|
constructor(options?: BugSessionOptions);
|
|
66
67
|
start(): Promise<void>;
|
|
67
68
|
captureScreenshot(region?: CaptureRegion): Promise<BugSessionArtifacts>;
|
|
@@ -71,6 +72,7 @@ declare class BugSession {
|
|
|
71
72
|
getMaxDurationMs(): number;
|
|
72
73
|
getLastArtifacts(): BugSessionArtifacts | null;
|
|
73
74
|
getLastCaptureMode(): ReportCaptureMode | null;
|
|
75
|
+
finalizeNetworkLogsForSubmit(captureMode: ReportCaptureMode): NetworkLogEntry[];
|
|
74
76
|
resetArtifacts(): void;
|
|
75
77
|
dispose(): Promise<void>;
|
|
76
78
|
private stopInternal;
|
|
@@ -90,6 +92,10 @@ type BugReporterSubmitOptions = {
|
|
|
90
92
|
consoleLogs?: ConsoleLogEntry[];
|
|
91
93
|
jsErrors?: CapturedJsError[];
|
|
92
94
|
onProgress?: SubmitProgressCallback;
|
|
95
|
+
stepsToReproduce?: string;
|
|
96
|
+
expectedResult?: string;
|
|
97
|
+
actualResult?: string;
|
|
98
|
+
additionalContext?: string;
|
|
93
99
|
};
|
|
94
100
|
declare class BugReporter {
|
|
95
101
|
private integration;
|
|
@@ -151,7 +157,12 @@ type BugReporterContextValue = {
|
|
|
151
157
|
screenshotHighlightCount: number;
|
|
152
158
|
updateScreenshotAnnotation: (annotation: ScreenshotAnnotationState) => void;
|
|
153
159
|
clearDraft: () => void;
|
|
154
|
-
submitReport: (title: string,
|
|
160
|
+
submitReport: (title: string, structuredFields: {
|
|
161
|
+
stepsToReproduce: string;
|
|
162
|
+
expectedResult: string;
|
|
163
|
+
actualResult: string;
|
|
164
|
+
additionalContext: string;
|
|
165
|
+
}) => Promise<BugSubmitResult | null>;
|
|
155
166
|
resetMessages: () => void;
|
|
156
167
|
};
|
|
157
168
|
declare function BugReporterProvider({ children, integrations, defaultProvider, maxDurationMs, }: BugReporterProviderProps): react_jsx_runtime.JSX.Element;
|
package/dist/index.js
CHANGED
|
@@ -876,42 +876,104 @@ var CloudIntegration = class {
|
|
|
876
876
|
throw new Error("CloudIntegration: projectKey is required.");
|
|
877
877
|
}
|
|
878
878
|
this.projectKey = options.projectKey;
|
|
879
|
-
this.endpoint = options.endpoint ?? "/api/ingest";
|
|
879
|
+
this.endpoint = options.ingestUrl ?? options.endpoint ?? "/api/ingest";
|
|
880
|
+
this.appVersion = options.appVersion;
|
|
881
|
+
this.environment = options.environment;
|
|
880
882
|
this.fetchFn = options.fetchImpl ?? fetch.bind(globalThis);
|
|
881
883
|
}
|
|
882
884
|
async submit(payload, onProgress = noop3) {
|
|
883
885
|
onProgress("Preparing report\u2026");
|
|
884
|
-
const ua = payload.userAgent ||
|
|
886
|
+
const ua = payload.userAgent || getRuntimeUserAgent();
|
|
885
887
|
const browserName = parseBrowserName(ua);
|
|
888
|
+
const browserVersion = parseBrowserVersion(ua);
|
|
886
889
|
const osName = parseOsName(ua);
|
|
887
|
-
const
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
890
|
+
const osVersion = parseOsVersion(ua, osName);
|
|
891
|
+
const fd = new FormData();
|
|
892
|
+
fd.set("project_key", this.projectKey);
|
|
893
|
+
fd.set("title", payload.title);
|
|
894
|
+
fd.set("description", payload.description || "");
|
|
895
|
+
if (payload.stepsToReproduce) fd.set("steps_to_reproduce", payload.stepsToReproduce);
|
|
896
|
+
if (payload.expectedResult) fd.set("expected_result", payload.expectedResult);
|
|
897
|
+
if (payload.actualResult) fd.set("actual_result", payload.actualResult);
|
|
898
|
+
if (payload.additionalContext) fd.set("additional_context", payload.additionalContext);
|
|
899
|
+
fd.set("provider", "cloud");
|
|
900
|
+
fd.set("capture_mode", payload.captureMode);
|
|
901
|
+
fd.set("has_screenshot", String(Boolean(payload.screenshotBlob)));
|
|
902
|
+
fd.set("has_video", String(Boolean(payload.videoBlob)));
|
|
903
|
+
fd.set("has_network_logs", String(payload.networkLogs.length > 0));
|
|
904
|
+
fd.set("has_console_logs", String(payload.consoleLogs.length > 0));
|
|
905
|
+
fd.set("js_error_count", String(payload.jsErrors.length));
|
|
906
|
+
fd.set("user_agent", ua);
|
|
907
|
+
fd.set("browser_name", browserName);
|
|
908
|
+
fd.set("browser_version", browserVersion ?? "");
|
|
909
|
+
fd.set("os_name", osName);
|
|
910
|
+
fd.set("os_version", osVersion ?? "");
|
|
911
|
+
fd.set("device_type", getDeviceType());
|
|
912
|
+
fd.set("screen_resolution", getScreenResolution());
|
|
913
|
+
fd.set("viewport", getViewport());
|
|
914
|
+
fd.set("color_scheme", payload.metadata.colorScheme !== "unknown" ? payload.metadata.colorScheme : "");
|
|
915
|
+
fd.set("locale", payload.metadata.locale ?? "");
|
|
916
|
+
fd.set("timezone", payload.metadata.timezone ?? "");
|
|
917
|
+
fd.set("connection_type", payload.metadata.connection?.effectiveType ?? "");
|
|
918
|
+
fd.set("page_url", payload.pageUrl || "");
|
|
919
|
+
fd.set("environment", this.environment ?? getEnvironment());
|
|
920
|
+
fd.set("app_version", this.appVersion ?? "");
|
|
921
|
+
fd.set("platform", payload.metadata.platform ?? "");
|
|
922
|
+
fd.set("duration_ms", String(payload.elapsedMs));
|
|
923
|
+
fd.set("stopped_at", payload.stoppedAt || "");
|
|
924
|
+
if (payload.metadata.mobile) {
|
|
925
|
+
fd.set("platform", payload.metadata.mobile.platform);
|
|
926
|
+
fd.set("device_model", payload.metadata.mobile.deviceModel ?? "");
|
|
927
|
+
fd.set("device_brand", payload.metadata.mobile.deviceBrand ?? "");
|
|
928
|
+
fd.set("os_version", payload.metadata.mobile.osVersion ?? "");
|
|
929
|
+
fd.set("app_build_number", payload.metadata.mobile.appBuildNumber ?? "");
|
|
930
|
+
fd.set("is_emulator", String(payload.metadata.mobile.isEmulator));
|
|
931
|
+
fd.set(
|
|
932
|
+
"battery_level",
|
|
933
|
+
payload.metadata.mobile.batteryLevel == null ? "" : String(payload.metadata.mobile.batteryLevel)
|
|
934
|
+
);
|
|
935
|
+
fd.set(
|
|
936
|
+
"free_storage_mb",
|
|
937
|
+
payload.metadata.mobile.freeStorageMb == null ? "" : String(payload.metadata.mobile.freeStorageMb)
|
|
938
|
+
);
|
|
939
|
+
fd.set("invocation_method", payload.metadata.mobile.invocationMethod);
|
|
940
|
+
}
|
|
941
|
+
if (payload.screenshotBlob) {
|
|
942
|
+
fd.append("screenshot", payload.screenshotBlob, "bug-screenshot.png");
|
|
943
|
+
}
|
|
944
|
+
if (payload.videoBlob) {
|
|
945
|
+
fd.append("video", payload.videoBlob, "bug-recording.webm");
|
|
946
|
+
}
|
|
947
|
+
fd.append(
|
|
948
|
+
"network_logs",
|
|
949
|
+
new Blob([formatNetworkLogs(payload.networkLogs)], { type: "text/plain" }),
|
|
950
|
+
"network-logs.txt"
|
|
951
|
+
);
|
|
952
|
+
const parts = [];
|
|
953
|
+
if (payload.jsErrors.length > 0) {
|
|
954
|
+
parts.push("=== JavaScript Errors ===\n" + formatJsErrors(payload.jsErrors));
|
|
955
|
+
} else {
|
|
956
|
+
parts.push("=== JavaScript Errors ===\nNo JavaScript errors captured.");
|
|
957
|
+
}
|
|
958
|
+
if (payload.consoleLogs.length > 0) {
|
|
959
|
+
parts.push("=== Console Output ===\n" + formatConsoleLogs(payload.consoleLogs));
|
|
960
|
+
} else {
|
|
961
|
+
parts.push("=== Console Output ===\nNo console output captured.");
|
|
962
|
+
}
|
|
963
|
+
fd.append(
|
|
964
|
+
"console_logs",
|
|
965
|
+
new Blob([parts.join("\n\n")], { type: "text/plain" }),
|
|
966
|
+
"console-logs.txt"
|
|
967
|
+
);
|
|
968
|
+
fd.append(
|
|
969
|
+
"metadata",
|
|
970
|
+
new Blob([JSON.stringify(payload.metadata, null, 2)], { type: "application/json" }),
|
|
971
|
+
"client-metadata.json"
|
|
972
|
+
);
|
|
910
973
|
onProgress("Sending report\u2026");
|
|
911
974
|
const response = await this.fetchFn(this.endpoint, {
|
|
912
975
|
method: "POST",
|
|
913
|
-
|
|
914
|
-
body: JSON.stringify(body)
|
|
976
|
+
body: fd
|
|
915
977
|
});
|
|
916
978
|
if (!response.ok) {
|
|
917
979
|
const errorBody = await response.json().catch(() => ({}));
|
|
@@ -920,12 +982,22 @@ var CloudIntegration = class {
|
|
|
920
982
|
}
|
|
921
983
|
const result = await response.json();
|
|
922
984
|
onProgress("Report submitted.");
|
|
985
|
+
const fwd = result.forwarding;
|
|
986
|
+
const externalKey = fwd?.key;
|
|
987
|
+
const externalUrl = fwd?.url;
|
|
988
|
+
const warnings = [];
|
|
989
|
+
if (result.forwarding_status === "queued") {
|
|
990
|
+
warnings.push("Tracker forwarding is running in the background.");
|
|
991
|
+
}
|
|
992
|
+
if (fwd?.error) {
|
|
993
|
+
warnings.push(`Forwarding: ${fwd.error}`);
|
|
994
|
+
}
|
|
923
995
|
return {
|
|
924
996
|
provider: "cloud",
|
|
925
997
|
issueId: result.id,
|
|
926
|
-
issueKey: `QB-${result.id.slice(0, 8)}`,
|
|
927
|
-
issueUrl: null,
|
|
928
|
-
warnings
|
|
998
|
+
issueKey: externalKey || `QB-${result.id.slice(0, 8)}`,
|
|
999
|
+
issueUrl: externalUrl || null,
|
|
1000
|
+
warnings
|
|
929
1001
|
};
|
|
930
1002
|
}
|
|
931
1003
|
};
|
|
@@ -937,14 +1009,50 @@ function parseBrowserName(ua) {
|
|
|
937
1009
|
if (ua.includes("Safari/") && !ua.includes("Chrome/")) return "Safari";
|
|
938
1010
|
return "Unknown";
|
|
939
1011
|
}
|
|
1012
|
+
function parseBrowserVersion(ua) {
|
|
1013
|
+
return matchVersion(ua, /Edg\/([\d.]+)/) || matchVersion(ua, /OPR\/([\d.]+)/) || matchVersion(ua, /Opera\/([\d.]+)/) || matchVersion(ua, /Chrome\/([\d.]+)/) || matchVersion(ua, /Firefox\/([\d.]+)/) || matchVersion(ua, /Version\/([\d.]+).*Safari/);
|
|
1014
|
+
}
|
|
1015
|
+
function getRuntimeUserAgent() {
|
|
1016
|
+
if (typeof navigator !== "undefined" && typeof navigator.userAgent === "string") {
|
|
1017
|
+
return navigator.userAgent;
|
|
1018
|
+
}
|
|
1019
|
+
return "";
|
|
1020
|
+
}
|
|
940
1021
|
function parseOsName(ua) {
|
|
941
1022
|
if (ua.includes("Windows")) return "Windows";
|
|
942
|
-
if (ua.includes("Mac OS")) return "macOS";
|
|
943
|
-
if (ua.includes("Linux")) return "Linux";
|
|
944
1023
|
if (ua.includes("Android")) return "Android";
|
|
945
1024
|
if (ua.includes("iPhone") || ua.includes("iPad")) return "iOS";
|
|
1025
|
+
if (ua.includes("Mac OS")) return "macOS";
|
|
1026
|
+
if (ua.includes("Linux")) return "Linux";
|
|
946
1027
|
return "Unknown";
|
|
947
1028
|
}
|
|
1029
|
+
function parseOsVersion(ua, osName) {
|
|
1030
|
+
if (osName === "Windows") {
|
|
1031
|
+
const nt = matchVersion(ua, /Windows NT ([\d.]+)/);
|
|
1032
|
+
if (!nt) return null;
|
|
1033
|
+
if (nt.startsWith("10.0")) return "10/11";
|
|
1034
|
+
if (nt.startsWith("6.3")) return "8.1";
|
|
1035
|
+
if (nt.startsWith("6.2")) return "8";
|
|
1036
|
+
if (nt.startsWith("6.1")) return "7";
|
|
1037
|
+
return nt;
|
|
1038
|
+
}
|
|
1039
|
+
if (osName === "macOS") {
|
|
1040
|
+
const mac = matchVersion(ua, /Mac OS X ([0-9_]+)/);
|
|
1041
|
+
return mac ? mac.replace(/_/g, ".") : null;
|
|
1042
|
+
}
|
|
1043
|
+
if (osName === "Android") {
|
|
1044
|
+
return matchVersion(ua, /Android ([\d.]+)/);
|
|
1045
|
+
}
|
|
1046
|
+
if (osName === "iOS") {
|
|
1047
|
+
const ios = matchVersion(ua, /OS ([0-9_]+) like Mac OS X/);
|
|
1048
|
+
return ios ? ios.replace(/_/g, ".") : null;
|
|
1049
|
+
}
|
|
1050
|
+
return null;
|
|
1051
|
+
}
|
|
1052
|
+
function matchVersion(ua, pattern) {
|
|
1053
|
+
const match = ua.match(pattern);
|
|
1054
|
+
return match?.[1] ?? null;
|
|
1055
|
+
}
|
|
948
1056
|
function getDeviceType() {
|
|
949
1057
|
if (typeof window === "undefined") return "unknown";
|
|
950
1058
|
const w = window.innerWidth;
|
|
@@ -1423,6 +1531,7 @@ var BugSession = class {
|
|
|
1423
1531
|
this.autoStopTimeout = null;
|
|
1424
1532
|
this.stopInFlight = null;
|
|
1425
1533
|
this.lastArtifacts = null;
|
|
1534
|
+
this.screenshotLogsPendingSubmit = false;
|
|
1426
1535
|
this.maxDurationMs = options.maxDurationMs ?? DEFAULT_MAX_RECORDING_MS;
|
|
1427
1536
|
this.screenRecorder = options.screenRecorder ?? new ScreenRecorder();
|
|
1428
1537
|
this.screenshotCapturer = options.screenshotCapturer ?? new ScreenshotCapturer();
|
|
@@ -1433,9 +1542,13 @@ var BugSession = class {
|
|
|
1433
1542
|
if (this.recording) {
|
|
1434
1543
|
return;
|
|
1435
1544
|
}
|
|
1545
|
+
if (this.networkLogger.isRecording()) {
|
|
1546
|
+
this.networkLogger.stop();
|
|
1547
|
+
}
|
|
1436
1548
|
this.clearAutoStopTimer();
|
|
1437
1549
|
this.networkLogger.clear();
|
|
1438
1550
|
this.lastArtifacts = null;
|
|
1551
|
+
this.screenshotLogsPendingSubmit = false;
|
|
1439
1552
|
this.networkLogger.start();
|
|
1440
1553
|
try {
|
|
1441
1554
|
await this.screenRecorder.start({
|
|
@@ -1458,14 +1571,18 @@ var BugSession = class {
|
|
|
1458
1571
|
if (this.recording) {
|
|
1459
1572
|
await this.stop("manual");
|
|
1460
1573
|
}
|
|
1574
|
+
if (this.networkLogger.isRecording()) {
|
|
1575
|
+
this.networkLogger.stop();
|
|
1576
|
+
}
|
|
1461
1577
|
this.clearAutoStopTimer();
|
|
1462
1578
|
this.networkLogger.clear();
|
|
1463
1579
|
this.lastArtifacts = null;
|
|
1580
|
+
this.screenshotLogsPendingSubmit = false;
|
|
1464
1581
|
const startedAtMs = Date.now();
|
|
1465
1582
|
this.networkLogger.start();
|
|
1466
1583
|
try {
|
|
1467
1584
|
const screenshotBlob = region ? await this.screenshotCapturer.captureRegion(region) : await this.screenshotCapturer.capture();
|
|
1468
|
-
const networkLogs = this.networkLogger.
|
|
1585
|
+
const networkLogs = this.networkLogger.getLogs();
|
|
1469
1586
|
const stoppedAtMs = Date.now();
|
|
1470
1587
|
const artifacts = {
|
|
1471
1588
|
videoBlob: null,
|
|
@@ -1478,10 +1595,12 @@ var BugSession = class {
|
|
|
1478
1595
|
stopReason: "manual"
|
|
1479
1596
|
};
|
|
1480
1597
|
this.lastArtifacts = artifacts;
|
|
1598
|
+
this.screenshotLogsPendingSubmit = true;
|
|
1481
1599
|
return artifacts;
|
|
1482
1600
|
} catch (error) {
|
|
1483
1601
|
this.networkLogger.stop();
|
|
1484
1602
|
this.networkLogger.clear();
|
|
1603
|
+
this.screenshotLogsPendingSubmit = false;
|
|
1485
1604
|
throw error;
|
|
1486
1605
|
}
|
|
1487
1606
|
}
|
|
@@ -1515,17 +1634,32 @@ var BugSession = class {
|
|
|
1515
1634
|
getLastCaptureMode() {
|
|
1516
1635
|
return this.lastArtifacts?.captureMode ?? null;
|
|
1517
1636
|
}
|
|
1637
|
+
finalizeNetworkLogsForSubmit(captureMode) {
|
|
1638
|
+
if (captureMode === "screenshot" && this.screenshotLogsPendingSubmit && this.networkLogger.isRecording()) {
|
|
1639
|
+
const logs = this.networkLogger.stop();
|
|
1640
|
+
this.screenshotLogsPendingSubmit = false;
|
|
1641
|
+
return logs;
|
|
1642
|
+
}
|
|
1643
|
+
return this.networkLogger.getLogs();
|
|
1644
|
+
}
|
|
1518
1645
|
resetArtifacts() {
|
|
1519
1646
|
this.lastArtifacts = null;
|
|
1520
1647
|
this.screenRecorder.clearLastBlob();
|
|
1648
|
+
if (this.networkLogger.isRecording()) {
|
|
1649
|
+
this.networkLogger.stop();
|
|
1650
|
+
}
|
|
1521
1651
|
this.networkLogger.clear();
|
|
1652
|
+
this.screenshotLogsPendingSubmit = false;
|
|
1522
1653
|
}
|
|
1523
1654
|
async dispose() {
|
|
1524
1655
|
await this.stop("manual");
|
|
1525
1656
|
this.clearAutoStopTimer();
|
|
1526
1657
|
this.screenRecorder.dispose();
|
|
1527
|
-
this.networkLogger.
|
|
1658
|
+
if (this.networkLogger.isRecording()) {
|
|
1659
|
+
this.networkLogger.stop();
|
|
1660
|
+
}
|
|
1528
1661
|
this.networkLogger.clear();
|
|
1662
|
+
this.screenshotLogsPendingSubmit = false;
|
|
1529
1663
|
}
|
|
1530
1664
|
async stopInternal(reason) {
|
|
1531
1665
|
this.clearAutoStopTimer();
|
|
@@ -1546,6 +1680,7 @@ var BugSession = class {
|
|
|
1546
1680
|
stopReason: reason
|
|
1547
1681
|
};
|
|
1548
1682
|
this.lastArtifacts = artifacts;
|
|
1683
|
+
this.screenshotLogsPendingSubmit = false;
|
|
1549
1684
|
return artifacts;
|
|
1550
1685
|
}
|
|
1551
1686
|
async handleForcedStop(reason) {
|
|
@@ -1696,9 +1831,13 @@ var BugReporter = class {
|
|
|
1696
1831
|
const payload = {
|
|
1697
1832
|
title: normalizedTitle,
|
|
1698
1833
|
description: normalizedDescription,
|
|
1834
|
+
stepsToReproduce: options.stepsToReproduce,
|
|
1835
|
+
expectedResult: options.expectedResult,
|
|
1836
|
+
actualResult: options.actualResult,
|
|
1837
|
+
additionalContext: options.additionalContext,
|
|
1699
1838
|
videoBlob: artifacts.videoBlob,
|
|
1700
1839
|
screenshotBlob: options.screenshotBlob ?? artifacts.screenshotBlob,
|
|
1701
|
-
networkLogs: artifacts.
|
|
1840
|
+
networkLogs: this.session.finalizeNetworkLogsForSubmit(artifacts.captureMode),
|
|
1702
1841
|
consoleLogs: options.consoleLogs ?? [],
|
|
1703
1842
|
jsErrors: options.jsErrors ?? [],
|
|
1704
1843
|
captureMode: artifacts.captureMode,
|
|
@@ -2228,7 +2367,7 @@ function BugReporterProvider({
|
|
|
2228
2367
|
setScreenshotAnnotation(annotation);
|
|
2229
2368
|
}, []);
|
|
2230
2369
|
const submitReport = useCallback(
|
|
2231
|
-
async (title,
|
|
2370
|
+
async (title, structuredFields) => {
|
|
2232
2371
|
const reporter = getOrCreateReporter();
|
|
2233
2372
|
if (!reporter) {
|
|
2234
2373
|
setError("No bug tracker integration is configured.");
|
|
@@ -2248,6 +2387,25 @@ function BugReporterProvider({
|
|
|
2248
2387
|
setSubmissionProgress("Preparing submission\u2026");
|
|
2249
2388
|
setError(null);
|
|
2250
2389
|
setSuccess(null);
|
|
2390
|
+
const { stepsToReproduce, expectedResult, actualResult, additionalContext } = structuredFields;
|
|
2391
|
+
const sections = [];
|
|
2392
|
+
if (stepsToReproduce.trim()) {
|
|
2393
|
+
sections.push(`## Steps to Reproduce
|
|
2394
|
+
${stepsToReproduce.trim()}`);
|
|
2395
|
+
}
|
|
2396
|
+
if (expectedResult.trim()) {
|
|
2397
|
+
sections.push(`## Expected Result
|
|
2398
|
+
${expectedResult.trim()}`);
|
|
2399
|
+
}
|
|
2400
|
+
if (actualResult.trim()) {
|
|
2401
|
+
sections.push(`## Actual Result
|
|
2402
|
+
${actualResult.trim()}`);
|
|
2403
|
+
}
|
|
2404
|
+
if (additionalContext.trim()) {
|
|
2405
|
+
sections.push(`## Additional Context
|
|
2406
|
+
${additionalContext.trim()}`);
|
|
2407
|
+
}
|
|
2408
|
+
const description = sections.length > 0 ? sections.join("\n\n") : "No description provided";
|
|
2251
2409
|
const screenshotBlobForSubmit = draftMode === "screenshot" ? screenshotAnnotation.annotatedBlob ?? screenshotBlob : null;
|
|
2252
2410
|
const metadata = {
|
|
2253
2411
|
annotation: draftMode === "screenshot" && screenshotAnnotation.highlights.length > 0 ? {
|
|
@@ -2262,13 +2420,21 @@ function BugReporterProvider({
|
|
|
2262
2420
|
};
|
|
2263
2421
|
try {
|
|
2264
2422
|
const result = await reporter.submit(title, description, {
|
|
2423
|
+
stepsToReproduce,
|
|
2424
|
+
expectedResult,
|
|
2425
|
+
actualResult,
|
|
2426
|
+
additionalContext,
|
|
2265
2427
|
screenshotBlob: screenshotBlobForSubmit,
|
|
2266
2428
|
metadata,
|
|
2267
2429
|
consoleLogs,
|
|
2268
2430
|
jsErrors,
|
|
2269
2431
|
onProgress: setSubmissionProgress
|
|
2270
2432
|
});
|
|
2271
|
-
|
|
2433
|
+
if (result.provider === "cloud" && !result.issueUrl) {
|
|
2434
|
+
setSuccess("Report received by QuickBugs Cloud. Tracker forwarding is running in the background.");
|
|
2435
|
+
} else {
|
|
2436
|
+
setSuccess(`Submitted to ${getProviderLabel(result.provider)} (${result.issueKey}).`);
|
|
2437
|
+
}
|
|
2272
2438
|
clearDraft();
|
|
2273
2439
|
setIsOpen(false);
|
|
2274
2440
|
return result;
|
|
@@ -3055,6 +3221,7 @@ function providerLabel(provider) {
|
|
|
3055
3221
|
if (provider === "cloud") return "QuickBugs Cloud";
|
|
3056
3222
|
return provider;
|
|
3057
3223
|
}
|
|
3224
|
+
var CHAR_LIMIT = 4e3;
|
|
3058
3225
|
function BugReporterModal() {
|
|
3059
3226
|
const {
|
|
3060
3227
|
autoStopNotice,
|
|
@@ -3084,9 +3251,39 @@ function BugReporterModal() {
|
|
|
3084
3251
|
videoPreviewUrl
|
|
3085
3252
|
} = useBugReporter();
|
|
3086
3253
|
const [title, setTitle] = useState("");
|
|
3087
|
-
const [
|
|
3254
|
+
const [stepsToReproduce, setStepsToReproduce] = useState("");
|
|
3255
|
+
const [expectedResult, setExpectedResult] = useState("");
|
|
3256
|
+
const [actualResult, setActualResult] = useState("");
|
|
3257
|
+
const [additionalContext, setAdditionalContext] = useState("");
|
|
3088
3258
|
const [step, setStep] = useState("review");
|
|
3259
|
+
const [activeTab, setActiveTab] = useState("steps");
|
|
3260
|
+
const totalChars = stepsToReproduce.length + expectedResult.length + actualResult.length + additionalContext.length;
|
|
3261
|
+
const isOverLimit = totalChars > CHAR_LIMIT;
|
|
3089
3262
|
const elapsedLabel = useMemo(() => formatElapsed2(elapsedMs), [elapsedMs]);
|
|
3263
|
+
const handleStepsKeyDown = (event) => {
|
|
3264
|
+
if (event.key === "Enter" && !event.shiftKey) {
|
|
3265
|
+
event.preventDefault();
|
|
3266
|
+
const textarea = event.currentTarget;
|
|
3267
|
+
const cursorPos = textarea.selectionStart;
|
|
3268
|
+
const textBeforeCursor = stepsToReproduce.substring(0, cursorPos);
|
|
3269
|
+
const textAfterCursor = stepsToReproduce.substring(cursorPos);
|
|
3270
|
+
const lines = textBeforeCursor.split("\n");
|
|
3271
|
+
const currentLine = lines[lines.length - 1];
|
|
3272
|
+
const numberMatch = currentLine.match(/^(\d+)\.\s/);
|
|
3273
|
+
const nextNumber = numberMatch ? parseInt(numberMatch[1]) + 1 : lines.length > 0 && stepsToReproduce.trim() === "" ? 1 : lines.length + 1;
|
|
3274
|
+
const newText = textBeforeCursor + "\n" + nextNumber + ". " + textAfterCursor;
|
|
3275
|
+
setStepsToReproduce(newText);
|
|
3276
|
+
setTimeout(() => {
|
|
3277
|
+
const newCursorPos = cursorPos + ("\n" + nextNumber + ". ").length;
|
|
3278
|
+
textarea.setSelectionRange(newCursorPos, newCursorPos);
|
|
3279
|
+
}, 0);
|
|
3280
|
+
}
|
|
3281
|
+
};
|
|
3282
|
+
const handleStepsFocus = () => {
|
|
3283
|
+
if (stepsToReproduce.trim() === "") {
|
|
3284
|
+
setStepsToReproduce("1. ");
|
|
3285
|
+
}
|
|
3286
|
+
};
|
|
3090
3287
|
const handleDialogOpenChange = (open) => {
|
|
3091
3288
|
if (open) {
|
|
3092
3289
|
openModal();
|
|
@@ -3097,15 +3294,24 @@ function BugReporterModal() {
|
|
|
3097
3294
|
};
|
|
3098
3295
|
const handleSubmit = async (event) => {
|
|
3099
3296
|
event.preventDefault();
|
|
3100
|
-
const result = await submitReport(title,
|
|
3297
|
+
const result = await submitReport(title, {
|
|
3298
|
+
stepsToReproduce,
|
|
3299
|
+
expectedResult,
|
|
3300
|
+
actualResult,
|
|
3301
|
+
additionalContext
|
|
3302
|
+
});
|
|
3101
3303
|
if (result) {
|
|
3102
3304
|
setTitle("");
|
|
3103
|
-
|
|
3305
|
+
setStepsToReproduce("");
|
|
3306
|
+
setExpectedResult("");
|
|
3307
|
+
setActualResult("");
|
|
3308
|
+
setAdditionalContext("");
|
|
3104
3309
|
setStep("review");
|
|
3310
|
+
setActiveTab("steps");
|
|
3105
3311
|
}
|
|
3106
3312
|
};
|
|
3107
3313
|
const hasIntegrations = availableProviders.length > 0;
|
|
3108
|
-
const canSubmit = !isSubmitting && !isCapturingScreenshot && hasIntegrations && !!selectedProvider && hasDraft && title.trim().length > 0;
|
|
3314
|
+
const canSubmit = !isSubmitting && !isCapturingScreenshot && hasIntegrations && !!selectedProvider && hasDraft && title.trim().length > 0 && !isOverLimit;
|
|
3109
3315
|
return /* @__PURE__ */ jsx(Dialog, { open: isOpen, onOpenChange: handleDialogOpenChange, children: /* @__PURE__ */ jsx(
|
|
3110
3316
|
DialogContent,
|
|
3111
3317
|
{
|
|
@@ -3271,18 +3477,91 @@ function BugReporterModal() {
|
|
|
3271
3477
|
}
|
|
3272
3478
|
)
|
|
3273
3479
|
] }),
|
|
3274
|
-
/* @__PURE__ */ jsxs("div", { className: "space-y-
|
|
3275
|
-
/* @__PURE__ */ jsx("label", { className: "text-sm font-medium",
|
|
3276
|
-
/* @__PURE__ */
|
|
3480
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2 sm:col-span-2", children: [
|
|
3481
|
+
/* @__PURE__ */ jsx("label", { className: "text-sm font-medium", children: "Bug Details" }),
|
|
3482
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-1 border-b border-gray-200", children: [
|
|
3483
|
+
/* @__PURE__ */ jsx(
|
|
3484
|
+
"button",
|
|
3485
|
+
{
|
|
3486
|
+
type: "button",
|
|
3487
|
+
className: `px-3 py-2 text-sm font-medium transition-colors ${activeTab === "steps" ? "border-b-2 border-indigo-600 text-indigo-600" : "text-gray-500 hover:text-gray-700"}`,
|
|
3488
|
+
onClick: () => setActiveTab("steps"),
|
|
3489
|
+
children: "Steps"
|
|
3490
|
+
}
|
|
3491
|
+
),
|
|
3492
|
+
/* @__PURE__ */ jsx(
|
|
3493
|
+
"button",
|
|
3494
|
+
{
|
|
3495
|
+
type: "button",
|
|
3496
|
+
className: `px-3 py-2 text-sm font-medium transition-colors ${activeTab === "expected" ? "border-b-2 border-indigo-600 text-indigo-600" : "text-gray-500 hover:text-gray-700"}`,
|
|
3497
|
+
onClick: () => setActiveTab("expected"),
|
|
3498
|
+
children: "Expected"
|
|
3499
|
+
}
|
|
3500
|
+
),
|
|
3501
|
+
/* @__PURE__ */ jsx(
|
|
3502
|
+
"button",
|
|
3503
|
+
{
|
|
3504
|
+
type: "button",
|
|
3505
|
+
className: `px-3 py-2 text-sm font-medium transition-colors ${activeTab === "actual" ? "border-b-2 border-indigo-600 text-indigo-600" : "text-gray-500 hover:text-gray-700"}`,
|
|
3506
|
+
onClick: () => setActiveTab("actual"),
|
|
3507
|
+
children: "Actual"
|
|
3508
|
+
}
|
|
3509
|
+
),
|
|
3510
|
+
/* @__PURE__ */ jsx(
|
|
3511
|
+
"button",
|
|
3512
|
+
{
|
|
3513
|
+
type: "button",
|
|
3514
|
+
className: `px-3 py-2 text-sm font-medium transition-colors ${activeTab === "context" ? "border-b-2 border-indigo-600 text-indigo-600" : "text-gray-500 hover:text-gray-700"}`,
|
|
3515
|
+
onClick: () => setActiveTab("context"),
|
|
3516
|
+
children: "Context"
|
|
3517
|
+
}
|
|
3518
|
+
)
|
|
3519
|
+
] }),
|
|
3520
|
+
activeTab === "steps" && /* @__PURE__ */ jsx(
|
|
3277
3521
|
Textarea,
|
|
3278
3522
|
{
|
|
3279
|
-
id: "bug-
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3523
|
+
id: "bug-steps",
|
|
3524
|
+
placeholder: "Press Enter to start numbering steps...",
|
|
3525
|
+
value: stepsToReproduce,
|
|
3526
|
+
onChange: (event) => setStepsToReproduce(event.target.value),
|
|
3527
|
+
onKeyDown: handleStepsKeyDown,
|
|
3528
|
+
onFocus: handleStepsFocus
|
|
3284
3529
|
}
|
|
3285
|
-
)
|
|
3530
|
+
),
|
|
3531
|
+
activeTab === "expected" && /* @__PURE__ */ jsx(
|
|
3532
|
+
Textarea,
|
|
3533
|
+
{
|
|
3534
|
+
id: "bug-expected",
|
|
3535
|
+
placeholder: "Describe what should happen...",
|
|
3536
|
+
value: expectedResult,
|
|
3537
|
+
onChange: (event) => setExpectedResult(event.target.value)
|
|
3538
|
+
}
|
|
3539
|
+
),
|
|
3540
|
+
activeTab === "actual" && /* @__PURE__ */ jsx(
|
|
3541
|
+
Textarea,
|
|
3542
|
+
{
|
|
3543
|
+
id: "bug-actual",
|
|
3544
|
+
placeholder: "Describe what actually happened...",
|
|
3545
|
+
value: actualResult,
|
|
3546
|
+
onChange: (event) => setActualResult(event.target.value)
|
|
3547
|
+
}
|
|
3548
|
+
),
|
|
3549
|
+
activeTab === "context" && /* @__PURE__ */ jsx(
|
|
3550
|
+
Textarea,
|
|
3551
|
+
{
|
|
3552
|
+
id: "bug-context",
|
|
3553
|
+
placeholder: "Any additional information, workarounds, or notes...",
|
|
3554
|
+
value: additionalContext,
|
|
3555
|
+
onChange: (event) => setAdditionalContext(event.target.value)
|
|
3556
|
+
}
|
|
3557
|
+
),
|
|
3558
|
+
/* @__PURE__ */ jsxs("p", { className: `text-xs ${isOverLimit ? "text-red-600 font-medium" : "text-gray-500"}`, children: [
|
|
3559
|
+
totalChars,
|
|
3560
|
+
"/",
|
|
3561
|
+
CHAR_LIMIT,
|
|
3562
|
+
" characters ",
|
|
3563
|
+
isOverLimit && "(over limit)"
|
|
3564
|
+
] })
|
|
3286
3565
|
] }),
|
|
3287
3566
|
/* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
3288
3567
|
/* @__PURE__ */ jsx("label", { className: "text-sm font-medium", htmlFor: "bug-provider", children: "Submit to" }),
|