@fogg/bug-reporter 1.0.1 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +443 -121
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +310 -8
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-6TCI6T2U.cjs +0 -45
- package/dist/chunk-6TCI6T2U.cjs.map +0 -1
- package/dist/chunk-S2YRP4GT.js +0 -22
- package/dist/chunk-S2YRP4GT.js.map +0 -1
- package/dist/recording-DZREYVVL.cjs +0 -122
- package/dist/recording-DZREYVVL.cjs.map +0 -1
- package/dist/recording-QOCPVR5Y.js +0 -120
- package/dist/recording-QOCPVR5Y.js.map +0 -1
- package/dist/screenshot-AWRHVVWJ.cjs +0 -179
- package/dist/screenshot-AWRHVVWJ.cjs.map +0 -1
- package/dist/screenshot-I2T6BTZB.js +0 -177
- package/dist/screenshot-I2T6BTZB.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -1,8 +1,35 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
4
|
-
var react = require('react');
|
|
3
|
+
var React = require('react');
|
|
5
4
|
var reactDom = require('react-dom');
|
|
5
|
+
var html2canvas = require('html2canvas');
|
|
6
|
+
|
|
7
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
8
|
+
|
|
9
|
+
function _interopNamespace(e) {
|
|
10
|
+
if (e && e.__esModule) return e;
|
|
11
|
+
var n = Object.create(null);
|
|
12
|
+
if (e) {
|
|
13
|
+
Object.keys(e).forEach(function (k) {
|
|
14
|
+
if (k !== 'default') {
|
|
15
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
16
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
17
|
+
enumerable: true,
|
|
18
|
+
get: function () { return e[k]; }
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
n.default = e;
|
|
24
|
+
return Object.freeze(n);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
var React__namespace = /*#__PURE__*/_interopNamespace(React);
|
|
28
|
+
var html2canvas__default = /*#__PURE__*/_interopDefault(html2canvas);
|
|
29
|
+
|
|
30
|
+
var __defProp = Object.defineProperty;
|
|
31
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
32
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
6
33
|
|
|
7
34
|
// src/diagnostics/ua.ts
|
|
8
35
|
function detectBrowserAndOS(userAgent) {
|
|
@@ -71,14 +98,14 @@ function collectDiagnostics(config, options) {
|
|
|
71
98
|
var ConsoleBuffer = class {
|
|
72
99
|
constructor(maxEntries) {
|
|
73
100
|
this.maxEntries = maxEntries;
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
101
|
+
__publicField(this, "entries", []);
|
|
102
|
+
__publicField(this, "originals", /* @__PURE__ */ new Map());
|
|
103
|
+
__publicField(this, "installed", false);
|
|
104
|
+
__publicField(this, "onWindowError", (event) => {
|
|
78
105
|
var _a;
|
|
79
106
|
this.push("error", [event.message, event.error instanceof Error ? (_a = event.error.stack) != null ? _a : "" : ""]);
|
|
80
107
|
});
|
|
81
|
-
|
|
108
|
+
__publicField(this, "onUnhandledRejection", (event) => {
|
|
82
109
|
this.push("error", ["Unhandled promise rejection", event.reason]);
|
|
83
110
|
});
|
|
84
111
|
}
|
|
@@ -166,11 +193,11 @@ function extractMethod(input, init) {
|
|
|
166
193
|
var NetworkBuffer = class {
|
|
167
194
|
constructor(maxEntries) {
|
|
168
195
|
this.maxEntries = maxEntries;
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
196
|
+
__publicField(this, "entries", []);
|
|
197
|
+
__publicField(this, "installed", false);
|
|
198
|
+
__publicField(this, "originalFetch");
|
|
199
|
+
__publicField(this, "originalXhrOpen");
|
|
200
|
+
__publicField(this, "originalXhrSend");
|
|
174
201
|
}
|
|
175
202
|
install() {
|
|
176
203
|
if (this.installed) {
|
|
@@ -311,7 +338,7 @@ var NetworkBuffer = class {
|
|
|
311
338
|
}
|
|
312
339
|
}
|
|
313
340
|
};
|
|
314
|
-
var BugReporterContext =
|
|
341
|
+
var BugReporterContext = React.createContext(void 0);
|
|
315
342
|
|
|
316
343
|
// src/core/defaults.ts
|
|
317
344
|
var DEFAULT_MASK_SELECTORS = [
|
|
@@ -371,6 +398,18 @@ function withDefaults(config) {
|
|
|
371
398
|
};
|
|
372
399
|
}
|
|
373
400
|
|
|
401
|
+
// src/types/index.ts
|
|
402
|
+
var BugReporterError = class extends Error {
|
|
403
|
+
constructor(code, message, cause) {
|
|
404
|
+
super(message);
|
|
405
|
+
__publicField(this, "code");
|
|
406
|
+
__publicField(this, "cause");
|
|
407
|
+
this.name = "BugReporterError";
|
|
408
|
+
this.code = code;
|
|
409
|
+
this.cause = cause;
|
|
410
|
+
}
|
|
411
|
+
};
|
|
412
|
+
|
|
374
413
|
// src/storage/local-public.ts
|
|
375
414
|
var LocalPublicProvider = class {
|
|
376
415
|
constructor(options) {
|
|
@@ -398,7 +437,7 @@ var LocalPublicProvider = class {
|
|
|
398
437
|
body: form
|
|
399
438
|
});
|
|
400
439
|
if (!response.ok) {
|
|
401
|
-
throw new
|
|
440
|
+
throw new BugReporterError("UPLOAD_ERROR", `Local upload failed (${response.status}).`);
|
|
402
441
|
}
|
|
403
442
|
const payload = await response.json();
|
|
404
443
|
onProgress == null ? void 0 : onProgress(1);
|
|
@@ -441,7 +480,7 @@ var ProxyProvider = class {
|
|
|
441
480
|
body: blob
|
|
442
481
|
});
|
|
443
482
|
if (!response.ok) {
|
|
444
|
-
throw new
|
|
483
|
+
throw new BugReporterError("UPLOAD_ERROR", `Proxy upload failed (${response.status}).`);
|
|
445
484
|
}
|
|
446
485
|
const payload = await response.json();
|
|
447
486
|
onProgress == null ? void 0 : onProgress(1);
|
|
@@ -473,11 +512,11 @@ var S3PresignedProvider = class {
|
|
|
473
512
|
body: JSON.stringify({ files })
|
|
474
513
|
});
|
|
475
514
|
if (!response.ok) {
|
|
476
|
-
throw new
|
|
515
|
+
throw new BugReporterError("UPLOAD_ERROR", `Failed to prepare uploads (${response.status}).`);
|
|
477
516
|
}
|
|
478
517
|
const payload = await response.json();
|
|
479
518
|
if (!((_a = payload.uploads) == null ? void 0 : _a.length)) {
|
|
480
|
-
throw new
|
|
519
|
+
throw new BugReporterError("UPLOAD_ERROR", "Presign endpoint did not return upload instructions.");
|
|
481
520
|
}
|
|
482
521
|
return payload.uploads;
|
|
483
522
|
}
|
|
@@ -493,7 +532,7 @@ var S3PresignedProvider = class {
|
|
|
493
532
|
body: formData
|
|
494
533
|
});
|
|
495
534
|
if (!response.ok) {
|
|
496
|
-
throw new
|
|
535
|
+
throw new BugReporterError("UPLOAD_ERROR", `S3 form upload failed (${response.status}).`);
|
|
497
536
|
}
|
|
498
537
|
} else {
|
|
499
538
|
const response = await fetch(instruction.uploadUrl, {
|
|
@@ -502,7 +541,7 @@ var S3PresignedProvider = class {
|
|
|
502
541
|
body: blob
|
|
503
542
|
});
|
|
504
543
|
if (!response.ok) {
|
|
505
|
-
throw new
|
|
544
|
+
throw new BugReporterError("UPLOAD_ERROR", `S3 upload failed (${response.status}).`);
|
|
506
545
|
}
|
|
507
546
|
}
|
|
508
547
|
onProgress == null ? void 0 : onProgress(1);
|
|
@@ -604,7 +643,7 @@ async function uploadAssets(options) {
|
|
|
604
643
|
for (const asset of options.assets) {
|
|
605
644
|
const instruction = byId.get(asset.id);
|
|
606
645
|
if (!instruction) {
|
|
607
|
-
throw new
|
|
646
|
+
throw new BugReporterError("UPLOAD_ERROR", `No upload instruction for asset ${asset.id}.`);
|
|
608
647
|
}
|
|
609
648
|
const ref = await withRetry(
|
|
610
649
|
() => options.provider.upload(instruction, asset.blob, (inner) => {
|
|
@@ -671,7 +710,7 @@ async function submitReport(options) {
|
|
|
671
710
|
};
|
|
672
711
|
const transformed = options.config.hooks.beforeSubmit ? await options.config.hooks.beforeSubmit(payloadBase) : payloadBase;
|
|
673
712
|
if (!transformed) {
|
|
674
|
-
throw new
|
|
713
|
+
throw new BugReporterError("ABORTED", "Submission aborted by beforeSubmit hook.");
|
|
675
714
|
}
|
|
676
715
|
console.log("[bug-reporter] payload to submit", transformed);
|
|
677
716
|
console.log("[bug-reporter] payload to submit (json)", JSON.stringify(transformed, null, 2));
|
|
@@ -686,7 +725,7 @@ async function submitReport(options) {
|
|
|
686
725
|
});
|
|
687
726
|
if (!response.ok) {
|
|
688
727
|
const body = await response.text().catch(() => "");
|
|
689
|
-
throw new
|
|
728
|
+
throw new BugReporterError("SUBMIT_ERROR", `Report submit failed (${response.status}): ${body || response.statusText}`);
|
|
690
729
|
}
|
|
691
730
|
return await response.json();
|
|
692
731
|
}
|
|
@@ -711,21 +750,21 @@ var BASE_STATE = {
|
|
|
711
750
|
error: void 0
|
|
712
751
|
};
|
|
713
752
|
function BugReporterProvider({ config, children }) {
|
|
714
|
-
const resolvedConfig =
|
|
753
|
+
const resolvedConfig = React.useMemo(() => withDefaults(config), [config]);
|
|
715
754
|
const initialDockSide = resolvedConfig.theme.position === "bottom-left" || resolvedConfig.theme.position === "top-left" ? "left" : "right";
|
|
716
|
-
const [state, setState] =
|
|
755
|
+
const [state, setState] = React.useState(() => ({
|
|
717
756
|
...BASE_STATE,
|
|
718
757
|
dockSide: initialDockSide,
|
|
719
758
|
attributes: { ...resolvedConfig.attributes }
|
|
720
759
|
}));
|
|
721
|
-
const [sessionActive, setSessionActive] =
|
|
722
|
-
const consoleBufferRef =
|
|
723
|
-
const networkBufferRef =
|
|
724
|
-
const assetsRef =
|
|
725
|
-
const resetAssets =
|
|
760
|
+
const [sessionActive, setSessionActive] = React.useState(false);
|
|
761
|
+
const consoleBufferRef = React.useRef(null);
|
|
762
|
+
const networkBufferRef = React.useRef(null);
|
|
763
|
+
const assetsRef = React.useRef([]);
|
|
764
|
+
const resetAssets = React.useCallback((assets) => {
|
|
726
765
|
assets.forEach((asset) => revokeObjectUrl(asset.previewUrl));
|
|
727
766
|
}, []);
|
|
728
|
-
const open =
|
|
767
|
+
const open = React.useCallback(() => {
|
|
729
768
|
setSessionActive(true);
|
|
730
769
|
setState((prev) => ({
|
|
731
770
|
...prev,
|
|
@@ -733,13 +772,13 @@ function BugReporterProvider({ config, children }) {
|
|
|
733
772
|
error: void 0
|
|
734
773
|
}));
|
|
735
774
|
}, []);
|
|
736
|
-
const close =
|
|
775
|
+
const close = React.useCallback(() => {
|
|
737
776
|
setState((prev) => ({
|
|
738
777
|
...prev,
|
|
739
778
|
isOpen: false
|
|
740
779
|
}));
|
|
741
780
|
}, []);
|
|
742
|
-
const reset =
|
|
781
|
+
const reset = React.useCallback(() => {
|
|
743
782
|
var _a, _b;
|
|
744
783
|
setSessionActive(false);
|
|
745
784
|
setState((prev) => {
|
|
@@ -754,13 +793,13 @@ function BugReporterProvider({ config, children }) {
|
|
|
754
793
|
(_a = consoleBufferRef.current) == null ? void 0 : _a.clear();
|
|
755
794
|
(_b = networkBufferRef.current) == null ? void 0 : _b.clear();
|
|
756
795
|
}, [resetAssets, resolvedConfig.attributes]);
|
|
757
|
-
const setStep =
|
|
796
|
+
const setStep = React.useCallback((step) => {
|
|
758
797
|
setState((prev) => ({ ...prev, step }));
|
|
759
798
|
}, []);
|
|
760
|
-
const setDockSide =
|
|
799
|
+
const setDockSide = React.useCallback((dockSide) => {
|
|
761
800
|
setState((prev) => ({ ...prev, dockSide }));
|
|
762
801
|
}, []);
|
|
763
|
-
const updateDraft =
|
|
802
|
+
const updateDraft = React.useCallback((next) => {
|
|
764
803
|
setState((prev) => ({
|
|
765
804
|
...prev,
|
|
766
805
|
draft: {
|
|
@@ -769,13 +808,13 @@ function BugReporterProvider({ config, children }) {
|
|
|
769
808
|
}
|
|
770
809
|
}));
|
|
771
810
|
}, []);
|
|
772
|
-
const setAttributes =
|
|
811
|
+
const setAttributes = React.useCallback((next) => {
|
|
773
812
|
setState((prev) => ({
|
|
774
813
|
...prev,
|
|
775
814
|
attributes: next
|
|
776
815
|
}));
|
|
777
816
|
}, []);
|
|
778
|
-
const updateAttribute =
|
|
817
|
+
const updateAttribute = React.useCallback((key, value2) => {
|
|
779
818
|
setState((prev) => ({
|
|
780
819
|
...prev,
|
|
781
820
|
attributes: {
|
|
@@ -784,7 +823,7 @@ function BugReporterProvider({ config, children }) {
|
|
|
784
823
|
}
|
|
785
824
|
}));
|
|
786
825
|
}, []);
|
|
787
|
-
const setAssetByType =
|
|
826
|
+
const setAssetByType = React.useCallback((type, next) => {
|
|
788
827
|
setState((prev) => {
|
|
789
828
|
const previous = prev.assets.find((asset) => asset.type === type);
|
|
790
829
|
if (previous) {
|
|
@@ -797,13 +836,13 @@ function BugReporterProvider({ config, children }) {
|
|
|
797
836
|
};
|
|
798
837
|
});
|
|
799
838
|
}, []);
|
|
800
|
-
const setScreenshot =
|
|
839
|
+
const setScreenshot = React.useCallback((asset) => {
|
|
801
840
|
setAssetByType("screenshot", asset);
|
|
802
841
|
}, [setAssetByType]);
|
|
803
|
-
const setRecording =
|
|
842
|
+
const setRecording = React.useCallback((asset) => {
|
|
804
843
|
setAssetByType("recording", asset);
|
|
805
844
|
}, [setAssetByType]);
|
|
806
|
-
const submit =
|
|
845
|
+
const submit = React.useCallback(async () => {
|
|
807
846
|
var _a, _b, _c, _d, _e, _f;
|
|
808
847
|
setState((prev) => ({
|
|
809
848
|
...prev,
|
|
@@ -848,10 +887,10 @@ function BugReporterProvider({ config, children }) {
|
|
|
848
887
|
(_f = (_e = resolvedConfig.hooks).onError) == null ? void 0 : _f.call(_e, error);
|
|
849
888
|
}
|
|
850
889
|
}, [resolvedConfig, state.assets, state.attributes, state.draft]);
|
|
851
|
-
const retrySubmit =
|
|
890
|
+
const retrySubmit = React.useCallback(async () => {
|
|
852
891
|
await submit();
|
|
853
892
|
}, [submit]);
|
|
854
|
-
const getDiagnosticsPreview =
|
|
893
|
+
const getDiagnosticsPreview = React.useCallback(() => {
|
|
855
894
|
var _a, _b, _c, _d;
|
|
856
895
|
const logs = resolvedConfig.features.consoleLogs ? (_b = (_a = consoleBufferRef.current) == null ? void 0 : _a.snapshot()) != null ? _b : [] : [];
|
|
857
896
|
const requests = resolvedConfig.features.networkInfo ? (_d = (_c = networkBufferRef.current) == null ? void 0 : _c.snapshot()) != null ? _d : [] : [];
|
|
@@ -863,7 +902,7 @@ function BugReporterProvider({ config, children }) {
|
|
|
863
902
|
})
|
|
864
903
|
};
|
|
865
904
|
}, [resolvedConfig.features.consoleLogs, resolvedConfig.features.networkInfo]);
|
|
866
|
-
|
|
905
|
+
React.useEffect(() => {
|
|
867
906
|
if (!sessionActive) {
|
|
868
907
|
return;
|
|
869
908
|
}
|
|
@@ -892,10 +931,10 @@ function BugReporterProvider({ config, children }) {
|
|
|
892
931
|
resolvedConfig.features.consoleLogs,
|
|
893
932
|
resolvedConfig.features.networkInfo
|
|
894
933
|
]);
|
|
895
|
-
|
|
934
|
+
React.useEffect(() => {
|
|
896
935
|
assetsRef.current = state.assets;
|
|
897
936
|
}, [state.assets]);
|
|
898
|
-
|
|
937
|
+
React.useEffect(() => {
|
|
899
938
|
return () => {
|
|
900
939
|
var _a, _b;
|
|
901
940
|
resetAssets(assetsRef.current);
|
|
@@ -903,7 +942,7 @@ function BugReporterProvider({ config, children }) {
|
|
|
903
942
|
(_b = networkBufferRef.current) == null ? void 0 : _b.uninstall();
|
|
904
943
|
};
|
|
905
944
|
}, [resetAssets]);
|
|
906
|
-
const value =
|
|
945
|
+
const value = React.useMemo(
|
|
907
946
|
() => ({
|
|
908
947
|
config: resolvedConfig,
|
|
909
948
|
state,
|
|
@@ -939,10 +978,10 @@ function BugReporterProvider({ config, children }) {
|
|
|
939
978
|
getDiagnosticsPreview
|
|
940
979
|
]
|
|
941
980
|
);
|
|
942
|
-
return /* @__PURE__ */
|
|
981
|
+
return /* @__PURE__ */ React__namespace.createElement(BugReporterContext.Provider, { value }, children);
|
|
943
982
|
}
|
|
944
983
|
function useBugReporter() {
|
|
945
|
-
const context =
|
|
984
|
+
const context = React.useContext(BugReporterContext);
|
|
946
985
|
if (!context) {
|
|
947
986
|
throw new Error("useBugReporter must be used inside BugReporterProvider.");
|
|
948
987
|
}
|
|
@@ -956,7 +995,7 @@ function getFocusable(node) {
|
|
|
956
995
|
).filter((el) => !el.hasAttribute("disabled"));
|
|
957
996
|
}
|
|
958
997
|
function useFocusTrap(enabled, containerRef) {
|
|
959
|
-
|
|
998
|
+
React.useEffect(() => {
|
|
960
999
|
const container = containerRef.current;
|
|
961
1000
|
if (!enabled || !container) {
|
|
962
1001
|
return;
|
|
@@ -1367,7 +1406,7 @@ function LauncherButton({ position, text, themeMode = "dark", buttonColor }) {
|
|
|
1367
1406
|
buttonColor: resolvedButtonColor,
|
|
1368
1407
|
themeMode
|
|
1369
1408
|
});
|
|
1370
|
-
return /* @__PURE__ */
|
|
1409
|
+
return /* @__PURE__ */ React__namespace.createElement(
|
|
1371
1410
|
"button",
|
|
1372
1411
|
{
|
|
1373
1412
|
type: "button",
|
|
@@ -1379,9 +1418,9 @@ function LauncherButton({ position, text, themeMode = "dark", buttonColor }) {
|
|
|
1379
1418
|
);
|
|
1380
1419
|
}
|
|
1381
1420
|
function Modal({ isOpen, dockSide, themeMode, buttonColor, title, zIndex, onRequestClose, children }) {
|
|
1382
|
-
const dialogRef =
|
|
1421
|
+
const dialogRef = React.useRef(null);
|
|
1383
1422
|
useFocusTrap(isOpen, dialogRef);
|
|
1384
|
-
|
|
1423
|
+
React.useEffect(() => {
|
|
1385
1424
|
if (!isOpen) {
|
|
1386
1425
|
return;
|
|
1387
1426
|
}
|
|
@@ -1400,23 +1439,309 @@ function Modal({ isOpen, dockSide, themeMode, buttonColor, title, zIndex, onRequ
|
|
|
1400
1439
|
const overlayStyle = getModalOverlayStyle({ dockSide, themeMode, zIndex });
|
|
1401
1440
|
const modalStyle = getModalStyle({ dockSide, themeMode, buttonColor });
|
|
1402
1441
|
return reactDom.createPortal(
|
|
1403
|
-
/* @__PURE__ */
|
|
1442
|
+
/* @__PURE__ */ React__namespace.createElement("div", { style: overlayStyle }, /* @__PURE__ */ React__namespace.createElement("div", { ref: dialogRef, style: modalStyle, role: "dialog", "aria-modal": "true", "aria-label": title }, children)),
|
|
1404
1443
|
document.body
|
|
1405
1444
|
);
|
|
1406
1445
|
}
|
|
1446
|
+
function applyMasking(selectors) {
|
|
1447
|
+
const masked = [];
|
|
1448
|
+
for (const selector of selectors) {
|
|
1449
|
+
document.querySelectorAll(selector).forEach((element) => {
|
|
1450
|
+
masked.push({ element, previous: element.style.filter });
|
|
1451
|
+
element.style.filter = "blur(12px)";
|
|
1452
|
+
});
|
|
1453
|
+
}
|
|
1454
|
+
return masked;
|
|
1455
|
+
}
|
|
1456
|
+
function resetMasking(masked) {
|
|
1457
|
+
masked.forEach(({ element, previous }) => {
|
|
1458
|
+
element.style.filter = previous;
|
|
1459
|
+
});
|
|
1460
|
+
}
|
|
1461
|
+
function scrubText(root, patterns) {
|
|
1462
|
+
var _a, _b;
|
|
1463
|
+
if (!patterns.length) {
|
|
1464
|
+
return [];
|
|
1465
|
+
}
|
|
1466
|
+
const walkers = [];
|
|
1467
|
+
const regexes = patterns.map((pattern) => typeof pattern === "string" ? new RegExp(pattern, "g") : pattern);
|
|
1468
|
+
const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);
|
|
1469
|
+
while (walker.nextNode()) {
|
|
1470
|
+
const text = walker.currentNode;
|
|
1471
|
+
let replaced = (_a = text.textContent) != null ? _a : "";
|
|
1472
|
+
for (const regex of regexes) {
|
|
1473
|
+
replaced = replaced.replace(regex, "[redacted]");
|
|
1474
|
+
}
|
|
1475
|
+
if (replaced !== text.textContent) {
|
|
1476
|
+
walkers.push({ node: text, previous: (_b = text.textContent) != null ? _b : "" });
|
|
1477
|
+
text.textContent = replaced;
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
return walkers;
|
|
1481
|
+
}
|
|
1482
|
+
function restoreText(changed) {
|
|
1483
|
+
changed.forEach(({ node, previous }) => {
|
|
1484
|
+
node.textContent = previous;
|
|
1485
|
+
});
|
|
1486
|
+
}
|
|
1487
|
+
function createSelectionOverlay() {
|
|
1488
|
+
return new Promise((resolve, reject) => {
|
|
1489
|
+
const overlay = document.createElement("div");
|
|
1490
|
+
overlay.setAttribute("data-bug-reporter-overlay", "true");
|
|
1491
|
+
overlay.style.position = "fixed";
|
|
1492
|
+
overlay.style.inset = "0";
|
|
1493
|
+
overlay.style.background = "rgba(0,0,0,0.35)";
|
|
1494
|
+
overlay.style.cursor = "crosshair";
|
|
1495
|
+
overlay.style.zIndex = "2147483647";
|
|
1496
|
+
const box = document.createElement("div");
|
|
1497
|
+
box.style.position = "fixed";
|
|
1498
|
+
box.style.border = "2px solid #ffffff";
|
|
1499
|
+
box.style.background = "rgba(27, 116, 228, 0.2)";
|
|
1500
|
+
box.style.pointerEvents = "none";
|
|
1501
|
+
box.style.display = "none";
|
|
1502
|
+
overlay.appendChild(box);
|
|
1503
|
+
const cleanup = () => {
|
|
1504
|
+
overlay.removeEventListener("mousedown", onMouseDown);
|
|
1505
|
+
overlay.removeEventListener("mousemove", onMouseMove);
|
|
1506
|
+
overlay.removeEventListener("mouseup", onMouseUp);
|
|
1507
|
+
window.removeEventListener("keydown", onKeyDown);
|
|
1508
|
+
overlay.remove();
|
|
1509
|
+
};
|
|
1510
|
+
let startX = 0;
|
|
1511
|
+
let startY = 0;
|
|
1512
|
+
let isDragging = false;
|
|
1513
|
+
const onKeyDown = (event) => {
|
|
1514
|
+
if (event.key === "Escape") {
|
|
1515
|
+
cleanup();
|
|
1516
|
+
reject(new BugReporterError("ABORTED", "Screenshot capture cancelled."));
|
|
1517
|
+
}
|
|
1518
|
+
};
|
|
1519
|
+
const onMouseDown = (event) => {
|
|
1520
|
+
isDragging = true;
|
|
1521
|
+
startX = event.clientX;
|
|
1522
|
+
startY = event.clientY;
|
|
1523
|
+
box.style.display = "block";
|
|
1524
|
+
box.style.left = `${startX}px`;
|
|
1525
|
+
box.style.top = `${startY}px`;
|
|
1526
|
+
box.style.width = "0px";
|
|
1527
|
+
box.style.height = "0px";
|
|
1528
|
+
};
|
|
1529
|
+
const onMouseMove = (event) => {
|
|
1530
|
+
if (!isDragging) {
|
|
1531
|
+
return;
|
|
1532
|
+
}
|
|
1533
|
+
const left = Math.min(startX, event.clientX);
|
|
1534
|
+
const top = Math.min(startY, event.clientY);
|
|
1535
|
+
const width = Math.abs(startX - event.clientX);
|
|
1536
|
+
const height = Math.abs(startY - event.clientY);
|
|
1537
|
+
box.style.left = `${left}px`;
|
|
1538
|
+
box.style.top = `${top}px`;
|
|
1539
|
+
box.style.width = `${width}px`;
|
|
1540
|
+
box.style.height = `${height}px`;
|
|
1541
|
+
};
|
|
1542
|
+
const onMouseUp = (event) => {
|
|
1543
|
+
if (!isDragging) {
|
|
1544
|
+
return;
|
|
1545
|
+
}
|
|
1546
|
+
isDragging = false;
|
|
1547
|
+
const left = Math.min(startX, event.clientX);
|
|
1548
|
+
const top = Math.min(startY, event.clientY);
|
|
1549
|
+
const width = Math.abs(startX - event.clientX);
|
|
1550
|
+
const height = Math.abs(startY - event.clientY);
|
|
1551
|
+
cleanup();
|
|
1552
|
+
if (width < 8 || height < 8) {
|
|
1553
|
+
reject(new BugReporterError("CAPTURE_ERROR", "Selection area is too small."));
|
|
1554
|
+
return;
|
|
1555
|
+
}
|
|
1556
|
+
resolve({ left, top, width, height });
|
|
1557
|
+
};
|
|
1558
|
+
overlay.addEventListener("mousedown", onMouseDown);
|
|
1559
|
+
overlay.addEventListener("mousemove", onMouseMove);
|
|
1560
|
+
overlay.addEventListener("mouseup", onMouseUp);
|
|
1561
|
+
window.addEventListener("keydown", onKeyDown);
|
|
1562
|
+
document.body.appendChild(overlay);
|
|
1563
|
+
});
|
|
1564
|
+
}
|
|
1565
|
+
function canvasToBlob(canvas) {
|
|
1566
|
+
return new Promise((resolve, reject) => {
|
|
1567
|
+
canvas.toBlob((blob) => {
|
|
1568
|
+
if (!blob) {
|
|
1569
|
+
reject(new BugReporterError("CAPTURE_ERROR", "Failed to build screenshot blob."));
|
|
1570
|
+
return;
|
|
1571
|
+
}
|
|
1572
|
+
resolve(blob);
|
|
1573
|
+
}, "image/png");
|
|
1574
|
+
});
|
|
1575
|
+
}
|
|
1576
|
+
async function captureScreenshotArea(options) {
|
|
1577
|
+
const selection = await createSelectionOverlay();
|
|
1578
|
+
const masked = applyMasking(options.maskSelectors);
|
|
1579
|
+
const textChanges = scrubText(document.body, options.redactTextPatterns);
|
|
1580
|
+
try {
|
|
1581
|
+
const baseCanvas = await html2canvas__default.default(document.documentElement, {
|
|
1582
|
+
useCORS: true,
|
|
1583
|
+
scrollX: -window.scrollX,
|
|
1584
|
+
scrollY: -window.scrollY,
|
|
1585
|
+
backgroundColor: null,
|
|
1586
|
+
logging: false,
|
|
1587
|
+
windowWidth: document.documentElement.scrollWidth,
|
|
1588
|
+
windowHeight: document.documentElement.scrollHeight
|
|
1589
|
+
});
|
|
1590
|
+
const scaleX = baseCanvas.width / window.innerWidth;
|
|
1591
|
+
const scaleY = baseCanvas.height / window.innerHeight;
|
|
1592
|
+
const sx = Math.round(selection.left * scaleX);
|
|
1593
|
+
const sy = Math.round(selection.top * scaleY);
|
|
1594
|
+
const sw = Math.round(selection.width * scaleX);
|
|
1595
|
+
const sh = Math.round(selection.height * scaleY);
|
|
1596
|
+
const cropped = document.createElement("canvas");
|
|
1597
|
+
cropped.width = sw;
|
|
1598
|
+
cropped.height = sh;
|
|
1599
|
+
const context = cropped.getContext("2d");
|
|
1600
|
+
if (!context) {
|
|
1601
|
+
throw new BugReporterError("CAPTURE_ERROR", "Canvas 2D context unavailable.");
|
|
1602
|
+
}
|
|
1603
|
+
context.drawImage(baseCanvas, sx, sy, sw, sh, 0, 0, sw, sh);
|
|
1604
|
+
return await canvasToBlob(cropped);
|
|
1605
|
+
} catch (error) {
|
|
1606
|
+
if (error instanceof BugReporterError) {
|
|
1607
|
+
throw error;
|
|
1608
|
+
}
|
|
1609
|
+
throw new BugReporterError("CAPTURE_ERROR", "Screenshot capture failed.", error);
|
|
1610
|
+
} finally {
|
|
1611
|
+
resetMasking(masked);
|
|
1612
|
+
restoreText(textChanges);
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1616
|
+
// src/core/recording.ts
|
|
1617
|
+
function pickMimeType() {
|
|
1618
|
+
const candidates = ["video/webm;codecs=vp9", "video/webm;codecs=vp8", "video/webm"];
|
|
1619
|
+
for (const candidate of candidates) {
|
|
1620
|
+
if (typeof MediaRecorder !== "undefined" && MediaRecorder.isTypeSupported(candidate)) {
|
|
1621
|
+
return candidate;
|
|
1622
|
+
}
|
|
1623
|
+
}
|
|
1624
|
+
return "video/webm";
|
|
1625
|
+
}
|
|
1626
|
+
async function startScreenRecording(options) {
|
|
1627
|
+
var _a;
|
|
1628
|
+
if (!((_a = navigator.mediaDevices) == null ? void 0 : _a.getDisplayMedia)) {
|
|
1629
|
+
throw new BugReporterError("RECORDING_ERROR", "Screen recording is not supported by this browser.");
|
|
1630
|
+
}
|
|
1631
|
+
let stream;
|
|
1632
|
+
try {
|
|
1633
|
+
stream = await navigator.mediaDevices.getDisplayMedia({
|
|
1634
|
+
video: true,
|
|
1635
|
+
audio: false
|
|
1636
|
+
});
|
|
1637
|
+
} catch (error) {
|
|
1638
|
+
throw new BugReporterError("PERMISSION_DENIED", "Permission denied for screen recording.", error);
|
|
1639
|
+
}
|
|
1640
|
+
const mimeType = pickMimeType();
|
|
1641
|
+
let recorder;
|
|
1642
|
+
try {
|
|
1643
|
+
recorder = new MediaRecorder(stream, { mimeType });
|
|
1644
|
+
} catch (error) {
|
|
1645
|
+
stream.getTracks().forEach((track) => track.stop());
|
|
1646
|
+
throw new BugReporterError("RECORDING_ERROR", "Could not initialize MediaRecorder.", error);
|
|
1647
|
+
}
|
|
1648
|
+
const chunks = [];
|
|
1649
|
+
const startedAt = Date.now();
|
|
1650
|
+
let latestSize = 0;
|
|
1651
|
+
let tickSeconds = 0;
|
|
1652
|
+
let completed = false;
|
|
1653
|
+
let isCancelled = false;
|
|
1654
|
+
let resolvePromise;
|
|
1655
|
+
let rejectPromise;
|
|
1656
|
+
const promise = new Promise((resolve, reject) => {
|
|
1657
|
+
resolvePromise = resolve;
|
|
1658
|
+
rejectPromise = reject;
|
|
1659
|
+
});
|
|
1660
|
+
const tickInterval = window.setInterval(() => {
|
|
1661
|
+
var _a2;
|
|
1662
|
+
tickSeconds += 1;
|
|
1663
|
+
(_a2 = options.onTick) == null ? void 0 : _a2.call(options, tickSeconds);
|
|
1664
|
+
}, 1e3);
|
|
1665
|
+
const hardStop = window.setTimeout(() => {
|
|
1666
|
+
if (recorder.state !== "inactive") {
|
|
1667
|
+
recorder.stop();
|
|
1668
|
+
}
|
|
1669
|
+
}, options.maxSeconds * 1e3);
|
|
1670
|
+
const teardown = () => {
|
|
1671
|
+
if (completed) {
|
|
1672
|
+
return;
|
|
1673
|
+
}
|
|
1674
|
+
completed = true;
|
|
1675
|
+
window.clearInterval(tickInterval);
|
|
1676
|
+
window.clearTimeout(hardStop);
|
|
1677
|
+
stream.getTracks().forEach((track) => track.stop());
|
|
1678
|
+
};
|
|
1679
|
+
recorder.addEventListener("dataavailable", (event) => {
|
|
1680
|
+
if (!event.data || event.data.size === 0) {
|
|
1681
|
+
return;
|
|
1682
|
+
}
|
|
1683
|
+
chunks.push(event.data);
|
|
1684
|
+
latestSize += event.data.size;
|
|
1685
|
+
if (latestSize > options.maxBytes) {
|
|
1686
|
+
isCancelled = true;
|
|
1687
|
+
recorder.stop();
|
|
1688
|
+
rejectPromise(
|
|
1689
|
+
new BugReporterError(
|
|
1690
|
+
"VALIDATION_ERROR",
|
|
1691
|
+
`Recording exceeds max size (${Math.round(options.maxBytes / 1024 / 1024)}MB).`
|
|
1692
|
+
)
|
|
1693
|
+
);
|
|
1694
|
+
}
|
|
1695
|
+
});
|
|
1696
|
+
recorder.addEventListener("error", (event) => {
|
|
1697
|
+
teardown();
|
|
1698
|
+
rejectPromise(new BugReporterError("RECORDING_ERROR", "Recording failed.", event));
|
|
1699
|
+
});
|
|
1700
|
+
recorder.addEventListener("stop", () => {
|
|
1701
|
+
teardown();
|
|
1702
|
+
if (isCancelled) {
|
|
1703
|
+
return;
|
|
1704
|
+
}
|
|
1705
|
+
const blob = new Blob(chunks, { type: mimeType });
|
|
1706
|
+
resolvePromise({
|
|
1707
|
+
blob,
|
|
1708
|
+
mimeType,
|
|
1709
|
+
durationMs: Date.now() - startedAt
|
|
1710
|
+
});
|
|
1711
|
+
});
|
|
1712
|
+
recorder.start(300);
|
|
1713
|
+
return {
|
|
1714
|
+
stop: () => {
|
|
1715
|
+
if (recorder.state !== "inactive") {
|
|
1716
|
+
recorder.stop();
|
|
1717
|
+
}
|
|
1718
|
+
},
|
|
1719
|
+
cancel: () => {
|
|
1720
|
+
isCancelled = true;
|
|
1721
|
+
if (recorder.state !== "inactive") {
|
|
1722
|
+
recorder.stop();
|
|
1723
|
+
}
|
|
1724
|
+
teardown();
|
|
1725
|
+
rejectPromise(new BugReporterError("ABORTED", "Recording cancelled by user."));
|
|
1726
|
+
},
|
|
1727
|
+
promise
|
|
1728
|
+
};
|
|
1729
|
+
}
|
|
1407
1730
|
|
|
1408
1731
|
// src/core/lazy.ts
|
|
1732
|
+
var screenshotCaptureModule = { captureScreenshotArea };
|
|
1733
|
+
var screenRecordingModule = { startScreenRecording };
|
|
1409
1734
|
async function loadScreenshotCapture() {
|
|
1410
|
-
return
|
|
1735
|
+
return screenshotCaptureModule;
|
|
1411
1736
|
}
|
|
1412
1737
|
async function loadScreenRecording() {
|
|
1413
|
-
return
|
|
1738
|
+
return screenRecordingModule;
|
|
1414
1739
|
}
|
|
1415
1740
|
|
|
1416
1741
|
// src/core/validation.ts
|
|
1417
1742
|
function validateScreenshotSize(size, maxBytes) {
|
|
1418
1743
|
if (size > maxBytes) {
|
|
1419
|
-
throw new
|
|
1744
|
+
throw new BugReporterError(
|
|
1420
1745
|
"VALIDATION_ERROR",
|
|
1421
1746
|
`Screenshot exceeds max size (${Math.round(maxBytes / 1024 / 1024)}MB).`
|
|
1422
1747
|
);
|
|
@@ -1424,7 +1749,7 @@ function validateScreenshotSize(size, maxBytes) {
|
|
|
1424
1749
|
}
|
|
1425
1750
|
function validateVideoSize(size, maxBytes) {
|
|
1426
1751
|
if (size > maxBytes) {
|
|
1427
|
-
throw new
|
|
1752
|
+
throw new BugReporterError("VALIDATION_ERROR", `Recording exceeds max size (${Math.round(maxBytes / 1024 / 1024)}MB).`);
|
|
1428
1753
|
}
|
|
1429
1754
|
}
|
|
1430
1755
|
function drawArrow(ctx, start, end) {
|
|
@@ -1441,14 +1766,14 @@ function drawArrow(ctx, start, end) {
|
|
|
1441
1766
|
ctx.closePath();
|
|
1442
1767
|
ctx.fill();
|
|
1443
1768
|
}
|
|
1444
|
-
var AnnotationCanvas =
|
|
1445
|
-
const canvasRef =
|
|
1446
|
-
const imageRef =
|
|
1447
|
-
const [tool, setTool] =
|
|
1448
|
-
const [shapes, setShapes] =
|
|
1449
|
-
const [draftShape, setDraftShape] =
|
|
1450
|
-
const dragStartRef =
|
|
1451
|
-
const redraw =
|
|
1769
|
+
var AnnotationCanvas = React.forwardRef(function AnnotationCanvas2({ imageUrl }, ref) {
|
|
1770
|
+
const canvasRef = React.useRef(null);
|
|
1771
|
+
const imageRef = React.useRef(null);
|
|
1772
|
+
const [tool, setTool] = React.useState("rectangle");
|
|
1773
|
+
const [shapes, setShapes] = React.useState([]);
|
|
1774
|
+
const [draftShape, setDraftShape] = React.useState(null);
|
|
1775
|
+
const dragStartRef = React.useRef(null);
|
|
1776
|
+
const redraw = React.useCallback(
|
|
1452
1777
|
(ctx) => {
|
|
1453
1778
|
const canvas = canvasRef.current;
|
|
1454
1779
|
const image = imageRef.current;
|
|
@@ -1477,7 +1802,7 @@ var AnnotationCanvas = react.forwardRef(function AnnotationCanvas2({ imageUrl },
|
|
|
1477
1802
|
},
|
|
1478
1803
|
[draftShape, shapes]
|
|
1479
1804
|
);
|
|
1480
|
-
|
|
1805
|
+
React.useEffect(() => {
|
|
1481
1806
|
const image = new Image();
|
|
1482
1807
|
image.crossOrigin = "anonymous";
|
|
1483
1808
|
image.onload = () => {
|
|
@@ -1496,7 +1821,7 @@ var AnnotationCanvas = react.forwardRef(function AnnotationCanvas2({ imageUrl },
|
|
|
1496
1821
|
};
|
|
1497
1822
|
image.src = imageUrl;
|
|
1498
1823
|
}, [imageUrl, redraw]);
|
|
1499
|
-
|
|
1824
|
+
React.useEffect(() => {
|
|
1500
1825
|
const canvas = canvasRef.current;
|
|
1501
1826
|
if (!canvas) {
|
|
1502
1827
|
return;
|
|
@@ -1507,7 +1832,7 @@ var AnnotationCanvas = react.forwardRef(function AnnotationCanvas2({ imageUrl },
|
|
|
1507
1832
|
}
|
|
1508
1833
|
redraw(ctx);
|
|
1509
1834
|
}, [redraw]);
|
|
1510
|
-
const getCoords =
|
|
1835
|
+
const getCoords = React.useCallback((event) => {
|
|
1511
1836
|
const canvas = canvasRef.current;
|
|
1512
1837
|
if (!canvas) {
|
|
1513
1838
|
return { x: 0, y: 0 };
|
|
@@ -1550,7 +1875,7 @@ var AnnotationCanvas = react.forwardRef(function AnnotationCanvas2({ imageUrl },
|
|
|
1550
1875
|
}
|
|
1551
1876
|
setDraftShape(null);
|
|
1552
1877
|
};
|
|
1553
|
-
const
|
|
1878
|
+
const canvasToBlob2 = React.useCallback(async () => {
|
|
1554
1879
|
const canvas = canvasRef.current;
|
|
1555
1880
|
if (!canvas) {
|
|
1556
1881
|
throw new Error("Annotation canvas unavailable.");
|
|
@@ -1563,23 +1888,23 @@ var AnnotationCanvas = react.forwardRef(function AnnotationCanvas2({ imageUrl },
|
|
|
1563
1888
|
}
|
|
1564
1889
|
return blob;
|
|
1565
1890
|
}, []);
|
|
1566
|
-
|
|
1891
|
+
React.useImperativeHandle(
|
|
1567
1892
|
ref,
|
|
1568
1893
|
() => ({
|
|
1569
|
-
exportBlob:
|
|
1894
|
+
exportBlob: canvasToBlob2,
|
|
1570
1895
|
clear: () => setShapes([]),
|
|
1571
1896
|
hasAnnotations: () => shapes.length > 0
|
|
1572
1897
|
}),
|
|
1573
|
-
[
|
|
1898
|
+
[canvasToBlob2, shapes.length]
|
|
1574
1899
|
);
|
|
1575
|
-
const toolButtons =
|
|
1900
|
+
const toolButtons = React.useMemo(
|
|
1576
1901
|
() => [
|
|
1577
1902
|
{ value: "rectangle", label: "Rectangle" },
|
|
1578
1903
|
{ value: "arrow", label: "Arrow" }
|
|
1579
1904
|
],
|
|
1580
1905
|
[]
|
|
1581
1906
|
);
|
|
1582
|
-
return /* @__PURE__ */
|
|
1907
|
+
return /* @__PURE__ */ React__namespace.createElement("div", { style: inlineStyles.annotation }, /* @__PURE__ */ React__namespace.createElement("div", { style: inlineStyles.annotationTools, role: "toolbar", "aria-label": "Annotation tools" }, toolButtons.map((option) => /* @__PURE__ */ React__namespace.createElement(
|
|
1583
1908
|
"button",
|
|
1584
1909
|
{
|
|
1585
1910
|
key: option.value,
|
|
@@ -1588,7 +1913,7 @@ var AnnotationCanvas = react.forwardRef(function AnnotationCanvas2({ imageUrl },
|
|
|
1588
1913
|
onClick: () => setTool(option.value)
|
|
1589
1914
|
},
|
|
1590
1915
|
option.label
|
|
1591
|
-
)), /* @__PURE__ */
|
|
1916
|
+
)), /* @__PURE__ */ React__namespace.createElement("button", { type: "button", style: getButtonStyle("secondary"), onClick: () => setShapes([]) }, "Clear")), /* @__PURE__ */ React__namespace.createElement(
|
|
1592
1917
|
"canvas",
|
|
1593
1918
|
{
|
|
1594
1919
|
ref: canvasRef,
|
|
@@ -1612,12 +1937,12 @@ function StepRecording({ onBack, onNext, embedded = false, compact = false }) {
|
|
|
1612
1937
|
state: { assets },
|
|
1613
1938
|
setRecording
|
|
1614
1939
|
} = useBugReporter();
|
|
1615
|
-
const recording =
|
|
1616
|
-
const activeRef =
|
|
1617
|
-
const mountedRef =
|
|
1618
|
-
const [isRecording, setIsRecording] =
|
|
1619
|
-
const [seconds, setSeconds] =
|
|
1620
|
-
const [error, setError] =
|
|
1940
|
+
const recording = React.useMemo(() => assets.find((asset) => asset.type === "recording"), [assets]);
|
|
1941
|
+
const activeRef = React.useRef(null);
|
|
1942
|
+
const mountedRef = React.useRef(true);
|
|
1943
|
+
const [isRecording, setIsRecording] = React.useState(Boolean(sharedRecording));
|
|
1944
|
+
const [seconds, setSeconds] = React.useState((_a = sharedRecording == null ? void 0 : sharedRecording.seconds) != null ? _a : 0);
|
|
1945
|
+
const [error, setError] = React.useState(null);
|
|
1621
1946
|
const start = async () => {
|
|
1622
1947
|
if (sharedRecording) {
|
|
1623
1948
|
setError("Recording is already in progress.");
|
|
@@ -1676,7 +2001,7 @@ function StepRecording({ onBack, onNext, embedded = false, compact = false }) {
|
|
|
1676
2001
|
}
|
|
1677
2002
|
(_a2 = activeRef.current) == null ? void 0 : _a2.stop();
|
|
1678
2003
|
};
|
|
1679
|
-
|
|
2004
|
+
React.useEffect(() => {
|
|
1680
2005
|
mountedRef.current = true;
|
|
1681
2006
|
const sync = () => {
|
|
1682
2007
|
var _a2, _b;
|
|
@@ -1692,10 +2017,10 @@ function StepRecording({ onBack, onNext, embedded = false, compact = false }) {
|
|
|
1692
2017
|
};
|
|
1693
2018
|
}, []);
|
|
1694
2019
|
if (compact) {
|
|
1695
|
-
const compactRecordingAction = !isRecording ? /* @__PURE__ */
|
|
1696
|
-
return /* @__PURE__ */
|
|
2020
|
+
const compactRecordingAction = !isRecording ? /* @__PURE__ */ React__namespace.createElement("button", { type: "button", style: { ...getButtonStyle("primary", { fullWidth: true }), ...inlineStyles.captureButton }, onClick: start }, /* @__PURE__ */ React__namespace.createElement("svg", { style: inlineStyles.captureIcon, viewBox: "0 0 24 24", "aria-hidden": "true", focusable: "false" }, /* @__PURE__ */ React__namespace.createElement("circle", { cx: "12", cy: "12", r: "4.5" }), /* @__PURE__ */ React__namespace.createElement("path", { d: "M3.5 8.5h3l1.2-2h8.6l1.2 2h3v7h-3l-1.2 2H7.7l-1.2-2h-3z" })), /* @__PURE__ */ React__namespace.createElement("span", null, recording ? "Retake recording" : "Record a video"), recording ? /* @__PURE__ */ React__namespace.createElement("span", { style: inlineStyles.captureDone }, "Saved") : null) : /* @__PURE__ */ React__namespace.createElement("button", { type: "button", style: { ...getButtonStyle("danger", { fullWidth: true }), ...inlineStyles.captureButton }, onClick: stop }, /* @__PURE__ */ React__namespace.createElement("svg", { style: inlineStyles.captureIcon, viewBox: "0 0 24 24", "aria-hidden": "true", focusable: "false" }, /* @__PURE__ */ React__namespace.createElement("rect", { x: "7", y: "7", width: "10", height: "10" })), /* @__PURE__ */ React__namespace.createElement("span", null, "Stop (", seconds, "s)"));
|
|
2021
|
+
return /* @__PURE__ */ React__namespace.createElement("div", { style: inlineStyles.captureItem }, compactRecordingAction, error ? /* @__PURE__ */ React__namespace.createElement("p", { style: { ...inlineStyles.error, marginTop: "8px" } }, error) : null);
|
|
1697
2022
|
}
|
|
1698
|
-
return /* @__PURE__ */
|
|
2023
|
+
return /* @__PURE__ */ React__namespace.createElement("div", { style: embedded ? void 0 : inlineStyles.step }, embedded ? /* @__PURE__ */ React__namespace.createElement("h3", { style: inlineStyles.h3 }, "Screen recording") : /* @__PURE__ */ React__namespace.createElement("h2", { style: inlineStyles.h2 }, "Screen recording"), /* @__PURE__ */ React__namespace.createElement("p", { style: inlineStyles.p }, "Record up to ", config.storage.limits.maxVideoSeconds, " seconds. You can minimize this sidebar while recording."), /* @__PURE__ */ React__namespace.createElement("div", { style: inlineStyles.actions }, onBack ? /* @__PURE__ */ React__namespace.createElement("button", { type: "button", style: getButtonStyle("secondary", { disabled: isRecording }), onClick: onBack, disabled: isRecording }, "Back") : null, !isRecording ? /* @__PURE__ */ React__namespace.createElement("button", { type: "button", style: getButtonStyle("primary"), onClick: start }, recording ? "Retake recording" : "Record a video") : /* @__PURE__ */ React__namespace.createElement("button", { type: "button", style: getButtonStyle("danger"), onClick: stop }, "Stop (", seconds, "s)")), recording ? /* @__PURE__ */ React__namespace.createElement("video", { src: recording.previewUrl, style: inlineStyles.preview, controls: true }) : null, error ? /* @__PURE__ */ React__namespace.createElement("p", { style: inlineStyles.error }, error) : null, onNext ? /* @__PURE__ */ React__namespace.createElement("div", { style: inlineStyles.actions }, /* @__PURE__ */ React__namespace.createElement("button", { type: "button", style: getButtonStyle("primary"), onClick: onNext }, "Continue")) : null);
|
|
1699
2024
|
}
|
|
1700
2025
|
|
|
1701
2026
|
// src/components/StepDescribe.tsx
|
|
@@ -1708,13 +2033,13 @@ function StepDescribe({ onNext, CustomForm }) {
|
|
|
1708
2033
|
updateAttribute,
|
|
1709
2034
|
setScreenshot
|
|
1710
2035
|
} = useBugReporter();
|
|
1711
|
-
const screenshot =
|
|
1712
|
-
const recording =
|
|
1713
|
-
const annotationRef =
|
|
1714
|
-
const customFormRef =
|
|
1715
|
-
const [isCapturing, setIsCapturing] =
|
|
1716
|
-
const [error, setError] =
|
|
1717
|
-
|
|
2036
|
+
const screenshot = React.useMemo(() => assets.find((asset) => asset.type === "screenshot"), [assets]);
|
|
2037
|
+
const recording = React.useMemo(() => assets.find((asset) => asset.type === "recording"), [assets]);
|
|
2038
|
+
const annotationRef = React.useRef(null);
|
|
2039
|
+
const customFormRef = React.useRef(null);
|
|
2040
|
+
const [isCapturing, setIsCapturing] = React.useState(false);
|
|
2041
|
+
const [error, setError] = React.useState(null);
|
|
2042
|
+
React.useEffect(() => {
|
|
1718
2043
|
if (!CustomForm) {
|
|
1719
2044
|
return;
|
|
1720
2045
|
}
|
|
@@ -1794,7 +2119,7 @@ function StepDescribe({ onNext, CustomForm }) {
|
|
|
1794
2119
|
onNext();
|
|
1795
2120
|
};
|
|
1796
2121
|
const canContinue = Boolean(draft.title.trim()) && (!config.features.screenshot || Boolean(screenshot));
|
|
1797
|
-
return /* @__PURE__ */
|
|
2122
|
+
return /* @__PURE__ */ React__namespace.createElement("div", { style: { ...inlineStyles.step, display: "flex", flexDirection: "column", minHeight: "100%" } }, /* @__PURE__ */ React__namespace.createElement("h2", { style: inlineStyles.h2 }, "Report a bug"), /* @__PURE__ */ React__namespace.createElement("p", { style: inlineStyles.p }, "Provide enough context so engineers can reproduce what happened."), /* @__PURE__ */ React__namespace.createElement("label", { style: inlineStyles.field }, "Title", /* @__PURE__ */ React__namespace.createElement(
|
|
1798
2123
|
"input",
|
|
1799
2124
|
{
|
|
1800
2125
|
style: inlineStyles.input,
|
|
@@ -1803,7 +2128,7 @@ function StepDescribe({ onNext, CustomForm }) {
|
|
|
1803
2128
|
placeholder: "Short summary",
|
|
1804
2129
|
required: true
|
|
1805
2130
|
}
|
|
1806
|
-
)), /* @__PURE__ */
|
|
2131
|
+
)), /* @__PURE__ */ React__namespace.createElement("label", { style: inlineStyles.field }, "Description", /* @__PURE__ */ React__namespace.createElement(
|
|
1807
2132
|
"textarea",
|
|
1808
2133
|
{
|
|
1809
2134
|
style: inlineStyles.input,
|
|
@@ -1812,7 +2137,7 @@ function StepDescribe({ onNext, CustomForm }) {
|
|
|
1812
2137
|
placeholder: "What happened?",
|
|
1813
2138
|
rows: 4
|
|
1814
2139
|
}
|
|
1815
|
-
)), CustomForm ? /* @__PURE__ */
|
|
2140
|
+
)), CustomForm ? /* @__PURE__ */ React__namespace.createElement("div", { ref: customFormRef }, /* @__PURE__ */ React__namespace.createElement(CustomForm, { attributes, setAttributes, updateAttribute })) : null, config.features.screenshot || config.features.recording ? /* @__PURE__ */ React__namespace.createElement(React__namespace.Fragment, null, /* @__PURE__ */ React__namespace.createElement("h3", { style: inlineStyles.h3 }, "Capture"), /* @__PURE__ */ React__namespace.createElement("div", { style: inlineStyles.captureRow }, config.features.screenshot ? /* @__PURE__ */ React__namespace.createElement("div", { style: inlineStyles.captureItem }, /* @__PURE__ */ React__namespace.createElement(
|
|
1816
2141
|
"button",
|
|
1817
2142
|
{
|
|
1818
2143
|
type: "button",
|
|
@@ -1820,10 +2145,10 @@ function StepDescribe({ onNext, CustomForm }) {
|
|
|
1820
2145
|
onClick: startCapture,
|
|
1821
2146
|
disabled: isCapturing
|
|
1822
2147
|
},
|
|
1823
|
-
/* @__PURE__ */
|
|
1824
|
-
/* @__PURE__ */
|
|
1825
|
-
screenshot ? /* @__PURE__ */
|
|
1826
|
-
)) : null, config.features.recording ? /* @__PURE__ */
|
|
2148
|
+
/* @__PURE__ */ React__namespace.createElement("svg", { style: inlineStyles.captureIcon, viewBox: "0 0 24 24", "aria-hidden": "true", focusable: "false" }, /* @__PURE__ */ React__namespace.createElement("path", { d: "M4 8.5h3l1.2-2h7.6l1.2 2h3v9H4z" }), /* @__PURE__ */ React__namespace.createElement("circle", { cx: "12", cy: "13", r: "3.5" })),
|
|
2149
|
+
/* @__PURE__ */ React__namespace.createElement("span", null, isCapturing ? "Capturing..." : screenshot ? "Retake screenshot" : "Screenshot"),
|
|
2150
|
+
screenshot ? /* @__PURE__ */ React__namespace.createElement("span", { style: inlineStyles.captureDone }, "Saved") : null
|
|
2151
|
+
)) : null, config.features.recording ? /* @__PURE__ */ React__namespace.createElement(StepRecording, { embedded: true, compact: true }) : null), /* @__PURE__ */ React__namespace.createElement("p", { style: inlineStyles.captureNote }, config.features.screenshot && config.features.recording ? `Capture a screenshot and optionally record up to ${config.storage.limits.maxVideoSeconds} seconds.` : config.features.screenshot ? "Capture the relevant area before continuing." : `Record up to ${config.storage.limits.maxVideoSeconds} seconds.`), screenshot ? /* @__PURE__ */ React__namespace.createElement("div", { style: inlineStyles.previewWrapper }, config.features.annotations ? /* @__PURE__ */ React__namespace.createElement(AnnotationCanvas, { ref: annotationRef, imageUrl: screenshot.previewUrl }) : /* @__PURE__ */ React__namespace.createElement("img", { src: screenshot.previewUrl, alt: "Screenshot preview", style: inlineStyles.preview })) : null, !screenshot && recording ? /* @__PURE__ */ React__namespace.createElement("p", { style: inlineStyles.captureNote }, "Recording saved.") : null) : null, error ? /* @__PURE__ */ React__namespace.createElement("p", { style: inlineStyles.error }, error) : null, /* @__PURE__ */ React__namespace.createElement(
|
|
1827
2152
|
"div",
|
|
1828
2153
|
{
|
|
1829
2154
|
style: {
|
|
@@ -1836,7 +2161,7 @@ function StepDescribe({ onNext, CustomForm }) {
|
|
|
1836
2161
|
paddingBottom: "12px"
|
|
1837
2162
|
}
|
|
1838
2163
|
},
|
|
1839
|
-
/* @__PURE__ */
|
|
2164
|
+
/* @__PURE__ */ React__namespace.createElement(
|
|
1840
2165
|
"button",
|
|
1841
2166
|
{
|
|
1842
2167
|
type: "button",
|
|
@@ -1855,7 +2180,7 @@ function StepReview({ onBack }) {
|
|
|
1855
2180
|
state: { assets, draft, isSubmitting, uploadProgress, error },
|
|
1856
2181
|
submit
|
|
1857
2182
|
} = useBugReporter();
|
|
1858
|
-
return /* @__PURE__ */
|
|
2183
|
+
return /* @__PURE__ */ React__namespace.createElement("div", { style: { ...inlineStyles.step, display: "flex", flexDirection: "column", minHeight: "100%" } }, /* @__PURE__ */ React__namespace.createElement("h2", { style: inlineStyles.h2 }, "Review and submit"), /* @__PURE__ */ React__namespace.createElement("p", { style: inlineStyles.p }, "Confirm details, then send your report."), /* @__PURE__ */ React__namespace.createElement("div", { style: inlineStyles.summary }, /* @__PURE__ */ React__namespace.createElement("strong", null, draft.title || "Untitled bug report"), /* @__PURE__ */ React__namespace.createElement("p", { style: inlineStyles.p }, draft.description || "No description provided.")), /* @__PURE__ */ React__namespace.createElement("div", { style: inlineStyles.assets }, assets.map((asset) => /* @__PURE__ */ React__namespace.createElement("div", { key: asset.id, style: inlineStyles.assetCard }, /* @__PURE__ */ React__namespace.createElement("span", { style: inlineStyles.assetType }, asset.type), asset.type === "recording" ? /* @__PURE__ */ React__namespace.createElement("video", { src: asset.previewUrl, controls: true, style: inlineStyles.preview }) : /* @__PURE__ */ React__namespace.createElement("img", { src: asset.previewUrl, alt: `${asset.type} preview`, style: inlineStyles.preview })))), isSubmitting ? /* @__PURE__ */ React__namespace.createElement("div", { style: inlineStyles.uploadProgress, "aria-live": "polite" }, "Uploading assets: ", Math.round(uploadProgress * 100), "%", /* @__PURE__ */ React__namespace.createElement("div", { style: inlineStyles.progressTrack }, /* @__PURE__ */ React__namespace.createElement("div", { style: { ...inlineStyles.progressFill, width: `${Math.round(uploadProgress * 100)}%` } }))) : null, error ? /* @__PURE__ */ React__namespace.createElement("p", { style: inlineStyles.error }, error) : null, /* @__PURE__ */ React__namespace.createElement(
|
|
1859
2184
|
"div",
|
|
1860
2185
|
{
|
|
1861
2186
|
style: {
|
|
@@ -1868,7 +2193,7 @@ function StepReview({ onBack }) {
|
|
|
1868
2193
|
paddingBottom: "12px"
|
|
1869
2194
|
}
|
|
1870
2195
|
},
|
|
1871
|
-
/* @__PURE__ */
|
|
2196
|
+
/* @__PURE__ */ React__namespace.createElement(
|
|
1872
2197
|
"button",
|
|
1873
2198
|
{
|
|
1874
2199
|
type: "button",
|
|
@@ -1878,7 +2203,7 @@ function StepReview({ onBack }) {
|
|
|
1878
2203
|
},
|
|
1879
2204
|
"Back"
|
|
1880
2205
|
),
|
|
1881
|
-
/* @__PURE__ */
|
|
2206
|
+
/* @__PURE__ */ React__namespace.createElement(
|
|
1882
2207
|
"button",
|
|
1883
2208
|
{
|
|
1884
2209
|
type: "button",
|
|
@@ -1895,15 +2220,15 @@ function StepReview({ onBack }) {
|
|
|
1895
2220
|
var DOCK_SIDES = ["left", "right", "top", "bottom"];
|
|
1896
2221
|
function DockIcon({ side }) {
|
|
1897
2222
|
if (side === "left") {
|
|
1898
|
-
return /* @__PURE__ */
|
|
2223
|
+
return /* @__PURE__ */ React__namespace.createElement("svg", { viewBox: "0 0 24 24", "aria-hidden": "true", focusable: "false", style: inlineStyles.iconSvg }, /* @__PURE__ */ React__namespace.createElement("path", { d: "M3.5 5.5h17v13h-17z" }), /* @__PURE__ */ React__namespace.createElement("path", { d: "M3.5 5.5h4.5v13H3.5z", fill: "currentColor", stroke: "none", opacity: "0.85" }));
|
|
1899
2224
|
}
|
|
1900
2225
|
if (side === "right") {
|
|
1901
|
-
return /* @__PURE__ */
|
|
2226
|
+
return /* @__PURE__ */ React__namespace.createElement("svg", { viewBox: "0 0 24 24", "aria-hidden": "true", focusable: "false", style: inlineStyles.iconSvg }, /* @__PURE__ */ React__namespace.createElement("path", { d: "M3.5 5.5h17v13h-17z" }), /* @__PURE__ */ React__namespace.createElement("path", { d: "M16 5.5h4.5v13H16z", fill: "currentColor", stroke: "none", opacity: "0.85" }));
|
|
1902
2227
|
}
|
|
1903
2228
|
if (side === "top") {
|
|
1904
|
-
return /* @__PURE__ */
|
|
2229
|
+
return /* @__PURE__ */ React__namespace.createElement("svg", { viewBox: "0 0 24 24", "aria-hidden": "true", focusable: "false", style: inlineStyles.iconSvg }, /* @__PURE__ */ React__namespace.createElement("path", { d: "M3.5 5.5h17v13h-17z" }), /* @__PURE__ */ React__namespace.createElement("path", { d: "M3.5 5.5h17v4.5h-17z", fill: "currentColor", stroke: "none", opacity: "0.85" }));
|
|
1905
2230
|
}
|
|
1906
|
-
return /* @__PURE__ */
|
|
2231
|
+
return /* @__PURE__ */ React__namespace.createElement("svg", { viewBox: "0 0 24 24", "aria-hidden": "true", focusable: "false", style: inlineStyles.iconSvg }, /* @__PURE__ */ React__namespace.createElement("path", { d: "M3.5 5.5h17v13h-17z" }), /* @__PURE__ */ React__namespace.createElement("path", { d: "M3.5 14h17v4.5h-17z", fill: "currentColor", stroke: "none", opacity: "0.85" }));
|
|
1907
2232
|
}
|
|
1908
2233
|
function BugReporterShell({ CustomForm, launcherPosition, launcherText, themeMode, buttonColor }) {
|
|
1909
2234
|
const { config, state, setDockSide, setStep, close, reset } = useBugReporter();
|
|
@@ -1917,7 +2242,7 @@ function BugReporterShell({ CustomForm, launcherPosition, launcherText, themeMod
|
|
|
1917
2242
|
close();
|
|
1918
2243
|
};
|
|
1919
2244
|
const modalTitle = state.step === "success" ? "Report submitted" : "Report a bug";
|
|
1920
|
-
return /* @__PURE__ */
|
|
2245
|
+
return /* @__PURE__ */ React__namespace.createElement(React__namespace.Fragment, null, /* @__PURE__ */ React__namespace.createElement(LauncherButton, { position: launcherPosition, text: launcherText, themeMode, buttonColor }), /* @__PURE__ */ React__namespace.createElement(
|
|
1921
2246
|
Modal,
|
|
1922
2247
|
{
|
|
1923
2248
|
isOpen: state.isOpen,
|
|
@@ -1928,9 +2253,9 @@ function BugReporterShell({ CustomForm, launcherPosition, launcherText, themeMod
|
|
|
1928
2253
|
zIndex: config.theme.zIndex + 1,
|
|
1929
2254
|
onRequestClose: requestClose
|
|
1930
2255
|
},
|
|
1931
|
-
/* @__PURE__ */
|
|
2256
|
+
/* @__PURE__ */ React__namespace.createElement("div", { style: inlineStyles.modalHeader }, /* @__PURE__ */ React__namespace.createElement("div", { style: inlineStyles.dockControls, role: "group", "aria-label": "Dock side" }, DOCK_SIDES.map((side) => {
|
|
1932
2257
|
const isActive = state.dockSide === side;
|
|
1933
|
-
return /* @__PURE__ */
|
|
2258
|
+
return /* @__PURE__ */ React__namespace.createElement(
|
|
1934
2259
|
"button",
|
|
1935
2260
|
{
|
|
1936
2261
|
key: side,
|
|
@@ -1940,11 +2265,11 @@ function BugReporterShell({ CustomForm, launcherPosition, launcherText, themeMod
|
|
|
1940
2265
|
"aria-pressed": isActive,
|
|
1941
2266
|
"aria-label": `Dock ${side}`
|
|
1942
2267
|
},
|
|
1943
|
-
/* @__PURE__ */
|
|
2268
|
+
/* @__PURE__ */ React__namespace.createElement(DockIcon, { side })
|
|
1944
2269
|
);
|
|
1945
|
-
})), /* @__PURE__ */
|
|
1946
|
-
state.step === "describe" ? /* @__PURE__ */
|
|
1947
|
-
state.step === "review" || state.step === "submitting" ? /* @__PURE__ */
|
|
2270
|
+
})), /* @__PURE__ */ React__namespace.createElement("button", { style: inlineStyles.iconButton, type: "button", onClick: requestClose, "aria-label": "Close bug reporter" }, /* @__PURE__ */ React__namespace.createElement("svg", { viewBox: "0 0 24 24", "aria-hidden": "true", focusable: "false", style: inlineStyles.iconSvg }, /* @__PURE__ */ React__namespace.createElement("path", { d: "M6 6 18 18M18 6 6 18" })))),
|
|
2271
|
+
state.step === "describe" ? /* @__PURE__ */ React__namespace.createElement(StepDescribe, { onNext: nextFromDescribe, CustomForm }) : null,
|
|
2272
|
+
state.step === "review" || state.step === "submitting" ? /* @__PURE__ */ React__namespace.createElement(
|
|
1948
2273
|
StepReview,
|
|
1949
2274
|
{
|
|
1950
2275
|
onBack: () => {
|
|
@@ -1952,7 +2277,7 @@ function BugReporterShell({ CustomForm, launcherPosition, launcherText, themeMod
|
|
|
1952
2277
|
}
|
|
1953
2278
|
}
|
|
1954
2279
|
) : null,
|
|
1955
|
-
state.step === "success" ? /* @__PURE__ */
|
|
2280
|
+
state.step === "success" ? /* @__PURE__ */ React__namespace.createElement("div", { style: inlineStyles.step }, /* @__PURE__ */ React__namespace.createElement("h2", { style: inlineStyles.h2 }, "Thanks, report submitted"), /* @__PURE__ */ React__namespace.createElement("p", { style: inlineStyles.p }, "Your bug report has been sent successfully."), /* @__PURE__ */ React__namespace.createElement("div", { style: inlineStyles.actions }, /* @__PURE__ */ React__namespace.createElement("button", { type: "button", style: getButtonStyle("primary"), onClick: requestClose }, "Close"))) : null
|
|
1956
2281
|
));
|
|
1957
2282
|
}
|
|
1958
2283
|
function BugReporter({
|
|
@@ -1965,7 +2290,7 @@ function BugReporter({
|
|
|
1965
2290
|
}) {
|
|
1966
2291
|
var _a, _b;
|
|
1967
2292
|
const resolvedButtonColor = (_b = buttonColor != null ? buttonColor : (_a = config.theme) == null ? void 0 : _a.primaryColor) != null ? _b : "#3b82f6";
|
|
1968
|
-
return /* @__PURE__ */
|
|
2293
|
+
return /* @__PURE__ */ React__namespace.createElement(BugReporterProvider, { config }, /* @__PURE__ */ React__namespace.createElement(
|
|
1969
2294
|
BugReporterShell,
|
|
1970
2295
|
{
|
|
1971
2296
|
CustomForm,
|
|
@@ -1977,11 +2302,8 @@ function BugReporter({
|
|
|
1977
2302
|
));
|
|
1978
2303
|
}
|
|
1979
2304
|
|
|
1980
|
-
Object.defineProperty(exports, "BugReporterError", {
|
|
1981
|
-
enumerable: true,
|
|
1982
|
-
get: function () { return chunk6TCI6T2U_cjs.BugReporterError; }
|
|
1983
|
-
});
|
|
1984
2305
|
exports.BugReporter = BugReporter;
|
|
2306
|
+
exports.BugReporterError = BugReporterError;
|
|
1985
2307
|
exports.BugReporterProvider = BugReporterProvider;
|
|
1986
2308
|
exports.useBugReporter = useBugReporter;
|
|
1987
2309
|
//# sourceMappingURL=index.cjs.map
|