glitchgrab 1.58.0 → 1.58.2
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/README.md +28 -0
- package/dist/index.d.mts +21 -3
- package/dist/index.d.ts +21 -3
- package/dist/index.js +744 -573
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +743 -573
- package/dist/index.mjs.map +1 -1
- package/dist/server.d.mts +36 -3
- package/dist/server.d.ts +36 -3
- package/dist/server.js +44 -13
- package/dist/server.js.map +1 -1
- package/dist/server.mjs +42 -13
- package/dist/server.mjs.map +1 -1
- package/dist/{types-DfVtKZcp.d.mts → types-BpTLWJ1y.d.mts} +8 -0
- package/dist/{types-DfVtKZcp.d.ts → types-BpTLWJ1y.d.ts} +8 -0
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -587,6 +587,9 @@ function contextMetadata(context) {
|
|
|
587
587
|
var DEFAULT_BASE_URL = "https://glitchgrab.dev";
|
|
588
588
|
var MAX_RETRIES = 3;
|
|
589
589
|
var RETRY_BASE_MS = 500;
|
|
590
|
+
function reportAttemptTimeoutMs(bodyLength) {
|
|
591
|
+
return 1e4 + Math.ceil(bodyLength / 1e5) * 1e3;
|
|
592
|
+
}
|
|
590
593
|
async function sendReport(payload, baseUrl) {
|
|
591
594
|
var _a;
|
|
592
595
|
try {
|
|
@@ -597,13 +600,18 @@ async function sendReport(payload, baseUrl) {
|
|
|
597
600
|
Authorization: `Bearer ${payload.token}`
|
|
598
601
|
};
|
|
599
602
|
const useKeepalive = body.length < 6e4;
|
|
603
|
+
const timeoutMs = reportAttemptTimeoutMs(body.length);
|
|
600
604
|
for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
|
|
605
|
+
const isLast = attempt === MAX_RETRIES - 1;
|
|
606
|
+
const controller = !isLast && typeof AbortController === "function" ? new AbortController() : null;
|
|
607
|
+
const timer = controller ? setTimeout(() => controller.abort(), timeoutMs) : null;
|
|
601
608
|
try {
|
|
602
609
|
const response = await fetch(url, {
|
|
603
610
|
method: "POST",
|
|
604
611
|
headers,
|
|
605
612
|
body,
|
|
606
|
-
...useKeepalive ? { keepalive: true } : {}
|
|
613
|
+
...useKeepalive ? { keepalive: true } : {},
|
|
614
|
+
...controller ? { signal: controller.signal } : {}
|
|
607
615
|
});
|
|
608
616
|
if (response.ok) {
|
|
609
617
|
const envelope = await response.json();
|
|
@@ -613,6 +621,8 @@ async function sendReport(payload, baseUrl) {
|
|
|
613
621
|
return null;
|
|
614
622
|
}
|
|
615
623
|
} catch (e) {
|
|
624
|
+
} finally {
|
|
625
|
+
if (timer) clearTimeout(timer);
|
|
616
626
|
}
|
|
617
627
|
if (attempt < MAX_RETRIES - 1) {
|
|
618
628
|
await new Promise(
|
|
@@ -729,7 +739,7 @@ async function enhanceText(text, token, baseUrl, screenshot, context, identity)
|
|
|
729
739
|
return text;
|
|
730
740
|
}
|
|
731
741
|
}
|
|
732
|
-
async function findSimilarIssues(description, token, baseUrl, identity) {
|
|
742
|
+
async function findSimilarIssues(description, token, baseUrl, identity, files) {
|
|
733
743
|
var _a;
|
|
734
744
|
try {
|
|
735
745
|
const url = `${baseUrl != null ? baseUrl : DEFAULT_BASE_URL}/api/v1/reports/similar`;
|
|
@@ -739,7 +749,11 @@ async function findSimilarIssues(description, token, baseUrl, identity) {
|
|
|
739
749
|
"Content-Type": "application/json",
|
|
740
750
|
Authorization: `Bearer ${token}`
|
|
741
751
|
},
|
|
742
|
-
body: JSON.stringify({
|
|
752
|
+
body: JSON.stringify({
|
|
753
|
+
text: description,
|
|
754
|
+
...(files == null ? void 0 : files.length) ? { files } : {},
|
|
755
|
+
...signedFields(identity)
|
|
756
|
+
})
|
|
743
757
|
});
|
|
744
758
|
const envelope = await response.json().catch(() => null);
|
|
745
759
|
if (!response.ok || !(envelope == null ? void 0 : envelope.success)) return null;
|
|
@@ -749,6 +763,25 @@ async function findSimilarIssues(description, token, baseUrl, identity) {
|
|
|
749
763
|
return null;
|
|
750
764
|
}
|
|
751
765
|
}
|
|
766
|
+
async function readAttachment(file, token, baseUrl, identity) {
|
|
767
|
+
var _a;
|
|
768
|
+
try {
|
|
769
|
+
const url = `${baseUrl != null ? baseUrl : DEFAULT_BASE_URL}/api/v1/ai/read-file`;
|
|
770
|
+
const response = await fetch(url, {
|
|
771
|
+
method: "POST",
|
|
772
|
+
headers: {
|
|
773
|
+
"Content-Type": "application/json",
|
|
774
|
+
Authorization: `Bearer ${token}`
|
|
775
|
+
},
|
|
776
|
+
body: JSON.stringify({ name: file.name, data: file.dataUrl, ...signedFields(identity) })
|
|
777
|
+
});
|
|
778
|
+
const envelope = await response.json().catch(() => null);
|
|
779
|
+
if (!response.ok || !(envelope == null ? void 0 : envelope.success) || !((_a = envelope.data) == null ? void 0 : _a.enabled)) return null;
|
|
780
|
+
return typeof envelope.data.text === "string" ? envelope.data.text : null;
|
|
781
|
+
} catch (e) {
|
|
782
|
+
return null;
|
|
783
|
+
}
|
|
784
|
+
}
|
|
752
785
|
async function assistTurn(params, token, baseUrl) {
|
|
753
786
|
const offline = assistOffline();
|
|
754
787
|
try {
|
|
@@ -1355,6 +1388,9 @@ var MAX_ASSIST_IMAGE_CHARS = 35e5;
|
|
|
1355
1388
|
var MAX_ASSIST_FILES = 3;
|
|
1356
1389
|
var MAX_ASSIST_FILE_CHARS = 3e5;
|
|
1357
1390
|
var MAX_ASSIST_FILE_CHARS_EACH = 2e5;
|
|
1391
|
+
var MAX_SIMILAR_FILE_CHARS = 2e4;
|
|
1392
|
+
var MAX_SIMILAR_FILES_TOTAL_CHARS = 4e4;
|
|
1393
|
+
var MAX_READABLE_PDF_BYTES = 3 * 1024 * 1024;
|
|
1358
1394
|
var TEXT_ATTACHMENT_EXTENSIONS = [
|
|
1359
1395
|
".html",
|
|
1360
1396
|
".htm",
|
|
@@ -1385,6 +1421,10 @@ var ATTACHMENT_ACCEPT = [
|
|
|
1385
1421
|
...BINARY_ATTACHMENT_EXTENSIONS
|
|
1386
1422
|
].join(",");
|
|
1387
1423
|
var ASSIST_ATTACHMENT_ACCEPT = ["image/*", ...TEXT_ATTACHMENT_EXTENSIONS].join(",");
|
|
1424
|
+
var ASSIST_ATTACHMENT_ACCEPT_WITH_PDF = [ASSIST_ATTACHMENT_ACCEPT, ".pdf"].join(",");
|
|
1425
|
+
function isPdfFile(name) {
|
|
1426
|
+
return name.toLowerCase().endsWith(".pdf");
|
|
1427
|
+
}
|
|
1388
1428
|
function isAssistReadableFile(name) {
|
|
1389
1429
|
const lower = name.toLowerCase();
|
|
1390
1430
|
return TEXT_ATTACHMENT_EXTENSIONS.some((ext) => lower.endsWith(ext));
|
|
@@ -1405,6 +1445,27 @@ function decodeTextDataUrl(dataUrl) {
|
|
|
1405
1445
|
return null;
|
|
1406
1446
|
}
|
|
1407
1447
|
}
|
|
1448
|
+
function heldFileKey(file) {
|
|
1449
|
+
return `${file.name}:${file.size}`;
|
|
1450
|
+
}
|
|
1451
|
+
function heldFileText(file) {
|
|
1452
|
+
if (isPdfFile(file.name)) return typeof file.text === "string" && file.text.trim() ? file.text : null;
|
|
1453
|
+
if (!isAssistReadableFile(file.name)) return null;
|
|
1454
|
+
const text = decodeTextDataUrl(file.dataUrl);
|
|
1455
|
+
return text && text.trim() ? text : null;
|
|
1456
|
+
}
|
|
1457
|
+
function filesForSimilar(files, each, total) {
|
|
1458
|
+
const picked = [];
|
|
1459
|
+
let used = 0;
|
|
1460
|
+
for (let i = files.length - 1; i >= 0 && used < total; i--) {
|
|
1461
|
+
const text = heldFileText(files[i]);
|
|
1462
|
+
if (!text) continue;
|
|
1463
|
+
const content = text.slice(0, Math.min(each, total - used));
|
|
1464
|
+
picked.unshift({ name: files[i].name, content });
|
|
1465
|
+
used += content.length;
|
|
1466
|
+
}
|
|
1467
|
+
return picked;
|
|
1468
|
+
}
|
|
1408
1469
|
function getTypeLabel(type) {
|
|
1409
1470
|
switch (type) {
|
|
1410
1471
|
case "RATING":
|
|
@@ -1848,9 +1909,8 @@ function pickAssistFiles(files) {
|
|
|
1848
1909
|
let total = 0;
|
|
1849
1910
|
for (let i = files.length - 1; i >= 0 && picked.length < MAX_ASSIST_FILES; i--) {
|
|
1850
1911
|
const attached = files[i];
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
if (!text || !text.trim()) continue;
|
|
1912
|
+
const text = heldFileText(attached);
|
|
1913
|
+
if (!text) continue;
|
|
1854
1914
|
const content = text.slice(0, MAX_ASSIST_FILE_CHARS_EACH);
|
|
1855
1915
|
if (total + content.length > MAX_ASSIST_FILE_CHARS) continue;
|
|
1856
1916
|
picked.unshift({ index: i, file: { name: attached.name, content } });
|
|
@@ -1947,6 +2007,7 @@ function AssistSheet({
|
|
|
1947
2007
|
pageShot = null,
|
|
1948
2008
|
onAddFiles,
|
|
1949
2009
|
files = NO_FILES,
|
|
2010
|
+
canReadPdf = false,
|
|
1950
2011
|
onRemoveFile,
|
|
1951
2012
|
onRemoveImage,
|
|
1952
2013
|
attachmentCount,
|
|
@@ -1974,6 +2035,8 @@ function AssistSheet({
|
|
|
1974
2035
|
isSubmitting,
|
|
1975
2036
|
submitted,
|
|
1976
2037
|
onSend,
|
|
2038
|
+
similarSlot,
|
|
2039
|
+
submittedText,
|
|
1977
2040
|
onDegrade,
|
|
1978
2041
|
onClose,
|
|
1979
2042
|
onFinish,
|
|
@@ -1997,6 +2060,7 @@ function AssistSheet({
|
|
|
1997
2060
|
const [autoFileLeft, setAutoFileLeft] = useState2(null);
|
|
1998
2061
|
const scrollRef = useRef2(null);
|
|
1999
2062
|
const draftRef = useRef2(null);
|
|
2063
|
+
const composerRef = useRef2(null);
|
|
2000
2064
|
const seededRef = useRef2(false);
|
|
2001
2065
|
const seedPendingRef = useRef2(false);
|
|
2002
2066
|
const eventsRef = useRef2([]);
|
|
@@ -2038,6 +2102,12 @@ function AssistSheet({
|
|
|
2038
2102
|
var _a;
|
|
2039
2103
|
if (phase === "draft") (_a = draftRef.current) == null ? void 0 : _a.focus();
|
|
2040
2104
|
}, [phase]);
|
|
2105
|
+
useEffect2(() => {
|
|
2106
|
+
const el = composerRef.current;
|
|
2107
|
+
if (!el) return;
|
|
2108
|
+
el.style.height = "auto";
|
|
2109
|
+
el.style.height = `${el.scrollHeight + 2}px`;
|
|
2110
|
+
}, [input]);
|
|
2041
2111
|
useEffect2(() => {
|
|
2042
2112
|
if (autoFileLeft === null) return;
|
|
2043
2113
|
if (isSubmitting || submitted || phase !== "draft") {
|
|
@@ -2264,6 +2334,7 @@ function AssistSheet({
|
|
|
2264
2334
|
@keyframes gg-sheet-up{from{transform:translateY(24px);opacity:0}to{transform:translateY(0);opacity:1}}
|
|
2265
2335
|
@keyframes gg-sheet-fade{from{opacity:0}to{opacity:1}}
|
|
2266
2336
|
@keyframes gg-autofile{from{transform:scaleX(0)}to{transform:scaleX(1)}}
|
|
2337
|
+
@keyframes gg-spin{to{transform:rotate(360deg)}}
|
|
2267
2338
|
@keyframes gg-dot{0%,80%,100%{transform:translateY(0);opacity:.4}40%{transform:translateY(-3px);opacity:1}}
|
|
2268
2339
|
[data-gg-sheet-send]:focus-visible,[data-gg-chip]:focus-visible,[data-gg-assist-remove]:focus-visible,[data-gg-stop-auto-file]:focus-visible{outline:2px solid currentColor;outline-offset:2px}
|
|
2269
2340
|
` }),
|
|
@@ -3138,7 +3209,7 @@ function AssistSheet({
|
|
|
3138
3209
|
}
|
|
3139
3210
|
)
|
|
3140
3211
|
] }),
|
|
3141
|
-
submitted && /* @__PURE__ */
|
|
3212
|
+
submitted && /* @__PURE__ */ jsx3(
|
|
3142
3213
|
"div",
|
|
3143
3214
|
{
|
|
3144
3215
|
style: {
|
|
@@ -3148,10 +3219,7 @@ function AssistSheet({
|
|
|
3148
3219
|
fontSize: "14px",
|
|
3149
3220
|
fontWeight: 600
|
|
3150
3221
|
},
|
|
3151
|
-
children:
|
|
3152
|
-
reportTypeLabel,
|
|
3153
|
-
" sent. Thank you!"
|
|
3154
|
-
]
|
|
3222
|
+
children: submittedText != null ? submittedText : `${reportTypeLabel} sent. Thank you!`
|
|
3155
3223
|
}
|
|
3156
3224
|
)
|
|
3157
3225
|
]
|
|
@@ -3217,342 +3285,363 @@ function AssistSheet({
|
|
|
3217
3285
|
// instead of remounting, and a draft is something you can
|
|
3218
3286
|
// still reply to (#360), not a form that replaced the chat.
|
|
3219
3287
|
/* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
3220
|
-
(
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3288
|
+
/* @__PURE__ */ jsxs3("div", { style: { display: similarSlot ? "none" : "contents" }, children: [
|
|
3289
|
+
(screenshots.length > 0 || files.length > 0 || onAddFiles) && /* @__PURE__ */ jsxs3(
|
|
3290
|
+
"div",
|
|
3291
|
+
{
|
|
3292
|
+
style: {
|
|
3293
|
+
display: "flex",
|
|
3294
|
+
gap: "8px",
|
|
3295
|
+
alignItems: "center",
|
|
3296
|
+
flexWrap: "wrap",
|
|
3297
|
+
marginBottom: "8px"
|
|
3298
|
+
},
|
|
3299
|
+
children: [
|
|
3300
|
+
screenshots.map((shot, i) => {
|
|
3301
|
+
const read = readShots.has(shot);
|
|
3302
|
+
return /* @__PURE__ */ jsxs3(
|
|
3303
|
+
"div",
|
|
3304
|
+
{
|
|
3305
|
+
style: {
|
|
3306
|
+
position: "relative",
|
|
3307
|
+
width: "40px",
|
|
3308
|
+
height: "40px",
|
|
3309
|
+
flexShrink: 0
|
|
3310
|
+
},
|
|
3311
|
+
children: [
|
|
3312
|
+
/* @__PURE__ */ jsx3(
|
|
3313
|
+
"img",
|
|
3314
|
+
{
|
|
3315
|
+
src: shot,
|
|
3316
|
+
alt: read ? "Attached image the assistant reads" : "Attached image",
|
|
3317
|
+
"data-gg-assist-thumb": "",
|
|
3318
|
+
style: {
|
|
3319
|
+
display: "block",
|
|
3320
|
+
width: "100%",
|
|
3321
|
+
height: "100%",
|
|
3322
|
+
objectFit: "cover",
|
|
3323
|
+
borderRadius: "8px",
|
|
3324
|
+
border: `1px solid ${read ? t.accent : t.inputBorder}`,
|
|
3325
|
+
opacity: read ? 1 : 0.45,
|
|
3326
|
+
boxSizing: "border-box"
|
|
3327
|
+
}
|
|
3258
3328
|
}
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
}
|
|
3315
|
-
|
|
3329
|
+
),
|
|
3330
|
+
onRemoveImage && /* @__PURE__ */ jsx3(
|
|
3331
|
+
"button",
|
|
3332
|
+
{
|
|
3333
|
+
type: "button",
|
|
3334
|
+
"data-gg-assist-remove": "",
|
|
3335
|
+
"aria-label": `Remove image ${i + 1}`,
|
|
3336
|
+
title: "Remove this image",
|
|
3337
|
+
onClick: () => {
|
|
3338
|
+
stopAutoFile("changed attachments");
|
|
3339
|
+
logEvent(
|
|
3340
|
+
"image_removed",
|
|
3341
|
+
pageShot && shot === pageShot ? "page capture" : "attached"
|
|
3342
|
+
);
|
|
3343
|
+
onRemoveImage(i);
|
|
3344
|
+
},
|
|
3345
|
+
style: {
|
|
3346
|
+
position: "absolute",
|
|
3347
|
+
top: "-13px",
|
|
3348
|
+
right: "-13px",
|
|
3349
|
+
width: "32px",
|
|
3350
|
+
height: "32px",
|
|
3351
|
+
border: "none",
|
|
3352
|
+
background: "transparent",
|
|
3353
|
+
padding: 0,
|
|
3354
|
+
cursor: "pointer",
|
|
3355
|
+
display: "flex",
|
|
3356
|
+
alignItems: "center",
|
|
3357
|
+
justifyContent: "center",
|
|
3358
|
+
borderRadius: "50%"
|
|
3359
|
+
},
|
|
3360
|
+
children: /* @__PURE__ */ jsx3(
|
|
3361
|
+
"span",
|
|
3362
|
+
{
|
|
3363
|
+
"aria-hidden": "true",
|
|
3364
|
+
style: {
|
|
3365
|
+
width: "20px",
|
|
3366
|
+
height: "20px",
|
|
3367
|
+
boxSizing: "border-box",
|
|
3368
|
+
borderRadius: "50%",
|
|
3369
|
+
border: `2px solid ${t.bg}`,
|
|
3370
|
+
background: "#ef4444",
|
|
3371
|
+
color: "#fff",
|
|
3372
|
+
display: "flex",
|
|
3373
|
+
alignItems: "center",
|
|
3374
|
+
justifyContent: "center"
|
|
3375
|
+
},
|
|
3376
|
+
children: /* @__PURE__ */ jsx3("svg", { width: "8", height: "8", viewBox: "0 0 10 10", fill: "none", children: /* @__PURE__ */ jsx3(
|
|
3377
|
+
"path",
|
|
3378
|
+
{
|
|
3379
|
+
d: "M1 1L9 9M9 1L1 9",
|
|
3380
|
+
stroke: "currentColor",
|
|
3381
|
+
strokeWidth: "1.6",
|
|
3382
|
+
strokeLinecap: "round"
|
|
3383
|
+
}
|
|
3384
|
+
) })
|
|
3385
|
+
}
|
|
3386
|
+
)
|
|
3387
|
+
}
|
|
3388
|
+
)
|
|
3389
|
+
]
|
|
3390
|
+
},
|
|
3391
|
+
`${i}-${shot.slice(-16)}`
|
|
3392
|
+
);
|
|
3393
|
+
}),
|
|
3394
|
+
files.map((file, i) => {
|
|
3395
|
+
const read = readFiles.has(i);
|
|
3396
|
+
const pdf = isPdfFile(file.name);
|
|
3397
|
+
const why = read ? "the assistant reads this file" : pdf && file.reading ? "reading the text in this PDF\u2026" : pdf && file.text !== void 0 ? file.text === null ? file.tooLarge ? "attached to the report \u2014 too large for the assistant to read (over 3 MB)" : "attached to the report \u2014 couldn't read this PDF" : file.text.trim() ? "attached, but more than one message can carry, so the assistant is not reading it" : "attached to the report \u2014 this PDF has no text to read (a scan?)" : isAssistReadableFile(file.name) ? "attached, but more than one message can carry, so the assistant is not reading it" : "attached to the report \u2014 the assistant can't read this kind of file";
|
|
3398
|
+
return /* @__PURE__ */ jsxs3(
|
|
3399
|
+
"div",
|
|
3400
|
+
{
|
|
3401
|
+
"data-gg-assist-file": "",
|
|
3402
|
+
"data-gg-assist-file-read": read ? "" : void 0,
|
|
3403
|
+
"data-gg-assist-file-reading": pdf && file.reading ? "" : void 0,
|
|
3404
|
+
title: `${file.name} \u2014 ${why}`,
|
|
3405
|
+
style: {
|
|
3406
|
+
position: "relative",
|
|
3407
|
+
height: "40px",
|
|
3408
|
+
maxWidth: "170px",
|
|
3409
|
+
display: "flex",
|
|
3410
|
+
alignItems: "center",
|
|
3411
|
+
gap: "6px",
|
|
3412
|
+
padding: "0 10px",
|
|
3413
|
+
borderRadius: "8px",
|
|
3414
|
+
border: `1px solid ${read ? t.accent : t.inputBorder}`,
|
|
3415
|
+
opacity: read || pdf && file.reading ? 1 : 0.55,
|
|
3416
|
+
boxSizing: "border-box",
|
|
3417
|
+
flexShrink: 0
|
|
3418
|
+
},
|
|
3419
|
+
children: [
|
|
3420
|
+
pdf && file.reading ? /* @__PURE__ */ jsx3(
|
|
3421
|
+
"span",
|
|
3422
|
+
{
|
|
3423
|
+
role: "status",
|
|
3424
|
+
"aria-label": "Reading this PDF",
|
|
3425
|
+
style: {
|
|
3426
|
+
width: "12px",
|
|
3427
|
+
height: "12px",
|
|
3428
|
+
flexShrink: 0,
|
|
3429
|
+
borderRadius: "50%",
|
|
3430
|
+
border: `1.5px solid ${t.inputBorder}`,
|
|
3431
|
+
borderTopColor: t.accent,
|
|
3432
|
+
boxSizing: "border-box",
|
|
3433
|
+
animation: "gg-spin .8s linear infinite"
|
|
3316
3434
|
}
|
|
3317
|
-
|
|
3318
|
-
}
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3435
|
+
}
|
|
3436
|
+
) : /* @__PURE__ */ jsx3("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", style: { flexShrink: 0 }, children: /* @__PURE__ */ jsx3(
|
|
3437
|
+
"path",
|
|
3438
|
+
{
|
|
3439
|
+
d: "M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z M14 2v6h6",
|
|
3440
|
+
stroke: t.textMuted,
|
|
3441
|
+
strokeWidth: "1.5",
|
|
3442
|
+
strokeLinecap: "round",
|
|
3443
|
+
strokeLinejoin: "round"
|
|
3444
|
+
}
|
|
3445
|
+
) }),
|
|
3446
|
+
/* @__PURE__ */ jsx3(
|
|
3447
|
+
"span",
|
|
3448
|
+
{
|
|
3449
|
+
style: {
|
|
3450
|
+
fontSize: "11.5px",
|
|
3451
|
+
color: t.text,
|
|
3452
|
+
overflow: "hidden",
|
|
3453
|
+
textOverflow: "ellipsis",
|
|
3454
|
+
whiteSpace: "nowrap"
|
|
3455
|
+
},
|
|
3456
|
+
children: file.name
|
|
3457
|
+
}
|
|
3458
|
+
),
|
|
3459
|
+
onRemoveFile && /* @__PURE__ */ jsx3(
|
|
3460
|
+
"button",
|
|
3461
|
+
{
|
|
3462
|
+
type: "button",
|
|
3463
|
+
"data-gg-assist-remove-file": "",
|
|
3464
|
+
"aria-label": `Remove file ${file.name}`,
|
|
3465
|
+
title: "Remove this file",
|
|
3466
|
+
onClick: () => {
|
|
3467
|
+
stopAutoFile("changed attachments");
|
|
3468
|
+
logEvent("file_removed", read ? "read" : "not read");
|
|
3469
|
+
onRemoveFile(i);
|
|
3470
|
+
},
|
|
3471
|
+
style: {
|
|
3472
|
+
position: "absolute",
|
|
3473
|
+
top: "-13px",
|
|
3474
|
+
right: "-13px",
|
|
3475
|
+
width: "32px",
|
|
3476
|
+
height: "32px",
|
|
3477
|
+
border: "none",
|
|
3478
|
+
background: "transparent",
|
|
3479
|
+
padding: 0,
|
|
3480
|
+
cursor: "pointer",
|
|
3481
|
+
display: "flex",
|
|
3482
|
+
alignItems: "center",
|
|
3483
|
+
justifyContent: "center",
|
|
3484
|
+
borderRadius: "50%"
|
|
3485
|
+
},
|
|
3486
|
+
children: /* @__PURE__ */ jsx3(
|
|
3487
|
+
"span",
|
|
3488
|
+
{
|
|
3489
|
+
"aria-hidden": "true",
|
|
3490
|
+
style: {
|
|
3491
|
+
width: "20px",
|
|
3492
|
+
height: "20px",
|
|
3493
|
+
boxSizing: "border-box",
|
|
3494
|
+
borderRadius: "50%",
|
|
3495
|
+
border: `2px solid ${t.bg}`,
|
|
3496
|
+
background: "#ef4444",
|
|
3497
|
+
color: "#fff",
|
|
3498
|
+
display: "flex",
|
|
3499
|
+
alignItems: "center",
|
|
3500
|
+
justifyContent: "center"
|
|
3501
|
+
},
|
|
3502
|
+
children: /* @__PURE__ */ jsx3("svg", { width: "8", height: "8", viewBox: "0 0 10 10", fill: "none", children: /* @__PURE__ */ jsx3(
|
|
3503
|
+
"path",
|
|
3504
|
+
{
|
|
3505
|
+
d: "M1 1L9 9M9 1L1 9",
|
|
3506
|
+
stroke: "currentColor",
|
|
3507
|
+
strokeWidth: "1.6",
|
|
3508
|
+
strokeLinecap: "round"
|
|
3509
|
+
}
|
|
3510
|
+
) })
|
|
3511
|
+
}
|
|
3512
|
+
)
|
|
3513
|
+
}
|
|
3514
|
+
)
|
|
3515
|
+
]
|
|
3516
|
+
},
|
|
3517
|
+
`file-${i}-${file.name}`
|
|
3518
|
+
);
|
|
3519
|
+
}),
|
|
3520
|
+
onAddFiles && /* @__PURE__ */ jsxs3(
|
|
3521
|
+
"label",
|
|
3330
3522
|
{
|
|
3331
|
-
"data-gg-assist-
|
|
3332
|
-
"
|
|
3333
|
-
title: `${file.name} \u2014 ${why}`,
|
|
3523
|
+
"data-gg-assist-attach": "",
|
|
3524
|
+
title: "Attach an image or a file (html, json, log\u2026) \u2014 or paste one",
|
|
3334
3525
|
style: {
|
|
3335
|
-
|
|
3526
|
+
width: "40px",
|
|
3336
3527
|
height: "40px",
|
|
3337
|
-
maxWidth: "170px",
|
|
3338
|
-
display: "flex",
|
|
3339
|
-
alignItems: "center",
|
|
3340
|
-
gap: "6px",
|
|
3341
|
-
padding: "0 10px",
|
|
3342
3528
|
borderRadius: "8px",
|
|
3343
|
-
border: `1px
|
|
3344
|
-
|
|
3345
|
-
|
|
3529
|
+
border: `1px dashed ${t.inputBorder}`,
|
|
3530
|
+
display: "inline-flex",
|
|
3531
|
+
alignItems: "center",
|
|
3532
|
+
justifyContent: "center",
|
|
3533
|
+
color: t.textMuted,
|
|
3534
|
+
fontSize: "18px",
|
|
3535
|
+
cursor: "pointer",
|
|
3346
3536
|
flexShrink: 0
|
|
3347
3537
|
},
|
|
3348
3538
|
children: [
|
|
3349
|
-
|
|
3350
|
-
"path",
|
|
3351
|
-
{
|
|
3352
|
-
d: "M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z M14 2v6h6",
|
|
3353
|
-
stroke: t.textMuted,
|
|
3354
|
-
strokeWidth: "1.5",
|
|
3355
|
-
strokeLinecap: "round",
|
|
3356
|
-
strokeLinejoin: "round"
|
|
3357
|
-
}
|
|
3358
|
-
) }),
|
|
3539
|
+
"+",
|
|
3359
3540
|
/* @__PURE__ */ jsx3(
|
|
3360
|
-
"
|
|
3361
|
-
{
|
|
3362
|
-
style: {
|
|
3363
|
-
fontSize: "11.5px",
|
|
3364
|
-
color: t.text,
|
|
3365
|
-
overflow: "hidden",
|
|
3366
|
-
textOverflow: "ellipsis",
|
|
3367
|
-
whiteSpace: "nowrap"
|
|
3368
|
-
},
|
|
3369
|
-
children: file.name
|
|
3370
|
-
}
|
|
3371
|
-
),
|
|
3372
|
-
onRemoveFile && /* @__PURE__ */ jsx3(
|
|
3373
|
-
"button",
|
|
3541
|
+
"input",
|
|
3374
3542
|
{
|
|
3375
|
-
type: "
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
height: "32px",
|
|
3390
|
-
border: "none",
|
|
3391
|
-
background: "transparent",
|
|
3392
|
-
padding: 0,
|
|
3393
|
-
cursor: "pointer",
|
|
3394
|
-
display: "flex",
|
|
3395
|
-
alignItems: "center",
|
|
3396
|
-
justifyContent: "center",
|
|
3397
|
-
borderRadius: "50%"
|
|
3398
|
-
},
|
|
3399
|
-
children: /* @__PURE__ */ jsx3(
|
|
3400
|
-
"span",
|
|
3401
|
-
{
|
|
3402
|
-
"aria-hidden": "true",
|
|
3403
|
-
style: {
|
|
3404
|
-
width: "20px",
|
|
3405
|
-
height: "20px",
|
|
3406
|
-
boxSizing: "border-box",
|
|
3407
|
-
borderRadius: "50%",
|
|
3408
|
-
border: `2px solid ${t.bg}`,
|
|
3409
|
-
background: "#ef4444",
|
|
3410
|
-
color: "#fff",
|
|
3411
|
-
display: "flex",
|
|
3412
|
-
alignItems: "center",
|
|
3413
|
-
justifyContent: "center"
|
|
3414
|
-
},
|
|
3415
|
-
children: /* @__PURE__ */ jsx3("svg", { width: "8", height: "8", viewBox: "0 0 10 10", fill: "none", children: /* @__PURE__ */ jsx3(
|
|
3416
|
-
"path",
|
|
3417
|
-
{
|
|
3418
|
-
d: "M1 1L9 9M9 1L1 9",
|
|
3419
|
-
stroke: "currentColor",
|
|
3420
|
-
strokeWidth: "1.6",
|
|
3421
|
-
strokeLinecap: "round"
|
|
3422
|
-
}
|
|
3423
|
-
) })
|
|
3543
|
+
type: "file",
|
|
3544
|
+
accept: canReadPdf ? ASSIST_ATTACHMENT_ACCEPT_WITH_PDF : ASSIST_ATTACHMENT_ACCEPT,
|
|
3545
|
+
multiple: true,
|
|
3546
|
+
onChange: (e) => {
|
|
3547
|
+
var _a;
|
|
3548
|
+
const picked = Array.from((_a = e.target.files) != null ? _a : []);
|
|
3549
|
+
if (picked.length) {
|
|
3550
|
+
stopAutoFile("changed attachments");
|
|
3551
|
+
const images = picked.filter((f) => f.type.startsWith("image/")).length;
|
|
3552
|
+
if (images) logEvent("image_added", String(images));
|
|
3553
|
+
if (picked.length > images) {
|
|
3554
|
+
logEvent("file_added", String(picked.length - images));
|
|
3555
|
+
}
|
|
3556
|
+
onAddFiles(picked);
|
|
3424
3557
|
}
|
|
3425
|
-
|
|
3558
|
+
e.target.value = "";
|
|
3559
|
+
},
|
|
3560
|
+
style: { display: "none" }
|
|
3426
3561
|
}
|
|
3427
3562
|
)
|
|
3428
3563
|
]
|
|
3429
|
-
}
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
style: {
|
|
3439
|
-
width: "40px",
|
|
3440
|
-
height: "40px",
|
|
3441
|
-
borderRadius: "8px",
|
|
3442
|
-
border: `1px dashed ${t.inputBorder}`,
|
|
3443
|
-
display: "inline-flex",
|
|
3444
|
-
alignItems: "center",
|
|
3445
|
-
justifyContent: "center",
|
|
3446
|
-
color: t.textMuted,
|
|
3447
|
-
fontSize: "18px",
|
|
3448
|
-
cursor: "pointer",
|
|
3449
|
-
flexShrink: 0
|
|
3450
|
-
},
|
|
3451
|
-
children: [
|
|
3452
|
-
"+",
|
|
3453
|
-
/* @__PURE__ */ jsx3(
|
|
3454
|
-
"input",
|
|
3455
|
-
{
|
|
3456
|
-
type: "file",
|
|
3457
|
-
accept: ASSIST_ATTACHMENT_ACCEPT,
|
|
3458
|
-
multiple: true,
|
|
3459
|
-
onChange: (e) => {
|
|
3460
|
-
var _a;
|
|
3461
|
-
const picked = Array.from((_a = e.target.files) != null ? _a : []);
|
|
3462
|
-
if (picked.length) {
|
|
3463
|
-
stopAutoFile("changed attachments");
|
|
3464
|
-
const images = picked.filter((f) => f.type.startsWith("image/")).length;
|
|
3465
|
-
if (images) logEvent("image_added", String(images));
|
|
3466
|
-
if (picked.length > images) {
|
|
3467
|
-
logEvent("file_added", String(picked.length - images));
|
|
3468
|
-
}
|
|
3469
|
-
onAddFiles(picked);
|
|
3470
|
-
}
|
|
3471
|
-
e.target.value = "";
|
|
3472
|
-
},
|
|
3473
|
-
style: { display: "none" }
|
|
3474
|
-
}
|
|
3475
|
-
)
|
|
3476
|
-
]
|
|
3477
|
-
}
|
|
3478
|
-
),
|
|
3479
|
-
readShots.size < screenshots.length && /* @__PURE__ */ jsxs3("span", { style: { color: t.textMuted, fontSize: "11px" }, children: [
|
|
3480
|
-
readShots.size,
|
|
3481
|
-
" of ",
|
|
3482
|
-
screenshots.length,
|
|
3483
|
-
" read"
|
|
3484
|
-
] })
|
|
3485
|
-
]
|
|
3486
|
-
}
|
|
3487
|
-
),
|
|
3488
|
-
/* @__PURE__ */ jsxs3("div", { style: { display: "flex", gap: "8px", alignItems: "flex-end" }, children: [
|
|
3489
|
-
/* @__PURE__ */ jsx3(
|
|
3490
|
-
"textarea",
|
|
3491
|
-
{
|
|
3492
|
-
value: input,
|
|
3493
|
-
onChange: (e) => {
|
|
3494
|
-
stopAutoFile("typing a change");
|
|
3495
|
-
setInput(e.target.value);
|
|
3496
|
-
},
|
|
3497
|
-
onKeyDown: (e) => {
|
|
3498
|
-
if (e.key === "Enter" && !e.shiftKey) {
|
|
3499
|
-
e.preventDefault();
|
|
3500
|
-
sendTyped(input);
|
|
3501
|
-
}
|
|
3502
|
-
},
|
|
3503
|
-
placeholder: phase === "draft" ? "Anything to change? Tell me here\u2026" : "Type your answer\u2026",
|
|
3504
|
-
rows: 1,
|
|
3505
|
-
disabled: busy,
|
|
3506
|
-
autoFocus: true,
|
|
3507
|
-
style: {
|
|
3508
|
-
flex: 1,
|
|
3509
|
-
minHeight: "42px",
|
|
3510
|
-
maxHeight: "120px",
|
|
3511
|
-
padding: "11px 12px",
|
|
3512
|
-
borderRadius: "10px",
|
|
3513
|
-
border: `1px solid ${t.inputBorder}`,
|
|
3514
|
-
backgroundColor: t.inputBg,
|
|
3515
|
-
color: t.text,
|
|
3516
|
-
fontSize: "13px",
|
|
3517
|
-
lineHeight: 1.4,
|
|
3518
|
-
fontFamily: "inherit",
|
|
3519
|
-
resize: "none",
|
|
3520
|
-
outline: "none",
|
|
3521
|
-
boxSizing: "border-box"
|
|
3522
|
-
}
|
|
3564
|
+
}
|
|
3565
|
+
),
|
|
3566
|
+
readShots.size < screenshots.length && /* @__PURE__ */ jsxs3("span", { style: { color: t.textMuted, fontSize: "11px" }, children: [
|
|
3567
|
+
readShots.size,
|
|
3568
|
+
" of ",
|
|
3569
|
+
screenshots.length,
|
|
3570
|
+
" read"
|
|
3571
|
+
] })
|
|
3572
|
+
]
|
|
3523
3573
|
}
|
|
3524
3574
|
),
|
|
3525
|
-
/* @__PURE__ */
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
|
|
3575
|
+
/* @__PURE__ */ jsxs3("div", { style: { display: "flex", gap: "8px", alignItems: "flex-end" }, children: [
|
|
3576
|
+
/* @__PURE__ */ jsx3(
|
|
3577
|
+
"textarea",
|
|
3578
|
+
{
|
|
3579
|
+
ref: composerRef,
|
|
3580
|
+
value: input,
|
|
3581
|
+
onChange: (e) => {
|
|
3582
|
+
stopAutoFile("typing a change");
|
|
3583
|
+
setInput(e.target.value);
|
|
3584
|
+
},
|
|
3585
|
+
onKeyDown: (e) => {
|
|
3586
|
+
if (e.key === "Enter" && !e.shiftKey) {
|
|
3587
|
+
e.preventDefault();
|
|
3588
|
+
sendTyped(input);
|
|
3589
|
+
}
|
|
3590
|
+
},
|
|
3591
|
+
placeholder: phase === "draft" ? "Anything to change? Tell me here\u2026" : "Type your answer\u2026",
|
|
3592
|
+
rows: 3,
|
|
3593
|
+
disabled: busy,
|
|
3594
|
+
autoFocus: true,
|
|
3595
|
+
style: {
|
|
3596
|
+
flex: 1,
|
|
3597
|
+
minHeight: "72px",
|
|
3598
|
+
maxHeight: "220px",
|
|
3599
|
+
padding: "11px 12px",
|
|
3600
|
+
borderRadius: "10px",
|
|
3601
|
+
border: `1px solid ${t.inputBorder}`,
|
|
3602
|
+
backgroundColor: t.inputBg,
|
|
3603
|
+
color: t.text,
|
|
3604
|
+
fontSize: "13px",
|
|
3605
|
+
lineHeight: 1.4,
|
|
3606
|
+
fontFamily: "inherit",
|
|
3607
|
+
resize: "none",
|
|
3608
|
+
outline: "none",
|
|
3609
|
+
boxSizing: "border-box"
|
|
3552
3610
|
}
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3611
|
+
}
|
|
3612
|
+
),
|
|
3613
|
+
/* @__PURE__ */ jsx3(
|
|
3614
|
+
"button",
|
|
3615
|
+
{
|
|
3616
|
+
type: "button",
|
|
3617
|
+
"data-gg-sheet-send": "",
|
|
3618
|
+
onClick: () => sendTyped(input),
|
|
3619
|
+
disabled: busy || !input.trim(),
|
|
3620
|
+
"aria-label": "Send message",
|
|
3621
|
+
"data-gg-send-message": "",
|
|
3622
|
+
style: {
|
|
3623
|
+
flexShrink: 0,
|
|
3624
|
+
width: "42px",
|
|
3625
|
+
height: "42px",
|
|
3626
|
+
borderRadius: "10px",
|
|
3627
|
+
border: "none",
|
|
3628
|
+
display: "inline-flex",
|
|
3629
|
+
alignItems: "center",
|
|
3630
|
+
justifyContent: "center",
|
|
3631
|
+
backgroundColor: busy || !input.trim() ? t.bgSecondary : t.accent,
|
|
3632
|
+
color: busy || !input.trim() ? t.textMuted : t.accentText,
|
|
3633
|
+
cursor: busy || !input.trim() ? "default" : "pointer"
|
|
3634
|
+
},
|
|
3635
|
+
children: /* @__PURE__ */ jsx3("svg", { width: "17", height: "17", viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx3(
|
|
3636
|
+
"path",
|
|
3637
|
+
{
|
|
3638
|
+
d: "M3 20.5L21.5 12 3 3.5V10l12 2-12 2v6.5z",
|
|
3639
|
+
fill: "currentColor"
|
|
3640
|
+
}
|
|
3641
|
+
) })
|
|
3642
|
+
}
|
|
3643
|
+
)
|
|
3644
|
+
] })
|
|
3556
3645
|
] }),
|
|
3557
3646
|
phase === "draft" && /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
3558
3647
|
validationError && /* @__PURE__ */ jsx3(
|
|
@@ -3566,7 +3655,7 @@ function AssistSheet({
|
|
|
3566
3655
|
children: validationError
|
|
3567
3656
|
}
|
|
3568
3657
|
),
|
|
3569
|
-
autoFileLeft !== null ? (
|
|
3658
|
+
similarSlot ? similarSlot : autoFileLeft !== null ? (
|
|
3570
3659
|
// #402: "Yes, that's it" was the confirmation. The draft
|
|
3571
3660
|
// files itself unless they stop it — the bar shows how
|
|
3572
3661
|
// long is left, the button sends now, the × on its right
|
|
@@ -4622,6 +4711,7 @@ function ReportDialog({
|
|
|
4622
4711
|
enhanceText: enhanceText2,
|
|
4623
4712
|
assist,
|
|
4624
4713
|
findSimilarIssues: findSimilarIssues2,
|
|
4714
|
+
readFile,
|
|
4625
4715
|
assistContext = null,
|
|
4626
4716
|
transcribeAudio: transcribeAudio2,
|
|
4627
4717
|
types,
|
|
@@ -4675,6 +4765,15 @@ function ReportDialog({
|
|
|
4675
4765
|
const pendingShotRef = useRef4(null);
|
|
4676
4766
|
const openGenRef = useRef4(0);
|
|
4677
4767
|
const [attachments, setAttachments] = useState4([]);
|
|
4768
|
+
const [pdfTexts, setPdfTexts] = useState4({});
|
|
4769
|
+
const heldFiles = useMemo2(
|
|
4770
|
+
() => attachments.map((a) => {
|
|
4771
|
+
const read = pdfTexts[heldFileKey(a)];
|
|
4772
|
+
if (!read) return a;
|
|
4773
|
+
return "reading" in read ? { ...a, reading: true } : { ...a, text: read.text, ...read.tooLarge ? { tooLarge: true } : {} };
|
|
4774
|
+
}),
|
|
4775
|
+
[attachments, pdfTexts]
|
|
4776
|
+
);
|
|
4678
4777
|
const [previewIndex, setPreviewIndex] = useState4(null);
|
|
4679
4778
|
const [annotatingIndex, setAnnotatingIndex] = useState4(null);
|
|
4680
4779
|
const [previewZoomed, setPreviewZoomed] = useState4(false);
|
|
@@ -4835,10 +4934,16 @@ function ReportDialog({
|
|
|
4835
4934
|
files.filter((file) => !file.type.startsWith("image/")).forEach((file) => {
|
|
4836
4935
|
const reader = new FileReader();
|
|
4837
4936
|
reader.onload = () => {
|
|
4838
|
-
|
|
4839
|
-
|
|
4840
|
-
|
|
4841
|
-
]);
|
|
4937
|
+
const dataUrl = reader.result;
|
|
4938
|
+
setAttachments((prev) => [...prev, { name: file.name, size: file.size, dataUrl }]);
|
|
4939
|
+
const key = heldFileKey(file);
|
|
4940
|
+
if (!readFile || !isPdfFile(file.name) || pdfTexts[key]) return;
|
|
4941
|
+
if (file.size > MAX_READABLE_PDF_BYTES) {
|
|
4942
|
+
setPdfTexts((prev) => ({ ...prev, [key]: { text: null, tooLarge: true } }));
|
|
4943
|
+
return;
|
|
4944
|
+
}
|
|
4945
|
+
setPdfTexts((prev) => ({ ...prev, [key]: { reading: true } }));
|
|
4946
|
+
void Promise.resolve().then(() => readFile({ name: file.name, dataUrl })).catch(() => null).then((text) => setPdfTexts((prev) => ({ ...prev, [key]: { text } })));
|
|
4842
4947
|
};
|
|
4843
4948
|
reader.readAsDataURL(file);
|
|
4844
4949
|
});
|
|
@@ -5274,6 +5379,10 @@ function ReportDialog({
|
|
|
5274
5379
|
stopListeningAndTranscribe();
|
|
5275
5380
|
}
|
|
5276
5381
|
};
|
|
5382
|
+
useEffect4(() => {
|
|
5383
|
+
setSimilarIssues(null);
|
|
5384
|
+
setPickedSimilar(null);
|
|
5385
|
+
}, [description]);
|
|
5277
5386
|
const handleSubmit = async (opts) => {
|
|
5278
5387
|
var _a2, _b, _c;
|
|
5279
5388
|
try {
|
|
@@ -5314,11 +5423,17 @@ function ReportDialog({
|
|
|
5314
5423
|
}
|
|
5315
5424
|
const duplicateOf = (_a2 = opts == null ? void 0 : opts.duplicateOf) != null ? _a2 : duplicateIssueNumber;
|
|
5316
5425
|
const attachKind = (opts == null ? void 0 : opts.duplicateOf) ? (_b = opts.kind) != null ? _b : "duplicate" : duplicateKind;
|
|
5317
|
-
if (findSimilarIssues2 && !(opts == null ? void 0 : opts.skipSimilar) && !duplicateOf
|
|
5426
|
+
if (findSimilarIssues2 && !(opts == null ? void 0 : opts.skipSimilar) && !duplicateOf) {
|
|
5427
|
+
const similarFiles = filesForSimilar(
|
|
5428
|
+
heldFiles,
|
|
5429
|
+
MAX_SIMILAR_FILE_CHARS,
|
|
5430
|
+
MAX_SIMILAR_FILES_TOTAL_CHARS
|
|
5431
|
+
);
|
|
5318
5432
|
setIsSubmitting(true);
|
|
5319
5433
|
setCheckingSimilar(true);
|
|
5320
5434
|
const matches = await Promise.race([
|
|
5321
|
-
|
|
5435
|
+
// A PDF still being read is left out rather than waited for.
|
|
5436
|
+
(similarFiles.length ? findSimilarIssues2(description.trim(), similarFiles) : findSimilarIssues2(description.trim())).catch(() => null),
|
|
5322
5437
|
new Promise((resolve) => setTimeout(() => resolve(null), SIMILAR_WAIT_MS))
|
|
5323
5438
|
]);
|
|
5324
5439
|
setCheckingSimilar(false);
|
|
@@ -5350,6 +5465,8 @@ function ReportDialog({
|
|
|
5350
5465
|
if (duplicateOf) {
|
|
5351
5466
|
metadata.duplicateIssueNumber = String(duplicateOf);
|
|
5352
5467
|
if (attachKind === "related") metadata.duplicateKind = "related";
|
|
5468
|
+
} else if (opts == null ? void 0 : opts.possiblyRelated) {
|
|
5469
|
+
metadata.possiblyRelatedIssue = String(opts.possiblyRelated);
|
|
5353
5470
|
}
|
|
5354
5471
|
if (aiConversationId) {
|
|
5355
5472
|
metadata.aiConversationId = aiConversationId;
|
|
@@ -5363,7 +5480,9 @@ function ReportDialog({
|
|
|
5363
5480
|
Object.keys(metadata).length > 0 ? metadata : void 0
|
|
5364
5481
|
);
|
|
5365
5482
|
if (result) {
|
|
5366
|
-
setAddedToIssue(
|
|
5483
|
+
setAddedToIssue(
|
|
5484
|
+
duplicateOf && result.issueNumber === duplicateOf ? duplicateOf : result.status === "DUPLICATE" && result.issueNumber ? result.issueNumber : null
|
|
5485
|
+
);
|
|
5367
5486
|
setAddedAsRelated(!!duplicateOf && result.issueNumber === duplicateOf && attachKind === "related");
|
|
5368
5487
|
const queued = result.status === "QUEUED" && !!result.message;
|
|
5369
5488
|
setQueuedMessage(queued ? (_c = result.message) != null ? _c : null : null);
|
|
@@ -5371,6 +5490,7 @@ function ReportDialog({
|
|
|
5371
5490
|
setDescription("");
|
|
5372
5491
|
setScreenshots([]);
|
|
5373
5492
|
setAttachments([]);
|
|
5493
|
+
setPdfTexts({});
|
|
5374
5494
|
setAiConversationId(null);
|
|
5375
5495
|
setIssueTitle("");
|
|
5376
5496
|
setDuplicateIssueNumber(null);
|
|
@@ -5391,6 +5511,220 @@ function ReportDialog({
|
|
|
5391
5511
|
setAttachingTo(null);
|
|
5392
5512
|
}
|
|
5393
5513
|
};
|
|
5514
|
+
const similarCard = similarIssues ? /* @__PURE__ */ jsxs5(
|
|
5515
|
+
"div",
|
|
5516
|
+
{
|
|
5517
|
+
"data-gg-similar": "",
|
|
5518
|
+
style: {
|
|
5519
|
+
marginTop: "12px",
|
|
5520
|
+
display: "flex",
|
|
5521
|
+
flexDirection: "column",
|
|
5522
|
+
gap: "10px",
|
|
5523
|
+
padding: "12px",
|
|
5524
|
+
borderRadius: "10px",
|
|
5525
|
+
border: "1px solid rgba(245,158,11,0.4)",
|
|
5526
|
+
backgroundColor: "rgba(245,158,11,0.08)"
|
|
5527
|
+
},
|
|
5528
|
+
children: [
|
|
5529
|
+
/* @__PURE__ */ jsxs5("div", { style: { display: "flex", flexDirection: "column", gap: "2px" }, children: [
|
|
5530
|
+
/* @__PURE__ */ jsx5("span", { style: { color: t.text, fontWeight: 600, fontSize: "13px" }, children: "Our team may already have this, or something close" }),
|
|
5531
|
+
/* @__PURE__ */ jsx5("span", { style: { color: t.textMuted, fontSize: "12px", lineHeight: 1.5 }, children: similarIssues.length > 1 ? "Pick the one that matches. Same problem, or a different request for the same feature \u2014 it's added to that issue, so the team has it in one place." : "Same problem, or a different request for the same feature? Pick it \u2014 it's added to that issue, so the team has it in one place." })
|
|
5532
|
+
] }),
|
|
5533
|
+
/* @__PURE__ */ jsx5(
|
|
5534
|
+
"div",
|
|
5535
|
+
{
|
|
5536
|
+
role: "radiogroup",
|
|
5537
|
+
"aria-label": "Existing issues",
|
|
5538
|
+
style: { display: "flex", flexDirection: "column", gap: "6px" },
|
|
5539
|
+
children: similarIssues.map((issue) => {
|
|
5540
|
+
const picked = pickedSimilar === issue.number;
|
|
5541
|
+
return /* @__PURE__ */ jsxs5(
|
|
5542
|
+
"button",
|
|
5543
|
+
{
|
|
5544
|
+
type: "button",
|
|
5545
|
+
role: "radio",
|
|
5546
|
+
"aria-checked": picked,
|
|
5547
|
+
disabled: isSubmitting,
|
|
5548
|
+
onClick: () => setPickedSimilar(issue.number),
|
|
5549
|
+
style: {
|
|
5550
|
+
display: "flex",
|
|
5551
|
+
alignItems: "flex-start",
|
|
5552
|
+
gap: "10px",
|
|
5553
|
+
width: "100%",
|
|
5554
|
+
minHeight: "44px",
|
|
5555
|
+
padding: "9px 10px",
|
|
5556
|
+
borderRadius: "8px",
|
|
5557
|
+
border: `1px solid ${picked ? t.accent : t.border}`,
|
|
5558
|
+
boxShadow: picked ? `0 0 0 1px ${t.accent}` : "none",
|
|
5559
|
+
backgroundColor: t.bg,
|
|
5560
|
+
color: t.text,
|
|
5561
|
+
textAlign: "left",
|
|
5562
|
+
cursor: isSubmitting ? "not-allowed" : "pointer",
|
|
5563
|
+
fontFamily: "inherit"
|
|
5564
|
+
},
|
|
5565
|
+
children: [
|
|
5566
|
+
/* @__PURE__ */ jsx5(
|
|
5567
|
+
"span",
|
|
5568
|
+
{
|
|
5569
|
+
"aria-hidden": "true",
|
|
5570
|
+
style: {
|
|
5571
|
+
flexShrink: 0,
|
|
5572
|
+
marginTop: "2px",
|
|
5573
|
+
width: "14px",
|
|
5574
|
+
height: "14px",
|
|
5575
|
+
boxSizing: "border-box",
|
|
5576
|
+
borderRadius: "50%",
|
|
5577
|
+
border: `2px solid ${picked ? t.accent : t.textMuted}`,
|
|
5578
|
+
backgroundColor: picked ? t.accent : "transparent",
|
|
5579
|
+
boxShadow: picked ? `inset 0 0 0 2px ${t.bg}` : "none"
|
|
5580
|
+
}
|
|
5581
|
+
}
|
|
5582
|
+
),
|
|
5583
|
+
/* @__PURE__ */ jsxs5(
|
|
5584
|
+
"span",
|
|
5585
|
+
{
|
|
5586
|
+
style: {
|
|
5587
|
+
display: "flex",
|
|
5588
|
+
flexDirection: "column",
|
|
5589
|
+
gap: "3px",
|
|
5590
|
+
minWidth: 0
|
|
5591
|
+
},
|
|
5592
|
+
children: [
|
|
5593
|
+
/* @__PURE__ */ jsxs5(
|
|
5594
|
+
"span",
|
|
5595
|
+
{
|
|
5596
|
+
title: issue.title,
|
|
5597
|
+
style: {
|
|
5598
|
+
fontSize: "12.5px",
|
|
5599
|
+
lineHeight: 1.45,
|
|
5600
|
+
wordBreak: "break-word",
|
|
5601
|
+
overflow: "hidden",
|
|
5602
|
+
display: "-webkit-box",
|
|
5603
|
+
WebkitLineClamp: 3,
|
|
5604
|
+
WebkitBoxOrient: "vertical"
|
|
5605
|
+
},
|
|
5606
|
+
children: [
|
|
5607
|
+
/* @__PURE__ */ jsxs5("span", { style: { color: t.textMuted, fontWeight: 600 }, children: [
|
|
5608
|
+
"#",
|
|
5609
|
+
issue.number
|
|
5610
|
+
] }),
|
|
5611
|
+
" ",
|
|
5612
|
+
issue.title
|
|
5613
|
+
]
|
|
5614
|
+
}
|
|
5615
|
+
),
|
|
5616
|
+
issue.status && /* @__PURE__ */ jsx5("span", { style: { fontSize: "11.5px", color: t.textMuted }, children: issue.status })
|
|
5617
|
+
]
|
|
5618
|
+
}
|
|
5619
|
+
)
|
|
5620
|
+
]
|
|
5621
|
+
},
|
|
5622
|
+
issue.number
|
|
5623
|
+
);
|
|
5624
|
+
})
|
|
5625
|
+
}
|
|
5626
|
+
),
|
|
5627
|
+
/* @__PURE__ */ jsx5(
|
|
5628
|
+
"button",
|
|
5629
|
+
{
|
|
5630
|
+
type: "button",
|
|
5631
|
+
disabled: isSubmitting || !pickedSimilar,
|
|
5632
|
+
onClick: () => {
|
|
5633
|
+
if (!pickedSimilar) return;
|
|
5634
|
+
setAttachingTo(pickedSimilar);
|
|
5635
|
+
setDuplicateIssueNumber(pickedSimilar);
|
|
5636
|
+
setDuplicateKind("duplicate");
|
|
5637
|
+
void handleSubmit({ duplicateOf: pickedSimilar, kind: "duplicate" });
|
|
5638
|
+
},
|
|
5639
|
+
style: {
|
|
5640
|
+
width: "100%",
|
|
5641
|
+
minHeight: "40px",
|
|
5642
|
+
padding: "10px",
|
|
5643
|
+
borderRadius: "8px",
|
|
5644
|
+
border: "none",
|
|
5645
|
+
backgroundColor: isSubmitting || !pickedSimilar ? t.bgSecondary : t.accent,
|
|
5646
|
+
color: isSubmitting || !pickedSimilar ? t.textMuted : t.accentText,
|
|
5647
|
+
fontSize: "14px",
|
|
5648
|
+
fontWeight: 600,
|
|
5649
|
+
cursor: isSubmitting || !pickedSimilar ? "not-allowed" : "pointer",
|
|
5650
|
+
fontFamily: "inherit"
|
|
5651
|
+
},
|
|
5652
|
+
children: typeof attachingTo === "number" && duplicateKind !== "related" ? /* @__PURE__ */ jsxs5(Fragment4, { children: [
|
|
5653
|
+
/* @__PURE__ */ jsx5(ButtonSpinner, {}),
|
|
5654
|
+
"Adding to #",
|
|
5655
|
+
attachingTo,
|
|
5656
|
+
"\u2026"
|
|
5657
|
+
] }) : pickedSimilar ? `Add to #${pickedSimilar}` : "Pick the matching issue"
|
|
5658
|
+
}
|
|
5659
|
+
),
|
|
5660
|
+
pickedSimilar && /* @__PURE__ */ jsx5(
|
|
5661
|
+
"button",
|
|
5662
|
+
{
|
|
5663
|
+
type: "button",
|
|
5664
|
+
disabled: isSubmitting,
|
|
5665
|
+
onClick: () => {
|
|
5666
|
+
setAttachingTo(pickedSimilar);
|
|
5667
|
+
setDuplicateIssueNumber(pickedSimilar);
|
|
5668
|
+
setDuplicateKind("related");
|
|
5669
|
+
void handleSubmit({ duplicateOf: pickedSimilar, kind: "related" });
|
|
5670
|
+
},
|
|
5671
|
+
style: {
|
|
5672
|
+
width: "100%",
|
|
5673
|
+
minHeight: "40px",
|
|
5674
|
+
marginTop: "-4px",
|
|
5675
|
+
padding: "9px 10px",
|
|
5676
|
+
borderRadius: "8px",
|
|
5677
|
+
border: "1px solid rgba(245,158,11,0.4)",
|
|
5678
|
+
backgroundColor: "transparent",
|
|
5679
|
+
color: isSubmitting ? t.textMuted : t.text,
|
|
5680
|
+
fontSize: "13px",
|
|
5681
|
+
fontWeight: 600,
|
|
5682
|
+
cursor: isSubmitting ? "not-allowed" : "pointer",
|
|
5683
|
+
fontFamily: "inherit"
|
|
5684
|
+
},
|
|
5685
|
+
children: typeof attachingTo === "number" && duplicateKind === "related" ? /* @__PURE__ */ jsxs5(Fragment4, { children: [
|
|
5686
|
+
/* @__PURE__ */ jsx5(ButtonSpinner, {}),
|
|
5687
|
+
"Adding to #",
|
|
5688
|
+
attachingTo,
|
|
5689
|
+
"\u2026"
|
|
5690
|
+
] }) : `Related request \u2014 add to #${pickedSimilar}`
|
|
5691
|
+
}
|
|
5692
|
+
),
|
|
5693
|
+
/* @__PURE__ */ jsx5(
|
|
5694
|
+
"button",
|
|
5695
|
+
{
|
|
5696
|
+
type: "button",
|
|
5697
|
+
disabled: isSubmitting,
|
|
5698
|
+
onClick: () => {
|
|
5699
|
+
var _a2;
|
|
5700
|
+
setAttachingTo("new");
|
|
5701
|
+
void handleSubmit({ skipSimilar: true, possiblyRelated: (_a2 = similarIssues[0]) == null ? void 0 : _a2.number });
|
|
5702
|
+
},
|
|
5703
|
+
style: {
|
|
5704
|
+
width: "100%",
|
|
5705
|
+
minHeight: "40px",
|
|
5706
|
+
marginTop: "-4px",
|
|
5707
|
+
padding: "8px",
|
|
5708
|
+
borderRadius: "8px",
|
|
5709
|
+
border: "none",
|
|
5710
|
+
backgroundColor: "transparent",
|
|
5711
|
+
color: isSubmitting ? t.textMuted : t.text,
|
|
5712
|
+
fontSize: "13px",
|
|
5713
|
+
fontWeight: 500,
|
|
5714
|
+
textDecoration: "underline",
|
|
5715
|
+
textUnderlineOffset: "3px",
|
|
5716
|
+
cursor: isSubmitting ? "not-allowed" : "pointer",
|
|
5717
|
+
fontFamily: "inherit"
|
|
5718
|
+
},
|
|
5719
|
+
children: attachingTo === "new" ? /* @__PURE__ */ jsxs5(Fragment4, { children: [
|
|
5720
|
+
/* @__PURE__ */ jsx5(ButtonSpinner, {}),
|
|
5721
|
+
"Sending as new\u2026"
|
|
5722
|
+
] }) : "No, mine is different \u2014 send as new"
|
|
5723
|
+
}
|
|
5724
|
+
)
|
|
5725
|
+
]
|
|
5726
|
+
}
|
|
5727
|
+
) : null;
|
|
5394
5728
|
if (!mounted) return null;
|
|
5395
5729
|
return /* @__PURE__ */ jsxs5(Fragment4, { children: [
|
|
5396
5730
|
isOpen && createPortal3(
|
|
@@ -6929,224 +7263,7 @@ function ReportDialog({
|
|
|
6929
7263
|
children: validationError
|
|
6930
7264
|
}
|
|
6931
7265
|
),
|
|
6932
|
-
|
|
6933
|
-
// Same visual language as the assistant's "Our team is
|
|
6934
|
-
// already on this" card, so both routes to a duplicate
|
|
6935
|
-
// read as one feature.
|
|
6936
|
-
/* @__PURE__ */ jsxs5(
|
|
6937
|
-
"div",
|
|
6938
|
-
{
|
|
6939
|
-
"data-gg-similar": "",
|
|
6940
|
-
style: {
|
|
6941
|
-
marginTop: "12px",
|
|
6942
|
-
display: "flex",
|
|
6943
|
-
flexDirection: "column",
|
|
6944
|
-
gap: "10px",
|
|
6945
|
-
padding: "12px",
|
|
6946
|
-
borderRadius: "10px",
|
|
6947
|
-
border: "1px solid rgba(245,158,11,0.4)",
|
|
6948
|
-
backgroundColor: "rgba(245,158,11,0.08)"
|
|
6949
|
-
},
|
|
6950
|
-
children: [
|
|
6951
|
-
/* @__PURE__ */ jsxs5("div", { style: { display: "flex", flexDirection: "column", gap: "2px" }, children: [
|
|
6952
|
-
/* @__PURE__ */ jsx5("span", { style: { color: t.text, fontWeight: 600, fontSize: "13px" }, children: "Our team may already have this, or something close" }),
|
|
6953
|
-
/* @__PURE__ */ jsx5("span", { style: { color: t.textMuted, fontSize: "12px", lineHeight: 1.5 }, children: similarIssues.length > 1 ? "Pick the one that matches. Same problem, or a different request for the same feature \u2014 it's added to that issue, so the team has it in one place." : "Same problem, or a different request for the same feature? Pick it \u2014 it's added to that issue, so the team has it in one place." })
|
|
6954
|
-
] }),
|
|
6955
|
-
/* @__PURE__ */ jsx5(
|
|
6956
|
-
"div",
|
|
6957
|
-
{
|
|
6958
|
-
role: "radiogroup",
|
|
6959
|
-
"aria-label": "Existing issues",
|
|
6960
|
-
style: { display: "flex", flexDirection: "column", gap: "6px" },
|
|
6961
|
-
children: similarIssues.map((issue) => {
|
|
6962
|
-
const picked = pickedSimilar === issue.number;
|
|
6963
|
-
return /* @__PURE__ */ jsxs5(
|
|
6964
|
-
"button",
|
|
6965
|
-
{
|
|
6966
|
-
type: "button",
|
|
6967
|
-
role: "radio",
|
|
6968
|
-
"aria-checked": picked,
|
|
6969
|
-
disabled: isSubmitting,
|
|
6970
|
-
onClick: () => setPickedSimilar(issue.number),
|
|
6971
|
-
style: {
|
|
6972
|
-
display: "flex",
|
|
6973
|
-
alignItems: "flex-start",
|
|
6974
|
-
gap: "10px",
|
|
6975
|
-
width: "100%",
|
|
6976
|
-
minHeight: "44px",
|
|
6977
|
-
padding: "9px 10px",
|
|
6978
|
-
borderRadius: "8px",
|
|
6979
|
-
border: `1px solid ${picked ? t.accent : t.border}`,
|
|
6980
|
-
boxShadow: picked ? `0 0 0 1px ${t.accent}` : "none",
|
|
6981
|
-
backgroundColor: t.bg,
|
|
6982
|
-
color: t.text,
|
|
6983
|
-
textAlign: "left",
|
|
6984
|
-
cursor: isSubmitting ? "not-allowed" : "pointer",
|
|
6985
|
-
fontFamily: "inherit"
|
|
6986
|
-
},
|
|
6987
|
-
children: [
|
|
6988
|
-
/* @__PURE__ */ jsx5(
|
|
6989
|
-
"span",
|
|
6990
|
-
{
|
|
6991
|
-
"aria-hidden": "true",
|
|
6992
|
-
style: {
|
|
6993
|
-
flexShrink: 0,
|
|
6994
|
-
marginTop: "2px",
|
|
6995
|
-
width: "14px",
|
|
6996
|
-
height: "14px",
|
|
6997
|
-
boxSizing: "border-box",
|
|
6998
|
-
borderRadius: "50%",
|
|
6999
|
-
border: `2px solid ${picked ? t.accent : t.textMuted}`,
|
|
7000
|
-
backgroundColor: picked ? t.accent : "transparent",
|
|
7001
|
-
boxShadow: picked ? `inset 0 0 0 2px ${t.bg}` : "none"
|
|
7002
|
-
}
|
|
7003
|
-
}
|
|
7004
|
-
),
|
|
7005
|
-
/* @__PURE__ */ jsxs5(
|
|
7006
|
-
"span",
|
|
7007
|
-
{
|
|
7008
|
-
style: {
|
|
7009
|
-
display: "flex",
|
|
7010
|
-
flexDirection: "column",
|
|
7011
|
-
gap: "3px",
|
|
7012
|
-
minWidth: 0
|
|
7013
|
-
},
|
|
7014
|
-
children: [
|
|
7015
|
-
/* @__PURE__ */ jsxs5(
|
|
7016
|
-
"span",
|
|
7017
|
-
{
|
|
7018
|
-
title: issue.title,
|
|
7019
|
-
style: {
|
|
7020
|
-
fontSize: "12.5px",
|
|
7021
|
-
lineHeight: 1.45,
|
|
7022
|
-
wordBreak: "break-word",
|
|
7023
|
-
overflow: "hidden",
|
|
7024
|
-
display: "-webkit-box",
|
|
7025
|
-
WebkitLineClamp: 3,
|
|
7026
|
-
WebkitBoxOrient: "vertical"
|
|
7027
|
-
},
|
|
7028
|
-
children: [
|
|
7029
|
-
/* @__PURE__ */ jsxs5("span", { style: { color: t.textMuted, fontWeight: 600 }, children: [
|
|
7030
|
-
"#",
|
|
7031
|
-
issue.number
|
|
7032
|
-
] }),
|
|
7033
|
-
" ",
|
|
7034
|
-
issue.title
|
|
7035
|
-
]
|
|
7036
|
-
}
|
|
7037
|
-
),
|
|
7038
|
-
issue.status && /* @__PURE__ */ jsx5("span", { style: { fontSize: "11.5px", color: t.textMuted }, children: issue.status })
|
|
7039
|
-
]
|
|
7040
|
-
}
|
|
7041
|
-
)
|
|
7042
|
-
]
|
|
7043
|
-
},
|
|
7044
|
-
issue.number
|
|
7045
|
-
);
|
|
7046
|
-
})
|
|
7047
|
-
}
|
|
7048
|
-
),
|
|
7049
|
-
/* @__PURE__ */ jsx5(
|
|
7050
|
-
"button",
|
|
7051
|
-
{
|
|
7052
|
-
type: "button",
|
|
7053
|
-
disabled: isSubmitting || !pickedSimilar,
|
|
7054
|
-
onClick: () => {
|
|
7055
|
-
if (!pickedSimilar) return;
|
|
7056
|
-
setAttachingTo(pickedSimilar);
|
|
7057
|
-
setDuplicateIssueNumber(pickedSimilar);
|
|
7058
|
-
setDuplicateKind("duplicate");
|
|
7059
|
-
void handleSubmit({ duplicateOf: pickedSimilar, kind: "duplicate" });
|
|
7060
|
-
},
|
|
7061
|
-
style: {
|
|
7062
|
-
width: "100%",
|
|
7063
|
-
minHeight: "40px",
|
|
7064
|
-
padding: "10px",
|
|
7065
|
-
borderRadius: "8px",
|
|
7066
|
-
border: "none",
|
|
7067
|
-
backgroundColor: isSubmitting || !pickedSimilar ? t.bgSecondary : t.accent,
|
|
7068
|
-
color: isSubmitting || !pickedSimilar ? t.textMuted : t.accentText,
|
|
7069
|
-
fontSize: "14px",
|
|
7070
|
-
fontWeight: 600,
|
|
7071
|
-
cursor: isSubmitting || !pickedSimilar ? "not-allowed" : "pointer",
|
|
7072
|
-
fontFamily: "inherit"
|
|
7073
|
-
},
|
|
7074
|
-
children: typeof attachingTo === "number" && duplicateKind !== "related" ? /* @__PURE__ */ jsxs5(Fragment4, { children: [
|
|
7075
|
-
/* @__PURE__ */ jsx5(ButtonSpinner, {}),
|
|
7076
|
-
"Adding to #",
|
|
7077
|
-
attachingTo,
|
|
7078
|
-
"\u2026"
|
|
7079
|
-
] }) : pickedSimilar ? `Add to #${pickedSimilar}` : "Pick the matching issue"
|
|
7080
|
-
}
|
|
7081
|
-
),
|
|
7082
|
-
pickedSimilar && /* @__PURE__ */ jsx5(
|
|
7083
|
-
"button",
|
|
7084
|
-
{
|
|
7085
|
-
type: "button",
|
|
7086
|
-
disabled: isSubmitting,
|
|
7087
|
-
onClick: () => {
|
|
7088
|
-
setAttachingTo(pickedSimilar);
|
|
7089
|
-
setDuplicateIssueNumber(pickedSimilar);
|
|
7090
|
-
setDuplicateKind("related");
|
|
7091
|
-
void handleSubmit({ duplicateOf: pickedSimilar, kind: "related" });
|
|
7092
|
-
},
|
|
7093
|
-
style: {
|
|
7094
|
-
width: "100%",
|
|
7095
|
-
minHeight: "40px",
|
|
7096
|
-
marginTop: "-4px",
|
|
7097
|
-
padding: "9px 10px",
|
|
7098
|
-
borderRadius: "8px",
|
|
7099
|
-
border: "1px solid rgba(245,158,11,0.4)",
|
|
7100
|
-
backgroundColor: "transparent",
|
|
7101
|
-
color: isSubmitting ? t.textMuted : t.text,
|
|
7102
|
-
fontSize: "13px",
|
|
7103
|
-
fontWeight: 600,
|
|
7104
|
-
cursor: isSubmitting ? "not-allowed" : "pointer",
|
|
7105
|
-
fontFamily: "inherit"
|
|
7106
|
-
},
|
|
7107
|
-
children: typeof attachingTo === "number" && duplicateKind === "related" ? /* @__PURE__ */ jsxs5(Fragment4, { children: [
|
|
7108
|
-
/* @__PURE__ */ jsx5(ButtonSpinner, {}),
|
|
7109
|
-
"Adding to #",
|
|
7110
|
-
attachingTo,
|
|
7111
|
-
"\u2026"
|
|
7112
|
-
] }) : `Related request \u2014 add to #${pickedSimilar}`
|
|
7113
|
-
}
|
|
7114
|
-
),
|
|
7115
|
-
/* @__PURE__ */ jsx5(
|
|
7116
|
-
"button",
|
|
7117
|
-
{
|
|
7118
|
-
type: "button",
|
|
7119
|
-
disabled: isSubmitting,
|
|
7120
|
-
onClick: () => {
|
|
7121
|
-
setAttachingTo("new");
|
|
7122
|
-
void handleSubmit({ skipSimilar: true });
|
|
7123
|
-
},
|
|
7124
|
-
style: {
|
|
7125
|
-
width: "100%",
|
|
7126
|
-
minHeight: "40px",
|
|
7127
|
-
marginTop: "-4px",
|
|
7128
|
-
padding: "8px",
|
|
7129
|
-
borderRadius: "8px",
|
|
7130
|
-
border: "none",
|
|
7131
|
-
backgroundColor: "transparent",
|
|
7132
|
-
color: isSubmitting ? t.textMuted : t.text,
|
|
7133
|
-
fontSize: "13px",
|
|
7134
|
-
fontWeight: 500,
|
|
7135
|
-
textDecoration: "underline",
|
|
7136
|
-
textUnderlineOffset: "3px",
|
|
7137
|
-
cursor: isSubmitting ? "not-allowed" : "pointer",
|
|
7138
|
-
fontFamily: "inherit"
|
|
7139
|
-
},
|
|
7140
|
-
children: attachingTo === "new" ? /* @__PURE__ */ jsxs5(Fragment4, { children: [
|
|
7141
|
-
/* @__PURE__ */ jsx5(ButtonSpinner, {}),
|
|
7142
|
-
"Sending as new\u2026"
|
|
7143
|
-
] }) : "No, mine is different \u2014 send as new"
|
|
7144
|
-
}
|
|
7145
|
-
)
|
|
7146
|
-
]
|
|
7147
|
-
}
|
|
7148
|
-
)
|
|
7149
|
-
) : /* @__PURE__ */ jsx5(
|
|
7266
|
+
similarCard != null ? similarCard : /* @__PURE__ */ jsx5(
|
|
7150
7267
|
"button",
|
|
7151
7268
|
{
|
|
7152
7269
|
type: "button",
|
|
@@ -7445,7 +7562,8 @@ function ReportDialog({
|
|
|
7445
7562
|
pageShot,
|
|
7446
7563
|
onAddFiles: addFiles,
|
|
7447
7564
|
onRemoveImage: (index) => setScreenshots((prev) => prev.filter((_, i) => i !== index)),
|
|
7448
|
-
files:
|
|
7565
|
+
files: heldFiles,
|
|
7566
|
+
canReadPdf: !!readFile,
|
|
7449
7567
|
onRemoveFile: (index) => setAttachments((prev) => prev.filter((_, i) => i !== index)),
|
|
7450
7568
|
attachmentCount: screenshots.length + attachments.length,
|
|
7451
7569
|
context: { ...assistContext != null ? assistContext : {}, reportType },
|
|
@@ -7459,6 +7577,10 @@ function ReportDialog({
|
|
|
7459
7577
|
setStep(2);
|
|
7460
7578
|
},
|
|
7461
7579
|
onDuplicateChange: (issueNumber, kind) => {
|
|
7580
|
+
if (issueNumber) {
|
|
7581
|
+
setSimilarIssues(null);
|
|
7582
|
+
setPickedSimilar(null);
|
|
7583
|
+
}
|
|
7462
7584
|
setDuplicateIssueNumber(issueNumber);
|
|
7463
7585
|
setDuplicateKind(issueNumber && kind === "related" ? "related" : "duplicate");
|
|
7464
7586
|
},
|
|
@@ -7488,7 +7610,9 @@ function ReportDialog({
|
|
|
7488
7610
|
severityRefused: needsSeverity,
|
|
7489
7611
|
isSubmitting,
|
|
7490
7612
|
submitted,
|
|
7491
|
-
onSend: () => void handleSubmit(
|
|
7613
|
+
onSend: () => void handleSubmit(),
|
|
7614
|
+
similarSlot: similarCard,
|
|
7615
|
+
submittedText: addedToIssue ? `Added to #${addedToIssue}${addedAsRelated ? " as a related request" : ""}. Thank you!` : null,
|
|
7492
7616
|
onReportGlitchgrabProblem: reportGlitchgrabProblem ? (prefill) => {
|
|
7493
7617
|
setSelfReportPrefill(prefill != null ? prefill : "");
|
|
7494
7618
|
setSelfReportFrom("assist");
|
|
@@ -7782,7 +7906,9 @@ function BookingDialog({
|
|
|
7782
7906
|
token,
|
|
7783
7907
|
baseUrl,
|
|
7784
7908
|
open,
|
|
7785
|
-
onClose
|
|
7909
|
+
onClose,
|
|
7910
|
+
preview = false,
|
|
7911
|
+
availabilityUrl
|
|
7786
7912
|
}) {
|
|
7787
7913
|
var _a, _b, _c, _d, _e;
|
|
7788
7914
|
const wide = useMediaQuery(WIDE_QUERY, true);
|
|
@@ -7810,15 +7936,18 @@ function BookingDialog({
|
|
|
7810
7936
|
const [answers, setAnswers] = useState5({});
|
|
7811
7937
|
const [pageLoaded, setPageLoaded] = useState5(false);
|
|
7812
7938
|
const [asked, setAsked] = useState5(false);
|
|
7939
|
+
const [requireOtp, setRequireOtp] = useState5(true);
|
|
7813
7940
|
const [code, setCode] = useState5("");
|
|
7814
7941
|
const [bookingId, setBookingId] = useState5(null);
|
|
7815
7942
|
const [meetUrl, setMeetUrl] = useState5(null);
|
|
7816
7943
|
const [busy, setBusy] = useState5(false);
|
|
7817
7944
|
const [error, setError] = useState5(null);
|
|
7818
|
-
const
|
|
7819
|
-
|
|
7820
|
-
|
|
7821
|
-
|
|
7945
|
+
const [previewEnded, setPreviewEnded] = useState5(false);
|
|
7946
|
+
const headers = useMemo3(() => {
|
|
7947
|
+
const out = { "Content-Type": "application/json" };
|
|
7948
|
+
if (token) out.Authorization = `Bearer ${token}`;
|
|
7949
|
+
return out;
|
|
7950
|
+
}, [token]);
|
|
7822
7951
|
const loadMonth = useCallback2(
|
|
7823
7952
|
async (target) => {
|
|
7824
7953
|
var _a2, _b2, _c2;
|
|
@@ -7830,8 +7959,9 @@ function BookingDialog({
|
|
|
7830
7959
|
return;
|
|
7831
7960
|
}
|
|
7832
7961
|
try {
|
|
7962
|
+
const source = availabilityUrl != null ? availabilityUrl : `${baseUrl}/api/v1/sdk/booking/availability`;
|
|
7833
7963
|
const res = await fetch(
|
|
7834
|
-
`${
|
|
7964
|
+
`${source}${source.includes("?") ? "&" : "?"}from=${encodeURIComponent(new Date(from).toISOString())}&to=${encodeURIComponent(new Date(to).toISOString())}${preview ? "&preview=1" : ""}`,
|
|
7835
7965
|
{ headers }
|
|
7836
7966
|
);
|
|
7837
7967
|
const json = await res.json();
|
|
@@ -7852,6 +7982,7 @@ function BookingDialog({
|
|
|
7852
7982
|
});
|
|
7853
7983
|
setWhatsappUrl((_c2 = data.whatsappUrl) != null ? _c2 : null);
|
|
7854
7984
|
setQuestions(readQuestions(data.questions));
|
|
7985
|
+
setRequireOtp(data.requireOtp !== false);
|
|
7855
7986
|
setPageLoaded(true);
|
|
7856
7987
|
} catch (e) {
|
|
7857
7988
|
setError("Couldn't reach the booking service");
|
|
@@ -7859,7 +7990,7 @@ function BookingDialog({
|
|
|
7859
7990
|
setPageLoaded(true);
|
|
7860
7991
|
}
|
|
7861
7992
|
},
|
|
7862
|
-
[baseUrl, headers]
|
|
7993
|
+
[baseUrl, headers, availabilityUrl, preview]
|
|
7863
7994
|
);
|
|
7864
7995
|
useEffect5(() => {
|
|
7865
7996
|
if (!open) return;
|
|
@@ -7873,6 +8004,7 @@ function BookingDialog({
|
|
|
7873
8004
|
setCode("");
|
|
7874
8005
|
setBookingId(null);
|
|
7875
8006
|
setError(null);
|
|
8007
|
+
setPreviewEnded(false);
|
|
7876
8008
|
setAvatarFailed(false);
|
|
7877
8009
|
setPageLoaded(false);
|
|
7878
8010
|
setAsked(false);
|
|
@@ -7909,8 +8041,12 @@ function BookingDialog({
|
|
|
7909
8041
|
const view = step !== "slots" ? step : !pageLoaded ? "loading" : questions.length > 0 && !asked ? "questions" : "slots";
|
|
7910
8042
|
const roomy = step === "slots";
|
|
7911
8043
|
async function startBooking() {
|
|
7912
|
-
var _a2;
|
|
8044
|
+
var _a2, _b2;
|
|
7913
8045
|
if (!selected) return;
|
|
8046
|
+
if (preview) {
|
|
8047
|
+
setPreviewEnded(true);
|
|
8048
|
+
return;
|
|
8049
|
+
}
|
|
7914
8050
|
setBusy(true);
|
|
7915
8051
|
setError(null);
|
|
7916
8052
|
try {
|
|
@@ -7925,7 +8061,10 @@ function BookingDialog({
|
|
|
7925
8061
|
answers: Object.fromEntries(given.map((q) => [q.id, q.answer])),
|
|
7926
8062
|
startsAt: selected.startsAt,
|
|
7927
8063
|
// The zone the times were shown in, which is the one the visitor chose.
|
|
7928
|
-
timezone: zone
|
|
8064
|
+
timezone: zone,
|
|
8065
|
+
// This dialog can show a booking confirmed in one step; the server
|
|
8066
|
+
// only skips the code for a dialog that says so.
|
|
8067
|
+
instant: true
|
|
7929
8068
|
})
|
|
7930
8069
|
});
|
|
7931
8070
|
const json = await res.json();
|
|
@@ -7933,6 +8072,11 @@ function BookingDialog({
|
|
|
7933
8072
|
setError((_a2 = json.error) != null ? _a2 : "Couldn't hold that slot");
|
|
7934
8073
|
return;
|
|
7935
8074
|
}
|
|
8075
|
+
if (json.data.confirmed) {
|
|
8076
|
+
setMeetUrl((_b2 = json.data.meetUrl) != null ? _b2 : null);
|
|
8077
|
+
setStep("done");
|
|
8078
|
+
return;
|
|
8079
|
+
}
|
|
7936
8080
|
setBookingId(json.data.bookingId);
|
|
7937
8081
|
setStep("code");
|
|
7938
8082
|
} catch (e) {
|
|
@@ -8096,6 +8240,22 @@ function BookingDialog({
|
|
|
8096
8240
|
) : null,
|
|
8097
8241
|
page.hostName && /* @__PURE__ */ jsx6("div", { style: { fontSize: 14, fontWeight: 500, ...muted }, children: page.hostName })
|
|
8098
8242
|
] }),
|
|
8243
|
+
preview && /* @__PURE__ */ jsx6(
|
|
8244
|
+
"div",
|
|
8245
|
+
{
|
|
8246
|
+
style: {
|
|
8247
|
+
alignSelf: "flex-start",
|
|
8248
|
+
padding: "3px 9px",
|
|
8249
|
+
borderRadius: 999,
|
|
8250
|
+
border: `1px dashed ${t.border}`,
|
|
8251
|
+
fontSize: 11.5,
|
|
8252
|
+
fontWeight: 600,
|
|
8253
|
+
letterSpacing: "0.02em",
|
|
8254
|
+
color: t.muted
|
|
8255
|
+
},
|
|
8256
|
+
children: "Preview \u2014 nothing is booked"
|
|
8257
|
+
}
|
|
8258
|
+
),
|
|
8099
8259
|
/* @__PURE__ */ jsx6("div", { style: { fontSize: 20, fontWeight: 600, lineHeight: 1.25, paddingRight: 32, overflowWrap: "anywhere" }, children: page.title }),
|
|
8100
8260
|
page.description && /* @__PURE__ */ jsx6("div", { style: { fontSize: 13, lineHeight: 1.5, overflowWrap: "anywhere", ...muted }, children: page.description }),
|
|
8101
8261
|
/* @__PURE__ */ jsxs6("div", { style: { display: "flex", flexDirection: "column", gap: 10, marginTop: 4, fontSize: 14 }, children: [
|
|
@@ -8533,6 +8693,7 @@ function BookingDialog({
|
|
|
8533
8693
|
},
|
|
8534
8694
|
children: /* @__PURE__ */ jsxs6("div", { style: { maxWidth: 440, display: "flex", flexDirection: "column", gap: 14 }, children: [
|
|
8535
8695
|
error && /* @__PURE__ */ jsx6("div", { style: { color: t.danger, fontSize: 13, lineHeight: 1.5 }, children: error }),
|
|
8696
|
+
previewEnded && /* @__PURE__ */ jsx6("div", { style: { fontSize: 13, lineHeight: 1.5, ...muted }, children: "End of the preview \u2014 a prospect would be booked here. Nothing was booked and no code was sent." }),
|
|
8536
8697
|
step === "details" && /* @__PURE__ */ jsxs6(Fragment5, { children: [
|
|
8537
8698
|
/* @__PURE__ */ jsx6(Field, { t, label: "Your name", value: name, onChange: setName, autoComplete: "name" }),
|
|
8538
8699
|
/* @__PURE__ */ jsx6(Field, { t, label: "Email", value: email, onChange: setEmail, type: "email", autoComplete: "email" }),
|
|
@@ -8545,7 +8706,7 @@ function BookingDialog({
|
|
|
8545
8706
|
onChange: setPhone,
|
|
8546
8707
|
type: "tel",
|
|
8547
8708
|
autoComplete: "tel",
|
|
8548
|
-
hint: "We send a code here to confirm, then a reminder before the call."
|
|
8709
|
+
hint: requireOtp ? "We send a code here to confirm, then a reminder before the call." : "We send a reminder here before the call."
|
|
8549
8710
|
}
|
|
8550
8711
|
),
|
|
8551
8712
|
/* @__PURE__ */ jsx6(Field, { t, label: "Anything we should know? (optional)", value: note, onChange: setNote, multiline: true }),
|
|
@@ -8570,7 +8731,7 @@ function BookingDialog({
|
|
|
8570
8731
|
onClick: startBooking,
|
|
8571
8732
|
disabled: !canSubmitDetails || busy,
|
|
8572
8733
|
style: primaryStyle(t, !canSubmitDetails || busy),
|
|
8573
|
-
children: busy ? "Sending code\u2026" : "Send verification code"
|
|
8734
|
+
children: requireOtp ? busy ? "Sending code\u2026" : "Send verification code" : busy ? "Booking\u2026" : "Book demo"
|
|
8574
8735
|
}
|
|
8575
8736
|
)
|
|
8576
8737
|
] })
|
|
@@ -9373,7 +9534,11 @@ function GlitchgrabProviderInner({
|
|
|
9373
9534
|
[token, baseUrl, session]
|
|
9374
9535
|
);
|
|
9375
9536
|
const findSimilar = useCallback3(
|
|
9376
|
-
(description) => findSimilarIssues(description, token, baseUrl, identity),
|
|
9537
|
+
(description, files) => findSimilarIssues(description, token, baseUrl, identity, files),
|
|
9538
|
+
[token, baseUrl, identity]
|
|
9539
|
+
);
|
|
9540
|
+
const readFile = useCallback3(
|
|
9541
|
+
(file) => readAttachment(file, token, baseUrl, identity),
|
|
9377
9542
|
[token, baseUrl, identity]
|
|
9378
9543
|
);
|
|
9379
9544
|
const transcribe = useCallback3(
|
|
@@ -9404,8 +9569,9 @@ function GlitchgrabProviderInner({
|
|
|
9404
9569
|
const [glitchgrabReport, setGlitchgrabReport] = useState6(false);
|
|
9405
9570
|
const [signedUsers, setSignedUsers] = useState6("OFF");
|
|
9406
9571
|
const aiBlockedUnsigned = signedUsers === "ENFORCE" && !signature;
|
|
9407
|
-
const [
|
|
9408
|
-
const openBookingDialog = useCallback3(() =>
|
|
9572
|
+
const [booking, setBooking] = useState6("closed");
|
|
9573
|
+
const openBookingDialog = useCallback3(() => setBooking("live"), []);
|
|
9574
|
+
const openBookingPreview = useCallback3(() => setBooking("preview"), []);
|
|
9409
9575
|
useEffect6(() => {
|
|
9410
9576
|
let cancelled = false;
|
|
9411
9577
|
fetch(`${baseUrl}/api/v1/sdk/project`, {
|
|
@@ -9480,6 +9646,7 @@ function GlitchgrabProviderInner({
|
|
|
9480
9646
|
addBreadcrumb: addBreadcrumb2,
|
|
9481
9647
|
openReportDialog,
|
|
9482
9648
|
openBookingDialog,
|
|
9649
|
+
openBookingPreview,
|
|
9483
9650
|
enhanceText: enhance,
|
|
9484
9651
|
shortcutLabel
|
|
9485
9652
|
},
|
|
@@ -9501,8 +9668,9 @@ function GlitchgrabProviderInner({
|
|
|
9501
9668
|
{
|
|
9502
9669
|
token,
|
|
9503
9670
|
baseUrl: baseUrl != null ? baseUrl : DEFAULT_BASE_URL2,
|
|
9504
|
-
open:
|
|
9505
|
-
|
|
9671
|
+
open: booking !== "closed",
|
|
9672
|
+
preview: booking === "preview",
|
|
9673
|
+
onClose: () => setBooking("closed")
|
|
9506
9674
|
}
|
|
9507
9675
|
),
|
|
9508
9676
|
/* @__PURE__ */ jsx7(
|
|
@@ -9513,6 +9681,7 @@ function GlitchgrabProviderInner({
|
|
|
9513
9681
|
enhanceText: aiAssist && !aiBlockedUnsigned ? enhance : void 0,
|
|
9514
9682
|
assist: aiAssist && !aiBlockedUnsigned ? assist : void 0,
|
|
9515
9683
|
findSimilarIssues: aiAssist && !aiBlockedUnsigned ? findSimilar : void 0,
|
|
9684
|
+
readFile: aiAssist && !aiBlockedUnsigned ? readFile : void 0,
|
|
9516
9685
|
reportGlitchgrabProblem: glitchgrabReport ? reportGlitchgrabProblem : void 0,
|
|
9517
9686
|
transcribeAudio: aiBlockedUnsigned ? void 0 : transcribe,
|
|
9518
9687
|
types,
|
|
@@ -9916,6 +10085,7 @@ function useGlitchgrabFeedback(query) {
|
|
|
9916
10085
|
}
|
|
9917
10086
|
export {
|
|
9918
10087
|
BookDemoButton,
|
|
10088
|
+
BookingDialog,
|
|
9919
10089
|
FeedbackButton,
|
|
9920
10090
|
GLITCHGRAB_SHORTCUT2 as GLITCHGRAB_SHORTCUT,
|
|
9921
10091
|
GLITCHGRAB_SHORTCUT_MAC2 as GLITCHGRAB_SHORTCUT_MAC,
|