@mhosaic/feedback 0.23.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-NCNRU3SR.mjs → chunk-WIQ7MTPI.mjs} +94 -53
- 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-DHNIgEcR.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-CKwnZDsE.d.ts → types-CRCxVLFH.d.ts} +5 -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-NCNRU3SR.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));
|
|
@@ -572,6 +575,7 @@ var DEFAULT_STRINGS = {
|
|
|
572
575
|
"form.screenshot.annotate": "Annotate",
|
|
573
576
|
"form.screenshot.error_type": "Unsupported file type. Use PNG, JPEG or WebP.",
|
|
574
577
|
"form.screenshot.error_size": "File too large (max {max} MB).",
|
|
578
|
+
"form.screenshot.error_count": "Too many screenshots (max {max}).",
|
|
575
579
|
"form.context.label": "Page",
|
|
576
580
|
"type.bug": "Bug",
|
|
577
581
|
"type.feature": "Feature request",
|
|
@@ -722,6 +726,7 @@ var FRENCH_STRINGS = {
|
|
|
722
726
|
"form.screenshot.annotate": "Annoter",
|
|
723
727
|
"form.screenshot.error_type": "Format non support\xE9. Utilisez PNG, JPEG ou WebP.",
|
|
724
728
|
"form.screenshot.error_size": "Fichier trop volumineux (max {max} Mo).",
|
|
729
|
+
"form.screenshot.error_count": "Trop de captures d\u2019\xE9cran (max {max}).",
|
|
725
730
|
"form.context.label": "Page",
|
|
726
731
|
"type.bug": "Bogue",
|
|
727
732
|
"type.feature": "Suggestion",
|
|
@@ -959,6 +964,7 @@ var ALLOWED_IMAGE_TYPES = [
|
|
|
959
964
|
"image/webp"
|
|
960
965
|
];
|
|
961
966
|
var MAX_SCREENSHOT_BYTES = 10 * 1024 * 1024;
|
|
967
|
+
var MAX_SCREENSHOTS = 5;
|
|
962
968
|
function validateScreenshotFile(file) {
|
|
963
969
|
if (!ALLOWED_IMAGE_TYPES.includes(
|
|
964
970
|
file.type
|
|
@@ -1128,8 +1134,8 @@ function ReportDetailView({
|
|
|
1128
1134
|
/* @__PURE__ */ jsxs2("div", { class: "report-detail-body", children: [
|
|
1129
1135
|
/* @__PURE__ */ jsx2(ContextBlock, { detail, strings }),
|
|
1130
1136
|
/* @__PURE__ */ jsx2("p", { class: "report-detail-description", children: detail.description }),
|
|
1131
|
-
detail.screenshot_url
|
|
1132
|
-
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);
|
|
1133
1139
|
return safeHref ? /* @__PURE__ */ jsx2(
|
|
1134
1140
|
"a",
|
|
1135
1141
|
{
|
|
@@ -1137,10 +1143,10 @@ function ReportDetailView({
|
|
|
1137
1143
|
href: safeHref,
|
|
1138
1144
|
target: "_blank",
|
|
1139
1145
|
rel: "noopener noreferrer",
|
|
1140
|
-
children: /* @__PURE__ */ jsx2("img", { src:
|
|
1146
|
+
children: /* @__PURE__ */ jsx2("img", { src: url, alt: "", loading: "lazy" })
|
|
1141
1147
|
}
|
|
1142
|
-
) : /* @__PURE__ */ jsx2("div", { class: "report-detail-screenshot", children: /* @__PURE__ */ jsx2("img", { src:
|
|
1143
|
-
})
|
|
1148
|
+
) : /* @__PURE__ */ jsx2("div", { class: "report-detail-screenshot", children: /* @__PURE__ */ jsx2("img", { src: url, alt: "", loading: "lazy" }) });
|
|
1149
|
+
}),
|
|
1144
1150
|
/* @__PURE__ */ jsx2("h3", { class: "report-detail-section", children: strings["detail.thread"] }),
|
|
1145
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 }) })) }),
|
|
1146
1152
|
detail.status_history && detail.status_history.length > 0 && /* @__PURE__ */ jsx2(StatusHistorySection, { rows: detail.status_history, strings }),
|
|
@@ -2519,45 +2525,58 @@ function Form({ strings, onSubmit, onCancel, status, errorMessage }) {
|
|
|
2519
2525
|
const [feedbackType, setFeedbackType] = useState5("bug");
|
|
2520
2526
|
const [severity, setSeverity] = useState5("medium");
|
|
2521
2527
|
const [localError, setLocalError] = useState5("");
|
|
2522
|
-
const [
|
|
2523
|
-
const [screenshotPreview, setScreenshotPreview] = useState5(null);
|
|
2528
|
+
const [shots, setShots] = useState5([]);
|
|
2524
2529
|
const [isDragOver, setIsDragOver] = useState5(false);
|
|
2525
|
-
const [
|
|
2530
|
+
const [annotatingIndex, setAnnotatingIndex] = useState5(null);
|
|
2526
2531
|
const fileInputRef = useRef4(null);
|
|
2527
2532
|
const dropZoneRef = useRef4(null);
|
|
2528
2533
|
const submitting = status === "submitting";
|
|
2529
2534
|
const submitLabel = submitting ? strings["form.submitting"] : strings["form.submit"];
|
|
2530
2535
|
const pageUrl = typeof window !== "undefined" ? window.location.href : "";
|
|
2536
|
+
const shotsRef = useRef4(shots);
|
|
2537
|
+
shotsRef.current = shots;
|
|
2531
2538
|
useEffect5(() => {
|
|
2532
2539
|
return () => {
|
|
2533
|
-
|
|
2540
|
+
shotsRef.current.forEach((s) => URL.revokeObjectURL(s.preview));
|
|
2534
2541
|
};
|
|
2535
|
-
}, [
|
|
2536
|
-
const
|
|
2542
|
+
}, []);
|
|
2543
|
+
const acceptFiles = (files) => {
|
|
2537
2544
|
setLocalError("");
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
)
|
|
2544
|
-
|
|
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
|
+
}
|
|
2545
2556
|
}
|
|
2546
2557
|
}
|
|
2547
|
-
if (
|
|
2548
|
-
|
|
2549
|
-
|
|
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
|
+
}
|
|
2550
2567
|
};
|
|
2551
|
-
const
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2568
|
+
const removeShot = (index) => {
|
|
2569
|
+
const shot = shots[index];
|
|
2570
|
+
if (shot) URL.revokeObjectURL(shot.preview);
|
|
2571
|
+
setShots((prev) => prev.filter((_, i) => i !== index));
|
|
2555
2572
|
setLocalError("");
|
|
2556
2573
|
if (fileInputRef.current) fileInputRef.current.value = "";
|
|
2557
2574
|
};
|
|
2558
2575
|
const handleFileInputChange = (e) => {
|
|
2559
|
-
const
|
|
2560
|
-
|
|
2576
|
+
const input = e.target;
|
|
2577
|
+
const files = Array.from(input.files ?? []);
|
|
2578
|
+
if (files.length) acceptFiles(files);
|
|
2579
|
+
input.value = "";
|
|
2561
2580
|
};
|
|
2562
2581
|
const handleDragOver = (e) => {
|
|
2563
2582
|
e.preventDefault();
|
|
@@ -2573,8 +2592,8 @@ function Form({ strings, onSubmit, onCancel, status, errorMessage }) {
|
|
|
2573
2592
|
e.preventDefault();
|
|
2574
2593
|
e.stopPropagation();
|
|
2575
2594
|
setIsDragOver(false);
|
|
2576
|
-
const
|
|
2577
|
-
if (
|
|
2595
|
+
const files = Array.from(e.dataTransfer?.files ?? []);
|
|
2596
|
+
if (files.length) acceptFiles(files);
|
|
2578
2597
|
};
|
|
2579
2598
|
useEffect5(() => {
|
|
2580
2599
|
const zone = dropZoneRef.current;
|
|
@@ -2587,7 +2606,7 @@ function Form({ strings, onSubmit, onCancel, status, errorMessage }) {
|
|
|
2587
2606
|
const file = item.getAsFile();
|
|
2588
2607
|
if (file) {
|
|
2589
2608
|
e.preventDefault();
|
|
2590
|
-
|
|
2609
|
+
acceptFiles([file]);
|
|
2591
2610
|
return;
|
|
2592
2611
|
}
|
|
2593
2612
|
}
|
|
@@ -2595,12 +2614,17 @@ function Form({ strings, onSubmit, onCancel, status, errorMessage }) {
|
|
|
2595
2614
|
};
|
|
2596
2615
|
zone.addEventListener("paste", onPaste);
|
|
2597
2616
|
return () => zone.removeEventListener("paste", onPaste);
|
|
2598
|
-
}, [
|
|
2617
|
+
}, [shots.length]);
|
|
2599
2618
|
const handleAnnotated = (annotated) => {
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
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);
|
|
2604
2628
|
};
|
|
2605
2629
|
const handleSubmit = (e) => {
|
|
2606
2630
|
e.preventDefault();
|
|
@@ -2614,8 +2638,8 @@ function Form({ strings, onSubmit, onCancel, status, errorMessage }) {
|
|
|
2614
2638
|
feedback_type: feedbackType,
|
|
2615
2639
|
severity
|
|
2616
2640
|
};
|
|
2617
|
-
if (
|
|
2618
|
-
values.
|
|
2641
|
+
if (shots.length) {
|
|
2642
|
+
values.screenshots = shots.map((s) => s.blob);
|
|
2619
2643
|
values.capture_method = "manual";
|
|
2620
2644
|
}
|
|
2621
2645
|
onSubmit(values);
|
|
@@ -2668,21 +2692,22 @@ function Form({ strings, onSubmit, onCancel, status, errorMessage }) {
|
|
|
2668
2692
|
ref: fileInputRef,
|
|
2669
2693
|
type: "file",
|
|
2670
2694
|
accept: "image/png,image/jpeg,image/webp",
|
|
2695
|
+
multiple: true,
|
|
2671
2696
|
class: "mfb-sr-only",
|
|
2672
2697
|
onChange: handleFileInputChange,
|
|
2673
2698
|
"aria-hidden": "true",
|
|
2674
2699
|
tabIndex: -1
|
|
2675
2700
|
}
|
|
2676
2701
|
),
|
|
2677
|
-
|
|
2678
|
-
/* @__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: "" }),
|
|
2679
2704
|
/* @__PURE__ */ jsxs8("div", { class: "screenshot-preview-actions", children: [
|
|
2680
2705
|
/* @__PURE__ */ jsxs8(
|
|
2681
2706
|
"button",
|
|
2682
2707
|
{
|
|
2683
2708
|
type: "button",
|
|
2684
2709
|
class: "btn btn--primary screenshot-annotate",
|
|
2685
|
-
onClick: () =>
|
|
2710
|
+
onClick: () => setAnnotatingIndex(index),
|
|
2686
2711
|
children: [
|
|
2687
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: [
|
|
2688
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" }),
|
|
@@ -2692,12 +2717,13 @@ function Form({ strings, onSubmit, onCancel, status, errorMessage }) {
|
|
|
2692
2717
|
]
|
|
2693
2718
|
}
|
|
2694
2719
|
),
|
|
2695
|
-
/* @__PURE__ */ jsxs8("button", { type: "button", class: "btn", onClick:
|
|
2720
|
+
/* @__PURE__ */ jsxs8("button", { type: "button", class: "btn", onClick: () => removeShot(index), children: [
|
|
2696
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" }) }),
|
|
2697
2722
|
strings["form.screenshot.remove"]
|
|
2698
2723
|
] })
|
|
2699
2724
|
] })
|
|
2700
|
-
] })
|
|
2725
|
+
] }, shot.preview)) }),
|
|
2726
|
+
shots.length < MAX_SCREENSHOTS && /* @__PURE__ */ jsxs8(
|
|
2701
2727
|
"div",
|
|
2702
2728
|
{
|
|
2703
2729
|
ref: dropZoneRef,
|
|
@@ -2742,12 +2768,12 @@ function Form({ strings, onSubmit, onCancel, status, errorMessage }) {
|
|
|
2742
2768
|
/* @__PURE__ */ jsx8("button", { type: "button", class: "btn", onClick: onCancel, disabled: submitting, children: strings["form.cancel"] }),
|
|
2743
2769
|
/* @__PURE__ */ jsx8("button", { type: "submit", class: "btn btn--primary", disabled: submitting, children: submitLabel })
|
|
2744
2770
|
] }),
|
|
2745
|
-
|
|
2771
|
+
annotatingIndex !== null && shots[annotatingIndex] && /* @__PURE__ */ jsx8(
|
|
2746
2772
|
Annotator,
|
|
2747
2773
|
{
|
|
2748
|
-
imageBlob:
|
|
2774
|
+
imageBlob: shots[annotatingIndex].blob,
|
|
2749
2775
|
strings,
|
|
2750
|
-
onCancel: () =>
|
|
2776
|
+
onCancel: () => setAnnotatingIndex(null),
|
|
2751
2777
|
onSave: handleAnnotated
|
|
2752
2778
|
}
|
|
2753
2779
|
)
|
|
@@ -3382,6 +3408,12 @@ var WIDGET_STYLES = `
|
|
|
3382
3408
|
.screenshot-cta strong { color: var(--mfb-accent); font-weight: 600; }
|
|
3383
3409
|
.screenshot-formats { font-size: var(--mfb-text-xs); color: var(--mfb-text-muted); }
|
|
3384
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
|
+
}
|
|
3385
3417
|
.screenshot-preview {
|
|
3386
3418
|
position: relative;
|
|
3387
3419
|
border: 1px solid var(--mfb-border);
|
|
@@ -3399,6 +3431,10 @@ var WIDGET_STYLES = `
|
|
|
3399
3431
|
object-fit: contain;
|
|
3400
3432
|
background: #1a1a1a;
|
|
3401
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
|
+
}
|
|
3402
3438
|
.screenshot-preview-actions {
|
|
3403
3439
|
display: flex;
|
|
3404
3440
|
gap: var(--mfb-space-2);
|
|
@@ -4935,7 +4971,8 @@ function createFeedback(config) {
|
|
|
4935
4971
|
document.body.appendChild(host);
|
|
4936
4972
|
}
|
|
4937
4973
|
async function buildAndSubmit(values) {
|
|
4938
|
-
const
|
|
4974
|
+
const attached = values.screenshots?.length ? values.screenshots : values.screenshot ? [values.screenshot] : void 0;
|
|
4975
|
+
const manualScreenshots = values.synthetic ? void 0 : attached;
|
|
4939
4976
|
const technical_context = capture.snapshot();
|
|
4940
4977
|
if (user) {
|
|
4941
4978
|
const { userHash: _hash, exp: _exp, ...safeUser } = user;
|
|
@@ -4944,7 +4981,7 @@ function createFeedback(config) {
|
|
|
4944
4981
|
if (metadata && Object.keys(metadata).length > 0) {
|
|
4945
4982
|
technical_context.metadata = { ...metadata };
|
|
4946
4983
|
}
|
|
4947
|
-
const captureMethod =
|
|
4984
|
+
const captureMethod = manualScreenshots?.length ? values.capture_method ?? "manual" : "none";
|
|
4948
4985
|
const payload = {
|
|
4949
4986
|
description: values.description,
|
|
4950
4987
|
feedback_type: values.feedback_type ?? "bug",
|
|
@@ -4955,10 +4992,13 @@ function createFeedback(config) {
|
|
|
4955
4992
|
capture_method: captureMethod,
|
|
4956
4993
|
technical_context
|
|
4957
4994
|
};
|
|
4958
|
-
if ("0.
|
|
4959
|
-
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];
|
|
4960
5001
|
}
|
|
4961
|
-
if (manualScreenshot) payload.screenshot = manualScreenshot;
|
|
4962
5002
|
if (values.synthetic) payload.synthetic = true;
|
|
4963
5003
|
const pagePath = currentPagePath(config);
|
|
4964
5004
|
if (pagePath) payload.page_path = pagePath;
|
|
@@ -5044,7 +5084,8 @@ function createFeedback(config) {
|
|
|
5044
5084
|
...partial.feedback_type !== void 0 && { feedback_type: partial.feedback_type },
|
|
5045
5085
|
...partial.severity !== void 0 && { severity: partial.severity },
|
|
5046
5086
|
...partial.synthetic !== void 0 && { synthetic: partial.synthetic },
|
|
5047
|
-
...partial.screenshot !== void 0 && { screenshot: partial.screenshot }
|
|
5087
|
+
...partial.screenshot !== void 0 && { screenshot: partial.screenshot },
|
|
5088
|
+
...partial.screenshots !== void 0 && { screenshots: partial.screenshots }
|
|
5048
5089
|
});
|
|
5049
5090
|
},
|
|
5050
5091
|
identify(u) {
|
|
@@ -5076,4 +5117,4 @@ function createFeedback(config) {
|
|
|
5076
5117
|
export {
|
|
5077
5118
|
createFeedback
|
|
5078
5119
|
};
|
|
5079
|
-
//# sourceMappingURL=chunk-
|
|
5120
|
+
//# sourceMappingURL=chunk-WIQ7MTPI.mjs.map
|