@jarve/bug-reporter 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -34,16 +34,21 @@ function cn(...inputs) {
34
34
 
35
35
  // src/floating-button.tsx
36
36
  import { jsx } from "react/jsx-runtime";
37
- function FloatingButton({ isActive, onClick }) {
37
+ function FloatingButton({ isActive, onClick, position = "right" }) {
38
+ const sideClasses = position === "left" ? "left-4 md:left-6" : "right-4 md:right-6";
38
39
  return /* @__PURE__ */ jsx(
39
40
  "button",
40
41
  {
41
42
  onClick,
42
43
  className: cn(
43
- "fixed bottom-6 right-6 z-[9999] flex h-12 w-12 items-center justify-center rounded-full shadow-lg transition-all duration-200",
44
- "hover:scale-110 focus:outline-none focus:ring-2 focus:ring-offset-2",
45
- isActive ? "bg-red-500 text-white animate-pulse focus:ring-red-400" : "bg-indigo-600 text-white hover:bg-indigo-700 focus:ring-indigo-400",
46
- "bottom-4 right-4 h-11 w-11 md:bottom-6 md:right-6 md:h-12 md:w-12"
44
+ "fixed z-[9999] flex items-center justify-center rounded-full shadow-lg transition-all duration-200",
45
+ "hover:scale-110 focus:ring-2 focus:ring-offset-2 focus:outline-none",
46
+ // size + vertical position
47
+ "bottom-4 h-11 w-11 md:bottom-6 md:h-12 md:w-12",
48
+ // horizontal side
49
+ sideClasses,
50
+ // active vs idle colors
51
+ isActive ? "animate-pulse bg-red-500 text-white focus:ring-red-400" : "bg-indigo-600 text-white hover:bg-indigo-700 focus:ring-indigo-400"
47
52
  ),
48
53
  title: isActive ? "Cancel bug capture" : "Report a bug",
49
54
  "aria-label": isActive ? "Cancel bug capture" : "Report a bug",
@@ -173,8 +178,7 @@ var errorListener = null;
173
178
  var rejectionListener = null;
174
179
  function startCapturing() {
175
180
  if (isCapturing) return;
176
- if (console.error.__bugReporterPatched)
177
- return;
181
+ if (console.error.__bugReporterPatched) return;
178
182
  isCapturing = true;
179
183
  capturedErrors = [];
180
184
  originalConsoleError = console.error;
@@ -420,12 +424,21 @@ function CaptureOverlay({
420
424
  skipFonts: true
421
425
  });
422
426
  const blob = dataUrlToBlob(dataUrl);
423
- const metadata = collectMetadata(section, siteId, reporterName, reporterEmail, elementInfo);
427
+ const metadata = collectMetadata(
428
+ section,
429
+ siteId,
430
+ reporterName,
431
+ reporterEmail,
432
+ elementInfo
433
+ );
424
434
  const consoleErrors = getCapturedErrors();
425
435
  const networkErrors = getCapturedNetworkErrors();
426
436
  onCapture({ screenshot: blob, metadata, consoleErrors, networkErrors });
427
437
  } catch (err) {
428
- console.warn("Bug reporter: first capture attempt failed, retrying with simpler settings", err);
438
+ console.warn(
439
+ "Bug reporter: first capture attempt failed, retrying with simpler settings",
440
+ err
441
+ );
429
442
  try {
430
443
  const dataUrl = await toPng(section, {
431
444
  quality: 0.6,
@@ -434,13 +447,25 @@ function CaptureOverlay({
434
447
  cacheBust: true
435
448
  });
436
449
  const retryBlob = dataUrlToBlob(dataUrl);
437
- const metadata = collectMetadata(section, siteId, reporterName, reporterEmail, elementInfo);
450
+ const metadata = collectMetadata(
451
+ section,
452
+ siteId,
453
+ reporterName,
454
+ reporterEmail,
455
+ elementInfo
456
+ );
438
457
  const consoleErrors = getCapturedErrors();
439
458
  const networkErrors = getCapturedNetworkErrors();
440
459
  onCapture({ screenshot: retryBlob, metadata, consoleErrors, networkErrors });
441
460
  } catch (e) {
442
461
  console.error("Bug reporter: screenshot capture failed after retry");
443
- const metadata = collectMetadata(section, siteId, reporterName, reporterEmail, elementInfo);
462
+ const metadata = collectMetadata(
463
+ section,
464
+ siteId,
465
+ reporterName,
466
+ reporterEmail,
467
+ elementInfo
468
+ );
444
469
  const consoleErrors = getCapturedErrors();
445
470
  const networkErrors = getCapturedNetworkErrors();
446
471
  onCapture({
@@ -571,7 +596,16 @@ function CaptureOverlay({
571
596
  document.removeEventListener("click", handleClick, true);
572
597
  if (rafRef.current) cancelAnimationFrame(rafRef.current);
573
598
  };
574
- }, [isActive, isTouchMode, handleMouseMove, handleClick, handleTouchEnd, handlePointerDown, handleKeyDown, handleScroll]);
599
+ }, [
600
+ isActive,
601
+ isTouchMode,
602
+ handleMouseMove,
603
+ handleClick,
604
+ handleTouchEnd,
605
+ handlePointerDown,
606
+ handleKeyDown,
607
+ handleScroll
608
+ ]);
575
609
  const highlightRect = isTouchMode ? selectedRect : hoveredRect;
576
610
  const showHighlight = isTouchMode ? !!selectedSection : !!hoveredElement && !!hoveredRect;
577
611
  if (!isActive) return null;
@@ -582,20 +616,21 @@ function CaptureOverlay({
582
616
  "data-bug-reporter": true,
583
617
  role: "alert",
584
618
  "aria-live": "assertive",
585
- className: "fixed top-0 left-0 right-0 z-[10000] bg-indigo-600 text-white text-center py-2 px-4 text-sm font-medium flex items-center justify-center gap-3",
619
+ className: "fixed top-0 right-0 left-0 z-[10000] flex items-center justify-center gap-3 bg-indigo-600 px-4 py-2 text-center text-sm font-medium text-white",
586
620
  children: isTouchMode ? /* @__PURE__ */ jsxs(Fragment, { children: [
587
621
  /* @__PURE__ */ jsx2("span", { children: "Tap the section with the bug" }),
588
622
  /* @__PURE__ */ jsx2(
589
623
  "button",
590
624
  {
591
625
  onClick: onCancel,
592
- className: "px-3 py-1 min-h-[44px] bg-white/20 rounded-md text-sm font-medium",
626
+ className: "min-h-[44px] rounded-md bg-white/20 px-3 py-1 text-sm font-medium",
593
627
  children: "Cancel"
594
628
  }
595
629
  )
596
630
  ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
597
- "Click on the section with the bug. Press ",
598
- /* @__PURE__ */ jsx2("kbd", { className: "px-1.5 py-0.5 bg-indigo-800 rounded text-xs mx-1", children: "Esc" }),
631
+ "Click on the section with the bug. Press",
632
+ " ",
633
+ /* @__PURE__ */ jsx2("kbd", { className: "mx-1 rounded bg-indigo-800 px-1.5 py-0.5 text-xs", children: "Esc" }),
599
634
  " to cancel."
600
635
  ] })
601
636
  }
@@ -605,7 +640,7 @@ function CaptureOverlay({
605
640
  {
606
641
  ref: overlayRef,
607
642
  "data-bug-reporter": true,
608
- className: "fixed pointer-events-none z-[9998] border-2 border-indigo-500 rounded-sm transition-all duration-150 ease-out",
643
+ className: "pointer-events-none fixed z-[9998] rounded-sm border-2 border-indigo-500 transition-all duration-150 ease-out",
609
644
  style: {
610
645
  top: highlightRect.top - 2,
611
646
  left: highlightRect.left - 2,
@@ -619,11 +654,11 @@ function CaptureOverlay({
619
654
  "div",
620
655
  {
621
656
  "data-bug-reporter": true,
622
- className: "fixed bottom-0 left-0 right-0 z-[10000] bg-white border-t border-gray-200 shadow-lg",
657
+ className: "fixed right-0 bottom-0 left-0 z-[10000] border-t border-gray-200 bg-white shadow-lg",
623
658
  style: { paddingBottom: "env(safe-area-inset-bottom, 0px)" },
624
- children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-4 py-3 gap-3", children: [
625
- /* @__PURE__ */ jsx2("span", { className: "text-sm font-medium text-gray-900 truncate", children: "Capture this section?" }),
626
- /* @__PURE__ */ jsxs("div", { className: "flex gap-2 shrink-0", children: [
659
+ children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3 px-4 py-3", children: [
660
+ /* @__PURE__ */ jsx2("span", { className: "truncate text-sm font-medium text-gray-900", children: "Capture this section?" }),
661
+ /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 gap-2", children: [
627
662
  /* @__PURE__ */ jsx2(
628
663
  "button",
629
664
  {
@@ -633,7 +668,7 @@ function CaptureOverlay({
633
668
  setSelectedTarget(null);
634
669
  touchCoordsRef.current = null;
635
670
  },
636
- className: "px-4 min-h-[44px] rounded-md border border-gray-300 text-sm font-medium text-gray-700",
671
+ className: "min-h-[44px] rounded-md border border-gray-300 px-4 text-sm font-medium text-gray-700",
637
672
  children: "Cancel"
638
673
  }
639
674
  ),
@@ -641,7 +676,7 @@ function CaptureOverlay({
641
676
  "button",
642
677
  {
643
678
  onClick: handleConfirmCapture,
644
- className: "px-4 min-h-[44px] rounded-md bg-indigo-600 text-white text-sm font-medium",
679
+ className: "min-h-[44px] rounded-md bg-indigo-600 px-4 text-sm font-medium text-white",
645
680
  children: "Capture"
646
681
  }
647
682
  )
@@ -715,7 +750,9 @@ function ReportModal({
715
750
  })
716
751
  });
717
752
  if (response.status === 401) {
718
- console.error("Bug reporter: invalid or missing API key. Check your BugReporter apiKey prop.");
753
+ console.error(
754
+ "Bug reporter: invalid or missing API key. Check your BugReporter apiKey prop."
755
+ );
719
756
  setMessages([
720
757
  {
721
758
  role: "assistant",
@@ -812,9 +849,7 @@ function ReportModal({
812
849
  setModalState("submitted");
813
850
  } catch (err) {
814
851
  console.error("Bug reporter: failed to submit report", err);
815
- setErrorMessage(
816
- err instanceof Error ? err.message : "Failed to submit report"
817
- );
852
+ setErrorMessage(err instanceof Error ? err.message : "Failed to submit report");
818
853
  setModalState("error");
819
854
  }
820
855
  },
@@ -827,10 +862,7 @@ function ReportModal({
827
862
  if (!input.trim() || isLoading || !captureResult) return;
828
863
  const userMessage = input.trim();
829
864
  setInput("");
830
- const newMessages = [
831
- ...messages,
832
- { role: "user", content: userMessage }
833
- ];
865
+ const newMessages = [...messages, { role: "user", content: userMessage }];
834
866
  setMessages(newMessages);
835
867
  setIsLoading(true);
836
868
  try {
@@ -846,7 +878,9 @@ function ReportModal({
846
878
  })
847
879
  });
848
880
  if (response.status === 401) {
849
- console.error("Bug reporter: invalid or missing API key. Check your BugReporter apiKey prop.");
881
+ console.error(
882
+ "Bug reporter: invalid or missing API key. Check your BugReporter apiKey prop."
883
+ );
850
884
  setMessages([
851
885
  ...newMessages,
852
886
  {
@@ -858,10 +892,7 @@ function ReportModal({
858
892
  }
859
893
  if (!response.ok) throw new Error("Failed to get AI response");
860
894
  const data = await response.json();
861
- setMessages([
862
- ...newMessages,
863
- { role: "assistant", content: data.message }
864
- ]);
895
+ setMessages([...newMessages, { role: "assistant", content: data.message }]);
865
896
  if (data.readyToSubmit && data.structuredReport) {
866
897
  await submitReport(
867
898
  [...newMessages, { role: "assistant", content: data.message }],
@@ -912,65 +943,66 @@ function ReportModal({
912
943
  "div",
913
944
  {
914
945
  className: cn(
915
- "bg-white dark:bg-gray-950 rounded-xl shadow-2xl border border-gray-200 dark:border-gray-800 flex flex-col overflow-hidden",
916
- "w-full max-w-lg mx-4",
917
- "max-[768px]:mx-0 max-[768px]:rounded-none max-[768px]:max-w-none max-[768px]:h-full",
946
+ "flex flex-col overflow-hidden rounded-xl border border-gray-200 bg-white shadow-2xl dark:border-gray-800 dark:bg-gray-950",
947
+ "mx-4 w-full max-w-lg",
948
+ "max-[768px]:mx-0 max-[768px]:h-full max-[768px]:max-w-none max-[768px]:rounded-none",
918
949
  "min-[769px]:max-h-[85vh]"
919
950
  ),
920
951
  children: [
921
- /* @__PURE__ */ jsxs2("div", { className: "flex items-center justify-between px-4 py-3 border-b border-gray-200 dark:border-gray-800 bg-gray-50/30 dark:bg-gray-900/30", children: [
952
+ /* @__PURE__ */ jsxs2("div", { className: "flex items-center justify-between border-b border-gray-200 bg-gray-50/30 px-4 py-3 dark:border-gray-800 dark:bg-gray-900/30", children: [
922
953
  /* @__PURE__ */ jsxs2("div", { children: [
923
- /* @__PURE__ */ jsx3("h2", { className: "font-semibold text-sm text-gray-900 dark:text-gray-100", children: "Bug Report" }),
954
+ /* @__PURE__ */ jsx3("h2", { className: "text-sm font-semibold text-gray-900 dark:text-gray-100", children: "Bug Report" }),
924
955
  /* @__PURE__ */ jsx3("p", { className: "text-xs text-gray-500 dark:text-gray-400", children: siteId })
925
956
  ] }),
926
957
  /* @__PURE__ */ jsx3(
927
958
  "button",
928
959
  {
929
960
  onClick: handleClose,
930
- className: "p-1.5 rounded-md hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors",
961
+ className: "rounded-md p-1.5 transition-colors hover:bg-gray-100 dark:hover:bg-gray-800",
931
962
  "aria-label": "Close",
932
963
  children: /* @__PURE__ */ jsx3(X, { className: "h-4 w-4 text-gray-600 dark:text-gray-400" })
933
964
  }
934
965
  )
935
966
  ] }),
936
- screenshotUrl && /* @__PURE__ */ jsx3("div", { className: "px-4 py-3 border-b border-gray-200 dark:border-gray-800 bg-gray-50/10 dark:bg-gray-900/10", children: /* @__PURE__ */ jsx3(
967
+ screenshotUrl && /* @__PURE__ */ jsx3("div", { className: "border-b border-gray-200 bg-gray-50/10 px-4 py-3 dark:border-gray-800 dark:bg-gray-900/10", children: /* @__PURE__ */ jsx3(
937
968
  "img",
938
969
  {
939
970
  src: screenshotUrl,
940
971
  alt: "Captured section",
941
- className: "w-full max-h-40 object-contain rounded-md border border-gray-200 dark:border-gray-700"
972
+ className: "max-h-40 w-full rounded-md border border-gray-200 object-contain dark:border-gray-700"
942
973
  }
943
974
  ) }),
944
- /* @__PURE__ */ jsx3("div", { className: "flex-1 overflow-y-auto px-4 py-3 space-y-3 min-h-0", children: modalState === "submitted" ? /* @__PURE__ */ jsxs2("div", { className: "flex flex-col items-center justify-center py-8 text-center", children: [
945
- /* @__PURE__ */ jsx3(CheckCircle2, { className: "h-12 w-12 text-green-500 mb-3" }),
946
- /* @__PURE__ */ jsx3("h3", { className: "font-semibold text-lg text-gray-900 dark:text-gray-100", children: "Report Submitted" }),
947
- /* @__PURE__ */ jsxs2("p", { className: "text-sm text-gray-500 dark:text-gray-400 mt-1", children: [
948
- "Reference: ",
949
- /* @__PURE__ */ jsx3("code", { className: "text-xs bg-gray-100 dark:bg-gray-800 px-1.5 py-0.5 rounded", children: reportId == null ? void 0 : reportId.slice(0, 8) })
975
+ /* @__PURE__ */ jsx3("div", { className: "min-h-0 flex-1 space-y-3 overflow-y-auto px-4 py-3", children: modalState === "submitted" ? /* @__PURE__ */ jsxs2("div", { className: "flex flex-col items-center justify-center py-8 text-center", children: [
976
+ /* @__PURE__ */ jsx3(CheckCircle2, { className: "mb-3 h-12 w-12 text-green-500" }),
977
+ /* @__PURE__ */ jsx3("h3", { className: "text-lg font-semibold text-gray-900 dark:text-gray-100", children: "Report Submitted" }),
978
+ /* @__PURE__ */ jsxs2("p", { className: "mt-1 text-sm text-gray-500 dark:text-gray-400", children: [
979
+ "Reference:",
980
+ " ",
981
+ /* @__PURE__ */ jsx3("code", { className: "rounded bg-gray-100 px-1.5 py-0.5 text-xs dark:bg-gray-800", children: reportId == null ? void 0 : reportId.slice(0, 8) })
950
982
  ] }),
951
- /* @__PURE__ */ jsx3("p", { className: "text-sm text-gray-500 dark:text-gray-400 mt-2", children: "Thanks for the report \u2014 we'll look into it." }),
983
+ /* @__PURE__ */ jsx3("p", { className: "mt-2 text-sm text-gray-500 dark:text-gray-400", children: "Thanks for the report \u2014 we'll look into it." }),
952
984
  /* @__PURE__ */ jsx3(
953
985
  "button",
954
986
  {
955
987
  onClick: handleClose,
956
- className: "mt-4 px-4 py-2 bg-indigo-600 text-white rounded-md text-sm hover:bg-indigo-700 transition-colors",
988
+ className: "mt-4 rounded-md bg-indigo-600 px-4 py-2 text-sm text-white transition-colors hover:bg-indigo-700",
957
989
  children: "Done"
958
990
  }
959
991
  )
960
992
  ] }) : modalState === "error" ? /* @__PURE__ */ jsxs2("div", { className: "flex flex-col items-center justify-center py-8 text-center", children: [
961
- /* @__PURE__ */ jsx3(X, { className: "h-12 w-12 text-red-500 mb-3" }),
962
- /* @__PURE__ */ jsx3("h3", { className: "font-semibold text-lg text-gray-900 dark:text-gray-100", children: "Submission Failed" }),
963
- /* @__PURE__ */ jsx3("p", { className: "text-sm text-gray-500 dark:text-gray-400 mt-1", children: errorMessage || "Something went wrong. Please try again." }),
993
+ /* @__PURE__ */ jsx3(X, { className: "mb-3 h-12 w-12 text-red-500" }),
994
+ /* @__PURE__ */ jsx3("h3", { className: "text-lg font-semibold text-gray-900 dark:text-gray-100", children: "Submission Failed" }),
995
+ /* @__PURE__ */ jsx3("p", { className: "mt-1 text-sm text-gray-500 dark:text-gray-400", children: errorMessage || "Something went wrong. Please try again." }),
964
996
  /* @__PURE__ */ jsx3(
965
997
  "button",
966
998
  {
967
999
  onClick: () => setModalState("chatting"),
968
- className: "mt-4 px-4 py-2 bg-indigo-600 text-white rounded-md text-sm hover:bg-indigo-700 transition-colors",
1000
+ className: "mt-4 rounded-md bg-indigo-600 px-4 py-2 text-sm text-white transition-colors hover:bg-indigo-700",
969
1001
  children: "Try Again"
970
1002
  }
971
1003
  )
972
1004
  ] }) : modalState === "submitting" ? /* @__PURE__ */ jsxs2("div", { className: "flex flex-col items-center justify-center py-8", children: [
973
- /* @__PURE__ */ jsx3(Loader2, { className: "h-8 w-8 animate-spin text-indigo-500 mb-3" }),
1005
+ /* @__PURE__ */ jsx3(Loader2, { className: "mb-3 h-8 w-8 animate-spin text-indigo-500" }),
974
1006
  /* @__PURE__ */ jsx3("p", { className: "text-sm text-gray-500 dark:text-gray-400", children: "Submitting your report..." })
975
1007
  ] }) : /* @__PURE__ */ jsxs2(Fragment2, { children: [
976
1008
  (captureResult == null ? void 0 : captureResult.screenshot.size) === 0 && /* @__PURE__ */ jsx3("p", { className: "text-xs text-amber-600", children: "Screenshot could not be captured. Please describe the visual issue in detail." }),
@@ -979,42 +1011,53 @@ function ReportModal({
979
1011
  {
980
1012
  className: cn(
981
1013
  "text-sm leading-relaxed",
982
- msg.role === "assistant" ? "bg-gray-100/50 dark:bg-gray-800/50 rounded-lg p-3 text-gray-900 dark:text-gray-100" : "bg-indigo-600 text-white rounded-lg p-3 ml-8"
1014
+ msg.role === "assistant" ? "rounded-lg bg-gray-100/50 p-3 text-gray-900 dark:bg-gray-800/50 dark:text-gray-100" : "ml-8 rounded-lg bg-indigo-600 p-3 text-white"
983
1015
  ),
984
1016
  children: msg.content
985
1017
  },
986
1018
  i
987
1019
  )),
988
- isLoading && /* @__PURE__ */ jsxs2("div", { className: "bg-gray-100/50 dark:bg-gray-800/50 rounded-lg p-3 flex items-center gap-2", children: [
1020
+ isLoading && /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2 rounded-lg bg-gray-100/50 p-3 dark:bg-gray-800/50", children: [
989
1021
  /* @__PURE__ */ jsx3(Loader2, { className: "h-3.5 w-3.5 animate-spin text-gray-500" }),
990
1022
  /* @__PURE__ */ jsx3("span", { className: "text-sm text-gray-500 dark:text-gray-400", children: "Thinking..." })
991
1023
  ] }),
992
1024
  /* @__PURE__ */ jsx3("div", { ref: chatEndRef })
993
1025
  ] }) }),
994
- modalState === "chatting" && /* @__PURE__ */ jsxs2("div", { className: "border-t border-gray-200 dark:border-gray-800 px-4 py-3", children: [
995
- /* @__PURE__ */ jsxs2("div", { className: "flex gap-2", children: [
996
- /* @__PURE__ */ jsx3(
997
- "textarea",
998
- {
999
- ref: inputRef,
1000
- value: input,
1001
- onChange: (e) => setInput(e.target.value),
1002
- onKeyDown: handleKeyDown,
1003
- placeholder: "Describe what's wrong...",
1004
- rows: 2,
1005
- className: "flex-1 resize-none rounded-md border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-950 px-3 py-2 text-sm text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-indigo-400 focus:border-transparent",
1006
- disabled: isLoading
1007
- }
1008
- ),
1009
- /* @__PURE__ */ jsxs2("div", { className: "flex flex-col gap-1", children: [
1010
- /* @__PURE__ */ jsx3(
1026
+ modalState === "chatting" && /* @__PURE__ */ jsx3("div", { className: "border-t border-gray-200 px-4 py-3 dark:border-gray-800", children: /* @__PURE__ */ jsxs2("div", { className: "flex flex-col gap-2", children: [
1027
+ /* @__PURE__ */ jsx3(
1028
+ "textarea",
1029
+ {
1030
+ ref: inputRef,
1031
+ value: input,
1032
+ onChange: (e) => setInput(e.target.value),
1033
+ onKeyDown: handleKeyDown,
1034
+ placeholder: "Describe what's wrong...",
1035
+ rows: 2,
1036
+ className: "w-full resize-none rounded-md border border-gray-300 bg-white px-3 py-2 text-sm text-gray-900 focus:border-transparent focus:ring-2 focus:ring-indigo-400 focus:outline-none dark:border-gray-700 dark:bg-gray-950 dark:text-gray-100",
1037
+ disabled: isLoading
1038
+ }
1039
+ ),
1040
+ /* @__PURE__ */ jsxs2("div", { className: "flex flex-col gap-2", children: [
1041
+ captureResult && (captureResult.consoleErrors.length > 0 || captureResult.networkErrors.length > 0) && /* @__PURE__ */ jsxs2("p", { className: "flex-1 text-xs text-amber-600", children: [
1042
+ [
1043
+ captureResult.consoleErrors.length > 0 ? `${captureResult.consoleErrors.length} console error${captureResult.consoleErrors.length !== 1 ? "s" : ""}` : null,
1044
+ captureResult.networkErrors.length > 0 ? `${captureResult.networkErrors.length} failed request${captureResult.networkErrors.length !== 1 ? "s" : ""}` : null
1045
+ ].filter(Boolean).join(" + "),
1046
+ " ",
1047
+ "captured \u2014 these will be included in the report."
1048
+ ] }),
1049
+ /* @__PURE__ */ jsxs2("div", { className: "flex justify-end gap-2", children: [
1050
+ /* @__PURE__ */ jsxs2(
1011
1051
  "button",
1012
1052
  {
1013
1053
  onClick: sendMessage,
1014
1054
  disabled: !input.trim() || isLoading,
1015
- className: "p-2 rounded-md bg-indigo-600 text-white hover:bg-indigo-700 disabled:opacity-50 disabled:cursor-not-allowed transition-colors",
1055
+ className: "flex items-center gap-1 rounded-md bg-indigo-600 px-3 py-2 text-nowrap text-white transition-colors hover:bg-indigo-700 disabled:cursor-not-allowed disabled:opacity-50",
1016
1056
  title: "Send message",
1017
- children: /* @__PURE__ */ jsx3(Send, { className: "h-4 w-4" })
1057
+ children: [
1058
+ /* @__PURE__ */ jsx3(Send, { className: "h-4 w-4" }),
1059
+ /* @__PURE__ */ jsx3("span", { className: "text-sm font-medium", children: "Send message" })
1060
+ ]
1018
1061
  }
1019
1062
  ),
1020
1063
  messages.length >= 2 && /* @__PURE__ */ jsx3(
@@ -1022,22 +1065,14 @@ function ReportModal({
1022
1065
  {
1023
1066
  onClick: handleManualSubmit,
1024
1067
  disabled: isLoading,
1025
- className: "px-2 py-1 rounded-md bg-green-600 text-white hover:bg-green-700 disabled:opacity-50 text-xs font-medium transition-colors",
1068
+ className: "rounded-md bg-green-600 px-3 py-2 text-xs font-medium text-nowrap text-white transition-colors hover:bg-green-700 disabled:opacity-50",
1026
1069
  title: "Submit report now",
1027
- children: "Submit"
1070
+ children: /* @__PURE__ */ jsx3("span", { className: "text-sm font-medium", children: "Submit report" })
1028
1071
  }
1029
1072
  )
1030
1073
  ] })
1031
- ] }),
1032
- captureResult && (captureResult.consoleErrors.length > 0 || captureResult.networkErrors.length > 0) && /* @__PURE__ */ jsxs2("p", { className: "text-xs text-amber-600 mt-1.5", children: [
1033
- [
1034
- captureResult.consoleErrors.length > 0 ? `${captureResult.consoleErrors.length} console error${captureResult.consoleErrors.length !== 1 ? "s" : ""}` : null,
1035
- captureResult.networkErrors.length > 0 ? `${captureResult.networkErrors.length} failed request${captureResult.networkErrors.length !== 1 ? "s" : ""}` : null
1036
- ].filter(Boolean).join(" + "),
1037
- " ",
1038
- "captured \u2014 these will be included in the report."
1039
1074
  ] })
1040
- ] })
1075
+ ] }) })
1041
1076
  ]
1042
1077
  }
1043
1078
  )
@@ -1051,8 +1086,10 @@ function JarveBugReporter({
1051
1086
  apiUrl,
1052
1087
  apiKey,
1053
1088
  user,
1089
+ buttonPosition,
1054
1090
  children
1055
1091
  }) {
1092
+ const safeApiKey = apiKey || "";
1056
1093
  const [captureMode, setCaptureMode] = useState3(false);
1057
1094
  const [captureResult, setCaptureResult] = useState3(null);
1058
1095
  const [showModal, setShowModal] = useState3(false);
@@ -1081,12 +1118,19 @@ function JarveBugReporter({
1081
1118
  clearCapturedErrors();
1082
1119
  clearCapturedNetworkErrors();
1083
1120
  }, []);
1084
- const siteId = apiKey.startsWith("brk_") ? apiKey.slice(4, 12) : "external";
1121
+ const siteId = safeApiKey.startsWith("brk_") ? safeApiKey.slice(4, 12) : "external";
1085
1122
  const reporterName = (user == null ? void 0 : user.name) || "Anonymous";
1086
1123
  const reporterEmail = (user == null ? void 0 : user.email) || "unknown@external";
1087
1124
  return /* @__PURE__ */ jsxs3(Fragment3, { children: [
1088
1125
  children,
1089
- /* @__PURE__ */ jsx4(FloatingButton, { isActive: captureMode, onClick: toggleCaptureMode }),
1126
+ /* @__PURE__ */ jsx4(
1127
+ FloatingButton,
1128
+ {
1129
+ isActive: captureMode,
1130
+ onClick: toggleCaptureMode,
1131
+ position: buttonPosition
1132
+ }
1133
+ ),
1090
1134
  /* @__PURE__ */ jsx4(
1091
1135
  CaptureOverlay,
1092
1136
  {