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.js CHANGED
@@ -707,10 +707,11 @@ function ReportDialog({
707
707
  const [description, setDescription] = (0, import_react.useState)("");
708
708
  const [isSubmitting, setIsSubmitting] = (0, import_react.useState)(false);
709
709
  const [submitted, setSubmitted] = (0, import_react.useState)(false);
710
- const [screenshot, setScreenshot] = (0, import_react.useState)(null);
711
- const [previewOpen, setPreviewOpen] = (0, import_react.useState)(false);
710
+ const [screenshots, setScreenshots] = (0, import_react.useState)([]);
711
+ const [previewIndex, setPreviewIndex] = (0, import_react.useState)(null);
712
712
  const fileInputRef = (0, import_react.useRef)(null);
713
713
  const modalRef = (0, import_react.useRef)(null);
714
+ const previewImgRef = (0, import_react.useRef)(null);
714
715
  const textareaRef = (0, import_react.useRef)(null);
715
716
  const recognitionRef = (0, import_react.useRef)(null);
716
717
  const voiceBaseRef = (0, import_react.useRef)("");
@@ -745,20 +746,21 @@ function ReportDialog({
745
746
  const isDark = useIsDark();
746
747
  const t = getTheme(isDark);
747
748
  const handleFileUpload = (e) => {
748
- var _a;
749
- const file = (_a = e.target.files) == null ? void 0 : _a[0];
750
- if (!file) return;
751
- const reader = new FileReader();
752
- reader.onload = () => {
753
- setScreenshot(reader.result);
754
- };
755
- reader.readAsDataURL(file);
749
+ const files = e.target.files;
750
+ if (!files || files.length === 0) return;
751
+ Array.from(files).forEach((file) => {
752
+ const reader = new FileReader();
753
+ reader.onload = () => {
754
+ setScreenshots((prev) => [...prev, reader.result]);
755
+ };
756
+ reader.readAsDataURL(file);
757
+ });
756
758
  e.target.value = "";
757
759
  };
758
760
  const retakeScreenshot = async () => {
759
- setPreviewOpen(false);
761
+ setPreviewIndex(null);
760
762
  setIsOpen(false);
761
- setScreenshot(null);
763
+ setScreenshots([]);
762
764
  await new Promise((r) => setTimeout(r, 300));
763
765
  try {
764
766
  const { default: html2canvas } = await import("html2canvas-pro");
@@ -768,7 +770,7 @@ function ReportDialog({
768
770
  useCORS: true,
769
771
  allowTaint: true
770
772
  });
771
- setScreenshot(canvas.toDataURL("image/jpeg", 0.6));
773
+ setScreenshots([canvas.toDataURL("image/jpeg", 0.6)]);
772
774
  } catch (e) {
773
775
  }
774
776
  setIsOpen(true);
@@ -787,7 +789,7 @@ function ReportDialog({
787
789
  useCORS: true,
788
790
  allowTaint: true
789
791
  });
790
- setScreenshot(canvas.toDataURL("image/jpeg", 0.6));
792
+ setScreenshots([canvas.toDataURL("image/jpeg", 0.6)]);
791
793
  } catch (e) {
792
794
  }
793
795
  setIsOpen(true);
@@ -810,13 +812,24 @@ function ReportDialog({
810
812
  if (!isOpen) return;
811
813
  const handleEsc = (e) => {
812
814
  if (e.key === "Escape") {
813
- if (previewOpen) setPreviewOpen(false);
815
+ if (previewIndex !== null) setPreviewIndex(null);
814
816
  else handleClose();
815
817
  }
816
818
  };
817
819
  document.addEventListener("keydown", handleEsc);
818
820
  return () => document.removeEventListener("keydown", handleEsc);
819
- }, [isOpen, previewOpen]);
821
+ }, [isOpen, previewIndex]);
822
+ (0, import_react.useEffect)(() => {
823
+ if (previewIndex === null) return;
824
+ const handlePointerDown = (e) => {
825
+ var _a;
826
+ if (!((_a = previewImgRef.current) == null ? void 0 : _a.contains(e.target))) {
827
+ setPreviewIndex(null);
828
+ }
829
+ };
830
+ document.addEventListener("pointerdown", handlePointerDown, true);
831
+ return () => document.removeEventListener("pointerdown", handlePointerDown, true);
832
+ }, [previewIndex]);
820
833
  (0, import_react.useEffect)(() => {
821
834
  if (!isOpen) return;
822
835
  const handlePaste = (e) => {
@@ -824,17 +837,18 @@ function ReportDialog({
824
837
  try {
825
838
  const items = (_a = e.clipboardData) == null ? void 0 : _a.items;
826
839
  if (!items) return;
840
+ let matched = false;
827
841
  for (let i = 0; i < items.length; i++) {
828
842
  if (items[i].type.startsWith("image/")) {
829
843
  const file = items[i].getAsFile();
830
844
  if (!file) continue;
845
+ matched = true;
831
846
  const reader = new FileReader();
832
- reader.onload = () => setScreenshot(reader.result);
847
+ reader.onload = () => setScreenshots((prev) => [...prev, reader.result]);
833
848
  reader.readAsDataURL(file);
834
- e.preventDefault();
835
- break;
836
849
  }
837
850
  }
851
+ if (matched) e.preventDefault();
838
852
  } catch (e2) {
839
853
  }
840
854
  };
@@ -896,8 +910,9 @@ function ReportDialog({
896
910
  let stream = null;
897
911
  try {
898
912
  stream = await navigator.mediaDevices.getUserMedia({ audio: true });
899
- } catch (e) {
900
- if (!SpeechRec) {
913
+ } catch (err) {
914
+ const isDenied = err instanceof DOMException && (err.name === "NotAllowedError" || err.name === "PermissionDeniedError");
915
+ if (!SpeechRec || isDenied) {
901
916
  setVoiceError("Microphone access denied");
902
917
  return;
903
918
  }
@@ -974,7 +989,8 @@ function ReportDialog({
974
989
  };
975
990
  recognition.onerror = (event) => {
976
991
  if (event.error === "aborted" || event.error === "no-speech") return;
977
- setVoiceError("Speech recognition not available \u2014 try Chrome");
992
+ const isDenied = event.error === "not-allowed" || event.error === "audio-capture";
993
+ setVoiceError(isDenied ? "Microphone access denied" : "Speech recognition not available \u2014 try Chrome");
978
994
  usingWebSpeechRef.current = false;
979
995
  recognitionRef.current = null;
980
996
  setIsListening(false);
@@ -986,35 +1002,35 @@ function ReportDialog({
986
1002
  }
987
1003
  };
988
1004
  const handleSpaceDown = (e) => {
1005
+ var _a;
989
1006
  if (e.code !== "Space") return;
990
- if (spaceTimerRef.current || isListening || isPushToTalkRef.current) {
1007
+ if (isListening || isPushToTalkRef.current) {
991
1008
  e.preventDefault();
992
1009
  return;
993
1010
  }
994
- if (e.repeat || !transcribeAudio2 || isTranscribing) return;
995
- e.preventDefault();
1011
+ if (e.repeat || !transcribeAudio2 || isTranscribing || spaceTimerRef.current)
1012
+ return;
1013
+ const pos = (_a = e.currentTarget.selectionStart) != null ? _a : description.length;
996
1014
  spaceTimerRef.current = setTimeout(() => {
1015
+ spaceTimerRef.current = null;
997
1016
  isPushToTalkRef.current = true;
1017
+ setDescription(
1018
+ (prev) => prev[pos] === " " ? prev.slice(0, pos) + prev.slice(pos + 1) : prev
1019
+ );
1020
+ requestAnimationFrame(() => {
1021
+ if (textareaRef.current) {
1022
+ textareaRef.current.selectionStart = pos;
1023
+ textareaRef.current.selectionEnd = pos;
1024
+ }
1025
+ });
998
1026
  void toggleVoice();
999
1027
  }, 400);
1000
1028
  };
1001
1029
  const handleSpaceUp = (e) => {
1002
- var _a;
1003
1030
  if (e.code !== "Space") return;
1004
1031
  if (spaceTimerRef.current) {
1005
1032
  clearTimeout(spaceTimerRef.current);
1006
1033
  spaceTimerRef.current = null;
1007
- if (!isPushToTalkRef.current) {
1008
- const ta = textareaRef.current;
1009
- const pos = ta ? (_a = ta.selectionStart) != null ? _a : description.length : description.length;
1010
- setDescription((prev) => prev.slice(0, pos) + " " + prev.slice(pos));
1011
- requestAnimationFrame(() => {
1012
- if (textareaRef.current) {
1013
- textareaRef.current.selectionStart = pos + 1;
1014
- textareaRef.current.selectionEnd = pos + 1;
1015
- }
1016
- });
1017
- }
1018
1034
  }
1019
1035
  if (isPushToTalkRef.current) {
1020
1036
  isPushToTalkRef.current = false;
@@ -1032,7 +1048,7 @@ function ReportDialog({
1032
1048
  }
1033
1049
  setIsSubmitting(true);
1034
1050
  const metadata = {};
1035
- if (screenshot) metadata.screenshot = screenshot;
1051
+ if (screenshots.length > 0) metadata.screenshots = JSON.stringify(screenshots);
1036
1052
  if (showSeverity && reportType === "BUG") {
1037
1053
  metadata.severity = severity;
1038
1054
  }
@@ -1044,7 +1060,7 @@ function ReportDialog({
1044
1060
  if (result) {
1045
1061
  setSubmitted(true);
1046
1062
  setDescription("");
1047
- setScreenshot(null);
1063
+ setScreenshots([]);
1048
1064
  setTimeout(() => {
1049
1065
  setSubmitted(false);
1050
1066
  handleClose();
@@ -1083,7 +1099,7 @@ function ReportDialog({
1083
1099
  fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'
1084
1100
  },
1085
1101
  onClick: () => {
1086
- if (!previewOpen) handleClose();
1102
+ if (previewIndex === null) handleClose();
1087
1103
  },
1088
1104
  onPointerDown: (e) => e.stopPropagation(),
1089
1105
  onFocus: (e) => e.stopPropagation(),
@@ -1467,12 +1483,13 @@ function ReportDialog({
1467
1483
  {
1468
1484
  type: "button",
1469
1485
  onClick: async () => {
1486
+ var _a;
1470
1487
  if (!description.trim() || isEnhancing) return;
1471
1488
  setIsEnhancing(true);
1472
1489
  try {
1473
1490
  const polished = await enhanceText2(
1474
1491
  description,
1475
- screenshot
1492
+ (_a = screenshots[0]) != null ? _a : null
1476
1493
  );
1477
1494
  if (polished && polished !== description) {
1478
1495
  setOriginalDescription(description);
@@ -1738,7 +1755,7 @@ function ReportDialog({
1738
1755
  }
1739
1756
  ),
1740
1757
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { marginTop: "10px" }, children: [
1741
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1758
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1742
1759
  "span",
1743
1760
  {
1744
1761
  style: {
@@ -1748,83 +1765,79 @@ function ReportDialog({
1748
1765
  display: "block",
1749
1766
  fontWeight: 500
1750
1767
  },
1751
- children: "Screenshot"
1768
+ children: [
1769
+ "Screenshots",
1770
+ screenshots.length > 0 ? ` (${screenshots.length})` : ""
1771
+ ]
1752
1772
  }
1753
1773
  ),
1754
- screenshot ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
1755
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { position: "relative" }, children: [
1756
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1757
- "img",
1758
- {
1759
- src: screenshot,
1760
- alt: "Page screenshot",
1761
- onClick: () => setPreviewOpen(true),
1762
- style: {
1763
- width: "100%",
1764
- borderRadius: "6px",
1765
- border: `1px solid ${t.border}`,
1766
- maxHeight: "64px",
1767
- objectFit: "cover",
1768
- objectPosition: "top",
1769
- cursor: "zoom-in",
1770
- display: "block"
1771
- }
1772
- }
1773
- ),
1774
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1775
- "span",
1774
+ screenshots.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1775
+ "div",
1776
+ {
1777
+ style: {
1778
+ display: "flex",
1779
+ flexWrap: "wrap",
1780
+ gap: "8px",
1781
+ marginBottom: "8px"
1782
+ },
1783
+ children: screenshots.map((src, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1784
+ "div",
1776
1785
  {
1777
1786
  style: {
1778
- position: "absolute",
1779
- bottom: "5px",
1780
- left: "6px",
1781
- background: "rgba(0,0,0,0.55)",
1782
- color: "#fff",
1783
- fontSize: "10px",
1784
- padding: "1px 5px",
1785
- borderRadius: "3px",
1786
- pointerEvents: "none"
1787
+ position: "relative",
1788
+ width: "56px",
1789
+ height: "56px"
1787
1790
  },
1788
- children: "click to preview"
1789
- }
1790
- )
1791
- ] }),
1792
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1793
- "div",
1794
- {
1795
- style: {
1796
- display: "flex",
1797
- gap: "6px",
1798
- marginTop: "6px"
1799
- },
1800
- children: [
1801
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1802
- "button",
1803
- {
1804
- type: "button",
1805
- onClick: () => setScreenshot(null),
1806
- style: {
1807
- flex: 1,
1808
- padding: "6px 0",
1809
- borderRadius: "6px",
1810
- border: `1px solid ${t.inputBorder}`,
1811
- background: "transparent",
1812
- color: "#ef4444",
1813
- fontSize: "12px",
1814
- fontWeight: 500,
1815
- cursor: "pointer",
1816
- fontFamily: "inherit",
1817
- display: "flex",
1818
- alignItems: "center",
1819
- justifyContent: "center",
1820
- gap: "4px"
1821
- },
1822
- children: [
1823
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1791
+ children: [
1792
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1793
+ "img",
1794
+ {
1795
+ src,
1796
+ alt: `Screenshot ${i + 1}`,
1797
+ onClick: () => setPreviewIndex(i),
1798
+ style: {
1799
+ width: "100%",
1800
+ height: "100%",
1801
+ borderRadius: "6px",
1802
+ border: `1px solid ${t.border}`,
1803
+ objectFit: "cover",
1804
+ objectPosition: "top",
1805
+ cursor: "zoom-in",
1806
+ display: "block"
1807
+ }
1808
+ }
1809
+ ),
1810
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1811
+ "button",
1812
+ {
1813
+ type: "button",
1814
+ onClick: () => setScreenshots(
1815
+ (prev) => prev.filter((_, idx) => idx !== i)
1816
+ ),
1817
+ "aria-label": `Remove screenshot ${i + 1}`,
1818
+ style: {
1819
+ position: "absolute",
1820
+ top: "-6px",
1821
+ right: "-6px",
1822
+ width: "17px",
1823
+ height: "17px",
1824
+ borderRadius: "50%",
1825
+ border: "none",
1826
+ background: "#ef4444",
1827
+ color: "#fff",
1828
+ fontSize: "11px",
1829
+ lineHeight: 1,
1830
+ padding: 0,
1831
+ cursor: "pointer",
1832
+ display: "flex",
1833
+ alignItems: "center",
1834
+ justifyContent: "center"
1835
+ },
1836
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1824
1837
  "svg",
1825
1838
  {
1826
- width: "11",
1827
- height: "11",
1839
+ width: "8",
1840
+ height: "8",
1828
1841
  viewBox: "0 0 10 10",
1829
1842
  fill: "none",
1830
1843
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
@@ -1837,171 +1850,136 @@ function ReportDialog({
1837
1850
  }
1838
1851
  )
1839
1852
  }
1840
- ),
1841
- "Remove"
1842
- ]
1843
- }
1844
- ),
1845
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1846
- "button",
1847
- {
1848
- type: "button",
1849
- onClick: () => {
1850
- var _a;
1851
- return (_a = fileInputRef.current) == null ? void 0 : _a.click();
1852
- },
1853
- style: {
1854
- flex: 1,
1855
- padding: "6px 0",
1856
- borderRadius: "6px",
1857
- border: `1px solid ${t.inputBorder}`,
1858
- background: "transparent",
1859
- color: t.textMuted,
1860
- fontSize: "12px",
1861
- fontWeight: 500,
1862
- cursor: "pointer",
1863
- fontFamily: "inherit",
1864
- display: "flex",
1865
- alignItems: "center",
1866
- justifyContent: "center",
1867
- gap: "4px"
1868
- },
1869
- children: [
1870
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1871
- "svg",
1872
- {
1873
- width: "11",
1874
- height: "11",
1875
- viewBox: "0 0 24 24",
1876
- fill: "none",
1877
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1878
- "path",
1879
- {
1880
- d: "M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4M17 8l-5-5-5 5M12 3v12",
1881
- stroke: "currentColor",
1882
- strokeWidth: "2",
1883
- strokeLinecap: "round",
1884
- strokeLinejoin: "round"
1885
- }
1886
- )
1887
- }
1888
- ),
1889
- "Replace"
1890
- ]
1891
- }
1892
- )
1893
- ]
1894
- }
1895
- )
1896
- ] }) : (
1897
- /* Drop zone */
1898
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1899
- "div",
1900
- {
1901
- onClick: () => {
1902
- var _a;
1903
- return (_a = fileInputRef.current) == null ? void 0 : _a.click();
1853
+ )
1854
+ }
1855
+ )
1856
+ ]
1904
1857
  },
1905
- onDrop: (e) => {
1906
- e.preventDefault();
1907
- setScreenshotDragOver(false);
1908
- const file = Array.from(
1909
- e.dataTransfer.files
1910
- ).find((f) => f.type.startsWith("image/"));
1911
- if (!file) return;
1858
+ i
1859
+ ))
1860
+ }
1861
+ ),
1862
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1863
+ "div",
1864
+ {
1865
+ onClick: () => {
1866
+ var _a;
1867
+ return (_a = fileInputRef.current) == null ? void 0 : _a.click();
1868
+ },
1869
+ onDrop: (e) => {
1870
+ e.preventDefault();
1871
+ setScreenshotDragOver(false);
1872
+ const files = Array.from(
1873
+ e.dataTransfer.files
1874
+ ).filter((f) => f.type.startsWith("image/"));
1875
+ files.forEach((file) => {
1912
1876
  const reader = new FileReader();
1913
- reader.onload = () => setScreenshot(reader.result);
1877
+ reader.onload = () => setScreenshots((prev) => [
1878
+ ...prev,
1879
+ reader.result
1880
+ ]);
1914
1881
  reader.readAsDataURL(file);
1915
- },
1916
- onDragOver: (e) => {
1917
- e.preventDefault();
1918
- setScreenshotDragOver(true);
1919
- },
1920
- onDragLeave: () => setScreenshotDragOver(false),
1921
- style: {
1922
- border: `1.5px dashed ${screenshotDragOver ? t.accent : t.inputBorder}`,
1923
- borderRadius: "8px",
1924
- padding: "18px 12px",
1925
- textAlign: "center",
1926
- cursor: "pointer",
1927
- background: screenshotDragOver ? isDark ? "rgba(34,211,238,0.06)" : "rgba(8,145,178,0.04)" : "transparent",
1928
- transition: "border-color 0.15s ease, background 0.15s ease"
1929
- },
1930
- children: [
1931
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1932
- "svg",
1933
- {
1934
- width: "22",
1935
- height: "22",
1936
- viewBox: "0 0 24 24",
1937
- fill: "none",
1938
- style: {
1939
- margin: "0 auto 6px",
1940
- display: "block",
1941
- opacity: 0.45
1942
- },
1943
- children: [
1944
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1945
- "rect",
1946
- {
1947
- x: "3",
1948
- y: "3",
1949
- width: "18",
1950
- height: "18",
1951
- rx: "2",
1952
- stroke: t.textMuted,
1953
- strokeWidth: "1.5"
1954
- }
1955
- ),
1956
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1957
- "circle",
1958
- {
1959
- cx: "8.5",
1960
- cy: "8.5",
1961
- r: "1.5",
1962
- stroke: t.textMuted,
1963
- strokeWidth: "1.5"
1964
- }
1965
- ),
1966
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1967
- "path",
1968
- {
1969
- d: "M21 15l-5-5L5 21",
1970
- stroke: t.textMuted,
1971
- strokeWidth: "1.5",
1972
- strokeLinecap: "round",
1973
- strokeLinejoin: "round"
1974
- }
1975
- )
1976
- ]
1977
- }
1978
- ),
1979
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1980
- "p",
1981
- {
1982
- style: {
1983
- margin: 0,
1984
- fontSize: "12px",
1985
- fontWeight: 600,
1986
- color: t.text
1987
- },
1988
- children: screenshotDragOver ? "Drop to attach" : "Add a screenshot"
1989
- }
1990
- ),
1991
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1992
- "p",
1993
- {
1994
- style: {
1995
- margin: "3px 0 0",
1996
- fontSize: "11px",
1997
- color: t.textMuted
1998
- },
1999
- children: "Drag & drop \xB7 Paste \u2318V / Ctrl+V \xB7 Click to browse"
2000
- }
2001
- )
2002
- ]
2003
- }
2004
- )
1882
+ });
1883
+ },
1884
+ onDragOver: (e) => {
1885
+ e.preventDefault();
1886
+ setScreenshotDragOver(true);
1887
+ },
1888
+ onDragLeave: () => setScreenshotDragOver(false),
1889
+ style: {
1890
+ border: `1.5px dashed ${screenshotDragOver ? t.accent : t.inputBorder}`,
1891
+ borderRadius: "8px",
1892
+ padding: screenshots.length > 0 ? "8px 12px" : "18px 12px",
1893
+ textAlign: "center",
1894
+ cursor: "pointer",
1895
+ background: screenshotDragOver ? isDark ? "rgba(34,211,238,0.06)" : "rgba(8,145,178,0.04)" : "transparent",
1896
+ transition: "border-color 0.15s ease, background 0.15s ease"
1897
+ },
1898
+ children: screenshots.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1899
+ "p",
1900
+ {
1901
+ style: {
1902
+ margin: 0,
1903
+ fontSize: "11px",
1904
+ fontWeight: 500,
1905
+ color: t.textMuted
1906
+ },
1907
+ children: screenshotDragOver ? "Drop to attach" : "+ Add more \xB7 Drag & drop \xB7 Paste"
1908
+ }
1909
+ ) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1910
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1911
+ "svg",
1912
+ {
1913
+ width: "22",
1914
+ height: "22",
1915
+ viewBox: "0 0 24 24",
1916
+ fill: "none",
1917
+ style: {
1918
+ margin: "0 auto 6px",
1919
+ display: "block",
1920
+ opacity: 0.45
1921
+ },
1922
+ children: [
1923
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1924
+ "rect",
1925
+ {
1926
+ x: "3",
1927
+ y: "3",
1928
+ width: "18",
1929
+ height: "18",
1930
+ rx: "2",
1931
+ stroke: t.textMuted,
1932
+ strokeWidth: "1.5"
1933
+ }
1934
+ ),
1935
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1936
+ "circle",
1937
+ {
1938
+ cx: "8.5",
1939
+ cy: "8.5",
1940
+ r: "1.5",
1941
+ stroke: t.textMuted,
1942
+ strokeWidth: "1.5"
1943
+ }
1944
+ ),
1945
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1946
+ "path",
1947
+ {
1948
+ d: "M21 15l-5-5L5 21",
1949
+ stroke: t.textMuted,
1950
+ strokeWidth: "1.5",
1951
+ strokeLinecap: "round",
1952
+ strokeLinejoin: "round"
1953
+ }
1954
+ )
1955
+ ]
1956
+ }
1957
+ ),
1958
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1959
+ "p",
1960
+ {
1961
+ style: {
1962
+ margin: 0,
1963
+ fontSize: "12px",
1964
+ fontWeight: 600,
1965
+ color: t.text
1966
+ },
1967
+ children: screenshotDragOver ? "Drop to attach" : "Add screenshots"
1968
+ }
1969
+ ),
1970
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1971
+ "p",
1972
+ {
1973
+ style: {
1974
+ margin: "3px 0 0",
1975
+ fontSize: "11px",
1976
+ color: t.textMuted
1977
+ },
1978
+ children: "Drag & drop \xB7 Paste \u2318V / Ctrl+V \xB7 Click to browse"
1979
+ }
1980
+ )
1981
+ ] })
1982
+ }
2005
1983
  )
2006
1984
  ] }),
2007
1985
  showSeverity && reportType === "BUG" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { marginTop: "10px" }, children: [
@@ -2101,11 +2079,12 @@ function ReportDialog({
2101
2079
  ref: fileInputRef,
2102
2080
  type: "file",
2103
2081
  accept: "image/*",
2082
+ multiple: true,
2104
2083
  onChange: handleFileUpload,
2105
2084
  style: { display: "none" }
2106
2085
  }
2107
2086
  ),
2108
- previewOpen && screenshot && (0, import_react_dom.createPortal)(
2087
+ previewIndex !== null && screenshots[previewIndex] && (0, import_react_dom.createPortal)(
2109
2088
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
2110
2089
  "div",
2111
2090
  {
@@ -2123,14 +2102,15 @@ function ReportDialog({
2123
2102
  },
2124
2103
  onClick: (e) => {
2125
2104
  e.stopPropagation();
2126
- setPreviewOpen(false);
2105
+ setPreviewIndex(null);
2127
2106
  },
2128
2107
  onMouseDown: (e) => e.stopPropagation(),
2129
2108
  children: [
2130
2109
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2131
2110
  "img",
2132
2111
  {
2133
- src: screenshot,
2112
+ ref: previewImgRef,
2113
+ src: screenshots[previewIndex],
2134
2114
  alt: "Screenshot preview",
2135
2115
  onClick: (e) => e.stopPropagation(),
2136
2116
  style: {
@@ -2150,7 +2130,7 @@ function ReportDialog({
2150
2130
  color: t.textMuted,
2151
2131
  fontSize: "12px"
2152
2132
  },
2153
- children: "Click outside to close"
2133
+ children: screenshots.length > 1 ? `${previewIndex + 1} / ${screenshots.length} \xB7 Click outside to close` : "Click outside to close"
2154
2134
  }
2155
2135
  )
2156
2136
  ]