@mhosaic/feedback 0.22.0 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-6OAG72JW.mjs → chunk-WIQ7MTPI.mjs} +190 -65
- package/dist/chunk-WIQ7MTPI.mjs.map +1 -0
- package/dist/embed.min.js +15 -5
- package/dist/embed.min.js.map +1 -1
- package/dist/error-tracking.d.ts +1 -1
- package/dist/{index-CmHNkJsj.d.ts → index-B8Q72Whs.d.ts} +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.mjs +1 -1
- package/dist/loader/react.d.ts +1 -1
- package/dist/loader.d.ts +1 -1
- package/dist/react.d.ts +2 -2
- package/dist/react.mjs +1 -1
- package/dist/replay.d.ts +1 -1
- package/dist/telemetry.d.ts +1 -1
- package/dist/{types-CfSqqfkw.d.ts → types-CRCxVLFH.d.ts} +21 -0
- package/dist/webvitals.d.ts +1 -1
- package/dist/widget.min.js +16 -6
- package/dist/widget.min.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-6OAG72JW.mjs.map +0 -1
|
@@ -50,7 +50,10 @@ function createApiClient(options) {
|
|
|
50
50
|
form.append(field, String(payload[field]));
|
|
51
51
|
}
|
|
52
52
|
form.append("technical_context", JSON.stringify(payload.technical_context));
|
|
53
|
-
|
|
53
|
+
const screenshots = payload.screenshots?.length ? payload.screenshots : payload.screenshot ? [payload.screenshot] : [];
|
|
54
|
+
screenshots.forEach((blob, i) => {
|
|
55
|
+
form.append("screenshot", blob, i === 0 ? "screenshot.png" : `screenshot-${i + 1}.png`);
|
|
56
|
+
});
|
|
54
57
|
if (payload.synthetic) form.append("synthetic", "true");
|
|
55
58
|
if (payload.user?.id) {
|
|
56
59
|
form.append("user", JSON.stringify(payload.user));
|
|
@@ -58,6 +61,9 @@ function createApiClient(options) {
|
|
|
58
61
|
if (payload.widget_version) {
|
|
59
62
|
form.append("widget_version", payload.widget_version);
|
|
60
63
|
}
|
|
64
|
+
if (payload.page_path) {
|
|
65
|
+
form.append("page_path", payload.page_path);
|
|
66
|
+
}
|
|
61
67
|
const headers = {
|
|
62
68
|
Authorization: `Bearer ${options.apiKey}`
|
|
63
69
|
};
|
|
@@ -197,6 +203,7 @@ function createApiClient(options) {
|
|
|
197
203
|
if (filters.mine) params.set("mine", "1");
|
|
198
204
|
if (filters.ordering) params.set("ordering", filters.ordering);
|
|
199
205
|
if (filters.page && filters.page > 1) params.set("page", String(filters.page));
|
|
206
|
+
if (filters.pagePath) params.set("page_path", filters.pagePath);
|
|
200
207
|
const qs = params.toString();
|
|
201
208
|
return qs ? `?${qs}` : "";
|
|
202
209
|
}
|
|
@@ -323,7 +330,7 @@ function installConsolePatch(buffer) {
|
|
|
323
330
|
const original = console[level];
|
|
324
331
|
if (typeof original !== "function") continue;
|
|
325
332
|
originals[level] = original;
|
|
326
|
-
console[level] = function
|
|
333
|
+
console[level] = function patched2(...args) {
|
|
327
334
|
try {
|
|
328
335
|
const rawMessage = args.map(safeStringify).join(" ");
|
|
329
336
|
const message = scrubCredentials(rawMessage).slice(0, 2e3);
|
|
@@ -350,7 +357,7 @@ function installFetchPatch(buffer, sanitize) {
|
|
|
350
357
|
if (typeof window === "undefined" || typeof window.fetch !== "function") return () => {
|
|
351
358
|
};
|
|
352
359
|
const original = window.fetch.bind(window);
|
|
353
|
-
window.fetch = async function
|
|
360
|
+
window.fetch = async function patched2(input, init) {
|
|
354
361
|
const start = performance.now();
|
|
355
362
|
const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
|
|
356
363
|
const method = (init?.method || (input instanceof Request ? input.method : "GET")).toUpperCase();
|
|
@@ -568,6 +575,7 @@ var DEFAULT_STRINGS = {
|
|
|
568
575
|
"form.screenshot.annotate": "Annotate",
|
|
569
576
|
"form.screenshot.error_type": "Unsupported file type. Use PNG, JPEG or WebP.",
|
|
570
577
|
"form.screenshot.error_size": "File too large (max {max} MB).",
|
|
578
|
+
"form.screenshot.error_count": "Too many screenshots (max {max}).",
|
|
571
579
|
"form.context.label": "Page",
|
|
572
580
|
"type.bug": "Bug",
|
|
573
581
|
"type.feature": "Feature request",
|
|
@@ -630,6 +638,7 @@ var DEFAULT_STRINGS = {
|
|
|
630
638
|
"board.detail.status_history": "History",
|
|
631
639
|
"board.scope.project": "Project reports",
|
|
632
640
|
"board.scope.mine": "Your reports",
|
|
641
|
+
"board.scope.thisPage": "This page",
|
|
633
642
|
"board.back": "Back",
|
|
634
643
|
"changelog.empty.title": "Nothing resolved yet",
|
|
635
644
|
"changelog.empty.body": "Once a report you sent is fixed it will appear here, grouped by week.",
|
|
@@ -717,6 +726,7 @@ var FRENCH_STRINGS = {
|
|
|
717
726
|
"form.screenshot.annotate": "Annoter",
|
|
718
727
|
"form.screenshot.error_type": "Format non support\xE9. Utilisez PNG, JPEG ou WebP.",
|
|
719
728
|
"form.screenshot.error_size": "Fichier trop volumineux (max {max} Mo).",
|
|
729
|
+
"form.screenshot.error_count": "Trop de captures d\u2019\xE9cran (max {max}).",
|
|
720
730
|
"form.context.label": "Page",
|
|
721
731
|
"type.bug": "Bogue",
|
|
722
732
|
"type.feature": "Suggestion",
|
|
@@ -779,6 +789,7 @@ var FRENCH_STRINGS = {
|
|
|
779
789
|
"board.detail.status_history": "Historique",
|
|
780
790
|
"board.scope.project": "Rapports du projet",
|
|
781
791
|
"board.scope.mine": "Vos rapports",
|
|
792
|
+
"board.scope.thisPage": "Cette page",
|
|
782
793
|
"board.back": "Retour",
|
|
783
794
|
"changelog.empty.title": "Rien de r\xE9solu pour l\u2019instant",
|
|
784
795
|
"changelog.empty.body": "Quand un rapport que vous avez envoy\xE9 est corrig\xE9, il appara\xEEtra ici, regroup\xE9 par semaine.",
|
|
@@ -865,6 +876,36 @@ function resolveStrings(overrides, options = {}) {
|
|
|
865
876
|
import { h, render } from "preact";
|
|
866
877
|
import { useCallback, useEffect as useEffect8, useState as useState7 } from "preact/hooks";
|
|
867
878
|
|
|
879
|
+
// src/widget/currentPage.ts
|
|
880
|
+
function currentPagePath(opts) {
|
|
881
|
+
const override = opts.getCurrentPage?.();
|
|
882
|
+
if (override) return override;
|
|
883
|
+
return typeof window !== "undefined" ? window.location.pathname : "/";
|
|
884
|
+
}
|
|
885
|
+
var LOCATION_CHANGE = "mfb:locationchange";
|
|
886
|
+
var patched = false;
|
|
887
|
+
function patchHistory() {
|
|
888
|
+
if (patched || typeof history === "undefined") return;
|
|
889
|
+
patched = true;
|
|
890
|
+
for (const m of ["pushState", "replaceState"]) {
|
|
891
|
+
const orig = history[m];
|
|
892
|
+
history[m] = function(...args) {
|
|
893
|
+
const r = orig.apply(this, args);
|
|
894
|
+
window.dispatchEvent(new Event(LOCATION_CHANGE));
|
|
895
|
+
return r;
|
|
896
|
+
};
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
function onLocationChange(cb) {
|
|
900
|
+
patchHistory();
|
|
901
|
+
window.addEventListener("popstate", cb);
|
|
902
|
+
window.addEventListener(LOCATION_CHANGE, cb);
|
|
903
|
+
return () => {
|
|
904
|
+
window.removeEventListener("popstate", cb);
|
|
905
|
+
window.removeEventListener(LOCATION_CHANGE, cb);
|
|
906
|
+
};
|
|
907
|
+
}
|
|
908
|
+
|
|
868
909
|
// src/widget/BoardView.tsx
|
|
869
910
|
import { useEffect as useEffect2, useMemo, useState as useState2 } from "preact/hooks";
|
|
870
911
|
|
|
@@ -923,6 +964,7 @@ var ALLOWED_IMAGE_TYPES = [
|
|
|
923
964
|
"image/webp"
|
|
924
965
|
];
|
|
925
966
|
var MAX_SCREENSHOT_BYTES = 10 * 1024 * 1024;
|
|
967
|
+
var MAX_SCREENSHOTS = 5;
|
|
926
968
|
function validateScreenshotFile(file) {
|
|
927
969
|
if (!ALLOWED_IMAGE_TYPES.includes(
|
|
928
970
|
file.type
|
|
@@ -1092,8 +1134,8 @@ function ReportDetailView({
|
|
|
1092
1134
|
/* @__PURE__ */ jsxs2("div", { class: "report-detail-body", children: [
|
|
1093
1135
|
/* @__PURE__ */ jsx2(ContextBlock, { detail, strings }),
|
|
1094
1136
|
/* @__PURE__ */ jsx2("p", { class: "report-detail-description", children: detail.description }),
|
|
1095
|
-
detail.screenshot_url
|
|
1096
|
-
const safeHref = safeExternalHref(
|
|
1137
|
+
(detail.screenshots?.length ? detail.screenshots.map((s) => s.url) : [detail.screenshot_url]).filter((url) => Boolean(url)).map((url) => {
|
|
1138
|
+
const safeHref = safeExternalHref(url);
|
|
1097
1139
|
return safeHref ? /* @__PURE__ */ jsx2(
|
|
1098
1140
|
"a",
|
|
1099
1141
|
{
|
|
@@ -1101,10 +1143,10 @@ function ReportDetailView({
|
|
|
1101
1143
|
href: safeHref,
|
|
1102
1144
|
target: "_blank",
|
|
1103
1145
|
rel: "noopener noreferrer",
|
|
1104
|
-
children: /* @__PURE__ */ jsx2("img", { src:
|
|
1146
|
+
children: /* @__PURE__ */ jsx2("img", { src: url, alt: "", loading: "lazy" })
|
|
1105
1147
|
}
|
|
1106
|
-
) : /* @__PURE__ */ jsx2("div", { class: "report-detail-screenshot", children: /* @__PURE__ */ jsx2("img", { src:
|
|
1107
|
-
})
|
|
1148
|
+
) : /* @__PURE__ */ jsx2("div", { class: "report-detail-screenshot", children: /* @__PURE__ */ jsx2("img", { src: url, alt: "", loading: "lazy" }) });
|
|
1149
|
+
}),
|
|
1108
1150
|
/* @__PURE__ */ jsx2("h3", { class: "report-detail-section", children: strings["detail.thread"] }),
|
|
1109
1151
|
detail.comments.length === 0 ? /* @__PURE__ */ jsx2("p", { class: "report-detail-empty", children: strings["detail.no_replies"] }) : /* @__PURE__ */ jsx2("ul", { class: "report-comments", children: detail.comments.map((c) => /* @__PURE__ */ jsx2("li", { children: /* @__PURE__ */ jsx2(CommentBubble, { comment: c, strings }) })) }),
|
|
1110
1152
|
detail.status_history && detail.status_history.length > 0 && /* @__PURE__ */ jsx2(StatusHistorySection, { rows: detail.status_history, strings }),
|
|
@@ -1335,8 +1377,9 @@ var STATUSES = [
|
|
|
1335
1377
|
];
|
|
1336
1378
|
var TYPES = ["bug", "feature", "question", "praise", "typo"];
|
|
1337
1379
|
var SEVERITIES = ["blocker", "high", "medium", "low"];
|
|
1338
|
-
function BoardView({ api, externalId, strings }) {
|
|
1380
|
+
function BoardView({ api, externalId, strings, getCurrentPage }) {
|
|
1339
1381
|
const [filters, setFilters] = useState2(() => loadBoardView(externalId));
|
|
1382
|
+
const [thisPage, setThisPage] = useState2(true);
|
|
1340
1383
|
useEffect2(() => {
|
|
1341
1384
|
saveBoardView(externalId, filters);
|
|
1342
1385
|
}, [externalId, filtersHash(filters)]);
|
|
@@ -1350,14 +1393,23 @@ function BoardView({ api, externalId, strings }) {
|
|
|
1350
1393
|
const activeFilterCount = useMemo(() => {
|
|
1351
1394
|
return (filters.status?.length ?? 0) + (filters.type?.length ?? 0) + (filters.severity?.length ?? 0) + (filters.q ? 1 : 0) + (filters.mine ? 1 : 0);
|
|
1352
1395
|
}, [filters]);
|
|
1396
|
+
useEffect2(() => {
|
|
1397
|
+
if (!thisPage) return;
|
|
1398
|
+
return onLocationChange(() => setReloadTick((t) => t + 1));
|
|
1399
|
+
}, [thisPage]);
|
|
1400
|
+
const fetchFilters = useMemo(
|
|
1401
|
+
() => thisPage ? { ...filters, pagePath: currentPagePath(getCurrentPage !== void 0 ? { getCurrentPage } : {}) } : filters,
|
|
1402
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1403
|
+
[filtersHash(filters), thisPage, reloadTick]
|
|
1404
|
+
);
|
|
1353
1405
|
useEffect2(() => {
|
|
1354
1406
|
let cancelled = false;
|
|
1355
1407
|
let timer = null;
|
|
1356
1408
|
const tick = async () => {
|
|
1357
1409
|
try {
|
|
1358
1410
|
const [list, k] = await Promise.all([
|
|
1359
|
-
api.listBoard(externalId,
|
|
1360
|
-
api.fetchBoardKpis(externalId,
|
|
1411
|
+
api.listBoard(externalId, fetchFilters),
|
|
1412
|
+
api.fetchBoardKpis(externalId, fetchFilters)
|
|
1361
1413
|
]);
|
|
1362
1414
|
if (cancelled) return;
|
|
1363
1415
|
setPage(list);
|
|
@@ -1377,7 +1429,7 @@ function BoardView({ api, externalId, strings }) {
|
|
|
1377
1429
|
cancelled = true;
|
|
1378
1430
|
if (timer) clearTimeout(timer);
|
|
1379
1431
|
};
|
|
1380
|
-
}, [api, externalId,
|
|
1432
|
+
}, [api, externalId, JSON.stringify(fetchFilters)]);
|
|
1381
1433
|
const selectedRow = useMemo(() => {
|
|
1382
1434
|
if (!selectedId || !page) return null;
|
|
1383
1435
|
return page.results.find((r) => r.id === selectedId) ?? null;
|
|
@@ -1394,7 +1446,9 @@ function BoardView({ api, externalId, strings }) {
|
|
|
1394
1446
|
filters,
|
|
1395
1447
|
onChange: setFilters,
|
|
1396
1448
|
activeCount: activeFilterCount,
|
|
1397
|
-
strings
|
|
1449
|
+
strings,
|
|
1450
|
+
thisPage,
|
|
1451
|
+
onToggleThisPage: () => setThisPage((v) => !v)
|
|
1398
1452
|
}
|
|
1399
1453
|
),
|
|
1400
1454
|
/* @__PURE__ */ jsxs3("div", { class: "board-body", children: [
|
|
@@ -1502,7 +1556,9 @@ function BoardFilters({
|
|
|
1502
1556
|
filters,
|
|
1503
1557
|
onChange,
|
|
1504
1558
|
activeCount,
|
|
1505
|
-
strings
|
|
1559
|
+
strings,
|
|
1560
|
+
thisPage,
|
|
1561
|
+
onToggleThisPage
|
|
1506
1562
|
}) {
|
|
1507
1563
|
const setStatus = (value) => {
|
|
1508
1564
|
if (value === "") {
|
|
@@ -1614,6 +1670,19 @@ function BoardFilters({
|
|
|
1614
1670
|
),
|
|
1615
1671
|
/* @__PURE__ */ jsx3("span", { children: strings["board.filter.mine"] })
|
|
1616
1672
|
] }),
|
|
1673
|
+
/* @__PURE__ */ jsxs3(
|
|
1674
|
+
"button",
|
|
1675
|
+
{
|
|
1676
|
+
type: "button",
|
|
1677
|
+
class: `board-chip ${thisPage ? "board-chip--on" : ""}`,
|
|
1678
|
+
onClick: onToggleThisPage,
|
|
1679
|
+
children: [
|
|
1680
|
+
"\u{1F4CD} ",
|
|
1681
|
+
strings["board.scope.thisPage"],
|
|
1682
|
+
thisPage ? " \u2715" : ""
|
|
1683
|
+
]
|
|
1684
|
+
}
|
|
1685
|
+
),
|
|
1617
1686
|
activeCount > 0 && /* @__PURE__ */ jsxs3("button", { type: "button", class: "board-filter-clear", onClick: clear, children: [
|
|
1618
1687
|
/* @__PURE__ */ jsx3(CloseIcon, {}),
|
|
1619
1688
|
strings["board.filter.clear"]
|
|
@@ -2456,45 +2525,58 @@ function Form({ strings, onSubmit, onCancel, status, errorMessage }) {
|
|
|
2456
2525
|
const [feedbackType, setFeedbackType] = useState5("bug");
|
|
2457
2526
|
const [severity, setSeverity] = useState5("medium");
|
|
2458
2527
|
const [localError, setLocalError] = useState5("");
|
|
2459
|
-
const [
|
|
2460
|
-
const [screenshotPreview, setScreenshotPreview] = useState5(null);
|
|
2528
|
+
const [shots, setShots] = useState5([]);
|
|
2461
2529
|
const [isDragOver, setIsDragOver] = useState5(false);
|
|
2462
|
-
const [
|
|
2530
|
+
const [annotatingIndex, setAnnotatingIndex] = useState5(null);
|
|
2463
2531
|
const fileInputRef = useRef4(null);
|
|
2464
2532
|
const dropZoneRef = useRef4(null);
|
|
2465
2533
|
const submitting = status === "submitting";
|
|
2466
2534
|
const submitLabel = submitting ? strings["form.submitting"] : strings["form.submit"];
|
|
2467
2535
|
const pageUrl = typeof window !== "undefined" ? window.location.href : "";
|
|
2536
|
+
const shotsRef = useRef4(shots);
|
|
2537
|
+
shotsRef.current = shots;
|
|
2468
2538
|
useEffect5(() => {
|
|
2469
2539
|
return () => {
|
|
2470
|
-
|
|
2540
|
+
shotsRef.current.forEach((s) => URL.revokeObjectURL(s.preview));
|
|
2471
2541
|
};
|
|
2472
|
-
}, [
|
|
2473
|
-
const
|
|
2542
|
+
}, []);
|
|
2543
|
+
const acceptFiles = (files) => {
|
|
2474
2544
|
setLocalError("");
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
)
|
|
2481
|
-
|
|
2545
|
+
const remaining = MAX_SCREENSHOTS - shots.length;
|
|
2546
|
+
const batch = files.slice(0, Math.max(0, remaining));
|
|
2547
|
+
for (const file of batch) {
|
|
2548
|
+
if (file instanceof File) {
|
|
2549
|
+
const err = validateScreenshotFile(file);
|
|
2550
|
+
if (err) {
|
|
2551
|
+
setLocalError(
|
|
2552
|
+
err.kind === "type" ? strings["form.screenshot.error_type"] : strings["form.screenshot.error_size"].replace("{max}", String(err.maxMb))
|
|
2553
|
+
);
|
|
2554
|
+
return;
|
|
2555
|
+
}
|
|
2482
2556
|
}
|
|
2483
2557
|
}
|
|
2484
|
-
if (
|
|
2485
|
-
|
|
2486
|
-
|
|
2558
|
+
if (batch.length) {
|
|
2559
|
+
const incoming = batch.map((blob) => ({ blob, preview: URL.createObjectURL(blob) }));
|
|
2560
|
+
setShots((prev) => [...prev, ...incoming]);
|
|
2561
|
+
}
|
|
2562
|
+
if (files.length > batch.length) {
|
|
2563
|
+
setLocalError(
|
|
2564
|
+
strings["form.screenshot.error_count"].replace("{max}", String(MAX_SCREENSHOTS))
|
|
2565
|
+
);
|
|
2566
|
+
}
|
|
2487
2567
|
};
|
|
2488
|
-
const
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2568
|
+
const removeShot = (index) => {
|
|
2569
|
+
const shot = shots[index];
|
|
2570
|
+
if (shot) URL.revokeObjectURL(shot.preview);
|
|
2571
|
+
setShots((prev) => prev.filter((_, i) => i !== index));
|
|
2492
2572
|
setLocalError("");
|
|
2493
2573
|
if (fileInputRef.current) fileInputRef.current.value = "";
|
|
2494
2574
|
};
|
|
2495
2575
|
const handleFileInputChange = (e) => {
|
|
2496
|
-
const
|
|
2497
|
-
|
|
2576
|
+
const input = e.target;
|
|
2577
|
+
const files = Array.from(input.files ?? []);
|
|
2578
|
+
if (files.length) acceptFiles(files);
|
|
2579
|
+
input.value = "";
|
|
2498
2580
|
};
|
|
2499
2581
|
const handleDragOver = (e) => {
|
|
2500
2582
|
e.preventDefault();
|
|
@@ -2510,8 +2592,8 @@ function Form({ strings, onSubmit, onCancel, status, errorMessage }) {
|
|
|
2510
2592
|
e.preventDefault();
|
|
2511
2593
|
e.stopPropagation();
|
|
2512
2594
|
setIsDragOver(false);
|
|
2513
|
-
const
|
|
2514
|
-
if (
|
|
2595
|
+
const files = Array.from(e.dataTransfer?.files ?? []);
|
|
2596
|
+
if (files.length) acceptFiles(files);
|
|
2515
2597
|
};
|
|
2516
2598
|
useEffect5(() => {
|
|
2517
2599
|
const zone = dropZoneRef.current;
|
|
@@ -2524,7 +2606,7 @@ function Form({ strings, onSubmit, onCancel, status, errorMessage }) {
|
|
|
2524
2606
|
const file = item.getAsFile();
|
|
2525
2607
|
if (file) {
|
|
2526
2608
|
e.preventDefault();
|
|
2527
|
-
|
|
2609
|
+
acceptFiles([file]);
|
|
2528
2610
|
return;
|
|
2529
2611
|
}
|
|
2530
2612
|
}
|
|
@@ -2532,12 +2614,17 @@ function Form({ strings, onSubmit, onCancel, status, errorMessage }) {
|
|
|
2532
2614
|
};
|
|
2533
2615
|
zone.addEventListener("paste", onPaste);
|
|
2534
2616
|
return () => zone.removeEventListener("paste", onPaste);
|
|
2535
|
-
}, [
|
|
2617
|
+
}, [shots.length]);
|
|
2536
2618
|
const handleAnnotated = (annotated) => {
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2619
|
+
const index = annotatingIndex;
|
|
2620
|
+
if (index === null || !shots[index]) {
|
|
2621
|
+
setAnnotatingIndex(null);
|
|
2622
|
+
return;
|
|
2623
|
+
}
|
|
2624
|
+
URL.revokeObjectURL(shots[index].preview);
|
|
2625
|
+
const replacement = { blob: annotated, preview: URL.createObjectURL(annotated) };
|
|
2626
|
+
setShots((prev) => prev.map((s, i) => i === index ? replacement : s));
|
|
2627
|
+
setAnnotatingIndex(null);
|
|
2541
2628
|
};
|
|
2542
2629
|
const handleSubmit = (e) => {
|
|
2543
2630
|
e.preventDefault();
|
|
@@ -2551,8 +2638,8 @@ function Form({ strings, onSubmit, onCancel, status, errorMessage }) {
|
|
|
2551
2638
|
feedback_type: feedbackType,
|
|
2552
2639
|
severity
|
|
2553
2640
|
};
|
|
2554
|
-
if (
|
|
2555
|
-
values.
|
|
2641
|
+
if (shots.length) {
|
|
2642
|
+
values.screenshots = shots.map((s) => s.blob);
|
|
2556
2643
|
values.capture_method = "manual";
|
|
2557
2644
|
}
|
|
2558
2645
|
onSubmit(values);
|
|
@@ -2605,21 +2692,22 @@ function Form({ strings, onSubmit, onCancel, status, errorMessage }) {
|
|
|
2605
2692
|
ref: fileInputRef,
|
|
2606
2693
|
type: "file",
|
|
2607
2694
|
accept: "image/png,image/jpeg,image/webp",
|
|
2695
|
+
multiple: true,
|
|
2608
2696
|
class: "mfb-sr-only",
|
|
2609
2697
|
onChange: handleFileInputChange,
|
|
2610
2698
|
"aria-hidden": "true",
|
|
2611
2699
|
tabIndex: -1
|
|
2612
2700
|
}
|
|
2613
2701
|
),
|
|
2614
|
-
|
|
2615
|
-
/* @__PURE__ */ jsx8("img", { src:
|
|
2702
|
+
shots.length > 0 && /* @__PURE__ */ jsx8("div", { class: "screenshot-strip", children: shots.map((shot, index) => /* @__PURE__ */ jsxs8("div", { class: "screenshot-preview", children: [
|
|
2703
|
+
/* @__PURE__ */ jsx8("img", { src: shot.preview, alt: "" }),
|
|
2616
2704
|
/* @__PURE__ */ jsxs8("div", { class: "screenshot-preview-actions", children: [
|
|
2617
2705
|
/* @__PURE__ */ jsxs8(
|
|
2618
2706
|
"button",
|
|
2619
2707
|
{
|
|
2620
2708
|
type: "button",
|
|
2621
2709
|
class: "btn btn--primary screenshot-annotate",
|
|
2622
|
-
onClick: () =>
|
|
2710
|
+
onClick: () => setAnnotatingIndex(index),
|
|
2623
2711
|
children: [
|
|
2624
2712
|
/* @__PURE__ */ jsxs8("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true", children: [
|
|
2625
2713
|
/* @__PURE__ */ jsx8("path", { d: "M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" }),
|
|
@@ -2629,12 +2717,13 @@ function Form({ strings, onSubmit, onCancel, status, errorMessage }) {
|
|
|
2629
2717
|
]
|
|
2630
2718
|
}
|
|
2631
2719
|
),
|
|
2632
|
-
/* @__PURE__ */ jsxs8("button", { type: "button", class: "btn", onClick:
|
|
2720
|
+
/* @__PURE__ */ jsxs8("button", { type: "button", class: "btn", onClick: () => removeShot(index), children: [
|
|
2633
2721
|
/* @__PURE__ */ jsx8("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx8("path", { d: "M3 6h18M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2m3 0v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6h14z" }) }),
|
|
2634
2722
|
strings["form.screenshot.remove"]
|
|
2635
2723
|
] })
|
|
2636
2724
|
] })
|
|
2637
|
-
] })
|
|
2725
|
+
] }, shot.preview)) }),
|
|
2726
|
+
shots.length < MAX_SCREENSHOTS && /* @__PURE__ */ jsxs8(
|
|
2638
2727
|
"div",
|
|
2639
2728
|
{
|
|
2640
2729
|
ref: dropZoneRef,
|
|
@@ -2679,12 +2768,12 @@ function Form({ strings, onSubmit, onCancel, status, errorMessage }) {
|
|
|
2679
2768
|
/* @__PURE__ */ jsx8("button", { type: "button", class: "btn", onClick: onCancel, disabled: submitting, children: strings["form.cancel"] }),
|
|
2680
2769
|
/* @__PURE__ */ jsx8("button", { type: "submit", class: "btn btn--primary", disabled: submitting, children: submitLabel })
|
|
2681
2770
|
] }),
|
|
2682
|
-
|
|
2771
|
+
annotatingIndex !== null && shots[annotatingIndex] && /* @__PURE__ */ jsx8(
|
|
2683
2772
|
Annotator,
|
|
2684
2773
|
{
|
|
2685
|
-
imageBlob:
|
|
2774
|
+
imageBlob: shots[annotatingIndex].blob,
|
|
2686
2775
|
strings,
|
|
2687
|
-
onCancel: () =>
|
|
2776
|
+
onCancel: () => setAnnotatingIndex(null),
|
|
2688
2777
|
onSave: handleAnnotated
|
|
2689
2778
|
}
|
|
2690
2779
|
)
|
|
@@ -3319,6 +3408,12 @@ var WIDGET_STYLES = `
|
|
|
3319
3408
|
.screenshot-cta strong { color: var(--mfb-accent); font-weight: 600; }
|
|
3320
3409
|
.screenshot-formats { font-size: var(--mfb-text-xs); color: var(--mfb-text-muted); }
|
|
3321
3410
|
|
|
3411
|
+
.screenshot-strip {
|
|
3412
|
+
display: flex;
|
|
3413
|
+
flex-direction: column;
|
|
3414
|
+
gap: var(--mfb-space-2);
|
|
3415
|
+
margin-bottom: var(--mfb-space-2);
|
|
3416
|
+
}
|
|
3322
3417
|
.screenshot-preview {
|
|
3323
3418
|
position: relative;
|
|
3324
3419
|
border: 1px solid var(--mfb-border);
|
|
@@ -3336,6 +3431,10 @@ var WIDGET_STYLES = `
|
|
|
3336
3431
|
object-fit: contain;
|
|
3337
3432
|
background: #1a1a1a;
|
|
3338
3433
|
}
|
|
3434
|
+
/* In the multi-shot strip each preview stays compact so several fit. */
|
|
3435
|
+
.screenshot-strip .screenshot-preview img {
|
|
3436
|
+
max-height: 120px;
|
|
3437
|
+
}
|
|
3339
3438
|
.screenshot-preview-actions {
|
|
3340
3439
|
display: flex;
|
|
3341
3440
|
gap: var(--mfb-space-2);
|
|
@@ -4626,6 +4725,17 @@ function mountWidget(options) {
|
|
|
4626
4725
|
void _drop;
|
|
4627
4726
|
return rest;
|
|
4628
4727
|
}
|
|
4728
|
+
function openWidget(externalId) {
|
|
4729
|
+
rerender({ ...currentState, open: true, tab: "send" });
|
|
4730
|
+
if (options.openToCurrentPageFeedback && options.api && externalId) {
|
|
4731
|
+
options.api.fetchBoardKpis(externalId, { pagePath: currentPagePath(options) }).then((kpis) => {
|
|
4732
|
+
if (kpis.total > 0 && currentState.open && currentState.tab === "send") {
|
|
4733
|
+
rerender({ ...currentState, tab: "board" });
|
|
4734
|
+
}
|
|
4735
|
+
}).catch(() => {
|
|
4736
|
+
});
|
|
4737
|
+
}
|
|
4738
|
+
}
|
|
4629
4739
|
function Root({ state }) {
|
|
4630
4740
|
const handleSubmit = useCallback(async (values) => {
|
|
4631
4741
|
rerender({ ...currentState, status: "submitting" });
|
|
@@ -4685,7 +4795,7 @@ function mountWidget(options) {
|
|
|
4685
4795
|
Fab,
|
|
4686
4796
|
{
|
|
4687
4797
|
label: options.strings["fab.label"],
|
|
4688
|
-
onClick: () =>
|
|
4798
|
+
onClick: () => openWidget(externalId)
|
|
4689
4799
|
}
|
|
4690
4800
|
),
|
|
4691
4801
|
state.open && /* @__PURE__ */ jsxs12(
|
|
@@ -4786,7 +4896,8 @@ function mountWidget(options) {
|
|
|
4786
4896
|
{
|
|
4787
4897
|
api: options.api,
|
|
4788
4898
|
externalId,
|
|
4789
|
-
strings: options.strings
|
|
4899
|
+
strings: options.strings,
|
|
4900
|
+
...options.getCurrentPage !== void 0 && { getCurrentPage: options.getCurrentPage }
|
|
4790
4901
|
}
|
|
4791
4902
|
)
|
|
4792
4903
|
]
|
|
@@ -4797,7 +4908,7 @@ function mountWidget(options) {
|
|
|
4797
4908
|
rerender(currentState);
|
|
4798
4909
|
return {
|
|
4799
4910
|
open() {
|
|
4800
|
-
|
|
4911
|
+
openWidget(options.getExternalId?.());
|
|
4801
4912
|
},
|
|
4802
4913
|
close() {
|
|
4803
4914
|
rerender({ ...currentState, open: false, status: "idle" });
|
|
@@ -4860,7 +4971,8 @@ function createFeedback(config) {
|
|
|
4860
4971
|
document.body.appendChild(host);
|
|
4861
4972
|
}
|
|
4862
4973
|
async function buildAndSubmit(values) {
|
|
4863
|
-
const
|
|
4974
|
+
const attached = values.screenshots?.length ? values.screenshots : values.screenshot ? [values.screenshot] : void 0;
|
|
4975
|
+
const manualScreenshots = values.synthetic ? void 0 : attached;
|
|
4864
4976
|
const technical_context = capture.snapshot();
|
|
4865
4977
|
if (user) {
|
|
4866
4978
|
const { userHash: _hash, exp: _exp, ...safeUser } = user;
|
|
@@ -4869,7 +4981,7 @@ function createFeedback(config) {
|
|
|
4869
4981
|
if (metadata && Object.keys(metadata).length > 0) {
|
|
4870
4982
|
technical_context.metadata = { ...metadata };
|
|
4871
4983
|
}
|
|
4872
|
-
const captureMethod =
|
|
4984
|
+
const captureMethod = manualScreenshots?.length ? values.capture_method ?? "manual" : "none";
|
|
4873
4985
|
const payload = {
|
|
4874
4986
|
description: values.description,
|
|
4875
4987
|
feedback_type: values.feedback_type ?? "bug",
|
|
@@ -4880,11 +4992,16 @@ function createFeedback(config) {
|
|
|
4880
4992
|
capture_method: captureMethod,
|
|
4881
4993
|
technical_context
|
|
4882
4994
|
};
|
|
4883
|
-
if ("0.
|
|
4884
|
-
payload.widget_version = "0.
|
|
4995
|
+
if ("0.24.0") {
|
|
4996
|
+
payload.widget_version = "0.24.0";
|
|
4997
|
+
}
|
|
4998
|
+
if (manualScreenshots?.length) {
|
|
4999
|
+
payload.screenshots = manualScreenshots;
|
|
5000
|
+
payload.screenshot = manualScreenshots[0];
|
|
4885
5001
|
}
|
|
4886
|
-
if (manualScreenshot) payload.screenshot = manualScreenshot;
|
|
4887
5002
|
if (values.synthetic) payload.synthetic = true;
|
|
5003
|
+
const pagePath = currentPagePath(config);
|
|
5004
|
+
if (pagePath) payload.page_path = pagePath;
|
|
4888
5005
|
if (user?.id !== void 0 && user.id !== null && user.id !== "") {
|
|
4889
5006
|
payload.user = {
|
|
4890
5007
|
// The host can pass `id` as a string or number; the backend
|
|
@@ -4925,7 +5042,14 @@ function createFeedback(config) {
|
|
|
4925
5042
|
// Send the identified email alongside the id so an email-based allowlist
|
|
4926
5043
|
// can match (the backend matches external_id OR email). Read from `user`
|
|
4927
5044
|
// live so identity set after createFeedback() is reflected.
|
|
4928
|
-
checkVisibility: (externalId) => api.checkVisibility(externalId, user?.email)
|
|
5045
|
+
checkVisibility: (externalId) => api.checkVisibility(externalId, user?.email),
|
|
5046
|
+
// Thread the host's getCurrentPage override so the Board chip resolves
|
|
5047
|
+
// the page identically to what the submit side records (§ currentPage).
|
|
5048
|
+
...config.getCurrentPage !== void 0 && { getCurrentPage: config.getCurrentPage },
|
|
5049
|
+
// Opt-in: FAB opens to the page-scoped Board (default off → Send-first).
|
|
5050
|
+
...config.openToCurrentPageFeedback !== void 0 && {
|
|
5051
|
+
openToCurrentPageFeedback: config.openToCurrentPageFeedback
|
|
5052
|
+
}
|
|
4929
5053
|
});
|
|
4930
5054
|
let qaHandle;
|
|
4931
5055
|
let qaDisposed = false;
|
|
@@ -4960,7 +5084,8 @@ function createFeedback(config) {
|
|
|
4960
5084
|
...partial.feedback_type !== void 0 && { feedback_type: partial.feedback_type },
|
|
4961
5085
|
...partial.severity !== void 0 && { severity: partial.severity },
|
|
4962
5086
|
...partial.synthetic !== void 0 && { synthetic: partial.synthetic },
|
|
4963
|
-
...partial.screenshot !== void 0 && { screenshot: partial.screenshot }
|
|
5087
|
+
...partial.screenshot !== void 0 && { screenshot: partial.screenshot },
|
|
5088
|
+
...partial.screenshots !== void 0 && { screenshots: partial.screenshots }
|
|
4964
5089
|
});
|
|
4965
5090
|
},
|
|
4966
5091
|
identify(u) {
|
|
@@ -4992,4 +5117,4 @@ function createFeedback(config) {
|
|
|
4992
5117
|
export {
|
|
4993
5118
|
createFeedback
|
|
4994
5119
|
};
|
|
4995
|
-
//# sourceMappingURL=chunk-
|
|
5120
|
+
//# sourceMappingURL=chunk-WIQ7MTPI.mjs.map
|