glitchgrab 1.41.0 → 1.43.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}"`);
@@ -647,7 +722,7 @@ async function enhanceText(text, token, baseUrl, screenshot, context) {
647
722
  }
648
723
  }
649
724
  async function assistTurn(params, token, baseUrl) {
650
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
725
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n;
651
726
  const offline = {
652
727
  conversationId: null,
653
728
  question: null,
@@ -680,6 +755,7 @@ async function assistTurn(params, token, baseUrl) {
680
755
  report: (_i = (_h = envelope.data) == null ? void 0 : _h.report) != null ? _i : null,
681
756
  duplicate: (_k = (_j = envelope.data) == null ? void 0 : _j.duplicate) != null ? _k : null,
682
757
  solved: (_m = (_l = envelope.data) == null ? void 0 : _l.solved) != null ? _m : null,
758
+ aboutGlitchgrab: ((_n = envelope.data) == null ? void 0 : _n.aboutGlitchgrab) === true,
683
759
  degraded: null
684
760
  };
685
761
  } catch (e) {
@@ -900,10 +976,14 @@ var import_jsx_runtime = require("react/jsx-runtime");
900
976
  var import_react3 = require("react");
901
977
  var import_react_dom2 = require("react-dom");
902
978
  var import_jsx_runtime2 = require("react/jsx-runtime");
979
+ var import_react4 = require("react");
980
+ var import_react_dom3 = require("react-dom");
903
981
  var import_jsx_runtime3 = require("react/jsx-runtime");
982
+ var import_jsx_runtime4 = require("react/jsx-runtime");
904
983
  var MAX_SCREENSHOT_DATA_URL_LENGTH = 18e5;
905
984
  var QUALITY_LADDER = [0.92, 0.85, 0.75];
906
985
  var DOWNSCALE_FACTOR = 0.7;
986
+ var MAX_ROUNDS = 4;
907
987
  function downscale(canvas, factor) {
908
988
  const next = document.createElement("canvas");
909
989
  next.width = Math.max(1, Math.round(canvas.width * factor));
@@ -917,18 +997,55 @@ function downscale(canvas, factor) {
917
997
  }
918
998
  function encodeScreenshot(canvas, maxLength = MAX_SCREENSHOT_DATA_URL_LENGTH) {
919
999
  let current = canvas;
920
- for (let round = 0; round < 2; round++) {
921
- let encoded = "";
1000
+ let encoded = "";
1001
+ for (let round = 0; round < MAX_ROUNDS; round++) {
922
1002
  for (const quality of QUALITY_LADDER) {
923
1003
  encoded = current.toDataURL("image/jpeg", quality);
924
1004
  if (encoded.length <= maxLength) return encoded;
925
1005
  }
926
- if (round === 1) return encoded;
1006
+ if (round === MAX_ROUNDS - 1) break;
927
1007
  const smaller = downscale(current, DOWNSCALE_FACTOR);
928
- if (!smaller) return encoded;
1008
+ if (!smaller) break;
929
1009
  current = smaller;
930
1010
  }
931
- return current.toDataURL("image/jpeg", QUALITY_LADDER[0]);
1011
+ return encoded;
1012
+ }
1013
+ function readAsDataUrl(file) {
1014
+ return new Promise((resolve, reject) => {
1015
+ const reader = new FileReader();
1016
+ reader.onload = () => resolve(reader.result);
1017
+ reader.onerror = () => reject(reader.error);
1018
+ reader.readAsDataURL(file);
1019
+ });
1020
+ }
1021
+ async function reencodeImageFile(file, maxLength) {
1022
+ var _a;
1023
+ if (typeof createImageBitmap !== "function") return null;
1024
+ const bitmap = await createImageBitmap(file);
1025
+ try {
1026
+ const canvas = document.createElement("canvas");
1027
+ canvas.width = bitmap.width;
1028
+ canvas.height = bitmap.height;
1029
+ const ctx = canvas.getContext("2d");
1030
+ if (!ctx) return null;
1031
+ ctx.fillStyle = "#ffffff";
1032
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
1033
+ ctx.drawImage(bitmap, 0, 0);
1034
+ return encodeScreenshot(canvas, maxLength);
1035
+ } finally {
1036
+ (_a = bitmap.close) == null ? void 0 : _a.call(bitmap);
1037
+ }
1038
+ }
1039
+ async function encodeImageFile(file, maxLength = MAX_SCREENSHOT_DATA_URL_LENGTH) {
1040
+ const rawLength = Math.ceil(file.size / 3) * 4 + 64;
1041
+ if (rawLength > maxLength) {
1042
+ try {
1043
+ const shrunk = await reencodeImageFile(file, maxLength);
1044
+ if (shrunk && shrunk.length < rawLength) return shrunk;
1045
+ } catch (e) {
1046
+ }
1047
+ }
1048
+ return readAsDataUrl(file);
932
1049
  }
933
1050
  var PALETTE = ["#ef4444", "#f59e0b", "#22c55e", "#3b82f6", "#a855f7", "#ffffff"];
934
1051
  function drawStroke(ctx, s) {
@@ -1219,6 +1336,7 @@ var SEVERITY_LABELS = {
1219
1336
  critical: "Critical"
1220
1337
  };
1221
1338
  var MAX_ASSIST_IMAGES = 3;
1339
+ var MAX_ASSIST_IMAGE_CHARS = 35e5;
1222
1340
  function getTypeLabel(type) {
1223
1341
  switch (type) {
1224
1342
  case "RATING":
@@ -1239,6 +1357,17 @@ function getTypeLabel(type) {
1239
1357
  return "Other";
1240
1358
  }
1241
1359
  }
1360
+ function pickAssistImages(all) {
1361
+ const picked = [];
1362
+ let total = 0;
1363
+ for (let i = all.length - 1; i >= 0 && picked.length < MAX_ASSIST_IMAGES; i--) {
1364
+ const shot = all[i];
1365
+ if (total + shot.length > MAX_ASSIST_IMAGE_CHARS) continue;
1366
+ picked.unshift(shot);
1367
+ total += shot.length;
1368
+ }
1369
+ return picked;
1370
+ }
1242
1371
  var STARTERS = [
1243
1372
  "Something on this page is broken",
1244
1373
  "It did the wrong thing",
@@ -1316,6 +1445,7 @@ function AssistSheet({
1316
1445
  screenshots,
1317
1446
  pageShot = null,
1318
1447
  onAddImages,
1448
+ onRemoveImage,
1319
1449
  attachmentCount,
1320
1450
  context,
1321
1451
  reportTypeLabel,
@@ -1341,7 +1471,8 @@ function AssistSheet({
1341
1471
  onSend,
1342
1472
  onDegrade,
1343
1473
  onClose,
1344
- onFinish
1474
+ onFinish,
1475
+ onReportGlitchgrabProblem
1345
1476
  }) {
1346
1477
  const narrow = useIsNarrow();
1347
1478
  const severityAsked = showSeverity && currentTile === "BUG";
@@ -1352,12 +1483,12 @@ function AssistSheet({
1352
1483
  const [hoveredStar, setHoveredStar] = (0, import_react3.useState)(0);
1353
1484
  const [options, setOptions] = (0, import_react3.useState)([]);
1354
1485
  const [duplicate, setDuplicate] = (0, import_react3.useState)(null);
1486
+ const [glitchgrabOffer, setGlitchgrabOffer] = (0, import_react3.useState)(false);
1355
1487
  const [solved, setSolved] = (0, import_react3.useState)(null);
1356
1488
  const [phase, setPhase] = (0, import_react3.useState)(
1357
1489
  !typePicked ? "type" : currentTile === "RATING" ? "rating" : "chat"
1358
1490
  );
1359
1491
  const scrollRef = (0, import_react3.useRef)(null);
1360
- const inputRef = (0, import_react3.useRef)(null);
1361
1492
  const draftRef = (0, import_react3.useRef)(null);
1362
1493
  const seededRef = (0, import_react3.useRef)(false);
1363
1494
  const eventsRef = (0, import_react3.useRef)([]);
@@ -1383,6 +1514,7 @@ function AssistSheet({
1383
1514
  } catch (e) {
1384
1515
  }
1385
1516
  }
1517
+ const readShots = new Set(pickAssistImages(screenshots));
1386
1518
  function leave(type, detail) {
1387
1519
  logEvent(type, detail);
1388
1520
  flushEvents();
@@ -1402,12 +1534,12 @@ function AssistSheet({
1402
1534
  onDegrade(message);
1403
1535
  }
1404
1536
  async function runTurn(history2) {
1405
- var _a, _b, _c, _d, _e;
1537
+ var _a, _b, _c, _d;
1406
1538
  setBusy(true);
1407
1539
  let result;
1408
1540
  const events = takeEvents();
1409
1541
  try {
1410
- const sent = screenshots.slice(-MAX_ASSIST_IMAGES);
1542
+ const sent = pickAssistImages(screenshots);
1411
1543
  result = await assist({
1412
1544
  // `kind` only on drafts, so an older server sees the exact shape it
1413
1545
  // always did — it just learns the draft text now instead of a
@@ -1417,10 +1549,12 @@ function AssistSheet({
1417
1549
  ),
1418
1550
  conversationId,
1419
1551
  screenshots: sent,
1420
- screenshot: (_a = sent[sent.length - 1]) != null ? _a : null,
1421
1552
  context: {
1422
1553
  ...context != null ? context : {},
1423
- imageSources: sent.map((shot) => pageShot && shot === pageShot ? "page" : "attached")
1554
+ imageSources: sent.map((shot) => pageShot && shot === pageShot ? "page" : "attached"),
1555
+ // Whether this dialog can open the #366 panel at all. A hint — the
1556
+ // server decides whether the model may offer it.
1557
+ canReportGlitchgrab: !!onReportGlitchgrabProblem
1424
1558
  },
1425
1559
  events
1426
1560
  });
@@ -1441,8 +1575,9 @@ function AssistSheet({
1441
1575
  setConversationId(result.conversationId);
1442
1576
  onConversationChange == null ? void 0 : onConversationChange(result.conversationId);
1443
1577
  }
1444
- setDuplicate((_b = result.duplicate) != null ? _b : null);
1445
- onDuplicateChange == null ? void 0 : onDuplicateChange((_d = (_c = result.duplicate) == null ? void 0 : _c.number) != null ? _d : null);
1578
+ setDuplicate((_a = result.duplicate) != null ? _a : null);
1579
+ onDuplicateChange == null ? void 0 : onDuplicateChange((_c = (_b = result.duplicate) == null ? void 0 : _b.number) != null ? _c : null);
1580
+ setGlitchgrabOffer(!!result.aboutGlitchgrab && !!onReportGlitchgrabProblem && !!result.question);
1446
1581
  if (result.solved) {
1447
1582
  setSolved(result.solved);
1448
1583
  setOptions([]);
@@ -1460,7 +1595,7 @@ function AssistSheet({
1460
1595
  }
1461
1596
  if (result.question) {
1462
1597
  setMessages([...history2, { role: "assistant", content: result.question }]);
1463
- setOptions((_e = result.options) != null ? _e : []);
1598
+ setOptions((_d = result.options) != null ? _d : []);
1464
1599
  return;
1465
1600
  }
1466
1601
  onDegrade("The assistant had nothing to add \u2014 write your report below and send it as normal.");
@@ -1479,7 +1614,15 @@ function AssistSheet({
1479
1614
  const value = text.trim();
1480
1615
  if (!value || busy) return;
1481
1616
  setOptions([]);
1482
- const history2 = [...messages, { role: "user", content: value }];
1617
+ setGlitchgrabOffer(false);
1618
+ const underDraft = phase === "draft";
1619
+ if (underDraft) {
1620
+ const edited = aiDraftRef.current !== null && description.trim() !== aiDraftRef.current.trim();
1621
+ logEvent("keep_chatting", edited ? "after editing" : void 0);
1622
+ setPhase("chat");
1623
+ }
1624
+ const base = underDraft ? withCurrentDraft(messages) : messages;
1625
+ const history2 = [...base, { role: "user", content: value }];
1483
1626
  setMessages(history2);
1484
1627
  setInput("");
1485
1628
  void runTurn(history2);
@@ -1488,27 +1631,16 @@ function AssistSheet({
1488
1631
  onPickType(tile);
1489
1632
  setPhase(tile === "RATING" ? "rating" : "chat");
1490
1633
  }
1491
- function keepChatting() {
1634
+ function withCurrentDraft(history2) {
1492
1635
  const current = description.trim();
1493
- logEvent(
1494
- "keep_chatting",
1495
- aiDraftRef.current && current !== aiDraftRef.current.trim() ? "after editing" : void 0
1496
- );
1497
- setMessages((prev) => {
1498
- const next = [...prev];
1499
- for (let i = next.length - 1; i >= 0; i--) {
1500
- if (next[i].kind === "report") {
1501
- if (current) next[i] = { ...next[i], content: current };
1502
- break;
1503
- }
1636
+ const next = [...history2];
1637
+ for (let i = next.length - 1; i >= 0; i--) {
1638
+ if (next[i].kind === "report") {
1639
+ if (current) next[i] = { ...next[i], content: current };
1640
+ break;
1504
1641
  }
1505
- return next;
1506
- });
1507
- setPhase("chat");
1508
- requestAnimationFrame(() => {
1509
- var _a;
1510
- return (_a = inputRef.current) == null ? void 0 : _a.focus();
1511
- });
1642
+ }
1643
+ return next;
1512
1644
  }
1513
1645
  function sendDraft() {
1514
1646
  const edited = aiDraftRef.current !== null && description.trim() !== aiDraftRef.current.trim();
@@ -1516,9 +1648,39 @@ function AssistSheet({
1516
1648
  flushEvents();
1517
1649
  onSend();
1518
1650
  }
1519
- const lastMessage = messages[messages.length - 1];
1520
- const correcting = phase === "chat" && (lastMessage == null ? void 0 : lastMessage.kind) === "report";
1521
1651
  const empty = phase === "chat" && messages.length === 0 && !busy;
1652
+ function renderKnownIssue({ title, footer }) {
1653
+ if (!duplicate) return null;
1654
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1655
+ "div",
1656
+ {
1657
+ "data-gg-known-issue": "",
1658
+ style: {
1659
+ display: "flex",
1660
+ flexDirection: "column",
1661
+ gap: "3px",
1662
+ padding: "9px 10px",
1663
+ borderRadius: "8px",
1664
+ border: "1px solid rgba(245,158,11,0.4)",
1665
+ backgroundColor: "rgba(245,158,11,0.08)",
1666
+ fontSize: "12px",
1667
+ lineHeight: 1.5,
1668
+ minWidth: 0
1669
+ },
1670
+ children: [
1671
+ /* @__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}` }),
1672
+ title && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { style: { color: t2.textMuted, wordBreak: "break-word" }, children: [
1673
+ "#",
1674
+ duplicate.number,
1675
+ " ",
1676
+ duplicate.title
1677
+ ] }),
1678
+ duplicate.status && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: t2.text, fontWeight: 600 }, children: duplicate.status }),
1679
+ footer && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: t2.textMuted }, children: footer })
1680
+ ]
1681
+ }
1682
+ );
1683
+ }
1522
1684
  const initials = (reporterName != null ? reporterName : "").split(/\s+/).filter(Boolean).slice(0, 2).map((w) => {
1523
1685
  var _a, _b;
1524
1686
  return (_b = (_a = w[0]) == null ? void 0 : _a.toUpperCase()) != null ? _b : "";
@@ -1543,7 +1705,7 @@ function AssistSheet({
1543
1705
  @keyframes gg-sheet-up{from{transform:translateY(24px);opacity:0}to{transform:translateY(0);opacity:1}}
1544
1706
  @keyframes gg-sheet-fade{from{opacity:0}to{opacity:1}}
1545
1707
  @keyframes gg-dot{0%,80%,100%{transform:translateY(0);opacity:.4}40%{transform:translateY(-3px);opacity:1}}
1546
- [data-gg-sheet-send]:focus-visible,[data-gg-chip]:focus-visible{outline:2px solid currentColor;outline-offset:2px}
1708
+ [data-gg-sheet-send]:focus-visible,[data-gg-chip]:focus-visible,[data-gg-assist-remove]:focus-visible{outline:2px solid currentColor;outline-offset:2px}
1547
1709
  ` }),
1548
1710
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1549
1711
  "div",
@@ -2009,7 +2171,83 @@ function AssistSheet({
2009
2171
  i
2010
2172
  );
2011
2173
  }),
2012
- options.length > 0 && !busy && phase === "chat" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2174
+ duplicate && !busy && phase === "chat" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { paddingLeft: "32px", minWidth: 0 }, children: renderKnownIssue({ title: false }) }),
2175
+ glitchgrabOffer && onReportGlitchgrabProblem && !busy && phase === "chat" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { paddingLeft: "32px", minWidth: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
2176
+ "div",
2177
+ {
2178
+ "data-gg-glitchgrab-offer": "",
2179
+ style: {
2180
+ display: "flex",
2181
+ flexDirection: "column",
2182
+ gap: "8px",
2183
+ padding: "10px",
2184
+ borderRadius: "8px",
2185
+ border: `1px solid ${t2.accent}55`,
2186
+ backgroundColor: `${t2.accent}14`,
2187
+ fontSize: "12px",
2188
+ lineHeight: 1.5
2189
+ },
2190
+ children: [
2191
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: t2.text, fontWeight: 600 }, children: "This is about Glitchgrab, not this app" }),
2192
+ /* @__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." }),
2193
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", flexWrap: "wrap", gap: "6px" }, children: [
2194
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2195
+ "button",
2196
+ {
2197
+ type: "button",
2198
+ "data-gg-glitchgrab-offer-send": "",
2199
+ onClick: () => {
2200
+ logEvent("glitchgrab_offer_tap");
2201
+ setGlitchgrabOffer(false);
2202
+ onReportGlitchgrabProblem(
2203
+ messages.filter((m) => m.role === "user").map((m) => m.content.trim()).filter(Boolean).join("\n\n")
2204
+ );
2205
+ },
2206
+ style: {
2207
+ padding: "8px 12px",
2208
+ borderRadius: "999px",
2209
+ border: "none",
2210
+ backgroundColor: t2.accent,
2211
+ color: t2.accentText,
2212
+ fontSize: "12px",
2213
+ fontWeight: 700,
2214
+ fontFamily: "inherit",
2215
+ cursor: "pointer",
2216
+ // Both stretch, so on a phone each wrapped line reads as
2217
+ // a choice rather than one button left stranded.
2218
+ flex: "1 1 auto"
2219
+ },
2220
+ children: "Send to the Glitchgrab team"
2221
+ }
2222
+ ),
2223
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2224
+ "button",
2225
+ {
2226
+ type: "button",
2227
+ "data-gg-glitchgrab-offer-no": "",
2228
+ onClick: () => {
2229
+ logEvent("glitchgrab_offer_declined");
2230
+ send("No \u2014 it's about this app, not the reporting tool");
2231
+ },
2232
+ style: {
2233
+ padding: "8px 12px",
2234
+ borderRadius: "999px",
2235
+ border: `1px solid ${t2.inputBorder}`,
2236
+ background: "transparent",
2237
+ color: t2.textMuted,
2238
+ fontSize: "12px",
2239
+ fontFamily: "inherit",
2240
+ cursor: "pointer",
2241
+ flex: "1 1 auto"
2242
+ },
2243
+ children: "No, it's about this app"
2244
+ }
2245
+ )
2246
+ ] })
2247
+ ]
2248
+ }
2249
+ ) }),
2250
+ options.length > 0 && !busy && phase === "chat" && !glitchgrabOffer && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2013
2251
  "div",
2014
2252
  {
2015
2253
  style: {
@@ -2107,35 +2345,13 @@ function AssistSheet({
2107
2345
  textTransform: "uppercase",
2108
2346
  color: t2.accent
2109
2347
  },
2110
- children: "Your report \xB7 edit anything"
2111
- }
2112
- ),
2113
- duplicate && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
2114
- "div",
2115
- {
2116
- style: {
2117
- display: "flex",
2118
- flexDirection: "column",
2119
- gap: "3px",
2120
- padding: "9px 10px",
2121
- borderRadius: "8px",
2122
- border: "1px solid rgba(245,158,11,0.4)",
2123
- backgroundColor: "rgba(245,158,11,0.08)",
2124
- fontSize: "12px",
2125
- lineHeight: 1.5
2126
- },
2127
- children: [
2128
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: "#f59e0b", fontWeight: 600 }, children: "Our team is already on this" }),
2129
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { style: { color: t2.textMuted, wordBreak: "break-word" }, children: [
2130
- "#",
2131
- duplicate.number,
2132
- " ",
2133
- duplicate.title
2134
- ] }),
2135
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: t2.textMuted }, children: "Your details will be added to it instead of opening a new issue." })
2136
- ]
2348
+ children: duplicate ? `What we'll add to #${duplicate.number} \xB7 edit anything` : "Your report \xB7 edit anything"
2137
2349
  }
2138
2350
  ),
2351
+ renderKnownIssue({
2352
+ title: true,
2353
+ footer: "Your details will be added to it instead of opening a new issue."
2354
+ }),
2139
2355
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2140
2356
  "textarea",
2141
2357
  {
@@ -2230,26 +2446,7 @@ function AssistSheet({
2230
2446
  " ",
2231
2447
  attachmentCount === 1 ? "attachment" : "attachments",
2232
2448
  " will be sent with this."
2233
- ] }),
2234
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2235
- "button",
2236
- {
2237
- type: "button",
2238
- onClick: keepChatting,
2239
- style: {
2240
- alignSelf: "flex-start",
2241
- border: "none",
2242
- background: "transparent",
2243
- color: t2.textMuted,
2244
- fontSize: "12px",
2245
- fontFamily: "inherit",
2246
- padding: "9px 0",
2247
- cursor: "pointer",
2248
- textDecoration: "underline"
2249
- },
2250
- children: "Not quite \u2014 keep chatting"
2251
- }
2252
- )
2449
+ ] })
2253
2450
  ]
2254
2451
  }
2255
2452
  ),
@@ -2353,189 +2550,270 @@ function AssistSheet({
2353
2550
  },
2354
2551
  children: isSubmitting ? "Sending\u2026" : rating < 1 ? "Pick a star first" : "Send Rating"
2355
2552
  }
2356
- ) : phase === "draft" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
2357
- validationError && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2358
- "p",
2359
- {
2360
- style: {
2361
- margin: "0 0 8px",
2362
- color: "#ef4444",
2363
- fontSize: "12px"
2364
- },
2365
- children: validationError
2366
- }
2367
- ),
2368
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2369
- "button",
2370
- {
2371
- type: "button",
2372
- "data-gg-sheet-send": "",
2373
- onClick: sendDraft,
2374
- disabled: isSubmitting || !description.trim(),
2375
- style: {
2376
- width: "100%",
2377
- padding: "12px",
2378
- borderRadius: "10px",
2379
- border: "none",
2380
- backgroundColor: isSubmitting || !description.trim() ? t2.bgSecondary : t2.accent,
2381
- color: isSubmitting || !description.trim() ? t2.textMuted : t2.accentText,
2382
- fontSize: "14px",
2383
- fontWeight: 700,
2384
- fontFamily: "inherit",
2385
- cursor: isSubmitting || !description.trim() ? "default" : "pointer"
2386
- },
2387
- children: isSubmitting ? "Sending\u2026" : duplicate ? `Add to #${duplicate.number}` : "Send Report"
2388
- }
2389
- )
2390
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
2391
- (screenshots.length > 0 || onAddImages) && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
2392
- "div",
2393
- {
2394
- style: {
2395
- display: "flex",
2396
- gap: "8px",
2397
- alignItems: "center",
2398
- flexWrap: "wrap",
2399
- marginBottom: "8px"
2400
- },
2401
- children: [
2402
- screenshots.map((shot, i) => {
2403
- const read = i >= screenshots.length - MAX_ASSIST_IMAGES;
2404
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2405
- "img",
2553
+ ) : (
2554
+ // Chat AND draft share this branch, so the chat box is the same
2555
+ // element on both sides of a draft landing — it stays put
2556
+ // instead of remounting, and a draft is something you can
2557
+ // still reply to (#360), not a form that replaced the chat.
2558
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
2559
+ (screenshots.length > 0 || onAddImages) && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
2560
+ "div",
2561
+ {
2562
+ style: {
2563
+ display: "flex",
2564
+ gap: "8px",
2565
+ alignItems: "center",
2566
+ flexWrap: "wrap",
2567
+ marginBottom: "8px"
2568
+ },
2569
+ children: [
2570
+ screenshots.map((shot, i) => {
2571
+ const read = readShots.has(shot);
2572
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
2573
+ "div",
2574
+ {
2575
+ style: {
2576
+ position: "relative",
2577
+ width: "40px",
2578
+ height: "40px",
2579
+ flexShrink: 0
2580
+ },
2581
+ children: [
2582
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2583
+ "img",
2584
+ {
2585
+ src: shot,
2586
+ alt: read ? "Attached image the assistant reads" : "Attached image",
2587
+ "data-gg-assist-thumb": "",
2588
+ style: {
2589
+ display: "block",
2590
+ width: "100%",
2591
+ height: "100%",
2592
+ objectFit: "cover",
2593
+ borderRadius: "8px",
2594
+ border: `1px solid ${read ? t2.accent : t2.inputBorder}`,
2595
+ opacity: read ? 1 : 0.45,
2596
+ boxSizing: "border-box"
2597
+ }
2598
+ }
2599
+ ),
2600
+ onRemoveImage && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2601
+ "button",
2602
+ {
2603
+ type: "button",
2604
+ "data-gg-assist-remove": "",
2605
+ "aria-label": `Remove image ${i + 1}`,
2606
+ title: "Remove this image",
2607
+ onClick: () => {
2608
+ logEvent(
2609
+ "image_removed",
2610
+ pageShot && shot === pageShot ? "page capture" : "attached"
2611
+ );
2612
+ onRemoveImage(i);
2613
+ },
2614
+ style: {
2615
+ position: "absolute",
2616
+ top: "-13px",
2617
+ right: "-13px",
2618
+ width: "32px",
2619
+ height: "32px",
2620
+ border: "none",
2621
+ background: "transparent",
2622
+ padding: 0,
2623
+ cursor: "pointer",
2624
+ display: "flex",
2625
+ alignItems: "center",
2626
+ justifyContent: "center",
2627
+ borderRadius: "50%"
2628
+ },
2629
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2630
+ "span",
2631
+ {
2632
+ "aria-hidden": "true",
2633
+ style: {
2634
+ width: "20px",
2635
+ height: "20px",
2636
+ boxSizing: "border-box",
2637
+ borderRadius: "50%",
2638
+ border: `2px solid ${t2.bg}`,
2639
+ background: "#ef4444",
2640
+ color: "#fff",
2641
+ display: "flex",
2642
+ alignItems: "center",
2643
+ justifyContent: "center"
2644
+ },
2645
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { width: "8", height: "8", viewBox: "0 0 10 10", fill: "none", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2646
+ "path",
2647
+ {
2648
+ d: "M1 1L9 9M9 1L1 9",
2649
+ stroke: "currentColor",
2650
+ strokeWidth: "1.6",
2651
+ strokeLinecap: "round"
2652
+ }
2653
+ ) })
2654
+ }
2655
+ )
2656
+ }
2657
+ )
2658
+ ]
2659
+ },
2660
+ `${i}-${shot.slice(-16)}`
2661
+ );
2662
+ }),
2663
+ onAddImages && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
2664
+ "label",
2406
2665
  {
2407
- src: shot,
2408
- alt: read ? "Attached image the assistant reads" : "Attached image",
2409
- "data-gg-assist-thumb": "",
2666
+ "data-gg-assist-attach": "",
2667
+ title: "Attach an image \u2014 or just paste one",
2410
2668
  style: {
2411
2669
  width: "40px",
2412
2670
  height: "40px",
2413
- objectFit: "cover",
2414
2671
  borderRadius: "8px",
2415
- border: `1px solid ${read ? t2.accent : t2.inputBorder}`,
2416
- opacity: read ? 1 : 0.45
2417
- }
2418
- },
2419
- `${i}-${shot.slice(-16)}`
2420
- );
2421
- }),
2422
- onAddImages && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
2423
- "label",
2424
- {
2425
- "data-gg-assist-attach": "",
2426
- title: "Attach an image \u2014 or just paste one",
2427
- style: {
2428
- width: "40px",
2429
- height: "40px",
2430
- borderRadius: "8px",
2431
- border: `1px dashed ${t2.inputBorder}`,
2432
- display: "inline-flex",
2433
- alignItems: "center",
2434
- justifyContent: "center",
2435
- color: t2.textMuted,
2436
- fontSize: "18px",
2437
- cursor: "pointer",
2438
- flexShrink: 0
2439
- },
2440
- children: [
2441
- "+",
2442
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2443
- "input",
2444
- {
2445
- type: "file",
2446
- accept: "image/*",
2447
- multiple: true,
2448
- onChange: (e) => {
2449
- var _a;
2450
- const files = Array.from((_a = e.target.files) != null ? _a : []);
2451
- if (files.length) {
2452
- logEvent("image_added", String(files.length));
2453
- onAddImages(files);
2454
- }
2455
- e.target.value = "";
2456
- },
2457
- style: { display: "none" }
2458
- }
2459
- )
2460
- ]
2461
- }
2462
- ),
2463
- screenshots.length > MAX_ASSIST_IMAGES && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { style: { color: t2.textMuted, fontSize: "11px" }, children: [
2464
- "newest ",
2465
- MAX_ASSIST_IMAGES,
2466
- " read"
2467
- ] })
2468
- ]
2469
- }
2470
- ),
2471
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", gap: "8px", alignItems: "flex-end" }, children: [
2472
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2473
- "textarea",
2474
- {
2475
- ref: inputRef,
2476
- value: input,
2477
- onChange: (e) => setInput(e.target.value),
2478
- onKeyDown: (e) => {
2479
- if (e.key === "Enter" && !e.shiftKey) {
2480
- e.preventDefault();
2481
- send(input);
2482
- }
2483
- },
2484
- placeholder: correcting ? "What should change in the draft?" : "Type your answer\u2026",
2485
- rows: 1,
2486
- disabled: busy,
2487
- autoFocus: true,
2488
- style: {
2489
- flex: 1,
2490
- minHeight: "42px",
2491
- maxHeight: "120px",
2492
- padding: "11px 12px",
2493
- borderRadius: "10px",
2494
- border: `1px solid ${t2.inputBorder}`,
2495
- backgroundColor: t2.inputBg,
2496
- color: t2.text,
2497
- fontSize: "13px",
2498
- lineHeight: 1.4,
2499
- fontFamily: "inherit",
2500
- resize: "none",
2501
- outline: "none",
2502
- boxSizing: "border-box"
2503
- }
2672
+ border: `1px dashed ${t2.inputBorder}`,
2673
+ display: "inline-flex",
2674
+ alignItems: "center",
2675
+ justifyContent: "center",
2676
+ color: t2.textMuted,
2677
+ fontSize: "18px",
2678
+ cursor: "pointer",
2679
+ flexShrink: 0
2680
+ },
2681
+ children: [
2682
+ "+",
2683
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2684
+ "input",
2685
+ {
2686
+ type: "file",
2687
+ accept: "image/*",
2688
+ multiple: true,
2689
+ onChange: (e) => {
2690
+ var _a;
2691
+ const files = Array.from((_a = e.target.files) != null ? _a : []);
2692
+ if (files.length) {
2693
+ logEvent("image_added", String(files.length));
2694
+ onAddImages(files);
2695
+ }
2696
+ e.target.value = "";
2697
+ },
2698
+ style: { display: "none" }
2699
+ }
2700
+ )
2701
+ ]
2702
+ }
2703
+ ),
2704
+ readShots.size < screenshots.length && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { style: { color: t2.textMuted, fontSize: "11px" }, children: [
2705
+ readShots.size,
2706
+ " of ",
2707
+ screenshots.length,
2708
+ " read"
2709
+ ] })
2710
+ ]
2504
2711
  }
2505
2712
  ),
2506
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2507
- "button",
2508
- {
2509
- type: "button",
2510
- "data-gg-sheet-send": "",
2511
- onClick: () => send(input),
2512
- disabled: busy || !input.trim(),
2513
- "aria-label": "Send message",
2514
- "data-gg-send-message": "",
2515
- style: {
2516
- flexShrink: 0,
2517
- width: "42px",
2518
- height: "42px",
2519
- borderRadius: "10px",
2520
- border: "none",
2521
- display: "inline-flex",
2522
- alignItems: "center",
2523
- justifyContent: "center",
2524
- backgroundColor: busy || !input.trim() ? t2.bgSecondary : t2.accent,
2525
- color: busy || !input.trim() ? t2.textMuted : t2.accentText,
2526
- cursor: busy || !input.trim() ? "default" : "pointer"
2527
- },
2528
- children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { width: "17", height: "17", viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2529
- "path",
2530
- {
2531
- d: "M3 20.5L21.5 12 3 3.5V10l12 2-12 2v6.5z",
2532
- fill: "currentColor"
2713
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", gap: "8px", alignItems: "flex-end" }, children: [
2714
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2715
+ "textarea",
2716
+ {
2717
+ value: input,
2718
+ onChange: (e) => setInput(e.target.value),
2719
+ onKeyDown: (e) => {
2720
+ if (e.key === "Enter" && !e.shiftKey) {
2721
+ e.preventDefault();
2722
+ send(input);
2723
+ }
2724
+ },
2725
+ placeholder: phase === "draft" ? "Anything to change? Tell me here\u2026" : "Type your answer\u2026",
2726
+ rows: 1,
2727
+ disabled: busy,
2728
+ autoFocus: true,
2729
+ style: {
2730
+ flex: 1,
2731
+ minHeight: "42px",
2732
+ maxHeight: "120px",
2733
+ padding: "11px 12px",
2734
+ borderRadius: "10px",
2735
+ border: `1px solid ${t2.inputBorder}`,
2736
+ backgroundColor: t2.inputBg,
2737
+ color: t2.text,
2738
+ fontSize: "13px",
2739
+ lineHeight: 1.4,
2740
+ fontFamily: "inherit",
2741
+ resize: "none",
2742
+ outline: "none",
2743
+ boxSizing: "border-box"
2533
2744
  }
2534
- ) })
2535
- }
2536
- )
2745
+ }
2746
+ ),
2747
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2748
+ "button",
2749
+ {
2750
+ type: "button",
2751
+ "data-gg-sheet-send": "",
2752
+ onClick: () => send(input),
2753
+ disabled: busy || !input.trim(),
2754
+ "aria-label": "Send message",
2755
+ "data-gg-send-message": "",
2756
+ style: {
2757
+ flexShrink: 0,
2758
+ width: "42px",
2759
+ height: "42px",
2760
+ borderRadius: "10px",
2761
+ border: "none",
2762
+ display: "inline-flex",
2763
+ alignItems: "center",
2764
+ justifyContent: "center",
2765
+ backgroundColor: busy || !input.trim() ? t2.bgSecondary : t2.accent,
2766
+ color: busy || !input.trim() ? t2.textMuted : t2.accentText,
2767
+ cursor: busy || !input.trim() ? "default" : "pointer"
2768
+ },
2769
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { width: "17", height: "17", viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2770
+ "path",
2771
+ {
2772
+ d: "M3 20.5L21.5 12 3 3.5V10l12 2-12 2v6.5z",
2773
+ fill: "currentColor"
2774
+ }
2775
+ ) })
2776
+ }
2777
+ )
2778
+ ] }),
2779
+ phase === "draft" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
2780
+ validationError && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2781
+ "p",
2782
+ {
2783
+ style: {
2784
+ margin: "8px 0 0",
2785
+ color: "#ef4444",
2786
+ fontSize: "12px"
2787
+ },
2788
+ children: validationError
2789
+ }
2790
+ ),
2791
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2792
+ "button",
2793
+ {
2794
+ type: "button",
2795
+ "data-gg-sheet-send": "",
2796
+ onClick: sendDraft,
2797
+ disabled: isSubmitting || !description.trim(),
2798
+ style: {
2799
+ width: "100%",
2800
+ marginTop: "8px",
2801
+ padding: "12px",
2802
+ borderRadius: "10px",
2803
+ border: "none",
2804
+ backgroundColor: isSubmitting || !description.trim() ? t2.bgSecondary : t2.accent,
2805
+ color: isSubmitting || !description.trim() ? t2.textMuted : t2.accentText,
2806
+ fontSize: "14px",
2807
+ fontWeight: 700,
2808
+ fontFamily: "inherit",
2809
+ cursor: isSubmitting || !description.trim() ? "default" : "pointer"
2810
+ },
2811
+ children: isSubmitting ? "Sending\u2026" : duplicate ? `Add to #${duplicate.number}` : "Send Report"
2812
+ }
2813
+ )
2814
+ ] })
2537
2815
  ] })
2538
- ] }),
2816
+ ),
2539
2817
  phase === "chat" && messages.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2540
2818
  "button",
2541
2819
  {
@@ -2555,16 +2833,274 @@ function AssistSheet({
2555
2833
  },
2556
2834
  children: "Not getting it? Fill the form yourself \u2192"
2557
2835
  }
2836
+ ),
2837
+ onReportGlitchgrabProblem && phase !== "solved" && !glitchgrabOffer && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2838
+ "button",
2839
+ {
2840
+ type: "button",
2841
+ "data-gg-self-report": "",
2842
+ onClick: () => onReportGlitchgrabProblem(),
2843
+ style: {
2844
+ display: "block",
2845
+ // ~32px tap target; the negative margins keep the composer
2846
+ // from growing to make room for it.
2847
+ margin: "-2px auto -8px",
2848
+ border: "none",
2849
+ background: "transparent",
2850
+ color: t2.textMuted,
2851
+ fontSize: "11.5px",
2852
+ fontFamily: "inherit",
2853
+ padding: "10px 8px",
2854
+ cursor: "pointer",
2855
+ textDecoration: "underline",
2856
+ textUnderlineOffset: "2px"
2857
+ },
2858
+ children: "Assistant misbehaving? Tell Glitchgrab"
2859
+ }
2558
2860
  )
2559
2861
  ]
2560
2862
  }
2561
2863
  )
2562
- ]
2563
- }
2564
- )
2565
- }
2566
- )
2567
- ] }),
2864
+ ]
2865
+ }
2866
+ )
2867
+ }
2868
+ )
2869
+ ] }),
2870
+ document.body
2871
+ );
2872
+ }
2873
+ var FONT = '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
2874
+ var FAILED = "Couldn't send it. Try again in a moment.";
2875
+ function describeSentAlong(hasChat, screenshotCount) {
2876
+ const parts = [
2877
+ ...hasChat ? ["your AI chat"] : [],
2878
+ "what you did on this page",
2879
+ ...screenshotCount > 0 ? [`${screenshotCount} screenshot${screenshotCount === 1 ? "" : "s"}`] : []
2880
+ ];
2881
+ if (parts.length === 1) return parts[0];
2882
+ return `${parts.slice(0, -1).join(", ")} and ${parts[parts.length - 1]}`;
2883
+ }
2884
+ function GlitchgrabProblemPanel({
2885
+ send,
2886
+ theme: t2,
2887
+ surface,
2888
+ conversationId,
2889
+ reportType,
2890
+ screenshots,
2891
+ initialText = "",
2892
+ onClose
2893
+ }) {
2894
+ const [text, setText] = (0, import_react4.useState)(initialText);
2895
+ const [busy, setBusy] = (0, import_react4.useState)(false);
2896
+ const [sent, setSent] = (0, import_react4.useState)(false);
2897
+ const [error, setError] = (0, import_react4.useState)(null);
2898
+ const inputRef = (0, import_react4.useRef)(null);
2899
+ (0, import_react4.useEffect)(() => {
2900
+ var _a;
2901
+ (_a = inputRef.current) == null ? void 0 : _a.focus();
2902
+ }, []);
2903
+ (0, import_react4.useEffect)(() => {
2904
+ const onKey = (e) => {
2905
+ if (e.key !== "Escape") return;
2906
+ e.stopPropagation();
2907
+ onClose();
2908
+ };
2909
+ window.addEventListener("keydown", onKey, true);
2910
+ return () => window.removeEventListener("keydown", onKey, true);
2911
+ }, [onClose]);
2912
+ const submit = async () => {
2913
+ const description = text.trim();
2914
+ if (!description || busy) return;
2915
+ setBusy(true);
2916
+ setError(null);
2917
+ try {
2918
+ const result = await send({ description, surface, conversationId, reportType, screenshots });
2919
+ if (result == null ? void 0 : result.success) setSent(true);
2920
+ else setError((result == null ? void 0 : result.message) || FAILED);
2921
+ } catch (e) {
2922
+ setError(FAILED);
2923
+ } finally {
2924
+ setBusy(false);
2925
+ }
2926
+ };
2927
+ const canSend = !!text.trim() && !busy;
2928
+ const secondaryButton = {
2929
+ padding: "10px 14px",
2930
+ borderRadius: "8px",
2931
+ border: `1px solid ${t2.inputBorder}`,
2932
+ background: "transparent",
2933
+ color: t2.text,
2934
+ fontSize: "13px",
2935
+ fontWeight: 600,
2936
+ fontFamily: "inherit",
2937
+ cursor: "pointer"
2938
+ };
2939
+ const primaryButton = (enabled) => ({
2940
+ padding: "10px 14px",
2941
+ borderRadius: "8px",
2942
+ border: "none",
2943
+ backgroundColor: enabled ? t2.accent : t2.bgSecondary,
2944
+ color: enabled ? t2.accentText : t2.textMuted,
2945
+ fontSize: "13px",
2946
+ fontWeight: 700,
2947
+ fontFamily: "inherit",
2948
+ cursor: enabled ? "pointer" : "default"
2949
+ });
2950
+ return (0, import_react_dom3.createPortal)(
2951
+ // No close on backdrop click: a stray click would throw away what they typed.
2952
+ // `data-glitchgrab-layer` exempts this from the dialog's anti-focus-trap
2953
+ // sweep, which sets `inert` on every other role="dialog" on the page —
2954
+ // without it the panel renders but cannot be typed into.
2955
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2956
+ "div",
2957
+ {
2958
+ "data-glitchgrab-layer": "",
2959
+ "data-gg-self-report-panel": "",
2960
+ role: "dialog",
2961
+ "aria-modal": "true",
2962
+ "aria-labelledby": "gg-self-report-title",
2963
+ style: {
2964
+ position: "fixed",
2965
+ inset: 0,
2966
+ zIndex: 2147483647,
2967
+ backgroundColor: "rgba(0,0,0,0.55)",
2968
+ display: "flex",
2969
+ alignItems: "center",
2970
+ justifyContent: "center",
2971
+ padding: "16px",
2972
+ boxSizing: "border-box",
2973
+ fontFamily: FONT
2974
+ },
2975
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2976
+ "div",
2977
+ {
2978
+ style: {
2979
+ width: "100%",
2980
+ maxWidth: "420px",
2981
+ boxSizing: "border-box",
2982
+ backgroundColor: t2.bg,
2983
+ color: t2.text,
2984
+ border: `1px solid ${t2.border}`,
2985
+ borderRadius: "12px",
2986
+ boxShadow: "0 20px 50px rgba(0,0,0,0.5)",
2987
+ padding: "18px"
2988
+ },
2989
+ children: sent ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
2990
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { id: "gg-self-report-title", style: { fontSize: "15px", fontWeight: 600 }, children: "Sent to the Glitchgrab team" }),
2991
+ /* @__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." }),
2992
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2993
+ "button",
2994
+ {
2995
+ type: "button",
2996
+ "data-gg-self-report-done": "",
2997
+ onClick: onClose,
2998
+ style: { ...primaryButton(true), width: "100%" },
2999
+ children: "Back to your report"
3000
+ }
3001
+ )
3002
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
3003
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
3004
+ "div",
3005
+ {
3006
+ style: {
3007
+ display: "flex",
3008
+ alignItems: "flex-start",
3009
+ justifyContent: "space-between",
3010
+ gap: "12px"
3011
+ },
3012
+ children: [
3013
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { id: "gg-self-report-title", style: { fontSize: "15px", fontWeight: 600 }, children: "Problem with Glitchgrab?" }),
3014
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
3015
+ "button",
3016
+ {
3017
+ type: "button",
3018
+ onClick: onClose,
3019
+ "aria-label": "Close",
3020
+ style: {
3021
+ border: "none",
3022
+ background: "transparent",
3023
+ color: t2.textMuted,
3024
+ fontSize: "18px",
3025
+ lineHeight: 1,
3026
+ cursor: "pointer",
3027
+ padding: "4px 6px",
3028
+ margin: "-4px -6px 0 0"
3029
+ },
3030
+ children: "\xD7"
3031
+ }
3032
+ )
3033
+ ]
3034
+ }
3035
+ ),
3036
+ /* @__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." }),
3037
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
3038
+ "textarea",
3039
+ {
3040
+ ref: inputRef,
3041
+ "data-gg-self-report-input": "",
3042
+ value: text,
3043
+ onChange: (e) => {
3044
+ setText(e.target.value);
3045
+ if (error) setError(null);
3046
+ },
3047
+ onKeyDown: (e) => {
3048
+ if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
3049
+ e.preventDefault();
3050
+ void submit();
3051
+ }
3052
+ },
3053
+ rows: 4,
3054
+ placeholder: "e.g. The assistant kept asking the same question and never wrote a draft",
3055
+ style: {
3056
+ width: "100%",
3057
+ boxSizing: "border-box",
3058
+ resize: "vertical",
3059
+ padding: "10px 12px",
3060
+ borderRadius: "8px",
3061
+ border: `1px solid ${t2.inputBorder}`,
3062
+ backgroundColor: t2.inputBg,
3063
+ color: t2.text,
3064
+ fontSize: "14px",
3065
+ lineHeight: 1.45,
3066
+ fontFamily: "inherit",
3067
+ outline: "none"
3068
+ }
3069
+ }
3070
+ ),
3071
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
3072
+ "p",
3073
+ {
3074
+ "data-gg-self-report-includes": "",
3075
+ style: { margin: "8px 0 0", fontSize: "11.5px", lineHeight: 1.45, color: t2.textMuted },
3076
+ children: [
3077
+ "Also sent, so we can see what happened:",
3078
+ " ",
3079
+ describeSentAlong(conversationId !== null, screenshots.length),
3080
+ "."
3081
+ ]
3082
+ }
3083
+ ),
3084
+ error && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { role: "alert", style: { margin: "8px 0 0", fontSize: "12px", color: "#ef4444" }, children: error }),
3085
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex", justifyContent: "flex-end", gap: "8px", marginTop: "14px" }, children: [
3086
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", onClick: onClose, style: secondaryButton, children: "Cancel" }),
3087
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
3088
+ "button",
3089
+ {
3090
+ type: "button",
3091
+ "data-gg-self-report-send": "",
3092
+ onClick: () => void submit(),
3093
+ disabled: !canSend,
3094
+ style: primaryButton(canSend),
3095
+ children: busy ? "Sending\u2026" : "Send to Glitchgrab"
3096
+ }
3097
+ )
3098
+ ] })
3099
+ ] })
3100
+ }
3101
+ )
3102
+ }
3103
+ ),
2568
3104
  document.body
2569
3105
  );
2570
3106
  }
@@ -2615,6 +3151,7 @@ var ATTACHMENT_ACCEPT = [
2615
3151
  ].join(",");
2616
3152
  var MAX_SCALE = 2;
2617
3153
  var FRAME_TIMEOUT_MS = 3e3;
3154
+ var GRAB_TIMEOUT_MS = FRAME_TIMEOUT_MS + 2e3;
2618
3155
  function canCaptureTabPixels() {
2619
3156
  var _a;
2620
3157
  try {
@@ -2681,7 +3218,13 @@ async function captureViaTabPixels() {
2681
3218
  stream = await navigator.mediaDevices.getDisplayMedia(options);
2682
3219
  const surface = (_b = (_a = stream.getVideoTracks()[0]) == null ? void 0 : _a.getSettings()) == null ? void 0 : _b.displaySurface;
2683
3220
  if (surface && surface !== "browser") return null;
2684
- const canvas = await grabFrame(stream);
3221
+ let timer;
3222
+ const timedOut = new Promise((resolve) => {
3223
+ timer = setTimeout(() => resolve(null), GRAB_TIMEOUT_MS);
3224
+ });
3225
+ const canvas = await Promise.race([grabFrame(stream), timedOut]).finally(
3226
+ () => clearTimeout(timer)
3227
+ );
2685
3228
  return canvas ? encodeScreenshot(canvas) : null;
2686
3229
  } catch (e) {
2687
3230
  return null;
@@ -2689,6 +3232,10 @@ async function captureViaTabPixels() {
2689
3232
  stream == null ? void 0 : stream.getTracks().forEach((track) => track.stop());
2690
3233
  }
2691
3234
  }
3235
+ var IMAGE_TIMEOUT_MS = 3e3;
3236
+ function isGlitchgrabLayer(el) {
3237
+ return el.hasAttribute("data-glitchgrab-layer");
3238
+ }
2692
3239
  async function captureViaHtml2Canvas() {
2693
3240
  try {
2694
3241
  const { default: html2canvas } = await import("html2canvas-pro");
@@ -2698,6 +3245,8 @@ async function captureViaHtml2Canvas() {
2698
3245
  logging: false,
2699
3246
  useCORS: true,
2700
3247
  allowTaint: true,
3248
+ imageTimeout: IMAGE_TIMEOUT_MS,
3249
+ ignoreElements: isGlitchgrabLayer,
2701
3250
  x: window.scrollX,
2702
3251
  y: window.scrollY,
2703
3252
  width: window.innerWidth,
@@ -2710,12 +3259,40 @@ async function captureViaHtml2Canvas() {
2710
3259
  return null;
2711
3260
  }
2712
3261
  }
2713
- async function captureDefaultScreenshot() {
2714
- var _a;
2715
- return (_a = await captureViaTabPixels()) != null ? _a : await captureViaHtml2Canvas();
3262
+ var inFlight = null;
3263
+ function nextPaint() {
3264
+ return new Promise((resolve) => {
3265
+ const cap = setTimeout(resolve, 100);
3266
+ if (typeof requestAnimationFrame !== "function") return;
3267
+ requestAnimationFrame(
3268
+ () => setTimeout(() => {
3269
+ clearTimeout(cap);
3270
+ resolve();
3271
+ }, 0)
3272
+ );
3273
+ });
3274
+ }
3275
+ function captureDefaultScreenshot(options) {
3276
+ if (inFlight) return inFlight;
3277
+ inFlight = (async () => {
3278
+ const pixels = await captureViaTabPixels();
3279
+ if (pixels) return pixels;
3280
+ if (options == null ? void 0 : options.onRedraw) {
3281
+ try {
3282
+ options.onRedraw();
3283
+ } catch (e) {
3284
+ }
3285
+ await nextPaint();
3286
+ }
3287
+ return captureViaHtml2Canvas();
3288
+ })().finally(() => {
3289
+ inFlight = null;
3290
+ });
3291
+ return inFlight;
2716
3292
  }
2717
3293
  var SEVERITY_REQUIRED = "Pick how bad it is before sending.";
2718
- function useIsDark() {
3294
+ var SHOT_WAIT_MS = 3e3;
3295
+ function detectDark() {
2719
3296
  if (typeof window === "undefined") return true;
2720
3297
  try {
2721
3298
  for (const el of [document.body, document.documentElement]) {
@@ -2849,13 +3426,13 @@ function ReporterChip({
2849
3426
  const label = isAnonymous ? "Anonymous" : name;
2850
3427
  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 ");
2851
3428
  const showAvatarImage = !!avatarUrl && !avatarFailed;
2852
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
3429
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
2853
3430
  "span",
2854
3431
  {
2855
3432
  title,
2856
3433
  style: { display: "flex", alignItems: "center", gap: "6px", minWidth: 0 },
2857
3434
  children: [
2858
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
3435
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2859
3436
  "span",
2860
3437
  {
2861
3438
  "aria-hidden": "true",
@@ -2875,7 +3452,7 @@ function ReporterChip({
2875
3452
  color: isAnonymous ? t2.textMuted : t2.accentText,
2876
3453
  border: `1px solid ${isAnonymous ? t2.inputBorder : "transparent"}`
2877
3454
  },
2878
- children: showAvatarImage ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
3455
+ children: showAvatarImage ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2879
3456
  "img",
2880
3457
  {
2881
3458
  src: avatarUrl,
@@ -2885,9 +3462,9 @@ function ReporterChip({
2885
3462
  onError: () => setAvatarFailed(true),
2886
3463
  style: { width: "100%", height: "100%", objectFit: "cover" }
2887
3464
  }
2888
- ) : initials ? initials : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("svg", { width: "10", height: "10", viewBox: "0 0 16 16", fill: "none", children: [
2889
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: "8", cy: "5.5", r: "2.75", stroke: "currentColor", strokeWidth: "1.4" }),
2890
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
3465
+ ) : initials ? initials : /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { width: "10", height: "10", viewBox: "0 0 16 16", fill: "none", children: [
3466
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: "8", cy: "5.5", r: "2.75", stroke: "currentColor", strokeWidth: "1.4" }),
3467
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2891
3468
  "path",
2892
3469
  {
2893
3470
  d: "M2.75 13.5c0-2.35 2.35-3.75 5.25-3.75s5.25 1.4 5.25 3.75",
@@ -2899,7 +3476,7 @@ function ReporterChip({
2899
3476
  ] })
2900
3477
  }
2901
3478
  ),
2902
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
3479
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
2903
3480
  "span",
2904
3481
  {
2905
3482
  style: {
@@ -3028,13 +3605,16 @@ function ReportDialog({
3028
3605
  captureScreenshot = captureDefaultScreenshot,
3029
3606
  reporter,
3030
3607
  onClose,
3031
- headerSlot
3608
+ headerSlot,
3609
+ reportGlitchgrabProblem
3032
3610
  }) {
3033
3611
  var _a;
3034
3612
  const [isEnhancing, setIsEnhancing] = (0, import_react.useState)(false);
3035
3613
  const [assistOpen, setAssistOpen] = (0, import_react.useState)(false);
3036
3614
  const [assistNotice, setAssistNotice] = (0, import_react.useState)(null);
3037
3615
  const [assistUsed, setAssistUsed] = (0, import_react.useState)(false);
3616
+ const [selfReportFrom, setSelfReportFrom] = (0, import_react.useState)(null);
3617
+ const [selfReportPrefill, setSelfReportPrefill] = (0, import_react.useState)("");
3038
3618
  const [typeChosen, setTypeChosen] = (0, import_react.useState)(false);
3039
3619
  const [duplicateIssueNumber, setDuplicateIssueNumber] = (0, import_react.useState)(null);
3040
3620
  const [aiConversationId, setAiConversationId] = (0, import_react.useState)(null);
@@ -3056,6 +3636,9 @@ function ReportDialog({
3056
3636
  const [submitted, setSubmitted] = (0, import_react.useState)(false);
3057
3637
  const [screenshots, setScreenshots] = (0, import_react.useState)([]);
3058
3638
  const [pageShot, setPageShot] = (0, import_react.useState)(null);
3639
+ const [shotPending, setShotPending] = (0, import_react.useState)(false);
3640
+ const pendingShotRef = (0, import_react.useRef)(null);
3641
+ const openGenRef = (0, import_react.useRef)(0);
3059
3642
  const [attachments, setAttachments] = (0, import_react.useState)([]);
3060
3643
  const [previewIndex, setPreviewIndex] = (0, import_react.useState)(null);
3061
3644
  const [annotatingIndex, setAnnotatingIndex] = (0, import_react.useState)(null);
@@ -3184,8 +3767,19 @@ function ReportDialog({
3184
3767
  const next = current === -1 ? 0 : Math.min(Math.max(current + delta, 0), tiles.length - 1);
3185
3768
  (_c = tiles[next]) == null ? void 0 : _c.focus();
3186
3769
  };
3187
- const isDark = useIsDark();
3770
+ const isDark = (0, import_react.useMemo)(() => isOpen ? detectDark() : true, [isOpen]);
3188
3771
  const t2 = getTheme(isDark);
3772
+ const layerTheme = {
3773
+ bg: t2.bg,
3774
+ bgSecondary: t2.bgSecondary,
3775
+ border: t2.border,
3776
+ text: t2.text,
3777
+ textMuted: t2.textMuted,
3778
+ inputBg: t2.inputBg,
3779
+ inputBorder: t2.inputBorder,
3780
+ accent: t2.accent,
3781
+ accentText: t2.accentText
3782
+ };
3189
3783
  const canZoomPreview = previewNatural !== null && typeof window !== "undefined" && (previewNatural.w > window.innerWidth - 32 || previewNatural.h > window.innerHeight - 80);
3190
3784
  const handleFileUpload = (e) => {
3191
3785
  const files = e.target.files;
@@ -3194,24 +3788,28 @@ function ReportDialog({
3194
3788
  e.target.value = "";
3195
3789
  };
3196
3790
  const addFiles = (files) => {
3197
- files.forEach((file) => {
3791
+ const images = files.filter((file) => file.type.startsWith("image/"));
3792
+ if (images.length > 0) {
3793
+ void Promise.all(images.map((file) => encodeImageFile(file).catch(() => null))).then(
3794
+ (encoded) => {
3795
+ const added = encoded.filter((url) => !!url);
3796
+ if (added.length > 0) setScreenshots((prev) => [...prev, ...added]);
3797
+ }
3798
+ );
3799
+ }
3800
+ files.filter((file) => !file.type.startsWith("image/")).forEach((file) => {
3198
3801
  const reader = new FileReader();
3199
- if (file.type.startsWith("image/")) {
3200
- reader.onload = () => {
3201
- setScreenshots((prev) => [...prev, reader.result]);
3202
- };
3203
- } else {
3204
- reader.onload = () => {
3205
- setAttachments((prev) => [
3206
- ...prev,
3207
- { name: file.name, size: file.size, dataUrl: reader.result }
3208
- ]);
3209
- };
3210
- }
3802
+ reader.onload = () => {
3803
+ setAttachments((prev) => [
3804
+ ...prev,
3805
+ { name: file.name, size: file.size, dataUrl: reader.result }
3806
+ ]);
3807
+ };
3211
3808
  reader.readAsDataURL(file);
3212
3809
  });
3213
3810
  };
3214
3811
  const handleOpen = async () => {
3812
+ const gen = openGenRef.current;
3215
3813
  setSubmitted(false);
3216
3814
  closePreview();
3217
3815
  setAnnotatingIndex(null);
@@ -3220,11 +3818,32 @@ function ReportDialog({
3220
3818
  setTypeChosen(true);
3221
3819
  setStep(2);
3222
3820
  }
3223
- const shot = await captureScreenshot();
3821
+ const show = () => {
3822
+ if (gen !== openGenRef.current) return;
3823
+ if (assist && !assistUsed) setAssistOpen(true);
3824
+ setIsOpen(true);
3825
+ };
3826
+ const capture = captureScreenshot({
3827
+ onRedraw: () => {
3828
+ if (gen !== openGenRef.current) return;
3829
+ setShotPending(true);
3830
+ show();
3831
+ }
3832
+ });
3833
+ pendingShotRef.current = capture;
3834
+ const previousPageShot = pageShot;
3835
+ const shot = await capture.catch(() => null);
3836
+ if (gen !== openGenRef.current) return;
3837
+ pendingShotRef.current = null;
3838
+ setShotPending(false);
3224
3839
  setPageShot(shot != null ? shot : null);
3225
- if (shot) setScreenshots([shot]);
3226
- if (assist && !assistUsed) setAssistOpen(true);
3227
- setIsOpen(true);
3840
+ if (shot) {
3841
+ setScreenshots((prev) => [
3842
+ shot,
3843
+ ...prev.filter((s) => s !== shot && s !== previousPageShot)
3844
+ ]);
3845
+ }
3846
+ show();
3228
3847
  };
3229
3848
  (0, import_react.useEffect)(() => {
3230
3849
  const handler = (e) => {
@@ -3417,6 +4036,9 @@ function ReportDialog({
3417
4036
  stopVoice();
3418
4037
  setIsOpen(false);
3419
4038
  onClose == null ? void 0 : onClose();
4039
+ openGenRef.current += 1;
4040
+ pendingShotRef.current = null;
4041
+ setShotPending(false);
3420
4042
  closePreview();
3421
4043
  setAnnotatingIndex(null);
3422
4044
  setStep(1);
@@ -3427,6 +4049,7 @@ function ReportDialog({
3427
4049
  setDuplicateIssueNumber(null);
3428
4050
  setSeverity(null);
3429
4051
  setValidationError(null);
4052
+ setSelfReportFrom(null);
3430
4053
  setVoiceError(null);
3431
4054
  setIsEnhanced(false);
3432
4055
  setOriginalDescription(null);
@@ -3649,8 +4272,19 @@ function ReportDialog({
3649
4272
  return;
3650
4273
  }
3651
4274
  setIsSubmitting(true);
4275
+ let shots = screenshots;
4276
+ const pending = pendingShotRef.current;
4277
+ if (pending) {
4278
+ const shot = await Promise.race([
4279
+ pending.catch(() => null),
4280
+ new Promise((resolve) => setTimeout(() => resolve(null), SHOT_WAIT_MS))
4281
+ ]);
4282
+ if (shot && !shots.includes(shot)) {
4283
+ shots = [shot, ...shots.filter((s) => s !== pageShot)];
4284
+ }
4285
+ }
3652
4286
  const metadata = {};
3653
- if (screenshots.length > 0) metadata.screenshots = JSON.stringify(screenshots);
4287
+ if (shots.length > 0) metadata.screenshots = JSON.stringify(shots);
3654
4288
  if (attachments.length > 0) metadata.attachments = JSON.stringify(attachments);
3655
4289
  if (showSeverity && reportType === "BUG" && severity) {
3656
4290
  metadata.severity = severity;
@@ -3686,9 +4320,9 @@ function ReportDialog({
3686
4320
  }
3687
4321
  };
3688
4322
  if (!mounted) return null;
3689
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
4323
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
3690
4324
  isOpen && (0, import_react_dom.createPortal)(
3691
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("style", { children: `
4325
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("style", { children: `
3692
4326
  @keyframes gg-pulse{0%,100%{opacity:1}50%{opacity:0.35}}
3693
4327
  @keyframes gg-b1{0%,100%{height:3px}40%{height:13px}}
3694
4328
  @keyframes gg-b2{0%,100%{height:8px}50%{height:3px}}
@@ -3725,7 +4359,7 @@ function ReportDialog({
3725
4359
  document.body
3726
4360
  ),
3727
4361
  isOpen && (0, import_react_dom.createPortal)(
3728
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4362
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3729
4363
  "div",
3730
4364
  {
3731
4365
  "data-glitchgrab-layer": "",
@@ -3748,10 +4382,13 @@ function ReportDialog({
3748
4382
  },
3749
4383
  onPointerDown: (e) => e.stopPropagation(),
3750
4384
  onFocus: (e) => e.stopPropagation(),
3751
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
4385
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
3752
4386
  "div",
3753
4387
  {
3754
4388
  ref: modalRef,
4389
+ role: "dialog",
4390
+ "aria-modal": "true",
4391
+ "aria-label": "Report an issue",
3755
4392
  onClick: (e) => e.stopPropagation(),
3756
4393
  style: {
3757
4394
  position: "relative",
@@ -3770,7 +4407,7 @@ function ReportDialog({
3770
4407
  isolation: "isolate"
3771
4408
  },
3772
4409
  children: [
3773
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
4410
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
3774
4411
  "div",
3775
4412
  {
3776
4413
  style: {
@@ -3779,7 +4416,7 @@ function ReportDialog({
3779
4416
  borderBottom: `1px solid ${t2.border}`
3780
4417
  },
3781
4418
  children: [
3782
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
4419
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
3783
4420
  "div",
3784
4421
  {
3785
4422
  style: {
@@ -3788,7 +4425,7 @@ function ReportDialog({
3788
4425
  alignItems: "center"
3789
4426
  },
3790
4427
  children: [
3791
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
4428
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
3792
4429
  "div",
3793
4430
  {
3794
4431
  style: {
@@ -3797,7 +4434,7 @@ function ReportDialog({
3797
4434
  gap: "8px"
3798
4435
  },
3799
4436
  children: [
3800
- step > 1 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4437
+ step > 1 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3801
4438
  "button",
3802
4439
  {
3803
4440
  type: "button",
@@ -3811,14 +4448,14 @@ function ReportDialog({
3811
4448
  alignItems: "center"
3812
4449
  },
3813
4450
  "aria-label": "Back",
3814
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4451
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3815
4452
  "svg",
3816
4453
  {
3817
4454
  width: "16",
3818
4455
  height: "16",
3819
4456
  viewBox: "0 0 16 16",
3820
4457
  fill: "none",
3821
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4458
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3822
4459
  "path",
3823
4460
  {
3824
4461
  d: "M10 3L5 8L10 13",
@@ -3832,7 +4469,7 @@ function ReportDialog({
3832
4469
  )
3833
4470
  }
3834
4471
  ),
3835
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4472
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3836
4473
  "span",
3837
4474
  {
3838
4475
  style: {
@@ -3846,7 +4483,7 @@ function ReportDialog({
3846
4483
  ]
3847
4484
  }
3848
4485
  ),
3849
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
4486
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
3850
4487
  "div",
3851
4488
  {
3852
4489
  style: {
@@ -3855,7 +4492,7 @@ function ReportDialog({
3855
4492
  gap: "8px"
3856
4493
  },
3857
4494
  children: [
3858
- step === 1 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4495
+ step === 1 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3859
4496
  "span",
3860
4497
  {
3861
4498
  title: "Press this anywhere to open Glitchgrab",
@@ -3873,7 +4510,7 @@ function ReportDialog({
3873
4510
  children: getShortcutLabel()
3874
4511
  }
3875
4512
  ),
3876
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4513
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3877
4514
  "button",
3878
4515
  {
3879
4516
  type: "button",
@@ -3888,7 +4525,7 @@ function ReportDialog({
3888
4525
  justifyContent: "center"
3889
4526
  },
3890
4527
  "aria-label": "Close",
3891
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4528
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3892
4529
  "svg",
3893
4530
  {
3894
4531
  width: "16",
@@ -3896,7 +4533,7 @@ function ReportDialog({
3896
4533
  viewBox: "0 0 16 16",
3897
4534
  fill: "none",
3898
4535
  style: { display: "block" },
3899
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4536
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3900
4537
  "path",
3901
4538
  {
3902
4539
  d: "M4 4L12 12M12 4L4 12",
@@ -3915,7 +4552,7 @@ function ReportDialog({
3915
4552
  ]
3916
4553
  }
3917
4554
  ),
3918
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4555
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3919
4556
  "div",
3920
4557
  {
3921
4558
  style: {
@@ -3925,12 +4562,12 @@ function ReportDialog({
3925
4562
  gap: "0",
3926
4563
  marginTop: "12px"
3927
4564
  },
3928
- children: [1, 2].map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
4565
+ children: [1, 2].map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
3929
4566
  "div",
3930
4567
  {
3931
4568
  style: { display: "flex", alignItems: "center" },
3932
4569
  children: [
3933
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4570
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3934
4571
  "div",
3935
4572
  {
3936
4573
  style: {
@@ -3942,7 +4579,7 @@ function ReportDialog({
3942
4579
  }
3943
4580
  }
3944
4581
  ),
3945
- i < 1 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4582
+ i < 1 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3946
4583
  "div",
3947
4584
  {
3948
4585
  style: {
@@ -3962,7 +4599,7 @@ function ReportDialog({
3962
4599
  ]
3963
4600
  }
3964
4601
  ),
3965
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4602
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3966
4603
  "div",
3967
4604
  {
3968
4605
  style: {
@@ -3971,7 +4608,7 @@ function ReportDialog({
3971
4608
  flex: "1 1 auto",
3972
4609
  minHeight: 0
3973
4610
  },
3974
- children: submitted ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
4611
+ children: submitted ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
3975
4612
  "div",
3976
4613
  {
3977
4614
  style: {
@@ -3986,9 +4623,9 @@ function ReportDialog({
3986
4623
  " sent. Thank you!"
3987
4624
  ]
3988
4625
  }
3989
- ) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
3990
- step === 1 && headerSlot ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { marginBottom: "14px" }, children: headerSlot }) : null,
3991
- step === 1 && sendFeedback2 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
4626
+ ) : /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
4627
+ step === 1 && headerSlot ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { marginBottom: "14px" }, children: headerSlot }) : null,
4628
+ step === 1 && sendFeedback2 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
3992
4629
  "div",
3993
4630
  {
3994
4631
  style: {
@@ -4005,16 +4642,16 @@ function ReportDialog({
4005
4642
  },
4006
4643
  onMouseLeave: () => setHoveredStar(0),
4007
4644
  children: [
4008
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4645
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4009
4646
  "span",
4010
4647
  {
4011
4648
  style: { fontSize: "13px", fontWeight: 600, color: t2.text },
4012
4649
  children: "How are we doing?"
4013
4650
  }
4014
4651
  ),
4015
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { display: "flex", gap: "4px" }, children: [1, 2, 3, 4, 5].map((star) => {
4652
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { display: "flex", gap: "4px" }, children: [1, 2, 3, 4, 5].map((star) => {
4016
4653
  const filled = star <= (hoveredStar || rating);
4017
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4654
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4018
4655
  "button",
4019
4656
  {
4020
4657
  type: "button",
@@ -4035,7 +4672,7 @@ function ReportDialog({
4035
4672
  cursor: "pointer",
4036
4673
  lineHeight: 0
4037
4674
  },
4038
- 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)(
4675
+ 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)(
4039
4676
  "path",
4040
4677
  {
4041
4678
  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",
@@ -4052,7 +4689,7 @@ function ReportDialog({
4052
4689
  ]
4053
4690
  }
4054
4691
  ),
4055
- step === 1 && sendFeedback2 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
4692
+ step === 1 && sendFeedback2 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
4056
4693
  "div",
4057
4694
  {
4058
4695
  style: {
@@ -4062,13 +4699,13 @@ function ReportDialog({
4062
4699
  marginBottom: "16px"
4063
4700
  },
4064
4701
  children: [
4065
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { flex: 1, height: "1px", background: t2.border } }),
4066
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { fontSize: "11px", color: t2.textMuted }, children: "or report something" }),
4067
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { flex: 1, height: "1px", background: t2.border } })
4702
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { flex: 1, height: "1px", background: t2.border } }),
4703
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { fontSize: "11px", color: t2.textMuted }, children: "or report something" }),
4704
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { flex: 1, height: "1px", background: t2.border } })
4068
4705
  ]
4069
4706
  }
4070
4707
  ),
4071
- step === 1 && screenshots.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
4708
+ step === 1 && screenshots.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
4072
4709
  "div",
4073
4710
  {
4074
4711
  style: {
@@ -4082,7 +4719,7 @@ function ReportDialog({
4082
4719
  background: t2.inputBg
4083
4720
  },
4084
4721
  children: [
4085
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4722
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4086
4723
  "img",
4087
4724
  {
4088
4725
  src: screenshots[0],
@@ -4097,8 +4734,8 @@ function ReportDialog({
4097
4734
  }
4098
4735
  }
4099
4736
  ),
4100
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { fontSize: "11px", color: t2.textMuted, flex: 1 }, children: "Screenshot attached" }),
4101
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4737
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { fontSize: "11px", color: t2.textMuted, flex: 1 }, children: "Screenshot attached" }),
4738
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4102
4739
  "button",
4103
4740
  {
4104
4741
  type: "button",
@@ -4119,7 +4756,7 @@ function ReportDialog({
4119
4756
  ]
4120
4757
  }
4121
4758
  ),
4122
- step === 1 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4759
+ step === 1 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4123
4760
  "div",
4124
4761
  {
4125
4762
  ref: tileGridRef,
@@ -4129,7 +4766,7 @@ function ReportDialog({
4129
4766
  gridTemplateColumns: "1fr 1fr 1fr",
4130
4767
  gap: "10px"
4131
4768
  },
4132
- children: availableTypes.map((type) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
4769
+ children: availableTypes.map((type) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
4133
4770
  "button",
4134
4771
  {
4135
4772
  type: "button",
@@ -4160,7 +4797,7 @@ function ReportDialog({
4160
4797
  },
4161
4798
  children: [
4162
4799
  getTypeIcon(type, t2.accent, 22),
4163
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4800
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4164
4801
  "span",
4165
4802
  {
4166
4803
  style: {
@@ -4178,8 +4815,8 @@ function ReportDialog({
4178
4815
  ))
4179
4816
  }
4180
4817
  ),
4181
- step === 2 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
4182
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
4818
+ step === 2 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
4819
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
4183
4820
  "button",
4184
4821
  {
4185
4822
  type: "button",
@@ -4202,11 +4839,11 @@ function ReportDialog({
4202
4839
  children: [
4203
4840
  getTypeIcon(reportType, isRating ? "#f59e0b" : t2.accent, 13),
4204
4841
  getTypeLabel(reportType),
4205
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { color: t2.textMuted, fontWeight: 400 }, children: "\xB7 change" })
4842
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { color: t2.textMuted, fontWeight: 400 }, children: "\xB7 change" })
4206
4843
  ]
4207
4844
  }
4208
4845
  ),
4209
- isRating && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4846
+ isRating && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4210
4847
  "div",
4211
4848
  {
4212
4849
  style: {
@@ -4217,7 +4854,7 @@ function ReportDialog({
4217
4854
  onMouseLeave: () => setHoveredStar(0),
4218
4855
  children: [1, 2, 3, 4, 5].map((star) => {
4219
4856
  const filled = star <= (hoveredStar || rating);
4220
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4857
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4221
4858
  "button",
4222
4859
  {
4223
4860
  type: "button",
@@ -4234,7 +4871,7 @@ function ReportDialog({
4234
4871
  cursor: "pointer",
4235
4872
  lineHeight: 0
4236
4873
  },
4237
- 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)(
4874
+ 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)(
4238
4875
  "path",
4239
4876
  {
4240
4877
  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",
@@ -4250,7 +4887,7 @@ function ReportDialog({
4250
4887
  })
4251
4888
  }
4252
4889
  ),
4253
- assist && !isRating && !assistUsed && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
4890
+ assist && !isRating && !assistUsed && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
4254
4891
  "button",
4255
4892
  {
4256
4893
  type: "button",
@@ -4277,7 +4914,7 @@ function ReportDialog({
4277
4914
  },
4278
4915
  title: "Answer a question or two and the assistant writes the report for you",
4279
4916
  children: [
4280
- /* @__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)(
4917
+ /* @__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)(
4281
4918
  "path",
4282
4919
  {
4283
4920
  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",
@@ -4290,8 +4927,8 @@ function ReportDialog({
4290
4927
  ]
4291
4928
  }
4292
4929
  ),
4293
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { position: "relative" }, children: [
4294
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4930
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { position: "relative" }, children: [
4931
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4295
4932
  "textarea",
4296
4933
  {
4297
4934
  ref: textareaRef,
@@ -4333,7 +4970,7 @@ function ReportDialog({
4333
4970
  autoFocus: true
4334
4971
  }
4335
4972
  ),
4336
- (isListening || isTranscribing) && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
4973
+ (isListening || isTranscribing) && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
4337
4974
  "span",
4338
4975
  {
4339
4976
  style: {
@@ -4351,7 +4988,7 @@ function ReportDialog({
4351
4988
  zIndex: 1
4352
4989
  },
4353
4990
  children: [
4354
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4991
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4355
4992
  "span",
4356
4993
  {
4357
4994
  style: {
@@ -4366,7 +5003,7 @@ function ReportDialog({
4366
5003
  }
4367
5004
  ),
4368
5005
  isTranscribing ? "Transcribing\u2026" : "REC",
4369
- isListening && !isTranscribing && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5006
+ isListening && !isTranscribing && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4370
5007
  "span",
4371
5008
  {
4372
5009
  style: {
@@ -4393,7 +5030,7 @@ function ReportDialog({
4393
5030
  dur: "0.55s",
4394
5031
  delay: "0.15s"
4395
5032
  }
4396
- ].map((b, i) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5033
+ ].map((b, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4397
5034
  "span",
4398
5035
  {
4399
5036
  style: {
@@ -4413,7 +5050,7 @@ function ReportDialog({
4413
5050
  ]
4414
5051
  }
4415
5052
  ),
4416
- enhanceText2 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
5053
+ enhanceText2 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
4417
5054
  "button",
4418
5055
  {
4419
5056
  type: "button",
@@ -4458,7 +5095,7 @@ function ReportDialog({
4458
5095
  },
4459
5096
  title: "Polish grammar \u2014 preserves your meaning",
4460
5097
  children: [
4461
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5098
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4462
5099
  "svg",
4463
5100
  {
4464
5101
  width: "11",
@@ -4466,7 +5103,7 @@ function ReportDialog({
4466
5103
  viewBox: "0 0 24 24",
4467
5104
  fill: "none",
4468
5105
  "aria-hidden": "true",
4469
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5106
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4470
5107
  "path",
4471
5108
  {
4472
5109
  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",
@@ -4481,7 +5118,7 @@ function ReportDialog({
4481
5118
  ]
4482
5119
  }
4483
5120
  ),
4484
- transcribeAudio2 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5121
+ transcribeAudio2 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4485
5122
  "button",
4486
5123
  {
4487
5124
  type: "button",
@@ -4507,7 +5144,7 @@ function ReportDialog({
4507
5144
  padding: 0,
4508
5145
  transition: "border-color 0.2s ease, color 0.2s ease"
4509
5146
  },
4510
- children: isListening ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5147
+ children: isListening ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4511
5148
  "svg",
4512
5149
  {
4513
5150
  width: "12",
@@ -4515,7 +5152,7 @@ function ReportDialog({
4515
5152
  viewBox: "0 0 24 24",
4516
5153
  fill: "currentColor",
4517
5154
  "aria-hidden": "true",
4518
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5155
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4519
5156
  "rect",
4520
5157
  {
4521
5158
  x: "6",
@@ -4526,7 +5163,7 @@ function ReportDialog({
4526
5163
  }
4527
5164
  )
4528
5165
  }
4529
- ) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
5166
+ ) : /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
4530
5167
  "svg",
4531
5168
  {
4532
5169
  width: "13",
@@ -4535,7 +5172,7 @@ function ReportDialog({
4535
5172
  fill: "none",
4536
5173
  "aria-hidden": "true",
4537
5174
  children: [
4538
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5175
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4539
5176
  "rect",
4540
5177
  {
4541
5178
  x: "9",
@@ -4547,7 +5184,7 @@ function ReportDialog({
4547
5184
  strokeWidth: "1.8"
4548
5185
  }
4549
5186
  ),
4550
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5187
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4551
5188
  "path",
4552
5189
  {
4553
5190
  d: "M5 10a7 7 0 0014 0M12 19v3M9 22h6",
@@ -4562,7 +5199,7 @@ function ReportDialog({
4562
5199
  }
4563
5200
  )
4564
5201
  ] }),
4565
- transcribeAudio2 && !isListening && !isTranscribing && !voiceError && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
5202
+ transcribeAudio2 && !isListening && !isTranscribing && !voiceError && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
4566
5203
  "div",
4567
5204
  {
4568
5205
  style: {
@@ -4576,16 +5213,16 @@ function ReportDialog({
4576
5213
  userSelect: "none"
4577
5214
  },
4578
5215
  children: [
4579
- /* @__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: [
4580
- /* @__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" }),
4581
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M19 10v2a7 7 0 0 1-14 0v-2" }),
4582
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: "12", x2: "12", y1: "19", y2: "22" })
5216
+ /* @__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: [
5217
+ /* @__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" }),
5218
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M19 10v2a7 7 0 0 1-14 0v-2" }),
5219
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("line", { x1: "12", x2: "12", y1: "19", y2: "22" })
4583
5220
  ] }),
4584
5221
  "Hold Space to speak \u2014 we'll transcribe it"
4585
5222
  ]
4586
5223
  }
4587
5224
  ),
4588
- isEnhanced && originalDescription !== null && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
5225
+ isEnhanced && originalDescription !== null && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
4589
5226
  "div",
4590
5227
  {
4591
5228
  style: {
@@ -4599,7 +5236,7 @@ function ReportDialog({
4599
5236
  border: `1px solid ${t2.accent}40`
4600
5237
  },
4601
5238
  children: [
4602
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5239
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4603
5240
  "svg",
4604
5241
  {
4605
5242
  width: "12",
@@ -4607,7 +5244,7 @@ function ReportDialog({
4607
5244
  viewBox: "0 0 24 24",
4608
5245
  fill: "none",
4609
5246
  style: { flexShrink: 0 },
4610
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5247
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4611
5248
  "path",
4612
5249
  {
4613
5250
  d: "M12 3l1.5 4.5L18 9l-4.5 1.5L12 15l-1.5-4.5L6 9l4.5-1.5L12 3z",
@@ -4618,7 +5255,7 @@ function ReportDialog({
4618
5255
  )
4619
5256
  }
4620
5257
  ),
4621
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5258
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4622
5259
  "span",
4623
5260
  {
4624
5261
  style: {
@@ -4629,7 +5266,7 @@ function ReportDialog({
4629
5266
  children: "AI enhanced"
4630
5267
  }
4631
5268
  ),
4632
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5269
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4633
5270
  "button",
4634
5271
  {
4635
5272
  type: "button",
@@ -4651,7 +5288,7 @@ function ReportDialog({
4651
5288
  children: "Keep"
4652
5289
  }
4653
5290
  ),
4654
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5291
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4655
5292
  "button",
4656
5293
  {
4657
5294
  type: "button",
@@ -4677,7 +5314,7 @@ function ReportDialog({
4677
5314
  ]
4678
5315
  }
4679
5316
  ),
4680
- voiceError && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5317
+ voiceError && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4681
5318
  "p",
4682
5319
  {
4683
5320
  role: "alert",
@@ -4691,7 +5328,7 @@ function ReportDialog({
4691
5328
  children: voiceError
4692
5329
  }
4693
5330
  ),
4694
- assistNotice && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5331
+ assistNotice && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4695
5332
  "p",
4696
5333
  {
4697
5334
  style: {
@@ -4704,8 +5341,8 @@ function ReportDialog({
4704
5341
  children: assistNotice
4705
5342
  }
4706
5343
  ),
4707
- !isRating && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { marginTop: "10px" }, children: [
4708
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
5344
+ !isRating && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { marginTop: "10px" }, children: [
5345
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
4709
5346
  "span",
4710
5347
  {
4711
5348
  style: {
@@ -4721,7 +5358,7 @@ function ReportDialog({
4721
5358
  ]
4722
5359
  }
4723
5360
  ),
4724
- screenshots.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5361
+ (screenshots.length > 0 || shotPending) && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
4725
5362
  "div",
4726
5363
  {
4727
5364
  style: {
@@ -4730,132 +5367,159 @@ function ReportDialog({
4730
5367
  gap: "8px",
4731
5368
  marginBottom: "8px"
4732
5369
  },
4733
- children: screenshots.map((src, i) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
4734
- "div",
4735
- {
4736
- style: {
4737
- position: "relative",
4738
- width: "56px",
4739
- height: "56px"
4740
- },
4741
- children: [
4742
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4743
- "img",
4744
- {
4745
- src,
4746
- alt: `Screenshot ${i + 1}`,
4747
- onClick: () => openPreview(i),
4748
- style: {
4749
- width: "100%",
4750
- height: "100%",
4751
- borderRadius: "6px",
4752
- border: `1px solid ${t2.border}`,
4753
- objectFit: "cover",
4754
- objectPosition: "top",
4755
- cursor: "zoom-in",
4756
- display: "block"
4757
- }
4758
- }
4759
- ),
4760
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4761
- "button",
4762
- {
4763
- type: "button",
4764
- onClick: () => setScreenshots(
4765
- (prev) => prev.filter((_, idx) => idx !== i)
4766
- ),
4767
- "aria-label": `Remove screenshot ${i + 1}`,
4768
- style: {
4769
- position: "absolute",
4770
- top: "-6px",
4771
- right: "-6px",
4772
- width: "17px",
4773
- height: "17px",
4774
- borderRadius: "50%",
4775
- border: "none",
4776
- background: "#ef4444",
4777
- color: "#fff",
4778
- fontSize: "11px",
4779
- lineHeight: 1,
4780
- padding: 0,
4781
- cursor: "pointer",
4782
- display: "flex",
4783
- alignItems: "center",
4784
- justifyContent: "center"
4785
- },
4786
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4787
- "svg",
4788
- {
4789
- width: "8",
4790
- height: "8",
4791
- viewBox: "0 0 10 10",
4792
- fill: "none",
4793
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4794
- "path",
4795
- {
4796
- d: "M1 1L9 9M9 1L1 9",
4797
- stroke: "currentColor",
4798
- strokeWidth: "1.5",
4799
- strokeLinecap: "round"
4800
- }
4801
- )
4802
- }
4803
- )
4804
- }
4805
- ),
4806
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4807
- "button",
4808
- {
4809
- type: "button",
4810
- onClick: () => setAnnotatingIndex(i),
4811
- "aria-label": `Annotate screenshot ${i + 1}`,
4812
- title: "Annotate",
4813
- style: {
4814
- position: "absolute",
4815
- top: "50%",
4816
- left: "50%",
4817
- transform: "translate(-50%, -50%)",
4818
- width: "24px",
4819
- height: "24px",
4820
- borderRadius: "50%",
4821
- border: "none",
4822
- background: t2.accent,
4823
- color: t2.accentText,
4824
- padding: 0,
4825
- cursor: "pointer",
4826
- display: "flex",
4827
- alignItems: "center",
4828
- justifyContent: "center",
4829
- boxShadow: "0 2px 8px rgba(0,0,0,0.45)"
4830
- },
4831
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4832
- "svg",
4833
- {
4834
- width: "12",
4835
- height: "12",
4836
- viewBox: "0 0 24 24",
4837
- fill: "none",
4838
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4839
- "path",
4840
- {
4841
- d: "M12 20h9M16.5 3.5a2.12 2.12 0 013 3L7 19l-4 1 1-4L16.5 3.5z",
4842
- stroke: "currentColor",
4843
- strokeWidth: "2.2",
4844
- strokeLinecap: "round",
4845
- strokeLinejoin: "round"
4846
- }
4847
- )
5370
+ children: [
5371
+ shotPending && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5372
+ "div",
5373
+ {
5374
+ role: "status",
5375
+ "aria-label": "Capturing screenshot",
5376
+ title: "Capturing a screenshot of the page\u2026",
5377
+ style: {
5378
+ width: "56px",
5379
+ height: "56px",
5380
+ borderRadius: "6px",
5381
+ border: `1px dashed ${t2.border}`,
5382
+ display: "flex",
5383
+ alignItems: "center",
5384
+ justifyContent: "center",
5385
+ padding: "4px",
5386
+ boxSizing: "border-box",
5387
+ fontSize: "9px",
5388
+ lineHeight: 1.2,
5389
+ textAlign: "center",
5390
+ color: t2.textMuted,
5391
+ animation: "gg-pulse 1.4s ease-in-out infinite"
5392
+ },
5393
+ children: "Capturing\u2026"
5394
+ }
5395
+ ),
5396
+ screenshots.map((src, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
5397
+ "div",
5398
+ {
5399
+ style: {
5400
+ position: "relative",
5401
+ width: "56px",
5402
+ height: "56px"
5403
+ },
5404
+ children: [
5405
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5406
+ "img",
5407
+ {
5408
+ src,
5409
+ alt: `Screenshot ${i + 1}`,
5410
+ onClick: () => openPreview(i),
5411
+ style: {
5412
+ width: "100%",
5413
+ height: "100%",
5414
+ borderRadius: "6px",
5415
+ border: `1px solid ${t2.border}`,
5416
+ objectFit: "cover",
5417
+ objectPosition: "top",
5418
+ cursor: "zoom-in",
5419
+ display: "block"
4848
5420
  }
4849
- )
4850
- }
4851
- )
4852
- ]
4853
- },
4854
- i
4855
- ))
5421
+ }
5422
+ ),
5423
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5424
+ "button",
5425
+ {
5426
+ type: "button",
5427
+ onClick: () => setScreenshots(
5428
+ (prev) => prev.filter((_, idx) => idx !== i)
5429
+ ),
5430
+ "aria-label": `Remove screenshot ${i + 1}`,
5431
+ style: {
5432
+ position: "absolute",
5433
+ top: "-6px",
5434
+ right: "-6px",
5435
+ width: "17px",
5436
+ height: "17px",
5437
+ borderRadius: "50%",
5438
+ border: "none",
5439
+ background: "#ef4444",
5440
+ color: "#fff",
5441
+ fontSize: "11px",
5442
+ lineHeight: 1,
5443
+ padding: 0,
5444
+ cursor: "pointer",
5445
+ display: "flex",
5446
+ alignItems: "center",
5447
+ justifyContent: "center"
5448
+ },
5449
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5450
+ "svg",
5451
+ {
5452
+ width: "8",
5453
+ height: "8",
5454
+ viewBox: "0 0 10 10",
5455
+ fill: "none",
5456
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5457
+ "path",
5458
+ {
5459
+ d: "M1 1L9 9M9 1L1 9",
5460
+ stroke: "currentColor",
5461
+ strokeWidth: "1.5",
5462
+ strokeLinecap: "round"
5463
+ }
5464
+ )
5465
+ }
5466
+ )
5467
+ }
5468
+ ),
5469
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5470
+ "button",
5471
+ {
5472
+ type: "button",
5473
+ onClick: () => setAnnotatingIndex(i),
5474
+ "aria-label": `Annotate screenshot ${i + 1}`,
5475
+ title: "Annotate",
5476
+ style: {
5477
+ position: "absolute",
5478
+ top: "50%",
5479
+ left: "50%",
5480
+ transform: "translate(-50%, -50%)",
5481
+ width: "24px",
5482
+ height: "24px",
5483
+ borderRadius: "50%",
5484
+ border: "none",
5485
+ background: t2.accent,
5486
+ color: t2.accentText,
5487
+ padding: 0,
5488
+ cursor: "pointer",
5489
+ display: "flex",
5490
+ alignItems: "center",
5491
+ justifyContent: "center",
5492
+ boxShadow: "0 2px 8px rgba(0,0,0,0.45)"
5493
+ },
5494
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5495
+ "svg",
5496
+ {
5497
+ width: "12",
5498
+ height: "12",
5499
+ viewBox: "0 0 24 24",
5500
+ fill: "none",
5501
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5502
+ "path",
5503
+ {
5504
+ d: "M12 20h9M16.5 3.5a2.12 2.12 0 013 3L7 19l-4 1 1-4L16.5 3.5z",
5505
+ stroke: "currentColor",
5506
+ strokeWidth: "2.2",
5507
+ strokeLinecap: "round",
5508
+ strokeLinejoin: "round"
5509
+ }
5510
+ )
5511
+ }
5512
+ )
5513
+ }
5514
+ )
5515
+ ]
5516
+ },
5517
+ i
5518
+ ))
5519
+ ]
4856
5520
  }
4857
5521
  ),
4858
- attachments.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5522
+ attachments.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4859
5523
  "div",
4860
5524
  {
4861
5525
  style: {
@@ -4864,7 +5528,7 @@ function ReportDialog({
4864
5528
  gap: "6px",
4865
5529
  marginBottom: "8px"
4866
5530
  },
4867
- children: attachments.map((a, i) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
5531
+ children: attachments.map((a, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
4868
5532
  "div",
4869
5533
  {
4870
5534
  style: {
@@ -4876,7 +5540,7 @@ function ReportDialog({
4876
5540
  border: `1px solid ${t2.border}`
4877
5541
  },
4878
5542
  children: [
4879
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
5543
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
4880
5544
  "svg",
4881
5545
  {
4882
5546
  width: "14",
@@ -4885,7 +5549,7 @@ function ReportDialog({
4885
5549
  fill: "none",
4886
5550
  style: { flexShrink: 0, opacity: 0.6 },
4887
5551
  children: [
4888
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5552
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4889
5553
  "path",
4890
5554
  {
4891
5555
  d: "M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z",
@@ -4895,7 +5559,7 @@ function ReportDialog({
4895
5559
  strokeLinejoin: "round"
4896
5560
  }
4897
5561
  ),
4898
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5562
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4899
5563
  "path",
4900
5564
  {
4901
5565
  d: "M14 2v6h6",
@@ -4908,7 +5572,7 @@ function ReportDialog({
4908
5572
  ]
4909
5573
  }
4910
5574
  ),
4911
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5575
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4912
5576
  "span",
4913
5577
  {
4914
5578
  style: {
@@ -4922,7 +5586,7 @@ function ReportDialog({
4922
5586
  children: a.name
4923
5587
  }
4924
5588
  ),
4925
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
5589
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
4926
5590
  "span",
4927
5591
  {
4928
5592
  style: { fontSize: "10px", color: t2.textMuted },
@@ -4932,7 +5596,7 @@ function ReportDialog({
4932
5596
  ]
4933
5597
  }
4934
5598
  ),
4935
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5599
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4936
5600
  "button",
4937
5601
  {
4938
5602
  type: "button",
@@ -4956,14 +5620,14 @@ function ReportDialog({
4956
5620
  justifyContent: "center",
4957
5621
  flexShrink: 0
4958
5622
  },
4959
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5623
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4960
5624
  "svg",
4961
5625
  {
4962
5626
  width: "8",
4963
5627
  height: "8",
4964
5628
  viewBox: "0 0 10 10",
4965
5629
  fill: "none",
4966
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5630
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4967
5631
  "path",
4968
5632
  {
4969
5633
  d: "M1 1L9 9M9 1L1 9",
@@ -4982,7 +5646,7 @@ function ReportDialog({
4982
5646
  ))
4983
5647
  }
4984
5648
  ),
4985
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5649
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4986
5650
  "div",
4987
5651
  {
4988
5652
  onClick: () => {
@@ -5008,7 +5672,7 @@ function ReportDialog({
5008
5672
  background: screenshotDragOver ? isDark ? "rgba(34,211,238,0.06)" : "rgba(8,145,178,0.04)" : "transparent",
5009
5673
  transition: "border-color 0.15s ease, background 0.15s ease"
5010
5674
  },
5011
- children: screenshots.length + attachments.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5675
+ children: screenshots.length + attachments.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5012
5676
  "p",
5013
5677
  {
5014
5678
  style: {
@@ -5019,8 +5683,8 @@ function ReportDialog({
5019
5683
  },
5020
5684
  children: screenshotDragOver ? "Drop to attach" : "+ Add more \xB7 Drag & drop \xB7 Paste"
5021
5685
  }
5022
- ) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
5023
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
5686
+ ) : /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
5687
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
5024
5688
  "svg",
5025
5689
  {
5026
5690
  width: "22",
@@ -5033,7 +5697,7 @@ function ReportDialog({
5033
5697
  opacity: 0.45
5034
5698
  },
5035
5699
  children: [
5036
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5700
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5037
5701
  "rect",
5038
5702
  {
5039
5703
  x: "3",
@@ -5045,7 +5709,7 @@ function ReportDialog({
5045
5709
  strokeWidth: "1.5"
5046
5710
  }
5047
5711
  ),
5048
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5712
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5049
5713
  "circle",
5050
5714
  {
5051
5715
  cx: "8.5",
@@ -5055,7 +5719,7 @@ function ReportDialog({
5055
5719
  strokeWidth: "1.5"
5056
5720
  }
5057
5721
  ),
5058
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5722
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5059
5723
  "path",
5060
5724
  {
5061
5725
  d: "M21 15l-5-5L5 21",
@@ -5068,7 +5732,7 @@ function ReportDialog({
5068
5732
  ]
5069
5733
  }
5070
5734
  ),
5071
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5735
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5072
5736
  "p",
5073
5737
  {
5074
5738
  style: {
@@ -5080,7 +5744,7 @@ function ReportDialog({
5080
5744
  children: screenshotDragOver ? "Drop to attach" : "Add screenshots or files"
5081
5745
  }
5082
5746
  ),
5083
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5747
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5084
5748
  "p",
5085
5749
  {
5086
5750
  style: {
@@ -5095,8 +5759,8 @@ function ReportDialog({
5095
5759
  }
5096
5760
  )
5097
5761
  ] }),
5098
- showSeverity && reportType === "BUG" && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { marginTop: "10px" }, children: [
5099
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
5762
+ showSeverity && reportType === "BUG" && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { marginTop: "10px" }, children: [
5763
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
5100
5764
  "span",
5101
5765
  {
5102
5766
  style: {
@@ -5109,7 +5773,7 @@ function ReportDialog({
5109
5773
  },
5110
5774
  children: [
5111
5775
  "How bad is it?",
5112
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5776
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5113
5777
  "span",
5114
5778
  {
5115
5779
  style: {
@@ -5122,7 +5786,7 @@ function ReportDialog({
5122
5786
  ]
5123
5787
  }
5124
5788
  ),
5125
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5789
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5126
5790
  "div",
5127
5791
  {
5128
5792
  role: "radiogroup",
@@ -5130,7 +5794,7 @@ function ReportDialog({
5130
5794
  style: { display: "flex", gap: "6px" },
5131
5795
  children: SEVERITY_LEVELS.map((s) => {
5132
5796
  const on = severity === s;
5133
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5797
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5134
5798
  "button",
5135
5799
  {
5136
5800
  type: "button",
@@ -5171,7 +5835,7 @@ function ReportDialog({
5171
5835
  }
5172
5836
  )
5173
5837
  ] }),
5174
- validationError && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5838
+ validationError && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5175
5839
  "p",
5176
5840
  {
5177
5841
  style: {
@@ -5182,7 +5846,7 @@ function ReportDialog({
5182
5846
  children: validationError
5183
5847
  }
5184
5848
  ),
5185
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5849
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5186
5850
  "button",
5187
5851
  {
5188
5852
  type: "button",
@@ -5202,14 +5866,14 @@ function ReportDialog({
5202
5866
  fontFamily: "inherit",
5203
5867
  transition: "background-color 0.15s ease"
5204
5868
  },
5205
- children: isSubmitting ? "Sending..." : isRating ? "Send Rating" : "Send Report"
5869
+ children: isSubmitting ? shotPending && !isRating ? "Attaching screenshot\u2026" : "Sending..." : isRating ? "Send Rating" : "Send Report"
5206
5870
  }
5207
5871
  )
5208
5872
  ] })
5209
5873
  ] })
5210
5874
  }
5211
5875
  ),
5212
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
5876
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
5213
5877
  "div",
5214
5878
  {
5215
5879
  style: {
@@ -5221,8 +5885,35 @@ function ReportDialog({
5221
5885
  gap: "12px"
5222
5886
  },
5223
5887
  children: [
5224
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ReporterChip, { reporter, t: t2 }),
5225
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5888
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ReporterChip, { reporter, t: t2 }),
5889
+ reportGlitchgrabProblem ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5890
+ "button",
5891
+ {
5892
+ type: "button",
5893
+ "data-gg-self-report": "",
5894
+ onClick: () => {
5895
+ setSelfReportPrefill("");
5896
+ setSelfReportFrom("form");
5897
+ },
5898
+ title: "Something wrong with this reporter itself? Tell the Glitchgrab team.",
5899
+ style: {
5900
+ fontSize: "11px",
5901
+ color: t2.textMuted,
5902
+ whiteSpace: "nowrap",
5903
+ flexShrink: 0,
5904
+ border: "none",
5905
+ background: "transparent",
5906
+ fontFamily: "inherit",
5907
+ cursor: "pointer",
5908
+ textDecoration: "underline",
5909
+ textUnderlineOffset: "2px",
5910
+ // A ~30px tap target without growing the footer.
5911
+ padding: "9px 0",
5912
+ margin: "-9px 0"
5913
+ },
5914
+ children: "Problem with Glitchgrab?"
5915
+ }
5916
+ ) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5226
5917
  "span",
5227
5918
  {
5228
5919
  style: {
@@ -5244,7 +5935,7 @@ function ReportDialog({
5244
5935
  ),
5245
5936
  document.body
5246
5937
  ),
5247
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5938
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5248
5939
  "input",
5249
5940
  {
5250
5941
  ref: fileInputRef,
@@ -5256,7 +5947,7 @@ function ReportDialog({
5256
5947
  }
5257
5948
  ),
5258
5949
  isOpen && previewIndex !== null && screenshots[previewIndex] && (0, import_react_dom.createPortal)(
5259
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
5950
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
5260
5951
  "div",
5261
5952
  {
5262
5953
  "data-glitchgrab-layer": "",
@@ -5278,7 +5969,7 @@ function ReportDialog({
5278
5969
  },
5279
5970
  onMouseDown: (e) => e.stopPropagation(),
5280
5971
  children: [
5281
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
5972
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
5282
5973
  "div",
5283
5974
  {
5284
5975
  ref: previewImgRef,
@@ -5290,7 +5981,7 @@ function ReportDialog({
5290
5981
  },
5291
5982
  onClick: (e) => e.stopPropagation(),
5292
5983
  children: [
5293
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
5984
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5294
5985
  "div",
5295
5986
  {
5296
5987
  ref: previewScrollRef,
@@ -5309,7 +6000,7 @@ function ReportDialog({
5309
6000
  touchAction: "none",
5310
6001
  cursor: "grab"
5311
6002
  } : { maxWidth: "100%", lineHeight: 0 },
5312
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
6003
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5313
6004
  "img",
5314
6005
  {
5315
6006
  ref: previewImgElRef,
@@ -5337,7 +6028,7 @@ function ReportDialog({
5337
6028
  )
5338
6029
  }
5339
6030
  ),
5340
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
6031
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
5341
6032
  "button",
5342
6033
  {
5343
6034
  type: "button",
@@ -5365,7 +6056,7 @@ function ReportDialog({
5365
6056
  animation: "gg-pulse 2s ease-in-out infinite"
5366
6057
  },
5367
6058
  children: [
5368
- /* @__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)(
6059
+ /* @__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)(
5369
6060
  "path",
5370
6061
  {
5371
6062
  d: "M12 20h9M16.5 3.5a2.12 2.12 0 013 3L7 19l-4 1 1-4L16.5 3.5z",
@@ -5382,7 +6073,7 @@ function ReportDialog({
5382
6073
  ]
5383
6074
  }
5384
6075
  ),
5385
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
6076
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5386
6077
  "div",
5387
6078
  {
5388
6079
  style: {
@@ -5391,7 +6082,7 @@ function ReportDialog({
5391
6082
  alignItems: "center",
5392
6083
  gap: "12px"
5393
6084
  },
5394
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
6085
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5395
6086
  "span",
5396
6087
  {
5397
6088
  style: {
@@ -5413,7 +6104,7 @@ function ReportDialog({
5413
6104
  document.body
5414
6105
  ),
5415
6106
  isOpen && annotatingIndex !== null && screenshots[annotatingIndex] && (0, import_react_dom.createPortal)(
5416
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
6107
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5417
6108
  AnnotationCanvas,
5418
6109
  {
5419
6110
  imageSrc: screenshots[annotatingIndex],
@@ -5429,24 +6120,28 @@ function ReportDialog({
5429
6120
  ),
5430
6121
  document.body
5431
6122
  ),
5432
- isOpen && sheetUp && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
6123
+ isOpen && selfReportFrom && reportGlitchgrabProblem && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
6124
+ GlitchgrabProblemPanel,
6125
+ {
6126
+ send: reportGlitchgrabProblem,
6127
+ theme: layerTheme,
6128
+ surface: selfReportFrom,
6129
+ conversationId: aiConversationId,
6130
+ reportType,
6131
+ screenshots,
6132
+ initialText: selfReportPrefill,
6133
+ onClose: () => setSelfReportFrom(null)
6134
+ }
6135
+ ),
6136
+ isOpen && sheetUp && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5433
6137
  AssistSheet,
5434
6138
  {
5435
6139
  assist,
5436
- theme: {
5437
- bg: t2.bg,
5438
- bgSecondary: t2.bgSecondary,
5439
- border: t2.border,
5440
- text: t2.text,
5441
- textMuted: t2.textMuted,
5442
- inputBg: t2.inputBg,
5443
- inputBorder: t2.inputBorder,
5444
- accent: t2.accent,
5445
- accentText: t2.accentText
5446
- },
6140
+ theme: layerTheme,
5447
6141
  screenshots,
5448
6142
  pageShot,
5449
6143
  onAddImages: addFiles,
6144
+ onRemoveImage: (index) => setScreenshots((prev) => prev.filter((_, i) => i !== index)),
5450
6145
  attachmentCount: screenshots.length + attachments.length,
5451
6146
  context: { ...assistContext != null ? assistContext : {}, reportType },
5452
6147
  reportTypeLabel: getTypeLabel(reportType),
@@ -5484,6 +6179,10 @@ function ReportDialog({
5484
6179
  isSubmitting,
5485
6180
  submitted,
5486
6181
  onSend: () => void handleSubmit(),
6182
+ onReportGlitchgrabProblem: reportGlitchgrabProblem ? (prefill) => {
6183
+ setSelfReportPrefill(prefill != null ? prefill : "");
6184
+ setSelfReportFrom("assist");
6185
+ } : void 0,
5487
6186
  onDegrade: (message) => {
5488
6187
  setAssistOpen(false);
5489
6188
  setAssistUsed(true);
@@ -5530,7 +6229,7 @@ function getTypeIcon(type, color, size = 24) {
5530
6229
  };
5531
6230
  switch (type) {
5532
6231
  case "RATING":
5533
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("svg", { ...props, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
6232
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("svg", { ...props, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5534
6233
  "path",
5535
6234
  {
5536
6235
  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",
@@ -5540,7 +6239,7 @@ function getTypeIcon(type, color, size = 24) {
5540
6239
  }
5541
6240
  ) });
5542
6241
  case "BUG":
5543
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("svg", { ...props, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
6242
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("svg", { ...props, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5544
6243
  "path",
5545
6244
  {
5546
6245
  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",
@@ -5550,7 +6249,7 @@ function getTypeIcon(type, color, size = 24) {
5550
6249
  }
5551
6250
  ) });
5552
6251
  case "FEATURE_REQUEST":
5553
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("svg", { ...props, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
6252
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("svg", { ...props, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5554
6253
  "path",
5555
6254
  {
5556
6255
  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",
@@ -5560,8 +6259,8 @@ function getTypeIcon(type, color, size = 24) {
5560
6259
  }
5561
6260
  ) });
5562
6261
  case "UI_IMPROVEMENT":
5563
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("svg", { ...props, children: [
5564
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
6262
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { ...props, children: [
6263
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5565
6264
  "rect",
5566
6265
  {
5567
6266
  x: "3",
@@ -5573,7 +6272,7 @@ function getTypeIcon(type, color, size = 24) {
5573
6272
  strokeWidth: "1.5"
5574
6273
  }
5575
6274
  ),
5576
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
6275
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5577
6276
  "path",
5578
6277
  {
5579
6278
  d: "M3 9h18M9 21V9",
@@ -5584,7 +6283,7 @@ function getTypeIcon(type, color, size = 24) {
5584
6283
  )
5585
6284
  ] });
5586
6285
  case "PERFORMANCE":
5587
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("svg", { ...props, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
6286
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("svg", { ...props, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5588
6287
  "path",
5589
6288
  {
5590
6289
  d: "M12 21a9 9 0 100-18 9 9 0 000 18zM12 12l4-4M12 7v.01",
@@ -5595,7 +6294,7 @@ function getTypeIcon(type, color, size = 24) {
5595
6294
  }
5596
6295
  ) });
5597
6296
  case "SECURITY":
5598
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("svg", { ...props, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
6297
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("svg", { ...props, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5599
6298
  "path",
5600
6299
  {
5601
6300
  d: "M12 3l7 3v5c0 4.5-3 8.5-7 10-4-1.5-7-5.5-7-10V6l7-3z",
@@ -5605,9 +6304,9 @@ function getTypeIcon(type, color, size = 24) {
5605
6304
  }
5606
6305
  ) });
5607
6306
  case "QUESTION":
5608
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("svg", { ...props, children: [
5609
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: "12", cy: "12", r: "10", stroke: color, strokeWidth: "1.5" }),
5610
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
6307
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { ...props, children: [
6308
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: "12", cy: "12", r: "10", stroke: color, strokeWidth: "1.5" }),
6309
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5611
6310
  "path",
5612
6311
  {
5613
6312
  d: "M9.09 9a3 3 0 015.83 1c0 2-3 3-3 3M12 17h.01",
@@ -5619,7 +6318,7 @@ function getTypeIcon(type, color, size = 24) {
5619
6318
  )
5620
6319
  ] });
5621
6320
  case "OTHER":
5622
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("svg", { ...props, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
6321
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("svg", { ...props, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
5623
6322
  "path",
5624
6323
  {
5625
6324
  d: "M21 15a2 2 0 01-2 2H7l-4 4V5a2 2 0 012-2h14a2 2 0 012 2v10z",
@@ -5632,8 +6331,8 @@ function getTypeIcon(type, color, size = 24) {
5632
6331
  }
5633
6332
 
5634
6333
  // src/booking-dialog.tsx
5635
- var import_react4 = require("react");
5636
- var import_jsx_runtime4 = require("react/jsx-runtime");
6334
+ var import_react5 = require("react");
6335
+ var import_jsx_runtime5 = require("react/jsx-runtime");
5637
6336
  var t = {
5638
6337
  bg: "#141414",
5639
6338
  surface: "#1c1c1c",
@@ -5670,28 +6369,28 @@ function BookingDialog({
5670
6369
  onClose
5671
6370
  }) {
5672
6371
  var _a;
5673
- const [step, setStep] = (0, import_react4.useState)("slots");
5674
- const [slots, setSlots] = (0, import_react4.useState)(null);
5675
- const [heading, setHeading] = (0, import_react4.useState)({
6372
+ const [step, setStep] = (0, import_react5.useState)("slots");
6373
+ const [slots, setSlots] = (0, import_react5.useState)(null);
6374
+ const [heading, setHeading] = (0, import_react5.useState)({
5676
6375
  title: "Book a demo",
5677
6376
  description: null
5678
6377
  });
5679
- const [whatsappUrl, setWhatsappUrl] = (0, import_react4.useState)(null);
5680
- const [selected, setSelected] = (0, import_react4.useState)(null);
5681
- const [name, setName] = (0, import_react4.useState)("");
5682
- const [email, setEmail] = (0, import_react4.useState)("");
5683
- const [phone, setPhone] = (0, import_react4.useState)("");
5684
- const [note, setNote] = (0, import_react4.useState)("");
5685
- const [code, setCode] = (0, import_react4.useState)("");
5686
- const [bookingId, setBookingId] = (0, import_react4.useState)(null);
5687
- const [meetUrl, setMeetUrl] = (0, import_react4.useState)(null);
5688
- const [busy, setBusy] = (0, import_react4.useState)(false);
5689
- const [error, setError] = (0, import_react4.useState)(null);
5690
- const headers = (0, import_react4.useMemo)(
6378
+ const [whatsappUrl, setWhatsappUrl] = (0, import_react5.useState)(null);
6379
+ const [selected, setSelected] = (0, import_react5.useState)(null);
6380
+ const [name, setName] = (0, import_react5.useState)("");
6381
+ const [email, setEmail] = (0, import_react5.useState)("");
6382
+ const [phone, setPhone] = (0, import_react5.useState)("");
6383
+ const [note, setNote] = (0, import_react5.useState)("");
6384
+ const [code, setCode] = (0, import_react5.useState)("");
6385
+ const [bookingId, setBookingId] = (0, import_react5.useState)(null);
6386
+ const [meetUrl, setMeetUrl] = (0, import_react5.useState)(null);
6387
+ const [busy, setBusy] = (0, import_react5.useState)(false);
6388
+ const [error, setError] = (0, import_react5.useState)(null);
6389
+ const headers = (0, import_react5.useMemo)(
5691
6390
  () => ({ Authorization: `Bearer ${token}`, "Content-Type": "application/json" }),
5692
6391
  [token]
5693
6392
  );
5694
- const loadSlots = (0, import_react4.useCallback)(async () => {
6393
+ const loadSlots = (0, import_react5.useCallback)(async () => {
5695
6394
  var _a2, _b, _c;
5696
6395
  setError(null);
5697
6396
  setSlots(null);
@@ -5716,7 +6415,7 @@ function BookingDialog({
5716
6415
  setSlots([]);
5717
6416
  }
5718
6417
  }, [baseUrl, headers]);
5719
- (0, import_react4.useEffect)(() => {
6418
+ (0, import_react5.useEffect)(() => {
5720
6419
  if (!open) return;
5721
6420
  setStep("slots");
5722
6421
  setSelected(null);
@@ -5725,7 +6424,7 @@ function BookingDialog({
5725
6424
  setError(null);
5726
6425
  void loadSlots();
5727
6426
  }, [open, loadSlots]);
5728
- (0, import_react4.useEffect)(() => {
6427
+ (0, import_react5.useEffect)(() => {
5729
6428
  if (!open) return;
5730
6429
  const onKey = (e) => {
5731
6430
  if (e.key === "Escape") onClose();
@@ -5795,7 +6494,7 @@ function BookingDialog({
5795
6494
  }
5796
6495
  }
5797
6496
  const canSubmitDetails = name.trim() && email.includes("@") && phone.replace(/\D/g, "").length >= 8;
5798
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
6497
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
5799
6498
  "div",
5800
6499
  {
5801
6500
  onClick: (e) => {
@@ -5812,7 +6511,7 @@ function BookingDialog({
5812
6511
  padding: 16,
5813
6512
  fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif"
5814
6513
  },
5815
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
6514
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
5816
6515
  "div",
5817
6516
  {
5818
6517
  role: "dialog",
@@ -5830,7 +6529,7 @@ function BookingDialog({
5830
6529
  overflow: "hidden"
5831
6530
  },
5832
6531
  children: [
5833
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
6532
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
5834
6533
  "div",
5835
6534
  {
5836
6535
  style: {
@@ -5842,11 +6541,11 @@ function BookingDialog({
5842
6541
  gap: 12
5843
6542
  },
5844
6543
  children: [
5845
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { children: [
5846
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { fontSize: 15, fontWeight: 600 }, children: heading.title }),
5847
- heading.description && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { fontSize: 12, color: t.muted, marginTop: 2 }, children: heading.description })
6544
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
6545
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: { fontSize: 15, fontWeight: 600 }, children: heading.title }),
6546
+ heading.description && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: { fontSize: 12, color: t.muted, marginTop: 2 }, children: heading.description })
5848
6547
  ] }),
5849
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
6548
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
5850
6549
  "button",
5851
6550
  {
5852
6551
  onClick: onClose,
@@ -5858,10 +6557,10 @@ function BookingDialog({
5858
6557
  ]
5859
6558
  }
5860
6559
  ),
5861
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { padding: 18, overflowY: "auto", flex: "1 1 auto" }, children: [
5862
- error && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { color: t.danger, fontSize: 12, marginBottom: 12, lineHeight: 1.5 }, children: error }),
5863
- step === "slots" && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
5864
- whatsappUrl && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
6560
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { padding: 18, overflowY: "auto", flex: "1 1 auto" }, children: [
6561
+ error && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: { color: t.danger, fontSize: 12, marginBottom: 12, lineHeight: 1.5 }, children: error }),
6562
+ step === "slots" && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
6563
+ whatsappUrl && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
5865
6564
  "a",
5866
6565
  {
5867
6566
  href: whatsappUrl,
@@ -5880,15 +6579,15 @@ function BookingDialog({
5880
6579
  },
5881
6580
  children: [
5882
6581
  "\u{1F4AC} Rather book on WhatsApp?",
5883
- /* @__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." })
6582
+ /* @__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." })
5884
6583
  ]
5885
6584
  }
5886
6585
  ),
5887
- slots === null && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { color: t.muted, fontSize: 13 }, children: "Finding times\u2026" }),
5888
- (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." }),
5889
- [...byDay.entries()].map(([day, daySlots]) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { marginBottom: 18 }, children: [
5890
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { fontSize: 11, color: t.muted, marginBottom: 8, textTransform: "uppercase", letterSpacing: ".6px" }, children: formatDay(daySlots[0].startsAt) }),
5891
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { display: "flex", flexWrap: "wrap", gap: 8 }, children: daySlots.map((slot) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
6586
+ slots === null && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: { color: t.muted, fontSize: 13 }, children: "Finding times\u2026" }),
6587
+ (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." }),
6588
+ [...byDay.entries()].map(([day, daySlots]) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { marginBottom: 18 }, children: [
6589
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: { fontSize: 11, color: t.muted, marginBottom: 8, textTransform: "uppercase", letterSpacing: ".6px" }, children: formatDay(daySlots[0].startsAt) }),
6590
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: { display: "flex", flexWrap: "wrap", gap: 8 }, children: daySlots.map((slot) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
5892
6591
  "button",
5893
6592
  {
5894
6593
  onClick: () => {
@@ -5910,13 +6609,13 @@ function BookingDialog({
5910
6609
  )) })
5911
6610
  ] }, day))
5912
6611
  ] }),
5913
- step === "details" && selected && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 10 }, children: [
5914
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { fontSize: 13, color: t.muted }, children: [
6612
+ step === "details" && selected && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 10 }, children: [
6613
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { fontSize: 13, color: t.muted }, children: [
5915
6614
  formatDay(selected.startsAt),
5916
6615
  " \xB7 ",
5917
6616
  formatTime(selected.startsAt),
5918
6617
  " \xB7 ",
5919
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
6618
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
5920
6619
  "button",
5921
6620
  {
5922
6621
  onClick: () => setStep("slots"),
@@ -5925,9 +6624,9 @@ function BookingDialog({
5925
6624
  }
5926
6625
  )
5927
6626
  ] }),
5928
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Field, { label: "Your name", value: name, onChange: setName }),
5929
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Field, { label: "Email", value: email, onChange: setEmail, type: "email" }),
5930
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
6627
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Field, { label: "Your name", value: name, onChange: setName }),
6628
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Field, { label: "Email", value: email, onChange: setEmail, type: "email" }),
6629
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
5931
6630
  Field,
5932
6631
  {
5933
6632
  label: "Mobile number",
@@ -5937,8 +6636,8 @@ function BookingDialog({
5937
6636
  hint: "We send a code here to confirm, then a reminder before the call."
5938
6637
  }
5939
6638
  ),
5940
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Field, { label: "Anything we should know? (optional)", value: note, onChange: setNote }),
5941
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
6639
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Field, { label: "Anything we should know? (optional)", value: note, onChange: setNote }),
6640
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
5942
6641
  "button",
5943
6642
  {
5944
6643
  onClick: startBooking,
@@ -5948,10 +6647,10 @@ function BookingDialog({
5948
6647
  }
5949
6648
  )
5950
6649
  ] }),
5951
- step === "code" && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 10 }, children: [
5952
- /* @__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." }),
5953
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Field, { label: "Code", value: code, onChange: setCode }),
5954
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
6650
+ step === "code" && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 10 }, children: [
6651
+ /* @__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." }),
6652
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Field, { label: "Code", value: code, onChange: setCode }),
6653
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
5955
6654
  "button",
5956
6655
  {
5957
6656
  onClick: confirmBooking,
@@ -5961,15 +6660,15 @@ function BookingDialog({
5961
6660
  }
5962
6661
  )
5963
6662
  ] }),
5964
- step === "done" && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 10 }, children: [
5965
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { fontSize: 15, fontWeight: 600 }, children: "You're booked." }),
5966
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { fontSize: 13, color: t.muted, lineHeight: 1.6 }, children: [
6663
+ step === "done" && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 10 }, children: [
6664
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: { fontSize: 15, fontWeight: 600 }, children: "You're booked." }),
6665
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { fontSize: 13, color: t.muted, lineHeight: 1.6 }, children: [
5967
6666
  selected && `${formatDay(selected.startsAt)} at ${formatTime(selected.startsAt)}. `,
5968
6667
  "A calendar invite is on its way to ",
5969
6668
  email,
5970
6669
  ", and we'll send a WhatsApp reminder before the call."
5971
6670
  ] }),
5972
- 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" })
6671
+ 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" })
5973
6672
  ] })
5974
6673
  ] })
5975
6674
  ]
@@ -5998,9 +6697,9 @@ function Field({
5998
6697
  type = "text",
5999
6698
  hint
6000
6699
  }) {
6001
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { style: { display: "block", fontSize: 11, color: t.muted }, children: [
6700
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { style: { display: "block", fontSize: 11, color: t.muted }, children: [
6002
6701
  label,
6003
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
6702
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
6004
6703
  "input",
6005
6704
  {
6006
6705
  type,
@@ -6021,7 +6720,7 @@ function Field({
6021
6720
  }
6022
6721
  }
6023
6722
  ),
6024
- hint && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { marginTop: 4, fontSize: 10, color: t.muted }, children: hint })
6723
+ hint && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: { marginTop: 4, fontSize: 10, color: t.muted }, children: hint })
6025
6724
  ] });
6026
6725
  }
6027
6726
 
@@ -6162,11 +6861,11 @@ function matchesShortcut(e) {
6162
6861
  }
6163
6862
 
6164
6863
  // src/provider.tsx
6165
- var import_jsx_runtime5 = require("react/jsx-runtime");
6864
+ var import_jsx_runtime6 = require("react/jsx-runtime");
6166
6865
  var DEFAULT_BASE_URL2 = "https://glitchgrab.dev";
6167
- var GlitchgrabContext = (0, import_react5.createContext)(null);
6866
+ var GlitchgrabContext = (0, import_react6.createContext)(null);
6168
6867
  function useGlitchgrab() {
6169
- const ctx = (0, import_react5.useContext)(GlitchgrabContext);
6868
+ const ctx = (0, import_react6.useContext)(GlitchgrabContext);
6170
6869
  if (!ctx) {
6171
6870
  throw new Error("useGlitchgrab must be used within a GlitchgrabProvider");
6172
6871
  }
@@ -6174,7 +6873,7 @@ function useGlitchgrab() {
6174
6873
  }
6175
6874
  function GlitchgrabProviderInner({
6176
6875
  token,
6177
- session,
6876
+ session: sessionProp,
6178
6877
  baseUrl,
6179
6878
  onError,
6180
6879
  onReportSent,
@@ -6189,7 +6888,16 @@ function GlitchgrabProviderInner({
6189
6888
  context: appContext2,
6190
6889
  responseBodyOrigins: responseBodyOrigins2
6191
6890
  }) {
6192
- const visitedPagesRef = (0, import_react5.useRef)([]);
6891
+ const visitedPagesRef = (0, import_react6.useRef)([]);
6892
+ const sessionKey = sessionProp ? [
6893
+ sessionProp.userId,
6894
+ sessionProp.name,
6895
+ sessionProp.email,
6896
+ sessionProp.phone,
6897
+ sessionProp.avatarUrl,
6898
+ sessionProp.image
6899
+ ].map((v) => v == null ? "" : String(v)).join("\0") : "";
6900
+ const session = (0, import_react6.useMemo)(() => sessionProp, [sessionKey]);
6193
6901
  setRelease(release2);
6194
6902
  if (appContext2) setContexts(appContext2);
6195
6903
  registerCaptureConfig({
@@ -6201,12 +6909,12 @@ function GlitchgrabProviderInner({
6201
6909
  onError,
6202
6910
  onReportSent
6203
6911
  });
6204
- (0, import_react5.useEffect)(() => {
6912
+ (0, import_react6.useEffect)(() => {
6205
6913
  if (enableBreadcrumbs) {
6206
6914
  initBreadcrumbs(maxBreadcrumbs2, { responseBodyOrigins: responseBodyOrigins2 });
6207
6915
  }
6208
6916
  }, [enableBreadcrumbs, maxBreadcrumbs2, responseBodyOrigins2 == null ? void 0 : responseBodyOrigins2.join(",")]);
6209
- (0, import_react5.useEffect)(() => {
6917
+ (0, import_react6.useEffect)(() => {
6210
6918
  try {
6211
6919
  if (typeof window === "undefined") return;
6212
6920
  const trackPage = () => {
@@ -6243,7 +6951,7 @@ function GlitchgrabProviderInner({
6243
6951
  } catch (e) {
6244
6952
  }
6245
6953
  }, []);
6246
- (0, import_react5.useEffect)(() => {
6954
+ (0, import_react6.useEffect)(() => {
6247
6955
  try {
6248
6956
  if (typeof window === "undefined") return;
6249
6957
  if (process.env.NODE_ENV === "development") return;
@@ -6370,7 +7078,7 @@ function GlitchgrabProviderInner({
6370
7078
  } catch (e) {
6371
7079
  }
6372
7080
  }, [token, baseUrl, onError, onReportSent, session, ignoreErrors]);
6373
- const report = (0, import_react5.useCallback)(
7081
+ const report = (0, import_react6.useCallback)(
6374
7082
  async (type, description, metadata) => {
6375
7083
  var _a;
6376
7084
  try {
@@ -6404,11 +7112,45 @@ function GlitchgrabProviderInner({
6404
7112
  },
6405
7113
  [token, baseUrl, onReportSent, session]
6406
7114
  );
6407
- const reportBug = (0, import_react5.useCallback)(
7115
+ const reportBug = (0, import_react6.useCallback)(
6408
7116
  (description, metadata) => report("BUG", description, metadata),
6409
7117
  [report]
6410
7118
  );
6411
- const feedback = (0, import_react5.useCallback)(
7119
+ const reportGlitchgrabProblem = (0, import_react6.useCallback)(
7120
+ async (params) => {
7121
+ var _a, _b;
7122
+ try {
7123
+ const context = captureContext(visitedPagesRef.current);
7124
+ const body = JSON.stringify({
7125
+ ...params,
7126
+ pageUrl: context.url,
7127
+ userAgent: context.userAgent,
7128
+ deviceInfo: (_a = context.deviceInfo) != null ? _a : void 0,
7129
+ visitedPages: context.visitedPages,
7130
+ breadcrumbs: context.breadcrumbs,
7131
+ ...(session == null ? void 0 : session.userId) ? { reporterId: session.userId } : {},
7132
+ ...(session == null ? void 0 : session.name) ? { reporterName: String(session.name) } : {},
7133
+ ...(session == null ? void 0 : session.email) ? { reporterEmail: String(session.email) } : {}
7134
+ });
7135
+ const res = await fetch(`${baseUrl}/api/v1/sdk/glitchgrab-report`, {
7136
+ method: "POST",
7137
+ headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` },
7138
+ body,
7139
+ // keepalive caps the body at 64KB — screenshots blow past it.
7140
+ ...body.length < 6e4 ? { keepalive: true } : {}
7141
+ });
7142
+ const json = await res.json().catch(() => null);
7143
+ if (!res.ok || !(json == null ? void 0 : json.success)) {
7144
+ return { success: false, message: (_b = json == null ? void 0 : json.error) != null ? _b : "Couldn't send it" };
7145
+ }
7146
+ return { success: true, ...json.data };
7147
+ } catch (e) {
7148
+ return null;
7149
+ }
7150
+ },
7151
+ [token, baseUrl, session]
7152
+ );
7153
+ const feedback = (0, import_react6.useCallback)(
6412
7154
  async (rating, message, metadata) => {
6413
7155
  try {
6414
7156
  const context = captureContext(visitedPagesRef.current);
@@ -6436,32 +7178,32 @@ function GlitchgrabProviderInner({
6436
7178
  },
6437
7179
  [token, baseUrl, session]
6438
7180
  );
6439
- const openFeedbackDialog = (0, import_react5.useCallback)(() => {
7181
+ const openFeedbackDialog = (0, import_react6.useCallback)(() => {
6440
7182
  if (typeof window !== "undefined") {
6441
7183
  window.dispatchEvent(
6442
7184
  new CustomEvent("glitchgrab:open-report", { detail: { type: "RATING" } })
6443
7185
  );
6444
7186
  }
6445
7187
  }, []);
6446
- const captureError2 = (0, import_react5.useCallback)(
7188
+ const captureError2 = (0, import_react6.useCallback)(
6447
7189
  (error, options) => {
6448
7190
  captureError(error, options);
6449
7191
  },
6450
7192
  []
6451
7193
  );
6452
- const setContext2 = (0, import_react5.useCallback)((key, value) => {
7194
+ const setContext2 = (0, import_react6.useCallback)((key, value) => {
6453
7195
  setContext(key, value);
6454
7196
  }, []);
6455
- const setContexts2 = (0, import_react5.useCallback)((values) => {
7197
+ const setContexts2 = (0, import_react6.useCallback)((values) => {
6456
7198
  setContexts(values);
6457
7199
  }, []);
6458
- const addBreadcrumb2 = (0, import_react5.useCallback)(
7200
+ const addBreadcrumb2 = (0, import_react6.useCallback)(
6459
7201
  (message, data) => {
6460
7202
  addBreadcrumb("custom", message, data);
6461
7203
  },
6462
7204
  []
6463
7205
  );
6464
- const enhance = (0, import_react5.useCallback)(
7206
+ const enhance = (0, import_react6.useCallback)(
6465
7207
  async (text, screenshot) => {
6466
7208
  try {
6467
7209
  const ctx = captureContext(visitedPagesRef.current);
@@ -6477,7 +7219,7 @@ function GlitchgrabProviderInner({
6477
7219
  },
6478
7220
  [token, baseUrl]
6479
7221
  );
6480
- const assist = (0, import_react5.useCallback)(
7222
+ const assist = (0, import_react6.useCallback)(
6481
7223
  async (params) => {
6482
7224
  var _a;
6483
7225
  const ctx = captureContext(visitedPagesRef.current);
@@ -6503,7 +7245,7 @@ function GlitchgrabProviderInner({
6503
7245
  },
6504
7246
  [token, baseUrl, session]
6505
7247
  );
6506
- const transcribe = (0, import_react5.useCallback)(
7248
+ const transcribe = (0, import_react6.useCallback)(
6507
7249
  async (blob) => {
6508
7250
  try {
6509
7251
  return await transcribeAudio(blob, token, baseUrl);
@@ -6513,12 +7255,12 @@ function GlitchgrabProviderInner({
6513
7255
  },
6514
7256
  [token, baseUrl]
6515
7257
  );
6516
- const openReportDialog = (0, import_react5.useCallback)((options) => {
7258
+ const openReportDialog = (0, import_react6.useCallback)((options) => {
6517
7259
  if (typeof window !== "undefined") {
6518
7260
  window.dispatchEvent(new CustomEvent("glitchgrab:open-report", { detail: options }));
6519
7261
  }
6520
7262
  }, []);
6521
- (0, import_react5.useEffect)(() => {
7263
+ (0, import_react6.useEffect)(() => {
6522
7264
  try {
6523
7265
  if (typeof document === "undefined") return;
6524
7266
  document.documentElement.setAttribute("data-glitchgrab-sdk", "1");
@@ -6526,11 +7268,12 @@ function GlitchgrabProviderInner({
6526
7268
  } catch (e) {
6527
7269
  }
6528
7270
  }, []);
6529
- const [projectName, setProjectName] = (0, import_react5.useState)(null);
6530
- const [aiAssist, setAiAssist] = (0, import_react5.useState)(false);
6531
- const [bookingOpen, setBookingOpen] = (0, import_react5.useState)(false);
6532
- const openBookingDialog = (0, import_react5.useCallback)(() => setBookingOpen(true), []);
6533
- (0, import_react5.useEffect)(() => {
7271
+ const [projectName, setProjectName] = (0, import_react6.useState)(null);
7272
+ const [aiAssist, setAiAssist] = (0, import_react6.useState)(false);
7273
+ const [glitchgrabReport, setGlitchgrabReport] = (0, import_react6.useState)(false);
7274
+ const [bookingOpen, setBookingOpen] = (0, import_react6.useState)(false);
7275
+ const openBookingDialog = (0, import_react6.useCallback)(() => setBookingOpen(true), []);
7276
+ (0, import_react6.useEffect)(() => {
6534
7277
  let cancelled = false;
6535
7278
  fetch(`${baseUrl}/api/v1/sdk/project`, {
6536
7279
  headers: { Authorization: `Bearer ${token}` }
@@ -6539,13 +7282,14 @@ function GlitchgrabProviderInner({
6539
7282
  if (cancelled || !(json == null ? void 0 : json.success) || !((_a = json.data) == null ? void 0 : _a.name)) return;
6540
7283
  setProjectName(json.data.name);
6541
7284
  setAiAssist(json.data.aiAssist === true);
7285
+ setGlitchgrabReport(json.data.glitchgrabReport === true);
6542
7286
  }).catch(() => {
6543
7287
  });
6544
7288
  return () => {
6545
7289
  cancelled = true;
6546
7290
  };
6547
7291
  }, [baseUrl, token]);
6548
- (0, import_react5.useEffect)(() => {
7292
+ (0, import_react6.useEffect)(() => {
6549
7293
  try {
6550
7294
  if (typeof window === "undefined") return;
6551
7295
  const handleKeyDown = (e) => {
@@ -6559,7 +7303,7 @@ function GlitchgrabProviderInner({
6559
7303
  } catch (e) {
6560
7304
  }
6561
7305
  }, [openReportDialog]);
6562
- const reporter = (0, import_react5.useMemo)(() => {
7306
+ const reporter = (0, import_react6.useMemo)(() => {
6563
7307
  var _a;
6564
7308
  if (!session) return null;
6565
7309
  const avatar = (_a = session.avatarUrl) != null ? _a : session.image;
@@ -6569,11 +7313,11 @@ function GlitchgrabProviderInner({
6569
7313
  avatarUrl: typeof avatar === "string" ? avatar : null
6570
7314
  };
6571
7315
  }, [session]);
6572
- const [shortcutLabel, setShortcutLabel] = (0, import_react5.useState)(GLITCHGRAB_SHORTCUT2);
6573
- (0, import_react5.useEffect)(() => {
7316
+ const [shortcutLabel, setShortcutLabel] = (0, import_react6.useState)(GLITCHGRAB_SHORTCUT2);
7317
+ (0, import_react6.useEffect)(() => {
6574
7318
  setShortcutLabel(getShortcutLabel2());
6575
7319
  }, []);
6576
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
7320
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
6577
7321
  GlitchgrabContext.Provider,
6578
7322
  {
6579
7323
  value: {
@@ -6593,7 +7337,7 @@ function GlitchgrabProviderInner({
6593
7337
  shortcutLabel
6594
7338
  },
6595
7339
  children: [
6596
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
7340
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
6597
7341
  GlitchgrabErrorBoundary,
6598
7342
  {
6599
7343
  token,
@@ -6605,7 +7349,7 @@ function GlitchgrabProviderInner({
6605
7349
  children
6606
7350
  }
6607
7351
  ),
6608
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
7352
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
6609
7353
  BookingDialog,
6610
7354
  {
6611
7355
  token,
@@ -6614,20 +7358,21 @@ function GlitchgrabProviderInner({
6614
7358
  onClose: () => setBookingOpen(false)
6615
7359
  }
6616
7360
  ),
6617
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
7361
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
6618
7362
  ReportDialog,
6619
7363
  {
6620
7364
  report,
6621
7365
  sendFeedback: feedback,
6622
7366
  enhanceText: enhance,
6623
7367
  assist: aiAssist ? assist : void 0,
7368
+ reportGlitchgrabProblem: glitchgrabReport ? reportGlitchgrabProblem : void 0,
6624
7369
  transcribeAudio: transcribe,
6625
7370
  types,
6626
7371
  showSeverity,
6627
7372
  reporter,
6628
- headerSlot: projectName ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { fontSize: 11, color: "#9aa0a6" }, children: [
7373
+ headerSlot: projectName ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { fontSize: 11, color: "#9aa0a6" }, children: [
6629
7374
  "Reporting to ",
6630
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("strong", { style: { color: "#e5e5e5" }, children: projectName })
7375
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("strong", { style: { color: "#e5e5e5" }, children: projectName })
6631
7376
  ] }) : null
6632
7377
  }
6633
7378
  )
@@ -6636,21 +7381,21 @@ function GlitchgrabProviderInner({
6636
7381
  );
6637
7382
  }
6638
7383
  function GlitchgrabProvider(props) {
6639
- if (!props.token) return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_jsx_runtime5.Fragment, { children: props.children });
7384
+ if (!props.token) return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_jsx_runtime6.Fragment, { children: props.children });
6640
7385
  const resolvedProps = {
6641
7386
  ...props,
6642
7387
  baseUrl: props.baseUrl || DEFAULT_BASE_URL2
6643
7388
  };
6644
7389
  try {
6645
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(GlitchgrabProviderInner, { ...resolvedProps });
7390
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(GlitchgrabProviderInner, { ...resolvedProps });
6646
7391
  } catch (e) {
6647
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_jsx_runtime5.Fragment, { children: props.children });
7392
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_jsx_runtime6.Fragment, { children: props.children });
6648
7393
  }
6649
7394
  }
6650
7395
 
6651
7396
  // src/report-button.tsx
6652
- var import_react6 = require("react");
6653
- var import_jsx_runtime6 = require("react/jsx-runtime");
7397
+ var import_react7 = require("react");
7398
+ var import_jsx_runtime7 = require("react/jsx-runtime");
6654
7399
  function useGlitchgrabSafe() {
6655
7400
  try {
6656
7401
  return useGlitchgrab();
@@ -6665,7 +7410,7 @@ function ReportButton({
6665
7410
  children
6666
7411
  }) {
6667
7412
  var _a, _b;
6668
- const [capturing, setCapturing] = (0, import_react6.useState)(false);
7413
+ const [capturing, setCapturing] = (0, import_react7.useState)(false);
6669
7414
  const glitchgrab = useGlitchgrabSafe();
6670
7415
  if (!glitchgrab) return null;
6671
7416
  const handleClick = async () => {
@@ -6674,7 +7419,7 @@ function ReportButton({
6674
7419
  setCapturing(false);
6675
7420
  };
6676
7421
  if (children) {
6677
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_jsx_runtime6.Fragment, { children: children({ onClick: handleClick, capturing }) });
7422
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_jsx_runtime7.Fragment, { children: children({ onClick: handleClick, capturing }) });
6678
7423
  }
6679
7424
  const isTop = (_a = position == null ? void 0 : position.startsWith("top")) != null ? _a : false;
6680
7425
  const isLeft = (_b = position == null ? void 0 : position.endsWith("left")) != null ? _b : false;
@@ -6682,7 +7427,7 @@ function ReportButton({
6682
7427
  ...isLeft ? { left: "16px" } : { right: "16px" },
6683
7428
  ...isTop ? { top: "16px" } : { bottom: "16px" }
6684
7429
  };
6685
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
7430
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
6686
7431
  "button",
6687
7432
  {
6688
7433
  type: "button",
@@ -6717,7 +7462,7 @@ function ReportButton({
6717
7462
  },
6718
7463
  "aria-label": label,
6719
7464
  children: [
6720
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
7465
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
6721
7466
  "svg",
6722
7467
  {
6723
7468
  width: "16",
@@ -6725,7 +7470,7 @@ function ReportButton({
6725
7470
  viewBox: "0 0 16 16",
6726
7471
  fill: "none",
6727
7472
  style: { flexShrink: 0 },
6728
- children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
7473
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
6729
7474
  "path",
6730
7475
  {
6731
7476
  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",
@@ -6741,15 +7486,15 @@ function ReportButton({
6741
7486
  }
6742
7487
 
6743
7488
  // src/book-demo-button.tsx
6744
- var import_jsx_runtime7 = require("react/jsx-runtime");
7489
+ var import_jsx_runtime8 = require("react/jsx-runtime");
6745
7490
  function BookDemoButton({
6746
7491
  label = "Book a demo",
6747
7492
  className,
6748
7493
  children
6749
7494
  }) {
6750
7495
  const { openBookingDialog } = useGlitchgrab();
6751
- if (children) return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_jsx_runtime7.Fragment, { children: children({ onClick: openBookingDialog }) });
6752
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
7496
+ if (children) return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_jsx_runtime8.Fragment, { children: children({ onClick: openBookingDialog }) });
7497
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
6753
7498
  "button",
6754
7499
  {
6755
7500
  type: "button",
@@ -6771,7 +7516,7 @@ function BookDemoButton({
6771
7516
  }
6772
7517
 
6773
7518
  // src/feedback-button.tsx
6774
- var import_jsx_runtime8 = require("react/jsx-runtime");
7519
+ var import_jsx_runtime9 = require("react/jsx-runtime");
6775
7520
  function useGlitchgrabSafe2() {
6776
7521
  try {
6777
7522
  return useGlitchgrab();
@@ -6790,7 +7535,7 @@ function FeedbackButton({
6790
7535
  if (!glitchgrab) return null;
6791
7536
  const handleClick = () => glitchgrab.openFeedbackDialog();
6792
7537
  if (children) {
6793
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_jsx_runtime8.Fragment, { children: children({ onClick: handleClick }) });
7538
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_jsx_runtime9.Fragment, { children: children({ onClick: handleClick }) });
6794
7539
  }
6795
7540
  const isTop = (_a = position == null ? void 0 : position.startsWith("top")) != null ? _a : false;
6796
7541
  const isLeft = (_b = position == null ? void 0 : position.endsWith("left")) != null ? _b : false;
@@ -6798,7 +7543,7 @@ function FeedbackButton({
6798
7543
  ...isLeft ? { left: "16px" } : { right: "16px" },
6799
7544
  ...isTop ? { top: "16px" } : { bottom: "16px" }
6800
7545
  };
6801
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
7546
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
6802
7547
  "button",
6803
7548
  {
6804
7549
  type: "button",
@@ -6831,7 +7576,7 @@ function FeedbackButton({
6831
7576
  },
6832
7577
  "aria-label": label,
6833
7578
  children: [
6834
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
7579
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
6835
7580
  "svg",
6836
7581
  {
6837
7582
  width: "16",
@@ -6840,7 +7585,7 @@ function FeedbackButton({
6840
7585
  fill: "none",
6841
7586
  style: { flexShrink: 0 },
6842
7587
  "aria-hidden": "true",
6843
- children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
7588
+ children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
6844
7589
  "path",
6845
7590
  {
6846
7591
  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",
@@ -6856,18 +7601,18 @@ function FeedbackButton({
6856
7601
  }
6857
7602
 
6858
7603
  // src/use-reports.ts
6859
- var import_react7 = require("react");
7604
+ var import_react8 = require("react");
6860
7605
  var BASE_URL = "https://glitchgrab.dev";
6861
7606
  function useGlitchgrabReports({
6862
7607
  token,
6863
7608
  userId,
6864
7609
  limit = 100
6865
7610
  }) {
6866
- const [reports, setReports] = (0, import_react7.useState)([]);
6867
- const [isLoading, setIsLoading] = (0, import_react7.useState)(true);
6868
- const [isFetching, setIsFetching] = (0, import_react7.useState)(false);
6869
- const [error, setError] = (0, import_react7.useState)(null);
6870
- const fetchReports = (0, import_react7.useCallback)(async () => {
7611
+ const [reports, setReports] = (0, import_react8.useState)([]);
7612
+ const [isLoading, setIsLoading] = (0, import_react8.useState)(true);
7613
+ const [isFetching, setIsFetching] = (0, import_react8.useState)(false);
7614
+ const [error, setError] = (0, import_react8.useState)(null);
7615
+ const fetchReports = (0, import_react8.useCallback)(async () => {
6871
7616
  var _a, _b;
6872
7617
  if (!token || !userId) {
6873
7618
  setIsLoading(false);
@@ -6893,7 +7638,7 @@ function useGlitchgrabReports({
6893
7638
  setIsFetching(false);
6894
7639
  }
6895
7640
  }, [token, userId, limit]);
6896
- (0, import_react7.useEffect)(() => {
7641
+ (0, import_react8.useEffect)(() => {
6897
7642
  fetchReports();
6898
7643
  }, [fetchReports]);
6899
7644
  return { reports, isLoading, isFetching, error, refetch: fetchReports };
@@ -6930,9 +7675,9 @@ function useGlitchgrabActions({
6930
7675
  onSuccess,
6931
7676
  onError
6932
7677
  }) {
6933
- const [isPending, setIsPending] = (0, import_react7.useState)(false);
6934
- const [error, setError] = (0, import_react7.useState)(null);
6935
- const run = (0, import_react7.useCallback)(
7678
+ const [isPending, setIsPending] = (0, import_react8.useState)(false);
7679
+ const [error, setError] = (0, import_react8.useState)(null);
7680
+ const run = (0, import_react8.useCallback)(
6936
7681
  async (reportId, body) => {
6937
7682
  setIsPending(true);
6938
7683
  setError(null);
@@ -6962,7 +7707,7 @@ function useGlitchgrabActions({
6962
7707
  }
6963
7708
 
6964
7709
  // src/use-feedback.ts
6965
- var import_react8 = require("react");
7710
+ var import_react9 = require("react");
6966
7711
  var DEFAULT_BASE_URL3 = "https://glitchgrab.dev";
6967
7712
  function buildUrl({
6968
7713
  approvedOnly,
@@ -6988,11 +7733,11 @@ async function fetchGlitchgrabFeedback(query) {
6988
7733
  }
6989
7734
  function useGlitchgrabFeedback(query) {
6990
7735
  const { token, approvedOnly, userId, minRating, limit, baseUrl } = query;
6991
- const [feedback, setFeedback] = (0, import_react8.useState)([]);
6992
- const [isLoading, setIsLoading] = (0, import_react8.useState)(true);
6993
- const [isFetching, setIsFetching] = (0, import_react8.useState)(false);
6994
- const [error, setError] = (0, import_react8.useState)(null);
6995
- const load = (0, import_react8.useCallback)(async () => {
7736
+ const [feedback, setFeedback] = (0, import_react9.useState)([]);
7737
+ const [isLoading, setIsLoading] = (0, import_react9.useState)(true);
7738
+ const [isFetching, setIsFetching] = (0, import_react9.useState)(false);
7739
+ const [error, setError] = (0, import_react9.useState)(null);
7740
+ const load = (0, import_react9.useCallback)(async () => {
6996
7741
  if (!token) {
6997
7742
  setIsLoading(false);
6998
7743
  return;
@@ -7016,7 +7761,7 @@ function useGlitchgrabFeedback(query) {
7016
7761
  setIsFetching(false);
7017
7762
  }
7018
7763
  }, [token, approvedOnly, userId, minRating, limit, baseUrl]);
7019
- (0, import_react8.useEffect)(() => {
7764
+ (0, import_react9.useEffect)(() => {
7020
7765
  load();
7021
7766
  }, [load]);
7022
7767
  return { feedback, isLoading, isFetching, error, refetch: load };