uidex 0.1.1 → 0.2.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/core/index.cjs +287 -32
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +109 -1
- package/dist/core/index.d.ts +109 -1
- package/dist/core/index.global.js +283 -31
- package/dist/core/index.global.js.map +1 -1
- package/dist/core/index.js +283 -31
- package/dist/core/index.js.map +1 -1
- package/dist/core/style.css +29 -22
- package/dist/index.cjs +295 -33
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +77 -2
- package/dist/index.d.ts +77 -2
- package/dist/index.js +295 -33
- package/dist/index.js.map +1 -1
- package/dist/react/index.cjs +295 -33
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +77 -2
- package/dist/react/index.d.ts +77 -2
- package/dist/react/index.js +295 -33
- package/dist/react/index.js.map +1 -1
- package/package.json +1 -1
package/dist/react/index.d.cts
CHANGED
|
@@ -35,6 +35,77 @@ interface KeyboardShortcut {
|
|
|
35
35
|
altKey?: boolean;
|
|
36
36
|
metaKey?: boolean;
|
|
37
37
|
}
|
|
38
|
+
interface IngestConfig {
|
|
39
|
+
endpoint: string;
|
|
40
|
+
apiKey: string;
|
|
41
|
+
environment?: string;
|
|
42
|
+
appVersion?: string;
|
|
43
|
+
reporter?: {
|
|
44
|
+
email?: string;
|
|
45
|
+
name?: string;
|
|
46
|
+
};
|
|
47
|
+
metadata?: Record<string, string>;
|
|
48
|
+
captureConsole?: boolean;
|
|
49
|
+
captureNetwork?: boolean;
|
|
50
|
+
}
|
|
51
|
+
type FeedbackResult = {
|
|
52
|
+
ok: true;
|
|
53
|
+
id: string;
|
|
54
|
+
sequenceNumber: number;
|
|
55
|
+
} | {
|
|
56
|
+
ok: false;
|
|
57
|
+
error: string;
|
|
58
|
+
};
|
|
59
|
+
type FeedbackType = 'bug' | 'feature' | 'improvement' | 'question';
|
|
60
|
+
type FeedbackSeverity = 'low' | 'medium' | 'high' | 'critical';
|
|
61
|
+
interface ConsoleLogEntry {
|
|
62
|
+
level: 'warn' | 'error';
|
|
63
|
+
message: string;
|
|
64
|
+
timestamp: string;
|
|
65
|
+
}
|
|
66
|
+
interface NetworkErrorEntry {
|
|
67
|
+
url: string;
|
|
68
|
+
method: string;
|
|
69
|
+
status: number | null;
|
|
70
|
+
statusText: string;
|
|
71
|
+
timestamp: string;
|
|
72
|
+
}
|
|
73
|
+
interface FeedbackReport {
|
|
74
|
+
type: FeedbackType;
|
|
75
|
+
severity: FeedbackSeverity;
|
|
76
|
+
title?: string;
|
|
77
|
+
description: string;
|
|
78
|
+
componentId: string;
|
|
79
|
+
element: string | null;
|
|
80
|
+
sources: {
|
|
81
|
+
filePath: string;
|
|
82
|
+
line: number;
|
|
83
|
+
}[];
|
|
84
|
+
url: string;
|
|
85
|
+
path: string;
|
|
86
|
+
route: string | null;
|
|
87
|
+
timestamp: string;
|
|
88
|
+
pageTitle: string;
|
|
89
|
+
locale: string;
|
|
90
|
+
sessionId: string;
|
|
91
|
+
viewport: {
|
|
92
|
+
width: number;
|
|
93
|
+
height: number;
|
|
94
|
+
};
|
|
95
|
+
screen: {
|
|
96
|
+
width: number;
|
|
97
|
+
height: number;
|
|
98
|
+
};
|
|
99
|
+
userAgent: string;
|
|
100
|
+
screenshot?: string;
|
|
101
|
+
reporterEmail?: string;
|
|
102
|
+
reporterName?: string;
|
|
103
|
+
environment?: string;
|
|
104
|
+
appVersion?: string;
|
|
105
|
+
metadata?: Record<string, string>;
|
|
106
|
+
consoleLogs?: ConsoleLogEntry[];
|
|
107
|
+
networkErrors?: NetworkErrorEntry[];
|
|
108
|
+
}
|
|
38
109
|
|
|
39
110
|
interface UidexDevtoolsProps {
|
|
40
111
|
components?: UidexMap;
|
|
@@ -44,8 +115,12 @@ interface UidexDevtoolsProps {
|
|
|
44
115
|
onSelect?: (id: string) => void;
|
|
45
116
|
/** Keyboard shortcut to toggle inspect mode. Default: Shift+Cmd+U. Set to false to disable. */
|
|
46
117
|
inspectShortcut?: KeyboardShortcut | false;
|
|
118
|
+
/** Ingest configuration for submitting feedback to a server. */
|
|
119
|
+
ingest?: IngestConfig;
|
|
120
|
+
/** Called after feedback submission with the report and result. */
|
|
121
|
+
onSubmit?: (report: FeedbackReport, result: FeedbackResult) => void;
|
|
47
122
|
}
|
|
48
|
-
declare function UidexDevtools({ components, config, buttonPosition, disabled, onSelect, inspectShortcut, }: UidexDevtoolsProps): null;
|
|
123
|
+
declare function UidexDevtools({ components, config, buttonPosition, disabled, onSelect, inspectShortcut, ingest, onSubmit, }: UidexDevtoolsProps): null;
|
|
49
124
|
|
|
50
125
|
interface UidexOverlayProps {
|
|
51
126
|
target: HTMLElement | null;
|
|
@@ -71,4 +146,4 @@ declare function getContrastColor(hexColor: string): string;
|
|
|
71
146
|
declare function hexToRgba(hex: string, alpha: number): string;
|
|
72
147
|
declare function resolveColor(color: string | undefined, colorMap?: Record<string, string>): string | undefined;
|
|
73
148
|
|
|
74
|
-
export { type BorderStyle, type ButtonPosition, type KeyboardShortcut, type LabelPosition, type UidexConfig, type UidexDefaults, UidexDevtools, type UidexDevtoolsProps, type UidexFeature, type UidexLocation, type UidexMap, UidexOverlay, type UidexOverlayProps, type UidexPage, classNames, getComponents, getContrastColor, getFeatures, getPages, hexToRgba, registerComponents, registerFeatures, registerPages, resolveColor };
|
|
149
|
+
export { type BorderStyle, type ButtonPosition, type FeedbackReport, type FeedbackResult, type FeedbackSeverity, type FeedbackType, type IngestConfig, type KeyboardShortcut, type LabelPosition, type UidexConfig, type UidexDefaults, UidexDevtools, type UidexDevtoolsProps, type UidexFeature, type UidexLocation, type UidexMap, UidexOverlay, type UidexOverlayProps, type UidexPage, classNames, getComponents, getContrastColor, getFeatures, getPages, hexToRgba, registerComponents, registerFeatures, registerPages, resolveColor };
|
package/dist/react/index.d.ts
CHANGED
|
@@ -35,6 +35,77 @@ interface KeyboardShortcut {
|
|
|
35
35
|
altKey?: boolean;
|
|
36
36
|
metaKey?: boolean;
|
|
37
37
|
}
|
|
38
|
+
interface IngestConfig {
|
|
39
|
+
endpoint: string;
|
|
40
|
+
apiKey: string;
|
|
41
|
+
environment?: string;
|
|
42
|
+
appVersion?: string;
|
|
43
|
+
reporter?: {
|
|
44
|
+
email?: string;
|
|
45
|
+
name?: string;
|
|
46
|
+
};
|
|
47
|
+
metadata?: Record<string, string>;
|
|
48
|
+
captureConsole?: boolean;
|
|
49
|
+
captureNetwork?: boolean;
|
|
50
|
+
}
|
|
51
|
+
type FeedbackResult = {
|
|
52
|
+
ok: true;
|
|
53
|
+
id: string;
|
|
54
|
+
sequenceNumber: number;
|
|
55
|
+
} | {
|
|
56
|
+
ok: false;
|
|
57
|
+
error: string;
|
|
58
|
+
};
|
|
59
|
+
type FeedbackType = 'bug' | 'feature' | 'improvement' | 'question';
|
|
60
|
+
type FeedbackSeverity = 'low' | 'medium' | 'high' | 'critical';
|
|
61
|
+
interface ConsoleLogEntry {
|
|
62
|
+
level: 'warn' | 'error';
|
|
63
|
+
message: string;
|
|
64
|
+
timestamp: string;
|
|
65
|
+
}
|
|
66
|
+
interface NetworkErrorEntry {
|
|
67
|
+
url: string;
|
|
68
|
+
method: string;
|
|
69
|
+
status: number | null;
|
|
70
|
+
statusText: string;
|
|
71
|
+
timestamp: string;
|
|
72
|
+
}
|
|
73
|
+
interface FeedbackReport {
|
|
74
|
+
type: FeedbackType;
|
|
75
|
+
severity: FeedbackSeverity;
|
|
76
|
+
title?: string;
|
|
77
|
+
description: string;
|
|
78
|
+
componentId: string;
|
|
79
|
+
element: string | null;
|
|
80
|
+
sources: {
|
|
81
|
+
filePath: string;
|
|
82
|
+
line: number;
|
|
83
|
+
}[];
|
|
84
|
+
url: string;
|
|
85
|
+
path: string;
|
|
86
|
+
route: string | null;
|
|
87
|
+
timestamp: string;
|
|
88
|
+
pageTitle: string;
|
|
89
|
+
locale: string;
|
|
90
|
+
sessionId: string;
|
|
91
|
+
viewport: {
|
|
92
|
+
width: number;
|
|
93
|
+
height: number;
|
|
94
|
+
};
|
|
95
|
+
screen: {
|
|
96
|
+
width: number;
|
|
97
|
+
height: number;
|
|
98
|
+
};
|
|
99
|
+
userAgent: string;
|
|
100
|
+
screenshot?: string;
|
|
101
|
+
reporterEmail?: string;
|
|
102
|
+
reporterName?: string;
|
|
103
|
+
environment?: string;
|
|
104
|
+
appVersion?: string;
|
|
105
|
+
metadata?: Record<string, string>;
|
|
106
|
+
consoleLogs?: ConsoleLogEntry[];
|
|
107
|
+
networkErrors?: NetworkErrorEntry[];
|
|
108
|
+
}
|
|
38
109
|
|
|
39
110
|
interface UidexDevtoolsProps {
|
|
40
111
|
components?: UidexMap;
|
|
@@ -44,8 +115,12 @@ interface UidexDevtoolsProps {
|
|
|
44
115
|
onSelect?: (id: string) => void;
|
|
45
116
|
/** Keyboard shortcut to toggle inspect mode. Default: Shift+Cmd+U. Set to false to disable. */
|
|
46
117
|
inspectShortcut?: KeyboardShortcut | false;
|
|
118
|
+
/** Ingest configuration for submitting feedback to a server. */
|
|
119
|
+
ingest?: IngestConfig;
|
|
120
|
+
/** Called after feedback submission with the report and result. */
|
|
121
|
+
onSubmit?: (report: FeedbackReport, result: FeedbackResult) => void;
|
|
47
122
|
}
|
|
48
|
-
declare function UidexDevtools({ components, config, buttonPosition, disabled, onSelect, inspectShortcut, }: UidexDevtoolsProps): null;
|
|
123
|
+
declare function UidexDevtools({ components, config, buttonPosition, disabled, onSelect, inspectShortcut, ingest, onSubmit, }: UidexDevtoolsProps): null;
|
|
49
124
|
|
|
50
125
|
interface UidexOverlayProps {
|
|
51
126
|
target: HTMLElement | null;
|
|
@@ -71,4 +146,4 @@ declare function getContrastColor(hexColor: string): string;
|
|
|
71
146
|
declare function hexToRgba(hex: string, alpha: number): string;
|
|
72
147
|
declare function resolveColor(color: string | undefined, colorMap?: Record<string, string>): string | undefined;
|
|
73
148
|
|
|
74
|
-
export { type BorderStyle, type ButtonPosition, type KeyboardShortcut, type LabelPosition, type UidexConfig, type UidexDefaults, UidexDevtools, type UidexDevtoolsProps, type UidexFeature, type UidexLocation, type UidexMap, UidexOverlay, type UidexOverlayProps, type UidexPage, classNames, getComponents, getContrastColor, getFeatures, getPages, hexToRgba, registerComponents, registerFeatures, registerPages, resolveColor };
|
|
149
|
+
export { type BorderStyle, type ButtonPosition, type FeedbackReport, type FeedbackResult, type FeedbackSeverity, type FeedbackType, type IngestConfig, type KeyboardShortcut, type LabelPosition, type UidexConfig, type UidexDefaults, UidexDevtools, type UidexDevtoolsProps, type UidexFeature, type UidexLocation, type UidexMap, UidexOverlay, type UidexOverlayProps, type UidexPage, classNames, getComponents, getContrastColor, getFeatures, getPages, hexToRgba, registerComponents, registerFeatures, registerPages, resolveColor };
|
package/dist/react/index.js
CHANGED
|
@@ -528,6 +528,154 @@ var Inspector = class {
|
|
|
528
528
|
}
|
|
529
529
|
};
|
|
530
530
|
|
|
531
|
+
// src/core/ingest.ts
|
|
532
|
+
var MAX_CONSOLE_LOGS = 50;
|
|
533
|
+
var MAX_NETWORK_ERRORS = 20;
|
|
534
|
+
var nativeFetch = null;
|
|
535
|
+
function safeStringify(value) {
|
|
536
|
+
if (typeof value === "string") return value;
|
|
537
|
+
try {
|
|
538
|
+
return JSON.stringify(value);
|
|
539
|
+
} catch {
|
|
540
|
+
return String(value);
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
var IngestCapture = class {
|
|
544
|
+
constructor(captureConsole, captureNetwork) {
|
|
545
|
+
this.captureConsole = captureConsole;
|
|
546
|
+
this.captureNetwork = captureNetwork;
|
|
547
|
+
}
|
|
548
|
+
consoleLogs = [];
|
|
549
|
+
networkErrors = [];
|
|
550
|
+
originalConsoleWarn = null;
|
|
551
|
+
originalConsoleError = null;
|
|
552
|
+
originalFetch = null;
|
|
553
|
+
start() {
|
|
554
|
+
this.consoleLogs = [];
|
|
555
|
+
this.networkErrors = [];
|
|
556
|
+
if (this.captureConsole) this.interceptConsole();
|
|
557
|
+
if (this.captureNetwork) this.interceptNetwork();
|
|
558
|
+
}
|
|
559
|
+
stop() {
|
|
560
|
+
this.restoreConsole();
|
|
561
|
+
this.restoreNetwork();
|
|
562
|
+
}
|
|
563
|
+
getConsoleLogs() {
|
|
564
|
+
return [...this.consoleLogs];
|
|
565
|
+
}
|
|
566
|
+
getNetworkErrors() {
|
|
567
|
+
return [...this.networkErrors];
|
|
568
|
+
}
|
|
569
|
+
interceptConsole() {
|
|
570
|
+
if (this.originalConsoleWarn) return;
|
|
571
|
+
this.originalConsoleWarn = console.warn;
|
|
572
|
+
this.originalConsoleError = console.error;
|
|
573
|
+
console.warn = (...args) => {
|
|
574
|
+
this.addConsoleLog("warn", args);
|
|
575
|
+
this.originalConsoleWarn.apply(console, args);
|
|
576
|
+
};
|
|
577
|
+
console.error = (...args) => {
|
|
578
|
+
this.addConsoleLog("error", args);
|
|
579
|
+
this.originalConsoleError.apply(console, args);
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
addConsoleLog(level, args) {
|
|
583
|
+
this.consoleLogs.push({
|
|
584
|
+
level,
|
|
585
|
+
message: args.map(safeStringify).join(" "),
|
|
586
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
587
|
+
});
|
|
588
|
+
if (this.consoleLogs.length > MAX_CONSOLE_LOGS) {
|
|
589
|
+
this.consoleLogs.shift();
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
restoreConsole() {
|
|
593
|
+
if (this.originalConsoleWarn) {
|
|
594
|
+
console.warn = this.originalConsoleWarn;
|
|
595
|
+
this.originalConsoleWarn = null;
|
|
596
|
+
}
|
|
597
|
+
if (this.originalConsoleError) {
|
|
598
|
+
console.error = this.originalConsoleError;
|
|
599
|
+
this.originalConsoleError = null;
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
interceptNetwork() {
|
|
603
|
+
if (this.originalFetch) return;
|
|
604
|
+
this.originalFetch = window.fetch;
|
|
605
|
+
if (!nativeFetch) nativeFetch = this.originalFetch;
|
|
606
|
+
window.fetch = async (...args) => {
|
|
607
|
+
try {
|
|
608
|
+
const response = await this.originalFetch.apply(window, args);
|
|
609
|
+
if (!response.ok) {
|
|
610
|
+
this.addNetworkError(args[0], args[1]?.method, response.status, response.statusText);
|
|
611
|
+
}
|
|
612
|
+
return response;
|
|
613
|
+
} catch (error) {
|
|
614
|
+
this.addNetworkError(
|
|
615
|
+
args[0],
|
|
616
|
+
args[1]?.method,
|
|
617
|
+
null,
|
|
618
|
+
error instanceof Error ? error.message : "Network error"
|
|
619
|
+
);
|
|
620
|
+
throw error;
|
|
621
|
+
}
|
|
622
|
+
};
|
|
623
|
+
}
|
|
624
|
+
addNetworkError(input, method, status, statusText) {
|
|
625
|
+
let url;
|
|
626
|
+
if (typeof input === "string") {
|
|
627
|
+
url = input;
|
|
628
|
+
} else if (input instanceof URL) {
|
|
629
|
+
url = input.href;
|
|
630
|
+
} else {
|
|
631
|
+
url = input.url;
|
|
632
|
+
method ??= input.method;
|
|
633
|
+
}
|
|
634
|
+
this.networkErrors.push({
|
|
635
|
+
url,
|
|
636
|
+
method: method ?? "GET",
|
|
637
|
+
status,
|
|
638
|
+
statusText,
|
|
639
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
640
|
+
});
|
|
641
|
+
if (this.networkErrors.length > MAX_NETWORK_ERRORS) {
|
|
642
|
+
this.networkErrors.shift();
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
restoreNetwork() {
|
|
646
|
+
if (this.originalFetch) {
|
|
647
|
+
window.fetch = this.originalFetch;
|
|
648
|
+
this.originalFetch = null;
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
};
|
|
652
|
+
function generateSessionId() {
|
|
653
|
+
if (typeof crypto !== "undefined" && crypto.randomUUID) {
|
|
654
|
+
return crypto.randomUUID();
|
|
655
|
+
}
|
|
656
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
657
|
+
const r = Math.random() * 16 | 0;
|
|
658
|
+
const v = c === "x" ? r : r & 3 | 8;
|
|
659
|
+
return v.toString(16);
|
|
660
|
+
});
|
|
661
|
+
}
|
|
662
|
+
async function submitFeedback(endpoint, apiKey, report) {
|
|
663
|
+
const fetchFn = nativeFetch ?? fetch;
|
|
664
|
+
const response = await fetchFn(endpoint, {
|
|
665
|
+
method: "POST",
|
|
666
|
+
headers: {
|
|
667
|
+
"Content-Type": "application/json",
|
|
668
|
+
Authorization: `Bearer ${apiKey}`
|
|
669
|
+
},
|
|
670
|
+
body: JSON.stringify(report)
|
|
671
|
+
});
|
|
672
|
+
if (!response.ok) {
|
|
673
|
+
const text = await response.text().catch(() => "");
|
|
674
|
+
throw new Error(`Ingest failed (${response.status}): ${text}`);
|
|
675
|
+
}
|
|
676
|
+
return response.json();
|
|
677
|
+
}
|
|
678
|
+
|
|
531
679
|
// src/core/modal.ts
|
|
532
680
|
var Modal = class _Modal {
|
|
533
681
|
backdrop = null;
|
|
@@ -1510,6 +1658,14 @@ var Modal = class _Modal {
|
|
|
1510
1658
|
"medium"
|
|
1511
1659
|
);
|
|
1512
1660
|
form.appendChild(severitySelect.group);
|
|
1661
|
+
const titleGroup = this.createFormGroup("Title");
|
|
1662
|
+
const titleInput = document.createElement("input");
|
|
1663
|
+
titleInput.type = "text";
|
|
1664
|
+
titleInput.className = "uidex-form-input";
|
|
1665
|
+
titleInput.placeholder = "Brief summary (optional)";
|
|
1666
|
+
titleInput.maxLength = 200;
|
|
1667
|
+
titleGroup.appendChild(titleInput);
|
|
1668
|
+
form.appendChild(titleGroup);
|
|
1513
1669
|
const descGroup = this.createFormGroup("Description");
|
|
1514
1670
|
const textarea = document.createElement("textarea");
|
|
1515
1671
|
textarea.className = "uidex-form-textarea";
|
|
@@ -1517,6 +1673,22 @@ var Modal = class _Modal {
|
|
|
1517
1673
|
textarea.rows = 4;
|
|
1518
1674
|
descGroup.appendChild(textarea);
|
|
1519
1675
|
form.appendChild(descGroup);
|
|
1676
|
+
let emailInput;
|
|
1677
|
+
let nameInput;
|
|
1678
|
+
if (this.options.ingest && !this.options.ingest.reporter) {
|
|
1679
|
+
const reporterGroup = this.createFormGroup("Reporter");
|
|
1680
|
+
nameInput = document.createElement("input");
|
|
1681
|
+
nameInput.type = "text";
|
|
1682
|
+
nameInput.className = "uidex-form-input";
|
|
1683
|
+
nameInput.placeholder = "Name (optional)";
|
|
1684
|
+
reporterGroup.appendChild(nameInput);
|
|
1685
|
+
emailInput = document.createElement("input");
|
|
1686
|
+
emailInput.type = "email";
|
|
1687
|
+
emailInput.className = "uidex-form-input";
|
|
1688
|
+
emailInput.placeholder = "Email (optional)";
|
|
1689
|
+
reporterGroup.appendChild(emailInput);
|
|
1690
|
+
form.appendChild(reporterGroup);
|
|
1691
|
+
}
|
|
1520
1692
|
const screenshotGroup = document.createElement("div");
|
|
1521
1693
|
screenshotGroup.className = "uidex-form-group";
|
|
1522
1694
|
const screenshotLabel = document.createElement("label");
|
|
@@ -1552,9 +1724,14 @@ var Modal = class _Modal {
|
|
|
1552
1724
|
}
|
|
1553
1725
|
const env = this.collectEnv();
|
|
1554
1726
|
const page = this.data.pages.find((p) => p.componentIds.includes(id));
|
|
1727
|
+
const { ingest } = this.options;
|
|
1728
|
+
const reporterEmail = ingest?.reporter?.email || emailInput?.value.trim() || void 0;
|
|
1729
|
+
const reporterName = ingest?.reporter?.name || nameInput?.value.trim() || void 0;
|
|
1730
|
+
const titleValue = titleInput.value.trim();
|
|
1555
1731
|
const report = {
|
|
1556
1732
|
type: typeSelect.select.value,
|
|
1557
1733
|
severity: severitySelect.select.value,
|
|
1734
|
+
...titleValue ? { title: titleValue } : {},
|
|
1558
1735
|
description: textarea.value.trim(),
|
|
1559
1736
|
componentId: id,
|
|
1560
1737
|
element: element ? this.describeElement(element) : null,
|
|
@@ -1563,17 +1740,66 @@ var Modal = class _Modal {
|
|
|
1563
1740
|
path: window.location.pathname,
|
|
1564
1741
|
route: page?.dir ?? null,
|
|
1565
1742
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1743
|
+
pageTitle: document.title,
|
|
1744
|
+
locale: navigator.language,
|
|
1745
|
+
sessionId: this.options.sessionId ?? "",
|
|
1566
1746
|
viewport: env.viewport,
|
|
1567
1747
|
screen: env.screen,
|
|
1568
1748
|
userAgent: env.userAgent,
|
|
1569
|
-
screenshot
|
|
1749
|
+
screenshot,
|
|
1750
|
+
...reporterEmail ? { reporterEmail } : {},
|
|
1751
|
+
...reporterName ? { reporterName } : {},
|
|
1752
|
+
...ingest?.environment ? { environment: ingest.environment } : {},
|
|
1753
|
+
...ingest?.appVersion ? { appVersion: ingest.appVersion } : {},
|
|
1754
|
+
...ingest?.metadata ? { metadata: ingest.metadata } : {}
|
|
1570
1755
|
};
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1756
|
+
const consoleLogs = this.options.getConsoleLogs?.();
|
|
1757
|
+
if (consoleLogs && consoleLogs.length > 0) {
|
|
1758
|
+
report.consoleLogs = consoleLogs;
|
|
1759
|
+
}
|
|
1760
|
+
const networkErrors = this.options.getNetworkErrors?.();
|
|
1761
|
+
if (networkErrors && networkErrors.length > 0) {
|
|
1762
|
+
report.networkErrors = networkErrors;
|
|
1763
|
+
}
|
|
1764
|
+
const showSuccess = (autoClose) => {
|
|
1765
|
+
submitBtn.textContent = "Submitted!";
|
|
1766
|
+
if (autoClose) {
|
|
1767
|
+
setTimeout(() => this.hide(), 1500);
|
|
1768
|
+
} else {
|
|
1769
|
+
setTimeout(() => {
|
|
1770
|
+
submitBtn.textContent = "Submit";
|
|
1771
|
+
submitBtn.disabled = false;
|
|
1772
|
+
}, 1500);
|
|
1773
|
+
}
|
|
1774
|
+
};
|
|
1775
|
+
if (ingest) {
|
|
1776
|
+
submitBtn.textContent = "Submitting\u2026";
|
|
1777
|
+
try {
|
|
1778
|
+
const serverResult = await submitFeedback(
|
|
1779
|
+
ingest.endpoint,
|
|
1780
|
+
ingest.apiKey,
|
|
1781
|
+
report
|
|
1782
|
+
);
|
|
1783
|
+
this.options.onSubmit?.(report, {
|
|
1784
|
+
ok: true,
|
|
1785
|
+
id: serverResult.id,
|
|
1786
|
+
sequenceNumber: serverResult.sequenceNumber
|
|
1787
|
+
});
|
|
1788
|
+
showSuccess(true);
|
|
1789
|
+
} catch (err) {
|
|
1790
|
+
const errorMessage = err instanceof Error ? err.message : "Unknown error";
|
|
1791
|
+
console.warn("[uidex] Feedback submission failed:", errorMessage);
|
|
1792
|
+
this.options.onSubmit?.(report, { ok: false, error: errorMessage });
|
|
1793
|
+
submitBtn.textContent = "Failed \u2014 retry?";
|
|
1794
|
+
submitBtn.disabled = false;
|
|
1795
|
+
}
|
|
1796
|
+
} else {
|
|
1797
|
+
if (!this.options.onSubmit) {
|
|
1798
|
+
console.log("[uidex] Feedback submitted:", report);
|
|
1799
|
+
}
|
|
1800
|
+
this.options.onSubmit?.(report, { ok: true, id: "", sequenceNumber: 0 });
|
|
1801
|
+
showSuccess(false);
|
|
1802
|
+
}
|
|
1577
1803
|
});
|
|
1578
1804
|
form.appendChild(submitBtn);
|
|
1579
1805
|
this.mainContent.appendChild(form);
|
|
@@ -2693,49 +2919,56 @@ body.uidex-inspecting * {
|
|
|
2693
2919
|
color: var(--uidex-color-text-muted);
|
|
2694
2920
|
}
|
|
2695
2921
|
|
|
2696
|
-
.uidex-form-select
|
|
2697
|
-
|
|
2698
|
-
|
|
2922
|
+
.uidex-form-select,
|
|
2923
|
+
.uidex-form-input,
|
|
2924
|
+
.uidex-form-textarea {
|
|
2699
2925
|
border: 1px solid var(--uidex-color-border);
|
|
2700
2926
|
border-radius: 0;
|
|
2701
2927
|
background: transparent;
|
|
2702
2928
|
color: var(--uidex-color-text);
|
|
2703
2929
|
font-size: var(--uidex-font-size-sm);
|
|
2704
2930
|
font-family: var(--uidex-font-mono);
|
|
2705
|
-
cursor: pointer;
|
|
2706
2931
|
outline: none;
|
|
2932
|
+
}
|
|
2933
|
+
|
|
2934
|
+
.uidex-form-select:focus,
|
|
2935
|
+
.uidex-form-input:focus,
|
|
2936
|
+
.uidex-form-textarea:focus {
|
|
2937
|
+
border-color: rgba(255, 255, 255, 0.3);
|
|
2938
|
+
}
|
|
2939
|
+
|
|
2940
|
+
.uidex-form-input::placeholder,
|
|
2941
|
+
.uidex-form-textarea::placeholder {
|
|
2942
|
+
color: var(--uidex-color-text-muted);
|
|
2943
|
+
}
|
|
2944
|
+
|
|
2945
|
+
.uidex-form-select {
|
|
2946
|
+
appearance: none;
|
|
2947
|
+
padding: 6px 10px;
|
|
2948
|
+
cursor: pointer;
|
|
2707
2949
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23ababab' d='M3 5l3 3 3-3'/%3E%3C/svg%3E");
|
|
2708
2950
|
background-repeat: no-repeat;
|
|
2709
2951
|
background-position: right 8px center;
|
|
2710
2952
|
padding-right: 26px;
|
|
2711
2953
|
}
|
|
2712
2954
|
|
|
2713
|
-
.uidex-form-
|
|
2714
|
-
|
|
2955
|
+
.uidex-form-input {
|
|
2956
|
+
padding: 6px 10px;
|
|
2957
|
+
width: 100%;
|
|
2958
|
+
box-sizing: border-box;
|
|
2959
|
+
}
|
|
2960
|
+
|
|
2961
|
+
.uidex-form-input + .uidex-form-input {
|
|
2962
|
+
margin-top: 6px;
|
|
2715
2963
|
}
|
|
2716
2964
|
|
|
2717
2965
|
.uidex-form-textarea {
|
|
2718
2966
|
padding: 8px 10px;
|
|
2719
|
-
border: 1px solid var(--uidex-color-border);
|
|
2720
|
-
border-radius: 0;
|
|
2721
|
-
background: transparent;
|
|
2722
|
-
color: var(--uidex-color-text);
|
|
2723
|
-
font-size: var(--uidex-font-size-sm);
|
|
2724
|
-
font-family: var(--uidex-font-mono);
|
|
2725
2967
|
line-height: 1.5;
|
|
2726
2968
|
resize: vertical;
|
|
2727
|
-
outline: none;
|
|
2728
2969
|
min-height: 80px;
|
|
2729
2970
|
}
|
|
2730
2971
|
|
|
2731
|
-
.uidex-form-textarea::placeholder {
|
|
2732
|
-
color: var(--uidex-color-text-muted);
|
|
2733
|
-
}
|
|
2734
|
-
|
|
2735
|
-
.uidex-form-textarea:focus {
|
|
2736
|
-
border-color: rgba(255, 255, 255, 0.3);
|
|
2737
|
-
}
|
|
2738
|
-
|
|
2739
2972
|
.uidex-form-submit {
|
|
2740
2973
|
padding: 6px 16px;
|
|
2741
2974
|
border: 1px solid var(--uidex-color-border);
|
|
@@ -2754,7 +2987,7 @@ body.uidex-inspecting * {
|
|
|
2754
2987
|
background: var(--uidex-color-primary-hover);
|
|
2755
2988
|
}
|
|
2756
2989
|
|
|
2757
|
-
.uidex-form-submit:active {
|
|
2990
|
+
.uidex-form-submit:not(:disabled):active {
|
|
2758
2991
|
transform: translateY(1px);
|
|
2759
2992
|
}
|
|
2760
2993
|
|
|
@@ -2898,8 +3131,17 @@ var UidexUI = class {
|
|
|
2898
3131
|
copyTimer = null;
|
|
2899
3132
|
currentPresentIds = [];
|
|
2900
3133
|
activeMode = null;
|
|
3134
|
+
sessionId;
|
|
3135
|
+
capture = null;
|
|
2901
3136
|
constructor(options = {}) {
|
|
2902
3137
|
this.options = options;
|
|
3138
|
+
this.sessionId = generateSessionId();
|
|
3139
|
+
if (options.ingest?.captureConsole || options.ingest?.captureNetwork) {
|
|
3140
|
+
this.capture = new IngestCapture(
|
|
3141
|
+
options.ingest.captureConsole ?? false,
|
|
3142
|
+
options.ingest.captureNetwork ?? false
|
|
3143
|
+
);
|
|
3144
|
+
}
|
|
2903
3145
|
this.overlay = new Overlay({
|
|
2904
3146
|
color: options.config?.defaults?.color,
|
|
2905
3147
|
borderStyle: options.config?.defaults?.borderStyle,
|
|
@@ -2916,7 +3158,12 @@ var UidexUI = class {
|
|
|
2916
3158
|
}
|
|
2917
3159
|
this.options.onSelect?.(id);
|
|
2918
3160
|
},
|
|
2919
|
-
elementGetter: (id) => this.findElement(id)
|
|
3161
|
+
elementGetter: (id) => this.findElement(id),
|
|
3162
|
+
ingest: options.ingest,
|
|
3163
|
+
onSubmit: options.onSubmit,
|
|
3164
|
+
sessionId: this.sessionId,
|
|
3165
|
+
getConsoleLogs: () => this.capture?.getConsoleLogs() ?? [],
|
|
3166
|
+
getNetworkErrors: () => this.capture?.getNetworkErrors() ?? []
|
|
2920
3167
|
});
|
|
2921
3168
|
this.menu = new Menu({
|
|
2922
3169
|
onInspectToggle: () => this.toggleMode("inspect"),
|
|
@@ -2967,12 +3214,14 @@ var UidexUI = class {
|
|
|
2967
3214
|
this.modal.setShadowRoot(this.shadowRoot);
|
|
2968
3215
|
this.updateModalData(presentIds);
|
|
2969
3216
|
this.inspector?.mount();
|
|
3217
|
+
this.capture?.start();
|
|
2970
3218
|
this.startObserving();
|
|
2971
3219
|
this.mounted = true;
|
|
2972
3220
|
}
|
|
2973
3221
|
destroy() {
|
|
2974
3222
|
if (!this.mounted) return;
|
|
2975
3223
|
this.stopObserving();
|
|
3224
|
+
this.capture?.stop();
|
|
2976
3225
|
if (this.copyTimer !== null) {
|
|
2977
3226
|
clearTimeout(this.copyTimer);
|
|
2978
3227
|
this.copyTimer = null;
|
|
@@ -3162,7 +3411,9 @@ function UidexDevtools({
|
|
|
3162
3411
|
buttonPosition = "bottom-right",
|
|
3163
3412
|
disabled = false,
|
|
3164
3413
|
onSelect,
|
|
3165
|
-
inspectShortcut
|
|
3414
|
+
inspectShortcut,
|
|
3415
|
+
ingest,
|
|
3416
|
+
onSubmit
|
|
3166
3417
|
}) {
|
|
3167
3418
|
const uiRef = useRef(null);
|
|
3168
3419
|
const stableShortcut = useMemo(
|
|
@@ -3172,6 +3423,15 @@ function UidexDevtools({
|
|
|
3172
3423
|
inspectShortcut === false ? false : `${inspectShortcut?.key}:${inspectShortcut?.ctrlKey}:${inspectShortcut?.shiftKey}:${inspectShortcut?.altKey}:${inspectShortcut?.metaKey}`
|
|
3173
3424
|
]
|
|
3174
3425
|
);
|
|
3426
|
+
const stableIngest = useMemo(
|
|
3427
|
+
() => ingest,
|
|
3428
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
3429
|
+
[
|
|
3430
|
+
ingest ? `${ingest.endpoint}:${ingest.apiKey}:${ingest.environment}:${ingest.appVersion}:${ingest.captureConsole}:${ingest.captureNetwork}:${ingest.reporter?.email}:${ingest.reporter?.name}` : void 0
|
|
3431
|
+
]
|
|
3432
|
+
);
|
|
3433
|
+
const onSubmitRef = useRef(onSubmit);
|
|
3434
|
+
onSubmitRef.current = onSubmit;
|
|
3175
3435
|
useEffect(() => {
|
|
3176
3436
|
if (disabled) {
|
|
3177
3437
|
return;
|
|
@@ -3181,7 +3441,9 @@ function UidexDevtools({
|
|
|
3181
3441
|
config,
|
|
3182
3442
|
buttonPosition,
|
|
3183
3443
|
onSelect,
|
|
3184
|
-
inspectShortcut: stableShortcut
|
|
3444
|
+
inspectShortcut: stableShortcut,
|
|
3445
|
+
ingest: stableIngest,
|
|
3446
|
+
onSubmit: (...args) => onSubmitRef.current?.(...args)
|
|
3185
3447
|
});
|
|
3186
3448
|
ui.mount();
|
|
3187
3449
|
uiRef.current = ui;
|
|
@@ -3189,7 +3451,7 @@ function UidexDevtools({
|
|
|
3189
3451
|
ui.destroy();
|
|
3190
3452
|
uiRef.current = null;
|
|
3191
3453
|
};
|
|
3192
|
-
}, [components, config, buttonPosition, disabled, onSelect, stableShortcut]);
|
|
3454
|
+
}, [components, config, buttonPosition, disabled, onSelect, stableShortcut, stableIngest]);
|
|
3193
3455
|
return null;
|
|
3194
3456
|
}
|
|
3195
3457
|
|