glitchgrab 1.35.0 → 1.36.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
@@ -647,7 +647,7 @@ async function enhanceText(text, token, baseUrl, screenshot, context) {
647
647
  }
648
648
  }
649
649
  async function assistTurn(params, token, baseUrl) {
650
- var _a, _b, _c, _d, _e, _f, _g;
650
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
651
651
  const offline = {
652
652
  conversationId: null,
653
653
  question: null,
@@ -676,7 +676,10 @@ async function assistTurn(params, token, baseUrl) {
676
676
  return {
677
677
  conversationId: (_c = (_b = envelope.data) == null ? void 0 : _b.conversationId) != null ? _c : null,
678
678
  question: (_e = (_d = envelope.data) == null ? void 0 : _d.question) != null ? _e : null,
679
- report: (_g = (_f = envelope.data) == null ? void 0 : _f.report) != null ? _g : null,
679
+ options: (_g = (_f = envelope.data) == null ? void 0 : _f.options) != null ? _g : [],
680
+ report: (_i = (_h = envelope.data) == null ? void 0 : _h.report) != null ? _i : null,
681
+ duplicate: (_k = (_j = envelope.data) == null ? void 0 : _j.duplicate) != null ? _k : null,
682
+ solved: (_m = (_l = envelope.data) == null ? void 0 : _l.solved) != null ? _m : null,
680
683
  degraded: null
681
684
  };
682
685
  } catch (e) {
@@ -1203,6 +1206,26 @@ function AnnotationCanvas({ imageSrc, onSave, onCancel }) {
1203
1206
  }
1204
1207
  );
1205
1208
  }
1209
+ function getTypeLabel(type) {
1210
+ switch (type) {
1211
+ case "RATING":
1212
+ return "Rating";
1213
+ case "BUG":
1214
+ return "Bug Report";
1215
+ case "FEATURE_REQUEST":
1216
+ return "Feature Request";
1217
+ case "UI_IMPROVEMENT":
1218
+ return "UI Improvement";
1219
+ case "PERFORMANCE":
1220
+ return "Performance";
1221
+ case "SECURITY":
1222
+ return "Security";
1223
+ case "QUESTION":
1224
+ return "Question";
1225
+ case "OTHER":
1226
+ return "Other";
1227
+ }
1228
+ }
1206
1229
  var STARTERS = [
1207
1230
  "Something on this page is broken",
1208
1231
  "It did the wrong thing",
@@ -1281,6 +1304,12 @@ function AssistSheet({
1281
1304
  attachmentCount,
1282
1305
  context,
1283
1306
  reportTypeLabel,
1307
+ typePicked,
1308
+ currentTile,
1309
+ tiles,
1310
+ onPickType,
1311
+ rating,
1312
+ onRatingChange,
1284
1313
  projectSlot,
1285
1314
  reporterName,
1286
1315
  description,
@@ -1288,18 +1317,26 @@ function AssistSheet({
1288
1317
  severity,
1289
1318
  onSeverityChange,
1290
1319
  showSeverity,
1320
+ onDuplicateChange,
1291
1321
  isSubmitting,
1292
1322
  submitted,
1293
1323
  onSend,
1294
1324
  onDegrade,
1295
- onClose
1325
+ onClose,
1326
+ onFinish
1296
1327
  }) {
1297
1328
  const narrow = useIsNarrow();
1298
1329
  const [messages, setMessages] = (0, import_react3.useState)([]);
1299
1330
  const [input, setInput] = (0, import_react3.useState)("");
1300
1331
  const [busy, setBusy] = (0, import_react3.useState)(false);
1301
1332
  const [conversationId, setConversationId] = (0, import_react3.useState)(null);
1302
- const [phase, setPhase] = (0, import_react3.useState)("chat");
1333
+ const [hoveredStar, setHoveredStar] = (0, import_react3.useState)(0);
1334
+ const [options, setOptions] = (0, import_react3.useState)([]);
1335
+ const [duplicate, setDuplicate] = (0, import_react3.useState)(null);
1336
+ const [solved, setSolved] = (0, import_react3.useState)(null);
1337
+ const [phase, setPhase] = (0, import_react3.useState)(
1338
+ !typePicked ? "type" : currentTile === "RATING" ? "rating" : "chat"
1339
+ );
1303
1340
  const scrollRef = (0, import_react3.useRef)(null);
1304
1341
  const inputRef = (0, import_react3.useRef)(null);
1305
1342
  const draftRef = (0, import_react3.useRef)(null);
@@ -1313,6 +1350,7 @@ function AssistSheet({
1313
1350
  if (phase === "draft") (_a = draftRef.current) == null ? void 0 : _a.focus();
1314
1351
  }, [phase]);
1315
1352
  async function runTurn(history2) {
1353
+ var _a, _b, _c, _d;
1316
1354
  setBusy(true);
1317
1355
  let result;
1318
1356
  try {
@@ -1328,14 +1366,24 @@ function AssistSheet({
1328
1366
  return;
1329
1367
  }
1330
1368
  if (result.conversationId) setConversationId(result.conversationId);
1369
+ setDuplicate((_a = result.duplicate) != null ? _a : null);
1370
+ onDuplicateChange == null ? void 0 : onDuplicateChange((_c = (_b = result.duplicate) == null ? void 0 : _b.number) != null ? _c : null);
1371
+ if (result.solved) {
1372
+ setSolved(result.solved);
1373
+ setOptions([]);
1374
+ setPhase("solved");
1375
+ return;
1376
+ }
1331
1377
  if (result.report) {
1332
1378
  onDescriptionChange(result.report);
1333
1379
  setMessages([...history2, { role: "assistant", content: "Here's your report \u2014 have a read." }]);
1380
+ setOptions([]);
1334
1381
  setPhase("draft");
1335
1382
  return;
1336
1383
  }
1337
1384
  if (result.question) {
1338
1385
  setMessages([...history2, { role: "assistant", content: result.question }]);
1386
+ setOptions((_d = result.options) != null ? _d : []);
1339
1387
  return;
1340
1388
  }
1341
1389
  onDegrade("The assistant had nothing to add \u2014 write your report below and send it as normal.");
@@ -1343,6 +1391,7 @@ function AssistSheet({
1343
1391
  (0, import_react3.useEffect)(() => {
1344
1392
  if (seededRef.current) return;
1345
1393
  seededRef.current = true;
1394
+ if (!typePicked || currentTile === "RATING") return;
1346
1395
  const seed = description.trim();
1347
1396
  if (!seed) return;
1348
1397
  const history2 = [{ role: "user", content: seed }];
@@ -1352,12 +1401,17 @@ function AssistSheet({
1352
1401
  function send(text) {
1353
1402
  const value = text.trim();
1354
1403
  if (!value || busy) return;
1404
+ setOptions([]);
1355
1405
  const history2 = [...messages, { role: "user", content: value }];
1356
1406
  setMessages(history2);
1357
1407
  setInput("");
1358
1408
  void runTurn(history2);
1359
1409
  }
1360
- const empty = messages.length === 0 && !busy;
1410
+ function pickType(tile) {
1411
+ onPickType(tile);
1412
+ setPhase(tile === "RATING" ? "rating" : "chat");
1413
+ }
1414
+ const empty = phase === "chat" && messages.length === 0 && !busy;
1361
1415
  const initials = (reporterName != null ? reporterName : "").split(/\s+/).filter(Boolean).slice(0, 2).map((w) => {
1362
1416
  var _a, _b;
1363
1417
  return (_b = (_a = w[0]) == null ? void 0 : _a.toUpperCase()) != null ? _b : "";
@@ -1473,7 +1527,7 @@ function AssistSheet({
1473
1527
  }
1474
1528
  ),
1475
1529
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { fontSize: "11.5px", color: t2.textMuted, marginTop: "3px" }, children: [
1476
- reportTypeLabel,
1530
+ phase === "type" ? null : reportTypeLabel,
1477
1531
  projectSlot ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { marginLeft: "6px" }, children: projectSlot }) : null
1478
1532
  ] })
1479
1533
  ] }),
@@ -1523,6 +1577,182 @@ function AssistSheet({
1523
1577
  minWidth: 0
1524
1578
  },
1525
1579
  children: [
1580
+ phase === "type" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: "12px" }, children: [
1581
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", gap: "8px" }, children: [
1582
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Avatar, { who: "assistant", theme: t2 }),
1583
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1584
+ "div",
1585
+ {
1586
+ style: {
1587
+ padding: "9px 12px",
1588
+ borderRadius: "12px 12px 12px 4px",
1589
+ backgroundColor: t2.bgSecondary,
1590
+ fontSize: "13px",
1591
+ lineHeight: 1.55,
1592
+ maxWidth: "88%"
1593
+ },
1594
+ children: [
1595
+ reporterName ? `Hi ${reporterName.split(" ")[0]} \u2014 w` : "W",
1596
+ "hat do you want to do? Pick one and I'll take it from there."
1597
+ ]
1598
+ }
1599
+ )
1600
+ ] }),
1601
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1602
+ "div",
1603
+ {
1604
+ style: {
1605
+ display: "flex",
1606
+ flexWrap: "wrap",
1607
+ gap: "6px",
1608
+ paddingLeft: "32px"
1609
+ },
1610
+ children: tiles.map((tile) => {
1611
+ const rate = tile === "RATING";
1612
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1613
+ "button",
1614
+ {
1615
+ type: "button",
1616
+ "data-gg-chip": "",
1617
+ "data-gg-tile": tile,
1618
+ onClick: () => pickType(tile),
1619
+ style: {
1620
+ display: "inline-flex",
1621
+ alignItems: "center",
1622
+ gap: "5px",
1623
+ // The rating is the only tile that isn't a report, so
1624
+ // it wears the star colour the dialog's hero row uses
1625
+ // rather than hiding among the report types.
1626
+ border: `1px solid ${rate ? "rgba(245,158,11,0.5)" : t2.inputBorder}`,
1627
+ background: rate ? "rgba(245,158,11,0.08)" : "transparent",
1628
+ color: rate ? "#f59e0b" : t2.textMuted,
1629
+ borderRadius: "999px",
1630
+ // 8px vertical keeps the tap target at ~32px.
1631
+ padding: "8px 12px",
1632
+ fontSize: "12px",
1633
+ fontWeight: rate ? 600 : 400,
1634
+ fontFamily: "inherit",
1635
+ cursor: "pointer"
1636
+ },
1637
+ children: rate ? "\u2605 Rate us" : getTypeLabel(tile)
1638
+ },
1639
+ tile
1640
+ );
1641
+ })
1642
+ }
1643
+ )
1644
+ ] }),
1645
+ phase === "rating" && !submitted && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: "12px" }, children: [
1646
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", gap: "8px" }, children: [
1647
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Avatar, { who: "assistant", theme: t2 }),
1648
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1649
+ "div",
1650
+ {
1651
+ style: {
1652
+ padding: "9px 12px",
1653
+ borderRadius: "12px 12px 12px 4px",
1654
+ backgroundColor: t2.bgSecondary,
1655
+ fontSize: "13px",
1656
+ lineHeight: 1.55,
1657
+ maxWidth: "88%"
1658
+ },
1659
+ children: "How are we doing? Tap a star \u2014 the words are optional."
1660
+ }
1661
+ )
1662
+ ] }),
1663
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1664
+ "div",
1665
+ {
1666
+ style: {
1667
+ display: "flex",
1668
+ flexDirection: "column",
1669
+ gap: "10px",
1670
+ marginLeft: "32px",
1671
+ padding: "12px",
1672
+ borderRadius: "12px",
1673
+ border: "1px solid rgba(245,158,11,0.4)",
1674
+ backgroundColor: "rgba(245,158,11,0.06)"
1675
+ },
1676
+ children: [
1677
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { display: "flex", gap: "4px" }, role: "group", "aria-label": "Rating", children: [1, 2, 3, 4, 5].map((star) => {
1678
+ const filled = star <= (hoveredStar || rating);
1679
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1680
+ "button",
1681
+ {
1682
+ type: "button",
1683
+ "aria-label": `${star} star${star > 1 ? "s" : ""}`,
1684
+ "aria-pressed": star <= rating,
1685
+ onClick: () => onRatingChange(star),
1686
+ onMouseEnter: () => setHoveredStar(star),
1687
+ onMouseLeave: () => setHoveredStar(0),
1688
+ style: {
1689
+ border: "none",
1690
+ background: "transparent",
1691
+ padding: 0,
1692
+ cursor: "pointer",
1693
+ lineHeight: 0
1694
+ },
1695
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { width: "30", height: "30", viewBox: "0 0 24 24", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1696
+ "path",
1697
+ {
1698
+ 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",
1699
+ fill: filled ? "#f59e0b" : "transparent",
1700
+ stroke: filled ? "#f59e0b" : t2.inputBorder,
1701
+ strokeWidth: "1.5",
1702
+ strokeLinejoin: "round"
1703
+ }
1704
+ ) })
1705
+ },
1706
+ star
1707
+ );
1708
+ }) }),
1709
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1710
+ "textarea",
1711
+ {
1712
+ value: description,
1713
+ onChange: (e) => onDescriptionChange(e.target.value),
1714
+ rows: 3,
1715
+ placeholder: "What made it good \u2014 or what let you down? (optional)",
1716
+ style: {
1717
+ width: "100%",
1718
+ padding: "10px",
1719
+ borderRadius: "8px",
1720
+ border: `1px solid ${t2.inputBorder}`,
1721
+ backgroundColor: t2.inputBg,
1722
+ color: t2.text,
1723
+ fontSize: "13px",
1724
+ lineHeight: 1.5,
1725
+ fontFamily: "inherit",
1726
+ resize: "vertical",
1727
+ outline: "none",
1728
+ boxSizing: "border-box"
1729
+ }
1730
+ }
1731
+ ),
1732
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1733
+ "button",
1734
+ {
1735
+ type: "button",
1736
+ onClick: () => setPhase("type"),
1737
+ style: {
1738
+ alignSelf: "flex-start",
1739
+ border: "none",
1740
+ background: "transparent",
1741
+ color: t2.textMuted,
1742
+ fontSize: "12px",
1743
+ fontFamily: "inherit",
1744
+ // A bare text link is ~14px tall — half a thumb.
1745
+ padding: "9px 0",
1746
+ cursor: "pointer",
1747
+ textDecoration: "underline"
1748
+ },
1749
+ children: "Report something instead"
1750
+ }
1751
+ )
1752
+ ]
1753
+ }
1754
+ )
1755
+ ] }),
1526
1756
  empty && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: "12px" }, children: [
1527
1757
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", gap: "8px" }, children: [
1528
1758
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Avatar, { who: "assistant", theme: t2 }),
@@ -1611,6 +1841,47 @@ function AssistSheet({
1611
1841
  i
1612
1842
  );
1613
1843
  }),
1844
+ options.length > 0 && !busy && phase === "chat" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1845
+ "div",
1846
+ {
1847
+ style: {
1848
+ display: "flex",
1849
+ // Long labels wrapped into a ragged row that always stranded
1850
+ // the last chip on a line of its own. Past ~24 characters
1851
+ // these stop being chips and become a choice list, so they
1852
+ // stack full width and read like one.
1853
+ flexDirection: options.some((o) => o.length > 24) ? "column" : "row",
1854
+ alignItems: options.some((o) => o.length > 24) ? "stretch" : "flex-start",
1855
+ flexWrap: "wrap",
1856
+ gap: "6px",
1857
+ paddingLeft: "32px"
1858
+ },
1859
+ children: options.map((option) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1860
+ "button",
1861
+ {
1862
+ type: "button",
1863
+ "data-gg-chip": "",
1864
+ "data-gg-option": "",
1865
+ onClick: () => send(option),
1866
+ style: {
1867
+ border: `1px solid ${t2.inputBorder}`,
1868
+ background: "transparent",
1869
+ color: t2.textMuted,
1870
+ borderRadius: "999px",
1871
+ // Same 8px as the picker chips — one tap target size in
1872
+ // this sheet, not two.
1873
+ padding: "8px 12px",
1874
+ fontSize: "12px",
1875
+ fontFamily: "inherit",
1876
+ cursor: "pointer",
1877
+ textAlign: "left"
1878
+ },
1879
+ children: option
1880
+ },
1881
+ option
1882
+ ))
1883
+ }
1884
+ ),
1614
1885
  busy && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", gap: "8px" }, role: "status", "aria-label": "Thinking", children: [
1615
1886
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Avatar, { who: "assistant", theme: t2 }),
1616
1887
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
@@ -1668,6 +1939,32 @@ function AssistSheet({
1668
1939
  children: "Your report \xB7 edit anything"
1669
1940
  }
1670
1941
  ),
1942
+ duplicate && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1943
+ "div",
1944
+ {
1945
+ style: {
1946
+ display: "flex",
1947
+ flexDirection: "column",
1948
+ gap: "3px",
1949
+ padding: "9px 10px",
1950
+ borderRadius: "8px",
1951
+ border: "1px solid rgba(245,158,11,0.4)",
1952
+ backgroundColor: "rgba(245,158,11,0.08)",
1953
+ fontSize: "12px",
1954
+ lineHeight: 1.5
1955
+ },
1956
+ children: [
1957
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: "#f59e0b", fontWeight: 600 }, children: "Our team is already on this" }),
1958
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { style: { color: t2.textMuted, wordBreak: "break-word" }, children: [
1959
+ "#",
1960
+ duplicate.number,
1961
+ " ",
1962
+ duplicate.title
1963
+ ] }),
1964
+ /* @__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." })
1965
+ ]
1966
+ }
1967
+ ),
1671
1968
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1672
1969
  "textarea",
1673
1970
  {
@@ -1751,7 +2048,7 @@ function AssistSheet({
1751
2048
  color: t2.textMuted,
1752
2049
  fontSize: "12px",
1753
2050
  fontFamily: "inherit",
1754
- padding: 0,
2051
+ padding: "9px 0",
1755
2052
  cursor: "pointer",
1756
2053
  textDecoration: "underline"
1757
2054
  },
@@ -1761,6 +2058,33 @@ function AssistSheet({
1761
2058
  ]
1762
2059
  }
1763
2060
  ),
2061
+ phase === "solved" && solved && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: "12px" }, children: [
2062
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", gap: "8px" }, children: [
2063
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Avatar, { who: "assistant", theme: t2 }),
2064
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2065
+ "div",
2066
+ {
2067
+ style: {
2068
+ padding: "9px 12px",
2069
+ borderRadius: "12px 12px 12px 4px",
2070
+ backgroundColor: t2.bgSecondary,
2071
+ fontSize: "13px",
2072
+ lineHeight: 1.55,
2073
+ maxWidth: "88%",
2074
+ whiteSpace: "pre-wrap"
2075
+ },
2076
+ children: solved
2077
+ }
2078
+ )
2079
+ ] }),
2080
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2081
+ "span",
2082
+ {
2083
+ style: { fontSize: "11.5px", color: t2.textMuted, paddingLeft: "32px" },
2084
+ children: "Nothing was filed \u2014 you are all set."
2085
+ }
2086
+ )
2087
+ ] }),
1764
2088
  submitted && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1765
2089
  "div",
1766
2090
  {
@@ -1782,7 +2106,7 @@ function AssistSheet({
1782
2106
  )
1783
2107
  }
1784
2108
  ),
1785
- !submitted && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2109
+ !submitted && phase !== "type" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1786
2110
  "div",
1787
2111
  {
1788
2112
  style: {
@@ -1792,7 +2116,48 @@ function AssistSheet({
1792
2116
  paddingBottom: narrow ? "max(12px, env(safe-area-inset-bottom))" : "12px",
1793
2117
  backgroundColor: t2.bg
1794
2118
  },
1795
- children: phase === "draft" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2119
+ children: phase === "solved" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2120
+ "button",
2121
+ {
2122
+ type: "button",
2123
+ "data-gg-sheet-send": "",
2124
+ onClick: () => (onFinish != null ? onFinish : onClose)(),
2125
+ style: {
2126
+ width: "100%",
2127
+ padding: "12px",
2128
+ borderRadius: "10px",
2129
+ border: "none",
2130
+ backgroundColor: t2.accent,
2131
+ color: t2.accentText,
2132
+ fontSize: "14px",
2133
+ fontWeight: 700,
2134
+ fontFamily: "inherit",
2135
+ cursor: "pointer"
2136
+ },
2137
+ children: "Done"
2138
+ }
2139
+ ) : phase === "rating" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2140
+ "button",
2141
+ {
2142
+ type: "button",
2143
+ "data-gg-sheet-send": "",
2144
+ onClick: onSend,
2145
+ disabled: isSubmitting || rating < 1,
2146
+ style: {
2147
+ width: "100%",
2148
+ padding: "12px",
2149
+ borderRadius: "10px",
2150
+ border: "none",
2151
+ backgroundColor: isSubmitting || rating < 1 ? t2.bgSecondary : "#f59e0b",
2152
+ color: isSubmitting || rating < 1 ? t2.textMuted : "#1a1a1a",
2153
+ fontSize: "14px",
2154
+ fontWeight: 700,
2155
+ fontFamily: "inherit",
2156
+ cursor: isSubmitting || rating < 1 ? "default" : "pointer"
2157
+ },
2158
+ children: isSubmitting ? "Sending\u2026" : rating < 1 ? "Pick a star first" : "Send Rating"
2159
+ }
2160
+ ) : phase === "draft" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1796
2161
  "button",
1797
2162
  {
1798
2163
  type: "button",
@@ -1811,7 +2176,7 @@ function AssistSheet({
1811
2176
  fontFamily: "inherit",
1812
2177
  cursor: isSubmitting || !description.trim() ? "default" : "pointer"
1813
2178
  },
1814
- children: isSubmitting ? "Sending\u2026" : "Send Report"
2179
+ children: isSubmitting ? "Sending\u2026" : duplicate ? `Add to #${duplicate.number}` : "Send Report"
1815
2180
  }
1816
2181
  ) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", gap: "8px", alignItems: "flex-end" }, children: [
1817
2182
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
@@ -2248,6 +2613,8 @@ function ReportDialog({
2248
2613
  const [assistOpen, setAssistOpen] = (0, import_react.useState)(false);
2249
2614
  const [assistNotice, setAssistNotice] = (0, import_react.useState)(null);
2250
2615
  const [assistUsed, setAssistUsed] = (0, import_react.useState)(false);
2616
+ const [typeChosen, setTypeChosen] = (0, import_react.useState)(false);
2617
+ const [duplicateIssueNumber, setDuplicateIssueNumber] = (0, import_react.useState)(null);
2251
2618
  const [isEnhanced, setIsEnhanced] = (0, import_react.useState)(false);
2252
2619
  const [originalDescription, setOriginalDescription] = (0, import_react.useState)(
2253
2620
  null
@@ -2355,6 +2722,7 @@ function ReportDialog({
2355
2722
  e.preventDefault();
2356
2723
  setRating(Number(e.key));
2357
2724
  setReportType("RATING");
2725
+ setTypeChosen(true);
2358
2726
  setStep(2);
2359
2727
  return;
2360
2728
  }
@@ -2366,6 +2734,7 @@ function ReportDialog({
2366
2734
  if (match) {
2367
2735
  e.preventDefault();
2368
2736
  setReportType(match);
2737
+ setTypeChosen(true);
2369
2738
  setStep(2);
2370
2739
  }
2371
2740
  };
@@ -2419,10 +2788,12 @@ function ReportDialog({
2419
2788
  setAnnotatingIndex(null);
2420
2789
  if (availableTypes.length === 1) {
2421
2790
  setReportType(availableTypes[0]);
2791
+ setTypeChosen(true);
2422
2792
  setStep(2);
2423
2793
  }
2424
2794
  const shot = await captureScreenshot();
2425
2795
  if (shot) setScreenshots([shot]);
2796
+ if (assist && !assistUsed) setAssistOpen(true);
2426
2797
  setIsOpen(true);
2427
2798
  };
2428
2799
  (0, import_react.useEffect)(() => {
@@ -2432,6 +2803,7 @@ function ReportDialog({
2432
2803
  if (detail == null ? void 0 : detail.description) setDescription(detail.description);
2433
2804
  if (detail == null ? void 0 : detail.type) {
2434
2805
  setReportType(detail.type);
2806
+ setTypeChosen(true);
2435
2807
  setStep(2);
2436
2808
  }
2437
2809
  handleOpen();
@@ -2583,7 +2955,7 @@ function ReportDialog({
2583
2955
  } catch (e) {
2584
2956
  }
2585
2957
  };
2586
- const sheetUp = !!assist && assistOpen && !isRating;
2958
+ const sheetUp = !!assist && assistOpen;
2587
2959
  (0, import_react.useEffect)(() => {
2588
2960
  const el = modalRef.current;
2589
2961
  if (!el) return;
@@ -2605,6 +2977,8 @@ function ReportDialog({
2605
2977
  setRating(0);
2606
2978
  setHoveredStar(0);
2607
2979
  setReportType("BUG");
2980
+ setTypeChosen(false);
2981
+ setDuplicateIssueNumber(null);
2608
2982
  setSeverity("medium");
2609
2983
  setValidationError(null);
2610
2984
  setVoiceError(null);
@@ -2789,6 +3163,9 @@ function ReportDialog({
2789
3163
  if (showSeverity && reportType === "BUG") {
2790
3164
  metadata.severity = severity;
2791
3165
  }
3166
+ if (duplicateIssueNumber) {
3167
+ metadata.duplicateIssueNumber = String(duplicateIssueNumber);
3168
+ }
2792
3169
  const result = await report(
2793
3170
  // The RATING branch returned above, so anything reaching here is a real
2794
3171
  // ReportType — the widened tile union can't leak into the report API.
@@ -3151,6 +3528,7 @@ function ReportDialog({
3151
3528
  onClick: () => {
3152
3529
  setRating(star);
3153
3530
  setReportType("RATING");
3531
+ setTypeChosen(true);
3154
3532
  setStep(2);
3155
3533
  },
3156
3534
  style: {
@@ -3261,6 +3639,7 @@ function ReportDialog({
3261
3639
  "data-gg-type": type,
3262
3640
  onClick: () => {
3263
3641
  setReportType(type);
3642
+ setTypeChosen(true);
3264
3643
  setStep(2);
3265
3644
  },
3266
3645
  style: {
@@ -3380,6 +3759,7 @@ function ReportDialog({
3380
3759
  type: "button",
3381
3760
  onClick: () => {
3382
3761
  setAssistNotice(null);
3762
+ setTypeChosen(true);
3383
3763
  setAssistOpen(true);
3384
3764
  },
3385
3765
  style: {
@@ -4528,6 +4908,21 @@ function ReportDialog({
4528
4908
  attachmentCount: screenshots.length + attachments.length,
4529
4909
  context: { ...assistContext != null ? assistContext : {}, reportType },
4530
4910
  reportTypeLabel: getTypeLabel(reportType),
4911
+ typePicked: typeChosen,
4912
+ currentTile: reportType,
4913
+ tiles: sendFeedback2 ? [...availableTypes, "RATING"] : availableTypes,
4914
+ onPickType: (tile) => {
4915
+ setReportType(tile);
4916
+ setTypeChosen(true);
4917
+ setStep(2);
4918
+ },
4919
+ onDuplicateChange: setDuplicateIssueNumber,
4920
+ onFinish: handleClose,
4921
+ rating,
4922
+ onRatingChange: (value) => {
4923
+ setRating(value);
4924
+ if (validationError) setValidationError(null);
4925
+ },
4531
4926
  projectSlot: headerSlot,
4532
4927
  reporterName: (_b = reporter == null ? void 0 : reporter.name) != null ? _b : null,
4533
4928
  description,
@@ -4557,26 +4952,6 @@ function ReportDialog({
4557
4952
  )
4558
4953
  ] });
4559
4954
  }
4560
- function getTypeLabel(type) {
4561
- switch (type) {
4562
- case "RATING":
4563
- return "Rating";
4564
- case "BUG":
4565
- return "Bug Report";
4566
- case "FEATURE_REQUEST":
4567
- return "Feature Request";
4568
- case "UI_IMPROVEMENT":
4569
- return "UI Improvement";
4570
- case "PERFORMANCE":
4571
- return "Performance";
4572
- case "SECURITY":
4573
- return "Security";
4574
- case "QUESTION":
4575
- return "Question";
4576
- case "OTHER":
4577
- return "Other";
4578
- }
4579
- }
4580
4955
  function getPlaceholder(type, idx = 0, hasVoice = false) {
4581
4956
  if (type === "RATING") return "What made it good \u2014 or what let you down? (optional)";
4582
4957
  if (hasVoice && idx === 1) return "Hold Space to speak \u2014 we'll transcribe it";