glitchgrab 1.12.0 → 1.13.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
@@ -4,8 +4,8 @@
4
4
  import {
5
5
  createContext,
6
6
  useContext,
7
- useRef,
8
- useEffect,
7
+ useRef as useRef2,
8
+ useEffect as useEffect2,
9
9
  useCallback
10
10
  } from "react";
11
11
 
@@ -316,244 +316,10 @@ var GlitchgrabErrorBoundary = class extends React.Component {
316
316
  }
317
317
  };
318
318
 
319
- // src/provider.tsx
320
- import { Fragment, jsx } from "react/jsx-runtime";
321
- var DEFAULT_BASE_URL2 = "https://www.glitchgrab.dev";
322
- var GlitchgrabContext = createContext(null);
323
- function useGlitchgrab() {
324
- const ctx = useContext(GlitchgrabContext);
325
- if (!ctx) {
326
- throw new Error("useGlitchgrab must be used within a GlitchgrabProvider");
327
- }
328
- return ctx;
329
- }
330
- function GlitchgrabProviderInner({
331
- token,
332
- session,
333
- baseUrl,
334
- onError,
335
- onReportSent,
336
- breadcrumbs: enableBreadcrumbs = true,
337
- maxBreadcrumbs: maxBreadcrumbs2 = 50,
338
- children,
339
- fallback
340
- }) {
341
- const visitedPagesRef = useRef([]);
342
- useEffect(() => {
343
- if (enableBreadcrumbs) {
344
- initBreadcrumbs(maxBreadcrumbs2);
345
- }
346
- }, [enableBreadcrumbs, maxBreadcrumbs2]);
347
- useEffect(() => {
348
- try {
349
- if (typeof window === "undefined") return;
350
- const trackPage = () => {
351
- try {
352
- const sanitized = sanitizeUrl(window.location.href);
353
- const pages = visitedPagesRef.current;
354
- if (pages[pages.length - 1] !== sanitized) {
355
- pages.push(sanitized);
356
- if (pages.length > 20) {
357
- pages.splice(0, pages.length - 20);
358
- }
359
- }
360
- } catch (e) {
361
- }
362
- };
363
- trackPage();
364
- const handlePopState = () => trackPage();
365
- window.addEventListener("popstate", handlePopState);
366
- const origPushState = history.pushState.bind(history);
367
- const origReplaceState = history.replaceState.bind(history);
368
- history.pushState = function(...args) {
369
- origPushState(...args);
370
- trackPage();
371
- };
372
- history.replaceState = function(...args) {
373
- origReplaceState(...args);
374
- trackPage();
375
- };
376
- return () => {
377
- window.removeEventListener("popstate", handlePopState);
378
- history.pushState = origPushState;
379
- history.replaceState = origReplaceState;
380
- };
381
- } catch (e) {
382
- }
383
- }, []);
384
- useEffect(() => {
385
- try {
386
- if (typeof window === "undefined") return;
387
- if (process.env.NODE_ENV === "development") return;
388
- const handleError = (event) => {
389
- var _a, _b, _c, _d, _e;
390
- try {
391
- const context = captureContext(visitedPagesRef.current);
392
- const payload = {
393
- token,
394
- source: "SDK_AUTO",
395
- type: "BUG",
396
- errorMessage: event.message,
397
- errorStack: (_a = event.error) == null ? void 0 : _a.stack,
398
- pageUrl: context.url,
399
- userAgent: context.userAgent,
400
- breadcrumbs: context.breadcrumbs,
401
- deviceInfo: (_b = context.deviceInfo) != null ? _b : void 0,
402
- metadata: {
403
- timestamp: context.timestamp,
404
- visitedPages: JSON.stringify(context.visitedPages),
405
- filename: (_c = event.filename) != null ? _c : "",
406
- lineno: String((_d = event.lineno) != null ? _d : ""),
407
- colno: String((_e = event.colno) != null ? _e : ""),
408
- ...(session == null ? void 0 : session.userId) ? { sessionUserId: session.userId } : {},
409
- ...(session == null ? void 0 : session.name) ? { sessionUserName: String(session.name) } : {},
410
- ...(session == null ? void 0 : session.email) ? { sessionUserEmail: String(session.email) } : {}
411
- }
412
- };
413
- sendReport(payload, baseUrl).then((result) => {
414
- if (result && onReportSent) onReportSent(result);
415
- });
416
- if (onError && event.error) onError(event.error);
417
- } catch (e) {
418
- }
419
- };
420
- const handleRejection = (event) => {
421
- var _a;
422
- try {
423
- const context = captureContext(visitedPagesRef.current);
424
- const reason = event.reason;
425
- const payload = {
426
- token,
427
- source: "SDK_AUTO",
428
- type: "BUG",
429
- errorMessage: reason instanceof Error ? reason.message : String(reason),
430
- errorStack: reason instanceof Error ? reason.stack : void 0,
431
- pageUrl: context.url,
432
- userAgent: context.userAgent,
433
- breadcrumbs: context.breadcrumbs,
434
- deviceInfo: (_a = context.deviceInfo) != null ? _a : void 0,
435
- metadata: {
436
- timestamp: context.timestamp,
437
- visitedPages: JSON.stringify(context.visitedPages),
438
- type: "unhandledrejection",
439
- ...(session == null ? void 0 : session.userId) ? { sessionUserId: session.userId } : {},
440
- ...(session == null ? void 0 : session.name) ? { sessionUserName: String(session.name) } : {},
441
- ...(session == null ? void 0 : session.email) ? { sessionUserEmail: String(session.email) } : {}
442
- }
443
- };
444
- sendReport(payload, baseUrl).then((result) => {
445
- if (result && onReportSent) onReportSent(result);
446
- });
447
- if (onError && reason instanceof Error) onError(reason);
448
- } catch (e) {
449
- }
450
- };
451
- window.addEventListener("error", handleError);
452
- window.addEventListener("unhandledrejection", handleRejection);
453
- return () => {
454
- window.removeEventListener("error", handleError);
455
- window.removeEventListener("unhandledrejection", handleRejection);
456
- };
457
- } catch (e) {
458
- }
459
- }, [token, baseUrl, onError, onReportSent]);
460
- const report = useCallback(
461
- async (type, description, metadata) => {
462
- var _a;
463
- try {
464
- const context = captureContext(visitedPagesRef.current);
465
- const payload = {
466
- token,
467
- source: "SDK_USER_REPORT",
468
- type,
469
- description,
470
- pageUrl: context.url,
471
- userAgent: context.userAgent,
472
- breadcrumbs: context.breadcrumbs,
473
- deviceInfo: (_a = context.deviceInfo) != null ? _a : void 0,
474
- metadata: {
475
- timestamp: context.timestamp,
476
- visitedPages: JSON.stringify(context.visitedPages),
477
- ...(session == null ? void 0 : session.userId) ? { sessionUserId: session.userId } : {},
478
- ...(session == null ? void 0 : session.name) ? { sessionUserName: String(session.name) } : {},
479
- ...(session == null ? void 0 : session.email) ? { sessionUserEmail: String(session.email) } : {},
480
- ...(session == null ? void 0 : session.phone) ? { sessionUserPhone: String(session.phone) } : {},
481
- ...metadata
482
- }
483
- };
484
- const result = await sendReport(payload, baseUrl);
485
- if (result && onReportSent) onReportSent(result);
486
- return result;
487
- } catch (e) {
488
- return null;
489
- }
490
- },
491
- [token, baseUrl, onReportSent, session]
492
- );
493
- const reportBug = useCallback(
494
- (description, metadata) => report("BUG", description, metadata),
495
- [report]
496
- );
497
- const addBreadcrumb2 = useCallback(
498
- (message, data) => {
499
- addBreadcrumb("custom", message, data);
500
- },
501
- []
502
- );
503
- const openReportDialog = useCallback((options) => {
504
- if (typeof window !== "undefined") {
505
- window.dispatchEvent(new CustomEvent("glitchgrab:open-report", { detail: options }));
506
- }
507
- }, []);
508
- return /* @__PURE__ */ jsx(
509
- GlitchgrabContext.Provider,
510
- {
511
- value: {
512
- token,
513
- baseUrl: baseUrl != null ? baseUrl : DEFAULT_BASE_URL2,
514
- reportBug,
515
- report,
516
- addBreadcrumb: addBreadcrumb2,
517
- openReportDialog
518
- },
519
- children: /* @__PURE__ */ jsx(
520
- GlitchgrabErrorBoundary,
521
- {
522
- token,
523
- baseUrl,
524
- onError,
525
- fallback,
526
- visitedPages: visitedPagesRef.current,
527
- children
528
- }
529
- )
530
- }
531
- );
532
- }
533
- function GlitchgrabProvider(props) {
534
- if (!props.token) return /* @__PURE__ */ jsx(Fragment, { children: props.children });
535
- const resolvedProps = {
536
- ...props,
537
- baseUrl: props.baseUrl || DEFAULT_BASE_URL2
538
- };
539
- try {
540
- return /* @__PURE__ */ jsx(GlitchgrabProviderInner, { ...resolvedProps });
541
- } catch (e) {
542
- return /* @__PURE__ */ jsx(Fragment, { children: props.children });
543
- }
544
- }
545
-
546
- // src/report-button.tsx
547
- import { useState as useState2, useRef as useRef2, useEffect as useEffect2 } from "react";
319
+ // src/report-dialog.tsx
320
+ import { useState, useRef, useEffect } from "react";
548
321
  import { createPortal } from "react-dom";
549
- import { Fragment as Fragment2, jsx as jsx2, jsxs } from "react/jsx-runtime";
550
- function useGlitchgrabSafe() {
551
- try {
552
- return useGlitchgrab();
553
- } catch (e) {
554
- return null;
555
- }
556
- }
322
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
557
323
  function useIsDark() {
558
324
  if (typeof window === "undefined") return true;
559
325
  try {
@@ -571,37 +337,28 @@ function useIsDark() {
571
337
  function getTheme(dark) {
572
338
  return dark ? { bg: "#1c1c1e", bgSecondary: "#27272a", border: "#2c2c2e", text: "#fafafa", textMuted: "#a1a1aa", accent: "#22d3ee", accentText: "#09090b", inputBg: "#27272a", inputBorder: "#3f3f46" } : { bg: "#ffffff", bgSecondary: "#f4f4f5", border: "#e4e4e7", text: "#18181b", textMuted: "#71717a", accent: "#0891b2", accentText: "#ffffff", inputBg: "#fafafa", inputBorder: "#d4d4d8" };
573
339
  }
574
- function ReportButton({
575
- position = "bottom-right",
576
- label = "Report Bug",
577
- className,
578
- variant = "classic",
579
- showSeverity = true,
340
+ function ReportDialog({
341
+ report,
580
342
  types,
581
- children
343
+ showSeverity = true
582
344
  }) {
583
- var _a, _b;
584
- const [capturing, setCapturing] = useState2(false);
585
- const [isOpen, setIsOpen] = useState2(false);
586
- const [description, setDescription] = useState2("");
587
- const [isSubmitting, setIsSubmitting] = useState2(false);
588
- const [submitted, setSubmitted] = useState2(false);
589
- const [screenshot, setScreenshot] = useState2(null);
590
- const [previewOpen, setPreviewOpen] = useState2(false);
591
- const fileInputRef = useRef2(null);
592
- const modalRef = useRef2(null);
593
- const glitchgrab = useGlitchgrabSafe();
594
- const [step, setStep] = useState2(1);
595
- const [reportType, setReportType] = useState2("BUG");
596
- const [severity, setSeverity] = useState2("medium");
345
+ const [isOpen, setIsOpen] = useState(false);
346
+ const [description, setDescription] = useState("");
347
+ const [isSubmitting, setIsSubmitting] = useState(false);
348
+ const [submitted, setSubmitted] = useState(false);
349
+ const [screenshot, setScreenshot] = useState(null);
350
+ const [previewOpen, setPreviewOpen] = useState(false);
351
+ const fileInputRef = useRef(null);
352
+ const modalRef = useRef(null);
353
+ const [step, setStep] = useState(1);
354
+ const [reportType, setReportType] = useState("BUG");
355
+ const [severity, setSeverity] = useState("medium");
597
356
  const availableTypes = types != null ? types : ["BUG", "FEATURE_REQUEST", "QUESTION", "OTHER"];
598
- if (!glitchgrab) return null;
599
- const { reportBug, report } = glitchgrab;
600
357
  const isDark = useIsDark();
601
358
  const t = getTheme(isDark);
602
359
  const handleFileUpload = (e) => {
603
- var _a2;
604
- const file = (_a2 = e.target.files) == null ? void 0 : _a2[0];
360
+ var _a;
361
+ const file = (_a = e.target.files) == null ? void 0 : _a[0];
605
362
  if (!file) return;
606
363
  const reader = new FileReader();
607
364
  reader.onload = () => {
@@ -629,9 +386,8 @@ function ReportButton({
629
386
  setIsOpen(true);
630
387
  };
631
388
  const handleOpen = async () => {
632
- setCapturing(true);
633
389
  setSubmitted(false);
634
- if (variant === "stepper" && availableTypes.length === 1) {
390
+ if (availableTypes.length === 1) {
635
391
  setReportType(availableTypes[0]);
636
392
  setStep(2);
637
393
  }
@@ -646,24 +402,23 @@ function ReportButton({
646
402
  setScreenshot(canvas.toDataURL("image/jpeg", 0.6));
647
403
  } catch (e) {
648
404
  }
649
- setCapturing(false);
650
405
  setIsOpen(true);
651
406
  };
652
- useEffect2(() => {
407
+ useEffect(() => {
653
408
  const handler = (e) => {
654
409
  if (isOpen) return;
655
410
  const detail = e.detail;
656
411
  if (detail == null ? void 0 : detail.description) setDescription(detail.description);
657
412
  if (detail == null ? void 0 : detail.type) {
658
413
  setReportType(detail.type);
659
- if (variant === "stepper") setStep(2);
414
+ setStep(2);
660
415
  }
661
416
  handleOpen();
662
417
  };
663
418
  window.addEventListener("glitchgrab:open-report", handler);
664
419
  return () => window.removeEventListener("glitchgrab:open-report", handler);
665
- }, [isOpen, handleOpen, variant]);
666
- useEffect2(() => {
420
+ }, [isOpen, handleOpen]);
421
+ useEffect(() => {
667
422
  if (!isOpen) return;
668
423
  const handleEsc = (e) => {
669
424
  if (e.key === "Escape") {
@@ -686,10 +441,10 @@ function ReportButton({
686
441
  setIsSubmitting(true);
687
442
  const metadata = {};
688
443
  if (screenshot) metadata.screenshot = screenshot;
689
- if (variant === "stepper" && showSeverity && reportType === "BUG") {
444
+ if (showSeverity && reportType === "BUG") {
690
445
  metadata.severity = severity;
691
446
  }
692
- const result = variant === "stepper" ? await report(reportType, description.trim(), Object.keys(metadata).length > 0 ? metadata : void 0) : await reportBug(description.trim(), Object.keys(metadata).length > 0 ? metadata : void 0);
447
+ const result = await report(reportType, description.trim(), Object.keys(metadata).length > 0 ? metadata : void 0);
693
448
  if (result) {
694
449
  setSubmitted(true);
695
450
  setDescription("");
@@ -704,27 +459,10 @@ function ReportButton({
704
459
  setIsSubmitting(false);
705
460
  }
706
461
  };
707
- const isTop = (_a = position == null ? void 0 : position.startsWith("top")) != null ? _a : false;
708
- const isLeft = (_b = position == null ? void 0 : position.endsWith("left")) != null ? _b : false;
709
- const modalPositionStyles = children ? {} : {
710
- ...isLeft ? { left: "16px" } : { right: "16px" },
711
- ...isTop ? { top: "64px" } : { bottom: "64px" }
712
- };
713
- return /* @__PURE__ */ jsxs(Fragment2, { children: [
714
- children ? children({ onClick: handleOpen, capturing }) : /* @__PURE__ */ jsx2(
715
- DefaultTrigger,
716
- {
717
- onClick: handleOpen,
718
- capturing,
719
- label,
720
- className,
721
- position,
722
- isLeft,
723
- isTop
724
- }
725
- ),
726
- isOpen && typeof document !== "undefined" && createPortal(
727
- /* @__PURE__ */ jsx2(
462
+ if (typeof document === "undefined") return null;
463
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
464
+ isOpen && createPortal(
465
+ /* @__PURE__ */ jsx(
728
466
  "div",
729
467
  {
730
468
  style: {
@@ -746,8 +484,7 @@ function ReportButton({
746
484
  ref: modalRef,
747
485
  onClick: (e) => e.stopPropagation(),
748
486
  style: {
749
- position: children ? "relative" : "fixed",
750
- ...modalPositionStyles,
487
+ position: "relative",
751
488
  zIndex: 2147483647,
752
489
  width: "340px",
753
490
  maxWidth: "calc(100% - 32px)",
@@ -778,7 +515,7 @@ function ReportButton({
778
515
  },
779
516
  children: [
780
517
  /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
781
- variant === "stepper" && step > 1 && /* @__PURE__ */ jsx2(
518
+ step > 1 && /* @__PURE__ */ jsx(
782
519
  "button",
783
520
  {
784
521
  type: "button",
@@ -792,10 +529,10 @@ function ReportButton({
792
529
  alignItems: "center"
793
530
  },
794
531
  "aria-label": "Back",
795
- children: /* @__PURE__ */ jsx2("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: /* @__PURE__ */ jsx2("path", { d: "M10 3L5 8L10 13", stroke: t.textMuted, strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) })
532
+ children: /* @__PURE__ */ jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: /* @__PURE__ */ jsx("path", { d: "M10 3L5 8L10 13", stroke: t.textMuted, strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) })
796
533
  }
797
534
  ),
798
- /* @__PURE__ */ jsx2(
535
+ /* @__PURE__ */ jsx(
799
536
  "span",
800
537
  {
801
538
  style: {
@@ -803,11 +540,11 @@ function ReportButton({
803
540
  fontWeight: 600,
804
541
  color: t.text
805
542
  },
806
- children: variant === "stepper" ? step === 1 ? "What's on your mind?" : step === 2 ? "Tell us more" : "Review & Send" : "Report a Bug"
543
+ children: step === 1 ? "What's on your mind?" : step === 2 ? "Tell us more" : "Review & Send"
807
544
  }
808
545
  )
809
546
  ] }),
810
- /* @__PURE__ */ jsx2(
547
+ /* @__PURE__ */ jsx(
811
548
  "button",
812
549
  {
813
550
  type: "button",
@@ -822,21 +559,21 @@ function ReportButton({
822
559
  justifyContent: "center"
823
560
  },
824
561
  "aria-label": "Close",
825
- children: /* @__PURE__ */ jsx2("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", style: { display: "block" }, children: /* @__PURE__ */ jsx2("path", { d: "M4 4L12 12M12 4L4 12", stroke: t.textMuted, strokeWidth: "2", strokeLinecap: "round" }) })
562
+ children: /* @__PURE__ */ jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", style: { display: "block" }, children: /* @__PURE__ */ jsx("path", { d: "M4 4L12 12M12 4L4 12", stroke: t.textMuted, strokeWidth: "2", strokeLinecap: "round" }) })
826
563
  }
827
564
  )
828
565
  ]
829
566
  }
830
567
  ),
831
- variant === "stepper" && /* @__PURE__ */ jsx2("div", { style: { display: "flex", alignItems: "center", justifyContent: "center", gap: "0", marginTop: "12px" }, children: [1, 2, 3].map((s, i) => /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center" }, children: [
832
- /* @__PURE__ */ jsx2("div", { style: {
568
+ /* @__PURE__ */ jsx("div", { style: { display: "flex", alignItems: "center", justifyContent: "center", gap: "0", marginTop: "12px" }, children: [1, 2, 3].map((s, i) => /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center" }, children: [
569
+ /* @__PURE__ */ jsx("div", { style: {
833
570
  width: "8px",
834
571
  height: "8px",
835
572
  borderRadius: "50%",
836
573
  backgroundColor: s <= step ? t.accent : t.inputBorder,
837
574
  transition: "background-color 0.2s ease"
838
575
  } }),
839
- i < 2 && /* @__PURE__ */ jsx2("div", { style: {
576
+ i < 2 && /* @__PURE__ */ jsx("div", { style: {
840
577
  width: "40px",
841
578
  height: "2px",
842
579
  backgroundColor: s < step ? t.accent : t.inputBorder,
@@ -846,7 +583,7 @@ function ReportButton({
846
583
  ]
847
584
  }
848
585
  ),
849
- /* @__PURE__ */ jsx2("div", { style: { padding: "16px" }, children: submitted ? /* @__PURE__ */ jsx2(
586
+ /* @__PURE__ */ jsx("div", { style: { padding: "16px" }, children: submitted ? /* @__PURE__ */ jsxs(
850
587
  "div",
851
588
  {
852
589
  style: {
@@ -856,283 +593,172 @@ function ReportButton({
856
593
  fontSize: "14px",
857
594
  fontWeight: 500
858
595
  },
859
- children: variant === "stepper" ? `${getTypeLabel(reportType)} sent. Thank you!` : "Bug report sent. Thank you!"
596
+ children: [
597
+ getTypeLabel(reportType),
598
+ " sent. Thank you!"
599
+ ]
860
600
  }
861
- ) : variant === "stepper" ? (
862
- /* ─── Stepper Flow ─── */
863
- /* @__PURE__ */ jsxs(Fragment2, { children: [
864
- step === 1 && /* @__PURE__ */ jsx2("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: "8px" }, children: availableTypes.map((type) => /* @__PURE__ */ jsxs(
865
- "button",
866
- {
867
- type: "button",
868
- onClick: () => {
869
- setReportType(type);
870
- setStep(2);
871
- },
872
- style: {
873
- display: "flex",
874
- flexDirection: "column",
875
- alignItems: "center",
876
- gap: "8px",
877
- padding: "16px 8px",
878
- borderRadius: "8px",
879
- border: `1px solid ${t.inputBorder}`,
880
- background: t.inputBg,
881
- cursor: "pointer",
882
- color: t.text,
883
- fontFamily: "inherit",
884
- transition: "border-color 0.15s ease"
885
- },
886
- onMouseEnter: (e) => {
887
- e.currentTarget.style.borderColor = t.accent;
888
- },
889
- onMouseLeave: (e) => {
890
- e.currentTarget.style.borderColor = t.inputBorder;
891
- },
892
- children: [
893
- getTypeIcon(type, t.accent),
894
- /* @__PURE__ */ jsx2("span", { style: { fontSize: "13px", fontWeight: 600 }, children: getTypeLabel(type) }),
895
- /* @__PURE__ */ jsx2("span", { style: { fontSize: "11px", color: t.textMuted, lineHeight: "1.3" }, children: getTypeSubtitle(type) })
896
- ]
601
+ ) : /* @__PURE__ */ jsxs(Fragment, { children: [
602
+ step === 1 && /* @__PURE__ */ jsx("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: "8px" }, children: availableTypes.map((type) => /* @__PURE__ */ jsxs(
603
+ "button",
604
+ {
605
+ type: "button",
606
+ onClick: () => {
607
+ setReportType(type);
608
+ setStep(2);
897
609
  },
898
- type
899
- )) }),
900
- step === 2 && /* @__PURE__ */ jsxs(Fragment2, { children: [
901
- /* @__PURE__ */ jsx2(
902
- "textarea",
903
- {
904
- value: description,
905
- onChange: (e) => setDescription(e.target.value),
906
- placeholder: getPlaceholder(reportType),
907
- style: {
908
- width: "100%",
909
- minHeight: "100px",
910
- padding: "10px 12px",
911
- borderRadius: "8px",
912
- border: `1px solid ${t.inputBorder}`,
913
- fontSize: "14px",
914
- fontFamily: "inherit",
915
- resize: "vertical",
916
- outline: "none",
917
- boxSizing: "border-box",
918
- color: t.text,
919
- backgroundColor: t.inputBg
920
- },
921
- onFocus: (e) => {
922
- e.target.style.borderColor = t.accent;
923
- },
924
- onBlur: (e) => {
925
- e.target.style.borderColor = t.inputBorder;
926
- },
927
- autoFocus: true
928
- }
929
- ),
930
- showSeverity && reportType === "BUG" && /* @__PURE__ */ jsxs("div", { style: { marginTop: "10px" }, children: [
931
- /* @__PURE__ */ jsx2("span", { style: { fontSize: "12px", color: t.textMuted, marginBottom: "6px", display: "block" }, children: "Severity" }),
932
- /* @__PURE__ */ jsx2("div", { style: { display: "flex", gap: "6px" }, children: ["low", "medium", "high"].map((s) => /* @__PURE__ */ jsx2(
933
- "button",
934
- {
935
- type: "button",
936
- onClick: () => setSeverity(s),
937
- style: {
938
- flex: 1,
939
- padding: "6px 0",
940
- borderRadius: "6px",
941
- border: `1px solid ${severity === s ? t.accent : t.inputBorder}`,
942
- background: severity === s ? t.accent : "transparent",
943
- color: severity === s ? t.accentText : t.text,
944
- fontSize: "12px",
945
- fontWeight: 600,
946
- cursor: "pointer",
947
- fontFamily: "inherit",
948
- textTransform: "capitalize",
949
- transition: "all 0.15s ease"
950
- },
951
- children: s
952
- },
953
- s
954
- )) })
955
- ] }),
956
- /* @__PURE__ */ jsx2(
610
+ style: {
611
+ display: "flex",
612
+ flexDirection: "column",
613
+ alignItems: "center",
614
+ gap: "8px",
615
+ padding: "16px 8px",
616
+ borderRadius: "8px",
617
+ border: `1px solid ${t.inputBorder}`,
618
+ background: t.inputBg,
619
+ cursor: "pointer",
620
+ color: t.text,
621
+ fontFamily: "inherit",
622
+ transition: "border-color 0.15s ease"
623
+ },
624
+ onMouseEnter: (e) => {
625
+ e.currentTarget.style.borderColor = t.accent;
626
+ },
627
+ onMouseLeave: (e) => {
628
+ e.currentTarget.style.borderColor = t.inputBorder;
629
+ },
630
+ children: [
631
+ getTypeIcon(type, t.accent),
632
+ /* @__PURE__ */ jsx("span", { style: { fontSize: "13px", fontWeight: 600 }, children: getTypeLabel(type) }),
633
+ /* @__PURE__ */ jsx("span", { style: { fontSize: "11px", color: t.textMuted, lineHeight: "1.3" }, children: getTypeSubtitle(type) })
634
+ ]
635
+ },
636
+ type
637
+ )) }),
638
+ step === 2 && /* @__PURE__ */ jsxs(Fragment, { children: [
639
+ /* @__PURE__ */ jsx(
640
+ "textarea",
641
+ {
642
+ value: description,
643
+ onChange: (e) => setDescription(e.target.value),
644
+ placeholder: getPlaceholder(reportType),
645
+ style: {
646
+ width: "100%",
647
+ minHeight: "100px",
648
+ padding: "10px 12px",
649
+ borderRadius: "8px",
650
+ border: `1px solid ${t.inputBorder}`,
651
+ fontSize: "14px",
652
+ fontFamily: "inherit",
653
+ resize: "vertical",
654
+ outline: "none",
655
+ boxSizing: "border-box",
656
+ color: t.text,
657
+ backgroundColor: t.inputBg
658
+ },
659
+ onFocus: (e) => {
660
+ e.target.style.borderColor = t.accent;
661
+ },
662
+ onBlur: (e) => {
663
+ e.target.style.borderColor = t.inputBorder;
664
+ },
665
+ autoFocus: true
666
+ }
667
+ ),
668
+ showSeverity && reportType === "BUG" && /* @__PURE__ */ jsxs("div", { style: { marginTop: "10px" }, children: [
669
+ /* @__PURE__ */ jsx("span", { style: { fontSize: "12px", color: t.textMuted, marginBottom: "6px", display: "block" }, children: "Severity" }),
670
+ /* @__PURE__ */ jsx("div", { style: { display: "flex", gap: "6px" }, children: ["low", "medium", "high"].map((s) => /* @__PURE__ */ jsx(
957
671
  "button",
958
672
  {
959
673
  type: "button",
960
- onClick: () => setStep(3),
961
- disabled: !description.trim(),
674
+ onClick: () => setSeverity(s),
962
675
  style: {
963
- marginTop: "12px",
964
- width: "100%",
965
- padding: "10px",
966
- borderRadius: "8px",
967
- border: "none",
968
- backgroundColor: !description.trim() ? t.bgSecondary : t.accent,
969
- color: !description.trim() ? t.textMuted : t.accentText,
970
- fontSize: "14px",
676
+ flex: 1,
677
+ padding: "6px 0",
678
+ borderRadius: "6px",
679
+ border: `1px solid ${severity === s ? t.accent : t.inputBorder}`,
680
+ background: severity === s ? t.accent : "transparent",
681
+ color: severity === s ? t.accentText : t.text,
682
+ fontSize: "12px",
971
683
  fontWeight: 600,
972
- cursor: !description.trim() ? "not-allowed" : "pointer",
684
+ cursor: "pointer",
973
685
  fontFamily: "inherit",
974
- transition: "background-color 0.15s ease"
686
+ textTransform: "capitalize",
687
+ transition: "all 0.15s ease"
975
688
  },
976
- children: "Next"
977
- }
978
- )
689
+ children: s
690
+ },
691
+ s
692
+ )) })
979
693
  ] }),
980
- step === 3 && /* @__PURE__ */ jsxs(Fragment2, { children: [
981
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "6px", flexWrap: "wrap", marginBottom: "12px" }, children: [
982
- /* @__PURE__ */ jsxs("span", { style: {
983
- display: "inline-flex",
984
- alignItems: "center",
985
- gap: "4px",
986
- padding: "4px 10px",
987
- borderRadius: "12px",
988
- fontSize: "12px",
989
- fontWeight: 600,
990
- backgroundColor: t.bgSecondary,
991
- color: t.text,
992
- border: `1px solid ${t.border}`
993
- }, children: [
994
- getTypeIcon(reportType, t.accent, 12),
995
- getTypeLabel(reportType)
996
- ] }),
997
- showSeverity && reportType === "BUG" && /* @__PURE__ */ jsx2("span", { style: {
998
- display: "inline-flex",
999
- alignItems: "center",
1000
- padding: "4px 10px",
1001
- borderRadius: "12px",
1002
- fontSize: "12px",
694
+ /* @__PURE__ */ jsx(
695
+ "button",
696
+ {
697
+ type: "button",
698
+ onClick: () => setStep(3),
699
+ disabled: !description.trim(),
700
+ style: {
701
+ marginTop: "12px",
702
+ width: "100%",
703
+ padding: "10px",
704
+ borderRadius: "8px",
705
+ border: "none",
706
+ backgroundColor: !description.trim() ? t.bgSecondary : t.accent,
707
+ color: !description.trim() ? t.textMuted : t.accentText,
708
+ fontSize: "14px",
1003
709
  fontWeight: 600,
1004
- backgroundColor: severity === "high" ? "#fef2f2" : severity === "medium" ? "#fffbeb" : "#f0fdf4",
1005
- color: severity === "high" ? "#dc2626" : severity === "medium" ? "#d97706" : "#16a34a",
1006
- border: `1px solid ${severity === "high" ? "#fecaca" : severity === "medium" ? "#fde68a" : "#bbf7d0"}`,
1007
- textTransform: "capitalize"
1008
- }, children: severity })
1009
- ] }),
1010
- /* @__PURE__ */ jsx2("div", { style: {
1011
- padding: "10px 12px",
1012
- borderRadius: "8px",
1013
- border: `1px solid ${t.inputBorder}`,
1014
- backgroundColor: t.inputBg,
1015
- fontSize: "13px",
710
+ cursor: !description.trim() ? "not-allowed" : "pointer",
711
+ fontFamily: "inherit",
712
+ transition: "background-color 0.15s ease"
713
+ },
714
+ children: "Next"
715
+ }
716
+ )
717
+ ] }),
718
+ step === 3 && /* @__PURE__ */ jsxs(Fragment, { children: [
719
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "6px", flexWrap: "wrap", marginBottom: "12px" }, children: [
720
+ /* @__PURE__ */ jsxs("span", { style: {
721
+ display: "inline-flex",
722
+ alignItems: "center",
723
+ gap: "4px",
724
+ padding: "4px 10px",
725
+ borderRadius: "12px",
726
+ fontSize: "12px",
727
+ fontWeight: 600,
728
+ backgroundColor: t.bgSecondary,
1016
729
  color: t.text,
1017
- marginBottom: "10px",
1018
- maxHeight: "80px",
1019
- overflow: "hidden",
1020
- lineHeight: "1.4"
1021
- }, children: description.length > 200 ? description.slice(0, 200) + "..." : description }),
1022
- screenshot && /* @__PURE__ */ jsxs("div", { style: { marginBottom: "10px", position: "relative" }, children: [
1023
- /* @__PURE__ */ jsx2(
1024
- "img",
1025
- {
1026
- src: screenshot,
1027
- alt: "Page screenshot",
1028
- onClick: () => setPreviewOpen(true),
1029
- style: {
1030
- width: "100%",
1031
- borderRadius: "6px",
1032
- border: `1px solid ${t.border}`,
1033
- maxHeight: "80px",
1034
- objectFit: "cover",
1035
- objectPosition: "top",
1036
- cursor: "pointer"
1037
- }
1038
- }
1039
- ),
1040
- /* @__PURE__ */ jsx2(
1041
- "button",
1042
- {
1043
- type: "button",
1044
- onClick: () => setScreenshot(null),
1045
- style: {
1046
- position: "absolute",
1047
- top: "4px",
1048
- right: "4px",
1049
- background: "rgba(0,0,0,0.6)",
1050
- border: "none",
1051
- borderRadius: "50%",
1052
- width: "20px",
1053
- height: "20px",
1054
- cursor: "pointer",
1055
- display: "flex",
1056
- alignItems: "center",
1057
- justifyContent: "center"
1058
- },
1059
- "aria-label": "Remove screenshot",
1060
- children: /* @__PURE__ */ jsx2("svg", { width: "10", height: "10", viewBox: "0 0 10 10", fill: "none", children: /* @__PURE__ */ jsx2("path", { d: "M1 1L9 9M9 1L1 9", stroke: "#fff", strokeWidth: "1.5", strokeLinecap: "round" }) })
1061
- }
1062
- )
1063
- ] }),
1064
- !screenshot && /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "6px", marginBottom: "10px" }, children: [
1065
- /* @__PURE__ */ jsx2(
1066
- "button",
1067
- {
1068
- type: "button",
1069
- onClick: () => {
1070
- var _a2;
1071
- return (_a2 = fileInputRef.current) == null ? void 0 : _a2.click();
1072
- },
1073
- style: {
1074
- flex: 1,
1075
- padding: "8px",
1076
- borderRadius: "6px",
1077
- border: `1px dashed ${t.inputBorder}`,
1078
- textAlign: "center",
1079
- fontSize: "11px",
1080
- color: t.textMuted,
1081
- background: "none",
1082
- cursor: "pointer"
1083
- },
1084
- children: "Upload screenshot"
1085
- }
1086
- ),
1087
- /* @__PURE__ */ jsx2(
1088
- "button",
1089
- {
1090
- type: "button",
1091
- onClick: retakeScreenshot,
1092
- style: {
1093
- flex: 1,
1094
- padding: "8px",
1095
- borderRadius: "6px",
1096
- border: `1px dashed ${t.inputBorder}`,
1097
- textAlign: "center",
1098
- fontSize: "11px",
1099
- color: t.textMuted,
1100
- background: "none",
1101
- cursor: "pointer"
1102
- },
1103
- children: "Retake screenshot"
1104
- }
1105
- )
730
+ border: `1px solid ${t.border}`
731
+ }, children: [
732
+ getTypeIcon(reportType, t.accent, 12),
733
+ getTypeLabel(reportType)
1106
734
  ] }),
1107
- /* @__PURE__ */ jsx2(
1108
- "button",
1109
- {
1110
- type: "button",
1111
- onClick: handleSubmit,
1112
- disabled: isSubmitting,
1113
- style: {
1114
- width: "100%",
1115
- padding: "10px",
1116
- borderRadius: "8px",
1117
- border: "none",
1118
- backgroundColor: isSubmitting ? t.bgSecondary : t.accent,
1119
- color: isSubmitting ? t.textMuted : t.accentText,
1120
- fontSize: "14px",
1121
- fontWeight: 600,
1122
- cursor: isSubmitting ? "not-allowed" : "pointer",
1123
- fontFamily: "inherit",
1124
- transition: "background-color 0.15s ease"
1125
- },
1126
- children: isSubmitting ? "Sending..." : "Send Report"
1127
- }
1128
- )
1129
- ] })
1130
- ] })
1131
- ) : (
1132
- /* ─── Classic Flow ─── */
1133
- /* @__PURE__ */ jsxs(Fragment2, { children: [
735
+ showSeverity && reportType === "BUG" && /* @__PURE__ */ jsx("span", { style: {
736
+ display: "inline-flex",
737
+ alignItems: "center",
738
+ padding: "4px 10px",
739
+ borderRadius: "12px",
740
+ fontSize: "12px",
741
+ fontWeight: 600,
742
+ backgroundColor: severity === "high" ? "#fef2f2" : severity === "medium" ? "#fffbeb" : "#f0fdf4",
743
+ color: severity === "high" ? "#dc2626" : severity === "medium" ? "#d97706" : "#16a34a",
744
+ border: `1px solid ${severity === "high" ? "#fecaca" : severity === "medium" ? "#fde68a" : "#bbf7d0"}`,
745
+ textTransform: "capitalize"
746
+ }, children: severity })
747
+ ] }),
748
+ /* @__PURE__ */ jsx("div", { style: {
749
+ padding: "10px 12px",
750
+ borderRadius: "8px",
751
+ border: `1px solid ${t.inputBorder}`,
752
+ backgroundColor: t.inputBg,
753
+ fontSize: "13px",
754
+ color: t.text,
755
+ marginBottom: "10px",
756
+ maxHeight: "80px",
757
+ overflow: "hidden",
758
+ lineHeight: "1.4"
759
+ }, children: description.length > 200 ? description.slice(0, 200) + "..." : description }),
1134
760
  screenshot && /* @__PURE__ */ jsxs("div", { style: { marginBottom: "10px", position: "relative" }, children: [
1135
- /* @__PURE__ */ jsx2(
761
+ /* @__PURE__ */ jsx(
1136
762
  "img",
1137
763
  {
1138
764
  src: screenshot,
@@ -1142,14 +768,14 @@ function ReportButton({
1142
768
  width: "100%",
1143
769
  borderRadius: "6px",
1144
770
  border: `1px solid ${t.border}`,
1145
- maxHeight: "120px",
771
+ maxHeight: "80px",
1146
772
  objectFit: "cover",
1147
773
  objectPosition: "top",
1148
774
  cursor: "pointer"
1149
775
  }
1150
776
  }
1151
777
  ),
1152
- /* @__PURE__ */ jsx2(
778
+ /* @__PURE__ */ jsx(
1153
779
  "button",
1154
780
  {
1155
781
  type: "button",
@@ -1169,153 +795,69 @@ function ReportButton({
1169
795
  justifyContent: "center"
1170
796
  },
1171
797
  "aria-label": "Remove screenshot",
1172
- children: /* @__PURE__ */ jsx2("svg", { width: "10", height: "10", viewBox: "0 0 10 10", fill: "none", children: /* @__PURE__ */ jsx2("path", { d: "M1 1L9 9M9 1L1 9", stroke: "#fff", strokeWidth: "1.5", strokeLinecap: "round" }) })
798
+ children: /* @__PURE__ */ jsx("svg", { width: "10", height: "10", viewBox: "0 0 10 10", fill: "none", children: /* @__PURE__ */ jsx("path", { d: "M1 1L9 9M9 1L1 9", stroke: "#fff", strokeWidth: "1.5", strokeLinecap: "round" }) })
799
+ }
800
+ )
801
+ ] }),
802
+ !screenshot && /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "6px", marginBottom: "10px" }, children: [
803
+ /* @__PURE__ */ jsx(
804
+ "button",
805
+ {
806
+ type: "button",
807
+ onClick: () => {
808
+ var _a;
809
+ return (_a = fileInputRef.current) == null ? void 0 : _a.click();
810
+ },
811
+ style: {
812
+ flex: 1,
813
+ padding: "8px",
814
+ borderRadius: "6px",
815
+ border: `1px dashed ${t.inputBorder}`,
816
+ textAlign: "center",
817
+ fontSize: "11px",
818
+ color: t.textMuted,
819
+ background: "none",
820
+ cursor: "pointer"
821
+ },
822
+ children: "Upload screenshot"
1173
823
  }
1174
824
  ),
1175
- /* @__PURE__ */ jsxs(
1176
- "div",
825
+ /* @__PURE__ */ jsx(
826
+ "button",
1177
827
  {
828
+ type: "button",
829
+ onClick: retakeScreenshot,
1178
830
  style: {
1179
- position: "absolute",
1180
- bottom: "4px",
1181
- left: "6px",
1182
- right: "6px",
1183
- display: "flex",
1184
- justifyContent: "space-between",
1185
- alignItems: "center"
831
+ flex: 1,
832
+ padding: "8px",
833
+ borderRadius: "6px",
834
+ border: `1px dashed ${t.inputBorder}`,
835
+ textAlign: "center",
836
+ fontSize: "11px",
837
+ color: t.textMuted,
838
+ background: "none",
839
+ cursor: "pointer"
1186
840
  },
1187
- children: [
1188
- /* @__PURE__ */ jsxs(
1189
- "span",
1190
- {
1191
- style: {
1192
- background: "rgba(0,0,0,0.6)",
1193
- color: "#fff",
1194
- fontSize: "10px",
1195
- padding: "2px 6px",
1196
- borderRadius: "4px"
1197
- },
1198
- children: [
1199
- "Screenshot (",
1200
- Math.round(screenshot.length * 0.75 / 1024),
1201
- "KB) \u2014 click to preview"
1202
- ]
1203
- }
1204
- ),
1205
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "4px" }, children: [
1206
- /* @__PURE__ */ jsx2(
1207
- "button",
1208
- {
1209
- type: "button",
1210
- onClick: () => {
1211
- var _a2;
1212
- return (_a2 = fileInputRef.current) == null ? void 0 : _a2.click();
1213
- },
1214
- style: {
1215
- background: t.accent,
1216
- color: t.accentText,
1217
- fontSize: "10px",
1218
- padding: "3px 8px",
1219
- borderRadius: "4px",
1220
- border: "none",
1221
- cursor: "pointer",
1222
- fontWeight: 600
1223
- },
1224
- children: "Upload"
1225
- }
1226
- ),
1227
- /* @__PURE__ */ jsx2(
1228
- "button",
1229
- {
1230
- type: "button",
1231
- onClick: retakeScreenshot,
1232
- style: {
1233
- background: t.accent,
1234
- color: t.accentText,
1235
- fontSize: "10px",
1236
- padding: "3px 8px",
1237
- borderRadius: "4px",
1238
- border: "none",
1239
- cursor: "pointer",
1240
- fontWeight: 600
1241
- },
1242
- children: "Retake"
1243
- }
1244
- )
1245
- ] })
1246
- ]
841
+ children: "Retake screenshot"
1247
842
  }
1248
843
  )
1249
844
  ] }),
1250
- !screenshot && !submitted && /* @__PURE__ */ jsx2(
1251
- "button",
1252
- {
1253
- type: "button",
1254
- onClick: () => {
1255
- var _a2;
1256
- return (_a2 = fileInputRef.current) == null ? void 0 : _a2.click();
1257
- },
1258
- style: {
1259
- marginBottom: "10px",
1260
- padding: "10px",
1261
- borderRadius: "6px",
1262
- border: `1px dashed ${t.inputBorder}`,
1263
- textAlign: "center",
1264
- fontSize: "11px",
1265
- color: t.textMuted,
1266
- width: "100%",
1267
- background: "none",
1268
- cursor: "pointer"
1269
- },
1270
- children: "Click to upload a screenshot"
1271
- }
1272
- ),
1273
- /* @__PURE__ */ jsx2(
1274
- "textarea",
1275
- {
1276
- value: description,
1277
- onChange: (e) => setDescription(e.target.value),
1278
- placeholder: "Describe what went wrong...",
1279
- style: {
1280
- width: "100%",
1281
- minHeight: "100px",
1282
- padding: "10px 12px",
1283
- borderRadius: "8px",
1284
- border: `1px solid ${t.inputBorder}`,
1285
- fontSize: "14px",
1286
- fontFamily: "inherit",
1287
- resize: "vertical",
1288
- outline: "none",
1289
- boxSizing: "border-box",
1290
- color: t.text,
1291
- backgroundColor: t.inputBg
1292
- },
1293
- onFocus: (e) => {
1294
- e.target.style.borderColor = t.accent;
1295
- },
1296
- onBlur: (e) => {
1297
- e.target.style.borderColor = t.inputBorder;
1298
- },
1299
- autoFocus: true
1300
- }
1301
- ),
1302
- /* @__PURE__ */ jsx2(
845
+ /* @__PURE__ */ jsx(
1303
846
  "button",
1304
847
  {
1305
848
  type: "button",
1306
849
  onClick: handleSubmit,
1307
- disabled: !description.trim() || isSubmitting,
850
+ disabled: isSubmitting,
1308
851
  style: {
1309
- marginTop: "12px",
1310
852
  width: "100%",
1311
853
  padding: "10px",
1312
854
  borderRadius: "8px",
1313
855
  border: "none",
1314
- backgroundColor: !description.trim() || isSubmitting ? t.bgSecondary : t.accent,
1315
- color: !description.trim() || isSubmitting ? t.textMuted : t.accentText,
856
+ backgroundColor: isSubmitting ? t.bgSecondary : t.accent,
857
+ color: isSubmitting ? t.textMuted : t.accentText,
1316
858
  fontSize: "14px",
1317
859
  fontWeight: 600,
1318
- cursor: !description.trim() || isSubmitting ? "not-allowed" : "pointer",
860
+ cursor: isSubmitting ? "not-allowed" : "pointer",
1319
861
  fontFamily: "inherit",
1320
862
  transition: "background-color 0.15s ease"
1321
863
  },
@@ -1323,8 +865,8 @@ function ReportButton({
1323
865
  }
1324
866
  )
1325
867
  ] })
1326
- ) }),
1327
- /* @__PURE__ */ jsx2(
868
+ ] }) }),
869
+ /* @__PURE__ */ jsx(
1328
870
  "div",
1329
871
  {
1330
872
  style: {
@@ -1332,7 +874,7 @@ function ReportButton({
1332
874
  borderTop: `1px solid ${t.border}`,
1333
875
  textAlign: "center"
1334
876
  },
1335
- children: /* @__PURE__ */ jsx2("span", { style: { fontSize: "11px", color: t.textMuted }, children: "Powered by Glitchgrab" })
877
+ children: /* @__PURE__ */ jsx("span", { style: { fontSize: "11px", color: t.textMuted }, children: "Powered by Glitchgrab" })
1336
878
  }
1337
879
  )
1338
880
  ]
@@ -1342,7 +884,7 @@ function ReportButton({
1342
884
  ),
1343
885
  document.body
1344
886
  ),
1345
- /* @__PURE__ */ jsx2(
887
+ /* @__PURE__ */ jsx(
1346
888
  "input",
1347
889
  {
1348
890
  ref: fileInputRef,
@@ -1352,7 +894,7 @@ function ReportButton({
1352
894
  style: { display: "none" }
1353
895
  }
1354
896
  ),
1355
- previewOpen && screenshot && typeof document !== "undefined" && createPortal(
897
+ previewOpen && screenshot && createPortal(
1356
898
  /* @__PURE__ */ jsxs(
1357
899
  "div",
1358
900
  {
@@ -1374,7 +916,7 @@ function ReportButton({
1374
916
  },
1375
917
  onMouseDown: (e) => e.stopPropagation(),
1376
918
  children: [
1377
- /* @__PURE__ */ jsx2(
919
+ /* @__PURE__ */ jsx(
1378
920
  "img",
1379
921
  {
1380
922
  src: screenshot,
@@ -1389,7 +931,7 @@ function ReportButton({
1389
931
  }
1390
932
  }
1391
933
  ),
1392
- /* @__PURE__ */ jsx2(
934
+ /* @__PURE__ */ jsx(
1393
935
  "span",
1394
936
  {
1395
937
  style: {
@@ -1447,36 +989,290 @@ function getTypeIcon(type, color, size = 24) {
1447
989
  const props = { width: size, height: size, viewBox: "0 0 24 24", fill: "none", style: { flexShrink: 0 } };
1448
990
  switch (type) {
1449
991
  case "BUG":
1450
- return /* @__PURE__ */ jsx2("svg", { ...props, children: /* @__PURE__ */ jsx2("path", { d: "M8 2L6.5 3.5M16 2L17.5 3.5M3 9H7M17 9H21M12 2a5 5 0 015 5v4a5 5 0 01-10 0V7a5 5 0 015-5zM7 16l-2 3M17 16l2 3", stroke: color, strokeWidth: "1.5", strokeLinecap: "round" }) });
992
+ return /* @__PURE__ */ jsx("svg", { ...props, children: /* @__PURE__ */ jsx("path", { d: "M8 2L6.5 3.5M16 2L17.5 3.5M3 9H7M17 9H21M12 2a5 5 0 015 5v4a5 5 0 01-10 0V7a5 5 0 015-5zM7 16l-2 3M17 16l2 3", stroke: color, strokeWidth: "1.5", strokeLinecap: "round" }) });
1451
993
  case "FEATURE_REQUEST":
1452
- return /* @__PURE__ */ jsx2("svg", { ...props, children: /* @__PURE__ */ jsx2("path", { d: "M12 2L15.09 8.26L22 9.27L17 14.14L18.18 21.02L12 17.77L5.82 21.02L7 14.14L2 9.27L8.91 8.26L12 2z", stroke: color, strokeWidth: "1.5", strokeLinejoin: "round" }) });
994
+ return /* @__PURE__ */ jsx("svg", { ...props, children: /* @__PURE__ */ jsx("path", { d: "M12 2L15.09 8.26L22 9.27L17 14.14L18.18 21.02L12 17.77L5.82 21.02L7 14.14L2 9.27L8.91 8.26L12 2z", stroke: color, strokeWidth: "1.5", strokeLinejoin: "round" }) });
1453
995
  case "QUESTION":
1454
996
  return /* @__PURE__ */ jsxs("svg", { ...props, children: [
1455
- /* @__PURE__ */ jsx2("circle", { cx: "12", cy: "12", r: "10", stroke: color, strokeWidth: "1.5" }),
1456
- /* @__PURE__ */ jsx2("path", { d: "M9.09 9a3 3 0 015.83 1c0 2-3 3-3 3M12 17h.01", stroke: color, strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" })
997
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10", stroke: color, strokeWidth: "1.5" }),
998
+ /* @__PURE__ */ jsx("path", { d: "M9.09 9a3 3 0 015.83 1c0 2-3 3-3 3M12 17h.01", stroke: color, strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" })
1457
999
  ] });
1458
1000
  case "OTHER":
1459
- return /* @__PURE__ */ jsx2("svg", { ...props, children: /* @__PURE__ */ jsx2("path", { d: "M21 15a2 2 0 01-2 2H7l-4 4V5a2 2 0 012-2h14a2 2 0 012 2v10z", stroke: color, strokeWidth: "1.5", strokeLinejoin: "round" }) });
1001
+ return /* @__PURE__ */ jsx("svg", { ...props, children: /* @__PURE__ */ jsx("path", { d: "M21 15a2 2 0 01-2 2H7l-4 4V5a2 2 0 012-2h14a2 2 0 012 2v10z", stroke: color, strokeWidth: "1.5", strokeLinejoin: "round" }) });
1002
+ }
1003
+ }
1004
+
1005
+ // src/provider.tsx
1006
+ import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
1007
+ var DEFAULT_BASE_URL2 = "https://www.glitchgrab.dev";
1008
+ var GlitchgrabContext = createContext(null);
1009
+ function useGlitchgrab() {
1010
+ const ctx = useContext(GlitchgrabContext);
1011
+ if (!ctx) {
1012
+ throw new Error("useGlitchgrab must be used within a GlitchgrabProvider");
1013
+ }
1014
+ return ctx;
1015
+ }
1016
+ function GlitchgrabProviderInner({
1017
+ token,
1018
+ session,
1019
+ baseUrl,
1020
+ onError,
1021
+ onReportSent,
1022
+ breadcrumbs: enableBreadcrumbs = true,
1023
+ maxBreadcrumbs: maxBreadcrumbs2 = 50,
1024
+ children,
1025
+ fallback,
1026
+ types,
1027
+ showSeverity
1028
+ }) {
1029
+ const visitedPagesRef = useRef2([]);
1030
+ useEffect2(() => {
1031
+ if (enableBreadcrumbs) {
1032
+ initBreadcrumbs(maxBreadcrumbs2);
1033
+ }
1034
+ }, [enableBreadcrumbs, maxBreadcrumbs2]);
1035
+ useEffect2(() => {
1036
+ try {
1037
+ if (typeof window === "undefined") return;
1038
+ const trackPage = () => {
1039
+ try {
1040
+ const sanitized = sanitizeUrl(window.location.href);
1041
+ const pages = visitedPagesRef.current;
1042
+ if (pages[pages.length - 1] !== sanitized) {
1043
+ pages.push(sanitized);
1044
+ if (pages.length > 20) {
1045
+ pages.splice(0, pages.length - 20);
1046
+ }
1047
+ }
1048
+ } catch (e) {
1049
+ }
1050
+ };
1051
+ trackPage();
1052
+ const handlePopState = () => trackPage();
1053
+ window.addEventListener("popstate", handlePopState);
1054
+ const origPushState = history.pushState.bind(history);
1055
+ const origReplaceState = history.replaceState.bind(history);
1056
+ history.pushState = function(...args) {
1057
+ origPushState(...args);
1058
+ trackPage();
1059
+ };
1060
+ history.replaceState = function(...args) {
1061
+ origReplaceState(...args);
1062
+ trackPage();
1063
+ };
1064
+ return () => {
1065
+ window.removeEventListener("popstate", handlePopState);
1066
+ history.pushState = origPushState;
1067
+ history.replaceState = origReplaceState;
1068
+ };
1069
+ } catch (e) {
1070
+ }
1071
+ }, []);
1072
+ useEffect2(() => {
1073
+ try {
1074
+ if (typeof window === "undefined") return;
1075
+ if (process.env.NODE_ENV === "development") return;
1076
+ const handleError = (event) => {
1077
+ var _a, _b, _c, _d, _e;
1078
+ try {
1079
+ const context = captureContext(visitedPagesRef.current);
1080
+ const payload = {
1081
+ token,
1082
+ source: "SDK_AUTO",
1083
+ type: "BUG",
1084
+ errorMessage: event.message,
1085
+ errorStack: (_a = event.error) == null ? void 0 : _a.stack,
1086
+ pageUrl: context.url,
1087
+ userAgent: context.userAgent,
1088
+ breadcrumbs: context.breadcrumbs,
1089
+ deviceInfo: (_b = context.deviceInfo) != null ? _b : void 0,
1090
+ metadata: {
1091
+ timestamp: context.timestamp,
1092
+ visitedPages: JSON.stringify(context.visitedPages),
1093
+ filename: (_c = event.filename) != null ? _c : "",
1094
+ lineno: String((_d = event.lineno) != null ? _d : ""),
1095
+ colno: String((_e = event.colno) != null ? _e : ""),
1096
+ ...(session == null ? void 0 : session.userId) ? { sessionUserId: session.userId } : {},
1097
+ ...(session == null ? void 0 : session.name) ? { sessionUserName: String(session.name) } : {},
1098
+ ...(session == null ? void 0 : session.email) ? { sessionUserEmail: String(session.email) } : {}
1099
+ }
1100
+ };
1101
+ sendReport(payload, baseUrl).then((result) => {
1102
+ if (result && onReportSent) onReportSent(result);
1103
+ });
1104
+ if (onError && event.error) onError(event.error);
1105
+ } catch (e) {
1106
+ }
1107
+ };
1108
+ const handleRejection = (event) => {
1109
+ var _a;
1110
+ try {
1111
+ const context = captureContext(visitedPagesRef.current);
1112
+ const reason = event.reason;
1113
+ const payload = {
1114
+ token,
1115
+ source: "SDK_AUTO",
1116
+ type: "BUG",
1117
+ errorMessage: reason instanceof Error ? reason.message : String(reason),
1118
+ errorStack: reason instanceof Error ? reason.stack : void 0,
1119
+ pageUrl: context.url,
1120
+ userAgent: context.userAgent,
1121
+ breadcrumbs: context.breadcrumbs,
1122
+ deviceInfo: (_a = context.deviceInfo) != null ? _a : void 0,
1123
+ metadata: {
1124
+ timestamp: context.timestamp,
1125
+ visitedPages: JSON.stringify(context.visitedPages),
1126
+ type: "unhandledrejection",
1127
+ ...(session == null ? void 0 : session.userId) ? { sessionUserId: session.userId } : {},
1128
+ ...(session == null ? void 0 : session.name) ? { sessionUserName: String(session.name) } : {},
1129
+ ...(session == null ? void 0 : session.email) ? { sessionUserEmail: String(session.email) } : {}
1130
+ }
1131
+ };
1132
+ sendReport(payload, baseUrl).then((result) => {
1133
+ if (result && onReportSent) onReportSent(result);
1134
+ });
1135
+ if (onError && reason instanceof Error) onError(reason);
1136
+ } catch (e) {
1137
+ }
1138
+ };
1139
+ window.addEventListener("error", handleError);
1140
+ window.addEventListener("unhandledrejection", handleRejection);
1141
+ return () => {
1142
+ window.removeEventListener("error", handleError);
1143
+ window.removeEventListener("unhandledrejection", handleRejection);
1144
+ };
1145
+ } catch (e) {
1146
+ }
1147
+ }, [token, baseUrl, onError, onReportSent]);
1148
+ const report = useCallback(
1149
+ async (type, description, metadata) => {
1150
+ var _a;
1151
+ try {
1152
+ const context = captureContext(visitedPagesRef.current);
1153
+ const payload = {
1154
+ token,
1155
+ source: "SDK_USER_REPORT",
1156
+ type,
1157
+ description,
1158
+ pageUrl: context.url,
1159
+ userAgent: context.userAgent,
1160
+ breadcrumbs: context.breadcrumbs,
1161
+ deviceInfo: (_a = context.deviceInfo) != null ? _a : void 0,
1162
+ metadata: {
1163
+ timestamp: context.timestamp,
1164
+ visitedPages: JSON.stringify(context.visitedPages),
1165
+ ...(session == null ? void 0 : session.userId) ? { sessionUserId: session.userId } : {},
1166
+ ...(session == null ? void 0 : session.name) ? { sessionUserName: String(session.name) } : {},
1167
+ ...(session == null ? void 0 : session.email) ? { sessionUserEmail: String(session.email) } : {},
1168
+ ...(session == null ? void 0 : session.phone) ? { sessionUserPhone: String(session.phone) } : {},
1169
+ ...metadata
1170
+ }
1171
+ };
1172
+ const result = await sendReport(payload, baseUrl);
1173
+ if (result && onReportSent) onReportSent(result);
1174
+ return result;
1175
+ } catch (e) {
1176
+ return null;
1177
+ }
1178
+ },
1179
+ [token, baseUrl, onReportSent, session]
1180
+ );
1181
+ const reportBug = useCallback(
1182
+ (description, metadata) => report("BUG", description, metadata),
1183
+ [report]
1184
+ );
1185
+ const addBreadcrumb2 = useCallback(
1186
+ (message, data) => {
1187
+ addBreadcrumb("custom", message, data);
1188
+ },
1189
+ []
1190
+ );
1191
+ const openReportDialog = useCallback((options) => {
1192
+ if (typeof window !== "undefined") {
1193
+ window.dispatchEvent(new CustomEvent("glitchgrab:open-report", { detail: options }));
1194
+ }
1195
+ }, []);
1196
+ return /* @__PURE__ */ jsxs2(
1197
+ GlitchgrabContext.Provider,
1198
+ {
1199
+ value: {
1200
+ token,
1201
+ baseUrl: baseUrl != null ? baseUrl : DEFAULT_BASE_URL2,
1202
+ reportBug,
1203
+ report,
1204
+ addBreadcrumb: addBreadcrumb2,
1205
+ openReportDialog
1206
+ },
1207
+ children: [
1208
+ /* @__PURE__ */ jsx2(
1209
+ GlitchgrabErrorBoundary,
1210
+ {
1211
+ token,
1212
+ baseUrl,
1213
+ onError,
1214
+ fallback,
1215
+ visitedPages: visitedPagesRef.current,
1216
+ children
1217
+ }
1218
+ ),
1219
+ /* @__PURE__ */ jsx2(ReportDialog, { report, types, showSeverity })
1220
+ ]
1221
+ }
1222
+ );
1223
+ }
1224
+ function GlitchgrabProvider(props) {
1225
+ if (!props.token) return /* @__PURE__ */ jsx2(Fragment2, { children: props.children });
1226
+ const resolvedProps = {
1227
+ ...props,
1228
+ baseUrl: props.baseUrl || DEFAULT_BASE_URL2
1229
+ };
1230
+ try {
1231
+ return /* @__PURE__ */ jsx2(GlitchgrabProviderInner, { ...resolvedProps });
1232
+ } catch (e) {
1233
+ return /* @__PURE__ */ jsx2(Fragment2, { children: props.children });
1460
1234
  }
1461
1235
  }
1462
- function DefaultTrigger({
1463
- onClick,
1464
- capturing,
1465
- label,
1236
+
1237
+ // src/report-button.tsx
1238
+ import { useState as useState3 } from "react";
1239
+ import { Fragment as Fragment3, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
1240
+ function useGlitchgrabSafe() {
1241
+ try {
1242
+ return useGlitchgrab();
1243
+ } catch (e) {
1244
+ return null;
1245
+ }
1246
+ }
1247
+ function ReportButton({
1248
+ position = "bottom-right",
1249
+ label = "Report Bug",
1466
1250
  className,
1467
- position,
1468
- isLeft,
1469
- isTop
1251
+ children
1470
1252
  }) {
1253
+ var _a, _b;
1254
+ const [capturing, setCapturing] = useState3(false);
1255
+ const glitchgrab = useGlitchgrabSafe();
1256
+ if (!glitchgrab) return null;
1257
+ const handleClick = async () => {
1258
+ setCapturing(true);
1259
+ glitchgrab.openReportDialog();
1260
+ setCapturing(false);
1261
+ };
1262
+ if (children) {
1263
+ return /* @__PURE__ */ jsx3(Fragment3, { children: children({ onClick: handleClick, capturing }) });
1264
+ }
1265
+ const isTop = (_a = position == null ? void 0 : position.startsWith("top")) != null ? _a : false;
1266
+ const isLeft = (_b = position == null ? void 0 : position.endsWith("left")) != null ? _b : false;
1471
1267
  const positionStyles = {
1472
1268
  ...isLeft ? { left: "16px" } : { right: "16px" },
1473
1269
  ...isTop ? { top: "16px" } : { bottom: "16px" }
1474
1270
  };
1475
- return /* @__PURE__ */ jsxs(
1271
+ return /* @__PURE__ */ jsxs3(
1476
1272
  "button",
1477
1273
  {
1478
1274
  type: "button",
1479
- onClick,
1275
+ onClick: handleClick,
1480
1276
  disabled: capturing,
1481
1277
  className,
1482
1278
  style: {
@@ -1507,7 +1303,7 @@ function DefaultTrigger({
1507
1303
  },
1508
1304
  "aria-label": label,
1509
1305
  children: [
1510
- /* @__PURE__ */ jsx2(
1306
+ /* @__PURE__ */ jsx3(
1511
1307
  "svg",
1512
1308
  {
1513
1309
  width: "16",
@@ -1515,7 +1311,7 @@ function DefaultTrigger({
1515
1311
  viewBox: "0 0 16 16",
1516
1312
  fill: "none",
1517
1313
  style: { flexShrink: 0 },
1518
- children: /* @__PURE__ */ jsx2(
1314
+ children: /* @__PURE__ */ jsx3(
1519
1315
  "path",
1520
1316
  {
1521
1317
  d: "M8 1C4.134 1 1 4.134 1 8s3.134 7 7 7 7-3.134 7-7-3.134-7-7-7zm0 12.5a.75.75 0 110-1.5.75.75 0 010 1.5zm.75-3.5a.75.75 0 01-1.5 0V5a.75.75 0 011.5 0v5z",
@@ -1531,17 +1327,17 @@ function DefaultTrigger({
1531
1327
  }
1532
1328
 
1533
1329
  // src/use-reports.ts
1534
- import { useState as useState3, useEffect as useEffect3, useCallback as useCallback2 } from "react";
1330
+ import { useState as useState4, useEffect as useEffect3, useCallback as useCallback2 } from "react";
1535
1331
  var BASE_URL = "https://www.glitchgrab.dev";
1536
1332
  function useGlitchgrabReports({
1537
1333
  token,
1538
1334
  userId,
1539
1335
  limit = 100
1540
1336
  }) {
1541
- const [reports, setReports] = useState3([]);
1542
- const [isLoading, setIsLoading] = useState3(true);
1543
- const [isFetching, setIsFetching] = useState3(false);
1544
- const [error, setError] = useState3(null);
1337
+ const [reports, setReports] = useState4([]);
1338
+ const [isLoading, setIsLoading] = useState4(true);
1339
+ const [isFetching, setIsFetching] = useState4(false);
1340
+ const [error, setError] = useState4(null);
1545
1341
  const fetchReports = useCallback2(async () => {
1546
1342
  var _a, _b;
1547
1343
  if (!token || !userId) {
@@ -1605,8 +1401,8 @@ function useGlitchgrabActions({
1605
1401
  onSuccess,
1606
1402
  onError
1607
1403
  }) {
1608
- const [isPending, setIsPending] = useState3(false);
1609
- const [error, setError] = useState3(null);
1404
+ const [isPending, setIsPending] = useState4(false);
1405
+ const [error, setError] = useState4(null);
1610
1406
  const run = useCallback2(
1611
1407
  async (reportId, body) => {
1612
1408
  setIsPending(true);