@bbearai/react 0.5.0 → 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.
Files changed (3) hide show
  1. package/dist/index.js +568 -72
  2. package/dist/index.mjs +542 -46
  3. 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 import_react14 = require("react");
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 [state, dispatch] = (0, import_react2.useReducer)(navReducer, { stack: [{ name: "HOME" }] });
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: state.stack[state.stack.length - 1],
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,8 +712,8 @@ 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 }) {
605
- const { assignments, unreadCount, threads, refreshAssignments, refreshThreads, issueCounts, refreshIssueCounts, isLoading } = useBugBear();
715
+ function HomeScreen({ nav, onReportBug }) {
716
+ const { assignments, unreadCount, threads, refreshAssignments, refreshThreads, issueCounts, refreshIssueCounts, dashboardUrl, isLoading } = useBugBear();
606
717
  (0, import_react3.useEffect)(() => {
607
718
  refreshAssignments();
608
719
  refreshThreads();
@@ -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 === " ") nav.push({ name: "REPORT", prefill: { type: "bug" } });
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,
@@ -1007,6 +1120,33 @@ function HomeScreen({ nav }) {
1007
1120
  " tests completed"
1008
1121
  ] })
1009
1122
  ] }),
1123
+ dashboardUrl && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1124
+ "a",
1125
+ {
1126
+ href: dashboardUrl,
1127
+ target: "_blank",
1128
+ rel: "noopener noreferrer",
1129
+ style: {
1130
+ display: "flex",
1131
+ alignItems: "center",
1132
+ backgroundColor: colors.card,
1133
+ border: `1px solid ${colors.border}`,
1134
+ borderRadius: 12,
1135
+ padding: 14,
1136
+ marginBottom: 16,
1137
+ textDecoration: "none",
1138
+ cursor: "pointer"
1139
+ },
1140
+ children: [
1141
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { fontSize: 22, marginRight: 12 }, children: "\u{1F310}" }),
1142
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { style: { flex: 1 }, children: [
1143
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { display: "block", fontSize: 14, fontWeight: 600, color: colors.textPrimary }, children: "Open Dashboard" }),
1144
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { display: "block", fontSize: 12, color: colors.textMuted, marginTop: 2 }, children: "View analytics, history & more" })
1145
+ ] }),
1146
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { fontSize: 16, color: colors.textMuted, marginLeft: 8 }, children: "\u2192" })
1147
+ ]
1148
+ }
1149
+ ),
1010
1150
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { display: "flex", justifyContent: "center", padding: "8px 0" }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1011
1151
  "button",
1012
1152
  {
@@ -1097,6 +1237,37 @@ function TestDetailScreen({ testId, nav }) {
1097
1237
  setIsSubmitting(false);
1098
1238
  }
1099
1239
  }, [client, displayedAssignment, refreshAssignments, nav, isSubmitting]);
1240
+ const handleReopen = (0, import_react4.useCallback)(async () => {
1241
+ if (!client || !displayedAssignment || isSubmitting) return;
1242
+ setIsSubmitting(true);
1243
+ try {
1244
+ await client.reopenAssignment(displayedAssignment.id);
1245
+ await refreshAssignments();
1246
+ } finally {
1247
+ setIsSubmitting(false);
1248
+ }
1249
+ }, [client, displayedAssignment, refreshAssignments, isSubmitting]);
1250
+ const handleChangeResult = (0, import_react4.useCallback)(async (newStatus) => {
1251
+ if (!client || !displayedAssignment || isSubmitting) return;
1252
+ setIsSubmitting(true);
1253
+ try {
1254
+ await client.reopenAssignment(displayedAssignment.id);
1255
+ await client.updateAssignmentStatus(displayedAssignment.id, newStatus);
1256
+ await refreshAssignments();
1257
+ if (newStatus === "failed") {
1258
+ nav.replace({
1259
+ name: "REPORT",
1260
+ prefill: {
1261
+ type: "test_fail",
1262
+ assignmentId: displayedAssignment.id,
1263
+ testCaseId: displayedAssignment.testCase.id
1264
+ }
1265
+ });
1266
+ }
1267
+ } finally {
1268
+ setIsSubmitting(false);
1269
+ }
1270
+ }, [client, displayedAssignment, refreshAssignments, nav, isSubmitting]);
1100
1271
  const handleSkip = (0, import_react4.useCallback)(async () => {
1101
1272
  if (!client || !displayedAssignment || !selectedSkipReason) return;
1102
1273
  setSkipping(true);
@@ -1604,6 +1775,11 @@ function TestDetailScreen({ testId, nav }) {
1604
1775
  ]
1605
1776
  }
1606
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
+ ] }),
1607
1783
  testCase.description && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { fontSize: 13, color: colors.textSecondary, lineHeight: "18px" }, children: testCase.description }),
1608
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: [
1609
1785
  "\u{1F4C1} ",
@@ -1612,7 +1788,113 @@ function TestDetailScreen({ testId, nav }) {
1612
1788
  ]
1613
1789
  }
1614
1790
  ),
1615
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", gap: 10, marginTop: 8 }, children: [
1791
+ displayedAssignment.status === "passed" || displayedAssignment.status === "failed" || displayedAssignment.status === "skipped" || displayedAssignment.status === "blocked" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
1792
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1793
+ "div",
1794
+ {
1795
+ style: {
1796
+ display: "flex",
1797
+ alignItems: "center",
1798
+ justifyContent: "center",
1799
+ gap: 6,
1800
+ padding: "8px 12px",
1801
+ borderRadius: 8,
1802
+ marginTop: 8,
1803
+ marginBottom: 8,
1804
+ backgroundColor: displayedAssignment.status === "passed" ? colors.greenDark : displayedAssignment.status === "failed" ? colors.redDark : colors.card,
1805
+ border: `1px solid ${displayedAssignment.status === "passed" ? colors.green : displayedAssignment.status === "failed" ? colors.red : colors.border}`
1806
+ },
1807
+ children: [
1808
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { fontSize: 14 }, children: displayedAssignment.status === "passed" ? "\u2705" : displayedAssignment.status === "failed" ? "\u274C" : displayedAssignment.status === "skipped" ? "\u23ED" : "\u{1F6AB}" }),
1809
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1810
+ "span",
1811
+ {
1812
+ style: {
1813
+ fontSize: 13,
1814
+ fontWeight: 600,
1815
+ color: displayedAssignment.status === "passed" ? colors.green : displayedAssignment.status === "failed" ? "#fca5a5" : colors.textSecondary
1816
+ },
1817
+ children: [
1818
+ "Marked as ",
1819
+ displayedAssignment.status.charAt(0).toUpperCase() + displayedAssignment.status.slice(1)
1820
+ ]
1821
+ }
1822
+ )
1823
+ ]
1824
+ }
1825
+ ),
1826
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", gap: 10, marginTop: 4 }, children: [
1827
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1828
+ "button",
1829
+ {
1830
+ type: "button",
1831
+ onClick: handleReopen,
1832
+ disabled: isSubmitting,
1833
+ style: {
1834
+ flex: 1,
1835
+ paddingTop: 14,
1836
+ paddingBottom: 14,
1837
+ borderRadius: 12,
1838
+ textAlign: "center",
1839
+ backgroundColor: colors.card,
1840
+ border: `1px solid ${colors.blue}`,
1841
+ cursor: isSubmitting ? "not-allowed" : "pointer",
1842
+ fontSize: 14,
1843
+ fontWeight: 600,
1844
+ color: colors.blue,
1845
+ opacity: isSubmitting ? 0.5 : 1
1846
+ },
1847
+ children: isSubmitting ? "Reopening..." : "\u{1F504} Reopen Test"
1848
+ }
1849
+ ),
1850
+ displayedAssignment.status === "passed" && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1851
+ "button",
1852
+ {
1853
+ type: "button",
1854
+ onClick: () => handleChangeResult("failed"),
1855
+ disabled: isSubmitting,
1856
+ style: {
1857
+ flex: 1,
1858
+ paddingTop: 14,
1859
+ paddingBottom: 14,
1860
+ borderRadius: 12,
1861
+ textAlign: "center",
1862
+ backgroundColor: colors.redDark,
1863
+ border: `1px solid ${colors.red}`,
1864
+ cursor: isSubmitting ? "not-allowed" : "pointer",
1865
+ fontSize: 14,
1866
+ fontWeight: 600,
1867
+ color: "#fca5a5",
1868
+ opacity: isSubmitting ? 0.5 : 1
1869
+ },
1870
+ children: "Change to Fail"
1871
+ }
1872
+ ),
1873
+ displayedAssignment.status === "failed" && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1874
+ "button",
1875
+ {
1876
+ type: "button",
1877
+ onClick: () => handleChangeResult("passed"),
1878
+ disabled: isSubmitting,
1879
+ style: {
1880
+ flex: 1,
1881
+ paddingTop: 14,
1882
+ paddingBottom: 14,
1883
+ borderRadius: 12,
1884
+ textAlign: "center",
1885
+ backgroundColor: colors.greenDark,
1886
+ border: `1px solid ${colors.green}`,
1887
+ cursor: isSubmitting ? "not-allowed" : "pointer",
1888
+ fontSize: 14,
1889
+ fontWeight: 600,
1890
+ color: "#86efac",
1891
+ opacity: isSubmitting ? 0.5 : 1
1892
+ },
1893
+ children: "Change to Pass"
1894
+ }
1895
+ )
1896
+ ] })
1897
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", gap: 10, marginTop: 8 }, children: [
1616
1898
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1617
1899
  "button",
1618
1900
  {
@@ -1831,10 +2113,11 @@ function TestDetailScreen({ testId, nav }) {
1831
2113
  var import_react5 = require("react");
1832
2114
  var import_jsx_runtime5 = require("react/jsx-runtime");
1833
2115
  function TestListScreen({ nav }) {
1834
- const { assignments, currentAssignment, refreshAssignments, isLoading } = useBugBear();
2116
+ const { assignments, currentAssignment, refreshAssignments, dashboardUrl, isLoading } = useBugBear();
1835
2117
  const [filter, setFilter] = (0, import_react5.useState)("all");
1836
2118
  const [roleFilter, setRoleFilter] = (0, import_react5.useState)(null);
1837
2119
  const [trackFilter, setTrackFilter] = (0, import_react5.useState)(null);
2120
+ const [platformFilter, setPlatformFilter] = (0, import_react5.useState)("web");
1838
2121
  const [searchQuery, setSearchQuery] = (0, import_react5.useState)("");
1839
2122
  const [sortMode, setSortMode] = (0, import_react5.useState)("priority");
1840
2123
  const [collapsedFolders, setCollapsedFolders] = (0, import_react5.useState)(/* @__PURE__ */ new Set());
@@ -1912,6 +2195,7 @@ function TestListScreen({ nav }) {
1912
2195
  });
1913
2196
  }, []);
1914
2197
  const filterAssignment = (0, import_react5.useCallback)((a) => {
2198
+ if (platformFilter && a.testCase.platforms && !a.testCase.platforms.includes(platformFilter)) return false;
1915
2199
  if (roleFilter && a.testCase.role?.id !== roleFilter) return false;
1916
2200
  if (trackFilter && a.testCase.track?.id !== trackFilter) return false;
1917
2201
  if (searchQuery) {
@@ -1924,7 +2208,7 @@ function TestListScreen({ nav }) {
1924
2208
  if (filter === "done") return a.status === "passed";
1925
2209
  if (filter === "reopened") return a.status === "failed";
1926
2210
  return true;
1927
- }, [roleFilter, trackFilter, searchQuery, filter]);
2211
+ }, [platformFilter, roleFilter, trackFilter, searchQuery, filter]);
1928
2212
  const pendingCount = assignments.filter(
1929
2213
  (a) => a.status === "pending" || a.status === "in_progress"
1930
2214
  ).length;
@@ -2058,6 +2342,61 @@ function TestListScreen({ nav }) {
2058
2342
  }
2059
2343
  }
2060
2344
  ) }),
2345
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { display: "flex", gap: 4, marginBottom: 8 }, children: [
2346
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2347
+ "button",
2348
+ {
2349
+ type: "button",
2350
+ onClick: () => setPlatformFilter(null),
2351
+ style: {
2352
+ padding: "3px 8px",
2353
+ borderRadius: 6,
2354
+ backgroundColor: !platformFilter ? colors.card : "transparent",
2355
+ border: !platformFilter ? `1px solid ${colors.border}` : "1px solid transparent",
2356
+ cursor: "pointer",
2357
+ fontSize: 11,
2358
+ color: !platformFilter ? colors.textPrimary : colors.textMuted,
2359
+ fontWeight: !platformFilter ? 600 : 400,
2360
+ whiteSpace: "nowrap"
2361
+ },
2362
+ children: "All"
2363
+ }
2364
+ ),
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;
2371
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
2372
+ "button",
2373
+ {
2374
+ type: "button",
2375
+ onClick: () => setPlatformFilter(isActive ? null : p.key),
2376
+ style: {
2377
+ display: "flex",
2378
+ alignItems: "center",
2379
+ gap: 4,
2380
+ padding: "3px 8px",
2381
+ borderRadius: 6,
2382
+ backgroundColor: isActive ? colors.blue + "20" : "transparent",
2383
+ border: isActive ? `1px solid ${colors.blue}60` : "1px solid transparent",
2384
+ cursor: "pointer",
2385
+ fontSize: 11,
2386
+ color: isActive ? colors.blue : colors.textMuted,
2387
+ fontWeight: isActive ? 600 : 400,
2388
+ whiteSpace: "nowrap"
2389
+ },
2390
+ children: [
2391
+ p.icon,
2392
+ " ",
2393
+ p.label
2394
+ ]
2395
+ },
2396
+ p.key
2397
+ );
2398
+ })
2399
+ ] }),
2061
2400
  (availableTracks.length >= 2 || true) && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 6, marginBottom: 12 }, children: [
2062
2401
  availableTracks.length >= 2 && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { display: "flex", gap: 4, flex: 1, overflow: "auto" }, children: [
2063
2402
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
@@ -2371,7 +2710,27 @@ function TestListScreen({ nav }) {
2371
2710
  ]
2372
2711
  }
2373
2712
  ),
2374
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: { display: "flex", justifyContent: "center", paddingTop: 12, paddingBottom: 8 }, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2713
+ dashboardUrl && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2714
+ "a",
2715
+ {
2716
+ href: `${dashboardUrl}/test-cases`,
2717
+ target: "_blank",
2718
+ rel: "noopener noreferrer",
2719
+ style: {
2720
+ display: "flex",
2721
+ alignItems: "center",
2722
+ justifyContent: "center",
2723
+ gap: 6,
2724
+ paddingTop: 12,
2725
+ fontSize: 13,
2726
+ fontWeight: 500,
2727
+ color: colors.blue,
2728
+ textDecoration: "none"
2729
+ },
2730
+ children: "\u{1F310} Manage on Dashboard \u2192"
2731
+ }
2732
+ ),
2733
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: { display: "flex", justifyContent: "center", paddingTop: 8, paddingBottom: 8 }, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2375
2734
  "button",
2376
2735
  {
2377
2736
  type: "button",
@@ -2442,6 +2801,25 @@ function useImageAttachments(uploadFn, maxImages, bucket = "screenshots") {
2442
2801
  const pickFromCamera = (0, import_react6.useCallback)(() => {
2443
2802
  triggerFilePicker("environment");
2444
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]);
2445
2823
  const removeImage = (0, import_react6.useCallback)((id) => {
2446
2824
  setImages((prev) => {
2447
2825
  const img = prev.find((i) => i.id === id);
@@ -2463,7 +2841,7 @@ function useImageAttachments(uploadFn, maxImages, bucket = "screenshots") {
2463
2841
  const getScreenshotUrls = (0, import_react6.useCallback)(() => {
2464
2842
  return images.filter((img) => img.status === "done" && img.remoteUrl).map((img) => img.remoteUrl);
2465
2843
  }, [images]);
2466
- return { images, pickFromGallery, pickFromCamera, removeImage, clear, isUploading, hasError, getAttachments, getScreenshotUrls };
2844
+ return { images, pickFromGallery, pickFromCamera, addFile, removeImage, clear, isUploading, hasError, getAttachments, getScreenshotUrls };
2467
2845
  }
2468
2846
 
2469
2847
  // src/widget/ImagePreviewStrip.tsx
@@ -2584,7 +2962,12 @@ function ImagePickerButtons({ images, maxImages, onPickGallery, onPickCamera, on
2584
2962
  maxImages
2585
2963
  ] })
2586
2964
  ] }),
2587
- /* @__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
+ ] })
2588
2971
  ] });
2589
2972
  }
2590
2973
 
@@ -2882,7 +3265,7 @@ var styles = {
2882
3265
  };
2883
3266
 
2884
3267
  // src/widget/screens/ReportScreen.tsx
2885
- var import_react8 = __toESM(require("react"));
3268
+ var import_react9 = __toESM(require("react"));
2886
3269
 
2887
3270
  // src/widget/CategoryDropdown.tsx
2888
3271
  var import_jsx_runtime9 = require("react/jsx-runtime");
@@ -2928,27 +3311,79 @@ function CategoryDropdown({ value, onChange, optional = true, disabled = false }
2928
3311
  );
2929
3312
  }
2930
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
+
2931
3350
  // src/widget/screens/ReportScreen.tsx
2932
3351
  var import_jsx_runtime10 = require("react/jsx-runtime");
2933
- function ReportScreen({ nav, prefill }) {
3352
+ function ReportScreen({ nav, prefill, autoCapture, onAutoCaptureConsumed }) {
2934
3353
  const { client, refreshAssignments, uploadImage } = useBugBear();
2935
3354
  const images = useImageAttachments(uploadImage, 5, "screenshots");
2936
- const [reportType, setReportType] = (0, import_react8.useState)(prefill?.type || "bug");
2937
- const [severity, setSeverity] = (0, import_react8.useState)("medium");
2938
- const [category, setCategory] = (0, import_react8.useState)(null);
2939
- const [description, setDescription] = (0, import_react8.useState)("");
2940
- const [affectedRoute, setAffectedRoute] = (0, import_react8.useState)("");
2941
- const [submitting, setSubmitting] = (0, import_react8.useState)(false);
2942
- const [error, setError] = (0, import_react8.useState)(null);
2943
- const submittingRef = (0, import_react8.useRef)(false);
2944
- import_react8.default.useEffect(() => {
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(() => {
2945
3380
  if (reportType === "feedback" || reportType === "suggestion") {
2946
3381
  setCategory("other");
2947
3382
  } else {
2948
3383
  setCategory(null);
2949
3384
  }
2950
3385
  }, [reportType]);
2951
- const observedRoute = (0, import_react8.useRef)(
3386
+ const observedRoute = (0, import_react9.useRef)(
2952
3387
  typeof window !== "undefined" ? window.location.pathname : "unknown"
2953
3388
  );
2954
3389
  const isRetestFailure = prefill?.type === "test_fail";
@@ -3004,7 +3439,7 @@ function ReportScreen({ nav, prefill }) {
3004
3439
  { sev: "medium", color: "#eab308" },
3005
3440
  { sev: "low", color: "#6b7280" }
3006
3441
  ];
3007
- 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: [
3008
3443
  /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: styles2.retestBanner, children: [
3009
3444
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { style: { fontSize: 16 }, children: "\u{1F504}" }),
3010
3445
  /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { children: [
@@ -3332,10 +3767,10 @@ var styles2 = {
3332
3767
  };
3333
3768
 
3334
3769
  // src/widget/screens/ReportSuccessScreen.tsx
3335
- var import_react9 = require("react");
3770
+ var import_react10 = require("react");
3336
3771
  var import_jsx_runtime11 = require("react/jsx-runtime");
3337
3772
  function ReportSuccessScreen({ nav }) {
3338
- (0, import_react9.useEffect)(() => {
3773
+ (0, import_react10.useEffect)(() => {
3339
3774
  const timer = setTimeout(() => nav.reset(), 2e3);
3340
3775
  return () => clearTimeout(timer);
3341
3776
  }, [nav]);
@@ -3373,7 +3808,7 @@ var styles3 = {
3373
3808
  // src/widget/screens/MessageListScreen.tsx
3374
3809
  var import_jsx_runtime12 = require("react/jsx-runtime");
3375
3810
  function MessageListScreen({ nav }) {
3376
- const { threads, unreadCount, refreshThreads, isLoading } = useBugBear();
3811
+ const { threads, unreadCount, refreshThreads, dashboardUrl, isLoading } = useBugBear();
3377
3812
  if (isLoading) return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(MessageListScreenSkeleton, {});
3378
3813
  return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { children: [
3379
3814
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
@@ -3562,6 +3997,26 @@ function MessageListScreen({ nav }) {
3562
3997
  },
3563
3998
  thread.id
3564
3999
  )) }),
4000
+ dashboardUrl && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
4001
+ "a",
4002
+ {
4003
+ href: `${dashboardUrl}/discussions`,
4004
+ target: "_blank",
4005
+ rel: "noopener noreferrer",
4006
+ style: {
4007
+ display: "flex",
4008
+ alignItems: "center",
4009
+ justifyContent: "center",
4010
+ gap: 6,
4011
+ paddingTop: 12,
4012
+ fontSize: 13,
4013
+ fontWeight: 500,
4014
+ color: colors.blue,
4015
+ textDecoration: "none"
4016
+ },
4017
+ children: "\u{1F310} View on Dashboard \u2192"
4018
+ }
4019
+ ),
3565
4020
  /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
3566
4021
  "div",
3567
4022
  {
@@ -3569,7 +4024,7 @@ function MessageListScreen({ nav }) {
3569
4024
  display: "flex",
3570
4025
  justifyContent: "space-between",
3571
4026
  alignItems: "center",
3572
- paddingTop: 12,
4027
+ paddingTop: 8,
3573
4028
  paddingLeft: 4,
3574
4029
  paddingRight: 4
3575
4030
  },
@@ -3605,7 +4060,7 @@ function MessageListScreen({ nav }) {
3605
4060
  }
3606
4061
 
3607
4062
  // src/widget/screens/ThreadDetailScreen.tsx
3608
- var import_react10 = require("react");
4063
+ var import_react11 = require("react");
3609
4064
  var import_jsx_runtime13 = require("react/jsx-runtime");
3610
4065
  var inputStyle = {
3611
4066
  backgroundColor: "#27272a",
@@ -3622,12 +4077,12 @@ function ThreadDetailScreen({
3622
4077
  }) {
3623
4078
  const { getThreadMessages, sendMessage, markAsRead, uploadImage } = useBugBear();
3624
4079
  const replyImages = useImageAttachments(uploadImage, 3, "discussion-attachments");
3625
- const [messages, setMessages] = (0, import_react10.useState)([]);
3626
- const [loading, setLoading] = (0, import_react10.useState)(true);
3627
- const [replyText, setReplyText] = (0, import_react10.useState)("");
3628
- const [sending, setSending] = (0, import_react10.useState)(false);
3629
- const [sendError, setSendError] = (0, import_react10.useState)(false);
3630
- (0, import_react10.useEffect)(() => {
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)(() => {
3631
4086
  let cancelled = false;
3632
4087
  setLoading(true);
3633
4088
  (async () => {
@@ -3894,7 +4349,7 @@ function ThreadDetailScreen({
3894
4349
  }
3895
4350
 
3896
4351
  // src/widget/screens/ComposeMessageScreen.tsx
3897
- var import_react11 = require("react");
4352
+ var import_react12 = require("react");
3898
4353
  var import_jsx_runtime14 = require("react/jsx-runtime");
3899
4354
  var inputStyle2 = {
3900
4355
  backgroundColor: "#27272a",
@@ -3908,9 +4363,9 @@ var inputStyle2 = {
3908
4363
  function ComposeMessageScreen({ nav }) {
3909
4364
  const { createThread, uploadImage } = useBugBear();
3910
4365
  const images = useImageAttachments(uploadImage, 3, "discussion-attachments");
3911
- const [subject, setSubject] = (0, import_react11.useState)("");
3912
- const [message, setMessage] = (0, import_react11.useState)("");
3913
- const [sending, setSending] = (0, import_react11.useState)(false);
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);
3914
4369
  const canSend = subject.trim().length > 0 && message.trim().length > 0 && !sending && !images.isUploading;
3915
4370
  const handleSend = async () => {
3916
4371
  if (!canSend) return;
@@ -4052,20 +4507,20 @@ function ComposeMessageScreen({ nav }) {
4052
4507
  }
4053
4508
 
4054
4509
  // src/widget/screens/ProfileScreen.tsx
4055
- var import_react12 = require("react");
4510
+ var import_react13 = require("react");
4056
4511
  var import_jsx_runtime15 = require("react/jsx-runtime");
4057
4512
  function ProfileScreen({ nav }) {
4058
4513
  const { testerInfo, assignments, updateTesterProfile, refreshTesterInfo } = useBugBear();
4059
- const [editing, setEditing] = (0, import_react12.useState)(false);
4060
- const [name, setName] = (0, import_react12.useState)(testerInfo?.name || "");
4061
- const [additionalEmails, setAdditionalEmails] = (0, import_react12.useState)(testerInfo?.additionalEmails || []);
4062
- const [newEmailInput, setNewEmailInput] = (0, import_react12.useState)("");
4063
- const [platforms, setPlatforms] = (0, import_react12.useState)(testerInfo?.platforms || []);
4064
- const [saving, setSaving] = (0, import_react12.useState)(false);
4065
- const [saved, setSaved] = (0, import_react12.useState)(false);
4066
- const [showDetails, setShowDetails] = (0, import_react12.useState)(false);
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);
4067
4522
  const completedCount = assignments.filter((a) => a.status === "passed" || a.status === "failed").length;
4068
- (0, import_react12.useEffect)(() => {
4523
+ (0, import_react13.useEffect)(() => {
4069
4524
  if (testerInfo) {
4070
4525
  setName(testerInfo.name);
4071
4526
  setAdditionalEmails(testerInfo.additionalEmails || []);
@@ -4529,7 +4984,7 @@ var styles4 = {
4529
4984
  };
4530
4985
 
4531
4986
  // src/widget/screens/IssueListScreen.tsx
4532
- var import_react13 = require("react");
4987
+ var import_react14 = require("react");
4533
4988
  var import_jsx_runtime16 = require("react/jsx-runtime");
4534
4989
  var CATEGORY_CONFIG = {
4535
4990
  open: { label: "Open Issues", accent: "#f97316", emptyIcon: "\u2705", emptyText: "No open issues" },
@@ -4544,10 +4999,10 @@ var SEVERITY_COLORS = {
4544
4999
  };
4545
5000
  function IssueListScreen({ nav, category }) {
4546
5001
  const { client } = useBugBear();
4547
- const [issues, setIssues] = (0, import_react13.useState)([]);
4548
- const [loading, setLoading] = (0, import_react13.useState)(true);
5002
+ const [issues, setIssues] = (0, import_react14.useState)([]);
5003
+ const [loading, setLoading] = (0, import_react14.useState)(true);
4549
5004
  const config = CATEGORY_CONFIG[category];
4550
- (0, import_react13.useEffect)(() => {
5005
+ (0, import_react14.useEffect)(() => {
4551
5006
  let cancelled = false;
4552
5007
  setLoading(true);
4553
5008
  (async () => {
@@ -4693,6 +5148,7 @@ var SEVERITY_CONFIG = {
4693
5148
  low: { label: "Low", color: "#71717a", bg: "#27272a" }
4694
5149
  };
4695
5150
  function IssueDetailScreen({ nav, issue }) {
5151
+ const { dashboardUrl } = useBugBear();
4696
5152
  const statusConfig = STATUS_LABELS[issue.status] || { label: issue.status, bg: "#27272a", color: "#a1a1aa" };
4697
5153
  const severityConfig = issue.severity ? SEVERITY_CONFIG[issue.severity] : null;
4698
5154
  return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { children: [
@@ -4810,7 +5266,29 @@ function IssueDetailScreen({ nav, issue }) {
4810
5266
  " \xB7 Updated ",
4811
5267
  formatRelativeTime(issue.updatedAt)
4812
5268
  ] })
4813
- ] })
5269
+ ] }),
5270
+ dashboardUrl && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5271
+ "a",
5272
+ {
5273
+ href: `${dashboardUrl}/reports`,
5274
+ target: "_blank",
5275
+ rel: "noopener noreferrer",
5276
+ style: {
5277
+ display: "flex",
5278
+ alignItems: "center",
5279
+ justifyContent: "center",
5280
+ gap: 6,
5281
+ marginTop: 12,
5282
+ padding: "10px 0",
5283
+ fontSize: 13,
5284
+ fontWeight: 500,
5285
+ color: colors.blue,
5286
+ textDecoration: "none",
5287
+ borderTop: `1px solid ${colors.border}`
5288
+ },
5289
+ children: "\u{1F310} View on Dashboard \u2192"
5290
+ }
5291
+ )
4814
5292
  ] });
4815
5293
  }
4816
5294
 
@@ -4865,12 +5343,13 @@ function BugBearPanel({
4865
5343
  }) {
4866
5344
  const { shouldShowWidget, testerInfo, assignments, isLoading, unreadCount } = useBugBear();
4867
5345
  const { currentScreen, canGoBack, push, pop, replace, reset } = useNavigation();
4868
- const [collapsed, setCollapsed] = (0, import_react14.useState)(defaultCollapsed);
4869
- const [panelPosition, setPanelPosition] = (0, import_react14.useState)(null);
4870
- const [isDragging, setIsDragging] = (0, import_react14.useState)(false);
4871
- const dragStartRef = (0, import_react14.useRef)(null);
4872
- const panelRef = (0, import_react14.useRef)(null);
4873
- (0, import_react14.useEffect)(() => {
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)(() => {
4874
5353
  if (typeof window === "undefined") return;
4875
5354
  try {
4876
5355
  const saved = localStorage.getItem(STORAGE_KEY);
@@ -4884,7 +5363,7 @@ function BugBearPanel({
4884
5363
  setPanelPosition(getDefaultPosition(position));
4885
5364
  }
4886
5365
  }, [position]);
4887
- (0, import_react14.useEffect)(() => {
5366
+ (0, import_react15.useEffect)(() => {
4888
5367
  if (panelPosition && typeof window !== "undefined") {
4889
5368
  try {
4890
5369
  localStorage.setItem(STORAGE_KEY, JSON.stringify(panelPosition));
@@ -4892,7 +5371,7 @@ function BugBearPanel({
4892
5371
  }
4893
5372
  }
4894
5373
  }, [panelPosition]);
4895
- (0, import_react14.useEffect)(() => {
5374
+ (0, import_react15.useEffect)(() => {
4896
5375
  if (typeof window === "undefined") return;
4897
5376
  const handleResize = () => {
4898
5377
  setPanelPosition((prev) => prev ? clampPosition(prev) : getDefaultPosition(position));
@@ -4900,7 +5379,7 @@ function BugBearPanel({
4900
5379
  window.addEventListener("resize", handleResize);
4901
5380
  return () => window.removeEventListener("resize", handleResize);
4902
5381
  }, [position]);
4903
- const handleMouseDown = (0, import_react14.useCallback)((e) => {
5382
+ const handleMouseDown = (0, import_react15.useCallback)((e) => {
4904
5383
  if (!draggable || !panelPosition) return;
4905
5384
  const target = e.target;
4906
5385
  if (!target.closest("[data-drag-handle]")) return;
@@ -4913,7 +5392,7 @@ function BugBearPanel({
4913
5392
  panelY: panelPosition.y
4914
5393
  };
4915
5394
  }, [draggable, panelPosition]);
4916
- (0, import_react14.useEffect)(() => {
5395
+ (0, import_react15.useEffect)(() => {
4917
5396
  if (!isDragging) return;
4918
5397
  const handleMouseMove = (e) => {
4919
5398
  if (!dragStartRef.current) return;
@@ -4935,7 +5414,7 @@ function BugBearPanel({
4935
5414
  document.removeEventListener("mouseup", handleMouseUp);
4936
5415
  };
4937
5416
  }, [isDragging]);
4938
- const handleDoubleClick = (0, import_react14.useCallback)(() => {
5417
+ const handleDoubleClick = (0, import_react15.useCallback)(() => {
4939
5418
  if (!draggable) return;
4940
5419
  setPanelPosition(getDefaultPosition(position));
4941
5420
  try {
@@ -4943,6 +5422,14 @@ function BugBearPanel({
4943
5422
  } catch {
4944
5423
  }
4945
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]);
4946
5433
  if (isLoading || !shouldShowWidget) return null;
4947
5434
  if (!panelPosition) return null;
4948
5435
  const pendingCount = assignments.filter((a) => a.status === "pending" || a.status === "in_progress").length;
@@ -4979,13 +5466,12 @@ function BugBearPanel({
4979
5466
  };
4980
5467
  const handleClose = () => {
4981
5468
  setCollapsed(true);
4982
- reset();
4983
5469
  };
4984
5470
  const nav = { push, pop, replace, reset, canGoBack };
4985
5471
  const renderScreen = () => {
4986
5472
  switch (currentScreen.name) {
4987
5473
  case "HOME":
4988
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(HomeScreen, { nav });
5474
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(HomeScreen, { nav, onReportBug: handleReportBug });
4989
5475
  case "TEST_DETAIL":
4990
5476
  return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(TestDetailScreen, { testId: currentScreen.testId, nav });
4991
5477
  case "TEST_LIST":
@@ -4993,7 +5479,17 @@ function BugBearPanel({
4993
5479
  case "TEST_FEEDBACK":
4994
5480
  return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(TestFeedbackScreen, { status: currentScreen.status, assignmentId: currentScreen.assignmentId, nav });
4995
5481
  case "REPORT":
4996
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ReportScreen, { nav, prefill: currentScreen.prefill });
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
+ );
4997
5493
  case "REPORT_SUCCESS":
4998
5494
  return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ReportSuccessScreen, { nav });
4999
5495
  case "MESSAGE_LIST":
@@ -5190,10 +5686,10 @@ function BugBearPanel({
5190
5686
  }
5191
5687
 
5192
5688
  // src/BugBearErrorBoundary.tsx
5193
- var import_react15 = require("react");
5689
+ var import_react16 = require("react");
5194
5690
  var import_core2 = require("@bbearai/core");
5195
5691
  var import_jsx_runtime19 = require("react/jsx-runtime");
5196
- var BugBearErrorBoundary = class extends import_react15.Component {
5692
+ var BugBearErrorBoundary = class extends import_react16.Component {
5197
5693
  constructor(props) {
5198
5694
  super(props);
5199
5695
  this.reset = () => {