glitchgrab 1.23.0 → 1.24.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
@@ -664,10 +664,11 @@ function ReportDialog({
664
664
  const [description, setDescription] = useState("");
665
665
  const [isSubmitting, setIsSubmitting] = useState(false);
666
666
  const [submitted, setSubmitted] = useState(false);
667
- const [screenshot, setScreenshot] = useState(null);
668
- const [previewOpen, setPreviewOpen] = useState(false);
667
+ const [screenshots, setScreenshots] = useState([]);
668
+ const [previewIndex, setPreviewIndex] = useState(null);
669
669
  const fileInputRef = useRef(null);
670
670
  const modalRef = useRef(null);
671
+ const previewImgRef = useRef(null);
671
672
  const textareaRef = useRef(null);
672
673
  const recognitionRef = useRef(null);
673
674
  const voiceBaseRef = useRef("");
@@ -702,20 +703,21 @@ function ReportDialog({
702
703
  const isDark = useIsDark();
703
704
  const t = getTheme(isDark);
704
705
  const handleFileUpload = (e) => {
705
- var _a;
706
- const file = (_a = e.target.files) == null ? void 0 : _a[0];
707
- if (!file) return;
708
- const reader = new FileReader();
709
- reader.onload = () => {
710
- setScreenshot(reader.result);
711
- };
712
- reader.readAsDataURL(file);
706
+ const files = e.target.files;
707
+ if (!files || files.length === 0) return;
708
+ Array.from(files).forEach((file) => {
709
+ const reader = new FileReader();
710
+ reader.onload = () => {
711
+ setScreenshots((prev) => [...prev, reader.result]);
712
+ };
713
+ reader.readAsDataURL(file);
714
+ });
713
715
  e.target.value = "";
714
716
  };
715
717
  const retakeScreenshot = async () => {
716
- setPreviewOpen(false);
718
+ setPreviewIndex(null);
717
719
  setIsOpen(false);
718
- setScreenshot(null);
720
+ setScreenshots([]);
719
721
  await new Promise((r) => setTimeout(r, 300));
720
722
  try {
721
723
  const { default: html2canvas } = await import("html2canvas-pro");
@@ -725,7 +727,7 @@ function ReportDialog({
725
727
  useCORS: true,
726
728
  allowTaint: true
727
729
  });
728
- setScreenshot(canvas.toDataURL("image/jpeg", 0.6));
730
+ setScreenshots([canvas.toDataURL("image/jpeg", 0.6)]);
729
731
  } catch (e) {
730
732
  }
731
733
  setIsOpen(true);
@@ -744,7 +746,7 @@ function ReportDialog({
744
746
  useCORS: true,
745
747
  allowTaint: true
746
748
  });
747
- setScreenshot(canvas.toDataURL("image/jpeg", 0.6));
749
+ setScreenshots([canvas.toDataURL("image/jpeg", 0.6)]);
748
750
  } catch (e) {
749
751
  }
750
752
  setIsOpen(true);
@@ -767,13 +769,24 @@ function ReportDialog({
767
769
  if (!isOpen) return;
768
770
  const handleEsc = (e) => {
769
771
  if (e.key === "Escape") {
770
- if (previewOpen) setPreviewOpen(false);
772
+ if (previewIndex !== null) setPreviewIndex(null);
771
773
  else handleClose();
772
774
  }
773
775
  };
774
776
  document.addEventListener("keydown", handleEsc);
775
777
  return () => document.removeEventListener("keydown", handleEsc);
776
- }, [isOpen, previewOpen]);
778
+ }, [isOpen, previewIndex]);
779
+ useEffect(() => {
780
+ if (previewIndex === null) return;
781
+ const handlePointerDown = (e) => {
782
+ var _a;
783
+ if (!((_a = previewImgRef.current) == null ? void 0 : _a.contains(e.target))) {
784
+ setPreviewIndex(null);
785
+ }
786
+ };
787
+ document.addEventListener("pointerdown", handlePointerDown, true);
788
+ return () => document.removeEventListener("pointerdown", handlePointerDown, true);
789
+ }, [previewIndex]);
777
790
  useEffect(() => {
778
791
  if (!isOpen) return;
779
792
  const handlePaste = (e) => {
@@ -781,17 +794,18 @@ function ReportDialog({
781
794
  try {
782
795
  const items = (_a = e.clipboardData) == null ? void 0 : _a.items;
783
796
  if (!items) return;
797
+ let matched = false;
784
798
  for (let i = 0; i < items.length; i++) {
785
799
  if (items[i].type.startsWith("image/")) {
786
800
  const file = items[i].getAsFile();
787
801
  if (!file) continue;
802
+ matched = true;
788
803
  const reader = new FileReader();
789
- reader.onload = () => setScreenshot(reader.result);
804
+ reader.onload = () => setScreenshots((prev) => [...prev, reader.result]);
790
805
  reader.readAsDataURL(file);
791
- e.preventDefault();
792
- break;
793
806
  }
794
807
  }
808
+ if (matched) e.preventDefault();
795
809
  } catch (e2) {
796
810
  }
797
811
  };
@@ -853,8 +867,9 @@ function ReportDialog({
853
867
  let stream = null;
854
868
  try {
855
869
  stream = await navigator.mediaDevices.getUserMedia({ audio: true });
856
- } catch (e) {
857
- if (!SpeechRec) {
870
+ } catch (err) {
871
+ const isDenied = err instanceof DOMException && (err.name === "NotAllowedError" || err.name === "PermissionDeniedError");
872
+ if (!SpeechRec || isDenied) {
858
873
  setVoiceError("Microphone access denied");
859
874
  return;
860
875
  }
@@ -931,7 +946,8 @@ function ReportDialog({
931
946
  };
932
947
  recognition.onerror = (event) => {
933
948
  if (event.error === "aborted" || event.error === "no-speech") return;
934
- setVoiceError("Speech recognition not available \u2014 try Chrome");
949
+ const isDenied = event.error === "not-allowed" || event.error === "audio-capture";
950
+ setVoiceError(isDenied ? "Microphone access denied" : "Speech recognition not available \u2014 try Chrome");
935
951
  usingWebSpeechRef.current = false;
936
952
  recognitionRef.current = null;
937
953
  setIsListening(false);
@@ -943,35 +959,35 @@ function ReportDialog({
943
959
  }
944
960
  };
945
961
  const handleSpaceDown = (e) => {
962
+ var _a;
946
963
  if (e.code !== "Space") return;
947
- if (spaceTimerRef.current || isListening || isPushToTalkRef.current) {
964
+ if (isListening || isPushToTalkRef.current) {
948
965
  e.preventDefault();
949
966
  return;
950
967
  }
951
- if (e.repeat || !transcribeAudio2 || isTranscribing) return;
952
- e.preventDefault();
968
+ if (e.repeat || !transcribeAudio2 || isTranscribing || spaceTimerRef.current)
969
+ return;
970
+ const pos = (_a = e.currentTarget.selectionStart) != null ? _a : description.length;
953
971
  spaceTimerRef.current = setTimeout(() => {
972
+ spaceTimerRef.current = null;
954
973
  isPushToTalkRef.current = true;
974
+ setDescription(
975
+ (prev) => prev[pos] === " " ? prev.slice(0, pos) + prev.slice(pos + 1) : prev
976
+ );
977
+ requestAnimationFrame(() => {
978
+ if (textareaRef.current) {
979
+ textareaRef.current.selectionStart = pos;
980
+ textareaRef.current.selectionEnd = pos;
981
+ }
982
+ });
955
983
  void toggleVoice();
956
984
  }, 400);
957
985
  };
958
986
  const handleSpaceUp = (e) => {
959
- var _a;
960
987
  if (e.code !== "Space") return;
961
988
  if (spaceTimerRef.current) {
962
989
  clearTimeout(spaceTimerRef.current);
963
990
  spaceTimerRef.current = null;
964
- if (!isPushToTalkRef.current) {
965
- const ta = textareaRef.current;
966
- const pos = ta ? (_a = ta.selectionStart) != null ? _a : description.length : description.length;
967
- setDescription((prev) => prev.slice(0, pos) + " " + prev.slice(pos));
968
- requestAnimationFrame(() => {
969
- if (textareaRef.current) {
970
- textareaRef.current.selectionStart = pos + 1;
971
- textareaRef.current.selectionEnd = pos + 1;
972
- }
973
- });
974
- }
975
991
  }
976
992
  if (isPushToTalkRef.current) {
977
993
  isPushToTalkRef.current = false;
@@ -989,7 +1005,7 @@ function ReportDialog({
989
1005
  }
990
1006
  setIsSubmitting(true);
991
1007
  const metadata = {};
992
- if (screenshot) metadata.screenshot = screenshot;
1008
+ if (screenshots.length > 0) metadata.screenshots = JSON.stringify(screenshots);
993
1009
  if (showSeverity && reportType === "BUG") {
994
1010
  metadata.severity = severity;
995
1011
  }
@@ -1001,7 +1017,7 @@ function ReportDialog({
1001
1017
  if (result) {
1002
1018
  setSubmitted(true);
1003
1019
  setDescription("");
1004
- setScreenshot(null);
1020
+ setScreenshots([]);
1005
1021
  setTimeout(() => {
1006
1022
  setSubmitted(false);
1007
1023
  handleClose();
@@ -1040,7 +1056,7 @@ function ReportDialog({
1040
1056
  fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'
1041
1057
  },
1042
1058
  onClick: () => {
1043
- if (!previewOpen) handleClose();
1059
+ if (previewIndex === null) handleClose();
1044
1060
  },
1045
1061
  onPointerDown: (e) => e.stopPropagation(),
1046
1062
  onFocus: (e) => e.stopPropagation(),
@@ -1424,12 +1440,13 @@ function ReportDialog({
1424
1440
  {
1425
1441
  type: "button",
1426
1442
  onClick: async () => {
1443
+ var _a;
1427
1444
  if (!description.trim() || isEnhancing) return;
1428
1445
  setIsEnhancing(true);
1429
1446
  try {
1430
1447
  const polished = await enhanceText2(
1431
1448
  description,
1432
- screenshot
1449
+ (_a = screenshots[0]) != null ? _a : null
1433
1450
  );
1434
1451
  if (polished && polished !== description) {
1435
1452
  setOriginalDescription(description);
@@ -1695,7 +1712,7 @@ function ReportDialog({
1695
1712
  }
1696
1713
  ),
1697
1714
  /* @__PURE__ */ jsxs("div", { style: { marginTop: "10px" }, children: [
1698
- /* @__PURE__ */ jsx(
1715
+ /* @__PURE__ */ jsxs(
1699
1716
  "span",
1700
1717
  {
1701
1718
  style: {
@@ -1705,83 +1722,79 @@ function ReportDialog({
1705
1722
  display: "block",
1706
1723
  fontWeight: 500
1707
1724
  },
1708
- children: "Screenshot"
1725
+ children: [
1726
+ "Screenshots",
1727
+ screenshots.length > 0 ? ` (${screenshots.length})` : ""
1728
+ ]
1709
1729
  }
1710
1730
  ),
1711
- screenshot ? /* @__PURE__ */ jsxs("div", { children: [
1712
- /* @__PURE__ */ jsxs("div", { style: { position: "relative" }, children: [
1713
- /* @__PURE__ */ jsx(
1714
- "img",
1715
- {
1716
- src: screenshot,
1717
- alt: "Page screenshot",
1718
- onClick: () => setPreviewOpen(true),
1719
- style: {
1720
- width: "100%",
1721
- borderRadius: "6px",
1722
- border: `1px solid ${t.border}`,
1723
- maxHeight: "64px",
1724
- objectFit: "cover",
1725
- objectPosition: "top",
1726
- cursor: "zoom-in",
1727
- display: "block"
1728
- }
1729
- }
1730
- ),
1731
- /* @__PURE__ */ jsx(
1732
- "span",
1731
+ screenshots.length > 0 && /* @__PURE__ */ jsx(
1732
+ "div",
1733
+ {
1734
+ style: {
1735
+ display: "flex",
1736
+ flexWrap: "wrap",
1737
+ gap: "8px",
1738
+ marginBottom: "8px"
1739
+ },
1740
+ children: screenshots.map((src, i) => /* @__PURE__ */ jsxs(
1741
+ "div",
1733
1742
  {
1734
1743
  style: {
1735
- position: "absolute",
1736
- bottom: "5px",
1737
- left: "6px",
1738
- background: "rgba(0,0,0,0.55)",
1739
- color: "#fff",
1740
- fontSize: "10px",
1741
- padding: "1px 5px",
1742
- borderRadius: "3px",
1743
- pointerEvents: "none"
1744
+ position: "relative",
1745
+ width: "56px",
1746
+ height: "56px"
1744
1747
  },
1745
- children: "click to preview"
1746
- }
1747
- )
1748
- ] }),
1749
- /* @__PURE__ */ jsxs(
1750
- "div",
1751
- {
1752
- style: {
1753
- display: "flex",
1754
- gap: "6px",
1755
- marginTop: "6px"
1756
- },
1757
- children: [
1758
- /* @__PURE__ */ jsxs(
1759
- "button",
1760
- {
1761
- type: "button",
1762
- onClick: () => setScreenshot(null),
1763
- style: {
1764
- flex: 1,
1765
- padding: "6px 0",
1766
- borderRadius: "6px",
1767
- border: `1px solid ${t.inputBorder}`,
1768
- background: "transparent",
1769
- color: "#ef4444",
1770
- fontSize: "12px",
1771
- fontWeight: 500,
1772
- cursor: "pointer",
1773
- fontFamily: "inherit",
1774
- display: "flex",
1775
- alignItems: "center",
1776
- justifyContent: "center",
1777
- gap: "4px"
1778
- },
1779
- children: [
1780
- /* @__PURE__ */ jsx(
1748
+ children: [
1749
+ /* @__PURE__ */ jsx(
1750
+ "img",
1751
+ {
1752
+ src,
1753
+ alt: `Screenshot ${i + 1}`,
1754
+ onClick: () => setPreviewIndex(i),
1755
+ style: {
1756
+ width: "100%",
1757
+ height: "100%",
1758
+ borderRadius: "6px",
1759
+ border: `1px solid ${t.border}`,
1760
+ objectFit: "cover",
1761
+ objectPosition: "top",
1762
+ cursor: "zoom-in",
1763
+ display: "block"
1764
+ }
1765
+ }
1766
+ ),
1767
+ /* @__PURE__ */ jsx(
1768
+ "button",
1769
+ {
1770
+ type: "button",
1771
+ onClick: () => setScreenshots(
1772
+ (prev) => prev.filter((_, idx) => idx !== i)
1773
+ ),
1774
+ "aria-label": `Remove screenshot ${i + 1}`,
1775
+ style: {
1776
+ position: "absolute",
1777
+ top: "-6px",
1778
+ right: "-6px",
1779
+ width: "17px",
1780
+ height: "17px",
1781
+ borderRadius: "50%",
1782
+ border: "none",
1783
+ background: "#ef4444",
1784
+ color: "#fff",
1785
+ fontSize: "11px",
1786
+ lineHeight: 1,
1787
+ padding: 0,
1788
+ cursor: "pointer",
1789
+ display: "flex",
1790
+ alignItems: "center",
1791
+ justifyContent: "center"
1792
+ },
1793
+ children: /* @__PURE__ */ jsx(
1781
1794
  "svg",
1782
1795
  {
1783
- width: "11",
1784
- height: "11",
1796
+ width: "8",
1797
+ height: "8",
1785
1798
  viewBox: "0 0 10 10",
1786
1799
  fill: "none",
1787
1800
  children: /* @__PURE__ */ jsx(
@@ -1794,171 +1807,136 @@ function ReportDialog({
1794
1807
  }
1795
1808
  )
1796
1809
  }
1797
- ),
1798
- "Remove"
1799
- ]
1800
- }
1801
- ),
1802
- /* @__PURE__ */ jsxs(
1803
- "button",
1804
- {
1805
- type: "button",
1806
- onClick: () => {
1807
- var _a;
1808
- return (_a = fileInputRef.current) == null ? void 0 : _a.click();
1809
- },
1810
- style: {
1811
- flex: 1,
1812
- padding: "6px 0",
1813
- borderRadius: "6px",
1814
- border: `1px solid ${t.inputBorder}`,
1815
- background: "transparent",
1816
- color: t.textMuted,
1817
- fontSize: "12px",
1818
- fontWeight: 500,
1819
- cursor: "pointer",
1820
- fontFamily: "inherit",
1821
- display: "flex",
1822
- alignItems: "center",
1823
- justifyContent: "center",
1824
- gap: "4px"
1825
- },
1826
- children: [
1827
- /* @__PURE__ */ jsx(
1828
- "svg",
1829
- {
1830
- width: "11",
1831
- height: "11",
1832
- viewBox: "0 0 24 24",
1833
- fill: "none",
1834
- children: /* @__PURE__ */ jsx(
1835
- "path",
1836
- {
1837
- d: "M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4M17 8l-5-5-5 5M12 3v12",
1838
- stroke: "currentColor",
1839
- strokeWidth: "2",
1840
- strokeLinecap: "round",
1841
- strokeLinejoin: "round"
1842
- }
1843
- )
1844
- }
1845
- ),
1846
- "Replace"
1847
- ]
1848
- }
1849
- )
1850
- ]
1851
- }
1852
- )
1853
- ] }) : (
1854
- /* Drop zone */
1855
- /* @__PURE__ */ jsxs(
1856
- "div",
1857
- {
1858
- onClick: () => {
1859
- var _a;
1860
- return (_a = fileInputRef.current) == null ? void 0 : _a.click();
1810
+ )
1811
+ }
1812
+ )
1813
+ ]
1861
1814
  },
1862
- onDrop: (e) => {
1863
- e.preventDefault();
1864
- setScreenshotDragOver(false);
1865
- const file = Array.from(
1866
- e.dataTransfer.files
1867
- ).find((f) => f.type.startsWith("image/"));
1868
- if (!file) return;
1815
+ i
1816
+ ))
1817
+ }
1818
+ ),
1819
+ /* @__PURE__ */ jsx(
1820
+ "div",
1821
+ {
1822
+ onClick: () => {
1823
+ var _a;
1824
+ return (_a = fileInputRef.current) == null ? void 0 : _a.click();
1825
+ },
1826
+ onDrop: (e) => {
1827
+ e.preventDefault();
1828
+ setScreenshotDragOver(false);
1829
+ const files = Array.from(
1830
+ e.dataTransfer.files
1831
+ ).filter((f) => f.type.startsWith("image/"));
1832
+ files.forEach((file) => {
1869
1833
  const reader = new FileReader();
1870
- reader.onload = () => setScreenshot(reader.result);
1834
+ reader.onload = () => setScreenshots((prev) => [
1835
+ ...prev,
1836
+ reader.result
1837
+ ]);
1871
1838
  reader.readAsDataURL(file);
1872
- },
1873
- onDragOver: (e) => {
1874
- e.preventDefault();
1875
- setScreenshotDragOver(true);
1876
- },
1877
- onDragLeave: () => setScreenshotDragOver(false),
1878
- style: {
1879
- border: `1.5px dashed ${screenshotDragOver ? t.accent : t.inputBorder}`,
1880
- borderRadius: "8px",
1881
- padding: "18px 12px",
1882
- textAlign: "center",
1883
- cursor: "pointer",
1884
- background: screenshotDragOver ? isDark ? "rgba(34,211,238,0.06)" : "rgba(8,145,178,0.04)" : "transparent",
1885
- transition: "border-color 0.15s ease, background 0.15s ease"
1886
- },
1887
- children: [
1888
- /* @__PURE__ */ jsxs(
1889
- "svg",
1890
- {
1891
- width: "22",
1892
- height: "22",
1893
- viewBox: "0 0 24 24",
1894
- fill: "none",
1895
- style: {
1896
- margin: "0 auto 6px",
1897
- display: "block",
1898
- opacity: 0.45
1899
- },
1900
- children: [
1901
- /* @__PURE__ */ jsx(
1902
- "rect",
1903
- {
1904
- x: "3",
1905
- y: "3",
1906
- width: "18",
1907
- height: "18",
1908
- rx: "2",
1909
- stroke: t.textMuted,
1910
- strokeWidth: "1.5"
1911
- }
1912
- ),
1913
- /* @__PURE__ */ jsx(
1914
- "circle",
1915
- {
1916
- cx: "8.5",
1917
- cy: "8.5",
1918
- r: "1.5",
1919
- stroke: t.textMuted,
1920
- strokeWidth: "1.5"
1921
- }
1922
- ),
1923
- /* @__PURE__ */ jsx(
1924
- "path",
1925
- {
1926
- d: "M21 15l-5-5L5 21",
1927
- stroke: t.textMuted,
1928
- strokeWidth: "1.5",
1929
- strokeLinecap: "round",
1930
- strokeLinejoin: "round"
1931
- }
1932
- )
1933
- ]
1934
- }
1935
- ),
1936
- /* @__PURE__ */ jsx(
1937
- "p",
1938
- {
1939
- style: {
1940
- margin: 0,
1941
- fontSize: "12px",
1942
- fontWeight: 600,
1943
- color: t.text
1944
- },
1945
- children: screenshotDragOver ? "Drop to attach" : "Add a screenshot"
1946
- }
1947
- ),
1948
- /* @__PURE__ */ jsx(
1949
- "p",
1950
- {
1951
- style: {
1952
- margin: "3px 0 0",
1953
- fontSize: "11px",
1954
- color: t.textMuted
1955
- },
1956
- children: "Drag & drop \xB7 Paste \u2318V / Ctrl+V \xB7 Click to browse"
1957
- }
1958
- )
1959
- ]
1960
- }
1961
- )
1839
+ });
1840
+ },
1841
+ onDragOver: (e) => {
1842
+ e.preventDefault();
1843
+ setScreenshotDragOver(true);
1844
+ },
1845
+ onDragLeave: () => setScreenshotDragOver(false),
1846
+ style: {
1847
+ border: `1.5px dashed ${screenshotDragOver ? t.accent : t.inputBorder}`,
1848
+ borderRadius: "8px",
1849
+ padding: screenshots.length > 0 ? "8px 12px" : "18px 12px",
1850
+ textAlign: "center",
1851
+ cursor: "pointer",
1852
+ background: screenshotDragOver ? isDark ? "rgba(34,211,238,0.06)" : "rgba(8,145,178,0.04)" : "transparent",
1853
+ transition: "border-color 0.15s ease, background 0.15s ease"
1854
+ },
1855
+ children: screenshots.length > 0 ? /* @__PURE__ */ jsx(
1856
+ "p",
1857
+ {
1858
+ style: {
1859
+ margin: 0,
1860
+ fontSize: "11px",
1861
+ fontWeight: 500,
1862
+ color: t.textMuted
1863
+ },
1864
+ children: screenshotDragOver ? "Drop to attach" : "+ Add more \xB7 Drag & drop \xB7 Paste"
1865
+ }
1866
+ ) : /* @__PURE__ */ jsxs(Fragment, { children: [
1867
+ /* @__PURE__ */ jsxs(
1868
+ "svg",
1869
+ {
1870
+ width: "22",
1871
+ height: "22",
1872
+ viewBox: "0 0 24 24",
1873
+ fill: "none",
1874
+ style: {
1875
+ margin: "0 auto 6px",
1876
+ display: "block",
1877
+ opacity: 0.45
1878
+ },
1879
+ children: [
1880
+ /* @__PURE__ */ jsx(
1881
+ "rect",
1882
+ {
1883
+ x: "3",
1884
+ y: "3",
1885
+ width: "18",
1886
+ height: "18",
1887
+ rx: "2",
1888
+ stroke: t.textMuted,
1889
+ strokeWidth: "1.5"
1890
+ }
1891
+ ),
1892
+ /* @__PURE__ */ jsx(
1893
+ "circle",
1894
+ {
1895
+ cx: "8.5",
1896
+ cy: "8.5",
1897
+ r: "1.5",
1898
+ stroke: t.textMuted,
1899
+ strokeWidth: "1.5"
1900
+ }
1901
+ ),
1902
+ /* @__PURE__ */ jsx(
1903
+ "path",
1904
+ {
1905
+ d: "M21 15l-5-5L5 21",
1906
+ stroke: t.textMuted,
1907
+ strokeWidth: "1.5",
1908
+ strokeLinecap: "round",
1909
+ strokeLinejoin: "round"
1910
+ }
1911
+ )
1912
+ ]
1913
+ }
1914
+ ),
1915
+ /* @__PURE__ */ jsx(
1916
+ "p",
1917
+ {
1918
+ style: {
1919
+ margin: 0,
1920
+ fontSize: "12px",
1921
+ fontWeight: 600,
1922
+ color: t.text
1923
+ },
1924
+ children: screenshotDragOver ? "Drop to attach" : "Add screenshots"
1925
+ }
1926
+ ),
1927
+ /* @__PURE__ */ jsx(
1928
+ "p",
1929
+ {
1930
+ style: {
1931
+ margin: "3px 0 0",
1932
+ fontSize: "11px",
1933
+ color: t.textMuted
1934
+ },
1935
+ children: "Drag & drop \xB7 Paste \u2318V / Ctrl+V \xB7 Click to browse"
1936
+ }
1937
+ )
1938
+ ] })
1939
+ }
1962
1940
  )
1963
1941
  ] }),
1964
1942
  showSeverity && reportType === "BUG" && /* @__PURE__ */ jsxs("div", { style: { marginTop: "10px" }, children: [
@@ -2058,11 +2036,12 @@ function ReportDialog({
2058
2036
  ref: fileInputRef,
2059
2037
  type: "file",
2060
2038
  accept: "image/*",
2039
+ multiple: true,
2061
2040
  onChange: handleFileUpload,
2062
2041
  style: { display: "none" }
2063
2042
  }
2064
2043
  ),
2065
- previewOpen && screenshot && createPortal(
2044
+ previewIndex !== null && screenshots[previewIndex] && createPortal(
2066
2045
  /* @__PURE__ */ jsxs(
2067
2046
  "div",
2068
2047
  {
@@ -2080,14 +2059,15 @@ function ReportDialog({
2080
2059
  },
2081
2060
  onClick: (e) => {
2082
2061
  e.stopPropagation();
2083
- setPreviewOpen(false);
2062
+ setPreviewIndex(null);
2084
2063
  },
2085
2064
  onMouseDown: (e) => e.stopPropagation(),
2086
2065
  children: [
2087
2066
  /* @__PURE__ */ jsx(
2088
2067
  "img",
2089
2068
  {
2090
- src: screenshot,
2069
+ ref: previewImgRef,
2070
+ src: screenshots[previewIndex],
2091
2071
  alt: "Screenshot preview",
2092
2072
  onClick: (e) => e.stopPropagation(),
2093
2073
  style: {
@@ -2107,7 +2087,7 @@ function ReportDialog({
2107
2087
  color: t.textMuted,
2108
2088
  fontSize: "12px"
2109
2089
  },
2110
- children: "Click outside to close"
2090
+ children: screenshots.length > 1 ? `${previewIndex + 1} / ${screenshots.length} \xB7 Click outside to close` : "Click outside to close"
2111
2091
  }
2112
2092
  )
2113
2093
  ]