@sailfish-ai/recorder 1.10.7 → 1.10.9
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/chunks/{chunkSerializer-BuEZW3N4.js → chunkSerializer-B_szIq8O.js} +1 -1
- package/dist/chunks/chunkSerializer-B_szIq8O.js.br +0 -0
- package/dist/chunks/chunkSerializer-B_szIq8O.js.gz +0 -0
- package/dist/chunks/{chunkSerializer-DM9muyGb.js → chunkSerializer-DdlgLmEH.js} +1 -1
- package/dist/chunks/chunkSerializer-DdlgLmEH.js.br +0 -0
- package/dist/chunks/chunkSerializer-DdlgLmEH.js.gz +0 -0
- package/dist/chunks/{index-soHaKXF4.js → index-B8gnDRst.js} +254 -236
- package/dist/chunks/index-B8gnDRst.js.br +0 -0
- package/dist/chunks/index-B8gnDRst.js.gz +0 -0
- package/dist/chunks/{index-BlPJWz9y.js → index-w2Ted1rp.js} +451 -376
- package/dist/chunks/index-w2Ted1rp.js.br +0 -0
- package/dist/chunks/index-w2Ted1rp.js.gz +0 -0
- package/dist/inAppReportIssueModal/index.js +106 -42
- package/dist/inAppReportIssueModal/integrations.js +0 -3
- package/dist/inAppReportIssueModal/types.js +1 -0
- package/dist/inAppReportIssueModal/ui.js +15 -8
- package/dist/index.js +15 -4
- package/dist/recorder.cjs +2 -2
- package/dist/recorder.js +12 -11
- package/dist/recorder.js.br +0 -0
- package/dist/recorder.js.gz +0 -0
- package/dist/types/inAppReportIssueModal/index.d.ts +5 -1
- package/dist/types/inAppReportIssueModal/types.d.ts +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/websocket.d.ts +4 -0
- package/dist/websocket.js +11 -0
- package/package.json +1 -1
- package/dist/chunks/chunkSerializer-BuEZW3N4.js.br +0 -0
- package/dist/chunks/chunkSerializer-BuEZW3N4.js.gz +0 -0
- package/dist/chunks/chunkSerializer-DM9muyGb.js.br +0 -0
- package/dist/chunks/chunkSerializer-DM9muyGb.js.gz +0 -0
- package/dist/chunks/index-BlPJWz9y.js.br +0 -0
- package/dist/chunks/index-BlPJWz9y.js.gz +0 -0
- package/dist/chunks/index-soHaKXF4.js.br +0 -0
- package/dist/chunks/index-soHaKXF4.js.gz +0 -0
|
Binary file
|
|
Binary file
|
|
@@ -21,14 +21,66 @@ export const ReportIssueContext = {
|
|
|
21
21
|
triageBaseUrl: "https://app.sailfishqa.com",
|
|
22
22
|
deactivateIsolation: () => { },
|
|
23
23
|
integrationData: null,
|
|
24
|
+
showEngTicketFieldsDefault: false,
|
|
24
25
|
};
|
|
25
26
|
let modalEl = null;
|
|
27
|
+
let effectiveShowEngTicketFields = false;
|
|
26
28
|
// Function to set up custom multiselect listeners
|
|
27
29
|
function setupCustomMultiSelectListeners(fieldId, onChange) {
|
|
28
30
|
const container = document.getElementById(`${fieldId}-container`);
|
|
29
31
|
const dropdown = document.getElementById(`${fieldId}-dropdown`);
|
|
30
32
|
if (!container || !dropdown)
|
|
31
33
|
return;
|
|
34
|
+
// Helper to rebuild chips display from current selection state
|
|
35
|
+
function updateChipsDisplay() {
|
|
36
|
+
const selectedValues = [];
|
|
37
|
+
const selectedChips = [];
|
|
38
|
+
dropdown.querySelectorAll(".sf-multiselect-option").forEach((opt) => {
|
|
39
|
+
const optEl = opt;
|
|
40
|
+
if (optEl.dataset.selected === "true") {
|
|
41
|
+
const value = optEl.dataset.value || "";
|
|
42
|
+
const label = optEl.textContent?.trim() || "";
|
|
43
|
+
selectedValues.push(value);
|
|
44
|
+
selectedChips.push(`<span class="sf-multiselect-chip" data-value="${value}" style="display:inline-flex; align-items:center; gap:4px; background:#e5e7eb; color:#374151; padding:2px 8px; border-radius:16px; font-size:13px; white-space:nowrap;">
|
|
45
|
+
${label}
|
|
46
|
+
<span class="sf-multiselect-chip-remove" data-value="${value}" style="cursor:pointer; display:inline-flex; align-items:center; font-size:16px; line-height:1; color:#6b7280;" onclick="event.stopPropagation();">×</span>
|
|
47
|
+
</span>`);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
const chipsContainer = container.querySelector(".sf-multiselect-chips");
|
|
51
|
+
if (chipsContainer) {
|
|
52
|
+
chipsContainer.innerHTML =
|
|
53
|
+
selectedChips.join("") ||
|
|
54
|
+
'<span style="color:#9ca3af;">Select...</span>';
|
|
55
|
+
// Re-bind chip remove listeners
|
|
56
|
+
chipsContainer
|
|
57
|
+
.querySelectorAll(".sf-multiselect-chip-remove")
|
|
58
|
+
.forEach((removeBtn) => {
|
|
59
|
+
removeBtn.addEventListener("click", (e) => {
|
|
60
|
+
e.stopPropagation();
|
|
61
|
+
const value = removeBtn.dataset.value || "";
|
|
62
|
+
// Deselect the option in the dropdown
|
|
63
|
+
const opt = dropdown.querySelector(`.sf-multiselect-option[data-value="${value}"]`);
|
|
64
|
+
if (opt) {
|
|
65
|
+
opt.dataset.selected = "false";
|
|
66
|
+
opt.style.backgroundColor = "";
|
|
67
|
+
}
|
|
68
|
+
updateChipsDisplay();
|
|
69
|
+
// Collect remaining selected values
|
|
70
|
+
const remaining = [];
|
|
71
|
+
dropdown
|
|
72
|
+
.querySelectorAll(".sf-multiselect-option")
|
|
73
|
+
.forEach((o) => {
|
|
74
|
+
if (o.dataset.selected === "true") {
|
|
75
|
+
remaining.push(o.dataset.value || "");
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
onChange(remaining);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
return selectedValues;
|
|
83
|
+
}
|
|
32
84
|
// Handle option clicks
|
|
33
85
|
const options = dropdown.querySelectorAll(".sf-multiselect-option");
|
|
34
86
|
options.forEach((option) => {
|
|
@@ -45,27 +97,12 @@ function setupCustomMultiSelectListeners(fieldId, onChange) {
|
|
|
45
97
|
else {
|
|
46
98
|
optionEl.style.backgroundColor = "";
|
|
47
99
|
}
|
|
48
|
-
|
|
49
|
-
const selectedValues = [];
|
|
50
|
-
const selectedLabels = [];
|
|
51
|
-
dropdown.querySelectorAll(".sf-multiselect-option").forEach((opt) => {
|
|
52
|
-
const optEl = opt;
|
|
53
|
-
if (optEl.dataset.selected === "true") {
|
|
54
|
-
selectedValues.push(optEl.dataset.value || "");
|
|
55
|
-
selectedLabels.push(optEl.textContent || "");
|
|
56
|
-
}
|
|
57
|
-
});
|
|
58
|
-
// Update input display
|
|
59
|
-
const input = container.querySelector(".sf-multiselect-input span");
|
|
60
|
-
const displayText = selectedLabels.join(", ");
|
|
61
|
-
if (input) {
|
|
62
|
-
input.textContent = displayText || "Select...";
|
|
63
|
-
input.style.color = displayText ? "#000" : "#9ca3af";
|
|
64
|
-
}
|
|
65
|
-
// Call onChange callback
|
|
100
|
+
const selectedValues = updateChipsDisplay();
|
|
66
101
|
onChange(selectedValues);
|
|
67
102
|
});
|
|
68
103
|
});
|
|
104
|
+
// Bind chip remove listeners for any pre-selected values
|
|
105
|
+
updateChipsDisplay();
|
|
69
106
|
// Close dropdown on outside click
|
|
70
107
|
document.addEventListener("click", (e) => {
|
|
71
108
|
const target = e.target;
|
|
@@ -126,7 +163,7 @@ function generateEngTicketFieldsHTML() {
|
|
|
126
163
|
</label>
|
|
127
164
|
<select id="sf-eng-ticket-team"
|
|
128
165
|
style="width:100%; padding:8px 12px; font-size:14px; border:1px solid #cbd5e1; border-radius:6px; outline:none; appearance:none; cursor:pointer; background-color: white; color: #9ca3af;">
|
|
129
|
-
<option value=""
|
|
166
|
+
<option value="" selected style="color: #9ca3af;">Select team...</option>
|
|
130
167
|
</select>
|
|
131
168
|
</div>
|
|
132
169
|
`;
|
|
@@ -139,7 +176,7 @@ function generateEngTicketFieldsHTML() {
|
|
|
139
176
|
</label>
|
|
140
177
|
<select id="sf-eng-ticket-project"
|
|
141
178
|
style="width:100%; padding:8px 12px; font-size:14px; border:1px solid #cbd5e1; border-radius:6px; outline:none; appearance:none; cursor:pointer; background-color: white; color: #9ca3af;">
|
|
142
|
-
<option value=""
|
|
179
|
+
<option value="" selected style="color: #9ca3af;">Select project...</option>
|
|
143
180
|
</select>
|
|
144
181
|
</div>
|
|
145
182
|
`;
|
|
@@ -152,7 +189,7 @@ function generateEngTicketFieldsHTML() {
|
|
|
152
189
|
</label>
|
|
153
190
|
<select id="sf-eng-ticket-type"
|
|
154
191
|
style="width:100%; padding:8px 12px; font-size:14px; border:1px solid #cbd5e1; border-radius:6px; outline:none; appearance:none; cursor:pointer; background-color: white; color: #9ca3af;">
|
|
155
|
-
<option value=""
|
|
192
|
+
<option value="" selected style="color: #9ca3af;">Select project first...</option>
|
|
156
193
|
</select>
|
|
157
194
|
</div>
|
|
158
195
|
`;
|
|
@@ -186,7 +223,7 @@ function generateEngTicketFieldsHTML() {
|
|
|
186
223
|
</label>
|
|
187
224
|
<select id="sf-eng-ticket-sprint"
|
|
188
225
|
style="width:100%; padding:8px 12px; font-size:14px; border:1px solid #cbd5e1; border-radius:6px; outline:none; appearance:none; cursor:pointer; background-color: white; color: #9ca3af;">
|
|
189
|
-
<option value=""
|
|
226
|
+
<option value="" selected style="color: #9ca3af;">Select sprint...</option>
|
|
190
227
|
</select>
|
|
191
228
|
</div>
|
|
192
229
|
`;
|
|
@@ -267,6 +304,8 @@ export function setupIssueReporting(options) {
|
|
|
267
304
|
ReportIssueContext.backendApi = options.backendApi;
|
|
268
305
|
ReportIssueContext.resolveSessionId = options.getSessionId;
|
|
269
306
|
ReportIssueContext.integrationData = options.integrationData || null;
|
|
307
|
+
ReportIssueContext.showEngTicketFieldsDefault =
|
|
308
|
+
options.showEngTicketFieldsInReportIssueModalDefault ?? false;
|
|
270
309
|
if (options.customBaseUrl)
|
|
271
310
|
ReportIssueContext.triageBaseUrl = options.customBaseUrl;
|
|
272
311
|
ReportIssueContext.shortcuts = mergeShortcutsConfig(options.shortcuts);
|
|
@@ -340,11 +379,13 @@ function getSessionIdSafely() {
|
|
|
340
379
|
}
|
|
341
380
|
return ReportIssueContext.resolveSessionId();
|
|
342
381
|
}
|
|
343
|
-
export function openReportIssueModal() {
|
|
382
|
+
export function openReportIssueModal(options) {
|
|
344
383
|
if (isRecording) {
|
|
345
384
|
stopRecording();
|
|
346
385
|
return;
|
|
347
386
|
}
|
|
387
|
+
effectiveShowEngTicketFields =
|
|
388
|
+
options?.showEngTicketFields ?? ReportIssueContext.showEngTicketFieldsDefault;
|
|
348
389
|
injectModalHTML();
|
|
349
390
|
if (modalEl)
|
|
350
391
|
document.body.appendChild(modalEl);
|
|
@@ -672,7 +713,7 @@ function injectModalHTML(initialMode = "lookback") {
|
|
|
672
713
|
Create an Issue
|
|
673
714
|
</label>
|
|
674
715
|
|
|
675
|
-
<label id="sf-create-eng-ticket-label" style="display:${ReportIssueContext.integrationData ? "flex" : "none"}; align-items:center; gap:8px; font-size:14px; font-weight:500; cursor:pointer;">
|
|
716
|
+
<label id="sf-create-eng-ticket-label" style="display:${ReportIssueContext.integrationData && effectiveShowEngTicketFields ? "flex" : "none"}; align-items:center; gap:8px; font-size:14px; font-weight:500; cursor:pointer;">
|
|
676
717
|
<input type="checkbox" id="sf-create-eng-ticket-checkbox" ${currentState.createEngTicket ? "checked" : ""}
|
|
677
718
|
style="width:16px; height:16px; accent-color:#295DBF; cursor:pointer;">
|
|
678
719
|
Create an Eng Ticket
|
|
@@ -694,7 +735,7 @@ function injectModalHTML(initialMode = "lookback") {
|
|
|
694
735
|
</div>
|
|
695
736
|
|
|
696
737
|
<!-- Engineering Ticket Fields (shown when create eng ticket is checked) -->
|
|
697
|
-
<div id="sf-eng-ticket-fields-container" style="display:${currentState.createEngTicket ? "block" : "none"}; margin-top: ${currentState.createIssue ? "12px" : "0"};">
|
|
738
|
+
<div id="sf-eng-ticket-fields-container" style="display:${currentState.createEngTicket && ReportIssueContext.integrationData ? "block" : "none"}; margin-top: ${currentState.createIssue ? "12px" : "0"};">
|
|
698
739
|
${generateEngTicketFieldsHTML()}
|
|
699
740
|
</div>
|
|
700
741
|
</div>
|
|
@@ -769,10 +810,12 @@ function injectModalHTML(initialMode = "lookback") {
|
|
|
769
810
|
renderDynamicFields(currentState.engTicketProject, currentState.engTicketIssueType);
|
|
770
811
|
}
|
|
771
812
|
}
|
|
772
|
-
// If integration just became available, show the checkbox
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
label
|
|
813
|
+
// If integration just became available, show the checkbox (only if eng ticket fields are enabled)
|
|
814
|
+
if (effectiveShowEngTicketFields) {
|
|
815
|
+
const label = document.getElementById("sf-create-eng-ticket-label");
|
|
816
|
+
if (label) {
|
|
817
|
+
label.style.display = "flex";
|
|
818
|
+
}
|
|
776
819
|
}
|
|
777
820
|
});
|
|
778
821
|
}
|
|
@@ -1407,45 +1450,58 @@ async function createTriage(startTimestamp, endTimestamp, description) {
|
|
|
1407
1450
|
try {
|
|
1408
1451
|
showStatusModal(true);
|
|
1409
1452
|
const response = await createTriageFromRecorder(ReportIssueContext.apiKey, ReportIssueContext.backendApi, getSessionIdSafely(), startTimestamp, endTimestamp, description);
|
|
1453
|
+
if (response?.errors?.length) {
|
|
1454
|
+
const errorMsg = response.errors.map((e) => e.message).join("; ");
|
|
1455
|
+
console.error("GraphQL error creating triage:", errorMsg);
|
|
1456
|
+
showStatusModal(false, null, errorMsg);
|
|
1457
|
+
return;
|
|
1458
|
+
}
|
|
1410
1459
|
const triageId = response?.data?.createTriageFromRecorder?.id;
|
|
1411
1460
|
if (triageId) {
|
|
1412
1461
|
showStatusModal(false, { type: "triage", id: triageId });
|
|
1413
1462
|
}
|
|
1414
1463
|
else {
|
|
1415
1464
|
console.error("No Triage ID returned from backend.");
|
|
1416
|
-
showStatusModal(false, null);
|
|
1465
|
+
showStatusModal(false, null, "No triage was created. Please try again.");
|
|
1417
1466
|
}
|
|
1418
1467
|
}
|
|
1419
1468
|
catch (error) {
|
|
1420
1469
|
console.error("Error creating triage:", error);
|
|
1421
|
-
showStatusModal(false, null);
|
|
1470
|
+
showStatusModal(false, null, "Something went wrong. Please try again.");
|
|
1422
1471
|
}
|
|
1423
1472
|
}
|
|
1424
1473
|
async function createTriageAndIssue(startTimestamp, endTimestamp, description, issueName, issueDescription, createEngTicket, engTicketTeam, engTicketProject, engTicketPriority, engTicketLabels, engTicketIssueType, engTicketCustomFields) {
|
|
1425
1474
|
try {
|
|
1426
1475
|
showStatusModal(true);
|
|
1427
1476
|
const response = await createTriageAndIssueFromRecorder(ReportIssueContext.apiKey, ReportIssueContext.backendApi, getSessionIdSafely(), startTimestamp, endTimestamp, description, issueName, issueDescription, createEngTicket, engTicketTeam, engTicketProject, engTicketPriority, engTicketLabels, engTicketIssueType, engTicketCustomFields);
|
|
1477
|
+
if (response?.errors?.length) {
|
|
1478
|
+
const errorMsg = response.errors.map((e) => e.message).join("; ");
|
|
1479
|
+
console.error("GraphQL error creating triage and issue:", errorMsg);
|
|
1480
|
+
showStatusModal(false, null, errorMsg);
|
|
1481
|
+
return;
|
|
1482
|
+
}
|
|
1428
1483
|
const issueId = response?.data?.createTriageAndIssueFromRecorder?.id;
|
|
1429
1484
|
if (issueId) {
|
|
1430
1485
|
showStatusModal(false, { type: "issue", id: issueId });
|
|
1431
1486
|
}
|
|
1432
1487
|
else {
|
|
1433
1488
|
console.error("No Issue ID returned from backend.");
|
|
1434
|
-
showStatusModal(false, null);
|
|
1489
|
+
showStatusModal(false, null, "No issue was created. Please try again.");
|
|
1435
1490
|
}
|
|
1436
1491
|
}
|
|
1437
1492
|
catch (error) {
|
|
1438
1493
|
console.error("Error creating triage and issue:", error);
|
|
1439
|
-
showStatusModal(false, null);
|
|
1494
|
+
showStatusModal(false, null, "Something went wrong. Please try again.");
|
|
1440
1495
|
}
|
|
1441
1496
|
}
|
|
1442
|
-
function showStatusModal(isLoading, result) {
|
|
1497
|
+
function showStatusModal(isLoading, result, errorMessage) {
|
|
1443
1498
|
const triageId = result?.type === "triage" ? result.id : undefined;
|
|
1444
1499
|
const issueId = result?.type === "issue" ? result.id : undefined;
|
|
1445
|
-
showTriageStatusModal(isLoading, triageId, issueId);
|
|
1500
|
+
showTriageStatusModal(isLoading, triageId, issueId, errorMessage);
|
|
1446
1501
|
}
|
|
1447
|
-
function showTriageStatusModal(isLoading, triageId, issueId) {
|
|
1502
|
+
function showTriageStatusModal(isLoading, triageId, issueId, errorMessage) {
|
|
1448
1503
|
removeExistingModals();
|
|
1504
|
+
const isError = !isLoading && errorMessage;
|
|
1449
1505
|
// Prefer issue URL if available, otherwise use triage URL
|
|
1450
1506
|
const resultUrl = issueId
|
|
1451
1507
|
? `${ReportIssueContext.triageBaseUrl}/issues/${issueId}?from=inAppReportIssue`
|
|
@@ -1462,10 +1518,16 @@ function showTriageStatusModal(isLoading, triageId, issueId) {
|
|
|
1462
1518
|
alignItems: "center",
|
|
1463
1519
|
justifyContent: "center",
|
|
1464
1520
|
});
|
|
1465
|
-
const statusTitle = isLoading
|
|
1521
|
+
const statusTitle = isLoading
|
|
1522
|
+
? "Reporting Issue..."
|
|
1523
|
+
: isError
|
|
1524
|
+
? "Failed to report issue"
|
|
1525
|
+
: "Issue reported!";
|
|
1466
1526
|
const statusSubtitle = isLoading
|
|
1467
1527
|
? `<p style="font-size:14px; color:#64748b; line-height:20px;">This may take ~10 seconds</p>`
|
|
1468
|
-
:
|
|
1528
|
+
: isError
|
|
1529
|
+
? `<p style="font-size:14px; color:#ef4444; line-height:20px;">${errorMessage}</p>`
|
|
1530
|
+
: "";
|
|
1469
1531
|
const spinner = isLoading
|
|
1470
1532
|
? `<div style="display:flex; justify-content:center; align-items:center; padding: 40px 0;">
|
|
1471
1533
|
<div style="width:24px; height:24px; border:2px solid #295dbf; border-top:2px solid white; border-radius:50%; animation:spin 1s linear infinite;"></div>
|
|
@@ -1496,11 +1558,11 @@ function showTriageStatusModal(isLoading, triageId, issueId) {
|
|
|
1496
1558
|
</svg>
|
|
1497
1559
|
</button>
|
|
1498
1560
|
|
|
1499
|
-
<h2 style="font-size:18px; font-weight:600; margin-bottom:${isLoading ? 8 : 40}px; line-height:28px;">${statusTitle}</h2>
|
|
1561
|
+
<h2 style="font-size:18px; font-weight:600; margin-bottom:${isLoading || isError ? 8 : 40}px; line-height:28px;">${statusTitle}</h2>
|
|
1500
1562
|
${statusSubtitle}
|
|
1501
1563
|
${spinner}
|
|
1502
1564
|
|
|
1503
|
-
<div style="display:flex; justify-content:flex-end; align-items:center; gap:8px;">
|
|
1565
|
+
<div style="display:${isError ? "none" : "flex"}; justify-content:flex-end; align-items:center; gap:8px;">
|
|
1504
1566
|
<button id="sf-copy-triage-link"
|
|
1505
1567
|
style="background:white; border:1px solid #e2e8f0; padding:8px 16px; border-radius:6px;
|
|
1506
1568
|
font-size:14px; color: #0f172a; cursor:pointer;">
|
|
@@ -1555,8 +1617,10 @@ function showTriageStatusModal(isLoading, triageId, issueId) {
|
|
|
1555
1617
|
window.open(resultUrl, "_blank");
|
|
1556
1618
|
}
|
|
1557
1619
|
});
|
|
1558
|
-
// Auto-hide after success
|
|
1559
|
-
|
|
1620
|
+
// Auto-hide after success (not on error, so user can read the message)
|
|
1621
|
+
if (!isError) {
|
|
1622
|
+
setTimeout(() => fadeCardAndRemove(container, card, 300), 10000);
|
|
1623
|
+
}
|
|
1560
1624
|
}
|
|
1561
1625
|
}
|
|
1562
1626
|
function fadeCardAndRemove(container, card, durationMs = 300) {
|
|
@@ -61,7 +61,6 @@ export async function refreshIntegrationData(apiKey, backendApi) {
|
|
|
61
61
|
export function populateSelectOptions(selectElement, options, defaultValue) {
|
|
62
62
|
const placeholderOption = document.createElement("option");
|
|
63
63
|
placeholderOption.value = "";
|
|
64
|
-
placeholderOption.disabled = true;
|
|
65
64
|
placeholderOption.selected = !defaultValue;
|
|
66
65
|
placeholderOption.textContent = "Select...";
|
|
67
66
|
placeholderOption.style.color = "#9ca3af";
|
|
@@ -129,7 +128,6 @@ export function populateSprintOptions(selectElement, sprints, currentValue) {
|
|
|
129
128
|
// Placeholder option
|
|
130
129
|
const placeholderOption = document.createElement("option");
|
|
131
130
|
placeholderOption.value = "";
|
|
132
|
-
placeholderOption.disabled = true;
|
|
133
131
|
placeholderOption.selected = !currentValue;
|
|
134
132
|
placeholderOption.textContent = "Select sprint...";
|
|
135
133
|
placeholderOption.style.color = "#9ca3af";
|
|
@@ -194,7 +192,6 @@ export function updateIssueTypeOptions(selectElement, projectId) {
|
|
|
194
192
|
selectElement.innerHTML = "";
|
|
195
193
|
const placeholderOption = document.createElement("option");
|
|
196
194
|
placeholderOption.value = "";
|
|
197
|
-
placeholderOption.disabled = true;
|
|
198
195
|
placeholderOption.textContent = "Select...";
|
|
199
196
|
placeholderOption.style.color = "#9ca3af";
|
|
200
197
|
selectElement.appendChild(placeholderOption);
|
|
@@ -3,10 +3,17 @@ export function renderCustomMultiSelect(fieldId, fieldName, options, selectedVal
|
|
|
3
3
|
const requiredMark = isRequired
|
|
4
4
|
? '<span style="color:#ef4444;">*</span>'
|
|
5
5
|
: "";
|
|
6
|
-
const
|
|
6
|
+
const selectedChips = options
|
|
7
7
|
.filter((opt) => selectedValues.includes(opt.id || opt.value || opt.name || opt))
|
|
8
|
-
.map((opt) =>
|
|
9
|
-
.
|
|
8
|
+
.map((opt) => {
|
|
9
|
+
const value = opt.id || opt.value || opt.name || opt;
|
|
10
|
+
const label = opt.name || opt.value || opt;
|
|
11
|
+
return `<span class="sf-multiselect-chip" data-value="${value}" style="display:inline-flex; align-items:center; gap:4px; background:#e5e7eb; color:#374151; padding:2px 8px; border-radius:16px; font-size:13px; white-space:nowrap;">
|
|
12
|
+
${label}
|
|
13
|
+
<span class="sf-multiselect-chip-remove" data-value="${value}" style="cursor:pointer; display:inline-flex; align-items:center; font-size:16px; line-height:1; color:#6b7280;" onclick="event.stopPropagation();">×</span>
|
|
14
|
+
</span>`;
|
|
15
|
+
})
|
|
16
|
+
.join("");
|
|
10
17
|
const optionsHTML = options
|
|
11
18
|
.map((opt) => {
|
|
12
19
|
const value = opt.id || opt.value || opt.name || opt;
|
|
@@ -25,9 +32,9 @@ export function renderCustomMultiSelect(fieldId, fieldName, options, selectedVal
|
|
|
25
32
|
${fieldName} ${requiredMark}
|
|
26
33
|
</label>
|
|
27
34
|
<div class="sf-custom-multiselect" id="${fieldId}-container" data-field-id="${fieldId}" style="position:relative;">
|
|
28
|
-
<div class="sf-multiselect-input" style="width:100%; padding:8px 12px; font-size:14px; border:1px solid #cbd5e1; border-radius:6px; background-color:white; cursor:pointer; min-height:38px; display:flex; align-items:center;" onclick="document.getElementById('${fieldId}-dropdown').style.display = document.getElementById('${fieldId}-dropdown').style.display === 'none' ? 'block' : 'none';">
|
|
29
|
-
<span
|
|
30
|
-
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" style="margin-left:8px;">
|
|
35
|
+
<div class="sf-multiselect-input" style="width:100%; padding:8px 12px; font-size:14px; border:1px solid #cbd5e1; border-radius:6px; background-color:white; cursor:pointer; min-height:38px; display:flex; align-items:center; flex-wrap:wrap; gap:4px;" onclick="document.getElementById('${fieldId}-dropdown').style.display = document.getElementById('${fieldId}-dropdown').style.display === 'none' ? 'block' : 'none';">
|
|
36
|
+
<span class="sf-multiselect-chips" style="display:flex; flex-wrap:wrap; gap:4px; flex:1;">${selectedChips || `<span style="color:#9ca3af;">Select...</span>`}</span>
|
|
37
|
+
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" style="margin-left:8px; flex-shrink:0;">
|
|
31
38
|
<path d="M4 6L8 10L12 6" stroke="#64748B" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
32
39
|
</svg>
|
|
33
40
|
</div>
|
|
@@ -183,7 +190,7 @@ export function renderDynamicField(field, fieldValue, users = []) {
|
|
|
183
190
|
style="${baseInputStyle} appearance:none; cursor:pointer; background-color: white; ${!hasValue ? "color: #9ca3af;" : ""}"
|
|
184
191
|
${isRequired ? "required" : ""}
|
|
185
192
|
>
|
|
186
|
-
<option value=""
|
|
193
|
+
<option value="" ${!hasValue ? "selected" : ""} style="color: #9ca3af;">Select ${fieldName.toLowerCase()}...</option>
|
|
187
194
|
${options}
|
|
188
195
|
</select>
|
|
189
196
|
</div>
|
|
@@ -214,7 +221,7 @@ export function renderDynamicField(field, fieldValue, users = []) {
|
|
|
214
221
|
style="${baseInputStyle} appearance:none; cursor:pointer; background-color: white; ${!hasValue ? "color: #9ca3af;" : ""}"
|
|
215
222
|
${isRequired ? "required" : ""}
|
|
216
223
|
>
|
|
217
|
-
<option value=""
|
|
224
|
+
<option value="" ${!hasValue ? "selected" : ""} style="color: #9ca3af;">Select ${fieldName.toLowerCase()}...</option>
|
|
218
225
|
${options}
|
|
219
226
|
</select>
|
|
220
227
|
</div>
|
package/dist/index.js
CHANGED
|
@@ -12,6 +12,7 @@ import { getUrlAndStoredUuids, initializeConsolePlugin, initializeDomContentEven
|
|
|
12
12
|
import { HAS_DOCUMENT, HAS_LOCAL_STORAGE, HAS_SESSION_STORAGE, HAS_WINDOW, } from "./runtimeEnv";
|
|
13
13
|
import { ensureSessionListeners, getOrSetSessionId } from "./session";
|
|
14
14
|
import { withAppUrlMetadata } from "./utils";
|
|
15
|
+
import { onNavigationChange } from "./websocket";
|
|
15
16
|
import { clearStaleFuncSpanState, getFuncSpanHeader, isFunctionSpanTrackingEnabled, restoreFuncSpanState, sendEvent, sendMessage, } from "./websocket";
|
|
16
17
|
import { yieldToMain } from "./scheduler";
|
|
17
18
|
const DEBUG = readDebugFlag(); // A wrapper around fetch that suppresses connection refused errors
|
|
@@ -146,7 +147,7 @@ function trackDomainChangesOnce() {
|
|
|
146
147
|
if (forceSend || currentDomain !== lastDomain) {
|
|
147
148
|
lastDomain = currentDomain;
|
|
148
149
|
const pageVisitUUID = uuidv4();
|
|
149
|
-
const prevPageVisitUUID = sessionStorage.getItem("pageVisitUUID");
|
|
150
|
+
const prevPageVisitUUID = sessionStorage.getItem("pageVisitUUID") ?? "";
|
|
150
151
|
sessionStorage.setItem("pageVisitUUID", pageVisitUUID);
|
|
151
152
|
sessionStorage.setItem("prevPageVisitUUID", prevPageVisitUUID);
|
|
152
153
|
invalidateUrlCache();
|
|
@@ -163,6 +164,8 @@ function trackDomainChangesOnce() {
|
|
|
163
164
|
}
|
|
164
165
|
};
|
|
165
166
|
const debouncedCheck = debounce(() => checkDomainChange(), 500);
|
|
167
|
+
// Fire immediately on pushState/replaceState/popstate — don't wait for interval
|
|
168
|
+
onNavigationChange(() => checkDomainChange());
|
|
166
169
|
// Force the first check to send the initial URL on load
|
|
167
170
|
checkDomainChange(true);
|
|
168
171
|
// Start a single interval and remember it
|
|
@@ -1044,13 +1047,20 @@ export async function startRecording({ apiKey, backendApi = "https://api-service
|
|
|
1044
1047
|
captureStreamPrefixKb,
|
|
1045
1048
|
captureStreamTimeoutMs,
|
|
1046
1049
|
};
|
|
1047
|
-
//
|
|
1050
|
+
// Seed pageVisitUUID in sessionStorage so the fetch/XHR interceptors can
|
|
1051
|
+
// build a complete X-Sf3-Rid header on the very first request.
|
|
1052
|
+
// trackDomainChangesOnce() sets this later (after WS connect), but that's
|
|
1053
|
+
// too late — requests fire before the first await completes.
|
|
1054
|
+
if (!sessionStorage.getItem("pageVisitUUID")) {
|
|
1055
|
+
sessionStorage.setItem("pageVisitUUID", uuidv4());
|
|
1056
|
+
invalidateUrlCache();
|
|
1057
|
+
}
|
|
1058
|
+
// 1a. XHR + Fetch interceptors (must both run synchronously before any yield,
|
|
1059
|
+
// so fire-and-forget callers patch fetch before frameworks capture it)
|
|
1048
1060
|
if (!g.xhrPatched) {
|
|
1049
1061
|
setupXMLHttpRequestInterceptor(domainsToNotPropagateHeaderTo, bodyCaptureConfig);
|
|
1050
1062
|
g.xhrPatched = true;
|
|
1051
1063
|
}
|
|
1052
|
-
await yieldToMain();
|
|
1053
|
-
// 1b. Fetch interceptor
|
|
1054
1064
|
if (!g.fetchPatched) {
|
|
1055
1065
|
setupFetchInterceptor(domainsToNotPropagateHeaderTo, bodyCaptureConfig);
|
|
1056
1066
|
g.fetchPatched = true;
|
|
@@ -1215,6 +1225,7 @@ export const initRecorder = async (options) => {
|
|
|
1215
1225
|
shortcuts: options.reportIssueShortcuts,
|
|
1216
1226
|
customBaseUrl: options.customBaseUrl,
|
|
1217
1227
|
integrationData,
|
|
1228
|
+
showEngTicketFieldsInReportIssueModalDefault: options.showEngTicketFieldsInReportIssueModalDefault,
|
|
1218
1229
|
});
|
|
1219
1230
|
g.issueReportingInit = true;
|
|
1220
1231
|
}
|
package/dist/recorder.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
-
const e = require("./chunks/index-
|
|
4
|
-
exports.DEFAULT_CAPTURE_SETTINGS = e.DEFAULT_CAPTURE_SETTINGS, exports.DEFAULT_CONSOLE_RECORDING_SETTINGS = e.DEFAULT_CONSOLE_RECORDING_SETTINGS, exports.STORAGE_VERSION = e.STORAGE_VERSION, exports.addOrUpdateMetadata = e.addOrUpdateMetadata, exports.buildBatches = e.buildBatches, exports.clearStaleFuncSpanState = e.clearStaleFuncSpanState, exports.createTriageAndIssueFromRecorder = e.createTriageAndIssueFromRecorder, exports.createTriageFromRecorder = e.createTriageFromRecorder, exports.disableFunctionSpanTracking = e.disableFunctionSpanTracking, exports.enableFunctionSpanTracking = e.enableFunctionSpanTracking, exports.ensureHrefCache = e.ensureHrefCache, exports.eventSize = e.eventSize, exports.fetchAndSendIp = e.fetchAndSendIp, exports.fetchCaptureSettings = e.fetchCaptureSettings, exports.fetchEngineeringTicketPlatformIntegrations = e.fetchEngineeringTicketPlatformIntegrations, exports.fetchFunctionSpanTrackingEnabled = e.fetchFunctionSpanTrackingEnabled, exports.flushBufferedEvents = e.flushBufferedEvents, exports.getCachedHref = e.getCachedHref, exports.getCachedHrefNoQuery = e.getCachedHrefNoQuery, exports.getFuncSpanHeader = e.getFuncSpanHeader, exports.getOrSetSessionId = e.getOrSetSessionId, exports.getUrlAndStoredUuids = e.getUrlAndStoredUuids, exports.identify = e.identify, exports.initRecorder = e.initRecorder, exports.initializeConsolePlugin = e.initializeConsolePlugin, exports.initializeDomContentEvents = e.initializeDomContentEvents, exports.initializeFunctionSpanTrackingFromApi = e.initializeFunctionSpanTrackingFromApi, exports.initializeRecording = e.initializeRecording, exports.initializeWebSocket = e.initializeWebSocket, exports.invalidateUrlCache = e.invalidateUrlCache, exports.isFunctionSpanTrackingEnabled = e.isFunctionSpanTrackingEnabled, exports.matchUrlWithWildcard = e.matchUrlWithWildcard, Object.defineProperty(exports, "nowTimestamp", { enumerable: true, get: () => e.nowTimestamp }), exports.openReportIssueModal = e.openReportIssueModal, exports.restoreFuncSpanState = e.restoreFuncSpanState, exports.sendDomainsToNotPropagateHeaderTo = e.sendDomainsToNotPropagateHeaderTo, exports.sendEvent = e.sendEvent, exports.sendGraphQLRequest = e.sendGraphQLRequest, exports.sendMessage = e.sendMessage, exports.startRecording = e.startRecording, exports.startRecordingSession = e.startRecordingSession, exports.trackingEvent = e.trackingEvent, exports.withAppUrlMetadata = e.withAppUrlMetadata;
|
|
3
|
+
const e = require("./chunks/index-B8gnDRst.js");
|
|
4
|
+
exports.DEFAULT_CAPTURE_SETTINGS = e.DEFAULT_CAPTURE_SETTINGS, exports.DEFAULT_CONSOLE_RECORDING_SETTINGS = e.DEFAULT_CONSOLE_RECORDING_SETTINGS, exports.STORAGE_VERSION = e.STORAGE_VERSION, exports.addOrUpdateMetadata = e.addOrUpdateMetadata, exports.buildBatches = e.buildBatches, exports.clearStaleFuncSpanState = e.clearStaleFuncSpanState, exports.createTriageAndIssueFromRecorder = e.createTriageAndIssueFromRecorder, exports.createTriageFromRecorder = e.createTriageFromRecorder, exports.disableFunctionSpanTracking = e.disableFunctionSpanTracking, exports.enableFunctionSpanTracking = e.enableFunctionSpanTracking, exports.ensureHrefCache = e.ensureHrefCache, exports.eventSize = e.eventSize, exports.fetchAndSendIp = e.fetchAndSendIp, exports.fetchCaptureSettings = e.fetchCaptureSettings, exports.fetchEngineeringTicketPlatformIntegrations = e.fetchEngineeringTicketPlatformIntegrations, exports.fetchFunctionSpanTrackingEnabled = e.fetchFunctionSpanTrackingEnabled, exports.flushBufferedEvents = e.flushBufferedEvents, exports.getCachedHref = e.getCachedHref, exports.getCachedHrefNoQuery = e.getCachedHrefNoQuery, exports.getFuncSpanHeader = e.getFuncSpanHeader, exports.getOrSetSessionId = e.getOrSetSessionId, exports.getUrlAndStoredUuids = e.getUrlAndStoredUuids, exports.identify = e.identify, exports.initRecorder = e.initRecorder, exports.initializeConsolePlugin = e.initializeConsolePlugin, exports.initializeDomContentEvents = e.initializeDomContentEvents, exports.initializeFunctionSpanTrackingFromApi = e.initializeFunctionSpanTrackingFromApi, exports.initializeRecording = e.initializeRecording, exports.initializeWebSocket = e.initializeWebSocket, exports.invalidateUrlCache = e.invalidateUrlCache, exports.isFunctionSpanTrackingEnabled = e.isFunctionSpanTrackingEnabled, exports.matchUrlWithWildcard = e.matchUrlWithWildcard, Object.defineProperty(exports, "nowTimestamp", { enumerable: true, get: () => e.nowTimestamp }), exports.onNavigationChange = e.onNavigationChange, exports.openReportIssueModal = e.openReportIssueModal, exports.restoreFuncSpanState = e.restoreFuncSpanState, exports.sendDomainsToNotPropagateHeaderTo = e.sendDomainsToNotPropagateHeaderTo, exports.sendEvent = e.sendEvent, exports.sendGraphQLRequest = e.sendGraphQLRequest, exports.sendMessage = e.sendMessage, exports.startRecording = e.startRecording, exports.startRecordingSession = e.startRecordingSession, exports.trackingEvent = e.trackingEvent, exports.withAppUrlMetadata = e.withAppUrlMetadata;
|
package/dist/recorder.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { D, a, S, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, z, A, B, C, E, F, G, H, I, J, K, L, M, N, O, P, Q } from "./chunks/index-
|
|
1
|
+
import { D, a, S, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, z, A, B, C, E, F, G, H, I, J, K, L, M, N, O, P, Q, R } from "./chunks/index-w2Ted1rp.js";
|
|
2
2
|
export {
|
|
3
3
|
D as DEFAULT_CAPTURE_SETTINGS,
|
|
4
4
|
a as DEFAULT_CONSOLE_RECORDING_SETTINGS,
|
|
@@ -33,14 +33,15 @@ export {
|
|
|
33
33
|
E as isFunctionSpanTrackingEnabled,
|
|
34
34
|
F as matchUrlWithWildcard,
|
|
35
35
|
G as nowTimestamp,
|
|
36
|
-
H as
|
|
37
|
-
I as
|
|
38
|
-
J as
|
|
39
|
-
K as
|
|
40
|
-
L as
|
|
41
|
-
M as
|
|
42
|
-
N as
|
|
43
|
-
O as
|
|
44
|
-
P as
|
|
45
|
-
Q as
|
|
36
|
+
H as onNavigationChange,
|
|
37
|
+
I as openReportIssueModal,
|
|
38
|
+
J as restoreFuncSpanState,
|
|
39
|
+
K as sendDomainsToNotPropagateHeaderTo,
|
|
40
|
+
L as sendEvent,
|
|
41
|
+
M as sendGraphQLRequest,
|
|
42
|
+
N as sendMessage,
|
|
43
|
+
O as startRecording,
|
|
44
|
+
P as startRecordingSession,
|
|
45
|
+
Q as trackingEvent,
|
|
46
|
+
R as withAppUrlMetadata
|
|
46
47
|
};
|
package/dist/recorder.js.br
CHANGED
|
Binary file
|
package/dist/recorder.js.gz
CHANGED
|
Binary file
|
|
@@ -27,6 +27,7 @@ export declare const ReportIssueContext: {
|
|
|
27
27
|
triageBaseUrl: string;
|
|
28
28
|
deactivateIsolation: () => void;
|
|
29
29
|
integrationData: any;
|
|
30
|
+
showEngTicketFieldsDefault: boolean;
|
|
30
31
|
};
|
|
31
32
|
export declare function setupIssueReporting(options: {
|
|
32
33
|
apiKey: string;
|
|
@@ -35,6 +36,9 @@ export declare function setupIssueReporting(options: {
|
|
|
35
36
|
shortcuts?: Partial<ShortcutsConfig>;
|
|
36
37
|
customBaseUrl?: string;
|
|
37
38
|
integrationData?: any;
|
|
39
|
+
showEngTicketFieldsInReportIssueModalDefault?: boolean;
|
|
40
|
+
}): void;
|
|
41
|
+
export declare function openReportIssueModal(options?: {
|
|
42
|
+
showEngTicketFields?: boolean;
|
|
38
43
|
}): void;
|
|
39
|
-
export declare function openReportIssueModal(): void;
|
|
40
44
|
export {};
|
package/dist/types/index.d.ts
CHANGED
|
@@ -73,6 +73,7 @@ export declare const initRecorder: (options: {
|
|
|
73
73
|
*/
|
|
74
74
|
chunkSnapshot?: boolean;
|
|
75
75
|
useWsWorker?: boolean;
|
|
76
|
+
showEngTicketFieldsInReportIssueModalDefault?: boolean;
|
|
76
77
|
}) => Promise<void>;
|
|
77
78
|
export * from "./graphql";
|
|
78
79
|
export { openReportIssueModal } from "./inAppReportIssueModal";
|
|
@@ -3,6 +3,9 @@ export interface WsHandle {
|
|
|
3
3
|
readyState: number;
|
|
4
4
|
close: () => void;
|
|
5
5
|
}
|
|
6
|
+
type NavigationCallback = () => void;
|
|
7
|
+
/** Register a callback to be invoked immediately on pushState/replaceState/popstate/hashchange. */
|
|
8
|
+
export declare function onNavigationChange(cb: NavigationCallback): void;
|
|
6
9
|
/** Install navigation listeners to keep the cached URL fresh. */
|
|
7
10
|
export declare function ensureHrefCache(): void;
|
|
8
11
|
/** Get cached href (falls back to live read if cache not initialized). */
|
|
@@ -47,3 +50,4 @@ export declare function getFuncSpanHeader(): {
|
|
|
47
50
|
name: string;
|
|
48
51
|
value: string;
|
|
49
52
|
} | null;
|
|
53
|
+
export {};
|
package/dist/websocket.js
CHANGED
|
@@ -102,9 +102,20 @@ let flushIntervalId = null;
|
|
|
102
102
|
let _cachedHref = "";
|
|
103
103
|
let _cachedHrefNoQuery = "";
|
|
104
104
|
let _hrefListenersInstalled = false;
|
|
105
|
+
const _navigationCallbacks = [];
|
|
106
|
+
/** Register a callback to be invoked immediately on pushState/replaceState/popstate/hashchange. */
|
|
107
|
+
export function onNavigationChange(cb) {
|
|
108
|
+
_navigationCallbacks.push(cb);
|
|
109
|
+
}
|
|
105
110
|
function _updateHrefCache() {
|
|
106
111
|
_cachedHref = window.location.href;
|
|
107
112
|
_cachedHrefNoQuery = window.location.origin + window.location.pathname;
|
|
113
|
+
for (const cb of _navigationCallbacks) {
|
|
114
|
+
try {
|
|
115
|
+
cb();
|
|
116
|
+
}
|
|
117
|
+
catch (_) { /* never break navigation */ }
|
|
118
|
+
}
|
|
108
119
|
}
|
|
109
120
|
/** Install navigation listeners to keep the cached URL fresh. */
|
|
110
121
|
export function ensureHrefCache() {
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|