glitchgrab 1.42.0 → 1.44.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,11 +4,11 @@
4
4
  import {
5
5
  createContext,
6
6
  useContext,
7
- useRef as useRef4,
8
- useEffect as useEffect5,
7
+ useRef as useRef5,
8
+ useEffect as useEffect6,
9
9
  useCallback as useCallback2,
10
- useMemo as useMemo2,
11
- useState as useState5
10
+ useMemo as useMemo3,
11
+ useState as useState6
12
12
  } from "react";
13
13
 
14
14
  // src/error-boundary.tsx
@@ -127,21 +127,84 @@ function interceptConsole() {
127
127
  origError.apply(console, args);
128
128
  };
129
129
  }
130
+ var CONSOLE_TEXT_MAX = 200;
130
131
  function argsToString(args) {
131
132
  try {
132
133
  return args.map((a) => {
133
- if (typeof a === "string") return a;
134
+ if (typeof a === "string") return a.slice(0, CONSOLE_TEXT_MAX);
134
135
  if (a instanceof Error) return a.message;
135
136
  try {
136
- return JSON.stringify(a);
137
+ return previewJson(a, CONSOLE_TEXT_MAX);
137
138
  } catch (e) {
138
139
  return String(a);
139
140
  }
140
- }).join(" ").slice(0, 200);
141
+ }).join(" ").slice(0, CONSOLE_TEXT_MAX);
141
142
  } catch (e) {
142
143
  return "[unknown]";
143
144
  }
144
145
  }
146
+ function previewJson(value, max) {
147
+ let out = "";
148
+ const path = /* @__PURE__ */ new WeakSet();
149
+ const write = (input, inArray) => {
150
+ if (out.length > max) return;
151
+ let v = input;
152
+ if (v !== null && typeof v === "object" && typeof v.toJSON === "function") {
153
+ v = v.toJSON();
154
+ }
155
+ if (v === null) {
156
+ out += "null";
157
+ return;
158
+ }
159
+ switch (typeof v) {
160
+ case "string":
161
+ out += JSON.stringify(v.length > max ? v.slice(0, max) : v);
162
+ return;
163
+ case "number":
164
+ out += Number.isFinite(v) ? String(v) : "null";
165
+ return;
166
+ case "boolean":
167
+ case "bigint":
168
+ out += String(v);
169
+ return;
170
+ case "object":
171
+ break;
172
+ default:
173
+ if (inArray) out += "null";
174
+ return;
175
+ }
176
+ const obj = v;
177
+ if (path.has(obj)) {
178
+ out += '"[Circular]"';
179
+ return;
180
+ }
181
+ path.add(obj);
182
+ if (Array.isArray(obj)) {
183
+ out += "[";
184
+ for (let i = 0; i < obj.length && out.length <= max; i++) {
185
+ if (i > 0) out += ",";
186
+ write(obj[i], true);
187
+ }
188
+ out += "]";
189
+ } else {
190
+ out += "{";
191
+ let first = true;
192
+ for (const key in obj) {
193
+ if (out.length > max) break;
194
+ if (!Object.prototype.hasOwnProperty.call(obj, key)) continue;
195
+ const child = obj[key];
196
+ if (child === void 0 || typeof child === "function" || typeof child === "symbol") continue;
197
+ out += `${first ? "" : ","}${JSON.stringify(key)}:`;
198
+ first = false;
199
+ write(child, false);
200
+ }
201
+ out += "}";
202
+ }
203
+ path.delete(obj);
204
+ };
205
+ write(value, false);
206
+ return out;
207
+ }
145
208
  async function readErrorBody(response, url) {
146
209
  try {
147
210
  if (response.ok) return "";
@@ -258,15 +321,27 @@ function interceptNavigation() {
258
321
  addBreadcrumb("navigation", `Back/Forward to ${window.location.pathname}`);
259
322
  });
260
323
  }
324
+ var CLICK_TEXT_MAX = 50;
325
+ var CLICK_TEXT_NODE_LIMIT = 500;
326
+ function leadingText(el, max) {
327
+ var _a;
328
+ let text = "";
329
+ let visited = 0;
330
+ const walker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT);
331
+ while (text.trimStart().length < max && visited++ < CLICK_TEXT_NODE_LIMIT && walker.nextNode()) {
332
+ text += (_a = walker.currentNode.nodeValue) != null ? _a : "";
333
+ }
334
+ return text.trim().slice(0, max);
335
+ }
261
336
  function interceptClicks() {
262
337
  document.addEventListener(
263
338
  "click",
264
339
  (e) => {
265
- var _a, _b, _c;
340
+ var _a;
266
341
  try {
267
342
  const target = e.target;
268
343
  const tag = (_a = target.tagName) == null ? void 0 : _a.toLowerCase();
269
- const text = (_c = (_b = target.textContent) == null ? void 0 : _b.trim().slice(0, 50)) != null ? _c : "";
344
+ const text = target instanceof Node ? leadingText(target, CLICK_TEXT_MAX) : "";
270
345
  const id = target.id ? `#${target.id}` : "";
271
346
  const cls = target.className && typeof target.className === "string" ? `.${target.className.split(" ")[0]}` : "";
272
347
  addBreadcrumb("click", `Click ${tag}${id}${cls} "${text}"`);
@@ -559,7 +634,11 @@ async function sendFeedback(payload, baseUrl) {
559
634
  return null;
560
635
  }
561
636
  }
562
- async function enhanceText(text, token, baseUrl, screenshot, context) {
637
+ function signedFields(identity) {
638
+ if (!(identity == null ? void 0 : identity.signature) || !identity.userId) return {};
639
+ return { userId: identity.userId, signature: identity.signature };
640
+ }
641
+ async function enhanceText(text, token, baseUrl, screenshot, context, identity) {
563
642
  var _a;
564
643
  try {
565
644
  const trimmed = text.trim();
@@ -574,7 +653,8 @@ async function enhanceText(text, token, baseUrl, screenshot, context) {
574
653
  body: JSON.stringify({
575
654
  text: trimmed,
576
655
  ...screenshot ? { screenshot } : {},
577
- ...context ? { context } : {}
656
+ ...context ? { context } : {},
657
+ ...signedFields(identity)
578
658
  })
579
659
  });
580
660
  if (!response.ok) return text;
@@ -587,8 +667,28 @@ async function enhanceText(text, token, baseUrl, screenshot, context) {
587
667
  return text;
588
668
  }
589
669
  }
670
+ async function findSimilarIssues(description, token, baseUrl, identity) {
671
+ var _a;
672
+ try {
673
+ const url = `${baseUrl != null ? baseUrl : DEFAULT_BASE_URL}/api/v1/reports/similar`;
674
+ const response = await fetch(url, {
675
+ method: "POST",
676
+ headers: {
677
+ "Content-Type": "application/json",
678
+ Authorization: `Bearer ${token}`
679
+ },
680
+ body: JSON.stringify({ text: description, ...signedFields(identity) })
681
+ });
682
+ const envelope = await response.json().catch(() => null);
683
+ if (!response.ok || !(envelope == null ? void 0 : envelope.success)) return null;
684
+ if (!((_a = envelope.data) == null ? void 0 : _a.enabled)) return [];
685
+ return Array.isArray(envelope.data.matches) ? envelope.data.matches : [];
686
+ } catch (e) {
687
+ return null;
688
+ }
689
+ }
590
690
  async function assistTurn(params, token, baseUrl) {
591
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
691
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n;
592
692
  const offline = {
593
693
  conversationId: null,
594
694
  question: null,
@@ -621,19 +721,25 @@ async function assistTurn(params, token, baseUrl) {
621
721
  report: (_i = (_h = envelope.data) == null ? void 0 : _h.report) != null ? _i : null,
622
722
  duplicate: (_k = (_j = envelope.data) == null ? void 0 : _j.duplicate) != null ? _k : null,
623
723
  solved: (_m = (_l = envelope.data) == null ? void 0 : _l.solved) != null ? _m : null,
724
+ aboutGlitchgrab: ((_n = envelope.data) == null ? void 0 : _n.aboutGlitchgrab) === true,
624
725
  degraded: null
625
726
  };
626
727
  } catch (e) {
627
728
  return offline;
628
729
  }
629
730
  }
630
- async function transcribeAudio(blob, token, baseUrl) {
731
+ async function transcribeAudio(blob, token, baseUrl, identity) {
631
732
  var _a;
632
733
  try {
633
734
  if (blob.size === 0) return "";
634
735
  const url = `${baseUrl != null ? baseUrl : DEFAULT_BASE_URL}/api/v1/sdk/stt`;
635
736
  const form = new FormData();
636
737
  form.append("file", blob, "audio.webm");
738
+ const signed = signedFields(identity);
739
+ if (signed.userId && signed.signature) {
740
+ form.append("userId", signed.userId);
741
+ form.append("signature", signed.signature);
742
+ }
637
743
  const response = await fetch(url, {
638
744
  method: "POST",
639
745
  headers: { Authorization: `Bearer ${token}` },
@@ -835,17 +941,21 @@ var GlitchgrabErrorBoundary = class extends React.Component {
835
941
 
836
942
  // ../report-ui/dist/index.mjs
837
943
  import {
838
- useState as useState3,
839
- useRef as useRef3,
840
- useEffect as useEffect3
944
+ useState as useState4,
945
+ useRef as useRef4,
946
+ useEffect as useEffect4,
947
+ useMemo
841
948
  } from "react";
842
- import { createPortal as createPortal2 } from "react-dom";
949
+ import { createPortal as createPortal3 } from "react-dom";
843
950
  import { useEffect, useRef, useState } from "react";
844
951
  import { jsx, jsxs } from "react/jsx-runtime";
845
952
  import { useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
846
953
  import { createPortal } from "react-dom";
847
954
  import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
955
+ import { useEffect as useEffect3, useRef as useRef3, useState as useState3 } from "react";
956
+ import { createPortal as createPortal2 } from "react-dom";
848
957
  import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
958
+ import { Fragment as Fragment3, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
849
959
  var MAX_SCREENSHOT_DATA_URL_LENGTH = 18e5;
850
960
  var QUALITY_LADDER = [0.92, 0.85, 0.75];
851
961
  var DOWNSCALE_FACTOR = 0.7;
@@ -1337,7 +1447,8 @@ function AssistSheet({
1337
1447
  onSend,
1338
1448
  onDegrade,
1339
1449
  onClose,
1340
- onFinish
1450
+ onFinish,
1451
+ onReportGlitchgrabProblem
1341
1452
  }) {
1342
1453
  const narrow = useIsNarrow();
1343
1454
  const severityAsked = showSeverity && currentTile === "BUG";
@@ -1348,6 +1459,7 @@ function AssistSheet({
1348
1459
  const [hoveredStar, setHoveredStar] = useState2(0);
1349
1460
  const [options, setOptions] = useState2([]);
1350
1461
  const [duplicate, setDuplicate] = useState2(null);
1462
+ const [glitchgrabOffer, setGlitchgrabOffer] = useState2(false);
1351
1463
  const [solved, setSolved] = useState2(null);
1352
1464
  const [phase, setPhase] = useState2(
1353
1465
  !typePicked ? "type" : currentTile === "RATING" ? "rating" : "chat"
@@ -1415,7 +1527,10 @@ function AssistSheet({
1415
1527
  screenshots: sent,
1416
1528
  context: {
1417
1529
  ...context != null ? context : {},
1418
- imageSources: sent.map((shot) => pageShot && shot === pageShot ? "page" : "attached")
1530
+ imageSources: sent.map((shot) => pageShot && shot === pageShot ? "page" : "attached"),
1531
+ // Whether this dialog can open the #366 panel at all. A hint — the
1532
+ // server decides whether the model may offer it.
1533
+ canReportGlitchgrab: !!onReportGlitchgrabProblem
1419
1534
  },
1420
1535
  events
1421
1536
  });
@@ -1438,6 +1553,7 @@ function AssistSheet({
1438
1553
  }
1439
1554
  setDuplicate((_a = result.duplicate) != null ? _a : null);
1440
1555
  onDuplicateChange == null ? void 0 : onDuplicateChange((_c = (_b = result.duplicate) == null ? void 0 : _b.number) != null ? _c : null);
1556
+ setGlitchgrabOffer(!!result.aboutGlitchgrab && !!onReportGlitchgrabProblem && !!result.question);
1441
1557
  if (result.solved) {
1442
1558
  setSolved(result.solved);
1443
1559
  setOptions([]);
@@ -1474,6 +1590,7 @@ function AssistSheet({
1474
1590
  const value = text.trim();
1475
1591
  if (!value || busy) return;
1476
1592
  setOptions([]);
1593
+ setGlitchgrabOffer(false);
1477
1594
  const underDraft = phase === "draft";
1478
1595
  if (underDraft) {
1479
1596
  const edited = aiDraftRef.current !== null && description.trim() !== aiDraftRef.current.trim();
@@ -1527,7 +1644,7 @@ function AssistSheet({
1527
1644
  minWidth: 0
1528
1645
  },
1529
1646
  children: [
1530
- /* @__PURE__ */ jsx2("span", { style: { color: "#f59e0b", fontWeight: 600 }, children: title ? "Our team is already on this" : `Our team is already on #${duplicate.number}` }),
1647
+ /* @__PURE__ */ jsx2("span", { style: { color: t2.text, fontWeight: 600 }, children: title ? "Our team is already on this" : `Our team is already on #${duplicate.number}` }),
1531
1648
  title && /* @__PURE__ */ jsxs2("span", { style: { color: t2.textMuted, wordBreak: "break-word" }, children: [
1532
1649
  "#",
1533
1650
  duplicate.number,
@@ -2031,7 +2148,82 @@ function AssistSheet({
2031
2148
  );
2032
2149
  }),
2033
2150
  duplicate && !busy && phase === "chat" && /* @__PURE__ */ jsx2("div", { style: { paddingLeft: "32px", minWidth: 0 }, children: renderKnownIssue({ title: false }) }),
2034
- options.length > 0 && !busy && phase === "chat" && /* @__PURE__ */ jsx2(
2151
+ glitchgrabOffer && onReportGlitchgrabProblem && !busy && phase === "chat" && /* @__PURE__ */ jsx2("div", { style: { paddingLeft: "32px", minWidth: 0 }, children: /* @__PURE__ */ jsxs2(
2152
+ "div",
2153
+ {
2154
+ "data-gg-glitchgrab-offer": "",
2155
+ style: {
2156
+ display: "flex",
2157
+ flexDirection: "column",
2158
+ gap: "8px",
2159
+ padding: "10px",
2160
+ borderRadius: "8px",
2161
+ border: `1px solid ${t2.accent}55`,
2162
+ backgroundColor: `${t2.accent}14`,
2163
+ fontSize: "12px",
2164
+ lineHeight: 1.5
2165
+ },
2166
+ children: [
2167
+ /* @__PURE__ */ jsx2("span", { style: { color: t2.text, fontWeight: 600 }, children: "This is about Glitchgrab, not this app" }),
2168
+ /* @__PURE__ */ jsx2("span", { style: { color: t2.textMuted }, children: "It goes to the team that builds this reporter, with your chat attached." }),
2169
+ /* @__PURE__ */ jsxs2("div", { style: { display: "flex", flexWrap: "wrap", gap: "6px" }, children: [
2170
+ /* @__PURE__ */ jsx2(
2171
+ "button",
2172
+ {
2173
+ type: "button",
2174
+ "data-gg-glitchgrab-offer-send": "",
2175
+ onClick: () => {
2176
+ logEvent("glitchgrab_offer_tap");
2177
+ setGlitchgrabOffer(false);
2178
+ onReportGlitchgrabProblem(
2179
+ messages.filter((m) => m.role === "user").map((m) => m.content.trim()).filter(Boolean).join("\n\n")
2180
+ );
2181
+ },
2182
+ style: {
2183
+ padding: "8px 12px",
2184
+ borderRadius: "999px",
2185
+ border: "none",
2186
+ backgroundColor: t2.accent,
2187
+ color: t2.accentText,
2188
+ fontSize: "12px",
2189
+ fontWeight: 700,
2190
+ fontFamily: "inherit",
2191
+ cursor: "pointer",
2192
+ // Both stretch, so on a phone each wrapped line reads as
2193
+ // a choice rather than one button left stranded.
2194
+ flex: "1 1 auto"
2195
+ },
2196
+ children: "Send to the Glitchgrab team"
2197
+ }
2198
+ ),
2199
+ /* @__PURE__ */ jsx2(
2200
+ "button",
2201
+ {
2202
+ type: "button",
2203
+ "data-gg-glitchgrab-offer-no": "",
2204
+ onClick: () => {
2205
+ logEvent("glitchgrab_offer_declined");
2206
+ send("No \u2014 it's about this app, not the reporting tool");
2207
+ },
2208
+ style: {
2209
+ padding: "8px 12px",
2210
+ borderRadius: "999px",
2211
+ border: `1px solid ${t2.inputBorder}`,
2212
+ background: "transparent",
2213
+ color: t2.textMuted,
2214
+ fontSize: "12px",
2215
+ fontFamily: "inherit",
2216
+ cursor: "pointer",
2217
+ flex: "1 1 auto"
2218
+ },
2219
+ children: "No, it's about this app"
2220
+ }
2221
+ )
2222
+ ] })
2223
+ ]
2224
+ }
2225
+ ) }),
2226
+ options.length > 0 && !busy && phase === "chat" && !glitchgrabOffer && /* @__PURE__ */ jsx2(
2035
2227
  "div",
2036
2228
  {
2037
2229
  style: {
@@ -2617,6 +2809,30 @@ function AssistSheet({
2617
2809
  },
2618
2810
  children: "Not getting it? Fill the form yourself \u2192"
2619
2811
  }
2812
+ ),
2813
+ onReportGlitchgrabProblem && phase !== "solved" && !glitchgrabOffer && /* @__PURE__ */ jsx2(
2814
+ "button",
2815
+ {
2816
+ type: "button",
2817
+ "data-gg-self-report": "",
2818
+ onClick: () => onReportGlitchgrabProblem(),
2819
+ style: {
2820
+ display: "block",
2821
+ // ~32px tap target; the negative margins keep the composer
2822
+ // from growing to make room for it.
2823
+ margin: "-2px auto -8px",
2824
+ border: "none",
2825
+ background: "transparent",
2826
+ color: t2.textMuted,
2827
+ fontSize: "11.5px",
2828
+ fontFamily: "inherit",
2829
+ padding: "10px 8px",
2830
+ cursor: "pointer",
2831
+ textDecoration: "underline",
2832
+ textUnderlineOffset: "2px"
2833
+ },
2834
+ children: "Assistant misbehaving? Tell Glitchgrab"
2835
+ }
2620
2836
  )
2621
2837
  ]
2622
2838
  }
@@ -2630,6 +2846,240 @@ function AssistSheet({
2630
2846
  document.body
2631
2847
  );
2632
2848
  }
2849
+ var FONT = '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
2850
+ var FAILED = "Couldn't send it. Try again in a moment.";
2851
+ function describeSentAlong(hasChat, screenshotCount) {
2852
+ const parts = [
2853
+ ...hasChat ? ["your AI chat"] : [],
2854
+ "what you did on this page",
2855
+ ...screenshotCount > 0 ? [`${screenshotCount} screenshot${screenshotCount === 1 ? "" : "s"}`] : []
2856
+ ];
2857
+ if (parts.length === 1) return parts[0];
2858
+ return `${parts.slice(0, -1).join(", ")} and ${parts[parts.length - 1]}`;
2859
+ }
2860
+ function GlitchgrabProblemPanel({
2861
+ send,
2862
+ theme: t2,
2863
+ surface,
2864
+ conversationId,
2865
+ reportType,
2866
+ screenshots,
2867
+ initialText = "",
2868
+ onClose
2869
+ }) {
2870
+ const [text, setText] = useState3(initialText);
2871
+ const [busy, setBusy] = useState3(false);
2872
+ const [sent, setSent] = useState3(false);
2873
+ const [error, setError] = useState3(null);
2874
+ const inputRef = useRef3(null);
2875
+ useEffect3(() => {
2876
+ var _a;
2877
+ (_a = inputRef.current) == null ? void 0 : _a.focus();
2878
+ }, []);
2879
+ useEffect3(() => {
2880
+ const onKey = (e) => {
2881
+ if (e.key !== "Escape") return;
2882
+ e.stopPropagation();
2883
+ onClose();
2884
+ };
2885
+ window.addEventListener("keydown", onKey, true);
2886
+ return () => window.removeEventListener("keydown", onKey, true);
2887
+ }, [onClose]);
2888
+ const submit = async () => {
2889
+ const description = text.trim();
2890
+ if (!description || busy) return;
2891
+ setBusy(true);
2892
+ setError(null);
2893
+ try {
2894
+ const result = await send({ description, surface, conversationId, reportType, screenshots });
2895
+ if (result == null ? void 0 : result.success) setSent(true);
2896
+ else setError((result == null ? void 0 : result.message) || FAILED);
2897
+ } catch (e) {
2898
+ setError(FAILED);
2899
+ } finally {
2900
+ setBusy(false);
2901
+ }
2902
+ };
2903
+ const canSend = !!text.trim() && !busy;
2904
+ const secondaryButton = {
2905
+ padding: "10px 14px",
2906
+ borderRadius: "8px",
2907
+ border: `1px solid ${t2.inputBorder}`,
2908
+ background: "transparent",
2909
+ color: t2.text,
2910
+ fontSize: "13px",
2911
+ fontWeight: 600,
2912
+ fontFamily: "inherit",
2913
+ cursor: "pointer"
2914
+ };
2915
+ const primaryButton = (enabled) => ({
2916
+ padding: "10px 14px",
2917
+ borderRadius: "8px",
2918
+ border: "none",
2919
+ backgroundColor: enabled ? t2.accent : t2.bgSecondary,
2920
+ color: enabled ? t2.accentText : t2.textMuted,
2921
+ fontSize: "13px",
2922
+ fontWeight: 700,
2923
+ fontFamily: "inherit",
2924
+ cursor: enabled ? "pointer" : "default"
2925
+ });
2926
+ return createPortal2(
2927
+ // No close on backdrop click: a stray click would throw away what they typed.
2928
+ // `data-glitchgrab-layer` exempts this from the dialog's anti-focus-trap
2929
+ // sweep, which sets `inert` on every other role="dialog" on the page —
2930
+ // without it the panel renders but cannot be typed into.
2931
+ /* @__PURE__ */ jsx3(
2932
+ "div",
2933
+ {
2934
+ "data-glitchgrab-layer": "",
2935
+ "data-gg-self-report-panel": "",
2936
+ role: "dialog",
2937
+ "aria-modal": "true",
2938
+ "aria-labelledby": "gg-self-report-title",
2939
+ style: {
2940
+ position: "fixed",
2941
+ inset: 0,
2942
+ zIndex: 2147483647,
2943
+ backgroundColor: "rgba(0,0,0,0.55)",
2944
+ display: "flex",
2945
+ alignItems: "center",
2946
+ justifyContent: "center",
2947
+ padding: "16px",
2948
+ boxSizing: "border-box",
2949
+ fontFamily: FONT
2950
+ },
2951
+ children: /* @__PURE__ */ jsx3(
2952
+ "div",
2953
+ {
2954
+ style: {
2955
+ width: "100%",
2956
+ maxWidth: "420px",
2957
+ boxSizing: "border-box",
2958
+ backgroundColor: t2.bg,
2959
+ color: t2.text,
2960
+ border: `1px solid ${t2.border}`,
2961
+ borderRadius: "12px",
2962
+ boxShadow: "0 20px 50px rgba(0,0,0,0.5)",
2963
+ padding: "18px"
2964
+ },
2965
+ children: sent ? /* @__PURE__ */ jsxs3(Fragment2, { children: [
2966
+ /* @__PURE__ */ jsx3("div", { id: "gg-self-report-title", style: { fontSize: "15px", fontWeight: 600 }, children: "Sent to the Glitchgrab team" }),
2967
+ /* @__PURE__ */ jsx3("p", { style: { margin: "6px 0 16px", fontSize: "13px", lineHeight: 1.5, color: t2.textMuted }, children: "Thanks \u2014 we'll look into it. Your report is still open behind this, exactly as you left it." }),
2968
+ /* @__PURE__ */ jsx3(
2969
+ "button",
2970
+ {
2971
+ type: "button",
2972
+ "data-gg-self-report-done": "",
2973
+ onClick: onClose,
2974
+ style: { ...primaryButton(true), width: "100%" },
2975
+ children: "Back to your report"
2976
+ }
2977
+ )
2978
+ ] }) : /* @__PURE__ */ jsxs3(Fragment2, { children: [
2979
+ /* @__PURE__ */ jsxs3(
2980
+ "div",
2981
+ {
2982
+ style: {
2983
+ display: "flex",
2984
+ alignItems: "flex-start",
2985
+ justifyContent: "space-between",
2986
+ gap: "12px"
2987
+ },
2988
+ children: [
2989
+ /* @__PURE__ */ jsx3("div", { id: "gg-self-report-title", style: { fontSize: "15px", fontWeight: 600 }, children: "Problem with Glitchgrab?" }),
2990
+ /* @__PURE__ */ jsx3(
2991
+ "button",
2992
+ {
2993
+ type: "button",
2994
+ onClick: onClose,
2995
+ "aria-label": "Close",
2996
+ style: {
2997
+ border: "none",
2998
+ background: "transparent",
2999
+ color: t2.textMuted,
3000
+ fontSize: "18px",
3001
+ lineHeight: 1,
3002
+ cursor: "pointer",
3003
+ padding: "4px 6px",
3004
+ margin: "-4px -6px 0 0"
3005
+ },
3006
+ children: "\xD7"
3007
+ }
3008
+ )
3009
+ ]
3010
+ }
3011
+ ),
3012
+ /* @__PURE__ */ jsx3("p", { style: { margin: "6px 0 12px", fontSize: "13px", lineHeight: 1.5, color: t2.textMuted }, children: "This goes to the people who build this reporter \u2014 not to this project's team. Tell us what went wrong: the AI chat, a button, anything." }),
3013
+ /* @__PURE__ */ jsx3(
3014
+ "textarea",
3015
+ {
3016
+ ref: inputRef,
3017
+ "data-gg-self-report-input": "",
3018
+ value: text,
3019
+ onChange: (e) => {
3020
+ setText(e.target.value);
3021
+ if (error) setError(null);
3022
+ },
3023
+ onKeyDown: (e) => {
3024
+ if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
3025
+ e.preventDefault();
3026
+ void submit();
3027
+ }
3028
+ },
3029
+ rows: 4,
3030
+ placeholder: "e.g. The assistant kept asking the same question and never wrote a draft",
3031
+ style: {
3032
+ width: "100%",
3033
+ boxSizing: "border-box",
3034
+ resize: "vertical",
3035
+ padding: "10px 12px",
3036
+ borderRadius: "8px",
3037
+ border: `1px solid ${t2.inputBorder}`,
3038
+ backgroundColor: t2.inputBg,
3039
+ color: t2.text,
3040
+ fontSize: "14px",
3041
+ lineHeight: 1.45,
3042
+ fontFamily: "inherit",
3043
+ outline: "none"
3044
+ }
3045
+ }
3046
+ ),
3047
+ /* @__PURE__ */ jsxs3(
3048
+ "p",
3049
+ {
3050
+ "data-gg-self-report-includes": "",
3051
+ style: { margin: "8px 0 0", fontSize: "11.5px", lineHeight: 1.45, color: t2.textMuted },
3052
+ children: [
3053
+ "Also sent, so we can see what happened:",
3054
+ " ",
3055
+ describeSentAlong(conversationId !== null, screenshots.length),
3056
+ "."
3057
+ ]
3058
+ }
3059
+ ),
3060
+ error && /* @__PURE__ */ jsx3("p", { role: "alert", style: { margin: "8px 0 0", fontSize: "12px", color: "#ef4444" }, children: error }),
3061
+ /* @__PURE__ */ jsxs3("div", { style: { display: "flex", justifyContent: "flex-end", gap: "8px", marginTop: "14px" }, children: [
3062
+ /* @__PURE__ */ jsx3("button", { type: "button", onClick: onClose, style: secondaryButton, children: "Cancel" }),
3063
+ /* @__PURE__ */ jsx3(
3064
+ "button",
3065
+ {
3066
+ type: "button",
3067
+ "data-gg-self-report-send": "",
3068
+ onClick: () => void submit(),
3069
+ disabled: !canSend,
3070
+ style: primaryButton(canSend),
3071
+ children: busy ? "Sending\u2026" : "Send to Glitchgrab"
3072
+ }
3073
+ )
3074
+ ] })
3075
+ ] })
3076
+ }
3077
+ )
3078
+ }
3079
+ ),
3080
+ document.body
3081
+ );
3082
+ }
2633
3083
  var GLITCHGRAB_SHORTCUT = "Ctrl+Shift+G";
2634
3084
  var GLITCHGRAB_SHORTCUT_MAC = "\u2318\u21E7G";
2635
3085
  function isMac() {
@@ -2677,6 +3127,7 @@ var ATTACHMENT_ACCEPT = [
2677
3127
  ].join(",");
2678
3128
  var MAX_SCALE = 2;
2679
3129
  var FRAME_TIMEOUT_MS = 3e3;
3130
+ var GRAB_TIMEOUT_MS = FRAME_TIMEOUT_MS + 2e3;
2680
3131
  function canCaptureTabPixels() {
2681
3132
  var _a;
2682
3133
  try {
@@ -2743,7 +3194,13 @@ async function captureViaTabPixels() {
2743
3194
  stream = await navigator.mediaDevices.getDisplayMedia(options);
2744
3195
  const surface = (_b = (_a = stream.getVideoTracks()[0]) == null ? void 0 : _a.getSettings()) == null ? void 0 : _b.displaySurface;
2745
3196
  if (surface && surface !== "browser") return null;
2746
- const canvas = await grabFrame(stream);
3197
+ let timer;
3198
+ const timedOut = new Promise((resolve) => {
3199
+ timer = setTimeout(() => resolve(null), GRAB_TIMEOUT_MS);
3200
+ });
3201
+ const canvas = await Promise.race([grabFrame(stream), timedOut]).finally(
3202
+ () => clearTimeout(timer)
3203
+ );
2747
3204
  return canvas ? encodeScreenshot(canvas) : null;
2748
3205
  } catch (e) {
2749
3206
  return null;
@@ -2751,6 +3208,10 @@ async function captureViaTabPixels() {
2751
3208
  stream == null ? void 0 : stream.getTracks().forEach((track) => track.stop());
2752
3209
  }
2753
3210
  }
3211
+ var IMAGE_TIMEOUT_MS = 3e3;
3212
+ function isGlitchgrabLayer(el) {
3213
+ return el.hasAttribute("data-glitchgrab-layer");
3214
+ }
2754
3215
  async function captureViaHtml2Canvas() {
2755
3216
  try {
2756
3217
  const { default: html2canvas } = await import("html2canvas-pro");
@@ -2760,6 +3221,8 @@ async function captureViaHtml2Canvas() {
2760
3221
  logging: false,
2761
3222
  useCORS: true,
2762
3223
  allowTaint: true,
3224
+ imageTimeout: IMAGE_TIMEOUT_MS,
3225
+ ignoreElements: isGlitchgrabLayer,
2763
3226
  x: window.scrollX,
2764
3227
  y: window.scrollY,
2765
3228
  width: window.innerWidth,
@@ -2772,12 +3235,61 @@ async function captureViaHtml2Canvas() {
2772
3235
  return null;
2773
3236
  }
2774
3237
  }
2775
- async function captureDefaultScreenshot() {
2776
- var _a;
2777
- return (_a = await captureViaTabPixels()) != null ? _a : await captureViaHtml2Canvas();
3238
+ var inFlight = null;
3239
+ function nextPaint() {
3240
+ return new Promise((resolve) => {
3241
+ const cap = setTimeout(resolve, 100);
3242
+ if (typeof requestAnimationFrame !== "function") return;
3243
+ requestAnimationFrame(
3244
+ () => setTimeout(() => {
3245
+ clearTimeout(cap);
3246
+ resolve();
3247
+ }, 0)
3248
+ );
3249
+ });
3250
+ }
3251
+ function captureDefaultScreenshot(options) {
3252
+ if (inFlight) return inFlight;
3253
+ inFlight = (async () => {
3254
+ const pixels = await captureViaTabPixels();
3255
+ if (pixels) return pixels;
3256
+ if (options == null ? void 0 : options.onRedraw) {
3257
+ try {
3258
+ options.onRedraw();
3259
+ } catch (e) {
3260
+ }
3261
+ await nextPaint();
3262
+ }
3263
+ return captureViaHtml2Canvas();
3264
+ })().finally(() => {
3265
+ inFlight = null;
3266
+ });
3267
+ return inFlight;
2778
3268
  }
2779
3269
  var SEVERITY_REQUIRED = "Pick how bad it is before sending.";
2780
- function useIsDark() {
3270
+ var SHOT_WAIT_MS = 3e3;
3271
+ var SIMILAR_WAIT_MS = 2500;
3272
+ var MAX_SIMILAR_SHOWN = 3;
3273
+ function ButtonSpinner() {
3274
+ return /* @__PURE__ */ jsx4(
3275
+ "span",
3276
+ {
3277
+ "aria-hidden": "true",
3278
+ style: {
3279
+ display: "inline-block",
3280
+ width: "11px",
3281
+ height: "11px",
3282
+ marginRight: "6px",
3283
+ verticalAlign: "-1px",
3284
+ border: "2px solid currentColor",
3285
+ borderRightColor: "transparent",
3286
+ borderRadius: "50%",
3287
+ animation: "gg-spin .7s linear infinite"
3288
+ }
3289
+ }
3290
+ );
3291
+ }
3292
+ function detectDark() {
2781
3293
  if (typeof window === "undefined") return true;
2782
3294
  try {
2783
3295
  for (const el of [document.body, document.documentElement]) {
@@ -2900,9 +3412,9 @@ function ReporterChip({
2900
3412
  t: t2
2901
3413
  }) {
2902
3414
  var _a;
2903
- const [avatarFailed, setAvatarFailed] = useState3(false);
3415
+ const [avatarFailed, setAvatarFailed] = useState4(false);
2904
3416
  const avatarUrl = (reporter == null ? void 0 : reporter.avatarUrl) || "";
2905
- useEffect3(() => {
3417
+ useEffect4(() => {
2906
3418
  setAvatarFailed(false);
2907
3419
  }, [avatarUrl]);
2908
3420
  const name = ((_a = reporter == null ? void 0 : reporter.name) == null ? void 0 : _a.trim()) || "";
@@ -2911,13 +3423,13 @@ function ReporterChip({
2911
3423
  const label = isAnonymous ? "Anonymous" : name;
2912
3424
  const title = isAnonymous ? "No reporter identity was passed to Glitchgrab \u2014 this report won't be attributed to anyone" : [name, reporter == null ? void 0 : reporter.email, reporter == null ? void 0 : reporter.role].filter(Boolean).join(" \xB7 ");
2913
3425
  const showAvatarImage = !!avatarUrl && !avatarFailed;
2914
- return /* @__PURE__ */ jsxs3(
3426
+ return /* @__PURE__ */ jsxs4(
2915
3427
  "span",
2916
3428
  {
2917
3429
  title,
2918
3430
  style: { display: "flex", alignItems: "center", gap: "6px", minWidth: 0 },
2919
3431
  children: [
2920
- /* @__PURE__ */ jsx3(
3432
+ /* @__PURE__ */ jsx4(
2921
3433
  "span",
2922
3434
  {
2923
3435
  "aria-hidden": "true",
@@ -2937,7 +3449,7 @@ function ReporterChip({
2937
3449
  color: isAnonymous ? t2.textMuted : t2.accentText,
2938
3450
  border: `1px solid ${isAnonymous ? t2.inputBorder : "transparent"}`
2939
3451
  },
2940
- children: showAvatarImage ? /* @__PURE__ */ jsx3(
3452
+ children: showAvatarImage ? /* @__PURE__ */ jsx4(
2941
3453
  "img",
2942
3454
  {
2943
3455
  src: avatarUrl,
@@ -2947,9 +3459,9 @@ function ReporterChip({
2947
3459
  onError: () => setAvatarFailed(true),
2948
3460
  style: { width: "100%", height: "100%", objectFit: "cover" }
2949
3461
  }
2950
- ) : initials ? initials : /* @__PURE__ */ jsxs3("svg", { width: "10", height: "10", viewBox: "0 0 16 16", fill: "none", children: [
2951
- /* @__PURE__ */ jsx3("circle", { cx: "8", cy: "5.5", r: "2.75", stroke: "currentColor", strokeWidth: "1.4" }),
2952
- /* @__PURE__ */ jsx3(
3462
+ ) : initials ? initials : /* @__PURE__ */ jsxs4("svg", { width: "10", height: "10", viewBox: "0 0 16 16", fill: "none", children: [
3463
+ /* @__PURE__ */ jsx4("circle", { cx: "8", cy: "5.5", r: "2.75", stroke: "currentColor", strokeWidth: "1.4" }),
3464
+ /* @__PURE__ */ jsx4(
2953
3465
  "path",
2954
3466
  {
2955
3467
  d: "M2.75 13.5c0-2.35 2.35-3.75 5.25-3.75s5.25 1.4 5.25 3.75",
@@ -2961,7 +3473,7 @@ function ReporterChip({
2961
3473
  ] })
2962
3474
  }
2963
3475
  ),
2964
- /* @__PURE__ */ jsxs3(
3476
+ /* @__PURE__ */ jsxs4(
2965
3477
  "span",
2966
3478
  {
2967
3479
  style: {
@@ -3083,6 +3595,7 @@ function ReportDialog({
3083
3595
  sendFeedback: sendFeedback2,
3084
3596
  enhanceText: enhanceText2,
3085
3597
  assist,
3598
+ findSimilarIssues: findSimilarIssues2,
3086
3599
  assistContext = null,
3087
3600
  transcribeAudio: transcribeAudio2,
3088
3601
  types,
@@ -3090,43 +3603,54 @@ function ReportDialog({
3090
3603
  captureScreenshot = captureDefaultScreenshot,
3091
3604
  reporter,
3092
3605
  onClose,
3093
- headerSlot
3606
+ headerSlot,
3607
+ reportGlitchgrabProblem
3094
3608
  }) {
3095
3609
  var _a;
3096
- const [isEnhancing, setIsEnhancing] = useState3(false);
3097
- const [assistOpen, setAssistOpen] = useState3(false);
3098
- const [assistNotice, setAssistNotice] = useState3(null);
3099
- const [assistUsed, setAssistUsed] = useState3(false);
3100
- const [typeChosen, setTypeChosen] = useState3(false);
3101
- const [duplicateIssueNumber, setDuplicateIssueNumber] = useState3(null);
3102
- const [aiConversationId, setAiConversationId] = useState3(null);
3103
- const [isEnhanced, setIsEnhanced] = useState3(false);
3104
- const [originalDescription, setOriginalDescription] = useState3(
3610
+ const [isEnhancing, setIsEnhancing] = useState4(false);
3611
+ const [assistOpen, setAssistOpen] = useState4(false);
3612
+ const [assistNotice, setAssistNotice] = useState4(null);
3613
+ const [assistUsed, setAssistUsed] = useState4(false);
3614
+ const [selfReportFrom, setSelfReportFrom] = useState4(null);
3615
+ const [selfReportPrefill, setSelfReportPrefill] = useState4("");
3616
+ const [typeChosen, setTypeChosen] = useState4(false);
3617
+ const [duplicateIssueNumber, setDuplicateIssueNumber] = useState4(null);
3618
+ const [similarIssues, setSimilarIssues] = useState4(null);
3619
+ const [checkingSimilar, setCheckingSimilar] = useState4(false);
3620
+ const [attachingTo, setAttachingTo] = useState4(null);
3621
+ const [pickedSimilar, setPickedSimilar] = useState4(null);
3622
+ const [addedToIssue, setAddedToIssue] = useState4(null);
3623
+ const [aiConversationId, setAiConversationId] = useState4(null);
3624
+ const [isEnhanced, setIsEnhanced] = useState4(false);
3625
+ const [originalDescription, setOriginalDescription] = useState4(
3105
3626
  null
3106
3627
  );
3107
- const [isListening, setIsListening] = useState3(false);
3108
- const [isTranscribing, setIsTranscribing] = useState3(false);
3109
- const [voiceError, setVoiceError] = useState3(null);
3110
- const [screenshotDragOver, setScreenshotDragOver] = useState3(false);
3111
- const [mounted, setMounted] = useState3(false);
3112
- useEffect3(() => {
3628
+ const [isListening, setIsListening] = useState4(false);
3629
+ const [isTranscribing, setIsTranscribing] = useState4(false);
3630
+ const [voiceError, setVoiceError] = useState4(null);
3631
+ const [screenshotDragOver, setScreenshotDragOver] = useState4(false);
3632
+ const [mounted, setMounted] = useState4(false);
3633
+ useEffect4(() => {
3113
3634
  setMounted(true);
3114
3635
  }, []);
3115
- const [isOpen, setIsOpen] = useState3(false);
3116
- const [description, setDescription] = useState3("");
3117
- const [isSubmitting, setIsSubmitting] = useState3(false);
3118
- const [submitted, setSubmitted] = useState3(false);
3119
- const [screenshots, setScreenshots] = useState3([]);
3120
- const [pageShot, setPageShot] = useState3(null);
3121
- const [attachments, setAttachments] = useState3([]);
3122
- const [previewIndex, setPreviewIndex] = useState3(null);
3123
- const [annotatingIndex, setAnnotatingIndex] = useState3(null);
3124
- const [previewZoomed, setPreviewZoomed] = useState3(false);
3125
- const [previewNatural, setPreviewNatural] = useState3(null);
3126
- const previewScrollRef = useRef3(null);
3127
- const previewImgElRef = useRef3(null);
3128
- const panRef = useRef3(null);
3129
- const zoomAnchorRef = useRef3(null);
3636
+ const [isOpen, setIsOpen] = useState4(false);
3637
+ const [description, setDescription] = useState4("");
3638
+ const [isSubmitting, setIsSubmitting] = useState4(false);
3639
+ const [submitted, setSubmitted] = useState4(false);
3640
+ const [screenshots, setScreenshots] = useState4([]);
3641
+ const [pageShot, setPageShot] = useState4(null);
3642
+ const [shotPending, setShotPending] = useState4(false);
3643
+ const pendingShotRef = useRef4(null);
3644
+ const openGenRef = useRef4(0);
3645
+ const [attachments, setAttachments] = useState4([]);
3646
+ const [previewIndex, setPreviewIndex] = useState4(null);
3647
+ const [annotatingIndex, setAnnotatingIndex] = useState4(null);
3648
+ const [previewZoomed, setPreviewZoomed] = useState4(false);
3649
+ const [previewNatural, setPreviewNatural] = useState4(null);
3650
+ const previewScrollRef = useRef4(null);
3651
+ const previewImgElRef = useRef4(null);
3652
+ const panRef = useRef4(null);
3653
+ const zoomAnchorRef = useRef4(null);
3130
3654
  const resetPreviewZoom = () => {
3131
3655
  setPreviewZoomed(false);
3132
3656
  setPreviewNatural(null);
@@ -3140,24 +3664,24 @@ function ReportDialog({
3140
3664
  resetPreviewZoom();
3141
3665
  setPreviewIndex(null);
3142
3666
  };
3143
- const fileInputRef = useRef3(null);
3144
- const modalRef = useRef3(null);
3145
- const previewImgRef = useRef3(null);
3146
- const textareaRef = useRef3(null);
3147
- const recognitionRef = useRef3(null);
3148
- const voiceBaseRef = useRef3("");
3149
- const usingWebSpeechRef = useRef3(false);
3150
- const textBeforeVoiceRef = useRef3("");
3151
- const mediaRecorderRef = useRef3(null);
3152
- const streamRef = useRef3(null);
3153
- const sarvamChunksRef = useRef3([]);
3154
- const spaceTimerRef = useRef3(null);
3155
- const isPushToTalkRef = useRef3(false);
3156
- const spaceEditsRef = useRef3(0);
3157
- const descriptionRef = useRef3("");
3158
- const lastVoiceTextRef = useRef3(null);
3159
- const hadWebSpeechRef = useRef3(false);
3160
- useEffect3(() => {
3667
+ const fileInputRef = useRef4(null);
3668
+ const modalRef = useRef4(null);
3669
+ const previewImgRef = useRef4(null);
3670
+ const textareaRef = useRef4(null);
3671
+ const recognitionRef = useRef4(null);
3672
+ const voiceBaseRef = useRef4("");
3673
+ const usingWebSpeechRef = useRef4(false);
3674
+ const textBeforeVoiceRef = useRef4("");
3675
+ const mediaRecorderRef = useRef4(null);
3676
+ const streamRef = useRef4(null);
3677
+ const sarvamChunksRef = useRef4([]);
3678
+ const spaceTimerRef = useRef4(null);
3679
+ const isPushToTalkRef = useRef4(false);
3680
+ const spaceEditsRef = useRef4(0);
3681
+ const descriptionRef = useRef4("");
3682
+ const lastVoiceTextRef = useRef4(null);
3683
+ const hadWebSpeechRef = useRef4(false);
3684
+ useEffect4(() => {
3161
3685
  if (!isOpen) return;
3162
3686
  const HOST_LAYERS = '[role="dialog"],[role="alertdialog"],[role="menu"],[role="listbox"]';
3163
3687
  const inerted = /* @__PURE__ */ new Set();
@@ -3183,13 +3707,13 @@ function ReportDialog({
3183
3707
  inerted.forEach((el) => el.removeAttribute("inert"));
3184
3708
  };
3185
3709
  }, [isOpen]);
3186
- const [step, setStep] = useState3(1);
3187
- const [reportType, setReportType] = useState3("BUG");
3188
- const [severity, setSeverity] = useState3(null);
3189
- const [validationError, setValidationError] = useState3(null);
3190
- const [placeholderIdx, setPlaceholderIdx] = useState3(0);
3191
- const [rating, setRating] = useState3(0);
3192
- const [hoveredStar, setHoveredStar] = useState3(0);
3710
+ const [step, setStep] = useState4(1);
3711
+ const [reportType, setReportType] = useState4("BUG");
3712
+ const [severity, setSeverity] = useState4(null);
3713
+ const [validationError, setValidationError] = useState4(null);
3714
+ const [placeholderIdx, setPlaceholderIdx] = useState4(0);
3715
+ const [rating, setRating] = useState4(0);
3716
+ const [hoveredStar, setHoveredStar] = useState4(0);
3193
3717
  const isRating = reportType === "RATING";
3194
3718
  const submitDisabled = isSubmitting || (isRating ? rating < 1 : !description.trim());
3195
3719
  const needsSeverity = validationError === SEVERITY_REQUIRED && showSeverity && reportType === "BUG" && !severity;
@@ -3201,8 +3725,8 @@ function ReportDialog({
3201
3725
  "SECURITY",
3202
3726
  "QUESTION"
3203
3727
  ];
3204
- const tileGridRef = useRef3(null);
3205
- useEffect3(() => {
3728
+ const tileGridRef = useRef4(null);
3729
+ useEffect4(() => {
3206
3730
  if (!isOpen || step !== 1) return;
3207
3731
  const onKey = (e) => {
3208
3732
  const el = e.target;
@@ -3246,8 +3770,19 @@ function ReportDialog({
3246
3770
  const next = current === -1 ? 0 : Math.min(Math.max(current + delta, 0), tiles.length - 1);
3247
3771
  (_c = tiles[next]) == null ? void 0 : _c.focus();
3248
3772
  };
3249
- const isDark = useIsDark();
3773
+ const isDark = useMemo(() => isOpen ? detectDark() : true, [isOpen]);
3250
3774
  const t2 = getTheme(isDark);
3775
+ const layerTheme = {
3776
+ bg: t2.bg,
3777
+ bgSecondary: t2.bgSecondary,
3778
+ border: t2.border,
3779
+ text: t2.text,
3780
+ textMuted: t2.textMuted,
3781
+ inputBg: t2.inputBg,
3782
+ inputBorder: t2.inputBorder,
3783
+ accent: t2.accent,
3784
+ accentText: t2.accentText
3785
+ };
3251
3786
  const canZoomPreview = previewNatural !== null && typeof window !== "undefined" && (previewNatural.w > window.innerWidth - 32 || previewNatural.h > window.innerHeight - 80);
3252
3787
  const handleFileUpload = (e) => {
3253
3788
  const files = e.target.files;
@@ -3277,6 +3812,7 @@ function ReportDialog({
3277
3812
  });
3278
3813
  };
3279
3814
  const handleOpen = async () => {
3815
+ const gen = openGenRef.current;
3280
3816
  setSubmitted(false);
3281
3817
  closePreview();
3282
3818
  setAnnotatingIndex(null);
@@ -3285,13 +3821,34 @@ function ReportDialog({
3285
3821
  setTypeChosen(true);
3286
3822
  setStep(2);
3287
3823
  }
3288
- const shot = await captureScreenshot();
3824
+ const show = () => {
3825
+ if (gen !== openGenRef.current) return;
3826
+ if (assist && !assistUsed) setAssistOpen(true);
3827
+ setIsOpen(true);
3828
+ };
3829
+ const capture = captureScreenshot({
3830
+ onRedraw: () => {
3831
+ if (gen !== openGenRef.current) return;
3832
+ setShotPending(true);
3833
+ show();
3834
+ }
3835
+ });
3836
+ pendingShotRef.current = capture;
3837
+ const previousPageShot = pageShot;
3838
+ const shot = await capture.catch(() => null);
3839
+ if (gen !== openGenRef.current) return;
3840
+ pendingShotRef.current = null;
3841
+ setShotPending(false);
3289
3842
  setPageShot(shot != null ? shot : null);
3290
- if (shot) setScreenshots([shot]);
3291
- if (assist && !assistUsed) setAssistOpen(true);
3292
- setIsOpen(true);
3843
+ if (shot) {
3844
+ setScreenshots((prev) => [
3845
+ shot,
3846
+ ...prev.filter((s) => s !== shot && s !== previousPageShot)
3847
+ ]);
3848
+ }
3849
+ show();
3293
3850
  };
3294
- useEffect3(() => {
3851
+ useEffect4(() => {
3295
3852
  const handler = (e) => {
3296
3853
  if (isOpen) return;
3297
3854
  const detail = e.detail;
@@ -3306,7 +3863,7 @@ function ReportDialog({
3306
3863
  window.addEventListener("glitchgrab:open-report", handler);
3307
3864
  return () => window.removeEventListener("glitchgrab:open-report", handler);
3308
3865
  }, [isOpen, handleOpen]);
3309
- useEffect3(() => {
3866
+ useEffect4(() => {
3310
3867
  if (!isOpen) return;
3311
3868
  const handleEsc = (e) => {
3312
3869
  if (e.key === "Escape") {
@@ -3319,7 +3876,7 @@ function ReportDialog({
3319
3876
  document.addEventListener("keydown", handleEsc);
3320
3877
  return () => document.removeEventListener("keydown", handleEsc);
3321
3878
  }, [isOpen, previewIndex, previewZoomed, annotatingIndex]);
3322
- useEffect3(() => {
3879
+ useEffect4(() => {
3323
3880
  if (previewIndex === null || annotatingIndex !== null) return;
3324
3881
  const handlePointerDown = (e) => {
3325
3882
  var _a2;
@@ -3330,7 +3887,7 @@ function ReportDialog({
3330
3887
  document.addEventListener("pointerdown", handlePointerDown, true);
3331
3888
  return () => document.removeEventListener("pointerdown", handlePointerDown, true);
3332
3889
  }, [previewIndex, annotatingIndex]);
3333
- useEffect3(() => {
3890
+ useEffect4(() => {
3334
3891
  var _a2;
3335
3892
  const el = previewScrollRef.current;
3336
3893
  if (!previewZoomed || !el) return;
@@ -3392,7 +3949,7 @@ function ReportDialog({
3392
3949
  panRef.current = null;
3393
3950
  }
3394
3951
  };
3395
- useEffect3(() => {
3952
+ useEffect4(() => {
3396
3953
  if (!isOpen) return;
3397
3954
  const handlePaste = (e) => {
3398
3955
  var _a2;
@@ -3415,14 +3972,14 @@ function ReportDialog({
3415
3972
  window.addEventListener("paste", handlePaste);
3416
3973
  return () => window.removeEventListener("paste", handlePaste);
3417
3974
  }, [isOpen]);
3418
- useEffect3(() => {
3975
+ useEffect4(() => {
3419
3976
  if (!isOpen || !transcribeAudio2) return;
3420
3977
  const id = setInterval(() => {
3421
3978
  setPlaceholderIdx((i) => (i + 1) % 2);
3422
3979
  }, 3e3);
3423
3980
  return () => clearInterval(id);
3424
3981
  }, [isOpen, transcribeAudio2]);
3425
- useEffect3(() => {
3982
+ useEffect4(() => {
3426
3983
  descriptionRef.current = description;
3427
3984
  }, [description]);
3428
3985
  const rebaseVoiceOnUserEdits = () => {
@@ -3467,7 +4024,7 @@ function ReportDialog({
3467
4024
  }
3468
4025
  };
3469
4026
  const sheetUp = !!assist && assistOpen;
3470
- useEffect3(() => {
4027
+ useEffect4(() => {
3471
4028
  const el = modalRef.current;
3472
4029
  if (!el) return;
3473
4030
  if (sheetUp) {
@@ -3482,6 +4039,9 @@ function ReportDialog({
3482
4039
  stopVoice();
3483
4040
  setIsOpen(false);
3484
4041
  onClose == null ? void 0 : onClose();
4042
+ openGenRef.current += 1;
4043
+ pendingShotRef.current = null;
4044
+ setShotPending(false);
3485
4045
  closePreview();
3486
4046
  setAnnotatingIndex(null);
3487
4047
  setStep(1);
@@ -3490,8 +4050,13 @@ function ReportDialog({
3490
4050
  setReportType("BUG");
3491
4051
  setTypeChosen(false);
3492
4052
  setDuplicateIssueNumber(null);
4053
+ setSimilarIssues(null);
4054
+ setPickedSimilar(null);
4055
+ setCheckingSimilar(false);
4056
+ setAttachingTo(null);
3493
4057
  setSeverity(null);
3494
4058
  setValidationError(null);
4059
+ setSelfReportFrom(null);
3495
4060
  setVoiceError(null);
3496
4061
  setIsEnhanced(false);
3497
4062
  setOriginalDescription(null);
@@ -3676,7 +4241,8 @@ function ReportDialog({
3676
4241
  stopListeningAndTranscribe();
3677
4242
  }
3678
4243
  };
3679
- const handleSubmit = async () => {
4244
+ const handleSubmit = async (opts) => {
4245
+ var _a2;
3680
4246
  try {
3681
4247
  if (isSubmitting) return;
3682
4248
  if (isRating) {
@@ -3713,15 +4279,42 @@ function ReportDialog({
3713
4279
  setStep(2);
3714
4280
  return;
3715
4281
  }
4282
+ const duplicateOf = (_a2 = opts == null ? void 0 : opts.duplicateOf) != null ? _a2 : duplicateIssueNumber;
4283
+ if (findSimilarIssues2 && !(opts == null ? void 0 : opts.skipSimilar) && !duplicateOf && !aiConversationId) {
4284
+ setIsSubmitting(true);
4285
+ setCheckingSimilar(true);
4286
+ const matches = await Promise.race([
4287
+ findSimilarIssues2(description.trim()).catch(() => null),
4288
+ new Promise((resolve) => setTimeout(() => resolve(null), SIMILAR_WAIT_MS))
4289
+ ]);
4290
+ setCheckingSimilar(false);
4291
+ if (matches && matches.length > 0) {
4292
+ setSimilarIssues(matches.slice(0, MAX_SIMILAR_SHOWN));
4293
+ setPickedSimilar(null);
4294
+ setIsSubmitting(false);
4295
+ return;
4296
+ }
4297
+ }
3716
4298
  setIsSubmitting(true);
4299
+ let shots = screenshots;
4300
+ const pending = pendingShotRef.current;
4301
+ if (pending) {
4302
+ const shot = await Promise.race([
4303
+ pending.catch(() => null),
4304
+ new Promise((resolve) => setTimeout(() => resolve(null), SHOT_WAIT_MS))
4305
+ ]);
4306
+ if (shot && !shots.includes(shot)) {
4307
+ shots = [shot, ...shots.filter((s) => s !== pageShot)];
4308
+ }
4309
+ }
3717
4310
  const metadata = {};
3718
- if (screenshots.length > 0) metadata.screenshots = JSON.stringify(screenshots);
4311
+ if (shots.length > 0) metadata.screenshots = JSON.stringify(shots);
3719
4312
  if (attachments.length > 0) metadata.attachments = JSON.stringify(attachments);
3720
4313
  if (showSeverity && reportType === "BUG" && severity) {
3721
4314
  metadata.severity = severity;
3722
4315
  }
3723
- if (duplicateIssueNumber) {
3724
- metadata.duplicateIssueNumber = String(duplicateIssueNumber);
4316
+ if (duplicateOf) {
4317
+ metadata.duplicateIssueNumber = String(duplicateOf);
3725
4318
  }
3726
4319
  if (aiConversationId) {
3727
4320
  metadata.aiConversationId = aiConversationId;
@@ -3734,27 +4327,34 @@ function ReportDialog({
3734
4327
  Object.keys(metadata).length > 0 ? metadata : void 0
3735
4328
  );
3736
4329
  if (result) {
4330
+ setAddedToIssue(duplicateOf && result.issueNumber === duplicateOf ? duplicateOf : null);
3737
4331
  setSubmitted(true);
3738
4332
  setDescription("");
3739
4333
  setScreenshots([]);
3740
4334
  setAttachments([]);
3741
4335
  setAiConversationId(null);
3742
4336
  setDuplicateIssueNumber(null);
4337
+ setSimilarIssues(null);
4338
+ setPickedSimilar(null);
3743
4339
  setTimeout(() => {
3744
4340
  setSubmitted(false);
3745
4341
  handleClose();
3746
4342
  }, 2e3);
3747
4343
  }
3748
4344
  setIsSubmitting(false);
4345
+ setAttachingTo(null);
3749
4346
  } catch (e) {
3750
4347
  setIsSubmitting(false);
4348
+ setCheckingSimilar(false);
4349
+ setAttachingTo(null);
3751
4350
  }
3752
4351
  };
3753
4352
  if (!mounted) return null;
3754
- return /* @__PURE__ */ jsxs3(Fragment2, { children: [
3755
- isOpen && createPortal2(
3756
- /* @__PURE__ */ jsx3("style", { children: `
4353
+ return /* @__PURE__ */ jsxs4(Fragment3, { children: [
4354
+ isOpen && createPortal3(
4355
+ /* @__PURE__ */ jsx4("style", { children: `
3757
4356
  @keyframes gg-pulse{0%,100%{opacity:1}50%{opacity:0.35}}
4357
+ @keyframes gg-spin{to{transform:rotate(360deg)}}
3758
4358
  @keyframes gg-b1{0%,100%{height:3px}40%{height:13px}}
3759
4359
  @keyframes gg-b2{0%,100%{height:8px}50%{height:3px}}
3760
4360
  @keyframes gg-b3{0%,100%{height:4px}30%{height:14px}70%{height:5px}}
@@ -3789,8 +4389,8 @@ function ReportDialog({
3789
4389
  ` }),
3790
4390
  document.body
3791
4391
  ),
3792
- isOpen && createPortal2(
3793
- /* @__PURE__ */ jsx3(
4392
+ isOpen && createPortal3(
4393
+ /* @__PURE__ */ jsx4(
3794
4394
  "div",
3795
4395
  {
3796
4396
  "data-glitchgrab-layer": "",
@@ -3813,10 +4413,13 @@ function ReportDialog({
3813
4413
  },
3814
4414
  onPointerDown: (e) => e.stopPropagation(),
3815
4415
  onFocus: (e) => e.stopPropagation(),
3816
- children: /* @__PURE__ */ jsxs3(
4416
+ children: /* @__PURE__ */ jsxs4(
3817
4417
  "div",
3818
4418
  {
3819
4419
  ref: modalRef,
4420
+ role: "dialog",
4421
+ "aria-modal": "true",
4422
+ "aria-label": "Report an issue",
3820
4423
  onClick: (e) => e.stopPropagation(),
3821
4424
  style: {
3822
4425
  position: "relative",
@@ -3835,7 +4438,7 @@ function ReportDialog({
3835
4438
  isolation: "isolate"
3836
4439
  },
3837
4440
  children: [
3838
- /* @__PURE__ */ jsxs3(
4441
+ /* @__PURE__ */ jsxs4(
3839
4442
  "div",
3840
4443
  {
3841
4444
  style: {
@@ -3844,7 +4447,7 @@ function ReportDialog({
3844
4447
  borderBottom: `1px solid ${t2.border}`
3845
4448
  },
3846
4449
  children: [
3847
- /* @__PURE__ */ jsxs3(
4450
+ /* @__PURE__ */ jsxs4(
3848
4451
  "div",
3849
4452
  {
3850
4453
  style: {
@@ -3853,7 +4456,7 @@ function ReportDialog({
3853
4456
  alignItems: "center"
3854
4457
  },
3855
4458
  children: [
3856
- /* @__PURE__ */ jsxs3(
4459
+ /* @__PURE__ */ jsxs4(
3857
4460
  "div",
3858
4461
  {
3859
4462
  style: {
@@ -3862,7 +4465,7 @@ function ReportDialog({
3862
4465
  gap: "8px"
3863
4466
  },
3864
4467
  children: [
3865
- step > 1 && /* @__PURE__ */ jsx3(
4468
+ step > 1 && /* @__PURE__ */ jsx4(
3866
4469
  "button",
3867
4470
  {
3868
4471
  type: "button",
@@ -3876,14 +4479,14 @@ function ReportDialog({
3876
4479
  alignItems: "center"
3877
4480
  },
3878
4481
  "aria-label": "Back",
3879
- children: /* @__PURE__ */ jsx3(
4482
+ children: /* @__PURE__ */ jsx4(
3880
4483
  "svg",
3881
4484
  {
3882
4485
  width: "16",
3883
4486
  height: "16",
3884
4487
  viewBox: "0 0 16 16",
3885
4488
  fill: "none",
3886
- children: /* @__PURE__ */ jsx3(
4489
+ children: /* @__PURE__ */ jsx4(
3887
4490
  "path",
3888
4491
  {
3889
4492
  d: "M10 3L5 8L10 13",
@@ -3897,7 +4500,7 @@ function ReportDialog({
3897
4500
  )
3898
4501
  }
3899
4502
  ),
3900
- /* @__PURE__ */ jsx3(
4503
+ /* @__PURE__ */ jsx4(
3901
4504
  "span",
3902
4505
  {
3903
4506
  style: {
@@ -3911,7 +4514,7 @@ function ReportDialog({
3911
4514
  ]
3912
4515
  }
3913
4516
  ),
3914
- /* @__PURE__ */ jsxs3(
4517
+ /* @__PURE__ */ jsxs4(
3915
4518
  "div",
3916
4519
  {
3917
4520
  style: {
@@ -3920,7 +4523,7 @@ function ReportDialog({
3920
4523
  gap: "8px"
3921
4524
  },
3922
4525
  children: [
3923
- step === 1 && /* @__PURE__ */ jsx3(
4526
+ step === 1 && /* @__PURE__ */ jsx4(
3924
4527
  "span",
3925
4528
  {
3926
4529
  title: "Press this anywhere to open Glitchgrab",
@@ -3938,7 +4541,7 @@ function ReportDialog({
3938
4541
  children: getShortcutLabel()
3939
4542
  }
3940
4543
  ),
3941
- /* @__PURE__ */ jsx3(
4544
+ /* @__PURE__ */ jsx4(
3942
4545
  "button",
3943
4546
  {
3944
4547
  type: "button",
@@ -3953,7 +4556,7 @@ function ReportDialog({
3953
4556
  justifyContent: "center"
3954
4557
  },
3955
4558
  "aria-label": "Close",
3956
- children: /* @__PURE__ */ jsx3(
4559
+ children: /* @__PURE__ */ jsx4(
3957
4560
  "svg",
3958
4561
  {
3959
4562
  width: "16",
@@ -3961,7 +4564,7 @@ function ReportDialog({
3961
4564
  viewBox: "0 0 16 16",
3962
4565
  fill: "none",
3963
4566
  style: { display: "block" },
3964
- children: /* @__PURE__ */ jsx3(
4567
+ children: /* @__PURE__ */ jsx4(
3965
4568
  "path",
3966
4569
  {
3967
4570
  d: "M4 4L12 12M12 4L4 12",
@@ -3980,7 +4583,7 @@ function ReportDialog({
3980
4583
  ]
3981
4584
  }
3982
4585
  ),
3983
- /* @__PURE__ */ jsx3(
4586
+ /* @__PURE__ */ jsx4(
3984
4587
  "div",
3985
4588
  {
3986
4589
  style: {
@@ -3990,12 +4593,12 @@ function ReportDialog({
3990
4593
  gap: "0",
3991
4594
  marginTop: "12px"
3992
4595
  },
3993
- children: [1, 2].map((s, i) => /* @__PURE__ */ jsxs3(
4596
+ children: [1, 2].map((s, i) => /* @__PURE__ */ jsxs4(
3994
4597
  "div",
3995
4598
  {
3996
4599
  style: { display: "flex", alignItems: "center" },
3997
4600
  children: [
3998
- /* @__PURE__ */ jsx3(
4601
+ /* @__PURE__ */ jsx4(
3999
4602
  "div",
4000
4603
  {
4001
4604
  style: {
@@ -4007,7 +4610,7 @@ function ReportDialog({
4007
4610
  }
4008
4611
  }
4009
4612
  ),
4010
- i < 1 && /* @__PURE__ */ jsx3(
4613
+ i < 1 && /* @__PURE__ */ jsx4(
4011
4614
  "div",
4012
4615
  {
4013
4616
  style: {
@@ -4027,7 +4630,7 @@ function ReportDialog({
4027
4630
  ]
4028
4631
  }
4029
4632
  ),
4030
- /* @__PURE__ */ jsx3(
4633
+ /* @__PURE__ */ jsx4(
4031
4634
  "div",
4032
4635
  {
4033
4636
  style: {
@@ -4036,7 +4639,7 @@ function ReportDialog({
4036
4639
  flex: "1 1 auto",
4037
4640
  minHeight: 0
4038
4641
  },
4039
- children: submitted ? /* @__PURE__ */ jsxs3(
4642
+ children: submitted ? /* @__PURE__ */ jsx4(
4040
4643
  "div",
4041
4644
  {
4042
4645
  style: {
@@ -4046,14 +4649,11 @@ function ReportDialog({
4046
4649
  fontSize: "14px",
4047
4650
  fontWeight: 500
4048
4651
  },
4049
- children: [
4050
- getTypeLabel(reportType),
4051
- " sent. Thank you!"
4052
- ]
4652
+ children: addedToIssue ? `Added to #${addedToIssue}. Thank you!` : `${getTypeLabel(reportType)} sent. Thank you!`
4053
4653
  }
4054
- ) : /* @__PURE__ */ jsxs3(Fragment2, { children: [
4055
- step === 1 && headerSlot ? /* @__PURE__ */ jsx3("div", { style: { marginBottom: "14px" }, children: headerSlot }) : null,
4056
- step === 1 && sendFeedback2 && /* @__PURE__ */ jsxs3(
4654
+ ) : /* @__PURE__ */ jsxs4(Fragment3, { children: [
4655
+ step === 1 && headerSlot ? /* @__PURE__ */ jsx4("div", { style: { marginBottom: "14px" }, children: headerSlot }) : null,
4656
+ step === 1 && sendFeedback2 && /* @__PURE__ */ jsxs4(
4057
4657
  "div",
4058
4658
  {
4059
4659
  style: {
@@ -4070,16 +4670,16 @@ function ReportDialog({
4070
4670
  },
4071
4671
  onMouseLeave: () => setHoveredStar(0),
4072
4672
  children: [
4073
- /* @__PURE__ */ jsx3(
4673
+ /* @__PURE__ */ jsx4(
4074
4674
  "span",
4075
4675
  {
4076
4676
  style: { fontSize: "13px", fontWeight: 600, color: t2.text },
4077
4677
  children: "How are we doing?"
4078
4678
  }
4079
4679
  ),
4080
- /* @__PURE__ */ jsx3("span", { style: { display: "flex", gap: "4px" }, children: [1, 2, 3, 4, 5].map((star) => {
4680
+ /* @__PURE__ */ jsx4("span", { style: { display: "flex", gap: "4px" }, children: [1, 2, 3, 4, 5].map((star) => {
4081
4681
  const filled = star <= (hoveredStar || rating);
4082
- return /* @__PURE__ */ jsx3(
4682
+ return /* @__PURE__ */ jsx4(
4083
4683
  "button",
4084
4684
  {
4085
4685
  type: "button",
@@ -4100,7 +4700,7 @@ function ReportDialog({
4100
4700
  cursor: "pointer",
4101
4701
  lineHeight: 0
4102
4702
  },
4103
- children: /* @__PURE__ */ jsx3("svg", { width: "26", height: "26", viewBox: "0 0 24 24", "aria-hidden": "true", children: /* @__PURE__ */ jsx3(
4703
+ children: /* @__PURE__ */ jsx4("svg", { width: "26", height: "26", viewBox: "0 0 24 24", "aria-hidden": "true", children: /* @__PURE__ */ jsx4(
4104
4704
  "path",
4105
4705
  {
4106
4706
  d: "M12 3l2.6 5.3 5.9.9-4.3 4.1 1 5.8L12 16.4 6.8 19.1l1-5.8L3.5 9.2l5.9-.9L12 3z",
@@ -4117,7 +4717,7 @@ function ReportDialog({
4117
4717
  ]
4118
4718
  }
4119
4719
  ),
4120
- step === 1 && sendFeedback2 && /* @__PURE__ */ jsxs3(
4720
+ step === 1 && sendFeedback2 && /* @__PURE__ */ jsxs4(
4121
4721
  "div",
4122
4722
  {
4123
4723
  style: {
@@ -4127,13 +4727,13 @@ function ReportDialog({
4127
4727
  marginBottom: "16px"
4128
4728
  },
4129
4729
  children: [
4130
- /* @__PURE__ */ jsx3("span", { style: { flex: 1, height: "1px", background: t2.border } }),
4131
- /* @__PURE__ */ jsx3("span", { style: { fontSize: "11px", color: t2.textMuted }, children: "or report something" }),
4132
- /* @__PURE__ */ jsx3("span", { style: { flex: 1, height: "1px", background: t2.border } })
4730
+ /* @__PURE__ */ jsx4("span", { style: { flex: 1, height: "1px", background: t2.border } }),
4731
+ /* @__PURE__ */ jsx4("span", { style: { fontSize: "11px", color: t2.textMuted }, children: "or report something" }),
4732
+ /* @__PURE__ */ jsx4("span", { style: { flex: 1, height: "1px", background: t2.border } })
4133
4733
  ]
4134
4734
  }
4135
4735
  ),
4136
- step === 1 && screenshots.length > 0 && /* @__PURE__ */ jsxs3(
4736
+ step === 1 && screenshots.length > 0 && /* @__PURE__ */ jsxs4(
4137
4737
  "div",
4138
4738
  {
4139
4739
  style: {
@@ -4147,7 +4747,7 @@ function ReportDialog({
4147
4747
  background: t2.inputBg
4148
4748
  },
4149
4749
  children: [
4150
- /* @__PURE__ */ jsx3(
4750
+ /* @__PURE__ */ jsx4(
4151
4751
  "img",
4152
4752
  {
4153
4753
  src: screenshots[0],
@@ -4162,8 +4762,8 @@ function ReportDialog({
4162
4762
  }
4163
4763
  }
4164
4764
  ),
4165
- /* @__PURE__ */ jsx3("span", { style: { fontSize: "11px", color: t2.textMuted, flex: 1 }, children: "Screenshot attached" }),
4166
- /* @__PURE__ */ jsx3(
4765
+ /* @__PURE__ */ jsx4("span", { style: { fontSize: "11px", color: t2.textMuted, flex: 1 }, children: "Screenshot attached" }),
4766
+ /* @__PURE__ */ jsx4(
4167
4767
  "button",
4168
4768
  {
4169
4769
  type: "button",
@@ -4184,7 +4784,7 @@ function ReportDialog({
4184
4784
  ]
4185
4785
  }
4186
4786
  ),
4187
- step === 1 && /* @__PURE__ */ jsx3(
4787
+ step === 1 && /* @__PURE__ */ jsx4(
4188
4788
  "div",
4189
4789
  {
4190
4790
  ref: tileGridRef,
@@ -4194,7 +4794,7 @@ function ReportDialog({
4194
4794
  gridTemplateColumns: "1fr 1fr 1fr",
4195
4795
  gap: "10px"
4196
4796
  },
4197
- children: availableTypes.map((type) => /* @__PURE__ */ jsxs3(
4797
+ children: availableTypes.map((type) => /* @__PURE__ */ jsxs4(
4198
4798
  "button",
4199
4799
  {
4200
4800
  type: "button",
@@ -4225,7 +4825,7 @@ function ReportDialog({
4225
4825
  },
4226
4826
  children: [
4227
4827
  getTypeIcon(type, t2.accent, 22),
4228
- /* @__PURE__ */ jsx3(
4828
+ /* @__PURE__ */ jsx4(
4229
4829
  "span",
4230
4830
  {
4231
4831
  style: {
@@ -4243,8 +4843,8 @@ function ReportDialog({
4243
4843
  ))
4244
4844
  }
4245
4845
  ),
4246
- step === 2 && /* @__PURE__ */ jsxs3(Fragment2, { children: [
4247
- /* @__PURE__ */ jsxs3(
4846
+ step === 2 && /* @__PURE__ */ jsxs4(Fragment3, { children: [
4847
+ /* @__PURE__ */ jsxs4(
4248
4848
  "button",
4249
4849
  {
4250
4850
  type: "button",
@@ -4267,11 +4867,11 @@ function ReportDialog({
4267
4867
  children: [
4268
4868
  getTypeIcon(reportType, isRating ? "#f59e0b" : t2.accent, 13),
4269
4869
  getTypeLabel(reportType),
4270
- /* @__PURE__ */ jsx3("span", { style: { color: t2.textMuted, fontWeight: 400 }, children: "\xB7 change" })
4870
+ /* @__PURE__ */ jsx4("span", { style: { color: t2.textMuted, fontWeight: 400 }, children: "\xB7 change" })
4271
4871
  ]
4272
4872
  }
4273
4873
  ),
4274
- isRating && /* @__PURE__ */ jsx3(
4874
+ isRating && /* @__PURE__ */ jsx4(
4275
4875
  "div",
4276
4876
  {
4277
4877
  style: {
@@ -4282,7 +4882,7 @@ function ReportDialog({
4282
4882
  onMouseLeave: () => setHoveredStar(0),
4283
4883
  children: [1, 2, 3, 4, 5].map((star) => {
4284
4884
  const filled = star <= (hoveredStar || rating);
4285
- return /* @__PURE__ */ jsx3(
4885
+ return /* @__PURE__ */ jsx4(
4286
4886
  "button",
4287
4887
  {
4288
4888
  type: "button",
@@ -4299,7 +4899,7 @@ function ReportDialog({
4299
4899
  cursor: "pointer",
4300
4900
  lineHeight: 0
4301
4901
  },
4302
- children: /* @__PURE__ */ jsx3("svg", { width: "30", height: "30", viewBox: "0 0 24 24", "aria-hidden": "true", children: /* @__PURE__ */ jsx3(
4902
+ children: /* @__PURE__ */ jsx4("svg", { width: "30", height: "30", viewBox: "0 0 24 24", "aria-hidden": "true", children: /* @__PURE__ */ jsx4(
4303
4903
  "path",
4304
4904
  {
4305
4905
  d: "M12 3l2.6 5.3 5.9.9-4.3 4.1 1 5.8L12 16.4 6.8 19.1l1-5.8L3.5 9.2l5.9-.9L12 3z",
@@ -4315,7 +4915,7 @@ function ReportDialog({
4315
4915
  })
4316
4916
  }
4317
4917
  ),
4318
- assist && !isRating && !assistUsed && /* @__PURE__ */ jsxs3(
4918
+ assist && !isRating && !assistUsed && /* @__PURE__ */ jsxs4(
4319
4919
  "button",
4320
4920
  {
4321
4921
  type: "button",
@@ -4342,7 +4942,7 @@ function ReportDialog({
4342
4942
  },
4343
4943
  title: "Answer a question or two and the assistant writes the report for you",
4344
4944
  children: [
4345
- /* @__PURE__ */ jsx3("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx3(
4945
+ /* @__PURE__ */ jsx4("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx4(
4346
4946
  "path",
4347
4947
  {
4348
4948
  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",
@@ -4355,8 +4955,8 @@ function ReportDialog({
4355
4955
  ]
4356
4956
  }
4357
4957
  ),
4358
- /* @__PURE__ */ jsxs3("div", { style: { position: "relative" }, children: [
4359
- /* @__PURE__ */ jsx3(
4958
+ /* @__PURE__ */ jsxs4("div", { style: { position: "relative" }, children: [
4959
+ /* @__PURE__ */ jsx4(
4360
4960
  "textarea",
4361
4961
  {
4362
4962
  ref: textareaRef,
@@ -4398,7 +4998,7 @@ function ReportDialog({
4398
4998
  autoFocus: true
4399
4999
  }
4400
5000
  ),
4401
- (isListening || isTranscribing) && /* @__PURE__ */ jsxs3(
5001
+ (isListening || isTranscribing) && /* @__PURE__ */ jsxs4(
4402
5002
  "span",
4403
5003
  {
4404
5004
  style: {
@@ -4416,7 +5016,7 @@ function ReportDialog({
4416
5016
  zIndex: 1
4417
5017
  },
4418
5018
  children: [
4419
- /* @__PURE__ */ jsx3(
5019
+ /* @__PURE__ */ jsx4(
4420
5020
  "span",
4421
5021
  {
4422
5022
  style: {
@@ -4431,7 +5031,7 @@ function ReportDialog({
4431
5031
  }
4432
5032
  ),
4433
5033
  isTranscribing ? "Transcribing\u2026" : "REC",
4434
- isListening && !isTranscribing && /* @__PURE__ */ jsx3(
5034
+ isListening && !isTranscribing && /* @__PURE__ */ jsx4(
4435
5035
  "span",
4436
5036
  {
4437
5037
  style: {
@@ -4458,7 +5058,7 @@ function ReportDialog({
4458
5058
  dur: "0.55s",
4459
5059
  delay: "0.15s"
4460
5060
  }
4461
- ].map((b, i) => /* @__PURE__ */ jsx3(
5061
+ ].map((b, i) => /* @__PURE__ */ jsx4(
4462
5062
  "span",
4463
5063
  {
4464
5064
  style: {
@@ -4478,7 +5078,7 @@ function ReportDialog({
4478
5078
  ]
4479
5079
  }
4480
5080
  ),
4481
- enhanceText2 && /* @__PURE__ */ jsxs3(
5081
+ enhanceText2 && /* @__PURE__ */ jsxs4(
4482
5082
  "button",
4483
5083
  {
4484
5084
  type: "button",
@@ -4523,7 +5123,7 @@ function ReportDialog({
4523
5123
  },
4524
5124
  title: "Polish grammar \u2014 preserves your meaning",
4525
5125
  children: [
4526
- /* @__PURE__ */ jsx3(
5126
+ /* @__PURE__ */ jsx4(
4527
5127
  "svg",
4528
5128
  {
4529
5129
  width: "11",
@@ -4531,7 +5131,7 @@ function ReportDialog({
4531
5131
  viewBox: "0 0 24 24",
4532
5132
  fill: "none",
4533
5133
  "aria-hidden": "true",
4534
- children: /* @__PURE__ */ jsx3(
5134
+ children: /* @__PURE__ */ jsx4(
4535
5135
  "path",
4536
5136
  {
4537
5137
  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",
@@ -4546,7 +5146,7 @@ function ReportDialog({
4546
5146
  ]
4547
5147
  }
4548
5148
  ),
4549
- transcribeAudio2 && /* @__PURE__ */ jsx3(
5149
+ transcribeAudio2 && /* @__PURE__ */ jsx4(
4550
5150
  "button",
4551
5151
  {
4552
5152
  type: "button",
@@ -4572,7 +5172,7 @@ function ReportDialog({
4572
5172
  padding: 0,
4573
5173
  transition: "border-color 0.2s ease, color 0.2s ease"
4574
5174
  },
4575
- children: isListening ? /* @__PURE__ */ jsx3(
5175
+ children: isListening ? /* @__PURE__ */ jsx4(
4576
5176
  "svg",
4577
5177
  {
4578
5178
  width: "12",
@@ -4580,7 +5180,7 @@ function ReportDialog({
4580
5180
  viewBox: "0 0 24 24",
4581
5181
  fill: "currentColor",
4582
5182
  "aria-hidden": "true",
4583
- children: /* @__PURE__ */ jsx3(
5183
+ children: /* @__PURE__ */ jsx4(
4584
5184
  "rect",
4585
5185
  {
4586
5186
  x: "6",
@@ -4591,7 +5191,7 @@ function ReportDialog({
4591
5191
  }
4592
5192
  )
4593
5193
  }
4594
- ) : /* @__PURE__ */ jsxs3(
5194
+ ) : /* @__PURE__ */ jsxs4(
4595
5195
  "svg",
4596
5196
  {
4597
5197
  width: "13",
@@ -4600,7 +5200,7 @@ function ReportDialog({
4600
5200
  fill: "none",
4601
5201
  "aria-hidden": "true",
4602
5202
  children: [
4603
- /* @__PURE__ */ jsx3(
5203
+ /* @__PURE__ */ jsx4(
4604
5204
  "rect",
4605
5205
  {
4606
5206
  x: "9",
@@ -4612,7 +5212,7 @@ function ReportDialog({
4612
5212
  strokeWidth: "1.8"
4613
5213
  }
4614
5214
  ),
4615
- /* @__PURE__ */ jsx3(
5215
+ /* @__PURE__ */ jsx4(
4616
5216
  "path",
4617
5217
  {
4618
5218
  d: "M5 10a7 7 0 0014 0M12 19v3M9 22h6",
@@ -4627,7 +5227,7 @@ function ReportDialog({
4627
5227
  }
4628
5228
  )
4629
5229
  ] }),
4630
- transcribeAudio2 && !isListening && !isTranscribing && !voiceError && /* @__PURE__ */ jsxs3(
5230
+ transcribeAudio2 && !isListening && !isTranscribing && !voiceError && /* @__PURE__ */ jsxs4(
4631
5231
  "div",
4632
5232
  {
4633
5233
  style: {
@@ -4641,16 +5241,16 @@ function ReportDialog({
4641
5241
  userSelect: "none"
4642
5242
  },
4643
5243
  children: [
4644
- /* @__PURE__ */ jsxs3("svg", { width: "11", height: "11", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
4645
- /* @__PURE__ */ jsx3("path", { d: "M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z" }),
4646
- /* @__PURE__ */ jsx3("path", { d: "M19 10v2a7 7 0 0 1-14 0v-2" }),
4647
- /* @__PURE__ */ jsx3("line", { x1: "12", x2: "12", y1: "19", y2: "22" })
5244
+ /* @__PURE__ */ jsxs4("svg", { width: "11", height: "11", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
5245
+ /* @__PURE__ */ jsx4("path", { d: "M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z" }),
5246
+ /* @__PURE__ */ jsx4("path", { d: "M19 10v2a7 7 0 0 1-14 0v-2" }),
5247
+ /* @__PURE__ */ jsx4("line", { x1: "12", x2: "12", y1: "19", y2: "22" })
4648
5248
  ] }),
4649
5249
  "Hold Space to speak \u2014 we'll transcribe it"
4650
5250
  ]
4651
5251
  }
4652
5252
  ),
4653
- isEnhanced && originalDescription !== null && /* @__PURE__ */ jsxs3(
5253
+ isEnhanced && originalDescription !== null && /* @__PURE__ */ jsxs4(
4654
5254
  "div",
4655
5255
  {
4656
5256
  style: {
@@ -4664,7 +5264,7 @@ function ReportDialog({
4664
5264
  border: `1px solid ${t2.accent}40`
4665
5265
  },
4666
5266
  children: [
4667
- /* @__PURE__ */ jsx3(
5267
+ /* @__PURE__ */ jsx4(
4668
5268
  "svg",
4669
5269
  {
4670
5270
  width: "12",
@@ -4672,7 +5272,7 @@ function ReportDialog({
4672
5272
  viewBox: "0 0 24 24",
4673
5273
  fill: "none",
4674
5274
  style: { flexShrink: 0 },
4675
- children: /* @__PURE__ */ jsx3(
5275
+ children: /* @__PURE__ */ jsx4(
4676
5276
  "path",
4677
5277
  {
4678
5278
  d: "M12 3l1.5 4.5L18 9l-4.5 1.5L12 15l-1.5-4.5L6 9l4.5-1.5L12 3z",
@@ -4683,7 +5283,7 @@ function ReportDialog({
4683
5283
  )
4684
5284
  }
4685
5285
  ),
4686
- /* @__PURE__ */ jsx3(
5286
+ /* @__PURE__ */ jsx4(
4687
5287
  "span",
4688
5288
  {
4689
5289
  style: {
@@ -4694,7 +5294,7 @@ function ReportDialog({
4694
5294
  children: "AI enhanced"
4695
5295
  }
4696
5296
  ),
4697
- /* @__PURE__ */ jsx3(
5297
+ /* @__PURE__ */ jsx4(
4698
5298
  "button",
4699
5299
  {
4700
5300
  type: "button",
@@ -4716,7 +5316,7 @@ function ReportDialog({
4716
5316
  children: "Keep"
4717
5317
  }
4718
5318
  ),
4719
- /* @__PURE__ */ jsx3(
5319
+ /* @__PURE__ */ jsx4(
4720
5320
  "button",
4721
5321
  {
4722
5322
  type: "button",
@@ -4742,7 +5342,7 @@ function ReportDialog({
4742
5342
  ]
4743
5343
  }
4744
5344
  ),
4745
- voiceError && /* @__PURE__ */ jsx3(
5345
+ voiceError && /* @__PURE__ */ jsx4(
4746
5346
  "p",
4747
5347
  {
4748
5348
  role: "alert",
@@ -4756,7 +5356,7 @@ function ReportDialog({
4756
5356
  children: voiceError
4757
5357
  }
4758
5358
  ),
4759
- assistNotice && /* @__PURE__ */ jsx3(
5359
+ assistNotice && /* @__PURE__ */ jsx4(
4760
5360
  "p",
4761
5361
  {
4762
5362
  style: {
@@ -4769,8 +5369,8 @@ function ReportDialog({
4769
5369
  children: assistNotice
4770
5370
  }
4771
5371
  ),
4772
- !isRating && /* @__PURE__ */ jsxs3("div", { style: { marginTop: "10px" }, children: [
4773
- /* @__PURE__ */ jsxs3(
5372
+ !isRating && /* @__PURE__ */ jsxs4("div", { style: { marginTop: "10px" }, children: [
5373
+ /* @__PURE__ */ jsxs4(
4774
5374
  "span",
4775
5375
  {
4776
5376
  style: {
@@ -4786,7 +5386,7 @@ function ReportDialog({
4786
5386
  ]
4787
5387
  }
4788
5388
  ),
4789
- screenshots.length > 0 && /* @__PURE__ */ jsx3(
5389
+ (screenshots.length > 0 || shotPending) && /* @__PURE__ */ jsxs4(
4790
5390
  "div",
4791
5391
  {
4792
5392
  style: {
@@ -4795,132 +5395,159 @@ function ReportDialog({
4795
5395
  gap: "8px",
4796
5396
  marginBottom: "8px"
4797
5397
  },
4798
- children: screenshots.map((src, i) => /* @__PURE__ */ jsxs3(
4799
- "div",
4800
- {
4801
- style: {
4802
- position: "relative",
4803
- width: "56px",
4804
- height: "56px"
4805
- },
4806
- children: [
4807
- /* @__PURE__ */ jsx3(
4808
- "img",
4809
- {
4810
- src,
4811
- alt: `Screenshot ${i + 1}`,
4812
- onClick: () => openPreview(i),
4813
- style: {
4814
- width: "100%",
4815
- height: "100%",
4816
- borderRadius: "6px",
4817
- border: `1px solid ${t2.border}`,
4818
- objectFit: "cover",
4819
- objectPosition: "top",
4820
- cursor: "zoom-in",
4821
- display: "block"
4822
- }
4823
- }
4824
- ),
4825
- /* @__PURE__ */ jsx3(
4826
- "button",
4827
- {
4828
- type: "button",
4829
- onClick: () => setScreenshots(
4830
- (prev) => prev.filter((_, idx) => idx !== i)
4831
- ),
4832
- "aria-label": `Remove screenshot ${i + 1}`,
4833
- style: {
4834
- position: "absolute",
4835
- top: "-6px",
4836
- right: "-6px",
4837
- width: "17px",
4838
- height: "17px",
4839
- borderRadius: "50%",
4840
- border: "none",
4841
- background: "#ef4444",
4842
- color: "#fff",
4843
- fontSize: "11px",
4844
- lineHeight: 1,
4845
- padding: 0,
4846
- cursor: "pointer",
4847
- display: "flex",
4848
- alignItems: "center",
4849
- justifyContent: "center"
4850
- },
4851
- children: /* @__PURE__ */ jsx3(
4852
- "svg",
4853
- {
4854
- width: "8",
4855
- height: "8",
4856
- viewBox: "0 0 10 10",
4857
- fill: "none",
4858
- children: /* @__PURE__ */ jsx3(
4859
- "path",
4860
- {
4861
- d: "M1 1L9 9M9 1L1 9",
4862
- stroke: "currentColor",
4863
- strokeWidth: "1.5",
4864
- strokeLinecap: "round"
4865
- }
4866
- )
4867
- }
4868
- )
4869
- }
4870
- ),
4871
- /* @__PURE__ */ jsx3(
4872
- "button",
4873
- {
4874
- type: "button",
4875
- onClick: () => setAnnotatingIndex(i),
4876
- "aria-label": `Annotate screenshot ${i + 1}`,
4877
- title: "Annotate",
4878
- style: {
4879
- position: "absolute",
4880
- top: "50%",
4881
- left: "50%",
4882
- transform: "translate(-50%, -50%)",
4883
- width: "24px",
4884
- height: "24px",
4885
- borderRadius: "50%",
4886
- border: "none",
4887
- background: t2.accent,
4888
- color: t2.accentText,
4889
- padding: 0,
4890
- cursor: "pointer",
4891
- display: "flex",
4892
- alignItems: "center",
4893
- justifyContent: "center",
4894
- boxShadow: "0 2px 8px rgba(0,0,0,0.45)"
4895
- },
4896
- children: /* @__PURE__ */ jsx3(
4897
- "svg",
4898
- {
4899
- width: "12",
4900
- height: "12",
4901
- viewBox: "0 0 24 24",
4902
- fill: "none",
4903
- children: /* @__PURE__ */ jsx3(
4904
- "path",
4905
- {
4906
- d: "M12 20h9M16.5 3.5a2.12 2.12 0 013 3L7 19l-4 1 1-4L16.5 3.5z",
4907
- stroke: "currentColor",
4908
- strokeWidth: "2.2",
4909
- strokeLinecap: "round",
4910
- strokeLinejoin: "round"
4911
- }
4912
- )
5398
+ children: [
5399
+ shotPending && /* @__PURE__ */ jsx4(
5400
+ "div",
5401
+ {
5402
+ role: "status",
5403
+ "aria-label": "Capturing screenshot",
5404
+ title: "Capturing a screenshot of the page\u2026",
5405
+ style: {
5406
+ width: "56px",
5407
+ height: "56px",
5408
+ borderRadius: "6px",
5409
+ border: `1px dashed ${t2.border}`,
5410
+ display: "flex",
5411
+ alignItems: "center",
5412
+ justifyContent: "center",
5413
+ padding: "4px",
5414
+ boxSizing: "border-box",
5415
+ fontSize: "9px",
5416
+ lineHeight: 1.2,
5417
+ textAlign: "center",
5418
+ color: t2.textMuted,
5419
+ animation: "gg-pulse 1.4s ease-in-out infinite"
5420
+ },
5421
+ children: "Capturing\u2026"
5422
+ }
5423
+ ),
5424
+ screenshots.map((src, i) => /* @__PURE__ */ jsxs4(
5425
+ "div",
5426
+ {
5427
+ style: {
5428
+ position: "relative",
5429
+ width: "56px",
5430
+ height: "56px"
5431
+ },
5432
+ children: [
5433
+ /* @__PURE__ */ jsx4(
5434
+ "img",
5435
+ {
5436
+ src,
5437
+ alt: `Screenshot ${i + 1}`,
5438
+ onClick: () => openPreview(i),
5439
+ style: {
5440
+ width: "100%",
5441
+ height: "100%",
5442
+ borderRadius: "6px",
5443
+ border: `1px solid ${t2.border}`,
5444
+ objectFit: "cover",
5445
+ objectPosition: "top",
5446
+ cursor: "zoom-in",
5447
+ display: "block"
4913
5448
  }
4914
- )
4915
- }
4916
- )
4917
- ]
4918
- },
4919
- i
4920
- ))
5449
+ }
5450
+ ),
5451
+ /* @__PURE__ */ jsx4(
5452
+ "button",
5453
+ {
5454
+ type: "button",
5455
+ onClick: () => setScreenshots(
5456
+ (prev) => prev.filter((_, idx) => idx !== i)
5457
+ ),
5458
+ "aria-label": `Remove screenshot ${i + 1}`,
5459
+ style: {
5460
+ position: "absolute",
5461
+ top: "-6px",
5462
+ right: "-6px",
5463
+ width: "17px",
5464
+ height: "17px",
5465
+ borderRadius: "50%",
5466
+ border: "none",
5467
+ background: "#ef4444",
5468
+ color: "#fff",
5469
+ fontSize: "11px",
5470
+ lineHeight: 1,
5471
+ padding: 0,
5472
+ cursor: "pointer",
5473
+ display: "flex",
5474
+ alignItems: "center",
5475
+ justifyContent: "center"
5476
+ },
5477
+ children: /* @__PURE__ */ jsx4(
5478
+ "svg",
5479
+ {
5480
+ width: "8",
5481
+ height: "8",
5482
+ viewBox: "0 0 10 10",
5483
+ fill: "none",
5484
+ children: /* @__PURE__ */ jsx4(
5485
+ "path",
5486
+ {
5487
+ d: "M1 1L9 9M9 1L1 9",
5488
+ stroke: "currentColor",
5489
+ strokeWidth: "1.5",
5490
+ strokeLinecap: "round"
5491
+ }
5492
+ )
5493
+ }
5494
+ )
5495
+ }
5496
+ ),
5497
+ /* @__PURE__ */ jsx4(
5498
+ "button",
5499
+ {
5500
+ type: "button",
5501
+ onClick: () => setAnnotatingIndex(i),
5502
+ "aria-label": `Annotate screenshot ${i + 1}`,
5503
+ title: "Annotate",
5504
+ style: {
5505
+ position: "absolute",
5506
+ top: "50%",
5507
+ left: "50%",
5508
+ transform: "translate(-50%, -50%)",
5509
+ width: "24px",
5510
+ height: "24px",
5511
+ borderRadius: "50%",
5512
+ border: "none",
5513
+ background: t2.accent,
5514
+ color: t2.accentText,
5515
+ padding: 0,
5516
+ cursor: "pointer",
5517
+ display: "flex",
5518
+ alignItems: "center",
5519
+ justifyContent: "center",
5520
+ boxShadow: "0 2px 8px rgba(0,0,0,0.45)"
5521
+ },
5522
+ children: /* @__PURE__ */ jsx4(
5523
+ "svg",
5524
+ {
5525
+ width: "12",
5526
+ height: "12",
5527
+ viewBox: "0 0 24 24",
5528
+ fill: "none",
5529
+ children: /* @__PURE__ */ jsx4(
5530
+ "path",
5531
+ {
5532
+ d: "M12 20h9M16.5 3.5a2.12 2.12 0 013 3L7 19l-4 1 1-4L16.5 3.5z",
5533
+ stroke: "currentColor",
5534
+ strokeWidth: "2.2",
5535
+ strokeLinecap: "round",
5536
+ strokeLinejoin: "round"
5537
+ }
5538
+ )
5539
+ }
5540
+ )
5541
+ }
5542
+ )
5543
+ ]
5544
+ },
5545
+ i
5546
+ ))
5547
+ ]
4921
5548
  }
4922
5549
  ),
4923
- attachments.length > 0 && /* @__PURE__ */ jsx3(
5550
+ attachments.length > 0 && /* @__PURE__ */ jsx4(
4924
5551
  "div",
4925
5552
  {
4926
5553
  style: {
@@ -4929,7 +5556,7 @@ function ReportDialog({
4929
5556
  gap: "6px",
4930
5557
  marginBottom: "8px"
4931
5558
  },
4932
- children: attachments.map((a, i) => /* @__PURE__ */ jsxs3(
5559
+ children: attachments.map((a, i) => /* @__PURE__ */ jsxs4(
4933
5560
  "div",
4934
5561
  {
4935
5562
  style: {
@@ -4941,7 +5568,7 @@ function ReportDialog({
4941
5568
  border: `1px solid ${t2.border}`
4942
5569
  },
4943
5570
  children: [
4944
- /* @__PURE__ */ jsxs3(
5571
+ /* @__PURE__ */ jsxs4(
4945
5572
  "svg",
4946
5573
  {
4947
5574
  width: "14",
@@ -4950,7 +5577,7 @@ function ReportDialog({
4950
5577
  fill: "none",
4951
5578
  style: { flexShrink: 0, opacity: 0.6 },
4952
5579
  children: [
4953
- /* @__PURE__ */ jsx3(
5580
+ /* @__PURE__ */ jsx4(
4954
5581
  "path",
4955
5582
  {
4956
5583
  d: "M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z",
@@ -4960,7 +5587,7 @@ function ReportDialog({
4960
5587
  strokeLinejoin: "round"
4961
5588
  }
4962
5589
  ),
4963
- /* @__PURE__ */ jsx3(
5590
+ /* @__PURE__ */ jsx4(
4964
5591
  "path",
4965
5592
  {
4966
5593
  d: "M14 2v6h6",
@@ -4973,7 +5600,7 @@ function ReportDialog({
4973
5600
  ]
4974
5601
  }
4975
5602
  ),
4976
- /* @__PURE__ */ jsx3(
5603
+ /* @__PURE__ */ jsx4(
4977
5604
  "span",
4978
5605
  {
4979
5606
  style: {
@@ -4987,7 +5614,7 @@ function ReportDialog({
4987
5614
  children: a.name
4988
5615
  }
4989
5616
  ),
4990
- /* @__PURE__ */ jsxs3(
5617
+ /* @__PURE__ */ jsxs4(
4991
5618
  "span",
4992
5619
  {
4993
5620
  style: { fontSize: "10px", color: t2.textMuted },
@@ -4997,7 +5624,7 @@ function ReportDialog({
4997
5624
  ]
4998
5625
  }
4999
5626
  ),
5000
- /* @__PURE__ */ jsx3(
5627
+ /* @__PURE__ */ jsx4(
5001
5628
  "button",
5002
5629
  {
5003
5630
  type: "button",
@@ -5021,14 +5648,14 @@ function ReportDialog({
5021
5648
  justifyContent: "center",
5022
5649
  flexShrink: 0
5023
5650
  },
5024
- children: /* @__PURE__ */ jsx3(
5651
+ children: /* @__PURE__ */ jsx4(
5025
5652
  "svg",
5026
5653
  {
5027
5654
  width: "8",
5028
5655
  height: "8",
5029
5656
  viewBox: "0 0 10 10",
5030
5657
  fill: "none",
5031
- children: /* @__PURE__ */ jsx3(
5658
+ children: /* @__PURE__ */ jsx4(
5032
5659
  "path",
5033
5660
  {
5034
5661
  d: "M1 1L9 9M9 1L1 9",
@@ -5047,7 +5674,7 @@ function ReportDialog({
5047
5674
  ))
5048
5675
  }
5049
5676
  ),
5050
- /* @__PURE__ */ jsx3(
5677
+ /* @__PURE__ */ jsx4(
5051
5678
  "div",
5052
5679
  {
5053
5680
  onClick: () => {
@@ -5073,7 +5700,7 @@ function ReportDialog({
5073
5700
  background: screenshotDragOver ? isDark ? "rgba(34,211,238,0.06)" : "rgba(8,145,178,0.04)" : "transparent",
5074
5701
  transition: "border-color 0.15s ease, background 0.15s ease"
5075
5702
  },
5076
- children: screenshots.length + attachments.length > 0 ? /* @__PURE__ */ jsx3(
5703
+ children: screenshots.length + attachments.length > 0 ? /* @__PURE__ */ jsx4(
5077
5704
  "p",
5078
5705
  {
5079
5706
  style: {
@@ -5084,8 +5711,8 @@ function ReportDialog({
5084
5711
  },
5085
5712
  children: screenshotDragOver ? "Drop to attach" : "+ Add more \xB7 Drag & drop \xB7 Paste"
5086
5713
  }
5087
- ) : /* @__PURE__ */ jsxs3(Fragment2, { children: [
5088
- /* @__PURE__ */ jsxs3(
5714
+ ) : /* @__PURE__ */ jsxs4(Fragment3, { children: [
5715
+ /* @__PURE__ */ jsxs4(
5089
5716
  "svg",
5090
5717
  {
5091
5718
  width: "22",
@@ -5098,7 +5725,7 @@ function ReportDialog({
5098
5725
  opacity: 0.45
5099
5726
  },
5100
5727
  children: [
5101
- /* @__PURE__ */ jsx3(
5728
+ /* @__PURE__ */ jsx4(
5102
5729
  "rect",
5103
5730
  {
5104
5731
  x: "3",
@@ -5110,7 +5737,7 @@ function ReportDialog({
5110
5737
  strokeWidth: "1.5"
5111
5738
  }
5112
5739
  ),
5113
- /* @__PURE__ */ jsx3(
5740
+ /* @__PURE__ */ jsx4(
5114
5741
  "circle",
5115
5742
  {
5116
5743
  cx: "8.5",
@@ -5120,7 +5747,7 @@ function ReportDialog({
5120
5747
  strokeWidth: "1.5"
5121
5748
  }
5122
5749
  ),
5123
- /* @__PURE__ */ jsx3(
5750
+ /* @__PURE__ */ jsx4(
5124
5751
  "path",
5125
5752
  {
5126
5753
  d: "M21 15l-5-5L5 21",
@@ -5133,7 +5760,7 @@ function ReportDialog({
5133
5760
  ]
5134
5761
  }
5135
5762
  ),
5136
- /* @__PURE__ */ jsx3(
5763
+ /* @__PURE__ */ jsx4(
5137
5764
  "p",
5138
5765
  {
5139
5766
  style: {
@@ -5145,7 +5772,7 @@ function ReportDialog({
5145
5772
  children: screenshotDragOver ? "Drop to attach" : "Add screenshots or files"
5146
5773
  }
5147
5774
  ),
5148
- /* @__PURE__ */ jsx3(
5775
+ /* @__PURE__ */ jsx4(
5149
5776
  "p",
5150
5777
  {
5151
5778
  style: {
@@ -5160,8 +5787,8 @@ function ReportDialog({
5160
5787
  }
5161
5788
  )
5162
5789
  ] }),
5163
- showSeverity && reportType === "BUG" && /* @__PURE__ */ jsxs3("div", { style: { marginTop: "10px" }, children: [
5164
- /* @__PURE__ */ jsxs3(
5790
+ showSeverity && reportType === "BUG" && /* @__PURE__ */ jsxs4("div", { style: { marginTop: "10px" }, children: [
5791
+ /* @__PURE__ */ jsxs4(
5165
5792
  "span",
5166
5793
  {
5167
5794
  style: {
@@ -5174,7 +5801,7 @@ function ReportDialog({
5174
5801
  },
5175
5802
  children: [
5176
5803
  "How bad is it?",
5177
- /* @__PURE__ */ jsx3(
5804
+ /* @__PURE__ */ jsx4(
5178
5805
  "span",
5179
5806
  {
5180
5807
  style: {
@@ -5187,7 +5814,7 @@ function ReportDialog({
5187
5814
  ]
5188
5815
  }
5189
5816
  ),
5190
- /* @__PURE__ */ jsx3(
5817
+ /* @__PURE__ */ jsx4(
5191
5818
  "div",
5192
5819
  {
5193
5820
  role: "radiogroup",
@@ -5195,7 +5822,7 @@ function ReportDialog({
5195
5822
  style: { display: "flex", gap: "6px" },
5196
5823
  children: SEVERITY_LEVELS.map((s) => {
5197
5824
  const on = severity === s;
5198
- return /* @__PURE__ */ jsx3(
5825
+ return /* @__PURE__ */ jsx4(
5199
5826
  "button",
5200
5827
  {
5201
5828
  type: "button",
@@ -5236,7 +5863,7 @@ function ReportDialog({
5236
5863
  }
5237
5864
  )
5238
5865
  ] }),
5239
- validationError && /* @__PURE__ */ jsx3(
5866
+ validationError && /* @__PURE__ */ jsx4(
5240
5867
  "p",
5241
5868
  {
5242
5869
  style: {
@@ -5247,11 +5874,194 @@ function ReportDialog({
5247
5874
  children: validationError
5248
5875
  }
5249
5876
  ),
5250
- /* @__PURE__ */ jsx3(
5877
+ similarIssues ? (
5878
+ // Same visual language as the assistant's "Our team is
5879
+ // already on this" card, so both routes to a duplicate
5880
+ // read as one feature.
5881
+ /* @__PURE__ */ jsxs4(
5882
+ "div",
5883
+ {
5884
+ "data-gg-similar": "",
5885
+ style: {
5886
+ marginTop: "12px",
5887
+ display: "flex",
5888
+ flexDirection: "column",
5889
+ gap: "10px",
5890
+ padding: "12px",
5891
+ borderRadius: "10px",
5892
+ border: "1px solid rgba(245,158,11,0.4)",
5893
+ backgroundColor: "rgba(245,158,11,0.08)"
5894
+ },
5895
+ children: [
5896
+ /* @__PURE__ */ jsxs4("div", { style: { display: "flex", flexDirection: "column", gap: "2px" }, children: [
5897
+ /* @__PURE__ */ jsx4("span", { style: { color: t2.text, fontWeight: 600, fontSize: "13px" }, children: "Our team may already have this" }),
5898
+ /* @__PURE__ */ jsx4("span", { style: { color: t2.textMuted, fontSize: "12px", lineHeight: 1.5 }, children: similarIssues.length > 1 ? "Pick the one that matches \u2014 your report is added to it, so the team sees everyone hitting it in one place." : "If it's the same problem, pick it \u2014 your report is added to it, so the team sees everyone hitting it in one place." })
5899
+ ] }),
5900
+ /* @__PURE__ */ jsx4(
5901
+ "div",
5902
+ {
5903
+ role: "radiogroup",
5904
+ "aria-label": "Existing issues",
5905
+ style: { display: "flex", flexDirection: "column", gap: "6px" },
5906
+ children: similarIssues.map((issue) => {
5907
+ const picked = pickedSimilar === issue.number;
5908
+ return /* @__PURE__ */ jsxs4(
5909
+ "button",
5910
+ {
5911
+ type: "button",
5912
+ role: "radio",
5913
+ "aria-checked": picked,
5914
+ disabled: isSubmitting,
5915
+ onClick: () => setPickedSimilar(issue.number),
5916
+ style: {
5917
+ display: "flex",
5918
+ alignItems: "flex-start",
5919
+ gap: "10px",
5920
+ width: "100%",
5921
+ minHeight: "44px",
5922
+ padding: "9px 10px",
5923
+ borderRadius: "8px",
5924
+ border: `1px solid ${picked ? t2.accent : t2.border}`,
5925
+ boxShadow: picked ? `0 0 0 1px ${t2.accent}` : "none",
5926
+ backgroundColor: t2.bg,
5927
+ color: t2.text,
5928
+ textAlign: "left",
5929
+ cursor: isSubmitting ? "not-allowed" : "pointer",
5930
+ fontFamily: "inherit"
5931
+ },
5932
+ children: [
5933
+ /* @__PURE__ */ jsx4(
5934
+ "span",
5935
+ {
5936
+ "aria-hidden": "true",
5937
+ style: {
5938
+ flexShrink: 0,
5939
+ marginTop: "2px",
5940
+ width: "14px",
5941
+ height: "14px",
5942
+ boxSizing: "border-box",
5943
+ borderRadius: "50%",
5944
+ border: `2px solid ${picked ? t2.accent : t2.textMuted}`,
5945
+ backgroundColor: picked ? t2.accent : "transparent",
5946
+ boxShadow: picked ? `inset 0 0 0 2px ${t2.bg}` : "none"
5947
+ }
5948
+ }
5949
+ ),
5950
+ /* @__PURE__ */ jsxs4(
5951
+ "span",
5952
+ {
5953
+ style: {
5954
+ display: "flex",
5955
+ flexDirection: "column",
5956
+ gap: "3px",
5957
+ minWidth: 0
5958
+ },
5959
+ children: [
5960
+ /* @__PURE__ */ jsxs4(
5961
+ "span",
5962
+ {
5963
+ title: issue.title,
5964
+ style: {
5965
+ fontSize: "12.5px",
5966
+ lineHeight: 1.45,
5967
+ wordBreak: "break-word",
5968
+ overflow: "hidden",
5969
+ display: "-webkit-box",
5970
+ WebkitLineClamp: 3,
5971
+ WebkitBoxOrient: "vertical"
5972
+ },
5973
+ children: [
5974
+ /* @__PURE__ */ jsxs4("span", { style: { color: t2.textMuted, fontWeight: 600 }, children: [
5975
+ "#",
5976
+ issue.number
5977
+ ] }),
5978
+ " ",
5979
+ issue.title
5980
+ ]
5981
+ }
5982
+ ),
5983
+ issue.status && /* @__PURE__ */ jsx4("span", { style: { fontSize: "11.5px", color: t2.textMuted }, children: issue.status })
5984
+ ]
5985
+ }
5986
+ )
5987
+ ]
5988
+ },
5989
+ issue.number
5990
+ );
5991
+ })
5992
+ }
5993
+ ),
5994
+ /* @__PURE__ */ jsx4(
5995
+ "button",
5996
+ {
5997
+ type: "button",
5998
+ disabled: isSubmitting || !pickedSimilar,
5999
+ onClick: () => {
6000
+ if (!pickedSimilar) return;
6001
+ setAttachingTo(pickedSimilar);
6002
+ setDuplicateIssueNumber(pickedSimilar);
6003
+ void handleSubmit({ duplicateOf: pickedSimilar });
6004
+ },
6005
+ style: {
6006
+ width: "100%",
6007
+ minHeight: "40px",
6008
+ padding: "10px",
6009
+ borderRadius: "8px",
6010
+ border: "none",
6011
+ backgroundColor: isSubmitting || !pickedSimilar ? t2.bgSecondary : t2.accent,
6012
+ color: isSubmitting || !pickedSimilar ? t2.textMuted : t2.accentText,
6013
+ fontSize: "14px",
6014
+ fontWeight: 600,
6015
+ cursor: isSubmitting || !pickedSimilar ? "not-allowed" : "pointer",
6016
+ fontFamily: "inherit"
6017
+ },
6018
+ children: typeof attachingTo === "number" ? /* @__PURE__ */ jsxs4(Fragment3, { children: [
6019
+ /* @__PURE__ */ jsx4(ButtonSpinner, {}),
6020
+ "Adding to #",
6021
+ attachingTo,
6022
+ "\u2026"
6023
+ ] }) : pickedSimilar ? `Add to #${pickedSimilar}` : "Pick the matching issue"
6024
+ }
6025
+ ),
6026
+ /* @__PURE__ */ jsx4(
6027
+ "button",
6028
+ {
6029
+ type: "button",
6030
+ disabled: isSubmitting,
6031
+ onClick: () => {
6032
+ setAttachingTo("new");
6033
+ void handleSubmit({ skipSimilar: true });
6034
+ },
6035
+ style: {
6036
+ width: "100%",
6037
+ minHeight: "40px",
6038
+ marginTop: "-4px",
6039
+ padding: "8px",
6040
+ borderRadius: "8px",
6041
+ border: "none",
6042
+ backgroundColor: "transparent",
6043
+ color: isSubmitting ? t2.textMuted : t2.text,
6044
+ fontSize: "13px",
6045
+ fontWeight: 500,
6046
+ textDecoration: "underline",
6047
+ textUnderlineOffset: "3px",
6048
+ cursor: isSubmitting ? "not-allowed" : "pointer",
6049
+ fontFamily: "inherit"
6050
+ },
6051
+ children: attachingTo === "new" ? /* @__PURE__ */ jsxs4(Fragment3, { children: [
6052
+ /* @__PURE__ */ jsx4(ButtonSpinner, {}),
6053
+ "Sending as new\u2026"
6054
+ ] }) : "No, mine is different \u2014 send as new"
6055
+ }
6056
+ )
6057
+ ]
6058
+ }
6059
+ )
6060
+ ) : /* @__PURE__ */ jsx4(
5251
6061
  "button",
5252
6062
  {
5253
6063
  type: "button",
5254
- onClick: handleSubmit,
6064
+ onClick: () => void handleSubmit(),
5255
6065
  disabled: submitDisabled,
5256
6066
  style: {
5257
6067
  marginTop: "12px",
@@ -5267,14 +6077,17 @@ function ReportDialog({
5267
6077
  fontFamily: "inherit",
5268
6078
  transition: "background-color 0.15s ease"
5269
6079
  },
5270
- children: isSubmitting ? "Sending..." : isRating ? "Send Rating" : "Send Report"
6080
+ children: checkingSimilar ? /* @__PURE__ */ jsxs4(Fragment3, { children: [
6081
+ /* @__PURE__ */ jsx4(ButtonSpinner, {}),
6082
+ "Checking for existing reports\u2026"
6083
+ ] }) : isSubmitting ? shotPending && !isRating ? "Attaching screenshot\u2026" : "Sending..." : isRating ? "Send Rating" : "Send Report"
5271
6084
  }
5272
6085
  )
5273
6086
  ] })
5274
6087
  ] })
5275
6088
  }
5276
6089
  ),
5277
- /* @__PURE__ */ jsxs3(
6090
+ /* @__PURE__ */ jsxs4(
5278
6091
  "div",
5279
6092
  {
5280
6093
  style: {
@@ -5286,8 +6099,35 @@ function ReportDialog({
5286
6099
  gap: "12px"
5287
6100
  },
5288
6101
  children: [
5289
- /* @__PURE__ */ jsx3(ReporterChip, { reporter, t: t2 }),
5290
- /* @__PURE__ */ jsx3(
6102
+ /* @__PURE__ */ jsx4(ReporterChip, { reporter, t: t2 }),
6103
+ reportGlitchgrabProblem ? /* @__PURE__ */ jsx4(
6104
+ "button",
6105
+ {
6106
+ type: "button",
6107
+ "data-gg-self-report": "",
6108
+ onClick: () => {
6109
+ setSelfReportPrefill("");
6110
+ setSelfReportFrom("form");
6111
+ },
6112
+ title: "Something wrong with this reporter itself? Tell the Glitchgrab team.",
6113
+ style: {
6114
+ fontSize: "11px",
6115
+ color: t2.textMuted,
6116
+ whiteSpace: "nowrap",
6117
+ flexShrink: 0,
6118
+ border: "none",
6119
+ background: "transparent",
6120
+ fontFamily: "inherit",
6121
+ cursor: "pointer",
6122
+ textDecoration: "underline",
6123
+ textUnderlineOffset: "2px",
6124
+ // A ~30px tap target without growing the footer.
6125
+ padding: "9px 0",
6126
+ margin: "-9px 0"
6127
+ },
6128
+ children: "Problem with Glitchgrab?"
6129
+ }
6130
+ ) : /* @__PURE__ */ jsx4(
5291
6131
  "span",
5292
6132
  {
5293
6133
  style: {
@@ -5309,7 +6149,7 @@ function ReportDialog({
5309
6149
  ),
5310
6150
  document.body
5311
6151
  ),
5312
- /* @__PURE__ */ jsx3(
6152
+ /* @__PURE__ */ jsx4(
5313
6153
  "input",
5314
6154
  {
5315
6155
  ref: fileInputRef,
@@ -5320,8 +6160,8 @@ function ReportDialog({
5320
6160
  style: { display: "none" }
5321
6161
  }
5322
6162
  ),
5323
- isOpen && previewIndex !== null && screenshots[previewIndex] && createPortal2(
5324
- /* @__PURE__ */ jsxs3(
6163
+ isOpen && previewIndex !== null && screenshots[previewIndex] && createPortal3(
6164
+ /* @__PURE__ */ jsxs4(
5325
6165
  "div",
5326
6166
  {
5327
6167
  "data-glitchgrab-layer": "",
@@ -5343,7 +6183,7 @@ function ReportDialog({
5343
6183
  },
5344
6184
  onMouseDown: (e) => e.stopPropagation(),
5345
6185
  children: [
5346
- /* @__PURE__ */ jsxs3(
6186
+ /* @__PURE__ */ jsxs4(
5347
6187
  "div",
5348
6188
  {
5349
6189
  ref: previewImgRef,
@@ -5355,7 +6195,7 @@ function ReportDialog({
5355
6195
  },
5356
6196
  onClick: (e) => e.stopPropagation(),
5357
6197
  children: [
5358
- /* @__PURE__ */ jsx3(
6198
+ /* @__PURE__ */ jsx4(
5359
6199
  "div",
5360
6200
  {
5361
6201
  ref: previewScrollRef,
@@ -5374,7 +6214,7 @@ function ReportDialog({
5374
6214
  touchAction: "none",
5375
6215
  cursor: "grab"
5376
6216
  } : { maxWidth: "100%", lineHeight: 0 },
5377
- children: /* @__PURE__ */ jsx3(
6217
+ children: /* @__PURE__ */ jsx4(
5378
6218
  "img",
5379
6219
  {
5380
6220
  ref: previewImgElRef,
@@ -5402,7 +6242,7 @@ function ReportDialog({
5402
6242
  )
5403
6243
  }
5404
6244
  ),
5405
- /* @__PURE__ */ jsxs3(
6245
+ /* @__PURE__ */ jsxs4(
5406
6246
  "button",
5407
6247
  {
5408
6248
  type: "button",
@@ -5430,7 +6270,7 @@ function ReportDialog({
5430
6270
  animation: "gg-pulse 2s ease-in-out infinite"
5431
6271
  },
5432
6272
  children: [
5433
- /* @__PURE__ */ jsx3("svg", { width: "13", height: "13", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx3(
6273
+ /* @__PURE__ */ jsx4("svg", { width: "13", height: "13", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx4(
5434
6274
  "path",
5435
6275
  {
5436
6276
  d: "M12 20h9M16.5 3.5a2.12 2.12 0 013 3L7 19l-4 1 1-4L16.5 3.5z",
@@ -5447,7 +6287,7 @@ function ReportDialog({
5447
6287
  ]
5448
6288
  }
5449
6289
  ),
5450
- /* @__PURE__ */ jsx3(
6290
+ /* @__PURE__ */ jsx4(
5451
6291
  "div",
5452
6292
  {
5453
6293
  style: {
@@ -5456,7 +6296,7 @@ function ReportDialog({
5456
6296
  alignItems: "center",
5457
6297
  gap: "12px"
5458
6298
  },
5459
- children: /* @__PURE__ */ jsx3(
6299
+ children: /* @__PURE__ */ jsx4(
5460
6300
  "span",
5461
6301
  {
5462
6302
  style: {
@@ -5477,8 +6317,8 @@ function ReportDialog({
5477
6317
  ),
5478
6318
  document.body
5479
6319
  ),
5480
- isOpen && annotatingIndex !== null && screenshots[annotatingIndex] && createPortal2(
5481
- /* @__PURE__ */ jsx3(
6320
+ isOpen && annotatingIndex !== null && screenshots[annotatingIndex] && createPortal3(
6321
+ /* @__PURE__ */ jsx4(
5482
6322
  AnnotationCanvas,
5483
6323
  {
5484
6324
  imageSrc: screenshots[annotatingIndex],
@@ -5494,21 +6334,24 @@ function ReportDialog({
5494
6334
  ),
5495
6335
  document.body
5496
6336
  ),
5497
- isOpen && sheetUp && /* @__PURE__ */ jsx3(
6337
+ isOpen && selfReportFrom && reportGlitchgrabProblem && /* @__PURE__ */ jsx4(
6338
+ GlitchgrabProblemPanel,
6339
+ {
6340
+ send: reportGlitchgrabProblem,
6341
+ theme: layerTheme,
6342
+ surface: selfReportFrom,
6343
+ conversationId: aiConversationId,
6344
+ reportType,
6345
+ screenshots,
6346
+ initialText: selfReportPrefill,
6347
+ onClose: () => setSelfReportFrom(null)
6348
+ }
6349
+ ),
6350
+ isOpen && sheetUp && /* @__PURE__ */ jsx4(
5498
6351
  AssistSheet,
5499
6352
  {
5500
6353
  assist,
5501
- theme: {
5502
- bg: t2.bg,
5503
- bgSecondary: t2.bgSecondary,
5504
- border: t2.border,
5505
- text: t2.text,
5506
- textMuted: t2.textMuted,
5507
- inputBg: t2.inputBg,
5508
- inputBorder: t2.inputBorder,
5509
- accent: t2.accent,
5510
- accentText: t2.accentText
5511
- },
6354
+ theme: layerTheme,
5512
6355
  screenshots,
5513
6356
  pageShot,
5514
6357
  onAddImages: addFiles,
@@ -5549,7 +6392,11 @@ function ReportDialog({
5549
6392
  severityRefused: needsSeverity,
5550
6393
  isSubmitting,
5551
6394
  submitted,
5552
- onSend: () => void handleSubmit(),
6395
+ onSend: () => void handleSubmit({ skipSimilar: true }),
6396
+ onReportGlitchgrabProblem: reportGlitchgrabProblem ? (prefill) => {
6397
+ setSelfReportPrefill(prefill != null ? prefill : "");
6398
+ setSelfReportFrom("assist");
6399
+ } : void 0,
5553
6400
  onDegrade: (message) => {
5554
6401
  setAssistOpen(false);
5555
6402
  setAssistUsed(true);
@@ -5596,7 +6443,7 @@ function getTypeIcon(type, color, size = 24) {
5596
6443
  };
5597
6444
  switch (type) {
5598
6445
  case "RATING":
5599
- return /* @__PURE__ */ jsx3("svg", { ...props, children: /* @__PURE__ */ jsx3(
6446
+ return /* @__PURE__ */ jsx4("svg", { ...props, children: /* @__PURE__ */ jsx4(
5600
6447
  "path",
5601
6448
  {
5602
6449
  d: "M12 3l2.6 5.3 5.9.9-4.3 4.1 1 5.8L12 16.4 6.8 19.1l1-5.8L3.5 9.2l5.9-.9L12 3z",
@@ -5606,7 +6453,7 @@ function getTypeIcon(type, color, size = 24) {
5606
6453
  }
5607
6454
  ) });
5608
6455
  case "BUG":
5609
- return /* @__PURE__ */ jsx3("svg", { ...props, children: /* @__PURE__ */ jsx3(
6456
+ return /* @__PURE__ */ jsx4("svg", { ...props, children: /* @__PURE__ */ jsx4(
5610
6457
  "path",
5611
6458
  {
5612
6459
  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",
@@ -5616,7 +6463,7 @@ function getTypeIcon(type, color, size = 24) {
5616
6463
  }
5617
6464
  ) });
5618
6465
  case "FEATURE_REQUEST":
5619
- return /* @__PURE__ */ jsx3("svg", { ...props, children: /* @__PURE__ */ jsx3(
6466
+ return /* @__PURE__ */ jsx4("svg", { ...props, children: /* @__PURE__ */ jsx4(
5620
6467
  "path",
5621
6468
  {
5622
6469
  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",
@@ -5626,8 +6473,8 @@ function getTypeIcon(type, color, size = 24) {
5626
6473
  }
5627
6474
  ) });
5628
6475
  case "UI_IMPROVEMENT":
5629
- return /* @__PURE__ */ jsxs3("svg", { ...props, children: [
5630
- /* @__PURE__ */ jsx3(
6476
+ return /* @__PURE__ */ jsxs4("svg", { ...props, children: [
6477
+ /* @__PURE__ */ jsx4(
5631
6478
  "rect",
5632
6479
  {
5633
6480
  x: "3",
@@ -5639,7 +6486,7 @@ function getTypeIcon(type, color, size = 24) {
5639
6486
  strokeWidth: "1.5"
5640
6487
  }
5641
6488
  ),
5642
- /* @__PURE__ */ jsx3(
6489
+ /* @__PURE__ */ jsx4(
5643
6490
  "path",
5644
6491
  {
5645
6492
  d: "M3 9h18M9 21V9",
@@ -5650,7 +6497,7 @@ function getTypeIcon(type, color, size = 24) {
5650
6497
  )
5651
6498
  ] });
5652
6499
  case "PERFORMANCE":
5653
- return /* @__PURE__ */ jsx3("svg", { ...props, children: /* @__PURE__ */ jsx3(
6500
+ return /* @__PURE__ */ jsx4("svg", { ...props, children: /* @__PURE__ */ jsx4(
5654
6501
  "path",
5655
6502
  {
5656
6503
  d: "M12 21a9 9 0 100-18 9 9 0 000 18zM12 12l4-4M12 7v.01",
@@ -5661,7 +6508,7 @@ function getTypeIcon(type, color, size = 24) {
5661
6508
  }
5662
6509
  ) });
5663
6510
  case "SECURITY":
5664
- return /* @__PURE__ */ jsx3("svg", { ...props, children: /* @__PURE__ */ jsx3(
6511
+ return /* @__PURE__ */ jsx4("svg", { ...props, children: /* @__PURE__ */ jsx4(
5665
6512
  "path",
5666
6513
  {
5667
6514
  d: "M12 3l7 3v5c0 4.5-3 8.5-7 10-4-1.5-7-5.5-7-10V6l7-3z",
@@ -5671,9 +6518,9 @@ function getTypeIcon(type, color, size = 24) {
5671
6518
  }
5672
6519
  ) });
5673
6520
  case "QUESTION":
5674
- return /* @__PURE__ */ jsxs3("svg", { ...props, children: [
5675
- /* @__PURE__ */ jsx3("circle", { cx: "12", cy: "12", r: "10", stroke: color, strokeWidth: "1.5" }),
5676
- /* @__PURE__ */ jsx3(
6521
+ return /* @__PURE__ */ jsxs4("svg", { ...props, children: [
6522
+ /* @__PURE__ */ jsx4("circle", { cx: "12", cy: "12", r: "10", stroke: color, strokeWidth: "1.5" }),
6523
+ /* @__PURE__ */ jsx4(
5677
6524
  "path",
5678
6525
  {
5679
6526
  d: "M9.09 9a3 3 0 015.83 1c0 2-3 3-3 3M12 17h.01",
@@ -5685,7 +6532,7 @@ function getTypeIcon(type, color, size = 24) {
5685
6532
  )
5686
6533
  ] });
5687
6534
  case "OTHER":
5688
- return /* @__PURE__ */ jsx3("svg", { ...props, children: /* @__PURE__ */ jsx3(
6535
+ return /* @__PURE__ */ jsx4("svg", { ...props, children: /* @__PURE__ */ jsx4(
5689
6536
  "path",
5690
6537
  {
5691
6538
  d: "M21 15a2 2 0 01-2 2H7l-4 4V5a2 2 0 012-2h14a2 2 0 012 2v10z",
@@ -5698,8 +6545,8 @@ function getTypeIcon(type, color, size = 24) {
5698
6545
  }
5699
6546
 
5700
6547
  // src/booking-dialog.tsx
5701
- import { useCallback, useEffect as useEffect4, useMemo, useState as useState4 } from "react";
5702
- import { Fragment as Fragment3, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
6548
+ import { useCallback, useEffect as useEffect5, useMemo as useMemo2, useState as useState5 } from "react";
6549
+ import { Fragment as Fragment4, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
5703
6550
  var t = {
5704
6551
  bg: "#141414",
5705
6552
  surface: "#1c1c1c",
@@ -5736,24 +6583,24 @@ function BookingDialog({
5736
6583
  onClose
5737
6584
  }) {
5738
6585
  var _a;
5739
- const [step, setStep] = useState4("slots");
5740
- const [slots, setSlots] = useState4(null);
5741
- const [heading, setHeading] = useState4({
6586
+ const [step, setStep] = useState5("slots");
6587
+ const [slots, setSlots] = useState5(null);
6588
+ const [heading, setHeading] = useState5({
5742
6589
  title: "Book a demo",
5743
6590
  description: null
5744
6591
  });
5745
- const [whatsappUrl, setWhatsappUrl] = useState4(null);
5746
- const [selected, setSelected] = useState4(null);
5747
- const [name, setName] = useState4("");
5748
- const [email, setEmail] = useState4("");
5749
- const [phone, setPhone] = useState4("");
5750
- const [note, setNote] = useState4("");
5751
- const [code, setCode] = useState4("");
5752
- const [bookingId, setBookingId] = useState4(null);
5753
- const [meetUrl, setMeetUrl] = useState4(null);
5754
- const [busy, setBusy] = useState4(false);
5755
- const [error, setError] = useState4(null);
5756
- const headers = useMemo(
6592
+ const [whatsappUrl, setWhatsappUrl] = useState5(null);
6593
+ const [selected, setSelected] = useState5(null);
6594
+ const [name, setName] = useState5("");
6595
+ const [email, setEmail] = useState5("");
6596
+ const [phone, setPhone] = useState5("");
6597
+ const [note, setNote] = useState5("");
6598
+ const [code, setCode] = useState5("");
6599
+ const [bookingId, setBookingId] = useState5(null);
6600
+ const [meetUrl, setMeetUrl] = useState5(null);
6601
+ const [busy, setBusy] = useState5(false);
6602
+ const [error, setError] = useState5(null);
6603
+ const headers = useMemo2(
5757
6604
  () => ({ Authorization: `Bearer ${token}`, "Content-Type": "application/json" }),
5758
6605
  [token]
5759
6606
  );
@@ -5782,7 +6629,7 @@ function BookingDialog({
5782
6629
  setSlots([]);
5783
6630
  }
5784
6631
  }, [baseUrl, headers]);
5785
- useEffect4(() => {
6632
+ useEffect5(() => {
5786
6633
  if (!open) return;
5787
6634
  setStep("slots");
5788
6635
  setSelected(null);
@@ -5791,7 +6638,7 @@ function BookingDialog({
5791
6638
  setError(null);
5792
6639
  void loadSlots();
5793
6640
  }, [open, loadSlots]);
5794
- useEffect4(() => {
6641
+ useEffect5(() => {
5795
6642
  if (!open) return;
5796
6643
  const onKey = (e) => {
5797
6644
  if (e.key === "Escape") onClose();
@@ -5861,7 +6708,7 @@ function BookingDialog({
5861
6708
  }
5862
6709
  }
5863
6710
  const canSubmitDetails = name.trim() && email.includes("@") && phone.replace(/\D/g, "").length >= 8;
5864
- return /* @__PURE__ */ jsx4(
6711
+ return /* @__PURE__ */ jsx5(
5865
6712
  "div",
5866
6713
  {
5867
6714
  onClick: (e) => {
@@ -5878,7 +6725,7 @@ function BookingDialog({
5878
6725
  padding: 16,
5879
6726
  fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif"
5880
6727
  },
5881
- children: /* @__PURE__ */ jsxs4(
6728
+ children: /* @__PURE__ */ jsxs5(
5882
6729
  "div",
5883
6730
  {
5884
6731
  role: "dialog",
@@ -5896,7 +6743,7 @@ function BookingDialog({
5896
6743
  overflow: "hidden"
5897
6744
  },
5898
6745
  children: [
5899
- /* @__PURE__ */ jsxs4(
6746
+ /* @__PURE__ */ jsxs5(
5900
6747
  "div",
5901
6748
  {
5902
6749
  style: {
@@ -5908,11 +6755,11 @@ function BookingDialog({
5908
6755
  gap: 12
5909
6756
  },
5910
6757
  children: [
5911
- /* @__PURE__ */ jsxs4("div", { children: [
5912
- /* @__PURE__ */ jsx4("div", { style: { fontSize: 15, fontWeight: 600 }, children: heading.title }),
5913
- heading.description && /* @__PURE__ */ jsx4("div", { style: { fontSize: 12, color: t.muted, marginTop: 2 }, children: heading.description })
6758
+ /* @__PURE__ */ jsxs5("div", { children: [
6759
+ /* @__PURE__ */ jsx5("div", { style: { fontSize: 15, fontWeight: 600 }, children: heading.title }),
6760
+ heading.description && /* @__PURE__ */ jsx5("div", { style: { fontSize: 12, color: t.muted, marginTop: 2 }, children: heading.description })
5914
6761
  ] }),
5915
- /* @__PURE__ */ jsx4(
6762
+ /* @__PURE__ */ jsx5(
5916
6763
  "button",
5917
6764
  {
5918
6765
  onClick: onClose,
@@ -5924,10 +6771,10 @@ function BookingDialog({
5924
6771
  ]
5925
6772
  }
5926
6773
  ),
5927
- /* @__PURE__ */ jsxs4("div", { style: { padding: 18, overflowY: "auto", flex: "1 1 auto" }, children: [
5928
- error && /* @__PURE__ */ jsx4("div", { style: { color: t.danger, fontSize: 12, marginBottom: 12, lineHeight: 1.5 }, children: error }),
5929
- step === "slots" && /* @__PURE__ */ jsxs4(Fragment3, { children: [
5930
- whatsappUrl && /* @__PURE__ */ jsxs4(
6774
+ /* @__PURE__ */ jsxs5("div", { style: { padding: 18, overflowY: "auto", flex: "1 1 auto" }, children: [
6775
+ error && /* @__PURE__ */ jsx5("div", { style: { color: t.danger, fontSize: 12, marginBottom: 12, lineHeight: 1.5 }, children: error }),
6776
+ step === "slots" && /* @__PURE__ */ jsxs5(Fragment4, { children: [
6777
+ whatsappUrl && /* @__PURE__ */ jsxs5(
5931
6778
  "a",
5932
6779
  {
5933
6780
  href: whatsappUrl,
@@ -5946,15 +6793,15 @@ function BookingDialog({
5946
6793
  },
5947
6794
  children: [
5948
6795
  "\u{1F4AC} Rather book on WhatsApp?",
5949
- /* @__PURE__ */ jsx4("span", { style: { display: "block", fontSize: 11, color: t.muted, marginTop: 2 }, children: "We'll ask the same questions in chat." })
6796
+ /* @__PURE__ */ jsx5("span", { style: { display: "block", fontSize: 11, color: t.muted, marginTop: 2 }, children: "We'll ask the same questions in chat." })
5950
6797
  ]
5951
6798
  }
5952
6799
  ),
5953
- slots === null && /* @__PURE__ */ jsx4("div", { style: { color: t.muted, fontSize: 13 }, children: "Finding times\u2026" }),
5954
- (slots == null ? void 0 : slots.length) === 0 && !error && /* @__PURE__ */ jsx4("div", { style: { color: t.muted, fontSize: 13 }, children: "No times available right now." }),
5955
- [...byDay.entries()].map(([day, daySlots]) => /* @__PURE__ */ jsxs4("div", { style: { marginBottom: 18 }, children: [
5956
- /* @__PURE__ */ jsx4("div", { style: { fontSize: 11, color: t.muted, marginBottom: 8, textTransform: "uppercase", letterSpacing: ".6px" }, children: formatDay(daySlots[0].startsAt) }),
5957
- /* @__PURE__ */ jsx4("div", { style: { display: "flex", flexWrap: "wrap", gap: 8 }, children: daySlots.map((slot) => /* @__PURE__ */ jsx4(
6800
+ slots === null && /* @__PURE__ */ jsx5("div", { style: { color: t.muted, fontSize: 13 }, children: "Finding times\u2026" }),
6801
+ (slots == null ? void 0 : slots.length) === 0 && !error && /* @__PURE__ */ jsx5("div", { style: { color: t.muted, fontSize: 13 }, children: "No times available right now." }),
6802
+ [...byDay.entries()].map(([day, daySlots]) => /* @__PURE__ */ jsxs5("div", { style: { marginBottom: 18 }, children: [
6803
+ /* @__PURE__ */ jsx5("div", { style: { fontSize: 11, color: t.muted, marginBottom: 8, textTransform: "uppercase", letterSpacing: ".6px" }, children: formatDay(daySlots[0].startsAt) }),
6804
+ /* @__PURE__ */ jsx5("div", { style: { display: "flex", flexWrap: "wrap", gap: 8 }, children: daySlots.map((slot) => /* @__PURE__ */ jsx5(
5958
6805
  "button",
5959
6806
  {
5960
6807
  onClick: () => {
@@ -5976,13 +6823,13 @@ function BookingDialog({
5976
6823
  )) })
5977
6824
  ] }, day))
5978
6825
  ] }),
5979
- step === "details" && selected && /* @__PURE__ */ jsxs4("div", { style: { display: "flex", flexDirection: "column", gap: 10 }, children: [
5980
- /* @__PURE__ */ jsxs4("div", { style: { fontSize: 13, color: t.muted }, children: [
6826
+ step === "details" && selected && /* @__PURE__ */ jsxs5("div", { style: { display: "flex", flexDirection: "column", gap: 10 }, children: [
6827
+ /* @__PURE__ */ jsxs5("div", { style: { fontSize: 13, color: t.muted }, children: [
5981
6828
  formatDay(selected.startsAt),
5982
6829
  " \xB7 ",
5983
6830
  formatTime(selected.startsAt),
5984
6831
  " \xB7 ",
5985
- /* @__PURE__ */ jsx4(
6832
+ /* @__PURE__ */ jsx5(
5986
6833
  "button",
5987
6834
  {
5988
6835
  onClick: () => setStep("slots"),
@@ -5991,9 +6838,9 @@ function BookingDialog({
5991
6838
  }
5992
6839
  )
5993
6840
  ] }),
5994
- /* @__PURE__ */ jsx4(Field, { label: "Your name", value: name, onChange: setName }),
5995
- /* @__PURE__ */ jsx4(Field, { label: "Email", value: email, onChange: setEmail, type: "email" }),
5996
- /* @__PURE__ */ jsx4(
6841
+ /* @__PURE__ */ jsx5(Field, { label: "Your name", value: name, onChange: setName }),
6842
+ /* @__PURE__ */ jsx5(Field, { label: "Email", value: email, onChange: setEmail, type: "email" }),
6843
+ /* @__PURE__ */ jsx5(
5997
6844
  Field,
5998
6845
  {
5999
6846
  label: "Mobile number",
@@ -6003,8 +6850,8 @@ function BookingDialog({
6003
6850
  hint: "We send a code here to confirm, then a reminder before the call."
6004
6851
  }
6005
6852
  ),
6006
- /* @__PURE__ */ jsx4(Field, { label: "Anything we should know? (optional)", value: note, onChange: setNote }),
6007
- /* @__PURE__ */ jsx4(
6853
+ /* @__PURE__ */ jsx5(Field, { label: "Anything we should know? (optional)", value: note, onChange: setNote }),
6854
+ /* @__PURE__ */ jsx5(
6008
6855
  "button",
6009
6856
  {
6010
6857
  onClick: startBooking,
@@ -6014,10 +6861,10 @@ function BookingDialog({
6014
6861
  }
6015
6862
  )
6016
6863
  ] }),
6017
- step === "code" && /* @__PURE__ */ jsxs4("div", { style: { display: "flex", flexDirection: "column", gap: 10 }, children: [
6018
- /* @__PURE__ */ jsx4("div", { style: { fontSize: 13, color: t.muted, lineHeight: 1.5 }, children: "We sent a 4-digit code to your phone. Your slot is held for 10 minutes." }),
6019
- /* @__PURE__ */ jsx4(Field, { label: "Code", value: code, onChange: setCode }),
6020
- /* @__PURE__ */ jsx4(
6864
+ step === "code" && /* @__PURE__ */ jsxs5("div", { style: { display: "flex", flexDirection: "column", gap: 10 }, children: [
6865
+ /* @__PURE__ */ jsx5("div", { style: { fontSize: 13, color: t.muted, lineHeight: 1.5 }, children: "We sent a 4-digit code to your phone. Your slot is held for 10 minutes." }),
6866
+ /* @__PURE__ */ jsx5(Field, { label: "Code", value: code, onChange: setCode }),
6867
+ /* @__PURE__ */ jsx5(
6021
6868
  "button",
6022
6869
  {
6023
6870
  onClick: confirmBooking,
@@ -6027,15 +6874,15 @@ function BookingDialog({
6027
6874
  }
6028
6875
  )
6029
6876
  ] }),
6030
- step === "done" && /* @__PURE__ */ jsxs4("div", { style: { display: "flex", flexDirection: "column", gap: 10 }, children: [
6031
- /* @__PURE__ */ jsx4("div", { style: { fontSize: 15, fontWeight: 600 }, children: "You're booked." }),
6032
- /* @__PURE__ */ jsxs4("div", { style: { fontSize: 13, color: t.muted, lineHeight: 1.6 }, children: [
6877
+ step === "done" && /* @__PURE__ */ jsxs5("div", { style: { display: "flex", flexDirection: "column", gap: 10 }, children: [
6878
+ /* @__PURE__ */ jsx5("div", { style: { fontSize: 15, fontWeight: 600 }, children: "You're booked." }),
6879
+ /* @__PURE__ */ jsxs5("div", { style: { fontSize: 13, color: t.muted, lineHeight: 1.6 }, children: [
6033
6880
  selected && `${formatDay(selected.startsAt)} at ${formatTime(selected.startsAt)}. `,
6034
6881
  "A calendar invite is on its way to ",
6035
6882
  email,
6036
6883
  ", and we'll send a WhatsApp reminder before the call."
6037
6884
  ] }),
6038
- meetUrl && /* @__PURE__ */ jsx4("a", { href: meetUrl, target: "_blank", rel: "noreferrer", style: { ...primaryStyle(false), textAlign: "center", textDecoration: "none" }, children: "Open the meeting link" })
6885
+ meetUrl && /* @__PURE__ */ jsx5("a", { href: meetUrl, target: "_blank", rel: "noreferrer", style: { ...primaryStyle(false), textAlign: "center", textDecoration: "none" }, children: "Open the meeting link" })
6039
6886
  ] })
6040
6887
  ] })
6041
6888
  ]
@@ -6064,9 +6911,9 @@ function Field({
6064
6911
  type = "text",
6065
6912
  hint
6066
6913
  }) {
6067
- return /* @__PURE__ */ jsxs4("label", { style: { display: "block", fontSize: 11, color: t.muted }, children: [
6914
+ return /* @__PURE__ */ jsxs5("label", { style: { display: "block", fontSize: 11, color: t.muted }, children: [
6068
6915
  label,
6069
- /* @__PURE__ */ jsx4(
6916
+ /* @__PURE__ */ jsx5(
6070
6917
  "input",
6071
6918
  {
6072
6919
  type,
@@ -6087,7 +6934,7 @@ function Field({
6087
6934
  }
6088
6935
  }
6089
6936
  ),
6090
- hint && /* @__PURE__ */ jsx4("div", { style: { marginTop: 4, fontSize: 10, color: t.muted }, children: hint })
6937
+ hint && /* @__PURE__ */ jsx5("div", { style: { marginTop: 4, fontSize: 10, color: t.muted }, children: hint })
6091
6938
  ] });
6092
6939
  }
6093
6940
 
@@ -6228,7 +7075,7 @@ function matchesShortcut(e) {
6228
7075
  }
6229
7076
 
6230
7077
  // src/provider.tsx
6231
- import { Fragment as Fragment4, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
7078
+ import { Fragment as Fragment5, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
6232
7079
  var DEFAULT_BASE_URL2 = "https://glitchgrab.dev";
6233
7080
  var GlitchgrabContext = createContext(null);
6234
7081
  function useGlitchgrab() {
@@ -6240,7 +7087,7 @@ function useGlitchgrab() {
6240
7087
  }
6241
7088
  function GlitchgrabProviderInner({
6242
7089
  token,
6243
- session,
7090
+ session: sessionProp,
6244
7091
  baseUrl,
6245
7092
  onError,
6246
7093
  onReportSent,
@@ -6255,7 +7102,16 @@ function GlitchgrabProviderInner({
6255
7102
  context: appContext2,
6256
7103
  responseBodyOrigins: responseBodyOrigins2
6257
7104
  }) {
6258
- const visitedPagesRef = useRef4([]);
7105
+ const visitedPagesRef = useRef5([]);
7106
+ const sessionKey = sessionProp ? [
7107
+ sessionProp.userId,
7108
+ sessionProp.name,
7109
+ sessionProp.email,
7110
+ sessionProp.phone,
7111
+ sessionProp.avatarUrl,
7112
+ sessionProp.image
7113
+ ].map((v) => v == null ? "" : String(v)).join("\0") : "";
7114
+ const session = useMemo3(() => sessionProp, [sessionKey]);
6259
7115
  setRelease(release2);
6260
7116
  if (appContext2) setContexts(appContext2);
6261
7117
  registerCaptureConfig({
@@ -6267,12 +7123,12 @@ function GlitchgrabProviderInner({
6267
7123
  onError,
6268
7124
  onReportSent
6269
7125
  });
6270
- useEffect5(() => {
7126
+ useEffect6(() => {
6271
7127
  if (enableBreadcrumbs) {
6272
7128
  initBreadcrumbs(maxBreadcrumbs2, { responseBodyOrigins: responseBodyOrigins2 });
6273
7129
  }
6274
7130
  }, [enableBreadcrumbs, maxBreadcrumbs2, responseBodyOrigins2 == null ? void 0 : responseBodyOrigins2.join(",")]);
6275
- useEffect5(() => {
7131
+ useEffect6(() => {
6276
7132
  try {
6277
7133
  if (typeof window === "undefined") return;
6278
7134
  const trackPage = () => {
@@ -6309,7 +7165,7 @@ function GlitchgrabProviderInner({
6309
7165
  } catch (e) {
6310
7166
  }
6311
7167
  }, []);
6312
- useEffect5(() => {
7168
+ useEffect6(() => {
6313
7169
  try {
6314
7170
  if (typeof window === "undefined") return;
6315
7171
  if (process.env.NODE_ENV === "development") return;
@@ -6474,6 +7330,40 @@ function GlitchgrabProviderInner({
6474
7330
  (description, metadata) => report("BUG", description, metadata),
6475
7331
  [report]
6476
7332
  );
7333
+ const reportGlitchgrabProblem = useCallback2(
7334
+ async (params) => {
7335
+ var _a, _b;
7336
+ try {
7337
+ const context = captureContext(visitedPagesRef.current);
7338
+ const body = JSON.stringify({
7339
+ ...params,
7340
+ pageUrl: context.url,
7341
+ userAgent: context.userAgent,
7342
+ deviceInfo: (_a = context.deviceInfo) != null ? _a : void 0,
7343
+ visitedPages: context.visitedPages,
7344
+ breadcrumbs: context.breadcrumbs,
7345
+ ...(session == null ? void 0 : session.userId) ? { reporterId: session.userId } : {},
7346
+ ...(session == null ? void 0 : session.name) ? { reporterName: String(session.name) } : {},
7347
+ ...(session == null ? void 0 : session.email) ? { reporterEmail: String(session.email) } : {}
7348
+ });
7349
+ const res = await fetch(`${baseUrl}/api/v1/sdk/glitchgrab-report`, {
7350
+ method: "POST",
7351
+ headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` },
7352
+ body,
7353
+ // keepalive caps the body at 64KB — screenshots blow past it.
7354
+ ...body.length < 6e4 ? { keepalive: true } : {}
7355
+ });
7356
+ const json = await res.json().catch(() => null);
7357
+ if (!res.ok || !(json == null ? void 0 : json.success)) {
7358
+ return { success: false, message: (_b = json == null ? void 0 : json.error) != null ? _b : "Couldn't send it" };
7359
+ }
7360
+ return { success: true, ...json.data };
7361
+ } catch (e) {
7362
+ return null;
7363
+ }
7364
+ },
7365
+ [token, baseUrl, session]
7366
+ );
6477
7367
  const feedback = useCallback2(
6478
7368
  async (rating, message, metadata) => {
6479
7369
  try {
@@ -6527,6 +7417,12 @@ function GlitchgrabProviderInner({
6527
7417
  },
6528
7418
  []
6529
7419
  );
7420
+ const signedUserId = session == null ? void 0 : session.userId;
7421
+ const signature = typeof (session == null ? void 0 : session.signature) === "string" ? session.signature : null;
7422
+ const identity = useMemo3(
7423
+ () => ({ userId: signedUserId != null ? signedUserId : null, signature }),
7424
+ [signedUserId, signature]
7425
+ );
6530
7426
  const enhance = useCallback2(
6531
7427
  async (text, screenshot) => {
6532
7428
  try {
@@ -6536,12 +7432,12 @@ function GlitchgrabProviderInner({
6536
7432
  visitedPages: ctx.visitedPages.slice(-5),
6537
7433
  breadcrumbs: ctx.breadcrumbs.slice(-10).map((b) => ({ type: b.type, message: b.message }))
6538
7434
  };
6539
- return await enhanceText(text, token, baseUrl, screenshot, context);
7435
+ return await enhanceText(text, token, baseUrl, screenshot, context, identity);
6540
7436
  } catch (e) {
6541
7437
  return text;
6542
7438
  }
6543
7439
  },
6544
- [token, baseUrl]
7440
+ [token, baseUrl, identity]
6545
7441
  );
6546
7442
  const assist = useCallback2(
6547
7443
  async (params) => {
@@ -6554,7 +7450,8 @@ function GlitchgrabProviderInner({
6554
7450
  reporter: session ? {
6555
7451
  key: session.userId,
6556
7452
  name: session.name ? String(session.name) : null,
6557
- email: session.email ? String(session.email) : null
7453
+ email: session.email ? String(session.email) : null,
7454
+ ...signature ? { signature } : {}
6558
7455
  } : void 0,
6559
7456
  context: {
6560
7457
  ...(_a = params.context) != null ? _a : {},
@@ -6569,22 +7466,26 @@ function GlitchgrabProviderInner({
6569
7466
  },
6570
7467
  [token, baseUrl, session]
6571
7468
  );
7469
+ const findSimilar = useCallback2(
7470
+ (description) => findSimilarIssues(description, token, baseUrl, identity),
7471
+ [token, baseUrl, identity]
7472
+ );
6572
7473
  const transcribe = useCallback2(
6573
7474
  async (blob) => {
6574
7475
  try {
6575
- return await transcribeAudio(blob, token, baseUrl);
7476
+ return await transcribeAudio(blob, token, baseUrl, identity);
6576
7477
  } catch (e) {
6577
7478
  return "";
6578
7479
  }
6579
7480
  },
6580
- [token, baseUrl]
7481
+ [token, baseUrl, identity]
6581
7482
  );
6582
7483
  const openReportDialog = useCallback2((options) => {
6583
7484
  if (typeof window !== "undefined") {
6584
7485
  window.dispatchEvent(new CustomEvent("glitchgrab:open-report", { detail: options }));
6585
7486
  }
6586
7487
  }, []);
6587
- useEffect5(() => {
7488
+ useEffect6(() => {
6588
7489
  try {
6589
7490
  if (typeof document === "undefined") return;
6590
7491
  document.documentElement.setAttribute("data-glitchgrab-sdk", "1");
@@ -6592,11 +7493,14 @@ function GlitchgrabProviderInner({
6592
7493
  } catch (e) {
6593
7494
  }
6594
7495
  }, []);
6595
- const [projectName, setProjectName] = useState5(null);
6596
- const [aiAssist, setAiAssist] = useState5(false);
6597
- const [bookingOpen, setBookingOpen] = useState5(false);
7496
+ const [projectName, setProjectName] = useState6(null);
7497
+ const [aiAssist, setAiAssist] = useState6(false);
7498
+ const [glitchgrabReport, setGlitchgrabReport] = useState6(false);
7499
+ const [signedUsers, setSignedUsers] = useState6("OFF");
7500
+ const aiBlockedUnsigned = signedUsers === "ENFORCE" && !signature;
7501
+ const [bookingOpen, setBookingOpen] = useState6(false);
6598
7502
  const openBookingDialog = useCallback2(() => setBookingOpen(true), []);
6599
- useEffect5(() => {
7503
+ useEffect6(() => {
6600
7504
  let cancelled = false;
6601
7505
  fetch(`${baseUrl}/api/v1/sdk/project`, {
6602
7506
  headers: { Authorization: `Bearer ${token}` }
@@ -6605,13 +7509,27 @@ function GlitchgrabProviderInner({
6605
7509
  if (cancelled || !(json == null ? void 0 : json.success) || !((_a = json.data) == null ? void 0 : _a.name)) return;
6606
7510
  setProjectName(json.data.name);
6607
7511
  setAiAssist(json.data.aiAssist === true);
7512
+ setGlitchgrabReport(json.data.glitchgrabReport === true);
7513
+ const mode = json.data.signedUsers;
7514
+ setSignedUsers(mode === "WARN" || mode === "ENFORCE" ? mode : "OFF");
6608
7515
  }).catch(() => {
6609
7516
  });
6610
7517
  return () => {
6611
7518
  cancelled = true;
6612
7519
  };
6613
7520
  }, [baseUrl, token]);
6614
- useEffect5(() => {
7521
+ useEffect6(() => {
7522
+ var _a;
7523
+ try {
7524
+ if (signedUsers === "OFF" || signature || !session) return;
7525
+ if (typeof process === "undefined" || ((_a = process.env) == null ? void 0 : _a.NODE_ENV) !== "development") return;
7526
+ console.warn(
7527
+ signedUsers === "ENFORCE" ? '[glitchgrab] This project enforces signed users and `session.signature` is missing \u2014 AI features are hidden. Sign it on your server with signSession() from "glitchgrab/server".' : '[glitchgrab] This project warns on unsigned users and `session.signature` is missing. Sign it on your server with signSession() from "glitchgrab/server" before the owner switches to Enforce.'
7528
+ );
7529
+ } catch (e) {
7530
+ }
7531
+ }, [signedUsers, signature, session]);
7532
+ useEffect6(() => {
6615
7533
  try {
6616
7534
  if (typeof window === "undefined") return;
6617
7535
  const handleKeyDown = (e) => {
@@ -6625,7 +7543,7 @@ function GlitchgrabProviderInner({
6625
7543
  } catch (e) {
6626
7544
  }
6627
7545
  }, [openReportDialog]);
6628
- const reporter = useMemo2(() => {
7546
+ const reporter = useMemo3(() => {
6629
7547
  var _a;
6630
7548
  if (!session) return null;
6631
7549
  const avatar = (_a = session.avatarUrl) != null ? _a : session.image;
@@ -6635,11 +7553,11 @@ function GlitchgrabProviderInner({
6635
7553
  avatarUrl: typeof avatar === "string" ? avatar : null
6636
7554
  };
6637
7555
  }, [session]);
6638
- const [shortcutLabel, setShortcutLabel] = useState5(GLITCHGRAB_SHORTCUT2);
6639
- useEffect5(() => {
7556
+ const [shortcutLabel, setShortcutLabel] = useState6(GLITCHGRAB_SHORTCUT2);
7557
+ useEffect6(() => {
6640
7558
  setShortcutLabel(getShortcutLabel2());
6641
7559
  }, []);
6642
- return /* @__PURE__ */ jsxs5(
7560
+ return /* @__PURE__ */ jsxs6(
6643
7561
  GlitchgrabContext.Provider,
6644
7562
  {
6645
7563
  value: {
@@ -6659,7 +7577,7 @@ function GlitchgrabProviderInner({
6659
7577
  shortcutLabel
6660
7578
  },
6661
7579
  children: [
6662
- /* @__PURE__ */ jsx5(
7580
+ /* @__PURE__ */ jsx6(
6663
7581
  GlitchgrabErrorBoundary,
6664
7582
  {
6665
7583
  token,
@@ -6671,7 +7589,7 @@ function GlitchgrabProviderInner({
6671
7589
  children
6672
7590
  }
6673
7591
  ),
6674
- /* @__PURE__ */ jsx5(
7592
+ /* @__PURE__ */ jsx6(
6675
7593
  BookingDialog,
6676
7594
  {
6677
7595
  token,
@@ -6680,20 +7598,22 @@ function GlitchgrabProviderInner({
6680
7598
  onClose: () => setBookingOpen(false)
6681
7599
  }
6682
7600
  ),
6683
- /* @__PURE__ */ jsx5(
7601
+ /* @__PURE__ */ jsx6(
6684
7602
  ReportDialog,
6685
7603
  {
6686
7604
  report,
6687
7605
  sendFeedback: feedback,
6688
- enhanceText: enhance,
6689
- assist: aiAssist ? assist : void 0,
6690
- transcribeAudio: transcribe,
7606
+ enhanceText: aiAssist && !aiBlockedUnsigned ? enhance : void 0,
7607
+ assist: aiAssist && !aiBlockedUnsigned ? assist : void 0,
7608
+ findSimilarIssues: aiAssist && !aiBlockedUnsigned ? findSimilar : void 0,
7609
+ reportGlitchgrabProblem: glitchgrabReport ? reportGlitchgrabProblem : void 0,
7610
+ transcribeAudio: aiBlockedUnsigned ? void 0 : transcribe,
6691
7611
  types,
6692
7612
  showSeverity,
6693
7613
  reporter,
6694
- headerSlot: projectName ? /* @__PURE__ */ jsxs5("div", { style: { fontSize: 11, color: "#9aa0a6" }, children: [
7614
+ headerSlot: projectName ? /* @__PURE__ */ jsxs6("div", { style: { fontSize: 11, color: "#9aa0a6" }, children: [
6695
7615
  "Reporting to ",
6696
- /* @__PURE__ */ jsx5("strong", { style: { color: "#e5e5e5" }, children: projectName })
7616
+ /* @__PURE__ */ jsx6("strong", { style: { color: "#e5e5e5" }, children: projectName })
6697
7617
  ] }) : null
6698
7618
  }
6699
7619
  )
@@ -6702,21 +7622,21 @@ function GlitchgrabProviderInner({
6702
7622
  );
6703
7623
  }
6704
7624
  function GlitchgrabProvider(props) {
6705
- if (!props.token) return /* @__PURE__ */ jsx5(Fragment4, { children: props.children });
7625
+ if (!props.token) return /* @__PURE__ */ jsx6(Fragment5, { children: props.children });
6706
7626
  const resolvedProps = {
6707
7627
  ...props,
6708
7628
  baseUrl: props.baseUrl || DEFAULT_BASE_URL2
6709
7629
  };
6710
7630
  try {
6711
- return /* @__PURE__ */ jsx5(GlitchgrabProviderInner, { ...resolvedProps });
7631
+ return /* @__PURE__ */ jsx6(GlitchgrabProviderInner, { ...resolvedProps });
6712
7632
  } catch (e) {
6713
- return /* @__PURE__ */ jsx5(Fragment4, { children: props.children });
7633
+ return /* @__PURE__ */ jsx6(Fragment5, { children: props.children });
6714
7634
  }
6715
7635
  }
6716
7636
 
6717
7637
  // src/report-button.tsx
6718
- import { useState as useState6 } from "react";
6719
- import { Fragment as Fragment5, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
7638
+ import { useState as useState7 } from "react";
7639
+ import { Fragment as Fragment6, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
6720
7640
  function useGlitchgrabSafe() {
6721
7641
  try {
6722
7642
  return useGlitchgrab();
@@ -6731,7 +7651,7 @@ function ReportButton({
6731
7651
  children
6732
7652
  }) {
6733
7653
  var _a, _b;
6734
- const [capturing, setCapturing] = useState6(false);
7654
+ const [capturing, setCapturing] = useState7(false);
6735
7655
  const glitchgrab = useGlitchgrabSafe();
6736
7656
  if (!glitchgrab) return null;
6737
7657
  const handleClick = async () => {
@@ -6740,7 +7660,7 @@ function ReportButton({
6740
7660
  setCapturing(false);
6741
7661
  };
6742
7662
  if (children) {
6743
- return /* @__PURE__ */ jsx6(Fragment5, { children: children({ onClick: handleClick, capturing }) });
7663
+ return /* @__PURE__ */ jsx7(Fragment6, { children: children({ onClick: handleClick, capturing }) });
6744
7664
  }
6745
7665
  const isTop = (_a = position == null ? void 0 : position.startsWith("top")) != null ? _a : false;
6746
7666
  const isLeft = (_b = position == null ? void 0 : position.endsWith("left")) != null ? _b : false;
@@ -6748,7 +7668,7 @@ function ReportButton({
6748
7668
  ...isLeft ? { left: "16px" } : { right: "16px" },
6749
7669
  ...isTop ? { top: "16px" } : { bottom: "16px" }
6750
7670
  };
6751
- return /* @__PURE__ */ jsxs6(
7671
+ return /* @__PURE__ */ jsxs7(
6752
7672
  "button",
6753
7673
  {
6754
7674
  type: "button",
@@ -6783,7 +7703,7 @@ function ReportButton({
6783
7703
  },
6784
7704
  "aria-label": label,
6785
7705
  children: [
6786
- /* @__PURE__ */ jsx6(
7706
+ /* @__PURE__ */ jsx7(
6787
7707
  "svg",
6788
7708
  {
6789
7709
  width: "16",
@@ -6791,7 +7711,7 @@ function ReportButton({
6791
7711
  viewBox: "0 0 16 16",
6792
7712
  fill: "none",
6793
7713
  style: { flexShrink: 0 },
6794
- children: /* @__PURE__ */ jsx6(
7714
+ children: /* @__PURE__ */ jsx7(
6795
7715
  "path",
6796
7716
  {
6797
7717
  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",
@@ -6807,15 +7727,15 @@ function ReportButton({
6807
7727
  }
6808
7728
 
6809
7729
  // src/book-demo-button.tsx
6810
- import { Fragment as Fragment6, jsx as jsx7 } from "react/jsx-runtime";
7730
+ import { Fragment as Fragment7, jsx as jsx8 } from "react/jsx-runtime";
6811
7731
  function BookDemoButton({
6812
7732
  label = "Book a demo",
6813
7733
  className,
6814
7734
  children
6815
7735
  }) {
6816
7736
  const { openBookingDialog } = useGlitchgrab();
6817
- if (children) return /* @__PURE__ */ jsx7(Fragment6, { children: children({ onClick: openBookingDialog }) });
6818
- return /* @__PURE__ */ jsx7(
7737
+ if (children) return /* @__PURE__ */ jsx8(Fragment7, { children: children({ onClick: openBookingDialog }) });
7738
+ return /* @__PURE__ */ jsx8(
6819
7739
  "button",
6820
7740
  {
6821
7741
  type: "button",
@@ -6837,7 +7757,7 @@ function BookDemoButton({
6837
7757
  }
6838
7758
 
6839
7759
  // src/feedback-button.tsx
6840
- import { Fragment as Fragment7, jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
7760
+ import { Fragment as Fragment8, jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
6841
7761
  function useGlitchgrabSafe2() {
6842
7762
  try {
6843
7763
  return useGlitchgrab();
@@ -6856,7 +7776,7 @@ function FeedbackButton({
6856
7776
  if (!glitchgrab) return null;
6857
7777
  const handleClick = () => glitchgrab.openFeedbackDialog();
6858
7778
  if (children) {
6859
- return /* @__PURE__ */ jsx8(Fragment7, { children: children({ onClick: handleClick }) });
7779
+ return /* @__PURE__ */ jsx9(Fragment8, { children: children({ onClick: handleClick }) });
6860
7780
  }
6861
7781
  const isTop = (_a = position == null ? void 0 : position.startsWith("top")) != null ? _a : false;
6862
7782
  const isLeft = (_b = position == null ? void 0 : position.endsWith("left")) != null ? _b : false;
@@ -6864,7 +7784,7 @@ function FeedbackButton({
6864
7784
  ...isLeft ? { left: "16px" } : { right: "16px" },
6865
7785
  ...isTop ? { top: "16px" } : { bottom: "16px" }
6866
7786
  };
6867
- return /* @__PURE__ */ jsxs7(
7787
+ return /* @__PURE__ */ jsxs8(
6868
7788
  "button",
6869
7789
  {
6870
7790
  type: "button",
@@ -6897,7 +7817,7 @@ function FeedbackButton({
6897
7817
  },
6898
7818
  "aria-label": label,
6899
7819
  children: [
6900
- /* @__PURE__ */ jsx8(
7820
+ /* @__PURE__ */ jsx9(
6901
7821
  "svg",
6902
7822
  {
6903
7823
  width: "16",
@@ -6906,7 +7826,7 @@ function FeedbackButton({
6906
7826
  fill: "none",
6907
7827
  style: { flexShrink: 0 },
6908
7828
  "aria-hidden": "true",
6909
- children: /* @__PURE__ */ jsx8(
7829
+ children: /* @__PURE__ */ jsx9(
6910
7830
  "path",
6911
7831
  {
6912
7832
  d: "M12 2.5l2.9 5.9 6.5.9-4.7 4.6 1.1 6.5L12 17.4l-5.8 3 1.1-6.5L2.6 9.3l6.5-.9L12 2.5z",
@@ -6922,17 +7842,17 @@ function FeedbackButton({
6922
7842
  }
6923
7843
 
6924
7844
  // src/use-reports.ts
6925
- import { useState as useState7, useEffect as useEffect6, useCallback as useCallback3 } from "react";
7845
+ import { useState as useState8, useEffect as useEffect7, useCallback as useCallback3 } from "react";
6926
7846
  var BASE_URL = "https://glitchgrab.dev";
6927
7847
  function useGlitchgrabReports({
6928
7848
  token,
6929
7849
  userId,
6930
7850
  limit = 100
6931
7851
  }) {
6932
- const [reports, setReports] = useState7([]);
6933
- const [isLoading, setIsLoading] = useState7(true);
6934
- const [isFetching, setIsFetching] = useState7(false);
6935
- const [error, setError] = useState7(null);
7852
+ const [reports, setReports] = useState8([]);
7853
+ const [isLoading, setIsLoading] = useState8(true);
7854
+ const [isFetching, setIsFetching] = useState8(false);
7855
+ const [error, setError] = useState8(null);
6936
7856
  const fetchReports = useCallback3(async () => {
6937
7857
  var _a, _b;
6938
7858
  if (!token || !userId) {
@@ -6959,7 +7879,7 @@ function useGlitchgrabReports({
6959
7879
  setIsFetching(false);
6960
7880
  }
6961
7881
  }, [token, userId, limit]);
6962
- useEffect6(() => {
7882
+ useEffect7(() => {
6963
7883
  fetchReports();
6964
7884
  }, [fetchReports]);
6965
7885
  return { reports, isLoading, isFetching, error, refetch: fetchReports };
@@ -6996,8 +7916,8 @@ function useGlitchgrabActions({
6996
7916
  onSuccess,
6997
7917
  onError
6998
7918
  }) {
6999
- const [isPending, setIsPending] = useState7(false);
7000
- const [error, setError] = useState7(null);
7919
+ const [isPending, setIsPending] = useState8(false);
7920
+ const [error, setError] = useState8(null);
7001
7921
  const run = useCallback3(
7002
7922
  async (reportId, body) => {
7003
7923
  setIsPending(true);
@@ -7028,7 +7948,7 @@ function useGlitchgrabActions({
7028
7948
  }
7029
7949
 
7030
7950
  // src/use-feedback.ts
7031
- import { useState as useState8, useEffect as useEffect7, useCallback as useCallback4 } from "react";
7951
+ import { useState as useState9, useEffect as useEffect8, useCallback as useCallback4 } from "react";
7032
7952
  var DEFAULT_BASE_URL3 = "https://glitchgrab.dev";
7033
7953
  function buildUrl({
7034
7954
  approvedOnly,
@@ -7054,10 +7974,10 @@ async function fetchGlitchgrabFeedback(query) {
7054
7974
  }
7055
7975
  function useGlitchgrabFeedback(query) {
7056
7976
  const { token, approvedOnly, userId, minRating, limit, baseUrl } = query;
7057
- const [feedback, setFeedback] = useState8([]);
7058
- const [isLoading, setIsLoading] = useState8(true);
7059
- const [isFetching, setIsFetching] = useState8(false);
7060
- const [error, setError] = useState8(null);
7977
+ const [feedback, setFeedback] = useState9([]);
7978
+ const [isLoading, setIsLoading] = useState9(true);
7979
+ const [isFetching, setIsFetching] = useState9(false);
7980
+ const [error, setError] = useState9(null);
7061
7981
  const load = useCallback4(async () => {
7062
7982
  if (!token) {
7063
7983
  setIsLoading(false);
@@ -7082,7 +8002,7 @@ function useGlitchgrabFeedback(query) {
7082
8002
  setIsFetching(false);
7083
8003
  }
7084
8004
  }, [token, approvedOnly, userId, minRating, limit, baseUrl]);
7085
- useEffect7(() => {
8005
+ useEffect8(() => {
7086
8006
  load();
7087
8007
  }, [load]);
7088
8008
  return { feedback, isLoading, isFetching, error, refetch: load };