@mhosaic/feedback 0.13.0 → 0.14.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-64HXWJCH.mjs → chunk-A4MBOFRR.mjs} +17 -177
- package/dist/chunk-A4MBOFRR.mjs.map +1 -0
- package/dist/embed.min.js +4 -38
- package/dist/embed.min.js.map +1 -1
- package/dist/error-tracking.mjs +12 -1
- package/dist/error-tracking.mjs.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/react.mjs +1 -1
- package/package.json +1 -1
- package/dist/chunk-64HXWJCH.mjs.map +0 -1
|
@@ -551,10 +551,6 @@ var DEFAULT_STRINGS = {
|
|
|
551
551
|
"form.screenshot.annotate": "Annotate",
|
|
552
552
|
"form.screenshot.error_type": "Unsupported file type. Use PNG, JPEG or WebP.",
|
|
553
553
|
"form.screenshot.error_size": "File too large (max {max} MB).",
|
|
554
|
-
"form.screenshot.or": "or",
|
|
555
|
-
"form.screenshot.capture_page": "Capture this page",
|
|
556
|
-
"form.screenshot.capture_page_hint": "Pick a tab or window to share",
|
|
557
|
-
"form.screenshot.capture_error": "Couldn\u2019t capture the page. Try uploading a screenshot instead.",
|
|
558
554
|
"form.context.label": "Page",
|
|
559
555
|
"type.bug": "Bug",
|
|
560
556
|
"type.feature": "Feature request",
|
|
@@ -688,10 +684,6 @@ var FRENCH_STRINGS = {
|
|
|
688
684
|
"form.screenshot.annotate": "Annoter",
|
|
689
685
|
"form.screenshot.error_type": "Format non support\xE9. Utilisez PNG, JPEG ou WebP.",
|
|
690
686
|
"form.screenshot.error_size": "Fichier trop volumineux (max {max} Mo).",
|
|
691
|
-
"form.screenshot.or": "ou",
|
|
692
|
-
"form.screenshot.capture_page": "Capturer la page",
|
|
693
|
-
"form.screenshot.capture_page_hint": "Choisissez un onglet ou une fen\xEAtre \xE0 partager",
|
|
694
|
-
"form.screenshot.capture_error": "Impossible de capturer la page. T\xE9l\xE9versez une capture \xE0 la place.",
|
|
695
687
|
"form.context.label": "Page",
|
|
696
688
|
"type.bug": "Bogue",
|
|
697
689
|
"type.feature": "Suggestion",
|
|
@@ -880,65 +872,6 @@ function truncateUrl(url, maxLength = 80) {
|
|
|
880
872
|
const keepEnd = maxLength - 1 - keepStart;
|
|
881
873
|
return `${url.slice(0, keepStart)}\u2026${url.slice(url.length - keepEnd)}`;
|
|
882
874
|
}
|
|
883
|
-
async function captureWithDisplayMedia() {
|
|
884
|
-
if (typeof navigator === "undefined" || !navigator.mediaDevices?.getDisplayMedia) {
|
|
885
|
-
return null;
|
|
886
|
-
}
|
|
887
|
-
let stream = null;
|
|
888
|
-
try {
|
|
889
|
-
stream = await navigator.mediaDevices.getDisplayMedia({
|
|
890
|
-
video: { displaySurface: "browser" },
|
|
891
|
-
audio: false
|
|
892
|
-
// Hint that audio isn't needed; not all browsers honor this but it
|
|
893
|
-
// avoids the audio-share toggle being defaulted on in some.
|
|
894
|
-
});
|
|
895
|
-
} catch {
|
|
896
|
-
return null;
|
|
897
|
-
}
|
|
898
|
-
try {
|
|
899
|
-
const track = stream.getVideoTracks()[0];
|
|
900
|
-
if (!track) return null;
|
|
901
|
-
const ImageCaptureCtor = window.ImageCapture;
|
|
902
|
-
let bitmap = null;
|
|
903
|
-
if (ImageCaptureCtor) {
|
|
904
|
-
try {
|
|
905
|
-
const ic = new ImageCaptureCtor(track);
|
|
906
|
-
bitmap = await ic.grabFrame();
|
|
907
|
-
} catch {
|
|
908
|
-
bitmap = null;
|
|
909
|
-
}
|
|
910
|
-
}
|
|
911
|
-
if (!bitmap) {
|
|
912
|
-
bitmap = await grabFrameViaVideo(stream);
|
|
913
|
-
}
|
|
914
|
-
if (!bitmap) return null;
|
|
915
|
-
const canvas = document.createElement("canvas");
|
|
916
|
-
canvas.width = bitmap.width;
|
|
917
|
-
canvas.height = bitmap.height;
|
|
918
|
-
const ctx = canvas.getContext("2d");
|
|
919
|
-
if (!ctx) return null;
|
|
920
|
-
ctx.drawImage(bitmap, 0, 0);
|
|
921
|
-
return await new Promise(
|
|
922
|
-
(resolve) => canvas.toBlob(resolve, "image/png")
|
|
923
|
-
);
|
|
924
|
-
} finally {
|
|
925
|
-
stream.getTracks().forEach((t) => t.stop());
|
|
926
|
-
}
|
|
927
|
-
}
|
|
928
|
-
async function grabFrameViaVideo(stream) {
|
|
929
|
-
const video = document.createElement("video");
|
|
930
|
-
video.muted = true;
|
|
931
|
-
video.playsInline = true;
|
|
932
|
-
video.srcObject = stream;
|
|
933
|
-
try {
|
|
934
|
-
await video.play();
|
|
935
|
-
await new Promise((r) => requestAnimationFrame(() => r()));
|
|
936
|
-
if (!video.videoWidth || !video.videoHeight) return null;
|
|
937
|
-
return await createImageBitmap(video);
|
|
938
|
-
} catch {
|
|
939
|
-
return null;
|
|
940
|
-
}
|
|
941
|
-
}
|
|
942
875
|
|
|
943
876
|
// src/widget/ReportDetailView.tsx
|
|
944
877
|
import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "preact/jsx-runtime";
|
|
@@ -2347,10 +2280,8 @@ function Form({ strings, onSubmit, onCancel, status, errorMessage }) {
|
|
|
2347
2280
|
const [localError, setLocalError] = useState5("");
|
|
2348
2281
|
const [screenshotBlob, setScreenshotBlob] = useState5(null);
|
|
2349
2282
|
const [screenshotPreview, setScreenshotPreview] = useState5(null);
|
|
2350
|
-
const [screenshotMethod, setScreenshotMethod] = useState5("manual");
|
|
2351
2283
|
const [isDragOver, setIsDragOver] = useState5(false);
|
|
2352
2284
|
const [annotatorOpen, setAnnotatorOpen] = useState5(false);
|
|
2353
|
-
const [capturing, setCapturing] = useState5(false);
|
|
2354
2285
|
const fileInputRef = useRef4(null);
|
|
2355
2286
|
const dropZoneRef = useRef4(null);
|
|
2356
2287
|
const submitting = status === "submitting";
|
|
@@ -2361,7 +2292,7 @@ function Form({ strings, onSubmit, onCancel, status, errorMessage }) {
|
|
|
2361
2292
|
if (screenshotPreview) URL.revokeObjectURL(screenshotPreview);
|
|
2362
2293
|
};
|
|
2363
2294
|
}, [screenshotPreview]);
|
|
2364
|
-
const acceptFile = (file
|
|
2295
|
+
const acceptFile = (file) => {
|
|
2365
2296
|
setLocalError("");
|
|
2366
2297
|
if (file instanceof File) {
|
|
2367
2298
|
const err = validateScreenshotFile(file);
|
|
@@ -2375,41 +2306,14 @@ function Form({ strings, onSubmit, onCancel, status, errorMessage }) {
|
|
|
2375
2306
|
if (screenshotPreview) URL.revokeObjectURL(screenshotPreview);
|
|
2376
2307
|
setScreenshotBlob(file);
|
|
2377
2308
|
setScreenshotPreview(URL.createObjectURL(file));
|
|
2378
|
-
setScreenshotMethod(method);
|
|
2379
2309
|
};
|
|
2380
2310
|
const clearScreenshot = () => {
|
|
2381
2311
|
if (screenshotPreview) URL.revokeObjectURL(screenshotPreview);
|
|
2382
2312
|
setScreenshotPreview(null);
|
|
2383
2313
|
setScreenshotBlob(null);
|
|
2384
|
-
setScreenshotMethod("manual");
|
|
2385
2314
|
setLocalError("");
|
|
2386
2315
|
if (fileInputRef.current) fileInputRef.current.value = "";
|
|
2387
2316
|
};
|
|
2388
|
-
const handleCapturePage = async () => {
|
|
2389
|
-
if (capturing) return;
|
|
2390
|
-
setLocalError("");
|
|
2391
|
-
setCapturing(true);
|
|
2392
|
-
try {
|
|
2393
|
-
const blob = await captureWithDisplayMedia();
|
|
2394
|
-
if (!blob) {
|
|
2395
|
-
if (typeof navigator === "undefined" || !navigator.mediaDevices?.getDisplayMedia) {
|
|
2396
|
-
setLocalError(strings["form.screenshot.capture_error"]);
|
|
2397
|
-
}
|
|
2398
|
-
return;
|
|
2399
|
-
}
|
|
2400
|
-
if (blob.size > 10 * 1024 * 1024) {
|
|
2401
|
-
setLocalError(
|
|
2402
|
-
strings["form.screenshot.error_size"].replace("{max}", "10")
|
|
2403
|
-
);
|
|
2404
|
-
return;
|
|
2405
|
-
}
|
|
2406
|
-
acceptFile(blob, "display_media");
|
|
2407
|
-
} catch {
|
|
2408
|
-
setLocalError(strings["form.screenshot.capture_error"]);
|
|
2409
|
-
} finally {
|
|
2410
|
-
setCapturing(false);
|
|
2411
|
-
}
|
|
2412
|
-
};
|
|
2413
2317
|
const handleFileInputChange = (e) => {
|
|
2414
2318
|
const file = e.target.files?.[0];
|
|
2415
2319
|
if (file) acceptFile(file);
|
|
@@ -2471,7 +2375,7 @@ function Form({ strings, onSubmit, onCancel, status, errorMessage }) {
|
|
|
2471
2375
|
};
|
|
2472
2376
|
if (screenshotBlob) {
|
|
2473
2377
|
values.screenshot = screenshotBlob;
|
|
2474
|
-
values.capture_method =
|
|
2378
|
+
values.capture_method = "manual";
|
|
2475
2379
|
}
|
|
2476
2380
|
onSubmit(values);
|
|
2477
2381
|
};
|
|
@@ -2584,43 +2488,7 @@ function Form({ strings, onSubmit, onCancel, status, errorMessage }) {
|
|
|
2584
2488
|
/* @__PURE__ */ jsx8("div", { class: "screenshot-formats", children: strings["form.screenshot.formats"] })
|
|
2585
2489
|
]
|
|
2586
2490
|
}
|
|
2587
|
-
)
|
|
2588
|
-
!screenshotPreview && /* @__PURE__ */ jsxs7("div", { class: "screenshot-alt", children: [
|
|
2589
|
-
/* @__PURE__ */ jsx8("span", { class: "screenshot-or", children: strings["form.screenshot.or"] }),
|
|
2590
|
-
/* @__PURE__ */ jsxs7(
|
|
2591
|
-
"button",
|
|
2592
|
-
{
|
|
2593
|
-
type: "button",
|
|
2594
|
-
class: "btn btn--ghost screenshot-capture-page",
|
|
2595
|
-
onClick: handleCapturePage,
|
|
2596
|
-
disabled: capturing || submitting,
|
|
2597
|
-
"aria-label": strings["form.screenshot.capture_page"],
|
|
2598
|
-
children: [
|
|
2599
|
-
/* @__PURE__ */ jsxs7(
|
|
2600
|
-
"svg",
|
|
2601
|
-
{
|
|
2602
|
-
width: "14",
|
|
2603
|
-
height: "14",
|
|
2604
|
-
viewBox: "0 0 24 24",
|
|
2605
|
-
fill: "none",
|
|
2606
|
-
stroke: "currentColor",
|
|
2607
|
-
"stroke-width": "2",
|
|
2608
|
-
"stroke-linecap": "round",
|
|
2609
|
-
"stroke-linejoin": "round",
|
|
2610
|
-
"aria-hidden": "true",
|
|
2611
|
-
children: [
|
|
2612
|
-
/* @__PURE__ */ jsx8("rect", { x: "2", y: "3", width: "20", height: "14", rx: "2", ry: "2" }),
|
|
2613
|
-
/* @__PURE__ */ jsx8("line", { x1: "8", y1: "21", x2: "16", y2: "21" }),
|
|
2614
|
-
/* @__PURE__ */ jsx8("line", { x1: "12", y1: "17", x2: "12", y2: "21" })
|
|
2615
|
-
]
|
|
2616
|
-
}
|
|
2617
|
-
),
|
|
2618
|
-
capturing ? strings["form.submitting"] : strings["form.screenshot.capture_page"]
|
|
2619
|
-
]
|
|
2620
|
-
}
|
|
2621
|
-
),
|
|
2622
|
-
/* @__PURE__ */ jsx8("span", { class: "screenshot-capture-hint", children: strings["form.screenshot.capture_page_hint"] })
|
|
2623
|
-
] })
|
|
2491
|
+
)
|
|
2624
2492
|
] }),
|
|
2625
2493
|
pageUrl && /* @__PURE__ */ jsxs7("div", { class: "page-context", title: pageUrl, children: [
|
|
2626
2494
|
/* @__PURE__ */ jsx8("span", { class: "page-context-label", children: strings["form.context.label"] }),
|
|
@@ -3261,40 +3129,6 @@ var WIDGET_STYLES = `
|
|
|
3261
3129
|
.screenshot-cta strong { color: var(--mfb-accent); font-weight: 600; }
|
|
3262
3130
|
.screenshot-formats { font-size: var(--mfb-text-xs); color: var(--mfb-text-muted); }
|
|
3263
3131
|
|
|
3264
|
-
/* "or \u2014 Capture this page" row sits below the dropzone. The thin "or"
|
|
3265
|
-
* separator borrows the pattern from auth forms so the second option
|
|
3266
|
-
* reads as an alternative, not a follow-up step. v0.12. */
|
|
3267
|
-
.screenshot-alt {
|
|
3268
|
-
display: flex;
|
|
3269
|
-
align-items: center;
|
|
3270
|
-
gap: 12px;
|
|
3271
|
-
margin-top: 8px;
|
|
3272
|
-
flex-wrap: wrap;
|
|
3273
|
-
}
|
|
3274
|
-
.screenshot-or {
|
|
3275
|
-
position: relative;
|
|
3276
|
-
font-size: var(--mfb-text-xs);
|
|
3277
|
-
color: var(--mfb-text-muted);
|
|
3278
|
-
text-transform: uppercase;
|
|
3279
|
-
letter-spacing: 0.08em;
|
|
3280
|
-
padding: 0 4px;
|
|
3281
|
-
}
|
|
3282
|
-
.screenshot-capture-page {
|
|
3283
|
-
display: inline-flex;
|
|
3284
|
-
align-items: center;
|
|
3285
|
-
gap: 6px;
|
|
3286
|
-
}
|
|
3287
|
-
.screenshot-capture-page:disabled {
|
|
3288
|
-
opacity: 0.55;
|
|
3289
|
-
cursor: progress;
|
|
3290
|
-
}
|
|
3291
|
-
.screenshot-capture-hint {
|
|
3292
|
-
font-size: var(--mfb-text-xs);
|
|
3293
|
-
color: var(--mfb-text-muted);
|
|
3294
|
-
flex: 1 1 100%;
|
|
3295
|
-
margin-top: 4px;
|
|
3296
|
-
}
|
|
3297
|
-
|
|
3298
3132
|
.screenshot-preview {
|
|
3299
3133
|
position: relative;
|
|
3300
3134
|
border: 1px solid var(--mfb-border);
|
|
@@ -4530,6 +4364,7 @@ function mountWidget(options) {
|
|
|
4530
4364
|
const mountPoint = document.createElement("div");
|
|
4531
4365
|
shadow.appendChild(mountPoint);
|
|
4532
4366
|
let currentState = { open: false, status: "idle", tab: "send" };
|
|
4367
|
+
let postSubmitTimer = null;
|
|
4533
4368
|
function rerender(state) {
|
|
4534
4369
|
currentState = state;
|
|
4535
4370
|
render(h(Root, { state }), mountPoint);
|
|
@@ -4545,8 +4380,10 @@ function mountWidget(options) {
|
|
|
4545
4380
|
try {
|
|
4546
4381
|
await options.onSubmit(values);
|
|
4547
4382
|
rerender({ ...currentState, status: "success" });
|
|
4548
|
-
|
|
4549
|
-
|
|
4383
|
+
if (postSubmitTimer !== null) clearTimeout(postSubmitTimer);
|
|
4384
|
+
postSubmitTimer = setTimeout(() => {
|
|
4385
|
+
postSubmitTimer = null;
|
|
4386
|
+
rerender({
|
|
4550
4387
|
...currentState,
|
|
4551
4388
|
open: false,
|
|
4552
4389
|
status: "idle",
|
|
@@ -4554,9 +4391,8 @@ function mountWidget(options) {
|
|
|
4554
4391
|
// they immediately see their just-filed report in the list
|
|
4555
4392
|
// (and the conversation that's about to start).
|
|
4556
4393
|
tab: options.api ? "mine" : "send"
|
|
4557
|
-
})
|
|
4558
|
-
|
|
4559
|
-
);
|
|
4394
|
+
});
|
|
4395
|
+
}, 1200);
|
|
4560
4396
|
} catch (err) {
|
|
4561
4397
|
rerender({
|
|
4562
4398
|
...currentState,
|
|
@@ -4691,6 +4527,10 @@ function mountWidget(options) {
|
|
|
4691
4527
|
rerender({ ...currentState, open: false, status: "idle" });
|
|
4692
4528
|
},
|
|
4693
4529
|
dispose() {
|
|
4530
|
+
if (postSubmitTimer !== null) {
|
|
4531
|
+
clearTimeout(postSubmitTimer);
|
|
4532
|
+
postSubmitTimer = null;
|
|
4533
|
+
}
|
|
4694
4534
|
render(null, mountPoint);
|
|
4695
4535
|
options.host.innerHTML = "";
|
|
4696
4536
|
},
|
|
@@ -4758,8 +4598,8 @@ function createFeedback(config) {
|
|
|
4758
4598
|
capture_method: captureMethod,
|
|
4759
4599
|
technical_context
|
|
4760
4600
|
};
|
|
4761
|
-
if ("0.
|
|
4762
|
-
payload.widget_version = "0.
|
|
4601
|
+
if ("0.14.0") {
|
|
4602
|
+
payload.widget_version = "0.14.0";
|
|
4763
4603
|
}
|
|
4764
4604
|
if (manualScreenshot) payload.screenshot = manualScreenshot;
|
|
4765
4605
|
if (values.synthetic) payload.synthetic = true;
|
|
@@ -4845,4 +4685,4 @@ function createFeedback(config) {
|
|
|
4845
4685
|
export {
|
|
4846
4686
|
createFeedback
|
|
4847
4687
|
};
|
|
4848
|
-
//# sourceMappingURL=chunk-
|
|
4688
|
+
//# sourceMappingURL=chunk-A4MBOFRR.mjs.map
|