@bbearai/react 0.5.1 → 0.5.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.js +288 -81
- package/dist/index.mjs +262 -55
- package/package.json +4 -2
package/dist/index.js
CHANGED
|
@@ -343,11 +343,80 @@ function BugBearProvider({ config, children, enabled = true }) {
|
|
|
343
343
|
}
|
|
344
344
|
|
|
345
345
|
// src/BugBearPanel.tsx
|
|
346
|
-
var
|
|
346
|
+
var import_react15 = require("react");
|
|
347
347
|
var import_react_dom = require("react-dom");
|
|
348
348
|
|
|
349
349
|
// src/widget/navigation.ts
|
|
350
350
|
var import_react2 = require("react");
|
|
351
|
+
var NAV_STORAGE_KEY = "bugbear-nav-screen";
|
|
352
|
+
function serializeScreen(screen) {
|
|
353
|
+
switch (screen.name) {
|
|
354
|
+
case "HOME":
|
|
355
|
+
case "TEST_LIST":
|
|
356
|
+
case "MESSAGE_LIST":
|
|
357
|
+
case "COMPOSE_MESSAGE":
|
|
358
|
+
case "PROFILE":
|
|
359
|
+
case "REPORT_SUCCESS":
|
|
360
|
+
return JSON.stringify({ name: screen.name });
|
|
361
|
+
case "TEST_DETAIL":
|
|
362
|
+
return JSON.stringify({ name: screen.name, testId: screen.testId });
|
|
363
|
+
case "REPORT":
|
|
364
|
+
return JSON.stringify({ name: screen.name });
|
|
365
|
+
case "ISSUE_LIST":
|
|
366
|
+
return JSON.stringify({ name: screen.name, category: screen.category });
|
|
367
|
+
// Complex screens — save their parent list instead
|
|
368
|
+
case "THREAD_DETAIL":
|
|
369
|
+
return JSON.stringify({ name: "MESSAGE_LIST" });
|
|
370
|
+
case "ISSUE_DETAIL":
|
|
371
|
+
return JSON.stringify({ name: "ISSUE_LIST", category: "open" });
|
|
372
|
+
case "TEST_FEEDBACK":
|
|
373
|
+
return JSON.stringify({ name: "TEST_LIST" });
|
|
374
|
+
default:
|
|
375
|
+
return null;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
function deserializeScreen(json) {
|
|
379
|
+
try {
|
|
380
|
+
const parsed = JSON.parse(json);
|
|
381
|
+
if (!parsed || !parsed.name) return null;
|
|
382
|
+
switch (parsed.name) {
|
|
383
|
+
case "HOME":
|
|
384
|
+
return { name: "HOME" };
|
|
385
|
+
case "TEST_LIST":
|
|
386
|
+
return { name: "TEST_LIST" };
|
|
387
|
+
case "MESSAGE_LIST":
|
|
388
|
+
return { name: "MESSAGE_LIST" };
|
|
389
|
+
case "COMPOSE_MESSAGE":
|
|
390
|
+
return { name: "COMPOSE_MESSAGE" };
|
|
391
|
+
case "PROFILE":
|
|
392
|
+
return { name: "PROFILE" };
|
|
393
|
+
case "REPORT_SUCCESS":
|
|
394
|
+
return { name: "REPORT_SUCCESS" };
|
|
395
|
+
case "TEST_DETAIL":
|
|
396
|
+
return { name: "TEST_DETAIL", testId: parsed.testId };
|
|
397
|
+
case "REPORT":
|
|
398
|
+
return { name: "REPORT" };
|
|
399
|
+
case "ISSUE_LIST":
|
|
400
|
+
return { name: "ISSUE_LIST", category: parsed.category || "open" };
|
|
401
|
+
default:
|
|
402
|
+
return null;
|
|
403
|
+
}
|
|
404
|
+
} catch {
|
|
405
|
+
return null;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
function getInitialScreen() {
|
|
409
|
+
if (typeof window === "undefined") return { name: "HOME" };
|
|
410
|
+
try {
|
|
411
|
+
const saved = localStorage.getItem(NAV_STORAGE_KEY);
|
|
412
|
+
if (saved) {
|
|
413
|
+
const screen = deserializeScreen(saved);
|
|
414
|
+
if (screen) return screen;
|
|
415
|
+
}
|
|
416
|
+
} catch {
|
|
417
|
+
}
|
|
418
|
+
return { name: "HOME" };
|
|
419
|
+
}
|
|
351
420
|
function navReducer(state, action) {
|
|
352
421
|
switch (action.type) {
|
|
353
422
|
case "PUSH":
|
|
@@ -363,9 +432,21 @@ function navReducer(state, action) {
|
|
|
363
432
|
}
|
|
364
433
|
}
|
|
365
434
|
function useNavigation() {
|
|
366
|
-
const
|
|
435
|
+
const initialScreen = (0, import_react2.useRef)(getInitialScreen());
|
|
436
|
+
const [state, dispatch] = (0, import_react2.useReducer)(navReducer, { stack: [initialScreen.current] });
|
|
437
|
+
const currentScreen = state.stack[state.stack.length - 1];
|
|
438
|
+
(0, import_react2.useEffect)(() => {
|
|
439
|
+
if (typeof window === "undefined") return;
|
|
440
|
+
try {
|
|
441
|
+
const serialized = serializeScreen(currentScreen);
|
|
442
|
+
if (serialized) {
|
|
443
|
+
localStorage.setItem(NAV_STORAGE_KEY, serialized);
|
|
444
|
+
}
|
|
445
|
+
} catch {
|
|
446
|
+
}
|
|
447
|
+
}, [currentScreen]);
|
|
367
448
|
return {
|
|
368
|
-
currentScreen
|
|
449
|
+
currentScreen,
|
|
369
450
|
canGoBack: state.stack.length > 1,
|
|
370
451
|
push: (screen) => dispatch({ type: "PUSH", screen }),
|
|
371
452
|
pop: () => dispatch({ type: "POP" }),
|
|
@@ -459,6 +540,36 @@ function getThreadTypeIcon(type) {
|
|
|
459
540
|
}
|
|
460
541
|
}
|
|
461
542
|
|
|
543
|
+
// src/widget/useScreenCapture.ts
|
|
544
|
+
var import_modern_screenshot = require("modern-screenshot");
|
|
545
|
+
async function capturePageScreenshot(options = {}) {
|
|
546
|
+
const { excludeElement, timeout = 3e3 } = options;
|
|
547
|
+
try {
|
|
548
|
+
const blob = await Promise.race([
|
|
549
|
+
(0, import_modern_screenshot.domToBlob)(document.documentElement, {
|
|
550
|
+
filter: (node) => {
|
|
551
|
+
if (excludeElement && node === excludeElement) return false;
|
|
552
|
+
return true;
|
|
553
|
+
},
|
|
554
|
+
scale: 1,
|
|
555
|
+
quality: 0.85
|
|
556
|
+
}),
|
|
557
|
+
new Promise(
|
|
558
|
+
(_, reject) => setTimeout(() => reject(new Error("Screenshot capture timed out")), timeout)
|
|
559
|
+
)
|
|
560
|
+
]);
|
|
561
|
+
if (!blob) return null;
|
|
562
|
+
return new File(
|
|
563
|
+
[blob],
|
|
564
|
+
`screenshot-auto-${Date.now()}.png`,
|
|
565
|
+
{ type: "image/png" }
|
|
566
|
+
);
|
|
567
|
+
} catch (err) {
|
|
568
|
+
console.warn("BugBear: Auto-capture failed, user can attach manually", err);
|
|
569
|
+
return null;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
|
|
462
573
|
// src/widget/screens/HomeScreen.tsx
|
|
463
574
|
var import_react3 = require("react");
|
|
464
575
|
|
|
@@ -601,7 +712,7 @@ function MessageListScreenSkeleton() {
|
|
|
601
712
|
|
|
602
713
|
// src/widget/screens/HomeScreen.tsx
|
|
603
714
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
604
|
-
function HomeScreen({ nav }) {
|
|
715
|
+
function HomeScreen({ nav, onReportBug }) {
|
|
605
716
|
const { assignments, unreadCount, threads, refreshAssignments, refreshThreads, issueCounts, refreshIssueCounts, dashboardUrl, isLoading } = useBugBear();
|
|
606
717
|
(0, import_react3.useEffect)(() => {
|
|
607
718
|
refreshAssignments();
|
|
@@ -790,9 +901,11 @@ function HomeScreen({ nav }) {
|
|
|
790
901
|
{
|
|
791
902
|
role: "button",
|
|
792
903
|
tabIndex: 0,
|
|
793
|
-
onClick: () => nav.push({ name: "REPORT", prefill: { type: "bug" } }),
|
|
904
|
+
onClick: () => onReportBug ? onReportBug() : nav.push({ name: "REPORT", prefill: { type: "bug" } }),
|
|
794
905
|
onKeyDown: (e) => {
|
|
795
|
-
if (e.key === "Enter" || e.key === " ")
|
|
906
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
907
|
+
onReportBug ? onReportBug() : nav.push({ name: "REPORT", prefill: { type: "bug" } });
|
|
908
|
+
}
|
|
796
909
|
},
|
|
797
910
|
style: {
|
|
798
911
|
backgroundColor: colors.card,
|
|
@@ -1662,6 +1775,11 @@ function TestDetailScreen({ testId, nav }) {
|
|
|
1662
1775
|
]
|
|
1663
1776
|
}
|
|
1664
1777
|
),
|
|
1778
|
+
testCase.track && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { fontSize: 12, color: colors.textSecondary, marginBottom: 6 }, children: [
|
|
1779
|
+
testCase.track.icon,
|
|
1780
|
+
" ",
|
|
1781
|
+
testCase.track.name
|
|
1782
|
+
] }),
|
|
1665
1783
|
testCase.description && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { fontSize: 13, color: colors.textSecondary, lineHeight: "18px" }, children: testCase.description }),
|
|
1666
1784
|
testCase.group && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { marginTop: 8 }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { style: { fontSize: 12, color: colors.textMuted }, children: [
|
|
1667
1785
|
"\u{1F4C1} ",
|
|
@@ -2017,13 +2135,6 @@ function TestListScreen({ nav }) {
|
|
|
2017
2135
|
}
|
|
2018
2136
|
return Array.from(trackMap.values());
|
|
2019
2137
|
}, [assignments]);
|
|
2020
|
-
const availablePlatforms = (0, import_react5.useMemo)(() => {
|
|
2021
|
-
const set = /* @__PURE__ */ new Set();
|
|
2022
|
-
for (const a of assignments) {
|
|
2023
|
-
if (a.testCase.platforms) a.testCase.platforms.forEach((p) => set.add(p));
|
|
2024
|
-
}
|
|
2025
|
-
return Array.from(set).sort();
|
|
2026
|
-
}, [assignments]);
|
|
2027
2138
|
const selectedRole = availableRoles.find((r) => r.id === roleFilter);
|
|
2028
2139
|
const groupedAssignments = (0, import_react5.useMemo)(() => {
|
|
2029
2140
|
const groups = /* @__PURE__ */ new Map();
|
|
@@ -2231,7 +2342,7 @@ function TestListScreen({ nav }) {
|
|
|
2231
2342
|
}
|
|
2232
2343
|
}
|
|
2233
2344
|
) }),
|
|
2234
|
-
|
|
2345
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { display: "flex", gap: 4, marginBottom: 8 }, children: [
|
|
2235
2346
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
2236
2347
|
"button",
|
|
2237
2348
|
{
|
|
@@ -2248,18 +2359,20 @@ function TestListScreen({ nav }) {
|
|
|
2248
2359
|
fontWeight: !platformFilter ? 600 : 400,
|
|
2249
2360
|
whiteSpace: "nowrap"
|
|
2250
2361
|
},
|
|
2251
|
-
children: "All
|
|
2362
|
+
children: "All"
|
|
2252
2363
|
}
|
|
2253
2364
|
),
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2365
|
+
[
|
|
2366
|
+
{ key: "web", label: "Web", icon: "\u{1F310}" },
|
|
2367
|
+
{ key: "ios", label: "iOS", icon: "\u{1F4F1}" },
|
|
2368
|
+
{ key: "android", label: "Android", icon: "\u{1F916}" }
|
|
2369
|
+
].map((p) => {
|
|
2370
|
+
const isActive = platformFilter === p.key;
|
|
2258
2371
|
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
2259
2372
|
"button",
|
|
2260
2373
|
{
|
|
2261
2374
|
type: "button",
|
|
2262
|
-
onClick: () => setPlatformFilter(isActive ? null : p),
|
|
2375
|
+
onClick: () => setPlatformFilter(isActive ? null : p.key),
|
|
2263
2376
|
style: {
|
|
2264
2377
|
display: "flex",
|
|
2265
2378
|
alignItems: "center",
|
|
@@ -2275,12 +2388,12 @@ function TestListScreen({ nav }) {
|
|
|
2275
2388
|
whiteSpace: "nowrap"
|
|
2276
2389
|
},
|
|
2277
2390
|
children: [
|
|
2278
|
-
icon,
|
|
2391
|
+
p.icon,
|
|
2279
2392
|
" ",
|
|
2280
|
-
label
|
|
2393
|
+
p.label
|
|
2281
2394
|
]
|
|
2282
2395
|
},
|
|
2283
|
-
p
|
|
2396
|
+
p.key
|
|
2284
2397
|
);
|
|
2285
2398
|
})
|
|
2286
2399
|
] }),
|
|
@@ -2688,6 +2801,25 @@ function useImageAttachments(uploadFn, maxImages, bucket = "screenshots") {
|
|
|
2688
2801
|
const pickFromCamera = (0, import_react6.useCallback)(() => {
|
|
2689
2802
|
triggerFilePicker("environment");
|
|
2690
2803
|
}, [triggerFilePicker]);
|
|
2804
|
+
const addFile = (0, import_react6.useCallback)((file) => {
|
|
2805
|
+
const id = `img-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`;
|
|
2806
|
+
const localUri = URL.createObjectURL(file);
|
|
2807
|
+
const name = file.name || `image-${id}.png`;
|
|
2808
|
+
setImages((prev) => {
|
|
2809
|
+
if (prev.length >= maxImages) return prev;
|
|
2810
|
+
return [...prev, { id, localUri, remoteUrl: null, name, status: "uploading" }];
|
|
2811
|
+
});
|
|
2812
|
+
uploadFn(file, bucket).then((url) => {
|
|
2813
|
+
setImages((prev) => prev.map(
|
|
2814
|
+
(img) => img.id === id ? { ...img, remoteUrl: url, status: url ? "done" : "error" } : img
|
|
2815
|
+
));
|
|
2816
|
+
}).catch((err) => {
|
|
2817
|
+
console.error("BugBear: Image upload failed", err);
|
|
2818
|
+
setImages((prev) => prev.map(
|
|
2819
|
+
(img) => img.id === id ? { ...img, status: "error" } : img
|
|
2820
|
+
));
|
|
2821
|
+
});
|
|
2822
|
+
}, [maxImages, uploadFn, bucket]);
|
|
2691
2823
|
const removeImage = (0, import_react6.useCallback)((id) => {
|
|
2692
2824
|
setImages((prev) => {
|
|
2693
2825
|
const img = prev.find((i) => i.id === id);
|
|
@@ -2709,7 +2841,7 @@ function useImageAttachments(uploadFn, maxImages, bucket = "screenshots") {
|
|
|
2709
2841
|
const getScreenshotUrls = (0, import_react6.useCallback)(() => {
|
|
2710
2842
|
return images.filter((img) => img.status === "done" && img.remoteUrl).map((img) => img.remoteUrl);
|
|
2711
2843
|
}, [images]);
|
|
2712
|
-
return { images, pickFromGallery, pickFromCamera, removeImage, clear, isUploading, hasError, getAttachments, getScreenshotUrls };
|
|
2844
|
+
return { images, pickFromGallery, pickFromCamera, addFile, removeImage, clear, isUploading, hasError, getAttachments, getScreenshotUrls };
|
|
2713
2845
|
}
|
|
2714
2846
|
|
|
2715
2847
|
// src/widget/ImagePreviewStrip.tsx
|
|
@@ -2830,7 +2962,12 @@ function ImagePickerButtons({ images, maxImages, onPickGallery, onPickCamera, on
|
|
|
2830
2962
|
maxImages
|
|
2831
2963
|
] })
|
|
2832
2964
|
] }),
|
|
2833
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ImagePreviewStrip, { images, onRemove })
|
|
2965
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ImagePreviewStrip, { images, onRemove }),
|
|
2966
|
+
typeof navigator !== "undefined" && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { style: { fontSize: 11, color: colors.textDim, display: "block", marginTop: 4 }, children: [
|
|
2967
|
+
"Paste images with ",
|
|
2968
|
+
typeof navigator !== "undefined" && navigator.platform?.includes("Mac") ? "\u2318" : "Ctrl",
|
|
2969
|
+
"+V"
|
|
2970
|
+
] })
|
|
2834
2971
|
] });
|
|
2835
2972
|
}
|
|
2836
2973
|
|
|
@@ -3128,7 +3265,7 @@ var styles = {
|
|
|
3128
3265
|
};
|
|
3129
3266
|
|
|
3130
3267
|
// src/widget/screens/ReportScreen.tsx
|
|
3131
|
-
var
|
|
3268
|
+
var import_react9 = __toESM(require("react"));
|
|
3132
3269
|
|
|
3133
3270
|
// src/widget/CategoryDropdown.tsx
|
|
3134
3271
|
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
@@ -3174,27 +3311,79 @@ function CategoryDropdown({ value, onChange, optional = true, disabled = false }
|
|
|
3174
3311
|
);
|
|
3175
3312
|
}
|
|
3176
3313
|
|
|
3314
|
+
// src/widget/useClipboardPaste.ts
|
|
3315
|
+
var import_react8 = require("react");
|
|
3316
|
+
function useClipboardPaste({
|
|
3317
|
+
containerRef,
|
|
3318
|
+
enabled,
|
|
3319
|
+
currentCount,
|
|
3320
|
+
maxImages,
|
|
3321
|
+
addFile
|
|
3322
|
+
}) {
|
|
3323
|
+
const handlePaste = (0, import_react8.useCallback)((event) => {
|
|
3324
|
+
if (!enabled || currentCount >= maxImages) return;
|
|
3325
|
+
const items = event.clipboardData?.items;
|
|
3326
|
+
if (!items) return;
|
|
3327
|
+
const imageFiles = [];
|
|
3328
|
+
for (let i = 0; i < items.length; i++) {
|
|
3329
|
+
const item = items[i];
|
|
3330
|
+
if (item.type.startsWith("image/")) {
|
|
3331
|
+
const file = item.getAsFile();
|
|
3332
|
+
if (file) imageFiles.push(file);
|
|
3333
|
+
}
|
|
3334
|
+
}
|
|
3335
|
+
if (imageFiles.length === 0) return;
|
|
3336
|
+
event.preventDefault();
|
|
3337
|
+
const remaining = maxImages - currentCount;
|
|
3338
|
+
for (const file of imageFiles.slice(0, remaining)) {
|
|
3339
|
+
addFile(file);
|
|
3340
|
+
}
|
|
3341
|
+
}, [enabled, currentCount, maxImages, addFile]);
|
|
3342
|
+
(0, import_react8.useEffect)(() => {
|
|
3343
|
+
const container = containerRef.current;
|
|
3344
|
+
if (!container || !enabled) return;
|
|
3345
|
+
container.addEventListener("paste", handlePaste);
|
|
3346
|
+
return () => container.removeEventListener("paste", handlePaste);
|
|
3347
|
+
}, [containerRef, enabled, handlePaste]);
|
|
3348
|
+
}
|
|
3349
|
+
|
|
3177
3350
|
// src/widget/screens/ReportScreen.tsx
|
|
3178
3351
|
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
3179
|
-
function ReportScreen({ nav, prefill }) {
|
|
3352
|
+
function ReportScreen({ nav, prefill, autoCapture, onAutoCaptureConsumed }) {
|
|
3180
3353
|
const { client, refreshAssignments, uploadImage } = useBugBear();
|
|
3181
3354
|
const images = useImageAttachments(uploadImage, 5, "screenshots");
|
|
3182
|
-
const
|
|
3183
|
-
const
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3355
|
+
const formRef = (0, import_react9.useRef)(null);
|
|
3356
|
+
const hasConsumedCapture = (0, import_react9.useRef)(false);
|
|
3357
|
+
(0, import_react9.useEffect)(() => {
|
|
3358
|
+
if (autoCapture && !hasConsumedCapture.current) {
|
|
3359
|
+
hasConsumedCapture.current = true;
|
|
3360
|
+
images.addFile(autoCapture);
|
|
3361
|
+
onAutoCaptureConsumed?.();
|
|
3362
|
+
}
|
|
3363
|
+
}, [autoCapture, onAutoCaptureConsumed]);
|
|
3364
|
+
useClipboardPaste({
|
|
3365
|
+
containerRef: formRef,
|
|
3366
|
+
enabled: true,
|
|
3367
|
+
currentCount: images.images.length,
|
|
3368
|
+
maxImages: 5,
|
|
3369
|
+
addFile: images.addFile
|
|
3370
|
+
});
|
|
3371
|
+
const [reportType, setReportType] = (0, import_react9.useState)(prefill?.type || "bug");
|
|
3372
|
+
const [severity, setSeverity] = (0, import_react9.useState)("medium");
|
|
3373
|
+
const [category, setCategory] = (0, import_react9.useState)(null);
|
|
3374
|
+
const [description, setDescription] = (0, import_react9.useState)("");
|
|
3375
|
+
const [affectedRoute, setAffectedRoute] = (0, import_react9.useState)("");
|
|
3376
|
+
const [submitting, setSubmitting] = (0, import_react9.useState)(false);
|
|
3377
|
+
const [error, setError] = (0, import_react9.useState)(null);
|
|
3378
|
+
const submittingRef = (0, import_react9.useRef)(false);
|
|
3379
|
+
import_react9.default.useEffect(() => {
|
|
3191
3380
|
if (reportType === "feedback" || reportType === "suggestion") {
|
|
3192
3381
|
setCategory("other");
|
|
3193
3382
|
} else {
|
|
3194
3383
|
setCategory(null);
|
|
3195
3384
|
}
|
|
3196
3385
|
}, [reportType]);
|
|
3197
|
-
const observedRoute = (0,
|
|
3386
|
+
const observedRoute = (0, import_react9.useRef)(
|
|
3198
3387
|
typeof window !== "undefined" ? window.location.pathname : "unknown"
|
|
3199
3388
|
);
|
|
3200
3389
|
const isRetestFailure = prefill?.type === "test_fail";
|
|
@@ -3250,7 +3439,7 @@ function ReportScreen({ nav, prefill }) {
|
|
|
3250
3439
|
{ sev: "medium", color: "#eab308" },
|
|
3251
3440
|
{ sev: "low", color: "#6b7280" }
|
|
3252
3441
|
];
|
|
3253
|
-
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { children: isRetestFailure ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
|
|
3442
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { ref: formRef, tabIndex: -1, style: { outline: "none" }, children: isRetestFailure ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
|
|
3254
3443
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: styles2.retestBanner, children: [
|
|
3255
3444
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { style: { fontSize: 16 }, children: "\u{1F504}" }),
|
|
3256
3445
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { children: [
|
|
@@ -3578,10 +3767,10 @@ var styles2 = {
|
|
|
3578
3767
|
};
|
|
3579
3768
|
|
|
3580
3769
|
// src/widget/screens/ReportSuccessScreen.tsx
|
|
3581
|
-
var
|
|
3770
|
+
var import_react10 = require("react");
|
|
3582
3771
|
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
3583
3772
|
function ReportSuccessScreen({ nav }) {
|
|
3584
|
-
(0,
|
|
3773
|
+
(0, import_react10.useEffect)(() => {
|
|
3585
3774
|
const timer = setTimeout(() => nav.reset(), 2e3);
|
|
3586
3775
|
return () => clearTimeout(timer);
|
|
3587
3776
|
}, [nav]);
|
|
@@ -3871,7 +4060,7 @@ function MessageListScreen({ nav }) {
|
|
|
3871
4060
|
}
|
|
3872
4061
|
|
|
3873
4062
|
// src/widget/screens/ThreadDetailScreen.tsx
|
|
3874
|
-
var
|
|
4063
|
+
var import_react11 = require("react");
|
|
3875
4064
|
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
3876
4065
|
var inputStyle = {
|
|
3877
4066
|
backgroundColor: "#27272a",
|
|
@@ -3888,12 +4077,12 @@ function ThreadDetailScreen({
|
|
|
3888
4077
|
}) {
|
|
3889
4078
|
const { getThreadMessages, sendMessage, markAsRead, uploadImage } = useBugBear();
|
|
3890
4079
|
const replyImages = useImageAttachments(uploadImage, 3, "discussion-attachments");
|
|
3891
|
-
const [messages, setMessages] = (0,
|
|
3892
|
-
const [loading, setLoading] = (0,
|
|
3893
|
-
const [replyText, setReplyText] = (0,
|
|
3894
|
-
const [sending, setSending] = (0,
|
|
3895
|
-
const [sendError, setSendError] = (0,
|
|
3896
|
-
(0,
|
|
4080
|
+
const [messages, setMessages] = (0, import_react11.useState)([]);
|
|
4081
|
+
const [loading, setLoading] = (0, import_react11.useState)(true);
|
|
4082
|
+
const [replyText, setReplyText] = (0, import_react11.useState)("");
|
|
4083
|
+
const [sending, setSending] = (0, import_react11.useState)(false);
|
|
4084
|
+
const [sendError, setSendError] = (0, import_react11.useState)(false);
|
|
4085
|
+
(0, import_react11.useEffect)(() => {
|
|
3897
4086
|
let cancelled = false;
|
|
3898
4087
|
setLoading(true);
|
|
3899
4088
|
(async () => {
|
|
@@ -4160,7 +4349,7 @@ function ThreadDetailScreen({
|
|
|
4160
4349
|
}
|
|
4161
4350
|
|
|
4162
4351
|
// src/widget/screens/ComposeMessageScreen.tsx
|
|
4163
|
-
var
|
|
4352
|
+
var import_react12 = require("react");
|
|
4164
4353
|
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
4165
4354
|
var inputStyle2 = {
|
|
4166
4355
|
backgroundColor: "#27272a",
|
|
@@ -4174,9 +4363,9 @@ var inputStyle2 = {
|
|
|
4174
4363
|
function ComposeMessageScreen({ nav }) {
|
|
4175
4364
|
const { createThread, uploadImage } = useBugBear();
|
|
4176
4365
|
const images = useImageAttachments(uploadImage, 3, "discussion-attachments");
|
|
4177
|
-
const [subject, setSubject] = (0,
|
|
4178
|
-
const [message, setMessage] = (0,
|
|
4179
|
-
const [sending, setSending] = (0,
|
|
4366
|
+
const [subject, setSubject] = (0, import_react12.useState)("");
|
|
4367
|
+
const [message, setMessage] = (0, import_react12.useState)("");
|
|
4368
|
+
const [sending, setSending] = (0, import_react12.useState)(false);
|
|
4180
4369
|
const canSend = subject.trim().length > 0 && message.trim().length > 0 && !sending && !images.isUploading;
|
|
4181
4370
|
const handleSend = async () => {
|
|
4182
4371
|
if (!canSend) return;
|
|
@@ -4318,20 +4507,20 @@ function ComposeMessageScreen({ nav }) {
|
|
|
4318
4507
|
}
|
|
4319
4508
|
|
|
4320
4509
|
// src/widget/screens/ProfileScreen.tsx
|
|
4321
|
-
var
|
|
4510
|
+
var import_react13 = require("react");
|
|
4322
4511
|
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
4323
4512
|
function ProfileScreen({ nav }) {
|
|
4324
4513
|
const { testerInfo, assignments, updateTesterProfile, refreshTesterInfo } = useBugBear();
|
|
4325
|
-
const [editing, setEditing] = (0,
|
|
4326
|
-
const [name, setName] = (0,
|
|
4327
|
-
const [additionalEmails, setAdditionalEmails] = (0,
|
|
4328
|
-
const [newEmailInput, setNewEmailInput] = (0,
|
|
4329
|
-
const [platforms, setPlatforms] = (0,
|
|
4330
|
-
const [saving, setSaving] = (0,
|
|
4331
|
-
const [saved, setSaved] = (0,
|
|
4332
|
-
const [showDetails, setShowDetails] = (0,
|
|
4514
|
+
const [editing, setEditing] = (0, import_react13.useState)(false);
|
|
4515
|
+
const [name, setName] = (0, import_react13.useState)(testerInfo?.name || "");
|
|
4516
|
+
const [additionalEmails, setAdditionalEmails] = (0, import_react13.useState)(testerInfo?.additionalEmails || []);
|
|
4517
|
+
const [newEmailInput, setNewEmailInput] = (0, import_react13.useState)("");
|
|
4518
|
+
const [platforms, setPlatforms] = (0, import_react13.useState)(testerInfo?.platforms || []);
|
|
4519
|
+
const [saving, setSaving] = (0, import_react13.useState)(false);
|
|
4520
|
+
const [saved, setSaved] = (0, import_react13.useState)(false);
|
|
4521
|
+
const [showDetails, setShowDetails] = (0, import_react13.useState)(false);
|
|
4333
4522
|
const completedCount = assignments.filter((a) => a.status === "passed" || a.status === "failed").length;
|
|
4334
|
-
(0,
|
|
4523
|
+
(0, import_react13.useEffect)(() => {
|
|
4335
4524
|
if (testerInfo) {
|
|
4336
4525
|
setName(testerInfo.name);
|
|
4337
4526
|
setAdditionalEmails(testerInfo.additionalEmails || []);
|
|
@@ -4795,7 +4984,7 @@ var styles4 = {
|
|
|
4795
4984
|
};
|
|
4796
4985
|
|
|
4797
4986
|
// src/widget/screens/IssueListScreen.tsx
|
|
4798
|
-
var
|
|
4987
|
+
var import_react14 = require("react");
|
|
4799
4988
|
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
4800
4989
|
var CATEGORY_CONFIG = {
|
|
4801
4990
|
open: { label: "Open Issues", accent: "#f97316", emptyIcon: "\u2705", emptyText: "No open issues" },
|
|
@@ -4810,10 +4999,10 @@ var SEVERITY_COLORS = {
|
|
|
4810
4999
|
};
|
|
4811
5000
|
function IssueListScreen({ nav, category }) {
|
|
4812
5001
|
const { client } = useBugBear();
|
|
4813
|
-
const [issues, setIssues] = (0,
|
|
4814
|
-
const [loading, setLoading] = (0,
|
|
5002
|
+
const [issues, setIssues] = (0, import_react14.useState)([]);
|
|
5003
|
+
const [loading, setLoading] = (0, import_react14.useState)(true);
|
|
4815
5004
|
const config = CATEGORY_CONFIG[category];
|
|
4816
|
-
(0,
|
|
5005
|
+
(0, import_react14.useEffect)(() => {
|
|
4817
5006
|
let cancelled = false;
|
|
4818
5007
|
setLoading(true);
|
|
4819
5008
|
(async () => {
|
|
@@ -5154,12 +5343,13 @@ function BugBearPanel({
|
|
|
5154
5343
|
}) {
|
|
5155
5344
|
const { shouldShowWidget, testerInfo, assignments, isLoading, unreadCount } = useBugBear();
|
|
5156
5345
|
const { currentScreen, canGoBack, push, pop, replace, reset } = useNavigation();
|
|
5157
|
-
const [collapsed, setCollapsed] = (0,
|
|
5158
|
-
const
|
|
5159
|
-
const [
|
|
5160
|
-
const
|
|
5161
|
-
const
|
|
5162
|
-
(0,
|
|
5346
|
+
const [collapsed, setCollapsed] = (0, import_react15.useState)(defaultCollapsed);
|
|
5347
|
+
const autoCaptureRef = (0, import_react15.useRef)(null);
|
|
5348
|
+
const [panelPosition, setPanelPosition] = (0, import_react15.useState)(null);
|
|
5349
|
+
const [isDragging, setIsDragging] = (0, import_react15.useState)(false);
|
|
5350
|
+
const dragStartRef = (0, import_react15.useRef)(null);
|
|
5351
|
+
const panelRef = (0, import_react15.useRef)(null);
|
|
5352
|
+
(0, import_react15.useEffect)(() => {
|
|
5163
5353
|
if (typeof window === "undefined") return;
|
|
5164
5354
|
try {
|
|
5165
5355
|
const saved = localStorage.getItem(STORAGE_KEY);
|
|
@@ -5173,7 +5363,7 @@ function BugBearPanel({
|
|
|
5173
5363
|
setPanelPosition(getDefaultPosition(position));
|
|
5174
5364
|
}
|
|
5175
5365
|
}, [position]);
|
|
5176
|
-
(0,
|
|
5366
|
+
(0, import_react15.useEffect)(() => {
|
|
5177
5367
|
if (panelPosition && typeof window !== "undefined") {
|
|
5178
5368
|
try {
|
|
5179
5369
|
localStorage.setItem(STORAGE_KEY, JSON.stringify(panelPosition));
|
|
@@ -5181,7 +5371,7 @@ function BugBearPanel({
|
|
|
5181
5371
|
}
|
|
5182
5372
|
}
|
|
5183
5373
|
}, [panelPosition]);
|
|
5184
|
-
(0,
|
|
5374
|
+
(0, import_react15.useEffect)(() => {
|
|
5185
5375
|
if (typeof window === "undefined") return;
|
|
5186
5376
|
const handleResize = () => {
|
|
5187
5377
|
setPanelPosition((prev) => prev ? clampPosition(prev) : getDefaultPosition(position));
|
|
@@ -5189,7 +5379,7 @@ function BugBearPanel({
|
|
|
5189
5379
|
window.addEventListener("resize", handleResize);
|
|
5190
5380
|
return () => window.removeEventListener("resize", handleResize);
|
|
5191
5381
|
}, [position]);
|
|
5192
|
-
const handleMouseDown = (0,
|
|
5382
|
+
const handleMouseDown = (0, import_react15.useCallback)((e) => {
|
|
5193
5383
|
if (!draggable || !panelPosition) return;
|
|
5194
5384
|
const target = e.target;
|
|
5195
5385
|
if (!target.closest("[data-drag-handle]")) return;
|
|
@@ -5202,7 +5392,7 @@ function BugBearPanel({
|
|
|
5202
5392
|
panelY: panelPosition.y
|
|
5203
5393
|
};
|
|
5204
5394
|
}, [draggable, panelPosition]);
|
|
5205
|
-
(0,
|
|
5395
|
+
(0, import_react15.useEffect)(() => {
|
|
5206
5396
|
if (!isDragging) return;
|
|
5207
5397
|
const handleMouseMove = (e) => {
|
|
5208
5398
|
if (!dragStartRef.current) return;
|
|
@@ -5224,7 +5414,7 @@ function BugBearPanel({
|
|
|
5224
5414
|
document.removeEventListener("mouseup", handleMouseUp);
|
|
5225
5415
|
};
|
|
5226
5416
|
}, [isDragging]);
|
|
5227
|
-
const handleDoubleClick = (0,
|
|
5417
|
+
const handleDoubleClick = (0, import_react15.useCallback)(() => {
|
|
5228
5418
|
if (!draggable) return;
|
|
5229
5419
|
setPanelPosition(getDefaultPosition(position));
|
|
5230
5420
|
try {
|
|
@@ -5232,6 +5422,14 @@ function BugBearPanel({
|
|
|
5232
5422
|
} catch {
|
|
5233
5423
|
}
|
|
5234
5424
|
}, [draggable, position]);
|
|
5425
|
+
const handleReportBug = (0, import_react15.useCallback)(async () => {
|
|
5426
|
+
const file = await capturePageScreenshot({
|
|
5427
|
+
excludeElement: panelRef.current,
|
|
5428
|
+
timeout: 3e3
|
|
5429
|
+
});
|
|
5430
|
+
autoCaptureRef.current = file;
|
|
5431
|
+
push({ name: "REPORT", prefill: { type: "bug" } });
|
|
5432
|
+
}, [push]);
|
|
5235
5433
|
if (isLoading || !shouldShowWidget) return null;
|
|
5236
5434
|
if (!panelPosition) return null;
|
|
5237
5435
|
const pendingCount = assignments.filter((a) => a.status === "pending" || a.status === "in_progress").length;
|
|
@@ -5268,13 +5466,12 @@ function BugBearPanel({
|
|
|
5268
5466
|
};
|
|
5269
5467
|
const handleClose = () => {
|
|
5270
5468
|
setCollapsed(true);
|
|
5271
|
-
reset();
|
|
5272
5469
|
};
|
|
5273
5470
|
const nav = { push, pop, replace, reset, canGoBack };
|
|
5274
5471
|
const renderScreen = () => {
|
|
5275
5472
|
switch (currentScreen.name) {
|
|
5276
5473
|
case "HOME":
|
|
5277
|
-
return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(HomeScreen, { nav });
|
|
5474
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(HomeScreen, { nav, onReportBug: handleReportBug });
|
|
5278
5475
|
case "TEST_DETAIL":
|
|
5279
5476
|
return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(TestDetailScreen, { testId: currentScreen.testId, nav });
|
|
5280
5477
|
case "TEST_LIST":
|
|
@@ -5282,7 +5479,17 @@ function BugBearPanel({
|
|
|
5282
5479
|
case "TEST_FEEDBACK":
|
|
5283
5480
|
return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(TestFeedbackScreen, { status: currentScreen.status, assignmentId: currentScreen.assignmentId, nav });
|
|
5284
5481
|
case "REPORT":
|
|
5285
|
-
return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
5482
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
5483
|
+
ReportScreen,
|
|
5484
|
+
{
|
|
5485
|
+
nav,
|
|
5486
|
+
prefill: currentScreen.prefill,
|
|
5487
|
+
autoCapture: autoCaptureRef.current,
|
|
5488
|
+
onAutoCaptureConsumed: () => {
|
|
5489
|
+
autoCaptureRef.current = null;
|
|
5490
|
+
}
|
|
5491
|
+
}
|
|
5492
|
+
);
|
|
5286
5493
|
case "REPORT_SUCCESS":
|
|
5287
5494
|
return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ReportSuccessScreen, { nav });
|
|
5288
5495
|
case "MESSAGE_LIST":
|
|
@@ -5479,10 +5686,10 @@ function BugBearPanel({
|
|
|
5479
5686
|
}
|
|
5480
5687
|
|
|
5481
5688
|
// src/BugBearErrorBoundary.tsx
|
|
5482
|
-
var
|
|
5689
|
+
var import_react16 = require("react");
|
|
5483
5690
|
var import_core2 = require("@bbearai/core");
|
|
5484
5691
|
var import_jsx_runtime19 = require("react/jsx-runtime");
|
|
5485
|
-
var BugBearErrorBoundary = class extends
|
|
5692
|
+
var BugBearErrorBoundary = class extends import_react16.Component {
|
|
5486
5693
|
constructor(props) {
|
|
5487
5694
|
super(props);
|
|
5488
5695
|
this.reset = () => {
|
package/dist/index.mjs
CHANGED
|
@@ -304,11 +304,80 @@ function BugBearProvider({ config, children, enabled = true }) {
|
|
|
304
304
|
}
|
|
305
305
|
|
|
306
306
|
// src/BugBearPanel.tsx
|
|
307
|
-
import { useState as useState11, useRef as
|
|
307
|
+
import { useState as useState11, useRef as useRef4, useEffect as useEffect11, useCallback as useCallback6 } from "react";
|
|
308
308
|
import { createPortal } from "react-dom";
|
|
309
309
|
|
|
310
310
|
// src/widget/navigation.ts
|
|
311
|
-
import { useReducer } from "react";
|
|
311
|
+
import { useReducer, useEffect as useEffect2, useRef as useRef2 } from "react";
|
|
312
|
+
var NAV_STORAGE_KEY = "bugbear-nav-screen";
|
|
313
|
+
function serializeScreen(screen) {
|
|
314
|
+
switch (screen.name) {
|
|
315
|
+
case "HOME":
|
|
316
|
+
case "TEST_LIST":
|
|
317
|
+
case "MESSAGE_LIST":
|
|
318
|
+
case "COMPOSE_MESSAGE":
|
|
319
|
+
case "PROFILE":
|
|
320
|
+
case "REPORT_SUCCESS":
|
|
321
|
+
return JSON.stringify({ name: screen.name });
|
|
322
|
+
case "TEST_DETAIL":
|
|
323
|
+
return JSON.stringify({ name: screen.name, testId: screen.testId });
|
|
324
|
+
case "REPORT":
|
|
325
|
+
return JSON.stringify({ name: screen.name });
|
|
326
|
+
case "ISSUE_LIST":
|
|
327
|
+
return JSON.stringify({ name: screen.name, category: screen.category });
|
|
328
|
+
// Complex screens — save their parent list instead
|
|
329
|
+
case "THREAD_DETAIL":
|
|
330
|
+
return JSON.stringify({ name: "MESSAGE_LIST" });
|
|
331
|
+
case "ISSUE_DETAIL":
|
|
332
|
+
return JSON.stringify({ name: "ISSUE_LIST", category: "open" });
|
|
333
|
+
case "TEST_FEEDBACK":
|
|
334
|
+
return JSON.stringify({ name: "TEST_LIST" });
|
|
335
|
+
default:
|
|
336
|
+
return null;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
function deserializeScreen(json) {
|
|
340
|
+
try {
|
|
341
|
+
const parsed = JSON.parse(json);
|
|
342
|
+
if (!parsed || !parsed.name) return null;
|
|
343
|
+
switch (parsed.name) {
|
|
344
|
+
case "HOME":
|
|
345
|
+
return { name: "HOME" };
|
|
346
|
+
case "TEST_LIST":
|
|
347
|
+
return { name: "TEST_LIST" };
|
|
348
|
+
case "MESSAGE_LIST":
|
|
349
|
+
return { name: "MESSAGE_LIST" };
|
|
350
|
+
case "COMPOSE_MESSAGE":
|
|
351
|
+
return { name: "COMPOSE_MESSAGE" };
|
|
352
|
+
case "PROFILE":
|
|
353
|
+
return { name: "PROFILE" };
|
|
354
|
+
case "REPORT_SUCCESS":
|
|
355
|
+
return { name: "REPORT_SUCCESS" };
|
|
356
|
+
case "TEST_DETAIL":
|
|
357
|
+
return { name: "TEST_DETAIL", testId: parsed.testId };
|
|
358
|
+
case "REPORT":
|
|
359
|
+
return { name: "REPORT" };
|
|
360
|
+
case "ISSUE_LIST":
|
|
361
|
+
return { name: "ISSUE_LIST", category: parsed.category || "open" };
|
|
362
|
+
default:
|
|
363
|
+
return null;
|
|
364
|
+
}
|
|
365
|
+
} catch {
|
|
366
|
+
return null;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
function getInitialScreen() {
|
|
370
|
+
if (typeof window === "undefined") return { name: "HOME" };
|
|
371
|
+
try {
|
|
372
|
+
const saved = localStorage.getItem(NAV_STORAGE_KEY);
|
|
373
|
+
if (saved) {
|
|
374
|
+
const screen = deserializeScreen(saved);
|
|
375
|
+
if (screen) return screen;
|
|
376
|
+
}
|
|
377
|
+
} catch {
|
|
378
|
+
}
|
|
379
|
+
return { name: "HOME" };
|
|
380
|
+
}
|
|
312
381
|
function navReducer(state, action) {
|
|
313
382
|
switch (action.type) {
|
|
314
383
|
case "PUSH":
|
|
@@ -324,9 +393,21 @@ function navReducer(state, action) {
|
|
|
324
393
|
}
|
|
325
394
|
}
|
|
326
395
|
function useNavigation() {
|
|
327
|
-
const
|
|
396
|
+
const initialScreen = useRef2(getInitialScreen());
|
|
397
|
+
const [state, dispatch] = useReducer(navReducer, { stack: [initialScreen.current] });
|
|
398
|
+
const currentScreen = state.stack[state.stack.length - 1];
|
|
399
|
+
useEffect2(() => {
|
|
400
|
+
if (typeof window === "undefined") return;
|
|
401
|
+
try {
|
|
402
|
+
const serialized = serializeScreen(currentScreen);
|
|
403
|
+
if (serialized) {
|
|
404
|
+
localStorage.setItem(NAV_STORAGE_KEY, serialized);
|
|
405
|
+
}
|
|
406
|
+
} catch {
|
|
407
|
+
}
|
|
408
|
+
}, [currentScreen]);
|
|
328
409
|
return {
|
|
329
|
-
currentScreen
|
|
410
|
+
currentScreen,
|
|
330
411
|
canGoBack: state.stack.length > 1,
|
|
331
412
|
push: (screen) => dispatch({ type: "PUSH", screen }),
|
|
332
413
|
pop: () => dispatch({ type: "POP" }),
|
|
@@ -420,8 +501,38 @@ function getThreadTypeIcon(type) {
|
|
|
420
501
|
}
|
|
421
502
|
}
|
|
422
503
|
|
|
504
|
+
// src/widget/useScreenCapture.ts
|
|
505
|
+
import { domToBlob } from "modern-screenshot";
|
|
506
|
+
async function capturePageScreenshot(options = {}) {
|
|
507
|
+
const { excludeElement, timeout = 3e3 } = options;
|
|
508
|
+
try {
|
|
509
|
+
const blob = await Promise.race([
|
|
510
|
+
domToBlob(document.documentElement, {
|
|
511
|
+
filter: (node) => {
|
|
512
|
+
if (excludeElement && node === excludeElement) return false;
|
|
513
|
+
return true;
|
|
514
|
+
},
|
|
515
|
+
scale: 1,
|
|
516
|
+
quality: 0.85
|
|
517
|
+
}),
|
|
518
|
+
new Promise(
|
|
519
|
+
(_, reject) => setTimeout(() => reject(new Error("Screenshot capture timed out")), timeout)
|
|
520
|
+
)
|
|
521
|
+
]);
|
|
522
|
+
if (!blob) return null;
|
|
523
|
+
return new File(
|
|
524
|
+
[blob],
|
|
525
|
+
`screenshot-auto-${Date.now()}.png`,
|
|
526
|
+
{ type: "image/png" }
|
|
527
|
+
);
|
|
528
|
+
} catch (err) {
|
|
529
|
+
console.warn("BugBear: Auto-capture failed, user can attach manually", err);
|
|
530
|
+
return null;
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
|
|
423
534
|
// src/widget/screens/HomeScreen.tsx
|
|
424
|
-
import { useEffect as
|
|
535
|
+
import { useEffect as useEffect3 } from "react";
|
|
425
536
|
|
|
426
537
|
// src/widget/Skeleton.tsx
|
|
427
538
|
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
@@ -562,9 +673,9 @@ function MessageListScreenSkeleton() {
|
|
|
562
673
|
|
|
563
674
|
// src/widget/screens/HomeScreen.tsx
|
|
564
675
|
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
565
|
-
function HomeScreen({ nav }) {
|
|
676
|
+
function HomeScreen({ nav, onReportBug }) {
|
|
566
677
|
const { assignments, unreadCount, threads, refreshAssignments, refreshThreads, issueCounts, refreshIssueCounts, dashboardUrl, isLoading } = useBugBear();
|
|
567
|
-
|
|
678
|
+
useEffect3(() => {
|
|
568
679
|
refreshAssignments();
|
|
569
680
|
refreshThreads();
|
|
570
681
|
refreshIssueCounts();
|
|
@@ -751,9 +862,11 @@ function HomeScreen({ nav }) {
|
|
|
751
862
|
{
|
|
752
863
|
role: "button",
|
|
753
864
|
tabIndex: 0,
|
|
754
|
-
onClick: () => nav.push({ name: "REPORT", prefill: { type: "bug" } }),
|
|
865
|
+
onClick: () => onReportBug ? onReportBug() : nav.push({ name: "REPORT", prefill: { type: "bug" } }),
|
|
755
866
|
onKeyDown: (e) => {
|
|
756
|
-
if (e.key === "Enter" || e.key === " ")
|
|
867
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
868
|
+
onReportBug ? onReportBug() : nav.push({ name: "REPORT", prefill: { type: "bug" } });
|
|
869
|
+
}
|
|
757
870
|
},
|
|
758
871
|
style: {
|
|
759
872
|
backgroundColor: colors.card,
|
|
@@ -1019,7 +1132,7 @@ function HomeScreen({ nav }) {
|
|
|
1019
1132
|
}
|
|
1020
1133
|
|
|
1021
1134
|
// src/widget/screens/TestDetailScreen.tsx
|
|
1022
|
-
import { useState as useState2, useEffect as
|
|
1135
|
+
import { useState as useState2, useEffect as useEffect4, useCallback as useCallback2 } from "react";
|
|
1023
1136
|
import { Fragment, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1024
1137
|
function TestDetailScreen({ testId, nav }) {
|
|
1025
1138
|
const { client, assignments, currentAssignment, refreshAssignments, onNavigate } = useBugBear();
|
|
@@ -1033,12 +1146,12 @@ function TestDetailScreen({ testId, nav }) {
|
|
|
1033
1146
|
const [skipNotes, setSkipNotes] = useState2("");
|
|
1034
1147
|
const [skipping, setSkipping] = useState2(false);
|
|
1035
1148
|
const [isSubmitting, setIsSubmitting] = useState2(false);
|
|
1036
|
-
|
|
1149
|
+
useEffect4(() => {
|
|
1037
1150
|
setCriteriaResults({});
|
|
1038
1151
|
setShowSteps(true);
|
|
1039
1152
|
setShowDetails(false);
|
|
1040
1153
|
}, [displayedAssignment?.id]);
|
|
1041
|
-
|
|
1154
|
+
useEffect4(() => {
|
|
1042
1155
|
const active = displayedAssignment?.status === "in_progress" ? displayedAssignment : null;
|
|
1043
1156
|
if (!active?.startedAt) {
|
|
1044
1157
|
setAssignmentElapsedTime(0);
|
|
@@ -1623,6 +1736,11 @@ function TestDetailScreen({ testId, nav }) {
|
|
|
1623
1736
|
]
|
|
1624
1737
|
}
|
|
1625
1738
|
),
|
|
1739
|
+
testCase.track && /* @__PURE__ */ jsxs3("div", { style: { fontSize: 12, color: colors.textSecondary, marginBottom: 6 }, children: [
|
|
1740
|
+
testCase.track.icon,
|
|
1741
|
+
" ",
|
|
1742
|
+
testCase.track.name
|
|
1743
|
+
] }),
|
|
1626
1744
|
testCase.description && /* @__PURE__ */ jsx4("div", { style: { fontSize: 13, color: colors.textSecondary, lineHeight: "18px" }, children: testCase.description }),
|
|
1627
1745
|
testCase.group && /* @__PURE__ */ jsx4("div", { style: { marginTop: 8 }, children: /* @__PURE__ */ jsxs3("span", { style: { fontSize: 12, color: colors.textMuted }, children: [
|
|
1628
1746
|
"\u{1F4C1} ",
|
|
@@ -1978,13 +2096,6 @@ function TestListScreen({ nav }) {
|
|
|
1978
2096
|
}
|
|
1979
2097
|
return Array.from(trackMap.values());
|
|
1980
2098
|
}, [assignments]);
|
|
1981
|
-
const availablePlatforms = useMemo(() => {
|
|
1982
|
-
const set = /* @__PURE__ */ new Set();
|
|
1983
|
-
for (const a of assignments) {
|
|
1984
|
-
if (a.testCase.platforms) a.testCase.platforms.forEach((p) => set.add(p));
|
|
1985
|
-
}
|
|
1986
|
-
return Array.from(set).sort();
|
|
1987
|
-
}, [assignments]);
|
|
1988
2099
|
const selectedRole = availableRoles.find((r) => r.id === roleFilter);
|
|
1989
2100
|
const groupedAssignments = useMemo(() => {
|
|
1990
2101
|
const groups = /* @__PURE__ */ new Map();
|
|
@@ -2192,7 +2303,7 @@ function TestListScreen({ nav }) {
|
|
|
2192
2303
|
}
|
|
2193
2304
|
}
|
|
2194
2305
|
) }),
|
|
2195
|
-
|
|
2306
|
+
/* @__PURE__ */ jsxs4("div", { style: { display: "flex", gap: 4, marginBottom: 8 }, children: [
|
|
2196
2307
|
/* @__PURE__ */ jsx5(
|
|
2197
2308
|
"button",
|
|
2198
2309
|
{
|
|
@@ -2209,18 +2320,20 @@ function TestListScreen({ nav }) {
|
|
|
2209
2320
|
fontWeight: !platformFilter ? 600 : 400,
|
|
2210
2321
|
whiteSpace: "nowrap"
|
|
2211
2322
|
},
|
|
2212
|
-
children: "All
|
|
2323
|
+
children: "All"
|
|
2213
2324
|
}
|
|
2214
2325
|
),
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2326
|
+
[
|
|
2327
|
+
{ key: "web", label: "Web", icon: "\u{1F310}" },
|
|
2328
|
+
{ key: "ios", label: "iOS", icon: "\u{1F4F1}" },
|
|
2329
|
+
{ key: "android", label: "Android", icon: "\u{1F916}" }
|
|
2330
|
+
].map((p) => {
|
|
2331
|
+
const isActive = platformFilter === p.key;
|
|
2219
2332
|
return /* @__PURE__ */ jsxs4(
|
|
2220
2333
|
"button",
|
|
2221
2334
|
{
|
|
2222
2335
|
type: "button",
|
|
2223
|
-
onClick: () => setPlatformFilter(isActive ? null : p),
|
|
2336
|
+
onClick: () => setPlatformFilter(isActive ? null : p.key),
|
|
2224
2337
|
style: {
|
|
2225
2338
|
display: "flex",
|
|
2226
2339
|
alignItems: "center",
|
|
@@ -2236,12 +2349,12 @@ function TestListScreen({ nav }) {
|
|
|
2236
2349
|
whiteSpace: "nowrap"
|
|
2237
2350
|
},
|
|
2238
2351
|
children: [
|
|
2239
|
-
icon,
|
|
2352
|
+
p.icon,
|
|
2240
2353
|
" ",
|
|
2241
|
-
label
|
|
2354
|
+
p.label
|
|
2242
2355
|
]
|
|
2243
2356
|
},
|
|
2244
|
-
p
|
|
2357
|
+
p.key
|
|
2245
2358
|
);
|
|
2246
2359
|
})
|
|
2247
2360
|
] }),
|
|
@@ -2649,6 +2762,25 @@ function useImageAttachments(uploadFn, maxImages, bucket = "screenshots") {
|
|
|
2649
2762
|
const pickFromCamera = useCallback4(() => {
|
|
2650
2763
|
triggerFilePicker("environment");
|
|
2651
2764
|
}, [triggerFilePicker]);
|
|
2765
|
+
const addFile = useCallback4((file) => {
|
|
2766
|
+
const id = `img-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`;
|
|
2767
|
+
const localUri = URL.createObjectURL(file);
|
|
2768
|
+
const name = file.name || `image-${id}.png`;
|
|
2769
|
+
setImages((prev) => {
|
|
2770
|
+
if (prev.length >= maxImages) return prev;
|
|
2771
|
+
return [...prev, { id, localUri, remoteUrl: null, name, status: "uploading" }];
|
|
2772
|
+
});
|
|
2773
|
+
uploadFn(file, bucket).then((url) => {
|
|
2774
|
+
setImages((prev) => prev.map(
|
|
2775
|
+
(img) => img.id === id ? { ...img, remoteUrl: url, status: url ? "done" : "error" } : img
|
|
2776
|
+
));
|
|
2777
|
+
}).catch((err) => {
|
|
2778
|
+
console.error("BugBear: Image upload failed", err);
|
|
2779
|
+
setImages((prev) => prev.map(
|
|
2780
|
+
(img) => img.id === id ? { ...img, status: "error" } : img
|
|
2781
|
+
));
|
|
2782
|
+
});
|
|
2783
|
+
}, [maxImages, uploadFn, bucket]);
|
|
2652
2784
|
const removeImage = useCallback4((id) => {
|
|
2653
2785
|
setImages((prev) => {
|
|
2654
2786
|
const img = prev.find((i) => i.id === id);
|
|
@@ -2670,7 +2802,7 @@ function useImageAttachments(uploadFn, maxImages, bucket = "screenshots") {
|
|
|
2670
2802
|
const getScreenshotUrls = useCallback4(() => {
|
|
2671
2803
|
return images.filter((img) => img.status === "done" && img.remoteUrl).map((img) => img.remoteUrl);
|
|
2672
2804
|
}, [images]);
|
|
2673
|
-
return { images, pickFromGallery, pickFromCamera, removeImage, clear, isUploading, hasError, getAttachments, getScreenshotUrls };
|
|
2805
|
+
return { images, pickFromGallery, pickFromCamera, addFile, removeImage, clear, isUploading, hasError, getAttachments, getScreenshotUrls };
|
|
2674
2806
|
}
|
|
2675
2807
|
|
|
2676
2808
|
// src/widget/ImagePreviewStrip.tsx
|
|
@@ -2791,7 +2923,12 @@ function ImagePickerButtons({ images, maxImages, onPickGallery, onPickCamera, on
|
|
|
2791
2923
|
maxImages
|
|
2792
2924
|
] })
|
|
2793
2925
|
] }),
|
|
2794
|
-
/* @__PURE__ */ jsx7(ImagePreviewStrip, { images, onRemove })
|
|
2926
|
+
/* @__PURE__ */ jsx7(ImagePreviewStrip, { images, onRemove }),
|
|
2927
|
+
typeof navigator !== "undefined" && /* @__PURE__ */ jsxs6("span", { style: { fontSize: 11, color: colors.textDim, display: "block", marginTop: 4 }, children: [
|
|
2928
|
+
"Paste images with ",
|
|
2929
|
+
typeof navigator !== "undefined" && navigator.platform?.includes("Mac") ? "\u2318" : "Ctrl",
|
|
2930
|
+
"+V"
|
|
2931
|
+
] })
|
|
2795
2932
|
] });
|
|
2796
2933
|
}
|
|
2797
2934
|
|
|
@@ -3089,7 +3226,7 @@ var styles = {
|
|
|
3089
3226
|
};
|
|
3090
3227
|
|
|
3091
3228
|
// src/widget/screens/ReportScreen.tsx
|
|
3092
|
-
import React6, { useState as useState6, useRef as
|
|
3229
|
+
import React6, { useState as useState6, useRef as useRef3, useEffect as useEffect6 } from "react";
|
|
3093
3230
|
|
|
3094
3231
|
// src/widget/CategoryDropdown.tsx
|
|
3095
3232
|
import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
@@ -3135,11 +3272,63 @@ function CategoryDropdown({ value, onChange, optional = true, disabled = false }
|
|
|
3135
3272
|
);
|
|
3136
3273
|
}
|
|
3137
3274
|
|
|
3275
|
+
// src/widget/useClipboardPaste.ts
|
|
3276
|
+
import { useEffect as useEffect5, useCallback as useCallback5 } from "react";
|
|
3277
|
+
function useClipboardPaste({
|
|
3278
|
+
containerRef,
|
|
3279
|
+
enabled,
|
|
3280
|
+
currentCount,
|
|
3281
|
+
maxImages,
|
|
3282
|
+
addFile
|
|
3283
|
+
}) {
|
|
3284
|
+
const handlePaste = useCallback5((event) => {
|
|
3285
|
+
if (!enabled || currentCount >= maxImages) return;
|
|
3286
|
+
const items = event.clipboardData?.items;
|
|
3287
|
+
if (!items) return;
|
|
3288
|
+
const imageFiles = [];
|
|
3289
|
+
for (let i = 0; i < items.length; i++) {
|
|
3290
|
+
const item = items[i];
|
|
3291
|
+
if (item.type.startsWith("image/")) {
|
|
3292
|
+
const file = item.getAsFile();
|
|
3293
|
+
if (file) imageFiles.push(file);
|
|
3294
|
+
}
|
|
3295
|
+
}
|
|
3296
|
+
if (imageFiles.length === 0) return;
|
|
3297
|
+
event.preventDefault();
|
|
3298
|
+
const remaining = maxImages - currentCount;
|
|
3299
|
+
for (const file of imageFiles.slice(0, remaining)) {
|
|
3300
|
+
addFile(file);
|
|
3301
|
+
}
|
|
3302
|
+
}, [enabled, currentCount, maxImages, addFile]);
|
|
3303
|
+
useEffect5(() => {
|
|
3304
|
+
const container = containerRef.current;
|
|
3305
|
+
if (!container || !enabled) return;
|
|
3306
|
+
container.addEventListener("paste", handlePaste);
|
|
3307
|
+
return () => container.removeEventListener("paste", handlePaste);
|
|
3308
|
+
}, [containerRef, enabled, handlePaste]);
|
|
3309
|
+
}
|
|
3310
|
+
|
|
3138
3311
|
// src/widget/screens/ReportScreen.tsx
|
|
3139
3312
|
import { Fragment as Fragment3, jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
3140
|
-
function ReportScreen({ nav, prefill }) {
|
|
3313
|
+
function ReportScreen({ nav, prefill, autoCapture, onAutoCaptureConsumed }) {
|
|
3141
3314
|
const { client, refreshAssignments, uploadImage } = useBugBear();
|
|
3142
3315
|
const images = useImageAttachments(uploadImage, 5, "screenshots");
|
|
3316
|
+
const formRef = useRef3(null);
|
|
3317
|
+
const hasConsumedCapture = useRef3(false);
|
|
3318
|
+
useEffect6(() => {
|
|
3319
|
+
if (autoCapture && !hasConsumedCapture.current) {
|
|
3320
|
+
hasConsumedCapture.current = true;
|
|
3321
|
+
images.addFile(autoCapture);
|
|
3322
|
+
onAutoCaptureConsumed?.();
|
|
3323
|
+
}
|
|
3324
|
+
}, [autoCapture, onAutoCaptureConsumed]);
|
|
3325
|
+
useClipboardPaste({
|
|
3326
|
+
containerRef: formRef,
|
|
3327
|
+
enabled: true,
|
|
3328
|
+
currentCount: images.images.length,
|
|
3329
|
+
maxImages: 5,
|
|
3330
|
+
addFile: images.addFile
|
|
3331
|
+
});
|
|
3143
3332
|
const [reportType, setReportType] = useState6(prefill?.type || "bug");
|
|
3144
3333
|
const [severity, setSeverity] = useState6("medium");
|
|
3145
3334
|
const [category, setCategory] = useState6(null);
|
|
@@ -3147,7 +3336,7 @@ function ReportScreen({ nav, prefill }) {
|
|
|
3147
3336
|
const [affectedRoute, setAffectedRoute] = useState6("");
|
|
3148
3337
|
const [submitting, setSubmitting] = useState6(false);
|
|
3149
3338
|
const [error, setError] = useState6(null);
|
|
3150
|
-
const submittingRef =
|
|
3339
|
+
const submittingRef = useRef3(false);
|
|
3151
3340
|
React6.useEffect(() => {
|
|
3152
3341
|
if (reportType === "feedback" || reportType === "suggestion") {
|
|
3153
3342
|
setCategory("other");
|
|
@@ -3155,7 +3344,7 @@ function ReportScreen({ nav, prefill }) {
|
|
|
3155
3344
|
setCategory(null);
|
|
3156
3345
|
}
|
|
3157
3346
|
}, [reportType]);
|
|
3158
|
-
const observedRoute =
|
|
3347
|
+
const observedRoute = useRef3(
|
|
3159
3348
|
typeof window !== "undefined" ? window.location.pathname : "unknown"
|
|
3160
3349
|
);
|
|
3161
3350
|
const isRetestFailure = prefill?.type === "test_fail";
|
|
@@ -3211,7 +3400,7 @@ function ReportScreen({ nav, prefill }) {
|
|
|
3211
3400
|
{ sev: "medium", color: "#eab308" },
|
|
3212
3401
|
{ sev: "low", color: "#6b7280" }
|
|
3213
3402
|
];
|
|
3214
|
-
return /* @__PURE__ */ jsx10("div", { children: isRetestFailure ? /* @__PURE__ */ jsxs9(Fragment3, { children: [
|
|
3403
|
+
return /* @__PURE__ */ jsx10("div", { ref: formRef, tabIndex: -1, style: { outline: "none" }, children: isRetestFailure ? /* @__PURE__ */ jsxs9(Fragment3, { children: [
|
|
3215
3404
|
/* @__PURE__ */ jsxs9("div", { style: styles2.retestBanner, children: [
|
|
3216
3405
|
/* @__PURE__ */ jsx10("span", { style: { fontSize: 16 }, children: "\u{1F504}" }),
|
|
3217
3406
|
/* @__PURE__ */ jsxs9("div", { children: [
|
|
@@ -3539,10 +3728,10 @@ var styles2 = {
|
|
|
3539
3728
|
};
|
|
3540
3729
|
|
|
3541
3730
|
// src/widget/screens/ReportSuccessScreen.tsx
|
|
3542
|
-
import { useEffect as
|
|
3731
|
+
import { useEffect as useEffect7 } from "react";
|
|
3543
3732
|
import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
3544
3733
|
function ReportSuccessScreen({ nav }) {
|
|
3545
|
-
|
|
3734
|
+
useEffect7(() => {
|
|
3546
3735
|
const timer = setTimeout(() => nav.reset(), 2e3);
|
|
3547
3736
|
return () => clearTimeout(timer);
|
|
3548
3737
|
}, [nav]);
|
|
@@ -3832,7 +4021,7 @@ function MessageListScreen({ nav }) {
|
|
|
3832
4021
|
}
|
|
3833
4022
|
|
|
3834
4023
|
// src/widget/screens/ThreadDetailScreen.tsx
|
|
3835
|
-
import { useState as useState7, useEffect as
|
|
4024
|
+
import { useState as useState7, useEffect as useEffect8 } from "react";
|
|
3836
4025
|
import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
3837
4026
|
var inputStyle = {
|
|
3838
4027
|
backgroundColor: "#27272a",
|
|
@@ -3854,7 +4043,7 @@ function ThreadDetailScreen({
|
|
|
3854
4043
|
const [replyText, setReplyText] = useState7("");
|
|
3855
4044
|
const [sending, setSending] = useState7(false);
|
|
3856
4045
|
const [sendError, setSendError] = useState7(false);
|
|
3857
|
-
|
|
4046
|
+
useEffect8(() => {
|
|
3858
4047
|
let cancelled = false;
|
|
3859
4048
|
setLoading(true);
|
|
3860
4049
|
(async () => {
|
|
@@ -4279,7 +4468,7 @@ function ComposeMessageScreen({ nav }) {
|
|
|
4279
4468
|
}
|
|
4280
4469
|
|
|
4281
4470
|
// src/widget/screens/ProfileScreen.tsx
|
|
4282
|
-
import { useState as useState9, useEffect as
|
|
4471
|
+
import { useState as useState9, useEffect as useEffect9 } from "react";
|
|
4283
4472
|
import { jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
4284
4473
|
function ProfileScreen({ nav }) {
|
|
4285
4474
|
const { testerInfo, assignments, updateTesterProfile, refreshTesterInfo } = useBugBear();
|
|
@@ -4292,7 +4481,7 @@ function ProfileScreen({ nav }) {
|
|
|
4292
4481
|
const [saved, setSaved] = useState9(false);
|
|
4293
4482
|
const [showDetails, setShowDetails] = useState9(false);
|
|
4294
4483
|
const completedCount = assignments.filter((a) => a.status === "passed" || a.status === "failed").length;
|
|
4295
|
-
|
|
4484
|
+
useEffect9(() => {
|
|
4296
4485
|
if (testerInfo) {
|
|
4297
4486
|
setName(testerInfo.name);
|
|
4298
4487
|
setAdditionalEmails(testerInfo.additionalEmails || []);
|
|
@@ -4756,7 +4945,7 @@ var styles4 = {
|
|
|
4756
4945
|
};
|
|
4757
4946
|
|
|
4758
4947
|
// src/widget/screens/IssueListScreen.tsx
|
|
4759
|
-
import { useState as useState10, useEffect as
|
|
4948
|
+
import { useState as useState10, useEffect as useEffect10 } from "react";
|
|
4760
4949
|
import { jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
4761
4950
|
var CATEGORY_CONFIG = {
|
|
4762
4951
|
open: { label: "Open Issues", accent: "#f97316", emptyIcon: "\u2705", emptyText: "No open issues" },
|
|
@@ -4774,7 +4963,7 @@ function IssueListScreen({ nav, category }) {
|
|
|
4774
4963
|
const [issues, setIssues] = useState10([]);
|
|
4775
4964
|
const [loading, setLoading] = useState10(true);
|
|
4776
4965
|
const config = CATEGORY_CONFIG[category];
|
|
4777
|
-
|
|
4966
|
+
useEffect10(() => {
|
|
4778
4967
|
let cancelled = false;
|
|
4779
4968
|
setLoading(true);
|
|
4780
4969
|
(async () => {
|
|
@@ -5116,11 +5305,12 @@ function BugBearPanel({
|
|
|
5116
5305
|
const { shouldShowWidget, testerInfo, assignments, isLoading, unreadCount } = useBugBear();
|
|
5117
5306
|
const { currentScreen, canGoBack, push, pop, replace, reset } = useNavigation();
|
|
5118
5307
|
const [collapsed, setCollapsed] = useState11(defaultCollapsed);
|
|
5308
|
+
const autoCaptureRef = useRef4(null);
|
|
5119
5309
|
const [panelPosition, setPanelPosition] = useState11(null);
|
|
5120
5310
|
const [isDragging, setIsDragging] = useState11(false);
|
|
5121
|
-
const dragStartRef =
|
|
5122
|
-
const panelRef =
|
|
5123
|
-
|
|
5311
|
+
const dragStartRef = useRef4(null);
|
|
5312
|
+
const panelRef = useRef4(null);
|
|
5313
|
+
useEffect11(() => {
|
|
5124
5314
|
if (typeof window === "undefined") return;
|
|
5125
5315
|
try {
|
|
5126
5316
|
const saved = localStorage.getItem(STORAGE_KEY);
|
|
@@ -5134,7 +5324,7 @@ function BugBearPanel({
|
|
|
5134
5324
|
setPanelPosition(getDefaultPosition(position));
|
|
5135
5325
|
}
|
|
5136
5326
|
}, [position]);
|
|
5137
|
-
|
|
5327
|
+
useEffect11(() => {
|
|
5138
5328
|
if (panelPosition && typeof window !== "undefined") {
|
|
5139
5329
|
try {
|
|
5140
5330
|
localStorage.setItem(STORAGE_KEY, JSON.stringify(panelPosition));
|
|
@@ -5142,7 +5332,7 @@ function BugBearPanel({
|
|
|
5142
5332
|
}
|
|
5143
5333
|
}
|
|
5144
5334
|
}, [panelPosition]);
|
|
5145
|
-
|
|
5335
|
+
useEffect11(() => {
|
|
5146
5336
|
if (typeof window === "undefined") return;
|
|
5147
5337
|
const handleResize = () => {
|
|
5148
5338
|
setPanelPosition((prev) => prev ? clampPosition(prev) : getDefaultPosition(position));
|
|
@@ -5150,7 +5340,7 @@ function BugBearPanel({
|
|
|
5150
5340
|
window.addEventListener("resize", handleResize);
|
|
5151
5341
|
return () => window.removeEventListener("resize", handleResize);
|
|
5152
5342
|
}, [position]);
|
|
5153
|
-
const handleMouseDown =
|
|
5343
|
+
const handleMouseDown = useCallback6((e) => {
|
|
5154
5344
|
if (!draggable || !panelPosition) return;
|
|
5155
5345
|
const target = e.target;
|
|
5156
5346
|
if (!target.closest("[data-drag-handle]")) return;
|
|
@@ -5163,7 +5353,7 @@ function BugBearPanel({
|
|
|
5163
5353
|
panelY: panelPosition.y
|
|
5164
5354
|
};
|
|
5165
5355
|
}, [draggable, panelPosition]);
|
|
5166
|
-
|
|
5356
|
+
useEffect11(() => {
|
|
5167
5357
|
if (!isDragging) return;
|
|
5168
5358
|
const handleMouseMove = (e) => {
|
|
5169
5359
|
if (!dragStartRef.current) return;
|
|
@@ -5185,7 +5375,7 @@ function BugBearPanel({
|
|
|
5185
5375
|
document.removeEventListener("mouseup", handleMouseUp);
|
|
5186
5376
|
};
|
|
5187
5377
|
}, [isDragging]);
|
|
5188
|
-
const handleDoubleClick =
|
|
5378
|
+
const handleDoubleClick = useCallback6(() => {
|
|
5189
5379
|
if (!draggable) return;
|
|
5190
5380
|
setPanelPosition(getDefaultPosition(position));
|
|
5191
5381
|
try {
|
|
@@ -5193,6 +5383,14 @@ function BugBearPanel({
|
|
|
5193
5383
|
} catch {
|
|
5194
5384
|
}
|
|
5195
5385
|
}, [draggable, position]);
|
|
5386
|
+
const handleReportBug = useCallback6(async () => {
|
|
5387
|
+
const file = await capturePageScreenshot({
|
|
5388
|
+
excludeElement: panelRef.current,
|
|
5389
|
+
timeout: 3e3
|
|
5390
|
+
});
|
|
5391
|
+
autoCaptureRef.current = file;
|
|
5392
|
+
push({ name: "REPORT", prefill: { type: "bug" } });
|
|
5393
|
+
}, [push]);
|
|
5196
5394
|
if (isLoading || !shouldShowWidget) return null;
|
|
5197
5395
|
if (!panelPosition) return null;
|
|
5198
5396
|
const pendingCount = assignments.filter((a) => a.status === "pending" || a.status === "in_progress").length;
|
|
@@ -5229,13 +5427,12 @@ function BugBearPanel({
|
|
|
5229
5427
|
};
|
|
5230
5428
|
const handleClose = () => {
|
|
5231
5429
|
setCollapsed(true);
|
|
5232
|
-
reset();
|
|
5233
5430
|
};
|
|
5234
5431
|
const nav = { push, pop, replace, reset, canGoBack };
|
|
5235
5432
|
const renderScreen = () => {
|
|
5236
5433
|
switch (currentScreen.name) {
|
|
5237
5434
|
case "HOME":
|
|
5238
|
-
return /* @__PURE__ */ jsx18(HomeScreen, { nav });
|
|
5435
|
+
return /* @__PURE__ */ jsx18(HomeScreen, { nav, onReportBug: handleReportBug });
|
|
5239
5436
|
case "TEST_DETAIL":
|
|
5240
5437
|
return /* @__PURE__ */ jsx18(TestDetailScreen, { testId: currentScreen.testId, nav });
|
|
5241
5438
|
case "TEST_LIST":
|
|
@@ -5243,7 +5440,17 @@ function BugBearPanel({
|
|
|
5243
5440
|
case "TEST_FEEDBACK":
|
|
5244
5441
|
return /* @__PURE__ */ jsx18(TestFeedbackScreen, { status: currentScreen.status, assignmentId: currentScreen.assignmentId, nav });
|
|
5245
5442
|
case "REPORT":
|
|
5246
|
-
return /* @__PURE__ */ jsx18(
|
|
5443
|
+
return /* @__PURE__ */ jsx18(
|
|
5444
|
+
ReportScreen,
|
|
5445
|
+
{
|
|
5446
|
+
nav,
|
|
5447
|
+
prefill: currentScreen.prefill,
|
|
5448
|
+
autoCapture: autoCaptureRef.current,
|
|
5449
|
+
onAutoCaptureConsumed: () => {
|
|
5450
|
+
autoCaptureRef.current = null;
|
|
5451
|
+
}
|
|
5452
|
+
}
|
|
5453
|
+
);
|
|
5247
5454
|
case "REPORT_SUCCESS":
|
|
5248
5455
|
return /* @__PURE__ */ jsx18(ReportSuccessScreen, { nav });
|
|
5249
5456
|
case "MESSAGE_LIST":
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bbearai/react",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.2",
|
|
4
4
|
"description": "BugBear React components for web apps",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -41,13 +41,15 @@
|
|
|
41
41
|
"prepublishOnly": "npm run build"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@bbearai/core": "^0.5.0"
|
|
44
|
+
"@bbearai/core": "^0.5.0",
|
|
45
|
+
"modern-screenshot": "^4.6.8"
|
|
45
46
|
},
|
|
46
47
|
"peerDependencies": {
|
|
47
48
|
"react": "^18.0.0 || ^19.0.0",
|
|
48
49
|
"react-dom": "^18.0.0 || ^19.0.0"
|
|
49
50
|
},
|
|
50
51
|
"devDependencies": {
|
|
52
|
+
"@testing-library/dom": "^10.4.1",
|
|
51
53
|
"@testing-library/jest-dom": "^6.9.1",
|
|
52
54
|
"@testing-library/react": "^16.3.2",
|
|
53
55
|
"@testing-library/user-event": "^14.6.1",
|