glitchgrab 1.19.2 → 1.21.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
@@ -90,7 +90,10 @@ function interceptFetch() {
90
90
  const method = (_a = init == null ? void 0 : init.method) != null ? _a : "GET";
91
91
  const start = Date.now();
92
92
  try {
93
- const response = await origFetch.apply(window, [input, init]);
93
+ const response = await origFetch(
94
+ input,
95
+ init
96
+ );
94
97
  addBreadcrumb("api", `${method} ${url.slice(0, 100)} \u2192 ${response.status}`, {
95
98
  method,
96
99
  status: String(response.status),
@@ -269,6 +272,30 @@ async function sendReport(payload, baseUrl) {
269
272
  return null;
270
273
  }
271
274
  }
275
+ async function enhanceText(text, token, baseUrl) {
276
+ var _a;
277
+ try {
278
+ const trimmed = text.trim();
279
+ if (!trimmed) return text;
280
+ const url = `${baseUrl != null ? baseUrl : DEFAULT_BASE_URL}/api/v1/ai/enhance-text`;
281
+ const response = await fetch(url, {
282
+ method: "POST",
283
+ headers: {
284
+ "Content-Type": "application/json",
285
+ Authorization: `Bearer ${token}`
286
+ },
287
+ body: JSON.stringify({ text: trimmed })
288
+ });
289
+ if (!response.ok) return text;
290
+ const envelope = await response.json();
291
+ if ((envelope == null ? void 0 : envelope.success) && typeof ((_a = envelope.data) == null ? void 0 : _a.text) === "string") {
292
+ return envelope.data.text;
293
+ }
294
+ return text;
295
+ } catch (e) {
296
+ return text;
297
+ }
298
+ }
272
299
 
273
300
  // src/dedup.ts
274
301
  var DEFAULT_WINDOW_MS = 5 * 60 * 1e3;
@@ -540,9 +567,12 @@ function isLowQualityText(text) {
540
567
  }
541
568
  function ReportDialog({
542
569
  report,
570
+ enhanceText: enhanceText2,
543
571
  types,
544
572
  showSeverity = true
545
573
  }) {
574
+ const [isEnhancing, setIsEnhancing] = useState(false);
575
+ const [screenshotDragOver, setScreenshotDragOver] = useState(false);
546
576
  const [mounted, setMounted] = useState(false);
547
577
  useEffect(() => {
548
578
  setMounted(true);
@@ -884,38 +914,221 @@ function ReportDialog({
884
914
  type
885
915
  )) }),
886
916
  step === 2 && /* @__PURE__ */ jsxs(Fragment, { children: [
887
- /* @__PURE__ */ jsx(
888
- "textarea",
889
- {
890
- value: description,
891
- onChange: (e) => {
892
- setDescription(e.target.value);
893
- if (validationError) setValidationError(null);
894
- },
895
- placeholder: getPlaceholder(reportType),
896
- style: {
897
- width: "100%",
898
- minHeight: "100px",
899
- padding: "10px 12px",
900
- borderRadius: "8px",
901
- border: `1px solid ${t.inputBorder}`,
902
- fontSize: "14px",
903
- fontFamily: "inherit",
904
- resize: "vertical",
905
- outline: "none",
906
- boxSizing: "border-box",
907
- color: t.text,
908
- backgroundColor: t.inputBg
909
- },
910
- onFocus: (e) => {
911
- e.target.style.borderColor = t.accent;
912
- },
913
- onBlur: (e) => {
914
- e.target.style.borderColor = t.inputBorder;
915
- },
916
- autoFocus: true
917
- }
918
- ),
917
+ /* @__PURE__ */ jsxs("div", { style: { position: "relative" }, children: [
918
+ /* @__PURE__ */ jsx(
919
+ "textarea",
920
+ {
921
+ value: description,
922
+ onChange: (e) => {
923
+ setDescription(e.target.value);
924
+ if (validationError) setValidationError(null);
925
+ },
926
+ placeholder: getPlaceholder(reportType),
927
+ style: {
928
+ width: "100%",
929
+ minHeight: "100px",
930
+ padding: enhanceText2 ? "28px 12px 10px" : "10px 12px",
931
+ borderRadius: "8px",
932
+ border: `1px solid ${t.inputBorder}`,
933
+ fontSize: "14px",
934
+ fontFamily: "inherit",
935
+ resize: "vertical",
936
+ outline: "none",
937
+ boxSizing: "border-box",
938
+ color: t.text,
939
+ backgroundColor: t.inputBg
940
+ },
941
+ onFocus: (e) => {
942
+ e.target.style.borderColor = t.accent;
943
+ },
944
+ onBlur: (e) => {
945
+ e.target.style.borderColor = t.inputBorder;
946
+ },
947
+ autoFocus: true
948
+ }
949
+ ),
950
+ enhanceText2 && /* @__PURE__ */ jsxs(
951
+ "button",
952
+ {
953
+ type: "button",
954
+ onClick: async () => {
955
+ if (!description.trim() || isEnhancing) return;
956
+ setIsEnhancing(true);
957
+ try {
958
+ const polished = await enhanceText2(description);
959
+ if (polished && polished !== description) {
960
+ setDescription(polished);
961
+ if (validationError) setValidationError(null);
962
+ }
963
+ } finally {
964
+ setIsEnhancing(false);
965
+ }
966
+ },
967
+ disabled: !description.trim() || isEnhancing,
968
+ style: {
969
+ position: "absolute",
970
+ top: "6px",
971
+ right: "6px",
972
+ display: "inline-flex",
973
+ alignItems: "center",
974
+ gap: "4px",
975
+ background: t.inputBg,
976
+ border: "none",
977
+ padding: "2px 6px",
978
+ borderRadius: "4px",
979
+ fontFamily: "inherit",
980
+ fontSize: "11px",
981
+ color: t.textMuted,
982
+ cursor: !description.trim() || isEnhancing ? "default" : "pointer",
983
+ opacity: !description.trim() ? 0.5 : 1,
984
+ zIndex: 1
985
+ },
986
+ title: "Polish grammar \u2014 preserves your meaning",
987
+ children: [
988
+ /* @__PURE__ */ jsx("svg", { width: "11", height: "11", viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { 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", stroke: t.textMuted, strokeWidth: "1.5", strokeLinejoin: "round" }) }),
989
+ isEnhancing ? "Enhancing..." : "AI enhance"
990
+ ]
991
+ }
992
+ )
993
+ ] }),
994
+ /* @__PURE__ */ jsxs("div", { style: { marginTop: "10px" }, children: [
995
+ /* @__PURE__ */ jsx("span", { style: { fontSize: "12px", color: t.textMuted, marginBottom: "6px", display: "block", fontWeight: 500 }, children: "Screenshot" }),
996
+ screenshot ? /* @__PURE__ */ jsxs("div", { children: [
997
+ /* @__PURE__ */ jsxs("div", { style: { position: "relative" }, children: [
998
+ /* @__PURE__ */ jsx(
999
+ "img",
1000
+ {
1001
+ src: screenshot,
1002
+ alt: "Page screenshot",
1003
+ onClick: () => setPreviewOpen(true),
1004
+ style: {
1005
+ width: "100%",
1006
+ borderRadius: "6px",
1007
+ border: `1px solid ${t.border}`,
1008
+ maxHeight: "64px",
1009
+ objectFit: "cover",
1010
+ objectPosition: "top",
1011
+ cursor: "zoom-in",
1012
+ display: "block"
1013
+ }
1014
+ }
1015
+ ),
1016
+ /* @__PURE__ */ jsx("span", { style: {
1017
+ position: "absolute",
1018
+ bottom: "5px",
1019
+ left: "6px",
1020
+ background: "rgba(0,0,0,0.55)",
1021
+ color: "#fff",
1022
+ fontSize: "10px",
1023
+ padding: "1px 5px",
1024
+ borderRadius: "3px",
1025
+ pointerEvents: "none"
1026
+ }, children: "click to preview" })
1027
+ ] }),
1028
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "6px", marginTop: "6px" }, children: [
1029
+ /* @__PURE__ */ jsxs(
1030
+ "button",
1031
+ {
1032
+ type: "button",
1033
+ onClick: () => setScreenshot(null),
1034
+ style: {
1035
+ flex: 1,
1036
+ padding: "6px 0",
1037
+ borderRadius: "6px",
1038
+ border: `1px solid ${t.inputBorder}`,
1039
+ background: "transparent",
1040
+ color: "#ef4444",
1041
+ fontSize: "12px",
1042
+ fontWeight: 500,
1043
+ cursor: "pointer",
1044
+ fontFamily: "inherit",
1045
+ display: "flex",
1046
+ alignItems: "center",
1047
+ justifyContent: "center",
1048
+ gap: "4px"
1049
+ },
1050
+ children: [
1051
+ /* @__PURE__ */ jsx("svg", { width: "11", height: "11", viewBox: "0 0 10 10", fill: "none", children: /* @__PURE__ */ jsx("path", { d: "M1 1L9 9M9 1L1 9", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }) }),
1052
+ "Remove"
1053
+ ]
1054
+ }
1055
+ ),
1056
+ /* @__PURE__ */ jsxs(
1057
+ "button",
1058
+ {
1059
+ type: "button",
1060
+ onClick: () => {
1061
+ var _a;
1062
+ return (_a = fileInputRef.current) == null ? void 0 : _a.click();
1063
+ },
1064
+ style: {
1065
+ flex: 1,
1066
+ padding: "6px 0",
1067
+ borderRadius: "6px",
1068
+ border: `1px solid ${t.inputBorder}`,
1069
+ background: "transparent",
1070
+ color: t.textMuted,
1071
+ fontSize: "12px",
1072
+ fontWeight: 500,
1073
+ cursor: "pointer",
1074
+ fontFamily: "inherit",
1075
+ display: "flex",
1076
+ alignItems: "center",
1077
+ justifyContent: "center",
1078
+ gap: "4px"
1079
+ },
1080
+ children: [
1081
+ /* @__PURE__ */ jsx("svg", { width: "11", height: "11", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx("path", { d: "M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4M17 8l-5-5-5 5M12 3v12", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }),
1082
+ "Replace"
1083
+ ]
1084
+ }
1085
+ )
1086
+ ] })
1087
+ ] }) : (
1088
+ /* Drop zone */
1089
+ /* @__PURE__ */ jsxs(
1090
+ "div",
1091
+ {
1092
+ onClick: () => {
1093
+ var _a;
1094
+ return (_a = fileInputRef.current) == null ? void 0 : _a.click();
1095
+ },
1096
+ onDrop: (e) => {
1097
+ e.preventDefault();
1098
+ setScreenshotDragOver(false);
1099
+ const file = Array.from(e.dataTransfer.files).find((f) => f.type.startsWith("image/"));
1100
+ if (!file) return;
1101
+ const reader = new FileReader();
1102
+ reader.onload = () => setScreenshot(reader.result);
1103
+ reader.readAsDataURL(file);
1104
+ },
1105
+ onDragOver: (e) => {
1106
+ e.preventDefault();
1107
+ setScreenshotDragOver(true);
1108
+ },
1109
+ onDragLeave: () => setScreenshotDragOver(false),
1110
+ style: {
1111
+ border: `1.5px dashed ${screenshotDragOver ? t.accent : t.inputBorder}`,
1112
+ borderRadius: "8px",
1113
+ padding: "18px 12px",
1114
+ textAlign: "center",
1115
+ cursor: "pointer",
1116
+ background: screenshotDragOver ? isDark ? "rgba(34,211,238,0.06)" : "rgba(8,145,178,0.04)" : "transparent",
1117
+ transition: "border-color 0.15s ease, background 0.15s ease"
1118
+ },
1119
+ children: [
1120
+ /* @__PURE__ */ jsxs("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", style: { margin: "0 auto 6px", display: "block", opacity: 0.45 }, children: [
1121
+ /* @__PURE__ */ jsx("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2", stroke: t.textMuted, strokeWidth: "1.5" }),
1122
+ /* @__PURE__ */ jsx("circle", { cx: "8.5", cy: "8.5", r: "1.5", stroke: t.textMuted, strokeWidth: "1.5" }),
1123
+ /* @__PURE__ */ jsx("path", { d: "M21 15l-5-5L5 21", stroke: t.textMuted, strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" })
1124
+ ] }),
1125
+ /* @__PURE__ */ jsx("p", { style: { margin: 0, fontSize: "12px", fontWeight: 600, color: t.text }, children: screenshotDragOver ? "Drop to attach" : "Add a screenshot" }),
1126
+ /* @__PURE__ */ jsx("p", { style: { margin: "3px 0 0", fontSize: "11px", color: t.textMuted }, children: "Drag & drop \xB7 Paste \u2318V / Ctrl+V \xB7 Click to browse" })
1127
+ ]
1128
+ }
1129
+ )
1130
+ )
1131
+ ] }),
919
1132
  showSeverity && reportType === "BUG" && /* @__PURE__ */ jsxs("div", { style: { marginTop: "10px" }, children: [
920
1133
  /* @__PURE__ */ jsx("span", { style: { fontSize: "12px", color: t.textMuted, marginBottom: "6px", display: "block" }, children: "Severity" }),
921
1134
  /* @__PURE__ */ jsx("div", { style: { display: "flex", gap: "6px" }, children: ["low", "medium", "high"].map((s) => /* @__PURE__ */ jsx(
@@ -1017,91 +1230,23 @@ function ReportDialog({
1017
1230
  overflow: "hidden",
1018
1231
  lineHeight: "1.4"
1019
1232
  }, children: description.length > 200 ? description.slice(0, 200) + "..." : description }),
1020
- screenshot && /* @__PURE__ */ jsxs("div", { style: { marginBottom: "10px", position: "relative" }, children: [
1021
- /* @__PURE__ */ jsx(
1022
- "img",
1023
- {
1024
- src: screenshot,
1025
- alt: "Page screenshot",
1026
- onClick: () => setPreviewOpen(true),
1027
- style: {
1028
- width: "100%",
1029
- borderRadius: "6px",
1030
- border: `1px solid ${t.border}`,
1031
- maxHeight: "80px",
1032
- objectFit: "cover",
1033
- objectPosition: "top",
1034
- cursor: "pointer"
1035
- }
1036
- }
1037
- ),
1038
- /* @__PURE__ */ jsx(
1039
- "button",
1040
- {
1041
- type: "button",
1042
- onClick: () => setScreenshot(null),
1043
- style: {
1044
- position: "absolute",
1045
- top: "4px",
1046
- right: "4px",
1047
- background: "rgba(0,0,0,0.6)",
1048
- border: "none",
1049
- borderRadius: "50%",
1050
- width: "20px",
1051
- height: "20px",
1052
- cursor: "pointer",
1053
- display: "flex",
1054
- alignItems: "center",
1055
- justifyContent: "center"
1056
- },
1057
- "aria-label": "Remove screenshot",
1058
- children: /* @__PURE__ */ jsx("svg", { width: "10", height: "10", viewBox: "0 0 10 10", fill: "none", children: /* @__PURE__ */ jsx("path", { d: "M1 1L9 9M9 1L1 9", stroke: "#fff", strokeWidth: "1.5", strokeLinecap: "round" }) })
1059
- }
1060
- )
1061
- ] }),
1062
- !screenshot && /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "6px", marginBottom: "10px" }, children: [
1063
- /* @__PURE__ */ jsx(
1064
- "button",
1065
- {
1066
- type: "button",
1067
- onClick: () => {
1068
- var _a;
1069
- return (_a = fileInputRef.current) == null ? void 0 : _a.click();
1070
- },
1071
- style: {
1072
- flex: 1,
1073
- padding: "8px",
1074
- borderRadius: "6px",
1075
- border: `1px dashed ${t.inputBorder}`,
1076
- textAlign: "center",
1077
- fontSize: "11px",
1078
- color: t.textMuted,
1079
- background: "none",
1080
- cursor: "pointer"
1081
- },
1082
- children: "Upload or paste (\u2318V)"
1083
- }
1084
- ),
1085
- /* @__PURE__ */ jsx(
1086
- "button",
1087
- {
1088
- type: "button",
1089
- onClick: retakeScreenshot,
1090
- style: {
1091
- flex: 1,
1092
- padding: "8px",
1093
- borderRadius: "6px",
1094
- border: `1px dashed ${t.inputBorder}`,
1095
- textAlign: "center",
1096
- fontSize: "11px",
1097
- color: t.textMuted,
1098
- background: "none",
1099
- cursor: "pointer"
1100
- },
1101
- children: "Retake screenshot"
1233
+ screenshot && /* @__PURE__ */ jsx("div", { style: { marginBottom: "10px", position: "relative" }, children: /* @__PURE__ */ jsx(
1234
+ "img",
1235
+ {
1236
+ src: screenshot,
1237
+ alt: "Page screenshot",
1238
+ onClick: () => setPreviewOpen(true),
1239
+ style: {
1240
+ width: "100%",
1241
+ borderRadius: "6px",
1242
+ border: `1px solid ${t.border}`,
1243
+ maxHeight: "80px",
1244
+ objectFit: "cover",
1245
+ objectPosition: "top",
1246
+ cursor: "pointer"
1102
1247
  }
1103
- )
1104
- ] }),
1248
+ }
1249
+ ) }),
1105
1250
  /* @__PURE__ */ jsx(
1106
1251
  "button",
1107
1252
  {
@@ -1333,14 +1478,25 @@ function GlitchgrabProviderInner({
1333
1478
  try {
1334
1479
  if (typeof window === "undefined") return;
1335
1480
  if (process.env.NODE_ENV === "development") return;
1481
+ const EXTENSION_ORIGIN_RE = /\b(?:chrome|safari|moz)-extension:\/\//;
1336
1482
  const handleError = (event) => {
1337
- var _a, _b, _c, _d, _e, _f;
1483
+ var _a, _b, _c, _d, _e, _f, _g;
1338
1484
  try {
1485
+ const isOpaqueCrossOrigin = event.message === "Script error." || event.message === "Script error" || !event.error && !event.filename;
1486
+ if (isOpaqueCrossOrigin) {
1487
+ return;
1488
+ }
1489
+ if (event.message === "ResizeObserver loop completed with undelivered notifications." || event.message === "ResizeObserver loop limit exceeded") {
1490
+ return;
1491
+ }
1492
+ if (event.filename && EXTENSION_ORIGIN_RE.test(event.filename) || ((_a = event.error) == null ? void 0 : _a.stack) && EXTENSION_ORIGIN_RE.test(event.error.stack)) {
1493
+ return;
1494
+ }
1339
1495
  const context = captureContext(visitedPagesRef.current);
1340
1496
  const sig = computeSignature({
1341
1497
  errorMessage: event.message,
1342
1498
  pageUrl: context.url,
1343
- errorStack: (_a = event.error) == null ? void 0 : _a.stack
1499
+ errorStack: (_b = event.error) == null ? void 0 : _b.stack
1344
1500
  });
1345
1501
  if (shouldSkipDuplicate(sig)) return;
1346
1502
  const payload = {
@@ -1348,17 +1504,17 @@ function GlitchgrabProviderInner({
1348
1504
  source: "SDK_AUTO",
1349
1505
  type: "BUG",
1350
1506
  errorMessage: event.message,
1351
- errorStack: (_b = event.error) == null ? void 0 : _b.stack,
1507
+ errorStack: (_c = event.error) == null ? void 0 : _c.stack,
1352
1508
  pageUrl: context.url,
1353
1509
  userAgent: context.userAgent,
1354
1510
  breadcrumbs: context.breadcrumbs,
1355
- deviceInfo: (_c = context.deviceInfo) != null ? _c : void 0,
1511
+ deviceInfo: (_d = context.deviceInfo) != null ? _d : void 0,
1356
1512
  metadata: {
1357
1513
  timestamp: context.timestamp,
1358
1514
  visitedPages: JSON.stringify(context.visitedPages),
1359
- filename: (_d = event.filename) != null ? _d : "",
1360
- lineno: String((_e = event.lineno) != null ? _e : ""),
1361
- colno: String((_f = event.colno) != null ? _f : ""),
1515
+ filename: (_e = event.filename) != null ? _e : "",
1516
+ lineno: String((_f = event.lineno) != null ? _f : ""),
1517
+ colno: String((_g = event.colno) != null ? _g : ""),
1362
1518
  ...(session == null ? void 0 : session.userId) ? { sessionUserId: session.userId } : {},
1363
1519
  ...(session == null ? void 0 : session.name) ? { sessionUserName: String(session.name) } : {},
1364
1520
  ...(session == null ? void 0 : session.email) ? { sessionUserEmail: String(session.email) } : {},
@@ -1378,7 +1534,11 @@ function GlitchgrabProviderInner({
1378
1534
  const context = captureContext(visitedPagesRef.current);
1379
1535
  const reason = event.reason;
1380
1536
  const errMsg = reason instanceof Error ? reason.message : String(reason);
1537
+ if (errMsg === "Script error." || errMsg === "Script error") {
1538
+ return;
1539
+ }
1381
1540
  const errStack = reason instanceof Error ? reason.stack : void 0;
1541
+ if (errStack && EXTENSION_ORIGIN_RE.test(errStack)) return;
1382
1542
  const sig = computeSignature({
1383
1543
  errorMessage: errMsg,
1384
1544
  pageUrl: context.url,
@@ -1464,6 +1624,16 @@ function GlitchgrabProviderInner({
1464
1624
  },
1465
1625
  []
1466
1626
  );
1627
+ const enhance = useCallback(
1628
+ async (text) => {
1629
+ try {
1630
+ return await enhanceText(text, token, baseUrl);
1631
+ } catch (e) {
1632
+ return text;
1633
+ }
1634
+ },
1635
+ [token, baseUrl]
1636
+ );
1467
1637
  const openReportDialog = useCallback((options) => {
1468
1638
  if (typeof window !== "undefined") {
1469
1639
  window.dispatchEvent(new CustomEvent("glitchgrab:open-report", { detail: options }));
@@ -1492,7 +1662,8 @@ function GlitchgrabProviderInner({
1492
1662
  reportBug,
1493
1663
  report,
1494
1664
  addBreadcrumb: addBreadcrumb2,
1495
- openReportDialog
1665
+ openReportDialog,
1666
+ enhanceText: enhance
1496
1667
  },
1497
1668
  children: [
1498
1669
  /* @__PURE__ */ jsx2(
@@ -1507,7 +1678,7 @@ function GlitchgrabProviderInner({
1507
1678
  children
1508
1679
  }
1509
1680
  ),
1510
- /* @__PURE__ */ jsx2(ReportDialog, { report, types, showSeverity })
1681
+ /* @__PURE__ */ jsx2(ReportDialog, { report, enhanceText: enhance, types, showSeverity })
1511
1682
  ]
1512
1683
  }
1513
1684
  );
@@ -1732,6 +1903,7 @@ export {
1732
1903
  clearBreadcrumbs,
1733
1904
  clearDedupCache,
1734
1905
  computeSignature,
1906
+ enhanceText,
1735
1907
  fetchGlitchgrabReports,
1736
1908
  getBreadcrumbs,
1737
1909
  initBreadcrumbs,