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.js CHANGED
@@ -68,7 +68,7 @@ __export(index_exports, {
68
68
  module.exports = __toCommonJS(index_exports);
69
69
 
70
70
  // src/provider.tsx
71
- var import_react5 = require("react");
71
+ var import_react6 = require("react");
72
72
 
73
73
  // src/error-boundary.tsx
74
74
  var React = __toESM(require("react"));
@@ -186,21 +186,84 @@ function interceptConsole() {
186
186
  origError.apply(console, args);
187
187
  };
188
188
  }
189
+ var CONSOLE_TEXT_MAX = 200;
189
190
  function argsToString(args) {
190
191
  try {
191
192
  return args.map((a) => {
192
- if (typeof a === "string") return a;
193
+ if (typeof a === "string") return a.slice(0, CONSOLE_TEXT_MAX);
193
194
  if (a instanceof Error) return a.message;
194
195
  try {
195
- return JSON.stringify(a);
196
+ return previewJson(a, CONSOLE_TEXT_MAX);
196
197
  } catch (e) {
197
198
  return String(a);
198
199
  }
199
- }).join(" ").slice(0, 200);
200
+ }).join(" ").slice(0, CONSOLE_TEXT_MAX);
200
201
  } catch (e) {
201
202
  return "[unknown]";
202
203
  }
203
204
  }
205
+ function previewJson(value, max) {
206
+ let out = "";
207
+ const path = /* @__PURE__ */ new WeakSet();
208
+ const write = (input, inArray) => {
209
+ if (out.length > max) return;
210
+ let v = input;
211
+ if (v !== null && typeof v === "object" && typeof v.toJSON === "function") {
212
+ v = v.toJSON();
213
+ }
214
+ if (v === null) {
215
+ out += "null";
216
+ return;
217
+ }
218
+ switch (typeof v) {
219
+ case "string":
220
+ out += JSON.stringify(v.length > max ? v.slice(0, max) : v);
221
+ return;
222
+ case "number":
223
+ out += Number.isFinite(v) ? String(v) : "null";
224
+ return;
225
+ case "boolean":
226
+ case "bigint":
227
+ out += String(v);
228
+ return;
229
+ case "object":
230
+ break;
231
+ default:
232
+ if (inArray) out += "null";
233
+ return;
234
+ }
235
+ const obj = v;
236
+ if (path.has(obj)) {
237
+ out += '"[Circular]"';
238
+ return;
239
+ }
240
+ path.add(obj);
241
+ if (Array.isArray(obj)) {
242
+ out += "[";
243
+ for (let i = 0; i < obj.length && out.length <= max; i++) {
244
+ if (i > 0) out += ",";
245
+ write(obj[i], true);
246
+ }
247
+ out += "]";
248
+ } else {
249
+ out += "{";
250
+ let first = true;
251
+ for (const key in obj) {
252
+ if (out.length > max) break;
253
+ if (!Object.prototype.hasOwnProperty.call(obj, key)) continue;
254
+ const child = obj[key];
255
+ if (child === void 0 || typeof child === "function" || typeof child === "symbol") continue;
256
+ out += `${first ? "" : ","}${JSON.stringify(key)}:`;
257
+ first = false;
258
+ write(child, false);
259
+ }
260
+ out += "}";
261
+ }
262
+ path.delete(obj);
263
+ };
264
+ write(value, false);
265
+ return out;
266
+ }
204
267
  async function readErrorBody(response, url) {
205
268
  try {
206
269
  if (response.ok) return "";
@@ -317,15 +380,27 @@ function interceptNavigation() {
317
380
  addBreadcrumb("navigation", `Back/Forward to ${window.location.pathname}`);
318
381
  });
319
382
  }
383
+ var CLICK_TEXT_MAX = 50;
384
+ var CLICK_TEXT_NODE_LIMIT = 500;
385
+ function leadingText(el, max) {
386
+ var _a;
387
+ let text = "";
388
+ let visited = 0;
389
+ const walker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT);
390
+ while (text.trimStart().length < max && visited++ < CLICK_TEXT_NODE_LIMIT && walker.nextNode()) {
391
+ text += (_a = walker.currentNode.nodeValue) != null ? _a : "";
392
+ }
393
+ return text.trim().slice(0, max);
394
+ }
320
395
  function interceptClicks() {
321
396
  document.addEventListener(
322
397
  "click",
323
398
  (e) => {
324
- var _a, _b, _c;
399
+ var _a;
325
400
  try {
326
401
  const target = e.target;
327
402
  const tag = (_a = target.tagName) == null ? void 0 : _a.toLowerCase();
328
- const text = (_c = (_b = target.textContent) == null ? void 0 : _b.trim().slice(0, 50)) != null ? _c : "";
403
+ const text = target instanceof Node ? leadingText(target, CLICK_TEXT_MAX) : "";
329
404
  const id = target.id ? `#${target.id}` : "";
330
405
  const cls = target.className && typeof target.className === "string" ? `.${target.className.split(" ")[0]}` : "";
331
406
  addBreadcrumb("click", `Click ${tag}${id}${cls} "${text}"`);
@@ -618,7 +693,11 @@ async function sendFeedback(payload, baseUrl) {
618
693
  return null;
619
694
  }
620
695
  }
621
- async function enhanceText(text, token, baseUrl, screenshot, context) {
696
+ function signedFields(identity) {
697
+ if (!(identity == null ? void 0 : identity.signature) || !identity.userId) return {};
698
+ return { userId: identity.userId, signature: identity.signature };
699
+ }
700
+ async function enhanceText(text, token, baseUrl, screenshot, context, identity) {
622
701
  var _a;
623
702
  try {
624
703
  const trimmed = text.trim();
@@ -633,7 +712,8 @@ async function enhanceText(text, token, baseUrl, screenshot, context) {
633
712
  body: JSON.stringify({
634
713
  text: trimmed,
635
714
  ...screenshot ? { screenshot } : {},
636
- ...context ? { context } : {}
715
+ ...context ? { context } : {},
716
+ ...signedFields(identity)
637
717
  })
638
718
  });
639
719
  if (!response.ok) return text;
@@ -646,8 +726,28 @@ async function enhanceText(text, token, baseUrl, screenshot, context) {
646
726
  return text;
647
727
  }
648
728
  }
729
+ async function findSimilarIssues(description, token, baseUrl, identity) {
730
+ var _a;
731
+ try {
732
+ const url = `${baseUrl != null ? baseUrl : DEFAULT_BASE_URL}/api/v1/reports/similar`;
733
+ const response = await fetch(url, {
734
+ method: "POST",
735
+ headers: {
736
+ "Content-Type": "application/json",
737
+ Authorization: `Bearer ${token}`
738
+ },
739
+ body: JSON.stringify({ text: description, ...signedFields(identity) })
740
+ });
741
+ const envelope = await response.json().catch(() => null);
742
+ if (!response.ok || !(envelope == null ? void 0 : envelope.success)) return null;
743
+ if (!((_a = envelope.data) == null ? void 0 : _a.enabled)) return [];
744
+ return Array.isArray(envelope.data.matches) ? envelope.data.matches : [];
745
+ } catch (e) {
746
+ return null;
747
+ }
748
+ }
649
749
  async function assistTurn(params, token, baseUrl) {
650
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
750
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n;
651
751
  const offline = {
652
752
  conversationId: null,
653
753
  question: null,
@@ -680,19 +780,25 @@ async function assistTurn(params, token, baseUrl) {
680
780
  report: (_i = (_h = envelope.data) == null ? void 0 : _h.report) != null ? _i : null,
681
781
  duplicate: (_k = (_j = envelope.data) == null ? void 0 : _j.duplicate) != null ? _k : null,
682
782
  solved: (_m = (_l = envelope.data) == null ? void 0 : _l.solved) != null ? _m : null,
783
+ aboutGlitchgrab: ((_n = envelope.data) == null ? void 0 : _n.aboutGlitchgrab) === true,
683
784
  degraded: null
684
785
  };
685
786
  } catch (e) {
686
787
  return offline;
687
788
  }
688
789
  }
689
- async function transcribeAudio(blob, token, baseUrl) {
790
+ async function transcribeAudio(blob, token, baseUrl, identity) {
690
791
  var _a;
691
792
  try {
692
793
  if (blob.size === 0) return "";
693
794
  const url = `${baseUrl != null ? baseUrl : DEFAULT_BASE_URL}/api/v1/sdk/stt`;
694
795
  const form = new FormData();
695
796
  form.append("file", blob, "audio.webm");
797
+ const signed = signedFields(identity);
798
+ if (signed.userId && signed.signature) {
799
+ form.append("userId", signed.userId);
800
+ form.append("signature", signed.signature);
801
+ }
696
802
  const response = await fetch(url, {
697
803
  method: "POST",
698
804
  headers: { Authorization: `Bearer ${token}` },
@@ -900,7 +1006,10 @@ var import_jsx_runtime = require("react/jsx-runtime");
900
1006
  var import_react3 = require("react");
901
1007
  var import_react_dom2 = require("react-dom");
902
1008
  var import_jsx_runtime2 = require("react/jsx-runtime");
1009
+ var import_react4 = require("react");
1010
+ var import_react_dom3 = require("react-dom");
903
1011
  var import_jsx_runtime3 = require("react/jsx-runtime");
1012
+ var import_jsx_runtime4 = require("react/jsx-runtime");
904
1013
  var MAX_SCREENSHOT_DATA_URL_LENGTH = 18e5;
905
1014
  var QUALITY_LADDER = [0.92, 0.85, 0.75];
906
1015
  var DOWNSCALE_FACTOR = 0.7;
@@ -1392,7 +1501,8 @@ function AssistSheet({
1392
1501
  onSend,
1393
1502
  onDegrade,
1394
1503
  onClose,
1395
- onFinish
1504
+ onFinish,
1505
+ onReportGlitchgrabProblem
1396
1506
  }) {
1397
1507
  const narrow = useIsNarrow();
1398
1508
  const severityAsked = showSeverity && currentTile === "BUG";
@@ -1403,6 +1513,7 @@ function AssistSheet({
1403
1513
  const [hoveredStar, setHoveredStar] = (0, import_react3.useState)(0);
1404
1514
  const [options, setOptions] = (0, import_react3.useState)([]);
1405
1515
  const [duplicate, setDuplicate] = (0, import_react3.useState)(null);
1516
+ const [glitchgrabOffer, setGlitchgrabOffer] = (0, import_react3.useState)(false);
1406
1517
  const [solved, setSolved] = (0, import_react3.useState)(null);
1407
1518
  const [phase, setPhase] = (0, import_react3.useState)(
1408
1519
  !typePicked ? "type" : currentTile === "RATING" ? "rating" : "chat"
@@ -1470,7 +1581,10 @@ function AssistSheet({
1470
1581
  screenshots: sent,
1471
1582
  context: {
1472
1583
  ...context != null ? context : {},
1473
- imageSources: sent.map((shot) => pageShot && shot === pageShot ? "page" : "attached")
1584
+ imageSources: sent.map((shot) => pageShot && shot === pageShot ? "page" : "attached"),
1585
+ // Whether this dialog can open the #366 panel at all. A hint — the
1586
+ // server decides whether the model may offer it.
1587
+ canReportGlitchgrab: !!onReportGlitchgrabProblem
1474
1588
  },
1475
1589
  events
1476
1590
  });
@@ -1493,6 +1607,7 @@ function AssistSheet({
1493
1607
  }
1494
1608
  setDuplicate((_a = result.duplicate) != null ? _a : null);
1495
1609
  onDuplicateChange == null ? void 0 : onDuplicateChange((_c = (_b = result.duplicate) == null ? void 0 : _b.number) != null ? _c : null);
1610
+ setGlitchgrabOffer(!!result.aboutGlitchgrab && !!onReportGlitchgrabProblem && !!result.question);
1496
1611
  if (result.solved) {
1497
1612
  setSolved(result.solved);
1498
1613
  setOptions([]);
@@ -1529,6 +1644,7 @@ function AssistSheet({
1529
1644
  const value = text.trim();
1530
1645
  if (!value || busy) return;
1531
1646
  setOptions([]);
1647
+ setGlitchgrabOffer(false);
1532
1648
  const underDraft = phase === "draft";
1533
1649
  if (underDraft) {
1534
1650
  const edited = aiDraftRef.current !== null && description.trim() !== aiDraftRef.current.trim();
@@ -1582,7 +1698,7 @@ function AssistSheet({
1582
1698
  minWidth: 0
1583
1699
  },
1584
1700
  children: [
1585
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: "#f59e0b", fontWeight: 600 }, children: title ? "Our team is already on this" : `Our team is already on #${duplicate.number}` }),
1701
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: t2.text, fontWeight: 600 }, children: title ? "Our team is already on this" : `Our team is already on #${duplicate.number}` }),
1586
1702
  title && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { style: { color: t2.textMuted, wordBreak: "break-word" }, children: [
1587
1703
  "#",
1588
1704
  duplicate.number,
@@ -2086,7 +2202,82 @@ function AssistSheet({
2086
2202
  );
2087
2203
  }),
2088
2204
  duplicate && !busy && phase === "chat" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { paddingLeft: "32px", minWidth: 0 }, children: renderKnownIssue({ title: false }) }),
2089
- options.length > 0 && !busy && phase === "chat" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2205
+ glitchgrabOffer && onReportGlitchgrabProblem && !busy && phase === "chat" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { paddingLeft: "32px", minWidth: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
2206
+ "div",
2207
+ {
2208
+ "data-gg-glitchgrab-offer": "",
2209
+ style: {
2210
+ display: "flex",
2211
+ flexDirection: "column",
2212
+ gap: "8px",
2213
+ padding: "10px",
2214
+ borderRadius: "8px",
2215
+ border: `1px solid ${t2.accent}55`,
2216
+ backgroundColor: `${t2.accent}14`,
2217
+ fontSize: "12px",
2218
+ lineHeight: 1.5
2219
+ },
2220
+ children: [
2221
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: t2.text, fontWeight: 600 }, children: "This is about Glitchgrab, not this app" }),
2222
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: t2.textMuted }, children: "It goes to the team that builds this reporter, with your chat attached." }),
2223
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", flexWrap: "wrap", gap: "6px" }, children: [
2224
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2225
+ "button",
2226
+ {
2227
+ type: "button",
2228
+ "data-gg-glitchgrab-offer-send": "",
2229
+ onClick: () => {
2230
+ logEvent("glitchgrab_offer_tap");
2231
+ setGlitchgrabOffer(false);
2232
+ onReportGlitchgrabProblem(
2233
+ messages.filter((m) => m.role === "user").map((m) => m.content.trim()).filter(Boolean).join("\n\n")
2234
+ );
2235
+ },
2236
+ style: {
2237
+ padding: "8px 12px",
2238
+ borderRadius: "999px",
2239
+ border: "none",
2240
+ backgroundColor: t2.accent,
2241
+ color: t2.accentText,
2242
+ fontSize: "12px",
2243
+ fontWeight: 700,
2244
+ fontFamily: "inherit",
2245
+ cursor: "pointer",
2246
+ // Both stretch, so on a phone each wrapped line reads as
2247
+ // a choice rather than one button left stranded.
2248
+ flex: "1 1 auto"
2249
+ },
2250
+ children: "Send to the Glitchgrab team"
2251
+ }
2252
+ ),
2253
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2254
+ "button",
2255
+ {
2256
+ type: "button",
2257
+ "data-gg-glitchgrab-offer-no": "",
2258
+ onClick: () => {
2259
+ logEvent("glitchgrab_offer_declined");
2260
+ send("No \u2014 it's about this app, not the reporting tool");
2261
+ },
2262
+ style: {
2263
+ padding: "8px 12px",
2264
+ borderRadius: "999px",
2265
+ border: `1px solid ${t2.inputBorder}`,
2266
+ background: "transparent",
2267
+ color: t2.textMuted,
2268
+ fontSize: "12px",
2269
+ fontFamily: "inherit",
2270
+ cursor: "pointer",
2271
+ flex: "1 1 auto"
2272
+ },
2273
+ children: "No, it's about this app"
2274
+ }
2275
+ )
2276
+ ] })
2277
+ ]
2278
+ }
2279
+ ) }),
2280
+ options.length > 0 && !busy && phase === "chat" && !glitchgrabOffer && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2090
2281
  "div",
2091
2282
  {
2092
2283
  style: {
@@ -2672,6 +2863,30 @@ function AssistSheet({
2672
2863
  },
2673
2864
  children: "Not getting it? Fill the form yourself \u2192"
2674
2865
  }
2866
+ ),
2867
+ onReportGlitchgrabProblem && phase !== "solved" && !glitchgrabOffer && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2868
+ "button",
2869
+ {
2870
+ type: "button",
2871
+ "data-gg-self-report": "",
2872
+ onClick: () => onReportGlitchgrabProblem(),
2873
+ style: {
2874
+ display: "block",
2875
+ // ~32px tap target; the negative margins keep the composer
2876
+ // from growing to make room for it.
2877
+ margin: "-2px auto -8px",
2878
+ border: "none",
2879
+ background: "transparent",
2880
+ color: t2.textMuted,
2881
+ fontSize: "11.5px",
2882
+ fontFamily: "inherit",
2883
+ padding: "10px 8px",
2884
+ cursor: "pointer",
2885
+ textDecoration: "underline",
2886
+ textUnderlineOffset: "2px"
2887
+ },
2888
+ children: "Assistant misbehaving? Tell Glitchgrab"
2889
+ }
2675
2890
  )
2676
2891
  ]
2677
2892
  }
@@ -2685,6 +2900,240 @@ function AssistSheet({
2685
2900
  document.body
2686
2901
  );
2687
2902
  }
2903
+ var FONT = '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
2904
+ var FAILED = "Couldn't send it. Try again in a moment.";
2905
+ function describeSentAlong(hasChat, screenshotCount) {
2906
+ const parts = [
2907
+ ...hasChat ? ["your AI chat"] : [],
2908
+ "what you did on this page",
2909
+ ...screenshotCount > 0 ? [`${screenshotCount} screenshot${screenshotCount === 1 ? "" : "s"}`] : []
2910
+ ];
2911
+ if (parts.length === 1) return parts[0];
2912
+ return `${parts.slice(0, -1).join(", ")} and ${parts[parts.length - 1]}`;
2913
+ }
2914
+ function GlitchgrabProblemPanel({
2915
+ send,
2916
+ theme: t2,
2917
+ surface,
2918
+ conversationId,
2919
+ reportType,
2920
+ screenshots,
2921
+ initialText = "",
2922
+ onClose
2923
+ }) {
2924
+ const [text, setText] = (0, import_react4.useState)(initialText);
2925
+ const [busy, setBusy] = (0, import_react4.useState)(false);
2926
+ const [sent, setSent] = (0, import_react4.useState)(false);
2927
+ const [error, setError] = (0, import_react4.useState)(null);
2928
+ const inputRef = (0, import_react4.useRef)(null);
2929
+ (0, import_react4.useEffect)(() => {
2930
+ var _a;
2931
+ (_a = inputRef.current) == null ? void 0 : _a.focus();
2932
+ }, []);
2933
+ (0, import_react4.useEffect)(() => {
2934
+ const onKey = (e) => {
2935
+ if (e.key !== "Escape") return;
2936
+ e.stopPropagation();
2937
+ onClose();
2938
+ };
2939
+ window.addEventListener("keydown", onKey, true);
2940
+ return () => window.removeEventListener("keydown", onKey, true);
2941
+ }, [onClose]);
2942
+ const submit = async () => {
2943
+ const description = text.trim();
2944
+ if (!description || busy) return;
2945
+ setBusy(true);
2946
+ setError(null);
2947
+ try {
2948
+ const result = await send({ description, surface, conversationId, reportType, screenshots });
2949
+ if (result == null ? void 0 : result.success) setSent(true);
2950
+ else setError((result == null ? void 0 : result.message) || FAILED);
2951
+ } catch (e) {
2952
+ setError(FAILED);
2953
+ } finally {
2954
+ setBusy(false);
2955
+ }
2956
+ };
2957
+ const canSend = !!text.trim() && !busy;
2958
+ const secondaryButton = {
2959
+ padding: "10px 14px",
2960
+ borderRadius: "8px",
2961
+ border: `1px solid ${t2.inputBorder}`,
2962
+ background: "transparent",
2963
+ color: t2.text,
2964
+ fontSize: "13px",
2965
+ fontWeight: 600,
2966
+ fontFamily: "inherit",
2967
+ cursor: "pointer"
2968
+ };
2969
+ const primaryButton = (enabled) => ({
2970
+ padding: "10px 14px",
2971
+ borderRadius: "8px",
2972
+ border: "none",
2973
+ backgroundColor: enabled ? t2.accent : t2.bgSecondary,
2974
+ color: enabled ? t2.accentText : t2.textMuted,
2975
+ fontSize: "13px",
2976
+ fontWeight: 700,
2977
+ fontFamily: "inherit",
2978
+ cursor: enabled ? "pointer" : "default"
2979
+ });
2980
+ return (0, import_react_dom3.createPortal)(
2981
+ // No close on backdrop click: a stray click would throw away what they typed.
2982
+ // `data-glitchgrab-layer` exempts this from the dialog's anti-focus-trap
2983
+ // sweep, which sets `inert` on every other role="dialog" on the page —
2984
+ // without it the panel renders but cannot be typed into.
2985
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2986
+ "div",
2987
+ {
2988
+ "data-glitchgrab-layer": "",
2989
+ "data-gg-self-report-panel": "",
2990
+ role: "dialog",
2991
+ "aria-modal": "true",
2992
+ "aria-labelledby": "gg-self-report-title",
2993
+ style: {
2994
+ position: "fixed",
2995
+ inset: 0,
2996
+ zIndex: 2147483647,
2997
+ backgroundColor: "rgba(0,0,0,0.55)",
2998
+ display: "flex",
2999
+ alignItems: "center",
3000
+ justifyContent: "center",
3001
+ padding: "16px",
3002
+ boxSizing: "border-box",
3003
+ fontFamily: FONT
3004
+ },
3005
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
3006
+ "div",
3007
+ {
3008
+ style: {
3009
+ width: "100%",
3010
+ maxWidth: "420px",
3011
+ boxSizing: "border-box",
3012
+ backgroundColor: t2.bg,
3013
+ color: t2.text,
3014
+ border: `1px solid ${t2.border}`,
3015
+ borderRadius: "12px",
3016
+ boxShadow: "0 20px 50px rgba(0,0,0,0.5)",
3017
+ padding: "18px"
3018
+ },
3019
+ children: sent ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
3020
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { id: "gg-self-report-title", style: { fontSize: "15px", fontWeight: 600 }, children: "Sent to the Glitchgrab team" }),
3021
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("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." }),
3022
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
3023
+ "button",
3024
+ {
3025
+ type: "button",
3026
+ "data-gg-self-report-done": "",
3027
+ onClick: onClose,
3028
+ style: { ...primaryButton(true), width: "100%" },
3029
+ children: "Back to your report"
3030
+ }
3031
+ )
3032
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
3033
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
3034
+ "div",
3035
+ {
3036
+ style: {
3037
+ display: "flex",
3038
+ alignItems: "flex-start",
3039
+ justifyContent: "space-between",
3040
+ gap: "12px"
3041
+ },
3042
+ children: [
3043
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { id: "gg-self-report-title", style: { fontSize: "15px", fontWeight: 600 }, children: "Problem with Glitchgrab?" }),
3044
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
3045
+ "button",
3046
+ {
3047
+ type: "button",
3048
+ onClick: onClose,
3049
+ "aria-label": "Close",
3050
+ style: {
3051
+ border: "none",
3052
+ background: "transparent",
3053
+ color: t2.textMuted,
3054
+ fontSize: "18px",
3055
+ lineHeight: 1,
3056
+ cursor: "pointer",
3057
+ padding: "4px 6px",
3058
+ margin: "-4px -6px 0 0"
3059
+ },
3060
+ children: "\xD7"
3061
+ }
3062
+ )
3063
+ ]
3064
+ }
3065
+ ),
3066
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("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." }),
3067
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
3068
+ "textarea",
3069
+ {
3070
+ ref: inputRef,
3071
+ "data-gg-self-report-input": "",
3072
+ value: text,
3073
+ onChange: (e) => {
3074
+ setText(e.target.value);
3075
+ if (error) setError(null);
3076
+ },
3077
+ onKeyDown: (e) => {
3078
+ if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
3079
+ e.preventDefault();
3080
+ void submit();
3081
+ }
3082
+ },
3083
+ rows: 4,
3084
+ placeholder: "e.g. The assistant kept asking the same question and never wrote a draft",
3085
+ style: {
3086
+ width: "100%",
3087
+ boxSizing: "border-box",
3088
+ resize: "vertical",
3089
+ padding: "10px 12px",
3090
+ borderRadius: "8px",
3091
+ border: `1px solid ${t2.inputBorder}`,
3092
+ backgroundColor: t2.inputBg,
3093
+ color: t2.text,
3094
+ fontSize: "14px",
3095
+ lineHeight: 1.45,
3096
+ fontFamily: "inherit",
3097
+ outline: "none"
3098
+ }
3099
+ }
3100
+ ),
3101
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
3102
+ "p",
3103
+ {
3104
+ "data-gg-self-report-includes": "",
3105
+ style: { margin: "8px 0 0", fontSize: "11.5px", lineHeight: 1.45, color: t2.textMuted },
3106
+ children: [
3107
+ "Also sent, so we can see what happened:",
3108
+ " ",
3109
+ describeSentAlong(conversationId !== null, screenshots.length),
3110
+ "."
3111
+ ]
3112
+ }
3113
+ ),
3114
+ error && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { role: "alert", style: { margin: "8px 0 0", fontSize: "12px", color: "#ef4444" }, children: error }),
3115
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex", justifyContent: "flex-end", gap: "8px", marginTop: "14px" }, children: [
3116
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", onClick: onClose, style: secondaryButton, children: "Cancel" }),
3117
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
3118
+ "button",
3119
+ {
3120
+ type: "button",
3121
+ "data-gg-self-report-send": "",
3122
+ onClick: () => void submit(),
3123
+ disabled: !canSend,
3124
+ style: primaryButton(canSend),
3125
+ children: busy ? "Sending\u2026" : "Send to Glitchgrab"
3126
+ }
3127
+ )
3128
+ ] })
3129
+ ] })
3130
+ }
3131
+ )
3132
+ }
3133
+ ),
3134
+ document.body
3135
+ );
3136
+ }
2688
3137
  var GLITCHGRAB_SHORTCUT = "Ctrl+Shift+G";
2689
3138
  var GLITCHGRAB_SHORTCUT_MAC = "\u2318\u21E7G";
2690
3139
  function isMac() {
@@ -2732,6 +3181,7 @@ var ATTACHMENT_ACCEPT = [
2732
3181
  ].join(",");
2733
3182
  var MAX_SCALE = 2;
2734
3183
  var FRAME_TIMEOUT_MS = 3e3;
3184
+ var GRAB_TIMEOUT_MS = FRAME_TIMEOUT_MS + 2e3;
2735
3185
  function canCaptureTabPixels() {
2736
3186
  var _a;
2737
3187
  try {
@@ -2798,7 +3248,13 @@ async function captureViaTabPixels() {
2798
3248
  stream = await navigator.mediaDevices.getDisplayMedia(options);
2799
3249
  const surface = (_b = (_a = stream.getVideoTracks()[0]) == null ? void 0 : _a.getSettings()) == null ? void 0 : _b.displaySurface;
2800
3250
  if (surface && surface !== "browser") return null;
2801
- const canvas = await grabFrame(stream);
3251
+ let timer;
3252
+ const timedOut = new Promise((resolve) => {
3253
+ timer = setTimeout(() => resolve(null), GRAB_TIMEOUT_MS);
3254
+ });
3255
+ const canvas = await Promise.race([grabFrame(stream), timedOut]).finally(
3256
+ () => clearTimeout(timer)
3257
+ );
2802
3258
  return canvas ? encodeScreenshot(canvas) : null;
2803
3259
  } catch (e) {
2804
3260
  return null;
@@ -2806,6 +3262,10 @@ async function captureViaTabPixels() {
2806
3262
  stream == null ? void 0 : stream.getTracks().forEach((track) => track.stop());
2807
3263
  }
2808
3264
  }
3265
+ var IMAGE_TIMEOUT_MS = 3e3;
3266
+ function isGlitchgrabLayer(el) {
3267
+ return el.hasAttribute("data-glitchgrab-layer");
3268
+ }
2809
3269
  async function captureViaHtml2Canvas() {
2810
3270
  try {
2811
3271
  const { default: html2canvas } = await import("html2canvas-pro");
@@ -2815,6 +3275,8 @@ async function captureViaHtml2Canvas() {
2815
3275
  logging: false,
2816
3276
  useCORS: true,
2817
3277
  allowTaint: true,
3278
+ imageTimeout: IMAGE_TIMEOUT_MS,
3279
+ ignoreElements: isGlitchgrabLayer,
2818
3280
  x: window.scrollX,
2819
3281
  y: window.scrollY,
2820
3282
  width: window.innerWidth,
@@ -2827,12 +3289,61 @@ async function captureViaHtml2Canvas() {
2827
3289
  return null;
2828
3290
  }
2829
3291
  }
2830
- async function captureDefaultScreenshot() {
2831
- var _a;
2832
- return (_a = await captureViaTabPixels()) != null ? _a : await captureViaHtml2Canvas();
3292
+ var inFlight = null;
3293
+ function nextPaint() {
3294
+ return new Promise((resolve) => {
3295
+ const cap = setTimeout(resolve, 100);
3296
+ if (typeof requestAnimationFrame !== "function") return;
3297
+ requestAnimationFrame(
3298
+ () => setTimeout(() => {
3299
+ clearTimeout(cap);
3300
+ resolve();
3301
+ }, 0)
3302
+ );
3303
+ });
3304
+ }
3305
+ function captureDefaultScreenshot(options) {
3306
+ if (inFlight) return inFlight;
3307
+ inFlight = (async () => {
3308
+ const pixels = await captureViaTabPixels();
3309
+ if (pixels) return pixels;
3310
+ if (options == null ? void 0 : options.onRedraw) {
3311
+ try {
3312
+ options.onRedraw();
3313
+ } catch (e) {
3314
+ }
3315
+ await nextPaint();
3316
+ }
3317
+ return captureViaHtml2Canvas();
3318
+ })().finally(() => {
3319
+ inFlight = null;
3320
+ });
3321
+ return inFlight;
2833
3322
  }
2834
3323
  var SEVERITY_REQUIRED = "Pick how bad it is before sending.";
2835
- function useIsDark() {
3324
+ var SHOT_WAIT_MS = 3e3;
3325
+ var SIMILAR_WAIT_MS = 2500;
3326
+ var MAX_SIMILAR_SHOWN = 3;
3327
+ function ButtonSpinner() {
3328
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3329
+ "span",
3330
+ {
3331
+ "aria-hidden": "true",
3332
+ style: {
3333
+ display: "inline-block",
3334
+ width: "11px",
3335
+ height: "11px",
3336
+ marginRight: "6px",
3337
+ verticalAlign: "-1px",
3338
+ border: "2px solid currentColor",
3339
+ borderRightColor: "transparent",
3340
+ borderRadius: "50%",
3341
+ animation: "gg-spin .7s linear infinite"
3342
+ }
3343
+ }
3344
+ );
3345
+ }
3346
+ function detectDark() {
2836
3347
  if (typeof window === "undefined") return true;
2837
3348
  try {
2838
3349
  for (const el of [document.body, document.documentElement]) {
@@ -2966,13 +3477,13 @@ function ReporterChip({
2966
3477
  const label = isAnonymous ? "Anonymous" : name;
2967
3478
  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 ");
2968
3479
  const showAvatarImage = !!avatarUrl && !avatarFailed;
2969
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
3480
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
2970
3481
  "span",
2971
3482
  {
2972
3483
  title,
2973
3484
  style: { display: "flex", alignItems: "center", gap: "6px", minWidth: 0 },
2974
3485
  children: [
2975
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
3486
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2976
3487
  "span",
2977
3488
  {
2978
3489
  "aria-hidden": "true",
@@ -2992,7 +3503,7 @@ function ReporterChip({
2992
3503
  color: isAnonymous ? t2.textMuted : t2.accentText,
2993
3504
  border: `1px solid ${isAnonymous ? t2.inputBorder : "transparent"}`
2994
3505
  },
2995
- children: showAvatarImage ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
3506
+ children: showAvatarImage ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2996
3507
  "img",
2997
3508
  {
2998
3509
  src: avatarUrl,
@@ -3002,9 +3513,9 @@ function ReporterChip({
3002
3513
  onError: () => setAvatarFailed(true),
3003
3514
  style: { width: "100%", height: "100%", objectFit: "cover" }
3004
3515
  }
3005
- ) : initials ? initials : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("svg", { width: "10", height: "10", viewBox: "0 0 16 16", fill: "none", children: [
3006
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: "8", cy: "5.5", r: "2.75", stroke: "currentColor", strokeWidth: "1.4" }),
3007
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
3516
+ ) : initials ? initials : /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { width: "10", height: "10", viewBox: "0 0 16 16", fill: "none", children: [
3517
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: "8", cy: "5.5", r: "2.75", stroke: "currentColor", strokeWidth: "1.4" }),
3518
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3008
3519
  "path",
3009
3520
  {
3010
3521
  d: "M2.75 13.5c0-2.35 2.35-3.75 5.25-3.75s5.25 1.4 5.25 3.75",
@@ -3016,7 +3527,7 @@ function ReporterChip({
3016
3527
  ] })
3017
3528
  }
3018
3529
  ),
3019
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
3530
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
3020
3531
  "span",
3021
3532
  {
3022
3533
  style: {
@@ -3138,6 +3649,7 @@ function ReportDialog({
3138
3649
  sendFeedback: sendFeedback2,
3139
3650
  enhanceText: enhanceText2,
3140
3651
  assist,
3652
+ findSimilarIssues: findSimilarIssues2,
3141
3653
  assistContext = null,
3142
3654
  transcribeAudio: transcribeAudio2,
3143
3655
  types,
@@ -3145,15 +3657,23 @@ function ReportDialog({
3145
3657
  captureScreenshot = captureDefaultScreenshot,
3146
3658
  reporter,
3147
3659
  onClose,
3148
- headerSlot
3660
+ headerSlot,
3661
+ reportGlitchgrabProblem
3149
3662
  }) {
3150
3663
  var _a;
3151
3664
  const [isEnhancing, setIsEnhancing] = (0, import_react.useState)(false);
3152
3665
  const [assistOpen, setAssistOpen] = (0, import_react.useState)(false);
3153
3666
  const [assistNotice, setAssistNotice] = (0, import_react.useState)(null);
3154
3667
  const [assistUsed, setAssistUsed] = (0, import_react.useState)(false);
3668
+ const [selfReportFrom, setSelfReportFrom] = (0, import_react.useState)(null);
3669
+ const [selfReportPrefill, setSelfReportPrefill] = (0, import_react.useState)("");
3155
3670
  const [typeChosen, setTypeChosen] = (0, import_react.useState)(false);
3156
3671
  const [duplicateIssueNumber, setDuplicateIssueNumber] = (0, import_react.useState)(null);
3672
+ const [similarIssues, setSimilarIssues] = (0, import_react.useState)(null);
3673
+ const [checkingSimilar, setCheckingSimilar] = (0, import_react.useState)(false);
3674
+ const [attachingTo, setAttachingTo] = (0, import_react.useState)(null);
3675
+ const [pickedSimilar, setPickedSimilar] = (0, import_react.useState)(null);
3676
+ const [addedToIssue, setAddedToIssue] = (0, import_react.useState)(null);
3157
3677
  const [aiConversationId, setAiConversationId] = (0, import_react.useState)(null);
3158
3678
  const [isEnhanced, setIsEnhanced] = (0, import_react.useState)(false);
3159
3679
  const [originalDescription, setOriginalDescription] = (0, import_react.useState)(
@@ -3173,6 +3693,9 @@ function ReportDialog({
3173
3693
  const [submitted, setSubmitted] = (0, import_react.useState)(false);
3174
3694
  const [screenshots, setScreenshots] = (0, import_react.useState)([]);
3175
3695
  const [pageShot, setPageShot] = (0, import_react.useState)(null);
3696
+ const [shotPending, setShotPending] = (0, import_react.useState)(false);
3697
+ const pendingShotRef = (0, import_react.useRef)(null);
3698
+ const openGenRef = (0, import_react.useRef)(0);
3176
3699
  const [attachments, setAttachments] = (0, import_react.useState)([]);
3177
3700
  const [previewIndex, setPreviewIndex] = (0, import_react.useState)(null);
3178
3701
  const [annotatingIndex, setAnnotatingIndex] = (0, import_react.useState)(null);
@@ -3301,8 +3824,19 @@ function ReportDialog({
3301
3824
  const next = current === -1 ? 0 : Math.min(Math.max(current + delta, 0), tiles.length - 1);
3302
3825
  (_c = tiles[next]) == null ? void 0 : _c.focus();
3303
3826
  };
3304
- const isDark = useIsDark();
3827
+ const isDark = (0, import_react.useMemo)(() => isOpen ? detectDark() : true, [isOpen]);
3305
3828
  const t2 = getTheme(isDark);
3829
+ const layerTheme = {
3830
+ bg: t2.bg,
3831
+ bgSecondary: t2.bgSecondary,
3832
+ border: t2.border,
3833
+ text: t2.text,
3834
+ textMuted: t2.textMuted,
3835
+ inputBg: t2.inputBg,
3836
+ inputBorder: t2.inputBorder,
3837
+ accent: t2.accent,
3838
+ accentText: t2.accentText
3839
+ };
3306
3840
  const canZoomPreview = previewNatural !== null && typeof window !== "undefined" && (previewNatural.w > window.innerWidth - 32 || previewNatural.h > window.innerHeight - 80);
3307
3841
  const handleFileUpload = (e) => {
3308
3842
  const files = e.target.files;
@@ -3332,6 +3866,7 @@ function ReportDialog({
3332
3866
  });
3333
3867
  };
3334
3868
  const handleOpen = async () => {
3869
+ const gen = openGenRef.current;
3335
3870
  setSubmitted(false);
3336
3871
  closePreview();
3337
3872
  setAnnotatingIndex(null);
@@ -3340,11 +3875,32 @@ function ReportDialog({
3340
3875
  setTypeChosen(true);
3341
3876
  setStep(2);
3342
3877
  }
3343
- const shot = await captureScreenshot();
3878
+ const show = () => {
3879
+ if (gen !== openGenRef.current) return;
3880
+ if (assist && !assistUsed) setAssistOpen(true);
3881
+ setIsOpen(true);
3882
+ };
3883
+ const capture = captureScreenshot({
3884
+ onRedraw: () => {
3885
+ if (gen !== openGenRef.current) return;
3886
+ setShotPending(true);
3887
+ show();
3888
+ }
3889
+ });
3890
+ pendingShotRef.current = capture;
3891
+ const previousPageShot = pageShot;
3892
+ const shot = await capture.catch(() => null);
3893
+ if (gen !== openGenRef.current) return;
3894
+ pendingShotRef.current = null;
3895
+ setShotPending(false);
3344
3896
  setPageShot(shot != null ? shot : null);
3345
- if (shot) setScreenshots([shot]);
3346
- if (assist && !assistUsed) setAssistOpen(true);
3347
- setIsOpen(true);
3897
+ if (shot) {
3898
+ setScreenshots((prev) => [
3899
+ shot,
3900
+ ...prev.filter((s) => s !== shot && s !== previousPageShot)
3901
+ ]);
3902
+ }
3903
+ show();
3348
3904
  };
3349
3905
  (0, import_react.useEffect)(() => {
3350
3906
  const handler = (e) => {
@@ -3537,6 +4093,9 @@ function ReportDialog({
3537
4093
  stopVoice();
3538
4094
  setIsOpen(false);
3539
4095
  onClose == null ? void 0 : onClose();
4096
+ openGenRef.current += 1;
4097
+ pendingShotRef.current = null;
4098
+ setShotPending(false);
3540
4099
  closePreview();
3541
4100
  setAnnotatingIndex(null);
3542
4101
  setStep(1);
@@ -3545,8 +4104,13 @@ function ReportDialog({
3545
4104
  setReportType("BUG");
3546
4105
  setTypeChosen(false);
3547
4106
  setDuplicateIssueNumber(null);
4107
+ setSimilarIssues(null);
4108
+ setPickedSimilar(null);
4109
+ setCheckingSimilar(false);
4110
+ setAttachingTo(null);
3548
4111
  setSeverity(null);
3549
4112
  setValidationError(null);
4113
+ setSelfReportFrom(null);
3550
4114
  setVoiceError(null);
3551
4115
  setIsEnhanced(false);
3552
4116
  setOriginalDescription(null);
@@ -3731,7 +4295,8 @@ function ReportDialog({
3731
4295
  stopListeningAndTranscribe();
3732
4296
  }
3733
4297
  };
3734
- const handleSubmit = async () => {
4298
+ const handleSubmit = async (opts) => {
4299
+ var _a2;
3735
4300
  try {
3736
4301
  if (isSubmitting) return;
3737
4302
  if (isRating) {
@@ -3768,15 +4333,42 @@ function ReportDialog({
3768
4333
  setStep(2);
3769
4334
  return;
3770
4335
  }
4336
+ const duplicateOf = (_a2 = opts == null ? void 0 : opts.duplicateOf) != null ? _a2 : duplicateIssueNumber;
4337
+ if (findSimilarIssues2 && !(opts == null ? void 0 : opts.skipSimilar) && !duplicateOf && !aiConversationId) {
4338
+ setIsSubmitting(true);
4339
+ setCheckingSimilar(true);
4340
+ const matches = await Promise.race([
4341
+ findSimilarIssues2(description.trim()).catch(() => null),
4342
+ new Promise((resolve) => setTimeout(() => resolve(null), SIMILAR_WAIT_MS))
4343
+ ]);
4344
+ setCheckingSimilar(false);
4345
+ if (matches && matches.length > 0) {
4346
+ setSimilarIssues(matches.slice(0, MAX_SIMILAR_SHOWN));
4347
+ setPickedSimilar(null);
4348
+ setIsSubmitting(false);
4349
+ return;
4350
+ }
4351
+ }
3771
4352
  setIsSubmitting(true);
4353
+ let shots = screenshots;
4354
+ const pending = pendingShotRef.current;
4355
+ if (pending) {
4356
+ const shot = await Promise.race([
4357
+ pending.catch(() => null),
4358
+ new Promise((resolve) => setTimeout(() => resolve(null), SHOT_WAIT_MS))
4359
+ ]);
4360
+ if (shot && !shots.includes(shot)) {
4361
+ shots = [shot, ...shots.filter((s) => s !== pageShot)];
4362
+ }
4363
+ }
3772
4364
  const metadata = {};
3773
- if (screenshots.length > 0) metadata.screenshots = JSON.stringify(screenshots);
4365
+ if (shots.length > 0) metadata.screenshots = JSON.stringify(shots);
3774
4366
  if (attachments.length > 0) metadata.attachments = JSON.stringify(attachments);
3775
4367
  if (showSeverity && reportType === "BUG" && severity) {
3776
4368
  metadata.severity = severity;
3777
4369
  }
3778
- if (duplicateIssueNumber) {
3779
- metadata.duplicateIssueNumber = String(duplicateIssueNumber);
4370
+ if (duplicateOf) {
4371
+ metadata.duplicateIssueNumber = String(duplicateOf);
3780
4372
  }
3781
4373
  if (aiConversationId) {
3782
4374
  metadata.aiConversationId = aiConversationId;
@@ -3789,27 +4381,34 @@ function ReportDialog({
3789
4381
  Object.keys(metadata).length > 0 ? metadata : void 0
3790
4382
  );
3791
4383
  if (result) {
4384
+ setAddedToIssue(duplicateOf && result.issueNumber === duplicateOf ? duplicateOf : null);
3792
4385
  setSubmitted(true);
3793
4386
  setDescription("");
3794
4387
  setScreenshots([]);
3795
4388
  setAttachments([]);
3796
4389
  setAiConversationId(null);
3797
4390
  setDuplicateIssueNumber(null);
4391
+ setSimilarIssues(null);
4392
+ setPickedSimilar(null);
3798
4393
  setTimeout(() => {
3799
4394
  setSubmitted(false);
3800
4395
  handleClose();
3801
4396
  }, 2e3);
3802
4397
  }
3803
4398
  setIsSubmitting(false);
4399
+ setAttachingTo(null);
3804
4400
  } catch (e) {
3805
4401
  setIsSubmitting(false);
4402
+ setCheckingSimilar(false);
4403
+ setAttachingTo(null);
3806
4404
  }
3807
4405
  };
3808
4406
  if (!mounted) return null;
3809
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
4407
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
3810
4408
  isOpen && (0, import_react_dom.createPortal)(
3811
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("style", { children: `
4409
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("style", { children: `
3812
4410
  @keyframes gg-pulse{0%,100%{opacity:1}50%{opacity:0.35}}
4411
+ @keyframes gg-spin{to{transform:rotate(360deg)}}
3813
4412
  @keyframes gg-b1{0%,100%{height:3px}40%{height:13px}}
3814
4413
  @keyframes gg-b2{0%,100%{height:8px}50%{height:3px}}
3815
4414
  @keyframes gg-b3{0%,100%{height:4px}30%{height:14px}70%{height:5px}}
@@ -3845,7 +4444,7 @@ function ReportDialog({
3845
4444
  document.body
3846
4445
  ),
3847
4446
  isOpen && (0, import_react_dom.createPortal)(
3848
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4447
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3849
4448
  "div",
3850
4449
  {
3851
4450
  "data-glitchgrab-layer": "",
@@ -3868,10 +4467,13 @@ function ReportDialog({
3868
4467
  },
3869
4468
  onPointerDown: (e) => e.stopPropagation(),
3870
4469
  onFocus: (e) => e.stopPropagation(),
3871
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
4470
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
3872
4471
  "div",
3873
4472
  {
3874
4473
  ref: modalRef,
4474
+ role: "dialog",
4475
+ "aria-modal": "true",
4476
+ "aria-label": "Report an issue",
3875
4477
  onClick: (e) => e.stopPropagation(),
3876
4478
  style: {
3877
4479
  position: "relative",
@@ -3890,7 +4492,7 @@ function ReportDialog({
3890
4492
  isolation: "isolate"
3891
4493
  },
3892
4494
  children: [
3893
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
4495
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
3894
4496
  "div",
3895
4497
  {
3896
4498
  style: {
@@ -3899,7 +4501,7 @@ function ReportDialog({
3899
4501
  borderBottom: `1px solid ${t2.border}`
3900
4502
  },
3901
4503
  children: [
3902
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
4504
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
3903
4505
  "div",
3904
4506
  {
3905
4507
  style: {
@@ -3908,7 +4510,7 @@ function ReportDialog({
3908
4510
  alignItems: "center"
3909
4511
  },
3910
4512
  children: [
3911
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
4513
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
3912
4514
  "div",
3913
4515
  {
3914
4516
  style: {
@@ -3917,7 +4519,7 @@ function ReportDialog({
3917
4519
  gap: "8px"
3918
4520
  },
3919
4521
  children: [
3920
- step > 1 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4522
+ step > 1 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3921
4523
  "button",
3922
4524
  {
3923
4525
  type: "button",
@@ -3931,14 +4533,14 @@ function ReportDialog({
3931
4533
  alignItems: "center"
3932
4534
  },
3933
4535
  "aria-label": "Back",
3934
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4536
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3935
4537
  "svg",
3936
4538
  {
3937
4539
  width: "16",
3938
4540
  height: "16",
3939
4541
  viewBox: "0 0 16 16",
3940
4542
  fill: "none",
3941
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4543
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3942
4544
  "path",
3943
4545
  {
3944
4546
  d: "M10 3L5 8L10 13",
@@ -3952,7 +4554,7 @@ function ReportDialog({
3952
4554
  )
3953
4555
  }
3954
4556
  ),
3955
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4557
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3956
4558
  "span",
3957
4559
  {
3958
4560
  style: {
@@ -3966,7 +4568,7 @@ function ReportDialog({
3966
4568
  ]
3967
4569
  }
3968
4570
  ),
3969
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
4571
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
3970
4572
  "div",
3971
4573
  {
3972
4574
  style: {
@@ -3975,7 +4577,7 @@ function ReportDialog({
3975
4577
  gap: "8px"
3976
4578
  },
3977
4579
  children: [
3978
- step === 1 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4580
+ step === 1 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3979
4581
  "span",
3980
4582
  {
3981
4583
  title: "Press this anywhere to open Glitchgrab",
@@ -3993,7 +4595,7 @@ function ReportDialog({
3993
4595
  children: getShortcutLabel()
3994
4596
  }
3995
4597
  ),
3996
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4598
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3997
4599
  "button",
3998
4600
  {
3999
4601
  type: "button",
@@ -4008,7 +4610,7 @@ function ReportDialog({
4008
4610
  justifyContent: "center"
4009
4611
  },
4010
4612
  "aria-label": "Close",
4011
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4613
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4012
4614
  "svg",
4013
4615
  {
4014
4616
  width: "16",
@@ -4016,7 +4618,7 @@ function ReportDialog({
4016
4618
  viewBox: "0 0 16 16",
4017
4619
  fill: "none",
4018
4620
  style: { display: "block" },
4019
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4621
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4020
4622
  "path",
4021
4623
  {
4022
4624
  d: "M4 4L12 12M12 4L4 12",
@@ -4035,7 +4637,7 @@ function ReportDialog({
4035
4637
  ]
4036
4638
  }
4037
4639
  ),
4038
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4640
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4039
4641
  "div",
4040
4642
  {
4041
4643
  style: {
@@ -4045,12 +4647,12 @@ function ReportDialog({
4045
4647
  gap: "0",
4046
4648
  marginTop: "12px"
4047
4649
  },
4048
- children: [1, 2].map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
4650
+ children: [1, 2].map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
4049
4651
  "div",
4050
4652
  {
4051
4653
  style: { display: "flex", alignItems: "center" },
4052
4654
  children: [
4053
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4655
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4054
4656
  "div",
4055
4657
  {
4056
4658
  style: {
@@ -4062,7 +4664,7 @@ function ReportDialog({
4062
4664
  }
4063
4665
  }
4064
4666
  ),
4065
- i < 1 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4667
+ i < 1 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4066
4668
  "div",
4067
4669
  {
4068
4670
  style: {
@@ -4082,7 +4684,7 @@ function ReportDialog({
4082
4684
  ]
4083
4685
  }
4084
4686
  ),
4085
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4687
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4086
4688
  "div",
4087
4689
  {
4088
4690
  style: {
@@ -4091,7 +4693,7 @@ function ReportDialog({
4091
4693
  flex: "1 1 auto",
4092
4694
  minHeight: 0
4093
4695
  },
4094
- children: submitted ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
4696
+ children: submitted ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4095
4697
  "div",
4096
4698
  {
4097
4699
  style: {
@@ -4101,14 +4703,11 @@ function ReportDialog({
4101
4703
  fontSize: "14px",
4102
4704
  fontWeight: 500
4103
4705
  },
4104
- children: [
4105
- getTypeLabel(reportType),
4106
- " sent. Thank you!"
4107
- ]
4706
+ children: addedToIssue ? `Added to #${addedToIssue}. Thank you!` : `${getTypeLabel(reportType)} sent. Thank you!`
4108
4707
  }
4109
- ) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
4110
- step === 1 && headerSlot ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { marginBottom: "14px" }, children: headerSlot }) : null,
4111
- step === 1 && sendFeedback2 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
4708
+ ) : /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
4709
+ step === 1 && headerSlot ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { marginBottom: "14px" }, children: headerSlot }) : null,
4710
+ step === 1 && sendFeedback2 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
4112
4711
  "div",
4113
4712
  {
4114
4713
  style: {
@@ -4125,16 +4724,16 @@ function ReportDialog({
4125
4724
  },
4126
4725
  onMouseLeave: () => setHoveredStar(0),
4127
4726
  children: [
4128
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4727
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4129
4728
  "span",
4130
4729
  {
4131
4730
  style: { fontSize: "13px", fontWeight: 600, color: t2.text },
4132
4731
  children: "How are we doing?"
4133
4732
  }
4134
4733
  ),
4135
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { display: "flex", gap: "4px" }, children: [1, 2, 3, 4, 5].map((star) => {
4734
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { display: "flex", gap: "4px" }, children: [1, 2, 3, 4, 5].map((star) => {
4136
4735
  const filled = star <= (hoveredStar || rating);
4137
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4736
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4138
4737
  "button",
4139
4738
  {
4140
4739
  type: "button",
@@ -4155,7 +4754,7 @@ function ReportDialog({
4155
4754
  cursor: "pointer",
4156
4755
  lineHeight: 0
4157
4756
  },
4158
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("svg", { width: "26", height: "26", viewBox: "0 0 24 24", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4757
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("svg", { width: "26", height: "26", viewBox: "0 0 24 24", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4159
4758
  "path",
4160
4759
  {
4161
4760
  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",
@@ -4172,7 +4771,7 @@ function ReportDialog({
4172
4771
  ]
4173
4772
  }
4174
4773
  ),
4175
- step === 1 && sendFeedback2 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
4774
+ step === 1 && sendFeedback2 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
4176
4775
  "div",
4177
4776
  {
4178
4777
  style: {
@@ -4182,13 +4781,13 @@ function ReportDialog({
4182
4781
  marginBottom: "16px"
4183
4782
  },
4184
4783
  children: [
4185
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { flex: 1, height: "1px", background: t2.border } }),
4186
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { fontSize: "11px", color: t2.textMuted }, children: "or report something" }),
4187
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { flex: 1, height: "1px", background: t2.border } })
4784
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { flex: 1, height: "1px", background: t2.border } }),
4785
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { fontSize: "11px", color: t2.textMuted }, children: "or report something" }),
4786
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { flex: 1, height: "1px", background: t2.border } })
4188
4787
  ]
4189
4788
  }
4190
4789
  ),
4191
- step === 1 && screenshots.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
4790
+ step === 1 && screenshots.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
4192
4791
  "div",
4193
4792
  {
4194
4793
  style: {
@@ -4202,7 +4801,7 @@ function ReportDialog({
4202
4801
  background: t2.inputBg
4203
4802
  },
4204
4803
  children: [
4205
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4804
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4206
4805
  "img",
4207
4806
  {
4208
4807
  src: screenshots[0],
@@ -4217,8 +4816,8 @@ function ReportDialog({
4217
4816
  }
4218
4817
  }
4219
4818
  ),
4220
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { fontSize: "11px", color: t2.textMuted, flex: 1 }, children: "Screenshot attached" }),
4221
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4819
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { fontSize: "11px", color: t2.textMuted, flex: 1 }, children: "Screenshot attached" }),
4820
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4222
4821
  "button",
4223
4822
  {
4224
4823
  type: "button",
@@ -4239,7 +4838,7 @@ function ReportDialog({
4239
4838
  ]
4240
4839
  }
4241
4840
  ),
4242
- step === 1 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4841
+ step === 1 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4243
4842
  "div",
4244
4843
  {
4245
4844
  ref: tileGridRef,
@@ -4249,7 +4848,7 @@ function ReportDialog({
4249
4848
  gridTemplateColumns: "1fr 1fr 1fr",
4250
4849
  gap: "10px"
4251
4850
  },
4252
- children: availableTypes.map((type) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
4851
+ children: availableTypes.map((type) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
4253
4852
  "button",
4254
4853
  {
4255
4854
  type: "button",
@@ -4280,7 +4879,7 @@ function ReportDialog({
4280
4879
  },
4281
4880
  children: [
4282
4881
  getTypeIcon(type, t2.accent, 22),
4283
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4882
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4284
4883
  "span",
4285
4884
  {
4286
4885
  style: {
@@ -4298,8 +4897,8 @@ function ReportDialog({
4298
4897
  ))
4299
4898
  }
4300
4899
  ),
4301
- step === 2 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
4302
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
4900
+ step === 2 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
4901
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
4303
4902
  "button",
4304
4903
  {
4305
4904
  type: "button",
@@ -4322,11 +4921,11 @@ function ReportDialog({
4322
4921
  children: [
4323
4922
  getTypeIcon(reportType, isRating ? "#f59e0b" : t2.accent, 13),
4324
4923
  getTypeLabel(reportType),
4325
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { color: t2.textMuted, fontWeight: 400 }, children: "\xB7 change" })
4924
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { color: t2.textMuted, fontWeight: 400 }, children: "\xB7 change" })
4326
4925
  ]
4327
4926
  }
4328
4927
  ),
4329
- isRating && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4928
+ isRating && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4330
4929
  "div",
4331
4930
  {
4332
4931
  style: {
@@ -4337,7 +4936,7 @@ function ReportDialog({
4337
4936
  onMouseLeave: () => setHoveredStar(0),
4338
4937
  children: [1, 2, 3, 4, 5].map((star) => {
4339
4938
  const filled = star <= (hoveredStar || rating);
4340
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4939
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4341
4940
  "button",
4342
4941
  {
4343
4942
  type: "button",
@@ -4354,7 +4953,7 @@ function ReportDialog({
4354
4953
  cursor: "pointer",
4355
4954
  lineHeight: 0
4356
4955
  },
4357
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("svg", { width: "30", height: "30", viewBox: "0 0 24 24", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4956
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("svg", { width: "30", height: "30", viewBox: "0 0 24 24", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4358
4957
  "path",
4359
4958
  {
4360
4959
  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",
@@ -4370,7 +4969,7 @@ function ReportDialog({
4370
4969
  })
4371
4970
  }
4372
4971
  ),
4373
- assist && !isRating && !assistUsed && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
4972
+ assist && !isRating && !assistUsed && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
4374
4973
  "button",
4375
4974
  {
4376
4975
  type: "button",
@@ -4397,7 +4996,7 @@ function ReportDialog({
4397
4996
  },
4398
4997
  title: "Answer a question or two and the assistant writes the report for you",
4399
4998
  children: [
4400
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4999
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4401
5000
  "path",
4402
5001
  {
4403
5002
  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",
@@ -4410,8 +5009,8 @@ function ReportDialog({
4410
5009
  ]
4411
5010
  }
4412
5011
  ),
4413
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { position: "relative" }, children: [
4414
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5012
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { position: "relative" }, children: [
5013
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4415
5014
  "textarea",
4416
5015
  {
4417
5016
  ref: textareaRef,
@@ -4453,7 +5052,7 @@ function ReportDialog({
4453
5052
  autoFocus: true
4454
5053
  }
4455
5054
  ),
4456
- (isListening || isTranscribing) && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
5055
+ (isListening || isTranscribing) && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
4457
5056
  "span",
4458
5057
  {
4459
5058
  style: {
@@ -4471,7 +5070,7 @@ function ReportDialog({
4471
5070
  zIndex: 1
4472
5071
  },
4473
5072
  children: [
4474
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5073
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4475
5074
  "span",
4476
5075
  {
4477
5076
  style: {
@@ -4486,7 +5085,7 @@ function ReportDialog({
4486
5085
  }
4487
5086
  ),
4488
5087
  isTranscribing ? "Transcribing\u2026" : "REC",
4489
- isListening && !isTranscribing && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5088
+ isListening && !isTranscribing && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4490
5089
  "span",
4491
5090
  {
4492
5091
  style: {
@@ -4513,7 +5112,7 @@ function ReportDialog({
4513
5112
  dur: "0.55s",
4514
5113
  delay: "0.15s"
4515
5114
  }
4516
- ].map((b, i) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5115
+ ].map((b, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4517
5116
  "span",
4518
5117
  {
4519
5118
  style: {
@@ -4533,7 +5132,7 @@ function ReportDialog({
4533
5132
  ]
4534
5133
  }
4535
5134
  ),
4536
- enhanceText2 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
5135
+ enhanceText2 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
4537
5136
  "button",
4538
5137
  {
4539
5138
  type: "button",
@@ -4578,7 +5177,7 @@ function ReportDialog({
4578
5177
  },
4579
5178
  title: "Polish grammar \u2014 preserves your meaning",
4580
5179
  children: [
4581
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5180
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4582
5181
  "svg",
4583
5182
  {
4584
5183
  width: "11",
@@ -4586,7 +5185,7 @@ function ReportDialog({
4586
5185
  viewBox: "0 0 24 24",
4587
5186
  fill: "none",
4588
5187
  "aria-hidden": "true",
4589
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5188
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4590
5189
  "path",
4591
5190
  {
4592
5191
  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",
@@ -4601,7 +5200,7 @@ function ReportDialog({
4601
5200
  ]
4602
5201
  }
4603
5202
  ),
4604
- transcribeAudio2 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5203
+ transcribeAudio2 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4605
5204
  "button",
4606
5205
  {
4607
5206
  type: "button",
@@ -4627,7 +5226,7 @@ function ReportDialog({
4627
5226
  padding: 0,
4628
5227
  transition: "border-color 0.2s ease, color 0.2s ease"
4629
5228
  },
4630
- children: isListening ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5229
+ children: isListening ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4631
5230
  "svg",
4632
5231
  {
4633
5232
  width: "12",
@@ -4635,7 +5234,7 @@ function ReportDialog({
4635
5234
  viewBox: "0 0 24 24",
4636
5235
  fill: "currentColor",
4637
5236
  "aria-hidden": "true",
4638
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5237
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4639
5238
  "rect",
4640
5239
  {
4641
5240
  x: "6",
@@ -4646,7 +5245,7 @@ function ReportDialog({
4646
5245
  }
4647
5246
  )
4648
5247
  }
4649
- ) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
5248
+ ) : /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
4650
5249
  "svg",
4651
5250
  {
4652
5251
  width: "13",
@@ -4655,7 +5254,7 @@ function ReportDialog({
4655
5254
  fill: "none",
4656
5255
  "aria-hidden": "true",
4657
5256
  children: [
4658
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5257
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4659
5258
  "rect",
4660
5259
  {
4661
5260
  x: "9",
@@ -4667,7 +5266,7 @@ function ReportDialog({
4667
5266
  strokeWidth: "1.8"
4668
5267
  }
4669
5268
  ),
4670
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5269
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4671
5270
  "path",
4672
5271
  {
4673
5272
  d: "M5 10a7 7 0 0014 0M12 19v3M9 22h6",
@@ -4682,7 +5281,7 @@ function ReportDialog({
4682
5281
  }
4683
5282
  )
4684
5283
  ] }),
4685
- transcribeAudio2 && !isListening && !isTranscribing && !voiceError && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
5284
+ transcribeAudio2 && !isListening && !isTranscribing && !voiceError && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
4686
5285
  "div",
4687
5286
  {
4688
5287
  style: {
@@ -4696,16 +5295,16 @@ function ReportDialog({
4696
5295
  userSelect: "none"
4697
5296
  },
4698
5297
  children: [
4699
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("svg", { width: "11", height: "11", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
4700
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z" }),
4701
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M19 10v2a7 7 0 0 1-14 0v-2" }),
4702
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: "12", x2: "12", y1: "19", y2: "22" })
5298
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { width: "11", height: "11", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
5299
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z" }),
5300
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M19 10v2a7 7 0 0 1-14 0v-2" }),
5301
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("line", { x1: "12", x2: "12", y1: "19", y2: "22" })
4703
5302
  ] }),
4704
5303
  "Hold Space to speak \u2014 we'll transcribe it"
4705
5304
  ]
4706
5305
  }
4707
5306
  ),
4708
- isEnhanced && originalDescription !== null && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
5307
+ isEnhanced && originalDescription !== null && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
4709
5308
  "div",
4710
5309
  {
4711
5310
  style: {
@@ -4719,7 +5318,7 @@ function ReportDialog({
4719
5318
  border: `1px solid ${t2.accent}40`
4720
5319
  },
4721
5320
  children: [
4722
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5321
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4723
5322
  "svg",
4724
5323
  {
4725
5324
  width: "12",
@@ -4727,7 +5326,7 @@ function ReportDialog({
4727
5326
  viewBox: "0 0 24 24",
4728
5327
  fill: "none",
4729
5328
  style: { flexShrink: 0 },
4730
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5329
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4731
5330
  "path",
4732
5331
  {
4733
5332
  d: "M12 3l1.5 4.5L18 9l-4.5 1.5L12 15l-1.5-4.5L6 9l4.5-1.5L12 3z",
@@ -4738,7 +5337,7 @@ function ReportDialog({
4738
5337
  )
4739
5338
  }
4740
5339
  ),
4741
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5340
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4742
5341
  "span",
4743
5342
  {
4744
5343
  style: {
@@ -4749,7 +5348,7 @@ function ReportDialog({
4749
5348
  children: "AI enhanced"
4750
5349
  }
4751
5350
  ),
4752
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5351
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4753
5352
  "button",
4754
5353
  {
4755
5354
  type: "button",
@@ -4771,7 +5370,7 @@ function ReportDialog({
4771
5370
  children: "Keep"
4772
5371
  }
4773
5372
  ),
4774
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5373
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4775
5374
  "button",
4776
5375
  {
4777
5376
  type: "button",
@@ -4797,7 +5396,7 @@ function ReportDialog({
4797
5396
  ]
4798
5397
  }
4799
5398
  ),
4800
- voiceError && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5399
+ voiceError && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4801
5400
  "p",
4802
5401
  {
4803
5402
  role: "alert",
@@ -4811,7 +5410,7 @@ function ReportDialog({
4811
5410
  children: voiceError
4812
5411
  }
4813
5412
  ),
4814
- assistNotice && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5413
+ assistNotice && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4815
5414
  "p",
4816
5415
  {
4817
5416
  style: {
@@ -4824,8 +5423,8 @@ function ReportDialog({
4824
5423
  children: assistNotice
4825
5424
  }
4826
5425
  ),
4827
- !isRating && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { marginTop: "10px" }, children: [
4828
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
5426
+ !isRating && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { marginTop: "10px" }, children: [
5427
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
4829
5428
  "span",
4830
5429
  {
4831
5430
  style: {
@@ -4841,7 +5440,7 @@ function ReportDialog({
4841
5440
  ]
4842
5441
  }
4843
5442
  ),
4844
- screenshots.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5443
+ (screenshots.length > 0 || shotPending) && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
4845
5444
  "div",
4846
5445
  {
4847
5446
  style: {
@@ -4850,132 +5449,159 @@ function ReportDialog({
4850
5449
  gap: "8px",
4851
5450
  marginBottom: "8px"
4852
5451
  },
4853
- children: screenshots.map((src, i) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
4854
- "div",
4855
- {
4856
- style: {
4857
- position: "relative",
4858
- width: "56px",
4859
- height: "56px"
4860
- },
4861
- children: [
4862
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4863
- "img",
4864
- {
4865
- src,
4866
- alt: `Screenshot ${i + 1}`,
4867
- onClick: () => openPreview(i),
4868
- style: {
4869
- width: "100%",
4870
- height: "100%",
4871
- borderRadius: "6px",
4872
- border: `1px solid ${t2.border}`,
4873
- objectFit: "cover",
4874
- objectPosition: "top",
4875
- cursor: "zoom-in",
4876
- display: "block"
4877
- }
4878
- }
4879
- ),
4880
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4881
- "button",
4882
- {
4883
- type: "button",
4884
- onClick: () => setScreenshots(
4885
- (prev) => prev.filter((_, idx) => idx !== i)
4886
- ),
4887
- "aria-label": `Remove screenshot ${i + 1}`,
4888
- style: {
4889
- position: "absolute",
4890
- top: "-6px",
4891
- right: "-6px",
4892
- width: "17px",
4893
- height: "17px",
4894
- borderRadius: "50%",
4895
- border: "none",
4896
- background: "#ef4444",
4897
- color: "#fff",
4898
- fontSize: "11px",
4899
- lineHeight: 1,
4900
- padding: 0,
4901
- cursor: "pointer",
4902
- display: "flex",
4903
- alignItems: "center",
4904
- justifyContent: "center"
4905
- },
4906
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4907
- "svg",
4908
- {
4909
- width: "8",
4910
- height: "8",
4911
- viewBox: "0 0 10 10",
4912
- fill: "none",
4913
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4914
- "path",
4915
- {
4916
- d: "M1 1L9 9M9 1L1 9",
4917
- stroke: "currentColor",
4918
- strokeWidth: "1.5",
4919
- strokeLinecap: "round"
4920
- }
4921
- )
4922
- }
4923
- )
4924
- }
4925
- ),
4926
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4927
- "button",
4928
- {
4929
- type: "button",
4930
- onClick: () => setAnnotatingIndex(i),
4931
- "aria-label": `Annotate screenshot ${i + 1}`,
4932
- title: "Annotate",
4933
- style: {
4934
- position: "absolute",
4935
- top: "50%",
4936
- left: "50%",
4937
- transform: "translate(-50%, -50%)",
4938
- width: "24px",
4939
- height: "24px",
4940
- borderRadius: "50%",
4941
- border: "none",
4942
- background: t2.accent,
4943
- color: t2.accentText,
4944
- padding: 0,
4945
- cursor: "pointer",
4946
- display: "flex",
4947
- alignItems: "center",
4948
- justifyContent: "center",
4949
- boxShadow: "0 2px 8px rgba(0,0,0,0.45)"
4950
- },
4951
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4952
- "svg",
4953
- {
4954
- width: "12",
4955
- height: "12",
4956
- viewBox: "0 0 24 24",
4957
- fill: "none",
4958
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4959
- "path",
4960
- {
4961
- d: "M12 20h9M16.5 3.5a2.12 2.12 0 013 3L7 19l-4 1 1-4L16.5 3.5z",
4962
- stroke: "currentColor",
4963
- strokeWidth: "2.2",
4964
- strokeLinecap: "round",
4965
- strokeLinejoin: "round"
4966
- }
4967
- )
5452
+ children: [
5453
+ shotPending && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5454
+ "div",
5455
+ {
5456
+ role: "status",
5457
+ "aria-label": "Capturing screenshot",
5458
+ title: "Capturing a screenshot of the page\u2026",
5459
+ style: {
5460
+ width: "56px",
5461
+ height: "56px",
5462
+ borderRadius: "6px",
5463
+ border: `1px dashed ${t2.border}`,
5464
+ display: "flex",
5465
+ alignItems: "center",
5466
+ justifyContent: "center",
5467
+ padding: "4px",
5468
+ boxSizing: "border-box",
5469
+ fontSize: "9px",
5470
+ lineHeight: 1.2,
5471
+ textAlign: "center",
5472
+ color: t2.textMuted,
5473
+ animation: "gg-pulse 1.4s ease-in-out infinite"
5474
+ },
5475
+ children: "Capturing\u2026"
5476
+ }
5477
+ ),
5478
+ screenshots.map((src, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
5479
+ "div",
5480
+ {
5481
+ style: {
5482
+ position: "relative",
5483
+ width: "56px",
5484
+ height: "56px"
5485
+ },
5486
+ children: [
5487
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5488
+ "img",
5489
+ {
5490
+ src,
5491
+ alt: `Screenshot ${i + 1}`,
5492
+ onClick: () => openPreview(i),
5493
+ style: {
5494
+ width: "100%",
5495
+ height: "100%",
5496
+ borderRadius: "6px",
5497
+ border: `1px solid ${t2.border}`,
5498
+ objectFit: "cover",
5499
+ objectPosition: "top",
5500
+ cursor: "zoom-in",
5501
+ display: "block"
4968
5502
  }
4969
- )
4970
- }
4971
- )
4972
- ]
4973
- },
4974
- i
4975
- ))
5503
+ }
5504
+ ),
5505
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5506
+ "button",
5507
+ {
5508
+ type: "button",
5509
+ onClick: () => setScreenshots(
5510
+ (prev) => prev.filter((_, idx) => idx !== i)
5511
+ ),
5512
+ "aria-label": `Remove screenshot ${i + 1}`,
5513
+ style: {
5514
+ position: "absolute",
5515
+ top: "-6px",
5516
+ right: "-6px",
5517
+ width: "17px",
5518
+ height: "17px",
5519
+ borderRadius: "50%",
5520
+ border: "none",
5521
+ background: "#ef4444",
5522
+ color: "#fff",
5523
+ fontSize: "11px",
5524
+ lineHeight: 1,
5525
+ padding: 0,
5526
+ cursor: "pointer",
5527
+ display: "flex",
5528
+ alignItems: "center",
5529
+ justifyContent: "center"
5530
+ },
5531
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5532
+ "svg",
5533
+ {
5534
+ width: "8",
5535
+ height: "8",
5536
+ viewBox: "0 0 10 10",
5537
+ fill: "none",
5538
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5539
+ "path",
5540
+ {
5541
+ d: "M1 1L9 9M9 1L1 9",
5542
+ stroke: "currentColor",
5543
+ strokeWidth: "1.5",
5544
+ strokeLinecap: "round"
5545
+ }
5546
+ )
5547
+ }
5548
+ )
5549
+ }
5550
+ ),
5551
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5552
+ "button",
5553
+ {
5554
+ type: "button",
5555
+ onClick: () => setAnnotatingIndex(i),
5556
+ "aria-label": `Annotate screenshot ${i + 1}`,
5557
+ title: "Annotate",
5558
+ style: {
5559
+ position: "absolute",
5560
+ top: "50%",
5561
+ left: "50%",
5562
+ transform: "translate(-50%, -50%)",
5563
+ width: "24px",
5564
+ height: "24px",
5565
+ borderRadius: "50%",
5566
+ border: "none",
5567
+ background: t2.accent,
5568
+ color: t2.accentText,
5569
+ padding: 0,
5570
+ cursor: "pointer",
5571
+ display: "flex",
5572
+ alignItems: "center",
5573
+ justifyContent: "center",
5574
+ boxShadow: "0 2px 8px rgba(0,0,0,0.45)"
5575
+ },
5576
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5577
+ "svg",
5578
+ {
5579
+ width: "12",
5580
+ height: "12",
5581
+ viewBox: "0 0 24 24",
5582
+ fill: "none",
5583
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5584
+ "path",
5585
+ {
5586
+ d: "M12 20h9M16.5 3.5a2.12 2.12 0 013 3L7 19l-4 1 1-4L16.5 3.5z",
5587
+ stroke: "currentColor",
5588
+ strokeWidth: "2.2",
5589
+ strokeLinecap: "round",
5590
+ strokeLinejoin: "round"
5591
+ }
5592
+ )
5593
+ }
5594
+ )
5595
+ }
5596
+ )
5597
+ ]
5598
+ },
5599
+ i
5600
+ ))
5601
+ ]
4976
5602
  }
4977
5603
  ),
4978
- attachments.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5604
+ attachments.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4979
5605
  "div",
4980
5606
  {
4981
5607
  style: {
@@ -4984,7 +5610,7 @@ function ReportDialog({
4984
5610
  gap: "6px",
4985
5611
  marginBottom: "8px"
4986
5612
  },
4987
- children: attachments.map((a, i) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
5613
+ children: attachments.map((a, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
4988
5614
  "div",
4989
5615
  {
4990
5616
  style: {
@@ -4996,7 +5622,7 @@ function ReportDialog({
4996
5622
  border: `1px solid ${t2.border}`
4997
5623
  },
4998
5624
  children: [
4999
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
5625
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
5000
5626
  "svg",
5001
5627
  {
5002
5628
  width: "14",
@@ -5005,7 +5631,7 @@ function ReportDialog({
5005
5631
  fill: "none",
5006
5632
  style: { flexShrink: 0, opacity: 0.6 },
5007
5633
  children: [
5008
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5634
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5009
5635
  "path",
5010
5636
  {
5011
5637
  d: "M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z",
@@ -5015,7 +5641,7 @@ function ReportDialog({
5015
5641
  strokeLinejoin: "round"
5016
5642
  }
5017
5643
  ),
5018
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5644
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5019
5645
  "path",
5020
5646
  {
5021
5647
  d: "M14 2v6h6",
@@ -5028,7 +5654,7 @@ function ReportDialog({
5028
5654
  ]
5029
5655
  }
5030
5656
  ),
5031
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5657
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5032
5658
  "span",
5033
5659
  {
5034
5660
  style: {
@@ -5042,7 +5668,7 @@ function ReportDialog({
5042
5668
  children: a.name
5043
5669
  }
5044
5670
  ),
5045
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
5671
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
5046
5672
  "span",
5047
5673
  {
5048
5674
  style: { fontSize: "10px", color: t2.textMuted },
@@ -5052,7 +5678,7 @@ function ReportDialog({
5052
5678
  ]
5053
5679
  }
5054
5680
  ),
5055
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5681
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5056
5682
  "button",
5057
5683
  {
5058
5684
  type: "button",
@@ -5076,14 +5702,14 @@ function ReportDialog({
5076
5702
  justifyContent: "center",
5077
5703
  flexShrink: 0
5078
5704
  },
5079
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5705
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5080
5706
  "svg",
5081
5707
  {
5082
5708
  width: "8",
5083
5709
  height: "8",
5084
5710
  viewBox: "0 0 10 10",
5085
5711
  fill: "none",
5086
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5712
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5087
5713
  "path",
5088
5714
  {
5089
5715
  d: "M1 1L9 9M9 1L1 9",
@@ -5102,7 +5728,7 @@ function ReportDialog({
5102
5728
  ))
5103
5729
  }
5104
5730
  ),
5105
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5731
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5106
5732
  "div",
5107
5733
  {
5108
5734
  onClick: () => {
@@ -5128,7 +5754,7 @@ function ReportDialog({
5128
5754
  background: screenshotDragOver ? isDark ? "rgba(34,211,238,0.06)" : "rgba(8,145,178,0.04)" : "transparent",
5129
5755
  transition: "border-color 0.15s ease, background 0.15s ease"
5130
5756
  },
5131
- children: screenshots.length + attachments.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5757
+ children: screenshots.length + attachments.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5132
5758
  "p",
5133
5759
  {
5134
5760
  style: {
@@ -5139,8 +5765,8 @@ function ReportDialog({
5139
5765
  },
5140
5766
  children: screenshotDragOver ? "Drop to attach" : "+ Add more \xB7 Drag & drop \xB7 Paste"
5141
5767
  }
5142
- ) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
5143
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
5768
+ ) : /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
5769
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
5144
5770
  "svg",
5145
5771
  {
5146
5772
  width: "22",
@@ -5153,7 +5779,7 @@ function ReportDialog({
5153
5779
  opacity: 0.45
5154
5780
  },
5155
5781
  children: [
5156
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5782
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5157
5783
  "rect",
5158
5784
  {
5159
5785
  x: "3",
@@ -5165,7 +5791,7 @@ function ReportDialog({
5165
5791
  strokeWidth: "1.5"
5166
5792
  }
5167
5793
  ),
5168
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5794
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5169
5795
  "circle",
5170
5796
  {
5171
5797
  cx: "8.5",
@@ -5175,7 +5801,7 @@ function ReportDialog({
5175
5801
  strokeWidth: "1.5"
5176
5802
  }
5177
5803
  ),
5178
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5804
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5179
5805
  "path",
5180
5806
  {
5181
5807
  d: "M21 15l-5-5L5 21",
@@ -5188,7 +5814,7 @@ function ReportDialog({
5188
5814
  ]
5189
5815
  }
5190
5816
  ),
5191
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5817
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5192
5818
  "p",
5193
5819
  {
5194
5820
  style: {
@@ -5200,7 +5826,7 @@ function ReportDialog({
5200
5826
  children: screenshotDragOver ? "Drop to attach" : "Add screenshots or files"
5201
5827
  }
5202
5828
  ),
5203
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5829
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5204
5830
  "p",
5205
5831
  {
5206
5832
  style: {
@@ -5215,8 +5841,8 @@ function ReportDialog({
5215
5841
  }
5216
5842
  )
5217
5843
  ] }),
5218
- showSeverity && reportType === "BUG" && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { marginTop: "10px" }, children: [
5219
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
5844
+ showSeverity && reportType === "BUG" && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { marginTop: "10px" }, children: [
5845
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
5220
5846
  "span",
5221
5847
  {
5222
5848
  style: {
@@ -5229,7 +5855,7 @@ function ReportDialog({
5229
5855
  },
5230
5856
  children: [
5231
5857
  "How bad is it?",
5232
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5858
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5233
5859
  "span",
5234
5860
  {
5235
5861
  style: {
@@ -5242,7 +5868,7 @@ function ReportDialog({
5242
5868
  ]
5243
5869
  }
5244
5870
  ),
5245
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5871
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5246
5872
  "div",
5247
5873
  {
5248
5874
  role: "radiogroup",
@@ -5250,7 +5876,7 @@ function ReportDialog({
5250
5876
  style: { display: "flex", gap: "6px" },
5251
5877
  children: SEVERITY_LEVELS.map((s) => {
5252
5878
  const on = severity === s;
5253
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5879
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5254
5880
  "button",
5255
5881
  {
5256
5882
  type: "button",
@@ -5291,7 +5917,7 @@ function ReportDialog({
5291
5917
  }
5292
5918
  )
5293
5919
  ] }),
5294
- validationError && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5920
+ validationError && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5295
5921
  "p",
5296
5922
  {
5297
5923
  style: {
@@ -5302,11 +5928,194 @@ function ReportDialog({
5302
5928
  children: validationError
5303
5929
  }
5304
5930
  ),
5305
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5931
+ similarIssues ? (
5932
+ // Same visual language as the assistant's "Our team is
5933
+ // already on this" card, so both routes to a duplicate
5934
+ // read as one feature.
5935
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
5936
+ "div",
5937
+ {
5938
+ "data-gg-similar": "",
5939
+ style: {
5940
+ marginTop: "12px",
5941
+ display: "flex",
5942
+ flexDirection: "column",
5943
+ gap: "10px",
5944
+ padding: "12px",
5945
+ borderRadius: "10px",
5946
+ border: "1px solid rgba(245,158,11,0.4)",
5947
+ backgroundColor: "rgba(245,158,11,0.08)"
5948
+ },
5949
+ children: [
5950
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: "2px" }, children: [
5951
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { color: t2.text, fontWeight: 600, fontSize: "13px" }, children: "Our team may already have this" }),
5952
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("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." })
5953
+ ] }),
5954
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5955
+ "div",
5956
+ {
5957
+ role: "radiogroup",
5958
+ "aria-label": "Existing issues",
5959
+ style: { display: "flex", flexDirection: "column", gap: "6px" },
5960
+ children: similarIssues.map((issue) => {
5961
+ const picked = pickedSimilar === issue.number;
5962
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
5963
+ "button",
5964
+ {
5965
+ type: "button",
5966
+ role: "radio",
5967
+ "aria-checked": picked,
5968
+ disabled: isSubmitting,
5969
+ onClick: () => setPickedSimilar(issue.number),
5970
+ style: {
5971
+ display: "flex",
5972
+ alignItems: "flex-start",
5973
+ gap: "10px",
5974
+ width: "100%",
5975
+ minHeight: "44px",
5976
+ padding: "9px 10px",
5977
+ borderRadius: "8px",
5978
+ border: `1px solid ${picked ? t2.accent : t2.border}`,
5979
+ boxShadow: picked ? `0 0 0 1px ${t2.accent}` : "none",
5980
+ backgroundColor: t2.bg,
5981
+ color: t2.text,
5982
+ textAlign: "left",
5983
+ cursor: isSubmitting ? "not-allowed" : "pointer",
5984
+ fontFamily: "inherit"
5985
+ },
5986
+ children: [
5987
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5988
+ "span",
5989
+ {
5990
+ "aria-hidden": "true",
5991
+ style: {
5992
+ flexShrink: 0,
5993
+ marginTop: "2px",
5994
+ width: "14px",
5995
+ height: "14px",
5996
+ boxSizing: "border-box",
5997
+ borderRadius: "50%",
5998
+ border: `2px solid ${picked ? t2.accent : t2.textMuted}`,
5999
+ backgroundColor: picked ? t2.accent : "transparent",
6000
+ boxShadow: picked ? `inset 0 0 0 2px ${t2.bg}` : "none"
6001
+ }
6002
+ }
6003
+ ),
6004
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
6005
+ "span",
6006
+ {
6007
+ style: {
6008
+ display: "flex",
6009
+ flexDirection: "column",
6010
+ gap: "3px",
6011
+ minWidth: 0
6012
+ },
6013
+ children: [
6014
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
6015
+ "span",
6016
+ {
6017
+ title: issue.title,
6018
+ style: {
6019
+ fontSize: "12.5px",
6020
+ lineHeight: 1.45,
6021
+ wordBreak: "break-word",
6022
+ overflow: "hidden",
6023
+ display: "-webkit-box",
6024
+ WebkitLineClamp: 3,
6025
+ WebkitBoxOrient: "vertical"
6026
+ },
6027
+ children: [
6028
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { style: { color: t2.textMuted, fontWeight: 600 }, children: [
6029
+ "#",
6030
+ issue.number
6031
+ ] }),
6032
+ " ",
6033
+ issue.title
6034
+ ]
6035
+ }
6036
+ ),
6037
+ issue.status && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { fontSize: "11.5px", color: t2.textMuted }, children: issue.status })
6038
+ ]
6039
+ }
6040
+ )
6041
+ ]
6042
+ },
6043
+ issue.number
6044
+ );
6045
+ })
6046
+ }
6047
+ ),
6048
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
6049
+ "button",
6050
+ {
6051
+ type: "button",
6052
+ disabled: isSubmitting || !pickedSimilar,
6053
+ onClick: () => {
6054
+ if (!pickedSimilar) return;
6055
+ setAttachingTo(pickedSimilar);
6056
+ setDuplicateIssueNumber(pickedSimilar);
6057
+ void handleSubmit({ duplicateOf: pickedSimilar });
6058
+ },
6059
+ style: {
6060
+ width: "100%",
6061
+ minHeight: "40px",
6062
+ padding: "10px",
6063
+ borderRadius: "8px",
6064
+ border: "none",
6065
+ backgroundColor: isSubmitting || !pickedSimilar ? t2.bgSecondary : t2.accent,
6066
+ color: isSubmitting || !pickedSimilar ? t2.textMuted : t2.accentText,
6067
+ fontSize: "14px",
6068
+ fontWeight: 600,
6069
+ cursor: isSubmitting || !pickedSimilar ? "not-allowed" : "pointer",
6070
+ fontFamily: "inherit"
6071
+ },
6072
+ children: typeof attachingTo === "number" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
6073
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ButtonSpinner, {}),
6074
+ "Adding to #",
6075
+ attachingTo,
6076
+ "\u2026"
6077
+ ] }) : pickedSimilar ? `Add to #${pickedSimilar}` : "Pick the matching issue"
6078
+ }
6079
+ ),
6080
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
6081
+ "button",
6082
+ {
6083
+ type: "button",
6084
+ disabled: isSubmitting,
6085
+ onClick: () => {
6086
+ setAttachingTo("new");
6087
+ void handleSubmit({ skipSimilar: true });
6088
+ },
6089
+ style: {
6090
+ width: "100%",
6091
+ minHeight: "40px",
6092
+ marginTop: "-4px",
6093
+ padding: "8px",
6094
+ borderRadius: "8px",
6095
+ border: "none",
6096
+ backgroundColor: "transparent",
6097
+ color: isSubmitting ? t2.textMuted : t2.text,
6098
+ fontSize: "13px",
6099
+ fontWeight: 500,
6100
+ textDecoration: "underline",
6101
+ textUnderlineOffset: "3px",
6102
+ cursor: isSubmitting ? "not-allowed" : "pointer",
6103
+ fontFamily: "inherit"
6104
+ },
6105
+ children: attachingTo === "new" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
6106
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ButtonSpinner, {}),
6107
+ "Sending as new\u2026"
6108
+ ] }) : "No, mine is different \u2014 send as new"
6109
+ }
6110
+ )
6111
+ ]
6112
+ }
6113
+ )
6114
+ ) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5306
6115
  "button",
5307
6116
  {
5308
6117
  type: "button",
5309
- onClick: handleSubmit,
6118
+ onClick: () => void handleSubmit(),
5310
6119
  disabled: submitDisabled,
5311
6120
  style: {
5312
6121
  marginTop: "12px",
@@ -5322,14 +6131,17 @@ function ReportDialog({
5322
6131
  fontFamily: "inherit",
5323
6132
  transition: "background-color 0.15s ease"
5324
6133
  },
5325
- children: isSubmitting ? "Sending..." : isRating ? "Send Rating" : "Send Report"
6134
+ children: checkingSimilar ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
6135
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ButtonSpinner, {}),
6136
+ "Checking for existing reports\u2026"
6137
+ ] }) : isSubmitting ? shotPending && !isRating ? "Attaching screenshot\u2026" : "Sending..." : isRating ? "Send Rating" : "Send Report"
5326
6138
  }
5327
6139
  )
5328
6140
  ] })
5329
6141
  ] })
5330
6142
  }
5331
6143
  ),
5332
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
6144
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
5333
6145
  "div",
5334
6146
  {
5335
6147
  style: {
@@ -5341,8 +6153,35 @@ function ReportDialog({
5341
6153
  gap: "12px"
5342
6154
  },
5343
6155
  children: [
5344
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ReporterChip, { reporter, t: t2 }),
5345
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
6156
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ReporterChip, { reporter, t: t2 }),
6157
+ reportGlitchgrabProblem ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
6158
+ "button",
6159
+ {
6160
+ type: "button",
6161
+ "data-gg-self-report": "",
6162
+ onClick: () => {
6163
+ setSelfReportPrefill("");
6164
+ setSelfReportFrom("form");
6165
+ },
6166
+ title: "Something wrong with this reporter itself? Tell the Glitchgrab team.",
6167
+ style: {
6168
+ fontSize: "11px",
6169
+ color: t2.textMuted,
6170
+ whiteSpace: "nowrap",
6171
+ flexShrink: 0,
6172
+ border: "none",
6173
+ background: "transparent",
6174
+ fontFamily: "inherit",
6175
+ cursor: "pointer",
6176
+ textDecoration: "underline",
6177
+ textUnderlineOffset: "2px",
6178
+ // A ~30px tap target without growing the footer.
6179
+ padding: "9px 0",
6180
+ margin: "-9px 0"
6181
+ },
6182
+ children: "Problem with Glitchgrab?"
6183
+ }
6184
+ ) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5346
6185
  "span",
5347
6186
  {
5348
6187
  style: {
@@ -5364,7 +6203,7 @@ function ReportDialog({
5364
6203
  ),
5365
6204
  document.body
5366
6205
  ),
5367
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
6206
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5368
6207
  "input",
5369
6208
  {
5370
6209
  ref: fileInputRef,
@@ -5376,7 +6215,7 @@ function ReportDialog({
5376
6215
  }
5377
6216
  ),
5378
6217
  isOpen && previewIndex !== null && screenshots[previewIndex] && (0, import_react_dom.createPortal)(
5379
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
6218
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
5380
6219
  "div",
5381
6220
  {
5382
6221
  "data-glitchgrab-layer": "",
@@ -5398,7 +6237,7 @@ function ReportDialog({
5398
6237
  },
5399
6238
  onMouseDown: (e) => e.stopPropagation(),
5400
6239
  children: [
5401
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
6240
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
5402
6241
  "div",
5403
6242
  {
5404
6243
  ref: previewImgRef,
@@ -5410,7 +6249,7 @@ function ReportDialog({
5410
6249
  },
5411
6250
  onClick: (e) => e.stopPropagation(),
5412
6251
  children: [
5413
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
6252
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5414
6253
  "div",
5415
6254
  {
5416
6255
  ref: previewScrollRef,
@@ -5429,7 +6268,7 @@ function ReportDialog({
5429
6268
  touchAction: "none",
5430
6269
  cursor: "grab"
5431
6270
  } : { maxWidth: "100%", lineHeight: 0 },
5432
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
6271
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5433
6272
  "img",
5434
6273
  {
5435
6274
  ref: previewImgElRef,
@@ -5457,7 +6296,7 @@ function ReportDialog({
5457
6296
  )
5458
6297
  }
5459
6298
  ),
5460
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
6299
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
5461
6300
  "button",
5462
6301
  {
5463
6302
  type: "button",
@@ -5485,7 +6324,7 @@ function ReportDialog({
5485
6324
  animation: "gg-pulse 2s ease-in-out infinite"
5486
6325
  },
5487
6326
  children: [
5488
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("svg", { width: "13", height: "13", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
6327
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("svg", { width: "13", height: "13", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5489
6328
  "path",
5490
6329
  {
5491
6330
  d: "M12 20h9M16.5 3.5a2.12 2.12 0 013 3L7 19l-4 1 1-4L16.5 3.5z",
@@ -5502,7 +6341,7 @@ function ReportDialog({
5502
6341
  ]
5503
6342
  }
5504
6343
  ),
5505
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
6344
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5506
6345
  "div",
5507
6346
  {
5508
6347
  style: {
@@ -5511,7 +6350,7 @@ function ReportDialog({
5511
6350
  alignItems: "center",
5512
6351
  gap: "12px"
5513
6352
  },
5514
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
6353
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5515
6354
  "span",
5516
6355
  {
5517
6356
  style: {
@@ -5533,7 +6372,7 @@ function ReportDialog({
5533
6372
  document.body
5534
6373
  ),
5535
6374
  isOpen && annotatingIndex !== null && screenshots[annotatingIndex] && (0, import_react_dom.createPortal)(
5536
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
6375
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5537
6376
  AnnotationCanvas,
5538
6377
  {
5539
6378
  imageSrc: screenshots[annotatingIndex],
@@ -5549,21 +6388,24 @@ function ReportDialog({
5549
6388
  ),
5550
6389
  document.body
5551
6390
  ),
5552
- isOpen && sheetUp && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
6391
+ isOpen && selfReportFrom && reportGlitchgrabProblem && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
6392
+ GlitchgrabProblemPanel,
6393
+ {
6394
+ send: reportGlitchgrabProblem,
6395
+ theme: layerTheme,
6396
+ surface: selfReportFrom,
6397
+ conversationId: aiConversationId,
6398
+ reportType,
6399
+ screenshots,
6400
+ initialText: selfReportPrefill,
6401
+ onClose: () => setSelfReportFrom(null)
6402
+ }
6403
+ ),
6404
+ isOpen && sheetUp && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5553
6405
  AssistSheet,
5554
6406
  {
5555
6407
  assist,
5556
- theme: {
5557
- bg: t2.bg,
5558
- bgSecondary: t2.bgSecondary,
5559
- border: t2.border,
5560
- text: t2.text,
5561
- textMuted: t2.textMuted,
5562
- inputBg: t2.inputBg,
5563
- inputBorder: t2.inputBorder,
5564
- accent: t2.accent,
5565
- accentText: t2.accentText
5566
- },
6408
+ theme: layerTheme,
5567
6409
  screenshots,
5568
6410
  pageShot,
5569
6411
  onAddImages: addFiles,
@@ -5604,7 +6446,11 @@ function ReportDialog({
5604
6446
  severityRefused: needsSeverity,
5605
6447
  isSubmitting,
5606
6448
  submitted,
5607
- onSend: () => void handleSubmit(),
6449
+ onSend: () => void handleSubmit({ skipSimilar: true }),
6450
+ onReportGlitchgrabProblem: reportGlitchgrabProblem ? (prefill) => {
6451
+ setSelfReportPrefill(prefill != null ? prefill : "");
6452
+ setSelfReportFrom("assist");
6453
+ } : void 0,
5608
6454
  onDegrade: (message) => {
5609
6455
  setAssistOpen(false);
5610
6456
  setAssistUsed(true);
@@ -5651,7 +6497,7 @@ function getTypeIcon(type, color, size = 24) {
5651
6497
  };
5652
6498
  switch (type) {
5653
6499
  case "RATING":
5654
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("svg", { ...props, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
6500
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("svg", { ...props, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5655
6501
  "path",
5656
6502
  {
5657
6503
  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",
@@ -5661,7 +6507,7 @@ function getTypeIcon(type, color, size = 24) {
5661
6507
  }
5662
6508
  ) });
5663
6509
  case "BUG":
5664
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("svg", { ...props, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
6510
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("svg", { ...props, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5665
6511
  "path",
5666
6512
  {
5667
6513
  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",
@@ -5671,7 +6517,7 @@ function getTypeIcon(type, color, size = 24) {
5671
6517
  }
5672
6518
  ) });
5673
6519
  case "FEATURE_REQUEST":
5674
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("svg", { ...props, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
6520
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("svg", { ...props, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5675
6521
  "path",
5676
6522
  {
5677
6523
  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",
@@ -5681,8 +6527,8 @@ function getTypeIcon(type, color, size = 24) {
5681
6527
  }
5682
6528
  ) });
5683
6529
  case "UI_IMPROVEMENT":
5684
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("svg", { ...props, children: [
5685
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
6530
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { ...props, children: [
6531
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5686
6532
  "rect",
5687
6533
  {
5688
6534
  x: "3",
@@ -5694,7 +6540,7 @@ function getTypeIcon(type, color, size = 24) {
5694
6540
  strokeWidth: "1.5"
5695
6541
  }
5696
6542
  ),
5697
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
6543
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5698
6544
  "path",
5699
6545
  {
5700
6546
  d: "M3 9h18M9 21V9",
@@ -5705,7 +6551,7 @@ function getTypeIcon(type, color, size = 24) {
5705
6551
  )
5706
6552
  ] });
5707
6553
  case "PERFORMANCE":
5708
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("svg", { ...props, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
6554
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("svg", { ...props, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5709
6555
  "path",
5710
6556
  {
5711
6557
  d: "M12 21a9 9 0 100-18 9 9 0 000 18zM12 12l4-4M12 7v.01",
@@ -5716,7 +6562,7 @@ function getTypeIcon(type, color, size = 24) {
5716
6562
  }
5717
6563
  ) });
5718
6564
  case "SECURITY":
5719
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("svg", { ...props, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
6565
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("svg", { ...props, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5720
6566
  "path",
5721
6567
  {
5722
6568
  d: "M12 3l7 3v5c0 4.5-3 8.5-7 10-4-1.5-7-5.5-7-10V6l7-3z",
@@ -5726,9 +6572,9 @@ function getTypeIcon(type, color, size = 24) {
5726
6572
  }
5727
6573
  ) });
5728
6574
  case "QUESTION":
5729
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("svg", { ...props, children: [
5730
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: "12", cy: "12", r: "10", stroke: color, strokeWidth: "1.5" }),
5731
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
6575
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { ...props, children: [
6576
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: "12", cy: "12", r: "10", stroke: color, strokeWidth: "1.5" }),
6577
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5732
6578
  "path",
5733
6579
  {
5734
6580
  d: "M9.09 9a3 3 0 015.83 1c0 2-3 3-3 3M12 17h.01",
@@ -5740,7 +6586,7 @@ function getTypeIcon(type, color, size = 24) {
5740
6586
  )
5741
6587
  ] });
5742
6588
  case "OTHER":
5743
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("svg", { ...props, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
6589
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("svg", { ...props, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5744
6590
  "path",
5745
6591
  {
5746
6592
  d: "M21 15a2 2 0 01-2 2H7l-4 4V5a2 2 0 012-2h14a2 2 0 012 2v10z",
@@ -5753,8 +6599,8 @@ function getTypeIcon(type, color, size = 24) {
5753
6599
  }
5754
6600
 
5755
6601
  // src/booking-dialog.tsx
5756
- var import_react4 = require("react");
5757
- var import_jsx_runtime4 = require("react/jsx-runtime");
6602
+ var import_react5 = require("react");
6603
+ var import_jsx_runtime5 = require("react/jsx-runtime");
5758
6604
  var t = {
5759
6605
  bg: "#141414",
5760
6606
  surface: "#1c1c1c",
@@ -5791,28 +6637,28 @@ function BookingDialog({
5791
6637
  onClose
5792
6638
  }) {
5793
6639
  var _a;
5794
- const [step, setStep] = (0, import_react4.useState)("slots");
5795
- const [slots, setSlots] = (0, import_react4.useState)(null);
5796
- const [heading, setHeading] = (0, import_react4.useState)({
6640
+ const [step, setStep] = (0, import_react5.useState)("slots");
6641
+ const [slots, setSlots] = (0, import_react5.useState)(null);
6642
+ const [heading, setHeading] = (0, import_react5.useState)({
5797
6643
  title: "Book a demo",
5798
6644
  description: null
5799
6645
  });
5800
- const [whatsappUrl, setWhatsappUrl] = (0, import_react4.useState)(null);
5801
- const [selected, setSelected] = (0, import_react4.useState)(null);
5802
- const [name, setName] = (0, import_react4.useState)("");
5803
- const [email, setEmail] = (0, import_react4.useState)("");
5804
- const [phone, setPhone] = (0, import_react4.useState)("");
5805
- const [note, setNote] = (0, import_react4.useState)("");
5806
- const [code, setCode] = (0, import_react4.useState)("");
5807
- const [bookingId, setBookingId] = (0, import_react4.useState)(null);
5808
- const [meetUrl, setMeetUrl] = (0, import_react4.useState)(null);
5809
- const [busy, setBusy] = (0, import_react4.useState)(false);
5810
- const [error, setError] = (0, import_react4.useState)(null);
5811
- const headers = (0, import_react4.useMemo)(
6646
+ const [whatsappUrl, setWhatsappUrl] = (0, import_react5.useState)(null);
6647
+ const [selected, setSelected] = (0, import_react5.useState)(null);
6648
+ const [name, setName] = (0, import_react5.useState)("");
6649
+ const [email, setEmail] = (0, import_react5.useState)("");
6650
+ const [phone, setPhone] = (0, import_react5.useState)("");
6651
+ const [note, setNote] = (0, import_react5.useState)("");
6652
+ const [code, setCode] = (0, import_react5.useState)("");
6653
+ const [bookingId, setBookingId] = (0, import_react5.useState)(null);
6654
+ const [meetUrl, setMeetUrl] = (0, import_react5.useState)(null);
6655
+ const [busy, setBusy] = (0, import_react5.useState)(false);
6656
+ const [error, setError] = (0, import_react5.useState)(null);
6657
+ const headers = (0, import_react5.useMemo)(
5812
6658
  () => ({ Authorization: `Bearer ${token}`, "Content-Type": "application/json" }),
5813
6659
  [token]
5814
6660
  );
5815
- const loadSlots = (0, import_react4.useCallback)(async () => {
6661
+ const loadSlots = (0, import_react5.useCallback)(async () => {
5816
6662
  var _a2, _b, _c;
5817
6663
  setError(null);
5818
6664
  setSlots(null);
@@ -5837,7 +6683,7 @@ function BookingDialog({
5837
6683
  setSlots([]);
5838
6684
  }
5839
6685
  }, [baseUrl, headers]);
5840
- (0, import_react4.useEffect)(() => {
6686
+ (0, import_react5.useEffect)(() => {
5841
6687
  if (!open) return;
5842
6688
  setStep("slots");
5843
6689
  setSelected(null);
@@ -5846,7 +6692,7 @@ function BookingDialog({
5846
6692
  setError(null);
5847
6693
  void loadSlots();
5848
6694
  }, [open, loadSlots]);
5849
- (0, import_react4.useEffect)(() => {
6695
+ (0, import_react5.useEffect)(() => {
5850
6696
  if (!open) return;
5851
6697
  const onKey = (e) => {
5852
6698
  if (e.key === "Escape") onClose();
@@ -5916,7 +6762,7 @@ function BookingDialog({
5916
6762
  }
5917
6763
  }
5918
6764
  const canSubmitDetails = name.trim() && email.includes("@") && phone.replace(/\D/g, "").length >= 8;
5919
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
6765
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
5920
6766
  "div",
5921
6767
  {
5922
6768
  onClick: (e) => {
@@ -5933,7 +6779,7 @@ function BookingDialog({
5933
6779
  padding: 16,
5934
6780
  fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif"
5935
6781
  },
5936
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
6782
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
5937
6783
  "div",
5938
6784
  {
5939
6785
  role: "dialog",
@@ -5951,7 +6797,7 @@ function BookingDialog({
5951
6797
  overflow: "hidden"
5952
6798
  },
5953
6799
  children: [
5954
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
6800
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
5955
6801
  "div",
5956
6802
  {
5957
6803
  style: {
@@ -5963,11 +6809,11 @@ function BookingDialog({
5963
6809
  gap: 12
5964
6810
  },
5965
6811
  children: [
5966
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { children: [
5967
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { fontSize: 15, fontWeight: 600 }, children: heading.title }),
5968
- heading.description && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { fontSize: 12, color: t.muted, marginTop: 2 }, children: heading.description })
6812
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
6813
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: { fontSize: 15, fontWeight: 600 }, children: heading.title }),
6814
+ heading.description && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: { fontSize: 12, color: t.muted, marginTop: 2 }, children: heading.description })
5969
6815
  ] }),
5970
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
6816
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
5971
6817
  "button",
5972
6818
  {
5973
6819
  onClick: onClose,
@@ -5979,10 +6825,10 @@ function BookingDialog({
5979
6825
  ]
5980
6826
  }
5981
6827
  ),
5982
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { padding: 18, overflowY: "auto", flex: "1 1 auto" }, children: [
5983
- error && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { color: t.danger, fontSize: 12, marginBottom: 12, lineHeight: 1.5 }, children: error }),
5984
- step === "slots" && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
5985
- whatsappUrl && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
6828
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { padding: 18, overflowY: "auto", flex: "1 1 auto" }, children: [
6829
+ error && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: { color: t.danger, fontSize: 12, marginBottom: 12, lineHeight: 1.5 }, children: error }),
6830
+ step === "slots" && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
6831
+ whatsappUrl && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
5986
6832
  "a",
5987
6833
  {
5988
6834
  href: whatsappUrl,
@@ -6001,15 +6847,15 @@ function BookingDialog({
6001
6847
  },
6002
6848
  children: [
6003
6849
  "\u{1F4AC} Rather book on WhatsApp?",
6004
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { display: "block", fontSize: 11, color: t.muted, marginTop: 2 }, children: "We'll ask the same questions in chat." })
6850
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: { display: "block", fontSize: 11, color: t.muted, marginTop: 2 }, children: "We'll ask the same questions in chat." })
6005
6851
  ]
6006
6852
  }
6007
6853
  ),
6008
- slots === null && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { color: t.muted, fontSize: 13 }, children: "Finding times\u2026" }),
6009
- (slots == null ? void 0 : slots.length) === 0 && !error && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { color: t.muted, fontSize: 13 }, children: "No times available right now." }),
6010
- [...byDay.entries()].map(([day, daySlots]) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { marginBottom: 18 }, children: [
6011
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { fontSize: 11, color: t.muted, marginBottom: 8, textTransform: "uppercase", letterSpacing: ".6px" }, children: formatDay(daySlots[0].startsAt) }),
6012
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { display: "flex", flexWrap: "wrap", gap: 8 }, children: daySlots.map((slot) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
6854
+ slots === null && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: { color: t.muted, fontSize: 13 }, children: "Finding times\u2026" }),
6855
+ (slots == null ? void 0 : slots.length) === 0 && !error && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: { color: t.muted, fontSize: 13 }, children: "No times available right now." }),
6856
+ [...byDay.entries()].map(([day, daySlots]) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { marginBottom: 18 }, children: [
6857
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: { fontSize: 11, color: t.muted, marginBottom: 8, textTransform: "uppercase", letterSpacing: ".6px" }, children: formatDay(daySlots[0].startsAt) }),
6858
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: { display: "flex", flexWrap: "wrap", gap: 8 }, children: daySlots.map((slot) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
6013
6859
  "button",
6014
6860
  {
6015
6861
  onClick: () => {
@@ -6031,13 +6877,13 @@ function BookingDialog({
6031
6877
  )) })
6032
6878
  ] }, day))
6033
6879
  ] }),
6034
- step === "details" && selected && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 10 }, children: [
6035
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { fontSize: 13, color: t.muted }, children: [
6880
+ step === "details" && selected && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 10 }, children: [
6881
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { fontSize: 13, color: t.muted }, children: [
6036
6882
  formatDay(selected.startsAt),
6037
6883
  " \xB7 ",
6038
6884
  formatTime(selected.startsAt),
6039
6885
  " \xB7 ",
6040
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
6886
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
6041
6887
  "button",
6042
6888
  {
6043
6889
  onClick: () => setStep("slots"),
@@ -6046,9 +6892,9 @@ function BookingDialog({
6046
6892
  }
6047
6893
  )
6048
6894
  ] }),
6049
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Field, { label: "Your name", value: name, onChange: setName }),
6050
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Field, { label: "Email", value: email, onChange: setEmail, type: "email" }),
6051
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
6895
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Field, { label: "Your name", value: name, onChange: setName }),
6896
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Field, { label: "Email", value: email, onChange: setEmail, type: "email" }),
6897
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
6052
6898
  Field,
6053
6899
  {
6054
6900
  label: "Mobile number",
@@ -6058,8 +6904,8 @@ function BookingDialog({
6058
6904
  hint: "We send a code here to confirm, then a reminder before the call."
6059
6905
  }
6060
6906
  ),
6061
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Field, { label: "Anything we should know? (optional)", value: note, onChange: setNote }),
6062
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
6907
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Field, { label: "Anything we should know? (optional)", value: note, onChange: setNote }),
6908
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
6063
6909
  "button",
6064
6910
  {
6065
6911
  onClick: startBooking,
@@ -6069,10 +6915,10 @@ function BookingDialog({
6069
6915
  }
6070
6916
  )
6071
6917
  ] }),
6072
- step === "code" && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 10 }, children: [
6073
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("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." }),
6074
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Field, { label: "Code", value: code, onChange: setCode }),
6075
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
6918
+ step === "code" && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 10 }, children: [
6919
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("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." }),
6920
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Field, { label: "Code", value: code, onChange: setCode }),
6921
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
6076
6922
  "button",
6077
6923
  {
6078
6924
  onClick: confirmBooking,
@@ -6082,15 +6928,15 @@ function BookingDialog({
6082
6928
  }
6083
6929
  )
6084
6930
  ] }),
6085
- step === "done" && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 10 }, children: [
6086
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { fontSize: 15, fontWeight: 600 }, children: "You're booked." }),
6087
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { fontSize: 13, color: t.muted, lineHeight: 1.6 }, children: [
6931
+ step === "done" && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 10 }, children: [
6932
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: { fontSize: 15, fontWeight: 600 }, children: "You're booked." }),
6933
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { fontSize: 13, color: t.muted, lineHeight: 1.6 }, children: [
6088
6934
  selected && `${formatDay(selected.startsAt)} at ${formatTime(selected.startsAt)}. `,
6089
6935
  "A calendar invite is on its way to ",
6090
6936
  email,
6091
6937
  ", and we'll send a WhatsApp reminder before the call."
6092
6938
  ] }),
6093
- meetUrl && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("a", { href: meetUrl, target: "_blank", rel: "noreferrer", style: { ...primaryStyle(false), textAlign: "center", textDecoration: "none" }, children: "Open the meeting link" })
6939
+ meetUrl && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("a", { href: meetUrl, target: "_blank", rel: "noreferrer", style: { ...primaryStyle(false), textAlign: "center", textDecoration: "none" }, children: "Open the meeting link" })
6094
6940
  ] })
6095
6941
  ] })
6096
6942
  ]
@@ -6119,9 +6965,9 @@ function Field({
6119
6965
  type = "text",
6120
6966
  hint
6121
6967
  }) {
6122
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { style: { display: "block", fontSize: 11, color: t.muted }, children: [
6968
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { style: { display: "block", fontSize: 11, color: t.muted }, children: [
6123
6969
  label,
6124
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
6970
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
6125
6971
  "input",
6126
6972
  {
6127
6973
  type,
@@ -6142,7 +6988,7 @@ function Field({
6142
6988
  }
6143
6989
  }
6144
6990
  ),
6145
- hint && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { marginTop: 4, fontSize: 10, color: t.muted }, children: hint })
6991
+ hint && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: { marginTop: 4, fontSize: 10, color: t.muted }, children: hint })
6146
6992
  ] });
6147
6993
  }
6148
6994
 
@@ -6283,11 +7129,11 @@ function matchesShortcut(e) {
6283
7129
  }
6284
7130
 
6285
7131
  // src/provider.tsx
6286
- var import_jsx_runtime5 = require("react/jsx-runtime");
7132
+ var import_jsx_runtime6 = require("react/jsx-runtime");
6287
7133
  var DEFAULT_BASE_URL2 = "https://glitchgrab.dev";
6288
- var GlitchgrabContext = (0, import_react5.createContext)(null);
7134
+ var GlitchgrabContext = (0, import_react6.createContext)(null);
6289
7135
  function useGlitchgrab() {
6290
- const ctx = (0, import_react5.useContext)(GlitchgrabContext);
7136
+ const ctx = (0, import_react6.useContext)(GlitchgrabContext);
6291
7137
  if (!ctx) {
6292
7138
  throw new Error("useGlitchgrab must be used within a GlitchgrabProvider");
6293
7139
  }
@@ -6295,7 +7141,7 @@ function useGlitchgrab() {
6295
7141
  }
6296
7142
  function GlitchgrabProviderInner({
6297
7143
  token,
6298
- session,
7144
+ session: sessionProp,
6299
7145
  baseUrl,
6300
7146
  onError,
6301
7147
  onReportSent,
@@ -6310,7 +7156,16 @@ function GlitchgrabProviderInner({
6310
7156
  context: appContext2,
6311
7157
  responseBodyOrigins: responseBodyOrigins2
6312
7158
  }) {
6313
- const visitedPagesRef = (0, import_react5.useRef)([]);
7159
+ const visitedPagesRef = (0, import_react6.useRef)([]);
7160
+ const sessionKey = sessionProp ? [
7161
+ sessionProp.userId,
7162
+ sessionProp.name,
7163
+ sessionProp.email,
7164
+ sessionProp.phone,
7165
+ sessionProp.avatarUrl,
7166
+ sessionProp.image
7167
+ ].map((v) => v == null ? "" : String(v)).join("\0") : "";
7168
+ const session = (0, import_react6.useMemo)(() => sessionProp, [sessionKey]);
6314
7169
  setRelease(release2);
6315
7170
  if (appContext2) setContexts(appContext2);
6316
7171
  registerCaptureConfig({
@@ -6322,12 +7177,12 @@ function GlitchgrabProviderInner({
6322
7177
  onError,
6323
7178
  onReportSent
6324
7179
  });
6325
- (0, import_react5.useEffect)(() => {
7180
+ (0, import_react6.useEffect)(() => {
6326
7181
  if (enableBreadcrumbs) {
6327
7182
  initBreadcrumbs(maxBreadcrumbs2, { responseBodyOrigins: responseBodyOrigins2 });
6328
7183
  }
6329
7184
  }, [enableBreadcrumbs, maxBreadcrumbs2, responseBodyOrigins2 == null ? void 0 : responseBodyOrigins2.join(",")]);
6330
- (0, import_react5.useEffect)(() => {
7185
+ (0, import_react6.useEffect)(() => {
6331
7186
  try {
6332
7187
  if (typeof window === "undefined") return;
6333
7188
  const trackPage = () => {
@@ -6364,7 +7219,7 @@ function GlitchgrabProviderInner({
6364
7219
  } catch (e) {
6365
7220
  }
6366
7221
  }, []);
6367
- (0, import_react5.useEffect)(() => {
7222
+ (0, import_react6.useEffect)(() => {
6368
7223
  try {
6369
7224
  if (typeof window === "undefined") return;
6370
7225
  if (process.env.NODE_ENV === "development") return;
@@ -6491,7 +7346,7 @@ function GlitchgrabProviderInner({
6491
7346
  } catch (e) {
6492
7347
  }
6493
7348
  }, [token, baseUrl, onError, onReportSent, session, ignoreErrors]);
6494
- const report = (0, import_react5.useCallback)(
7349
+ const report = (0, import_react6.useCallback)(
6495
7350
  async (type, description, metadata) => {
6496
7351
  var _a;
6497
7352
  try {
@@ -6525,11 +7380,45 @@ function GlitchgrabProviderInner({
6525
7380
  },
6526
7381
  [token, baseUrl, onReportSent, session]
6527
7382
  );
6528
- const reportBug = (0, import_react5.useCallback)(
7383
+ const reportBug = (0, import_react6.useCallback)(
6529
7384
  (description, metadata) => report("BUG", description, metadata),
6530
7385
  [report]
6531
7386
  );
6532
- const feedback = (0, import_react5.useCallback)(
7387
+ const reportGlitchgrabProblem = (0, import_react6.useCallback)(
7388
+ async (params) => {
7389
+ var _a, _b;
7390
+ try {
7391
+ const context = captureContext(visitedPagesRef.current);
7392
+ const body = JSON.stringify({
7393
+ ...params,
7394
+ pageUrl: context.url,
7395
+ userAgent: context.userAgent,
7396
+ deviceInfo: (_a = context.deviceInfo) != null ? _a : void 0,
7397
+ visitedPages: context.visitedPages,
7398
+ breadcrumbs: context.breadcrumbs,
7399
+ ...(session == null ? void 0 : session.userId) ? { reporterId: session.userId } : {},
7400
+ ...(session == null ? void 0 : session.name) ? { reporterName: String(session.name) } : {},
7401
+ ...(session == null ? void 0 : session.email) ? { reporterEmail: String(session.email) } : {}
7402
+ });
7403
+ const res = await fetch(`${baseUrl}/api/v1/sdk/glitchgrab-report`, {
7404
+ method: "POST",
7405
+ headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` },
7406
+ body,
7407
+ // keepalive caps the body at 64KB — screenshots blow past it.
7408
+ ...body.length < 6e4 ? { keepalive: true } : {}
7409
+ });
7410
+ const json = await res.json().catch(() => null);
7411
+ if (!res.ok || !(json == null ? void 0 : json.success)) {
7412
+ return { success: false, message: (_b = json == null ? void 0 : json.error) != null ? _b : "Couldn't send it" };
7413
+ }
7414
+ return { success: true, ...json.data };
7415
+ } catch (e) {
7416
+ return null;
7417
+ }
7418
+ },
7419
+ [token, baseUrl, session]
7420
+ );
7421
+ const feedback = (0, import_react6.useCallback)(
6533
7422
  async (rating, message, metadata) => {
6534
7423
  try {
6535
7424
  const context = captureContext(visitedPagesRef.current);
@@ -6557,32 +7446,38 @@ function GlitchgrabProviderInner({
6557
7446
  },
6558
7447
  [token, baseUrl, session]
6559
7448
  );
6560
- const openFeedbackDialog = (0, import_react5.useCallback)(() => {
7449
+ const openFeedbackDialog = (0, import_react6.useCallback)(() => {
6561
7450
  if (typeof window !== "undefined") {
6562
7451
  window.dispatchEvent(
6563
7452
  new CustomEvent("glitchgrab:open-report", { detail: { type: "RATING" } })
6564
7453
  );
6565
7454
  }
6566
7455
  }, []);
6567
- const captureError2 = (0, import_react5.useCallback)(
7456
+ const captureError2 = (0, import_react6.useCallback)(
6568
7457
  (error, options) => {
6569
7458
  captureError(error, options);
6570
7459
  },
6571
7460
  []
6572
7461
  );
6573
- const setContext2 = (0, import_react5.useCallback)((key, value) => {
7462
+ const setContext2 = (0, import_react6.useCallback)((key, value) => {
6574
7463
  setContext(key, value);
6575
7464
  }, []);
6576
- const setContexts2 = (0, import_react5.useCallback)((values) => {
7465
+ const setContexts2 = (0, import_react6.useCallback)((values) => {
6577
7466
  setContexts(values);
6578
7467
  }, []);
6579
- const addBreadcrumb2 = (0, import_react5.useCallback)(
7468
+ const addBreadcrumb2 = (0, import_react6.useCallback)(
6580
7469
  (message, data) => {
6581
7470
  addBreadcrumb("custom", message, data);
6582
7471
  },
6583
7472
  []
6584
7473
  );
6585
- const enhance = (0, import_react5.useCallback)(
7474
+ const signedUserId = session == null ? void 0 : session.userId;
7475
+ const signature = typeof (session == null ? void 0 : session.signature) === "string" ? session.signature : null;
7476
+ const identity = (0, import_react6.useMemo)(
7477
+ () => ({ userId: signedUserId != null ? signedUserId : null, signature }),
7478
+ [signedUserId, signature]
7479
+ );
7480
+ const enhance = (0, import_react6.useCallback)(
6586
7481
  async (text, screenshot) => {
6587
7482
  try {
6588
7483
  const ctx = captureContext(visitedPagesRef.current);
@@ -6591,14 +7486,14 @@ function GlitchgrabProviderInner({
6591
7486
  visitedPages: ctx.visitedPages.slice(-5),
6592
7487
  breadcrumbs: ctx.breadcrumbs.slice(-10).map((b) => ({ type: b.type, message: b.message }))
6593
7488
  };
6594
- return await enhanceText(text, token, baseUrl, screenshot, context);
7489
+ return await enhanceText(text, token, baseUrl, screenshot, context, identity);
6595
7490
  } catch (e) {
6596
7491
  return text;
6597
7492
  }
6598
7493
  },
6599
- [token, baseUrl]
7494
+ [token, baseUrl, identity]
6600
7495
  );
6601
- const assist = (0, import_react5.useCallback)(
7496
+ const assist = (0, import_react6.useCallback)(
6602
7497
  async (params) => {
6603
7498
  var _a;
6604
7499
  const ctx = captureContext(visitedPagesRef.current);
@@ -6609,7 +7504,8 @@ function GlitchgrabProviderInner({
6609
7504
  reporter: session ? {
6610
7505
  key: session.userId,
6611
7506
  name: session.name ? String(session.name) : null,
6612
- email: session.email ? String(session.email) : null
7507
+ email: session.email ? String(session.email) : null,
7508
+ ...signature ? { signature } : {}
6613
7509
  } : void 0,
6614
7510
  context: {
6615
7511
  ...(_a = params.context) != null ? _a : {},
@@ -6624,22 +7520,26 @@ function GlitchgrabProviderInner({
6624
7520
  },
6625
7521
  [token, baseUrl, session]
6626
7522
  );
6627
- const transcribe = (0, import_react5.useCallback)(
7523
+ const findSimilar = (0, import_react6.useCallback)(
7524
+ (description) => findSimilarIssues(description, token, baseUrl, identity),
7525
+ [token, baseUrl, identity]
7526
+ );
7527
+ const transcribe = (0, import_react6.useCallback)(
6628
7528
  async (blob) => {
6629
7529
  try {
6630
- return await transcribeAudio(blob, token, baseUrl);
7530
+ return await transcribeAudio(blob, token, baseUrl, identity);
6631
7531
  } catch (e) {
6632
7532
  return "";
6633
7533
  }
6634
7534
  },
6635
- [token, baseUrl]
7535
+ [token, baseUrl, identity]
6636
7536
  );
6637
- const openReportDialog = (0, import_react5.useCallback)((options) => {
7537
+ const openReportDialog = (0, import_react6.useCallback)((options) => {
6638
7538
  if (typeof window !== "undefined") {
6639
7539
  window.dispatchEvent(new CustomEvent("glitchgrab:open-report", { detail: options }));
6640
7540
  }
6641
7541
  }, []);
6642
- (0, import_react5.useEffect)(() => {
7542
+ (0, import_react6.useEffect)(() => {
6643
7543
  try {
6644
7544
  if (typeof document === "undefined") return;
6645
7545
  document.documentElement.setAttribute("data-glitchgrab-sdk", "1");
@@ -6647,11 +7547,14 @@ function GlitchgrabProviderInner({
6647
7547
  } catch (e) {
6648
7548
  }
6649
7549
  }, []);
6650
- const [projectName, setProjectName] = (0, import_react5.useState)(null);
6651
- const [aiAssist, setAiAssist] = (0, import_react5.useState)(false);
6652
- const [bookingOpen, setBookingOpen] = (0, import_react5.useState)(false);
6653
- const openBookingDialog = (0, import_react5.useCallback)(() => setBookingOpen(true), []);
6654
- (0, import_react5.useEffect)(() => {
7550
+ const [projectName, setProjectName] = (0, import_react6.useState)(null);
7551
+ const [aiAssist, setAiAssist] = (0, import_react6.useState)(false);
7552
+ const [glitchgrabReport, setGlitchgrabReport] = (0, import_react6.useState)(false);
7553
+ const [signedUsers, setSignedUsers] = (0, import_react6.useState)("OFF");
7554
+ const aiBlockedUnsigned = signedUsers === "ENFORCE" && !signature;
7555
+ const [bookingOpen, setBookingOpen] = (0, import_react6.useState)(false);
7556
+ const openBookingDialog = (0, import_react6.useCallback)(() => setBookingOpen(true), []);
7557
+ (0, import_react6.useEffect)(() => {
6655
7558
  let cancelled = false;
6656
7559
  fetch(`${baseUrl}/api/v1/sdk/project`, {
6657
7560
  headers: { Authorization: `Bearer ${token}` }
@@ -6660,13 +7563,27 @@ function GlitchgrabProviderInner({
6660
7563
  if (cancelled || !(json == null ? void 0 : json.success) || !((_a = json.data) == null ? void 0 : _a.name)) return;
6661
7564
  setProjectName(json.data.name);
6662
7565
  setAiAssist(json.data.aiAssist === true);
7566
+ setGlitchgrabReport(json.data.glitchgrabReport === true);
7567
+ const mode = json.data.signedUsers;
7568
+ setSignedUsers(mode === "WARN" || mode === "ENFORCE" ? mode : "OFF");
6663
7569
  }).catch(() => {
6664
7570
  });
6665
7571
  return () => {
6666
7572
  cancelled = true;
6667
7573
  };
6668
7574
  }, [baseUrl, token]);
6669
- (0, import_react5.useEffect)(() => {
7575
+ (0, import_react6.useEffect)(() => {
7576
+ var _a;
7577
+ try {
7578
+ if (signedUsers === "OFF" || signature || !session) return;
7579
+ if (typeof process === "undefined" || ((_a = process.env) == null ? void 0 : _a.NODE_ENV) !== "development") return;
7580
+ console.warn(
7581
+ 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.'
7582
+ );
7583
+ } catch (e) {
7584
+ }
7585
+ }, [signedUsers, signature, session]);
7586
+ (0, import_react6.useEffect)(() => {
6670
7587
  try {
6671
7588
  if (typeof window === "undefined") return;
6672
7589
  const handleKeyDown = (e) => {
@@ -6680,7 +7597,7 @@ function GlitchgrabProviderInner({
6680
7597
  } catch (e) {
6681
7598
  }
6682
7599
  }, [openReportDialog]);
6683
- const reporter = (0, import_react5.useMemo)(() => {
7600
+ const reporter = (0, import_react6.useMemo)(() => {
6684
7601
  var _a;
6685
7602
  if (!session) return null;
6686
7603
  const avatar = (_a = session.avatarUrl) != null ? _a : session.image;
@@ -6690,11 +7607,11 @@ function GlitchgrabProviderInner({
6690
7607
  avatarUrl: typeof avatar === "string" ? avatar : null
6691
7608
  };
6692
7609
  }, [session]);
6693
- const [shortcutLabel, setShortcutLabel] = (0, import_react5.useState)(GLITCHGRAB_SHORTCUT2);
6694
- (0, import_react5.useEffect)(() => {
7610
+ const [shortcutLabel, setShortcutLabel] = (0, import_react6.useState)(GLITCHGRAB_SHORTCUT2);
7611
+ (0, import_react6.useEffect)(() => {
6695
7612
  setShortcutLabel(getShortcutLabel2());
6696
7613
  }, []);
6697
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
7614
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
6698
7615
  GlitchgrabContext.Provider,
6699
7616
  {
6700
7617
  value: {
@@ -6714,7 +7631,7 @@ function GlitchgrabProviderInner({
6714
7631
  shortcutLabel
6715
7632
  },
6716
7633
  children: [
6717
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
7634
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
6718
7635
  GlitchgrabErrorBoundary,
6719
7636
  {
6720
7637
  token,
@@ -6726,7 +7643,7 @@ function GlitchgrabProviderInner({
6726
7643
  children
6727
7644
  }
6728
7645
  ),
6729
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
7646
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
6730
7647
  BookingDialog,
6731
7648
  {
6732
7649
  token,
@@ -6735,20 +7652,22 @@ function GlitchgrabProviderInner({
6735
7652
  onClose: () => setBookingOpen(false)
6736
7653
  }
6737
7654
  ),
6738
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
7655
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
6739
7656
  ReportDialog,
6740
7657
  {
6741
7658
  report,
6742
7659
  sendFeedback: feedback,
6743
- enhanceText: enhance,
6744
- assist: aiAssist ? assist : void 0,
6745
- transcribeAudio: transcribe,
7660
+ enhanceText: aiAssist && !aiBlockedUnsigned ? enhance : void 0,
7661
+ assist: aiAssist && !aiBlockedUnsigned ? assist : void 0,
7662
+ findSimilarIssues: aiAssist && !aiBlockedUnsigned ? findSimilar : void 0,
7663
+ reportGlitchgrabProblem: glitchgrabReport ? reportGlitchgrabProblem : void 0,
7664
+ transcribeAudio: aiBlockedUnsigned ? void 0 : transcribe,
6746
7665
  types,
6747
7666
  showSeverity,
6748
7667
  reporter,
6749
- headerSlot: projectName ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { fontSize: 11, color: "#9aa0a6" }, children: [
7668
+ headerSlot: projectName ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { fontSize: 11, color: "#9aa0a6" }, children: [
6750
7669
  "Reporting to ",
6751
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("strong", { style: { color: "#e5e5e5" }, children: projectName })
7670
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("strong", { style: { color: "#e5e5e5" }, children: projectName })
6752
7671
  ] }) : null
6753
7672
  }
6754
7673
  )
@@ -6757,21 +7676,21 @@ function GlitchgrabProviderInner({
6757
7676
  );
6758
7677
  }
6759
7678
  function GlitchgrabProvider(props) {
6760
- if (!props.token) return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_jsx_runtime5.Fragment, { children: props.children });
7679
+ if (!props.token) return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_jsx_runtime6.Fragment, { children: props.children });
6761
7680
  const resolvedProps = {
6762
7681
  ...props,
6763
7682
  baseUrl: props.baseUrl || DEFAULT_BASE_URL2
6764
7683
  };
6765
7684
  try {
6766
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(GlitchgrabProviderInner, { ...resolvedProps });
7685
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(GlitchgrabProviderInner, { ...resolvedProps });
6767
7686
  } catch (e) {
6768
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_jsx_runtime5.Fragment, { children: props.children });
7687
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_jsx_runtime6.Fragment, { children: props.children });
6769
7688
  }
6770
7689
  }
6771
7690
 
6772
7691
  // src/report-button.tsx
6773
- var import_react6 = require("react");
6774
- var import_jsx_runtime6 = require("react/jsx-runtime");
7692
+ var import_react7 = require("react");
7693
+ var import_jsx_runtime7 = require("react/jsx-runtime");
6775
7694
  function useGlitchgrabSafe() {
6776
7695
  try {
6777
7696
  return useGlitchgrab();
@@ -6786,7 +7705,7 @@ function ReportButton({
6786
7705
  children
6787
7706
  }) {
6788
7707
  var _a, _b;
6789
- const [capturing, setCapturing] = (0, import_react6.useState)(false);
7708
+ const [capturing, setCapturing] = (0, import_react7.useState)(false);
6790
7709
  const glitchgrab = useGlitchgrabSafe();
6791
7710
  if (!glitchgrab) return null;
6792
7711
  const handleClick = async () => {
@@ -6795,7 +7714,7 @@ function ReportButton({
6795
7714
  setCapturing(false);
6796
7715
  };
6797
7716
  if (children) {
6798
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_jsx_runtime6.Fragment, { children: children({ onClick: handleClick, capturing }) });
7717
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_jsx_runtime7.Fragment, { children: children({ onClick: handleClick, capturing }) });
6799
7718
  }
6800
7719
  const isTop = (_a = position == null ? void 0 : position.startsWith("top")) != null ? _a : false;
6801
7720
  const isLeft = (_b = position == null ? void 0 : position.endsWith("left")) != null ? _b : false;
@@ -6803,7 +7722,7 @@ function ReportButton({
6803
7722
  ...isLeft ? { left: "16px" } : { right: "16px" },
6804
7723
  ...isTop ? { top: "16px" } : { bottom: "16px" }
6805
7724
  };
6806
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
7725
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
6807
7726
  "button",
6808
7727
  {
6809
7728
  type: "button",
@@ -6838,7 +7757,7 @@ function ReportButton({
6838
7757
  },
6839
7758
  "aria-label": label,
6840
7759
  children: [
6841
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
7760
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
6842
7761
  "svg",
6843
7762
  {
6844
7763
  width: "16",
@@ -6846,7 +7765,7 @@ function ReportButton({
6846
7765
  viewBox: "0 0 16 16",
6847
7766
  fill: "none",
6848
7767
  style: { flexShrink: 0 },
6849
- children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
7768
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
6850
7769
  "path",
6851
7770
  {
6852
7771
  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",
@@ -6862,15 +7781,15 @@ function ReportButton({
6862
7781
  }
6863
7782
 
6864
7783
  // src/book-demo-button.tsx
6865
- var import_jsx_runtime7 = require("react/jsx-runtime");
7784
+ var import_jsx_runtime8 = require("react/jsx-runtime");
6866
7785
  function BookDemoButton({
6867
7786
  label = "Book a demo",
6868
7787
  className,
6869
7788
  children
6870
7789
  }) {
6871
7790
  const { openBookingDialog } = useGlitchgrab();
6872
- if (children) return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_jsx_runtime7.Fragment, { children: children({ onClick: openBookingDialog }) });
6873
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
7791
+ if (children) return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_jsx_runtime8.Fragment, { children: children({ onClick: openBookingDialog }) });
7792
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
6874
7793
  "button",
6875
7794
  {
6876
7795
  type: "button",
@@ -6892,7 +7811,7 @@ function BookDemoButton({
6892
7811
  }
6893
7812
 
6894
7813
  // src/feedback-button.tsx
6895
- var import_jsx_runtime8 = require("react/jsx-runtime");
7814
+ var import_jsx_runtime9 = require("react/jsx-runtime");
6896
7815
  function useGlitchgrabSafe2() {
6897
7816
  try {
6898
7817
  return useGlitchgrab();
@@ -6911,7 +7830,7 @@ function FeedbackButton({
6911
7830
  if (!glitchgrab) return null;
6912
7831
  const handleClick = () => glitchgrab.openFeedbackDialog();
6913
7832
  if (children) {
6914
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_jsx_runtime8.Fragment, { children: children({ onClick: handleClick }) });
7833
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_jsx_runtime9.Fragment, { children: children({ onClick: handleClick }) });
6915
7834
  }
6916
7835
  const isTop = (_a = position == null ? void 0 : position.startsWith("top")) != null ? _a : false;
6917
7836
  const isLeft = (_b = position == null ? void 0 : position.endsWith("left")) != null ? _b : false;
@@ -6919,7 +7838,7 @@ function FeedbackButton({
6919
7838
  ...isLeft ? { left: "16px" } : { right: "16px" },
6920
7839
  ...isTop ? { top: "16px" } : { bottom: "16px" }
6921
7840
  };
6922
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
7841
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
6923
7842
  "button",
6924
7843
  {
6925
7844
  type: "button",
@@ -6952,7 +7871,7 @@ function FeedbackButton({
6952
7871
  },
6953
7872
  "aria-label": label,
6954
7873
  children: [
6955
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
7874
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
6956
7875
  "svg",
6957
7876
  {
6958
7877
  width: "16",
@@ -6961,7 +7880,7 @@ function FeedbackButton({
6961
7880
  fill: "none",
6962
7881
  style: { flexShrink: 0 },
6963
7882
  "aria-hidden": "true",
6964
- children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
7883
+ children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
6965
7884
  "path",
6966
7885
  {
6967
7886
  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",
@@ -6977,18 +7896,18 @@ function FeedbackButton({
6977
7896
  }
6978
7897
 
6979
7898
  // src/use-reports.ts
6980
- var import_react7 = require("react");
7899
+ var import_react8 = require("react");
6981
7900
  var BASE_URL = "https://glitchgrab.dev";
6982
7901
  function useGlitchgrabReports({
6983
7902
  token,
6984
7903
  userId,
6985
7904
  limit = 100
6986
7905
  }) {
6987
- const [reports, setReports] = (0, import_react7.useState)([]);
6988
- const [isLoading, setIsLoading] = (0, import_react7.useState)(true);
6989
- const [isFetching, setIsFetching] = (0, import_react7.useState)(false);
6990
- const [error, setError] = (0, import_react7.useState)(null);
6991
- const fetchReports = (0, import_react7.useCallback)(async () => {
7906
+ const [reports, setReports] = (0, import_react8.useState)([]);
7907
+ const [isLoading, setIsLoading] = (0, import_react8.useState)(true);
7908
+ const [isFetching, setIsFetching] = (0, import_react8.useState)(false);
7909
+ const [error, setError] = (0, import_react8.useState)(null);
7910
+ const fetchReports = (0, import_react8.useCallback)(async () => {
6992
7911
  var _a, _b;
6993
7912
  if (!token || !userId) {
6994
7913
  setIsLoading(false);
@@ -7014,7 +7933,7 @@ function useGlitchgrabReports({
7014
7933
  setIsFetching(false);
7015
7934
  }
7016
7935
  }, [token, userId, limit]);
7017
- (0, import_react7.useEffect)(() => {
7936
+ (0, import_react8.useEffect)(() => {
7018
7937
  fetchReports();
7019
7938
  }, [fetchReports]);
7020
7939
  return { reports, isLoading, isFetching, error, refetch: fetchReports };
@@ -7051,9 +7970,9 @@ function useGlitchgrabActions({
7051
7970
  onSuccess,
7052
7971
  onError
7053
7972
  }) {
7054
- const [isPending, setIsPending] = (0, import_react7.useState)(false);
7055
- const [error, setError] = (0, import_react7.useState)(null);
7056
- const run = (0, import_react7.useCallback)(
7973
+ const [isPending, setIsPending] = (0, import_react8.useState)(false);
7974
+ const [error, setError] = (0, import_react8.useState)(null);
7975
+ const run = (0, import_react8.useCallback)(
7057
7976
  async (reportId, body) => {
7058
7977
  setIsPending(true);
7059
7978
  setError(null);
@@ -7083,7 +8002,7 @@ function useGlitchgrabActions({
7083
8002
  }
7084
8003
 
7085
8004
  // src/use-feedback.ts
7086
- var import_react8 = require("react");
8005
+ var import_react9 = require("react");
7087
8006
  var DEFAULT_BASE_URL3 = "https://glitchgrab.dev";
7088
8007
  function buildUrl({
7089
8008
  approvedOnly,
@@ -7109,11 +8028,11 @@ async function fetchGlitchgrabFeedback(query) {
7109
8028
  }
7110
8029
  function useGlitchgrabFeedback(query) {
7111
8030
  const { token, approvedOnly, userId, minRating, limit, baseUrl } = query;
7112
- const [feedback, setFeedback] = (0, import_react8.useState)([]);
7113
- const [isLoading, setIsLoading] = (0, import_react8.useState)(true);
7114
- const [isFetching, setIsFetching] = (0, import_react8.useState)(false);
7115
- const [error, setError] = (0, import_react8.useState)(null);
7116
- const load = (0, import_react8.useCallback)(async () => {
8031
+ const [feedback, setFeedback] = (0, import_react9.useState)([]);
8032
+ const [isLoading, setIsLoading] = (0, import_react9.useState)(true);
8033
+ const [isFetching, setIsFetching] = (0, import_react9.useState)(false);
8034
+ const [error, setError] = (0, import_react9.useState)(null);
8035
+ const load = (0, import_react9.useCallback)(async () => {
7117
8036
  if (!token) {
7118
8037
  setIsLoading(false);
7119
8038
  return;
@@ -7137,7 +8056,7 @@ function useGlitchgrabFeedback(query) {
7137
8056
  setIsFetching(false);
7138
8057
  }
7139
8058
  }, [token, approvedOnly, userId, minRating, limit, baseUrl]);
7140
- (0, import_react8.useEffect)(() => {
8059
+ (0, import_react9.useEffect)(() => {
7141
8060
  load();
7142
8061
  }, [load]);
7143
8062
  return { feedback, isLoading, isFetching, error, refetch: load };