glitchgrab 1.26.0 → 1.28.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,9 +4,10 @@
4
4
  import {
5
5
  createContext,
6
6
  useContext,
7
- useRef as useRef2,
8
- useEffect as useEffect2,
9
- useCallback
7
+ useRef as useRef3,
8
+ useEffect as useEffect3,
9
+ useCallback,
10
+ useState as useState3
10
11
  } from "react";
11
12
 
12
13
  // src/error-boundary.tsx
@@ -453,12 +454,311 @@ var GlitchgrabErrorBoundary = class extends React.Component {
453
454
 
454
455
  // src/report-dialog.tsx
455
456
  import {
456
- useState,
457
- useRef,
458
- useEffect
457
+ useState as useState2,
458
+ useRef as useRef2,
459
+ useEffect as useEffect2
459
460
  } from "react";
460
461
  import { createPortal } from "react-dom";
461
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
462
+
463
+ // src/annotation-canvas.tsx
464
+ import { useEffect, useRef, useState } from "react";
465
+ import { jsx, jsxs } from "react/jsx-runtime";
466
+ var PALETTE = ["#ef4444", "#f59e0b", "#22c55e", "#3b82f6", "#a855f7", "#ffffff"];
467
+ function drawStroke(ctx, s) {
468
+ ctx.strokeStyle = s.color;
469
+ ctx.lineWidth = s.width;
470
+ ctx.lineCap = "round";
471
+ ctx.lineJoin = "round";
472
+ if (s.type === "freehand") {
473
+ if (s.points.length < 2) return;
474
+ ctx.beginPath();
475
+ ctx.moveTo(s.points[0].x, s.points[0].y);
476
+ for (let i = 1; i < s.points.length; i++) ctx.lineTo(s.points[i].x, s.points[i].y);
477
+ ctx.stroke();
478
+ } else {
479
+ ctx.beginPath();
480
+ ctx.arc(s.cx, s.cy, s.r, 0, Math.PI * 2);
481
+ ctx.stroke();
482
+ }
483
+ }
484
+ function AnnotationCanvas({ imageSrc, onSave, onCancel }) {
485
+ const canvasRef = useRef(null);
486
+ const imgRef = useRef(null);
487
+ const [ready, setReady] = useState(false);
488
+ const [tool, setTool] = useState("pen");
489
+ const [color, setColor] = useState(PALETTE[0]);
490
+ const [strokeWidth, setStrokeWidth] = useState(4);
491
+ const [strokes, setStrokes] = useState([]);
492
+ const [currentStroke, setCurrentStroke] = useState(null);
493
+ useEffect(() => {
494
+ const img = new Image();
495
+ img.onload = () => {
496
+ imgRef.current = img;
497
+ const canvas = canvasRef.current;
498
+ if (canvas) {
499
+ canvas.width = img.naturalWidth;
500
+ canvas.height = img.naturalHeight;
501
+ }
502
+ setReady(true);
503
+ };
504
+ img.src = imageSrc;
505
+ }, [imageSrc]);
506
+ useEffect(() => {
507
+ if (!ready) return;
508
+ const canvas = canvasRef.current;
509
+ const img = imgRef.current;
510
+ if (!canvas || !img) return;
511
+ const ctx = canvas.getContext("2d");
512
+ if (!ctx) return;
513
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
514
+ ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
515
+ for (const s of strokes) drawStroke(ctx, s);
516
+ if (currentStroke) drawStroke(ctx, currentStroke);
517
+ }, [ready, strokes, currentStroke]);
518
+ const getPos = (e) => {
519
+ const canvas = e.currentTarget;
520
+ const rect = canvas.getBoundingClientRect();
521
+ return {
522
+ x: (e.clientX - rect.left) * canvas.width / rect.width,
523
+ y: (e.clientY - rect.top) * canvas.height / rect.height
524
+ };
525
+ };
526
+ const handlePointerDown = (e) => {
527
+ e.currentTarget.setPointerCapture(e.pointerId);
528
+ const { x, y } = getPos(e);
529
+ setCurrentStroke(
530
+ tool === "pen" ? { type: "freehand", color, width: strokeWidth, points: [{ x, y }] } : { type: "circle", color, width: strokeWidth, cx: x, cy: y, r: 0 }
531
+ );
532
+ };
533
+ const handlePointerMove = (e) => {
534
+ if (!currentStroke) return;
535
+ const { x, y } = getPos(e);
536
+ setCurrentStroke((prev) => {
537
+ if (!prev) return prev;
538
+ if (prev.type === "freehand") return { ...prev, points: [...prev.points, { x, y }] };
539
+ return { ...prev, r: Math.hypot(x - prev.cx, y - prev.cy) };
540
+ });
541
+ };
542
+ const handlePointerUp = () => {
543
+ setCurrentStroke((prev) => {
544
+ if (prev) setStrokes((s) => [...s, prev]);
545
+ return null;
546
+ });
547
+ };
548
+ const handleSave = () => {
549
+ const canvas = canvasRef.current;
550
+ if (!canvas) return;
551
+ onSave(canvas.toDataURL("image/jpeg", 0.85));
552
+ };
553
+ const iconBtn = (active = false) => ({
554
+ width: "30px",
555
+ height: "30px",
556
+ display: "flex",
557
+ alignItems: "center",
558
+ justifyContent: "center",
559
+ borderRadius: "6px",
560
+ border: `1.5px solid ${active ? "#22d3ee" : "rgba(255,255,255,0.15)"}`,
561
+ background: active ? "rgba(34,211,238,0.15)" : "transparent",
562
+ color: "#fff",
563
+ cursor: "pointer",
564
+ padding: 0
565
+ });
566
+ return /* @__PURE__ */ jsxs(
567
+ "div",
568
+ {
569
+ style: {
570
+ position: "fixed",
571
+ inset: 0,
572
+ zIndex: 2147483647,
573
+ backgroundColor: "rgba(0,0,0,0.9)",
574
+ display: "flex",
575
+ flexDirection: "column",
576
+ alignItems: "center",
577
+ justifyContent: "center",
578
+ padding: "16px"
579
+ },
580
+ onClick: (e) => {
581
+ e.stopPropagation();
582
+ if (e.target === e.currentTarget) onCancel();
583
+ },
584
+ onPointerDown: (e) => e.stopPropagation(),
585
+ children: [
586
+ /* @__PURE__ */ jsx(
587
+ "canvas",
588
+ {
589
+ ref: canvasRef,
590
+ onPointerDown: handlePointerDown,
591
+ onPointerMove: handlePointerMove,
592
+ onPointerUp: handlePointerUp,
593
+ style: {
594
+ maxWidth: "100%",
595
+ maxHeight: "calc(100vh - 140px)",
596
+ borderRadius: "8px",
597
+ touchAction: "none",
598
+ cursor: "crosshair",
599
+ display: "block"
600
+ }
601
+ }
602
+ ),
603
+ /* @__PURE__ */ jsxs(
604
+ "div",
605
+ {
606
+ style: {
607
+ marginTop: "14px",
608
+ display: "flex",
609
+ alignItems: "center",
610
+ gap: "8px",
611
+ flexWrap: "wrap",
612
+ justifyContent: "center",
613
+ maxWidth: "100%"
614
+ },
615
+ children: [
616
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: () => setTool("pen"), style: iconBtn(tool === "pen"), title: "Freehand", "aria-label": "Freehand tool", children: /* @__PURE__ */ jsx("svg", { width: "15", height: "15", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx("path", { d: "M12 20h9M16.5 3.5a2.12 2.12 0 013 3L7 19l-4 1 1-4L16.5 3.5z", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round" }) }) }),
617
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: () => setTool("circle"), style: iconBtn(tool === "circle"), title: "Circle", "aria-label": "Circle tool", children: /* @__PURE__ */ jsx("svg", { width: "15", height: "15", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "8", stroke: "currentColor", strokeWidth: "1.8" }) }) }),
618
+ /* @__PURE__ */ jsx("div", { style: { width: "1px", height: "20px", background: "rgba(255,255,255,0.15)" } }),
619
+ PALETTE.map((c) => /* @__PURE__ */ jsx(
620
+ "button",
621
+ {
622
+ type: "button",
623
+ onClick: () => setColor(c),
624
+ "aria-label": `Color ${c}`,
625
+ style: {
626
+ width: "22px",
627
+ height: "22px",
628
+ borderRadius: "50%",
629
+ background: c,
630
+ border: color === c ? "2px solid #22d3ee" : "2px solid rgba(255,255,255,0.3)",
631
+ cursor: "pointer",
632
+ padding: 0
633
+ }
634
+ },
635
+ c
636
+ )),
637
+ /* @__PURE__ */ jsx(
638
+ "input",
639
+ {
640
+ type: "color",
641
+ value: color,
642
+ onChange: (e) => setColor(e.target.value),
643
+ title: "Custom color",
644
+ "aria-label": "Custom color",
645
+ style: {
646
+ width: "22px",
647
+ height: "22px",
648
+ padding: 0,
649
+ border: "none",
650
+ background: "none",
651
+ cursor: "pointer"
652
+ }
653
+ }
654
+ ),
655
+ /* @__PURE__ */ jsx("div", { style: { width: "1px", height: "20px", background: "rgba(255,255,255,0.15)" } }),
656
+ /* @__PURE__ */ jsx(
657
+ "input",
658
+ {
659
+ type: "range",
660
+ min: 2,
661
+ max: 12,
662
+ value: strokeWidth,
663
+ onChange: (e) => setStrokeWidth(Number(e.target.value)),
664
+ title: "Stroke width",
665
+ "aria-label": "Stroke width",
666
+ style: { width: "70px" }
667
+ }
668
+ ),
669
+ /* @__PURE__ */ jsx("div", { style: { width: "1px", height: "20px", background: "rgba(255,255,255,0.15)" } }),
670
+ /* @__PURE__ */ jsx(
671
+ "button",
672
+ {
673
+ type: "button",
674
+ onClick: () => setStrokes((s) => s.slice(0, -1)),
675
+ disabled: strokes.length === 0,
676
+ style: { ...iconBtn(), opacity: strokes.length === 0 ? 0.35 : 1 },
677
+ title: "Undo",
678
+ "aria-label": "Undo",
679
+ children: /* @__PURE__ */ jsx("svg", { width: "15", height: "15", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx("path", { d: "M9 14L4 9l5-5M4 9h10a6 6 0 010 12h-2", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round" }) })
680
+ }
681
+ ),
682
+ /* @__PURE__ */ jsx(
683
+ "button",
684
+ {
685
+ type: "button",
686
+ onClick: () => setStrokes([]),
687
+ disabled: strokes.length === 0,
688
+ style: { ...iconBtn(), opacity: strokes.length === 0 ? 0.35 : 1 },
689
+ title: "Clear all",
690
+ "aria-label": "Clear all",
691
+ children: /* @__PURE__ */ jsx("svg", { width: "15", height: "15", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx("path", { d: "M3 6h18M8 6V4a1 1 0 011-1h6a1 1 0 011 1v2m2 0v14a1 1 0 01-1 1H7a1 1 0 01-1-1V6h12z", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round" }) })
692
+ }
693
+ )
694
+ ]
695
+ }
696
+ ),
697
+ /* @__PURE__ */ jsxs("div", { style: { marginTop: "12px", display: "flex", gap: "8px" }, children: [
698
+ /* @__PURE__ */ jsx(
699
+ "button",
700
+ {
701
+ type: "button",
702
+ onClick: onCancel,
703
+ style: {
704
+ padding: "8px 16px",
705
+ borderRadius: "8px",
706
+ border: "1.5px solid rgba(255,255,255,0.2)",
707
+ background: "transparent",
708
+ color: "#fff",
709
+ fontSize: "13px",
710
+ fontWeight: 600,
711
+ cursor: "pointer",
712
+ fontFamily: "inherit"
713
+ },
714
+ children: "Cancel"
715
+ }
716
+ ),
717
+ /* @__PURE__ */ jsx(
718
+ "button",
719
+ {
720
+ type: "button",
721
+ onClick: handleSave,
722
+ style: {
723
+ padding: "8px 16px",
724
+ borderRadius: "8px",
725
+ border: "none",
726
+ background: "#22d3ee",
727
+ color: "#09090b",
728
+ fontSize: "13px",
729
+ fontWeight: 600,
730
+ cursor: "pointer",
731
+ fontFamily: "inherit"
732
+ },
733
+ children: "Save annotation"
734
+ }
735
+ )
736
+ ] })
737
+ ]
738
+ }
739
+ );
740
+ }
741
+
742
+ // src/shortcut.ts
743
+ var GLITCHGRAB_SHORTCUT = "Ctrl+Shift+G";
744
+ var GLITCHGRAB_SHORTCUT_MAC = "\u2318\u21E7G";
745
+ function isMac() {
746
+ try {
747
+ if (typeof navigator === "undefined") return false;
748
+ return /Mac|iPhone|iPad|iPod/i.test(navigator.platform || navigator.userAgent);
749
+ } catch (e) {
750
+ return false;
751
+ }
752
+ }
753
+ function getShortcutLabel() {
754
+ return isMac() ? GLITCHGRAB_SHORTCUT_MAC : GLITCHGRAB_SHORTCUT;
755
+ }
756
+ function matchesShortcut(e) {
757
+ return (e.metaKey || e.ctrlKey) && e.shiftKey && e.key.toLowerCase() === "g";
758
+ }
759
+
760
+ // src/report-dialog.tsx
761
+ import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
462
762
  function useIsDark() {
463
763
  if (typeof window === "undefined") return true;
464
764
  try {
@@ -647,39 +947,40 @@ function ReportDialog({
647
947
  types,
648
948
  showSeverity = true
649
949
  }) {
650
- const [isEnhancing, setIsEnhancing] = useState(false);
651
- const [isEnhanced, setIsEnhanced] = useState(false);
652
- const [originalDescription, setOriginalDescription] = useState(
950
+ const [isEnhancing, setIsEnhancing] = useState2(false);
951
+ const [isEnhanced, setIsEnhanced] = useState2(false);
952
+ const [originalDescription, setOriginalDescription] = useState2(
653
953
  null
654
954
  );
655
- const [isListening, setIsListening] = useState(false);
656
- const [isTranscribing, setIsTranscribing] = useState(false);
657
- const [voiceError, setVoiceError] = useState(null);
658
- const [screenshotDragOver, setScreenshotDragOver] = useState(false);
659
- const [mounted, setMounted] = useState(false);
660
- useEffect(() => {
955
+ const [isListening, setIsListening] = useState2(false);
956
+ const [isTranscribing, setIsTranscribing] = useState2(false);
957
+ const [voiceError, setVoiceError] = useState2(null);
958
+ const [screenshotDragOver, setScreenshotDragOver] = useState2(false);
959
+ const [mounted, setMounted] = useState2(false);
960
+ useEffect2(() => {
661
961
  setMounted(true);
662
962
  }, []);
663
- const [isOpen, setIsOpen] = useState(false);
664
- const [description, setDescription] = useState("");
665
- const [isSubmitting, setIsSubmitting] = useState(false);
666
- const [submitted, setSubmitted] = useState(false);
667
- const [screenshots, setScreenshots] = useState([]);
668
- const [previewIndex, setPreviewIndex] = useState(null);
669
- const fileInputRef = useRef(null);
670
- const modalRef = useRef(null);
671
- const previewImgRef = useRef(null);
672
- const textareaRef = useRef(null);
673
- const recognitionRef = useRef(null);
674
- const voiceBaseRef = useRef("");
675
- const usingWebSpeechRef = useRef(false);
676
- const textBeforeVoiceRef = useRef("");
677
- const mediaRecorderRef = useRef(null);
678
- const streamRef = useRef(null);
679
- const sarvamChunksRef = useRef([]);
680
- const spaceTimerRef = useRef(null);
681
- const isPushToTalkRef = useRef(false);
682
- useEffect(() => {
963
+ const [isOpen, setIsOpen] = useState2(false);
964
+ const [description, setDescription] = useState2("");
965
+ const [isSubmitting, setIsSubmitting] = useState2(false);
966
+ const [submitted, setSubmitted] = useState2(false);
967
+ const [screenshots, setScreenshots] = useState2([]);
968
+ const [previewIndex, setPreviewIndex] = useState2(null);
969
+ const [annotatingIndex, setAnnotatingIndex] = useState2(null);
970
+ const fileInputRef = useRef2(null);
971
+ const modalRef = useRef2(null);
972
+ const previewImgRef = useRef2(null);
973
+ const textareaRef = useRef2(null);
974
+ const recognitionRef = useRef2(null);
975
+ const voiceBaseRef = useRef2("");
976
+ const usingWebSpeechRef = useRef2(false);
977
+ const textBeforeVoiceRef = useRef2("");
978
+ const mediaRecorderRef = useRef2(null);
979
+ const streamRef = useRef2(null);
980
+ const sarvamChunksRef = useRef2([]);
981
+ const spaceTimerRef = useRef2(null);
982
+ const isPushToTalkRef = useRef2(false);
983
+ useEffect2(() => {
683
984
  if (!isOpen) return;
684
985
  const dialogs = Array.from(
685
986
  document.querySelectorAll(
@@ -689,14 +990,17 @@ function ReportDialog({
689
990
  dialogs.forEach((d) => d.setAttribute("inert", ""));
690
991
  return () => dialogs.forEach((d) => d.removeAttribute("inert"));
691
992
  }, [isOpen]);
692
- const [step, setStep] = useState(1);
693
- const [reportType, setReportType] = useState("BUG");
694
- const [severity, setSeverity] = useState("medium");
695
- const [validationError, setValidationError] = useState(null);
696
- const [placeholderIdx, setPlaceholderIdx] = useState(0);
993
+ const [step, setStep] = useState2(1);
994
+ const [reportType, setReportType] = useState2("BUG");
995
+ const [severity, setSeverity] = useState2("medium");
996
+ const [validationError, setValidationError] = useState2(null);
997
+ const [placeholderIdx, setPlaceholderIdx] = useState2(0);
697
998
  const availableTypes = types != null ? types : [
698
999
  "BUG",
699
1000
  "FEATURE_REQUEST",
1001
+ "UI_IMPROVEMENT",
1002
+ "PERFORMANCE",
1003
+ "SECURITY",
700
1004
  "QUESTION",
701
1005
  "OTHER"
702
1006
  ];
@@ -725,7 +1029,13 @@ function ReportDialog({
725
1029
  scale: 0.5,
726
1030
  logging: false,
727
1031
  useCORS: true,
728
- allowTaint: true
1032
+ allowTaint: true,
1033
+ x: window.scrollX,
1034
+ y: window.scrollY,
1035
+ width: window.innerWidth,
1036
+ height: window.innerHeight,
1037
+ windowWidth: window.innerWidth,
1038
+ windowHeight: window.innerHeight
729
1039
  });
730
1040
  setScreenshots([canvas.toDataURL("image/jpeg", 0.6)]);
731
1041
  } catch (e) {
@@ -744,14 +1054,20 @@ function ReportDialog({
744
1054
  scale: 0.5,
745
1055
  logging: false,
746
1056
  useCORS: true,
747
- allowTaint: true
1057
+ allowTaint: true,
1058
+ x: window.scrollX,
1059
+ y: window.scrollY,
1060
+ width: window.innerWidth,
1061
+ height: window.innerHeight,
1062
+ windowWidth: window.innerWidth,
1063
+ windowHeight: window.innerHeight
748
1064
  });
749
1065
  setScreenshots([canvas.toDataURL("image/jpeg", 0.6)]);
750
1066
  } catch (e) {
751
1067
  }
752
1068
  setIsOpen(true);
753
1069
  };
754
- useEffect(() => {
1070
+ useEffect2(() => {
755
1071
  const handler = (e) => {
756
1072
  if (isOpen) return;
757
1073
  const detail = e.detail;
@@ -765,7 +1081,7 @@ function ReportDialog({
765
1081
  window.addEventListener("glitchgrab:open-report", handler);
766
1082
  return () => window.removeEventListener("glitchgrab:open-report", handler);
767
1083
  }, [isOpen, handleOpen]);
768
- useEffect(() => {
1084
+ useEffect2(() => {
769
1085
  if (!isOpen) return;
770
1086
  const handleEsc = (e) => {
771
1087
  if (e.key === "Escape") {
@@ -776,8 +1092,8 @@ function ReportDialog({
776
1092
  document.addEventListener("keydown", handleEsc);
777
1093
  return () => document.removeEventListener("keydown", handleEsc);
778
1094
  }, [isOpen, previewIndex]);
779
- useEffect(() => {
780
- if (previewIndex === null) return;
1095
+ useEffect2(() => {
1096
+ if (previewIndex === null || annotatingIndex !== null) return;
781
1097
  const handlePointerDown = (e) => {
782
1098
  var _a;
783
1099
  if (!((_a = previewImgRef.current) == null ? void 0 : _a.contains(e.target))) {
@@ -786,8 +1102,8 @@ function ReportDialog({
786
1102
  };
787
1103
  document.addEventListener("pointerdown", handlePointerDown, true);
788
1104
  return () => document.removeEventListener("pointerdown", handlePointerDown, true);
789
- }, [previewIndex]);
790
- useEffect(() => {
1105
+ }, [previewIndex, annotatingIndex]);
1106
+ useEffect2(() => {
791
1107
  if (!isOpen) return;
792
1108
  const handlePaste = (e) => {
793
1109
  var _a;
@@ -812,7 +1128,7 @@ function ReportDialog({
812
1128
  window.addEventListener("paste", handlePaste);
813
1129
  return () => window.removeEventListener("paste", handlePaste);
814
1130
  }, [isOpen]);
815
- useEffect(() => {
1131
+ useEffect2(() => {
816
1132
  if (!isOpen || !transcribeAudio2) return;
817
1133
  const id = setInterval(() => {
818
1134
  setPlaceholderIdx((i) => (i + 1) % 2);
@@ -1029,9 +1345,9 @@ function ReportDialog({
1029
1345
  }
1030
1346
  };
1031
1347
  if (!mounted) return null;
1032
- return /* @__PURE__ */ jsxs(Fragment, { children: [
1348
+ return /* @__PURE__ */ jsxs2(Fragment, { children: [
1033
1349
  isOpen && createPortal(
1034
- /* @__PURE__ */ jsx("style", { children: `
1350
+ /* @__PURE__ */ jsx2("style", { children: `
1035
1351
  @keyframes gg-pulse{0%,100%{opacity:1}50%{opacity:0.35}}
1036
1352
  @keyframes gg-b1{0%,100%{height:3px}40%{height:13px}}
1037
1353
  @keyframes gg-b2{0%,100%{height:8px}50%{height:3px}}
@@ -1041,7 +1357,7 @@ function ReportDialog({
1041
1357
  document.body
1042
1358
  ),
1043
1359
  isOpen && createPortal(
1044
- /* @__PURE__ */ jsx(
1360
+ /* @__PURE__ */ jsx2(
1045
1361
  "div",
1046
1362
  {
1047
1363
  style: {
@@ -1060,7 +1376,7 @@ function ReportDialog({
1060
1376
  },
1061
1377
  onPointerDown: (e) => e.stopPropagation(),
1062
1378
  onFocus: (e) => e.stopPropagation(),
1063
- children: /* @__PURE__ */ jsxs(
1379
+ children: /* @__PURE__ */ jsxs2(
1064
1380
  "div",
1065
1381
  {
1066
1382
  ref: modalRef,
@@ -1079,7 +1395,7 @@ function ReportDialog({
1079
1395
  isolation: "isolate"
1080
1396
  },
1081
1397
  children: [
1082
- /* @__PURE__ */ jsxs(
1398
+ /* @__PURE__ */ jsxs2(
1083
1399
  "div",
1084
1400
  {
1085
1401
  style: {
@@ -1087,7 +1403,7 @@ function ReportDialog({
1087
1403
  borderBottom: `1px solid ${t.border}`
1088
1404
  },
1089
1405
  children: [
1090
- /* @__PURE__ */ jsxs(
1406
+ /* @__PURE__ */ jsxs2(
1091
1407
  "div",
1092
1408
  {
1093
1409
  style: {
@@ -1096,7 +1412,7 @@ function ReportDialog({
1096
1412
  alignItems: "center"
1097
1413
  },
1098
1414
  children: [
1099
- /* @__PURE__ */ jsxs(
1415
+ /* @__PURE__ */ jsxs2(
1100
1416
  "div",
1101
1417
  {
1102
1418
  style: {
@@ -1105,7 +1421,7 @@ function ReportDialog({
1105
1421
  gap: "8px"
1106
1422
  },
1107
1423
  children: [
1108
- step > 1 && /* @__PURE__ */ jsx(
1424
+ step > 1 && /* @__PURE__ */ jsx2(
1109
1425
  "button",
1110
1426
  {
1111
1427
  type: "button",
@@ -1119,14 +1435,14 @@ function ReportDialog({
1119
1435
  alignItems: "center"
1120
1436
  },
1121
1437
  "aria-label": "Back",
1122
- children: /* @__PURE__ */ jsx(
1438
+ children: /* @__PURE__ */ jsx2(
1123
1439
  "svg",
1124
1440
  {
1125
1441
  width: "16",
1126
1442
  height: "16",
1127
1443
  viewBox: "0 0 16 16",
1128
1444
  fill: "none",
1129
- children: /* @__PURE__ */ jsx(
1445
+ children: /* @__PURE__ */ jsx2(
1130
1446
  "path",
1131
1447
  {
1132
1448
  d: "M10 3L5 8L10 13",
@@ -1140,7 +1456,7 @@ function ReportDialog({
1140
1456
  )
1141
1457
  }
1142
1458
  ),
1143
- /* @__PURE__ */ jsx(
1459
+ /* @__PURE__ */ jsx2(
1144
1460
  "span",
1145
1461
  {
1146
1462
  style: {
@@ -1150,11 +1466,29 @@ function ReportDialog({
1150
1466
  },
1151
1467
  children: step === 1 ? "What's on your mind?" : "Tell us more"
1152
1468
  }
1469
+ ),
1470
+ step === 1 && /* @__PURE__ */ jsx2(
1471
+ "span",
1472
+ {
1473
+ title: "Press this anywhere to open Glitchgrab",
1474
+ style: {
1475
+ fontSize: "10px",
1476
+ fontWeight: 600,
1477
+ lineHeight: 1,
1478
+ padding: "3px 6px",
1479
+ borderRadius: "4px",
1480
+ border: `1px solid ${t.inputBorder}`,
1481
+ background: t.inputBg,
1482
+ color: t.textMuted,
1483
+ whiteSpace: "nowrap"
1484
+ },
1485
+ children: getShortcutLabel()
1486
+ }
1153
1487
  )
1154
1488
  ]
1155
1489
  }
1156
1490
  ),
1157
- /* @__PURE__ */ jsx(
1491
+ /* @__PURE__ */ jsx2(
1158
1492
  "button",
1159
1493
  {
1160
1494
  type: "button",
@@ -1169,7 +1503,7 @@ function ReportDialog({
1169
1503
  justifyContent: "center"
1170
1504
  },
1171
1505
  "aria-label": "Close",
1172
- children: /* @__PURE__ */ jsx(
1506
+ children: /* @__PURE__ */ jsx2(
1173
1507
  "svg",
1174
1508
  {
1175
1509
  width: "16",
@@ -1177,7 +1511,7 @@ function ReportDialog({
1177
1511
  viewBox: "0 0 16 16",
1178
1512
  fill: "none",
1179
1513
  style: { display: "block" },
1180
- children: /* @__PURE__ */ jsx(
1514
+ children: /* @__PURE__ */ jsx2(
1181
1515
  "path",
1182
1516
  {
1183
1517
  d: "M4 4L12 12M12 4L4 12",
@@ -1193,7 +1527,7 @@ function ReportDialog({
1193
1527
  ]
1194
1528
  }
1195
1529
  ),
1196
- /* @__PURE__ */ jsx(
1530
+ /* @__PURE__ */ jsx2(
1197
1531
  "div",
1198
1532
  {
1199
1533
  style: {
@@ -1203,12 +1537,12 @@ function ReportDialog({
1203
1537
  gap: "0",
1204
1538
  marginTop: "12px"
1205
1539
  },
1206
- children: [1, 2].map((s, i) => /* @__PURE__ */ jsxs(
1540
+ children: [1, 2].map((s, i) => /* @__PURE__ */ jsxs2(
1207
1541
  "div",
1208
1542
  {
1209
1543
  style: { display: "flex", alignItems: "center" },
1210
1544
  children: [
1211
- /* @__PURE__ */ jsx(
1545
+ /* @__PURE__ */ jsx2(
1212
1546
  "div",
1213
1547
  {
1214
1548
  style: {
@@ -1220,7 +1554,7 @@ function ReportDialog({
1220
1554
  }
1221
1555
  }
1222
1556
  ),
1223
- i < 1 && /* @__PURE__ */ jsx(
1557
+ i < 1 && /* @__PURE__ */ jsx2(
1224
1558
  "div",
1225
1559
  {
1226
1560
  style: {
@@ -1240,7 +1574,7 @@ function ReportDialog({
1240
1574
  ]
1241
1575
  }
1242
1576
  ),
1243
- /* @__PURE__ */ jsx("div", { style: { padding: "16px" }, children: submitted ? /* @__PURE__ */ jsxs(
1577
+ /* @__PURE__ */ jsx2("div", { style: { padding: "16px" }, children: submitted ? /* @__PURE__ */ jsxs2(
1244
1578
  "div",
1245
1579
  {
1246
1580
  style: {
@@ -1255,8 +1589,8 @@ function ReportDialog({
1255
1589
  " sent. Thank you!"
1256
1590
  ]
1257
1591
  }
1258
- ) : /* @__PURE__ */ jsxs(Fragment, { children: [
1259
- step === 1 && /* @__PURE__ */ jsx(
1592
+ ) : /* @__PURE__ */ jsxs2(Fragment, { children: [
1593
+ step === 1 && /* @__PURE__ */ jsx2(
1260
1594
  "div",
1261
1595
  {
1262
1596
  style: {
@@ -1264,7 +1598,7 @@ function ReportDialog({
1264
1598
  gridTemplateColumns: "1fr 1fr",
1265
1599
  gap: "8px"
1266
1600
  },
1267
- children: availableTypes.map((type) => /* @__PURE__ */ jsxs(
1601
+ children: availableTypes.map((type) => /* @__PURE__ */ jsxs2(
1268
1602
  "button",
1269
1603
  {
1270
1604
  type: "button",
@@ -1294,8 +1628,8 @@ function ReportDialog({
1294
1628
  },
1295
1629
  children: [
1296
1630
  getTypeIcon(type, t.accent),
1297
- /* @__PURE__ */ jsx("span", { style: { fontSize: "13px", fontWeight: 600 }, children: getTypeLabel(type) }),
1298
- /* @__PURE__ */ jsx(
1631
+ /* @__PURE__ */ jsx2("span", { style: { fontSize: "13px", fontWeight: 600 }, children: getTypeLabel(type) }),
1632
+ /* @__PURE__ */ jsx2(
1299
1633
  "span",
1300
1634
  {
1301
1635
  style: {
@@ -1312,9 +1646,9 @@ function ReportDialog({
1312
1646
  ))
1313
1647
  }
1314
1648
  ),
1315
- step === 2 && /* @__PURE__ */ jsxs(Fragment, { children: [
1316
- /* @__PURE__ */ jsxs("div", { style: { position: "relative" }, children: [
1317
- /* @__PURE__ */ jsx(
1649
+ step === 2 && /* @__PURE__ */ jsxs2(Fragment, { children: [
1650
+ /* @__PURE__ */ jsxs2("div", { style: { position: "relative" }, children: [
1651
+ /* @__PURE__ */ jsx2(
1318
1652
  "textarea",
1319
1653
  {
1320
1654
  ref: textareaRef,
@@ -1355,7 +1689,7 @@ function ReportDialog({
1355
1689
  autoFocus: true
1356
1690
  }
1357
1691
  ),
1358
- (isListening || isTranscribing) && /* @__PURE__ */ jsxs(
1692
+ (isListening || isTranscribing) && /* @__PURE__ */ jsxs2(
1359
1693
  "span",
1360
1694
  {
1361
1695
  style: {
@@ -1373,7 +1707,7 @@ function ReportDialog({
1373
1707
  zIndex: 1
1374
1708
  },
1375
1709
  children: [
1376
- /* @__PURE__ */ jsx(
1710
+ /* @__PURE__ */ jsx2(
1377
1711
  "span",
1378
1712
  {
1379
1713
  style: {
@@ -1388,7 +1722,7 @@ function ReportDialog({
1388
1722
  }
1389
1723
  ),
1390
1724
  isTranscribing ? "Transcribing\u2026" : "REC",
1391
- isListening && !isTranscribing && /* @__PURE__ */ jsx(
1725
+ isListening && !isTranscribing && /* @__PURE__ */ jsx2(
1392
1726
  "span",
1393
1727
  {
1394
1728
  style: {
@@ -1415,7 +1749,7 @@ function ReportDialog({
1415
1749
  dur: "0.55s",
1416
1750
  delay: "0.15s"
1417
1751
  }
1418
- ].map((b, i) => /* @__PURE__ */ jsx(
1752
+ ].map((b, i) => /* @__PURE__ */ jsx2(
1419
1753
  "span",
1420
1754
  {
1421
1755
  style: {
@@ -1435,7 +1769,7 @@ function ReportDialog({
1435
1769
  ]
1436
1770
  }
1437
1771
  ),
1438
- enhanceText2 && /* @__PURE__ */ jsxs(
1772
+ enhanceText2 && /* @__PURE__ */ jsxs2(
1439
1773
  "button",
1440
1774
  {
1441
1775
  type: "button",
@@ -1480,7 +1814,7 @@ function ReportDialog({
1480
1814
  },
1481
1815
  title: "Polish grammar \u2014 preserves your meaning",
1482
1816
  children: [
1483
- /* @__PURE__ */ jsx(
1817
+ /* @__PURE__ */ jsx2(
1484
1818
  "svg",
1485
1819
  {
1486
1820
  width: "11",
@@ -1488,7 +1822,7 @@ function ReportDialog({
1488
1822
  viewBox: "0 0 24 24",
1489
1823
  fill: "none",
1490
1824
  "aria-hidden": "true",
1491
- children: /* @__PURE__ */ jsx(
1825
+ children: /* @__PURE__ */ jsx2(
1492
1826
  "path",
1493
1827
  {
1494
1828
  d: "M12 3l1.5 4.5L18 9l-4.5 1.5L12 15l-1.5-4.5L6 9l4.5-1.5L12 3zM19 15l.75 2.25L22 18l-2.25.75L19 21l-.75-2.25L16 18l2.25-.75L19 15z",
@@ -1503,7 +1837,7 @@ function ReportDialog({
1503
1837
  ]
1504
1838
  }
1505
1839
  ),
1506
- transcribeAudio2 && /* @__PURE__ */ jsx(
1840
+ transcribeAudio2 && /* @__PURE__ */ jsx2(
1507
1841
  "button",
1508
1842
  {
1509
1843
  type: "button",
@@ -1529,7 +1863,7 @@ function ReportDialog({
1529
1863
  padding: 0,
1530
1864
  transition: "border-color 0.2s ease, color 0.2s ease"
1531
1865
  },
1532
- children: isListening ? /* @__PURE__ */ jsx(
1866
+ children: isListening ? /* @__PURE__ */ jsx2(
1533
1867
  "svg",
1534
1868
  {
1535
1869
  width: "12",
@@ -1537,7 +1871,7 @@ function ReportDialog({
1537
1871
  viewBox: "0 0 24 24",
1538
1872
  fill: "currentColor",
1539
1873
  "aria-hidden": "true",
1540
- children: /* @__PURE__ */ jsx(
1874
+ children: /* @__PURE__ */ jsx2(
1541
1875
  "rect",
1542
1876
  {
1543
1877
  x: "6",
@@ -1548,7 +1882,7 @@ function ReportDialog({
1548
1882
  }
1549
1883
  )
1550
1884
  }
1551
- ) : /* @__PURE__ */ jsxs(
1885
+ ) : /* @__PURE__ */ jsxs2(
1552
1886
  "svg",
1553
1887
  {
1554
1888
  width: "13",
@@ -1557,7 +1891,7 @@ function ReportDialog({
1557
1891
  fill: "none",
1558
1892
  "aria-hidden": "true",
1559
1893
  children: [
1560
- /* @__PURE__ */ jsx(
1894
+ /* @__PURE__ */ jsx2(
1561
1895
  "rect",
1562
1896
  {
1563
1897
  x: "9",
@@ -1569,7 +1903,7 @@ function ReportDialog({
1569
1903
  strokeWidth: "1.8"
1570
1904
  }
1571
1905
  ),
1572
- /* @__PURE__ */ jsx(
1906
+ /* @__PURE__ */ jsx2(
1573
1907
  "path",
1574
1908
  {
1575
1909
  d: "M5 10a7 7 0 0014 0M12 19v3M9 22h6",
@@ -1584,7 +1918,7 @@ function ReportDialog({
1584
1918
  }
1585
1919
  )
1586
1920
  ] }),
1587
- transcribeAudio2 && !isListening && !isTranscribing && /* @__PURE__ */ jsxs(
1921
+ transcribeAudio2 && !isListening && !isTranscribing && /* @__PURE__ */ jsxs2(
1588
1922
  "div",
1589
1923
  {
1590
1924
  style: {
@@ -1598,16 +1932,16 @@ function ReportDialog({
1598
1932
  userSelect: "none"
1599
1933
  },
1600
1934
  children: [
1601
- /* @__PURE__ */ jsxs("svg", { width: "11", height: "11", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
1602
- /* @__PURE__ */ jsx("path", { d: "M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z" }),
1603
- /* @__PURE__ */ jsx("path", { d: "M19 10v2a7 7 0 0 1-14 0v-2" }),
1604
- /* @__PURE__ */ jsx("line", { x1: "12", x2: "12", y1: "19", y2: "22" })
1935
+ /* @__PURE__ */ jsxs2("svg", { width: "11", height: "11", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
1936
+ /* @__PURE__ */ jsx2("path", { d: "M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z" }),
1937
+ /* @__PURE__ */ jsx2("path", { d: "M19 10v2a7 7 0 0 1-14 0v-2" }),
1938
+ /* @__PURE__ */ jsx2("line", { x1: "12", x2: "12", y1: "19", y2: "22" })
1605
1939
  ] }),
1606
1940
  "Hold Space to speak \u2014 we'll transcribe it"
1607
1941
  ]
1608
1942
  }
1609
1943
  ),
1610
- isEnhanced && originalDescription !== null && /* @__PURE__ */ jsxs(
1944
+ isEnhanced && originalDescription !== null && /* @__PURE__ */ jsxs2(
1611
1945
  "div",
1612
1946
  {
1613
1947
  style: {
@@ -1621,7 +1955,7 @@ function ReportDialog({
1621
1955
  border: `1px solid ${t.accent}40`
1622
1956
  },
1623
1957
  children: [
1624
- /* @__PURE__ */ jsx(
1958
+ /* @__PURE__ */ jsx2(
1625
1959
  "svg",
1626
1960
  {
1627
1961
  width: "12",
@@ -1629,7 +1963,7 @@ function ReportDialog({
1629
1963
  viewBox: "0 0 24 24",
1630
1964
  fill: "none",
1631
1965
  style: { flexShrink: 0 },
1632
- children: /* @__PURE__ */ jsx(
1966
+ children: /* @__PURE__ */ jsx2(
1633
1967
  "path",
1634
1968
  {
1635
1969
  d: "M12 3l1.5 4.5L18 9l-4.5 1.5L12 15l-1.5-4.5L6 9l4.5-1.5L12 3z",
@@ -1640,7 +1974,7 @@ function ReportDialog({
1640
1974
  )
1641
1975
  }
1642
1976
  ),
1643
- /* @__PURE__ */ jsx(
1977
+ /* @__PURE__ */ jsx2(
1644
1978
  "span",
1645
1979
  {
1646
1980
  style: {
@@ -1651,7 +1985,7 @@ function ReportDialog({
1651
1985
  children: "AI enhanced"
1652
1986
  }
1653
1987
  ),
1654
- /* @__PURE__ */ jsx(
1988
+ /* @__PURE__ */ jsx2(
1655
1989
  "button",
1656
1990
  {
1657
1991
  type: "button",
@@ -1673,7 +2007,7 @@ function ReportDialog({
1673
2007
  children: "Keep"
1674
2008
  }
1675
2009
  ),
1676
- /* @__PURE__ */ jsx(
2010
+ /* @__PURE__ */ jsx2(
1677
2011
  "button",
1678
2012
  {
1679
2013
  type: "button",
@@ -1699,7 +2033,7 @@ function ReportDialog({
1699
2033
  ]
1700
2034
  }
1701
2035
  ),
1702
- voiceError && /* @__PURE__ */ jsx(
2036
+ voiceError && /* @__PURE__ */ jsx2(
1703
2037
  "p",
1704
2038
  {
1705
2039
  style: {
@@ -1711,8 +2045,8 @@ function ReportDialog({
1711
2045
  children: voiceError
1712
2046
  }
1713
2047
  ),
1714
- /* @__PURE__ */ jsxs("div", { style: { marginTop: "10px" }, children: [
1715
- /* @__PURE__ */ jsxs(
2048
+ /* @__PURE__ */ jsxs2("div", { style: { marginTop: "10px" }, children: [
2049
+ /* @__PURE__ */ jsxs2(
1716
2050
  "span",
1717
2051
  {
1718
2052
  style: {
@@ -1728,7 +2062,7 @@ function ReportDialog({
1728
2062
  ]
1729
2063
  }
1730
2064
  ),
1731
- screenshots.length > 0 && /* @__PURE__ */ jsx(
2065
+ screenshots.length > 0 && /* @__PURE__ */ jsx2(
1732
2066
  "div",
1733
2067
  {
1734
2068
  style: {
@@ -1737,7 +2071,7 @@ function ReportDialog({
1737
2071
  gap: "8px",
1738
2072
  marginBottom: "8px"
1739
2073
  },
1740
- children: screenshots.map((src, i) => /* @__PURE__ */ jsxs(
2074
+ children: screenshots.map((src, i) => /* @__PURE__ */ jsxs2(
1741
2075
  "div",
1742
2076
  {
1743
2077
  style: {
@@ -1746,7 +2080,7 @@ function ReportDialog({
1746
2080
  height: "56px"
1747
2081
  },
1748
2082
  children: [
1749
- /* @__PURE__ */ jsx(
2083
+ /* @__PURE__ */ jsx2(
1750
2084
  "img",
1751
2085
  {
1752
2086
  src,
@@ -1764,7 +2098,7 @@ function ReportDialog({
1764
2098
  }
1765
2099
  }
1766
2100
  ),
1767
- /* @__PURE__ */ jsx(
2101
+ /* @__PURE__ */ jsx2(
1768
2102
  "button",
1769
2103
  {
1770
2104
  type: "button",
@@ -1790,14 +2124,14 @@ function ReportDialog({
1790
2124
  alignItems: "center",
1791
2125
  justifyContent: "center"
1792
2126
  },
1793
- children: /* @__PURE__ */ jsx(
2127
+ children: /* @__PURE__ */ jsx2(
1794
2128
  "svg",
1795
2129
  {
1796
2130
  width: "8",
1797
2131
  height: "8",
1798
2132
  viewBox: "0 0 10 10",
1799
2133
  fill: "none",
1800
- children: /* @__PURE__ */ jsx(
2134
+ children: /* @__PURE__ */ jsx2(
1801
2135
  "path",
1802
2136
  {
1803
2137
  d: "M1 1L9 9M9 1L1 9",
@@ -1809,6 +2143,52 @@ function ReportDialog({
1809
2143
  }
1810
2144
  )
1811
2145
  }
2146
+ ),
2147
+ /* @__PURE__ */ jsx2(
2148
+ "button",
2149
+ {
2150
+ type: "button",
2151
+ onClick: () => setAnnotatingIndex(i),
2152
+ "aria-label": `Annotate screenshot ${i + 1}`,
2153
+ title: "Annotate",
2154
+ style: {
2155
+ position: "absolute",
2156
+ top: "50%",
2157
+ left: "50%",
2158
+ transform: "translate(-50%, -50%)",
2159
+ width: "24px",
2160
+ height: "24px",
2161
+ borderRadius: "50%",
2162
+ border: "none",
2163
+ background: t.accent,
2164
+ color: t.accentText,
2165
+ padding: 0,
2166
+ cursor: "pointer",
2167
+ display: "flex",
2168
+ alignItems: "center",
2169
+ justifyContent: "center",
2170
+ boxShadow: "0 2px 8px rgba(0,0,0,0.45)"
2171
+ },
2172
+ children: /* @__PURE__ */ jsx2(
2173
+ "svg",
2174
+ {
2175
+ width: "12",
2176
+ height: "12",
2177
+ viewBox: "0 0 24 24",
2178
+ fill: "none",
2179
+ children: /* @__PURE__ */ jsx2(
2180
+ "path",
2181
+ {
2182
+ d: "M12 20h9M16.5 3.5a2.12 2.12 0 013 3L7 19l-4 1 1-4L16.5 3.5z",
2183
+ stroke: "currentColor",
2184
+ strokeWidth: "2.2",
2185
+ strokeLinecap: "round",
2186
+ strokeLinejoin: "round"
2187
+ }
2188
+ )
2189
+ }
2190
+ )
2191
+ }
1812
2192
  )
1813
2193
  ]
1814
2194
  },
@@ -1816,7 +2196,7 @@ function ReportDialog({
1816
2196
  ))
1817
2197
  }
1818
2198
  ),
1819
- /* @__PURE__ */ jsx(
2199
+ /* @__PURE__ */ jsx2(
1820
2200
  "div",
1821
2201
  {
1822
2202
  onClick: () => {
@@ -1852,7 +2232,7 @@ function ReportDialog({
1852
2232
  background: screenshotDragOver ? isDark ? "rgba(34,211,238,0.06)" : "rgba(8,145,178,0.04)" : "transparent",
1853
2233
  transition: "border-color 0.15s ease, background 0.15s ease"
1854
2234
  },
1855
- children: screenshots.length > 0 ? /* @__PURE__ */ jsx(
2235
+ children: screenshots.length > 0 ? /* @__PURE__ */ jsx2(
1856
2236
  "p",
1857
2237
  {
1858
2238
  style: {
@@ -1863,8 +2243,8 @@ function ReportDialog({
1863
2243
  },
1864
2244
  children: screenshotDragOver ? "Drop to attach" : "+ Add more \xB7 Drag & drop \xB7 Paste"
1865
2245
  }
1866
- ) : /* @__PURE__ */ jsxs(Fragment, { children: [
1867
- /* @__PURE__ */ jsxs(
2246
+ ) : /* @__PURE__ */ jsxs2(Fragment, { children: [
2247
+ /* @__PURE__ */ jsxs2(
1868
2248
  "svg",
1869
2249
  {
1870
2250
  width: "22",
@@ -1877,7 +2257,7 @@ function ReportDialog({
1877
2257
  opacity: 0.45
1878
2258
  },
1879
2259
  children: [
1880
- /* @__PURE__ */ jsx(
2260
+ /* @__PURE__ */ jsx2(
1881
2261
  "rect",
1882
2262
  {
1883
2263
  x: "3",
@@ -1889,7 +2269,7 @@ function ReportDialog({
1889
2269
  strokeWidth: "1.5"
1890
2270
  }
1891
2271
  ),
1892
- /* @__PURE__ */ jsx(
2272
+ /* @__PURE__ */ jsx2(
1893
2273
  "circle",
1894
2274
  {
1895
2275
  cx: "8.5",
@@ -1899,7 +2279,7 @@ function ReportDialog({
1899
2279
  strokeWidth: "1.5"
1900
2280
  }
1901
2281
  ),
1902
- /* @__PURE__ */ jsx(
2282
+ /* @__PURE__ */ jsx2(
1903
2283
  "path",
1904
2284
  {
1905
2285
  d: "M21 15l-5-5L5 21",
@@ -1912,7 +2292,7 @@ function ReportDialog({
1912
2292
  ]
1913
2293
  }
1914
2294
  ),
1915
- /* @__PURE__ */ jsx(
2295
+ /* @__PURE__ */ jsx2(
1916
2296
  "p",
1917
2297
  {
1918
2298
  style: {
@@ -1924,7 +2304,7 @@ function ReportDialog({
1924
2304
  children: screenshotDragOver ? "Drop to attach" : "Add screenshots"
1925
2305
  }
1926
2306
  ),
1927
- /* @__PURE__ */ jsx(
2307
+ /* @__PURE__ */ jsx2(
1928
2308
  "p",
1929
2309
  {
1930
2310
  style: {
@@ -1939,8 +2319,8 @@ function ReportDialog({
1939
2319
  }
1940
2320
  )
1941
2321
  ] }),
1942
- showSeverity && reportType === "BUG" && /* @__PURE__ */ jsxs("div", { style: { marginTop: "10px" }, children: [
1943
- /* @__PURE__ */ jsx(
2322
+ showSeverity && reportType === "BUG" && /* @__PURE__ */ jsxs2("div", { style: { marginTop: "10px" }, children: [
2323
+ /* @__PURE__ */ jsx2(
1944
2324
  "span",
1945
2325
  {
1946
2326
  style: {
@@ -1952,7 +2332,7 @@ function ReportDialog({
1952
2332
  children: "Severity"
1953
2333
  }
1954
2334
  ),
1955
- /* @__PURE__ */ jsx("div", { style: { display: "flex", gap: "6px" }, children: ["low", "medium", "high"].map((s) => /* @__PURE__ */ jsx(
2335
+ /* @__PURE__ */ jsx2("div", { style: { display: "flex", gap: "6px" }, children: ["low", "medium", "high"].map((s) => /* @__PURE__ */ jsx2(
1956
2336
  "button",
1957
2337
  {
1958
2338
  type: "button",
@@ -1976,7 +2356,7 @@ function ReportDialog({
1976
2356
  s
1977
2357
  )) })
1978
2358
  ] }),
1979
- validationError && /* @__PURE__ */ jsx(
2359
+ validationError && /* @__PURE__ */ jsx2(
1980
2360
  "p",
1981
2361
  {
1982
2362
  style: {
@@ -1987,7 +2367,7 @@ function ReportDialog({
1987
2367
  children: validationError
1988
2368
  }
1989
2369
  ),
1990
- /* @__PURE__ */ jsx(
2370
+ /* @__PURE__ */ jsx2(
1991
2371
  "button",
1992
2372
  {
1993
2373
  type: "button",
@@ -2012,7 +2392,7 @@ function ReportDialog({
2012
2392
  )
2013
2393
  ] })
2014
2394
  ] }) }),
2015
- /* @__PURE__ */ jsx(
2395
+ /* @__PURE__ */ jsx2(
2016
2396
  "div",
2017
2397
  {
2018
2398
  style: {
@@ -2020,7 +2400,7 @@ function ReportDialog({
2020
2400
  borderTop: `1px solid ${t.border}`,
2021
2401
  textAlign: "center"
2022
2402
  },
2023
- children: /* @__PURE__ */ jsx("span", { style: { fontSize: "11px", color: t.textMuted }, children: "Powered by Glitchgrab" })
2403
+ children: /* @__PURE__ */ jsx2("span", { style: { fontSize: "11px", color: t.textMuted }, children: "Powered by Glitchgrab" })
2024
2404
  }
2025
2405
  )
2026
2406
  ]
@@ -2030,7 +2410,7 @@ function ReportDialog({
2030
2410
  ),
2031
2411
  document.body
2032
2412
  ),
2033
- /* @__PURE__ */ jsx(
2413
+ /* @__PURE__ */ jsx2(
2034
2414
  "input",
2035
2415
  {
2036
2416
  ref: fileInputRef,
@@ -2042,7 +2422,7 @@ function ReportDialog({
2042
2422
  }
2043
2423
  ),
2044
2424
  previewIndex !== null && screenshots[previewIndex] && createPortal(
2045
- /* @__PURE__ */ jsxs(
2425
+ /* @__PURE__ */ jsxs2(
2046
2426
  "div",
2047
2427
  {
2048
2428
  style: {
@@ -2063,37 +2443,114 @@ function ReportDialog({
2063
2443
  },
2064
2444
  onMouseDown: (e) => e.stopPropagation(),
2065
2445
  children: [
2066
- /* @__PURE__ */ jsx(
2067
- "img",
2446
+ /* @__PURE__ */ jsxs2(
2447
+ "div",
2068
2448
  {
2069
2449
  ref: previewImgRef,
2070
- src: screenshots[previewIndex],
2071
- alt: "Screenshot preview",
2450
+ style: { position: "relative", maxWidth: "100%" },
2072
2451
  onClick: (e) => e.stopPropagation(),
2073
- style: {
2074
- maxWidth: "100%",
2075
- maxHeight: "calc(100vh - 80px)",
2076
- borderRadius: "8px",
2077
- objectFit: "contain",
2078
- cursor: "default"
2079
- }
2452
+ children: [
2453
+ /* @__PURE__ */ jsx2(
2454
+ "img",
2455
+ {
2456
+ src: screenshots[previewIndex],
2457
+ alt: "Screenshot preview",
2458
+ style: {
2459
+ maxWidth: "100%",
2460
+ maxHeight: "calc(100vh - 80px)",
2461
+ borderRadius: "8px",
2462
+ objectFit: "contain",
2463
+ cursor: "default",
2464
+ display: "block"
2465
+ }
2466
+ }
2467
+ ),
2468
+ /* @__PURE__ */ jsxs2(
2469
+ "button",
2470
+ {
2471
+ type: "button",
2472
+ onClick: (e) => {
2473
+ e.stopPropagation();
2474
+ setAnnotatingIndex(previewIndex);
2475
+ },
2476
+ style: {
2477
+ position: "absolute",
2478
+ top: "12px",
2479
+ right: "12px",
2480
+ display: "inline-flex",
2481
+ alignItems: "center",
2482
+ gap: "6px",
2483
+ padding: "8px 14px",
2484
+ borderRadius: "999px",
2485
+ border: "none",
2486
+ background: t.accent,
2487
+ color: t.accentText,
2488
+ fontSize: "13px",
2489
+ fontWeight: 700,
2490
+ cursor: "pointer",
2491
+ fontFamily: "inherit",
2492
+ boxShadow: "0 4px 14px rgba(0,0,0,0.35)",
2493
+ animation: "gg-pulse 2s ease-in-out infinite"
2494
+ },
2495
+ children: [
2496
+ /* @__PURE__ */ jsx2("svg", { width: "13", height: "13", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx2(
2497
+ "path",
2498
+ {
2499
+ d: "M12 20h9M16.5 3.5a2.12 2.12 0 013 3L7 19l-4 1 1-4L16.5 3.5z",
2500
+ stroke: "currentColor",
2501
+ strokeWidth: "2",
2502
+ strokeLinecap: "round",
2503
+ strokeLinejoin: "round"
2504
+ }
2505
+ ) }),
2506
+ "Annotate"
2507
+ ]
2508
+ }
2509
+ )
2510
+ ]
2080
2511
  }
2081
2512
  ),
2082
- /* @__PURE__ */ jsx(
2083
- "span",
2513
+ /* @__PURE__ */ jsx2(
2514
+ "div",
2084
2515
  {
2085
2516
  style: {
2086
2517
  marginTop: "12px",
2087
- color: t.textMuted,
2088
- fontSize: "12px"
2518
+ display: "flex",
2519
+ alignItems: "center",
2520
+ gap: "12px"
2089
2521
  },
2090
- children: screenshots.length > 1 ? `${previewIndex + 1} / ${screenshots.length} \xB7 Click outside to close` : "Click outside to close"
2522
+ children: /* @__PURE__ */ jsx2(
2523
+ "span",
2524
+ {
2525
+ style: {
2526
+ color: t.textMuted,
2527
+ fontSize: "12px"
2528
+ },
2529
+ children: screenshots.length > 1 ? `${previewIndex + 1} / ${screenshots.length} \xB7 Click outside to close` : "Click outside to close"
2530
+ }
2531
+ )
2091
2532
  }
2092
2533
  )
2093
2534
  ]
2094
2535
  }
2095
2536
  ),
2096
2537
  document.body
2538
+ ),
2539
+ annotatingIndex !== null && screenshots[annotatingIndex] && createPortal(
2540
+ /* @__PURE__ */ jsx2(
2541
+ AnnotationCanvas,
2542
+ {
2543
+ imageSrc: screenshots[annotatingIndex],
2544
+ onCancel: () => setAnnotatingIndex(null),
2545
+ onSave: (dataUrl) => {
2546
+ setScreenshots(
2547
+ (prev) => prev.map((s, i) => i === annotatingIndex ? dataUrl : s)
2548
+ );
2549
+ setAnnotatingIndex(null);
2550
+ }
2551
+ }
2552
+ ),
2553
+ document.body
2097
2554
  )
2098
2555
  ] });
2099
2556
  }
@@ -2103,6 +2560,12 @@ function getTypeLabel(type) {
2103
2560
  return "Bug Report";
2104
2561
  case "FEATURE_REQUEST":
2105
2562
  return "Feature Request";
2563
+ case "UI_IMPROVEMENT":
2564
+ return "UI Improvement";
2565
+ case "PERFORMANCE":
2566
+ return "Performance";
2567
+ case "SECURITY":
2568
+ return "Security";
2106
2569
  case "QUESTION":
2107
2570
  return "Question";
2108
2571
  case "OTHER":
@@ -2115,6 +2578,12 @@ function getTypeSubtitle(type) {
2115
2578
  return "Something isn't working";
2116
2579
  case "FEATURE_REQUEST":
2117
2580
  return "Suggest an improvement";
2581
+ case "UI_IMPROVEMENT":
2582
+ return "Something looks off";
2583
+ case "PERFORMANCE":
2584
+ return "Something is slow";
2585
+ case "SECURITY":
2586
+ return "Report a security concern";
2118
2587
  case "QUESTION":
2119
2588
  return "Ask a question";
2120
2589
  case "OTHER":
@@ -2128,6 +2597,12 @@ function getPlaceholder(type, idx = 0, hasVoice = false) {
2128
2597
  return "What went wrong? Describe it or paste an error...";
2129
2598
  case "FEATURE_REQUEST":
2130
2599
  return "Describe the feature you'd like...";
2600
+ case "UI_IMPROVEMENT":
2601
+ return "What looks off? Describe the visual issue...";
2602
+ case "PERFORMANCE":
2603
+ return "What's slow? Describe when it happens...";
2604
+ case "SECURITY":
2605
+ return "Describe the security concern...";
2131
2606
  case "QUESTION":
2132
2607
  return "What would you like to know?";
2133
2608
  case "OTHER":
@@ -2144,7 +2619,7 @@ function getTypeIcon(type, color, size = 24) {
2144
2619
  };
2145
2620
  switch (type) {
2146
2621
  case "BUG":
2147
- return /* @__PURE__ */ jsx("svg", { ...props, children: /* @__PURE__ */ jsx(
2622
+ return /* @__PURE__ */ jsx2("svg", { ...props, children: /* @__PURE__ */ jsx2(
2148
2623
  "path",
2149
2624
  {
2150
2625
  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",
@@ -2154,7 +2629,7 @@ function getTypeIcon(type, color, size = 24) {
2154
2629
  }
2155
2630
  ) });
2156
2631
  case "FEATURE_REQUEST":
2157
- return /* @__PURE__ */ jsx("svg", { ...props, children: /* @__PURE__ */ jsx(
2632
+ return /* @__PURE__ */ jsx2("svg", { ...props, children: /* @__PURE__ */ jsx2(
2158
2633
  "path",
2159
2634
  {
2160
2635
  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",
@@ -2163,10 +2638,55 @@ function getTypeIcon(type, color, size = 24) {
2163
2638
  strokeLinejoin: "round"
2164
2639
  }
2165
2640
  ) });
2641
+ case "UI_IMPROVEMENT":
2642
+ return /* @__PURE__ */ jsxs2("svg", { ...props, children: [
2643
+ /* @__PURE__ */ jsx2(
2644
+ "rect",
2645
+ {
2646
+ x: "3",
2647
+ y: "3",
2648
+ width: "18",
2649
+ height: "18",
2650
+ rx: "2",
2651
+ stroke: color,
2652
+ strokeWidth: "1.5"
2653
+ }
2654
+ ),
2655
+ /* @__PURE__ */ jsx2(
2656
+ "path",
2657
+ {
2658
+ d: "M3 9h18M9 21V9",
2659
+ stroke: color,
2660
+ strokeWidth: "1.5",
2661
+ strokeLinecap: "round"
2662
+ }
2663
+ )
2664
+ ] });
2665
+ case "PERFORMANCE":
2666
+ return /* @__PURE__ */ jsx2("svg", { ...props, children: /* @__PURE__ */ jsx2(
2667
+ "path",
2668
+ {
2669
+ d: "M12 21a9 9 0 100-18 9 9 0 000 18zM12 12l4-4M12 7v.01",
2670
+ stroke: color,
2671
+ strokeWidth: "1.5",
2672
+ strokeLinecap: "round",
2673
+ strokeLinejoin: "round"
2674
+ }
2675
+ ) });
2676
+ case "SECURITY":
2677
+ return /* @__PURE__ */ jsx2("svg", { ...props, children: /* @__PURE__ */ jsx2(
2678
+ "path",
2679
+ {
2680
+ d: "M12 3l7 3v5c0 4.5-3 8.5-7 10-4-1.5-7-5.5-7-10V6l7-3z",
2681
+ stroke: color,
2682
+ strokeWidth: "1.5",
2683
+ strokeLinejoin: "round"
2684
+ }
2685
+ ) });
2166
2686
  case "QUESTION":
2167
- return /* @__PURE__ */ jsxs("svg", { ...props, children: [
2168
- /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10", stroke: color, strokeWidth: "1.5" }),
2169
- /* @__PURE__ */ jsx(
2687
+ return /* @__PURE__ */ jsxs2("svg", { ...props, children: [
2688
+ /* @__PURE__ */ jsx2("circle", { cx: "12", cy: "12", r: "10", stroke: color, strokeWidth: "1.5" }),
2689
+ /* @__PURE__ */ jsx2(
2170
2690
  "path",
2171
2691
  {
2172
2692
  d: "M9.09 9a3 3 0 015.83 1c0 2-3 3-3 3M12 17h.01",
@@ -2178,7 +2698,7 @@ function getTypeIcon(type, color, size = 24) {
2178
2698
  )
2179
2699
  ] });
2180
2700
  case "OTHER":
2181
- return /* @__PURE__ */ jsx("svg", { ...props, children: /* @__PURE__ */ jsx(
2701
+ return /* @__PURE__ */ jsx2("svg", { ...props, children: /* @__PURE__ */ jsx2(
2182
2702
  "path",
2183
2703
  {
2184
2704
  d: "M21 15a2 2 0 01-2 2H7l-4 4V5a2 2 0 012-2h14a2 2 0 012 2v10z",
@@ -2191,7 +2711,7 @@ function getTypeIcon(type, color, size = 24) {
2191
2711
  }
2192
2712
 
2193
2713
  // src/provider.tsx
2194
- import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
2714
+ import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
2195
2715
  var DEFAULT_BASE_URL2 = "https://glitchgrab.dev";
2196
2716
  var GlitchgrabContext = createContext(null);
2197
2717
  function useGlitchgrab() {
@@ -2215,13 +2735,13 @@ function GlitchgrabProviderInner({
2215
2735
  showSeverity,
2216
2736
  ignoreErrors
2217
2737
  }) {
2218
- const visitedPagesRef = useRef2([]);
2219
- useEffect2(() => {
2738
+ const visitedPagesRef = useRef3([]);
2739
+ useEffect3(() => {
2220
2740
  if (enableBreadcrumbs) {
2221
2741
  initBreadcrumbs(maxBreadcrumbs2);
2222
2742
  }
2223
2743
  }, [enableBreadcrumbs, maxBreadcrumbs2]);
2224
- useEffect2(() => {
2744
+ useEffect3(() => {
2225
2745
  try {
2226
2746
  if (typeof window === "undefined") return;
2227
2747
  const trackPage = () => {
@@ -2258,7 +2778,7 @@ function GlitchgrabProviderInner({
2258
2778
  } catch (e) {
2259
2779
  }
2260
2780
  }, []);
2261
- useEffect2(() => {
2781
+ useEffect3(() => {
2262
2782
  try {
2263
2783
  if (typeof window === "undefined") return;
2264
2784
  if (process.env.NODE_ENV === "development") return;
@@ -2455,11 +2975,11 @@ function GlitchgrabProviderInner({
2455
2975
  window.dispatchEvent(new CustomEvent("glitchgrab:open-report", { detail: options }));
2456
2976
  }
2457
2977
  }, []);
2458
- useEffect2(() => {
2978
+ useEffect3(() => {
2459
2979
  try {
2460
2980
  if (typeof window === "undefined") return;
2461
2981
  const handleKeyDown = (e) => {
2462
- if ((e.metaKey || e.ctrlKey) && e.shiftKey && e.key.toLowerCase() === "g") {
2982
+ if (matchesShortcut(e)) {
2463
2983
  e.preventDefault();
2464
2984
  openReportDialog();
2465
2985
  }
@@ -2469,7 +2989,11 @@ function GlitchgrabProviderInner({
2469
2989
  } catch (e) {
2470
2990
  }
2471
2991
  }, [openReportDialog]);
2472
- return /* @__PURE__ */ jsxs2(
2992
+ const [shortcutLabel, setShortcutLabel] = useState3(GLITCHGRAB_SHORTCUT);
2993
+ useEffect3(() => {
2994
+ setShortcutLabel(getShortcutLabel());
2995
+ }, []);
2996
+ return /* @__PURE__ */ jsxs3(
2473
2997
  GlitchgrabContext.Provider,
2474
2998
  {
2475
2999
  value: {
@@ -2479,10 +3003,11 @@ function GlitchgrabProviderInner({
2479
3003
  report,
2480
3004
  addBreadcrumb: addBreadcrumb2,
2481
3005
  openReportDialog,
2482
- enhanceText: enhance
3006
+ enhanceText: enhance,
3007
+ shortcutLabel
2483
3008
  },
2484
3009
  children: [
2485
- /* @__PURE__ */ jsx2(
3010
+ /* @__PURE__ */ jsx3(
2486
3011
  GlitchgrabErrorBoundary,
2487
3012
  {
2488
3013
  token,
@@ -2494,27 +3019,27 @@ function GlitchgrabProviderInner({
2494
3019
  children
2495
3020
  }
2496
3021
  ),
2497
- /* @__PURE__ */ jsx2(ReportDialog, { report, enhanceText: enhance, transcribeAudio: transcribe, types, showSeverity })
3022
+ /* @__PURE__ */ jsx3(ReportDialog, { report, enhanceText: enhance, transcribeAudio: transcribe, types, showSeverity })
2498
3023
  ]
2499
3024
  }
2500
3025
  );
2501
3026
  }
2502
3027
  function GlitchgrabProvider(props) {
2503
- if (!props.token) return /* @__PURE__ */ jsx2(Fragment2, { children: props.children });
3028
+ if (!props.token) return /* @__PURE__ */ jsx3(Fragment2, { children: props.children });
2504
3029
  const resolvedProps = {
2505
3030
  ...props,
2506
3031
  baseUrl: props.baseUrl || DEFAULT_BASE_URL2
2507
3032
  };
2508
3033
  try {
2509
- return /* @__PURE__ */ jsx2(GlitchgrabProviderInner, { ...resolvedProps });
3034
+ return /* @__PURE__ */ jsx3(GlitchgrabProviderInner, { ...resolvedProps });
2510
3035
  } catch (e) {
2511
- return /* @__PURE__ */ jsx2(Fragment2, { children: props.children });
3036
+ return /* @__PURE__ */ jsx3(Fragment2, { children: props.children });
2512
3037
  }
2513
3038
  }
2514
3039
 
2515
3040
  // src/report-button.tsx
2516
- import { useState as useState3 } from "react";
2517
- import { Fragment as Fragment3, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
3041
+ import { useState as useState4 } from "react";
3042
+ import { Fragment as Fragment3, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
2518
3043
  function useGlitchgrabSafe() {
2519
3044
  try {
2520
3045
  return useGlitchgrab();
@@ -2529,7 +3054,7 @@ function ReportButton({
2529
3054
  children
2530
3055
  }) {
2531
3056
  var _a, _b;
2532
- const [capturing, setCapturing] = useState3(false);
3057
+ const [capturing, setCapturing] = useState4(false);
2533
3058
  const glitchgrab = useGlitchgrabSafe();
2534
3059
  if (!glitchgrab) return null;
2535
3060
  const handleClick = async () => {
@@ -2538,7 +3063,7 @@ function ReportButton({
2538
3063
  setCapturing(false);
2539
3064
  };
2540
3065
  if (children) {
2541
- return /* @__PURE__ */ jsx3(Fragment3, { children: children({ onClick: handleClick, capturing }) });
3066
+ return /* @__PURE__ */ jsx4(Fragment3, { children: children({ onClick: handleClick, capturing }) });
2542
3067
  }
2543
3068
  const isTop = (_a = position == null ? void 0 : position.startsWith("top")) != null ? _a : false;
2544
3069
  const isLeft = (_b = position == null ? void 0 : position.endsWith("left")) != null ? _b : false;
@@ -2546,7 +3071,7 @@ function ReportButton({
2546
3071
  ...isLeft ? { left: "16px" } : { right: "16px" },
2547
3072
  ...isTop ? { top: "16px" } : { bottom: "16px" }
2548
3073
  };
2549
- return /* @__PURE__ */ jsxs3(
3074
+ return /* @__PURE__ */ jsxs4(
2550
3075
  "button",
2551
3076
  {
2552
3077
  type: "button",
@@ -2581,7 +3106,7 @@ function ReportButton({
2581
3106
  },
2582
3107
  "aria-label": label,
2583
3108
  children: [
2584
- /* @__PURE__ */ jsx3(
3109
+ /* @__PURE__ */ jsx4(
2585
3110
  "svg",
2586
3111
  {
2587
3112
  width: "16",
@@ -2589,7 +3114,7 @@ function ReportButton({
2589
3114
  viewBox: "0 0 16 16",
2590
3115
  fill: "none",
2591
3116
  style: { flexShrink: 0 },
2592
- children: /* @__PURE__ */ jsx3(
3117
+ children: /* @__PURE__ */ jsx4(
2593
3118
  "path",
2594
3119
  {
2595
3120
  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",
@@ -2605,17 +3130,17 @@ function ReportButton({
2605
3130
  }
2606
3131
 
2607
3132
  // src/use-reports.ts
2608
- import { useState as useState4, useEffect as useEffect3, useCallback as useCallback2 } from "react";
3133
+ import { useState as useState5, useEffect as useEffect4, useCallback as useCallback2 } from "react";
2609
3134
  var BASE_URL = "https://glitchgrab.dev";
2610
3135
  function useGlitchgrabReports({
2611
3136
  token,
2612
3137
  userId,
2613
3138
  limit = 100
2614
3139
  }) {
2615
- const [reports, setReports] = useState4([]);
2616
- const [isLoading, setIsLoading] = useState4(true);
2617
- const [isFetching, setIsFetching] = useState4(false);
2618
- const [error, setError] = useState4(null);
3140
+ const [reports, setReports] = useState5([]);
3141
+ const [isLoading, setIsLoading] = useState5(true);
3142
+ const [isFetching, setIsFetching] = useState5(false);
3143
+ const [error, setError] = useState5(null);
2619
3144
  const fetchReports = useCallback2(async () => {
2620
3145
  var _a, _b;
2621
3146
  if (!token || !userId) {
@@ -2642,7 +3167,7 @@ function useGlitchgrabReports({
2642
3167
  setIsFetching(false);
2643
3168
  }
2644
3169
  }, [token, userId, limit]);
2645
- useEffect3(() => {
3170
+ useEffect4(() => {
2646
3171
  fetchReports();
2647
3172
  }, [fetchReports]);
2648
3173
  return { reports, isLoading, isFetching, error, refetch: fetchReports };
@@ -2679,8 +3204,8 @@ function useGlitchgrabActions({
2679
3204
  onSuccess,
2680
3205
  onError
2681
3206
  }) {
2682
- const [isPending, setIsPending] = useState4(false);
2683
- const [error, setError] = useState4(null);
3207
+ const [isPending, setIsPending] = useState5(false);
3208
+ const [error, setError] = useState5(null);
2684
3209
  const run = useCallback2(
2685
3210
  async (reportId, body) => {
2686
3211
  setIsPending(true);
@@ -2710,6 +3235,8 @@ function useGlitchgrabActions({
2710
3235
  };
2711
3236
  }
2712
3237
  export {
3238
+ GLITCHGRAB_SHORTCUT,
3239
+ GLITCHGRAB_SHORTCUT_MAC,
2713
3240
  GlitchgrabErrorBoundary,
2714
3241
  GlitchgrabProvider,
2715
3242
  ReportButton,
@@ -2722,7 +3249,9 @@ export {
2722
3249
  enhanceText,
2723
3250
  fetchGlitchgrabReports,
2724
3251
  getBreadcrumbs,
3252
+ getShortcutLabel,
2725
3253
  initBreadcrumbs,
3254
+ matchesShortcut,
2726
3255
  sanitizeUrl,
2727
3256
  sendReport,
2728
3257
  shouldSkipDuplicate,