quick-bug-reporter-react 1.4.0 → 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 +158 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +10 -1
- package/dist/index.d.ts +10 -1
- package/dist/index.js +158 -14
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -7,6 +7,7 @@ Drop-in bug reporter for React apps — screenshot capture, video recording, ann
|
|
|
7
7
|
- **Screenshot capture** — Full-page or region selection via `html2canvas-pro`
|
|
8
8
|
- **Video recording** — Screen + microphone via `MediaRecorder` API
|
|
9
9
|
- **Annotation** — Drag-to-highlight on captured screenshots
|
|
10
|
+
- **Structured bug reports** — Tab-based UI for Steps/Expected/Actual/Context (4000 char total)
|
|
10
11
|
- **Network logging** — Automatic fetch interception during capture
|
|
11
12
|
- **Console capture** — Automatic console log and JS error capture
|
|
12
13
|
- **Integrations** — Linear and Jira (direct API or backend proxy)
|
package/dist/index.cjs
CHANGED
|
@@ -898,6 +898,10 @@ var CloudIntegration = class {
|
|
|
898
898
|
fd.set("project_key", this.projectKey);
|
|
899
899
|
fd.set("title", payload.title);
|
|
900
900
|
fd.set("description", payload.description || "");
|
|
901
|
+
if (payload.stepsToReproduce) fd.set("steps_to_reproduce", payload.stepsToReproduce);
|
|
902
|
+
if (payload.expectedResult) fd.set("expected_result", payload.expectedResult);
|
|
903
|
+
if (payload.actualResult) fd.set("actual_result", payload.actualResult);
|
|
904
|
+
if (payload.additionalContext) fd.set("additional_context", payload.additionalContext);
|
|
901
905
|
fd.set("provider", "cloud");
|
|
902
906
|
fd.set("capture_mode", payload.captureMode);
|
|
903
907
|
fd.set("has_screenshot", String(Boolean(payload.screenshotBlob)));
|
|
@@ -1833,6 +1837,10 @@ var BugReporter = class {
|
|
|
1833
1837
|
const payload = {
|
|
1834
1838
|
title: normalizedTitle,
|
|
1835
1839
|
description: normalizedDescription,
|
|
1840
|
+
stepsToReproduce: options.stepsToReproduce,
|
|
1841
|
+
expectedResult: options.expectedResult,
|
|
1842
|
+
actualResult: options.actualResult,
|
|
1843
|
+
additionalContext: options.additionalContext,
|
|
1836
1844
|
videoBlob: artifacts.videoBlob,
|
|
1837
1845
|
screenshotBlob: options.screenshotBlob ?? artifacts.screenshotBlob,
|
|
1838
1846
|
networkLogs: this.session.finalizeNetworkLogsForSubmit(artifacts.captureMode),
|
|
@@ -2365,7 +2373,7 @@ function BugReporterProvider({
|
|
|
2365
2373
|
setScreenshotAnnotation(annotation);
|
|
2366
2374
|
}, []);
|
|
2367
2375
|
const submitReport = react.useCallback(
|
|
2368
|
-
async (title,
|
|
2376
|
+
async (title, structuredFields) => {
|
|
2369
2377
|
const reporter = getOrCreateReporter();
|
|
2370
2378
|
if (!reporter) {
|
|
2371
2379
|
setError("No bug tracker integration is configured.");
|
|
@@ -2385,6 +2393,25 @@ function BugReporterProvider({
|
|
|
2385
2393
|
setSubmissionProgress("Preparing submission\u2026");
|
|
2386
2394
|
setError(null);
|
|
2387
2395
|
setSuccess(null);
|
|
2396
|
+
const { stepsToReproduce, expectedResult, actualResult, additionalContext } = structuredFields;
|
|
2397
|
+
const sections = [];
|
|
2398
|
+
if (stepsToReproduce.trim()) {
|
|
2399
|
+
sections.push(`## Steps to Reproduce
|
|
2400
|
+
${stepsToReproduce.trim()}`);
|
|
2401
|
+
}
|
|
2402
|
+
if (expectedResult.trim()) {
|
|
2403
|
+
sections.push(`## Expected Result
|
|
2404
|
+
${expectedResult.trim()}`);
|
|
2405
|
+
}
|
|
2406
|
+
if (actualResult.trim()) {
|
|
2407
|
+
sections.push(`## Actual Result
|
|
2408
|
+
${actualResult.trim()}`);
|
|
2409
|
+
}
|
|
2410
|
+
if (additionalContext.trim()) {
|
|
2411
|
+
sections.push(`## Additional Context
|
|
2412
|
+
${additionalContext.trim()}`);
|
|
2413
|
+
}
|
|
2414
|
+
const description = sections.length > 0 ? sections.join("\n\n") : "No description provided";
|
|
2388
2415
|
const screenshotBlobForSubmit = draftMode === "screenshot" ? screenshotAnnotation.annotatedBlob ?? screenshotBlob : null;
|
|
2389
2416
|
const metadata = {
|
|
2390
2417
|
annotation: draftMode === "screenshot" && screenshotAnnotation.highlights.length > 0 ? {
|
|
@@ -2399,6 +2426,10 @@ function BugReporterProvider({
|
|
|
2399
2426
|
};
|
|
2400
2427
|
try {
|
|
2401
2428
|
const result = await reporter.submit(title, description, {
|
|
2429
|
+
stepsToReproduce,
|
|
2430
|
+
expectedResult,
|
|
2431
|
+
actualResult,
|
|
2432
|
+
additionalContext,
|
|
2402
2433
|
screenshotBlob: screenshotBlobForSubmit,
|
|
2403
2434
|
metadata,
|
|
2404
2435
|
consoleLogs,
|
|
@@ -3196,6 +3227,7 @@ function providerLabel(provider) {
|
|
|
3196
3227
|
if (provider === "cloud") return "QuickBugs Cloud";
|
|
3197
3228
|
return provider;
|
|
3198
3229
|
}
|
|
3230
|
+
var CHAR_LIMIT = 4e3;
|
|
3199
3231
|
function BugReporterModal() {
|
|
3200
3232
|
const {
|
|
3201
3233
|
autoStopNotice,
|
|
@@ -3225,9 +3257,39 @@ function BugReporterModal() {
|
|
|
3225
3257
|
videoPreviewUrl
|
|
3226
3258
|
} = useBugReporter();
|
|
3227
3259
|
const [title, setTitle] = react.useState("");
|
|
3228
|
-
const [
|
|
3260
|
+
const [stepsToReproduce, setStepsToReproduce] = react.useState("");
|
|
3261
|
+
const [expectedResult, setExpectedResult] = react.useState("");
|
|
3262
|
+
const [actualResult, setActualResult] = react.useState("");
|
|
3263
|
+
const [additionalContext, setAdditionalContext] = react.useState("");
|
|
3229
3264
|
const [step, setStep] = react.useState("review");
|
|
3265
|
+
const [activeTab, setActiveTab] = react.useState("steps");
|
|
3266
|
+
const totalChars = stepsToReproduce.length + expectedResult.length + actualResult.length + additionalContext.length;
|
|
3267
|
+
const isOverLimit = totalChars > CHAR_LIMIT;
|
|
3230
3268
|
const elapsedLabel = react.useMemo(() => formatElapsed2(elapsedMs), [elapsedMs]);
|
|
3269
|
+
const handleStepsKeyDown = (event) => {
|
|
3270
|
+
if (event.key === "Enter" && !event.shiftKey) {
|
|
3271
|
+
event.preventDefault();
|
|
3272
|
+
const textarea = event.currentTarget;
|
|
3273
|
+
const cursorPos = textarea.selectionStart;
|
|
3274
|
+
const textBeforeCursor = stepsToReproduce.substring(0, cursorPos);
|
|
3275
|
+
const textAfterCursor = stepsToReproduce.substring(cursorPos);
|
|
3276
|
+
const lines = textBeforeCursor.split("\n");
|
|
3277
|
+
const currentLine = lines[lines.length - 1];
|
|
3278
|
+
const numberMatch = currentLine.match(/^(\d+)\.\s/);
|
|
3279
|
+
const nextNumber = numberMatch ? parseInt(numberMatch[1]) + 1 : lines.length > 0 && stepsToReproduce.trim() === "" ? 1 : lines.length + 1;
|
|
3280
|
+
const newText = textBeforeCursor + "\n" + nextNumber + ". " + textAfterCursor;
|
|
3281
|
+
setStepsToReproduce(newText);
|
|
3282
|
+
setTimeout(() => {
|
|
3283
|
+
const newCursorPos = cursorPos + ("\n" + nextNumber + ". ").length;
|
|
3284
|
+
textarea.setSelectionRange(newCursorPos, newCursorPos);
|
|
3285
|
+
}, 0);
|
|
3286
|
+
}
|
|
3287
|
+
};
|
|
3288
|
+
const handleStepsFocus = () => {
|
|
3289
|
+
if (stepsToReproduce.trim() === "") {
|
|
3290
|
+
setStepsToReproduce("1. ");
|
|
3291
|
+
}
|
|
3292
|
+
};
|
|
3231
3293
|
const handleDialogOpenChange = (open) => {
|
|
3232
3294
|
if (open) {
|
|
3233
3295
|
openModal();
|
|
@@ -3238,15 +3300,24 @@ function BugReporterModal() {
|
|
|
3238
3300
|
};
|
|
3239
3301
|
const handleSubmit = async (event) => {
|
|
3240
3302
|
event.preventDefault();
|
|
3241
|
-
const result = await submitReport(title,
|
|
3303
|
+
const result = await submitReport(title, {
|
|
3304
|
+
stepsToReproduce,
|
|
3305
|
+
expectedResult,
|
|
3306
|
+
actualResult,
|
|
3307
|
+
additionalContext
|
|
3308
|
+
});
|
|
3242
3309
|
if (result) {
|
|
3243
3310
|
setTitle("");
|
|
3244
|
-
|
|
3311
|
+
setStepsToReproduce("");
|
|
3312
|
+
setExpectedResult("");
|
|
3313
|
+
setActualResult("");
|
|
3314
|
+
setAdditionalContext("");
|
|
3245
3315
|
setStep("review");
|
|
3316
|
+
setActiveTab("steps");
|
|
3246
3317
|
}
|
|
3247
3318
|
};
|
|
3248
3319
|
const hasIntegrations = availableProviders.length > 0;
|
|
3249
|
-
const canSubmit = !isSubmitting && !isCapturingScreenshot && hasIntegrations && !!selectedProvider && hasDraft && title.trim().length > 0;
|
|
3320
|
+
const canSubmit = !isSubmitting && !isCapturingScreenshot && hasIntegrations && !!selectedProvider && hasDraft && title.trim().length > 0 && !isOverLimit;
|
|
3250
3321
|
return /* @__PURE__ */ jsxRuntime.jsx(Dialog, { open: isOpen, onOpenChange: handleDialogOpenChange, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3251
3322
|
DialogContent,
|
|
3252
3323
|
{
|
|
@@ -3412,18 +3483,91 @@ function BugReporterModal() {
|
|
|
3412
3483
|
}
|
|
3413
3484
|
)
|
|
3414
3485
|
] }),
|
|
3415
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-
|
|
3416
|
-
/* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-sm font-medium",
|
|
3417
|
-
/* @__PURE__ */ jsxRuntime.
|
|
3486
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2 sm:col-span-2", children: [
|
|
3487
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-sm font-medium", children: "Bug Details" }),
|
|
3488
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-1 border-b border-gray-200", children: [
|
|
3489
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3490
|
+
"button",
|
|
3491
|
+
{
|
|
3492
|
+
type: "button",
|
|
3493
|
+
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"}`,
|
|
3494
|
+
onClick: () => setActiveTab("steps"),
|
|
3495
|
+
children: "Steps"
|
|
3496
|
+
}
|
|
3497
|
+
),
|
|
3498
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3499
|
+
"button",
|
|
3500
|
+
{
|
|
3501
|
+
type: "button",
|
|
3502
|
+
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"}`,
|
|
3503
|
+
onClick: () => setActiveTab("expected"),
|
|
3504
|
+
children: "Expected"
|
|
3505
|
+
}
|
|
3506
|
+
),
|
|
3507
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3508
|
+
"button",
|
|
3509
|
+
{
|
|
3510
|
+
type: "button",
|
|
3511
|
+
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"}`,
|
|
3512
|
+
onClick: () => setActiveTab("actual"),
|
|
3513
|
+
children: "Actual"
|
|
3514
|
+
}
|
|
3515
|
+
),
|
|
3516
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3517
|
+
"button",
|
|
3518
|
+
{
|
|
3519
|
+
type: "button",
|
|
3520
|
+
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"}`,
|
|
3521
|
+
onClick: () => setActiveTab("context"),
|
|
3522
|
+
children: "Context"
|
|
3523
|
+
}
|
|
3524
|
+
)
|
|
3525
|
+
] }),
|
|
3526
|
+
activeTab === "steps" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3418
3527
|
Textarea,
|
|
3419
3528
|
{
|
|
3420
|
-
id: "bug-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3529
|
+
id: "bug-steps",
|
|
3530
|
+
placeholder: "Press Enter to start numbering steps...",
|
|
3531
|
+
value: stepsToReproduce,
|
|
3532
|
+
onChange: (event) => setStepsToReproduce(event.target.value),
|
|
3533
|
+
onKeyDown: handleStepsKeyDown,
|
|
3534
|
+
onFocus: handleStepsFocus
|
|
3425
3535
|
}
|
|
3426
|
-
)
|
|
3536
|
+
),
|
|
3537
|
+
activeTab === "expected" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3538
|
+
Textarea,
|
|
3539
|
+
{
|
|
3540
|
+
id: "bug-expected",
|
|
3541
|
+
placeholder: "Describe what should happen...",
|
|
3542
|
+
value: expectedResult,
|
|
3543
|
+
onChange: (event) => setExpectedResult(event.target.value)
|
|
3544
|
+
}
|
|
3545
|
+
),
|
|
3546
|
+
activeTab === "actual" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3547
|
+
Textarea,
|
|
3548
|
+
{
|
|
3549
|
+
id: "bug-actual",
|
|
3550
|
+
placeholder: "Describe what actually happened...",
|
|
3551
|
+
value: actualResult,
|
|
3552
|
+
onChange: (event) => setActualResult(event.target.value)
|
|
3553
|
+
}
|
|
3554
|
+
),
|
|
3555
|
+
activeTab === "context" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3556
|
+
Textarea,
|
|
3557
|
+
{
|
|
3558
|
+
id: "bug-context",
|
|
3559
|
+
placeholder: "Any additional information, workarounds, or notes...",
|
|
3560
|
+
value: additionalContext,
|
|
3561
|
+
onChange: (event) => setAdditionalContext(event.target.value)
|
|
3562
|
+
}
|
|
3563
|
+
),
|
|
3564
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: `text-xs ${isOverLimit ? "text-red-600 font-medium" : "text-gray-500"}`, children: [
|
|
3565
|
+
totalChars,
|
|
3566
|
+
"/",
|
|
3567
|
+
CHAR_LIMIT,
|
|
3568
|
+
" characters ",
|
|
3569
|
+
isOverLimit && "(over limit)"
|
|
3570
|
+
] })
|
|
3427
3571
|
] }),
|
|
3428
3572
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [
|
|
3429
3573
|
/* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-sm font-medium", htmlFor: "bug-provider", children: "Submit to" }),
|