glitchgrab 1.23.1 → 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
  };
@@ -988,35 +1002,35 @@ function ReportDialog({
988
1002
  }
989
1003
  };
990
1004
  const handleSpaceDown = (e) => {
1005
+ var _a;
991
1006
  if (e.code !== "Space") return;
992
- if (spaceTimerRef.current || isListening || isPushToTalkRef.current) {
1007
+ if (isListening || isPushToTalkRef.current) {
993
1008
  e.preventDefault();
994
1009
  return;
995
1010
  }
996
- if (e.repeat || !transcribeAudio2 || isTranscribing) return;
997
- e.preventDefault();
1011
+ if (e.repeat || !transcribeAudio2 || isTranscribing || spaceTimerRef.current)
1012
+ return;
1013
+ const pos = (_a = e.currentTarget.selectionStart) != null ? _a : description.length;
998
1014
  spaceTimerRef.current = setTimeout(() => {
1015
+ spaceTimerRef.current = null;
999
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
+ });
1000
1026
  void toggleVoice();
1001
1027
  }, 400);
1002
1028
  };
1003
1029
  const handleSpaceUp = (e) => {
1004
- var _a;
1005
1030
  if (e.code !== "Space") return;
1006
1031
  if (spaceTimerRef.current) {
1007
1032
  clearTimeout(spaceTimerRef.current);
1008
1033
  spaceTimerRef.current = null;
1009
- if (!isPushToTalkRef.current) {
1010
- const ta = textareaRef.current;
1011
- const pos = ta ? (_a = ta.selectionStart) != null ? _a : description.length : description.length;
1012
- setDescription((prev) => prev.slice(0, pos) + " " + prev.slice(pos));
1013
- requestAnimationFrame(() => {
1014
- if (textareaRef.current) {
1015
- textareaRef.current.selectionStart = pos + 1;
1016
- textareaRef.current.selectionEnd = pos + 1;
1017
- }
1018
- });
1019
- }
1020
1034
  }
1021
1035
  if (isPushToTalkRef.current) {
1022
1036
  isPushToTalkRef.current = false;
@@ -1034,7 +1048,7 @@ function ReportDialog({
1034
1048
  }
1035
1049
  setIsSubmitting(true);
1036
1050
  const metadata = {};
1037
- if (screenshot) metadata.screenshot = screenshot;
1051
+ if (screenshots.length > 0) metadata.screenshots = JSON.stringify(screenshots);
1038
1052
  if (showSeverity && reportType === "BUG") {
1039
1053
  metadata.severity = severity;
1040
1054
  }
@@ -1046,7 +1060,7 @@ function ReportDialog({
1046
1060
  if (result) {
1047
1061
  setSubmitted(true);
1048
1062
  setDescription("");
1049
- setScreenshot(null);
1063
+ setScreenshots([]);
1050
1064
  setTimeout(() => {
1051
1065
  setSubmitted(false);
1052
1066
  handleClose();
@@ -1085,7 +1099,7 @@ function ReportDialog({
1085
1099
  fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'
1086
1100
  },
1087
1101
  onClick: () => {
1088
- if (!previewOpen) handleClose();
1102
+ if (previewIndex === null) handleClose();
1089
1103
  },
1090
1104
  onPointerDown: (e) => e.stopPropagation(),
1091
1105
  onFocus: (e) => e.stopPropagation(),
@@ -1469,12 +1483,13 @@ function ReportDialog({
1469
1483
  {
1470
1484
  type: "button",
1471
1485
  onClick: async () => {
1486
+ var _a;
1472
1487
  if (!description.trim() || isEnhancing) return;
1473
1488
  setIsEnhancing(true);
1474
1489
  try {
1475
1490
  const polished = await enhanceText2(
1476
1491
  description,
1477
- screenshot
1492
+ (_a = screenshots[0]) != null ? _a : null
1478
1493
  );
1479
1494
  if (polished && polished !== description) {
1480
1495
  setOriginalDescription(description);
@@ -1740,7 +1755,7 @@ function ReportDialog({
1740
1755
  }
1741
1756
  ),
1742
1757
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { marginTop: "10px" }, children: [
1743
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1758
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1744
1759
  "span",
1745
1760
  {
1746
1761
  style: {
@@ -1750,83 +1765,79 @@ function ReportDialog({
1750
1765
  display: "block",
1751
1766
  fontWeight: 500
1752
1767
  },
1753
- children: "Screenshot"
1768
+ children: [
1769
+ "Screenshots",
1770
+ screenshots.length > 0 ? ` (${screenshots.length})` : ""
1771
+ ]
1754
1772
  }
1755
1773
  ),
1756
- screenshot ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
1757
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { position: "relative" }, children: [
1758
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1759
- "img",
1760
- {
1761
- src: screenshot,
1762
- alt: "Page screenshot",
1763
- onClick: () => setPreviewOpen(true),
1764
- style: {
1765
- width: "100%",
1766
- borderRadius: "6px",
1767
- border: `1px solid ${t.border}`,
1768
- maxHeight: "64px",
1769
- objectFit: "cover",
1770
- objectPosition: "top",
1771
- cursor: "zoom-in",
1772
- display: "block"
1773
- }
1774
- }
1775
- ),
1776
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1777
- "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",
1778
1785
  {
1779
1786
  style: {
1780
- position: "absolute",
1781
- bottom: "5px",
1782
- left: "6px",
1783
- background: "rgba(0,0,0,0.55)",
1784
- color: "#fff",
1785
- fontSize: "10px",
1786
- padding: "1px 5px",
1787
- borderRadius: "3px",
1788
- pointerEvents: "none"
1787
+ position: "relative",
1788
+ width: "56px",
1789
+ height: "56px"
1789
1790
  },
1790
- children: "click to preview"
1791
- }
1792
- )
1793
- ] }),
1794
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1795
- "div",
1796
- {
1797
- style: {
1798
- display: "flex",
1799
- gap: "6px",
1800
- marginTop: "6px"
1801
- },
1802
- children: [
1803
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1804
- "button",
1805
- {
1806
- type: "button",
1807
- onClick: () => setScreenshot(null),
1808
- style: {
1809
- flex: 1,
1810
- padding: "6px 0",
1811
- borderRadius: "6px",
1812
- border: `1px solid ${t.inputBorder}`,
1813
- background: "transparent",
1814
- color: "#ef4444",
1815
- fontSize: "12px",
1816
- fontWeight: 500,
1817
- cursor: "pointer",
1818
- fontFamily: "inherit",
1819
- display: "flex",
1820
- alignItems: "center",
1821
- justifyContent: "center",
1822
- gap: "4px"
1823
- },
1824
- children: [
1825
- /* @__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)(
1826
1837
  "svg",
1827
1838
  {
1828
- width: "11",
1829
- height: "11",
1839
+ width: "8",
1840
+ height: "8",
1830
1841
  viewBox: "0 0 10 10",
1831
1842
  fill: "none",
1832
1843
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
@@ -1839,171 +1850,136 @@ function ReportDialog({
1839
1850
  }
1840
1851
  )
1841
1852
  }
1842
- ),
1843
- "Remove"
1844
- ]
1845
- }
1846
- ),
1847
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1848
- "button",
1849
- {
1850
- type: "button",
1851
- onClick: () => {
1852
- var _a;
1853
- return (_a = fileInputRef.current) == null ? void 0 : _a.click();
1854
- },
1855
- style: {
1856
- flex: 1,
1857
- padding: "6px 0",
1858
- borderRadius: "6px",
1859
- border: `1px solid ${t.inputBorder}`,
1860
- background: "transparent",
1861
- color: t.textMuted,
1862
- fontSize: "12px",
1863
- fontWeight: 500,
1864
- cursor: "pointer",
1865
- fontFamily: "inherit",
1866
- display: "flex",
1867
- alignItems: "center",
1868
- justifyContent: "center",
1869
- gap: "4px"
1870
- },
1871
- children: [
1872
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1873
- "svg",
1874
- {
1875
- width: "11",
1876
- height: "11",
1877
- viewBox: "0 0 24 24",
1878
- fill: "none",
1879
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1880
- "path",
1881
- {
1882
- d: "M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4M17 8l-5-5-5 5M12 3v12",
1883
- stroke: "currentColor",
1884
- strokeWidth: "2",
1885
- strokeLinecap: "round",
1886
- strokeLinejoin: "round"
1887
- }
1888
- )
1889
- }
1890
- ),
1891
- "Replace"
1892
- ]
1893
- }
1894
- )
1895
- ]
1896
- }
1897
- )
1898
- ] }) : (
1899
- /* Drop zone */
1900
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1901
- "div",
1902
- {
1903
- onClick: () => {
1904
- var _a;
1905
- return (_a = fileInputRef.current) == null ? void 0 : _a.click();
1853
+ )
1854
+ }
1855
+ )
1856
+ ]
1906
1857
  },
1907
- onDrop: (e) => {
1908
- e.preventDefault();
1909
- setScreenshotDragOver(false);
1910
- const file = Array.from(
1911
- e.dataTransfer.files
1912
- ).find((f) => f.type.startsWith("image/"));
1913
- 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) => {
1914
1876
  const reader = new FileReader();
1915
- reader.onload = () => setScreenshot(reader.result);
1877
+ reader.onload = () => setScreenshots((prev) => [
1878
+ ...prev,
1879
+ reader.result
1880
+ ]);
1916
1881
  reader.readAsDataURL(file);
1917
- },
1918
- onDragOver: (e) => {
1919
- e.preventDefault();
1920
- setScreenshotDragOver(true);
1921
- },
1922
- onDragLeave: () => setScreenshotDragOver(false),
1923
- style: {
1924
- border: `1.5px dashed ${screenshotDragOver ? t.accent : t.inputBorder}`,
1925
- borderRadius: "8px",
1926
- padding: "18px 12px",
1927
- textAlign: "center",
1928
- cursor: "pointer",
1929
- background: screenshotDragOver ? isDark ? "rgba(34,211,238,0.06)" : "rgba(8,145,178,0.04)" : "transparent",
1930
- transition: "border-color 0.15s ease, background 0.15s ease"
1931
- },
1932
- children: [
1933
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1934
- "svg",
1935
- {
1936
- width: "22",
1937
- height: "22",
1938
- viewBox: "0 0 24 24",
1939
- fill: "none",
1940
- style: {
1941
- margin: "0 auto 6px",
1942
- display: "block",
1943
- opacity: 0.45
1944
- },
1945
- children: [
1946
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1947
- "rect",
1948
- {
1949
- x: "3",
1950
- y: "3",
1951
- width: "18",
1952
- height: "18",
1953
- rx: "2",
1954
- stroke: t.textMuted,
1955
- strokeWidth: "1.5"
1956
- }
1957
- ),
1958
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1959
- "circle",
1960
- {
1961
- cx: "8.5",
1962
- cy: "8.5",
1963
- r: "1.5",
1964
- stroke: t.textMuted,
1965
- strokeWidth: "1.5"
1966
- }
1967
- ),
1968
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1969
- "path",
1970
- {
1971
- d: "M21 15l-5-5L5 21",
1972
- stroke: t.textMuted,
1973
- strokeWidth: "1.5",
1974
- strokeLinecap: "round",
1975
- strokeLinejoin: "round"
1976
- }
1977
- )
1978
- ]
1979
- }
1980
- ),
1981
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1982
- "p",
1983
- {
1984
- style: {
1985
- margin: 0,
1986
- fontSize: "12px",
1987
- fontWeight: 600,
1988
- color: t.text
1989
- },
1990
- children: screenshotDragOver ? "Drop to attach" : "Add a screenshot"
1991
- }
1992
- ),
1993
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1994
- "p",
1995
- {
1996
- style: {
1997
- margin: "3px 0 0",
1998
- fontSize: "11px",
1999
- color: t.textMuted
2000
- },
2001
- children: "Drag & drop \xB7 Paste \u2318V / Ctrl+V \xB7 Click to browse"
2002
- }
2003
- )
2004
- ]
2005
- }
2006
- )
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
+ }
2007
1983
  )
2008
1984
  ] }),
2009
1985
  showSeverity && reportType === "BUG" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { marginTop: "10px" }, children: [
@@ -2103,11 +2079,12 @@ function ReportDialog({
2103
2079
  ref: fileInputRef,
2104
2080
  type: "file",
2105
2081
  accept: "image/*",
2082
+ multiple: true,
2106
2083
  onChange: handleFileUpload,
2107
2084
  style: { display: "none" }
2108
2085
  }
2109
2086
  ),
2110
- previewOpen && screenshot && (0, import_react_dom.createPortal)(
2087
+ previewIndex !== null && screenshots[previewIndex] && (0, import_react_dom.createPortal)(
2111
2088
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
2112
2089
  "div",
2113
2090
  {
@@ -2125,14 +2102,15 @@ function ReportDialog({
2125
2102
  },
2126
2103
  onClick: (e) => {
2127
2104
  e.stopPropagation();
2128
- setPreviewOpen(false);
2105
+ setPreviewIndex(null);
2129
2106
  },
2130
2107
  onMouseDown: (e) => e.stopPropagation(),
2131
2108
  children: [
2132
2109
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2133
2110
  "img",
2134
2111
  {
2135
- src: screenshot,
2112
+ ref: previewImgRef,
2113
+ src: screenshots[previewIndex],
2136
2114
  alt: "Screenshot preview",
2137
2115
  onClick: (e) => e.stopPropagation(),
2138
2116
  style: {
@@ -2152,7 +2130,7 @@ function ReportDialog({
2152
2130
  color: t.textMuted,
2153
2131
  fontSize: "12px"
2154
2132
  },
2155
- children: "Click outside to close"
2133
+ children: screenshots.length > 1 ? `${previewIndex + 1} / ${screenshots.length} \xB7 Click outside to close` : "Click outside to close"
2156
2134
  }
2157
2135
  )
2158
2136
  ]